Home | History | Annotate | Download | only in server
      1 /*
      2  *
      3  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      4  * use this file except in compliance with the License. You may obtain a copy of
      5  * the License at
      6  *
      7  * http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software
     10  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     11  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     12  * License for the specific language governing permissions and limitations under
     13  * the License.
     14  */
     15 
     16 package com.android.server;
     17 
     18 import static java.lang.annotation.RetentionPolicy.SOURCE;
     19 
     20 import com.android.internal.content.PackageMonitor;
     21 import com.android.internal.inputmethod.IInputContentUriToken;
     22 import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController;
     23 import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
     24 import com.android.internal.inputmethod.InputMethodUtils;
     25 import com.android.internal.inputmethod.InputMethodUtils.InputMethodSettings;
     26 import com.android.internal.os.HandlerCaller;
     27 import com.android.internal.os.SomeArgs;
     28 import com.android.internal.util.FastXmlSerializer;
     29 import com.android.internal.view.IInputContext;
     30 import com.android.internal.view.IInputMethod;
     31 import com.android.internal.view.IInputMethodClient;
     32 import com.android.internal.view.IInputMethodManager;
     33 import com.android.internal.view.IInputMethodSession;
     34 import com.android.internal.view.IInputSessionCallback;
     35 import com.android.internal.view.InputBindResult;
     36 import com.android.internal.view.InputMethodClient;
     37 import com.android.server.statusbar.StatusBarManagerService;
     38 
     39 import org.xmlpull.v1.XmlPullParser;
     40 import org.xmlpull.v1.XmlPullParserException;
     41 import org.xmlpull.v1.XmlSerializer;
     42 
     43 import android.annotation.IntDef;
     44 import android.annotation.NonNull;
     45 import android.annotation.Nullable;
     46 import android.annotation.UserIdInt;
     47 import android.app.ActivityManagerNative;
     48 import android.app.AlertDialog;
     49 import android.app.AppGlobals;
     50 import android.app.AppOpsManager;
     51 import android.app.KeyguardManager;
     52 import android.app.Notification;
     53 import android.app.NotificationManager;
     54 import android.app.PendingIntent;
     55 import android.content.BroadcastReceiver;
     56 import android.content.ComponentName;
     57 import android.content.ContentResolver;
     58 import android.content.Context;
     59 import android.content.DialogInterface;
     60 import android.content.DialogInterface.OnCancelListener;
     61 import android.content.DialogInterface.OnClickListener;
     62 import android.content.Intent;
     63 import android.content.IntentFilter;
     64 import android.content.ServiceConnection;
     65 import android.content.pm.ApplicationInfo;
     66 import android.content.pm.IPackageManager;
     67 import android.content.pm.PackageManager;
     68 import android.content.pm.ResolveInfo;
     69 import android.content.pm.ServiceInfo;
     70 import android.content.res.Configuration;
     71 import android.content.res.Resources;
     72 import android.content.res.TypedArray;
     73 import android.database.ContentObserver;
     74 import android.graphics.drawable.Drawable;
     75 import android.hardware.input.InputManagerInternal;
     76 import android.inputmethodservice.InputMethodService;
     77 import android.net.Uri;
     78 import android.os.Binder;
     79 import android.os.Bundle;
     80 import android.os.Debug;
     81 import android.os.Environment;
     82 import android.os.Handler;
     83 import android.os.IBinder;
     84 import android.os.IInterface;
     85 import android.os.Message;
     86 import android.os.LocaleList;
     87 import android.os.Parcel;
     88 import android.os.Process;
     89 import android.os.RemoteException;
     90 import android.os.ResultReceiver;
     91 import android.os.ServiceManager;
     92 import android.os.SystemClock;
     93 import android.os.UserHandle;
     94 import android.os.UserManager;
     95 import android.provider.Settings;
     96 import android.text.TextUtils;
     97 import android.text.style.SuggestionSpan;
     98 import android.util.ArrayMap;
     99 import android.util.ArraySet;
    100 import android.util.AtomicFile;
    101 import android.util.EventLog;
    102 import android.util.LruCache;
    103 import android.util.Pair;
    104 import android.util.PrintWriterPrinter;
    105 import android.util.Printer;
    106 import android.util.Slog;
    107 import android.util.Xml;
    108 import android.view.ContextThemeWrapper;
    109 import android.view.IWindowManager;
    110 import android.view.InputChannel;
    111 import android.view.LayoutInflater;
    112 import android.view.View;
    113 import android.view.ViewGroup;
    114 import android.view.WindowManager;
    115 import android.view.WindowManagerInternal;
    116 import android.view.inputmethod.EditorInfo;
    117 import android.view.inputmethod.InputBinding;
    118 import android.view.inputmethod.InputConnectionInspector;
    119 import android.view.inputmethod.InputMethod;
    120 import android.view.inputmethod.InputMethodInfo;
    121 import android.view.inputmethod.InputMethodManager;
    122 import android.view.inputmethod.InputMethodManagerInternal;
    123 import android.view.inputmethod.InputMethodSubtype;
    124 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
    125 import android.widget.ArrayAdapter;
    126 import android.widget.CompoundButton;
    127 import android.widget.CompoundButton.OnCheckedChangeListener;
    128 import android.widget.RadioButton;
    129 import android.widget.Switch;
    130 import android.widget.TextView;
    131 import android.widget.Toast;
    132 
    133 import java.io.File;
    134 import java.io.FileDescriptor;
    135 import java.io.FileInputStream;
    136 import java.io.FileOutputStream;
    137 import java.io.IOException;
    138 import java.io.PrintWriter;
    139 import java.lang.annotation.Retention;
    140 import java.nio.charset.StandardCharsets;
    141 import java.security.InvalidParameterException;
    142 import java.util.ArrayList;
    143 import java.util.Collections;
    144 import java.util.HashMap;
    145 import java.util.List;
    146 
    147 /**
    148  * This class provides a system service that manages input methods.
    149  */
    150 public class InputMethodManagerService extends IInputMethodManager.Stub
    151         implements ServiceConnection, Handler.Callback {
    152     static final boolean DEBUG = false;
    153     static final boolean DEBUG_RESTORE = DEBUG || false;
    154     static final String TAG = "InputMethodManagerService";
    155 
    156     static final int MSG_SHOW_IM_SUBTYPE_PICKER = 1;
    157     static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 2;
    158     static final int MSG_SHOW_IM_CONFIG = 3;
    159 
    160     static final int MSG_UNBIND_INPUT = 1000;
    161     static final int MSG_BIND_INPUT = 1010;
    162     static final int MSG_SHOW_SOFT_INPUT = 1020;
    163     static final int MSG_HIDE_SOFT_INPUT = 1030;
    164     static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035;
    165     static final int MSG_ATTACH_TOKEN = 1040;
    166     static final int MSG_CREATE_SESSION = 1050;
    167 
    168     static final int MSG_START_INPUT = 2000;
    169     static final int MSG_RESTART_INPUT = 2010;
    170 
    171     static final int MSG_UNBIND_CLIENT = 3000;
    172     static final int MSG_BIND_CLIENT = 3010;
    173     static final int MSG_SET_ACTIVE = 3020;
    174     static final int MSG_SET_INTERACTIVE = 3030;
    175     static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 3040;
    176     static final int MSG_SWITCH_IME = 3050;
    177 
    178     static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
    179 
    180     static final int MSG_SYSTEM_UNLOCK_USER = 5000;
    181 
    182     static final long TIME_TO_RECONNECT = 3 * 1000;
    183 
    184     static final int SECURE_SUGGESTION_SPANS_MAX_SIZE = 20;
    185 
    186     private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
    187     private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
    188 
    189     @Retention(SOURCE)
    190     @IntDef({HardKeyboardBehavior.WIRELESS_AFFORDANCE, HardKeyboardBehavior.WIRED_AFFORDANCE})
    191     private @interface  HardKeyboardBehavior {
    192         int WIRELESS_AFFORDANCE = 0;
    193         int WIRED_AFFORDANCE = 1;
    194     }
    195 
    196     final Context mContext;
    197     final Resources mRes;
    198     final Handler mHandler;
    199     final InputMethodSettings mSettings;
    200     final SettingsObserver mSettingsObserver;
    201     final IWindowManager mIWindowManager;
    202     final WindowManagerInternal mWindowManagerInternal;
    203     final HandlerCaller mCaller;
    204     final boolean mHasFeature;
    205     private InputMethodFileManager mFileManager;
    206     private final HardKeyboardListener mHardKeyboardListener;
    207     private final AppOpsManager mAppOpsManager;
    208     private final UserManager mUserManager;
    209 
    210     final InputBindResult mNoBinding = new InputBindResult(null, null, null, -1, -1);
    211 
    212     // All known input methods.  mMethodMap also serves as the global
    213     // lock for this class.
    214     final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
    215     final HashMap<String, InputMethodInfo> mMethodMap = new HashMap<>();
    216     private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
    217             new LruCache<>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
    218     private final InputMethodSubtypeSwitchingController mSwitchingController;
    219 
    220     // Used to bring IME service up to visible adjustment while it is being shown.
    221     final ServiceConnection mVisibleConnection = new ServiceConnection() {
    222         @Override public void onServiceConnected(ComponentName name, IBinder service) {
    223         }
    224 
    225         @Override public void onServiceDisconnected(ComponentName name) {
    226         }
    227     };
    228     boolean mVisibleBound = false;
    229 
    230     // Ongoing notification
    231     private NotificationManager mNotificationManager;
    232     private KeyguardManager mKeyguardManager;
    233     private @Nullable StatusBarManagerService mStatusBar;
    234     private Notification.Builder mImeSwitcherNotification;
    235     private PendingIntent mImeSwitchPendingIntent;
    236     private boolean mShowOngoingImeSwitcherForPhones;
    237     private boolean mNotificationShown;
    238     private final boolean mImeSelectedOnBoot;
    239 
    240     static class SessionState {
    241         final ClientState client;
    242         final IInputMethod method;
    243 
    244         IInputMethodSession session;
    245         InputChannel channel;
    246 
    247         @Override
    248         public String toString() {
    249             return "SessionState{uid " + client.uid + " pid " + client.pid
    250                     + " method " + Integer.toHexString(
    251                             System.identityHashCode(method))
    252                     + " session " + Integer.toHexString(
    253                             System.identityHashCode(session))
    254                     + " channel " + channel
    255                     + "}";
    256         }
    257 
    258         SessionState(ClientState _client, IInputMethod _method,
    259                 IInputMethodSession _session, InputChannel _channel) {
    260             client = _client;
    261             method = _method;
    262             session = _session;
    263             channel = _channel;
    264         }
    265     }
    266 
    267     static final class ClientState {
    268         final IInputMethodClient client;
    269         final IInputContext inputContext;
    270         final int uid;
    271         final int pid;
    272         final InputBinding binding;
    273 
    274         boolean sessionRequested;
    275         SessionState curSession;
    276 
    277         @Override
    278         public String toString() {
    279             return "ClientState{" + Integer.toHexString(
    280                     System.identityHashCode(this)) + " uid " + uid
    281                     + " pid " + pid + "}";
    282         }
    283 
    284         ClientState(IInputMethodClient _client, IInputContext _inputContext,
    285                 int _uid, int _pid) {
    286             client = _client;
    287             inputContext = _inputContext;
    288             uid = _uid;
    289             pid = _pid;
    290             binding = new InputBinding(null, inputContext.asBinder(), uid, pid);
    291         }
    292     }
    293 
    294     final HashMap<IBinder, ClientState> mClients = new HashMap<>();
    295 
    296     /**
    297      * Set once the system is ready to run third party code.
    298      */
    299     boolean mSystemReady;
    300 
    301     /**
    302      * Id obtained with {@link InputMethodInfo#getId()} for the currently selected input method.
    303      * method.  This is to be synchronized with the secure settings keyed with
    304      * {@link Settings.Secure#DEFAULT_INPUT_METHOD}.
    305      *
    306      * <p>This can be transiently {@code null} when the system is re-initializing input method
    307      * settings, e.g., the system locale is just changed.</p>
    308      *
    309      * <p>Note that {@link #mCurId} is used to track which IME is being connected to
    310      * {@link InputMethodManagerService}.</p>
    311      *
    312      * @see #mCurId
    313      */
    314     @Nullable
    315     String mCurMethodId;
    316 
    317     /**
    318      * The current binding sequence number, incremented every time there is
    319      * a new bind performed.
    320      */
    321     int mCurSeq;
    322 
    323     /**
    324      * The client that is currently bound to an input method.
    325      */
    326     ClientState mCurClient;
    327 
    328     /**
    329      * The last window token that we confirmed to be focused.  This is always updated upon reports
    330      * from the input method client.  If the window state is already changed before the report is
    331      * handled, this field just keeps the last value.
    332      */
    333     IBinder mCurFocusedWindow;
    334 
    335     /**
    336      * The client by which {@link #mCurFocusedWindow} was reported.  Used only for debugging.
    337      */
    338     ClientState mCurFocusedWindowClient;
    339 
    340     /**
    341      * The input context last provided by the current client.
    342      */
    343     IInputContext mCurInputContext;
    344 
    345     /**
    346      * The missing method flags for the input context last provided by the current client.
    347      *
    348      * @see android.view.inputmethod.InputConnectionInspector.MissingMethodFlags
    349      */
    350     int mCurInputContextMissingMethods;
    351 
    352     /**
    353      * The attributes last provided by the current client.
    354      */
    355     EditorInfo mCurAttribute;
    356 
    357     /**
    358      * Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently
    359      * connected to or in the process of connecting to.
    360      *
    361      * <p>This can be {@code null} when no input method is connected.</p>
    362      *
    363      * @see #mCurMethodId
    364      */
    365     @Nullable
    366     String mCurId;
    367 
    368     /**
    369      * The current subtype of the current input method.
    370      */
    371     private InputMethodSubtype mCurrentSubtype;
    372 
    373     // This list contains the pairs of InputMethodInfo and InputMethodSubtype.
    374     private final HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>
    375             mShortcutInputMethodsAndSubtypes = new HashMap<>();
    376 
    377     // Was the keyguard locked when this client became current?
    378     private boolean mCurClientInKeyguard;
    379 
    380     /**
    381      * Set to true if our ServiceConnection is currently actively bound to
    382      * a service (whether or not we have gotten its IBinder back yet).
    383      */
    384     boolean mHaveConnection;
    385 
    386     /**
    387      * Set if the client has asked for the input method to be shown.
    388      */
    389     boolean mShowRequested;
    390 
    391     /**
    392      * Set if we were explicitly told to show the input method.
    393      */
    394     boolean mShowExplicitlyRequested;
    395 
    396     /**
    397      * Set if we were forced to be shown.
    398      */
    399     boolean mShowForced;
    400 
    401     /**
    402      * Set if we last told the input method to show itself.
    403      */
    404     boolean mInputShown;
    405 
    406     /**
    407      * The Intent used to connect to the current input method.
    408      */
    409     Intent mCurIntent;
    410 
    411     /**
    412      * The token we have made for the currently active input method, to
    413      * identify it in the future.
    414      */
    415     IBinder mCurToken;
    416 
    417     /**
    418      * If non-null, this is the input method service we are currently connected
    419      * to.
    420      */
    421     IInputMethod mCurMethod;
    422 
    423     /**
    424      * Time that we last initiated a bind to the input method, to determine
    425      * if we should try to disconnect and reconnect to it.
    426      */
    427     long mLastBindTime;
    428 
    429     /**
    430      * Have we called mCurMethod.bindInput()?
    431      */
    432     boolean mBoundToMethod;
    433 
    434     /**
    435      * Currently enabled session.  Only touched by service thread, not
    436      * protected by a lock.
    437      */
    438     SessionState mEnabledSession;
    439 
    440     /**
    441      * True if the device is currently interactive with user.  The value is true initially.
    442      */
    443     boolean mIsInteractive = true;
    444 
    445     int mCurUserActionNotificationSequenceNumber = 0;
    446 
    447     int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
    448 
    449     /**
    450      * A set of status bits regarding the active IME.
    451      *
    452      * <p>This value is a combination of following two bits:</p>
    453      * <dl>
    454      * <dt>{@link InputMethodService#IME_ACTIVE}</dt>
    455      * <dd>
    456      *   If this bit is ON, connected IME is ready to accept touch/key events.
    457      * </dd>
    458      * <dt>{@link InputMethodService#IME_VISIBLE}</dt>
    459      * <dd>
    460      *   If this bit is ON, some of IME view, e.g. software input, candidate view, is visible.
    461      * </dd>
    462      * </dl>
    463      * <em>Do not update this value outside of setImeWindowStatus.</em>
    464      */
    465     int mImeWindowVis;
    466 
    467     private AlertDialog.Builder mDialogBuilder;
    468     private AlertDialog mSwitchingDialog;
    469     private View mSwitchingDialogTitleView;
    470     private Toast mSubtypeSwitchedByShortCutToast;
    471     private InputMethodInfo[] mIms;
    472     private int[] mSubtypeIds;
    473     private LocaleList mLastSystemLocales;
    474     private boolean mShowImeWithHardKeyboard;
    475     private boolean mAccessibilityRequestingNoSoftKeyboard;
    476     private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
    477     private final IPackageManager mIPackageManager;
    478     private final String mSlotIme;
    479     @HardKeyboardBehavior
    480     private final int mHardKeyboardBehavior;
    481 
    482     class SettingsObserver extends ContentObserver {
    483         int mUserId;
    484         boolean mRegistered = false;
    485         @NonNull
    486         String mLastEnabled = "";
    487 
    488         /**
    489          * <em>This constructor must be called within the lock.</em>
    490          */
    491         SettingsObserver(Handler handler) {
    492             super(handler);
    493         }
    494 
    495         public void registerContentObserverLocked(@UserIdInt int userId) {
    496             if (mRegistered && mUserId == userId) {
    497                 return;
    498             }
    499             ContentResolver resolver = mContext.getContentResolver();
    500             if (mRegistered) {
    501                 mContext.getContentResolver().unregisterContentObserver(this);
    502                 mRegistered = false;
    503             }
    504             if (mUserId != userId) {
    505                 mLastEnabled = "";
    506                 mUserId = userId;
    507             }
    508             resolver.registerContentObserver(Settings.Secure.getUriFor(
    509                     Settings.Secure.DEFAULT_INPUT_METHOD), false, this, userId);
    510             resolver.registerContentObserver(Settings.Secure.getUriFor(
    511                     Settings.Secure.ENABLED_INPUT_METHODS), false, this, userId);
    512             resolver.registerContentObserver(Settings.Secure.getUriFor(
    513                     Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this, userId);
    514             resolver.registerContentObserver(Settings.Secure.getUriFor(
    515                     Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this, userId);
    516             resolver.registerContentObserver(Settings.Secure.getUriFor(
    517                     Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE), false, this, userId);
    518             mRegistered = true;
    519         }
    520 
    521         @Override public void onChange(boolean selfChange, Uri uri) {
    522             final Uri showImeUri = Settings.Secure.getUriFor(
    523                     Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
    524             final Uri accessibilityRequestingNoImeUri = Settings.Secure.getUriFor(
    525                     Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
    526             synchronized (mMethodMap) {
    527                 if (showImeUri.equals(uri)) {
    528                     updateKeyboardFromSettingsLocked();
    529                 } else if (accessibilityRequestingNoImeUri.equals(uri)) {
    530                     mAccessibilityRequestingNoSoftKeyboard = Settings.Secure.getIntForUser(
    531                             mContext.getContentResolver(),
    532                             Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
    533                             0, mUserId) == 1;
    534                     if (mAccessibilityRequestingNoSoftKeyboard) {
    535                         final boolean showRequested = mShowRequested;
    536                         hideCurrentInputLocked(0, null);
    537                         mShowRequested = showRequested;
    538                     } else if (mShowRequested) {
    539                         showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
    540                     }
    541                 } else {
    542                     boolean enabledChanged = false;
    543                     String newEnabled = mSettings.getEnabledInputMethodsStr();
    544                     if (!mLastEnabled.equals(newEnabled)) {
    545                         mLastEnabled = newEnabled;
    546                         enabledChanged = true;
    547                     }
    548                     updateInputMethodsFromSettingsLocked(enabledChanged);
    549                 }
    550             }
    551         }
    552 
    553         @Override
    554         public String toString() {
    555             return "SettingsObserver{mUserId=" + mUserId + " mRegistered=" + mRegistered
    556                     + " mLastEnabled=" + mLastEnabled + "}";
    557         }
    558     }
    559 
    560     class ImmsBroadcastReceiver extends android.content.BroadcastReceiver {
    561         @Override
    562         public void onReceive(Context context, Intent intent) {
    563             final String action = intent.getAction();
    564             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
    565                 hideInputMethodMenu();
    566                 // No need to update mIsInteractive
    567                 return;
    568             } else if (Intent.ACTION_USER_ADDED.equals(action)
    569                     || Intent.ACTION_USER_REMOVED.equals(action)) {
    570                 updateCurrentProfileIds();
    571                 return;
    572             } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) {
    573                 final String name = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
    574                 if (Settings.Secure.ENABLED_INPUT_METHODS.equals(name)) {
    575                     final String prevValue = intent.getStringExtra(
    576                             Intent.EXTRA_SETTING_PREVIOUS_VALUE);
    577                     final String newValue = intent.getStringExtra(
    578                             Intent.EXTRA_SETTING_NEW_VALUE);
    579                     restoreEnabledInputMethods(mContext, prevValue, newValue);
    580                 }
    581             } else {
    582                 Slog.w(TAG, "Unexpected intent " + intent);
    583             }
    584         }
    585     }
    586 
    587     // Apply the results of a restore operation to the set of enabled IMEs.  Note that this
    588     // does not attempt to validate on the fly with any installed device policy, so must only
    589     // be run in the context of initial device setup.
    590     //
    591     // TODO: Move this method to InputMethodUtils with adding unit tests.
    592     static void restoreEnabledInputMethods(Context context, String prevValue, String newValue) {
    593         if (DEBUG_RESTORE) {
    594             Slog.i(TAG, "Restoring enabled input methods:");
    595             Slog.i(TAG, "prev=" + prevValue);
    596             Slog.i(TAG, " new=" + newValue);
    597         }
    598         // 'new' is the just-restored state, 'prev' is what was in settings prior to the restore
    599         ArrayMap<String, ArraySet<String>> prevMap =
    600                 InputMethodUtils.parseInputMethodsAndSubtypesString(prevValue);
    601         ArrayMap<String, ArraySet<String>> newMap =
    602                 InputMethodUtils.parseInputMethodsAndSubtypesString(newValue);
    603 
    604         // Merge the restored ime+subtype enabled states into the live state
    605         for (ArrayMap.Entry<String, ArraySet<String>> entry : newMap.entrySet()) {
    606             final String imeId = entry.getKey();
    607             ArraySet<String> prevSubtypes = prevMap.get(imeId);
    608             if (prevSubtypes == null) {
    609                 prevSubtypes = new ArraySet<>(2);
    610                 prevMap.put(imeId, prevSubtypes);
    611             }
    612             prevSubtypes.addAll(entry.getValue());
    613         }
    614 
    615         final String mergedImesAndSubtypesString =
    616                 InputMethodUtils.buildInputMethodsAndSubtypesString(prevMap);
    617         if (DEBUG_RESTORE) {
    618             Slog.i(TAG, "Merged IME string:");
    619             Slog.i(TAG, "     " + mergedImesAndSubtypesString);
    620         }
    621         Settings.Secure.putString(context.getContentResolver(),
    622                 Settings.Secure.ENABLED_INPUT_METHODS, mergedImesAndSubtypesString);
    623     }
    624 
    625     class MyPackageMonitor extends PackageMonitor {
    626         private boolean isChangingPackagesOfCurrentUser() {
    627             final int userId = getChangingUserId();
    628             final boolean retval = userId == mSettings.getCurrentUserId();
    629             if (DEBUG) {
    630                 if (!retval) {
    631                     Slog.d(TAG, "--- ignore this call back from a background user: " + userId);
    632                 }
    633             }
    634             return retval;
    635         }
    636 
    637         @Override
    638         public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
    639             if (!isChangingPackagesOfCurrentUser()) {
    640                 return false;
    641             }
    642             synchronized (mMethodMap) {
    643                 String curInputMethodId = mSettings.getSelectedInputMethod();
    644                 final int N = mMethodList.size();
    645                 if (curInputMethodId != null) {
    646                     for (int i=0; i<N; i++) {
    647                         InputMethodInfo imi = mMethodList.get(i);
    648                         if (imi.getId().equals(curInputMethodId)) {
    649                             for (String pkg : packages) {
    650                                 if (imi.getPackageName().equals(pkg)) {
    651                                     if (!doit) {
    652                                         return true;
    653                                     }
    654                                     resetSelectedInputMethodAndSubtypeLocked("");
    655                                     chooseNewDefaultIMELocked();
    656                                     return true;
    657                                 }
    658                             }
    659                         }
    660                     }
    661                 }
    662             }
    663             return false;
    664         }
    665 
    666         @Override
    667         public void onSomePackagesChanged() {
    668             if (!isChangingPackagesOfCurrentUser()) {
    669                 return;
    670             }
    671             synchronized (mMethodMap) {
    672                 InputMethodInfo curIm = null;
    673                 String curInputMethodId = mSettings.getSelectedInputMethod();
    674                 final int N = mMethodList.size();
    675                 if (curInputMethodId != null) {
    676                     for (int i=0; i<N; i++) {
    677                         InputMethodInfo imi = mMethodList.get(i);
    678                         final String imiId = imi.getId();
    679                         if (imiId.equals(curInputMethodId)) {
    680                             curIm = imi;
    681                         }
    682 
    683                         int change = isPackageDisappearing(imi.getPackageName());
    684                         if (isPackageModified(imi.getPackageName())) {
    685                             mFileManager.deleteAllInputMethodSubtypes(imiId);
    686                         }
    687                         if (change == PACKAGE_TEMPORARY_CHANGE
    688                                 || change == PACKAGE_PERMANENT_CHANGE) {
    689                             Slog.i(TAG, "Input method uninstalled, disabling: "
    690                                     + imi.getComponent());
    691                             setInputMethodEnabledLocked(imi.getId(), false);
    692                         }
    693                     }
    694                 }
    695 
    696                 buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
    697 
    698                 boolean changed = false;
    699 
    700                 if (curIm != null) {
    701                     int change = isPackageDisappearing(curIm.getPackageName());
    702                     if (change == PACKAGE_TEMPORARY_CHANGE
    703                             || change == PACKAGE_PERMANENT_CHANGE) {
    704                         ServiceInfo si = null;
    705                         try {
    706                             si = mIPackageManager.getServiceInfo(
    707                                     curIm.getComponent(), 0, mSettings.getCurrentUserId());
    708                         } catch (RemoteException ex) {
    709                         }
    710                         if (si == null) {
    711                             // Uh oh, current input method is no longer around!
    712                             // Pick another one...
    713                             Slog.i(TAG, "Current input method removed: " + curInputMethodId);
    714                             updateSystemUiLocked(mCurToken, 0 /* vis */, mBackDisposition);
    715                             if (!chooseNewDefaultIMELocked()) {
    716                                 changed = true;
    717                                 curIm = null;
    718                                 Slog.i(TAG, "Unsetting current input method");
    719                                 resetSelectedInputMethodAndSubtypeLocked("");
    720                             }
    721                         }
    722                     }
    723                 }
    724 
    725                 if (curIm == null) {
    726                     // We currently don't have a default input method... is
    727                     // one now available?
    728                     changed = chooseNewDefaultIMELocked();
    729                 } else if (!changed && isPackageModified(curIm.getPackageName())) {
    730                     // Even if the current input method is still available, mCurrentSubtype could
    731                     // be obsolete when the package is modified in practice.
    732                     changed = true;
    733                 }
    734 
    735                 if (changed) {
    736                     updateFromSettingsLocked(false);
    737                 }
    738             }
    739         }
    740     }
    741 
    742     private static final class MethodCallback extends IInputSessionCallback.Stub {
    743         private final InputMethodManagerService mParentIMMS;
    744         private final IInputMethod mMethod;
    745         private final InputChannel mChannel;
    746 
    747         MethodCallback(InputMethodManagerService imms, IInputMethod method,
    748                 InputChannel channel) {
    749             mParentIMMS = imms;
    750             mMethod = method;
    751             mChannel = channel;
    752         }
    753 
    754         @Override
    755         public void sessionCreated(IInputMethodSession session) {
    756             long ident = Binder.clearCallingIdentity();
    757             try {
    758                 mParentIMMS.onSessionCreated(mMethod, session, mChannel);
    759             } finally {
    760                 Binder.restoreCallingIdentity(ident);
    761             }
    762         }
    763     }
    764 
    765     private class HardKeyboardListener
    766             implements WindowManagerInternal.OnHardKeyboardStatusChangeListener {
    767         @Override
    768         public void onHardKeyboardStatusChange(boolean available) {
    769             mHandler.sendMessage(mHandler.obtainMessage(MSG_HARD_KEYBOARD_SWITCH_CHANGED,
    770                         available ? 1 : 0));
    771         }
    772 
    773         public void handleHardKeyboardStatusChange(boolean available) {
    774             if (DEBUG) {
    775                 Slog.w(TAG, "HardKeyboardStatusChanged: available=" + available);
    776             }
    777             synchronized(mMethodMap) {
    778                 if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
    779                         && mSwitchingDialog.isShowing()) {
    780                     mSwitchingDialogTitleView.findViewById(
    781                             com.android.internal.R.id.hard_keyboard_section).setVisibility(
    782                                     available ? View.VISIBLE : View.GONE);
    783                 }
    784             }
    785         }
    786     }
    787 
    788     public static final class Lifecycle extends SystemService {
    789         private InputMethodManagerService mService;
    790 
    791         public Lifecycle(Context context) {
    792             super(context);
    793             mService = new InputMethodManagerService(context);
    794         }
    795 
    796         @Override
    797         public void onStart() {
    798             LocalServices.addService(InputMethodManagerInternal.class,
    799                     new LocalServiceImpl(mService.mHandler));
    800             publishBinderService(Context.INPUT_METHOD_SERVICE, mService);
    801         }
    802 
    803         @Override
    804         public void onSwitchUser(@UserIdInt int userHandle) {
    805             // Called on ActivityManager thread.
    806             // TODO: Dispatch this to a worker thread as needed.
    807             mService.onSwitchUser(userHandle);
    808         }
    809 
    810         @Override
    811         public void onBootPhase(int phase) {
    812             // Called on ActivityManager thread.
    813             // TODO: Dispatch this to a worker thread as needed.
    814             if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
    815                 StatusBarManagerService statusBarService = (StatusBarManagerService) ServiceManager
    816                         .getService(Context.STATUS_BAR_SERVICE);
    817                 mService.systemRunning(statusBarService);
    818             }
    819         }
    820 
    821         @Override
    822         public void onUnlockUser(final @UserIdInt int userHandle) {
    823             // Called on ActivityManager thread.
    824             mService.mHandler.sendMessage(mService.mHandler.obtainMessage(MSG_SYSTEM_UNLOCK_USER,
    825                     userHandle /* arg1 */, 0 /* arg2 */));
    826         }
    827     }
    828 
    829     void onUnlockUser(@UserIdInt int userId) {
    830         synchronized(mMethodMap) {
    831             final int currentUserId = mSettings.getCurrentUserId();
    832             if (DEBUG) {
    833                 Slog.d(TAG, "onUnlockUser: userId=" + userId + " curUserId=" + currentUserId);
    834             }
    835             if (userId != currentUserId) {
    836                 return;
    837             }
    838             mSettings.switchCurrentUser(currentUserId, !mSystemReady);
    839             // We need to rebuild IMEs.
    840             buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
    841             updateInputMethodsFromSettingsLocked(true /* enabledChanged */);
    842         }
    843     }
    844 
    845     void onSwitchUser(@UserIdInt int userId) {
    846         synchronized (mMethodMap) {
    847             switchUserLocked(userId);
    848         }
    849     }
    850 
    851     public InputMethodManagerService(Context context) {
    852         mIPackageManager = AppGlobals.getPackageManager();
    853         mContext = context;
    854         mRes = context.getResources();
    855         mHandler = new Handler(this);
    856         // Note: SettingsObserver doesn't register observers in its constructor.
    857         mSettingsObserver = new SettingsObserver(mHandler);
    858         mIWindowManager = IWindowManager.Stub.asInterface(
    859                 ServiceManager.getService(Context.WINDOW_SERVICE));
    860         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
    861         mCaller = new HandlerCaller(context, null, new HandlerCaller.Callback() {
    862             @Override
    863             public void executeMessage(Message msg) {
    864                 handleMessage(msg);
    865             }
    866         }, true /*asyncHandler*/);
    867         mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
    868         mUserManager = mContext.getSystemService(UserManager.class);
    869         mHardKeyboardListener = new HardKeyboardListener();
    870         mHasFeature = context.getPackageManager().hasSystemFeature(
    871                 PackageManager.FEATURE_INPUT_METHODS);
    872         mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);
    873         mHardKeyboardBehavior = mContext.getResources().getInteger(
    874                 com.android.internal.R.integer.config_externalHardKeyboardBehavior);
    875 
    876         Bundle extras = new Bundle();
    877         extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true);
    878         mImeSwitcherNotification = new Notification.Builder(mContext)
    879             .setSmallIcon(com.android.internal.R.drawable.ic_notification_ime_default)
    880             .setWhen(0)
    881             .setOngoing(true)
    882             .addExtras(extras)
    883             .setCategory(Notification.CATEGORY_SYSTEM)
    884             .setColor(com.android.internal.R.color.system_notification_accent_color);
    885 
    886         Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER);
    887         mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
    888 
    889         mShowOngoingImeSwitcherForPhones = false;
    890 
    891         final IntentFilter broadcastFilter = new IntentFilter();
    892         broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
    893         broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
    894         broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
    895         broadcastFilter.addAction(Intent.ACTION_SETTING_RESTORED);
    896         mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter);
    897 
    898         mNotificationShown = false;
    899         int userId = 0;
    900         try {
    901             userId = ActivityManagerNative.getDefault().getCurrentUser().id;
    902         } catch (RemoteException e) {
    903             Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);
    904         }
    905         mMyPackageMonitor.register(mContext, null, UserHandle.ALL, true);
    906 
    907         // mSettings should be created before buildInputMethodListLocked
    908         mSettings = new InputMethodSettings(
    909                 mRes, context.getContentResolver(), mMethodMap, mMethodList, userId, !mSystemReady);
    910 
    911         updateCurrentProfileIds();
    912         mFileManager = new InputMethodFileManager(mMethodMap, userId);
    913         synchronized (mMethodMap) {
    914             mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
    915                     mSettings, context);
    916         }
    917 
    918         // Just checking if defaultImiId is empty or not
    919         final String defaultImiId = mSettings.getSelectedInputMethod();
    920         if (DEBUG) {
    921             Slog.d(TAG, "Initial default ime = " + defaultImiId);
    922         }
    923         mImeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
    924 
    925         synchronized (mMethodMap) {
    926             buildInputMethodListLocked(!mImeSelectedOnBoot /* resetDefaultEnabledIme */);
    927         }
    928         mSettings.enableAllIMEsIfThereIsNoEnabledIME();
    929 
    930         if (!mImeSelectedOnBoot) {
    931             Slog.w(TAG, "No IME selected. Choose the most applicable IME.");
    932             synchronized (mMethodMap) {
    933                 resetDefaultImeLocked(context);
    934             }
    935         }
    936 
    937         synchronized (mMethodMap) {
    938             mSettingsObserver.registerContentObserverLocked(userId);
    939             updateFromSettingsLocked(true);
    940         }
    941 
    942         // IMMS wants to receive Intent.ACTION_LOCALE_CHANGED in order to update the current IME
    943         // according to the new system locale.
    944         final IntentFilter filter = new IntentFilter();
    945         filter.addAction(Intent.ACTION_LOCALE_CHANGED);
    946         mContext.registerReceiver(
    947                 new BroadcastReceiver() {
    948                     @Override
    949                     public void onReceive(Context context, Intent intent) {
    950                         synchronized(mMethodMap) {
    951                             resetStateIfCurrentLocaleChangedLocked();
    952                         }
    953                     }
    954                 }, filter);
    955     }
    956 
    957     private void resetDefaultImeLocked(Context context) {
    958         // Do not reset the default (current) IME when it is a 3rd-party IME
    959         if (mCurMethodId != null && !InputMethodUtils.isSystemIme(mMethodMap.get(mCurMethodId))) {
    960             return;
    961         }
    962         final List<InputMethodInfo> suitableImes = InputMethodUtils.getDefaultEnabledImes(
    963                 context, mSystemReady, mSettings.getEnabledInputMethodListLocked());
    964         if (suitableImes.isEmpty()) {
    965             Slog.i(TAG, "No default found");
    966             return;
    967         }
    968         final InputMethodInfo defIm = suitableImes.get(0);
    969         Slog.i(TAG, "Default found, using " + defIm.getId());
    970         setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false);
    971     }
    972 
    973     private void resetAllInternalStateLocked(final boolean updateOnlyWhenLocaleChanged,
    974             final boolean resetDefaultEnabledIme) {
    975         if (!mSystemReady) {
    976             // not system ready
    977             return;
    978         }
    979         final LocaleList newLocales = mRes.getConfiguration().getLocales();
    980         if (!updateOnlyWhenLocaleChanged
    981                 || (newLocales != null && !newLocales.equals(mLastSystemLocales))) {
    982             if (!updateOnlyWhenLocaleChanged) {
    983                 hideCurrentInputLocked(0, null);
    984                 resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_RESET_IME);
    985             }
    986             if (DEBUG) {
    987                 Slog.i(TAG, "LocaleList has been changed to " + newLocales);
    988             }
    989             buildInputMethodListLocked(resetDefaultEnabledIme);
    990             if (!updateOnlyWhenLocaleChanged) {
    991                 final String selectedImiId = mSettings.getSelectedInputMethod();
    992                 if (TextUtils.isEmpty(selectedImiId)) {
    993                     // This is the first time of the user switch and
    994                     // set the current ime to the proper one.
    995                     resetDefaultImeLocked(mContext);
    996                 }
    997             } else {
    998                 // If the locale is changed, needs to reset the default ime
    999                 resetDefaultImeLocked(mContext);
   1000             }
   1001             updateFromSettingsLocked(true);
   1002             mLastSystemLocales = newLocales;
   1003             if (!updateOnlyWhenLocaleChanged) {
   1004                 try {
   1005                     startInputInnerLocked();
   1006                 } catch (RuntimeException e) {
   1007                     Slog.w(TAG, "Unexpected exception", e);
   1008                 }
   1009             }
   1010         }
   1011     }
   1012 
   1013     private void resetStateIfCurrentLocaleChangedLocked() {
   1014         resetAllInternalStateLocked(true /* updateOnlyWhenLocaleChanged */,
   1015                 true /* resetDefaultImeLocked */);
   1016     }
   1017 
   1018     private void switchUserLocked(int newUserId) {
   1019         if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
   1020                 + " currentUserId=" + mSettings.getCurrentUserId());
   1021 
   1022         // ContentObserver should be registered again when the user is changed
   1023         mSettingsObserver.registerContentObserverLocked(newUserId);
   1024 
   1025         // If the system is not ready or the device is not yed unlocked by the user, then we use
   1026         // copy-on-write settings.
   1027         final boolean useCopyOnWriteSettings =
   1028                 !mSystemReady || !mUserManager.isUserUnlockingOrUnlocked(newUserId);
   1029         mSettings.switchCurrentUser(newUserId, useCopyOnWriteSettings);
   1030         updateCurrentProfileIds();
   1031         // InputMethodFileManager should be reset when the user is changed
   1032         mFileManager = new InputMethodFileManager(mMethodMap, newUserId);
   1033         final String defaultImiId = mSettings.getSelectedInputMethod();
   1034 
   1035         if (DEBUG) Slog.d(TAG, "Switching user stage 2/3. newUserId=" + newUserId
   1036                 + " defaultImiId=" + defaultImiId);
   1037 
   1038         // For secondary users, the list of enabled IMEs may not have been updated since the
   1039         // callbacks to PackageMonitor are ignored for the secondary user. Here, defaultImiId may
   1040         // not be empty even if the IME has been uninstalled by the primary user.
   1041         // Even in such cases, IMMS works fine because it will find the most applicable
   1042         // IME for that user.
   1043         final boolean initialUserSwitch = TextUtils.isEmpty(defaultImiId);
   1044         resetAllInternalStateLocked(false  /* updateOnlyWhenLocaleChanged */,
   1045                 initialUserSwitch /* needsToResetDefaultIme */);
   1046         if (initialUserSwitch) {
   1047             InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
   1048                     mSettings.getEnabledInputMethodListLocked(), newUserId,
   1049                     mContext.getBasePackageName());
   1050         }
   1051 
   1052         if (DEBUG) Slog.d(TAG, "Switching user stage 3/3. newUserId=" + newUserId
   1053                 + " selectedIme=" + mSettings.getSelectedInputMethod());
   1054     }
   1055 
   1056     void updateCurrentProfileIds() {
   1057         mSettings.setCurrentProfileIds(
   1058                 mUserManager.getProfileIdsWithDisabled(mSettings.getCurrentUserId()));
   1059     }
   1060 
   1061     @Override
   1062     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
   1063             throws RemoteException {
   1064         try {
   1065             return super.onTransact(code, data, reply, flags);
   1066         } catch (RuntimeException e) {
   1067             // The input method manager only throws security exceptions, so let's
   1068             // log all others.
   1069             if (!(e instanceof SecurityException)) {
   1070                 Slog.wtf(TAG, "Input Method Manager Crash", e);
   1071             }
   1072             throw e;
   1073         }
   1074     }
   1075 
   1076     public void systemRunning(StatusBarManagerService statusBar) {
   1077         synchronized (mMethodMap) {
   1078             if (DEBUG) {
   1079                 Slog.d(TAG, "--- systemReady");
   1080             }
   1081             if (!mSystemReady) {
   1082                 mSystemReady = true;
   1083                 final int currentUserId = mSettings.getCurrentUserId();
   1084                 mSettings.switchCurrentUser(currentUserId,
   1085                         !mUserManager.isUserUnlockingOrUnlocked(currentUserId));
   1086                 mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
   1087                 mNotificationManager = mContext.getSystemService(NotificationManager.class);
   1088                 mStatusBar = statusBar;
   1089                 if (mStatusBar != null) {
   1090                     mStatusBar.setIconVisibility(mSlotIme, false);
   1091                 }
   1092                 updateSystemUiLocked(mCurToken, mImeWindowVis, mBackDisposition);
   1093                 mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
   1094                         com.android.internal.R.bool.show_ongoing_ime_switcher);
   1095                 if (mShowOngoingImeSwitcherForPhones) {
   1096                     mWindowManagerInternal.setOnHardKeyboardStatusChangeListener(
   1097                             mHardKeyboardListener);
   1098                 }
   1099                 buildInputMethodListLocked(!mImeSelectedOnBoot /* resetDefaultEnabledIme */);
   1100                 if (!mImeSelectedOnBoot) {
   1101                     Slog.w(TAG, "Reset the default IME as \"Resource\" is ready here.");
   1102                     resetStateIfCurrentLocaleChangedLocked();
   1103                     InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
   1104                             mSettings.getEnabledInputMethodListLocked(),
   1105                             mSettings.getCurrentUserId(), mContext.getBasePackageName());
   1106                 }
   1107                 mLastSystemLocales = mRes.getConfiguration().getLocales();
   1108                 try {
   1109                     startInputInnerLocked();
   1110                 } catch (RuntimeException e) {
   1111                     Slog.w(TAG, "Unexpected exception", e);
   1112                 }
   1113             }
   1114         }
   1115     }
   1116 
   1117     // ---------------------------------------------------------------------------------------
   1118     // Check whether or not this is a valid IPC. Assumes an IPC is valid when either
   1119     // 1) it comes from the system process
   1120     // 2) the calling process' user id is identical to the current user id IMMS thinks.
   1121     private boolean calledFromValidUser() {
   1122         final int uid = Binder.getCallingUid();
   1123         final int userId = UserHandle.getUserId(uid);
   1124         if (DEBUG) {
   1125             Slog.d(TAG, "--- calledFromForegroundUserOrSystemProcess ? "
   1126                     + "calling uid = " + uid + " system uid = " + Process.SYSTEM_UID
   1127                     + " calling userId = " + userId + ", foreground user id = "
   1128                     + mSettings.getCurrentUserId() + ", calling pid = " + Binder.getCallingPid()
   1129                     + InputMethodUtils.getApiCallStack());
   1130         }
   1131         if (uid == Process.SYSTEM_UID || mSettings.isCurrentProfile(userId)) {
   1132             return true;
   1133         }
   1134 
   1135         // Caveat: A process which has INTERACT_ACROSS_USERS_FULL gets results for the
   1136         // foreground user, not for the user of that process. Accordingly InputMethodManagerService
   1137         // must not manage background users' states in any functions.
   1138         // Note that privacy-sensitive IPCs, such as setInputMethod, are still securely guarded
   1139         // by a token.
   1140         if (mContext.checkCallingOrSelfPermission(
   1141                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
   1142                         == PackageManager.PERMISSION_GRANTED) {
   1143             if (DEBUG) {
   1144                 Slog.d(TAG, "--- Access granted because the calling process has "
   1145                         + "the INTERACT_ACROSS_USERS_FULL permission");
   1146             }
   1147             return true;
   1148         }
   1149         Slog.w(TAG, "--- IPC called from background users. Ignore. callers="
   1150                 + Debug.getCallers(10));
   1151         return false;
   1152     }
   1153 
   1154 
   1155     /**
   1156      * Returns true iff the caller is identified to be the current input method with the token.
   1157      * @param token The window token given to the input method when it was started.
   1158      * @return true if and only if non-null valid token is specified.
   1159      */
   1160     private boolean calledWithValidToken(IBinder token) {
   1161         if (token == null || mCurToken != token) {
   1162             return false;
   1163         }
   1164         return true;
   1165     }
   1166 
   1167     private boolean bindCurrentInputMethodService(
   1168             Intent service, ServiceConnection conn, int flags) {
   1169         if (service == null || conn == null) {
   1170             Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn);
   1171             return false;
   1172         }
   1173         return mContext.bindServiceAsUser(service, conn, flags,
   1174                 new UserHandle(mSettings.getCurrentUserId()));
   1175     }
   1176 
   1177     @Override
   1178     public List<InputMethodInfo> getInputMethodList() {
   1179         // TODO: Make this work even for non-current users?
   1180         if (!calledFromValidUser()) {
   1181             return Collections.emptyList();
   1182         }
   1183         synchronized (mMethodMap) {
   1184             return new ArrayList<>(mMethodList);
   1185         }
   1186     }
   1187 
   1188     @Override
   1189     public List<InputMethodInfo> getEnabledInputMethodList() {
   1190         // TODO: Make this work even for non-current users?
   1191         if (!calledFromValidUser()) {
   1192             return Collections.emptyList();
   1193         }
   1194         synchronized (mMethodMap) {
   1195             return mSettings.getEnabledInputMethodListLocked();
   1196         }
   1197     }
   1198 
   1199     /**
   1200      * @param imiId if null, returns enabled subtypes for the current imi
   1201      * @return enabled subtypes of the specified imi
   1202      */
   1203     @Override
   1204     public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
   1205             boolean allowsImplicitlySelectedSubtypes) {
   1206         // TODO: Make this work even for non-current users?
   1207         if (!calledFromValidUser()) {
   1208             return Collections.emptyList();
   1209         }
   1210         synchronized (mMethodMap) {
   1211             final InputMethodInfo imi;
   1212             if (imiId == null && mCurMethodId != null) {
   1213                 imi = mMethodMap.get(mCurMethodId);
   1214             } else {
   1215                 imi = mMethodMap.get(imiId);
   1216             }
   1217             if (imi == null) {
   1218                 return Collections.emptyList();
   1219             }
   1220             return mSettings.getEnabledInputMethodSubtypeListLocked(
   1221                     mContext, imi, allowsImplicitlySelectedSubtypes);
   1222         }
   1223     }
   1224 
   1225     @Override
   1226     public void addClient(IInputMethodClient client,
   1227             IInputContext inputContext, int uid, int pid) {
   1228         if (!calledFromValidUser()) {
   1229             return;
   1230         }
   1231         synchronized (mMethodMap) {
   1232             mClients.put(client.asBinder(), new ClientState(client,
   1233                     inputContext, uid, pid));
   1234         }
   1235     }
   1236 
   1237     @Override
   1238     public void removeClient(IInputMethodClient client) {
   1239         if (!calledFromValidUser()) {
   1240             return;
   1241         }
   1242         synchronized (mMethodMap) {
   1243             ClientState cs = mClients.remove(client.asBinder());
   1244             if (cs != null) {
   1245                 clearClientSessionLocked(cs);
   1246                 if (mCurClient == cs) {
   1247                     mCurClient = null;
   1248                 }
   1249                 if (mCurFocusedWindowClient == cs) {
   1250                     mCurFocusedWindowClient = null;
   1251                 }
   1252             }
   1253         }
   1254     }
   1255 
   1256     void executeOrSendMessage(IInterface target, Message msg) {
   1257          if (target.asBinder() instanceof Binder) {
   1258              mCaller.sendMessage(msg);
   1259          } else {
   1260              handleMessage(msg);
   1261              msg.recycle();
   1262          }
   1263     }
   1264 
   1265     void unbindCurrentClientLocked(
   1266             /* @InputMethodClient.UnbindReason */ final int unbindClientReason) {
   1267         if (mCurClient != null) {
   1268             if (DEBUG) Slog.v(TAG, "unbindCurrentInputLocked: client="
   1269                     + mCurClient.client.asBinder());
   1270             if (mBoundToMethod) {
   1271                 mBoundToMethod = false;
   1272                 if (mCurMethod != null) {
   1273                     executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
   1274                             MSG_UNBIND_INPUT, mCurMethod));
   1275                 }
   1276             }
   1277 
   1278             executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
   1279                     MSG_SET_ACTIVE, 0, mCurClient));
   1280             executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
   1281                     MSG_UNBIND_CLIENT, mCurSeq, unbindClientReason, mCurClient.client));
   1282             mCurClient.sessionRequested = false;
   1283             mCurClient = null;
   1284 
   1285             hideInputMethodMenuLocked();
   1286         }
   1287     }
   1288 
   1289     private int getImeShowFlags() {
   1290         int flags = 0;
   1291         if (mShowForced) {
   1292             flags |= InputMethod.SHOW_FORCED
   1293                     | InputMethod.SHOW_EXPLICIT;
   1294         } else if (mShowExplicitlyRequested) {
   1295             flags |= InputMethod.SHOW_EXPLICIT;
   1296         }
   1297         return flags;
   1298     }
   1299 
   1300     private int getAppShowFlags() {
   1301         int flags = 0;
   1302         if (mShowForced) {
   1303             flags |= InputMethodManager.SHOW_FORCED;
   1304         } else if (!mShowExplicitlyRequested) {
   1305             flags |= InputMethodManager.SHOW_IMPLICIT;
   1306         }
   1307         return flags;
   1308     }
   1309 
   1310     InputBindResult attachNewInputLocked(boolean initial) {
   1311         if (!mBoundToMethod) {
   1312             executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
   1313                     MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
   1314             mBoundToMethod = true;
   1315         }
   1316         final SessionState session = mCurClient.curSession;
   1317         if (initial) {
   1318             executeOrSendMessage(session.method, mCaller.obtainMessageIOOO(
   1319                     MSG_START_INPUT, mCurInputContextMissingMethods, session, mCurInputContext,
   1320                     mCurAttribute));
   1321         } else {
   1322             executeOrSendMessage(session.method, mCaller.obtainMessageIOOO(
   1323                     MSG_RESTART_INPUT, mCurInputContextMissingMethods, session, mCurInputContext,
   1324                     mCurAttribute));
   1325         }
   1326         if (mShowRequested) {
   1327             if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
   1328             showCurrentInputLocked(getAppShowFlags(), null);
   1329         }
   1330         return new InputBindResult(session.session,
   1331                 (session.channel != null ? session.channel.dup() : null),
   1332                 mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);
   1333     }
   1334 
   1335     InputBindResult startInputLocked(
   1336             /* @InputMethodClient.StartInputReason */ final int startInputReason,
   1337             IInputMethodClient client, IInputContext inputContext,
   1338             /* @InputConnectionInspector.missingMethods */ final int missingMethods,
   1339             @Nullable EditorInfo attribute, int controlFlags) {
   1340         // If no method is currently selected, do nothing.
   1341         if (mCurMethodId == null) {
   1342             return mNoBinding;
   1343         }
   1344 
   1345         ClientState cs = mClients.get(client.asBinder());
   1346         if (cs == null) {
   1347             throw new IllegalArgumentException("unknown client "
   1348                     + client.asBinder());
   1349         }
   1350 
   1351         if (attribute == null) {
   1352             Slog.w(TAG, "Ignoring startInput with null EditorInfo."
   1353                     + " uid=" + cs.uid + " pid=" + cs.pid);
   1354             return null;
   1355         }
   1356 
   1357         try {
   1358             if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) {
   1359                 // Check with the window manager to make sure this client actually
   1360                 // has a window with focus.  If not, reject.  This is thread safe
   1361                 // because if the focus changes some time before or after, the
   1362                 // next client receiving focus that has any interest in input will
   1363                 // be calling through here after that change happens.
   1364                 Slog.w(TAG, "Starting input on non-focused client " + cs.client
   1365                         + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
   1366                 return null;
   1367             }
   1368         } catch (RemoteException e) {
   1369         }
   1370 
   1371         return startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
   1372                 controlFlags);
   1373     }
   1374 
   1375     InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
   1376             /* @InputConnectionInspector.missingMethods */ final int missingMethods,
   1377             @NonNull EditorInfo attribute, int controlFlags) {
   1378         // If no method is currently selected, do nothing.
   1379         if (mCurMethodId == null) {
   1380             return mNoBinding;
   1381         }
   1382 
   1383         if (!InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, cs.uid,
   1384                 attribute.packageName)) {
   1385             Slog.e(TAG, "Rejecting this client as it reported an invalid package name."
   1386                     + " uid=" + cs.uid + " package=" + attribute.packageName);
   1387             return mNoBinding;
   1388         }
   1389 
   1390         if (mCurClient != cs) {
   1391             // Was the keyguard locked when switching over to the new client?
   1392             mCurClientInKeyguard = isKeyguardLocked();
   1393             // If the client is changing, we need to switch over to the new
   1394             // one.
   1395             unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_SWITCH_CLIENT);
   1396             if (DEBUG) Slog.v(TAG, "switching to client: client="
   1397                     + cs.client.asBinder() + " keyguard=" + mCurClientInKeyguard);
   1398 
   1399             // If the screen is on, inform the new client it is active
   1400             if (mIsInteractive) {
   1401                 executeOrSendMessage(cs.client, mCaller.obtainMessageIO(
   1402                         MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, cs));
   1403             }
   1404         }
   1405 
   1406         // Bump up the sequence for this client and attach it.
   1407         mCurSeq++;
   1408         if (mCurSeq <= 0) mCurSeq = 1;
   1409         mCurClient = cs;
   1410         mCurInputContext = inputContext;
   1411         mCurInputContextMissingMethods = missingMethods;
   1412         mCurAttribute = attribute;
   1413 
   1414         // Check if the input method is changing.
   1415         if (mCurId != null && mCurId.equals(mCurMethodId)) {
   1416             if (cs.curSession != null) {
   1417                 // Fast case: if we are already connected to the input method,
   1418                 // then just return it.
   1419                 return attachNewInputLocked(
   1420                         (controlFlags&InputMethodManager.CONTROL_START_INITIAL) != 0);
   1421             }
   1422             if (mHaveConnection) {
   1423                 if (mCurMethod != null) {
   1424                     // Return to client, and we will get back with it when
   1425                     // we have had a session made for it.
   1426                     requestClientSessionLocked(cs);
   1427                     return new InputBindResult(null, null, mCurId, mCurSeq,
   1428                             mCurUserActionNotificationSequenceNumber);
   1429                 } else if (SystemClock.uptimeMillis()
   1430                         < (mLastBindTime+TIME_TO_RECONNECT)) {
   1431                     // In this case we have connected to the service, but
   1432                     // don't yet have its interface.  If it hasn't been too
   1433                     // long since we did the connection, we'll return to
   1434                     // the client and wait to get the service interface so
   1435                     // we can report back.  If it has been too long, we want
   1436                     // to fall through so we can try a disconnect/reconnect
   1437                     // to see if we can get back in touch with the service.
   1438                     return new InputBindResult(null, null, mCurId, mCurSeq,
   1439                             mCurUserActionNotificationSequenceNumber);
   1440                 } else {
   1441                     EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
   1442                             mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
   1443                 }
   1444             }
   1445         }
   1446 
   1447         return startInputInnerLocked();
   1448     }
   1449 
   1450     InputBindResult startInputInnerLocked() {
   1451         if (mCurMethodId == null) {
   1452             return mNoBinding;
   1453         }
   1454 
   1455         if (!mSystemReady) {
   1456             // If the system is not yet ready, we shouldn't be running third
   1457             // party code.
   1458             return new InputBindResult(null, null, mCurMethodId, mCurSeq,
   1459                     mCurUserActionNotificationSequenceNumber);
   1460         }
   1461 
   1462         InputMethodInfo info = mMethodMap.get(mCurMethodId);
   1463         if (info == null) {
   1464             throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
   1465         }
   1466 
   1467         unbindCurrentMethodLocked(true);
   1468 
   1469         mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
   1470         mCurIntent.setComponent(info.getComponent());
   1471         mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
   1472                 com.android.internal.R.string.input_method_binding_label);
   1473         mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
   1474                 mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
   1475         if (bindCurrentInputMethodService(mCurIntent, this, Context.BIND_AUTO_CREATE
   1476                 | Context.BIND_NOT_VISIBLE | Context.BIND_NOT_FOREGROUND
   1477                 | Context.BIND_SHOWING_UI)) {
   1478             mLastBindTime = SystemClock.uptimeMillis();
   1479             mHaveConnection = true;
   1480             mCurId = info.getId();
   1481             mCurToken = new Binder();
   1482             try {
   1483                 if (true || DEBUG) Slog.v(TAG, "Adding window token: " + mCurToken);
   1484                 mIWindowManager.addWindowToken(mCurToken,
   1485                         WindowManager.LayoutParams.TYPE_INPUT_METHOD);
   1486             } catch (RemoteException e) {
   1487             }
   1488             return new InputBindResult(null, null, mCurId, mCurSeq,
   1489                     mCurUserActionNotificationSequenceNumber);
   1490         } else {
   1491             mCurIntent = null;
   1492             Slog.w(TAG, "Failure connecting to input method service: "
   1493                     + mCurIntent);
   1494         }
   1495         return null;
   1496     }
   1497 
   1498     private InputBindResult startInput(
   1499             /* @InputMethodClient.StartInputReason */ final int startInputReason,
   1500             IInputMethodClient client, IInputContext inputContext,
   1501             /* @InputConnectionInspector.missingMethods */ final int missingMethods,
   1502             @Nullable EditorInfo attribute, int controlFlags) {
   1503         if (!calledFromValidUser()) {
   1504             return null;
   1505         }
   1506         synchronized (mMethodMap) {
   1507             if (DEBUG) {
   1508                 Slog.v(TAG, "startInput: reason="
   1509                         + InputMethodClient.getStartInputReason(startInputReason)
   1510                         + " client = " + client.asBinder()
   1511                         + " inputContext=" + inputContext
   1512                         + " missingMethods="
   1513                         + InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods)
   1514                         + " attribute=" + attribute
   1515                         + " controlFlags=#" + Integer.toHexString(controlFlags));
   1516             }
   1517             final long ident = Binder.clearCallingIdentity();
   1518             try {
   1519                 return startInputLocked(startInputReason, client, inputContext, missingMethods,
   1520                         attribute, controlFlags);
   1521             } finally {
   1522                 Binder.restoreCallingIdentity(ident);
   1523             }
   1524         }
   1525     }
   1526 
   1527     @Override
   1528     public void finishInput(IInputMethodClient client) {
   1529     }
   1530 
   1531     @Override
   1532     public void onServiceConnected(ComponentName name, IBinder service) {
   1533         synchronized (mMethodMap) {
   1534             if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
   1535                 mCurMethod = IInputMethod.Stub.asInterface(service);
   1536                 if (mCurToken == null) {
   1537                     Slog.w(TAG, "Service connected without a token!");
   1538                     unbindCurrentMethodLocked(false);
   1539                     return;
   1540                 }
   1541                 if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
   1542                 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
   1543                         MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
   1544                 if (mCurClient != null) {
   1545                     clearClientSessionLocked(mCurClient);
   1546                     requestClientSessionLocked(mCurClient);
   1547                 }
   1548             }
   1549         }
   1550     }
   1551 
   1552     void onSessionCreated(IInputMethod method, IInputMethodSession session,
   1553             InputChannel channel) {
   1554         synchronized (mMethodMap) {
   1555             if (mCurMethod != null && method != null
   1556                     && mCurMethod.asBinder() == method.asBinder()) {
   1557                 if (mCurClient != null) {
   1558                     clearClientSessionLocked(mCurClient);
   1559                     mCurClient.curSession = new SessionState(mCurClient,
   1560                             method, session, channel);
   1561                     InputBindResult res = attachNewInputLocked(true);
   1562                     if (res.method != null) {
   1563                         executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
   1564                                 MSG_BIND_CLIENT, mCurClient.client, res));
   1565                     }
   1566                     return;
   1567                 }
   1568             }
   1569         }
   1570 
   1571         // Session abandoned.  Close its associated input channel.
   1572         channel.dispose();
   1573     }
   1574 
   1575     void unbindCurrentMethodLocked(boolean savePosition) {
   1576         if (mVisibleBound) {
   1577             mContext.unbindService(mVisibleConnection);
   1578             mVisibleBound = false;
   1579         }
   1580 
   1581         if (mHaveConnection) {
   1582             mContext.unbindService(this);
   1583             mHaveConnection = false;
   1584         }
   1585 
   1586         if (mCurToken != null) {
   1587             try {
   1588                 if (DEBUG) Slog.v(TAG, "Removing window token: " + mCurToken);
   1589                 if ((mImeWindowVis & InputMethodService.IME_ACTIVE) != 0 && savePosition) {
   1590                     // The current IME is shown. Hence an IME switch (transition) is happening.
   1591                     mWindowManagerInternal.saveLastInputMethodWindowForTransition();
   1592                 }
   1593                 mIWindowManager.removeWindowToken(mCurToken);
   1594             } catch (RemoteException e) {
   1595             }
   1596             mCurToken = null;
   1597         }
   1598 
   1599         mCurId = null;
   1600         clearCurMethodLocked();
   1601     }
   1602 
   1603     void resetCurrentMethodAndClient(
   1604             /* @InputMethodClient.UnbindReason */ final int unbindClientReason) {
   1605         mCurMethodId = null;
   1606         unbindCurrentMethodLocked(false);
   1607         unbindCurrentClientLocked(unbindClientReason);
   1608     }
   1609 
   1610     void requestClientSessionLocked(ClientState cs) {
   1611         if (!cs.sessionRequested) {
   1612             if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
   1613             InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString());
   1614             cs.sessionRequested = true;
   1615             executeOrSendMessage(mCurMethod, mCaller.obtainMessageOOO(
   1616                     MSG_CREATE_SESSION, mCurMethod, channels[1],
   1617                     new MethodCallback(this, mCurMethod, channels[0])));
   1618         }
   1619     }
   1620 
   1621     void clearClientSessionLocked(ClientState cs) {
   1622         finishSessionLocked(cs.curSession);
   1623         cs.curSession = null;
   1624         cs.sessionRequested = false;
   1625     }
   1626 
   1627     private void finishSessionLocked(SessionState sessionState) {
   1628         if (sessionState != null) {
   1629             if (sessionState.session != null) {
   1630                 try {
   1631                     sessionState.session.finishSession();
   1632                 } catch (RemoteException e) {
   1633                     Slog.w(TAG, "Session failed to close due to remote exception", e);
   1634                     updateSystemUiLocked(mCurToken, 0 /* vis */, mBackDisposition);
   1635                 }
   1636                 sessionState.session = null;
   1637             }
   1638             if (sessionState.channel != null) {
   1639                 sessionState.channel.dispose();
   1640                 sessionState.channel = null;
   1641             }
   1642         }
   1643     }
   1644 
   1645     void clearCurMethodLocked() {
   1646         if (mCurMethod != null) {
   1647             for (ClientState cs : mClients.values()) {
   1648                 clearClientSessionLocked(cs);
   1649             }
   1650 
   1651             finishSessionLocked(mEnabledSession);
   1652             mEnabledSession = null;
   1653             mCurMethod = null;
   1654         }
   1655         if (mStatusBar != null) {
   1656             mStatusBar.setIconVisibility(mSlotIme, false);
   1657         }
   1658     }
   1659 
   1660     @Override
   1661     public void onServiceDisconnected(ComponentName name) {
   1662         synchronized (mMethodMap) {
   1663             if (DEBUG) Slog.v(TAG, "Service disconnected: " + name
   1664                     + " mCurIntent=" + mCurIntent);
   1665             if (mCurMethod != null && mCurIntent != null
   1666                     && name.equals(mCurIntent.getComponent())) {
   1667                 clearCurMethodLocked();
   1668                 // We consider this to be a new bind attempt, since the system
   1669                 // should now try to restart the service for us.
   1670                 mLastBindTime = SystemClock.uptimeMillis();
   1671                 mShowRequested = mInputShown;
   1672                 mInputShown = false;
   1673                 if (mCurClient != null) {
   1674                     executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
   1675                             MSG_UNBIND_CLIENT, InputMethodClient.UNBIND_REASON_DISCONNECT_IME,
   1676                             mCurSeq, mCurClient.client));
   1677                 }
   1678             }
   1679         }
   1680     }
   1681 
   1682     @Override
   1683     public void updateStatusIcon(IBinder token, String packageName, int iconId) {
   1684         long ident = Binder.clearCallingIdentity();
   1685         try {
   1686             synchronized (mMethodMap) {
   1687                 if (!calledWithValidToken(token)) {
   1688                     final int uid = Binder.getCallingUid();
   1689                     Slog.e(TAG, "Ignoring updateStatusIcon due to an invalid token. uid:" + uid
   1690                             + " token:" + token);
   1691                     return;
   1692                 }
   1693                 if (iconId == 0) {
   1694                     if (DEBUG) Slog.d(TAG, "hide the small icon for the input method");
   1695                     if (mStatusBar != null) {
   1696                         mStatusBar.setIconVisibility(mSlotIme, false);
   1697                     }
   1698                 } else if (packageName != null) {
   1699                     if (DEBUG) Slog.d(TAG, "show a small icon for the input method");
   1700                     CharSequence contentDescription = null;
   1701                     try {
   1702                         // Use PackageManager to load label
   1703                         final PackageManager packageManager = mContext.getPackageManager();
   1704                         contentDescription = packageManager.getApplicationLabel(
   1705                                 mIPackageManager.getApplicationInfo(packageName, 0,
   1706                                         mSettings.getCurrentUserId()));
   1707                     } catch (RemoteException e) {
   1708                         /* ignore */
   1709                     }
   1710                     if (mStatusBar != null) {
   1711                         mStatusBar.setIcon(mSlotIme, packageName, iconId, 0,
   1712                                 contentDescription  != null
   1713                                         ? contentDescription.toString() : null);
   1714                         mStatusBar.setIconVisibility(mSlotIme, true);
   1715                     }
   1716                 }
   1717             }
   1718         } finally {
   1719             Binder.restoreCallingIdentity(ident);
   1720         }
   1721     }
   1722 
   1723     private boolean shouldShowImeSwitcherLocked(int visibility) {
   1724         if (!mShowOngoingImeSwitcherForPhones) return false;
   1725         if (mSwitchingDialog != null) return false;
   1726         if (isScreenLocked()) return false;
   1727         if ((visibility & InputMethodService.IME_ACTIVE) == 0) return false;
   1728         if (mWindowManagerInternal.isHardKeyboardAvailable()) {
   1729             if (mHardKeyboardBehavior == HardKeyboardBehavior.WIRELESS_AFFORDANCE) {
   1730                 // When physical keyboard is attached, we show the ime switcher (or notification if
   1731                 // NavBar is not available) because SHOW_IME_WITH_HARD_KEYBOARD settings currently
   1732                 // exists in the IME switcher dialog.  Might be OK to remove this condition once
   1733                 // SHOW_IME_WITH_HARD_KEYBOARD settings finds a good place to live.
   1734                 return true;
   1735             }
   1736         } else if ((visibility & InputMethodService.IME_VISIBLE) == 0) {
   1737             return false;
   1738         }
   1739 
   1740         List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
   1741         final int N = imis.size();
   1742         if (N > 2) return true;
   1743         if (N < 1) return false;
   1744         int nonAuxCount = 0;
   1745         int auxCount = 0;
   1746         InputMethodSubtype nonAuxSubtype = null;
   1747         InputMethodSubtype auxSubtype = null;
   1748         for(int i = 0; i < N; ++i) {
   1749             final InputMethodInfo imi = imis.get(i);
   1750             final List<InputMethodSubtype> subtypes =
   1751                     mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true);
   1752             final int subtypeCount = subtypes.size();
   1753             if (subtypeCount == 0) {
   1754                 ++nonAuxCount;
   1755             } else {
   1756                 for (int j = 0; j < subtypeCount; ++j) {
   1757                     final InputMethodSubtype subtype = subtypes.get(j);
   1758                     if (!subtype.isAuxiliary()) {
   1759                         ++nonAuxCount;
   1760                         nonAuxSubtype = subtype;
   1761                     } else {
   1762                         ++auxCount;
   1763                         auxSubtype = subtype;
   1764                     }
   1765                 }
   1766             }
   1767         }
   1768         if (nonAuxCount > 1 || auxCount > 1) {
   1769             return true;
   1770         } else if (nonAuxCount == 1 && auxCount == 1) {
   1771             if (nonAuxSubtype != null && auxSubtype != null
   1772                     && (nonAuxSubtype.getLocale().equals(auxSubtype.getLocale())
   1773                             || auxSubtype.overridesImplicitlyEnabledSubtype()
   1774                             || nonAuxSubtype.overridesImplicitlyEnabledSubtype())
   1775                     && nonAuxSubtype.containsExtraValueKey(TAG_TRY_SUPPRESSING_IME_SWITCHER)) {
   1776                 return false;
   1777             }
   1778             return true;
   1779         }
   1780         return false;
   1781     }
   1782 
   1783     private boolean isKeyguardLocked() {
   1784         return mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
   1785     }
   1786 
   1787     @SuppressWarnings("deprecation")
   1788     @Override
   1789     public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
   1790         if (!calledWithValidToken(token)) {
   1791             final int uid = Binder.getCallingUid();
   1792             Slog.e(TAG, "Ignoring setImeWindowStatus due to an invalid token. uid:" + uid
   1793                     + " token:" + token);
   1794             return;
   1795         }
   1796 
   1797         synchronized (mMethodMap) {
   1798             mImeWindowVis = vis;
   1799             mBackDisposition = backDisposition;
   1800             updateSystemUiLocked(token, vis, backDisposition);
   1801         }
   1802     }
   1803 
   1804     private void updateSystemUi(IBinder token, int vis, int backDisposition) {
   1805         synchronized (mMethodMap) {
   1806             updateSystemUiLocked(token, vis, backDisposition);
   1807         }
   1808     }
   1809 
   1810     // Caution! This method is called in this class. Handle multi-user carefully
   1811     private void updateSystemUiLocked(IBinder token, int vis, int backDisposition) {
   1812         if (!calledWithValidToken(token)) {
   1813             final int uid = Binder.getCallingUid();
   1814             Slog.e(TAG, "Ignoring updateSystemUiLocked due to an invalid token. uid:" + uid
   1815                     + " token:" + token);
   1816             return;
   1817         }
   1818 
   1819         // TODO: Move this clearing calling identity block to setImeWindowStatus after making sure
   1820         // all updateSystemUi happens on system previlege.
   1821         final long ident = Binder.clearCallingIdentity();
   1822         try {
   1823             // apply policy for binder calls
   1824             if (vis != 0 && isKeyguardLocked() && !mCurClientInKeyguard) {
   1825                 vis = 0;
   1826             }
   1827             // mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked().
   1828             final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
   1829             if (mStatusBar != null) {
   1830                 mStatusBar.setImeWindowStatus(token, vis, backDisposition,
   1831                         needsToShowImeSwitcher);
   1832             }
   1833             final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
   1834             if (imi != null && needsToShowImeSwitcher) {
   1835                 // Used to load label
   1836                 final CharSequence title = mRes.getText(
   1837                         com.android.internal.R.string.select_input_method);
   1838                 final CharSequence summary = InputMethodUtils.getImeAndSubtypeDisplayName(
   1839                         mContext, imi, mCurrentSubtype);
   1840                 mImeSwitcherNotification.setContentTitle(title)
   1841                         .setContentText(summary)
   1842                         .setContentIntent(mImeSwitchPendingIntent);
   1843                 try {
   1844                     if ((mNotificationManager != null)
   1845                             && !mIWindowManager.hasNavigationBar()) {
   1846                         if (DEBUG) {
   1847                             Slog.d(TAG, "--- show notification: label =  " + summary);
   1848                         }
   1849                         mNotificationManager.notifyAsUser(null,
   1850                                 com.android.internal.R.string.select_input_method,
   1851                                 mImeSwitcherNotification.build(), UserHandle.ALL);
   1852                         mNotificationShown = true;
   1853                     }
   1854                 } catch (RemoteException e) {
   1855                 }
   1856             } else {
   1857                 if (mNotificationShown && mNotificationManager != null) {
   1858                     if (DEBUG) {
   1859                         Slog.d(TAG, "--- hide notification");
   1860                     }
   1861                     mNotificationManager.cancelAsUser(null,
   1862                             com.android.internal.R.string.select_input_method, UserHandle.ALL);
   1863                     mNotificationShown = false;
   1864                 }
   1865             }
   1866         } finally {
   1867             Binder.restoreCallingIdentity(ident);
   1868         }
   1869     }
   1870 
   1871     @Override
   1872     public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) {
   1873         if (!calledFromValidUser()) {
   1874             return;
   1875         }
   1876         synchronized (mMethodMap) {
   1877             final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
   1878             for (int i = 0; i < spans.length; ++i) {
   1879                 SuggestionSpan ss = spans[i];
   1880                 if (!TextUtils.isEmpty(ss.getNotificationTargetClassName())) {
   1881                     mSecureSuggestionSpans.put(ss, currentImi);
   1882                 }
   1883             }
   1884         }
   1885     }
   1886 
   1887     @Override
   1888     public boolean notifySuggestionPicked(SuggestionSpan span, String originalString, int index) {
   1889         if (!calledFromValidUser()) {
   1890             return false;
   1891         }
   1892         synchronized (mMethodMap) {
   1893             final InputMethodInfo targetImi = mSecureSuggestionSpans.get(span);
   1894             // TODO: Do not send the intent if the process of the targetImi is already dead.
   1895             if (targetImi != null) {
   1896                 final String[] suggestions = span.getSuggestions();
   1897                 if (index < 0 || index >= suggestions.length) return false;
   1898                 final String className = span.getNotificationTargetClassName();
   1899                 final Intent intent = new Intent();
   1900                 // Ensures that only a class in the original IME package will receive the
   1901                 // notification.
   1902                 intent.setClassName(targetImi.getPackageName(), className);
   1903                 intent.setAction(SuggestionSpan.ACTION_SUGGESTION_PICKED);
   1904                 intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_BEFORE, originalString);
   1905                 intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_AFTER, suggestions[index]);
   1906                 intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_HASHCODE, span.hashCode());
   1907                 final long ident = Binder.clearCallingIdentity();
   1908                 try {
   1909                     mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
   1910                 } finally {
   1911                     Binder.restoreCallingIdentity(ident);
   1912                 }
   1913                 return true;
   1914             }
   1915         }
   1916         return false;
   1917     }
   1918 
   1919     void updateFromSettingsLocked(boolean enabledMayChange) {
   1920         updateInputMethodsFromSettingsLocked(enabledMayChange);
   1921         updateKeyboardFromSettingsLocked();
   1922     }
   1923 
   1924     void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
   1925         if (enabledMayChange) {
   1926             List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
   1927             for (int i=0; i<enabled.size(); i++) {
   1928                 // We allow the user to select "disabled until used" apps, so if they
   1929                 // are enabling one of those here we now need to make it enabled.
   1930                 InputMethodInfo imm = enabled.get(i);
   1931                 try {
   1932                     ApplicationInfo ai = mIPackageManager.getApplicationInfo(imm.getPackageName(),
   1933                             PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
   1934                             mSettings.getCurrentUserId());
   1935                     if (ai != null && ai.enabledSetting
   1936                             == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
   1937                         if (DEBUG) {
   1938                             Slog.d(TAG, "Update state(" + imm.getId()
   1939                                     + "): DISABLED_UNTIL_USED -> DEFAULT");
   1940                         }
   1941                         mIPackageManager.setApplicationEnabledSetting(imm.getPackageName(),
   1942                                 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
   1943                                 PackageManager.DONT_KILL_APP, mSettings.getCurrentUserId(),
   1944                                 mContext.getBasePackageName());
   1945                     }
   1946                 } catch (RemoteException e) {
   1947                 }
   1948             }
   1949         }
   1950         // We are assuming that whoever is changing DEFAULT_INPUT_METHOD and
   1951         // ENABLED_INPUT_METHODS is taking care of keeping them correctly in
   1952         // sync, so we will never have a DEFAULT_INPUT_METHOD that is not
   1953         // enabled.
   1954         String id = mSettings.getSelectedInputMethod();
   1955         // There is no input method selected, try to choose new applicable input method.
   1956         if (TextUtils.isEmpty(id) && chooseNewDefaultIMELocked()) {
   1957             id = mSettings.getSelectedInputMethod();
   1958         }
   1959         if (!TextUtils.isEmpty(id)) {
   1960             try {
   1961                 setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id));
   1962             } catch (IllegalArgumentException e) {
   1963                 Slog.w(TAG, "Unknown input method from prefs: " + id, e);
   1964                 resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_SWITCH_IME_FAILED);
   1965             }
   1966             mShortcutInputMethodsAndSubtypes.clear();
   1967         } else {
   1968             // There is no longer an input method set, so stop any current one.
   1969             resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_NO_IME);
   1970         }
   1971         // Here is not the perfect place to reset the switching controller. Ideally
   1972         // mSwitchingController and mSettings should be able to share the same state.
   1973         // TODO: Make sure that mSwitchingController and mSettings are sharing the
   1974         // the same enabled IMEs list.
   1975         mSwitchingController.resetCircularListLocked(mContext);
   1976 
   1977     }
   1978 
   1979     public void updateKeyboardFromSettingsLocked() {
   1980         mShowImeWithHardKeyboard = mSettings.isShowImeWithHardKeyboardEnabled();
   1981         if (mSwitchingDialog != null
   1982                 && mSwitchingDialogTitleView != null
   1983                 && mSwitchingDialog.isShowing()) {
   1984             final Switch hardKeySwitch = (Switch)mSwitchingDialogTitleView.findViewById(
   1985                     com.android.internal.R.id.hard_keyboard_switch);
   1986             hardKeySwitch.setChecked(mShowImeWithHardKeyboard);
   1987         }
   1988     }
   1989 
   1990     private void notifyInputMethodSubtypeChanged(final int userId,
   1991             @Nullable final InputMethodInfo inputMethodInfo,
   1992             @Nullable final InputMethodSubtype subtype) {
   1993         final InputManagerInternal inputManagerInternal =
   1994                 LocalServices.getService(InputManagerInternal.class);
   1995         if (inputManagerInternal != null) {
   1996             inputManagerInternal.onInputMethodSubtypeChanged(userId, inputMethodInfo, subtype);
   1997         }
   1998     }
   1999 
   2000     /* package */ void setInputMethodLocked(String id, int subtypeId) {
   2001         InputMethodInfo info = mMethodMap.get(id);
   2002         if (info == null) {
   2003             throw new IllegalArgumentException("Unknown id: " + id);
   2004         }
   2005 
   2006         // See if we need to notify a subtype change within the same IME.
   2007         if (id.equals(mCurMethodId)) {
   2008             final int subtypeCount = info.getSubtypeCount();
   2009             if (subtypeCount <= 0) {
   2010                 return;
   2011             }
   2012             final InputMethodSubtype oldSubtype = mCurrentSubtype;
   2013             final InputMethodSubtype newSubtype;
   2014             if (subtypeId >= 0 && subtypeId < subtypeCount) {
   2015                 newSubtype = info.getSubtypeAt(subtypeId);
   2016             } else {
   2017                 // If subtype is null, try to find the most applicable one from
   2018                 // getCurrentInputMethodSubtype.
   2019                 newSubtype = getCurrentInputMethodSubtypeLocked();
   2020             }
   2021             if (newSubtype == null || oldSubtype == null) {
   2022                 Slog.w(TAG, "Illegal subtype state: old subtype = " + oldSubtype
   2023                         + ", new subtype = " + newSubtype);
   2024                 return;
   2025             }
   2026             if (newSubtype != oldSubtype) {
   2027                 setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
   2028                 if (mCurMethod != null) {
   2029                     try {
   2030                         updateSystemUiLocked(mCurToken, mImeWindowVis, mBackDisposition);
   2031                         mCurMethod.changeInputMethodSubtype(newSubtype);
   2032                     } catch (RemoteException e) {
   2033                         Slog.w(TAG, "Failed to call changeInputMethodSubtype");
   2034                         return;
   2035                     }
   2036                 }
   2037                 notifyInputMethodSubtypeChanged(mSettings.getCurrentUserId(), info, newSubtype);
   2038             }
   2039             return;
   2040         }
   2041 
   2042         // Changing to a different IME.
   2043         final long ident = Binder.clearCallingIdentity();
   2044         try {
   2045             // Set a subtype to this input method.
   2046             // subtypeId the name of a subtype which will be set.
   2047             setSelectedInputMethodAndSubtypeLocked(info, subtypeId, false);
   2048             // mCurMethodId should be updated after setSelectedInputMethodAndSubtypeLocked()
   2049             // because mCurMethodId is stored as a history in
   2050             // setSelectedInputMethodAndSubtypeLocked().
   2051             mCurMethodId = id;
   2052 
   2053             if (ActivityManagerNative.isSystemReady()) {
   2054                 Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
   2055                 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
   2056                 intent.putExtra("input_method_id", id);
   2057                 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
   2058             }
   2059             unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_SWITCH_IME);
   2060         } finally {
   2061             Binder.restoreCallingIdentity(ident);
   2062         }
   2063 
   2064         notifyInputMethodSubtypeChanged(mSettings.getCurrentUserId(), info,
   2065                 getCurrentInputMethodSubtypeLocked());
   2066     }
   2067 
   2068     @Override
   2069     public boolean showSoftInput(IInputMethodClient client, int flags,
   2070             ResultReceiver resultReceiver) {
   2071         if (!calledFromValidUser()) {
   2072             return false;
   2073         }
   2074         int uid = Binder.getCallingUid();
   2075         long ident = Binder.clearCallingIdentity();
   2076         try {
   2077             synchronized (mMethodMap) {
   2078                 if (mCurClient == null || client == null
   2079                         || mCurClient.client.asBinder() != client.asBinder()) {
   2080                     try {
   2081                         // We need to check if this is the current client with
   2082                         // focus in the window manager, to allow this call to
   2083                         // be made before input is started in it.
   2084                         if (!mIWindowManager.inputMethodClientHasFocus(client)) {
   2085                             Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client);
   2086                             return false;
   2087                         }
   2088                     } catch (RemoteException e) {
   2089                         return false;
   2090                     }
   2091                 }
   2092 
   2093                 if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
   2094                 return showCurrentInputLocked(flags, resultReceiver);
   2095             }
   2096         } finally {
   2097             Binder.restoreCallingIdentity(ident);
   2098         }
   2099     }
   2100 
   2101     boolean showCurrentInputLocked(int flags, ResultReceiver resultReceiver) {
   2102         mShowRequested = true;
   2103         if (mAccessibilityRequestingNoSoftKeyboard) {
   2104             return false;
   2105         }
   2106 
   2107         if ((flags&InputMethodManager.SHOW_FORCED) != 0) {
   2108             mShowExplicitlyRequested = true;
   2109             mShowForced = true;
   2110         } else if ((flags&InputMethodManager.SHOW_IMPLICIT) == 0) {
   2111             mShowExplicitlyRequested = true;
   2112         }
   2113 
   2114         if (!mSystemReady) {
   2115             return false;
   2116         }
   2117 
   2118         boolean res = false;
   2119         if (mCurMethod != null) {
   2120             if (DEBUG) Slog.d(TAG, "showCurrentInputLocked: mCurToken=" + mCurToken);
   2121             executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(
   2122                     MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod,
   2123                     resultReceiver));
   2124             mInputShown = true;
   2125             if (mHaveConnection && !mVisibleBound) {
   2126                 bindCurrentInputMethodService(
   2127                         mCurIntent, mVisibleConnection, Context.BIND_AUTO_CREATE
   2128                                 | Context.BIND_TREAT_LIKE_ACTIVITY
   2129                                 | Context.BIND_FOREGROUND_SERVICE);
   2130                 mVisibleBound = true;
   2131             }
   2132             res = true;
   2133         } else if (mHaveConnection && SystemClock.uptimeMillis()
   2134                 >= (mLastBindTime+TIME_TO_RECONNECT)) {
   2135             // The client has asked to have the input method shown, but
   2136             // we have been sitting here too long with a connection to the
   2137             // service and no interface received, so let's disconnect/connect
   2138             // to try to prod things along.
   2139             EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, mCurMethodId,
   2140                     SystemClock.uptimeMillis()-mLastBindTime,1);
   2141             Slog.w(TAG, "Force disconnect/connect to the IME in showCurrentInputLocked()");
   2142             mContext.unbindService(this);
   2143             bindCurrentInputMethodService(mCurIntent, this, Context.BIND_AUTO_CREATE
   2144                     | Context.BIND_NOT_VISIBLE);
   2145         } else {
   2146             if (DEBUG) {
   2147                 Slog.d(TAG, "Can't show input: connection = " + mHaveConnection + ", time = "
   2148                         + ((mLastBindTime+TIME_TO_RECONNECT) - SystemClock.uptimeMillis()));
   2149             }
   2150         }
   2151 
   2152         return res;
   2153     }
   2154 
   2155     @Override
   2156     public boolean hideSoftInput(IInputMethodClient client, int flags,
   2157             ResultReceiver resultReceiver) {
   2158         if (!calledFromValidUser()) {
   2159             return false;
   2160         }
   2161         int uid = Binder.getCallingUid();
   2162         long ident = Binder.clearCallingIdentity();
   2163         try {
   2164             synchronized (mMethodMap) {
   2165                 if (mCurClient == null || client == null
   2166                         || mCurClient.client.asBinder() != client.asBinder()) {
   2167                     try {
   2168                         // We need to check if this is the current client with
   2169                         // focus in the window manager, to allow this call to
   2170                         // be made before input is started in it.
   2171                         if (!mIWindowManager.inputMethodClientHasFocus(client)) {
   2172                             if (DEBUG) Slog.w(TAG, "Ignoring hideSoftInput of uid "
   2173                                     + uid + ": " + client);
   2174                             return false;
   2175                         }
   2176                     } catch (RemoteException e) {
   2177                         return false;
   2178                     }
   2179                 }
   2180 
   2181                 if (DEBUG) Slog.v(TAG, "Client requesting input be hidden");
   2182                 return hideCurrentInputLocked(flags, resultReceiver);
   2183             }
   2184         } finally {
   2185             Binder.restoreCallingIdentity(ident);
   2186         }
   2187     }
   2188 
   2189     boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver) {
   2190         if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
   2191                 && (mShowExplicitlyRequested || mShowForced)) {
   2192             if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
   2193             return false;
   2194         }
   2195         if (mShowForced && (flags&InputMethodManager.HIDE_NOT_ALWAYS) != 0) {
   2196             if (DEBUG) Slog.v(TAG, "Not hiding: forced show not cancelled by not-always hide");
   2197             return false;
   2198         }
   2199 
   2200         // There is a chance that IMM#hideSoftInput() is called in a transient state where
   2201         // IMMS#InputShown is already updated to be true whereas IMMS#mImeWindowVis is still waiting
   2202         // to be updated with the new value sent from IME process.  Even in such a transient state
   2203         // historically we have accepted an incoming call of IMM#hideSoftInput() from the
   2204         // application process as a valid request, and have even promised such a behavior with CTS
   2205         // since Android Eclair.  That's why we need to accept IMM#hideSoftInput() even when only
   2206         // IMMS#InputShown indicates that the software keyboard is shown.
   2207         // TODO: Clean up, IMMS#mInputShown, IMMS#mImeWindowVis and mShowRequested.
   2208         final boolean shouldHideSoftInput = (mCurMethod != null) && (mInputShown ||
   2209                 (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
   2210         boolean res;
   2211         if (shouldHideSoftInput) {
   2212             // The IME will report its visible state again after the following message finally
   2213             // delivered to the IME process as an IPC.  Hence the inconsistency between
   2214             // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in
   2215             // the final state.
   2216             executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
   2217                     MSG_HIDE_SOFT_INPUT, mCurMethod, resultReceiver));
   2218             res = true;
   2219         } else {
   2220             res = false;
   2221         }
   2222         if (mHaveConnection && mVisibleBound) {
   2223             mContext.unbindService(mVisibleConnection);
   2224             mVisibleBound = false;
   2225         }
   2226         mInputShown = false;
   2227         mShowRequested = false;
   2228         mShowExplicitlyRequested = false;
   2229         mShowForced = false;
   2230         return res;
   2231     }
   2232 
   2233     @Override
   2234     public InputBindResult startInputOrWindowGainedFocus(
   2235             /* @InputMethodClient.StartInputReason */ final int startInputReason,
   2236             IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode,
   2237             int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
   2238             /* @InputConnectionInspector.missingMethods */ final int missingMethods) {
   2239         if (windowToken != null) {
   2240             return windowGainedFocus(startInputReason, client, windowToken, controlFlags,
   2241                     softInputMode, windowFlags, attribute, inputContext, missingMethods);
   2242         } else {
   2243             return startInput(startInputReason, client, inputContext, missingMethods, attribute,
   2244                     controlFlags);
   2245         }
   2246     }
   2247 
   2248     private InputBindResult windowGainedFocus(
   2249             /* @InputMethodClient.StartInputReason */ final int startInputReason,
   2250             IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode,
   2251             int windowFlags, EditorInfo attribute, IInputContext inputContext,
   2252             /* @InputConnectionInspector.missingMethods */  final int missingMethods) {
   2253         // Needs to check the validity before clearing calling identity
   2254         final boolean calledFromValidUser = calledFromValidUser();
   2255         InputBindResult res = null;
   2256         long ident = Binder.clearCallingIdentity();
   2257         try {
   2258             synchronized (mMethodMap) {
   2259                 if (DEBUG) Slog.v(TAG, "windowGainedFocus: reason="
   2260                         + InputMethodClient.getStartInputReason(startInputReason)
   2261                         + " client=" + client.asBinder()
   2262                         + " inputContext=" + inputContext
   2263                         + " missingMethods="
   2264                         + InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods)
   2265                         + " attribute=" + attribute
   2266                         + " controlFlags=#" + Integer.toHexString(controlFlags)
   2267                         + " softInputMode=#" + Integer.toHexString(softInputMode)
   2268                         + " windowFlags=#" + Integer.toHexString(windowFlags));
   2269 
   2270                 ClientState cs = mClients.get(client.asBinder());
   2271                 if (cs == null) {
   2272                     throw new IllegalArgumentException("unknown client "
   2273                             + client.asBinder());
   2274                 }
   2275 
   2276                 try {
   2277                     if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) {
   2278                         // Check with the window manager to make sure this client actually
   2279                         // has a window with focus.  If not, reject.  This is thread safe
   2280                         // because if the focus changes some time before or after, the
   2281                         // next client receiving focus that has any interest in input will
   2282                         // be calling through here after that change happens.
   2283                         Slog.w(TAG, "Focus gain on non-focused client " + cs.client
   2284                                 + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
   2285                         return null;
   2286                     }
   2287                 } catch (RemoteException e) {
   2288                 }
   2289 
   2290                 if (!calledFromValidUser) {
   2291                     Slog.w(TAG, "A background user is requesting window. Hiding IME.");
   2292                     Slog.w(TAG, "If you want to interect with IME, you need "
   2293                             + "android.permission.INTERACT_ACROSS_USERS_FULL");
   2294                     hideCurrentInputLocked(0, null);
   2295                     return null;
   2296                 }
   2297 
   2298                 if (mCurFocusedWindow == windowToken) {
   2299                     Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client
   2300                             + " attribute=" + attribute + ", token = " + windowToken);
   2301                     if (attribute != null) {
   2302                         return startInputUncheckedLocked(cs, inputContext, missingMethods,
   2303                                 attribute, controlFlags);
   2304                     }
   2305                     return null;
   2306                 }
   2307                 mCurFocusedWindow = windowToken;
   2308                 mCurFocusedWindowClient = cs;
   2309 
   2310                 // Should we auto-show the IME even if the caller has not
   2311                 // specified what should be done with it?
   2312                 // We only do this automatically if the window can resize
   2313                 // to accommodate the IME (so what the user sees will give
   2314                 // them good context without input information being obscured
   2315                 // by the IME) or if running on a large screen where there
   2316                 // is more room for the target window + IME.
   2317                 final boolean doAutoShow =
   2318                         (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
   2319                                 == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
   2320                         || mRes.getConfiguration().isLayoutSizeAtLeast(
   2321                                 Configuration.SCREENLAYOUT_SIZE_LARGE);
   2322                 final boolean isTextEditor =
   2323                         (controlFlags&InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR) != 0;
   2324 
   2325                 // We want to start input before showing the IME, but after closing
   2326                 // it.  We want to do this after closing it to help the IME disappear
   2327                 // more quickly (not get stuck behind it initializing itself for the
   2328                 // new focused input, even if its window wants to hide the IME).
   2329                 boolean didStart = false;
   2330 
   2331                 switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) {
   2332                     case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
   2333                         if (!isTextEditor || !doAutoShow) {
   2334                             if (WindowManager.LayoutParams.mayUseInputMethod(windowFlags)) {
   2335                                 // There is no focus view, and this window will
   2336                                 // be behind any soft input window, so hide the
   2337                                 // soft input window if it is shown.
   2338                                 if (DEBUG) Slog.v(TAG, "Unspecified window will hide input");
   2339                                 hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null);
   2340                             }
   2341                         } else if (isTextEditor && doAutoShow && (softInputMode &
   2342                                 WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
   2343                             // There is a focus view, and we are navigating forward
   2344                             // into the window, so show the input window for the user.
   2345                             // We only do this automatically if the window can resize
   2346                             // to accommodate the IME (so what the user sees will give
   2347                             // them good context without input information being obscured
   2348                             // by the IME) or if running on a large screen where there
   2349                             // is more room for the target window + IME.
   2350                             if (DEBUG) Slog.v(TAG, "Unspecified window will show input");
   2351                             if (attribute != null) {
   2352                                 res = startInputUncheckedLocked(cs, inputContext,
   2353                                         missingMethods, attribute, controlFlags);
   2354                                 didStart = true;
   2355                             }
   2356                             showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
   2357                         }
   2358                         break;
   2359                     case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
   2360                         // Do nothing.
   2361                         break;
   2362                     case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
   2363                         if ((softInputMode &
   2364                                 WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
   2365                             if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward");
   2366                             hideCurrentInputLocked(0, null);
   2367                         }
   2368                         break;
   2369                     case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
   2370                         if (DEBUG) Slog.v(TAG, "Window asks to hide input");
   2371                         hideCurrentInputLocked(0, null);
   2372                         break;
   2373                     case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
   2374                         if ((softInputMode &
   2375                                 WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
   2376                             if (DEBUG) Slog.v(TAG, "Window asks to show input going forward");
   2377                             if (attribute != null) {
   2378                                 res = startInputUncheckedLocked(cs, inputContext,
   2379                                         missingMethods, attribute, controlFlags);
   2380                                 didStart = true;
   2381                             }
   2382                             showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
   2383                         }
   2384                         break;
   2385                     case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
   2386                         if (DEBUG) Slog.v(TAG, "Window asks to always show input");
   2387                         if (attribute != null) {
   2388                             res = startInputUncheckedLocked(cs, inputContext, missingMethods,
   2389                                     attribute, controlFlags);
   2390                             didStart = true;
   2391                         }
   2392                         showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
   2393                         break;
   2394                 }
   2395 
   2396                 if (!didStart && attribute != null) {
   2397                     res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
   2398                             controlFlags);
   2399                 }
   2400             }
   2401         } finally {
   2402             Binder.restoreCallingIdentity(ident);
   2403         }
   2404 
   2405         return res;
   2406     }
   2407 
   2408     @Override
   2409     public void showInputMethodPickerFromClient(
   2410             IInputMethodClient client, int auxiliarySubtypeMode) {
   2411         if (!calledFromValidUser()) {
   2412             return;
   2413         }
   2414         synchronized (mMethodMap) {
   2415             if (mCurClient == null || client == null
   2416                     || mCurClient.client.asBinder() != client.asBinder()) {
   2417                 Slog.w(TAG, "Ignoring showInputMethodPickerFromClient of uid "
   2418                         + Binder.getCallingUid() + ": " + client);
   2419             }
   2420 
   2421             // Always call subtype picker, because subtype picker is a superset of input method
   2422             // picker.
   2423             mHandler.sendMessage(mCaller.obtainMessageI(
   2424                     MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode));
   2425         }
   2426     }
   2427 
   2428     @Override
   2429     public void setInputMethod(IBinder token, String id) {
   2430         if (!calledFromValidUser()) {
   2431             return;
   2432         }
   2433         setInputMethodWithSubtypeId(token, id, NOT_A_SUBTYPE_ID);
   2434     }
   2435 
   2436     @Override
   2437     public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
   2438         if (!calledFromValidUser()) {
   2439             return;
   2440         }
   2441         synchronized (mMethodMap) {
   2442             if (subtype != null) {
   2443                 setInputMethodWithSubtypeIdLocked(token, id,
   2444                         InputMethodUtils.getSubtypeIdFromHashCode(mMethodMap.get(id),
   2445                                 subtype.hashCode()));
   2446             } else {
   2447                 setInputMethod(token, id);
   2448             }
   2449         }
   2450     }
   2451 
   2452     @Override
   2453     public void showInputMethodAndSubtypeEnablerFromClient(
   2454             IInputMethodClient client, String inputMethodId) {
   2455         if (!calledFromValidUser()) {
   2456             return;
   2457         }
   2458         synchronized (mMethodMap) {
   2459             executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
   2460                     MSG_SHOW_IM_SUBTYPE_ENABLER, inputMethodId));
   2461         }
   2462     }
   2463 
   2464     @Override
   2465     public boolean switchToLastInputMethod(IBinder token) {
   2466         if (!calledFromValidUser()) {
   2467             return false;
   2468         }
   2469         synchronized (mMethodMap) {
   2470             final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
   2471             final InputMethodInfo lastImi;
   2472             if (lastIme != null) {
   2473                 lastImi = mMethodMap.get(lastIme.first);
   2474             } else {
   2475                 lastImi = null;
   2476             }
   2477             String targetLastImiId = null;
   2478             int subtypeId = NOT_A_SUBTYPE_ID;
   2479             if (lastIme != null && lastImi != null) {
   2480                 final boolean imiIdIsSame = lastImi.getId().equals(mCurMethodId);
   2481                 final int lastSubtypeHash = Integer.parseInt(lastIme.second);
   2482                 final int currentSubtypeHash = mCurrentSubtype == null ? NOT_A_SUBTYPE_ID
   2483                         : mCurrentSubtype.hashCode();
   2484                 // If the last IME is the same as the current IME and the last subtype is not
   2485                 // defined, there is no need to switch to the last IME.
   2486                 if (!imiIdIsSame || lastSubtypeHash != currentSubtypeHash) {
   2487                     targetLastImiId = lastIme.first;
   2488                     subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(lastImi, lastSubtypeHash);
   2489                 }
   2490             }
   2491 
   2492             if (TextUtils.isEmpty(targetLastImiId)
   2493                     && !InputMethodUtils.canAddToLastInputMethod(mCurrentSubtype)) {
   2494                 // This is a safety net. If the currentSubtype can't be added to the history
   2495                 // and the framework couldn't find the last ime, we will make the last ime be
   2496                 // the most applicable enabled keyboard subtype of the system imes.
   2497                 final List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
   2498                 if (enabled != null) {
   2499                     final int N = enabled.size();
   2500                     final String locale = mCurrentSubtype == null
   2501                             ? mRes.getConfiguration().locale.toString()
   2502                             : mCurrentSubtype.getLocale();
   2503                     for (int i = 0; i < N; ++i) {
   2504                         final InputMethodInfo imi = enabled.get(i);
   2505                         if (imi.getSubtypeCount() > 0 && InputMethodUtils.isSystemIme(imi)) {
   2506                             InputMethodSubtype keyboardSubtype =
   2507                                     InputMethodUtils.findLastResortApplicableSubtypeLocked(mRes,
   2508                                             InputMethodUtils.getSubtypes(imi),
   2509                                             InputMethodUtils.SUBTYPE_MODE_KEYBOARD, locale, true);
   2510                             if (keyboardSubtype != null) {
   2511                                 targetLastImiId = imi.getId();
   2512                                 subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
   2513                                         imi, keyboardSubtype.hashCode());
   2514                                 if(keyboardSubtype.getLocale().equals(locale)) {
   2515                                     break;
   2516                                 }
   2517                             }
   2518                         }
   2519                     }
   2520                 }
   2521             }
   2522 
   2523             if (!TextUtils.isEmpty(targetLastImiId)) {
   2524                 if (DEBUG) {
   2525                     Slog.d(TAG, "Switch to: " + lastImi.getId() + ", " + lastIme.second
   2526                             + ", from: " + mCurMethodId + ", " + subtypeId);
   2527                 }
   2528                 setInputMethodWithSubtypeIdLocked(token, targetLastImiId, subtypeId);
   2529                 return true;
   2530             } else {
   2531                 return false;
   2532             }
   2533         }
   2534     }
   2535 
   2536     @Override
   2537     public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) {
   2538         if (!calledFromValidUser()) {
   2539             return false;
   2540         }
   2541         synchronized (mMethodMap) {
   2542             if (!calledWithValidToken(token)) {
   2543                 final int uid = Binder.getCallingUid();
   2544                 Slog.e(TAG, "Ignoring switchToNextInputMethod due to an invalid token. uid:" + uid
   2545                         + " token:" + token);
   2546                 return false;
   2547             }
   2548             final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
   2549                     onlyCurrentIme, mMethodMap.get(mCurMethodId), mCurrentSubtype,
   2550                     true /* forward */);
   2551             if (nextSubtype == null) {
   2552                 return false;
   2553             }
   2554             setInputMethodWithSubtypeIdLocked(token, nextSubtype.mImi.getId(),
   2555                     nextSubtype.mSubtypeId);
   2556             return true;
   2557         }
   2558     }
   2559 
   2560     @Override
   2561     public boolean shouldOfferSwitchingToNextInputMethod(IBinder token) {
   2562         if (!calledFromValidUser()) {
   2563             return false;
   2564         }
   2565         synchronized (mMethodMap) {
   2566             if (!calledWithValidToken(token)) {
   2567                 final int uid = Binder.getCallingUid();
   2568                 Slog.e(TAG, "Ignoring shouldOfferSwitchingToNextInputMethod due to an invalid "
   2569                         + "token. uid:" + uid + " token:" + token);
   2570                 return false;
   2571             }
   2572             final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
   2573                     false /* onlyCurrentIme */, mMethodMap.get(mCurMethodId), mCurrentSubtype,
   2574                     true /* forward */);
   2575             if (nextSubtype == null) {
   2576                 return false;
   2577             }
   2578             return true;
   2579         }
   2580     }
   2581 
   2582     @Override
   2583     public InputMethodSubtype getLastInputMethodSubtype() {
   2584         if (!calledFromValidUser()) {
   2585             return null;
   2586         }
   2587         synchronized (mMethodMap) {
   2588             final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
   2589             // TODO: Handle the case of the last IME with no subtypes
   2590             if (lastIme == null || TextUtils.isEmpty(lastIme.first)
   2591                     || TextUtils.isEmpty(lastIme.second)) return null;
   2592             final InputMethodInfo lastImi = mMethodMap.get(lastIme.first);
   2593             if (lastImi == null) return null;
   2594             try {
   2595                 final int lastSubtypeHash = Integer.parseInt(lastIme.second);
   2596                 final int lastSubtypeId =
   2597                         InputMethodUtils.getSubtypeIdFromHashCode(lastImi, lastSubtypeHash);
   2598                 if (lastSubtypeId < 0 || lastSubtypeId >= lastImi.getSubtypeCount()) {
   2599                     return null;
   2600                 }
   2601                 return lastImi.getSubtypeAt(lastSubtypeId);
   2602             } catch (NumberFormatException e) {
   2603                 return null;
   2604             }
   2605         }
   2606     }
   2607 
   2608     @Override
   2609     public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
   2610         if (!calledFromValidUser()) {
   2611             return;
   2612         }
   2613         // By this IPC call, only a process which shares the same uid with the IME can add
   2614         // additional input method subtypes to the IME.
   2615         if (TextUtils.isEmpty(imiId) || subtypes == null) return;
   2616         synchronized (mMethodMap) {
   2617             final InputMethodInfo imi = mMethodMap.get(imiId);
   2618             if (imi == null) return;
   2619             final String[] packageInfos;
   2620             try {
   2621                 packageInfos = mIPackageManager.getPackagesForUid(Binder.getCallingUid());
   2622             } catch (RemoteException e) {
   2623                 Slog.e(TAG, "Failed to get package infos");
   2624                 return;
   2625             }
   2626             if (packageInfos != null) {
   2627                 final int packageNum = packageInfos.length;
   2628                 for (int i = 0; i < packageNum; ++i) {
   2629                     if (packageInfos[i].equals(imi.getPackageName())) {
   2630                         mFileManager.addInputMethodSubtypes(imi, subtypes);
   2631                         final long ident = Binder.clearCallingIdentity();
   2632                         try {
   2633                             buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
   2634                         } finally {
   2635                             Binder.restoreCallingIdentity(ident);
   2636                         }
   2637                         return;
   2638                     }
   2639                 }
   2640             }
   2641         }
   2642         return;
   2643     }
   2644 
   2645     @Override
   2646     public int getInputMethodWindowVisibleHeight() {
   2647         return mWindowManagerInternal.getInputMethodWindowVisibleHeight();
   2648     }
   2649 
   2650     @Override
   2651     public void clearLastInputMethodWindowForTransition(IBinder token) {
   2652         if (!calledFromValidUser()) {
   2653             return;
   2654         }
   2655         final long ident = Binder.clearCallingIdentity();
   2656         try {
   2657             synchronized (mMethodMap) {
   2658                 if (!calledWithValidToken(token)) {
   2659                     final int uid = Binder.getCallingUid();
   2660                     Slog.e(TAG, "Ignoring clearLastInputMethodWindowForTransition due to an "
   2661                             + "invalid token. uid:" + uid + " token:" + token);
   2662                     return;
   2663                 }
   2664             }
   2665             mWindowManagerInternal.clearLastInputMethodWindowForTransition();
   2666         } finally {
   2667             Binder.restoreCallingIdentity(ident);
   2668         }
   2669     }
   2670 
   2671     @Override
   2672     public void notifyUserAction(int sequenceNumber) {
   2673         if (DEBUG) {
   2674             Slog.d(TAG, "Got the notification of a user action. sequenceNumber:" + sequenceNumber);
   2675         }
   2676         synchronized (mMethodMap) {
   2677             if (mCurUserActionNotificationSequenceNumber != sequenceNumber) {
   2678                 if (DEBUG) {
   2679                     Slog.d(TAG, "Ignoring the user action notification due to the sequence number "
   2680                             + "mismatch. expected:" + mCurUserActionNotificationSequenceNumber
   2681                             + " actual: " + sequenceNumber);
   2682                 }
   2683                 return;
   2684             }
   2685             final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
   2686             if (imi != null) {
   2687                 mSwitchingController.onUserActionLocked(imi, mCurrentSubtype);
   2688             }
   2689         }
   2690     }
   2691 
   2692     private void setInputMethodWithSubtypeId(IBinder token, String id, int subtypeId) {
   2693         synchronized (mMethodMap) {
   2694             setInputMethodWithSubtypeIdLocked(token, id, subtypeId);
   2695         }
   2696     }
   2697 
   2698     private void setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId) {
   2699         if (token == null) {
   2700             if (mContext.checkCallingOrSelfPermission(
   2701                     android.Manifest.permission.WRITE_SECURE_SETTINGS)
   2702                     != PackageManager.PERMISSION_GRANTED) {
   2703                 throw new SecurityException(
   2704                         "Using null token requires permission "
   2705                         + android.Manifest.permission.WRITE_SECURE_SETTINGS);
   2706             }
   2707         } else if (mCurToken != token) {
   2708             Slog.w(TAG, "Ignoring setInputMethod of uid " + Binder.getCallingUid()
   2709                     + " token: " + token);
   2710             return;
   2711         }
   2712 
   2713         final long ident = Binder.clearCallingIdentity();
   2714         try {
   2715             setInputMethodLocked(id, subtypeId);
   2716         } finally {
   2717             Binder.restoreCallingIdentity(ident);
   2718         }
   2719     }
   2720 
   2721     @Override
   2722     public void hideMySoftInput(IBinder token, int flags) {
   2723         if (!calledFromValidUser()) {
   2724             return;
   2725         }
   2726         synchronized (mMethodMap) {
   2727             if (!calledWithValidToken(token)) {
   2728                 final int uid = Binder.getCallingUid();
   2729                 Slog.e(TAG, "Ignoring hideInputMethod due to an invalid token. uid:"
   2730                         + uid + " token:" + token);
   2731                 return;
   2732             }
   2733             long ident = Binder.clearCallingIdentity();
   2734             try {
   2735                 hideCurrentInputLocked(flags, null);
   2736             } finally {
   2737                 Binder.restoreCallingIdentity(ident);
   2738             }
   2739         }
   2740     }
   2741 
   2742     @Override
   2743     public void showMySoftInput(IBinder token, int flags) {
   2744         if (!calledFromValidUser()) {
   2745             return;
   2746         }
   2747         synchronized (mMethodMap) {
   2748             if (!calledWithValidToken(token)) {
   2749                 final int uid = Binder.getCallingUid();
   2750                 Slog.e(TAG, "Ignoring showMySoftInput due to an invalid token. uid:"
   2751                         + uid + " token:" + token);
   2752                 return;
   2753             }
   2754             long ident = Binder.clearCallingIdentity();
   2755             try {
   2756                 showCurrentInputLocked(flags, null);
   2757             } finally {
   2758                 Binder.restoreCallingIdentity(ident);
   2759             }
   2760         }
   2761     }
   2762 
   2763     void setEnabledSessionInMainThread(SessionState session) {
   2764         if (mEnabledSession != session) {
   2765             if (mEnabledSession != null && mEnabledSession.session != null) {
   2766                 try {
   2767                     if (DEBUG) Slog.v(TAG, "Disabling: " + mEnabledSession);
   2768                     mEnabledSession.method.setSessionEnabled(mEnabledSession.session, false);
   2769                 } catch (RemoteException e) {
   2770                 }
   2771             }
   2772             mEnabledSession = session;
   2773             if (mEnabledSession != null && mEnabledSession.session != null) {
   2774                 try {
   2775                     if (DEBUG) Slog.v(TAG, "Enabling: " + mEnabledSession);
   2776                     mEnabledSession.method.setSessionEnabled(mEnabledSession.session, true);
   2777                 } catch (RemoteException e) {
   2778                 }
   2779             }
   2780         }
   2781     }
   2782 
   2783     @Override
   2784     public boolean handleMessage(Message msg) {
   2785         SomeArgs args;
   2786         switch (msg.what) {
   2787             case MSG_SHOW_IM_SUBTYPE_PICKER:
   2788                 final boolean showAuxSubtypes;
   2789                 switch (msg.arg1) {
   2790                     case InputMethodManager.SHOW_IM_PICKER_MODE_AUTO:
   2791                         // This is undocumented so far, but IMM#showInputMethodPicker() has been
   2792                         // implemented so that auxiliary subtypes will be excluded when the soft
   2793                         // keyboard is invisible.
   2794                         showAuxSubtypes = mInputShown;
   2795                         break;
   2796                     case InputMethodManager.SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES:
   2797                         showAuxSubtypes = true;
   2798                         break;
   2799                     case InputMethodManager.SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES:
   2800                         showAuxSubtypes = false;
   2801                         break;
   2802                     default:
   2803                         Slog.e(TAG, "Unknown subtype picker mode = " + msg.arg1);
   2804                         return false;
   2805                 }
   2806                 showInputMethodMenu(showAuxSubtypes);
   2807                 return true;
   2808 
   2809             case MSG_SHOW_IM_SUBTYPE_ENABLER:
   2810                 showInputMethodAndSubtypeEnabler((String)msg.obj);
   2811                 return true;
   2812 
   2813             case MSG_SHOW_IM_CONFIG:
   2814                 showConfigureInputMethods();
   2815                 return true;
   2816 
   2817             // ---------------------------------------------------------
   2818 
   2819             case MSG_UNBIND_INPUT:
   2820                 try {
   2821                     ((IInputMethod)msg.obj).unbindInput();
   2822                 } catch (RemoteException e) {
   2823                     // There is nothing interesting about the method dying.
   2824                 }
   2825                 return true;
   2826             case MSG_BIND_INPUT:
   2827                 args = (SomeArgs)msg.obj;
   2828                 try {
   2829                     ((IInputMethod)args.arg1).bindInput((InputBinding)args.arg2);
   2830                 } catch (RemoteException e) {
   2831                 }
   2832                 args.recycle();
   2833                 return true;
   2834             case MSG_SHOW_SOFT_INPUT:
   2835                 args = (SomeArgs)msg.obj;
   2836                 try {
   2837                     if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput("
   2838                             + msg.arg1 + ", " + args.arg2 + ")");
   2839                     ((IInputMethod)args.arg1).showSoftInput(msg.arg1, (ResultReceiver)args.arg2);
   2840                 } catch (RemoteException e) {
   2841                 }
   2842                 args.recycle();
   2843                 return true;
   2844             case MSG_HIDE_SOFT_INPUT:
   2845                 args = (SomeArgs)msg.obj;
   2846                 try {
   2847                     if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, "
   2848                             + args.arg2 + ")");
   2849                     ((IInputMethod)args.arg1).hideSoftInput(0, (ResultReceiver)args.arg2);
   2850                 } catch (RemoteException e) {
   2851                 }
   2852                 args.recycle();
   2853                 return true;
   2854             case MSG_HIDE_CURRENT_INPUT_METHOD:
   2855                 synchronized (mMethodMap) {
   2856                     hideCurrentInputLocked(0, null);
   2857                 }
   2858                 return true;
   2859             case MSG_ATTACH_TOKEN:
   2860                 args = (SomeArgs)msg.obj;
   2861                 try {
   2862                     if (DEBUG) Slog.v(TAG, "Sending attach of token: " + args.arg2);
   2863                     ((IInputMethod)args.arg1).attachToken((IBinder)args.arg2);
   2864                 } catch (RemoteException e) {
   2865                 }
   2866                 args.recycle();
   2867                 return true;
   2868             case MSG_CREATE_SESSION: {
   2869                 args = (SomeArgs)msg.obj;
   2870                 IInputMethod method = (IInputMethod)args.arg1;
   2871                 InputChannel channel = (InputChannel)args.arg2;
   2872                 try {
   2873                     method.createSession(channel, (IInputSessionCallback)args.arg3);
   2874                 } catch (RemoteException e) {
   2875                 } finally {
   2876                     // Dispose the channel if the input method is not local to this process
   2877                     // because the remote proxy will get its own copy when unparceled.
   2878                     if (channel != null && Binder.isProxy(method)) {
   2879                         channel.dispose();
   2880                     }
   2881                 }
   2882                 args.recycle();
   2883                 return true;
   2884             }
   2885             // ---------------------------------------------------------
   2886 
   2887             case MSG_START_INPUT: {
   2888                 int missingMethods = msg.arg1;
   2889                 args = (SomeArgs) msg.obj;
   2890                 try {
   2891                     SessionState session = (SessionState) args.arg1;
   2892                     setEnabledSessionInMainThread(session);
   2893                     session.method.startInput((IInputContext) args.arg2, missingMethods,
   2894                             (EditorInfo) args.arg3);
   2895                 } catch (RemoteException e) {
   2896                 }
   2897                 args.recycle();
   2898                 return true;
   2899             }
   2900             case MSG_RESTART_INPUT: {
   2901                 int missingMethods = msg.arg1;
   2902                 args = (SomeArgs) msg.obj;
   2903                 try {
   2904                     SessionState session = (SessionState) args.arg1;
   2905                     setEnabledSessionInMainThread(session);
   2906                     session.method.restartInput((IInputContext) args.arg2, missingMethods,
   2907                             (EditorInfo) args.arg3);
   2908                 } catch (RemoteException e) {
   2909                 }
   2910                 args.recycle();
   2911                 return true;
   2912             }
   2913 
   2914             // ---------------------------------------------------------
   2915 
   2916             case MSG_UNBIND_CLIENT:
   2917                 try {
   2918                     ((IInputMethodClient)msg.obj).onUnbindMethod(msg.arg1, msg.arg2);
   2919                 } catch (RemoteException e) {
   2920                     // There is nothing interesting about the last client dying.
   2921                 }
   2922                 return true;
   2923             case MSG_BIND_CLIENT: {
   2924                 args = (SomeArgs)msg.obj;
   2925                 IInputMethodClient client = (IInputMethodClient)args.arg1;
   2926                 InputBindResult res = (InputBindResult)args.arg2;
   2927                 try {
   2928                     client.onBindMethod(res);
   2929                 } catch (RemoteException e) {
   2930                     Slog.w(TAG, "Client died receiving input method " + args.arg2);
   2931                 } finally {
   2932                     // Dispose the channel if the input method is not local to this process
   2933                     // because the remote proxy will get its own copy when unparceled.
   2934                     if (res.channel != null && Binder.isProxy(client)) {
   2935                         res.channel.dispose();
   2936                     }
   2937                 }
   2938                 args.recycle();
   2939                 return true;
   2940             }
   2941             case MSG_SET_ACTIVE:
   2942                 try {
   2943                     ((ClientState)msg.obj).client.setActive(msg.arg1 != 0);
   2944                 } catch (RemoteException e) {
   2945                     Slog.w(TAG, "Got RemoteException sending setActive(false) notification to pid "
   2946                             + ((ClientState)msg.obj).pid + " uid "
   2947                             + ((ClientState)msg.obj).uid);
   2948                 }
   2949                 return true;
   2950             case MSG_SET_INTERACTIVE:
   2951                 handleSetInteractive(msg.arg1 != 0);
   2952                 return true;
   2953             case MSG_SWITCH_IME:
   2954                 handleSwitchInputMethod(msg.arg1 != 0);
   2955                 return true;
   2956             case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: {
   2957                 final int sequenceNumber = msg.arg1;
   2958                 final ClientState clientState = (ClientState)msg.obj;
   2959                 try {
   2960                     clientState.client.setUserActionNotificationSequenceNumber(sequenceNumber);
   2961                 } catch (RemoteException e) {
   2962                     Slog.w(TAG, "Got RemoteException sending "
   2963                             + "setUserActionNotificationSequenceNumber("
   2964                             + sequenceNumber + ") notification to pid "
   2965                             + clientState.pid + " uid "
   2966                             + clientState.uid);
   2967                 }
   2968                 return true;
   2969             }
   2970 
   2971             // --------------------------------------------------------------
   2972             case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
   2973                 mHardKeyboardListener.handleHardKeyboardStatusChange(msg.arg1 == 1);
   2974                 return true;
   2975             case MSG_SYSTEM_UNLOCK_USER:
   2976                 final int userId = msg.arg1;
   2977                 onUnlockUser(userId);
   2978                 return true;
   2979         }
   2980         return false;
   2981     }
   2982 
   2983     private void handleSetInteractive(final boolean interactive) {
   2984         synchronized (mMethodMap) {
   2985             mIsInteractive = interactive;
   2986             updateSystemUiLocked(mCurToken, interactive ? mImeWindowVis : 0, mBackDisposition);
   2987 
   2988             // Inform the current client of the change in active status
   2989             if (mCurClient != null && mCurClient.client != null) {
   2990                 executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
   2991                         MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, mCurClient));
   2992             }
   2993         }
   2994     }
   2995 
   2996     private void handleSwitchInputMethod(final boolean forwardDirection) {
   2997         synchronized (mMethodMap) {
   2998             final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
   2999                     false, mMethodMap.get(mCurMethodId), mCurrentSubtype, forwardDirection);
   3000             if (nextSubtype == null) {
   3001                 return;
   3002             }
   3003             setInputMethodLocked(nextSubtype.mImi.getId(), nextSubtype.mSubtypeId);
   3004             final InputMethodInfo newInputMethodInfo = mMethodMap.get(mCurMethodId);
   3005             if (newInputMethodInfo == null) {
   3006                 return;
   3007             }
   3008             final CharSequence toastText = InputMethodUtils.getImeAndSubtypeDisplayName(mContext,
   3009                     newInputMethodInfo, mCurrentSubtype);
   3010             if (!TextUtils.isEmpty(toastText)) {
   3011                 if (mSubtypeSwitchedByShortCutToast == null) {
   3012                     mSubtypeSwitchedByShortCutToast = Toast.makeText(mContext, toastText,
   3013                             Toast.LENGTH_SHORT);
   3014                 } else {
   3015                     mSubtypeSwitchedByShortCutToast.setText(toastText);
   3016                 }
   3017                 mSubtypeSwitchedByShortCutToast.show();
   3018             }
   3019         }
   3020     }
   3021 
   3022     private boolean chooseNewDefaultIMELocked() {
   3023         final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME(
   3024                 mSettings.getEnabledInputMethodListLocked());
   3025         if (imi != null) {
   3026             if (DEBUG) {
   3027                 Slog.d(TAG, "New default IME was selected: " + imi.getId());
   3028             }
   3029             resetSelectedInputMethodAndSubtypeLocked(imi.getId());
   3030             return true;
   3031         }
   3032 
   3033         return false;
   3034     }
   3035 
   3036     void buildInputMethodListLocked(boolean resetDefaultEnabledIme) {
   3037         if (DEBUG) {
   3038             Slog.d(TAG, "--- re-buildInputMethodList reset = " + resetDefaultEnabledIme
   3039                     + " \n ------ caller=" + Debug.getCallers(10));
   3040         }
   3041         mMethodList.clear();
   3042         mMethodMap.clear();
   3043 
   3044         // Use for queryIntentServicesAsUser
   3045         final PackageManager pm = mContext.getPackageManager();
   3046 
   3047         // Note: We do not specify PackageManager.MATCH_ENCRYPTION_* flags here because the default
   3048         // behavior of PackageManager is exactly what we want.  It by default picks up appropriate
   3049         // services depending on the unlock state for the specified user.
   3050         final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
   3051                 new Intent(InputMethod.SERVICE_INTERFACE),
   3052                 PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
   3053                 mSettings.getCurrentUserId());
   3054 
   3055         final HashMap<String, List<InputMethodSubtype>> additionalSubtypes =
   3056                 mFileManager.getAllAdditionalInputMethodSubtypes();
   3057         for (int i = 0; i < services.size(); ++i) {
   3058             ResolveInfo ri = services.get(i);
   3059             ServiceInfo si = ri.serviceInfo;
   3060             ComponentName compName = new ComponentName(si.packageName, si.name);
   3061             if (!android.Manifest.permission.BIND_INPUT_METHOD.equals(
   3062                     si.permission)) {
   3063                 Slog.w(TAG, "Skipping input method " + compName
   3064                         + ": it does not require the permission "
   3065                         + android.Manifest.permission.BIND_INPUT_METHOD);
   3066                 continue;
   3067             }
   3068 
   3069             if (DEBUG) Slog.d(TAG, "Checking " + compName);
   3070 
   3071             try {
   3072                 InputMethodInfo p = new InputMethodInfo(mContext, ri, additionalSubtypes);
   3073                 mMethodList.add(p);
   3074                 final String id = p.getId();
   3075                 mMethodMap.put(id, p);
   3076 
   3077                 if (DEBUG) {
   3078                     Slog.d(TAG, "Found an input method " + p);
   3079                 }
   3080             } catch (Exception e) {
   3081                 Slog.wtf(TAG, "Unable to load input method " + compName, e);
   3082             }
   3083         }
   3084 
   3085         // TODO: The following code should find better place to live.
   3086         if (!resetDefaultEnabledIme) {
   3087             boolean enabledImeFound = false;
   3088             final List<InputMethodInfo> enabledImes = mSettings.getEnabledInputMethodListLocked();
   3089             final int N = enabledImes.size();
   3090             for (int i = 0; i < N; ++i) {
   3091                 final InputMethodInfo imi = enabledImes.get(i);
   3092                 if (mMethodList.contains(imi)) {
   3093                     enabledImeFound = true;
   3094                     break;
   3095                 }
   3096             }
   3097             if (!enabledImeFound) {
   3098                 Slog.i(TAG, "All the enabled IMEs are gone. Reset default enabled IMEs.");
   3099                 resetDefaultEnabledIme = true;
   3100                 resetSelectedInputMethodAndSubtypeLocked("");
   3101             }
   3102         }
   3103 
   3104         if (resetDefaultEnabledIme) {
   3105             final ArrayList<InputMethodInfo> defaultEnabledIme =
   3106                     InputMethodUtils.getDefaultEnabledImes(mContext, mSystemReady, mMethodList);
   3107             final int N = defaultEnabledIme.size();
   3108             for (int i = 0; i < N; ++i) {
   3109                 final InputMethodInfo imi =  defaultEnabledIme.get(i);
   3110                 if (DEBUG) {
   3111                     Slog.d(TAG, "--- enable ime = " + imi);
   3112                 }
   3113                 setInputMethodEnabledLocked(imi.getId(), true);
   3114             }
   3115         }
   3116 
   3117         final String defaultImiId = mSettings.getSelectedInputMethod();
   3118         if (!TextUtils.isEmpty(defaultImiId)) {
   3119             if (!mMethodMap.containsKey(defaultImiId)) {
   3120                 Slog.w(TAG, "Default IME is uninstalled. Choose new default IME.");
   3121                 if (chooseNewDefaultIMELocked()) {
   3122                     updateInputMethodsFromSettingsLocked(true);
   3123                 }
   3124             } else {
   3125                 // Double check that the default IME is certainly enabled.
   3126                 setInputMethodEnabledLocked(defaultImiId, true);
   3127             }
   3128         }
   3129         // Here is not the perfect place to reset the switching controller. Ideally
   3130         // mSwitchingController and mSettings should be able to share the same state.
   3131         // TODO: Make sure that mSwitchingController and mSettings are sharing the
   3132         // the same enabled IMEs list.
   3133         mSwitchingController.resetCircularListLocked(mContext);
   3134     }
   3135 
   3136     // ----------------------------------------------------------------------
   3137 
   3138     private void showInputMethodAndSubtypeEnabler(String inputMethodId) {
   3139         Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS);
   3140         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   3141                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
   3142                 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
   3143         if (!TextUtils.isEmpty(inputMethodId)) {
   3144             intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, inputMethodId);
   3145         }
   3146         final int userId;
   3147         synchronized (mMethodMap) {
   3148             userId = mSettings.getCurrentUserId();
   3149         }
   3150         mContext.startActivityAsUser(intent, null, UserHandle.of(userId));
   3151     }
   3152 
   3153     private void showConfigureInputMethods() {
   3154         Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS);
   3155         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   3156                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
   3157                 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
   3158         mContext.startActivityAsUser(intent, null, UserHandle.CURRENT);
   3159     }
   3160 
   3161     private boolean isScreenLocked() {
   3162         return mKeyguardManager != null
   3163                 && mKeyguardManager.isKeyguardLocked() && mKeyguardManager.isKeyguardSecure();
   3164     }
   3165 
   3166     private void showInputMethodMenu(boolean showAuxSubtypes) {
   3167         if (DEBUG) Slog.v(TAG, "Show switching menu. showAuxSubtypes=" + showAuxSubtypes);
   3168 
   3169         final Context context = mContext;
   3170         final boolean isScreenLocked = isScreenLocked();
   3171 
   3172         final String lastInputMethodId = mSettings.getSelectedInputMethod();
   3173         int lastInputMethodSubtypeId = mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
   3174         if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
   3175 
   3176         synchronized (mMethodMap) {
   3177             final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
   3178                     mSettings.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(
   3179                             mContext);
   3180             if (immis == null || immis.size() == 0) {
   3181                 return;
   3182             }
   3183 
   3184             hideInputMethodMenuLocked();
   3185 
   3186             final List<ImeSubtypeListItem> imList =
   3187                     mSwitchingController.getSortedInputMethodAndSubtypeListLocked(
   3188                             showAuxSubtypes, isScreenLocked);
   3189 
   3190             if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
   3191                 final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtypeLocked();
   3192                 if (currentSubtype != null) {
   3193                     final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
   3194                     lastInputMethodSubtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
   3195                             currentImi, currentSubtype.hashCode());
   3196                 }
   3197             }
   3198 
   3199             final int N = imList.size();
   3200             mIms = new InputMethodInfo[N];
   3201             mSubtypeIds = new int[N];
   3202             int checkedItem = 0;
   3203             for (int i = 0; i < N; ++i) {
   3204                 final ImeSubtypeListItem item = imList.get(i);
   3205                 mIms[i] = item.mImi;
   3206                 mSubtypeIds[i] = item.mSubtypeId;
   3207                 if (mIms[i].getId().equals(lastInputMethodId)) {
   3208                     int subtypeId = mSubtypeIds[i];
   3209                     if ((subtypeId == NOT_A_SUBTYPE_ID)
   3210                             || (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID && subtypeId == 0)
   3211                             || (subtypeId == lastInputMethodSubtypeId)) {
   3212                         checkedItem = i;
   3213                     }
   3214                 }
   3215             }
   3216 
   3217             final Context settingsContext = new ContextThemeWrapper(context,
   3218                     com.android.internal.R.style.Theme_DeviceDefault_Settings);
   3219 
   3220             mDialogBuilder = new AlertDialog.Builder(settingsContext);
   3221             mDialogBuilder.setOnCancelListener(new OnCancelListener() {
   3222                 @Override
   3223                 public void onCancel(DialogInterface dialog) {
   3224                     hideInputMethodMenu();
   3225                 }
   3226             });
   3227 
   3228             final Context dialogContext = mDialogBuilder.getContext();
   3229             final TypedArray a = dialogContext.obtainStyledAttributes(null,
   3230                     com.android.internal.R.styleable.DialogPreference,
   3231                     com.android.internal.R.attr.alertDialogStyle, 0);
   3232             final Drawable dialogIcon = a.getDrawable(
   3233                     com.android.internal.R.styleable.DialogPreference_dialogIcon);
   3234             a.recycle();
   3235 
   3236             mDialogBuilder.setIcon(dialogIcon);
   3237 
   3238             final LayoutInflater inflater = dialogContext.getSystemService(LayoutInflater.class);
   3239             final View tv = inflater.inflate(
   3240                     com.android.internal.R.layout.input_method_switch_dialog_title, null);
   3241             mDialogBuilder.setCustomTitle(tv);
   3242 
   3243             // Setup layout for a toggle switch of the hardware keyboard
   3244             mSwitchingDialogTitleView = tv;
   3245             mSwitchingDialogTitleView
   3246                     .findViewById(com.android.internal.R.id.hard_keyboard_section)
   3247                     .setVisibility(mWindowManagerInternal.isHardKeyboardAvailable()
   3248                             ? View.VISIBLE : View.GONE);
   3249             final Switch hardKeySwitch = (Switch) mSwitchingDialogTitleView.findViewById(
   3250                     com.android.internal.R.id.hard_keyboard_switch);
   3251             hardKeySwitch.setChecked(mShowImeWithHardKeyboard);
   3252             hardKeySwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
   3253                 @Override
   3254                 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
   3255                     mSettings.setShowImeWithHardKeyboard(isChecked);
   3256                     // Ensure that the input method dialog is dismissed when changing
   3257                     // the hardware keyboard state.
   3258                     hideInputMethodMenu();
   3259                 }
   3260             });
   3261 
   3262             final ImeSubtypeListAdapter adapter = new ImeSubtypeListAdapter(dialogContext,
   3263                     com.android.internal.R.layout.input_method_switch_item, imList, checkedItem);
   3264             final OnClickListener choiceListener = new OnClickListener() {
   3265                 @Override
   3266                 public void onClick(final DialogInterface dialog, final int which) {
   3267                     synchronized (mMethodMap) {
   3268                         if (mIms == null || mIms.length <= which || mSubtypeIds == null
   3269                                 || mSubtypeIds.length <= which) {
   3270                             return;
   3271                         }
   3272                         final InputMethodInfo im = mIms[which];
   3273                         int subtypeId = mSubtypeIds[which];
   3274                         adapter.mCheckedItem = which;
   3275                         adapter.notifyDataSetChanged();
   3276                         hideInputMethodMenu();
   3277                         if (im != null) {
   3278                             if (subtypeId < 0 || subtypeId >= im.getSubtypeCount()) {
   3279                                 subtypeId = NOT_A_SUBTYPE_ID;
   3280                             }
   3281                             setInputMethodLocked(im.getId(), subtypeId);
   3282                         }
   3283                     }
   3284                 }
   3285             };
   3286             mDialogBuilder.setSingleChoiceItems(adapter, checkedItem, choiceListener);
   3287 
   3288             mSwitchingDialog = mDialogBuilder.create();
   3289             mSwitchingDialog.setCanceledOnTouchOutside(true);
   3290             mSwitchingDialog.getWindow().setType(
   3291                     WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
   3292             mSwitchingDialog.getWindow().getAttributes().privateFlags |=
   3293                     WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
   3294             mSwitchingDialog.getWindow().getAttributes().setTitle("Select input method");
   3295             updateSystemUi(mCurToken, mImeWindowVis, mBackDisposition);
   3296             mSwitchingDialog.show();
   3297         }
   3298     }
   3299 
   3300     private static class ImeSubtypeListAdapter extends ArrayAdapter<ImeSubtypeListItem> {
   3301         private final LayoutInflater mInflater;
   3302         private final int mTextViewResourceId;
   3303         private final List<ImeSubtypeListItem> mItemsList;
   3304         public int mCheckedItem;
   3305         public ImeSubtypeListAdapter(Context context, int textViewResourceId,
   3306                 List<ImeSubtypeListItem> itemsList, int checkedItem) {
   3307             super(context, textViewResourceId, itemsList);
   3308 
   3309             mTextViewResourceId = textViewResourceId;
   3310             mItemsList = itemsList;
   3311             mCheckedItem = checkedItem;
   3312             mInflater = context.getSystemService(LayoutInflater.class);
   3313         }
   3314 
   3315         @Override
   3316         public View getView(int position, View convertView, ViewGroup parent) {
   3317             final View view = convertView != null ? convertView
   3318                     : mInflater.inflate(mTextViewResourceId, null);
   3319             if (position < 0 || position >= mItemsList.size()) return view;
   3320             final ImeSubtypeListItem item = mItemsList.get(position);
   3321             final CharSequence imeName = item.mImeName;
   3322             final CharSequence subtypeName = item.mSubtypeName;
   3323             final TextView firstTextView = (TextView)view.findViewById(android.R.id.text1);
   3324             final TextView secondTextView = (TextView)view.findViewById(android.R.id.text2);
   3325             if (TextUtils.isEmpty(subtypeName)) {
   3326                 firstTextView.setText(imeName);
   3327                 secondTextView.setVisibility(View.GONE);
   3328             } else {
   3329                 firstTextView.setText(subtypeName);
   3330                 secondTextView.setText(imeName);
   3331                 secondTextView.setVisibility(View.VISIBLE);
   3332             }
   3333             final RadioButton radioButton =
   3334                     (RadioButton)view.findViewById(com.android.internal.R.id.radio);
   3335             radioButton.setChecked(position == mCheckedItem);
   3336             return view;
   3337         }
   3338     }
   3339 
   3340     void hideInputMethodMenu() {
   3341         synchronized (mMethodMap) {
   3342             hideInputMethodMenuLocked();
   3343         }
   3344     }
   3345 
   3346     void hideInputMethodMenuLocked() {
   3347         if (DEBUG) Slog.v(TAG, "Hide switching menu");
   3348 
   3349         if (mSwitchingDialog != null) {
   3350             mSwitchingDialog.dismiss();
   3351             mSwitchingDialog = null;
   3352         }
   3353 
   3354         updateSystemUiLocked(mCurToken, mImeWindowVis, mBackDisposition);
   3355         mDialogBuilder = null;
   3356         mIms = null;
   3357     }
   3358 
   3359     // ----------------------------------------------------------------------
   3360 
   3361     @Override
   3362     public boolean setInputMethodEnabled(String id, boolean enabled) {
   3363         // TODO: Make this work even for non-current users?
   3364         if (!calledFromValidUser()) {
   3365             return false;
   3366         }
   3367         synchronized (mMethodMap) {
   3368             if (mContext.checkCallingOrSelfPermission(
   3369                     android.Manifest.permission.WRITE_SECURE_SETTINGS)
   3370                     != PackageManager.PERMISSION_GRANTED) {
   3371                 throw new SecurityException(
   3372                         "Requires permission "
   3373                         + android.Manifest.permission.WRITE_SECURE_SETTINGS);
   3374             }
   3375 
   3376             long ident = Binder.clearCallingIdentity();
   3377             try {
   3378                 return setInputMethodEnabledLocked(id, enabled);
   3379             } finally {
   3380                 Binder.restoreCallingIdentity(ident);
   3381             }
   3382         }
   3383     }
   3384 
   3385     boolean setInputMethodEnabledLocked(String id, boolean enabled) {
   3386         // Make sure this is a valid input method.
   3387         InputMethodInfo imm = mMethodMap.get(id);
   3388         if (imm == null) {
   3389             throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
   3390         }
   3391 
   3392         List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings
   3393                 .getEnabledInputMethodsAndSubtypeListLocked();
   3394 
   3395         if (enabled) {
   3396             for (Pair<String, ArrayList<String>> pair: enabledInputMethodsList) {
   3397                 if (pair.first.equals(id)) {
   3398                     // We are enabling this input method, but it is already enabled.
   3399                     // Nothing to do. The previous state was enabled.
   3400                     return true;
   3401                 }
   3402             }
   3403             mSettings.appendAndPutEnabledInputMethodLocked(id, false);
   3404             // Previous state was disabled.
   3405             return false;
   3406         } else {
   3407             StringBuilder builder = new StringBuilder();
   3408             if (mSettings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
   3409                     builder, enabledInputMethodsList, id)) {
   3410                 // Disabled input method is currently selected, switch to another one.
   3411                 final String selId = mSettings.getSelectedInputMethod();
   3412                 if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
   3413                     Slog.i(TAG, "Can't find new IME, unsetting the current input method.");
   3414                     resetSelectedInputMethodAndSubtypeLocked("");
   3415                 }
   3416                 // Previous state was enabled.
   3417                 return true;
   3418             } else {
   3419                 // We are disabling the input method but it is already disabled.
   3420                 // Nothing to do.  The previous state was disabled.
   3421                 return false;
   3422             }
   3423         }
   3424     }
   3425 
   3426     private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
   3427             boolean setSubtypeOnly) {
   3428         // Update the history of InputMethod and Subtype
   3429         mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype);
   3430 
   3431         mCurUserActionNotificationSequenceNumber =
   3432                 Math.max(mCurUserActionNotificationSequenceNumber + 1, 1);
   3433         if (DEBUG) {
   3434             Slog.d(TAG, "Bump mCurUserActionNotificationSequenceNumber:"
   3435                     + mCurUserActionNotificationSequenceNumber);
   3436         }
   3437 
   3438         if (mCurClient != null && mCurClient.client != null) {
   3439             executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
   3440                     MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER,
   3441                     mCurUserActionNotificationSequenceNumber, mCurClient));
   3442         }
   3443 
   3444         // Set Subtype here
   3445         if (imi == null || subtypeId < 0) {
   3446             mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
   3447             mCurrentSubtype = null;
   3448         } else {
   3449             if (subtypeId < imi.getSubtypeCount()) {
   3450                 InputMethodSubtype subtype = imi.getSubtypeAt(subtypeId);
   3451                 mSettings.putSelectedSubtype(subtype.hashCode());
   3452                 mCurrentSubtype = subtype;
   3453             } else {
   3454                 mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
   3455                 // If the subtype is not specified, choose the most applicable one
   3456                 mCurrentSubtype = getCurrentInputMethodSubtypeLocked();
   3457             }
   3458         }
   3459 
   3460         if (!setSubtypeOnly) {
   3461             // Set InputMethod here
   3462             mSettings.putSelectedInputMethod(imi != null ? imi.getId() : "");
   3463         }
   3464     }
   3465 
   3466     private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) {
   3467         InputMethodInfo imi = mMethodMap.get(newDefaultIme);
   3468         int lastSubtypeId = NOT_A_SUBTYPE_ID;
   3469         // newDefaultIme is empty when there is no candidate for the selected IME.
   3470         if (imi != null && !TextUtils.isEmpty(newDefaultIme)) {
   3471             String subtypeHashCode = mSettings.getLastSubtypeForInputMethodLocked(newDefaultIme);
   3472             if (subtypeHashCode != null) {
   3473                 try {
   3474                     lastSubtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
   3475                             imi, Integer.parseInt(subtypeHashCode));
   3476                 } catch (NumberFormatException e) {
   3477                     Slog.w(TAG, "HashCode for subtype looks broken: " + subtypeHashCode, e);
   3478                 }
   3479             }
   3480         }
   3481         setSelectedInputMethodAndSubtypeLocked(imi, lastSubtypeId, false);
   3482     }
   3483 
   3484     // If there are no selected shortcuts, tries finding the most applicable ones.
   3485     private Pair<InputMethodInfo, InputMethodSubtype>
   3486             findLastResortApplicableShortcutInputMethodAndSubtypeLocked(String mode) {
   3487         List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
   3488         InputMethodInfo mostApplicableIMI = null;
   3489         InputMethodSubtype mostApplicableSubtype = null;
   3490         boolean foundInSystemIME = false;
   3491 
   3492         // Search applicable subtype for each InputMethodInfo
   3493         for (InputMethodInfo imi: imis) {
   3494             final String imiId = imi.getId();
   3495             if (foundInSystemIME && !imiId.equals(mCurMethodId)) {
   3496                 continue;
   3497             }
   3498             InputMethodSubtype subtype = null;
   3499             final List<InputMethodSubtype> enabledSubtypes =
   3500                     mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true);
   3501             // 1. Search by the current subtype's locale from enabledSubtypes.
   3502             if (mCurrentSubtype != null) {
   3503                 subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
   3504                         mRes, enabledSubtypes, mode, mCurrentSubtype.getLocale(), false);
   3505             }
   3506             // 2. Search by the system locale from enabledSubtypes.
   3507             // 3. Search the first enabled subtype matched with mode from enabledSubtypes.
   3508             if (subtype == null) {
   3509                 subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
   3510                         mRes, enabledSubtypes, mode, null, true);
   3511             }
   3512             final ArrayList<InputMethodSubtype> overridingImplicitlyEnabledSubtypes =
   3513                     InputMethodUtils.getOverridingImplicitlyEnabledSubtypes(imi, mode);
   3514             final ArrayList<InputMethodSubtype> subtypesForSearch =
   3515                     overridingImplicitlyEnabledSubtypes.isEmpty()
   3516                             ? InputMethodUtils.getSubtypes(imi)
   3517                             : overridingImplicitlyEnabledSubtypes;
   3518             // 4. Search by the current subtype's locale from all subtypes.
   3519             if (subtype == null && mCurrentSubtype != null) {
   3520                 subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
   3521                         mRes, subtypesForSearch, mode, mCurrentSubtype.getLocale(), false);
   3522             }
   3523             // 5. Search by the system locale from all subtypes.
   3524             // 6. Search the first enabled subtype matched with mode from all subtypes.
   3525             if (subtype == null) {
   3526                 subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
   3527                         mRes, subtypesForSearch, mode, null, true);
   3528             }
   3529             if (subtype != null) {
   3530                 if (imiId.equals(mCurMethodId)) {
   3531                     // The current input method is the most applicable IME.
   3532                     mostApplicableIMI = imi;
   3533                     mostApplicableSubtype = subtype;
   3534                     break;
   3535                 } else if (!foundInSystemIME) {
   3536                     // The system input method is 2nd applicable IME.
   3537                     mostApplicableIMI = imi;
   3538                     mostApplicableSubtype = subtype;
   3539                     if ((imi.getServiceInfo().applicationInfo.flags
   3540                             & ApplicationInfo.FLAG_SYSTEM) != 0) {
   3541                         foundInSystemIME = true;
   3542                     }
   3543                 }
   3544             }
   3545         }
   3546         if (DEBUG) {
   3547             if (mostApplicableIMI != null) {
   3548                 Slog.w(TAG, "Most applicable shortcut input method was:"
   3549                         + mostApplicableIMI.getId());
   3550                 if (mostApplicableSubtype != null) {
   3551                     Slog.w(TAG, "Most applicable shortcut input method subtype was:"
   3552                             + "," + mostApplicableSubtype.getMode() + ","
   3553                             + mostApplicableSubtype.getLocale());
   3554                 }
   3555             }
   3556         }
   3557         if (mostApplicableIMI != null) {
   3558             return new Pair<> (mostApplicableIMI, mostApplicableSubtype);
   3559         } else {
   3560             return null;
   3561         }
   3562     }
   3563 
   3564     /**
   3565      * @return Return the current subtype of this input method.
   3566      */
   3567     @Override
   3568     public InputMethodSubtype getCurrentInputMethodSubtype() {
   3569         // TODO: Make this work even for non-current users?
   3570         if (!calledFromValidUser()) {
   3571             return null;
   3572         }
   3573         synchronized (mMethodMap) {
   3574             return getCurrentInputMethodSubtypeLocked();
   3575         }
   3576     }
   3577 
   3578     private InputMethodSubtype getCurrentInputMethodSubtypeLocked() {
   3579         if (mCurMethodId == null) {
   3580             return null;
   3581         }
   3582         final boolean subtypeIsSelected = mSettings.isSubtypeSelected();
   3583         final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
   3584         if (imi == null || imi.getSubtypeCount() == 0) {
   3585             return null;
   3586         }
   3587         if (!subtypeIsSelected || mCurrentSubtype == null
   3588                 || !InputMethodUtils.isValidSubtypeId(imi, mCurrentSubtype.hashCode())) {
   3589             int subtypeId = mSettings.getSelectedInputMethodSubtypeId(mCurMethodId);
   3590             if (subtypeId == NOT_A_SUBTYPE_ID) {
   3591                 // If there are no selected subtypes, the framework will try to find
   3592                 // the most applicable subtype from explicitly or implicitly enabled
   3593                 // subtypes.
   3594                 List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes =
   3595                         mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true);
   3596                 // If there is only one explicitly or implicitly enabled subtype,
   3597                 // just returns it.
   3598                 if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) {
   3599                     mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0);
   3600                 } else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) {
   3601                     mCurrentSubtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
   3602                             mRes, explicitlyOrImplicitlyEnabledSubtypes,
   3603                             InputMethodUtils.SUBTYPE_MODE_KEYBOARD, null, true);
   3604                     if (mCurrentSubtype == null) {
   3605                         mCurrentSubtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
   3606                                 mRes, explicitlyOrImplicitlyEnabledSubtypes, null, null,
   3607                                 true);
   3608                     }
   3609                 }
   3610             } else {
   3611                 mCurrentSubtype = InputMethodUtils.getSubtypes(imi).get(subtypeId);
   3612             }
   3613         }
   3614         return mCurrentSubtype;
   3615     }
   3616 
   3617     // TODO: We should change the return type from List to List<Parcelable>
   3618     @SuppressWarnings("rawtypes")
   3619     @Override
   3620     public List getShortcutInputMethodsAndSubtypes() {
   3621         synchronized (mMethodMap) {
   3622             ArrayList<Object> ret = new ArrayList<>();
   3623             if (mShortcutInputMethodsAndSubtypes.size() == 0) {
   3624                 // If there are no selected shortcut subtypes, the framework will try to find
   3625                 // the most applicable subtype from all subtypes whose mode is
   3626                 // SUBTYPE_MODE_VOICE. This is an exceptional case, so we will hardcode the mode.
   3627                 Pair<InputMethodInfo, InputMethodSubtype> info =
   3628                     findLastResortApplicableShortcutInputMethodAndSubtypeLocked(
   3629                             InputMethodUtils.SUBTYPE_MODE_VOICE);
   3630                 if (info != null) {
   3631                     ret.add(info.first);
   3632                     ret.add(info.second);
   3633                 }
   3634                 return ret;
   3635             }
   3636             for (InputMethodInfo imi: mShortcutInputMethodsAndSubtypes.keySet()) {
   3637                 ret.add(imi);
   3638                 for (InputMethodSubtype subtype: mShortcutInputMethodsAndSubtypes.get(imi)) {
   3639                     ret.add(subtype);
   3640                 }
   3641             }
   3642             return ret;
   3643         }
   3644     }
   3645 
   3646     @Override
   3647     public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
   3648         // TODO: Make this work even for non-current users?
   3649         if (!calledFromValidUser()) {
   3650             return false;
   3651         }
   3652         synchronized (mMethodMap) {
   3653             if (subtype != null && mCurMethodId != null) {
   3654                 InputMethodInfo imi = mMethodMap.get(mCurMethodId);
   3655                 int subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode());
   3656                 if (subtypeId != NOT_A_SUBTYPE_ID) {
   3657                     setInputMethodLocked(mCurMethodId, subtypeId);
   3658                     return true;
   3659                 }
   3660             }
   3661             return false;
   3662         }
   3663     }
   3664 
   3665     // TODO: Cache the state for each user and reset when the cached user is removed.
   3666     private static class InputMethodFileManager {
   3667         private static final String SYSTEM_PATH = "system";
   3668         private static final String INPUT_METHOD_PATH = "inputmethod";
   3669         private static final String ADDITIONAL_SUBTYPES_FILE_NAME = "subtypes.xml";
   3670         private static final String NODE_SUBTYPES = "subtypes";
   3671         private static final String NODE_SUBTYPE = "subtype";
   3672         private static final String NODE_IMI = "imi";
   3673         private static final String ATTR_ID = "id";
   3674         private static final String ATTR_LABEL = "label";
   3675         private static final String ATTR_ICON = "icon";
   3676         private static final String ATTR_IME_SUBTYPE_ID = "subtypeId";
   3677         private static final String ATTR_IME_SUBTYPE_LOCALE = "imeSubtypeLocale";
   3678         private static final String ATTR_IME_SUBTYPE_LANGUAGE_TAG = "languageTag";
   3679         private static final String ATTR_IME_SUBTYPE_MODE = "imeSubtypeMode";
   3680         private static final String ATTR_IME_SUBTYPE_EXTRA_VALUE = "imeSubtypeExtraValue";
   3681         private static final String ATTR_IS_AUXILIARY = "isAuxiliary";
   3682         private static final String ATTR_IS_ASCII_CAPABLE = "isAsciiCapable";
   3683         private final AtomicFile mAdditionalInputMethodSubtypeFile;
   3684         private final HashMap<String, InputMethodInfo> mMethodMap;
   3685         private final HashMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
   3686                 new HashMap<>();
   3687         public InputMethodFileManager(HashMap<String, InputMethodInfo> methodMap, int userId) {
   3688             if (methodMap == null) {
   3689                 throw new NullPointerException("methodMap is null");
   3690             }
   3691             mMethodMap = methodMap;
   3692             final File systemDir = userId == UserHandle.USER_SYSTEM
   3693                     ? new File(Environment.getDataDirectory(), SYSTEM_PATH)
   3694                     : Environment.getUserSystemDirectory(userId);
   3695             final File inputMethodDir = new File(systemDir, INPUT_METHOD_PATH);
   3696             if (!inputMethodDir.exists() && !inputMethodDir.mkdirs()) {
   3697                 Slog.w(TAG, "Couldn't create dir.: " + inputMethodDir.getAbsolutePath());
   3698             }
   3699             final File subtypeFile = new File(inputMethodDir, ADDITIONAL_SUBTYPES_FILE_NAME);
   3700             mAdditionalInputMethodSubtypeFile = new AtomicFile(subtypeFile);
   3701             if (!subtypeFile.exists()) {
   3702                 // If "subtypes.xml" doesn't exist, create a blank file.
   3703                 writeAdditionalInputMethodSubtypes(
   3704                         mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, methodMap);
   3705             } else {
   3706                 readAdditionalInputMethodSubtypes(
   3707                         mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile);
   3708             }
   3709         }
   3710 
   3711         private void deleteAllInputMethodSubtypes(String imiId) {
   3712             synchronized (mMethodMap) {
   3713                 mAdditionalSubtypesMap.remove(imiId);
   3714                 writeAdditionalInputMethodSubtypes(
   3715                         mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap);
   3716             }
   3717         }
   3718 
   3719         public void addInputMethodSubtypes(
   3720                 InputMethodInfo imi, InputMethodSubtype[] additionalSubtypes) {
   3721             synchronized (mMethodMap) {
   3722                 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
   3723                 final int N = additionalSubtypes.length;
   3724                 for (int i = 0; i < N; ++i) {
   3725                     final InputMethodSubtype subtype = additionalSubtypes[i];
   3726                     if (!subtypes.contains(subtype)) {
   3727                         subtypes.add(subtype);
   3728                     } else {
   3729                         Slog.w(TAG, "Duplicated subtype definition found: "
   3730                                 + subtype.getLocale() + ", " + subtype.getMode());
   3731                     }
   3732                 }
   3733                 mAdditionalSubtypesMap.put(imi.getId(), subtypes);
   3734                 writeAdditionalInputMethodSubtypes(
   3735                         mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap);
   3736             }
   3737         }
   3738 
   3739         public HashMap<String, List<InputMethodSubtype>> getAllAdditionalInputMethodSubtypes() {
   3740             synchronized (mMethodMap) {
   3741                 return mAdditionalSubtypesMap;
   3742             }
   3743         }
   3744 
   3745         private static void writeAdditionalInputMethodSubtypes(
   3746                 HashMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile,
   3747                 HashMap<String, InputMethodInfo> methodMap) {
   3748             // Safety net for the case that this function is called before methodMap is set.
   3749             final boolean isSetMethodMap = methodMap != null && methodMap.size() > 0;
   3750             FileOutputStream fos = null;
   3751             try {
   3752                 fos = subtypesFile.startWrite();
   3753                 final XmlSerializer out = new FastXmlSerializer();
   3754                 out.setOutput(fos, StandardCharsets.UTF_8.name());
   3755                 out.startDocument(null, true);
   3756                 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
   3757                 out.startTag(null, NODE_SUBTYPES);
   3758                 for (String imiId : allSubtypes.keySet()) {
   3759                     if (isSetMethodMap && !methodMap.containsKey(imiId)) {
   3760                         Slog.w(TAG, "IME uninstalled or not valid.: " + imiId);
   3761                         continue;
   3762                     }
   3763                     out.startTag(null, NODE_IMI);
   3764                     out.attribute(null, ATTR_ID, imiId);
   3765                     final List<InputMethodSubtype> subtypesList = allSubtypes.get(imiId);
   3766                     final int N = subtypesList.size();
   3767                     for (int i = 0; i < N; ++i) {
   3768                         final InputMethodSubtype subtype = subtypesList.get(i);
   3769                         out.startTag(null, NODE_SUBTYPE);
   3770                         if (subtype.hasSubtypeId()) {
   3771                             out.attribute(null, ATTR_IME_SUBTYPE_ID,
   3772                                     String.valueOf(subtype.getSubtypeId()));
   3773                         }
   3774                         out.attribute(null, ATTR_ICON, String.valueOf(subtype.getIconResId()));
   3775                         out.attribute(null, ATTR_LABEL, String.valueOf(subtype.getNameResId()));
   3776                         out.attribute(null, ATTR_IME_SUBTYPE_LOCALE, subtype.getLocale());
   3777                         out.attribute(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG,
   3778                                 subtype.getLanguageTag());
   3779                         out.attribute(null, ATTR_IME_SUBTYPE_MODE, subtype.getMode());
   3780                         out.attribute(null, ATTR_IME_SUBTYPE_EXTRA_VALUE, subtype.getExtraValue());
   3781                         out.attribute(null, ATTR_IS_AUXILIARY,
   3782                                 String.valueOf(subtype.isAuxiliary() ? 1 : 0));
   3783                         out.attribute(null, ATTR_IS_ASCII_CAPABLE,
   3784                                 String.valueOf(subtype.isAsciiCapable() ? 1 : 0));
   3785                         out.endTag(null, NODE_SUBTYPE);
   3786                     }
   3787                     out.endTag(null, NODE_IMI);
   3788                 }
   3789                 out.endTag(null, NODE_SUBTYPES);
   3790                 out.endDocument();
   3791                 subtypesFile.finishWrite(fos);
   3792             } catch (java.io.IOException e) {
   3793                 Slog.w(TAG, "Error writing subtypes", e);
   3794                 if (fos != null) {
   3795                     subtypesFile.failWrite(fos);
   3796                 }
   3797             }
   3798         }
   3799 
   3800         private static void readAdditionalInputMethodSubtypes(
   3801                 HashMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile) {
   3802             if (allSubtypes == null || subtypesFile == null) return;
   3803             allSubtypes.clear();
   3804             try (final FileInputStream fis = subtypesFile.openRead()) {
   3805                 final XmlPullParser parser = Xml.newPullParser();
   3806                 parser.setInput(fis, StandardCharsets.UTF_8.name());
   3807                 int type = parser.getEventType();
   3808                 // Skip parsing until START_TAG
   3809                 while ((type = parser.next()) != XmlPullParser.START_TAG
   3810                         && type != XmlPullParser.END_DOCUMENT) {}
   3811                 String firstNodeName = parser.getName();
   3812                 if (!NODE_SUBTYPES.equals(firstNodeName)) {
   3813                     throw new XmlPullParserException("Xml doesn't start with subtypes");
   3814                 }
   3815                 final int depth =parser.getDepth();
   3816                 String currentImiId = null;
   3817                 ArrayList<InputMethodSubtype> tempSubtypesArray = null;
   3818                 while (((type = parser.next()) != XmlPullParser.END_TAG
   3819                         || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
   3820                     if (type != XmlPullParser.START_TAG)
   3821                         continue;
   3822                     final String nodeName = parser.getName();
   3823                     if (NODE_IMI.equals(nodeName)) {
   3824                         currentImiId = parser.getAttributeValue(null, ATTR_ID);
   3825                         if (TextUtils.isEmpty(currentImiId)) {
   3826                             Slog.w(TAG, "Invalid imi id found in subtypes.xml");
   3827                             continue;
   3828                         }
   3829                         tempSubtypesArray = new ArrayList<>();
   3830                         allSubtypes.put(currentImiId, tempSubtypesArray);
   3831                     } else if (NODE_SUBTYPE.equals(nodeName)) {
   3832                         if (TextUtils.isEmpty(currentImiId) || tempSubtypesArray == null) {
   3833                             Slog.w(TAG, "IME uninstalled or not valid.: " + currentImiId);
   3834                             continue;
   3835                         }
   3836                         final int icon = Integer.parseInt(
   3837                                 parser.getAttributeValue(null, ATTR_ICON));
   3838                         final int label = Integer.parseInt(
   3839                                 parser.getAttributeValue(null, ATTR_LABEL));
   3840                         final String imeSubtypeLocale =
   3841                                 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LOCALE);
   3842                         final String languageTag =
   3843                                 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG);
   3844                         final String imeSubtypeMode =
   3845                                 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_MODE);
   3846                         final String imeSubtypeExtraValue =
   3847                                 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_EXTRA_VALUE);
   3848                         final boolean isAuxiliary = "1".equals(String.valueOf(
   3849                                 parser.getAttributeValue(null, ATTR_IS_AUXILIARY)));
   3850                         final boolean isAsciiCapable = "1".equals(String.valueOf(
   3851                                 parser.getAttributeValue(null, ATTR_IS_ASCII_CAPABLE)));
   3852                         final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder()
   3853                                 .setSubtypeNameResId(label)
   3854                                 .setSubtypeIconResId(icon)
   3855                                 .setSubtypeLocale(imeSubtypeLocale)
   3856                                 .setLanguageTag(languageTag)
   3857                                 .setSubtypeMode(imeSubtypeMode)
   3858                                 .setSubtypeExtraValue(imeSubtypeExtraValue)
   3859                                 .setIsAuxiliary(isAuxiliary)
   3860                                 .setIsAsciiCapable(isAsciiCapable);
   3861                         final String subtypeIdString =
   3862                                 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_ID);
   3863                         if (subtypeIdString != null) {
   3864                             builder.setSubtypeId(Integer.parseInt(subtypeIdString));
   3865                         }
   3866                         tempSubtypesArray.add(builder.build());
   3867                     }
   3868                 }
   3869             } catch (XmlPullParserException | IOException | NumberFormatException e) {
   3870                 Slog.w(TAG, "Error reading subtypes", e);
   3871                 return;
   3872             }
   3873         }
   3874     }
   3875 
   3876     private static final class LocalServiceImpl implements InputMethodManagerInternal {
   3877         @NonNull
   3878         private final Handler mHandler;
   3879 
   3880         LocalServiceImpl(@NonNull final Handler handler) {
   3881             mHandler = handler;
   3882         }
   3883 
   3884         @Override
   3885         public void setInteractive(boolean interactive) {
   3886             // Do everything in handler so as not to block the caller.
   3887             mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_INTERACTIVE,
   3888                     interactive ? 1 : 0, 0));
   3889         }
   3890 
   3891         @Override
   3892         public void switchInputMethod(boolean forwardDirection) {
   3893             // Do everything in handler so as not to block the caller.
   3894             mHandler.sendMessage(mHandler.obtainMessage(MSG_SWITCH_IME,
   3895                     forwardDirection ? 1 : 0, 0));
   3896         }
   3897 
   3898         @Override
   3899         public void hideCurrentInputMethod() {
   3900             mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
   3901             mHandler.sendEmptyMessage(MSG_HIDE_CURRENT_INPUT_METHOD);
   3902         }
   3903     }
   3904 
   3905     private static String imeWindowStatusToString(final int imeWindowVis) {
   3906         final StringBuilder sb = new StringBuilder();
   3907         boolean first = true;
   3908         if ((imeWindowVis & InputMethodService.IME_ACTIVE) != 0) {
   3909             sb.append("Active");
   3910             first = false;
   3911         }
   3912         if ((imeWindowVis & InputMethodService.IME_VISIBLE) != 0) {
   3913             if (!first) {
   3914                 sb.append("|");
   3915             }
   3916             sb.append("Visible");
   3917         }
   3918         return sb.toString();
   3919     }
   3920 
   3921     @Override
   3922     public IInputContentUriToken createInputContentUriToken(@Nullable IBinder token,
   3923             @Nullable Uri contentUri, @Nullable String packageName) {
   3924         if (!calledFromValidUser()) {
   3925             return null;
   3926         }
   3927 
   3928         if (token == null) {
   3929             throw new NullPointerException("token");
   3930         }
   3931         if (packageName == null) {
   3932             throw new NullPointerException("packageName");
   3933         }
   3934         if (contentUri == null) {
   3935             throw new NullPointerException("contentUri");
   3936         }
   3937         final String contentUriScheme = contentUri.getScheme();
   3938         if (!"content".equals(contentUriScheme)) {
   3939             throw new InvalidParameterException("contentUri must have content scheme");
   3940         }
   3941 
   3942         synchronized (mMethodMap) {
   3943             final int uid = Binder.getCallingUid();
   3944             if (mCurMethodId == null) {
   3945                 return null;
   3946             }
   3947             if (mCurToken != token) {
   3948                 Slog.e(TAG, "Ignoring createInputContentUriToken mCurToken=" + mCurToken
   3949                         + " token=" + token);
   3950                 return null;
   3951             }
   3952             // We cannot simply distinguish a bad IME that reports an arbitrary package name from
   3953             // an unfortunate IME whose internal state is already obsolete due to the asynchronous
   3954             // nature of our system.  Let's compare it with our internal record.
   3955             if (!TextUtils.equals(mCurAttribute.packageName, packageName)) {
   3956                 Slog.e(TAG, "Ignoring createInputContentUriToken mCurAttribute.packageName="
   3957                     + mCurAttribute.packageName + " packageName=" + packageName);
   3958                 return null;
   3959             }
   3960             final int imeUserId = UserHandle.getUserId(uid);
   3961             final int appUserId = UserHandle.getUserId(mCurClient.uid);
   3962             return new InputContentUriTokenHandler(contentUri, uid, packageName, imeUserId,
   3963                     appUserId);
   3964         }
   3965     }
   3966 
   3967     @Override
   3968     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   3969         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
   3970                 != PackageManager.PERMISSION_GRANTED) {
   3971 
   3972             pw.println("Permission Denial: can't dump InputMethodManager from from pid="
   3973                     + Binder.getCallingPid()
   3974                     + ", uid=" + Binder.getCallingUid());
   3975             return;
   3976         }
   3977 
   3978         IInputMethod method;
   3979         ClientState client;
   3980         ClientState focusedWindowClient;
   3981 
   3982         final Printer p = new PrintWriterPrinter(pw);
   3983 
   3984         synchronized (mMethodMap) {
   3985             p.println("Current Input Method Manager state:");
   3986             int N = mMethodList.size();
   3987             p.println("  Input Methods:");
   3988             for (int i=0; i<N; i++) {
   3989                 InputMethodInfo info = mMethodList.get(i);
   3990                 p.println("  InputMethod #" + i + ":");
   3991                 info.dump(p, "    ");
   3992             }
   3993             p.println("  Clients:");
   3994             for (ClientState ci : mClients.values()) {
   3995                 p.println("  Client " + ci + ":");
   3996                 p.println("    client=" + ci.client);
   3997                 p.println("    inputContext=" + ci.inputContext);
   3998                 p.println("    sessionRequested=" + ci.sessionRequested);
   3999                 p.println("    curSession=" + ci.curSession);
   4000             }
   4001             p.println("  mCurMethodId=" + mCurMethodId);
   4002             client = mCurClient;
   4003             p.println("  mCurClient=" + client + " mCurSeq=" + mCurSeq);
   4004             p.println("  mCurFocusedWindow=" + mCurFocusedWindow);
   4005             focusedWindowClient = mCurFocusedWindowClient;
   4006             p.println("  mCurFocusedWindowClient=" + focusedWindowClient);
   4007             p.println("  mCurId=" + mCurId + " mHaveConnect=" + mHaveConnection
   4008                     + " mBoundToMethod=" + mBoundToMethod);
   4009             p.println("  mCurToken=" + mCurToken);
   4010             p.println("  mCurIntent=" + mCurIntent);
   4011             method = mCurMethod;
   4012             p.println("  mCurMethod=" + mCurMethod);
   4013             p.println("  mEnabledSession=" + mEnabledSession);
   4014             p.println("  mImeWindowVis=" + imeWindowStatusToString(mImeWindowVis));
   4015             p.println("  mShowRequested=" + mShowRequested
   4016                     + " mShowExplicitlyRequested=" + mShowExplicitlyRequested
   4017                     + " mShowForced=" + mShowForced
   4018                     + " mInputShown=" + mInputShown);
   4019             p.println("  mCurUserActionNotificationSequenceNumber="
   4020                     + mCurUserActionNotificationSequenceNumber);
   4021             p.println("  mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive);
   4022             p.println("  mSettingsObserver=" + mSettingsObserver);
   4023             p.println("  mSwitchingController:");
   4024             mSwitchingController.dump(p);
   4025             p.println("  mSettings:");
   4026             mSettings.dumpLocked(p, "    ");
   4027         }
   4028 
   4029         p.println(" ");
   4030         if (client != null) {
   4031             pw.flush();
   4032             try {
   4033                 client.client.asBinder().dump(fd, args);
   4034             } catch (RemoteException e) {
   4035                 p.println("Input method client dead: " + e);
   4036             }
   4037         } else {
   4038             p.println("No input method client.");
   4039         }
   4040 
   4041         if (focusedWindowClient != null && client != focusedWindowClient) {
   4042             p.println(" ");
   4043             p.println("Warning: Current input method client doesn't match the last focused. "
   4044                     + "window.");
   4045             p.println("Dumping input method client in the last focused window just in case.");
   4046             p.println(" ");
   4047             pw.flush();
   4048             try {
   4049                 focusedWindowClient.client.asBinder().dump(fd, args);
   4050             } catch (RemoteException e) {
   4051                 p.println("Input method client in focused window dead: " + e);
   4052             }
   4053         }
   4054 
   4055         p.println(" ");
   4056         if (method != null) {
   4057             pw.flush();
   4058             try {
   4059                 method.asBinder().dump(fd, args);
   4060             } catch (RemoteException e) {
   4061                 p.println("Input method service dead: " + e);
   4062             }
   4063         } else {
   4064             p.println("No input method service.");
   4065         }
   4066     }
   4067 }
   4068