diff options
Diffstat (limited to 'services/java/com/android/server/InputMethodManagerService.java')
-rw-r--r-- | services/java/com/android/server/InputMethodManagerService.java | 3574 |
1 files changed, 0 insertions, 3574 deletions
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java deleted file mode 100644 index a996dbd..0000000 --- a/services/java/com/android/server/InputMethodManagerService.java +++ /dev/null @@ -1,3574 +0,0 @@ -/* - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.android.server; - -import com.android.internal.content.PackageMonitor; -import com.android.internal.inputmethod.InputMethodUtils; -import com.android.internal.inputmethod.InputMethodUtils.InputMethodSettings; -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; -import com.android.internal.view.IInputSessionCallback; -import com.android.internal.view.IInputMethodClient; -import com.android.internal.view.IInputMethodManager; -import com.android.internal.view.IInputMethodSession; -import com.android.internal.view.InputBindResult; -import com.android.server.EventLogTags; -import com.android.server.wm.WindowManagerService; - -import org.xmlpull.v1.XmlPullParser; -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; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnCancelListener; -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.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.database.ContentObserver; -import android.inputmethodservice.InputMethodService; -import android.os.Binder; -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.text.TextUtils; -import android.text.style.SuggestionSpan; -import android.util.AtomicFile; -import android.util.EventLog; -import android.util.LruCache; -import android.util.Pair; -import android.util.PrintWriterPrinter; -import android.util.Printer; -import android.util.Slog; -import android.util.Xml; -import android.view.IWindowManager; -import android.view.InputChannel; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputBinding; -import android.view.inputmethod.InputMethod; -import android.view.inputmethod.InputMethodInfo; -import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.InputMethodSubtype; -import android.widget.ArrayAdapter; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; -import android.widget.RadioButton; -import android.widget.Switch; -import android.widget.TextView; - -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.TreeMap; - -/** - * This class provides a system service that manages input methods. - */ -public class InputMethodManagerService extends IInputMethodManager.Stub - implements ServiceConnection, Handler.Callback { - static final boolean DEBUG = false; - static final String TAG = "InputMethodManagerService"; - - static final int MSG_SHOW_IM_PICKER = 1; - static final int MSG_SHOW_IM_SUBTYPE_PICKER = 2; - static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 3; - static final int MSG_SHOW_IM_CONFIG = 4; - - static final int MSG_UNBIND_INPUT = 1000; - static final int MSG_BIND_INPUT = 1010; - static final int MSG_SHOW_SOFT_INPUT = 1020; - static final int MSG_HIDE_SOFT_INPUT = 1030; - static final int MSG_ATTACH_TOKEN = 1040; - static final int MSG_CREATE_SESSION = 1050; - - static final int MSG_START_INPUT = 2000; - static final int MSG_RESTART_INPUT = 2010; - - static final int MSG_UNBIND_METHOD = 3000; - static final int MSG_BIND_METHOD = 3010; - static final int MSG_SET_ACTIVE = 3020; - - static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000; - - static final long TIME_TO_RECONNECT = 10*1000; - - static final int SECURE_SUGGESTION_SPANS_MAX_SIZE = 20; - - private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID; - private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher"; - - - final Context mContext; - final Resources mRes; - final Handler mHandler; - final InputMethodSettings mSettings; - final SettingsObserver mSettingsObserver; - final IWindowManager mIWindowManager; - final HandlerCaller mCaller; - final boolean mHasFeature; - private InputMethodFileManager mFileManager; - private InputMethodAndSubtypeListManager mImListManager; - private final HardKeyboardListener mHardKeyboardListener; - private final WindowManagerService mWindowManagerService; - - final InputBindResult mNoBinding = new InputBindResult(null, null, null, -1); - - // All known input methods. mMethodMap also serves as the global - // lock for this class. - final ArrayList<InputMethodInfo> mMethodList = new ArrayList<InputMethodInfo>(); - final HashMap<String, InputMethodInfo> mMethodMap = new HashMap<String, InputMethodInfo>(); - private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans = - new LruCache<SuggestionSpan, InputMethodInfo>(SECURE_SUGGESTION_SPANS_MAX_SIZE); - - // Used to bring IME service up to visible adjustment while it is being shown. - final ServiceConnection mVisibleConnection = new ServiceConnection() { - @Override public void onServiceConnected(ComponentName name, IBinder service) { - } - - @Override public void onServiceDisconnected(ComponentName name) { - } - }; - boolean mVisibleBound = false; - - // Ongoing notification - private NotificationManager mNotificationManager; - private KeyguardManager mKeyguardManager; - private StatusBarManagerService mStatusBar; - private Notification mImeSwitcherNotification; - private PendingIntent mImeSwitchPendingIntent; - private boolean mShowOngoingImeSwitcherForPhones; - private boolean mNotificationShown; - private final boolean mImeSelectedOnBoot; - - class SessionState { - final ClientState client; - final IInputMethod method; - - IInputMethodSession session; - InputChannel channel; - - @Override - public String toString() { - return "SessionState{uid " + client.uid + " pid " + client.pid - + " method " + Integer.toHexString( - System.identityHashCode(method)) - + " session " + Integer.toHexString( - System.identityHashCode(session)) - + " channel " + channel - + "}"; - } - - SessionState(ClientState _client, IInputMethod _method, - IInputMethodSession _session, InputChannel _channel) { - client = _client; - method = _method; - session = _session; - channel = _channel; - } - } - - static final class ClientState { - final IInputMethodClient client; - final IInputContext inputContext; - final int uid; - final int pid; - final InputBinding binding; - - boolean sessionRequested; - SessionState curSession; - - @Override - public String toString() { - return "ClientState{" + Integer.toHexString( - System.identityHashCode(this)) + " uid " + uid - + " pid " + pid + "}"; - } - - ClientState(IInputMethodClient _client, IInputContext _inputContext, - int _uid, int _pid) { - client = _client; - inputContext = _inputContext; - uid = _uid; - pid = _pid; - binding = new InputBinding(null, inputContext.asBinder(), uid, pid); - } - } - - final HashMap<IBinder, ClientState> mClients - = new HashMap<IBinder, ClientState>(); - - /** - * Set once the system is ready to run third party code. - */ - boolean mSystemReady; - - /** - * Id of the currently selected input method. - */ - String mCurMethodId; - - /** - * The current binding sequence number, incremented every time there is - * a new bind performed. - */ - int mCurSeq; - - /** - * The client that is currently bound to an input method. - */ - ClientState mCurClient; - - /** - * The last window token that gained focus. - */ - IBinder mCurFocusedWindow; - - /** - * The input context last provided by the current client. - */ - IInputContext mCurInputContext; - - /** - * The attributes last provided by the current client. - */ - EditorInfo mCurAttribute; - - /** - * The input method ID of the input method service that we are currently - * connected to or in the process of connecting to. - */ - String mCurId; - - /** - * The current subtype of the current input method. - */ - private InputMethodSubtype mCurrentSubtype; - - // This list contains the pairs of InputMethodInfo and InputMethodSubtype. - private final HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>> - mShortcutInputMethodsAndSubtypes = - new HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>(); - - // Was the keyguard locked when this client became current? - private boolean mCurClientInKeyguard; - - /** - * Set to true if our ServiceConnection is currently actively bound to - * a service (whether or not we have gotten its IBinder back yet). - */ - boolean mHaveConnection; - - /** - * Set if the client has asked for the input method to be shown. - */ - boolean mShowRequested; - - /** - * Set if we were explicitly told to show the input method. - */ - boolean mShowExplicitlyRequested; - - /** - * Set if we were forced to be shown. - */ - boolean mShowForced; - - /** - * Set if we last told the input method to show itself. - */ - boolean mInputShown; - - /** - * The Intent used to connect to the current input method. - */ - Intent mCurIntent; - - /** - * The token we have made for the currently active input method, to - * identify it in the future. - */ - IBinder mCurToken; - - /** - * If non-null, this is the input method service we are currently connected - * to. - */ - IInputMethod mCurMethod; - - /** - * Time that we last initiated a bind to the input method, to determine - * if we should try to disconnect and reconnect to it. - */ - long mLastBindTime; - - /** - * Have we called mCurMethod.bindInput()? - */ - boolean mBoundToMethod; - - /** - * Currently enabled session. Only touched by service thread, not - * protected by a lock. - */ - SessionState mEnabledSession; - - /** - * True if the screen is on. The value is true initially. - */ - boolean mScreenOn = true; - - int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; - int mImeWindowVis; - - private AlertDialog.Builder mDialogBuilder; - private AlertDialog mSwitchingDialog; - private View mSwitchingDialogTitleView; - private InputMethodInfo[] mIms; - private int[] mSubtypeIds; - private Locale mLastSystemLocale; - private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor(); - private final IPackageManager mIPackageManager; - - class SettingsObserver extends ContentObserver { - String mLastEnabled = ""; - - SettingsObserver(Handler handler) { - super(handler); - ContentResolver resolver = mContext.getContentResolver(); - resolver.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.DEFAULT_INPUT_METHOD), false, this); - resolver.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.ENABLED_INPUT_METHODS), false, this); - resolver.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this); - } - - @Override public void onChange(boolean selfChange) { - synchronized (mMethodMap) { - boolean enabledChanged = false; - String newEnabled = mSettings.getEnabledInputMethodsStr(); - if (!mLastEnabled.equals(newEnabled)) { - mLastEnabled = newEnabled; - enabledChanged = true; - } - updateFromSettingsLocked(enabledChanged); - } - } - } - - class ImmsBroadcastReceiver extends android.content.BroadcastReceiver { - private void updateActive() { - // Inform the current client of the change in active status - if (mCurClient != null && mCurClient.client != null) { - executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( - MSG_SET_ACTIVE, mScreenOn ? 1 : 0, mCurClient)); - } - } - - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (Intent.ACTION_SCREEN_ON.equals(action)) { - mScreenOn = true; - refreshImeWindowVisibilityLocked(); - updateActive(); - return; - } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { - mScreenOn = false; - setImeWindowVisibilityStatusHiddenLocked(); - updateActive(); - return; - } else if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { - hideInputMethodMenu(); - // No need to updateActive - return; - } else { - Slog.w(TAG, "Unexpected intent " + intent); - } - } - } - - class MyPackageMonitor extends PackageMonitor { - private boolean isChangingPackagesOfCurrentUser() { - final int userId = getChangingUserId(); - final boolean retval = userId == mSettings.getCurrentUserId(); - if (DEBUG) { - if (!retval) { - 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 = mSettings.getSelectedInputMethod(); - final int N = mMethodList.size(); - if (curInputMethodId != null) { - for (int i=0; i<N; i++) { - InputMethodInfo imi = mMethodList.get(i); - if (imi.getId().equals(curInputMethodId)) { - for (String pkg : packages) { - if (imi.getPackageName().equals(pkg)) { - if (!doit) { - return true; - } - resetSelectedInputMethodAndSubtypeLocked(""); - chooseNewDefaultIMELocked(); - return true; - } - } - } - } - } - } - return false; - } - - @Override - public void onSomePackagesChanged() { - if (!isChangingPackagesOfCurrentUser()) { - return; - } - synchronized (mMethodMap) { - InputMethodInfo curIm = null; - String curInputMethodId = mSettings.getSelectedInputMethod(); - final int N = mMethodList.size(); - if (curInputMethodId != null) { - for (int i=0; i<N; i++) { - InputMethodInfo imi = mMethodList.get(i); - final String imiId = imi.getId(); - if (imiId.equals(curInputMethodId)) { - curIm = imi; - } - - int change = isPackageDisappearing(imi.getPackageName()); - if (isPackageModified(imi.getPackageName())) { - mFileManager.deleteAllInputMethodSubtypes(imiId); - } - if (change == PACKAGE_TEMPORARY_CHANGE - || change == PACKAGE_PERMANENT_CHANGE) { - Slog.i(TAG, "Input method uninstalled, disabling: " - + imi.getComponent()); - setInputMethodEnabledLocked(imi.getId(), false); - } - } - } - - buildInputMethodListLocked( - mMethodList, mMethodMap, false /* resetDefaultEnabledIme */); - - boolean changed = false; - - if (curIm != null) { - int change = isPackageDisappearing(curIm.getPackageName()); - if (change == PACKAGE_TEMPORARY_CHANGE - || change == PACKAGE_PERMANENT_CHANGE) { - ServiceInfo si = null; - try { - si = mIPackageManager.getServiceInfo( - curIm.getComponent(), 0, mSettings.getCurrentUserId()); - } catch (RemoteException ex) { - } - if (si == null) { - // Uh oh, current input method is no longer around! - // Pick another one... - Slog.i(TAG, "Current input method removed: " + curInputMethodId); - setImeWindowVisibilityStatusHiddenLocked(); - if (!chooseNewDefaultIMELocked()) { - changed = true; - curIm = null; - Slog.i(TAG, "Unsetting current input method"); - resetSelectedInputMethodAndSubtypeLocked(""); - } - } - } - } - - if (curIm == null) { - // We currently don't have a default input method... is - // one now available? - changed = chooseNewDefaultIMELocked(); - } - - if (changed) { - updateFromSettingsLocked(false); - } - } - } - } - - private static final class MethodCallback extends IInputSessionCallback.Stub { - private final InputMethodManagerService mParentIMMS; - private final IInputMethod mMethod; - private final InputChannel mChannel; - - MethodCallback(InputMethodManagerService imms, IInputMethod method, - InputChannel channel) { - mParentIMMS = imms; - mMethod = method; - mChannel = channel; - } - - @Override - public void sessionCreated(IInputMethodSession session) { - mParentIMMS.onSessionCreated(mMethod, session, mChannel); - } - } - - private class HardKeyboardListener - implements WindowManagerService.OnHardKeyboardStatusChangeListener { - @Override - public void onHardKeyboardStatusChange(boolean available, boolean enabled) { - mHandler.sendMessage(mHandler.obtainMessage( - MSG_HARD_KEYBOARD_SWITCH_CHANGED, available ? 1 : 0, enabled ? 1 : 0)); - } - - public void handleHardKeyboardStatusChange(boolean available, boolean enabled) { - if (DEBUG) { - Slog.w(TAG, "HardKeyboardStatusChanged: available = " + available + ", enabled = " - + enabled); - } - synchronized(mMethodMap) { - if (mSwitchingDialog != null && mSwitchingDialogTitleView != null - && mSwitchingDialog.isShowing()) { - mSwitchingDialogTitleView.findViewById( - com.android.internal.R.id.hard_keyboard_section).setVisibility( - available ? View.VISIBLE : View.GONE); - } - } - } - } - - public InputMethodManagerService(Context context, WindowManagerService windowManager) { - mIPackageManager = AppGlobals.getPackageManager(); - mContext = context; - mRes = context.getResources(); - mHandler = new Handler(this); - mIWindowManager = IWindowManager.Stub.asInterface( - ServiceManager.getService(Context.WINDOW_SERVICE)); - mCaller = new HandlerCaller(context, null, new HandlerCaller.Callback() { - @Override - public void executeMessage(Message msg) { - handleMessage(msg); - } - }, true /*asyncHandler*/); - mWindowManagerService = windowManager; - mHardKeyboardListener = new HardKeyboardListener(); - mHasFeature = context.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_INPUT_METHODS); - - mImeSwitcherNotification = new Notification(); - mImeSwitcherNotification.icon = com.android.internal.R.drawable.ic_notification_ime_default; - mImeSwitcherNotification.when = 0; - mImeSwitcherNotification.flags = Notification.FLAG_ONGOING_EVENT; - mImeSwitcherNotification.tickerText = null; - mImeSwitcherNotification.defaults = 0; // please be quiet - mImeSwitcherNotification.sound = null; - mImeSwitcherNotification.vibrate = null; - - // Tag this notification specially so SystemUI knows it's important - mImeSwitcherNotification.kind = new String[] { "android.system.imeswitcher" }; - - Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER); - mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); - - mShowOngoingImeSwitcherForPhones = false; - - 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, UserHandle.ALL, true); - - // mSettings should be created before buildInputMethodListLocked - mSettings = new InputMethodSettings( - 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 = mSettings.getSelectedInputMethod(); - if (DEBUG) { - Slog.d(TAG, "Initial default ime = " + defaultImiId); - } - mImeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId); - - buildInputMethodListLocked(mMethodList, mMethodMap, - !mImeSelectedOnBoot /* resetDefaultEnabledIme */); - mSettings.enableAllIMEsIfThereIsNoEnabledIME(); - - if (!mImeSelectedOnBoot) { - Slog.w(TAG, "No IME selected. Choose the most applicable IME."); - resetDefaultImeLocked(context); - } - - mSettingsObserver = new SettingsObserver(mHandler); - updateFromSettingsLocked(true); - - // IMMS wants to receive Intent.ACTION_LOCALE_CHANGED in order to update the current IME - // according to the new system locale. - final IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_LOCALE_CHANGED); - mContext.registerReceiver( - new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - synchronized(mMethodMap) { - resetStateIfCurrentLocaleChangedLocked(); - } - } - }, filter); - } - - private void resetDefaultImeLocked(Context context) { - // Do not reset the default (current) IME when it is a 3rd-party IME - if (mCurMethodId != null - && !InputMethodUtils.isSystemIme(mMethodMap.get(mCurMethodId))) { - return; - } - - InputMethodInfo defIm = null; - for (InputMethodInfo imi : mMethodList) { - if (defIm == null) { - if (InputMethodUtils.isValidSystemDefaultIme( - mSystemReady, imi, context)) { - defIm = imi; - Slog.i(TAG, "Selected default: " + imi.getId()); - } - } - } - if (defIm == null && mMethodList.size() > 0) { - defIm = InputMethodUtils.getMostApplicableDefaultIME( - mSettings.getEnabledInputMethodListLocked()); - Slog.i(TAG, "No default found, using " + defIm.getId()); - } - if (defIm != null) { - setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false); - } - } - - private void resetAllInternalStateLocked(final boolean updateOnlyWhenLocaleChanged, - final boolean resetDefaultEnabledIme) { - 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, resetDefaultEnabledIme); - 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(true); - mLastSystemLocale = newLocale; - if (!updateOnlyWhenLocaleChanged) { - try { - startInputInnerLocked(); - } catch (RuntimeException e) { - Slog.w(TAG, "Unexpected exception", e); - } - } - } - } - - private void resetStateIfCurrentLocaleChangedLocked() { - resetAllInternalStateLocked(true /* updateOnlyWhenLocaleChanged */, - true /* resetDefaultImeLocked */); - } - - private void switchUserLocked(int newUserId) { - mSettings.setCurrentUserId(newUserId); - // InputMethodFileManager should be reset when the user is changed - mFileManager = new InputMethodFileManager(mMethodMap, newUserId); - final String defaultImiId = mSettings.getSelectedInputMethod(); - // For secondary users, the list of enabled IMEs may not have been updated since the - // callbacks to PackageMonitor are ignored for the secondary user. Here, defaultImiId may - // not be empty even if the IME has been uninstalled by the primary user. - // Even in such cases, IMMS works fine because it will find the most applicable - // IME for that user. - final boolean initialUserSwitch = TextUtils.isEmpty(defaultImiId); - if (DEBUG) { - Slog.d(TAG, "Switch user: " + newUserId + " current ime = " + defaultImiId); - } - resetAllInternalStateLocked(false /* updateOnlyWhenLocaleChanged */, - initialUserSwitch /* needsToResetDefaultIme */); - if (initialUserSwitch) { - InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mContext.getPackageManager(), - mSettings.getEnabledInputMethodListLocked()); - } - } - - @Override - public boolean onTransact(int code, Parcel data, Parcel reply, int flags) - throws RemoteException { - try { - return super.onTransact(code, data, reply, flags); - } catch (RuntimeException e) { - // The input method manager only throws security exceptions, so let's - // log all others. - if (!(e instanceof SecurityException)) { - Slog.wtf(TAG, "Input Method Manager Crash", e); - } - throw e; - } - } - - public void systemRunning(StatusBarManagerService statusBar) { - synchronized (mMethodMap) { - if (DEBUG) { - Slog.d(TAG, "--- systemReady"); - } - if (!mSystemReady) { - mSystemReady = true; - mKeyguardManager = - (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); - mNotificationManager = (NotificationManager) - mContext.getSystemService(Context.NOTIFICATION_SERVICE); - mStatusBar = statusBar; - statusBar.setIconVisibility("ime", false); - updateImeWindowStatusLocked(); - mShowOngoingImeSwitcherForPhones = mRes.getBoolean( - com.android.internal.R.bool.show_ongoing_ime_switcher); - if (mShowOngoingImeSwitcherForPhones) { - mWindowManagerService.setOnHardKeyboardStatusChangeListener( - mHardKeyboardListener); - } - buildInputMethodListLocked(mMethodList, mMethodMap, - !mImeSelectedOnBoot /* resetDefaultEnabledIme */); - if (!mImeSelectedOnBoot) { - Slog.w(TAG, "Reset the default IME as \"Resource\" is ready here."); - resetStateIfCurrentLocaleChangedLocked(); - InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed( - mContext.getPackageManager(), - mSettings.getEnabledInputMethodListLocked()); - } - mLastSystemLocale = mRes.getConfiguration().locale; - try { - startInputInnerLocked(); - } catch (RuntimeException e) { - Slog.w(TAG, "Unexpected exception", e); - } - } - } - } - - private void setImeWindowVisibilityStatusHiddenLocked() { - mImeWindowVis = 0; - updateImeWindowStatusLocked(); - } - - private void refreshImeWindowVisibilityLocked() { - final Configuration conf = mRes.getConfiguration(); - final boolean haveHardKeyboard = conf.keyboard - != Configuration.KEYBOARD_NOKEYS; - final boolean hardKeyShown = haveHardKeyboard - && conf.hardKeyboardHidden - != Configuration.HARDKEYBOARDHIDDEN_YES; - - final boolean isScreenLocked = isKeyguardLocked(); - final boolean inputActive = !isScreenLocked && (mInputShown || hardKeyShown); - // We assume the softkeyboard is shown when the input is active as long as the - // hard keyboard is not shown. - final boolean inputVisible = inputActive && !hardKeyShown; - mImeWindowVis = (inputActive ? InputMethodService.IME_ACTIVE : 0) - | (inputVisible ? InputMethodService.IME_VISIBLE : 0); - updateImeWindowStatusLocked(); - } - - private void updateImeWindowStatusLocked() { - 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() - + InputMethodUtils.getApiCallStack()); - } - 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" - + InputMethodUtils.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.bindServiceAsUser(service, conn, flags, - new UserHandle(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); - } - } - - @Override - public List<InputMethodInfo> getEnabledInputMethodList() { - // TODO: Make this work even for non-current users? - if (!calledFromValidUser()) { - return Collections.emptyList(); - } - synchronized (mMethodMap) { - return mSettings.getEnabledInputMethodListLocked(); - } - } - - private HashMap<InputMethodInfo, List<InputMethodSubtype>> - getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked() { - HashMap<InputMethodInfo, List<InputMethodSubtype>> enabledInputMethodAndSubtypes = - new HashMap<InputMethodInfo, List<InputMethodSubtype>>(); - for (InputMethodInfo imi: mSettings.getEnabledInputMethodListLocked()) { - enabledInputMethodAndSubtypes.put( - imi, mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true)); - } - return enabledInputMethodAndSubtypes; - } - - /** - * @param imiId if null, returns enabled subtypes for the current imi - * @return enabled subtypes of the specified imi - */ - @Override - public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId, - boolean allowsImplicitlySelectedSubtypes) { - // TODO: Make this work even for non-current users? - if (!calledFromValidUser()) { - return Collections.<InputMethodSubtype>emptyList(); - } - synchronized (mMethodMap) { - final InputMethodInfo imi; - if (imiId == null && mCurMethodId != null) { - imi = mMethodMap.get(mCurMethodId); - } else { - imi = mMethodMap.get(imiId); - } - if (imi == null) { - return Collections.<InputMethodSubtype>emptyList(); - } - return mSettings.getEnabledInputMethodSubtypeListLocked( - mContext, imi, allowsImplicitlySelectedSubtypes); - } - } - - @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)); - } - } - - @Override - public void removeClient(IInputMethodClient client) { - if (!calledFromValidUser()) { - return; - } - synchronized (mMethodMap) { - ClientState cs = mClients.remove(client.asBinder()); - if (cs != null) { - clearClientSessionLocked(cs); - } - } - } - - void executeOrSendMessage(IInterface target, Message msg) { - if (target.asBinder() instanceof Binder) { - mCaller.sendMessage(msg); - } else { - handleMessage(msg); - msg.recycle(); - } - } - - void unbindCurrentClientLocked() { - if (mCurClient != null) { - if (DEBUG) Slog.v(TAG, "unbindCurrentInputLocked: client = " - + mCurClient.client.asBinder()); - if (mBoundToMethod) { - mBoundToMethod = false; - if (mCurMethod != null) { - executeOrSendMessage(mCurMethod, mCaller.obtainMessageO( - MSG_UNBIND_INPUT, mCurMethod)); - } - } - - executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( - MSG_SET_ACTIVE, 0, mCurClient)); - executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( - MSG_UNBIND_METHOD, mCurSeq, mCurClient.client)); - mCurClient.sessionRequested = false; - mCurClient = null; - - hideInputMethodMenuLocked(); - } - } - - private int getImeShowFlags() { - int flags = 0; - if (mShowForced) { - flags |= InputMethod.SHOW_FORCED - | InputMethod.SHOW_EXPLICIT; - } else if (mShowExplicitlyRequested) { - flags |= InputMethod.SHOW_EXPLICIT; - } - return flags; - } - - private int getAppShowFlags() { - int flags = 0; - if (mShowForced) { - flags |= InputMethodManager.SHOW_FORCED; - } else if (!mShowExplicitlyRequested) { - flags |= InputMethodManager.SHOW_IMPLICIT; - } - return flags; - } - - InputBindResult attachNewInputLocked(boolean initial) { - if (!mBoundToMethod) { - executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( - MSG_BIND_INPUT, mCurMethod, mCurClient.binding)); - mBoundToMethod = true; - } - final SessionState session = mCurClient.curSession; - if (initial) { - executeOrSendMessage(session.method, mCaller.obtainMessageOOO( - MSG_START_INPUT, session, mCurInputContext, mCurAttribute)); - } else { - executeOrSendMessage(session.method, mCaller.obtainMessageOOO( - MSG_RESTART_INPUT, session, mCurInputContext, mCurAttribute)); - } - if (mShowRequested) { - if (DEBUG) Slog.v(TAG, "Attach new input asks to show input"); - showCurrentInputLocked(getAppShowFlags(), null); - } - return new InputBindResult(session.session, - session.channel != null ? session.channel.dup() : null, mCurId, mCurSeq); - } - - InputBindResult startInputLocked(IInputMethodClient client, - IInputContext inputContext, EditorInfo attribute, int controlFlags) { - // If no method is currently selected, do nothing. - if (mCurMethodId == null) { - return mNoBinding; - } - - ClientState cs = mClients.get(client.asBinder()); - if (cs == null) { - throw new IllegalArgumentException("unknown client " - + client.asBinder()); - } - - try { - if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) { - // Check with the window manager to make sure this client actually - // has a window with focus. If not, reject. This is thread safe - // because if the focus changes some time before or after, the - // next client receiving focus that has any interest in input will - // be calling through here after that change happens. - Slog.w(TAG, "Starting input on non-focused client " + cs.client - + " (uid=" + cs.uid + " pid=" + cs.pid + ")"); - return null; - } - } catch (RemoteException e) { - } - - return startInputUncheckedLocked(cs, inputContext, attribute, controlFlags); - } - - InputBindResult startInputUncheckedLocked(ClientState cs, - IInputContext inputContext, EditorInfo attribute, int controlFlags) { - // If no method is currently selected, do nothing. - if (mCurMethodId == null) { - return mNoBinding; - } - - if (mCurClient != cs) { - // Was the keyguard locked when switching over to the new client? - mCurClientInKeyguard = isKeyguardLocked(); - // If the client is changing, we need to switch over to the new - // one. - unbindCurrentClientLocked(); - if (DEBUG) Slog.v(TAG, "switching to client: client = " - + cs.client.asBinder() + " keyguard=" + mCurClientInKeyguard); - - // If the screen is on, inform the new client it is active - if (mScreenOn) { - executeOrSendMessage(cs.client, mCaller.obtainMessageIO( - MSG_SET_ACTIVE, mScreenOn ? 1 : 0, cs)); - } - } - - // Bump up the sequence for this client and attach it. - mCurSeq++; - if (mCurSeq <= 0) mCurSeq = 1; - mCurClient = cs; - mCurInputContext = inputContext; - mCurAttribute = attribute; - - // Check if the input method is changing. - if (mCurId != null && mCurId.equals(mCurMethodId)) { - if (cs.curSession != null) { - // Fast case: if we are already connected to the input method, - // then just return it. - return attachNewInputLocked( - (controlFlags&InputMethodManager.CONTROL_START_INITIAL) != 0); - } - if (mHaveConnection) { - if (mCurMethod != null) { - // Return to client, and we will get back with it when - // we have had a session made for it. - requestClientSessionLocked(cs); - return new InputBindResult(null, null, mCurId, mCurSeq); - } else if (SystemClock.uptimeMillis() - < (mLastBindTime+TIME_TO_RECONNECT)) { - // In this case we have connected to the service, but - // don't yet have its interface. If it hasn't been too - // long since we did the connection, we'll return to - // the client and wait to get the service interface so - // we can report back. If it has been too long, we want - // to fall through so we can try a disconnect/reconnect - // to see if we can get back in touch with the service. - return new InputBindResult(null, null, mCurId, mCurSeq); - } else { - EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, - mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0); - } - } - } - - return startInputInnerLocked(); - } - - InputBindResult startInputInnerLocked() { - if (mCurMethodId == null) { - return mNoBinding; - } - - if (!mSystemReady) { - // If the system is not yet ready, we shouldn't be running third - // party code. - return new InputBindResult(null, null, mCurMethodId, mCurSeq); - } - - InputMethodInfo info = mMethodMap.get(mCurMethodId); - if (info == null) { - throw new IllegalArgumentException("Unknown id: " + mCurMethodId); - } - - unbindCurrentMethodLocked(false, true); - - mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE); - mCurIntent.setComponent(info.getComponent()); - mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL, - 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 (bindCurrentInputMethodService(mCurIntent, this, Context.BIND_AUTO_CREATE - | Context.BIND_NOT_VISIBLE | Context.BIND_SHOWING_UI)) { - mLastBindTime = SystemClock.uptimeMillis(); - mHaveConnection = true; - mCurId = info.getId(); - mCurToken = new Binder(); - try { - if (true || DEBUG) Slog.v(TAG, "Adding window token: " + mCurToken); - mIWindowManager.addWindowToken(mCurToken, - WindowManager.LayoutParams.TYPE_INPUT_METHOD); - } catch (RemoteException e) { - } - return new InputBindResult(null, null, mCurId, mCurSeq); - } else { - mCurIntent = null; - Slog.w(TAG, "Failure connecting to input method service: " - + mCurIntent); - } - return null; - } - - @Override - public InputBindResult startInput(IInputMethodClient client, - IInputContext inputContext, EditorInfo attribute, int controlFlags) { - if (!calledFromValidUser()) { - return null; - } - synchronized (mMethodMap) { - final long ident = Binder.clearCallingIdentity(); - try { - return startInputLocked(client, inputContext, attribute, controlFlags); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - @Override - public void finishInput(IInputMethodClient client) { - } - - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - synchronized (mMethodMap) { - if (mCurIntent != null && name.equals(mCurIntent.getComponent())) { - mCurMethod = IInputMethod.Stub.asInterface(service); - if (mCurToken == null) { - Slog.w(TAG, "Service connected without a token!"); - unbindCurrentMethodLocked(false, false); - return; - } - if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken); - executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( - MSG_ATTACH_TOKEN, mCurMethod, mCurToken)); - if (mCurClient != null) { - clearClientSessionLocked(mCurClient); - requestClientSessionLocked(mCurClient); - } - } - } - } - - void onSessionCreated(IInputMethod method, IInputMethodSession session, - InputChannel channel) { - synchronized (mMethodMap) { - if (mCurMethod != null && method != null - && mCurMethod.asBinder() == method.asBinder()) { - if (mCurClient != null) { - clearClientSessionLocked(mCurClient); - mCurClient.curSession = new SessionState(mCurClient, - method, session, channel); - InputBindResult res = attachNewInputLocked(true); - if (res.method != null) { - executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO( - MSG_BIND_METHOD, mCurClient.client, res)); - } - return; - } - } - } - - // Session abandoned. Close its associated input channel. - channel.dispose(); - } - - void unbindCurrentMethodLocked(boolean reportToClient, boolean savePosition) { - if (mVisibleBound) { - mContext.unbindService(mVisibleConnection); - mVisibleBound = false; - } - - if (mHaveConnection) { - mContext.unbindService(this); - mHaveConnection = false; - } - - if (mCurToken != null) { - try { - if (DEBUG) Slog.v(TAG, "Removing window token: " + mCurToken); - if ((mImeWindowVis & InputMethodService.IME_ACTIVE) != 0 && savePosition) { - // The current IME is shown. Hence an IME switch (transition) is happening. - mWindowManagerService.saveLastInputMethodWindowForTransition(); - } - mIWindowManager.removeWindowToken(mCurToken); - } catch (RemoteException e) { - } - mCurToken = null; - } - - mCurId = null; - clearCurMethodLocked(); - - if (reportToClient && mCurClient != null) { - executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( - MSG_UNBIND_METHOD, mCurSeq, mCurClient.client)); - } - } - - void requestClientSessionLocked(ClientState cs) { - if (!cs.sessionRequested) { - if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs); - InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString()); - cs.sessionRequested = true; - executeOrSendMessage(mCurMethod, mCaller.obtainMessageOOO( - MSG_CREATE_SESSION, mCurMethod, channels[1], - new MethodCallback(this, mCurMethod, channels[0]))); - } - } - - void clearClientSessionLocked(ClientState cs) { - finishSessionLocked(cs.curSession); - cs.curSession = null; - cs.sessionRequested = false; - } - - private void finishSessionLocked(SessionState sessionState) { - if (sessionState != null) { - if (sessionState.session != null) { - try { - sessionState.session.finishSession(); - } catch (RemoteException e) { - Slog.w(TAG, "Session failed to close due to remote exception", e); - setImeWindowVisibilityStatusHiddenLocked(); - } - sessionState.session = null; - } - if (sessionState.channel != null) { - sessionState.channel.dispose(); - sessionState.channel = null; - } - } - } - - void clearCurMethodLocked() { - if (mCurMethod != null) { - for (ClientState cs : mClients.values()) { - clearClientSessionLocked(cs); - } - - finishSessionLocked(mEnabledSession); - mEnabledSession = null; - mCurMethod = null; - } - if (mStatusBar != null) { - mStatusBar.setIconVisibility("ime", false); - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - synchronized (mMethodMap) { - if (DEBUG) Slog.v(TAG, "Service disconnected: " + name - + " mCurIntent=" + mCurIntent); - if (mCurMethod != null && mCurIntent != null - && name.equals(mCurIntent.getComponent())) { - clearCurMethodLocked(); - // We consider this to be a new bind attempt, since the system - // should now try to restart the service for us. - mLastBindTime = SystemClock.uptimeMillis(); - mShowRequested = mInputShown; - mInputShown = false; - if (mCurClient != null) { - executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( - MSG_UNBIND_METHOD, mCurSeq, mCurClient.client)); - } - } - } - } - - @Override - public void updateStatusIcon(IBinder token, String packageName, int iconId) { - int uid = Binder.getCallingUid(); - long ident = Binder.clearCallingIdentity(); - try { - if (token == null || mCurToken != token) { - Slog.w(TAG, "Ignoring setInputMethod of uid " + uid + " token: " + token); - return; - } - - synchronized (mMethodMap) { - if (iconId == 0) { - if (DEBUG) Slog.d(TAG, "hide the small icon for the input method"); - if (mStatusBar != null) { - mStatusBar.setIconVisibility("ime", false); - } - } else if (packageName != null) { - if (DEBUG) Slog.d(TAG, "show a small icon for the input method"); - CharSequence contentDescription = null; - try { - // Use PackageManager to load label - final PackageManager packageManager = mContext.getPackageManager(); - contentDescription = packageManager.getApplicationLabel( - mIPackageManager.getApplicationInfo(packageName, 0, - mSettings.getCurrentUserId())); - } catch (RemoteException e) { - /* ignore */ - } - if (mStatusBar != null) { - mStatusBar.setIcon("ime", packageName, iconId, 0, - contentDescription != null - ? contentDescription.toString() : null); - mStatusBar.setIconVisibility("ime", true); - } - } - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - private boolean needsToShowImeSwitchOngoingNotification() { - if (!mShowOngoingImeSwitcherForPhones) return false; - if (isScreenLocked()) return false; - synchronized (mMethodMap) { - List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked(); - final int N = imis.size(); - if (N > 2) return true; - if (N < 1) return false; - int nonAuxCount = 0; - int auxCount = 0; - InputMethodSubtype nonAuxSubtype = null; - InputMethodSubtype auxSubtype = null; - for(int i = 0; i < N; ++i) { - final InputMethodInfo imi = imis.get(i); - final List<InputMethodSubtype> subtypes = - mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true); - final int subtypeCount = subtypes.size(); - if (subtypeCount == 0) { - ++nonAuxCount; - } else { - for (int j = 0; j < subtypeCount; ++j) { - final InputMethodSubtype subtype = subtypes.get(j); - if (!subtype.isAuxiliary()) { - ++nonAuxCount; - nonAuxSubtype = subtype; - } else { - ++auxCount; - auxSubtype = subtype; - } - } - } - } - if (nonAuxCount > 1 || auxCount > 1) { - return true; - } else if (nonAuxCount == 1 && auxCount == 1) { - if (nonAuxSubtype != null && auxSubtype != null - && (nonAuxSubtype.getLocale().equals(auxSubtype.getLocale()) - || auxSubtype.overridesImplicitlyEnabledSubtype() - || nonAuxSubtype.overridesImplicitlyEnabledSubtype()) - && nonAuxSubtype.containsExtraValueKey(TAG_TRY_SUPPRESSING_IME_SWITCHER)) { - return false; - } - return true; - } - return false; - } - } - - private boolean isKeyguardLocked() { - return mKeyguardManager != null && mKeyguardManager.isKeyguardLocked(); - } - - // Caution! This method is called in this class. Handle multi-user carefully - @SuppressWarnings("deprecation") - @Override - public void setImeWindowStatus(IBinder token, int vis, int backDisposition) { - 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; - } - synchronized (mMethodMap) { - // apply policy for binder calls - if (vis != 0 && isKeyguardLocked() && !mCurClientInKeyguard) { - vis = 0; - } - mImeWindowVis = vis; - mBackDisposition = backDisposition; - if (mStatusBar != null) { - mStatusBar.setImeWindowStatus(token, vis, backDisposition); - } - final boolean iconVisibility = ((vis & (InputMethodService.IME_ACTIVE)) != 0) - && (mWindowManagerService.isHardKeyboardAvailable() - || (vis & (InputMethodService.IME_VISIBLE)) != 0); - final InputMethodInfo imi = mMethodMap.get(mCurMethodId); - if (imi != null && iconVisibility && needsToShowImeSwitchOngoingNotification()) { - // Used to load label - final CharSequence title = mRes.getText( - com.android.internal.R.string.select_input_method); - final CharSequence summary = InputMethodUtils.getImeAndSubtypeDisplayName( - mContext, imi, mCurrentSubtype); - - mImeSwitcherNotification.setLatestEventInfo( - mContext, title, summary, mImeSwitchPendingIntent); - if (mNotificationManager != null) { - if (DEBUG) { - Slog.d(TAG, "--- show notification: label = " + summary); - } - mNotificationManager.notifyAsUser(null, - com.android.internal.R.string.select_input_method, - mImeSwitcherNotification, UserHandle.ALL); - mNotificationShown = true; - } - } else { - if (mNotificationShown && mNotificationManager != null) { - if (DEBUG) { - Slog.d(TAG, "--- hide notification"); - } - mNotificationManager.cancelAsUser(null, - com.android.internal.R.string.select_input_method, UserHandle.ALL); - mNotificationShown = false; - } - } - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @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) { - SuggestionSpan ss = spans[i]; - if (!TextUtils.isEmpty(ss.getNotificationTargetClassName())) { - mSecureSuggestionSpans.put(ss, currentImi); - } - } - } - } - - @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. - if (targetImi != null) { - final String[] suggestions = span.getSuggestions(); - if (index < 0 || index >= suggestions.length) return false; - final String className = span.getNotificationTargetClassName(); - final Intent intent = new Intent(); - // Ensures that only a class in the original IME package will receive the - // notification. - intent.setClassName(targetImi.getPackageName(), className); - intent.setAction(SuggestionSpan.ACTION_SUGGESTION_PICKED); - 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()); - final long ident = Binder.clearCallingIdentity(); - try { - mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT); - } finally { - Binder.restoreCallingIdentity(ident); - } - return true; - } - } - return false; - } - - void updateFromSettingsLocked(boolean enabledMayChange) { - if (enabledMayChange) { - List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked(); - for (int i=0; i<enabled.size(); i++) { - // We allow the user to select "disabled until used" apps, so if they - // are enabling one of those here we now need to make it enabled. - InputMethodInfo imm = enabled.get(i); - try { - ApplicationInfo ai = mIPackageManager.getApplicationInfo(imm.getPackageName(), - PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, - mSettings.getCurrentUserId()); - if (ai != null && ai.enabledSetting - == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { - if (DEBUG) { - Slog.d(TAG, "Update state(" + imm.getId() - + "): DISABLED_UNTIL_USED -> DEFAULT"); - } - mIPackageManager.setApplicationEnabledSetting(imm.getPackageName(), - PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, - PackageManager.DONT_KILL_APP, mSettings.getCurrentUserId(), - mContext.getBasePackageName()); - } - } catch (RemoteException e) { - } - } - } - // We are assuming that whoever is changing DEFAULT_INPUT_METHOD and - // 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 = mSettings.getSelectedInputMethod(); - // There is no input method selected, try to choose new applicable input method. - if (TextUtils.isEmpty(id) && chooseNewDefaultIMELocked()) { - id = mSettings.getSelectedInputMethod(); - } - if (!TextUtils.isEmpty(id)) { - try { - setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id)); - } catch (IllegalArgumentException e) { - Slog.w(TAG, "Unknown input method from prefs: " + id, e); - mCurMethodId = null; - unbindCurrentMethodLocked(true, false); - } - mShortcutInputMethodsAndSubtypes.clear(); - } else { - // There is no longer an input method set, so stop any current one. - mCurMethodId = null; - unbindCurrentMethodLocked(true, false); - } - } - - /* package */ void setInputMethodLocked(String id, int subtypeId) { - InputMethodInfo info = mMethodMap.get(id); - if (info == null) { - throw new IllegalArgumentException("Unknown id: " + id); - } - - // See if we need to notify a subtype change within the same IME. - if (id.equals(mCurMethodId)) { - final int subtypeCount = info.getSubtypeCount(); - if (subtypeCount <= 0) { - return; - } - final InputMethodSubtype oldSubtype = mCurrentSubtype; - final InputMethodSubtype newSubtype; - if (subtypeId >= 0 && subtypeId < subtypeCount) { - newSubtype = info.getSubtypeAt(subtypeId); - } else { - // If subtype is null, try to find the most applicable one from - // getCurrentInputMethodSubtype. - newSubtype = getCurrentInputMethodSubtypeLocked(); - } - if (newSubtype == null || oldSubtype == null) { - Slog.w(TAG, "Illegal subtype state: old subtype = " + oldSubtype - + ", new subtype = " + newSubtype); - return; - } - if (newSubtype != oldSubtype) { - setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true); - if (mCurMethod != null) { - try { - refreshImeWindowVisibilityLocked(); - mCurMethod.changeInputMethodSubtype(newSubtype); - } catch (RemoteException e) { - Slog.w(TAG, "Failed to call changeInputMethodSubtype"); - } - } - } - return; - } - - // Changing to a different IME. - final long ident = Binder.clearCallingIdentity(); - try { - // Set a subtype to this input method. - // subtypeId the name of a subtype which will be set. - setSelectedInputMethodAndSubtypeLocked(info, subtypeId, false); - // mCurMethodId should be updated after setSelectedInputMethodAndSubtypeLocked() - // because mCurMethodId is stored as a history in - // setSelectedInputMethodAndSubtypeLocked(). - mCurMethodId = id; - - if (ActivityManagerNative.isSystemReady()) { - Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - intent.putExtra("input_method_id", id); - mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT); - } - unbindCurrentClientLocked(); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public boolean showSoftInput(IInputMethodClient client, int flags, - ResultReceiver resultReceiver) { - if (!calledFromValidUser()) { - return false; - } - int uid = Binder.getCallingUid(); - long ident = Binder.clearCallingIdentity(); - try { - synchronized (mMethodMap) { - if (mCurClient == null || client == null - || mCurClient.client.asBinder() != client.asBinder()) { - try { - // We need to check if this is the current client with - // focus in the window manager, to allow this call to - // be made before input is started in it. - if (!mIWindowManager.inputMethodClientHasFocus(client)) { - Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client); - return false; - } - } catch (RemoteException e) { - return false; - } - } - - if (DEBUG) Slog.v(TAG, "Client requesting input be shown"); - return showCurrentInputLocked(flags, resultReceiver); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - boolean showCurrentInputLocked(int flags, ResultReceiver resultReceiver) { - mShowRequested = true; - if ((flags&InputMethodManager.SHOW_IMPLICIT) == 0) { - mShowExplicitlyRequested = true; - } - if ((flags&InputMethodManager.SHOW_FORCED) != 0) { - mShowExplicitlyRequested = true; - mShowForced = true; - } - - if (!mSystemReady) { - return false; - } - - boolean res = false; - if (mCurMethod != null) { - if (DEBUG) Slog.d(TAG, "showCurrentInputLocked: mCurToken=" + mCurToken); - executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO( - MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod, - resultReceiver)); - mInputShown = true; - if (mHaveConnection && !mVisibleBound) { - bindCurrentInputMethodService( - mCurIntent, mVisibleConnection, Context.BIND_AUTO_CREATE); - mVisibleBound = true; - } - res = true; - } else if (mHaveConnection && SystemClock.uptimeMillis() - >= (mLastBindTime+TIME_TO_RECONNECT)) { - // The client has asked to have the input method shown, but - // we have been sitting here too long with a connection to the - // service and no interface received, so let's disconnect/connect - // to try to prod things along. - EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, mCurMethodId, - SystemClock.uptimeMillis()-mLastBindTime,1); - Slog.w(TAG, "Force disconnect/connect to the IME in showCurrentInputLocked()"); - mContext.unbindService(this); - 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; - } - - @Override - public boolean hideSoftInput(IInputMethodClient client, int flags, - ResultReceiver resultReceiver) { - if (!calledFromValidUser()) { - return false; - } - int uid = Binder.getCallingUid(); - long ident = Binder.clearCallingIdentity(); - try { - synchronized (mMethodMap) { - if (mCurClient == null || client == null - || mCurClient.client.asBinder() != client.asBinder()) { - try { - // We need to check if this is the current client with - // focus in the window manager, to allow this call to - // be made before input is started in it. - if (!mIWindowManager.inputMethodClientHasFocus(client)) { - if (DEBUG) Slog.w(TAG, "Ignoring hideSoftInput of uid " - + uid + ": " + client); - setImeWindowVisibilityStatusHiddenLocked(); - return false; - } - } catch (RemoteException e) { - setImeWindowVisibilityStatusHiddenLocked(); - return false; - } - } - - if (DEBUG) Slog.v(TAG, "Client requesting input be hidden"); - return hideCurrentInputLocked(flags, resultReceiver); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver) { - if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0 - && (mShowExplicitlyRequested || mShowForced)) { - if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide"); - return false; - } - if (mShowForced && (flags&InputMethodManager.HIDE_NOT_ALWAYS) != 0) { - if (DEBUG) Slog.v(TAG, "Not hiding: forced show not cancelled by not-always hide"); - return false; - } - boolean res; - if (mInputShown && mCurMethod != null) { - executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( - MSG_HIDE_SOFT_INPUT, mCurMethod, resultReceiver)); - res = true; - } else { - res = false; - } - if (mHaveConnection && mVisibleBound) { - mContext.unbindService(mVisibleConnection); - mVisibleBound = false; - } - mInputShown = false; - mShowRequested = false; - mShowExplicitlyRequested = false; - mShowForced = false; - return res; - } - - @Override - public InputBindResult windowGainedFocus(IInputMethodClient client, IBinder windowToken, - int controlFlags, int softInputMode, int windowFlags, - EditorInfo attribute, IInputContext inputContext) { - // Needs to check the validity before clearing calling identity - final boolean calledFromValidUser = calledFromValidUser(); - - InputBindResult res = null; - long ident = Binder.clearCallingIdentity(); - try { - synchronized (mMethodMap) { - if (DEBUG) Slog.v(TAG, "windowGainedFocus: " + client.asBinder() - + " controlFlags=#" + Integer.toHexString(controlFlags) - + " softInputMode=#" + Integer.toHexString(softInputMode) - + " windowFlags=#" + Integer.toHexString(windowFlags)); - - ClientState cs = mClients.get(client.asBinder()); - if (cs == null) { - throw new IllegalArgumentException("unknown client " - + client.asBinder()); - } - - try { - if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) { - // Check with the window manager to make sure this client actually - // has a window with focus. If not, reject. This is thread safe - // because if the focus changes some time before or after, the - // next client receiving focus that has any interest in input will - // be calling through here after that change happens. - Slog.w(TAG, "Focus gain on non-focused client " + cs.client - + " (uid=" + cs.uid + " pid=" + cs.pid + ")"); - return null; - } - } catch (RemoteException e) { - } - - if (!calledFromValidUser) { - Slog.w(TAG, "A background user is requesting window. Hiding IME."); - Slog.w(TAG, "If you want to interect with IME, you need " - + "android.permission.INTERACT_ACROSS_USERS_FULL"); - hideCurrentInputLocked(0, null); - return null; - } - - if (mCurFocusedWindow == windowToken) { - Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client - + " attribute=" + attribute + ", token = " + windowToken); - if (attribute != null) { - return startInputUncheckedLocked(cs, inputContext, attribute, - controlFlags); - } - return null; - } - mCurFocusedWindow = windowToken; - - // Should we auto-show the IME even if the caller has not - // specified what should be done with it? - // We only do this automatically if the window can resize - // to accommodate the IME (so what the user sees will give - // them good context without input information being obscured - // by the IME) or if running on a large screen where there - // is more room for the target window + IME. - final boolean doAutoShow = - (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) - == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE - || mRes.getConfiguration().isLayoutSizeAtLeast( - Configuration.SCREENLAYOUT_SIZE_LARGE); - final boolean isTextEditor = - (controlFlags&InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR) != 0; - - // We want to start input before showing the IME, but after closing - // it. We want to do this after closing it to help the IME disappear - // more quickly (not get stuck behind it initializing itself for the - // new focused input, even if its window wants to hide the IME). - boolean didStart = false; - - switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) { - case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: - if (!isTextEditor || !doAutoShow) { - if (WindowManager.LayoutParams.mayUseInputMethod(windowFlags)) { - // There is no focus view, and this window will - // be behind any soft input window, so hide the - // soft input window if it is shown. - if (DEBUG) Slog.v(TAG, "Unspecified window will hide input"); - hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null); - } - } else if (isTextEditor && doAutoShow && (softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { - // There is a focus view, and we are navigating forward - // into the window, so show the input window for the user. - // We only do this automatically if the window can resize - // to accommodate the IME (so what the user sees will give - // them good context without input information being obscured - // by the IME) or if running on a large screen where there - // is more room for the target window + IME. - if (DEBUG) Slog.v(TAG, "Unspecified window will show input"); - if (attribute != null) { - res = startInputUncheckedLocked(cs, inputContext, attribute, - controlFlags); - didStart = true; - } - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); - } - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED: - // Do nothing. - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN: - if ((softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { - if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward"); - hideCurrentInputLocked(0, null); - } - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN: - if (DEBUG) Slog.v(TAG, "Window asks to hide input"); - hideCurrentInputLocked(0, null); - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE: - if ((softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { - if (DEBUG) Slog.v(TAG, "Window asks to show input going forward"); - if (attribute != null) { - res = startInputUncheckedLocked(cs, inputContext, attribute, - controlFlags); - didStart = true; - } - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); - } - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: - if (DEBUG) Slog.v(TAG, "Window asks to always show input"); - if (attribute != null) { - res = startInputUncheckedLocked(cs, inputContext, attribute, - controlFlags); - didStart = true; - } - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); - break; - } - - if (!didStart && attribute != null) { - res = startInputUncheckedLocked(cs, inputContext, attribute, - controlFlags); - } - } - } finally { - Binder.restoreCallingIdentity(ident); - } - - return res; - } - - @Override - public void showInputMethodPickerFromClient(IInputMethodClient client) { - if (!calledFromValidUser()) { - return; - } - synchronized (mMethodMap) { - if (mCurClient == null || client == null - || mCurClient.client.asBinder() != client.asBinder()) { - Slog.w(TAG, "Ignoring showInputMethodPickerFromClient of uid " - + Binder.getCallingUid() + ": " + client); - } - - // Always call subtype picker, because subtype picker is a superset of input method - // picker. - mHandler.sendEmptyMessage(MSG_SHOW_IM_SUBTYPE_PICKER); - } - } - - @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, InputMethodUtils.getSubtypeIdFromHashCode( - mMethodMap.get(id), subtype.hashCode())); - } else { - setInputMethod(token, id); - } - } - } - - @Override - public void showInputMethodAndSubtypeEnablerFromClient( - IInputMethodClient client, String inputMethodId) { - if (!calledFromValidUser()) { - return; - } - synchronized (mMethodMap) { - if (mCurClient == null || client == null - || mCurClient.client.asBinder() != client.asBinder()) { - Slog.w(TAG, "Ignoring showInputMethodAndSubtypeEnablerFromClient of: " + client); - } - executeOrSendMessage(mCurMethod, mCaller.obtainMessageO( - MSG_SHOW_IM_SUBTYPE_ENABLER, inputMethodId)); - } - } - - @Override - public boolean switchToLastInputMethod(IBinder token) { - if (!calledFromValidUser()) { - return false; - } - synchronized (mMethodMap) { - final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked(); - final InputMethodInfo lastImi; - if (lastIme != null) { - lastImi = mMethodMap.get(lastIme.first); - } else { - lastImi = null; - } - String targetLastImiId = null; - int subtypeId = NOT_A_SUBTYPE_ID; - if (lastIme != null && lastImi != null) { - final boolean imiIdIsSame = lastImi.getId().equals(mCurMethodId); - final int lastSubtypeHash = Integer.valueOf(lastIme.second); - final int currentSubtypeHash = mCurrentSubtype == null ? NOT_A_SUBTYPE_ID - : mCurrentSubtype.hashCode(); - // If the last IME is the same as the current IME and the last subtype is not - // defined, there is no need to switch to the last IME. - if (!imiIdIsSame || lastSubtypeHash != currentSubtypeHash) { - targetLastImiId = lastIme.first; - subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(lastImi, lastSubtypeHash); - } - } - - if (TextUtils.isEmpty(targetLastImiId) - && !InputMethodUtils.canAddToLastInputMethod(mCurrentSubtype)) { - // This is a safety net. If the currentSubtype can't be added to the history - // and the framework couldn't find the last ime, we will make the last ime be - // the most applicable enabled keyboard subtype of the system imes. - final List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked(); - if (enabled != null) { - final int N = enabled.size(); - final String locale = mCurrentSubtype == null - ? mRes.getConfiguration().locale.toString() - : mCurrentSubtype.getLocale(); - for (int i = 0; i < N; ++i) { - final InputMethodInfo imi = enabled.get(i); - if (imi.getSubtypeCount() > 0 && InputMethodUtils.isSystemIme(imi)) { - InputMethodSubtype keyboardSubtype = - InputMethodUtils.findLastResortApplicableSubtypeLocked(mRes, - InputMethodUtils.getSubtypes(imi), - InputMethodUtils.SUBTYPE_MODE_KEYBOARD, locale, true); - if (keyboardSubtype != null) { - targetLastImiId = imi.getId(); - subtypeId = InputMethodUtils.getSubtypeIdFromHashCode( - imi, keyboardSubtype.hashCode()); - if(keyboardSubtype.getLocale().equals(locale)) { - break; - } - } - } - } - } - } - - if (!TextUtils.isEmpty(targetLastImiId)) { - if (DEBUG) { - Slog.d(TAG, "Switch to: " + lastImi.getId() + ", " + lastIme.second - + ", from: " + mCurMethodId + ", " + subtypeId); - } - setInputMethodWithSubtypeId(token, targetLastImiId, subtypeId); - return true; - } else { - return false; - } - } - } - - @Override - public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) { - if (!calledFromValidUser()) { - return false; - } - synchronized (mMethodMap) { - final ImeSubtypeListItem nextSubtype = mImListManager.getNextInputMethod( - onlyCurrentIme, mMethodMap.get(mCurMethodId), mCurrentSubtype); - if (nextSubtype == null) { - return false; - } - setInputMethodWithSubtypeId(token, nextSubtype.mImi.getId(), nextSubtype.mSubtypeId); - return true; - } - } - - @Override - public boolean shouldOfferSwitchingToNextInputMethod(IBinder token) { - if (!calledFromValidUser()) { - return false; - } - synchronized (mMethodMap) { - final ImeSubtypeListItem nextSubtype = mImListManager.getNextInputMethod( - false /* onlyCurrentIme */, mMethodMap.get(mCurMethodId), mCurrentSubtype); - if (nextSubtype == null) { - return false; - } - return true; - } - } - - @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 - if (lastIme == null || TextUtils.isEmpty(lastIme.first) - || TextUtils.isEmpty(lastIme.second)) return null; - final InputMethodInfo lastImi = mMethodMap.get(lastIme.first); - if (lastImi == null) return null; - try { - final int lastSubtypeHash = Integer.valueOf(lastIme.second); - final int lastSubtypeId = - InputMethodUtils.getSubtypeIdFromHashCode(lastImi, lastSubtypeHash); - if (lastSubtypeId < 0 || lastSubtypeId >= lastImi.getSubtypeCount()) { - return null; - } - return lastImi.getSubtypeAt(lastSubtypeId); - } catch (NumberFormatException e) { - return null; - } - } - } - - @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 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) { - if (packageInfos[i].equals(imi.getPackageName())) { - mFileManager.addInputMethodSubtypes(imi, subtypes); - final long ident = Binder.clearCallingIdentity(); - try { - buildInputMethodListLocked(mMethodList, mMethodMap, - false /* resetDefaultEnabledIme */); - } finally { - Binder.restoreCallingIdentity(ident); - } - return; - } - } - } - } - return; - } - - private void setInputMethodWithSubtypeId(IBinder token, String id, int subtypeId) { - synchronized (mMethodMap) { - if (token == null) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException( - "Using null token requires permission " - + android.Manifest.permission.WRITE_SECURE_SETTINGS); - } - } else if (mCurToken != token) { - Slog.w(TAG, "Ignoring setInputMethod of uid " + Binder.getCallingUid() - + " token: " + token); - return; - } - - final long ident = Binder.clearCallingIdentity(); - try { - setInputMethodLocked(id, subtypeId); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - @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 " - + Binder.getCallingUid() + " token: " + token); - return; - } - long ident = Binder.clearCallingIdentity(); - try { - hideCurrentInputLocked(flags, null); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - @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 " - + Binder.getCallingUid() + " token: " + token); - return; - } - long ident = Binder.clearCallingIdentity(); - try { - showCurrentInputLocked(flags, null); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - void setEnabledSessionInMainThread(SessionState session) { - if (mEnabledSession != session) { - if (mEnabledSession != null) { - try { - if (DEBUG) Slog.v(TAG, "Disabling: " + mEnabledSession); - mEnabledSession.method.setSessionEnabled( - mEnabledSession.session, false); - } catch (RemoteException e) { - } - } - mEnabledSession = session; - try { - if (DEBUG) Slog.v(TAG, "Enabling: " + mEnabledSession); - session.method.setSessionEnabled( - session.session, true); - } catch (RemoteException e) { - } - } - } - - @Override - public boolean handleMessage(Message msg) { - SomeArgs args; - switch (msg.what) { - case MSG_SHOW_IM_PICKER: - showInputMethodMenu(); - return true; - - case MSG_SHOW_IM_SUBTYPE_PICKER: - showInputMethodSubtypeMenu(); - return true; - - case MSG_SHOW_IM_SUBTYPE_ENABLER: - args = (SomeArgs)msg.obj; - showInputMethodAndSubtypeEnabler((String)args.arg1); - args.recycle(); - return true; - - case MSG_SHOW_IM_CONFIG: - showConfigureInputMethods(); - return true; - - // --------------------------------------------------------- - - case MSG_UNBIND_INPUT: - try { - ((IInputMethod)msg.obj).unbindInput(); - } catch (RemoteException e) { - // There is nothing interesting about the method dying. - } - return true; - case MSG_BIND_INPUT: - 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 = (SomeArgs)msg.obj; - try { - if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput(" - + msg.arg1 + ", " + args.arg2 + ")"); - ((IInputMethod)args.arg1).showSoftInput(msg.arg1, (ResultReceiver)args.arg2); - } catch (RemoteException e) { - } - args.recycle(); - return true; - case MSG_HIDE_SOFT_INPUT: - args = (SomeArgs)msg.obj; - try { - if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, " - + args.arg2 + ")"); - ((IInputMethod)args.arg1).hideSoftInput(0, (ResultReceiver)args.arg2); - } catch (RemoteException e) { - } - args.recycle(); - return true; - case MSG_ATTACH_TOKEN: - 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 = (SomeArgs)msg.obj; - IInputMethod method = (IInputMethod)args.arg1; - InputChannel channel = (InputChannel)args.arg2; - try { - method.createSession(channel, (IInputSessionCallback)args.arg3); - } catch (RemoteException e) { - } finally { - // Dispose the channel if the input method is not local to this process - // because the remote proxy will get its own copy when unparceled. - if (channel != null && Binder.isProxy(method)) { - channel.dispose(); - } - } - args.recycle(); - return true; - } - // --------------------------------------------------------- - - case MSG_START_INPUT: - args = (SomeArgs)msg.obj; - try { - SessionState session = (SessionState)args.arg1; - setEnabledSessionInMainThread(session); - session.method.startInput((IInputContext)args.arg2, - (EditorInfo)args.arg3); - } catch (RemoteException e) { - } - args.recycle(); - return true; - case MSG_RESTART_INPUT: - args = (SomeArgs)msg.obj; - try { - SessionState session = (SessionState)args.arg1; - setEnabledSessionInMainThread(session); - session.method.restartInput((IInputContext)args.arg2, - (EditorInfo)args.arg3); - } catch (RemoteException e) { - } - args.recycle(); - return true; - - // --------------------------------------------------------- - - case MSG_UNBIND_METHOD: - try { - ((IInputMethodClient)msg.obj).onUnbindMethod(msg.arg1); - } catch (RemoteException e) { - // There is nothing interesting about the last client dying. - } - return true; - case MSG_BIND_METHOD: { - args = (SomeArgs)msg.obj; - IInputMethodClient client = (IInputMethodClient)args.arg1; - InputBindResult res = (InputBindResult)args.arg2; - try { - client.onBindMethod(res); - } catch (RemoteException e) { - Slog.w(TAG, "Client died receiving input method " + args.arg2); - } finally { - // Dispose the channel if the input method is not local to this process - // because the remote proxy will get its own copy when unparceled. - if (res.channel != null && Binder.isProxy(client)) { - res.channel.dispose(); - } - } - args.recycle(); - return true; - } - case MSG_SET_ACTIVE: - try { - ((ClientState)msg.obj).client.setActive(msg.arg1 != 0); - } catch (RemoteException e) { - Slog.w(TAG, "Got RemoteException sending setActive(false) notification to pid " - + ((ClientState)msg.obj).pid + " uid " - + ((ClientState)msg.obj).uid); - } - return true; - - // -------------------------------------------------------------- - case MSG_HARD_KEYBOARD_SWITCH_CHANGED: - mHardKeyboardListener.handleHardKeyboardStatusChange( - msg.arg1 == 1, msg.arg2 == 1); - return true; - } - return false; - } - - private boolean chooseNewDefaultIMELocked() { - final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME( - mSettings.getEnabledInputMethodListLocked()); - if (imi != null) { - if (DEBUG) { - Slog.d(TAG, "New default IME was selected: " + imi.getId()); - } - resetSelectedInputMethodAndSubtypeLocked(imi.getId()); - return true; - } - - return false; - } - - void buildInputMethodListLocked(ArrayList<InputMethodInfo> list, - HashMap<String, InputMethodInfo> map, boolean resetDefaultEnabledIme) { - if (DEBUG) { - Slog.d(TAG, "--- re-buildInputMethodList reset = " + resetDefaultEnabledIme - + " \n ------ \n" + InputMethodUtils.getStackTrace()); - } - list.clear(); - map.clear(); - - // Use for queryIntentServicesAsUser - final PackageManager pm = mContext.getPackageManager(); - String disabledSysImes = mSettings.getDisabledSystemInputMethods(); - if (disabledSysImes == null) disabledSysImes = ""; - - final List<ResolveInfo> services = pm.queryIntentServicesAsUser( - new Intent(InputMethod.SERVICE_INTERFACE), - PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, - mSettings.getCurrentUserId()); - - final HashMap<String, List<InputMethodSubtype>> additionalSubtypes = - mFileManager.getAllAdditionalInputMethodSubtypes(); - for (int i = 0; i < services.size(); ++i) { - ResolveInfo ri = services.get(i); - ServiceInfo si = ri.serviceInfo; - ComponentName compName = new ComponentName(si.packageName, si.name); - if (!android.Manifest.permission.BIND_INPUT_METHOD.equals( - si.permission)) { - Slog.w(TAG, "Skipping input method " + compName - + ": it does not require the permission " - + android.Manifest.permission.BIND_INPUT_METHOD); - continue; - } - - if (DEBUG) Slog.d(TAG, "Checking " + compName); - - try { - InputMethodInfo p = new InputMethodInfo(mContext, ri, additionalSubtypes); - list.add(p); - final String id = p.getId(); - map.put(id, p); - - if (DEBUG) { - Slog.d(TAG, "Found an input method " + p); - } - - } catch (XmlPullParserException e) { - Slog.w(TAG, "Unable to load input method " + compName, e); - } catch (IOException e) { - Slog.w(TAG, "Unable to load input method " + compName, e); - } - } - - if (resetDefaultEnabledIme) { - final ArrayList<InputMethodInfo> defaultEnabledIme = - InputMethodUtils.getDefaultEnabledImes(mContext, mSystemReady, list); - for (int i = 0; i < defaultEnabledIme.size(); ++i) { - final InputMethodInfo imi = defaultEnabledIme.get(i); - if (DEBUG) { - Slog.d(TAG, "--- enable ime = " + imi); - } - setInputMethodEnabledLocked(imi.getId(), true); - } - } - - final String defaultImiId = mSettings.getSelectedInputMethod(); - if (!TextUtils.isEmpty(defaultImiId)) { - if (!map.containsKey(defaultImiId)) { - Slog.w(TAG, "Default IME is uninstalled. Choose new default IME."); - if (chooseNewDefaultIMELocked()) { - updateFromSettingsLocked(true); - } - } else { - // Double check that the default IME is certainly enabled. - setInputMethodEnabledLocked(defaultImiId, true); - } - } - } - - // ---------------------------------------------------------------------- - - private void showInputMethodMenu() { - showInputMethodMenuInternal(false); - } - - private void showInputMethodSubtypeMenu() { - showInputMethodMenuInternal(true); - } - - private void showInputMethodAndSubtypeEnabler(String inputMethodId) { - Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED - | Intent.FLAG_ACTIVITY_CLEAR_TOP); - if (!TextUtils.isEmpty(inputMethodId)) { - intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, inputMethodId); - } - mContext.startActivityAsUser(intent, null, UserHandle.CURRENT); - } - - private void showConfigureInputMethods() { - Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED - | Intent.FLAG_ACTIVITY_CLEAR_TOP); - mContext.startActivityAsUser(intent, null, UserHandle.CURRENT); - } - - private boolean isScreenLocked() { - return mKeyguardManager != null - && mKeyguardManager.isKeyguardLocked() && mKeyguardManager.isKeyguardSecure(); - } - private void showInputMethodMenuInternal(boolean showSubtypes) { - if (DEBUG) Slog.v(TAG, "Show switching menu"); - - final Context context = mContext; - final boolean isScreenLocked = isScreenLocked(); - - final String lastInputMethodId = mSettings.getSelectedInputMethod(); - int lastInputMethodSubtypeId = mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId); - if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId); - - synchronized (mMethodMap) { - final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis = - getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(); - if (immis == null || immis.size() == 0) { - return; - } - - hideInputMethodMenuLocked(); - - final List<ImeSubtypeListItem> imList = - mImListManager.getSortedInputMethodAndSubtypeList( - showSubtypes, mInputShown, isScreenLocked); - - if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) { - final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtypeLocked(); - if (currentSubtype != null) { - final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId); - lastInputMethodSubtypeId = InputMethodUtils.getSubtypeIdFromHashCode( - currentImi, currentSubtype.hashCode()); - } - } - - final int N = imList.size(); - mIms = new InputMethodInfo[N]; - mSubtypeIds = new int[N]; - int checkedItem = 0; - for (int i = 0; i < N; ++i) { - final ImeSubtypeListItem item = imList.get(i); - mIms[i] = item.mImi; - mSubtypeIds[i] = item.mSubtypeId; - if (mIms[i].getId().equals(lastInputMethodId)) { - int subtypeId = mSubtypeIds[i]; - if ((subtypeId == NOT_A_SUBTYPE_ID) - || (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID && subtypeId == 0) - || (subtypeId == lastInputMethodSubtypeId)) { - checkedItem = i; - } - } - } - final TypedArray a = context.obtainStyledAttributes(null, - com.android.internal.R.styleable.DialogPreference, - com.android.internal.R.attr.alertDialogStyle, 0); - mDialogBuilder = new AlertDialog.Builder(context) - .setOnCancelListener(new OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - hideInputMethodMenu(); - } - }) - .setIcon(a.getDrawable( - com.android.internal.R.styleable.DialogPreference_dialogTitle)); - a.recycle(); - final LayoutInflater inflater = - (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - final View tv = inflater.inflate( - com.android.internal.R.layout.input_method_switch_dialog_title, null); - mDialogBuilder.setCustomTitle(tv); - - // Setup layout for a toggle switch of the hardware keyboard - mSwitchingDialogTitleView = tv; - mSwitchingDialogTitleView.findViewById( - com.android.internal.R.id.hard_keyboard_section).setVisibility( - mWindowManagerService.isHardKeyboardAvailable() ? - View.VISIBLE : View.GONE); - final Switch hardKeySwitch = ((Switch)mSwitchingDialogTitleView.findViewById( - com.android.internal.R.id.hard_keyboard_switch)); - hardKeySwitch.setChecked(mWindowManagerService.isHardKeyboardEnabled()); - hardKeySwitch.setOnCheckedChangeListener( - new OnCheckedChangeListener() { - @Override - public void onCheckedChanged( - CompoundButton buttonView, boolean isChecked) { - mWindowManagerService.setHardKeyboardEnabled(isChecked); - // Ensure that the input method dialog is dismissed when changing - // the hardware keyboard state. - hideInputMethodMenu(); - } - }); - - final ImeSubtypeListAdapter adapter = new ImeSubtypeListAdapter(context, - com.android.internal.R.layout.simple_list_item_2_single_choice, imList, - checkedItem); - - mDialogBuilder.setSingleChoiceItems(adapter, checkedItem, - new AlertDialog.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - synchronized (mMethodMap) { - if (mIms == null || mIms.length <= which - || mSubtypeIds == null || mSubtypeIds.length <= which) { - return; - } - InputMethodInfo im = mIms[which]; - int subtypeId = mSubtypeIds[which]; - adapter.mCheckedItem = which; - adapter.notifyDataSetChanged(); - hideInputMethodMenu(); - if (im != null) { - if ((subtypeId < 0) - || (subtypeId >= im.getSubtypeCount())) { - subtypeId = NOT_A_SUBTYPE_ID; - } - setInputMethodLocked(im.getId(), subtypeId); - } - } - } - }); - - if (showSubtypes && !isScreenLocked) { - mDialogBuilder.setPositiveButton( - com.android.internal.R.string.configure_input_methods, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int whichButton) { - showConfigureInputMethods(); - } - }); - } - mSwitchingDialog = mDialogBuilder.create(); - 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(); - } - } - - private static class ImeSubtypeListItem implements Comparable<ImeSubtypeListItem> { - public final CharSequence mImeName; - public final CharSequence mSubtypeName; - public final InputMethodInfo mImi; - public final int mSubtypeId; - private final boolean mIsSystemLocale; - private final boolean mIsSystemLanguage; - - public ImeSubtypeListItem(CharSequence imeName, CharSequence subtypeName, - InputMethodInfo imi, int subtypeId, String subtypeLocale, String systemLocale) { - mImeName = imeName; - mSubtypeName = subtypeName; - mImi = imi; - mSubtypeId = subtypeId; - if (TextUtils.isEmpty(subtypeLocale)) { - mIsSystemLocale = false; - mIsSystemLanguage = false; - } else { - mIsSystemLocale = subtypeLocale.equals(systemLocale); - mIsSystemLanguage = mIsSystemLocale - || subtypeLocale.startsWith(systemLocale.substring(0, 2)); - } - } - - @Override - public int compareTo(ImeSubtypeListItem other) { - if (TextUtils.isEmpty(mImeName)) { - return 1; - } - if (TextUtils.isEmpty(other.mImeName)) { - return -1; - } - if (!TextUtils.equals(mImeName, other.mImeName)) { - return mImeName.toString().compareTo(other.mImeName.toString()); - } - if (TextUtils.equals(mSubtypeName, other.mSubtypeName)) { - return 0; - } - if (mIsSystemLocale) { - return -1; - } - if (other.mIsSystemLocale) { - return 1; - } - if (mIsSystemLanguage) { - return -1; - } - if (other.mIsSystemLanguage) { - return 1; - } - if (TextUtils.isEmpty(mSubtypeName)) { - return 1; - } - if (TextUtils.isEmpty(other.mSubtypeName)) { - return -1; - } - return mSubtypeName.toString().compareTo(other.mSubtypeName.toString()); - } - } - - private static class ImeSubtypeListAdapter extends ArrayAdapter<ImeSubtypeListItem> { - private final LayoutInflater mInflater; - private final int mTextViewResourceId; - private final List<ImeSubtypeListItem> mItemsList; - public int mCheckedItem; - public ImeSubtypeListAdapter(Context context, int textViewResourceId, - List<ImeSubtypeListItem> itemsList, int checkedItem) { - super(context, textViewResourceId, itemsList); - mTextViewResourceId = textViewResourceId; - mItemsList = itemsList; - mCheckedItem = checkedItem; - mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - final View view = convertView != null ? convertView - : mInflater.inflate(mTextViewResourceId, null); - if (position < 0 || position >= mItemsList.size()) return view; - final ImeSubtypeListItem item = mItemsList.get(position); - final CharSequence imeName = item.mImeName; - final CharSequence subtypeName = item.mSubtypeName; - final TextView firstTextView = (TextView)view.findViewById(android.R.id.text1); - final TextView secondTextView = (TextView)view.findViewById(android.R.id.text2); - if (TextUtils.isEmpty(subtypeName)) { - firstTextView.setText(imeName); - secondTextView.setVisibility(View.GONE); - } else { - firstTextView.setText(subtypeName); - secondTextView.setText(imeName); - secondTextView.setVisibility(View.VISIBLE); - } - final RadioButton radioButton = - (RadioButton)view.findViewById(com.android.internal.R.id.radio); - radioButton.setChecked(position == mCheckedItem); - return view; - } - } - - void hideInputMethodMenu() { - synchronized (mMethodMap) { - hideInputMethodMenuLocked(); - } - } - - void hideInputMethodMenuLocked() { - if (DEBUG) Slog.v(TAG, "Hide switching menu"); - - if (mSwitchingDialog != null) { - mSwitchingDialog.dismiss(); - mSwitchingDialog = null; - } - - mDialogBuilder = null; - mIms = null; - } - - // ---------------------------------------------------------------------- - - @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) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException( - "Requires permission " - + android.Manifest.permission.WRITE_SECURE_SETTINGS); - } - - long ident = Binder.clearCallingIdentity(); - try { - return setInputMethodEnabledLocked(id, enabled); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - boolean setInputMethodEnabledLocked(String id, boolean enabled) { - // Make sure this is a valid input method. - InputMethodInfo imm = mMethodMap.get(id); - if (imm == null) { - throw new IllegalArgumentException("Unknown id: " + mCurMethodId); - } - - List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings - .getEnabledInputMethodsAndSubtypeListLocked(); - - if (enabled) { - for (Pair<String, ArrayList<String>> pair: enabledInputMethodsList) { - if (pair.first.equals(id)) { - // We are enabling this input method, but it is already enabled. - // Nothing to do. The previous state was enabled. - return true; - } - } - mSettings.appendAndPutEnabledInputMethodLocked(id, false); - // Previous state was disabled. - return false; - } else { - StringBuilder builder = new StringBuilder(); - if (mSettings.buildAndPutEnabledInputMethodsStrRemovingIdLocked( - builder, enabledInputMethodsList, id)) { - // Disabled input method is currently selected, switch to another one. - final String selId = mSettings.getSelectedInputMethod(); - if (id.equals(selId) && !chooseNewDefaultIMELocked()) { - Slog.i(TAG, "Can't find new IME, unsetting the current input method."); - resetSelectedInputMethodAndSubtypeLocked(""); - } - // Previous state was enabled. - return true; - } else { - // We are disabling the input method but it is already disabled. - // Nothing to do. The previous state was disabled. - return false; - } - } - } - - private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId, - boolean setSubtypeOnly) { - // Update the history of InputMethod and Subtype - mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype); - - // Set Subtype here - if (imi == null || subtypeId < 0) { - mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID); - mCurrentSubtype = null; - } else { - if (subtypeId < imi.getSubtypeCount()) { - InputMethodSubtype subtype = imi.getSubtypeAt(subtypeId); - mSettings.putSelectedSubtype(subtype.hashCode()); - mCurrentSubtype = subtype; - } else { - mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID); - // If the subtype is not specified, choose the most applicable one - mCurrentSubtype = getCurrentInputMethodSubtypeLocked(); - } - } - - // Workaround. - // ASEC is not ready in the IMMS constructor. Accordingly, forward-locked - // IMEs are not recognized and considered uninstalled. - // Actually, we can't move everything after SystemReady because - // IMMS needs to run in the encryption lock screen. So, we just skip changing - // the default IME here and try cheking the default IME again in systemReady(). - // TODO: Do nothing before system ready and implement a separated logic for - // the encryption lock screen. - // TODO: ASEC should be ready before IMMS is instantiated. - if (mSystemReady && !setSubtypeOnly) { - // Set InputMethod here - mSettings.putSelectedInputMethod(imi != null ? imi.getId() : ""); - } - } - - private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) { - InputMethodInfo imi = mMethodMap.get(newDefaultIme); - int lastSubtypeId = NOT_A_SUBTYPE_ID; - // newDefaultIme is empty when there is no candidate for the selected IME. - if (imi != null && !TextUtils.isEmpty(newDefaultIme)) { - String subtypeHashCode = mSettings.getLastSubtypeForInputMethodLocked(newDefaultIme); - if (subtypeHashCode != null) { - try { - lastSubtypeId = InputMethodUtils.getSubtypeIdFromHashCode( - imi, Integer.valueOf(subtypeHashCode)); - } catch (NumberFormatException e) { - Slog.w(TAG, "HashCode for subtype looks broken: " + subtypeHashCode, e); - } - } - } - setSelectedInputMethodAndSubtypeLocked(imi, lastSubtypeId, false); - } - - // If there are no selected shortcuts, tries finding the most applicable ones. - private Pair<InputMethodInfo, InputMethodSubtype> - findLastResortApplicableShortcutInputMethodAndSubtypeLocked(String mode) { - List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked(); - InputMethodInfo mostApplicableIMI = null; - InputMethodSubtype mostApplicableSubtype = null; - boolean foundInSystemIME = false; - - // Search applicable subtype for each InputMethodInfo - for (InputMethodInfo imi: imis) { - final String imiId = imi.getId(); - if (foundInSystemIME && !imiId.equals(mCurMethodId)) { - continue; - } - InputMethodSubtype subtype = null; - final List<InputMethodSubtype> enabledSubtypes = - mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true); - // 1. Search by the current subtype's locale from enabledSubtypes. - if (mCurrentSubtype != null) { - subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked( - mRes, enabledSubtypes, mode, mCurrentSubtype.getLocale(), false); - } - // 2. Search by the system locale from enabledSubtypes. - // 3. Search the first enabled subtype matched with mode from enabledSubtypes. - if (subtype == null) { - subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked( - mRes, enabledSubtypes, mode, null, true); - } - final ArrayList<InputMethodSubtype> overridingImplicitlyEnabledSubtypes = - InputMethodUtils.getOverridingImplicitlyEnabledSubtypes(imi, mode); - final ArrayList<InputMethodSubtype> subtypesForSearch = - overridingImplicitlyEnabledSubtypes.isEmpty() - ? InputMethodUtils.getSubtypes(imi) - : overridingImplicitlyEnabledSubtypes; - // 4. Search by the current subtype's locale from all subtypes. - if (subtype == null && mCurrentSubtype != null) { - subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked( - mRes, subtypesForSearch, mode, mCurrentSubtype.getLocale(), false); - } - // 5. Search by the system locale from all subtypes. - // 6. Search the first enabled subtype matched with mode from all subtypes. - if (subtype == null) { - subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked( - mRes, subtypesForSearch, mode, null, true); - } - if (subtype != null) { - if (imiId.equals(mCurMethodId)) { - // The current input method is the most applicable IME. - mostApplicableIMI = imi; - mostApplicableSubtype = subtype; - break; - } else if (!foundInSystemIME) { - // The system input method is 2nd applicable IME. - mostApplicableIMI = imi; - mostApplicableSubtype = subtype; - if ((imi.getServiceInfo().applicationInfo.flags - & ApplicationInfo.FLAG_SYSTEM) != 0) { - foundInSystemIME = true; - } - } - } - } - if (DEBUG) { - if (mostApplicableIMI != null) { - Slog.w(TAG, "Most applicable shortcut input method was:" - + mostApplicableIMI.getId()); - if (mostApplicableSubtype != null) { - Slog.w(TAG, "Most applicable shortcut input method subtype was:" - + "," + mostApplicableSubtype.getMode() + "," - + mostApplicableSubtype.getLocale()); - } - } - } - if (mostApplicableIMI != null) { - return new Pair<InputMethodInfo, InputMethodSubtype> (mostApplicableIMI, - mostApplicableSubtype); - } else { - return null; - } - } - - /** - * @return Return the current subtype of this input method. - */ - @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; - } - final boolean subtypeIsSelected = mSettings.isSubtypeSelected(); - final InputMethodInfo imi = mMethodMap.get(mCurMethodId); - if (imi == null || imi.getSubtypeCount() == 0) { - return null; - } - if (!subtypeIsSelected || mCurrentSubtype == null - || !InputMethodUtils.isValidSubtypeId(imi, mCurrentSubtype.hashCode())) { - int subtypeId = mSettings.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 = - mSettings.getEnabledInputMethodSubtypeListLocked(mContext, 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 = InputMethodUtils.findLastResortApplicableSubtypeLocked( - mRes, explicitlyOrImplicitlyEnabledSubtypes, - InputMethodUtils.SUBTYPE_MODE_KEYBOARD, null, true); - if (mCurrentSubtype == null) { - mCurrentSubtype = InputMethodUtils.findLastResortApplicableSubtypeLocked( - mRes, explicitlyOrImplicitlyEnabledSubtypes, null, null, - true); - } - } - } else { - mCurrentSubtype = InputMethodUtils.getSubtypes(imi).get(subtypeId); - } - } - return mCurrentSubtype; - } - - private void addShortcutInputMethodAndSubtypes(InputMethodInfo imi, - InputMethodSubtype subtype) { - if (mShortcutInputMethodsAndSubtypes.containsKey(imi)) { - mShortcutInputMethodsAndSubtypes.get(imi).add(subtype); - } else { - ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); - subtypes.add(subtype); - mShortcutInputMethodsAndSubtypes.put(imi, subtypes); - } - } - - // TODO: We should change the return type from List to List<Parcelable> - @SuppressWarnings("rawtypes") - @Override - public List getShortcutInputMethodsAndSubtypes() { - synchronized (mMethodMap) { - ArrayList<Object> ret = new ArrayList<Object>(); - if (mShortcutInputMethodsAndSubtypes.size() == 0) { - // If there are no selected shortcut subtypes, the framework will try to find - // the most applicable subtype from all subtypes whose mode is - // SUBTYPE_MODE_VOICE. This is an exceptional case, so we will hardcode the mode. - Pair<InputMethodInfo, InputMethodSubtype> info = - findLastResortApplicableShortcutInputMethodAndSubtypeLocked( - InputMethodUtils.SUBTYPE_MODE_VOICE); - if (info != null) { - ret.add(info.first); - ret.add(info.second); - } - return ret; - } - for (InputMethodInfo imi: mShortcutInputMethodsAndSubtypes.keySet()) { - ret.add(imi); - for (InputMethodSubtype subtype: mShortcutInputMethodsAndSubtypes.get(imi)) { - ret.add(subtype); - } - } - return ret; - } - } - - @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); - int subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode()); - if (subtypeId != NOT_A_SUBTYPE_ID) { - setInputMethodLocked(mCurMethodId, subtypeId); - return true; - } - } - return false; - } - } - - private static class InputMethodAndSubtypeListManager { - private final Context mContext; - // Used to load label - private final PackageManager mPm; - private final InputMethodManagerService mImms; - private final String mSystemLocaleStr; - public InputMethodAndSubtypeListManager(Context context, InputMethodManagerService imms) { - mContext = context; - mPm = context.getPackageManager(); - mImms = imms; - final Locale locale = context.getResources().getConfiguration().locale; - mSystemLocaleStr = locale != null ? locale.toString() : ""; - } - - private final TreeMap<InputMethodInfo, List<InputMethodSubtype>> mSortedImmis = - new TreeMap<InputMethodInfo, List<InputMethodSubtype>>( - new Comparator<InputMethodInfo>() { - @Override - public int compare(InputMethodInfo imi1, InputMethodInfo imi2) { - if (imi2 == null) return 0; - if (imi1 == null) return 1; - if (mPm == null) { - return imi1.getId().compareTo(imi2.getId()); - } - CharSequence imiId1 = imi1.loadLabel(mPm) + "/" + imi1.getId(); - CharSequence imiId2 = imi2.loadLabel(mPm) + "/" + imi2.getId(); - return imiId1.toString().compareTo(imiId2.toString()); - } - }); - - public ImeSubtypeListItem getNextInputMethod( - boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) { - if (imi == null) { - return null; - } - final List<ImeSubtypeListItem> imList = getSortedInputMethodAndSubtypeList(); - if (imList.size() <= 1) { - return null; - } - final int N = imList.size(); - final int currentSubtypeId = subtype != null - ? InputMethodUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode()) - : NOT_A_SUBTYPE_ID; - for (int i = 0; i < N; ++i) { - final ImeSubtypeListItem isli = imList.get(i); - if (isli.mImi.equals(imi) && isli.mSubtypeId == currentSubtypeId) { - if (!onlyCurrentIme) { - return imList.get((i + 1) % N); - } - for (int j = 0; j < N - 1; ++j) { - final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N); - if (candidate.mImi.equals(imi)) { - return candidate; - } - } - return null; - } - } - return null; - } - - public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList() { - return getSortedInputMethodAndSubtypeList(true, false, false); - } - - public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(boolean showSubtypes, - boolean inputShown, boolean isScreenLocked) { - final ArrayList<ImeSubtypeListItem> imList = new ArrayList<ImeSubtypeListItem>(); - final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis = - mImms.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(); - if (immis == null || immis.size() == 0) { - return Collections.emptyList(); - } - mSortedImmis.clear(); - mSortedImmis.putAll(immis); - for (InputMethodInfo imi : mSortedImmis.keySet()) { - if (imi == null) continue; - List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi); - HashSet<String> enabledSubtypeSet = new HashSet<String>(); - for (InputMethodSubtype subtype: explicitlyOrImplicitlyEnabledSubtypeList) { - enabledSubtypeSet.add(String.valueOf(subtype.hashCode())); - } - final CharSequence imeLabel = imi.loadLabel(mPm); - if (showSubtypes && enabledSubtypeSet.size() > 0) { - final int subtypeCount = imi.getSubtypeCount(); - if (DEBUG) { - Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId()); - } - for (int j = 0; j < subtypeCount; ++j) { - final InputMethodSubtype subtype = imi.getSubtypeAt(j); - final String subtypeHashCode = String.valueOf(subtype.hashCode()); - // We show all enabled IMEs and subtypes when an IME is shown. - if (enabledSubtypeSet.contains(subtypeHashCode) - && ((inputShown && !isScreenLocked) || !subtype.isAuxiliary())) { - final CharSequence subtypeLabel = - subtype.overridesImplicitlyEnabledSubtype() ? null - : subtype.getDisplayName(mContext, imi.getPackageName(), - imi.getServiceInfo().applicationInfo); - imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, imi, j, - subtype.getLocale(), mSystemLocaleStr)); - - // Removing this subtype from enabledSubtypeSet because we no longer - // need to add an entry of this subtype to imList to avoid duplicated - // entries. - enabledSubtypeSet.remove(subtypeHashCode); - } - } - } else { - imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID, - null, mSystemLocaleStr)); - } - } - Collections.sort(imList); - return imList; - } - } - - // 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"; - private static final String ADDITIONAL_SUBTYPES_FILE_NAME = "subtypes.xml"; - private static final String NODE_SUBTYPES = "subtypes"; - private static final String NODE_SUBTYPE = "subtype"; - private static final String NODE_IMI = "imi"; - private static final String ATTR_ID = "id"; - private static final String ATTR_LABEL = "label"; - private static final String ATTR_ICON = "icon"; - private static final String ATTR_IME_SUBTYPE_LOCALE = "imeSubtypeLocale"; - private static final String ATTR_IME_SUBTYPE_MODE = "imeSubtypeMode"; - private static final String ATTR_IME_SUBTYPE_EXTRA_VALUE = "imeSubtypeExtraValue"; - private static final String ATTR_IS_AUXILIARY = "isAuxiliary"; - private final AtomicFile mAdditionalInputMethodSubtypeFile; - private final HashMap<String, InputMethodInfo> mMethodMap; - private final HashMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap = - new HashMap<String, List<InputMethodSubtype>>(); - public InputMethodFileManager(HashMap<String, InputMethodInfo> methodMap, int userId) { - if (methodMap == null) { - throw new NullPointerException("methodMap is null"); - } - mMethodMap = methodMap; - 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()); - } - final File subtypeFile = new File(inputMethodDir, ADDITIONAL_SUBTYPES_FILE_NAME); - mAdditionalInputMethodSubtypeFile = new AtomicFile(subtypeFile); - if (!subtypeFile.exists()) { - // If "subtypes.xml" doesn't exist, create a blank file. - writeAdditionalInputMethodSubtypes( - mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, methodMap); - } else { - readAdditionalInputMethodSubtypes( - mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile); - } - } - - private void deleteAllInputMethodSubtypes(String imiId) { - synchronized (mMethodMap) { - mAdditionalSubtypesMap.remove(imiId); - writeAdditionalInputMethodSubtypes( - mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap); - } - } - - public void addInputMethodSubtypes( - InputMethodInfo imi, InputMethodSubtype[] additionalSubtypes) { - synchronized (mMethodMap) { - final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); - final int N = additionalSubtypes.length; - for (int i = 0; i < N; ++i) { - final InputMethodSubtype subtype = additionalSubtypes[i]; - if (!subtypes.contains(subtype)) { - subtypes.add(subtype); - } else { - Slog.w(TAG, "Duplicated subtype definition found: " - + subtype.getLocale() + ", " + subtype.getMode()); - } - } - mAdditionalSubtypesMap.put(imi.getId(), subtypes); - writeAdditionalInputMethodSubtypes( - mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap); - } - } - - public HashMap<String, List<InputMethodSubtype>> getAllAdditionalInputMethodSubtypes() { - synchronized (mMethodMap) { - return mAdditionalSubtypesMap; - } - } - - private static void writeAdditionalInputMethodSubtypes( - HashMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile, - HashMap<String, InputMethodInfo> methodMap) { - // Safety net for the case that this function is called before methodMap is set. - final boolean isSetMethodMap = methodMap != null && methodMap.size() > 0; - FileOutputStream fos = null; - try { - fos = subtypesFile.startWrite(); - final XmlSerializer out = new FastXmlSerializer(); - out.setOutput(fos, "utf-8"); - out.startDocument(null, true); - out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - out.startTag(null, NODE_SUBTYPES); - for (String imiId : allSubtypes.keySet()) { - if (isSetMethodMap && !methodMap.containsKey(imiId)) { - Slog.w(TAG, "IME uninstalled or not valid.: " + imiId); - continue; - } - out.startTag(null, NODE_IMI); - out.attribute(null, ATTR_ID, imiId); - final List<InputMethodSubtype> subtypesList = allSubtypes.get(imiId); - final int N = subtypesList.size(); - for (int i = 0; i < N; ++i) { - final InputMethodSubtype subtype = subtypesList.get(i); - out.startTag(null, NODE_SUBTYPE); - out.attribute(null, ATTR_ICON, String.valueOf(subtype.getIconResId())); - out.attribute(null, ATTR_LABEL, String.valueOf(subtype.getNameResId())); - out.attribute(null, ATTR_IME_SUBTYPE_LOCALE, subtype.getLocale()); - out.attribute(null, ATTR_IME_SUBTYPE_MODE, subtype.getMode()); - out.attribute(null, ATTR_IME_SUBTYPE_EXTRA_VALUE, subtype.getExtraValue()); - out.attribute(null, ATTR_IS_AUXILIARY, - String.valueOf(subtype.isAuxiliary() ? 1 : 0)); - out.endTag(null, NODE_SUBTYPE); - } - out.endTag(null, NODE_IMI); - } - out.endTag(null, NODE_SUBTYPES); - out.endDocument(); - subtypesFile.finishWrite(fos); - } catch (java.io.IOException e) { - Slog.w(TAG, "Error writing subtypes", e); - if (fos != null) { - subtypesFile.failWrite(fos); - } - } - } - - private static void readAdditionalInputMethodSubtypes( - HashMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile) { - if (allSubtypes == null || subtypesFile == null) return; - allSubtypes.clear(); - FileInputStream fis = null; - try { - fis = subtypesFile.openRead(); - final XmlPullParser parser = Xml.newPullParser(); - parser.setInput(fis, null); - int type = parser.getEventType(); - // Skip parsing until START_TAG - while ((type = parser.next()) != XmlPullParser.START_TAG - && type != XmlPullParser.END_DOCUMENT) {} - String firstNodeName = parser.getName(); - if (!NODE_SUBTYPES.equals(firstNodeName)) { - throw new XmlPullParserException("Xml doesn't start with subtypes"); - } - final int depth =parser.getDepth(); - String currentImiId = null; - ArrayList<InputMethodSubtype> tempSubtypesArray = null; - while (((type = parser.next()) != XmlPullParser.END_TAG - || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { - if (type != XmlPullParser.START_TAG) - continue; - final String nodeName = parser.getName(); - if (NODE_IMI.equals(nodeName)) { - currentImiId = parser.getAttributeValue(null, ATTR_ID); - if (TextUtils.isEmpty(currentImiId)) { - Slog.w(TAG, "Invalid imi id found in subtypes.xml"); - continue; - } - tempSubtypesArray = new ArrayList<InputMethodSubtype>(); - allSubtypes.put(currentImiId, tempSubtypesArray); - } else if (NODE_SUBTYPE.equals(nodeName)) { - if (TextUtils.isEmpty(currentImiId) || tempSubtypesArray == null) { - Slog.w(TAG, "IME uninstalled or not valid.: " + currentImiId); - continue; - } - final int icon = Integer.valueOf( - parser.getAttributeValue(null, ATTR_ICON)); - final int label = Integer.valueOf( - parser.getAttributeValue(null, ATTR_LABEL)); - final String imeSubtypeLocale = - parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LOCALE); - final String imeSubtypeMode = - parser.getAttributeValue(null, ATTR_IME_SUBTYPE_MODE); - final String imeSubtypeExtraValue = - parser.getAttributeValue(null, ATTR_IME_SUBTYPE_EXTRA_VALUE); - final boolean isAuxiliary = "1".equals(String.valueOf( - parser.getAttributeValue(null, ATTR_IS_AUXILIARY))); - final InputMethodSubtype subtype = - new InputMethodSubtype(label, icon, imeSubtypeLocale, - imeSubtypeMode, imeSubtypeExtraValue, isAuxiliary); - tempSubtypesArray.add(subtype); - } - } - } catch (XmlPullParserException e) { - Slog.w(TAG, "Error reading subtypes: " + e); - return; - } catch (java.io.IOException e) { - Slog.w(TAG, "Error reading subtypes: " + e); - return; - } catch (NumberFormatException e) { - Slog.w(TAG, "Error reading subtypes: " + e); - return; - } finally { - if (fis != null) { - try { - fis.close(); - } catch (java.io.IOException e1) { - Slog.w(TAG, "Failed to close."); - } - } - } - } - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - - pw.println("Permission Denial: can't dump InputMethodManager from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); - return; - } - - IInputMethod method; - ClientState client; - - final Printer p = new PrintWriterPrinter(pw); - - synchronized (mMethodMap) { - p.println("Current Input Method Manager state:"); - int N = mMethodList.size(); - p.println(" Input Methods:"); - for (int i=0; i<N; i++) { - InputMethodInfo info = mMethodList.get(i); - p.println(" InputMethod #" + i + ":"); - info.dump(p, " "); - } - p.println(" Clients:"); - for (ClientState ci : mClients.values()) { - p.println(" Client " + ci + ":"); - p.println(" client=" + ci.client); - p.println(" inputContext=" + ci.inputContext); - p.println(" sessionRequested=" + ci.sessionRequested); - p.println(" curSession=" + ci.curSession); - } - p.println(" mCurMethodId=" + mCurMethodId); - client = mCurClient; - p.println(" mCurClient=" + client + " mCurSeq=" + mCurSeq); - p.println(" mCurFocusedWindow=" + mCurFocusedWindow); - p.println(" mCurId=" + mCurId + " mHaveConnect=" + mHaveConnection - + " mBoundToMethod=" + mBoundToMethod); - p.println(" mCurToken=" + mCurToken); - p.println(" mCurIntent=" + mCurIntent); - method = mCurMethod; - p.println(" mCurMethod=" + mCurMethod); - p.println(" mEnabledSession=" + mEnabledSession); - p.println(" mShowRequested=" + mShowRequested - + " mShowExplicitlyRequested=" + mShowExplicitlyRequested - + " mShowForced=" + mShowForced - + " mInputShown=" + mInputShown); - p.println(" mSystemReady=" + mSystemReady + " mScreenOn=" + mScreenOn); - } - - p.println(" "); - if (client != null) { - pw.flush(); - try { - client.client.asBinder().dump(fd, args); - } catch (RemoteException e) { - p.println("Input method client dead: " + e); - } - } else { - p.println("No input method client."); - } - - p.println(" "); - if (method != null) { - pw.flush(); - try { - method.asBinder().dump(fd, args); - } catch (RemoteException e) { - p.println("Input method service dead: " + e); - } - } else { - p.println("No input method service."); - } - } -} |