Merge "Add Copy-On-Write mode to InputMethodSettings." into nyc-dev
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index c101150..4c63941 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -18,6 +18,7 @@
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.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 @@
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 @@
return imsList;
}
+ @Deprecated
public InputMethodSettings(
Resources res, ContentResolver resolver,
HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
- int userId) {
- setCurrentUserId(userId);
+ @UserIdInt int userId) {
+ this(res, resolver, methodMap, methodList, userId, false /* copyOnWrite */);
+ }
+
+ public InputMethodSettings(
+ Resources res, ContentResolver resolver,
+ HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
+ @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);
}
- // IMMS settings are kept per user, so keep track of current user
+ if (mCurrentUserId != userId || mCopyOnWrite != copyOnWrite) {
+ mCopyOnWriteDataStore.clear();
+ mEnabledInputMethodsStrCache = "";
+ // TODO: mCurrentProfileIds should be cleared here.
+ }
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 @@
putBoolean(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, show);
}
+ @UserIdInt
public int getCurrentUserId() {
return mCurrentUserId;
}
@@ -1324,6 +1371,13 @@
}
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 4a9412f..f522288 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -857,7 +857,7 @@
// 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 @@
// 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 @@
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 @@
// 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 @@
}
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 @@
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 @@
}
}
- // 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 @@
p.println(" mSettingsObserver=" + mSettingsObserver);
p.println(" mSwitchingController:");
mSwitchingController.dump(p);
+ p.println(" mSettings:");
+ mSettings.dumpLocked(p, " ");
}
p.println(" ");