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
2739606a007a5b1309dd000234f2b8cf156c49fd0fDianne Hackbornimport android.util.AtomicFile;
28cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport android.util.Slog;
29cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport android.util.Xml;
30cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
31cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.BufferedInputStream;
32cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.BufferedOutputStream;
33cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.File;
34cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.FileNotFoundException;
35cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.FileOutputStream;
36cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.IOException;
37cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.InputStream;
38cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.ArrayList;
39cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.Collections;
40cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.HashMap;
41cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.Map;
42cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.Set;
43cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
44cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport libcore.io.IoUtils;
45cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport libcore.util.Objects;
46cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
47cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown/**
48cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * Manages persistent state recorded by the input manager service as an XML file.
49cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * Caller must acquire lock on the data store before accessing it.
50cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown *
51cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * File format:
52cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * <code>
53cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * &lt;input-mananger-state>
54cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown *   &lt;input-devices>
55cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown *     &lt;input-device descriptor="xxxxx" keyboard-layout="yyyyy" />
56cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown *   &gt;input-devices>
57cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * &gt;/input-manager-state>
58cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * </code>
59cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown */
60cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownfinal class PersistentDataStore {
61cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    static final String TAG = "InputManager";
62cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
63cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    // Input device state by descriptor.
64cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private final HashMap<String, InputDeviceState> mInputDevices =
65cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            new HashMap<String, InputDeviceState>();
66cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private final AtomicFile mAtomicFile;
67cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
68cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    // True if the data has been loaded.
69cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private boolean mLoaded;
70cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
71cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    // True if there are changes to be saved.
72cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private boolean mDirty;
73cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
74cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public PersistentDataStore() {
75cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        mAtomicFile = new AtomicFile(new File("/data/system/input-manager-state.xml"));
76cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
77cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
78cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public void saveIfNeeded() {
79cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (mDirty) {
80cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            save();
81cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            mDirty = false;
82cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
83cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
84cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
85cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public String getCurrentKeyboardLayout(String inputDeviceDescriptor) {
86cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
87cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return state != null ? state.getCurrentKeyboardLayout() : null;
88cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
89cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
90cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public boolean setCurrentKeyboardLayout(String inputDeviceDescriptor,
91cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            String keyboardLayoutDescriptor) {
92cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
93cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (state.setCurrentKeyboardLayout(keyboardLayoutDescriptor)) {
94cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
95cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
96cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
97cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return false;
98cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
99cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
100cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public String[] getKeyboardLayouts(String inputDeviceDescriptor) {
101cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
102cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (state == null) {
103cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return (String[])ArrayUtils.emptyArray(String.class);
104cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
105cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return state.getKeyboardLayouts();
106cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
107cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
108cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public boolean addKeyboardLayout(String inputDeviceDescriptor,
109cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            String keyboardLayoutDescriptor) {
110cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
111cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (state.addKeyboardLayout(keyboardLayoutDescriptor)) {
112cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
113cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
114cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
115cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return false;
116cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
117cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
118cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public boolean removeKeyboardLayout(String inputDeviceDescriptor,
119cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            String keyboardLayoutDescriptor) {
120cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
121cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (state.removeKeyboardLayout(keyboardLayoutDescriptor)) {
122cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
123cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
124cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
125cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return false;
126cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
127cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
128cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public boolean switchKeyboardLayout(String inputDeviceDescriptor, int direction) {
129cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
130cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (state != null && state.switchKeyboardLayout(direction)) {
131cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
132cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
133cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
134cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return false;
135cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
136cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
137cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    public boolean removeUninstalledKeyboardLayouts(Set<String> availableKeyboardLayouts) {
138cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        boolean changed = false;
139cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        for (InputDeviceState state : mInputDevices.values()) {
140cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (state.removeUninstalledKeyboardLayouts(availableKeyboardLayouts)) {
141cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                changed = true;
142cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
143cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
144cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (changed) {
145cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
146cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
147cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
148cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return false;
149cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
150cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
151cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private InputDeviceState getInputDeviceState(String inputDeviceDescriptor,
152cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            boolean createIfAbsent) {
153cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        loadIfNeeded();
154cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        InputDeviceState state = mInputDevices.get(inputDeviceDescriptor);
155cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (state == null && createIfAbsent) {
156cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            state = new InputDeviceState();
157cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            mInputDevices.put(inputDeviceDescriptor, state);
158cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            setDirty();
159cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
160cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        return state;
161cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
162cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
163cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void loadIfNeeded() {
164cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        if (!mLoaded) {
165cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            load();
166cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            mLoaded = true;
167cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
168cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
169cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
170cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void setDirty() {
171cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        mDirty = true;
172cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
173cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
174cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void clearState() {
175cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        mInputDevices.clear();
176cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
177cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
178cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void load() {
179cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        clearState();
180cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
181cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        final InputStream is;
182cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        try {
183cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            is = mAtomicFile.openRead();
184cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        } catch (FileNotFoundException ex) {
185cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return;
186cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
187cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
188cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        XmlPullParser parser;
189cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        try {
190cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            parser = Xml.newPullParser();
191cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            parser.setInput(new BufferedInputStream(is), null);
192cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            loadFromXml(parser);
193cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        } catch (IOException ex) {
194cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            Slog.w(InputManagerService.TAG, "Failed to load input manager persistent store data.", ex);
195cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            clearState();
196cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        } catch (XmlPullParserException ex) {
197cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            Slog.w(InputManagerService.TAG, "Failed to load input manager persistent store data.", ex);
198cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            clearState();
199cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        } finally {
200cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            IoUtils.closeQuietly(is);
201cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
202cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
203cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
204cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void save() {
205cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        final FileOutputStream os;
206cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        try {
207cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            os = mAtomicFile.startWrite();
208cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            boolean success = false;
209cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            try {
210cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                XmlSerializer serializer = new FastXmlSerializer();
211cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                serializer.setOutput(new BufferedOutputStream(os), "utf-8");
212cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                saveToXml(serializer);
213cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                serializer.flush();
214cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                success = true;
215cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            } finally {
216cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (success) {
217cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    mAtomicFile.finishWrite(os);
218cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                } else {
219cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    mAtomicFile.failWrite(os);
220cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
221cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
222cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        } catch (IOException ex) {
223cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            Slog.w(InputManagerService.TAG, "Failed to save input manager persistent store data.", ex);
224cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
225cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
226cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
227cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void loadFromXml(XmlPullParser parser)
228cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            throws IOException, XmlPullParserException {
229cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        XmlUtils.beginDocument(parser, "input-manager-state");
230cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        final int outerDepth = parser.getDepth();
231cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
232cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (parser.getName().equals("input-devices")) {
233cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                loadInputDevicesFromXml(parser);
234cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
235cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
236cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
237cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
238cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void loadInputDevicesFromXml(XmlPullParser parser)
239cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            throws IOException, XmlPullParserException {
240cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        final int outerDepth = parser.getDepth();
241cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
242cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (parser.getName().equals("input-device")) {
243cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                String descriptor = parser.getAttributeValue(null, "descriptor");
244cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (descriptor == null) {
245cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    throw new XmlPullParserException(
246cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                            "Missing descriptor attribute on input-device.");
247cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
248cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (mInputDevices.containsKey(descriptor)) {
249cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    throw new XmlPullParserException("Found duplicate input device.");
250cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
251cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
252cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                InputDeviceState state = new InputDeviceState();
253cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                state.loadFromXml(parser);
254cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                mInputDevices.put(descriptor, state);
255cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
256cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
257cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
258cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
259cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private void saveToXml(XmlSerializer serializer) throws IOException {
260cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.startDocument(null, true);
261cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
262cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.startTag(null, "input-manager-state");
263cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.startTag(null, "input-devices");
264cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        for (Map.Entry<String, InputDeviceState> entry : mInputDevices.entrySet()) {
265cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            final String descriptor = entry.getKey();
266cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            final InputDeviceState state = entry.getValue();
267cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            serializer.startTag(null, "input-device");
268cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            serializer.attribute(null, "descriptor", descriptor);
269cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            state.saveToXml(serializer);
270cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            serializer.endTag(null, "input-device");
271cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
272cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.endTag(null, "input-devices");
273cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.endTag(null, "input-manager-state");
274cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        serializer.endDocument();
275cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
276cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
277cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    private static final class InputDeviceState {
278cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        private String mCurrentKeyboardLayout;
279cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        private ArrayList<String> mKeyboardLayouts = new ArrayList<String>();
280cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
281cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public String getCurrentKeyboardLayout() {
282cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return mCurrentKeyboardLayout;
283cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
284cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
285cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public boolean setCurrentKeyboardLayout(String keyboardLayout) {
286cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (Objects.equal(mCurrentKeyboardLayout, keyboardLayout)) {
287cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                return false;
288cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
289cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            addKeyboardLayout(keyboardLayout);
290cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            mCurrentKeyboardLayout = keyboardLayout;
291cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
292cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
293cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
294cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public String[] getKeyboardLayouts() {
295cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (mKeyboardLayouts.isEmpty()) {
296cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                return (String[])ArrayUtils.emptyArray(String.class);
297cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
298cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return mKeyboardLayouts.toArray(new String[mKeyboardLayouts.size()]);
299cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
300cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
301cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public boolean addKeyboardLayout(String keyboardLayout) {
302cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            int index = Collections.binarySearch(mKeyboardLayouts, keyboardLayout);
303cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (index >= 0) {
304cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                return false;
305cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
306cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            mKeyboardLayouts.add(-index - 1, keyboardLayout);
307cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (mCurrentKeyboardLayout == null) {
308cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                mCurrentKeyboardLayout = keyboardLayout;
309cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
310cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
311cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
312cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
313cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public boolean removeKeyboardLayout(String keyboardLayout) {
314cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            int index = Collections.binarySearch(mKeyboardLayouts, keyboardLayout);
315cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (index < 0) {
316cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                return false;
317cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
318cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            mKeyboardLayouts.remove(index);
319cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, index);
320cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
321cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
322cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
323cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        private void updateCurrentKeyboardLayoutIfRemoved(
324cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                String removedKeyboardLayout, int removedIndex) {
325cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (Objects.equal(mCurrentKeyboardLayout, removedKeyboardLayout)) {
326cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (!mKeyboardLayouts.isEmpty()) {
327cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    int index = removedIndex;
328cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    if (index == mKeyboardLayouts.size()) {
329cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                        index = 0;
330cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    }
331cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    mCurrentKeyboardLayout = mKeyboardLayouts.get(index);
332cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                } else {
333cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    mCurrentKeyboardLayout = null;
334cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
335cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
336cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
337cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
338cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public boolean switchKeyboardLayout(int direction) {
339cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            final int size = mKeyboardLayouts.size();
340cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (size < 2) {
341cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                return false;
342cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
343cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            int index = Collections.binarySearch(mKeyboardLayouts, mCurrentKeyboardLayout);
344cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            assert index >= 0;
345cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (direction > 0) {
346cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                index = (index + 1) % size;
347cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            } else {
348cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                index = (index + size - 1) % size;
349cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
350cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            mCurrentKeyboardLayout = mKeyboardLayouts.get(index);
351cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return true;
352cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
353cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
354cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public boolean removeUninstalledKeyboardLayouts(Set<String> availableKeyboardLayouts) {
355cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            boolean changed = false;
356cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            for (int i = mKeyboardLayouts.size(); i-- > 0; ) {
357cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                String keyboardLayout = mKeyboardLayouts.get(i);
358cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (!availableKeyboardLayouts.contains(keyboardLayout)) {
359cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    Slog.i(TAG, "Removing uninstalled keyboard layout " + keyboardLayout);
360cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    mKeyboardLayouts.remove(i);
361cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, i);
362cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    changed = true;
363cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
364cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
365cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            return changed;
366cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
367cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
368cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public void loadFromXml(XmlPullParser parser)
369cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                throws IOException, XmlPullParserException {
370cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            final int outerDepth = parser.getDepth();
371cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
372cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (parser.getName().equals("keyboard-layout")) {
373cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    String descriptor = parser.getAttributeValue(null, "descriptor");
374cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    if (descriptor == null) {
375cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                        throw new XmlPullParserException(
376cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                                "Missing descriptor attribute on keyboard-layout.");
377cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    }
378cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    String current = parser.getAttributeValue(null, "current");
379cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    if (mKeyboardLayouts.contains(descriptor)) {
380cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                        throw new XmlPullParserException(
381cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                                "Found duplicate keyboard layout.");
382cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    }
383cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
384cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    mKeyboardLayouts.add(descriptor);
385cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    if (current != null && current.equals("true")) {
386cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                        if (mCurrentKeyboardLayout != null) {
387cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                            throw new XmlPullParserException(
388cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                                    "Found multiple current keyboard layouts.");
389cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                        }
390cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                        mCurrentKeyboardLayout = descriptor;
391cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    }
392cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
393cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
394cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
395cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            // Maintain invariant that layouts are sorted.
396cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            Collections.sort(mKeyboardLayouts);
397cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
398cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            // Maintain invariant that there is always a current keyboard layout unless
399cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            // there are none installed.
400cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            if (mCurrentKeyboardLayout == null && !mKeyboardLayouts.isEmpty()) {
401cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                mCurrentKeyboardLayout = mKeyboardLayouts.get(0);
402cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
403cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
404cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown
405cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        public void saveToXml(XmlSerializer serializer) throws IOException {
406cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            for (String layout : mKeyboardLayouts) {
407cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                serializer.startTag(null, "keyboard-layout");
408cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                serializer.attribute(null, "descriptor", layout);
409cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                if (layout.equals(mCurrentKeyboardLayout)) {
410cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                    serializer.attribute(null, "current", "true");
411cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                }
412cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown                serializer.endTag(null, "keyboard-layout");
413cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown            }
414cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown        }
415cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown    }
416cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown}