OSDN Git Service

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