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