diff options
author | Jeff Brown <jeffbrown@google.com> | 2012-04-17 11:42:25 -0700 |
---|---|---|
committer | Jeff Brown <jeffbrown@google.com> | 2012-04-17 11:42:25 -0700 |
commit | a3bc565882dd3984e995363642b1295fe3d24d10 (patch) | |
tree | e2894e12724075687867545d988c0eae4769b8e5 /services | |
parent | 4481d9c10ceaf3b886fb5cab1d20941932af5b0f (diff) | |
download | frameworks_base-a3bc565882dd3984e995363642b1295fe3d24d10.zip frameworks_base-a3bc565882dd3984e995363642b1295fe3d24d10.tar.gz frameworks_base-a3bc565882dd3984e995363642b1295fe3d24d10.tar.bz2 |
Add persistence for selected keyboard layout.
Bug: 6110399
Change-Id: I99544bf05e9755385bee478b5f047ccec2e5cae3
Diffstat (limited to 'services')
-rw-r--r-- | services/java/com/android/server/input/InputManagerService.java | 214 |
1 files changed, 208 insertions, 6 deletions
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java index e819432..7640eff 100644 --- a/services/java/com/android/server/input/InputManagerService.java +++ b/services/java/com/android/server/input/InputManagerService.java @@ -16,11 +16,16 @@ package com.android.server.input; +import com.android.internal.os.AtomicFile; +import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import com.android.server.Watchdog; import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; +import android.Manifest; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -63,15 +68,23 @@ import android.view.ViewConfiguration; import android.view.WindowManagerPolicy; import android.view.KeyCharacterMap.UnavailableException; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; +import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; + +import libcore.io.IoUtils; +import libcore.util.Objects; /* * Wraps the C++ InputManager and provides its callbacks. @@ -91,9 +104,8 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. private final Callbacks mCallbacks; private final InputManagerHandler mHandler; - // Used to simulate a persistent data store for keyboard layouts. - // TODO: Replace with the real thing. - private final HashMap<String, String> mFakeRegistry = new HashMap<String, String>(); + // Persistent data store. Must be locked each time during use. + private final PersistentDataStore mDataStore = new PersistentDataStore(); // List of currently registered input devices changed listeners by process id. private Object mInputDevicesLock = new Object(); @@ -635,7 +647,9 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); } - return mFakeRegistry.get(inputDeviceDescriptor); + synchronized (mDataStore) { + return mDataStore.getKeyboardLayout(inputDeviceDescriptor); + } } @Override // Binder call @@ -650,7 +664,13 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); } - mFakeRegistry.put(inputDeviceDescriptor, keyboardLayoutDescriptor); + synchronized (mDataStore) { + try { + mDataStore.setKeyboardLayout(inputDeviceDescriptor, keyboardLayoutDescriptor); + } finally { + mDataStore.saveIfNeeded(); + } + } } /** @@ -862,7 +882,7 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingOrSelfPermission("android.permission.DUMP") + if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump InputManager from from pid=" + Binder.getCallingPid() @@ -1198,4 +1218,186 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. onVibratorTokenDied(this); } } + + /** + * Manages persistent state recorded by the input manager service as an XML file. + * Caller must acquire lock on the data store before accessing it. + * + * File format: + * <code> + * <input-mananger-state> + * <input-devices> + * <input-device descriptor="xxxxx" keyboard-layout="yyyyy" /> + * >input-devices> + * >/input-manager-state> + * </code> + */ + private static final class PersistentDataStore { + // Input device state by descriptor. + private final HashMap<String, InputDeviceState> mInputDevices = + new HashMap<String, InputDeviceState>(); + private final AtomicFile mAtomicFile; + + // True if the data has been loaded. + private boolean mLoaded; + + // True if there are changes to be saved. + private boolean mDirty; + + public PersistentDataStore() { + mAtomicFile = new AtomicFile(new File("/data/system/input-manager-state.xml")); + } + + public void saveIfNeeded() { + if (mDirty) { + save(); + mDirty = false; + } + } + + public String getKeyboardLayout(String inputDeviceDescriptor) { + InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false); + return state != null ? state.keyboardLayoutDescriptor : null; + } + + public boolean setKeyboardLayout(String inputDeviceDescriptor, + String keyboardLayoutDescriptor) { + InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true); + if (!Objects.equal(state.keyboardLayoutDescriptor, keyboardLayoutDescriptor)) { + state.keyboardLayoutDescriptor = keyboardLayoutDescriptor; + setDirty(); + return true; + } + return false; + } + + private InputDeviceState getInputDeviceState(String inputDeviceDescriptor, + boolean createIfAbsent) { + loadIfNeeded(); + InputDeviceState state = mInputDevices.get(inputDeviceDescriptor); + if (state == null && createIfAbsent) { + state = new InputDeviceState(); + mInputDevices.put(inputDeviceDescriptor, state); + setDirty(); + } + return state; + } + + private void loadIfNeeded() { + if (!mLoaded) { + load(); + mLoaded = true; + } + } + + private void setDirty() { + mDirty = true; + } + + private void clearState() { + mInputDevices.clear(); + } + + private void load() { + clearState(); + + final InputStream is; + try { + is = mAtomicFile.openRead(); + } catch (FileNotFoundException ex) { + return; + } + + XmlPullParser parser; + try { + parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream(is), null); + loadFromXml(parser); + } catch (IOException ex) { + Slog.w(TAG, "Failed to load input manager persistent store data.", ex); + clearState(); + } catch (XmlPullParserException ex) { + Slog.w(TAG, "Failed to load input manager persistent store data.", ex); + clearState(); + } finally { + IoUtils.closeQuietly(is); + } + } + + private void save() { + final FileOutputStream os; + try { + os = mAtomicFile.startWrite(); + boolean success = false; + try { + XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(new BufferedOutputStream(os), "utf-8"); + saveToXml(serializer); + serializer.flush(); + success = true; + } finally { + if (success) { + mAtomicFile.finishWrite(os); + } else { + mAtomicFile.failWrite(os); + } + } + } catch (IOException ex) { + Slog.w(TAG, "Failed to save input manager persistent store data.", ex); + } + } + + private void loadFromXml(XmlPullParser parser) + throws IOException, XmlPullParserException { + XmlUtils.beginDocument(parser, "input-manager-state"); + final int outerDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, outerDepth)) { + if (parser.getName().equals("input-devices")) { + loadInputDevicesFromXml(parser); + } + } + } + + private void loadInputDevicesFromXml(XmlPullParser parser) + throws IOException, XmlPullParserException { + final int outerDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, outerDepth)) { + if (parser.getName().equals("input-device")) { + String descriptor = parser.getAttributeValue(null, "descriptor"); + if (descriptor == null) { + throw new XmlPullParserException( + "Missing descriptor attribute on input-device"); + } + InputDeviceState state = new InputDeviceState(); + state.keyboardLayoutDescriptor = + parser.getAttributeValue(null, "keyboard-layout"); + mInputDevices.put(descriptor, state); + } + } + } + + private void saveToXml(XmlSerializer serializer) throws IOException { + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + serializer.startTag(null, "input-manager-state"); + serializer.startTag(null, "input-devices"); + for (Map.Entry<String, InputDeviceState> entry : mInputDevices.entrySet()) { + final String descriptor = entry.getKey(); + final InputDeviceState state = entry.getValue(); + serializer.startTag(null, "input-device"); + serializer.attribute(null, "descriptor", descriptor); + if (state.keyboardLayoutDescriptor != null) { + serializer.attribute(null, "keyboard-layout", state.keyboardLayoutDescriptor); + } + serializer.endTag(null, "input-device"); + } + serializer.endTag(null, "input-devices"); + serializer.endTag(null, "input-manager-state"); + serializer.endDocument(); + } + } + + private static final class InputDeviceState { + public String keyboardLayoutDescriptor; + } } |