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

Commit c4f2a1ea authored by Ming-Shin Lu's avatar Ming-Shin Lu
Browse files

Update user related fields when InputMethodSettings#switchCurrentUser

Ensure mUserAwareContext/mResolver/mRes in InputMetodSettings may
out-of-sync after switchCurrentUser that some utilility methods depends
on this will get wrong result, like
- getEnabledInputMethodSubtypeListLocked (Depends on the locale
  of the user resources configuration)
- isShowImeWithHardKeyboardEnabled (Depends on the content provider of
  the user to query settings)

Also, added a unit test to verify this behavior works as expected.

Bug: 251814906
Test: atest InputMethodUtilTest#\
       testInputMethodSettings_SwitchCurrentUser
Change-Id: I6d514310623d74972e1f9b8a32bb201a635c601b
parent f9ca7a2f
Loading
Loading
Loading
Loading
+13 −6
Original line number Diff line number Diff line
@@ -212,9 +212,9 @@ final class InputMethodUtils {
                new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR);

        @NonNull
        private final Context mUserAwareContext;
        private final Resources mRes;
        private final ContentResolver mResolver;
        private Context mUserAwareContext;
        private Resources mRes;
        private ContentResolver mResolver;
        private final ArrayMap<String, InputMethodInfo> mMethodMap;

        /**
@@ -272,15 +272,19 @@ final class InputMethodUtils {
            return imsList;
        }

        InputMethodSettings(@NonNull Context context,
                ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId,
                boolean copyOnWrite) {
        private void initContentWithUserContext(@NonNull Context context, @UserIdInt int userId) {
            mUserAwareContext = context.getUserId() == userId
                    ? context
                    : context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
            mRes = mUserAwareContext.getResources();
            mResolver = mUserAwareContext.getContentResolver();
        }

        InputMethodSettings(@NonNull Context context,
                ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId,
                boolean copyOnWrite) {
            mMethodMap = methodMap;
            initContentWithUserContext(context, userId);
            switchCurrentUser(userId, copyOnWrite);
        }

@@ -301,6 +305,9 @@ final class InputMethodUtils {
                mEnabledInputMethodsStrCache = "";
                // TODO: mCurrentProfileIds should be cleared here.
            }
            if (mUserAwareContext.getUserId() != userId) {
                initContentWithUserContext(mUserAwareContext, userId);
            }
            mCurrentUserId = userId;
            mCopyOnWrite = copyOnWrite;
            // TODO: mCurrentProfileIds should be updated here.
+104 −0
Original line number Diff line number Diff line
@@ -25,8 +25,16 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.IContentProvider;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -35,6 +43,9 @@ import android.content.res.Resources;
import android.os.Build;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.UserHandle;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.IntArray;
@@ -1214,6 +1225,42 @@ public class InputMethodUtilsTest {
                StartInputFlags.VIEW_HAS_FOCUS | StartInputFlags.IS_TEXT_EDITOR));
    }

    @Test
    public void testInputMethodSettings_SwitchCurrentUser() {
        TestContext ownerUserContext = createMockContext(0 /* userId */);
        final InputMethodInfo systemIme = createFakeInputMethodInfo(
                "SystemIme", "fake.latin", true /* isSystem */);
        final InputMethodInfo nonSystemIme = createFakeInputMethodInfo("NonSystemIme",
                "fake.voice0", false /* isSystem */);
        final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
        methodMap.put(systemIme.getId(), systemIme);

        // Init InputMethodSettings for the owner user (userId=0), verify calls can get the
        // corresponding user's context, contentResolver and the resources configuration.
        InputMethodUtils.InputMethodSettings settings = new InputMethodUtils.InputMethodSettings(
                ownerUserContext, methodMap, 0 /* userId */, true);
        assertEquals(0, settings.getCurrentUserId());

        settings.isShowImeWithHardKeyboardEnabled();
        verify(ownerUserContext.getContentResolver(), atLeastOnce()).getAttributionSource();

        settings.getEnabledInputMethodSubtypeListLocked(nonSystemIme, true);
        verify(ownerUserContext.getResources(), atLeastOnce()).getConfiguration();

        // Calling switchCurrentUser to the secondary user (userId=10), verify calls can get the
        // corresponding user's context, contentResolver and the resources configuration.
        settings.switchCurrentUser(10 /* userId */, true);
        assertEquals(10, settings.getCurrentUserId());

        settings.isShowImeWithHardKeyboardEnabled();
        verify(TestContext.getSecondaryUserContext().getContentResolver(),
                atLeastOnce()).getAttributionSource();

        settings.getEnabledInputMethodSubtypeListLocked(nonSystemIme, true);
        verify(TestContext.getSecondaryUserContext().getResources(),
                atLeastOnce()).getConfiguration();
    }

    private static IntArray createSubtypeHashCodeArrayFromStr(String subtypeHashCodesStr) {
        final IntArray subtypes = new IntArray();
        final TextUtils.SimpleStringSplitter imeSubtypeSplitter =
@@ -1236,6 +1283,63 @@ public class InputMethodUtilsTest {
                        imeId, createSubtypeHashCodeArrayFromStr(enabledSubtypeHashCodesStr)));
    }

    private static TestContext createMockContext(int userId) {
        return new TestContext(InstrumentationRegistry.getInstrumentation()
                .getTargetContext(), userId);
    }

    private static class TestContext extends ContextWrapper {
        private int mUserId;
        private ContentResolver mResolver;
        private Resources mResources;

        private static TestContext sSecondaryUserContext;

        TestContext(@NonNull Context context, int userId) {
            super(context);
            mUserId = userId;
            mResolver = mock(MockContentResolver.class);
            when(mResolver.acquireProvider(Settings.Secure.CONTENT_URI)).thenReturn(
                    mock(IContentProvider.class));
            mResources = mock(Resources.class);

            final Configuration configuration = new Configuration();
            if (userId == 0) {
                configuration.setLocale(LOCALE_EN_US);
            } else {
                configuration.setLocale(LOCALE_FR_CA);
            }
            doReturn(configuration).when(mResources).getConfiguration();
        }

        @Override
        public Context createContextAsUser(UserHandle user, int flags) {
            if (user.getIdentifier() != UserHandle.USER_SYSTEM) {
                return sSecondaryUserContext = new TestContext(this, user.getIdentifier());
            }
            return this;
        }

        @Override
        public int getUserId() {
            return mUserId;
        }

        @Override
        public ContentResolver getContentResolver() {
            return mResolver;
        }

        @Override
        public Resources getResources() {
            return mResources;
        }

        static Context getSecondaryUserContext() {
            return sSecondaryUserContext;
        }
    }

    @Test
    public void updateEnabledImeStringTest() {
        // No change cases