Add persistence for selected keyboard layout.

Bug: 6110399
Change-Id: I99544bf05e9755385bee478b5f047ccec2e5cae3
This commit is contained in:
Jeff Brown
2012-04-17 11:42:25 -07:00
parent 4481d9c10c
commit a3bc565882
2 changed files with 223 additions and 6 deletions

View File

@@ -888,4 +888,19 @@ public class XmlUtils
;
}
}
public static boolean nextElementWithin(XmlPullParser parser, int outerDepth)
throws IOException, XmlPullParserException {
for (;;) {
int type = parser.next();
if (type == XmlPullParser.END_DOCUMENT
|| (type == XmlPullParser.END_TAG && parser.getDepth() == outerDepth)) {
return false;
}
if (type == XmlPullParser.START_TAG
&& parser.getDepth() == outerDepth + 1) {
return true;
}
}
}
}

View File

@@ -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>
* &lt;input-mananger-state>
* &lt;input-devices>
* &lt;input-device descriptor="xxxxx" keyboard-layout="yyyyy" />
* &gt;input-devices>
* &gt;/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;
}
}