diff options
| author | Satoshi Kataoka <satok@google.com> | 2012-09-13 21:34:20 +0900 |
|---|---|---|
| committer | Satoshi Kataoka <satok@google.com> | 2012-09-26 19:22:04 +0900 |
| commit | 4e1ab15b305aac26ad8819fc3b2951e20985944d (patch) | |
| tree | dcd8182556e4c360d6b1391e5c45fd4bc7e80c05 /services/java/com/android/server/InputMethodManagerService.java | |
| parent | f05d511bc95c5d0bdfa6d64c6a8a02c6a099703f (diff) | |
Enable multi-user function for IMF
Bug: 6931482
Change-Id: Ib23849d352db33f0747aa9d5a178f00ac726c13b
Diffstat (limited to 'services/java/com/android/server/InputMethodManagerService.java')
| -rw-r--r-- | services/java/com/android/server/InputMethodManagerService.java | 537 |
1 files changed, 402 insertions, 135 deletions
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index c6854733318b..6952d723c622 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 { + 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) { - if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { + final String action = intent.getAction(); + if (Intent.ACTION_SCREEN_ON.equals(action)) { mScreenOn = true; refreshImeWindowVisibilityLocked(); - } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { + updateActive(); + return; + } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { mScreenOn = false; setImeWindowVisibilityStatusHiddenLocked(); - } else if (intent.getAction().equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) { + updateActive(); + return; + } else if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { hideInputMethodMenu(); + // No need to updateActive return; } else { Slog.w(TAG, "Unexpected intent " + intent); } - - // 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)); - } } } 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<N; i++) { @@ -453,10 +479,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void onSomePackagesChanged() { + if (!isChangingPackagesOfCurrentUser()) { + return; + } synchronized (mMethodMap) { InputMethodInfo curIm = null; - 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<N; i++) { @@ -489,9 +517,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub || change == PACKAGE_PERMANENT_CHANGE) { ServiceInfo si = null; try { - si = mContext.getPackageManager().getServiceInfo( - curIm.getComponent(), 0); - } catch (PackageManager.NameNotFoundException ex) { + si = mIPackageManager.getServiceInfo( + curIm.getComponent(), 0, mSettings.getCurrentUserId()); + } catch (RemoteException ex) { } if (si == null) { // Uh oh, current input method is no longer around! @@ -565,6 +593,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } public InputMethodManagerService(Context context, WindowManagerService windowManager) { + mIPackageManager = AppGlobals.getPackageManager(); mContext = context; mRes = context.getResources(); mHandler = new Handler(this); @@ -601,23 +630,44 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } mImListManager = new InputMethodAndSubtypeListManager(context, this); - (new MyPackageMonitor()).register(mContext, null, true); - - IntentFilter screenOnOffFilt = new IntentFilter(); - screenOnOffFilt.addAction(Intent.ACTION_SCREEN_ON); - screenOnOffFilt.addAction(Intent.ACTION_SCREEN_OFF); - screenOnOffFilt.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - mContext.registerReceiver(new ScreenOnOffReceiver(), screenOnOffFilt); + final IntentFilter broadcastFilter = new IntentFilter(); + broadcastFilter.addAction(Intent.ACTION_SCREEN_ON); + broadcastFilter.addAction(Intent.ACTION_SCREEN_OFF); + broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter); mNotificationShown = false; + int userId = 0; + try { + ActivityManagerNative.getDefault().registerUserSwitchObserver( + new IUserSwitchObserver.Stub() { + @Override + public void onUserSwitching(int newUserId, IRemoteCallback reply) { + switchUser(newUserId); + if (reply != null) { + try { + reply.sendResult(null); + } catch (RemoteException e) { + } + } + } + + @Override + public void onUserSwitchComplete(int newUserId) throws RemoteException { + } + }); + userId = ActivityManagerNative.getDefault().getCurrentUser().id; + } catch (RemoteException e) { + Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e); + } + mMyPackageMonitor.register(mContext, null, true); // mSettings should be created before buildInputMethodListLocked mSettings = new InputMethodSettings( - mRes, context.getContentResolver(), mMethodMap, mMethodList); + mRes, context.getContentResolver(), mMethodMap, mMethodList, userId); // Just checking if defaultImiId is empty or not - final String defaultImiId = Settings.Secure.getString( - mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); + final String defaultImiId = mSettings.getSelectedInputMethod(); mImeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId); buildInputMethodListLocked(mMethodList, mMethodMap); @@ -646,24 +696,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub }, filter); } - private void checkCurrentLocaleChangedLocked() { - if (!mSystemReady) { - // not system ready - return; - } - final Locale newLocale = mRes.getConfiguration().locale; - if (newLocale != null && !newLocale.equals(mLastSystemLocale)) { - if (DEBUG) { - Slog.i(TAG, "Locale has been changed to " + newLocale); - } - buildInputMethodListLocked(mMethodList, mMethodMap); - // Reset the current ime to the proper one - resetDefaultImeLocked(mContext); - updateFromSettingsLocked(); - mLastSystemLocale = newLocale; - } - } - private void resetDefaultImeLocked(Context context) { // Do not reset the default (current) IME when it is a 3rd-party IME if (mCurMethodId != null && !isSystemIme(mMethodMap.get(mCurMethodId))) { @@ -688,6 +720,52 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + private void resetAllInternalStateLocked(boolean updateOnlyWhenLocaleChanged) { + if (!mSystemReady) { + // not system ready + return; + } + final Locale newLocale = mRes.getConfiguration().locale; + if (!updateOnlyWhenLocaleChanged + || (newLocale != null && !newLocale.equals(mLastSystemLocale))) { + if (!updateOnlyWhenLocaleChanged) { + hideCurrentInputLocked(0, null); + mCurMethodId = null; + unbindCurrentMethodLocked(true); + } + if (DEBUG) { + Slog.i(TAG, "Locale has been changed to " + newLocale); + } + buildInputMethodListLocked(mMethodList, mMethodMap); + if (!updateOnlyWhenLocaleChanged) { + final String selectedImiId = mSettings.getSelectedInputMethod(); + if (TextUtils.isEmpty(selectedImiId)) { + // This is the first time of the user switch and + // set the current ime to the proper one. + resetDefaultImeLocked(mContext); + } + } + updateFromSettingsLocked(); + mLastSystemLocale = newLocale; + if (!updateOnlyWhenLocaleChanged) { + try { + startInputInnerLocked(); + } catch (RuntimeException e) { + Slog.w(TAG, "Unexpected exception", e); + } + } + } + } + + private void checkCurrentLocaleChangedLocked() { + resetAllInternalStateLocked(true); + } + + private void switchUser(int newUserId) { + mSettings.setCurrentUserId(newUserId); + resetAllInternalStateLocked(false); + } + private boolean isValidSystemDefaultIme(InputMethodInfo imi, Context context) { if (!mSystemReady) { return false; @@ -748,10 +826,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public void systemReady(StatusBarManagerService statusBar) { synchronized (mMethodMap) { + if (DEBUG) { + Slog.d(TAG, "--- systemReady"); + } if (!mSystemReady) { mSystemReady = true; - mKeyguardManager = (KeyguardManager) - mContext.getSystemService(Context.KEYGUARD_SERVICE); + mKeyguardManager = + (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); mStatusBar = statusBar; @@ -802,8 +883,42 @@ public class InputMethodManagerService extends IInputMethodManager.Stub setImeWindowStatus(mCurToken, mImeWindowVis, mBackDisposition); } + // --------------------------------------------------------------------------------------- + // Check whether or not this is a valid IPC. Assumes an IPC is valid when either + // 1) it comes from the system process + // 2) the calling process' user id is identical to the current user id IMMS thinks. + private boolean calledFromValidUser() { + final int uid = Binder.getCallingUid(); + final int userId = UserHandle.getUserId(uid); + if (DEBUG) { + Slog.d(TAG, "--- calledFromForegroundUserOrSystemProcess ? " + + "calling uid = " + uid + " system uid = " + Process.SYSTEM_UID + + " calling userId = " + userId + ", foreground user id = " + + mSettings.getCurrentUserId()); + } + if (uid == Process.SYSTEM_UID || userId == mSettings.getCurrentUserId()) { + return true; + } else { + Slog.w(TAG, "--- IPC called from background users. Ignore. \n" + getStackTrace()); + return false; + } + } + + private boolean bindCurrentInputMethodService( + Intent service, ServiceConnection conn, int flags) { + if (service == null || conn == null) { + Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn); + return false; + } + return mContext.bindService(service, conn, flags, mSettings.getCurrentUserId()); + } + @Override public List<InputMethodInfo> getInputMethodList() { + // TODO: Make this work even for non-current users? + if (!calledFromValidUser()) { + return Collections.emptyList(); + } synchronized (mMethodMap) { return new ArrayList<InputMethodInfo>(mMethodList); } @@ -811,6 +926,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public List<InputMethodInfo> getEnabledInputMethodList() { + // TODO: Make this work even for non-current users? + if (!calledFromValidUser()) { + return Collections.emptyList(); + } synchronized (mMethodMap) { return mSettings.getEnabledInputMethodListLocked(); } @@ -820,7 +939,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked() { HashMap<InputMethodInfo, List<InputMethodSubtype>> enabledInputMethodAndSubtypes = new HashMap<InputMethodInfo, List<InputMethodSubtype>>(); - for (InputMethodInfo imi: getEnabledInputMethodList()) { + for (InputMethodInfo imi: mSettings.getEnabledInputMethodListLocked()) { enabledInputMethodAndSubtypes.put( imi, getEnabledInputMethodSubtypeListLocked(imi, true)); } @@ -843,6 +962,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi, boolean allowsImplicitlySelectedSubtypes) { + // TODO: Make this work even for non-current users? + if (!calledFromValidUser()) { + return Collections.emptyList(); + } synchronized (mMethodMap) { return getEnabledInputMethodSubtypeListLocked(imi, allowsImplicitlySelectedSubtypes); } @@ -851,6 +974,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void addClient(IInputMethodClient client, IInputContext inputContext, int uid, int pid) { + if (!calledFromValidUser()) { + return; + } synchronized (mMethodMap) { mClients.put(client.asBinder(), new ClientState(client, inputContext, uid, pid)); @@ -859,6 +985,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void removeClient(IInputMethodClient client) { + if (!calledFromValidUser()) { + return; + } synchronized (mMethodMap) { mClients.remove(client.asBinder()); } @@ -1060,7 +1189,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub com.android.internal.R.string.input_method_binding_label); mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0)); - if (mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE + if (bindCurrentInputMethodService(mCurIntent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_VISIBLE)) { mLastBindTime = SystemClock.uptimeMillis(); mHaveConnection = true; @@ -1084,6 +1213,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public InputBindResult startInput(IInputMethodClient client, IInputContext inputContext, EditorInfo attribute, int controlFlags) { + if (!calledFromValidUser()) { + return null; + } synchronized (mMethodMap) { final long ident = Binder.clearCallingIdentity(); try { @@ -1242,10 +1374,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) Slog.d(TAG, "show a small icon for the input method"); CharSequence contentDescription = null; try { - PackageManager packageManager = mContext.getPackageManager(); + // Use PackageManager to load label + final PackageManager packageManager = mContext.getPackageManager(); contentDescription = packageManager.getApplicationLabel( - packageManager.getApplicationInfo(packageName, 0)); - } catch (NameNotFoundException nnfe) { + mIPackageManager.getApplicationInfo(packageName, 0, + mSettings.getCurrentUserId())); + } catch (RemoteException e) { /* ignore */ } if (mStatusBar != null) { @@ -1309,13 +1443,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + // Caution! This method is called in this class. Handle multi-user carefully @SuppressWarnings("deprecation") @Override public void setImeWindowStatus(IBinder token, int vis, int backDisposition) { - int uid = Binder.getCallingUid(); - long ident = Binder.clearCallingIdentity(); + final long ident = Binder.clearCallingIdentity(); try { if (token == null || mCurToken != token) { + int uid = Binder.getCallingUid(); Slog.w(TAG, "Ignoring setImeWindowStatus of uid " + uid + " token: " + token); return; } @@ -1329,10 +1464,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final boolean iconVisibility = (vis & InputMethodService.IME_ACTIVE) != 0; final InputMethodInfo imi = mMethodMap.get(mCurMethodId); if (imi != null && iconVisibility && needsToShowImeSwitchOngoingNotification()) { + // Used to load label final PackageManager pm = mContext.getPackageManager(); final CharSequence title = mRes.getText( com.android.internal.R.string.select_input_method); final CharSequence imiLabel = imi.loadLabel(pm); + if (DEBUG) { + Slog.d(TAG, "--- imiLabel = " + imiLabel); + } final CharSequence summary = mCurrentSubtype != null ? TextUtils.concat(mCurrentSubtype.getDisplayName(mContext, imi.getPackageName(), imi.getServiceInfo().applicationInfo), @@ -1363,6 +1502,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) { + if (!calledFromValidUser()) { + return; + } synchronized (mMethodMap) { final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId); for (int i = 0; i < spans.length; ++i) { @@ -1377,6 +1519,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public boolean notifySuggestionPicked(SuggestionSpan span, String originalString, int index) { + if (!calledFromValidUser()) { + return false; + } synchronized (mMethodMap) { final InputMethodInfo targetImi = mSecureSuggestionSpans.get(span); // TODO: Do not send the intent if the process of the targetImi is already dead. @@ -1404,12 +1549,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // ENABLED_INPUT_METHODS is taking care of keeping them correctly in // sync, so we will never have a DEFAULT_INPUT_METHOD that is not // enabled. - String id = Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.DEFAULT_INPUT_METHOD); + String id = mSettings.getSelectedInputMethod(); // There is no input method selected, try to choose new applicable input method. if (TextUtils.isEmpty(id) && chooseNewDefaultIMELocked()) { - id = Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.DEFAULT_INPUT_METHOD); + id = mSettings.getSelectedInputMethod(); } if (!TextUtils.isEmpty(id)) { try { @@ -1446,7 +1589,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } else { // If subtype is null, try to find the most applicable one from // getCurrentInputMethodSubtype. - newSubtype = getCurrentInputMethodSubtype(); + newSubtype = getCurrentInputMethodSubtypeLocked(); } if (newSubtype == null || oldSubtype == null) { Slog.w(TAG, "Illegal subtype state: old subtype = " + oldSubtype @@ -1493,6 +1636,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public boolean showSoftInput(IInputMethodClient client, int flags, ResultReceiver resultReceiver) { + if (!calledFromValidUser()) { + return false; + } int uid = Binder.getCallingUid(); long ident = Binder.clearCallingIdentity(); try { @@ -1541,7 +1687,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub resultReceiver)); mInputShown = true; if (mHaveConnection && !mVisibleBound) { - mContext.bindService(mCurIntent, mVisibleConnection, Context.BIND_AUTO_CREATE); + bindCurrentInputMethodService( + mCurIntent, mVisibleConnection, Context.BIND_AUTO_CREATE); mVisibleBound = true; } res = true; @@ -1555,8 +1702,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub SystemClock.uptimeMillis()-mLastBindTime,1); Slog.w(TAG, "Force disconnect/connect to the IME in showCurrentInputLocked()"); mContext.unbindService(this); - mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE + bindCurrentInputMethodService(mCurIntent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_VISIBLE); + } else { + if (DEBUG) { + Slog.d(TAG, "Can't show input: connection = " + mHaveConnection + ", time = " + + ((mLastBindTime+TIME_TO_RECONNECT) - SystemClock.uptimeMillis())); + } } return res; @@ -1565,6 +1717,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public boolean hideSoftInput(IInputMethodClient client, int flags, ResultReceiver resultReceiver) { + if (!calledFromValidUser()) { + return false; + } int uid = Binder.getCallingUid(); long ident = Binder.clearCallingIdentity(); try { @@ -1630,6 +1785,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public InputBindResult windowGainedFocus(IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode, int windowFlags, EditorInfo attribute, IInputContext inputContext) { + if (!calledFromValidUser()) { + return null; + } InputBindResult res = null; long ident = Binder.clearCallingIdentity(); try { @@ -1770,6 +1928,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void showInputMethodPickerFromClient(IInputMethodClient client) { + if (!calledFromValidUser()) { + return; + } synchronized (mMethodMap) { if (mCurClient == null || client == null || mCurClient.client.asBinder() != client.asBinder()) { @@ -1785,11 +1946,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void setInputMethod(IBinder token, String id) { + if (!calledFromValidUser()) { + return; + } setInputMethodWithSubtypeId(token, id, NOT_A_SUBTYPE_ID); } @Override public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) { + if (!calledFromValidUser()) { + return; + } synchronized (mMethodMap) { if (subtype != null) { setInputMethodWithSubtypeId(token, id, getSubtypeIdFromHashCode( @@ -1803,6 +1970,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void showInputMethodAndSubtypeEnablerFromClient( IInputMethodClient client, String inputMethodId) { + if (!calledFromValidUser()) { + return; + } synchronized (mMethodMap) { if (mCurClient == null || client == null || mCurClient.client.asBinder() != client.asBinder()) { @@ -1815,6 +1985,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public boolean switchToLastInputMethod(IBinder token) { + if (!calledFromValidUser()) { + return false; + } synchronized (mMethodMap) { final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked(); final InputMethodInfo lastImi; @@ -1882,6 +2055,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) { + if (!calledFromValidUser()) { + return false; + } synchronized (mMethodMap) { final ImeSubtypeListItem nextSubtype = mImListManager.getNextInputMethod( onlyCurrentIme, mMethodMap.get(mCurMethodId), mCurrentSubtype); @@ -1895,6 +2071,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public InputMethodSubtype getLastInputMethodSubtype() { + if (!calledFromValidUser()) { + return null; + } synchronized (mMethodMap) { final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked(); // TODO: Handle the case of the last IME with no subtypes @@ -1917,14 +2096,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) { + if (!calledFromValidUser()) { + return; + } // By this IPC call, only a process which shares the same uid with the IME can add // additional input method subtypes to the IME. if (TextUtils.isEmpty(imiId) || subtypes == null || subtypes.length == 0) return; synchronized (mMethodMap) { final InputMethodInfo imi = mMethodMap.get(imiId); if (imi == null) return; - final PackageManager pm = mContext.getPackageManager(); - final String[] packageInfos = pm.getPackagesForUid(Binder.getCallingUid()); + final String[] packageInfos; + try { + packageInfos = mIPackageManager.getPackagesForUid(Binder.getCallingUid()); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to get package infos"); + return; + } if (packageInfos != null) { final int packageNum = packageInfos.length; for (int i = 0; i < packageNum; ++i) { @@ -1971,6 +2158,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void hideMySoftInput(IBinder token, int flags) { + if (!calledFromValidUser()) { + return; + } synchronized (mMethodMap) { if (token == null || mCurToken != token) { if (DEBUG) Slog.w(TAG, "Ignoring hideInputMethod of uid " @@ -1988,6 +2178,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void showMySoftInput(IBinder token, int flags) { + if (!calledFromValidUser()) { + return; + } synchronized (mMethodMap) { if (token == null || mCurToken != token) { Slog.w(TAG, "Ignoring showMySoftInput of uid " @@ -2224,19 +2417,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub void buildInputMethodListLocked(ArrayList<InputMethodInfo> list, HashMap<String, InputMethodInfo> 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<ResolveInfo> services = pm.queryIntentServices( + final List<ResolveInfo> services = pm.queryIntentServicesAsUser( new Intent(InputMethod.SERVICE_INTERFACE), - PackageManager.GET_META_DATA); + PackageManager.GET_META_DATA, mSettings.getCurrentUserId()); final HashMap<String, List<InputMethodSubtype>> 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<InputMethodSubtype> 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<InputMethodSubtype> 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<InputMethodSubtype> 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<InputMethodInfo> mMethodList; private String mEnabledInputMethodsStrCache; + private int mCurrentUserId; private static void buildEnabledInputMethodsSettingString( StringBuilder builder, Pair<String, ArrayList<String>> pair) { @@ -3208,13 +3408,24 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public InputMethodSettings( Resources res, ContentResolver resolver, - HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList) { + HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> 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<InputMethodInfo> 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<String, String> 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) { |
