diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index c6854733318bc..6952d723c622e 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -34,7 +34,9 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.app.ActivityManagerNative; +import android.app.AppGlobals; import android.app.AlertDialog; +import android.app.IUserSwitchObserver; import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationManager; @@ -49,6 +51,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; @@ -63,12 +66,15 @@ import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.IInterface; +import android.os.IRemoteCallback; import android.os.Message; +import android.os.Process; import android.os.Parcel; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; import android.os.SystemClock; +import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.Secure; import android.provider.Settings.SettingNotFoundException; @@ -378,6 +384,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private InputMethodInfo[] mIms; private int[] mSubtypeIds; private Locale mLastSystemLocale; + private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor(); + private final IPackageManager mIPackageManager; class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler) { @@ -398,37 +406,55 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - class ScreenOnOffReceiver extends android.content.BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { - mScreenOn = true; - refreshImeWindowVisibilityLocked(); - } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { - mScreenOn = false; - setImeWindowVisibilityStatusHiddenLocked(); - } else if (intent.getAction().equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) { - hideInputMethodMenu(); - return; - } else { - Slog.w(TAG, "Unexpected intent " + intent); - } - + class ImmsBroadcastReceiver extends android.content.BroadcastReceiver { + private void updateActive() { // Inform the current client of the change in active status if (mCurClient != null && mCurClient.client != null) { executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( MSG_SET_ACTIVE, mScreenOn ? 1 : 0, mCurClient)); } } + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (Intent.ACTION_SCREEN_ON.equals(action)) { + mScreenOn = true; + refreshImeWindowVisibilityLocked(); + updateActive(); + return; + } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { + mScreenOn = false; + setImeWindowVisibilityStatusHiddenLocked(); + updateActive(); + return; + } else if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { + hideInputMethodMenu(); + // No need to updateActive + return; + } else { + Slog.w(TAG, "Unexpected intent " + intent); + } + } } class MyPackageMonitor extends PackageMonitor { - + private boolean isChangingPackagesOfCurrentUser() { + final int userId = getChangingUserId(); + final boolean retval = userId == mSettings.getCurrentUserId(); + if (DEBUG) { + Slog.d(TAG, "--- ignore this call back from a background user: " + userId); + } + return retval; + } + @Override public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { + if (!isChangingPackagesOfCurrentUser()) { + return false; + } synchronized (mMethodMap) { - String curInputMethodId = Settings.Secure.getString(mContext - .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); + String curInputMethodId = mSettings.getSelectedInputMethod(); final int N = mMethodList.size(); if (curInputMethodId != null) { for (int i=0; i list, HashMap map) { + if (DEBUG) { + Slog.d(TAG, "--- re-buildInputMethodList " + ", \n ------ \n" + getStackTrace()); + } list.clear(); map.clear(); - PackageManager pm = mContext.getPackageManager(); + // Use for queryIntentServicesAsUser + final PackageManager pm = mContext.getPackageManager(); final Configuration config = mRes.getConfiguration(); final boolean haveHardKeyboard = config.keyboard == Configuration.KEYBOARD_QWERTY; - String disabledSysImes = Settings.Secure.getString(mContext.getContentResolver(), - Secure.DISABLED_SYSTEM_INPUT_METHODS); + String disabledSysImes = mSettings.getDisabledSystemInputMethods(); if (disabledSysImes == null) disabledSysImes = ""; - List services = pm.queryIntentServices( + final List services = pm.queryIntentServicesAsUser( new Intent(InputMethod.SERVICE_INTERFACE), - PackageManager.GET_META_DATA); + PackageManager.GET_META_DATA, mSettings.getCurrentUserId()); final HashMap> additionalSubtypes = mFileManager.getAllAdditionalInputMethodSubtypes(); @@ -2279,8 +2475,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - final String defaultImiId = Settings.Secure.getString(mContext - .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); + final String defaultImiId = mSettings.getSelectedInputMethod(); if (!TextUtils.isEmpty(defaultImiId)) { if (!map.containsKey(defaultImiId)) { Slog.w(TAG, "Default IME is uninstalled. Choose new default IME."); @@ -2331,11 +2526,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) Slog.v(TAG, "Show switching menu"); final Context context = mContext; - final PackageManager pm = context.getPackageManager(); final boolean isScreenLocked = isScreenLocked(); - final String lastInputMethodId = Settings.Secure.getString(context - .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); + final String lastInputMethodId = mSettings.getSelectedInputMethod(); int lastInputMethodSubtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId); if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId); @@ -2353,7 +2546,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub showSubtypes, mInputShown, isScreenLocked); if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) { - final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtype(); + final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtypeLocked(); if (currentSubtype != null) { final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId); lastInputMethodSubtypeId = @@ -2582,6 +2775,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public boolean setInputMethodEnabled(String id, boolean enabled) { + // TODO: Make this work even for non-current users? + if (!calledFromValidUser()) { + return false; + } synchronized (mMethodMap) { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.WRITE_SECURE_SETTINGS) @@ -2626,8 +2823,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (mSettings.buildAndPutEnabledInputMethodsStrRemovingIdLocked( builder, enabledInputMethodsList, id)) { // Disabled input method is currently selected, switch to another one. - String selId = Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.DEFAULT_INPUT_METHOD); + final String selId = mSettings.getSelectedInputMethod(); if (id.equals(selId) && !chooseNewDefaultIMELocked()) { Slog.i(TAG, "Can't find new IME, unsetting the current input method."); resetSelectedInputMethodAndSubtypeLocked(""); @@ -2674,7 +2870,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } else { mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID); // If the subtype is not specified, choose the most applicable one - mCurrentSubtype = getCurrentInputMethodSubtype(); + mCurrentSubtype = getCurrentInputMethodSubtypeLocked(); } } @@ -2716,14 +2912,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (imi == null) { return NOT_A_SUBTYPE_ID; } - int subtypeId; - try { - subtypeId = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE); - } catch (SettingNotFoundException e) { - return NOT_A_SUBTYPE_ID; - } - return getSubtypeIdFromHashCode(imi, subtypeId); + final int subtypeHashCode = mSettings.getSelectedInputMethodSubtypeHashCode(); + return getSubtypeIdFromHashCode(imi, subtypeHashCode); } private static boolean isValidSubtypeId(InputMethodInfo imi, int subtypeHashCode) { @@ -2886,7 +3076,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } InputMethodSubtype subtype = null; final List enabledSubtypes = - getEnabledInputMethodSubtypeList(imi, true); + getEnabledInputMethodSubtypeListLocked(imi, true); // 1. Search by the current subtype's locale from enabledSubtypes. if (mCurrentSubtype != null) { subtype = findLastResortApplicableSubtypeLocked( @@ -2955,49 +3145,53 @@ public class InputMethodManagerService extends IInputMethodManager.Stub */ @Override public InputMethodSubtype getCurrentInputMethodSubtype() { + // TODO: Make this work even for non-current users? + if (!calledFromValidUser()) { + return null; + } + synchronized (mMethodMap) { + return getCurrentInputMethodSubtypeLocked(); + } + } + + private InputMethodSubtype getCurrentInputMethodSubtypeLocked() { if (mCurMethodId == null) { return null; } - boolean subtypeIsSelected = false; - try { - subtypeIsSelected = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE) != NOT_A_SUBTYPE_ID; - } catch (SettingNotFoundException e) { + final boolean subtypeIsSelected = + mSettings.getSelectedInputMethodSubtypeHashCode() != NOT_A_SUBTYPE_ID; + final InputMethodInfo imi = mMethodMap.get(mCurMethodId); + if (imi == null || imi.getSubtypeCount() == 0) { + return null; } - synchronized (mMethodMap) { - final InputMethodInfo imi = mMethodMap.get(mCurMethodId); - if (imi == null || imi.getSubtypeCount() == 0) { - return null; - } - if (!subtypeIsSelected || mCurrentSubtype == null - || !isValidSubtypeId(imi, mCurrentSubtype.hashCode())) { - int subtypeId = getSelectedInputMethodSubtypeId(mCurMethodId); - if (subtypeId == NOT_A_SUBTYPE_ID) { - // If there are no selected subtypes, the framework will try to find - // the most applicable subtype from explicitly or implicitly enabled - // subtypes. - List explicitlyOrImplicitlyEnabledSubtypes = - getEnabledInputMethodSubtypeList(imi, true); - // If there is only one explicitly or implicitly enabled subtype, - // just returns it. - if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) { - mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0); - } else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) { + if (!subtypeIsSelected || mCurrentSubtype == null + || !isValidSubtypeId(imi, mCurrentSubtype.hashCode())) { + int subtypeId = getSelectedInputMethodSubtypeId(mCurMethodId); + if (subtypeId == NOT_A_SUBTYPE_ID) { + // If there are no selected subtypes, the framework will try to find + // the most applicable subtype from explicitly or implicitly enabled + // subtypes. + List explicitlyOrImplicitlyEnabledSubtypes = + getEnabledInputMethodSubtypeListLocked(imi, true); + // If there is only one explicitly or implicitly enabled subtype, + // just returns it. + if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) { + mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0); + } else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) { + mCurrentSubtype = findLastResortApplicableSubtypeLocked( + mRes, explicitlyOrImplicitlyEnabledSubtypes, + SUBTYPE_MODE_KEYBOARD, null, true); + if (mCurrentSubtype == null) { mCurrentSubtype = findLastResortApplicableSubtypeLocked( - mRes, explicitlyOrImplicitlyEnabledSubtypes, - SUBTYPE_MODE_KEYBOARD, null, true); - if (mCurrentSubtype == null) { - mCurrentSubtype = findLastResortApplicableSubtypeLocked( - mRes, explicitlyOrImplicitlyEnabledSubtypes, null, null, - true); - } + mRes, explicitlyOrImplicitlyEnabledSubtypes, null, null, + true); } - } else { - mCurrentSubtype = getSubtypes(imi).get(subtypeId); } + } else { + mCurrentSubtype = getSubtypes(imi).get(subtypeId); } - return mCurrentSubtype; } + return mCurrentSubtype; } private void addShortcutInputMethodAndSubtypes(InputMethodInfo imi, @@ -3042,6 +3236,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) { + // TODO: Make this work even for non-current users? + if (!calledFromValidUser()) { + return false; + } synchronized (mMethodMap) { if (subtype != null && mCurMethodId != null) { InputMethodInfo imi = mMethodMap.get(mCurMethodId); @@ -3057,6 +3255,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private static class InputMethodAndSubtypeListManager { private final Context mContext; + // Used to load label private final PackageManager mPm; private final InputMethodManagerService mImms; private final String mSystemLocaleStr; @@ -3193,6 +3392,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private final ArrayList mMethodList; private String mEnabledInputMethodsStrCache; + private int mCurrentUserId; private static void buildEnabledInputMethodsSettingString( StringBuilder builder, Pair> pair) { @@ -3208,13 +3408,24 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public InputMethodSettings( Resources res, ContentResolver resolver, - HashMap methodMap, ArrayList methodList) { + HashMap methodMap, ArrayList methodList, + int userId) { + setCurrentUserId(userId); mRes = res; mResolver = resolver; mMethodMap = methodMap; mMethodList = methodList; } + public void setCurrentUserId(int userId) { + if (DEBUG) { + Slog.d(TAG, "--- Swtich the current user from " + mCurrentUserId + " to " + + userId + ", new ime = " + getSelectedInputMethod()); + } + // IMMS settings are kept per user, so keep track of current user + mCurrentUserId = userId; + } + public List getEnabledInputMethodListLocked() { return createEnabledInputMethodListLocked( getEnabledInputMethodsAndSubtypeListLocked()); @@ -3363,15 +3574,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } private void putEnabledInputMethodsStr(String str) { - Settings.Secure.putString(mResolver, Settings.Secure.ENABLED_INPUT_METHODS, str); + Settings.Secure.putStringForUser( + mResolver, Settings.Secure.ENABLED_INPUT_METHODS, str, mCurrentUserId); mEnabledInputMethodsStrCache = str; + if (DEBUG) { + Slog.d(TAG, "putEnabledInputMethodStr: " + str); + } } private String getEnabledInputMethodsStr() { - mEnabledInputMethodsStrCache = Settings.Secure.getString( - mResolver, Settings.Secure.ENABLED_INPUT_METHODS); + mEnabledInputMethodsStrCache = Settings.Secure.getStringForUser( + mResolver, Settings.Secure.ENABLED_INPUT_METHODS, mCurrentUserId); if (DEBUG) { - Slog.d(TAG, "getEnabledInputMethodsStr: " + mEnabledInputMethodsStrCache); + Slog.d(TAG, "getEnabledInputMethodsStr: " + mEnabledInputMethodsStrCache + + ", " + mCurrentUserId); } return mEnabledInputMethodsStrCache; } @@ -3426,8 +3642,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) { Slog.d(TAG, "putSubtypeHistoryStr: " + str); } - Settings.Secure.putString( - mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, str); + Settings.Secure.putStringForUser( + mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, str, mCurrentUserId); } public Pair getLastInputMethodAndSubtypeLocked() { @@ -3546,20 +3762,57 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private String getSubtypeHistoryStr() { if (DEBUG) { - Slog.d(TAG, "getSubtypeHistoryStr: " + Settings.Secure.getString( - mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY)); + Slog.d(TAG, "getSubtypeHistoryStr: " + Settings.Secure.getStringForUser( + mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, mCurrentUserId)); } - return Settings.Secure.getString( - mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY); + return Settings.Secure.getStringForUser( + mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, mCurrentUserId); } public void putSelectedInputMethod(String imeId) { - Settings.Secure.putString(mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, imeId); + if (DEBUG) { + Slog.d(TAG, "putSelectedInputMethodStr: " + imeId + ", " + + mCurrentUserId); + } + Settings.Secure.putStringForUser( + mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, imeId, mCurrentUserId); } public void putSelectedSubtype(int subtypeId) { - Settings.Secure.putInt( - mResolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, subtypeId); + if (DEBUG) { + Slog.d(TAG, "putSelectedInputMethodSubtypeStr: " + subtypeId + ", " + + mCurrentUserId); + } + Settings.Secure.putIntForUser(mResolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, + subtypeId, mCurrentUserId); + } + + public String getDisabledSystemInputMethods() { + return Settings.Secure.getStringForUser( + mResolver, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS, mCurrentUserId); + } + + public String getSelectedInputMethod() { + if (DEBUG) { + Slog.d(TAG, "getSelectedInputMethodStr: " + Settings.Secure.getStringForUser( + mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, mCurrentUserId) + + ", " + mCurrentUserId); + } + return Settings.Secure.getStringForUser( + mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, mCurrentUserId); + } + + public int getSelectedInputMethodSubtypeHashCode() { + try { + return Settings.Secure.getIntForUser( + mResolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, mCurrentUserId); + } catch (SettingNotFoundException e) { + return NOT_A_SUBTYPE_ID; + } + } + + public int getCurrentUserId() { + return mCurrentUserId; } } @@ -3762,6 +4015,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } // ---------------------------------------------------------------------- + // Utilities for debug + private static String getStackTrace() { + final StringBuilder sb = new StringBuilder(); + try { + throw new RuntimeException(); + } catch (RuntimeException e) { + final StackTraceElement[] frames = e.getStackTrace(); + // Start at 1 because the first frame is here and we don't care about it + for (int j = 1; j < frames.length; ++j) { + sb.append(frames[j].toString() + "\n"); + } + } + return sb.toString(); + } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {