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