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 * <input-mananger-state> 54cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * <input-devices> 55cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * <input-device descriptor="xxxxx" keyboard-layout="yyyyy" /> 56cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * >input-devices> 57cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * >/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}