summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Yohei Yukawa <yukawa@google.com> 2024-03-07 11:18:06 -0800
committer Yohei Yukawa <yukawa@google.com> 2024-03-07 11:18:06 -0800
commit8fc358e7c6c9e55ff1158302cc75c80eff1f565c (patch)
treee5ea3d6abf6b082680ae70af4dcaa891f1c89a6d
parentc37d81c43ac7124ae765f6a4471fee53237dc337 (diff)
Replace IMMS#mAdditionalSubtypeMap with AdditionalSubtypeMapRepository
With this commit InputMethodManagerService#mAdditionalSubtypeMap will be superseded by a newly introduced AdditionalSubtypeMapRepository so that all users' additional subtypes can be tracked equally rather than treating current user's one specially. A notable improvement is that this change allows us to save unnecessary file loading of additional subtype persistent files, which may help us mitigate the potential regression discussed in Bug 327861441. There must be no observable behavior change in this commit. Bug: 309837937 Test: atest CtsInputMethodTestCases:InputMethodSubtypeEndToEndTest Test: atest CtsInputMethodInstallTestCases:AdditionalSubtypeLifecycleTest Test: atest FrameworksInputMethodSystemServerTests Change-Id: Ic003648cfc9950f190a6b3fec7fd8fb363bfe081
-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 2205986fe9c9..912b8147d3ce 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());