Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ffb30156 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Remove UserLifecycleListener deps from UserDataRepository" into main

parents 0a5b75cc cb958f75
Loading
Loading
Loading
Loading
+19 −2
Original line number Diff line number Diff line
@@ -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
@@ -1005,6 +1010,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.
@@ -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);

+10 −19
Original line number Diff line number Diff line
@@ -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(() -> {
    }

    @AnyThread
    void remove(@UserIdInt int userId) {
        mUserDataLock.writeLock().lock();
        try {
            mUserData.remove(userId);
        } finally {
            mUserDataLock.writeLock().unlock();
        }
                        });
                    }
                });
    }

    /** Placeholder for all IMMS user specific fields */
+5 −57
Original line number Diff line number Diff line
@@ -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;

@@ -51,14 +40,9 @@ public final class UserDataRepositoryTest {
    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
            .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();
    }
}