Loading services/core/java/com/android/server/TextServicesManagerService.java +83 −15 Original line number Original line Diff line number Diff line Loading @@ -27,6 +27,7 @@ import com.android.internal.textservice.ITextServicesSessionListener; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.annotation.UserIdInt; import android.app.ActivityManagerNative; import android.app.ActivityManagerNative; import android.app.AppGlobals; import android.app.AppGlobals; Loading Loading @@ -80,6 +81,8 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { private final ArrayList<SpellCheckerInfo> mSpellCheckerList = new ArrayList<>(); private final ArrayList<SpellCheckerInfo> mSpellCheckerList = new ArrayList<>(); private final HashMap<String, SpellCheckerBindGroup> mSpellCheckerBindGroups = new HashMap<>(); private final HashMap<String, SpellCheckerBindGroup> mSpellCheckerBindGroups = new HashMap<>(); private final TextServicesSettings mSettings; private final TextServicesSettings mSettings; @NonNull private final UserManager mUserManager; public static final class Lifecycle extends SystemService { public static final class Lifecycle extends SystemService { private TextServicesManagerService mService; private TextServicesManagerService mService; Loading Loading @@ -109,19 +112,37 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { mService.systemRunning(); mService.systemRunning(); } } } } @Override public void onUnlockUser(@UserIdInt int userHandle) { // Called on the system server's main looper thread. // TODO: Dispatch this to a worker thread as needed. mService.onUnlockUser(userHandle); } } } void systemRunning() { void systemRunning() { synchronized (mSpellCheckerMap) { synchronized (mSpellCheckerMap) { if (!mSystemReady) { if (!mSystemReady) { mSystemReady = true; mSystemReady = true; resetInternalState(mSettings.getCurrentUserId()); } } } } } } void onSwitchUser(@UserIdInt int userId) { void onSwitchUser(@UserIdInt int userId) { synchronized (mSpellCheckerMap) { synchronized (mSpellCheckerMap) { switchUserLocked(userId); resetInternalState(userId); } } void onUnlockUser(@UserIdInt int userId) { synchronized(mSpellCheckerMap) { final int currentUserId = mSettings.getCurrentUserId(); if (userId != currentUserId) { return; } resetInternalState(currentUserId); } } } } Loading @@ -129,6 +150,8 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { mSystemReady = false; mSystemReady = false; mContext = context; mContext = context; mUserManager = mContext.getSystemService(UserManager.class); final IntentFilter broadcastFilter = new IntentFilter(); final IntentFilter broadcastFilter = new IntentFilter(); broadcastFilter.addAction(Intent.ACTION_USER_ADDED); broadcastFilter.addAction(Intent.ACTION_USER_ADDED); broadcastFilter.addAction(Intent.ACTION_USER_REMOVED); broadcastFilter.addAction(Intent.ACTION_USER_REMOVED); Loading @@ -142,14 +165,19 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } } mMonitor = new TextServicesMonitor(); mMonitor = new TextServicesMonitor(); mMonitor.register(context, null, true); mMonitor.register(context, null, true); mSettings = new TextServicesSettings(context.getContentResolver(), userId); final boolean useCopyOnWriteSettings = !mSystemReady || !mUserManager.isUserUnlocked(userId); mSettings = new TextServicesSettings(context.getContentResolver(), userId, useCopyOnWriteSettings); // "switchUserLocked" initializes the states for the foreground user // "resetInternalState" initializes the states for the foreground user switchUserLocked(userId); resetInternalState(userId); } } private void switchUserLocked(@UserIdInt int userId) { private void resetInternalState(@UserIdInt int userId) { mSettings.setCurrentUserId(userId); final boolean useCopyOnWriteSettings = !mSystemReady || !mUserManager.isUserUnlocked(userId); mSettings.switchCurrentUser(userId, useCopyOnWriteSettings); updateCurrentProfileIds(); updateCurrentProfileIds(); unbindServiceLocked(); unbindServiceLocked(); buildSpellCheckerMapLocked(mContext, mSpellCheckerList, mSpellCheckerMap, mSettings); buildSpellCheckerMapLocked(mContext, mSpellCheckerList, mSpellCheckerMap, mSettings); Loading @@ -166,8 +194,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } } void updateCurrentProfileIds() { void updateCurrentProfileIds() { List<UserInfo> profiles = final List<UserInfo> profiles = mUserManager.getProfiles(mSettings.getCurrentUserId()); UserManager.get(mContext).getProfiles(mSettings.getCurrentUserId()); int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null for (int i = 0; i < currentProfileIds.length; i++) { for (int i = 0; i < currentProfileIds.length; i++) { currentProfileIds[i] = profiles.get(i).id; currentProfileIds[i] = profiles.get(i).id; Loading Loading @@ -232,6 +259,9 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { list.clear(); list.clear(); map.clear(); map.clear(); final PackageManager pm = context.getPackageManager(); final PackageManager pm = context.getPackageManager(); // Note: We do not specify PackageManager.MATCH_ENCRYPTION_* flags here because the default // behavior of PackageManager is exactly what we want. It by default picks up appropriate // services depending on the unlock state for the specified user. final List<ResolveInfo> services = pm.queryIntentServicesAsUser( final List<ResolveInfo> services = pm.queryIntentServicesAsUser( new Intent(SpellCheckerService.SERVICE_INTERFACE), PackageManager.GET_META_DATA, new Intent(SpellCheckerService.SERVICE_INTERFACE), PackageManager.GET_META_DATA, settings.getCurrentUserId()); settings.getCurrentUserId()); Loading Loading @@ -1023,33 +1053,70 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { private int[] mCurrentProfileIds = new int[0]; private int[] mCurrentProfileIds = new int[0]; private Object mLock = new Object(); private Object mLock = new Object(); public TextServicesSettings(ContentResolver resolver, @UserIdInt int userId) { /** * On-memory data store to emulate when {@link #mCopyOnWrite} is {@code true}. */ private final HashMap<String, String> mCopyOnWriteDataStore = new HashMap<>(); private boolean mCopyOnWrite = false; public TextServicesSettings(ContentResolver resolver, @UserIdInt int userId, boolean copyOnWrite) { mResolver = resolver; mResolver = resolver; mCurrentUserId = userId; switchCurrentUser(userId, copyOnWrite); } } public void setCurrentUserId(@UserIdInt int userId) { /** * Must be called when the current user is changed. * * @param userId The user ID. * @param copyOnWrite If {@code true}, for each settings key * (e.g. {@link Settings.Secure#SELECTED_SPELL_CHECKER}) we use the actual settings on the * {@link Settings.Secure} until we do the first write operation. */ public void switchCurrentUser(@UserIdInt int userId, boolean copyOnWrite) { if (DBG) { if (DBG) { Slog.d(TAG, "--- Swtich the current user from " + mCurrentUserId + " to " Slog.d(TAG, "--- Swtich the current user from " + mCurrentUserId + " to " + userId + ", new ime = " + getSelectedSpellChecker()); + userId + ", new ime = " + getSelectedSpellChecker()); } } if (mCurrentUserId != userId || mCopyOnWrite != copyOnWrite) { mCopyOnWriteDataStore.clear(); // TODO: mCurrentProfileIds should be cleared here. } // TSMS settings are kept per user, so keep track of current user // TSMS settings are kept per user, so keep track of current user mCurrentUserId = userId; mCurrentUserId = userId; mCopyOnWrite = copyOnWrite; // TODO: mCurrentProfileIds should be updated here. } } private void putString(final String key, final String str) { private void putString(final String key, final String str) { if (mCopyOnWrite) { mCopyOnWriteDataStore.put(key, str); } else { Settings.Secure.putStringForUser(mResolver, key, str, mCurrentUserId); Settings.Secure.putStringForUser(mResolver, key, str, mCurrentUserId); } } } private String getString(final String key) { private String getString(final String key) { if (mCopyOnWrite && mCopyOnWriteDataStore.containsKey(key)) { final String result = mCopyOnWriteDataStore.get(key); return result != null ? result : ""; } return Settings.Secure.getStringForUser(mResolver, key, mCurrentUserId); return Settings.Secure.getStringForUser(mResolver, key, mCurrentUserId); } } private void putInt(final String key, final int value) { private void putInt(final String key, final int value) { if (mCopyOnWrite) { mCopyOnWriteDataStore.put(key, String.valueOf(value)); } else { Settings.Secure.putIntForUser(mResolver, key, value, mCurrentUserId); Settings.Secure.putIntForUser(mResolver, key, value, mCurrentUserId); } } } private int getInt(final String key, final int defaultValue) { private int getInt(final String key, final int defaultValue) { if (mCopyOnWrite && mCopyOnWriteDataStore.containsKey(key)) { final String result = mCopyOnWriteDataStore.get(key); return result != null ? Integer.valueOf(result) : 0; } return Settings.Secure.getIntForUser(mResolver, key, defaultValue, mCurrentUserId); return Settings.Secure.getIntForUser(mResolver, key, defaultValue, mCurrentUserId); } } Loading Loading @@ -1109,6 +1176,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { public void dumpLocked(final PrintWriter pw, final String prefix) { public void dumpLocked(final PrintWriter pw, final String prefix) { pw.println(prefix + "mCurrentUserId=" + mCurrentUserId); pw.println(prefix + "mCurrentUserId=" + mCurrentUserId); pw.println(prefix + "mCurrentProfileIds=" + Arrays.toString(mCurrentProfileIds)); pw.println(prefix + "mCurrentProfileIds=" + Arrays.toString(mCurrentProfileIds)); pw.println(prefix + "mCopyOnWrite=" + mCopyOnWrite); } } } } Loading Loading
services/core/java/com/android/server/TextServicesManagerService.java +83 −15 Original line number Original line Diff line number Diff line Loading @@ -27,6 +27,7 @@ import com.android.internal.textservice.ITextServicesSessionListener; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.annotation.UserIdInt; import android.app.ActivityManagerNative; import android.app.ActivityManagerNative; import android.app.AppGlobals; import android.app.AppGlobals; Loading Loading @@ -80,6 +81,8 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { private final ArrayList<SpellCheckerInfo> mSpellCheckerList = new ArrayList<>(); private final ArrayList<SpellCheckerInfo> mSpellCheckerList = new ArrayList<>(); private final HashMap<String, SpellCheckerBindGroup> mSpellCheckerBindGroups = new HashMap<>(); private final HashMap<String, SpellCheckerBindGroup> mSpellCheckerBindGroups = new HashMap<>(); private final TextServicesSettings mSettings; private final TextServicesSettings mSettings; @NonNull private final UserManager mUserManager; public static final class Lifecycle extends SystemService { public static final class Lifecycle extends SystemService { private TextServicesManagerService mService; private TextServicesManagerService mService; Loading Loading @@ -109,19 +112,37 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { mService.systemRunning(); mService.systemRunning(); } } } } @Override public void onUnlockUser(@UserIdInt int userHandle) { // Called on the system server's main looper thread. // TODO: Dispatch this to a worker thread as needed. mService.onUnlockUser(userHandle); } } } void systemRunning() { void systemRunning() { synchronized (mSpellCheckerMap) { synchronized (mSpellCheckerMap) { if (!mSystemReady) { if (!mSystemReady) { mSystemReady = true; mSystemReady = true; resetInternalState(mSettings.getCurrentUserId()); } } } } } } void onSwitchUser(@UserIdInt int userId) { void onSwitchUser(@UserIdInt int userId) { synchronized (mSpellCheckerMap) { synchronized (mSpellCheckerMap) { switchUserLocked(userId); resetInternalState(userId); } } void onUnlockUser(@UserIdInt int userId) { synchronized(mSpellCheckerMap) { final int currentUserId = mSettings.getCurrentUserId(); if (userId != currentUserId) { return; } resetInternalState(currentUserId); } } } } Loading @@ -129,6 +150,8 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { mSystemReady = false; mSystemReady = false; mContext = context; mContext = context; mUserManager = mContext.getSystemService(UserManager.class); final IntentFilter broadcastFilter = new IntentFilter(); final IntentFilter broadcastFilter = new IntentFilter(); broadcastFilter.addAction(Intent.ACTION_USER_ADDED); broadcastFilter.addAction(Intent.ACTION_USER_ADDED); broadcastFilter.addAction(Intent.ACTION_USER_REMOVED); broadcastFilter.addAction(Intent.ACTION_USER_REMOVED); Loading @@ -142,14 +165,19 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } } mMonitor = new TextServicesMonitor(); mMonitor = new TextServicesMonitor(); mMonitor.register(context, null, true); mMonitor.register(context, null, true); mSettings = new TextServicesSettings(context.getContentResolver(), userId); final boolean useCopyOnWriteSettings = !mSystemReady || !mUserManager.isUserUnlocked(userId); mSettings = new TextServicesSettings(context.getContentResolver(), userId, useCopyOnWriteSettings); // "switchUserLocked" initializes the states for the foreground user // "resetInternalState" initializes the states for the foreground user switchUserLocked(userId); resetInternalState(userId); } } private void switchUserLocked(@UserIdInt int userId) { private void resetInternalState(@UserIdInt int userId) { mSettings.setCurrentUserId(userId); final boolean useCopyOnWriteSettings = !mSystemReady || !mUserManager.isUserUnlocked(userId); mSettings.switchCurrentUser(userId, useCopyOnWriteSettings); updateCurrentProfileIds(); updateCurrentProfileIds(); unbindServiceLocked(); unbindServiceLocked(); buildSpellCheckerMapLocked(mContext, mSpellCheckerList, mSpellCheckerMap, mSettings); buildSpellCheckerMapLocked(mContext, mSpellCheckerList, mSpellCheckerMap, mSettings); Loading @@ -166,8 +194,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } } void updateCurrentProfileIds() { void updateCurrentProfileIds() { List<UserInfo> profiles = final List<UserInfo> profiles = mUserManager.getProfiles(mSettings.getCurrentUserId()); UserManager.get(mContext).getProfiles(mSettings.getCurrentUserId()); int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null for (int i = 0; i < currentProfileIds.length; i++) { for (int i = 0; i < currentProfileIds.length; i++) { currentProfileIds[i] = profiles.get(i).id; currentProfileIds[i] = profiles.get(i).id; Loading Loading @@ -232,6 +259,9 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { list.clear(); list.clear(); map.clear(); map.clear(); final PackageManager pm = context.getPackageManager(); final PackageManager pm = context.getPackageManager(); // Note: We do not specify PackageManager.MATCH_ENCRYPTION_* flags here because the default // behavior of PackageManager is exactly what we want. It by default picks up appropriate // services depending on the unlock state for the specified user. final List<ResolveInfo> services = pm.queryIntentServicesAsUser( final List<ResolveInfo> services = pm.queryIntentServicesAsUser( new Intent(SpellCheckerService.SERVICE_INTERFACE), PackageManager.GET_META_DATA, new Intent(SpellCheckerService.SERVICE_INTERFACE), PackageManager.GET_META_DATA, settings.getCurrentUserId()); settings.getCurrentUserId()); Loading Loading @@ -1023,33 +1053,70 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { private int[] mCurrentProfileIds = new int[0]; private int[] mCurrentProfileIds = new int[0]; private Object mLock = new Object(); private Object mLock = new Object(); public TextServicesSettings(ContentResolver resolver, @UserIdInt int userId) { /** * On-memory data store to emulate when {@link #mCopyOnWrite} is {@code true}. */ private final HashMap<String, String> mCopyOnWriteDataStore = new HashMap<>(); private boolean mCopyOnWrite = false; public TextServicesSettings(ContentResolver resolver, @UserIdInt int userId, boolean copyOnWrite) { mResolver = resolver; mResolver = resolver; mCurrentUserId = userId; switchCurrentUser(userId, copyOnWrite); } } public void setCurrentUserId(@UserIdInt int userId) { /** * Must be called when the current user is changed. * * @param userId The user ID. * @param copyOnWrite If {@code true}, for each settings key * (e.g. {@link Settings.Secure#SELECTED_SPELL_CHECKER}) we use the actual settings on the * {@link Settings.Secure} until we do the first write operation. */ public void switchCurrentUser(@UserIdInt int userId, boolean copyOnWrite) { if (DBG) { if (DBG) { Slog.d(TAG, "--- Swtich the current user from " + mCurrentUserId + " to " Slog.d(TAG, "--- Swtich the current user from " + mCurrentUserId + " to " + userId + ", new ime = " + getSelectedSpellChecker()); + userId + ", new ime = " + getSelectedSpellChecker()); } } if (mCurrentUserId != userId || mCopyOnWrite != copyOnWrite) { mCopyOnWriteDataStore.clear(); // TODO: mCurrentProfileIds should be cleared here. } // TSMS settings are kept per user, so keep track of current user // TSMS settings are kept per user, so keep track of current user mCurrentUserId = userId; mCurrentUserId = userId; mCopyOnWrite = copyOnWrite; // TODO: mCurrentProfileIds should be updated here. } } private void putString(final String key, final String str) { private void putString(final String key, final String str) { if (mCopyOnWrite) { mCopyOnWriteDataStore.put(key, str); } else { Settings.Secure.putStringForUser(mResolver, key, str, mCurrentUserId); Settings.Secure.putStringForUser(mResolver, key, str, mCurrentUserId); } } } private String getString(final String key) { private String getString(final String key) { if (mCopyOnWrite && mCopyOnWriteDataStore.containsKey(key)) { final String result = mCopyOnWriteDataStore.get(key); return result != null ? result : ""; } return Settings.Secure.getStringForUser(mResolver, key, mCurrentUserId); return Settings.Secure.getStringForUser(mResolver, key, mCurrentUserId); } } private void putInt(final String key, final int value) { private void putInt(final String key, final int value) { if (mCopyOnWrite) { mCopyOnWriteDataStore.put(key, String.valueOf(value)); } else { Settings.Secure.putIntForUser(mResolver, key, value, mCurrentUserId); Settings.Secure.putIntForUser(mResolver, key, value, mCurrentUserId); } } } private int getInt(final String key, final int defaultValue) { private int getInt(final String key, final int defaultValue) { if (mCopyOnWrite && mCopyOnWriteDataStore.containsKey(key)) { final String result = mCopyOnWriteDataStore.get(key); return result != null ? Integer.valueOf(result) : 0; } return Settings.Secure.getIntForUser(mResolver, key, defaultValue, mCurrentUserId); return Settings.Secure.getIntForUser(mResolver, key, defaultValue, mCurrentUserId); } } Loading Loading @@ -1109,6 +1176,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { public void dumpLocked(final PrintWriter pw, final String prefix) { public void dumpLocked(final PrintWriter pw, final String prefix) { pw.println(prefix + "mCurrentUserId=" + mCurrentUserId); pw.println(prefix + "mCurrentUserId=" + mCurrentUserId); pw.println(prefix + "mCurrentProfileIds=" + Arrays.toString(mCurrentProfileIds)); pw.println(prefix + "mCurrentProfileIds=" + Arrays.toString(mCurrentProfileIds)); pw.println(prefix + "mCopyOnWrite=" + mCopyOnWrite); } } } } Loading