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
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user