1616
1717package com .android .server .input ;
1818
19+ import com .android .internal .os .AtomicFile ;
20+ import com .android .internal .util .FastXmlSerializer ;
1921import com .android .internal .util .XmlUtils ;
2022import com .android .server .Watchdog ;
2123
2224import org .xmlpull .v1 .XmlPullParser ;
25+ import org .xmlpull .v1 .XmlPullParserException ;
26+ import org .xmlpull .v1 .XmlSerializer ;
2327
28+ import android .Manifest ;
2429import android .content .ComponentName ;
2530import android .content .Context ;
2631import android .content .Intent ;
6368import android .view .WindowManagerPolicy ;
6469import android .view .KeyCharacterMap .UnavailableException ;
6570
71+ import java .io .BufferedInputStream ;
72+ import java .io .BufferedOutputStream ;
6673import java .io .File ;
6774import java .io .FileDescriptor ;
6875import java .io .FileNotFoundException ;
76+ import java .io .FileOutputStream ;
6977import java .io .FileReader ;
7078import java .io .IOException ;
79+ import java .io .InputStream ;
7180import java .io .PrintWriter ;
7281import java .util .ArrayList ;
7382import java .util .HashMap ;
7483import 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+ * <input-mananger-state>
1229+ * <input-devices>
1230+ * <input-device descriptor="xxxxx" keyboard-layout="yyyyy" />
1231+ * >input-devices>
1232+ * >/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