Skip to content

Commit 0192e96

Browse files
Jeff BrownAndroid (Google) Code Review
authored andcommitted
Merge "Add persistence for selected keyboard layout."
2 parents 1645ee2 + a3bc565 commit 0192e96

File tree

2 files changed

+223
-6
lines changed

2 files changed

+223
-6
lines changed

core/java/com/android/internal/util/XmlUtils.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,4 +888,19 @@ public static final void nextElement(XmlPullParser parser) throws XmlPullParserE
888888
;
889889
}
890890
}
891+
892+
public static boolean nextElementWithin(XmlPullParser parser, int outerDepth)
893+
throws IOException, XmlPullParserException {
894+
for (;;) {
895+
int type = parser.next();
896+
if (type == XmlPullParser.END_DOCUMENT
897+
|| (type == XmlPullParser.END_TAG && parser.getDepth() == outerDepth)) {
898+
return false;
899+
}
900+
if (type == XmlPullParser.START_TAG
901+
&& parser.getDepth() == outerDepth + 1) {
902+
return true;
903+
}
904+
}
905+
}
891906
}

services/java/com/android/server/input/InputManagerService.java

Lines changed: 208 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,16 @@
1616

1717
package com.android.server.input;
1818

19+
import com.android.internal.os.AtomicFile;
20+
import com.android.internal.util.FastXmlSerializer;
1921
import com.android.internal.util.XmlUtils;
2022
import com.android.server.Watchdog;
2123

2224
import org.xmlpull.v1.XmlPullParser;
25+
import org.xmlpull.v1.XmlPullParserException;
26+
import org.xmlpull.v1.XmlSerializer;
2327

28+
import android.Manifest;
2429
import android.content.ComponentName;
2530
import android.content.Context;
2631
import android.content.Intent;
@@ -63,15 +68,23 @@
6368
import android.view.WindowManagerPolicy;
6469
import android.view.KeyCharacterMap.UnavailableException;
6570

71+
import java.io.BufferedInputStream;
72+
import java.io.BufferedOutputStream;
6673
import java.io.File;
6774
import java.io.FileDescriptor;
6875
import java.io.FileNotFoundException;
76+
import java.io.FileOutputStream;
6977
import java.io.FileReader;
7078
import java.io.IOException;
79+
import java.io.InputStream;
7180
import java.io.PrintWriter;
7281
import java.util.ArrayList;
7382
import java.util.HashMap;
7483
import java.util.List;
84+
import java.util.Map;
85+
86+
import libcore.io.IoUtils;
87+
import libcore.util.Objects;
7588

7689
/*
7790
* Wraps the C++ InputManager and provides its callbacks.
@@ -91,9 +104,8 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
91104
private final Callbacks mCallbacks;
92105
private final InputManagerHandler mHandler;
93106

94-
// Used to simulate a persistent data store for keyboard layouts.
95-
// TODO: Replace with the real thing.
96-
private final HashMap<String, String> mFakeRegistry = new HashMap<String, String>();
107+
// Persistent data store. Must be locked each time during use.
108+
private final PersistentDataStore mDataStore = new PersistentDataStore();
97109

98110
// List of currently registered input devices changed listeners by process id.
99111
private Object mInputDevicesLock = new Object();
@@ -635,7 +647,9 @@ public String getKeyboardLayoutForInputDevice(String inputDeviceDescriptor) {
635647
throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
636648
}
637649

638-
return mFakeRegistry.get(inputDeviceDescriptor);
650+
synchronized (mDataStore) {
651+
return mDataStore.getKeyboardLayout(inputDeviceDescriptor);
652+
}
639653
}
640654

641655
@Override // Binder call
@@ -650,7 +664,13 @@ public void setKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
650664
throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
651665
}
652666

653-
mFakeRegistry.put(inputDeviceDescriptor, keyboardLayoutDescriptor);
667+
synchronized (mDataStore) {
668+
try {
669+
mDataStore.setKeyboardLayout(inputDeviceDescriptor, keyboardLayoutDescriptor);
670+
} finally {
671+
mDataStore.saveIfNeeded();
672+
}
673+
}
654674
}
655675

656676
/**
@@ -862,7 +882,7 @@ private void cancelVibrateIfNeeded(VibratorToken v) {
862882

863883
@Override
864884
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
865-
if (mContext.checkCallingOrSelfPermission("android.permission.DUMP")
885+
if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
866886
!= PackageManager.PERMISSION_GRANTED) {
867887
pw.println("Permission Denial: can't dump InputManager from from pid="
868888
+ Binder.getCallingPid()
@@ -1198,4 +1218,186 @@ public void binderDied() {
11981218
onVibratorTokenDied(this);
11991219
}
12001220
}
1221+
1222+
/**
1223+
* Manages persistent state recorded by the input manager service as an XML file.
1224+
* Caller must acquire lock on the data store before accessing it.
1225+
*
1226+
* File format:
1227+
* <code>
1228+
* &lt;input-mananger-state>
1229+
* &lt;input-devices>
1230+
* &lt;input-device descriptor="xxxxx" keyboard-layout="yyyyy" />
1231+
* &gt;input-devices>
1232+
* &gt;/input-manager-state>
1233+
* </code>
1234+
*/
1235+
private static final class PersistentDataStore {
1236+
// Input device state by descriptor.
1237+
private final HashMap<String, InputDeviceState> mInputDevices =
1238+
new HashMap<String, InputDeviceState>();
1239+
private final AtomicFile mAtomicFile;
1240+
1241+
// True if the data has been loaded.
1242+
private boolean mLoaded;
1243+
1244+
// True if there are changes to be saved.
1245+
private boolean mDirty;
1246+
1247+
public PersistentDataStore() {
1248+
mAtomicFile = new AtomicFile(new File("/data/system/input-manager-state.xml"));
1249+
}
1250+
1251+
public void saveIfNeeded() {
1252+
if (mDirty) {
1253+
save();
1254+
mDirty = false;
1255+
}
1256+
}
1257+
1258+
public String getKeyboardLayout(String inputDeviceDescriptor) {
1259+
InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
1260+
return state != null ? state.keyboardLayoutDescriptor : null;
1261+
}
1262+
1263+
public boolean setKeyboardLayout(String inputDeviceDescriptor,
1264+
String keyboardLayoutDescriptor) {
1265+
InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
1266+
if (!Objects.equal(state.keyboardLayoutDescriptor, keyboardLayoutDescriptor)) {
1267+
state.keyboardLayoutDescriptor = keyboardLayoutDescriptor;
1268+
setDirty();
1269+
return true;
1270+
}
1271+
return false;
1272+
}
1273+
1274+
private InputDeviceState getInputDeviceState(String inputDeviceDescriptor,
1275+
boolean createIfAbsent) {
1276+
loadIfNeeded();
1277+
InputDeviceState state = mInputDevices.get(inputDeviceDescriptor);
1278+
if (state == null && createIfAbsent) {
1279+
state = new InputDeviceState();
1280+
mInputDevices.put(inputDeviceDescriptor, state);
1281+
setDirty();
1282+
}
1283+
return state;
1284+
}
1285+
1286+
private void loadIfNeeded() {
1287+
if (!mLoaded) {
1288+
load();
1289+
mLoaded = true;
1290+
}
1291+
}
1292+
1293+
private void setDirty() {
1294+
mDirty = true;
1295+
}
1296+
1297+
private void clearState() {
1298+
mInputDevices.clear();
1299+
}
1300+
1301+
private void load() {
1302+
clearState();
1303+
1304+
final InputStream is;
1305+
try {
1306+
is = mAtomicFile.openRead();
1307+
} catch (FileNotFoundException ex) {
1308+
return;
1309+
}
1310+
1311+
XmlPullParser parser;
1312+
try {
1313+
parser = Xml.newPullParser();
1314+
parser.setInput(new BufferedInputStream(is), null);
1315+
loadFromXml(parser);
1316+
} catch (IOException ex) {
1317+
Slog.w(TAG, "Failed to load input manager persistent store data.", ex);
1318+
clearState();
1319+
} catch (XmlPullParserException ex) {
1320+
Slog.w(TAG, "Failed to load input manager persistent store data.", ex);
1321+
clearState();
1322+
} finally {
1323+
IoUtils.closeQuietly(is);
1324+
}
1325+
}
1326+
1327+
private void save() {
1328+
final FileOutputStream os;
1329+
try {
1330+
os = mAtomicFile.startWrite();
1331+
boolean success = false;
1332+
try {
1333+
XmlSerializer serializer = new FastXmlSerializer();
1334+
serializer.setOutput(new BufferedOutputStream(os), "utf-8");
1335+
saveToXml(serializer);
1336+
serializer.flush();
1337+
success = true;
1338+
} finally {
1339+
if (success) {
1340+
mAtomicFile.finishWrite(os);
1341+
} else {
1342+
mAtomicFile.failWrite(os);
1343+
}
1344+
}
1345+
} catch (IOException ex) {
1346+
Slog.w(TAG, "Failed to save input manager persistent store data.", ex);
1347+
}
1348+
}
1349+
1350+
private void loadFromXml(XmlPullParser parser)
1351+
throws IOException, XmlPullParserException {
1352+
XmlUtils.beginDocument(parser, "input-manager-state");
1353+
final int outerDepth = parser.getDepth();
1354+
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
1355+
if (parser.getName().equals("input-devices")) {
1356+
loadInputDevicesFromXml(parser);
1357+
}
1358+
}
1359+
}
1360+
1361+
private void loadInputDevicesFromXml(XmlPullParser parser)
1362+
throws IOException, XmlPullParserException {
1363+
final int outerDepth = parser.getDepth();
1364+
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
1365+
if (parser.getName().equals("input-device")) {
1366+
String descriptor = parser.getAttributeValue(null, "descriptor");
1367+
if (descriptor == null) {
1368+
throw new XmlPullParserException(
1369+
"Missing descriptor attribute on input-device");
1370+
}
1371+
InputDeviceState state = new InputDeviceState();
1372+
state.keyboardLayoutDescriptor =
1373+
parser.getAttributeValue(null, "keyboard-layout");
1374+
mInputDevices.put(descriptor, state);
1375+
}
1376+
}
1377+
}
1378+
1379+
private void saveToXml(XmlSerializer serializer) throws IOException {
1380+
serializer.startDocument(null, true);
1381+
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
1382+
serializer.startTag(null, "input-manager-state");
1383+
serializer.startTag(null, "input-devices");
1384+
for (Map.Entry<String, InputDeviceState> entry : mInputDevices.entrySet()) {
1385+
final String descriptor = entry.getKey();
1386+
final InputDeviceState state = entry.getValue();
1387+
serializer.startTag(null, "input-device");
1388+
serializer.attribute(null, "descriptor", descriptor);
1389+
if (state.keyboardLayoutDescriptor != null) {
1390+
serializer.attribute(null, "keyboard-layout", state.keyboardLayoutDescriptor);
1391+
}
1392+
serializer.endTag(null, "input-device");
1393+
}
1394+
serializer.endTag(null, "input-devices");
1395+
serializer.endTag(null, "input-manager-state");
1396+
serializer.endDocument();
1397+
}
1398+
}
1399+
1400+
private static final class InputDeviceState {
1401+
public String keyboardLayoutDescriptor;
1402+
}
12011403
}

0 commit comments

Comments
 (0)