summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java21
-rw-r--r--services/core/java/com/android/server/inputmethod/UserDataRepository.java29
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java62
3 files changed, 34 insertions, 78 deletions
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 731c1ea28ade..3c0d8e94616b 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -79,6 +79,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.hardware.input.InputManager;
import android.inputmethodservice.InputMethodService;
@@ -923,11 +924,15 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
* {@link SystemService} used to publish and manage the lifecycle of
* {@link InputMethodManagerService}.
*/
- public static final class Lifecycle extends SystemService {
+ public static final class Lifecycle extends SystemService
+ implements UserManagerInternal.UserLifecycleListener {
private final InputMethodManagerService mService;
public Lifecycle(Context context) {
this(context, createServiceForProduction(context));
+
+ // For production code, hook up user lifecycle
+ mService.mUserManagerInternal.addUserLifecycleListener(this);
}
@VisibleForTesting
@@ -1006,6 +1011,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
}
@Override
+ public void onUserCreated(UserInfo user, @Nullable Object token) {
+ // Called directly from UserManagerService. Do not block the calling thread.
+ }
+
+ @Override
+ public void onUserRemoved(UserInfo user) {
+ // Called directly from UserManagerService. Do not block the calling thread.
+ final int userId = user.id;
+ mService.mUserDataRepository.remove(userId);
+ }
+
+ @Override
public void onUserUnlocking(@NonNull TargetUser user) {
// Called on ActivityManager thread.
SecureSettingsWrapper.onUserUnlocking(user.getUserIdentifier());
@@ -1114,7 +1131,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
@SuppressWarnings("GuardedBy") final IntFunction<InputMethodBindingController>
bindingControllerFactory = userId -> new InputMethodBindingController(userId,
InputMethodManagerService.this);
- mUserDataRepository = new UserDataRepository(mHandler, mUserManagerInternal,
+ mUserDataRepository = new UserDataRepository(
bindingControllerForTesting != null ? bindingControllerForTesting
: bindingControllerFactory);
diff --git a/services/core/java/com/android/server/inputmethod/UserDataRepository.java b/services/core/java/com/android/server/inputmethod/UserDataRepository.java
index 292f9f8e3501..98d7548d3dd2 100644
--- a/services/core/java/com/android/server/inputmethod/UserDataRepository.java
+++ b/services/core/java/com/android/server/inputmethod/UserDataRepository.java
@@ -20,8 +20,6 @@ import android.annotation.AnyThread;
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 android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ImeTracker;
@@ -30,7 +28,6 @@ import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.IRemoteInputConnection;
-import com.android.server.pm.UserManagerInternal;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Consumer;
@@ -76,24 +73,18 @@ final class UserDataRepository {
}
UserDataRepository(
- @NonNull Handler handler, @NonNull UserManagerInternal userManagerInternal,
@NonNull IntFunction<InputMethodBindingController> bindingControllerFactory) {
mBindingControllerFactory = bindingControllerFactory;
- userManagerInternal.addUserLifecycleListener(
- new UserManagerInternal.UserLifecycleListener() {
- @Override
- public void onUserRemoved(UserInfo user) {
- final int userId = user.id;
- handler.post(() -> {
- mUserDataLock.writeLock().lock();
- try {
- mUserData.remove(userId);
- } finally {
- mUserDataLock.writeLock().unlock();
- }
- });
- }
- });
+ }
+
+ @AnyThread
+ void remove(@UserIdInt int userId) {
+ mUserDataLock.writeLock().lock();
+ try {
+ mUserData.remove(userId);
+ } finally {
+ mUserDataLock.writeLock().unlock();
+ }
}
/** Placeholder for all IMMS user specific fields */
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
index e07a05566f1d..81fb1a092887 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
@@ -18,23 +18,12 @@ package com.android.server.inputmethod;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.pm.UserInfo;
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.Looper;
import android.platform.test.ravenwood.RavenwoodRule;
-import com.android.server.pm.UserManagerInternal;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -52,13 +41,8 @@ public final class UserDataRepositoryTest {
.setProvideMainThread(true).build();
@Mock
- private UserManagerInternal mMockUserManagerInternal;
-
- @Mock
private InputMethodManagerService mMockInputMethodManagerService;
- private Handler mHandler;
-
private IntFunction<InputMethodBindingController> mBindingControllerFactory;
@Before
@@ -66,7 +50,6 @@ public final class UserDataRepositoryTest {
MockitoAnnotations.initMocks(this);
SecureSettingsWrapper.startTestMode();
- mHandler = new Handler(Looper.getMainLooper());
mBindingControllerFactory = new IntFunction<InputMethodBindingController>() {
@Override
@@ -81,49 +64,20 @@ public final class UserDataRepositoryTest {
SecureSettingsWrapper.endTestMode();
}
- @Test
- public void testUserDataRepository_addsNewUserInfoOnUserCreatedEvent() {
- // Create UserDataRepository and capture the user lifecycle listener
- final var captor = ArgumentCaptor.forClass(UserManagerInternal.UserLifecycleListener.class);
- final var bindingControllerFactorySpy = spy(mBindingControllerFactory);
- final var repository = new UserDataRepository(mHandler,
- mMockUserManagerInternal, bindingControllerFactorySpy);
-
- verify(mMockUserManagerInternal, times(1)).addUserLifecycleListener(captor.capture());
- final var listener = captor.getValue();
-
- // Assert that UserDataRepository is empty and then call onUserCreated
- assertThat(collectUserData(repository)).isEmpty();
- final var userInfo = new UserInfo();
- userInfo.id = ANY_USER_ID;
- listener.onUserCreated(userInfo, /* unused token */ new Object());
- waitForIdle();
-
- // Assert UserDataRepository remains to be empty.
- assertThat(collectUserData(repository)).isEmpty();
- }
-
+ // TODO(b/352615651): Move this to end-to-end test.
@Test
public void testUserDataRepository_removesUserInfoOnUserRemovedEvent() {
- // Create UserDataRepository and capture the user lifecycle listener
- final var captor = ArgumentCaptor.forClass(UserManagerInternal.UserLifecycleListener.class);
- final var repository = new UserDataRepository(mHandler,
- mMockUserManagerInternal,
+ // Create UserDataRepository
+ final var repository = new UserDataRepository(
userId -> new InputMethodBindingController(userId, mMockInputMethodManagerService));
- verify(mMockUserManagerInternal, times(1)).addUserLifecycleListener(captor.capture());
- final var listener = captor.getValue();
-
// Add one UserData ...
final var userData = repository.getOrCreate(ANY_USER_ID);
assertThat(userData.mUserId).isEqualTo(ANY_USER_ID);
// ... and then call onUserRemoved
assertThat(collectUserData(repository)).hasSize(1);
- final var userInfo = new UserInfo();
- userInfo.id = ANY_USER_ID;
- listener.onUserRemoved(userInfo);
- waitForIdle();
+ repository.remove(ANY_USER_ID);
// Assert UserDataRepository is now empty
assertThat(collectUserData(repository)).isEmpty();
@@ -131,8 +85,7 @@ public final class UserDataRepositoryTest {
@Test
public void testGetOrCreate() {
- final var repository = new UserDataRepository(mHandler,
- mMockUserManagerInternal, mBindingControllerFactory);
+ final var repository = new UserDataRepository(mBindingControllerFactory);
final var userData = repository.getOrCreate(ANY_USER_ID);
assertThat(userData.mUserId).isEqualTo(ANY_USER_ID);
@@ -151,9 +104,4 @@ public final class UserDataRepositoryTest {
return collected;
}
- private void waitForIdle() {
- final var done = new ConditionVariable();
- mHandler.post(done::open);
- done.block();
- }
}