diff options
Diffstat (limited to 'services/java/com/android/server/InputMethodManagerService.java')
-rw-r--r-- | services/java/com/android/server/InputMethodManagerService.java | 677 |
1 files changed, 496 insertions, 181 deletions
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index fdb278d..c9ff595 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -16,8 +16,8 @@ package com.android.server; import com.android.internal.content.PackageMonitor; -import com.android.internal.os.AtomicFile; import com.android.internal.os.HandlerCaller; +import com.android.internal.os.SomeArgs; import com.android.internal.util.FastXmlSerializer; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethod; @@ -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,17 +66,21 @@ 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; import android.text.TextUtils; import android.text.style.SuggestionSpan; +import android.util.AtomicFile; import android.util.EventLog; import android.util.LruCache; import android.util.Pair; @@ -164,8 +171,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final SettingsObserver mSettingsObserver; final IWindowManager mIWindowManager; final HandlerCaller mCaller; - private final InputMethodFileManager mFileManager; - private final InputMethodAndSubtypeListManager mImListManager; + private InputMethodFileManager mFileManager; + private InputMethodAndSubtypeListManager mImListManager; private final HardKeyboardListener mHardKeyboardListener; private final WindowManagerService mWindowManagerService; @@ -377,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) { @@ -397,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++) { @@ -452,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++) { @@ -488,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! @@ -564,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); @@ -595,28 +625,48 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mShowOngoingImeSwitcherForPhones = false; - synchronized (mMethodMap) { - mFileManager = new InputMethodFileManager(mMethodMap); - } - 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) { + synchronized(mMethodMap) { + switchUserLocked(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); + mFileManager = new InputMethodFileManager(mMethodMap, userId); + mImListManager = new InputMethodAndSubtypeListManager(context, this); // 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); @@ -645,24 +695,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))) { @@ -687,6 +719,59 @@ 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, false); + } + if (DEBUG) { + Slog.i(TAG, "Locale has been changed to " + newLocale); + } + // InputMethodAndSubtypeListManager should be reset when the locale is changed. + mImListManager = new InputMethodAndSubtypeListManager(mContext, this); + 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); + } + } else { + // If the locale is changed, needs to reset the default ime + 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 switchUserLocked(int newUserId) { + mSettings.setCurrentUserId(newUserId); + // InputMethodFileManager should be reset when the user is changed + mFileManager = new InputMethodFileManager(mMethodMap, newUserId); + resetAllInternalStateLocked(false); + } + private boolean isValidSystemDefaultIme(InputMethodInfo imi, Context context) { if (!mSystemReady) { return false; @@ -747,10 +832,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; @@ -801,8 +889,56 @@ 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() + ", calling pid = " + Binder.getCallingPid()); + } + if (uid == Process.SYSTEM_UID || userId == mSettings.getCurrentUserId()) { + return true; + } + + // Caveat: A process which has INTERACT_ACROSS_USERS_FULL gets results for the + // foreground user, not for the user of that process. Accordingly InputMethodManagerService + // must not manage background users' states in any functions. + // Note that privacy-sensitive IPCs, such as setInputMethod, are still securely guarded + // by a token. + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + == PackageManager.PERMISSION_GRANTED) { + if (DEBUG) { + Slog.d(TAG, "--- Access granted because the calling process has " + + "the INTERACT_ACROSS_USERS_FULL permission"); + } + return true; + } + 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); } @@ -810,6 +946,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(); } @@ -819,7 +959,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)); } @@ -842,6 +982,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); } @@ -850,6 +994,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)); @@ -858,6 +1005,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void removeClient(IInputMethodClient client) { + if (!calledFromValidUser()) { + return; + } synchronized (mMethodMap) { mClients.remove(client.asBinder()); } @@ -1051,7 +1201,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub throw new IllegalArgumentException("Unknown id: " + mCurMethodId); } - unbindCurrentMethodLocked(false); + unbindCurrentMethodLocked(false, true); mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE); mCurIntent.setComponent(info.getComponent()); @@ -1059,7 +1209,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; @@ -1083,6 +1233,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 { @@ -1104,7 +1257,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurMethod = IInputMethod.Stub.asInterface(service); if (mCurToken == null) { Slog.w(TAG, "Service connected without a token!"); - unbindCurrentMethodLocked(false); + unbindCurrentMethodLocked(false, false); return; } if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken); @@ -1139,7 +1292,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - void unbindCurrentMethodLocked(boolean reportToClient) { + void unbindCurrentMethodLocked(boolean reportToClient, boolean savePosition) { if (mVisibleBound) { mContext.unbindService(mVisibleConnection); mVisibleBound = false; @@ -1153,7 +1306,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (mCurToken != null) { try { if (DEBUG) Slog.v(TAG, "Removing window token: " + mCurToken); - if ((mImeWindowVis & InputMethodService.IME_ACTIVE) != 0) { + if ((mImeWindowVis & InputMethodService.IME_ACTIVE) != 0 && savePosition) { // The current IME is shown. Hence an IME switch (transition) is happening. mWindowManagerService.saveLastInputMethodWindowForTransition(); } @@ -1241,10 +1394,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) { @@ -1308,13 +1463,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; } @@ -1328,6 +1484,7 @@ 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); @@ -1342,15 +1499,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mImeSwitcherNotification.setLatestEventInfo( mContext, title, summary, mImeSwitchPendingIntent); if (mNotificationManager != null) { - mNotificationManager.notify( + if (DEBUG) { + Slog.d(TAG, "--- show notification: label = " + imiLabel + + ", summary = " + summary); + } + mNotificationManager.notifyAsUser(null, com.android.internal.R.string.select_input_method, - mImeSwitcherNotification); + mImeSwitcherNotification, UserHandle.ALL); mNotificationShown = true; } } else { if (mNotificationShown && mNotificationManager != null) { - mNotificationManager.cancel( - com.android.internal.R.string.select_input_method); + if (DEBUG) { + Slog.d(TAG, "--- hide notification"); + } + mNotificationManager.cancelAsUser(null, + com.android.internal.R.string.select_input_method, UserHandle.ALL); mNotificationShown = false; } } @@ -1362,6 +1526,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) { @@ -1376,6 +1543,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. @@ -1391,7 +1561,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_BEFORE, originalString); intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_AFTER, suggestions[index]); intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_HASHCODE, span.hashCode()); - mContext.sendBroadcast(intent); + final long ident = Binder.clearCallingIdentity(); + try { + mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT); + } finally { + Binder.restoreCallingIdentity(ident); + } return true; } } @@ -1403,12 +1578,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 { @@ -1416,13 +1589,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } catch (IllegalArgumentException e) { Slog.w(TAG, "Unknown input method from prefs: " + id, e); mCurMethodId = null; - unbindCurrentMethodLocked(true); + unbindCurrentMethodLocked(true, false); } mShortcutInputMethodsAndSubtypes.clear(); } else { // There is no longer an input method set, so stop any current one. mCurMethodId = null; - unbindCurrentMethodLocked(true); + unbindCurrentMethodLocked(true, false); } } @@ -1445,7 +1618,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 @@ -1481,7 +1654,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.putExtra("input_method_id", id); - mContext.sendBroadcast(intent); + mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT); } unbindCurrentClientLocked(); } finally { @@ -1492,6 +1665,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 { @@ -1540,7 +1716,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; @@ -1554,8 +1731,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; @@ -1564,6 +1746,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 { @@ -1629,6 +1814,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 { @@ -1660,7 +1848,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (mCurFocusedWindow == windowToken) { Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client - + " attribute=" + attribute); + + " attribute=" + attribute + ", token = " + windowToken); if (attribute != null) { return startInputUncheckedLocked(cs, inputContext, attribute, controlFlags); @@ -1769,6 +1957,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()) { @@ -1784,11 +1975,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( @@ -1802,6 +1999,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()) { @@ -1814,6 +2014,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; @@ -1881,6 +2084,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); @@ -1894,6 +2100,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 @@ -1916,14 +2125,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) { @@ -1970,6 +2187,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 " @@ -1987,6 +2207,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 " @@ -2024,7 +2247,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public boolean handleMessage(Message msg) { - HandlerCaller.SomeArgs args; + SomeArgs args; switch (msg.what) { case MSG_SHOW_IM_PICKER: showInputMethodMenu(); @@ -2035,8 +2258,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return true; case MSG_SHOW_IM_SUBTYPE_ENABLER: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; showInputMethodAndSubtypeEnabler((String)args.arg1); + args.recycle(); return true; case MSG_SHOW_IM_CONFIG: @@ -2053,48 +2277,53 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } return true; case MSG_BIND_INPUT: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; try { ((IInputMethod)args.arg1).bindInput((InputBinding)args.arg2); } catch (RemoteException e) { } + args.recycle(); return true; case MSG_SHOW_SOFT_INPUT: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; try { ((IInputMethod)args.arg1).showSoftInput(msg.arg1, (ResultReceiver)args.arg2); } catch (RemoteException e) { } + args.recycle(); return true; case MSG_HIDE_SOFT_INPUT: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; try { ((IInputMethod)args.arg1).hideSoftInput(0, (ResultReceiver)args.arg2); } catch (RemoteException e) { } + args.recycle(); return true; case MSG_ATTACH_TOKEN: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; try { if (DEBUG) Slog.v(TAG, "Sending attach of token: " + args.arg2); ((IInputMethod)args.arg1).attachToken((IBinder)args.arg2); } catch (RemoteException e) { } + args.recycle(); return true; case MSG_CREATE_SESSION: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; try { ((IInputMethod)args.arg1).createSession( (IInputMethodCallback)args.arg2); } catch (RemoteException e) { } + args.recycle(); return true; // --------------------------------------------------------- case MSG_START_INPUT: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; try { SessionState session = (SessionState)args.arg1; setEnabledSessionInMainThread(session); @@ -2102,9 +2331,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub (EditorInfo)args.arg3); } catch (RemoteException e) { } + args.recycle(); return true; case MSG_RESTART_INPUT: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; try { SessionState session = (SessionState)args.arg1; setEnabledSessionInMainThread(session); @@ -2112,6 +2342,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub (EditorInfo)args.arg3); } catch (RemoteException e) { } + args.recycle(); return true; // --------------------------------------------------------- @@ -2124,13 +2355,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } return true; case MSG_BIND_METHOD: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; try { ((IInputMethodClient)args.arg1).onBindMethod( (InputBindResult)args.arg2); } catch (RemoteException e) { Slog.w(TAG, "Client died receiving input method " + args.arg2); } + args.recycle(); return true; case MSG_SET_ACTIVE: try { @@ -2214,19 +2446,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(); @@ -2269,8 +2504,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."); @@ -2302,7 +2536,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (!TextUtils.isEmpty(inputMethodId)) { intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, inputMethodId); } - mContext.startActivity(intent); + mContext.startActivityAsUser(intent, null, UserHandle.CURRENT); } private void showConfigureInputMethods() { @@ -2310,7 +2544,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_CLEAR_TOP); - mContext.startActivity(intent); + mContext.startActivityAsUser(intent, null, UserHandle.CURRENT); } private boolean isScreenLocked() { @@ -2321,11 +2555,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); @@ -2343,7 +2575,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 = @@ -2446,6 +2678,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mSwitchingDialog.setCanceledOnTouchOutside(true); mSwitchingDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG); + mSwitchingDialog.getWindow().getAttributes().privateFlags |= + WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; mSwitchingDialog.getWindow().getAttributes().setTitle("Select input method"); mSwitchingDialog.show(); } @@ -2572,6 +2806,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) @@ -2616,8 +2854,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(""); @@ -2664,7 +2901,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(); } } @@ -2706,14 +2943,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) { @@ -2876,7 +3107,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( @@ -2945,49 +3176,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, @@ -3032,6 +3267,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); @@ -3047,6 +3286,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; @@ -3183,6 +3423,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) { @@ -3198,13 +3439,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()); @@ -3353,15 +3605,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; } @@ -3416,8 +3673,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() { @@ -3536,23 +3793,61 @@ 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; } } + // TODO: Cache the state for each user and reset when the cached user is removed. private static class InputMethodFileManager { private static final String SYSTEM_PATH = "system"; private static final String INPUT_METHOD_PATH = "inputmethod"; @@ -3569,14 +3864,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private static final String ATTR_IS_AUXILIARY = "isAuxiliary"; private final AtomicFile mAdditionalInputMethodSubtypeFile; private final HashMap<String, InputMethodInfo> mMethodMap; - private final HashMap<String, List<InputMethodSubtype>> mSubtypesMap = + private final HashMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap = new HashMap<String, List<InputMethodSubtype>>(); - public InputMethodFileManager(HashMap<String, InputMethodInfo> methodMap) { + public InputMethodFileManager(HashMap<String, InputMethodInfo> methodMap, int userId) { if (methodMap == null) { throw new NullPointerException("methodMap is null"); } mMethodMap = methodMap; - final File systemDir = new File(Environment.getDataDirectory(), SYSTEM_PATH); + final File systemDir = userId == UserHandle.USER_OWNER + ? new File(Environment.getDataDirectory(), SYSTEM_PATH) + : Environment.getUserSystemDirectory(userId); final File inputMethodDir = new File(systemDir, INPUT_METHOD_PATH); if (!inputMethodDir.mkdirs()) { Slog.w(TAG, "Couldn't create dir.: " + inputMethodDir.getAbsolutePath()); @@ -3585,18 +3882,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mAdditionalInputMethodSubtypeFile = new AtomicFile(subtypeFile); if (!subtypeFile.exists()) { // If "subtypes.xml" doesn't exist, create a blank file. - writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile, - methodMap); + writeAdditionalInputMethodSubtypes( + mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, methodMap); } else { - readAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile); + readAdditionalInputMethodSubtypes( + mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile); } } private void deleteAllInputMethodSubtypes(String imiId) { synchronized (mMethodMap) { - mSubtypesMap.remove(imiId); - writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile, - mMethodMap); + mAdditionalSubtypesMap.remove(imiId); + writeAdditionalInputMethodSubtypes( + mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap); } } @@ -3609,17 +3907,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final InputMethodSubtype subtype = additionalSubtypes[i]; if (!subtypes.contains(subtype)) { subtypes.add(subtype); + } else { + Slog.w(TAG, "Duplicated subtype definition found: " + + subtype.getLocale() + ", " + subtype.getMode()); } } - mSubtypesMap.put(imi.getId(), subtypes); - writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile, - mMethodMap); + mAdditionalSubtypesMap.put(imi.getId(), subtypes); + writeAdditionalInputMethodSubtypes( + mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap); } } public HashMap<String, List<InputMethodSubtype>> getAllAdditionalInputMethodSubtypes() { synchronized (mMethodMap) { - return mSubtypesMap; + return mAdditionalSubtypesMap; } } @@ -3748,6 +4049,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) { |