summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java106
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java107
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java1
3 files changed, 153 insertions, 61 deletions
diff --git a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java
new file mode 100644
index 000000000000..c7b60da2fc51
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.UserInfo;
+import android.os.Handler;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+
+/**
+ * Provides accesses to per-user additional {@link android.view.inputmethod.InputMethodSubtype}
+ * persistent storages.
+ */
+final class AdditionalSubtypeMapRepository {
+ @GuardedBy("ImfLock.class")
+ @NonNull
+ private static final SparseArray<AdditionalSubtypeMap> sPerUserMap = new SparseArray<>();
+
+ /**
+ * Not intended to be instantiated.
+ */
+ private AdditionalSubtypeMapRepository() {
+ }
+
+ @NonNull
+ @GuardedBy("ImfLock.class")
+ static AdditionalSubtypeMap get(@UserIdInt int userId) {
+ final AdditionalSubtypeMap map = sPerUserMap.get(userId);
+ if (map != null) {
+ return map;
+ }
+ final AdditionalSubtypeMap newMap = AdditionalSubtypeUtils.load(userId);
+ sPerUserMap.put(userId, newMap);
+ return newMap;
+ }
+
+ @GuardedBy("ImfLock.class")
+ static void putAndSave(@UserIdInt int userId, @NonNull AdditionalSubtypeMap map,
+ @NonNull InputMethodMap inputMethodMap) {
+ final AdditionalSubtypeMap previous = sPerUserMap.get(userId);
+ if (previous == map) {
+ return;
+ }
+ sPerUserMap.put(userId, map);
+ // TODO: Offload this to a background thread.
+ // TODO: Skip if the previous data is exactly the same as new one.
+ AdditionalSubtypeUtils.save(map, inputMethodMap, userId);
+ }
+
+ static void initialize(@NonNull Handler handler) {
+ final UserManagerInternal userManagerInternal =
+ LocalServices.getService(UserManagerInternal.class);
+ handler.post(() -> {
+ userManagerInternal.addUserLifecycleListener(
+ new UserManagerInternal.UserLifecycleListener() {
+ @Override
+ public void onUserCreated(UserInfo user, @Nullable Object token) {
+ final int userId = user.id;
+ handler.post(() -> {
+ synchronized (ImfLock.class) {
+ if (!sPerUserMap.contains(userId)) {
+ sPerUserMap.put(userId,
+ AdditionalSubtypeUtils.load(userId));
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onUserRemoved(UserInfo user) {
+ final int userId = user.id;
+ handler.post(() -> {
+ synchronized (ImfLock.class) {
+ sPerUserMap.remove(userId);
+ }
+ });
+ }
+ });
+ synchronized (ImfLock.class) {
+ for (int userId : userManagerInternal.getUserIds()) {
+ sPerUserMap.put(userId, AdditionalSubtypeUtils.load(userId));
+ }
+ }
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6688f536c8c7..12a95165f0ff 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -288,9 +288,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final ImePlatformCompatUtils mImePlatformCompatUtils;
final InputMethodDeviceConfigs mInputMethodDeviceConfigs;
- @GuardedBy("ImfLock.class")
- @NonNull
- private AdditionalSubtypeMap mAdditionalSubtypeMap = AdditionalSubtypeMap.EMPTY_MAP;
private final UserManagerInternal mUserManagerInternal;
private final InputMethodMenuController mMenuController;
@NonNull private final InputMethodBindingController mBindingController;
@@ -1310,13 +1307,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final int userId = getChangingUserId();
synchronized (ImfLock.class) {
final boolean isCurrentUser = (userId == mSettings.getUserId());
- final AdditionalSubtypeMap additionalSubtypeMap;
+ final AdditionalSubtypeMap additionalSubtypeMap =
+ AdditionalSubtypeMapRepository.get(userId);
final InputMethodSettings settings;
if (isCurrentUser) {
- additionalSubtypeMap = mAdditionalSubtypeMap;
settings = mSettings;
} else {
- additionalSubtypeMap = AdditionalSubtypeUtils.load(userId);
settings = queryInputMethodServicesInternal(mContext, userId,
additionalSubtypeMap, DirectBootAwareness.AUTO);
}
@@ -1331,11 +1327,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final AdditionalSubtypeMap newMap =
additionalSubtypeMap.cloneWithRemoveOrSelf(changedImes);
if (newMap != additionalSubtypeMap) {
- if (isCurrentUser) {
- mAdditionalSubtypeMap = newMap;
- }
- AdditionalSubtypeUtils.save(
- newMap, settings.getMethodMap(), settings.getUserId());
+ AdditionalSubtypeMapRepository.putAndSave(userId, newMap,
+ settings.getMethodMap());
}
if (!changedImes.isEmpty()) {
mChangedPackages.add(packageName);
@@ -1382,13 +1375,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
synchronized (ImfLock.class) {
final int userId = getChangingUserId();
final boolean isCurrentUser = (userId == mSettings.getUserId());
- AdditionalSubtypeMap additionalSubtypeMap;
+ AdditionalSubtypeMap additionalSubtypeMap =
+ AdditionalSubtypeMapRepository.get(userId);
final InputMethodSettings settings;
if (isCurrentUser) {
- additionalSubtypeMap = mAdditionalSubtypeMap;
settings = mSettings;
} else {
- additionalSubtypeMap = AdditionalSubtypeUtils.load(userId);
settings = queryInputMethodServicesInternal(mContext, userId,
additionalSubtypeMap, DirectBootAwareness.AUTO);
}
@@ -1419,11 +1411,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
+ imi.getComponent());
additionalSubtypeMap =
additionalSubtypeMap.cloneWithRemoveOrSelf(imi.getId());
- AdditionalSubtypeUtils.save(additionalSubtypeMap,
- settings.getMethodMap(), userId);
- if (isCurrentUser) {
- mAdditionalSubtypeMap = additionalSubtypeMap;
- }
+ AdditionalSubtypeMapRepository.putAndSave(userId,
+ additionalSubtypeMap, settings.getMethodMap());
}
}
@@ -1658,12 +1647,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mShowOngoingImeSwitcherForPhones = false;
+ AdditionalSubtypeMapRepository.initialize(mHandler);
+
final int userId = mActivityManagerInternal.getCurrentUserId();
// mSettings should be created before buildInputMethodListLocked
mSettings = InputMethodSettings.createEmptyMap(userId);
- mAdditionalSubtypeMap = AdditionalSubtypeUtils.load(userId);
mSwitchingController =
InputMethodSubtypeSwitchingController.createInstanceLocked(context,
mSettings.getMethodMap(), userId);
@@ -1808,8 +1798,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mSettingsObserver.registerContentObserverLocked(newUserId);
mSettings = InputMethodSettings.createEmptyMap(newUserId);
- // Additional subtypes should be reset when the user is changed
- mAdditionalSubtypeMap = AdditionalSubtypeUtils.load(newUserId);
final String defaultImiId = mSettings.getSelectedInputMethod();
if (DEBUG) {
@@ -2017,7 +2005,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
//TODO(b/197848765): This can be optimized by caching multi-user methodMaps/methodList.
//TODO(b/210039666): use cache.
- final InputMethodSettings settings = queryMethodMapForUser(userId);
+ final InputMethodSettings settings = queryMethodMapForUserLocked(userId);
final InputMethodInfo imi = settings.getMethodMap().get(
settings.getSelectedInputMethod());
return imi != null && imi.supportsStylusHandwriting()
@@ -2045,7 +2033,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
&& directBootAwareness == DirectBootAwareness.AUTO) {
settings = mSettings;
} else {
- final AdditionalSubtypeMap additionalSubtypeMap = AdditionalSubtypeUtils.load(userId);
+ final AdditionalSubtypeMap additionalSubtypeMap =
+ AdditionalSubtypeMapRepository.get(userId);
settings = queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
directBootAwareness);
}
@@ -2066,7 +2055,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
methodList = mSettings.getEnabledInputMethodList();
settings = mSettings;
} else {
- settings = queryMethodMapForUser(userId);
+ settings = queryMethodMapForUserLocked(userId);
methodList = settings.getEnabledInputMethodList();
}
// filter caller's access to input methods
@@ -2141,7 +2130,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
return mSettings.getEnabledInputMethodSubtypeList(
imi, allowsImplicitlyEnabledSubtypes);
}
- final InputMethodSettings settings = queryMethodMapForUser(userId);
+ final InputMethodSettings settings = queryMethodMapForUserLocked(userId);
final InputMethodInfo imi = settings.getMethodMap().get(imiId);
if (imi == null) {
return Collections.emptyList();
@@ -4307,7 +4296,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
return mSettings.getLastInputMethodSubtype();
}
- final InputMethodSettings settings = queryMethodMapForUser(userId);
+ final InputMethodSettings settings = queryMethodMapForUserLocked(userId);
return settings.getLastInputMethodSubtype();
}
}
@@ -4338,33 +4327,25 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
return;
}
- if (mSettings.getUserId() == userId) {
- final var newAdditionalSubtypeMap = mSettings.getNewAdditionalSubtypeMap(
- imiId, toBeAdded, mAdditionalSubtypeMap, mPackageManagerInternal,
- callingUid);
- if (mAdditionalSubtypeMap == newAdditionalSubtypeMap) {
- return;
- }
- AdditionalSubtypeUtils.save(newAdditionalSubtypeMap, mSettings.getMethodMap(),
- mSettings.getUserId());
- mAdditionalSubtypeMap = newAdditionalSubtypeMap;
- final long ident = Binder.clearCallingIdentity();
- try {
- buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- return;
- }
-
- final AdditionalSubtypeMap additionalSubtypeMap = AdditionalSubtypeUtils.load(userId);
- final InputMethodSettings settings = queryInputMethodServicesInternal(mContext, userId,
- additionalSubtypeMap, DirectBootAwareness.AUTO);
+ final var additionalSubtypeMap = AdditionalSubtypeMapRepository.get(userId);
+ final boolean isCurrentUser = (mSettings.getUserId() == userId);
+ final InputMethodSettings settings = isCurrentUser
+ ? mSettings
+ : queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
+ DirectBootAwareness.AUTO);
final var newAdditionalSubtypeMap = settings.getNewAdditionalSubtypeMap(
imiId, toBeAdded, additionalSubtypeMap, mPackageManagerInternal, callingUid);
if (additionalSubtypeMap != newAdditionalSubtypeMap) {
- AdditionalSubtypeUtils.save(newAdditionalSubtypeMap, settings.getMethodMap(),
- settings.getUserId());
+ AdditionalSubtypeMapRepository.putAndSave(userId, newAdditionalSubtypeMap,
+ settings.getMethodMap());
+ if (isCurrentUser) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
}
}
@@ -4391,7 +4372,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
synchronized (ImfLock.class) {
final boolean currentUser = (mSettings.getUserId() == userId);
final InputMethodSettings settings = currentUser
- ? mSettings : queryMethodMapForUser(userId);
+ ? mSettings : queryMethodMapForUserLocked(userId);
if (!settings.setEnabledInputMethodSubtypes(imeId, subtypeHashCodes)) {
return;
}
@@ -5293,7 +5274,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mMyPackageMonitor.clearKnownImePackageNamesLocked();
mSettings = queryInputMethodServicesInternal(mContext, mSettings.getUserId(),
- mAdditionalSubtypeMap, DirectBootAwareness.AUTO);
+ AdditionalSubtypeMapRepository.get(mSettings.getUserId()),
+ DirectBootAwareness.AUTO);
// Construct the set of possible IME packages for onPackageChanged() to avoid false
// negatives when the package state remains to be the same but only the component state is
@@ -5565,7 +5547,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
return getCurrentInputMethodSubtypeLocked();
}
- final InputMethodSettings settings = queryMethodMapForUser(userId);
+ final InputMethodSettings settings = queryMethodMapForUserLocked(userId);
return settings.getCurrentInputMethodSubtypeForNonCurrentUsers();
}
}
@@ -5632,15 +5614,18 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (userId == mSettings.getUserId()) {
settings = mSettings;
} else {
- final AdditionalSubtypeMap additionalSubtypeMap = AdditionalSubtypeUtils.load(userId);
+ final AdditionalSubtypeMap additionalSubtypeMap =
+ AdditionalSubtypeMapRepository.get(userId);
settings = queryInputMethodServicesInternal(mContext, userId,
additionalSubtypeMap, DirectBootAwareness.AUTO);
}
return settings.getMethodMap().get(settings.getSelectedInputMethod());
}
- private InputMethodSettings queryMethodMapForUser(@UserIdInt int userId) {
- final AdditionalSubtypeMap additionalSubtypeMap = AdditionalSubtypeUtils.load(userId);
+ @GuardedBy("ImfLock.class")
+ private InputMethodSettings queryMethodMapForUserLocked(@UserIdInt int userId) {
+ final AdditionalSubtypeMap additionalSubtypeMap =
+ AdditionalSubtypeMapRepository.get(userId);
return queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
DirectBootAwareness.AUTO);
}
@@ -5656,7 +5641,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID);
return true;
}
- final InputMethodSettings settings = queryMethodMapForUser(userId);
+ final InputMethodSettings settings = queryMethodMapForUserLocked(userId);
if (!settings.getMethodMap().containsKey(imeId)
|| !settings.getEnabledInputMethodList().contains(
settings.getMethodMap().get(imeId))) {
@@ -5796,7 +5781,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
setInputMethodEnabledLocked(imeId, enabled);
return true;
}
- final InputMethodSettings settings = queryMethodMapForUser(userId);
+ final InputMethodSettings settings = queryMethodMapForUserLocked(userId);
if (!settings.getMethodMap().containsKey(imeId)) {
return false; // IME is not found.
}
@@ -6550,7 +6535,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
previouslyEnabled = setInputMethodEnabledLocked(imeId, enabled);
}
} else {
- final InputMethodSettings settings = queryMethodMapForUser(userId);
+ final InputMethodSettings settings = queryMethodMapForUserLocked(userId);
if (enabled) {
if (!settings.getMethodMap().containsKey(imeId)) {
failedToEnableUnknownIme = true;
@@ -6685,7 +6670,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
nextEnabledImes = mSettings.getEnabledInputMethodList();
} else {
final AdditionalSubtypeMap additionalSubtypeMap =
- AdditionalSubtypeUtils.load(userId);
+ AdditionalSubtypeMapRepository.get(userId);
final InputMethodSettings settings = queryInputMethodServicesInternal(
mContext, userId, additionalSubtypeMap, DirectBootAwareness.AUTO);
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index f4d95afaacf4..b9c5b36f9775 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -201,6 +201,7 @@ public class InputMethodManagerServiceTestBase {
when(mMockUserManagerInternal.isUserRunning(anyInt())).thenReturn(true);
when(mMockUserManagerInternal.getProfileIds(anyInt(), anyBoolean()))
.thenReturn(new int[] {0});
+ when(mMockUserManagerInternal.getUserIds()).thenReturn(new int[] {0});
when(mMockActivityManagerInternal.isSystemReady()).thenReturn(true);
when(mMockPackageManagerInternal.getPackageUid(anyString(), anyLong(), anyInt()))
.thenReturn(Binder.getCallingUid());