am 4e1ab15b: Enable multi-user function for IMF

* commit '4e1ab15b305aac26ad8819fc3b2951e20985944d':
  Enable multi-user function for IMF
This commit is contained in:
Satoshi Kataoka
2012-09-26 03:25:07 -07:00
committed by Android Git Automerger

View File

@@ -34,7 +34,9 @@ import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer; import org.xmlpull.v1.XmlSerializer;
import android.app.ActivityManagerNative; import android.app.ActivityManagerNative;
import android.app.AppGlobals;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.IUserSwitchObserver;
import android.app.KeyguardManager; import android.app.KeyguardManager;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationManager; import android.app.NotificationManager;
@@ -49,6 +51,7 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
@@ -63,12 +66,15 @@ import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.IInterface; import android.os.IInterface;
import android.os.IRemoteCallback;
import android.os.Message; import android.os.Message;
import android.os.Process;
import android.os.Parcel; import android.os.Parcel;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ResultReceiver; import android.os.ResultReceiver;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings; import android.provider.Settings;
import android.provider.Settings.Secure; import android.provider.Settings.Secure;
import android.provider.Settings.SettingNotFoundException; import android.provider.Settings.SettingNotFoundException;
@@ -378,6 +384,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private InputMethodInfo[] mIms; private InputMethodInfo[] mIms;
private int[] mSubtypeIds; private int[] mSubtypeIds;
private Locale mLastSystemLocale; private Locale mLastSystemLocale;
private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
private final IPackageManager mIPackageManager;
class SettingsObserver extends ContentObserver { class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) { SettingsObserver(Handler handler) {
@@ -398,37 +406,55 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
} }
} }
class ScreenOnOffReceiver extends android.content.BroadcastReceiver { class ImmsBroadcastReceiver extends android.content.BroadcastReceiver {
@Override private void updateActive() {
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
mScreenOn = true;
refreshImeWindowVisibilityLocked();
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
mScreenOn = false;
setImeWindowVisibilityStatusHiddenLocked();
} else if (intent.getAction().equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
hideInputMethodMenu();
return;
} else {
Slog.w(TAG, "Unexpected intent " + intent);
}
// Inform the current client of the change in active status // Inform the current client of the change in active status
if (mCurClient != null && mCurClient.client != null) { if (mCurClient != null && mCurClient.client != null) {
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
MSG_SET_ACTIVE, mScreenOn ? 1 : 0, mCurClient)); MSG_SET_ACTIVE, mScreenOn ? 1 : 0, mCurClient));
} }
} }
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (Intent.ACTION_SCREEN_ON.equals(action)) {
mScreenOn = true;
refreshImeWindowVisibilityLocked();
updateActive();
return;
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
mScreenOn = false;
setImeWindowVisibilityStatusHiddenLocked();
updateActive();
return;
} else if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
hideInputMethodMenu();
// No need to updateActive
return;
} else {
Slog.w(TAG, "Unexpected intent " + intent);
}
}
} }
class MyPackageMonitor extends PackageMonitor { 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 @Override
public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
if (!isChangingPackagesOfCurrentUser()) {
return false;
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
String curInputMethodId = Settings.Secure.getString(mContext String curInputMethodId = mSettings.getSelectedInputMethod();
.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
final int N = mMethodList.size(); final int N = mMethodList.size();
if (curInputMethodId != null) { if (curInputMethodId != null) {
for (int i=0; i<N; i++) { for (int i=0; i<N; i++) {
@@ -453,10 +479,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public void onSomePackagesChanged() { public void onSomePackagesChanged() {
if (!isChangingPackagesOfCurrentUser()) {
return;
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
InputMethodInfo curIm = null; InputMethodInfo curIm = null;
String curInputMethodId = Settings.Secure.getString(mContext String curInputMethodId = mSettings.getSelectedInputMethod();
.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
final int N = mMethodList.size(); final int N = mMethodList.size();
if (curInputMethodId != null) { if (curInputMethodId != null) {
for (int i=0; i<N; i++) { for (int i=0; i<N; i++) {
@@ -489,9 +517,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|| change == PACKAGE_PERMANENT_CHANGE) { || change == PACKAGE_PERMANENT_CHANGE) {
ServiceInfo si = null; ServiceInfo si = null;
try { try {
si = mContext.getPackageManager().getServiceInfo( si = mIPackageManager.getServiceInfo(
curIm.getComponent(), 0); curIm.getComponent(), 0, mSettings.getCurrentUserId());
} catch (PackageManager.NameNotFoundException ex) { } catch (RemoteException ex) {
} }
if (si == null) { if (si == null) {
// Uh oh, current input method is no longer around! // Uh oh, current input method is no longer around!
@@ -565,6 +593,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
} }
public InputMethodManagerService(Context context, WindowManagerService windowManager) { public InputMethodManagerService(Context context, WindowManagerService windowManager) {
mIPackageManager = AppGlobals.getPackageManager();
mContext = context; mContext = context;
mRes = context.getResources(); mRes = context.getResources();
mHandler = new Handler(this); mHandler = new Handler(this);
@@ -601,23 +630,44 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
} }
mImListManager = new InputMethodAndSubtypeListManager(context, this); mImListManager = new InputMethodAndSubtypeListManager(context, this);
(new MyPackageMonitor()).register(mContext, null, true); final IntentFilter broadcastFilter = new IntentFilter();
broadcastFilter.addAction(Intent.ACTION_SCREEN_ON);
IntentFilter screenOnOffFilt = new IntentFilter(); broadcastFilter.addAction(Intent.ACTION_SCREEN_OFF);
screenOnOffFilt.addAction(Intent.ACTION_SCREEN_ON); broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
screenOnOffFilt.addAction(Intent.ACTION_SCREEN_OFF); mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter);
screenOnOffFilt.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mContext.registerReceiver(new ScreenOnOffReceiver(), screenOnOffFilt);
mNotificationShown = false; mNotificationShown = false;
int userId = 0;
try {
ActivityManagerNative.getDefault().registerUserSwitchObserver(
new IUserSwitchObserver.Stub() {
@Override
public void onUserSwitching(int newUserId, IRemoteCallback reply) {
switchUser(newUserId);
if (reply != null) {
try {
reply.sendResult(null);
} catch (RemoteException e) {
}
}
}
@Override
public void onUserSwitchComplete(int newUserId) throws RemoteException {
}
});
userId = ActivityManagerNative.getDefault().getCurrentUser().id;
} catch (RemoteException e) {
Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);
}
mMyPackageMonitor.register(mContext, null, true);
// mSettings should be created before buildInputMethodListLocked // mSettings should be created before buildInputMethodListLocked
mSettings = new InputMethodSettings( mSettings = new InputMethodSettings(
mRes, context.getContentResolver(), mMethodMap, mMethodList); mRes, context.getContentResolver(), mMethodMap, mMethodList, userId);
// Just checking if defaultImiId is empty or not // Just checking if defaultImiId is empty or not
final String defaultImiId = Settings.Secure.getString( final String defaultImiId = mSettings.getSelectedInputMethod();
mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
mImeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId); mImeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
buildInputMethodListLocked(mMethodList, mMethodMap); buildInputMethodListLocked(mMethodList, mMethodMap);
@@ -646,24 +696,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}, filter); }, 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) { private void resetDefaultImeLocked(Context context) {
// Do not reset the default (current) IME when it is a 3rd-party IME // Do not reset the default (current) IME when it is a 3rd-party IME
if (mCurMethodId != null && !isSystemIme(mMethodMap.get(mCurMethodId))) { if (mCurMethodId != null && !isSystemIme(mMethodMap.get(mCurMethodId))) {
@@ -688,6 +720,52 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
} }
} }
private void resetAllInternalStateLocked(boolean updateOnlyWhenLocaleChanged) {
if (!mSystemReady) {
// not system ready
return;
}
final Locale newLocale = mRes.getConfiguration().locale;
if (!updateOnlyWhenLocaleChanged
|| (newLocale != null && !newLocale.equals(mLastSystemLocale))) {
if (!updateOnlyWhenLocaleChanged) {
hideCurrentInputLocked(0, null);
mCurMethodId = null;
unbindCurrentMethodLocked(true);
}
if (DEBUG) {
Slog.i(TAG, "Locale has been changed to " + newLocale);
}
buildInputMethodListLocked(mMethodList, mMethodMap);
if (!updateOnlyWhenLocaleChanged) {
final String selectedImiId = mSettings.getSelectedInputMethod();
if (TextUtils.isEmpty(selectedImiId)) {
// This is the first time of the user switch and
// set the current ime to the proper one.
resetDefaultImeLocked(mContext);
}
}
updateFromSettingsLocked();
mLastSystemLocale = newLocale;
if (!updateOnlyWhenLocaleChanged) {
try {
startInputInnerLocked();
} catch (RuntimeException e) {
Slog.w(TAG, "Unexpected exception", e);
}
}
}
}
private void checkCurrentLocaleChangedLocked() {
resetAllInternalStateLocked(true);
}
private void switchUser(int newUserId) {
mSettings.setCurrentUserId(newUserId);
resetAllInternalStateLocked(false);
}
private boolean isValidSystemDefaultIme(InputMethodInfo imi, Context context) { private boolean isValidSystemDefaultIme(InputMethodInfo imi, Context context) {
if (!mSystemReady) { if (!mSystemReady) {
return false; return false;
@@ -748,10 +826,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public void systemReady(StatusBarManagerService statusBar) { public void systemReady(StatusBarManagerService statusBar) {
synchronized (mMethodMap) { synchronized (mMethodMap) {
if (DEBUG) {
Slog.d(TAG, "--- systemReady");
}
if (!mSystemReady) { if (!mSystemReady) {
mSystemReady = true; mSystemReady = true;
mKeyguardManager = (KeyguardManager) mKeyguardManager =
mContext.getSystemService(Context.KEYGUARD_SERVICE); (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
mNotificationManager = (NotificationManager) mNotificationManager = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE); mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mStatusBar = statusBar; mStatusBar = statusBar;
@@ -802,8 +883,42 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
setImeWindowStatus(mCurToken, mImeWindowVis, mBackDisposition); setImeWindowStatus(mCurToken, mImeWindowVis, mBackDisposition);
} }
// ---------------------------------------------------------------------------------------
// Check whether or not this is a valid IPC. Assumes an IPC is valid when either
// 1) it comes from the system process
// 2) the calling process' user id is identical to the current user id IMMS thinks.
private boolean calledFromValidUser() {
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(uid);
if (DEBUG) {
Slog.d(TAG, "--- calledFromForegroundUserOrSystemProcess ? "
+ "calling uid = " + uid + " system uid = " + Process.SYSTEM_UID
+ " calling userId = " + userId + ", foreground user id = "
+ mSettings.getCurrentUserId());
}
if (uid == Process.SYSTEM_UID || userId == mSettings.getCurrentUserId()) {
return true;
} else {
Slog.w(TAG, "--- IPC called from background users. Ignore. \n" + getStackTrace());
return false;
}
}
private boolean bindCurrentInputMethodService(
Intent service, ServiceConnection conn, int flags) {
if (service == null || conn == null) {
Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn);
return false;
}
return mContext.bindService(service, conn, flags, mSettings.getCurrentUserId());
}
@Override @Override
public List<InputMethodInfo> getInputMethodList() { public List<InputMethodInfo> getInputMethodList() {
// TODO: Make this work even for non-current users?
if (!calledFromValidUser()) {
return Collections.emptyList();
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
return new ArrayList<InputMethodInfo>(mMethodList); return new ArrayList<InputMethodInfo>(mMethodList);
} }
@@ -811,6 +926,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public List<InputMethodInfo> getEnabledInputMethodList() { public List<InputMethodInfo> getEnabledInputMethodList() {
// TODO: Make this work even for non-current users?
if (!calledFromValidUser()) {
return Collections.emptyList();
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
return mSettings.getEnabledInputMethodListLocked(); return mSettings.getEnabledInputMethodListLocked();
} }
@@ -820,7 +939,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked() { getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked() {
HashMap<InputMethodInfo, List<InputMethodSubtype>> enabledInputMethodAndSubtypes = HashMap<InputMethodInfo, List<InputMethodSubtype>> enabledInputMethodAndSubtypes =
new HashMap<InputMethodInfo, List<InputMethodSubtype>>(); new HashMap<InputMethodInfo, List<InputMethodSubtype>>();
for (InputMethodInfo imi: getEnabledInputMethodList()) { for (InputMethodInfo imi: mSettings.getEnabledInputMethodListLocked()) {
enabledInputMethodAndSubtypes.put( enabledInputMethodAndSubtypes.put(
imi, getEnabledInputMethodSubtypeListLocked(imi, true)); imi, getEnabledInputMethodSubtypeListLocked(imi, true));
} }
@@ -843,6 +962,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi, public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi,
boolean allowsImplicitlySelectedSubtypes) { boolean allowsImplicitlySelectedSubtypes) {
// TODO: Make this work even for non-current users?
if (!calledFromValidUser()) {
return Collections.emptyList();
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
return getEnabledInputMethodSubtypeListLocked(imi, allowsImplicitlySelectedSubtypes); return getEnabledInputMethodSubtypeListLocked(imi, allowsImplicitlySelectedSubtypes);
} }
@@ -851,6 +974,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public void addClient(IInputMethodClient client, public void addClient(IInputMethodClient client,
IInputContext inputContext, int uid, int pid) { IInputContext inputContext, int uid, int pid) {
if (!calledFromValidUser()) {
return;
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
mClients.put(client.asBinder(), new ClientState(client, mClients.put(client.asBinder(), new ClientState(client,
inputContext, uid, pid)); inputContext, uid, pid));
@@ -859,6 +985,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public void removeClient(IInputMethodClient client) { public void removeClient(IInputMethodClient client) {
if (!calledFromValidUser()) {
return;
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
mClients.remove(client.asBinder()); mClients.remove(client.asBinder());
} }
@@ -1060,7 +1189,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
com.android.internal.R.string.input_method_binding_label); com.android.internal.R.string.input_method_binding_label);
mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0)); 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)) { | Context.BIND_NOT_VISIBLE)) {
mLastBindTime = SystemClock.uptimeMillis(); mLastBindTime = SystemClock.uptimeMillis();
mHaveConnection = true; mHaveConnection = true;
@@ -1084,6 +1213,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public InputBindResult startInput(IInputMethodClient client, public InputBindResult startInput(IInputMethodClient client,
IInputContext inputContext, EditorInfo attribute, int controlFlags) { IInputContext inputContext, EditorInfo attribute, int controlFlags) {
if (!calledFromValidUser()) {
return null;
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
final long ident = Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();
try { try {
@@ -1242,10 +1374,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (DEBUG) Slog.d(TAG, "show a small icon for the input method"); if (DEBUG) Slog.d(TAG, "show a small icon for the input method");
CharSequence contentDescription = null; CharSequence contentDescription = null;
try { try {
PackageManager packageManager = mContext.getPackageManager(); // Use PackageManager to load label
final PackageManager packageManager = mContext.getPackageManager();
contentDescription = packageManager.getApplicationLabel( contentDescription = packageManager.getApplicationLabel(
packageManager.getApplicationInfo(packageName, 0)); mIPackageManager.getApplicationInfo(packageName, 0,
} catch (NameNotFoundException nnfe) { mSettings.getCurrentUserId()));
} catch (RemoteException e) {
/* ignore */ /* ignore */
} }
if (mStatusBar != null) { if (mStatusBar != null) {
@@ -1309,13 +1443,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
} }
} }
// Caution! This method is called in this class. Handle multi-user carefully
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public void setImeWindowStatus(IBinder token, int vis, int backDisposition) { public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity();
long ident = Binder.clearCallingIdentity();
try { try {
if (token == null || mCurToken != token) { if (token == null || mCurToken != token) {
int uid = Binder.getCallingUid();
Slog.w(TAG, "Ignoring setImeWindowStatus of uid " + uid + " token: " + token); Slog.w(TAG, "Ignoring setImeWindowStatus of uid " + uid + " token: " + token);
return; return;
} }
@@ -1329,10 +1464,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final boolean iconVisibility = (vis & InputMethodService.IME_ACTIVE) != 0; final boolean iconVisibility = (vis & InputMethodService.IME_ACTIVE) != 0;
final InputMethodInfo imi = mMethodMap.get(mCurMethodId); final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
if (imi != null && iconVisibility && needsToShowImeSwitchOngoingNotification()) { if (imi != null && iconVisibility && needsToShowImeSwitchOngoingNotification()) {
// Used to load label
final PackageManager pm = mContext.getPackageManager(); final PackageManager pm = mContext.getPackageManager();
final CharSequence title = mRes.getText( final CharSequence title = mRes.getText(
com.android.internal.R.string.select_input_method); com.android.internal.R.string.select_input_method);
final CharSequence imiLabel = imi.loadLabel(pm); final CharSequence imiLabel = imi.loadLabel(pm);
if (DEBUG) {
Slog.d(TAG, "--- imiLabel = " + imiLabel);
}
final CharSequence summary = mCurrentSubtype != null final CharSequence summary = mCurrentSubtype != null
? TextUtils.concat(mCurrentSubtype.getDisplayName(mContext, ? TextUtils.concat(mCurrentSubtype.getDisplayName(mContext,
imi.getPackageName(), imi.getServiceInfo().applicationInfo), imi.getPackageName(), imi.getServiceInfo().applicationInfo),
@@ -1363,6 +1502,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) { public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) {
if (!calledFromValidUser()) {
return;
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId); final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
for (int i = 0; i < spans.length; ++i) { for (int i = 0; i < spans.length; ++i) {
@@ -1377,6 +1519,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public boolean notifySuggestionPicked(SuggestionSpan span, String originalString, int index) { public boolean notifySuggestionPicked(SuggestionSpan span, String originalString, int index) {
if (!calledFromValidUser()) {
return false;
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
final InputMethodInfo targetImi = mSecureSuggestionSpans.get(span); final InputMethodInfo targetImi = mSecureSuggestionSpans.get(span);
// TODO: Do not send the intent if the process of the targetImi is already dead. // TODO: Do not send the intent if the process of the targetImi is already dead.
@@ -1404,12 +1549,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// ENABLED_INPUT_METHODS is taking care of keeping them correctly in // ENABLED_INPUT_METHODS is taking care of keeping them correctly in
// sync, so we will never have a DEFAULT_INPUT_METHOD that is not // sync, so we will never have a DEFAULT_INPUT_METHOD that is not
// enabled. // enabled.
String id = Settings.Secure.getString(mContext.getContentResolver(), String id = mSettings.getSelectedInputMethod();
Settings.Secure.DEFAULT_INPUT_METHOD);
// There is no input method selected, try to choose new applicable input method. // There is no input method selected, try to choose new applicable input method.
if (TextUtils.isEmpty(id) && chooseNewDefaultIMELocked()) { if (TextUtils.isEmpty(id) && chooseNewDefaultIMELocked()) {
id = Settings.Secure.getString(mContext.getContentResolver(), id = mSettings.getSelectedInputMethod();
Settings.Secure.DEFAULT_INPUT_METHOD);
} }
if (!TextUtils.isEmpty(id)) { if (!TextUtils.isEmpty(id)) {
try { try {
@@ -1446,7 +1589,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
} else { } else {
// If subtype is null, try to find the most applicable one from // If subtype is null, try to find the most applicable one from
// getCurrentInputMethodSubtype. // getCurrentInputMethodSubtype.
newSubtype = getCurrentInputMethodSubtype(); newSubtype = getCurrentInputMethodSubtypeLocked();
} }
if (newSubtype == null || oldSubtype == null) { if (newSubtype == null || oldSubtype == null) {
Slog.w(TAG, "Illegal subtype state: old subtype = " + oldSubtype Slog.w(TAG, "Illegal subtype state: old subtype = " + oldSubtype
@@ -1493,6 +1636,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public boolean showSoftInput(IInputMethodClient client, int flags, public boolean showSoftInput(IInputMethodClient client, int flags,
ResultReceiver resultReceiver) { ResultReceiver resultReceiver) {
if (!calledFromValidUser()) {
return false;
}
int uid = Binder.getCallingUid(); int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity(); long ident = Binder.clearCallingIdentity();
try { try {
@@ -1541,7 +1687,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
resultReceiver)); resultReceiver));
mInputShown = true; mInputShown = true;
if (mHaveConnection && !mVisibleBound) { if (mHaveConnection && !mVisibleBound) {
mContext.bindService(mCurIntent, mVisibleConnection, Context.BIND_AUTO_CREATE); bindCurrentInputMethodService(
mCurIntent, mVisibleConnection, Context.BIND_AUTO_CREATE);
mVisibleBound = true; mVisibleBound = true;
} }
res = true; res = true;
@@ -1555,8 +1702,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
SystemClock.uptimeMillis()-mLastBindTime,1); SystemClock.uptimeMillis()-mLastBindTime,1);
Slog.w(TAG, "Force disconnect/connect to the IME in showCurrentInputLocked()"); Slog.w(TAG, "Force disconnect/connect to the IME in showCurrentInputLocked()");
mContext.unbindService(this); mContext.unbindService(this);
mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE bindCurrentInputMethodService(mCurIntent, this, Context.BIND_AUTO_CREATE
| Context.BIND_NOT_VISIBLE); | 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; return res;
@@ -1565,6 +1717,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public boolean hideSoftInput(IInputMethodClient client, int flags, public boolean hideSoftInput(IInputMethodClient client, int flags,
ResultReceiver resultReceiver) { ResultReceiver resultReceiver) {
if (!calledFromValidUser()) {
return false;
}
int uid = Binder.getCallingUid(); int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity(); long ident = Binder.clearCallingIdentity();
try { try {
@@ -1630,6 +1785,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public InputBindResult windowGainedFocus(IInputMethodClient client, IBinder windowToken, public InputBindResult windowGainedFocus(IInputMethodClient client, IBinder windowToken,
int controlFlags, int softInputMode, int windowFlags, int controlFlags, int softInputMode, int windowFlags,
EditorInfo attribute, IInputContext inputContext) { EditorInfo attribute, IInputContext inputContext) {
if (!calledFromValidUser()) {
return null;
}
InputBindResult res = null; InputBindResult res = null;
long ident = Binder.clearCallingIdentity(); long ident = Binder.clearCallingIdentity();
try { try {
@@ -1770,6 +1928,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public void showInputMethodPickerFromClient(IInputMethodClient client) { public void showInputMethodPickerFromClient(IInputMethodClient client) {
if (!calledFromValidUser()) {
return;
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
if (mCurClient == null || client == null if (mCurClient == null || client == null
|| mCurClient.client.asBinder() != client.asBinder()) { || mCurClient.client.asBinder() != client.asBinder()) {
@@ -1785,11 +1946,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public void setInputMethod(IBinder token, String id) { public void setInputMethod(IBinder token, String id) {
if (!calledFromValidUser()) {
return;
}
setInputMethodWithSubtypeId(token, id, NOT_A_SUBTYPE_ID); setInputMethodWithSubtypeId(token, id, NOT_A_SUBTYPE_ID);
} }
@Override @Override
public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) { public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
if (!calledFromValidUser()) {
return;
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
if (subtype != null) { if (subtype != null) {
setInputMethodWithSubtypeId(token, id, getSubtypeIdFromHashCode( setInputMethodWithSubtypeId(token, id, getSubtypeIdFromHashCode(
@@ -1803,6 +1970,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public void showInputMethodAndSubtypeEnablerFromClient( public void showInputMethodAndSubtypeEnablerFromClient(
IInputMethodClient client, String inputMethodId) { IInputMethodClient client, String inputMethodId) {
if (!calledFromValidUser()) {
return;
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
if (mCurClient == null || client == null if (mCurClient == null || client == null
|| mCurClient.client.asBinder() != client.asBinder()) { || mCurClient.client.asBinder() != client.asBinder()) {
@@ -1815,6 +1985,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public boolean switchToLastInputMethod(IBinder token) { public boolean switchToLastInputMethod(IBinder token) {
if (!calledFromValidUser()) {
return false;
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked(); final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
final InputMethodInfo lastImi; final InputMethodInfo lastImi;
@@ -1882,6 +2055,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) { public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) {
if (!calledFromValidUser()) {
return false;
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
final ImeSubtypeListItem nextSubtype = mImListManager.getNextInputMethod( final ImeSubtypeListItem nextSubtype = mImListManager.getNextInputMethod(
onlyCurrentIme, mMethodMap.get(mCurMethodId), mCurrentSubtype); onlyCurrentIme, mMethodMap.get(mCurMethodId), mCurrentSubtype);
@@ -1895,6 +2071,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public InputMethodSubtype getLastInputMethodSubtype() { public InputMethodSubtype getLastInputMethodSubtype() {
if (!calledFromValidUser()) {
return null;
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked(); final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
// TODO: Handle the case of the last IME with no subtypes // TODO: Handle the case of the last IME with no subtypes
@@ -1917,14 +2096,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) { 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 // By this IPC call, only a process which shares the same uid with the IME can add
// additional input method subtypes to the IME. // additional input method subtypes to the IME.
if (TextUtils.isEmpty(imiId) || subtypes == null || subtypes.length == 0) return; if (TextUtils.isEmpty(imiId) || subtypes == null || subtypes.length == 0) return;
synchronized (mMethodMap) { synchronized (mMethodMap) {
final InputMethodInfo imi = mMethodMap.get(imiId); final InputMethodInfo imi = mMethodMap.get(imiId);
if (imi == null) return; if (imi == null) return;
final PackageManager pm = mContext.getPackageManager(); final String[] packageInfos;
final String[] packageInfos = pm.getPackagesForUid(Binder.getCallingUid()); try {
packageInfos = mIPackageManager.getPackagesForUid(Binder.getCallingUid());
} catch (RemoteException e) {
Slog.e(TAG, "Failed to get package infos");
return;
}
if (packageInfos != null) { if (packageInfos != null) {
final int packageNum = packageInfos.length; final int packageNum = packageInfos.length;
for (int i = 0; i < packageNum; ++i) { for (int i = 0; i < packageNum; ++i) {
@@ -1971,6 +2158,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public void hideMySoftInput(IBinder token, int flags) { public void hideMySoftInput(IBinder token, int flags) {
if (!calledFromValidUser()) {
return;
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
if (token == null || mCurToken != token) { if (token == null || mCurToken != token) {
if (DEBUG) Slog.w(TAG, "Ignoring hideInputMethod of uid " if (DEBUG) Slog.w(TAG, "Ignoring hideInputMethod of uid "
@@ -1988,6 +2178,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public void showMySoftInput(IBinder token, int flags) { public void showMySoftInput(IBinder token, int flags) {
if (!calledFromValidUser()) {
return;
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
if (token == null || mCurToken != token) { if (token == null || mCurToken != token) {
Slog.w(TAG, "Ignoring showMySoftInput of uid " Slog.w(TAG, "Ignoring showMySoftInput of uid "
@@ -2224,19 +2417,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
void buildInputMethodListLocked(ArrayList<InputMethodInfo> list, void buildInputMethodListLocked(ArrayList<InputMethodInfo> list,
HashMap<String, InputMethodInfo> map) { HashMap<String, InputMethodInfo> map) {
if (DEBUG) {
Slog.d(TAG, "--- re-buildInputMethodList " + ", \n ------ \n" + getStackTrace());
}
list.clear(); list.clear();
map.clear(); map.clear();
PackageManager pm = mContext.getPackageManager(); // Use for queryIntentServicesAsUser
final PackageManager pm = mContext.getPackageManager();
final Configuration config = mRes.getConfiguration(); final Configuration config = mRes.getConfiguration();
final boolean haveHardKeyboard = config.keyboard == Configuration.KEYBOARD_QWERTY; final boolean haveHardKeyboard = config.keyboard == Configuration.KEYBOARD_QWERTY;
String disabledSysImes = Settings.Secure.getString(mContext.getContentResolver(), String disabledSysImes = mSettings.getDisabledSystemInputMethods();
Secure.DISABLED_SYSTEM_INPUT_METHODS);
if (disabledSysImes == null) disabledSysImes = ""; if (disabledSysImes == null) disabledSysImes = "";
List<ResolveInfo> services = pm.queryIntentServices( final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
new Intent(InputMethod.SERVICE_INTERFACE), new Intent(InputMethod.SERVICE_INTERFACE),
PackageManager.GET_META_DATA); PackageManager.GET_META_DATA, mSettings.getCurrentUserId());
final HashMap<String, List<InputMethodSubtype>> additionalSubtypes = final HashMap<String, List<InputMethodSubtype>> additionalSubtypes =
mFileManager.getAllAdditionalInputMethodSubtypes(); mFileManager.getAllAdditionalInputMethodSubtypes();
@@ -2279,8 +2475,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
} }
} }
final String defaultImiId = Settings.Secure.getString(mContext final String defaultImiId = mSettings.getSelectedInputMethod();
.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
if (!TextUtils.isEmpty(defaultImiId)) { if (!TextUtils.isEmpty(defaultImiId)) {
if (!map.containsKey(defaultImiId)) { if (!map.containsKey(defaultImiId)) {
Slog.w(TAG, "Default IME is uninstalled. Choose new default IME."); Slog.w(TAG, "Default IME is uninstalled. Choose new default IME.");
@@ -2331,11 +2526,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (DEBUG) Slog.v(TAG, "Show switching menu"); if (DEBUG) Slog.v(TAG, "Show switching menu");
final Context context = mContext; final Context context = mContext;
final PackageManager pm = context.getPackageManager();
final boolean isScreenLocked = isScreenLocked(); final boolean isScreenLocked = isScreenLocked();
final String lastInputMethodId = Settings.Secure.getString(context final String lastInputMethodId = mSettings.getSelectedInputMethod();
.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
int lastInputMethodSubtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId); int lastInputMethodSubtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId);
if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId); if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
@@ -2353,7 +2546,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
showSubtypes, mInputShown, isScreenLocked); showSubtypes, mInputShown, isScreenLocked);
if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) { if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtype(); final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtypeLocked();
if (currentSubtype != null) { if (currentSubtype != null) {
final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId); final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
lastInputMethodSubtypeId = lastInputMethodSubtypeId =
@@ -2582,6 +2775,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public boolean setInputMethodEnabled(String id, boolean enabled) { public boolean setInputMethodEnabled(String id, boolean enabled) {
// TODO: Make this work even for non-current users?
if (!calledFromValidUser()) {
return false;
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
if (mContext.checkCallingOrSelfPermission( if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.WRITE_SECURE_SETTINGS) android.Manifest.permission.WRITE_SECURE_SETTINGS)
@@ -2626,8 +2823,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (mSettings.buildAndPutEnabledInputMethodsStrRemovingIdLocked( if (mSettings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
builder, enabledInputMethodsList, id)) { builder, enabledInputMethodsList, id)) {
// Disabled input method is currently selected, switch to another one. // Disabled input method is currently selected, switch to another one.
String selId = Settings.Secure.getString(mContext.getContentResolver(), final String selId = mSettings.getSelectedInputMethod();
Settings.Secure.DEFAULT_INPUT_METHOD);
if (id.equals(selId) && !chooseNewDefaultIMELocked()) { if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
Slog.i(TAG, "Can't find new IME, unsetting the current input method."); Slog.i(TAG, "Can't find new IME, unsetting the current input method.");
resetSelectedInputMethodAndSubtypeLocked(""); resetSelectedInputMethodAndSubtypeLocked("");
@@ -2674,7 +2870,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
} else { } else {
mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID); mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
// If the subtype is not specified, choose the most applicable one // If the subtype is not specified, choose the most applicable one
mCurrentSubtype = getCurrentInputMethodSubtype(); mCurrentSubtype = getCurrentInputMethodSubtypeLocked();
} }
} }
@@ -2716,14 +2912,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (imi == null) { if (imi == null) {
return NOT_A_SUBTYPE_ID; return NOT_A_SUBTYPE_ID;
} }
int subtypeId; final int subtypeHashCode = mSettings.getSelectedInputMethodSubtypeHashCode();
try { return getSubtypeIdFromHashCode(imi, subtypeHashCode);
subtypeId = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE);
} catch (SettingNotFoundException e) {
return NOT_A_SUBTYPE_ID;
}
return getSubtypeIdFromHashCode(imi, subtypeId);
} }
private static boolean isValidSubtypeId(InputMethodInfo imi, int subtypeHashCode) { private static boolean isValidSubtypeId(InputMethodInfo imi, int subtypeHashCode) {
@@ -2886,7 +3076,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
} }
InputMethodSubtype subtype = null; InputMethodSubtype subtype = null;
final List<InputMethodSubtype> enabledSubtypes = final List<InputMethodSubtype> enabledSubtypes =
getEnabledInputMethodSubtypeList(imi, true); getEnabledInputMethodSubtypeListLocked(imi, true);
// 1. Search by the current subtype's locale from enabledSubtypes. // 1. Search by the current subtype's locale from enabledSubtypes.
if (mCurrentSubtype != null) { if (mCurrentSubtype != null) {
subtype = findLastResortApplicableSubtypeLocked( subtype = findLastResortApplicableSubtypeLocked(
@@ -2955,49 +3145,53 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
*/ */
@Override @Override
public InputMethodSubtype getCurrentInputMethodSubtype() { 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) { if (mCurMethodId == null) {
return null; return null;
} }
boolean subtypeIsSelected = false; final boolean subtypeIsSelected =
try { mSettings.getSelectedInputMethodSubtypeHashCode() != NOT_A_SUBTYPE_ID;
subtypeIsSelected = Settings.Secure.getInt(mContext.getContentResolver(), final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE) != NOT_A_SUBTYPE_ID; if (imi == null || imi.getSubtypeCount() == 0) {
} catch (SettingNotFoundException e) { return null;
} }
synchronized (mMethodMap) { if (!subtypeIsSelected || mCurrentSubtype == null
final InputMethodInfo imi = mMethodMap.get(mCurMethodId); || !isValidSubtypeId(imi, mCurrentSubtype.hashCode())) {
if (imi == null || imi.getSubtypeCount() == 0) { int subtypeId = getSelectedInputMethodSubtypeId(mCurMethodId);
return null; if (subtypeId == NOT_A_SUBTYPE_ID) {
} // If there are no selected subtypes, the framework will try to find
if (!subtypeIsSelected || mCurrentSubtype == null // the most applicable subtype from explicitly or implicitly enabled
|| !isValidSubtypeId(imi, mCurrentSubtype.hashCode())) { // subtypes.
int subtypeId = getSelectedInputMethodSubtypeId(mCurMethodId); List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes =
if (subtypeId == NOT_A_SUBTYPE_ID) { getEnabledInputMethodSubtypeListLocked(imi, true);
// If there are no selected subtypes, the framework will try to find // If there is only one explicitly or implicitly enabled subtype,
// the most applicable subtype from explicitly or implicitly enabled // just returns it.
// subtypes. if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) {
List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes = mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0);
getEnabledInputMethodSubtypeList(imi, true); } else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) {
// If there is only one explicitly or implicitly enabled subtype, mCurrentSubtype = findLastResortApplicableSubtypeLocked(
// just returns it. mRes, explicitlyOrImplicitlyEnabledSubtypes,
if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) { SUBTYPE_MODE_KEYBOARD, null, true);
mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0); if (mCurrentSubtype == null) {
} else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) {
mCurrentSubtype = findLastResortApplicableSubtypeLocked( mCurrentSubtype = findLastResortApplicableSubtypeLocked(
mRes, explicitlyOrImplicitlyEnabledSubtypes, mRes, explicitlyOrImplicitlyEnabledSubtypes, null, null,
SUBTYPE_MODE_KEYBOARD, null, true); true);
if (mCurrentSubtype == null) {
mCurrentSubtype = findLastResortApplicableSubtypeLocked(
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, private void addShortcutInputMethodAndSubtypes(InputMethodInfo imi,
@@ -3042,6 +3236,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override @Override
public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) { public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
// TODO: Make this work even for non-current users?
if (!calledFromValidUser()) {
return false;
}
synchronized (mMethodMap) { synchronized (mMethodMap) {
if (subtype != null && mCurMethodId != null) { if (subtype != null && mCurMethodId != null) {
InputMethodInfo imi = mMethodMap.get(mCurMethodId); InputMethodInfo imi = mMethodMap.get(mCurMethodId);
@@ -3057,6 +3255,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private static class InputMethodAndSubtypeListManager { private static class InputMethodAndSubtypeListManager {
private final Context mContext; private final Context mContext;
// Used to load label
private final PackageManager mPm; private final PackageManager mPm;
private final InputMethodManagerService mImms; private final InputMethodManagerService mImms;
private final String mSystemLocaleStr; private final String mSystemLocaleStr;
@@ -3193,6 +3392,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private final ArrayList<InputMethodInfo> mMethodList; private final ArrayList<InputMethodInfo> mMethodList;
private String mEnabledInputMethodsStrCache; private String mEnabledInputMethodsStrCache;
private int mCurrentUserId;
private static void buildEnabledInputMethodsSettingString( private static void buildEnabledInputMethodsSettingString(
StringBuilder builder, Pair<String, ArrayList<String>> pair) { StringBuilder builder, Pair<String, ArrayList<String>> pair) {
@@ -3208,13 +3408,24 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public InputMethodSettings( public InputMethodSettings(
Resources res, ContentResolver resolver, Resources res, ContentResolver resolver,
HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList) { HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
int userId) {
setCurrentUserId(userId);
mRes = res; mRes = res;
mResolver = resolver; mResolver = resolver;
mMethodMap = methodMap; mMethodMap = methodMap;
mMethodList = methodList; 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() { public List<InputMethodInfo> getEnabledInputMethodListLocked() {
return createEnabledInputMethodListLocked( return createEnabledInputMethodListLocked(
getEnabledInputMethodsAndSubtypeListLocked()); getEnabledInputMethodsAndSubtypeListLocked());
@@ -3363,15 +3574,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
} }
private void putEnabledInputMethodsStr(String str) { 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; mEnabledInputMethodsStrCache = str;
if (DEBUG) {
Slog.d(TAG, "putEnabledInputMethodStr: " + str);
}
} }
private String getEnabledInputMethodsStr() { private String getEnabledInputMethodsStr() {
mEnabledInputMethodsStrCache = Settings.Secure.getString( mEnabledInputMethodsStrCache = Settings.Secure.getStringForUser(
mResolver, Settings.Secure.ENABLED_INPUT_METHODS); mResolver, Settings.Secure.ENABLED_INPUT_METHODS, mCurrentUserId);
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "getEnabledInputMethodsStr: " + mEnabledInputMethodsStrCache); Slog.d(TAG, "getEnabledInputMethodsStr: " + mEnabledInputMethodsStrCache
+ ", " + mCurrentUserId);
} }
return mEnabledInputMethodsStrCache; return mEnabledInputMethodsStrCache;
} }
@@ -3426,8 +3642,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "putSubtypeHistoryStr: " + str); Slog.d(TAG, "putSubtypeHistoryStr: " + str);
} }
Settings.Secure.putString( Settings.Secure.putStringForUser(
mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, str); mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, str, mCurrentUserId);
} }
public Pair<String, String> getLastInputMethodAndSubtypeLocked() { public Pair<String, String> getLastInputMethodAndSubtypeLocked() {
@@ -3546,20 +3762,57 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private String getSubtypeHistoryStr() { private String getSubtypeHistoryStr() {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "getSubtypeHistoryStr: " + Settings.Secure.getString( Slog.d(TAG, "getSubtypeHistoryStr: " + Settings.Secure.getStringForUser(
mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY)); mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, mCurrentUserId));
} }
return Settings.Secure.getString( return Settings.Secure.getStringForUser(
mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY); mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, mCurrentUserId);
} }
public void putSelectedInputMethod(String imeId) { 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) { public void putSelectedSubtype(int subtypeId) {
Settings.Secure.putInt( if (DEBUG) {
mResolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, subtypeId); Slog.d(TAG, "putSelectedInputMethodSubtypeStr: " + subtypeId + ", "
+ mCurrentUserId);
}
Settings.Secure.putIntForUser(mResolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
subtypeId, mCurrentUserId);
}
public String getDisabledSystemInputMethods() {
return Settings.Secure.getStringForUser(
mResolver, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS, mCurrentUserId);
}
public String getSelectedInputMethod() {
if (DEBUG) {
Slog.d(TAG, "getSelectedInputMethodStr: " + Settings.Secure.getStringForUser(
mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, mCurrentUserId)
+ ", " + mCurrentUserId);
}
return Settings.Secure.getStringForUser(
mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, mCurrentUserId);
}
public int getSelectedInputMethodSubtypeHashCode() {
try {
return Settings.Secure.getIntForUser(
mResolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, mCurrentUserId);
} catch (SettingNotFoundException e) {
return NOT_A_SUBTYPE_ID;
}
}
public int getCurrentUserId() {
return mCurrentUserId;
} }
} }
@@ -3762,6 +4015,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
} }
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// Utilities for debug
private static String getStackTrace() {
final StringBuilder sb = new StringBuilder();
try {
throw new RuntimeException();
} catch (RuntimeException e) {
final StackTraceElement[] frames = e.getStackTrace();
// Start at 1 because the first frame is here and we don't care about it
for (int j = 1; j < frames.length; ++j) {
sb.append(frames[j].toString() + "\n");
}
}
return sb.toString();
}
@Override @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {