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