Loading core/java/android/provider/Settings.java +5 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,7 @@ import android.util.ArraySet; import android.util.Log; import android.util.MemoryIntArray; import android.util.StatsLog; import android.view.textservice.TextServicesManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.widget.ILockSettings; Loading Loading @@ -7970,6 +7971,10 @@ public final class Settings { CLONE_TO_MANAGED_PROFILE.add(LOCATION_MODE); CLONE_TO_MANAGED_PROFILE.add(LOCATION_PROVIDERS_ALLOWED); CLONE_TO_MANAGED_PROFILE.add(SELECTED_INPUT_METHOD_SUBTYPE); if (TextServicesManager.DISABLE_PER_PROFILE_SPELL_CHECKER) { CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER); CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER_SUBTYPE); } } /** @hide */ Loading core/java/android/view/textservice/TextServicesManager.java +6 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,12 @@ public final class TextServicesManager { private static final String TAG = TextServicesManager.class.getSimpleName(); private static final boolean DBG = false; /** * A compile time switch to control per-profile spell checker, which is not yet ready. * @hide */ public static final boolean DISABLE_PER_PROFILE_SPELL_CHECKER = true; private static TextServicesManager sInstance; private final ITextServicesManager mService; Loading core/java/com/android/internal/textservice/LazyIntToIntMap.java 0 → 100644 +67 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.internal.textservice; import android.annotation.NonNull; import android.util.SparseIntArray; import com.android.internal.annotations.VisibleForTesting; import java.util.function.IntUnaryOperator; /** * Simple int-to-int key-value-store that is to be lazily initialized with the given * {@link IntUnaryOperator}. */ @VisibleForTesting public final class LazyIntToIntMap { private final SparseIntArray mMap = new SparseIntArray(); @NonNull private final IntUnaryOperator mMappingFunction; /** * @param mappingFunction int to int mapping rules to be (lazily) evaluated */ public LazyIntToIntMap(@NonNull IntUnaryOperator mappingFunction) { mMappingFunction = mappingFunction; } /** * Deletes {@code key} and associated value. * @param key key to be deleted */ public void delete(int key) { mMap.delete(key); } /** * @param key key associated with the value * @return value associated with the {@code key}. If this is the first time to access * {@code key}, then {@code mappingFunction} passed to the constructor will be evaluated */ public int get(int key) { final int index = mMap.indexOfKey(key); if (index >= 0) { return mMap.valueAt(index); } final int value = mMappingFunction.applyAsInt(key); mMap.append(key, value); return value; } } core/tests/coretests/src/com/android/internal/textservice/LazyIntToIntMapTest.java 0 → 100644 +92 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.internal.textservice; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import java.util.concurrent.atomic.AtomicReference; import java.util.function.IntUnaryOperator; @SmallTest @RunWith(AndroidJUnit4.class) public class LazyIntToIntMapTest { @Test public void testLaziness() { final IntUnaryOperator func = mock(IntUnaryOperator.class); when(func.applyAsInt(eq(1))).thenReturn(11); when(func.applyAsInt(eq(2))).thenReturn(22); final LazyIntToIntMap map = new LazyIntToIntMap(func); verify(func, never()).applyAsInt(anyInt()); assertEquals(22, map.get(2)); verify(func, times(0)).applyAsInt(eq(1)); verify(func, times(1)).applyAsInt(eq(2)); // Accessing to the same key does not evaluate the function again. assertEquals(22, map.get(2)); verify(func, times(0)).applyAsInt(eq(1)); verify(func, times(1)).applyAsInt(eq(2)); } @Test public void testDelete() { final IntUnaryOperator func1 = mock(IntUnaryOperator.class); when(func1.applyAsInt(eq(1))).thenReturn(11); when(func1.applyAsInt(eq(2))).thenReturn(22); final IntUnaryOperator func2 = mock(IntUnaryOperator.class); when(func2.applyAsInt(eq(1))).thenReturn(111); when(func2.applyAsInt(eq(2))).thenReturn(222); final AtomicReference<IntUnaryOperator> funcRef = new AtomicReference<>(func1); final LazyIntToIntMap map = new LazyIntToIntMap(i -> funcRef.get().applyAsInt(i)); verify(func1, never()).applyAsInt(anyInt()); verify(func2, never()).applyAsInt(anyInt()); assertEquals(22, map.get(2)); verify(func1, times(1)).applyAsInt(eq(2)); verify(func2, times(0)).applyAsInt(eq(2)); // Swap func1 with func2 then invalidate the key=2 funcRef.set(func2); map.delete(2); // Calling get(2) again should re-evaluate the value. assertEquals(222, map.get(2)); verify(func1, times(1)).applyAsInt(eq(2)); verify(func2, times(1)).applyAsInt(eq(2)); // Trying to delete non-existing keys does nothing. map.delete(1); } } services/core/java/com/android/server/TextServicesManagerService.java +67 −6 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.server; import static android.view.textservice.TextServicesManager.DISABLE_PER_PROFILE_SPELL_CHECKER; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.inputmethod.InputMethodUtils; import com.android.internal.textservice.ISpellCheckerService; Loading @@ -24,6 +27,7 @@ import com.android.internal.textservice.ISpellCheckerSession; import com.android.internal.textservice.ISpellCheckerSessionListener; import com.android.internal.textservice.ITextServicesManager; import com.android.internal.textservice.ITextServicesSessionListener; import com.android.internal.textservice.LazyIntToIntMap; import com.android.internal.util.DumpUtils; import org.xmlpull.v1.XmlPullParserException; Loading Loading @@ -79,6 +83,10 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { private final UserManager mUserManager; private final Object mLock = new Object(); @NonNull @GuardedBy("mLock") private final LazyIntToIntMap mSpellCheckerOwnerUserIdMap; private static class TextServicesData { @UserIdInt private final int mUserId; Loading Loading @@ -294,6 +302,9 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { void onStopUser(@UserIdInt int userId) { synchronized (mLock) { // Clear user ID mapping table. mSpellCheckerOwnerUserIdMap.delete(userId); // Clean per-user data TextServicesData tsd = mUserData.get(userId); if (tsd == null) return; Loading @@ -313,12 +324,32 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { public TextServicesManagerService(Context context) { mContext = context; mUserManager = mContext.getSystemService(UserManager.class); mSpellCheckerOwnerUserIdMap = new LazyIntToIntMap(callingUserId -> { if (DISABLE_PER_PROFILE_SPELL_CHECKER) { final long token = Binder.clearCallingIdentity(); try { final UserInfo parent = mUserManager.getProfileParent(callingUserId); return (parent != null) ? parent.id : callingUserId; } finally { Binder.restoreCallingIdentity(token); } } else { return callingUserId; } }); mMonitor = new TextServicesMonitor(); mMonitor.register(context, null, UserHandle.ALL, true); } private void initializeInternalStateLocked(@UserIdInt int userId) { // When DISABLE_PER_PROFILE_SPELL_CHECKER is true, we make sure here that work profile users // will never have non-null TextServicesData for their user ID. if (DISABLE_PER_PROFILE_SPELL_CHECKER && userId != mSpellCheckerOwnerUserIdMap.get(userId)) { return; } TextServicesData tsd = mUserData.get(userId); if (tsd == null) { tsd = new TextServicesData(userId, mContext); Loading Loading @@ -470,7 +501,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { public SpellCheckerInfo getCurrentSpellChecker(String locale) { int userId = UserHandle.getCallingUserId(); synchronized (mLock) { TextServicesData tsd = mUserData.get(userId); final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); if (tsd == null) return null; return tsd.getCurrentSpellChecker(); Loading @@ -488,7 +519,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { final int userId = UserHandle.getCallingUserId(); synchronized (mLock) { TextServicesData tsd = mUserData.get(userId); final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); if (tsd == null) return null; subtypeHashCode = Loading Loading @@ -569,7 +600,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { int callingUserId = UserHandle.getCallingUserId(); synchronized (mLock) { TextServicesData tsd = mUserData.get(callingUserId); final TextServicesData tsd = getDataFromCallingUserIdLocked(callingUserId); if (tsd == null) return; HashMap<String, SpellCheckerInfo> spellCheckerMap = tsd.mSpellCheckerMap; Loading Loading @@ -606,7 +637,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { int userId = UserHandle.getCallingUserId(); synchronized (mLock) { TextServicesData tsd = mUserData.get(userId); final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); if (tsd == null) return false; return tsd.isSpellCheckerEnabled(); Loading Loading @@ -643,7 +674,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { int callingUserId = UserHandle.getCallingUserId(); synchronized (mLock) { TextServicesData tsd = mUserData.get(callingUserId); final TextServicesData tsd = getDataFromCallingUserIdLocked(callingUserId); if (tsd == null) return null; ArrayList<SpellCheckerInfo> spellCheckerList = tsd.mSpellCheckerList; Loading @@ -666,7 +697,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { int userId = UserHandle.getCallingUserId(); synchronized (mLock) { TextServicesData tsd = mUserData.get(userId); final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); if (tsd == null) return; final ArrayList<SpellCheckerBindGroup> removeList = new ArrayList<>(); Loading Loading @@ -737,6 +768,36 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } } /** * @param callingUserId user ID of the calling process * @return {@link TextServicesData} for the given user. {@code null} if spell checker is not * temporarily / permanently available for the specified user */ @Nullable private TextServicesData getDataFromCallingUserIdLocked(@UserIdInt int callingUserId) { final int spellCheckerOwnerUserId = mSpellCheckerOwnerUserIdMap.get(callingUserId); final TextServicesData data = mUserData.get(spellCheckerOwnerUserId); if (DISABLE_PER_PROFILE_SPELL_CHECKER) { if (spellCheckerOwnerUserId != callingUserId) { // Calling process is running under child profile. if (data == null) { return null; } final SpellCheckerInfo info = data.getCurrentSpellChecker(); if (info == null) { return null; } final ServiceInfo serviceInfo = info.getServiceInfo(); if ((serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { // To be conservative, non pre-installed spell checker services are not allowed // to be used for child profiles. return null; } } } return data; } private static final class SessionRequest { public final int mUid; @Nullable Loading Loading
core/java/android/provider/Settings.java +5 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,7 @@ import android.util.ArraySet; import android.util.Log; import android.util.MemoryIntArray; import android.util.StatsLog; import android.view.textservice.TextServicesManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.widget.ILockSettings; Loading Loading @@ -7970,6 +7971,10 @@ public final class Settings { CLONE_TO_MANAGED_PROFILE.add(LOCATION_MODE); CLONE_TO_MANAGED_PROFILE.add(LOCATION_PROVIDERS_ALLOWED); CLONE_TO_MANAGED_PROFILE.add(SELECTED_INPUT_METHOD_SUBTYPE); if (TextServicesManager.DISABLE_PER_PROFILE_SPELL_CHECKER) { CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER); CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER_SUBTYPE); } } /** @hide */ Loading
core/java/android/view/textservice/TextServicesManager.java +6 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,12 @@ public final class TextServicesManager { private static final String TAG = TextServicesManager.class.getSimpleName(); private static final boolean DBG = false; /** * A compile time switch to control per-profile spell checker, which is not yet ready. * @hide */ public static final boolean DISABLE_PER_PROFILE_SPELL_CHECKER = true; private static TextServicesManager sInstance; private final ITextServicesManager mService; Loading
core/java/com/android/internal/textservice/LazyIntToIntMap.java 0 → 100644 +67 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.internal.textservice; import android.annotation.NonNull; import android.util.SparseIntArray; import com.android.internal.annotations.VisibleForTesting; import java.util.function.IntUnaryOperator; /** * Simple int-to-int key-value-store that is to be lazily initialized with the given * {@link IntUnaryOperator}. */ @VisibleForTesting public final class LazyIntToIntMap { private final SparseIntArray mMap = new SparseIntArray(); @NonNull private final IntUnaryOperator mMappingFunction; /** * @param mappingFunction int to int mapping rules to be (lazily) evaluated */ public LazyIntToIntMap(@NonNull IntUnaryOperator mappingFunction) { mMappingFunction = mappingFunction; } /** * Deletes {@code key} and associated value. * @param key key to be deleted */ public void delete(int key) { mMap.delete(key); } /** * @param key key associated with the value * @return value associated with the {@code key}. If this is the first time to access * {@code key}, then {@code mappingFunction} passed to the constructor will be evaluated */ public int get(int key) { final int index = mMap.indexOfKey(key); if (index >= 0) { return mMap.valueAt(index); } final int value = mMappingFunction.applyAsInt(key); mMap.append(key, value); return value; } }
core/tests/coretests/src/com/android/internal/textservice/LazyIntToIntMapTest.java 0 → 100644 +92 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.internal.textservice; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import java.util.concurrent.atomic.AtomicReference; import java.util.function.IntUnaryOperator; @SmallTest @RunWith(AndroidJUnit4.class) public class LazyIntToIntMapTest { @Test public void testLaziness() { final IntUnaryOperator func = mock(IntUnaryOperator.class); when(func.applyAsInt(eq(1))).thenReturn(11); when(func.applyAsInt(eq(2))).thenReturn(22); final LazyIntToIntMap map = new LazyIntToIntMap(func); verify(func, never()).applyAsInt(anyInt()); assertEquals(22, map.get(2)); verify(func, times(0)).applyAsInt(eq(1)); verify(func, times(1)).applyAsInt(eq(2)); // Accessing to the same key does not evaluate the function again. assertEquals(22, map.get(2)); verify(func, times(0)).applyAsInt(eq(1)); verify(func, times(1)).applyAsInt(eq(2)); } @Test public void testDelete() { final IntUnaryOperator func1 = mock(IntUnaryOperator.class); when(func1.applyAsInt(eq(1))).thenReturn(11); when(func1.applyAsInt(eq(2))).thenReturn(22); final IntUnaryOperator func2 = mock(IntUnaryOperator.class); when(func2.applyAsInt(eq(1))).thenReturn(111); when(func2.applyAsInt(eq(2))).thenReturn(222); final AtomicReference<IntUnaryOperator> funcRef = new AtomicReference<>(func1); final LazyIntToIntMap map = new LazyIntToIntMap(i -> funcRef.get().applyAsInt(i)); verify(func1, never()).applyAsInt(anyInt()); verify(func2, never()).applyAsInt(anyInt()); assertEquals(22, map.get(2)); verify(func1, times(1)).applyAsInt(eq(2)); verify(func2, times(0)).applyAsInt(eq(2)); // Swap func1 with func2 then invalidate the key=2 funcRef.set(func2); map.delete(2); // Calling get(2) again should re-evaluate the value. assertEquals(222, map.get(2)); verify(func1, times(1)).applyAsInt(eq(2)); verify(func2, times(1)).applyAsInt(eq(2)); // Trying to delete non-existing keys does nothing. map.delete(1); } }
services/core/java/com/android/server/TextServicesManagerService.java +67 −6 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.server; import static android.view.textservice.TextServicesManager.DISABLE_PER_PROFILE_SPELL_CHECKER; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.inputmethod.InputMethodUtils; import com.android.internal.textservice.ISpellCheckerService; Loading @@ -24,6 +27,7 @@ import com.android.internal.textservice.ISpellCheckerSession; import com.android.internal.textservice.ISpellCheckerSessionListener; import com.android.internal.textservice.ITextServicesManager; import com.android.internal.textservice.ITextServicesSessionListener; import com.android.internal.textservice.LazyIntToIntMap; import com.android.internal.util.DumpUtils; import org.xmlpull.v1.XmlPullParserException; Loading Loading @@ -79,6 +83,10 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { private final UserManager mUserManager; private final Object mLock = new Object(); @NonNull @GuardedBy("mLock") private final LazyIntToIntMap mSpellCheckerOwnerUserIdMap; private static class TextServicesData { @UserIdInt private final int mUserId; Loading Loading @@ -294,6 +302,9 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { void onStopUser(@UserIdInt int userId) { synchronized (mLock) { // Clear user ID mapping table. mSpellCheckerOwnerUserIdMap.delete(userId); // Clean per-user data TextServicesData tsd = mUserData.get(userId); if (tsd == null) return; Loading @@ -313,12 +324,32 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { public TextServicesManagerService(Context context) { mContext = context; mUserManager = mContext.getSystemService(UserManager.class); mSpellCheckerOwnerUserIdMap = new LazyIntToIntMap(callingUserId -> { if (DISABLE_PER_PROFILE_SPELL_CHECKER) { final long token = Binder.clearCallingIdentity(); try { final UserInfo parent = mUserManager.getProfileParent(callingUserId); return (parent != null) ? parent.id : callingUserId; } finally { Binder.restoreCallingIdentity(token); } } else { return callingUserId; } }); mMonitor = new TextServicesMonitor(); mMonitor.register(context, null, UserHandle.ALL, true); } private void initializeInternalStateLocked(@UserIdInt int userId) { // When DISABLE_PER_PROFILE_SPELL_CHECKER is true, we make sure here that work profile users // will never have non-null TextServicesData for their user ID. if (DISABLE_PER_PROFILE_SPELL_CHECKER && userId != mSpellCheckerOwnerUserIdMap.get(userId)) { return; } TextServicesData tsd = mUserData.get(userId); if (tsd == null) { tsd = new TextServicesData(userId, mContext); Loading Loading @@ -470,7 +501,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { public SpellCheckerInfo getCurrentSpellChecker(String locale) { int userId = UserHandle.getCallingUserId(); synchronized (mLock) { TextServicesData tsd = mUserData.get(userId); final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); if (tsd == null) return null; return tsd.getCurrentSpellChecker(); Loading @@ -488,7 +519,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { final int userId = UserHandle.getCallingUserId(); synchronized (mLock) { TextServicesData tsd = mUserData.get(userId); final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); if (tsd == null) return null; subtypeHashCode = Loading Loading @@ -569,7 +600,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { int callingUserId = UserHandle.getCallingUserId(); synchronized (mLock) { TextServicesData tsd = mUserData.get(callingUserId); final TextServicesData tsd = getDataFromCallingUserIdLocked(callingUserId); if (tsd == null) return; HashMap<String, SpellCheckerInfo> spellCheckerMap = tsd.mSpellCheckerMap; Loading Loading @@ -606,7 +637,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { int userId = UserHandle.getCallingUserId(); synchronized (mLock) { TextServicesData tsd = mUserData.get(userId); final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); if (tsd == null) return false; return tsd.isSpellCheckerEnabled(); Loading Loading @@ -643,7 +674,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { int callingUserId = UserHandle.getCallingUserId(); synchronized (mLock) { TextServicesData tsd = mUserData.get(callingUserId); final TextServicesData tsd = getDataFromCallingUserIdLocked(callingUserId); if (tsd == null) return null; ArrayList<SpellCheckerInfo> spellCheckerList = tsd.mSpellCheckerList; Loading @@ -666,7 +697,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { int userId = UserHandle.getCallingUserId(); synchronized (mLock) { TextServicesData tsd = mUserData.get(userId); final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); if (tsd == null) return; final ArrayList<SpellCheckerBindGroup> removeList = new ArrayList<>(); Loading Loading @@ -737,6 +768,36 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } } /** * @param callingUserId user ID of the calling process * @return {@link TextServicesData} for the given user. {@code null} if spell checker is not * temporarily / permanently available for the specified user */ @Nullable private TextServicesData getDataFromCallingUserIdLocked(@UserIdInt int callingUserId) { final int spellCheckerOwnerUserId = mSpellCheckerOwnerUserIdMap.get(callingUserId); final TextServicesData data = mUserData.get(spellCheckerOwnerUserId); if (DISABLE_PER_PROFILE_SPELL_CHECKER) { if (spellCheckerOwnerUserId != callingUserId) { // Calling process is running under child profile. if (data == null) { return null; } final SpellCheckerInfo info = data.getCurrentSpellChecker(); if (info == null) { return null; } final ServiceInfo serviceInfo = info.getServiceInfo(); if ((serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { // To be conservative, non pre-installed spell checker services are not allowed // to be used for child profiles. return null; } } } return data; } private static final class SessionRequest { public final int mUid; @Nullable Loading