diff options
| author | 2016-02-17 07:54:20 -0800 | |
|---|---|---|
| committer | 2016-02-17 07:54:20 -0800 | |
| commit | 68645a638ad1bfb734b2b0f56b17fe206bb891c5 (patch) | |
| tree | ff0f81da2b25e50dcbb6b595138c5dac2452ce2f | |
| parent | 5db2f16f15549c490c9541602a96f42bef984acb (diff) | |
Add Copy-On-Write mode to InputMethodSettings.
This is a preparation for File-Based Encryption (FBE) support in IMMS.
In order to support File-Based Encryption (FBE), IMMS needs to reset
its internal state exactly when the device is unlocked by user first
time. This is important because IMMS would recognize only
encryption-aware input method services until the the device is unlocked
by the current user.
Even if we reset the internal state when the device is unlocked by the
current user, there are still two tricky points.
1. Except for the initial boot, IMMS uses Secure Settings to determine
what IMEs are enabled and what IME is currently selected. These
persistent state may not be suitable for the situation where the
device is not unlocked yet, because some of IMEs referenced there
may or may not be encryption-aware. Depending on the situations,
we may need to enable at least one encryption-aware IME to ensure
that the user is able to type password to unlock the device, even if
such an IME is not Settings.Secure.ENABLED_INPUT_METHODS. We have
to be careful when doing this because we don't want non
pre-installed IMEs to be enabled until the user approves it.
2. IMMS tends to save its internal state into Secure Settings.
However, because of the point 1, we may need to automatically enable
a certain IME to make sure the user is able to type even when the
device is not unlocked yet. We don't want such a temporary state
to be persistent in Secure Settings.
The basic idea of this CL is to implement Copy-On-Write (COW) mode in
InputMethodSettings so that we can later discard any changes made before
the device is unlocked. As the initial step, we start using this COW
mode until the the ActivityManager becomes ready. With this change we
can revert a previous commit [1] for Bug 6685037, where forward-locked
encrypted apks need to be taken care of an early boot phase.
[1] Ifb311f85154beadd4787ec73669bedfdf1f5172d
4c0e7152e74d091eb78af8baacd38287ba95a1a1
Bug: 26279466
Change-Id: I9c6f9bb3d51174198e5f73588637f87ea0d90e11
| -rw-r--r-- | core/java/com/android/internal/inputmethod/InputMethodUtils.java | 74 | ||||
| -rw-r--r-- | services/core/java/com/android/server/InputMethodManagerService.java | 31 |
2 files changed, 80 insertions, 25 deletions
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java index c1011503dae7..4c63941abe0c 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java +++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java @@ -18,6 +18,7 @@ package com.android.internal.inputmethod; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.content.ContentResolver; import android.content.Context; @@ -32,6 +33,7 @@ import android.text.TextUtils.SimpleStringSplitter; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Pair; +import android.util.Printer; import android.util.Slog; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; @@ -826,7 +828,14 @@ public class InputMethodUtils { private final HashMap<String, InputMethodInfo> mMethodMap; private final ArrayList<InputMethodInfo> mMethodList; + /** + * On-memory data store to emulate when {@link #mCopyOnWrite} is {@code true}. + */ + private final HashMap<String, String> mCopyOnWriteDataStore = new HashMap<>(); + + private boolean mCopyOnWrite = false; private String mEnabledInputMethodsStrCache; + @UserIdInt private int mCurrentUserId; private int[] mCurrentProfileIds = new int[0]; @@ -879,48 +888,85 @@ public class InputMethodUtils { return imsList; } + @Deprecated + public InputMethodSettings( + Resources res, ContentResolver resolver, + HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList, + @UserIdInt int userId) { + this(res, resolver, methodMap, methodList, userId, false /* copyOnWrite */); + } + public InputMethodSettings( Resources res, ContentResolver resolver, HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList, - int userId) { - setCurrentUserId(userId); + @UserIdInt int userId, boolean copyOnWrite) { mRes = res; mResolver = resolver; mMethodMap = methodMap; mMethodList = methodList; + switchCurrentUser(userId, copyOnWrite); } - public void setCurrentUserId(int userId) { + /** + * Must be called when the current user is changed. + * + * @param userId The user ID. + * @param copyOnWrite If {@code true}, for each settings key + * (e.g. {@link Settings.Secure#ACTION_INPUT_METHOD_SUBTYPE_SETTINGS}) we use the actual + * settings on the {@link Settings.Secure} until we do the first write operation. + */ + public void switchCurrentUser(@UserIdInt int userId, boolean copyOnWrite) { if (DEBUG) { - Slog.d(TAG, "--- Swtich the current user from " + mCurrentUserId + " to " + userId); + Slog.d(TAG, "--- Switch the current user from " + mCurrentUserId + " to " + userId); + } + if (mCurrentUserId != userId || mCopyOnWrite != copyOnWrite) { + mCopyOnWriteDataStore.clear(); + mEnabledInputMethodsStrCache = ""; + // TODO: mCurrentProfileIds should be cleared here. } - // IMMS settings are kept per user, so keep track of current user mCurrentUserId = userId; + mCopyOnWrite = copyOnWrite; + // TODO: mCurrentProfileIds should be updated here. } private void putString(final String key, final String str) { - Settings.Secure.putStringForUser(mResolver, key, str, mCurrentUserId); + if (mCopyOnWrite) { + mCopyOnWriteDataStore.put(key, str); + } else { + Settings.Secure.putStringForUser(mResolver, key, str, mCurrentUserId); + } } private String getString(final String key) { + if (mCopyOnWrite && mCopyOnWriteDataStore.containsKey(key)) { + final String result = mCopyOnWriteDataStore.get(key); + return result != null ? result : ""; + } return Settings.Secure.getStringForUser(mResolver, key, mCurrentUserId); } private void putInt(final String key, final int value) { - Settings.Secure.putIntForUser(mResolver, key, value, mCurrentUserId); + if (mCopyOnWrite) { + mCopyOnWriteDataStore.put(key, String.valueOf(value)); + } else { + Settings.Secure.putIntForUser(mResolver, key, value, mCurrentUserId); + } } private int getInt(final String key, final int defaultValue) { + if (mCopyOnWrite && mCopyOnWriteDataStore.containsKey(key)) { + final String result = mCopyOnWriteDataStore.get(key); + return result != null ? Integer.valueOf(result) : 0; + } return Settings.Secure.getIntForUser(mResolver, key, defaultValue, mCurrentUserId); } private void putBoolean(final String key, final boolean value) { - Settings.Secure.putIntForUser(mResolver, key, value ? 1 : 0, mCurrentUserId); + putInt(key, value ? 1 : 0); } private boolean getBoolean(final String key, final boolean defaultValue) { - return Settings.Secure.getIntForUser(mResolver, key, defaultValue ? 1 : 0, - mCurrentUserId) == 1; + return getInt(key, defaultValue ? 1 : 0) == 1; } public void setCurrentProfileIds(int[] currentProfileIds) { @@ -1290,6 +1336,7 @@ public class InputMethodUtils { putBoolean(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, show); } + @UserIdInt public int getCurrentUserId() { return mCurrentUserId; } @@ -1324,6 +1371,13 @@ public class InputMethodUtils { } return enabledInputMethodAndSubtypes; } + + public void dumpLocked(final Printer pw, final String prefix) { + pw.println(prefix + "mCurrentUserId=" + mCurrentUserId); + pw.println(prefix + "mCurrentProfileIds=" + Arrays.toString(mCurrentProfileIds)); + pw.println(prefix + "mCopyOnWrite=" + mCopyOnWrite); + pw.println(prefix + "mEnabledInputMethodsStrCache=" + mEnabledInputMethodsStrCache); + } } // For spell checker service manager. diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 4a9412ffb958..f522288884f8 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -857,7 +857,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // mSettings should be created before buildInputMethodListLocked mSettings = new InputMethodSettings( - mRes, context.getContentResolver(), mMethodMap, mMethodList, userId); + mRes, context.getContentResolver(), mMethodMap, mMethodList, userId, !mSystemReady); // Let the package manager query which are the default imes // as they get certain permissions granted by default. @@ -872,7 +872,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // TODO: We are switching the current user id in the settings // object to query it and then revert the user id. Ideally, we // should call a API in settings with the user id as an argument. - mSettings.setCurrentUserId(userId); + mSettings.switchCurrentUser(userId, true /* copyOnWrite */); List<InputMethodInfo> imes = mSettings .getEnabledInputMethodListLocked(); String[] packageNames = null; @@ -884,7 +884,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub packageNames[i] = ime.getPackageName(); } } - mSettings.setCurrentUserId(currentUserId); + // If the system is not ready, then we use copy-on-write mode. + final boolean useCopyOnWriteSettings = !mSystemReady; + mSettings.switchCurrentUser(currentUserId, useCopyOnWriteSettings); return packageNames; } } @@ -1020,7 +1022,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // ContentObserver should be registered again when the user is changed mSettingsObserver.registerContentObserverLocked(newUserId); - mSettings.setCurrentUserId(newUserId); + + // If the system is not ready, then we use copy-on-write settings. + final boolean useCopyOnWriteSettings = !mSystemReady; + mSettings.switchCurrentUser(newUserId, useCopyOnWriteSettings); updateCurrentProfileIds(); // InputMethodFileManager should be reset when the user is changed mFileManager = new InputMethodFileManager(mMethodMap, newUserId); @@ -1079,6 +1084,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (!mSystemReady) { mSystemReady = true; + final int currentUserId = mSettings.getCurrentUserId(); + mSettings.switchCurrentUser(currentUserId, false /* copyOnWrite */); mKeyguardManager = mContext.getSystemService(KeyguardManager.class); mNotificationManager = mContext.getSystemService(NotificationManager.class); mStatusBar = statusBar; @@ -3000,7 +3007,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (resetDefaultEnabledIme) { final ArrayList<InputMethodInfo> defaultEnabledIme = InputMethodUtils.getDefaultEnabledImes(mContext, mSystemReady, mMethodList); - for (int i = 0; i < defaultEnabledIme.size(); ++i) { + final int N = defaultEnabledIme.size(); + for (int i = 0; i < N; ++i) { final InputMethodInfo imi = defaultEnabledIme.get(i); if (DEBUG) { Slog.d(TAG, "--- enable ime = " + imi); @@ -3362,16 +3370,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - // Workaround. - // ASEC is not ready in the IMMS constructor. Accordingly, forward-locked - // IMEs are not recognized and considered uninstalled. - // Actually, we can't move everything after SystemReady because - // IMMS needs to run in the encryption lock screen. So, we just skip changing - // the default IME here and try cheking the default IME again in systemReady(). - // TODO: Do nothing before system ready and implement a separated logic for - // the encryption lock screen. - // TODO: ASEC should be ready before IMMS is instantiated. - if (mSystemReady && !setSubtypeOnly) { + if (!setSubtypeOnly) { // Set InputMethod here mSettings.putSelectedInputMethod(imi != null ? imi.getId() : ""); } @@ -3852,6 +3851,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub p.println(" mSettingsObserver=" + mSettingsObserver); p.println(" mSwitchingController:"); mSwitchingController.dump(p); + p.println(" mSettings:"); + mSettings.dumpLocked(p, " "); } p.println(" "); |