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
19cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport com.android.internal.util.ArrayUtils;
20cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport com.android.internal.util.FastXmlSerializer;
21cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport com.android.internal.util.XmlUtils;
22cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
23cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport org.xmlpull.v1.XmlPullParser;
24cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport org.xmlpull.v1.XmlPullParserException;
25cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport org.xmlpull.v1.XmlSerializer;
26cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
27abb12ed4f2fe5843d5f23a1b3d29ade4f9da76e0Keisuke Kuroyanagiimport android.annotation.Nullable;
28d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gereckeimport android.view.Surface;
29d6396d67201fb2b64d13070324bb115c9c23b08aJason Gereckeimport android.hardware.input.TouchCalibration;
3039606a007a5b1309dd000234f2b8cf156c49fd0fDianne Hackbornimport android.util.AtomicFile;
31cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport android.util.Slog;
32cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport android.util.Xml;
33cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
34cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.BufferedInputStream;
35cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.BufferedOutputStream;
36cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.File;
37cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.FileNotFoundException;
38cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.FileOutputStream;
39cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.IOException;
40cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.InputStream;
419e9e2e73c6ec7bece20268196dc89ad0c8bafad4Wojciech Staszkiewiczimport java.nio.charset.StandardCharsets;
42cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.ArrayList;
43cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.Collections;
44cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.HashMap;
45cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.Map;
46607223f3b7a1c4dc3ac995f742f8d2da50d85ffcNarayan Kamathimport java.util.Objects;
47cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.Set;
48cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
49cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport libcore.io.IoUtils;
50cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
51cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown/**
52cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * Manages persistent state recorded by the input manager service as an XML file.
53cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * Caller must acquire lock on the data store before accessing it.
54cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown *
55cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * File format:
56cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * <code>
57cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * &lt;input-mananger-state>
58cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown *   &lt;input-devices>
59cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown *     &lt;input-device descriptor="xxxxx" keyboard-layout="yyyyy" />
60cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown *   &gt;input-devices>
61cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * &gt;/input-manager-state>
62cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * </code>
63cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown */
64cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownfinal class PersistentDataStore {
65cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    static final String TAG = "InputManager";
66cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
67cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    // Input device state by descriptor.
68cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private final HashMap<String, InputDeviceState> mInputDevices =
69cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            new HashMap<String, InputDeviceState>();
70cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private final AtomicFile mAtomicFile;
71cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
72cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    // True if the data has been loaded.
73cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private boolean mLoaded;
74cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
75cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    // True if there are changes to be saved.
76cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private boolean mDirty;
77cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
78cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public PersistentDataStore() {
79e17b445b6c813f6f9bc93a5e3811128a197ef50bDianne Hackborn        mAtomicFile = new AtomicFile(new File("/data/system/input-manager-state.xml"),
80e17b445b6c813f6f9bc93a5e3811128a197ef50bDianne Hackborn                "input-state");
81cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
82cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
83cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public void saveIfNeeded() {
84cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (mDirty) {
85cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            save();
86cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            mDirty = false;
87cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
88cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
89cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
90d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke    public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
91d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
92d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        if (state == null) {
93d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke            return TouchCalibration.IDENTITY;
94d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        }
95d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
96d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        TouchCalibration cal = state.getTouchCalibration(surfaceRotation);
97d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        if (cal == null) {
98d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            return TouchCalibration.IDENTITY;
99d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        }
100d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        return cal;
101d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke    }
102d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke
103d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke    public boolean setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, TouchCalibration calibration) {
104d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
105d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
106d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        if (state.setTouchCalibration(surfaceRotation, calibration)) {
107d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke            setDirty();
108d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke            return true;
109d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        }
110d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
111d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        return false;
112d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke    }
113d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke
114cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public String getCurrentKeyboardLayout(String inputDeviceDescriptor) {
115cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
116cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return state != null ? state.getCurrentKeyboardLayout() : null;
117cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
118cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
119cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public boolean setCurrentKeyboardLayout(String inputDeviceDescriptor,
120cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            String keyboardLayoutDescriptor) {
121cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
122cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (state.setCurrentKeyboardLayout(keyboardLayoutDescriptor)) {
123cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
124cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
125cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
126cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return false;
127cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
128cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
129cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public String[] getKeyboardLayouts(String inputDeviceDescriptor) {
130cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
131cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (state == null) {
132cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return (String[])ArrayUtils.emptyArray(String.class);
133cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
134cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return state.getKeyboardLayouts();
135cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
136cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
1372d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa    public boolean addKeyboardLayout(String inputDeviceDescriptor,
1382d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            String keyboardLayoutDescriptor) {
139cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
140cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (state.addKeyboardLayout(keyboardLayoutDescriptor)) {
141cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
142cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
143cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
144cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return false;
145cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
146cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
147cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public boolean removeKeyboardLayout(String inputDeviceDescriptor,
148cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            String keyboardLayoutDescriptor) {
149cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
150cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (state.removeKeyboardLayout(keyboardLayoutDescriptor)) {
151cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
152cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
153cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
154cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return false;
155cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
156cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
1572d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa    public boolean switchKeyboardLayout(String inputDeviceDescriptor, int direction) {
158cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
1592d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa        if (state != null && state.switchKeyboardLayout(direction)) {
160cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
161cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
162cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
163cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return false;
164cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
165cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
166cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public boolean removeUninstalledKeyboardLayouts(Set<String> availableKeyboardLayouts) {
167cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        boolean changed = false;
168cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        for (InputDeviceState state : mInputDevices.values()) {
169cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (state.removeUninstalledKeyboardLayouts(availableKeyboardLayouts)) {
170cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                changed = true;
171cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
172cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
173cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (changed) {
174cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
175cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
176cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
177cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return false;
178cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
179cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
180cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private InputDeviceState getInputDeviceState(String inputDeviceDescriptor,
181cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            boolean createIfAbsent) {
182cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        loadIfNeeded();
183cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = mInputDevices.get(inputDeviceDescriptor);
184cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (state == null && createIfAbsent) {
185cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            state = new InputDeviceState();
186cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            mInputDevices.put(inputDeviceDescriptor, state);
187cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
188cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
189cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return state;
190cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
191cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
192cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void loadIfNeeded() {
193cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (!mLoaded) {
194cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            load();
195cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            mLoaded = true;
196cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
197cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
198cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
199cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void setDirty() {
200cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        mDirty = true;
201cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
202cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
203cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void clearState() {
204cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        mInputDevices.clear();
205cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
206cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
207cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void load() {
208cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        clearState();
209cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
210cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        final InputStream is;
211cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        try {
212cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            is = mAtomicFile.openRead();
213cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        } catch (FileNotFoundException ex) {
214cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return;
215cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
216cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
217cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        XmlPullParser parser;
218cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        try {
219cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            parser = Xml.newPullParser();
2209e9e2e73c6ec7bece20268196dc89ad0c8bafad4Wojciech Staszkiewicz            parser.setInput(new BufferedInputStream(is), StandardCharsets.UTF_8.name());
221cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            loadFromXml(parser);
222cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        } catch (IOException ex) {
223cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            Slog.w(InputManagerService.TAG, "Failed to load input manager persistent store data.", ex);
224cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            clearState();
225cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        } catch (XmlPullParserException ex) {
226cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            Slog.w(InputManagerService.TAG, "Failed to load input manager persistent store data.", ex);
227cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            clearState();
228cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        } finally {
229cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            IoUtils.closeQuietly(is);
230cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
231cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
232cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
233cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void save() {
234cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        final FileOutputStream os;
235cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        try {
236cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            os = mAtomicFile.startWrite();
237cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            boolean success = false;
238cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            try {
239cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                XmlSerializer serializer = new FastXmlSerializer();
2409e9e2e73c6ec7bece20268196dc89ad0c8bafad4Wojciech Staszkiewicz                serializer.setOutput(new BufferedOutputStream(os), StandardCharsets.UTF_8.name());
241cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                saveToXml(serializer);
242cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                serializer.flush();
243cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                success = true;
244cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            } finally {
245cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (success) {
246cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    mAtomicFile.finishWrite(os);
247cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                } else {
248cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    mAtomicFile.failWrite(os);
249cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
250cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
251cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        } catch (IOException ex) {
252cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            Slog.w(InputManagerService.TAG, "Failed to save input manager persistent store data.", ex);
253cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
254cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
255cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
256cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void loadFromXml(XmlPullParser parser)
257cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            throws IOException, XmlPullParserException {
258cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        XmlUtils.beginDocument(parser, "input-manager-state");
259cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        final int outerDepth = parser.getDepth();
260cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
261cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (parser.getName().equals("input-devices")) {
262cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                loadInputDevicesFromXml(parser);
263cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
264cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
265cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
266cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
267cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void loadInputDevicesFromXml(XmlPullParser parser)
268cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            throws IOException, XmlPullParserException {
269cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        final int outerDepth = parser.getDepth();
270cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
271cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (parser.getName().equals("input-device")) {
272cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                String descriptor = parser.getAttributeValue(null, "descriptor");
273cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (descriptor == null) {
274cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    throw new XmlPullParserException(
275cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                            "Missing descriptor attribute on input-device.");
276cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
277cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (mInputDevices.containsKey(descriptor)) {
278cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    throw new XmlPullParserException("Found duplicate input device.");
279cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
280cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
281cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                InputDeviceState state = new InputDeviceState();
282cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                state.loadFromXml(parser);
283cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                mInputDevices.put(descriptor, state);
284cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
285cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
286cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
287cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
288cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void saveToXml(XmlSerializer serializer) throws IOException {
289cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.startDocument(null, true);
290cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
291cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.startTag(null, "input-manager-state");
292cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.startTag(null, "input-devices");
293cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        for (Map.Entry<String, InputDeviceState> entry : mInputDevices.entrySet()) {
294cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            final String descriptor = entry.getKey();
295cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            final InputDeviceState state = entry.getValue();
296cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            serializer.startTag(null, "input-device");
297cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            serializer.attribute(null, "descriptor", descriptor);
298cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            state.saveToXml(serializer);
299cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            serializer.endTag(null, "input-device");
300cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
301cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.endTag(null, "input-devices");
302cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.endTag(null, "input-manager-state");
303cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.endDocument();
304cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
305cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
306cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private static final class InputDeviceState {
307d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        private static final String[] CALIBRATION_NAME = { "x_scale",
308d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                "x_ymix", "x_offset", "y_xmix", "y_scale", "y_offset" };
309d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke
310d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        private TouchCalibration[] mTouchCalibration = new TouchCalibration[4];
311abb12ed4f2fe5843d5f23a1b3d29ade4f9da76e0Keisuke Kuroyanagi        @Nullable
312cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        private String mCurrentKeyboardLayout;
3132d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa        private ArrayList<String> mKeyboardLayouts = new ArrayList<String>();
314cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
315d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        public TouchCalibration getTouchCalibration(int surfaceRotation) {
316d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            try {
317d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                return mTouchCalibration[surfaceRotation];
318d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            } catch (ArrayIndexOutOfBoundsException ex) {
319d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                Slog.w(InputManagerService.TAG, "Cannot get touch calibration.", ex);
320d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                return null;
321d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            }
322d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        }
323d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke
324d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        public boolean setTouchCalibration(int surfaceRotation, TouchCalibration calibration) {
325d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            try {
326d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                if (!calibration.equals(mTouchCalibration[surfaceRotation])) {
327d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    mTouchCalibration[surfaceRotation] = calibration;
328d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    return true;
329d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                }
330d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                return false;
331d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            } catch (ArrayIndexOutOfBoundsException ex) {
332d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                Slog.w(InputManagerService.TAG, "Cannot set touch calibration.", ex);
333d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                return false;
334d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke            }
335d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke        }
336d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke
337abb12ed4f2fe5843d5f23a1b3d29ade4f9da76e0Keisuke Kuroyanagi        @Nullable
338cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public String getCurrentKeyboardLayout() {
339cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return mCurrentKeyboardLayout;
340cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
341cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
342cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public boolean setCurrentKeyboardLayout(String keyboardLayout) {
343607223f3b7a1c4dc3ac995f742f8d2da50d85ffcNarayan Kamath            if (Objects.equals(mCurrentKeyboardLayout, keyboardLayout)) {
344cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                return false;
345cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
346cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            addKeyboardLayout(keyboardLayout);
347cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            mCurrentKeyboardLayout = keyboardLayout;
348cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
349cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
350cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
351cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public String[] getKeyboardLayouts() {
3522d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            if (mKeyboardLayouts.isEmpty()) {
353cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                return (String[])ArrayUtils.emptyArray(String.class);
354cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
3552d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            return mKeyboardLayouts.toArray(new String[mKeyboardLayouts.size()]);
356cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
357cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
358cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public boolean addKeyboardLayout(String keyboardLayout) {
3592d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            int index = Collections.binarySearch(mKeyboardLayouts, keyboardLayout);
360cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (index >= 0) {
361cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                return false;
362cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
3632d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            mKeyboardLayouts.add(-index - 1, keyboardLayout);
364cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (mCurrentKeyboardLayout == null) {
365cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                mCurrentKeyboardLayout = keyboardLayout;
366cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
367cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
368cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
369cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
370cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public boolean removeKeyboardLayout(String keyboardLayout) {
3712d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            int index = Collections.binarySearch(mKeyboardLayouts, keyboardLayout);
372cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (index < 0) {
373cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                return false;
374cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
3752d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            mKeyboardLayouts.remove(index);
376cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, index);
377cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
378cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
379cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
380cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        private void updateCurrentKeyboardLayoutIfRemoved(
381cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                String removedKeyboardLayout, int removedIndex) {
382607223f3b7a1c4dc3ac995f742f8d2da50d85ffcNarayan Kamath            if (Objects.equals(mCurrentKeyboardLayout, removedKeyboardLayout)) {
3832d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa                if (!mKeyboardLayouts.isEmpty()) {
384cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    int index = removedIndex;
3852d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa                    if (index == mKeyboardLayouts.size()) {
386cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                        index = 0;
387cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    }
3882d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa                    mCurrentKeyboardLayout = mKeyboardLayouts.get(index);
389cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                } else {
390cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    mCurrentKeyboardLayout = null;
391cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
392cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
393cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
394cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
3952d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa        public boolean switchKeyboardLayout(int direction) {
3962d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            final int size = mKeyboardLayouts.size();
3972d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            if (size < 2) {
3982d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa                return false;
3992d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            }
4002d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            int index = Collections.binarySearch(mKeyboardLayouts, mCurrentKeyboardLayout);
4012d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            assert index >= 0;
4022d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            if (direction > 0) {
4032d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa                index = (index + 1) % size;
4042d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            } else {
4052d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa                index = (index + size - 1) % size;
406cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
4072d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            mCurrentKeyboardLayout = mKeyboardLayouts.get(index);
4082d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            return true;
409cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
410cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
411cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public boolean removeUninstalledKeyboardLayouts(Set<String> availableKeyboardLayouts) {
412cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            boolean changed = false;
4132d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            for (int i = mKeyboardLayouts.size(); i-- > 0; ) {
4142d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa                String keyboardLayout = mKeyboardLayouts.get(i);
415cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (!availableKeyboardLayouts.contains(keyboardLayout)) {
416cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    Slog.i(TAG, "Removing uninstalled keyboard layout " + keyboardLayout);
4172d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa                    mKeyboardLayouts.remove(i);
418cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, i);
419cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    changed = true;
420cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
421cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
422cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return changed;
423cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
424cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
425cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public void loadFromXml(XmlPullParser parser)
426cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                throws IOException, XmlPullParserException {
427cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            final int outerDepth = parser.getDepth();
428cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
429cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (parser.getName().equals("keyboard-layout")) {
430cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    String descriptor = parser.getAttributeValue(null, "descriptor");
431cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    if (descriptor == null) {
432cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                        throw new XmlPullParserException(
433cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                                "Missing descriptor attribute on keyboard-layout.");
434cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    }
435d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright                    String current = parser.getAttributeValue(null, "current");
4362d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa                    if (mKeyboardLayouts.contains(descriptor)) {
4372d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa                        throw new XmlPullParserException(
4382d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa                                "Found duplicate keyboard layout.");
4392d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa                    }
4402d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa
4412d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa                    mKeyboardLayouts.add(descriptor);
442cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    if (current != null && current.equals("true")) {
443cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                        if (mCurrentKeyboardLayout != null) {
444cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                            throw new XmlPullParserException(
445cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                                    "Found multiple current keyboard layouts.");
446cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                        }
447cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                        mCurrentKeyboardLayout = descriptor;
448cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    }
449d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                } else if (parser.getName().equals("calibration")) {
450d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                    String format = parser.getAttributeValue(null, "format");
451d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    String rotation = parser.getAttributeValue(null, "rotation");
452d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    int r = -1;
453d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
454d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                    if (format == null) {
455d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                        throw new XmlPullParserException(
456d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                                "Missing format attribute on calibration.");
457d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                    }
458d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    if (!format.equals("affine")) {
459d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        throw new XmlPullParserException(
460d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                                "Unsupported format for calibration.");
461d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    }
462d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    if (rotation != null) {
463d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        try {
464d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                            r = stringToSurfaceRotation(rotation);
465d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        } catch (IllegalArgumentException e) {
466d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                            throw new XmlPullParserException(
467d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                                    "Unsupported rotation for calibration.");
468d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        }
469d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    }
470d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
471d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    float[] matrix = TouchCalibration.IDENTITY.getAffineTransform();
472d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    int depth = parser.getDepth();
473d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    while (XmlUtils.nextElementWithin(parser, depth)) {
474d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        String tag = parser.getName().toLowerCase();
475d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        String value = parser.nextText();
476d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
477d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        for (int i = 0; i < matrix.length && i < CALIBRATION_NAME.length; i++) {
478d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                            if (tag.equals(CALIBRATION_NAME[i])) {
479d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                                matrix[i] = Float.parseFloat(value);
480d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                                break;
481d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                            }
482d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                        }
483d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    }
484d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
485d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    if (r == -1) {
486d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        // Assume calibration applies to all rotations
487d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        for (r = 0; r < mTouchCalibration.length; r++) {
488d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                            mTouchCalibration[r] = new TouchCalibration(matrix[0],
489d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                                matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
490d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        }
491d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                    } else {
492d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        mTouchCalibration[r] = new TouchCalibration(matrix[0],
493d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                            matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
494d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke                    }
495cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
496cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
497cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
498cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            // Maintain invariant that layouts are sorted.
4992d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            Collections.sort(mKeyboardLayouts);
500cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
501cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            // Maintain invariant that there is always a current keyboard layout unless
502cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            // there are none installed.
5032d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            if (mCurrentKeyboardLayout == null && !mKeyboardLayouts.isEmpty()) {
5042d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa                mCurrentKeyboardLayout = mKeyboardLayouts.get(0);
505cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
506cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
507cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
508cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public void saveToXml(XmlSerializer serializer) throws IOException {
5092d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa            for (String layout : mKeyboardLayouts) {
510cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                serializer.startTag(null, "keyboard-layout");
511cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                serializer.attribute(null, "descriptor", layout);
512cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (layout.equals(mCurrentKeyboardLayout)) {
513cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    serializer.attribute(null, "current", "true");
514cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
515cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                serializer.endTag(null, "keyboard-layout");
516cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
517d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke
518d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            for (int i = 0; i < mTouchCalibration.length; i++) {
519d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                if (mTouchCalibration[i] != null) {
520d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    String rotation = surfaceRotationToString(i);
521d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    float[] transform = mTouchCalibration[i].getAffineTransform();
522d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
523d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    serializer.startTag(null, "calibration");
524d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    serializer.attribute(null, "format", "affine");
525d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    serializer.attribute(null, "rotation", rotation);
526d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    for (int j = 0; j < transform.length && j < CALIBRATION_NAME.length; j++) {
527d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        serializer.startTag(null, CALIBRATION_NAME[j]);
528d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        serializer.text(Float.toString(transform[j]));
529d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                        serializer.endTag(null, CALIBRATION_NAME[j]);
530d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    }
531d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                    serializer.endTag(null, "calibration");
532d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                }
533d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            }
534d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        }
535d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
536d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        private static String surfaceRotationToString(int surfaceRotation) {
537d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            switch (surfaceRotation) {
538d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                case Surface.ROTATION_0:   return "0";
539d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                case Surface.ROTATION_90:  return "90";
540d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                case Surface.ROTATION_180: return "180";
541d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                case Surface.ROTATION_270: return "270";
542d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            }
543d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            throw new IllegalArgumentException("Unsupported surface rotation value" + surfaceRotation);
544d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        }
545d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke
546d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke        private static int stringToSurfaceRotation(String s) {
547d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            if ("0".equals(s)) {
548d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                return Surface.ROTATION_0;
549d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            }
550d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            if ("90".equals(s)) {
551d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                return Surface.ROTATION_90;
552d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            }
553d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            if ("180".equals(s)) {
554d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                return Surface.ROTATION_180;
555d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            }
556d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            if ("270".equals(s)) {
557d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke                return Surface.ROTATION_270;
558d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke            }
559d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke            throw new IllegalArgumentException("Unsupported surface rotation string '" + s + "'");
560cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
561cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
562d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke}
563