1cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown/*
2cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * Copyright (C) 2012 The Android Open Source Project
3cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown *
4cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
5cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * you may not use this file except in compliance with the License.
6cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * You may obtain a copy of the License at
7cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown *
8cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
9cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown *
10cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * Unless required by applicable law or agreed to in writing, software
11cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
12cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * See the License for the specific language governing permissions and
14cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * limitations under the License.
15cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown */
16cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
17cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownpackage com.android.server.input;
18cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
19d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wrightimport com.android.internal.inputmethod.InputMethodSubtypeHandle;
20cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport com.android.internal.util.ArrayUtils;
21cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport com.android.internal.util.FastXmlSerializer;
22cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport com.android.internal.util.XmlUtils;
23cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
24cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport org.xmlpull.v1.XmlPullParser;
25cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport org.xmlpull.v1.XmlPullParserException;
26cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport org.xmlpull.v1.XmlSerializer;
27cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
28abb12ed4f2fe5843d5f23a1b3d29ade4f9da76e0Keisuke Kuroyanagiimport android.annotation.Nullable;
29d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gereckeimport android.view.Surface;
30d6396d67201fb2b64d13070324bb115c9c23b08aJason Gereckeimport android.hardware.input.TouchCalibration;
31d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wrightimport android.text.TextUtils;
32d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wrightimport android.util.ArrayMap;
3339606a007a5b1309dd000234f2b8cf156c49fd0fDianne Hackbornimport android.util.AtomicFile;
34cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport android.util.Slog;
35cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport android.util.Xml;
36cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
37cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.BufferedInputStream;
38cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.BufferedOutputStream;
39cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.File;
40cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.FileNotFoundException;
41cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.FileOutputStream;
42cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.IOException;
43cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.InputStream;
44d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wrightimport java.io.PrintWriter;
459e9e2e73c6ec7bece20268196dc89ad0c8bafad4Wojciech Staszkiewiczimport java.nio.charset.StandardCharsets;
46cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.ArrayList;
47d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wrightimport java.util.Arrays;
48cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.Collections;
49cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.HashMap;
50d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wrightimport java.util.List;
51cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.Map;
52cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.Set;
53cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
54cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport libcore.io.IoUtils;
55cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport libcore.util.Objects;
56cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
57cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown/**
58cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * Manages persistent state recorded by the input manager service as an XML file.
59cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * Caller must acquire lock on the data store before accessing it.
60cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown *
61cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * File format:
62cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * <code>
63cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * &lt;input-mananger-state>
64cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown *   &lt;input-devices>
65cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown *     &lt;input-device descriptor="xxxxx" keyboard-layout="yyyyy" />
66cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown *   &gt;input-devices>
67cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * &gt;/input-manager-state>
68cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * </code>
69cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown */
70cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownfinal class PersistentDataStore {
71cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    static final String TAG = "InputManager";
72cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
73cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    // Input device state by descriptor.
74cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private final HashMap<String, InputDeviceState> mInputDevices =
75cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            new HashMap<String, InputDeviceState>();
76cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private final AtomicFile mAtomicFile;
77cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
78cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    // True if the data has been loaded.
79cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private boolean mLoaded;
80cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
81cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    // True if there are changes to be saved.
82cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private boolean mDirty;
83cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
84cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public PersistentDataStore() {
85cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        mAtomicFile = new AtomicFile(new File("/data/system/input-manager-state.xml"));
86cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
87cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
88cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public void saveIfNeeded() {
89cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (mDirty) {
90cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            save();
91cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            mDirty = false;
92cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
93cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
94cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
95d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke    public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
96d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
97d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        if (state == null) {
98d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke            return TouchCalibration.IDENTITY;
99d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        }
100d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
101d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        TouchCalibration cal = state.getTouchCalibration(surfaceRotation);
102d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        if (cal == null) {
103d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            return TouchCalibration.IDENTITY;
104d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        }
105d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        return cal;
106d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke    }
107d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke
108d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke    public boolean setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, TouchCalibration calibration) {
109d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
110d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
111d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        if (state.setTouchCalibration(surfaceRotation, calibration)) {
112d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke            setDirty();
113d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke            return true;
114d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        }
115d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
116d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        return false;
117d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke    }
118d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke
119cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public String getCurrentKeyboardLayout(String inputDeviceDescriptor) {
120cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
121cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return state != null ? state.getCurrentKeyboardLayout() : null;
122cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
123cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
124cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public boolean setCurrentKeyboardLayout(String inputDeviceDescriptor,
125cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            String keyboardLayoutDescriptor) {
126cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
127cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (state.setCurrentKeyboardLayout(keyboardLayoutDescriptor)) {
128cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
129cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
130cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
131cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return false;
132cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
133cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
134cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public String[] getKeyboardLayouts(String inputDeviceDescriptor) {
135cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
136cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (state == null) {
137cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return (String[])ArrayUtils.emptyArray(String.class);
138cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
139cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return state.getKeyboardLayouts();
140cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
141d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright    public String getKeyboardLayout(String inputDeviceDescriptor,
142d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            InputMethodSubtypeHandle imeHandle) {
143d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
144d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        if (state == null) {
145d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            return null;
146d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        }
147d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        return state.getKeyboardLayout(imeHandle);
148d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright    }
149cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
150d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright    public boolean setKeyboardLayout(String inputDeviceDescriptor,
151d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            InputMethodSubtypeHandle imeHandle, String keyboardLayoutDescriptor) {
152d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
153d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        if (state.setKeyboardLayout(imeHandle, keyboardLayoutDescriptor)) {
154d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            setDirty();
155d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            return true;
156d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        }
157d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        return false;
158d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright    }
159d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright
160d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright    public boolean addKeyboardLayout(String inputDeviceDescriptor, String keyboardLayoutDescriptor) {
161cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
162cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (state.addKeyboardLayout(keyboardLayoutDescriptor)) {
163cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
164cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
165cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
166cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return false;
167cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
168cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
169cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public boolean removeKeyboardLayout(String inputDeviceDescriptor,
170cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            String keyboardLayoutDescriptor) {
171cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
172cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (state.removeKeyboardLayout(keyboardLayoutDescriptor)) {
173cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
174cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
175cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
176cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return false;
177cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
178cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
179d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright    public boolean switchKeyboardLayout(String inputDeviceDescriptor,
180d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            InputMethodSubtypeHandle imeHandle) {
181cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
182d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        if (state != null && state.switchKeyboardLayout(imeHandle)) {
183cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
184cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
185cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
186cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return false;
187cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
188cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
189cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public boolean removeUninstalledKeyboardLayouts(Set<String> availableKeyboardLayouts) {
190cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        boolean changed = false;
191cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        for (InputDeviceState state : mInputDevices.values()) {
192cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (state.removeUninstalledKeyboardLayouts(availableKeyboardLayouts)) {
193cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                changed = true;
194cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
195cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
196cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (changed) {
197cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
198cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
199cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
200cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return false;
201cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
202cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
203cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private InputDeviceState getInputDeviceState(String inputDeviceDescriptor,
204cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            boolean createIfAbsent) {
205cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        loadIfNeeded();
206cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = mInputDevices.get(inputDeviceDescriptor);
207cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (state == null && createIfAbsent) {
208cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            state = new InputDeviceState();
209cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            mInputDevices.put(inputDeviceDescriptor, state);
210cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
211cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
212cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return state;
213cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
214cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
215cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void loadIfNeeded() {
216cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (!mLoaded) {
217cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            load();
218cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            mLoaded = true;
219cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
220cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
221cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
222cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void setDirty() {
223cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        mDirty = true;
224cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
225cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
226cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void clearState() {
227cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        mInputDevices.clear();
228cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
229cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
230cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void load() {
231cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        clearState();
232cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
233cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        final InputStream is;
234cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        try {
235cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            is = mAtomicFile.openRead();
236cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        } catch (FileNotFoundException ex) {
237cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return;
238cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
239cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
240cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        XmlPullParser parser;
241cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        try {
242cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            parser = Xml.newPullParser();
2439e9e2e73c6ec7bece20268196dc89ad0c8bafad4Wojciech Staszkiewicz            parser.setInput(new BufferedInputStream(is), StandardCharsets.UTF_8.name());
244cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            loadFromXml(parser);
245cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        } catch (IOException ex) {
246cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            Slog.w(InputManagerService.TAG, "Failed to load input manager persistent store data.", ex);
247cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            clearState();
248cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        } catch (XmlPullParserException ex) {
249cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            Slog.w(InputManagerService.TAG, "Failed to load input manager persistent store data.", ex);
250cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            clearState();
251cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        } finally {
252cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            IoUtils.closeQuietly(is);
253cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
254cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
255cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
256cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void save() {
257cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        final FileOutputStream os;
258cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        try {
259cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            os = mAtomicFile.startWrite();
260cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            boolean success = false;
261cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            try {
262cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                XmlSerializer serializer = new FastXmlSerializer();
2639e9e2e73c6ec7bece20268196dc89ad0c8bafad4Wojciech Staszkiewicz                serializer.setOutput(new BufferedOutputStream(os), StandardCharsets.UTF_8.name());
264cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                saveToXml(serializer);
265cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                serializer.flush();
266cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                success = true;
267cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            } finally {
268cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (success) {
269cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    mAtomicFile.finishWrite(os);
270cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                } else {
271cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    mAtomicFile.failWrite(os);
272cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
273cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
274cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        } catch (IOException ex) {
275cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            Slog.w(InputManagerService.TAG, "Failed to save input manager persistent store data.", ex);
276cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
277cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
278cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
279cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void loadFromXml(XmlPullParser parser)
280cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            throws IOException, XmlPullParserException {
281cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        XmlUtils.beginDocument(parser, "input-manager-state");
282cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        final int outerDepth = parser.getDepth();
283cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
284cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (parser.getName().equals("input-devices")) {
285cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                loadInputDevicesFromXml(parser);
286cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
287cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
288cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
289cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
290cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void loadInputDevicesFromXml(XmlPullParser parser)
291cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            throws IOException, XmlPullParserException {
292cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        final int outerDepth = parser.getDepth();
293cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
294cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (parser.getName().equals("input-device")) {
295cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                String descriptor = parser.getAttributeValue(null, "descriptor");
296cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (descriptor == null) {
297cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    throw new XmlPullParserException(
298cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                            "Missing descriptor attribute on input-device.");
299cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
300cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (mInputDevices.containsKey(descriptor)) {
301cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    throw new XmlPullParserException("Found duplicate input device.");
302cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
303cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
304cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                InputDeviceState state = new InputDeviceState();
305cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                state.loadFromXml(parser);
306cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                mInputDevices.put(descriptor, state);
307cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
308cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
309cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
310cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
311cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void saveToXml(XmlSerializer serializer) throws IOException {
312cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.startDocument(null, true);
313cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
314cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.startTag(null, "input-manager-state");
315cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.startTag(null, "input-devices");
316cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        for (Map.Entry<String, InputDeviceState> entry : mInputDevices.entrySet()) {
317cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            final String descriptor = entry.getKey();
318cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            final InputDeviceState state = entry.getValue();
319cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            serializer.startTag(null, "input-device");
320cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            serializer.attribute(null, "descriptor", descriptor);
321cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            state.saveToXml(serializer);
322cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            serializer.endTag(null, "input-device");
323cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
324cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.endTag(null, "input-devices");
325cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.endTag(null, "input-manager-state");
326cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.endDocument();
327cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
328cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
329d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright    public void dump(PrintWriter pw, String prefix) {
330d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        pw.println(prefix + "PersistentDataStore");
331d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        pw.println(prefix + "  mLoaded=" + mLoaded);
332d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        pw.println(prefix + "  mDirty=" + mDirty);
333d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        pw.println(prefix + "  InputDeviceStates:");
334d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        int i = 0;
335d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        for (Map.Entry<String, InputDeviceState> entry : mInputDevices.entrySet()) {
336d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            pw.println(prefix + "    " + i++ + ": " + entry.getKey());
337d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            entry.getValue().dump(pw, prefix + "      ");
338d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        }
339d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright    }
340d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright
341cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private static final class InputDeviceState {
342d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        private static final String[] CALIBRATION_NAME = { "x_scale",
343d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                "x_ymix", "x_offset", "y_xmix", "y_scale", "y_offset" };
344d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke
345d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        private TouchCalibration[] mTouchCalibration = new TouchCalibration[4];
346abb12ed4f2fe5843d5f23a1b3d29ade4f9da76e0Keisuke Kuroyanagi        @Nullable
347cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        private String mCurrentKeyboardLayout;
348d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        private List<String> mUnassociatedKeyboardLayouts = new ArrayList<>();
349d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        private ArrayMap<InputMethodSubtypeHandle, String> mKeyboardLayouts = new ArrayMap<>();
350cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
351d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        public TouchCalibration getTouchCalibration(int surfaceRotation) {
352d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            try {
353d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                return mTouchCalibration[surfaceRotation];
354d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            } catch (ArrayIndexOutOfBoundsException ex) {
355d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                Slog.w(InputManagerService.TAG, "Cannot get touch calibration.", ex);
356d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                return null;
357d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            }
358d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        }
359d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke
360d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        public boolean setTouchCalibration(int surfaceRotation, TouchCalibration calibration) {
361d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            try {
362d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                if (!calibration.equals(mTouchCalibration[surfaceRotation])) {
363d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    mTouchCalibration[surfaceRotation] = calibration;
364d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    return true;
365d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                }
366d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                return false;
367d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            } catch (ArrayIndexOutOfBoundsException ex) {
368d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                Slog.w(InputManagerService.TAG, "Cannot set touch calibration.", ex);
369d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                return false;
370d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke            }
371d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        }
372d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke
373abb12ed4f2fe5843d5f23a1b3d29ade4f9da76e0Keisuke Kuroyanagi        @Nullable
374cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public String getCurrentKeyboardLayout() {
375cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return mCurrentKeyboardLayout;
376cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
377cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
378cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public boolean setCurrentKeyboardLayout(String keyboardLayout) {
379cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (Objects.equal(mCurrentKeyboardLayout, keyboardLayout)) {
380cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                return false;
381cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
382cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            addKeyboardLayout(keyboardLayout);
383cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            mCurrentKeyboardLayout = keyboardLayout;
384cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
385cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
386cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
387cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public String[] getKeyboardLayouts() {
388d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            if (mUnassociatedKeyboardLayouts.isEmpty()) {
389cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                return (String[])ArrayUtils.emptyArray(String.class);
390cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
391d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            return mUnassociatedKeyboardLayouts.toArray(
392d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                    new String[mUnassociatedKeyboardLayouts.size()]);
393d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        }
394d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright
395d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        public String getKeyboardLayout(InputMethodSubtypeHandle handle) {
396d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            return mKeyboardLayouts.get(handle);
397d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        }
398d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright
399d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        public boolean setKeyboardLayout(InputMethodSubtypeHandle imeHandle,
400d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                String keyboardLayout) {
401d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            String existingLayout = mKeyboardLayouts.get(imeHandle);
402d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            if (TextUtils.equals(existingLayout, keyboardLayout)) {
403d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                return false;
404d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            }
405d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            mKeyboardLayouts.put(imeHandle, keyboardLayout);
406d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            return true;
407cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
408cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
409cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public boolean addKeyboardLayout(String keyboardLayout) {
410d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            int index = Collections.binarySearch(
411d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                    mUnassociatedKeyboardLayouts, keyboardLayout);
412cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (index >= 0) {
413cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                return false;
414cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
415d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            mUnassociatedKeyboardLayouts.add(-index - 1, keyboardLayout);
416cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (mCurrentKeyboardLayout == null) {
417cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                mCurrentKeyboardLayout = keyboardLayout;
418cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
419cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
420cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
421cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
422cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public boolean removeKeyboardLayout(String keyboardLayout) {
423d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            int index = Collections.binarySearch(mUnassociatedKeyboardLayouts, keyboardLayout);
424cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (index < 0) {
425cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                return false;
426cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
427d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            mUnassociatedKeyboardLayouts.remove(index);
428cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, index);
429cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
430cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
431cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
432cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        private void updateCurrentKeyboardLayoutIfRemoved(
433cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                String removedKeyboardLayout, int removedIndex) {
434cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (Objects.equal(mCurrentKeyboardLayout, removedKeyboardLayout)) {
435d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                if (!mUnassociatedKeyboardLayouts.isEmpty()) {
436cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    int index = removedIndex;
437d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                    if (index == mUnassociatedKeyboardLayouts.size()) {
438cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                        index = 0;
439cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    }
440d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                    mCurrentKeyboardLayout = mUnassociatedKeyboardLayouts.get(index);
441cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                } else {
442cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    mCurrentKeyboardLayout = null;
443cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
444cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
445cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
446cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
447d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        public boolean switchKeyboardLayout(InputMethodSubtypeHandle imeHandle) {
448d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            final String layout = mKeyboardLayouts.get(imeHandle);
449abb12ed4f2fe5843d5f23a1b3d29ade4f9da76e0Keisuke Kuroyanagi            if (!TextUtils.equals(mCurrentKeyboardLayout, layout)) {
450d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                mCurrentKeyboardLayout = layout;
451d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                return true;
452cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
453d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            return false;
454cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
455cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
456cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public boolean removeUninstalledKeyboardLayouts(Set<String> availableKeyboardLayouts) {
457cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            boolean changed = false;
458d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            for (int i = mUnassociatedKeyboardLayouts.size(); i-- > 0; ) {
459d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                String keyboardLayout = mUnassociatedKeyboardLayouts.get(i);
460cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (!availableKeyboardLayouts.contains(keyboardLayout)) {
461cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    Slog.i(TAG, "Removing uninstalled keyboard layout " + keyboardLayout);
462d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                    mUnassociatedKeyboardLayouts.remove(i);
463cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, i);
464cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    changed = true;
465cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
466cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
467cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return changed;
468cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
469cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
470cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public void loadFromXml(XmlPullParser parser)
471cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                throws IOException, XmlPullParserException {
472cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            final int outerDepth = parser.getDepth();
473cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
474cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (parser.getName().equals("keyboard-layout")) {
475cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    String descriptor = parser.getAttributeValue(null, "descriptor");
476cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    if (descriptor == null) {
477cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                        throw new XmlPullParserException(
478cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                                "Missing descriptor attribute on keyboard-layout.");
479cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    }
480cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
481d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                    String current = parser.getAttributeValue(null, "current");
482cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    if (current != null && current.equals("true")) {
483cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                        if (mCurrentKeyboardLayout != null) {
484cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                            throw new XmlPullParserException(
485cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                                    "Found multiple current keyboard layouts.");
486cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                        }
487cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                        mCurrentKeyboardLayout = descriptor;
488cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    }
489d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright
490d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                    String inputMethodId = parser.getAttributeValue(null, "input-method-id");
491d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                    String inputMethodSubtypeId =
492d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                        parser.getAttributeValue(null, "input-method-subtype-id");
493d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                    if (inputMethodId == null && inputMethodSubtypeId != null
494d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                            || inputMethodId != null && inputMethodSubtypeId == null) {
495d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                        throw new XmlPullParserException(
496d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                                "Found an incomplete input method description");
497d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                    }
498d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright
499d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                    if (inputMethodSubtypeId != null) {
500d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                        InputMethodSubtypeHandle handle = new InputMethodSubtypeHandle(
501d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                                inputMethodId, Integer.parseInt(inputMethodSubtypeId));
502d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                        if (mKeyboardLayouts.containsKey(handle)) {
503d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                            throw new XmlPullParserException(
504d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                                    "Found duplicate subtype to keyboard layout mapping: "
505d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                                    + handle);
506d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                        }
507d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                        mKeyboardLayouts.put(handle, descriptor);
508d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                    } else {
509d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                        if (mUnassociatedKeyboardLayouts.contains(descriptor)) {
510d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                            throw new XmlPullParserException(
511d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                                    "Found duplicate unassociated keyboard layout: " + descriptor);
512d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                        }
513d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                        mUnassociatedKeyboardLayouts.add(descriptor);
514d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                    }
515d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                } else if (parser.getName().equals("calibration")) {
516d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                    String format = parser.getAttributeValue(null, "format");
517d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    String rotation = parser.getAttributeValue(null, "rotation");
518d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    int r = -1;
519d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
520d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                    if (format == null) {
521d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                        throw new XmlPullParserException(
522d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                                "Missing format attribute on calibration.");
523d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                    }
524d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    if (!format.equals("affine")) {
525d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        throw new XmlPullParserException(
526d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                                "Unsupported format for calibration.");
527d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    }
528d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    if (rotation != null) {
529d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        try {
530d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                            r = stringToSurfaceRotation(rotation);
531d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        } catch (IllegalArgumentException e) {
532d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                            throw new XmlPullParserException(
533d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                                    "Unsupported rotation for calibration.");
534d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        }
535d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    }
536d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
537d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    float[] matrix = TouchCalibration.IDENTITY.getAffineTransform();
538d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    int depth = parser.getDepth();
539d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    while (XmlUtils.nextElementWithin(parser, depth)) {
540d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        String tag = parser.getName().toLowerCase();
541d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        String value = parser.nextText();
542d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
543d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        for (int i = 0; i < matrix.length && i < CALIBRATION_NAME.length; i++) {
544d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                            if (tag.equals(CALIBRATION_NAME[i])) {
545d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                                matrix[i] = Float.parseFloat(value);
546d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                                break;
547d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                            }
548d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                        }
549d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    }
550d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
551d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    if (r == -1) {
552d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        // Assume calibration applies to all rotations
553d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        for (r = 0; r < mTouchCalibration.length; r++) {
554d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                            mTouchCalibration[r] = new TouchCalibration(matrix[0],
555d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                                matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
556d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        }
557d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                    } else {
558d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        mTouchCalibration[r] = new TouchCalibration(matrix[0],
559d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                            matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
560d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                    }
561cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
562cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
563cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
564cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            // Maintain invariant that layouts are sorted.
565d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            Collections.sort(mUnassociatedKeyboardLayouts);
566cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
567cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            // Maintain invariant that there is always a current keyboard layout unless
568cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            // there are none installed.
569d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            if (mCurrentKeyboardLayout == null && !mUnassociatedKeyboardLayouts.isEmpty()) {
570d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                mCurrentKeyboardLayout = mUnassociatedKeyboardLayouts.get(0);
571cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
572cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
573cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
574cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public void saveToXml(XmlSerializer serializer) throws IOException {
575d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            for (String layout : mUnassociatedKeyboardLayouts) {
576d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                serializer.startTag(null, "keyboard-layout");
577d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                serializer.attribute(null, "descriptor", layout);
578d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                serializer.endTag(null, "keyboard-layout");
579d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            }
580d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright
581d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            final int N = mKeyboardLayouts.size();
582d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            for (int i = 0; i < N; i++) {
583d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                final InputMethodSubtypeHandle handle = mKeyboardLayouts.keyAt(i);
584d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                final String layout = mKeyboardLayouts.valueAt(i);
585cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                serializer.startTag(null, "keyboard-layout");
586cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                serializer.attribute(null, "descriptor", layout);
587d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                serializer.attribute(null, "input-method-id", handle.getInputMethodId());
588d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                serializer.attribute(null, "input-method-subtype-id",
589d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                        Integer.toString(handle.getSubtypeId()));
590cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (layout.equals(mCurrentKeyboardLayout)) {
591cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    serializer.attribute(null, "current", "true");
592cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
593cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                serializer.endTag(null, "keyboard-layout");
594cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
595d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke
596d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            for (int i = 0; i < mTouchCalibration.length; i++) {
597d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                if (mTouchCalibration[i] != null) {
598d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    String rotation = surfaceRotationToString(i);
599d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    float[] transform = mTouchCalibration[i].getAffineTransform();
600d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
601d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    serializer.startTag(null, "calibration");
602d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    serializer.attribute(null, "format", "affine");
603d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    serializer.attribute(null, "rotation", rotation);
604d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    for (int j = 0; j < transform.length && j < CALIBRATION_NAME.length; j++) {
605d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        serializer.startTag(null, CALIBRATION_NAME[j]);
606d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        serializer.text(Float.toString(transform[j]));
607d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        serializer.endTag(null, CALIBRATION_NAME[j]);
608d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    }
609d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    serializer.endTag(null, "calibration");
610d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                }
611d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            }
612d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        }
613d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright
614d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        private void dump(final PrintWriter pw, final String prefix) {
615d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            pw.println(prefix + "CurrentKeyboardLayout=" + mCurrentKeyboardLayout);
616d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            pw.println(prefix + "UnassociatedKeyboardLayouts=" + mUnassociatedKeyboardLayouts);
617d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            pw.println(prefix + "TouchCalibration=" + Arrays.toString(mTouchCalibration));
618d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            pw.println(prefix + "Subtype to Layout Mappings:");
619d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            final int N = mKeyboardLayouts.size();
620d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            if (N != 0) {
621d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                for (int i = 0; i < N; i++) {
622d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                    pw.println(prefix + "  " + mKeyboardLayouts.keyAt(i) + ": "
623d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                            + mKeyboardLayouts.valueAt(i));
624d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                }
625d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            } else {
626d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                pw.println(prefix + "  <none>");
627d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright            }
628d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright        }
629d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
630d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        private static String surfaceRotationToString(int surfaceRotation) {
631d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            switch (surfaceRotation) {
632d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                case Surface.ROTATION_0:   return "0";
633d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                case Surface.ROTATION_90:  return "90";
634d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                case Surface.ROTATION_180: return "180";
635d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                case Surface.ROTATION_270: return "270";
636d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            }
637d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            throw new IllegalArgumentException("Unsupported surface rotation value" + surfaceRotation);
638d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        }
639d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
640d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        private static int stringToSurfaceRotation(String s) {
641d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            if ("0".equals(s)) {
642d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                return Surface.ROTATION_0;
643d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            }
644d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            if ("90".equals(s)) {
645d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                return Surface.ROTATION_90;
646d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            }
647d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            if ("180".equals(s)) {
648d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                return Surface.ROTATION_180;
649d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            }
650d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            if ("270".equals(s)) {
651d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                return Surface.ROTATION_270;
652d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke            }
653d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            throw new IllegalArgumentException("Unsupported surface rotation string '" + s + "'");
654cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
655cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
656d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke}
657