summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server/InputMethodManagerService.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android/server/InputMethodManagerService.java')
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java677
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) {