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.inputmethod;
17 
18 import static android.view.Display.DEFAULT_DISPLAY;
19 import static android.view.Display.INVALID_DISPLAY;
20 
21 import static java.lang.annotation.RetentionPolicy.SOURCE;
22 
23 import android.Manifest;
24 import android.accessibilityservice.AccessibilityService;
25 import android.annotation.AnyThread;
26 import android.annotation.BinderThread;
27 import android.annotation.ColorInt;
28 import android.annotation.DrawableRes;
29 import android.annotation.IntDef;
30 import android.annotation.MainThread;
31 import android.annotation.NonNull;
32 import android.annotation.Nullable;
33 import android.annotation.RequiresPermission;
34 import android.annotation.UserIdInt;
35 import android.app.ActivityManager;
36 import android.app.ActivityManagerInternal;
37 import android.app.ActivityThread;
38 import android.app.AlertDialog;
39 import android.app.AppGlobals;
40 import android.app.AppOpsManager;
41 import android.app.KeyguardManager;
42 import android.app.Notification;
43 import android.app.NotificationManager;
44 import android.app.PendingIntent;
45 import android.content.BroadcastReceiver;
46 import android.content.ComponentName;
47 import android.content.ContentProvider;
48 import android.content.ContentResolver;
49 import android.content.Context;
50 import android.content.DialogInterface;
51 import android.content.DialogInterface.OnCancelListener;
52 import android.content.DialogInterface.OnClickListener;
53 import android.content.Intent;
54 import android.content.IntentFilter;
55 import android.content.ServiceConnection;
56 import android.content.pm.ApplicationInfo;
57 import android.content.pm.IPackageManager;
58 import android.content.pm.PackageManager;
59 import android.content.pm.PackageManagerInternal;
60 import android.content.pm.ResolveInfo;
61 import android.content.pm.ServiceInfo;
62 import android.content.res.Configuration;
63 import android.content.res.Resources;
64 import android.content.res.TypedArray;
65 import android.database.ContentObserver;
66 import android.graphics.Matrix;
67 import android.graphics.drawable.Drawable;
68 import android.hardware.display.DisplayManagerInternal;
69 import android.hardware.input.InputManagerInternal;
70 import android.inputmethodservice.InputMethodService;
71 import android.media.AudioManagerInternal;
72 import android.net.Uri;
73 import android.os.Binder;
74 import android.os.Bundle;
75 import android.os.Debug;
76 import android.os.Handler;
77 import android.os.IBinder;
78 import android.os.IInterface;
79 import android.os.LocaleList;
80 import android.os.Message;
81 import android.os.Parcel;
82 import android.os.Process;
83 import android.os.RemoteException;
84 import android.os.ResultReceiver;
85 import android.os.ServiceManager;
86 import android.os.ShellCallback;
87 import android.os.ShellCommand;
88 import android.os.SystemClock;
89 import android.os.SystemProperties;
90 import android.os.UserHandle;
91 import android.os.UserManager;
92 import android.os.UserManagerInternal;
93 import android.provider.Settings;
94 import android.text.TextUtils;
95 import android.text.style.SuggestionSpan;
96 import android.util.ArrayMap;
97 import android.util.ArraySet;
98 import android.util.EventLog;
99 import android.util.LruCache;
100 import android.util.Pair;
101 import android.util.PrintWriterPrinter;
102 import android.util.Printer;
103 import android.util.Slog;
104 import android.util.SparseArray;
105 import android.view.ContextThemeWrapper;
106 import android.view.DisplayInfo;
107 import android.view.IWindowManager;
108 import android.view.InputChannel;
109 import android.view.LayoutInflater;
110 import android.view.View;
111 import android.view.ViewGroup;
112 import android.view.Window;
113 import android.view.WindowManager.LayoutParams;
114 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
115 import android.view.autofill.AutofillId;
116 import android.view.inputmethod.EditorInfo;
117 import android.view.inputmethod.InlineSuggestionsRequest;
118 import android.view.inputmethod.InputBinding;
119 import android.view.inputmethod.InputConnection;
120 import android.view.inputmethod.InputConnectionInspector;
121 import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
122 import android.view.inputmethod.InputMethod;
123 import android.view.inputmethod.InputMethodInfo;
124 import android.view.inputmethod.InputMethodManager;
125 import android.view.inputmethod.InputMethodSubtype;
126 import android.widget.ArrayAdapter;
127 import android.widget.CompoundButton;
128 import android.widget.CompoundButton.OnCheckedChangeListener;
129 import android.widget.RadioButton;
130 import android.widget.Switch;
131 import android.widget.TextView;
132 
133 import com.android.internal.annotations.GuardedBy;
134 import com.android.internal.content.PackageMonitor;
135 import com.android.internal.inputmethod.IInputContentUriToken;
136 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
137 import com.android.internal.inputmethod.InputMethodDebug;
138 import com.android.internal.inputmethod.SoftInputShowHideReason;
139 import com.android.internal.inputmethod.StartInputFlags;
140 import com.android.internal.inputmethod.StartInputReason;
141 import com.android.internal.inputmethod.UnbindReason;
142 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
143 import com.android.internal.notification.SystemNotificationChannels;
144 import com.android.internal.os.HandlerCaller;
145 import com.android.internal.os.SomeArgs;
146 import com.android.internal.os.TransferPipe;
147 import com.android.internal.util.DumpUtils;
148 import com.android.internal.util.IndentingPrintWriter;
149 import com.android.internal.view.IInlineSuggestionsRequestCallback;
150 import com.android.internal.view.IInlineSuggestionsResponseCallback;
151 import com.android.internal.view.IInputContext;
152 import com.android.internal.view.IInputMethod;
153 import com.android.internal.view.IInputMethodClient;
154 import com.android.internal.view.IInputMethodManager;
155 import com.android.internal.view.IInputMethodSession;
156 import com.android.internal.view.IInputSessionCallback;
157 import com.android.internal.view.InlineSuggestionsRequestInfo;
158 import com.android.internal.view.InputBindResult;
159 import com.android.server.EventLogTags;
160 import com.android.server.LocalServices;
161 import com.android.server.SystemService;
162 import com.android.server.inputmethod.InputMethodManagerInternal.InputMethodListListener;
163 import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
164 import com.android.server.inputmethod.InputMethodUtils.InputMethodSettings;
165 import com.android.server.statusbar.StatusBarManagerService;
166 import com.android.server.wm.WindowManagerInternal;
167 
168 import java.io.FileDescriptor;
169 import java.io.IOException;
170 import java.io.PrintWriter;
171 import java.lang.annotation.Retention;
172 import java.security.InvalidParameterException;
173 import java.text.SimpleDateFormat;
174 import java.util.ArrayList;
175 import java.util.Arrays;
176 import java.util.Collections;
177 import java.util.Date;
178 import java.util.List;
179 import java.util.Locale;
180 import java.util.Objects;
181 import java.util.WeakHashMap;
182 import java.util.concurrent.CopyOnWriteArrayList;
183 import java.util.concurrent.atomic.AtomicInteger;
184 
185 /**
186  * This class provides a system service that manages input methods.
187  */
188 public class InputMethodManagerService extends IInputMethodManager.Stub
189         implements ServiceConnection, Handler.Callback {
190     static final boolean DEBUG = false;
191     static final String TAG = "InputMethodManagerService";
192 
193     @Retention(SOURCE)
194     @IntDef({ShellCommandResult.SUCCESS, ShellCommandResult.FAILURE})
195     private @interface ShellCommandResult {
196         int SUCCESS = 0;
197         int FAILURE = -1;
198     }
199 
200     static final int MSG_SHOW_IM_SUBTYPE_PICKER = 1;
201     static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 2;
202     static final int MSG_SHOW_IM_CONFIG = 3;
203 
204     static final int MSG_UNBIND_INPUT = 1000;
205     static final int MSG_BIND_INPUT = 1010;
206     static final int MSG_SHOW_SOFT_INPUT = 1020;
207     static final int MSG_HIDE_SOFT_INPUT = 1030;
208     static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035;
209     static final int MSG_INITIALIZE_IME = 1040;
210     static final int MSG_CREATE_SESSION = 1050;
211     static final int MSG_REMOVE_IME_SURFACE = 1060;
212     static final int MSG_REMOVE_IME_SURFACE_FROM_WINDOW = 1061;
213 
214     static final int MSG_START_INPUT = 2000;
215 
216     static final int MSG_UNBIND_CLIENT = 3000;
217     static final int MSG_BIND_CLIENT = 3010;
218     static final int MSG_SET_ACTIVE = 3020;
219     static final int MSG_SET_INTERACTIVE = 3030;
220     static final int MSG_REPORT_FULLSCREEN_MODE = 3045;
221     static final int MSG_REPORT_PRE_RENDERED = 3060;
222     static final int MSG_APPLY_IME_VISIBILITY = 3070;
223 
224     static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
225 
226     static final int MSG_SYSTEM_UNLOCK_USER = 5000;
227     static final int MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED = 5010;
228 
229     static final int MSG_INLINE_SUGGESTIONS_REQUEST = 6000;
230 
231     static final int MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE = 7000;
232 
233     static final long TIME_TO_RECONNECT = 3 * 1000;
234 
235     static final int SECURE_SUGGESTION_SPANS_MAX_SIZE = 20;
236 
237     private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
238     private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
239 
240     /**
241      * Binding flags for establishing connection to the {@link InputMethodService}.
242      */
243     private static final int IME_CONNECTION_BIND_FLAGS =
244             Context.BIND_AUTO_CREATE
245             | Context.BIND_NOT_VISIBLE
246             | Context.BIND_NOT_FOREGROUND
247             | Context.BIND_IMPORTANT_BACKGROUND;
248 
249     /**
250      * Binding flags used only while the {@link InputMethodService} is showing window.
251      */
252     private static final int IME_VISIBLE_BIND_FLAGS =
253             Context.BIND_AUTO_CREATE
254             | Context.BIND_TREAT_LIKE_ACTIVITY
255             | Context.BIND_FOREGROUND_SERVICE
256             | Context.BIND_INCLUDE_CAPABILITIES
257             | Context.BIND_SHOWING_UI
258             | Context.BIND_SCHEDULE_LIKE_TOP_APP;
259 
260     /**
261      * A protected broadcast intent action for internal use for {@link PendingIntent} in
262      * the notification.
263      */
264     private static final String ACTION_SHOW_INPUT_METHOD_PICKER =
265             "com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER";
266 
267     /**
268      * Debug flag for overriding runtime {@link SystemProperties}.
269      */
270     @AnyThread
271     private static final class DebugFlag {
272         private static final Object LOCK = new Object();
273         private final String mKey;
274         private final boolean mDefaultValue;
275         @GuardedBy("LOCK")
276         private boolean mValue;
277 
DebugFlag(String key, boolean defaultValue)278         public DebugFlag(String key, boolean defaultValue) {
279             mKey = key;
280             mDefaultValue = defaultValue;
281             mValue = SystemProperties.getBoolean(key, defaultValue);
282         }
283 
refresh()284         void refresh() {
285             synchronized (LOCK) {
286                 mValue = SystemProperties.getBoolean(mKey, mDefaultValue);
287             }
288         }
289 
value()290         boolean value() {
291             synchronized (LOCK) {
292                 return mValue;
293             }
294         }
295     }
296 
297     /**
298      * Debug flags that can be overridden using "adb shell setprop <key>"
299      * Note: These flags are cached. To refresh, run "adb shell ime refresh_debug_properties".
300      */
301     private static final class DebugFlags {
302         static final DebugFlag FLAG_OPTIMIZE_START_INPUT =
303                 new DebugFlag("debug.optimize_startinput", false);
304         static final DebugFlag FLAG_PRE_RENDER_IME_VIEWS =
305                 new DebugFlag("persist.pre_render_ime_views", false);
306     }
307 
308     @UserIdInt
309     private int mLastSwitchUserId;
310 
311     final Context mContext;
312     final Resources mRes;
313     final Handler mHandler;
314     final InputMethodSettings mSettings;
315     final SettingsObserver mSettingsObserver;
316     final IWindowManager mIWindowManager;
317     final WindowManagerInternal mWindowManagerInternal;
318     final PackageManagerInternal mPackageManagerInternal;
319     final InputManagerInternal mInputManagerInternal;
320     private final DisplayManagerInternal mDisplayManagerInternal;
321     final HandlerCaller mCaller;
322     final boolean mHasFeature;
323     private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypeMap =
324             new ArrayMap<>();
325     private final boolean mIsLowRam;
326     private final HardKeyboardListener mHardKeyboardListener;
327     private final AppOpsManager mAppOpsManager;
328     private final UserManager mUserManager;
329     private final UserManagerInternal mUserManagerInternal;
330 
331     /**
332      * Cache the result of {@code LocalServices.getService(AudioManagerInternal.class)}.
333      *
334      * <p>This field is used only within {@link #handleMessage(Message)} hence synchronization is
335      * not necessary.</p>
336      */
337     @Nullable
338     private AudioManagerInternal mAudioManagerInternal = null;
339 
340 
341     // All known input methods.  mMethodMap also serves as the global
342     // lock for this class.
343     final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
344     final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
345     private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
346             new LruCache<>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
347     private final InputMethodSubtypeSwitchingController mSwitchingController;
348 
349     /**
350      * Tracks how many times {@link #mMethodMap} was updated.
351      */
352     @GuardedBy("mMethodMap")
353     private int mMethodMapUpdateCount = 0;
354 
355     // Used to bring IME service up to visible adjustment while it is being shown.
356     final ServiceConnection mVisibleConnection = new ServiceConnection() {
357         @Override public void onBindingDied(ComponentName name) {
358             synchronized (mMethodMap) {
359                 if (mVisibleBound) {
360                     mContext.unbindService(mVisibleConnection);
361                     mVisibleBound = false;
362                 }
363             }
364         }
365 
366         @Override public void onServiceConnected(ComponentName name, IBinder service) {
367         }
368 
369         @Override public void onServiceDisconnected(ComponentName name) {
370         }
371     };
372     boolean mVisibleBound = false;
373 
374     // Ongoing notification
375     private NotificationManager mNotificationManager;
376     private KeyguardManager mKeyguardManager;
377     private @Nullable StatusBarManagerService mStatusBar;
378     private Notification.Builder mImeSwitcherNotification;
379     private PendingIntent mImeSwitchPendingIntent;
380     private boolean mShowOngoingImeSwitcherForPhones;
381     private boolean mNotificationShown;
382 
383     static class SessionState {
384         final ClientState client;
385         final IInputMethod method;
386 
387         IInputMethodSession session;
388         InputChannel channel;
389 
390         @Override
toString()391         public String toString() {
392             return "SessionState{uid " + client.uid + " pid " + client.pid
393                     + " method " + Integer.toHexString(
394                             System.identityHashCode(method))
395                     + " session " + Integer.toHexString(
396                             System.identityHashCode(session))
397                     + " channel " + channel
398                     + "}";
399         }
400 
SessionState(ClientState _client, IInputMethod _method, IInputMethodSession _session, InputChannel _channel)401         SessionState(ClientState _client, IInputMethod _method,
402                 IInputMethodSession _session, InputChannel _channel) {
403             client = _client;
404             method = _method;
405             session = _session;
406             channel = _channel;
407         }
408     }
409 
410     private static final class ClientDeathRecipient implements IBinder.DeathRecipient {
411         private final InputMethodManagerService mImms;
412         private final IInputMethodClient mClient;
413 
ClientDeathRecipient(InputMethodManagerService imms, IInputMethodClient client)414         ClientDeathRecipient(InputMethodManagerService imms, IInputMethodClient client) {
415             mImms = imms;
416             mClient = client;
417         }
418 
419         @Override
binderDied()420         public void binderDied() {
421             mImms.removeClient(mClient);
422         }
423     }
424 
425     static final class ClientState {
426         final IInputMethodClient client;
427         final IInputContext inputContext;
428         final int uid;
429         final int pid;
430         final int selfReportedDisplayId;
431         final InputBinding binding;
432         final ClientDeathRecipient clientDeathRecipient;
433 
434         boolean sessionRequested;
435         // Determines if IMEs should be pre-rendered.
436         // DebugFlag can be flipped anytime. This flag is kept per-client to maintain behavior
437         // through the life of the current client.
438         boolean shouldPreRenderIme;
439         SessionState curSession;
440 
441         @Override
toString()442         public String toString() {
443             return "ClientState{" + Integer.toHexString(
444                     System.identityHashCode(this)) + " uid=" + uid
445                     + " pid=" + pid + " displayId=" + selfReportedDisplayId + "}";
446         }
447 
ClientState(IInputMethodClient _client, IInputContext _inputContext, int _uid, int _pid, int _selfReportedDisplayId, ClientDeathRecipient _clientDeathRecipient)448         ClientState(IInputMethodClient _client, IInputContext _inputContext,
449                 int _uid, int _pid, int _selfReportedDisplayId,
450                 ClientDeathRecipient _clientDeathRecipient) {
451             client = _client;
452             inputContext = _inputContext;
453             uid = _uid;
454             pid = _pid;
455             selfReportedDisplayId = _selfReportedDisplayId;
456             binding = new InputBinding(null, inputContext.asBinder(), uid, pid);
457             clientDeathRecipient = _clientDeathRecipient;
458         }
459     }
460 
461     final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
462 
463     private static final class ActivityViewInfo {
464         /**
465          * {@link ClientState} where {@link android.app.ActivityView} is running.
466          */
467         private final ClientState mParentClient;
468         /**
469          * {@link Matrix} to convert screen coordinates in the embedded virtual display to
470          * screen coordinates where {@link #mParentClient} exists.
471          */
472         private final Matrix mMatrix;
473 
ActivityViewInfo(ClientState parentClient, Matrix matrix)474         ActivityViewInfo(ClientState parentClient, Matrix matrix) {
475             mParentClient = parentClient;
476             mMatrix = matrix;
477         }
478     }
479 
480     /**
481      * A mapping table from virtual display IDs created for {@link android.app.ActivityView}
482      * to its parent IME client where {@link android.app.ActivityView} is running.
483      *
484      * <p>Note: this can be used only for virtual display IDs created by
485      * {@link android.app.ActivityView}.</p>
486      */
487     private SparseArray<ActivityViewInfo> mActivityViewDisplayIdToParentMap = new SparseArray<>();
488 
489     /**
490      * Set once the system is ready to run third party code.
491      */
492     boolean mSystemReady;
493 
494     /**
495      * Id obtained with {@link InputMethodInfo#getId()} for the currently selected input method.
496      * method.  This is to be synchronized with the secure settings keyed with
497      * {@link Settings.Secure#DEFAULT_INPUT_METHOD}.
498      *
499      * <p>This can be transiently {@code null} when the system is re-initializing input method
500      * settings, e.g., the system locale is just changed.</p>
501      *
502      * <p>Note that {@link #mCurId} is used to track which IME is being connected to
503      * {@link InputMethodManagerService}.</p>
504      *
505      * @see #mCurId
506      */
507     @Nullable
508     String mCurMethodId;
509 
510     /**
511      * The current binding sequence number, incremented every time there is
512      * a new bind performed.
513      */
514     int mCurSeq;
515 
516     /**
517      * The client that is currently bound to an input method.
518      */
519     ClientState mCurClient;
520 
521     /**
522      * The last window token that we confirmed to be focused.  This is always updated upon reports
523      * from the input method client.  If the window state is already changed before the report is
524      * handled, this field just keeps the last value.
525      */
526     IBinder mCurFocusedWindow;
527 
528     /**
529      * The last window token that we confirmed that IME started talking to.  This is always updated
530      * upon reports from the input method.  If the window state is already changed before the report
531      * is handled, this field just keeps the last value.
532      */
533     IBinder mLastImeTargetWindow;
534 
535     /**
536      * {@link LayoutParams#softInputMode} of {@link #mCurFocusedWindow}.
537      *
538      * @see #mCurFocusedWindow
539      */
540     @SoftInputModeFlags
541     int mCurFocusedWindowSoftInputMode;
542 
543     /**
544      * The client by which {@link #mCurFocusedWindow} was reported.
545      */
546     ClientState mCurFocusedWindowClient;
547 
548     /**
549      * The input context last provided by the current client.
550      */
551     IInputContext mCurInputContext;
552 
553     /**
554      * The missing method flags for the input context last provided by the current client.
555      *
556      * @see android.view.inputmethod.InputConnectionInspector.MissingMethodFlags
557      */
558     @MissingMethodFlags
559     int mCurInputContextMissingMethods;
560 
561     /**
562      * The attributes last provided by the current client.
563      */
564     EditorInfo mCurAttribute;
565 
566     /**
567      * A special {@link Matrix} to convert virtual screen coordinates to the IME target display
568      * coordinates.
569      *
570      * <p>Used only while the IME client is running in a virtual display inside
571      * {@link android.app.ActivityView}. {@code null} otherwise.</p>
572      */
573     @Nullable
574     private Matrix mCurActivityViewToScreenMatrix = null;
575 
576     /**
577      * Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently
578      * connected to or in the process of connecting to.
579      *
580      * <p>This can be {@code null} when no input method is connected.</p>
581      *
582      * @see #mCurMethodId
583      */
584     @Nullable
585     String mCurId;
586 
587     /**
588      * The current subtype of the current input method.
589      */
590     private InputMethodSubtype mCurrentSubtype;
591 
592     // Was the keyguard locked when this client became current?
593     private boolean mCurClientInKeyguard;
594 
595     /**
596      * {@code true} if the IME has not been mostly hidden via {@link android.view.InsetsController}
597      */
598     private boolean mCurPerceptible;
599 
600     /**
601      * Set to true if our ServiceConnection is currently actively bound to
602      * a service (whether or not we have gotten its IBinder back yet).
603      */
604     boolean mHaveConnection;
605 
606     /**
607      * Set if the client has asked for the input method to be shown.
608      */
609     boolean mShowRequested;
610 
611     /**
612      * Set if we were explicitly told to show the input method.
613      */
614     boolean mShowExplicitlyRequested;
615 
616     /**
617      * Set if we were forced to be shown.
618      */
619     boolean mShowForced;
620 
621     /**
622      * Set if we last told the input method to show itself.
623      */
624     boolean mInputShown;
625 
626     /**
627      * {@code true} if the current input method is in fullscreen mode.
628      */
629     boolean mInFullscreenMode;
630 
631     /**
632      * The Intent used to connect to the current input method.
633      */
634     Intent mCurIntent;
635 
636     /**
637      * The token we have made for the currently active input method, to
638      * identify it in the future.
639      */
640     IBinder mCurToken;
641 
642     /**
643      * The displayId of current active input method.
644      */
645     int mCurTokenDisplayId = INVALID_DISPLAY;
646 
647     /**
648      * The host input token of the current active input method.
649      */
650     @GuardedBy("mMethodMap")
651     @Nullable
652     private IBinder mCurHostInputToken;
653 
654     /**
655      * The display ID of the input method indicates the fallback display which returned by
656      * {@link #computeImeDisplayIdForTarget}.
657      */
658     private static final int FALLBACK_DISPLAY_ID = DEFAULT_DISPLAY;
659 
660     final ImeDisplayValidator mImeDisplayValidator;
661 
662     /**
663      * If non-null, this is the input method service we are currently connected
664      * to.
665      */
666     IInputMethod mCurMethod;
667 
668     /**
669      * If not {@link Process#INVALID_UID}, then the UID of {@link #mCurIntent}.
670      */
671     int mCurMethodUid = Process.INVALID_UID;
672 
673     /**
674      * Time that we last initiated a bind to the input method, to determine
675      * if we should try to disconnect and reconnect to it.
676      */
677     long mLastBindTime;
678 
679     /**
680      * Have we called mCurMethod.bindInput()?
681      */
682     boolean mBoundToMethod;
683 
684     /**
685      * Currently enabled session.  Only touched by service thread, not
686      * protected by a lock.
687      */
688     SessionState mEnabledSession;
689 
690     /**
691      * True if the device is currently interactive with user.  The value is true initially.
692      */
693     boolean mIsInteractive = true;
694 
695     int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
696 
697     /**
698      * A set of status bits regarding the active IME.
699      *
700      * <p>This value is a combination of following two bits:</p>
701      * <dl>
702      * <dt>{@link InputMethodService#IME_ACTIVE}</dt>
703      * <dd>
704      *   If this bit is ON, connected IME is ready to accept touch/key events.
705      * </dd>
706      * <dt>{@link InputMethodService#IME_VISIBLE}</dt>
707      * <dd>
708      *   If this bit is ON, some of IME view, e.g. software input, candidate view, is visible.
709      * </dd>
710      * dt>{@link InputMethodService#IME_INVISIBLE}</dt>
711      * <dd> If this bit is ON, IME is ready with views from last EditorInfo but is
712      *    currently invisible.
713      * </dd>
714      * </dl>
715      * <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and
716      * {@link #unbindCurrentMethodLocked()}.</em>
717      */
718     int mImeWindowVis;
719 
720     private AlertDialog.Builder mDialogBuilder;
721     private AlertDialog mSwitchingDialog;
722     private IBinder mSwitchingDialogToken = new Binder();
723     private View mSwitchingDialogTitleView;
724     private InputMethodInfo[] mIms;
725     private int[] mSubtypeIds;
726     private LocaleList mLastSystemLocales;
727     private boolean mShowImeWithHardKeyboard;
728     private boolean mAccessibilityRequestingNoSoftKeyboard;
729     private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
730     private final IPackageManager mIPackageManager;
731     private final String mSlotIme;
732 
733     /**
734      * Registered {@link InputMethodListListeners}.
735      * This variable can be accessed from both of MainThread and BinderThread.
736      */
737     private final CopyOnWriteArrayList<InputMethodListListener> mInputMethodListListeners =
738             new CopyOnWriteArrayList<>();
739 
740     /**
741      * Internal state snapshot when {@link #MSG_START_INPUT} message is about to be posted to the
742      * internal message queue. Any subsequent state change inside {@link InputMethodManagerService}
743      * will not affect those tasks that are already posted.
744      *
745      * <p>Posting {@link #MSG_START_INPUT} message basically means that
746      * {@link InputMethodService#doStartInput(InputConnection, EditorInfo, boolean)} will be called
747      * back in the current IME process shortly, which will also affect what the current IME starts
748      * receiving from {@link InputMethodService#getCurrentInputConnection()}. In other words, this
749      * snapshot will be taken every time when {@link InputMethodManagerService} is initiating a new
750      * logical input session between the client application and the current IME.</p>
751      *
752      * <p>Be careful to not keep strong references to this object forever, which can prevent
753      * {@link StartInputInfo#mImeToken} and {@link StartInputInfo#mTargetWindow} from being GC-ed.
754      * </p>
755      */
756     private static class StartInputInfo {
757         private static final AtomicInteger sSequenceNumber = new AtomicInteger(0);
758 
759         final int mSequenceNumber;
760         final long mTimestamp;
761         final long mWallTime;
762         @UserIdInt
763         final int mImeUserId;
764         @NonNull
765         final IBinder mImeToken;
766         final int mImeDisplayId;
767         @NonNull
768         final String mImeId;
769         @StartInputReason
770         final int mStartInputReason;
771         final boolean mRestarting;
772         @UserIdInt
773         final int mTargetUserId;
774         final int mTargetDisplayId;
775         @Nullable
776         final IBinder mTargetWindow;
777         @NonNull
778         final EditorInfo mEditorInfo;
779         @SoftInputModeFlags
780         final int mTargetWindowSoftInputMode;
781         final int mClientBindSequenceNumber;
782 
StartInputInfo(@serIdInt int imeUserId, @NonNull IBinder imeToken, int imeDisplayId, @NonNull String imeId, @StartInputReason int startInputReason, boolean restarting, @UserIdInt int targetUserId, int targetDisplayId, @Nullable IBinder targetWindow, @NonNull EditorInfo editorInfo, @SoftInputModeFlags int targetWindowSoftInputMode, int clientBindSequenceNumber)783         StartInputInfo(@UserIdInt int imeUserId, @NonNull IBinder imeToken, int imeDisplayId,
784                 @NonNull String imeId, @StartInputReason int startInputReason, boolean restarting,
785                 @UserIdInt int targetUserId, int targetDisplayId, @Nullable IBinder targetWindow,
786                 @NonNull EditorInfo editorInfo, @SoftInputModeFlags int targetWindowSoftInputMode,
787                 int clientBindSequenceNumber) {
788             mSequenceNumber = sSequenceNumber.getAndIncrement();
789             mTimestamp = SystemClock.uptimeMillis();
790             mWallTime = System.currentTimeMillis();
791             mImeUserId = imeUserId;
792             mImeToken = imeToken;
793             mImeDisplayId = imeDisplayId;
794             mImeId = imeId;
795             mStartInputReason = startInputReason;
796             mRestarting = restarting;
797             mTargetUserId = targetUserId;
798             mTargetDisplayId = targetDisplayId;
799             mTargetWindow = targetWindow;
800             mEditorInfo = editorInfo;
801             mTargetWindowSoftInputMode = targetWindowSoftInputMode;
802             mClientBindSequenceNumber = clientBindSequenceNumber;
803         }
804     }
805 
806     @GuardedBy("mMethodMap")
807     private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
808 
809     private static final class SoftInputShowHideHistory {
810         private Entry[] mEntries = new Entry[16];
811         private int mNextIndex = 0;
812         private static final AtomicInteger sSequenceNumber = new AtomicInteger(0);
813 
814         private static final class Entry {
815             final int mSequenceNumber = sSequenceNumber.getAndIncrement();
816             final ClientState mClientState;
817             @SoftInputModeFlags
818             final int mFocusedWindowSoftInputMode;
819             @SoftInputShowHideReason
820             final int mReason;
821             // The timing of handling MSG_SHOW_SOFT_INPUT or MSG_HIDE_SOFT_INPUT.
822             final long mTimestamp;
823             final long mWallTime;
824             final boolean mInFullscreenMode;
825             @NonNull
826             final String mFocusedWindowName;
827             @NonNull
828             final EditorInfo mEditorInfo;
829             @NonNull
830             final String mRequestWindowName;
831             @Nullable
832             final String mImeControlTargetName;
833             @Nullable
834             final String mImeTargetNameFromWm;
835 
Entry(ClientState client, EditorInfo editorInfo, String focusedWindowName, @SoftInputModeFlags int softInputMode, @SoftInputShowHideReason int reason, boolean inFullscreenMode, String requestWindowName, @Nullable String imeControlTargetName, @Nullable String imeTargetName)836             Entry(ClientState client, EditorInfo editorInfo, String focusedWindowName,
837                     @SoftInputModeFlags int softInputMode, @SoftInputShowHideReason int reason,
838                     boolean inFullscreenMode, String requestWindowName,
839                     @Nullable String imeControlTargetName, @Nullable String imeTargetName) {
840                 mClientState = client;
841                 mEditorInfo = editorInfo;
842                 mFocusedWindowName = focusedWindowName;
843                 mFocusedWindowSoftInputMode = softInputMode;
844                 mReason = reason;
845                 mTimestamp = SystemClock.uptimeMillis();
846                 mWallTime = System.currentTimeMillis();
847                 mInFullscreenMode = inFullscreenMode;
848                 mRequestWindowName = requestWindowName;
849                 mImeControlTargetName = imeControlTargetName;
850                 mImeTargetNameFromWm = imeTargetName;
851             }
852         }
853 
addEntry(@onNull Entry entry)854         void addEntry(@NonNull Entry entry) {
855             final int index = mNextIndex;
856             mEntries[index] = entry;
857             mNextIndex = (mNextIndex + 1) % mEntries.length;
858         }
859 
dump(@onNull PrintWriter pw, @NonNull String prefix)860         void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
861             final SimpleDateFormat dataFormat =
862                     new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
863 
864             for (int i = 0; i < mEntries.length; ++i) {
865                 final Entry entry = mEntries[(i + mNextIndex) % mEntries.length];
866                 if (entry == null) {
867                     continue;
868                 }
869                 pw.print(prefix);
870                 pw.println("SoftInputShowHideHistory #" + entry.mSequenceNumber + ":");
871 
872                 pw.print(prefix);
873                 pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime))
874                         + " (timestamp=" + entry.mTimestamp + ")");
875 
876                 pw.print(prefix);
877                 pw.print(" reason=" + InputMethodDebug.softInputDisplayReasonToString(
878                         entry.mReason));
879                 pw.println(" inFullscreenMode=" + entry.mInFullscreenMode);
880 
881                 pw.print(prefix);
882                 pw.println(" requestClient=" + entry.mClientState);
883 
884                 pw.print(prefix);
885                 pw.println(" focusedWindowName=" + entry.mFocusedWindowName);
886 
887                 pw.print(prefix);
888                 pw.println(" requestWindowName=" + entry.mRequestWindowName);
889 
890                 pw.print(prefix);
891                 pw.println(" imeControlTargetName=" + entry.mImeControlTargetName);
892 
893                 pw.print(prefix);
894                 pw.println(" imeTargetNameFromWm=" + entry.mImeTargetNameFromWm);
895 
896                 pw.print(prefix);
897                 pw.print(" editorInfo: ");
898                 pw.print(" inputType=" + entry.mEditorInfo.inputType);
899                 pw.print(" privateImeOptions=" + entry.mEditorInfo.privateImeOptions);
900                 pw.println(" fieldId (viewId)=" + entry.mEditorInfo.fieldId);
901 
902                 pw.print(prefix);
903                 pw.println(" focusedWindowSoftInputMode=" + InputMethodDebug.softInputModeToString(
904                         entry.mFocusedWindowSoftInputMode));
905             }
906         }
907     }
908 
909     /**
910      * Map of generated token to windowToken that is requesting
911      * {@link InputMethodManager#showSoftInput(View, int)}.
912      * This map tracks origin of showSoftInput requests.
913      */
914     @GuardedBy("mMethodMap")
915     private final WeakHashMap<IBinder, IBinder> mShowRequestWindowMap = new WeakHashMap<>();
916 
917     /**
918      * Map of generated token to windowToken that is requesting
919      * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)}.
920      * This map tracks origin of hideSoftInput requests.
921      */
922     @GuardedBy("mMethodMap")
923     private final WeakHashMap<IBinder, IBinder> mHideRequestWindowMap = new WeakHashMap<>();
924 
925     /**
926      * A ring buffer to store the history of {@link StartInputInfo}.
927      */
928     private static final class StartInputHistory {
929         /**
930          * Entry size for non low-RAM devices.
931          *
932          * <p>TODO: Consider to follow what other system services have been doing to manage
933          * constants (e.g. {@link android.provider.Settings.Global#ACTIVITY_MANAGER_CONSTANTS}).</p>
934          */
935         private final static int ENTRY_SIZE_FOR_HIGH_RAM_DEVICE = 16;
936 
937         /**
938          * Entry size for non low-RAM devices.
939          *
940          * <p>TODO: Consider to follow what other system services have been doing to manage
941          * constants (e.g. {@link android.provider.Settings.Global#ACTIVITY_MANAGER_CONSTANTS}).</p>
942          */
943         private final static int ENTRY_SIZE_FOR_LOW_RAM_DEVICE = 5;
944 
getEntrySize()945         private static int getEntrySize() {
946             if (ActivityManager.isLowRamDeviceStatic()) {
947                 return ENTRY_SIZE_FOR_LOW_RAM_DEVICE;
948             } else {
949                 return ENTRY_SIZE_FOR_HIGH_RAM_DEVICE;
950             }
951         }
952 
953         /**
954          * Backing store for the ring bugger.
955          */
956         private final Entry[] mEntries = new Entry[getEntrySize()];
957 
958         /**
959          * An index of {@link #mEntries}, to which next {@link #addEntry(StartInputInfo)} should
960          * write.
961          */
962         private int mNextIndex = 0;
963 
964         /**
965          * Recyclable entry to store the information in {@link StartInputInfo}.
966          */
967         private static final class Entry {
968             int mSequenceNumber;
969             long mTimestamp;
970             long mWallTime;
971             @UserIdInt
972             int mImeUserId;
973             @NonNull
974             String mImeTokenString;
975             int mImeDisplayId;
976             @NonNull
977             String mImeId;
978             @StartInputReason
979             int mStartInputReason;
980             boolean mRestarting;
981             @UserIdInt
982             int mTargetUserId;
983             int mTargetDisplayId;
984             @NonNull
985             String mTargetWindowString;
986             @NonNull
987             EditorInfo mEditorInfo;
988             @SoftInputModeFlags
989             int mTargetWindowSoftInputMode;
990             int mClientBindSequenceNumber;
991 
Entry(@onNull StartInputInfo original)992             Entry(@NonNull StartInputInfo original) {
993                 set(original);
994             }
995 
set(@onNull StartInputInfo original)996             void set(@NonNull StartInputInfo original) {
997                 mSequenceNumber = original.mSequenceNumber;
998                 mTimestamp = original.mTimestamp;
999                 mWallTime = original.mWallTime;
1000                 mImeUserId = original.mImeUserId;
1001                 // Intentionally convert to String so as not to keep a strong reference to a Binder
1002                 // object.
1003                 mImeTokenString = String.valueOf(original.mImeToken);
1004                 mImeDisplayId = original.mImeDisplayId;
1005                 mImeId = original.mImeId;
1006                 mStartInputReason = original.mStartInputReason;
1007                 mRestarting = original.mRestarting;
1008                 mTargetUserId = original.mTargetUserId;
1009                 mTargetDisplayId = original.mTargetDisplayId;
1010                 // Intentionally convert to String so as not to keep a strong reference to a Binder
1011                 // object.
1012                 mTargetWindowString = String.valueOf(original.mTargetWindow);
1013                 mEditorInfo = original.mEditorInfo;
1014                 mTargetWindowSoftInputMode = original.mTargetWindowSoftInputMode;
1015                 mClientBindSequenceNumber = original.mClientBindSequenceNumber;
1016             }
1017         }
1018 
1019         /**
1020          * Add a new entry and discard the oldest entry as needed.
1021          * @param info {@lin StartInputInfo} to be added.
1022          */
addEntry(@onNull StartInputInfo info)1023         void addEntry(@NonNull StartInputInfo info) {
1024             final int index = mNextIndex;
1025             if (mEntries[index] == null) {
1026                 mEntries[index] = new Entry(info);
1027             } else {
1028                 mEntries[index].set(info);
1029             }
1030             mNextIndex = (mNextIndex + 1) % mEntries.length;
1031         }
1032 
dump(@onNull PrintWriter pw, @NonNull String prefix)1033         void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
1034             final SimpleDateFormat dataFormat =
1035                     new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
1036 
1037             for (int i = 0; i < mEntries.length; ++i) {
1038                 final Entry entry = mEntries[(i + mNextIndex) % mEntries.length];
1039                 if (entry == null) {
1040                     continue;
1041                 }
1042                 pw.print(prefix);
1043                 pw.println("StartInput #" + entry.mSequenceNumber + ":");
1044 
1045                 pw.print(prefix);
1046                 pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime))
1047                         + " (timestamp=" + entry.mTimestamp + ")"
1048                         + " reason="
1049                         + InputMethodDebug.startInputReasonToString(entry.mStartInputReason)
1050                         + " restarting=" + entry.mRestarting);
1051 
1052                 pw.print(prefix);
1053                 pw.print(" imeToken=" + entry.mImeTokenString + " [" + entry.mImeId + "]");
1054                 pw.print(" imeUserId=" + entry.mImeUserId);
1055                 pw.println(" imeDisplayId=" + entry.mImeDisplayId);
1056 
1057                 pw.print(prefix);
1058                 pw.println(" targetWin=" + entry.mTargetWindowString
1059                         + " [" + entry.mEditorInfo.packageName + "]"
1060                         + " targetUserId=" + entry.mTargetUserId
1061                         + " targetDisplayId=" + entry.mTargetDisplayId
1062                         + " clientBindSeq=" + entry.mClientBindSequenceNumber);
1063 
1064                 pw.print(prefix);
1065                 pw.println(" softInputMode=" + InputMethodDebug.softInputModeToString(
1066                                 entry.mTargetWindowSoftInputMode));
1067 
1068                 pw.print(prefix);
1069                 pw.println(" inputType=0x" + Integer.toHexString(entry.mEditorInfo.inputType)
1070                         + " imeOptions=0x" + Integer.toHexString(entry.mEditorInfo.imeOptions)
1071                         + " fieldId=0x" + Integer.toHexString(entry.mEditorInfo.fieldId)
1072                         + " fieldName=" + entry.mEditorInfo.fieldName
1073                         + " actionId=" + entry.mEditorInfo.actionId
1074                         + " actionLabel=" + entry.mEditorInfo.actionLabel);
1075             }
1076         }
1077     }
1078 
1079     @GuardedBy("mMethodMap")
1080     @NonNull
1081     private final StartInputHistory mStartInputHistory = new StartInputHistory();
1082 
1083     @GuardedBy("mMethodMap")
1084     @NonNull
1085     private final SoftInputShowHideHistory mSoftInputShowHideHistory =
1086             new SoftInputShowHideHistory();
1087 
1088     class SettingsObserver extends ContentObserver {
1089         int mUserId;
1090         boolean mRegistered = false;
1091         @NonNull
1092         String mLastEnabled = "";
1093 
1094         /**
1095          * <em>This constructor must be called within the lock.</em>
1096          */
SettingsObserver(Handler handler)1097         SettingsObserver(Handler handler) {
1098             super(handler);
1099         }
1100 
registerContentObserverLocked(@serIdInt int userId)1101         public void registerContentObserverLocked(@UserIdInt int userId) {
1102             if (mRegistered && mUserId == userId) {
1103                 return;
1104             }
1105             ContentResolver resolver = mContext.getContentResolver();
1106             if (mRegistered) {
1107                 mContext.getContentResolver().unregisterContentObserver(this);
1108                 mRegistered = false;
1109             }
1110             if (mUserId != userId) {
1111                 mLastEnabled = "";
1112                 mUserId = userId;
1113             }
1114             resolver.registerContentObserver(Settings.Secure.getUriFor(
1115                     Settings.Secure.DEFAULT_INPUT_METHOD), false, this, userId);
1116             resolver.registerContentObserver(Settings.Secure.getUriFor(
1117                     Settings.Secure.ENABLED_INPUT_METHODS), false, this, userId);
1118             resolver.registerContentObserver(Settings.Secure.getUriFor(
1119                     Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this, userId);
1120             resolver.registerContentObserver(Settings.Secure.getUriFor(
1121                     Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this, userId);
1122             resolver.registerContentObserver(Settings.Secure.getUriFor(
1123                     Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE), false, this, userId);
1124             mRegistered = true;
1125         }
1126 
onChange(boolean selfChange, Uri uri)1127         @Override public void onChange(boolean selfChange, Uri uri) {
1128             final Uri showImeUri = Settings.Secure.getUriFor(
1129                     Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
1130             final Uri accessibilityRequestingNoImeUri = Settings.Secure.getUriFor(
1131                     Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
1132             synchronized (mMethodMap) {
1133                 if (showImeUri.equals(uri)) {
1134                     updateKeyboardFromSettingsLocked();
1135                 } else if (accessibilityRequestingNoImeUri.equals(uri)) {
1136                     final int accessibilitySoftKeyboardSetting = Settings.Secure.getIntForUser(
1137                             mContext.getContentResolver(),
1138                             Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0, mUserId);
1139                     mAccessibilityRequestingNoSoftKeyboard =
1140                             (accessibilitySoftKeyboardSetting & AccessibilityService.SHOW_MODE_MASK)
1141                                     == AccessibilityService.SHOW_MODE_HIDDEN;
1142                     if (mAccessibilityRequestingNoSoftKeyboard) {
1143                         final boolean showRequested = mShowRequested;
1144                         hideCurrentInputLocked(mCurFocusedWindow, 0, null,
1145                                 SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE);
1146                         mShowRequested = showRequested;
1147                     } else if (mShowRequested) {
1148                         showCurrentInputLocked(mCurFocusedWindow,
1149                                 InputMethodManager.SHOW_IMPLICIT, null,
1150                                 SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE);
1151                     }
1152                 } else {
1153                     boolean enabledChanged = false;
1154                     String newEnabled = mSettings.getEnabledInputMethodsStr();
1155                     if (!mLastEnabled.equals(newEnabled)) {
1156                         mLastEnabled = newEnabled;
1157                         enabledChanged = true;
1158                     }
1159                     updateInputMethodsFromSettingsLocked(enabledChanged);
1160                 }
1161             }
1162         }
1163 
1164         @Override
toString()1165         public String toString() {
1166             return "SettingsObserver{mUserId=" + mUserId + " mRegistered=" + mRegistered
1167                     + " mLastEnabled=" + mLastEnabled + "}";
1168         }
1169     }
1170 
1171     /**
1172      * {@link BroadcastReceiver} that is intended to listen to broadcasts sent to the system user
1173      * only.
1174      */
1175     private final class ImmsBroadcastReceiverForSystemUser extends BroadcastReceiver {
1176         @Override
onReceive(Context context, Intent intent)1177         public void onReceive(Context context, Intent intent) {
1178             final String action = intent.getAction();
1179             if (Intent.ACTION_USER_ADDED.equals(action)
1180                     || Intent.ACTION_USER_REMOVED.equals(action)) {
1181                 updateCurrentProfileIds();
1182                 return;
1183             } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
1184                 onActionLocaleChanged();
1185             } else if (ACTION_SHOW_INPUT_METHOD_PICKER.equals(action)) {
1186                 // ACTION_SHOW_INPUT_METHOD_PICKER action is a protected-broadcast and it is
1187                 // guaranteed to be send only from the system, so that there is no need for extra
1188                 // security check such as
1189                 // {@link #canShowInputMethodPickerLocked(IInputMethodClient)}.
1190                 mHandler.obtainMessage(
1191                         MSG_SHOW_IM_SUBTYPE_PICKER,
1192                         // TODO(b/120076400): Design and implement IME switcher for heterogeneous
1193                         // navbar configuration.
1194                         InputMethodManager.SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES,
1195                         DEFAULT_DISPLAY).sendToTarget();
1196             } else {
1197                 Slog.w(TAG, "Unexpected intent " + intent);
1198             }
1199         }
1200     }
1201 
1202     /**
1203      * {@link BroadcastReceiver} that is intended to listen to broadcasts sent to all the users.
1204      */
1205     private final class ImmsBroadcastReceiverForAllUsers extends BroadcastReceiver {
1206         @Override
onReceive(Context context, Intent intent)1207         public void onReceive(Context context, Intent intent) {
1208             final String action = intent.getAction();
1209             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
1210                 final PendingResult pendingResult = getPendingResult();
1211                 if (pendingResult == null) {
1212                     return;
1213                 }
1214                 // sender userId can be a real user ID or USER_ALL.
1215                 final int senderUserId = pendingResult.getSendingUserId();
1216                 if (senderUserId != UserHandle.USER_ALL) {
1217                     if (senderUserId != mSettings.getCurrentUserId()) {
1218                         // A background user is trying to hide the dialog. Ignore.
1219                         return;
1220                     }
1221                 }
1222                 hideInputMethodMenu();
1223             } else {
1224                 Slog.w(TAG, "Unexpected intent " + intent);
1225             }
1226         }
1227     }
1228 
1229     /**
1230      * Handles {@link Intent#ACTION_LOCALE_CHANGED}.
1231      *
1232      * <p>Note: For historical reasons, {@link Intent#ACTION_LOCALE_CHANGED} has been sent to all
1233      * the users. We should ignore this event if this is about any background user's locale.</p>
1234      *
1235      * <p>Caution: This method must not be called when system is not ready.</p>
1236      */
onActionLocaleChanged()1237     void onActionLocaleChanged() {
1238         synchronized (mMethodMap) {
1239             final LocaleList possibleNewLocale = mRes.getConfiguration().getLocales();
1240             if (possibleNewLocale != null && possibleNewLocale.equals(mLastSystemLocales)) {
1241                 return;
1242             }
1243             buildInputMethodListLocked(true);
1244             // If the locale is changed, needs to reset the default ime
1245             resetDefaultImeLocked(mContext);
1246             updateFromSettingsLocked(true);
1247             mLastSystemLocales = possibleNewLocale;
1248         }
1249     }
1250 
1251     final class MyPackageMonitor extends PackageMonitor {
1252         /**
1253          * Package names that are known to contain {@link InputMethodService}.
1254          *
1255          * <p>No need to include packages because of direct-boot unaware IMEs since we always rescan
1256          * all the packages when the user is unlocked, and direct-boot awareness will not be changed
1257          * dynamically unless the entire package is updated, which also always triggers package
1258          * rescanning.</p>
1259          */
1260         @GuardedBy("mMethodMap")
1261         final private ArraySet<String> mKnownImePackageNames = new ArraySet<>();
1262 
1263         /**
1264          * Packages that are appeared, disappeared, or modified for whatever reason.
1265          *
1266          * <p>Note: For now we intentionally use {@link ArrayList} instead of {@link ArraySet}
1267          * because 1) the number of elements is almost always 1 or so, and 2) we do not care
1268          * duplicate elements for our use case.</p>
1269          *
1270          * <p>This object must be accessed only from callback methods in {@link PackageMonitor},
1271          * which should be bound to {@link #getRegisteredHandler()}.</p>
1272          */
1273         private final ArrayList<String> mChangedPackages = new ArrayList<>();
1274 
1275         /**
1276          * {@code true} if one or more packages that contain {@link InputMethodService} appeared.
1277          *
1278          * <p>This field must be accessed only from callback methods in {@link PackageMonitor},
1279          * which should be bound to {@link #getRegisteredHandler()}.</p>
1280          */
1281         private boolean mImePackageAppeared = false;
1282 
1283         @GuardedBy("mMethodMap")
clearKnownImePackageNamesLocked()1284         void clearKnownImePackageNamesLocked() {
1285             mKnownImePackageNames.clear();
1286         }
1287 
1288         @GuardedBy("mMethodMap")
addKnownImePackageNameLocked(@onNull String packageName)1289         final void addKnownImePackageNameLocked(@NonNull String packageName) {
1290             mKnownImePackageNames.add(packageName);
1291         }
1292 
1293         @GuardedBy("mMethodMap")
isChangingPackagesOfCurrentUserLocked()1294         private boolean isChangingPackagesOfCurrentUserLocked() {
1295             final int userId = getChangingUserId();
1296             final boolean retval = userId == mSettings.getCurrentUserId();
1297             if (DEBUG) {
1298                 if (!retval) {
1299                     Slog.d(TAG, "--- ignore this call back from a background user: " + userId);
1300                 }
1301             }
1302             return retval;
1303         }
1304 
1305         @Override
onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit)1306         public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
1307             synchronized (mMethodMap) {
1308                 if (!isChangingPackagesOfCurrentUserLocked()) {
1309                     return false;
1310                 }
1311                 String curInputMethodId = mSettings.getSelectedInputMethod();
1312                 final int N = mMethodList.size();
1313                 if (curInputMethodId != null) {
1314                     for (int i=0; i<N; i++) {
1315                         InputMethodInfo imi = mMethodList.get(i);
1316                         if (imi.getId().equals(curInputMethodId)) {
1317                             for (String pkg : packages) {
1318                                 if (imi.getPackageName().equals(pkg)) {
1319                                     if (!doit) {
1320                                         return true;
1321                                     }
1322                                     resetSelectedInputMethodAndSubtypeLocked("");
1323                                     chooseNewDefaultIMELocked();
1324                                     return true;
1325                                 }
1326                             }
1327                         }
1328                     }
1329                 }
1330             }
1331             return false;
1332         }
1333 
1334         @Override
onBeginPackageChanges()1335         public void onBeginPackageChanges() {
1336             clearPackageChangeState();
1337         }
1338 
1339         @Override
onPackageAppeared(String packageName, int reason)1340         public void onPackageAppeared(String packageName, int reason) {
1341             if (!mImePackageAppeared) {
1342                 final PackageManager pm = mContext.getPackageManager();
1343                 final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
1344                         new Intent(InputMethod.SERVICE_INTERFACE).setPackage(packageName),
1345                         PackageManager.MATCH_DISABLED_COMPONENTS, getChangingUserId());
1346                 // No need to lock this because we access it only on getRegisteredHandler().
1347                 if (!services.isEmpty()) {
1348                     mImePackageAppeared = true;
1349                 }
1350             }
1351             // No need to lock this because we access it only on getRegisteredHandler().
1352             mChangedPackages.add(packageName);
1353         }
1354 
1355         @Override
onPackageDisappeared(String packageName, int reason)1356         public void onPackageDisappeared(String packageName, int reason) {
1357             // No need to lock this because we access it only on getRegisteredHandler().
1358             mChangedPackages.add(packageName);
1359         }
1360 
1361         @Override
onPackageModified(String packageName)1362         public void onPackageModified(String packageName) {
1363             // No need to lock this because we access it only on getRegisteredHandler().
1364             mChangedPackages.add(packageName);
1365         }
1366 
1367         @Override
onPackagesSuspended(String[] packages)1368         public void onPackagesSuspended(String[] packages) {
1369             // No need to lock this because we access it only on getRegisteredHandler().
1370             for (String packageName : packages) {
1371                 mChangedPackages.add(packageName);
1372             }
1373         }
1374 
1375         @Override
onPackagesUnsuspended(String[] packages)1376         public void onPackagesUnsuspended(String[] packages) {
1377             // No need to lock this because we access it only on getRegisteredHandler().
1378             for (String packageName : packages) {
1379                 mChangedPackages.add(packageName);
1380             }
1381         }
1382 
1383         @Override
onFinishPackageChanges()1384         public void onFinishPackageChanges() {
1385             onFinishPackageChangesInternal();
1386             clearPackageChangeState();
1387         }
1388 
clearPackageChangeState()1389         private void clearPackageChangeState() {
1390             // No need to lock them because we access these fields only on getRegisteredHandler().
1391             mChangedPackages.clear();
1392             mImePackageAppeared = false;
1393         }
1394 
1395         @GuardedBy("mMethodMap")
shouldRebuildInputMethodListLocked()1396         private boolean shouldRebuildInputMethodListLocked() {
1397             // This method is guaranteed to be called only by getRegisteredHandler().
1398 
1399             // If there is any new package that contains at least one IME, then rebuilt the list
1400             // of IMEs.
1401             if (mImePackageAppeared) {
1402                 return true;
1403             }
1404 
1405             // Otherwise, check if mKnownImePackageNames and mChangedPackages have any intersection.
1406             // TODO: Consider to create a utility method to do the following test. List.retainAll()
1407             // is an option, but it may still do some extra operations that we do not need here.
1408             final int N = mChangedPackages.size();
1409             for (int i = 0; i < N; ++i) {
1410                 final String packageName = mChangedPackages.get(i);
1411                 if (mKnownImePackageNames.contains(packageName)) {
1412                     return true;
1413                 }
1414             }
1415             return false;
1416         }
1417 
onFinishPackageChangesInternal()1418         private void onFinishPackageChangesInternal() {
1419             synchronized (mMethodMap) {
1420                 if (!isChangingPackagesOfCurrentUserLocked()) {
1421                     return;
1422                 }
1423                 if (!shouldRebuildInputMethodListLocked()) {
1424                     return;
1425                 }
1426 
1427                 InputMethodInfo curIm = null;
1428                 String curInputMethodId = mSettings.getSelectedInputMethod();
1429                 final int N = mMethodList.size();
1430                 if (curInputMethodId != null) {
1431                     for (int i=0; i<N; i++) {
1432                         InputMethodInfo imi = mMethodList.get(i);
1433                         final String imiId = imi.getId();
1434                         if (imiId.equals(curInputMethodId)) {
1435                             curIm = imi;
1436                         }
1437 
1438                         int change = isPackageDisappearing(imi.getPackageName());
1439                         if (isPackageModified(imi.getPackageName())) {
1440                             mAdditionalSubtypeMap.remove(imi.getId());
1441                             AdditionalSubtypeUtils.save(mAdditionalSubtypeMap, mMethodMap,
1442                                     mSettings.getCurrentUserId());
1443                         }
1444                         if (change == PACKAGE_TEMPORARY_CHANGE
1445                                 || change == PACKAGE_PERMANENT_CHANGE) {
1446                             Slog.i(TAG, "Input method uninstalled, disabling: "
1447                                     + imi.getComponent());
1448                             setInputMethodEnabledLocked(imi.getId(), false);
1449                         }
1450                     }
1451                 }
1452 
1453                 buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
1454 
1455                 boolean changed = false;
1456 
1457                 if (curIm != null) {
1458                     int change = isPackageDisappearing(curIm.getPackageName());
1459                     if (change == PACKAGE_TEMPORARY_CHANGE
1460                             || change == PACKAGE_PERMANENT_CHANGE) {
1461                         ServiceInfo si = null;
1462                         try {
1463                             si = mIPackageManager.getServiceInfo(
1464                                     curIm.getComponent(), 0, mSettings.getCurrentUserId());
1465                         } catch (RemoteException ex) {
1466                         }
1467                         if (si == null) {
1468                             // Uh oh, current input method is no longer around!
1469                             // Pick another one...
1470                             Slog.i(TAG, "Current input method removed: " + curInputMethodId);
1471                             updateSystemUiLocked(0 /* vis */, mBackDisposition);
1472                             if (!chooseNewDefaultIMELocked()) {
1473                                 changed = true;
1474                                 curIm = null;
1475                                 Slog.i(TAG, "Unsetting current input method");
1476                                 resetSelectedInputMethodAndSubtypeLocked("");
1477                             }
1478                         }
1479                     }
1480                 }
1481 
1482                 if (curIm == null) {
1483                     // We currently don't have a default input method... is
1484                     // one now available?
1485                     changed = chooseNewDefaultIMELocked();
1486                 } else if (!changed && isPackageModified(curIm.getPackageName())) {
1487                     // Even if the current input method is still available, mCurrentSubtype could
1488                     // be obsolete when the package is modified in practice.
1489                     changed = true;
1490                 }
1491 
1492                 if (changed) {
1493                     updateFromSettingsLocked(false);
1494                 }
1495             }
1496         }
1497     }
1498 
1499     private static final class MethodCallback extends IInputSessionCallback.Stub {
1500         private final InputMethodManagerService mParentIMMS;
1501         private final IInputMethod mMethod;
1502         private final InputChannel mChannel;
1503 
MethodCallback(InputMethodManagerService imms, IInputMethod method, InputChannel channel)1504         MethodCallback(InputMethodManagerService imms, IInputMethod method,
1505                 InputChannel channel) {
1506             mParentIMMS = imms;
1507             mMethod = method;
1508             mChannel = channel;
1509         }
1510 
1511         @Override
sessionCreated(IInputMethodSession session)1512         public void sessionCreated(IInputMethodSession session) {
1513             long ident = Binder.clearCallingIdentity();
1514             try {
1515                 mParentIMMS.onSessionCreated(mMethod, session, mChannel);
1516             } finally {
1517                 Binder.restoreCallingIdentity(ident);
1518             }
1519         }
1520     }
1521 
1522     private class HardKeyboardListener
1523             implements WindowManagerInternal.OnHardKeyboardStatusChangeListener {
1524         @Override
onHardKeyboardStatusChange(boolean available)1525         public void onHardKeyboardStatusChange(boolean available) {
1526             mHandler.sendMessage(mHandler.obtainMessage(MSG_HARD_KEYBOARD_SWITCH_CHANGED,
1527                         available ? 1 : 0));
1528         }
1529 
handleHardKeyboardStatusChange(boolean available)1530         public void handleHardKeyboardStatusChange(boolean available) {
1531             if (DEBUG) {
1532                 Slog.w(TAG, "HardKeyboardStatusChanged: available=" + available);
1533             }
1534             synchronized(mMethodMap) {
1535                 if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
1536                         && mSwitchingDialog.isShowing()) {
1537                     mSwitchingDialogTitleView.findViewById(
1538                             com.android.internal.R.id.hard_keyboard_section).setVisibility(
1539                                     available ? View.VISIBLE : View.GONE);
1540                 }
1541             }
1542         }
1543     }
1544 
1545     private static final class UserSwitchHandlerTask implements Runnable {
1546         final InputMethodManagerService mService;
1547 
1548         @UserIdInt
1549         final int mToUserId;
1550 
1551         @Nullable
1552         IInputMethodClient mClientToBeReset;
1553 
UserSwitchHandlerTask(InputMethodManagerService service, @UserIdInt int toUserId, @Nullable IInputMethodClient clientToBeReset)1554         UserSwitchHandlerTask(InputMethodManagerService service, @UserIdInt int toUserId,
1555                 @Nullable IInputMethodClient clientToBeReset) {
1556             mService = service;
1557             mToUserId = toUserId;
1558             mClientToBeReset = clientToBeReset;
1559         }
1560 
1561         @Override
run()1562         public void run() {
1563             synchronized (mService.mMethodMap) {
1564                 if (mService.mUserSwitchHandlerTask != this) {
1565                     // This task was already canceled before it is handled here. So do nothing.
1566                     return;
1567                 }
1568                 mService.switchUserOnHandlerLocked(mService.mUserSwitchHandlerTask.mToUserId,
1569                         mClientToBeReset);
1570                 mService.mUserSwitchHandlerTask = null;
1571             }
1572         }
1573     }
1574 
1575     /**
1576      * When non-{@code null}, this represents pending user-switch task, which is to be executed as
1577      * a handler callback.  This needs to be set and unset only within the lock.
1578      */
1579     @Nullable
1580     @GuardedBy("mMethodMap")
1581     private UserSwitchHandlerTask mUserSwitchHandlerTask;
1582 
1583     public static final class Lifecycle extends SystemService {
1584         private InputMethodManagerService mService;
1585 
Lifecycle(Context context)1586         public Lifecycle(Context context) {
1587             super(context);
1588             mService = new InputMethodManagerService(context);
1589         }
1590 
1591         @Override
onStart()1592         public void onStart() {
1593             LocalServices.addService(InputMethodManagerInternal.class,
1594                     new LocalServiceImpl(mService));
1595             publishBinderService(Context.INPUT_METHOD_SERVICE, mService);
1596         }
1597 
1598         @Override
onSwitchUser(@serIdInt int userHandle)1599         public void onSwitchUser(@UserIdInt int userHandle) {
1600             // Called on ActivityManager thread.
1601             synchronized (mService.mMethodMap) {
1602                 mService.scheduleSwitchUserTaskLocked(userHandle, null /* clientToBeReset */);
1603             }
1604         }
1605 
1606         @Override
onBootPhase(int phase)1607         public void onBootPhase(int phase) {
1608             // Called on ActivityManager thread.
1609             // TODO: Dispatch this to a worker thread as needed.
1610             if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
1611                 StatusBarManagerService statusBarService = (StatusBarManagerService) ServiceManager
1612                         .getService(Context.STATUS_BAR_SERVICE);
1613                 mService.systemRunning(statusBarService);
1614             }
1615         }
1616 
1617         @Override
onUnlockUser(final @UserIdInt int userHandle)1618         public void onUnlockUser(final @UserIdInt int userHandle) {
1619             // Called on ActivityManager thread.
1620             mService.mHandler.sendMessage(mService.mHandler.obtainMessage(MSG_SYSTEM_UNLOCK_USER,
1621                     userHandle /* arg1 */, 0 /* arg2 */));
1622         }
1623     }
1624 
onUnlockUser(@serIdInt int userId)1625     void onUnlockUser(@UserIdInt int userId) {
1626         synchronized(mMethodMap) {
1627             final int currentUserId = mSettings.getCurrentUserId();
1628             if (DEBUG) {
1629                 Slog.d(TAG, "onUnlockUser: userId=" + userId + " curUserId=" + currentUserId);
1630             }
1631             if (userId != currentUserId) {
1632                 return;
1633             }
1634             mSettings.switchCurrentUser(currentUserId, !mSystemReady);
1635             if (mSystemReady) {
1636                 // We need to rebuild IMEs.
1637                 buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
1638                 updateInputMethodsFromSettingsLocked(true /* enabledChanged */);
1639             }
1640         }
1641     }
1642 
1643     @GuardedBy("mMethodMap")
scheduleSwitchUserTaskLocked(@serIdInt int userId, @Nullable IInputMethodClient clientToBeReset)1644     void scheduleSwitchUserTaskLocked(@UserIdInt int userId,
1645             @Nullable IInputMethodClient clientToBeReset) {
1646         if (mUserSwitchHandlerTask != null) {
1647             if (mUserSwitchHandlerTask.mToUserId == userId) {
1648                 mUserSwitchHandlerTask.mClientToBeReset = clientToBeReset;
1649                 return;
1650             }
1651             mHandler.removeCallbacks(mUserSwitchHandlerTask);
1652         }
1653         final UserSwitchHandlerTask task = new UserSwitchHandlerTask(this, userId,
1654                 clientToBeReset);
1655         mUserSwitchHandlerTask = task;
1656         mHandler.post(task);
1657     }
1658 
InputMethodManagerService(Context context)1659     public InputMethodManagerService(Context context) {
1660         mIPackageManager = AppGlobals.getPackageManager();
1661         mContext = context;
1662         mRes = context.getResources();
1663         mHandler = new Handler(this);
1664         // Note: SettingsObserver doesn't register observers in its constructor.
1665         mSettingsObserver = new SettingsObserver(mHandler);
1666         mIWindowManager = IWindowManager.Stub.asInterface(
1667                 ServiceManager.getService(Context.WINDOW_SERVICE));
1668         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
1669         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
1670         mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
1671         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
1672         mImeDisplayValidator = displayId -> mWindowManagerInternal.shouldShowIme(displayId);
1673         mCaller = new HandlerCaller(context, null, new HandlerCaller.Callback() {
1674             @Override
1675             public void executeMessage(Message msg) {
1676                 handleMessage(msg);
1677             }
1678         }, true /*asyncHandler*/);
1679         mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
1680         mUserManager = mContext.getSystemService(UserManager.class);
1681         mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
1682         mHardKeyboardListener = new HardKeyboardListener();
1683         mHasFeature = context.getPackageManager().hasSystemFeature(
1684                 PackageManager.FEATURE_INPUT_METHODS);
1685         mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);
1686         mIsLowRam = ActivityManager.isLowRamDeviceStatic();
1687 
1688         Bundle extras = new Bundle();
1689         extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true);
1690         @ColorInt final int accentColor = mContext.getColor(
1691                 com.android.internal.R.color.system_notification_accent_color);
1692         mImeSwitcherNotification =
1693                 new Notification.Builder(mContext, SystemNotificationChannels.VIRTUAL_KEYBOARD)
1694                         .setSmallIcon(com.android.internal.R.drawable.ic_notification_ime_default)
1695                         .setWhen(0)
1696                         .setOngoing(true)
1697                         .addExtras(extras)
1698                         .setCategory(Notification.CATEGORY_SYSTEM)
1699                         .setColor(accentColor);
1700 
1701         Intent intent = new Intent(ACTION_SHOW_INPUT_METHOD_PICKER)
1702                 .setPackage(mContext.getPackageName());
1703         mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
1704 
1705         mShowOngoingImeSwitcherForPhones = false;
1706 
1707         mNotificationShown = false;
1708         int userId = 0;
1709         try {
1710             userId = ActivityManager.getService().getCurrentUser().id;
1711         } catch (RemoteException e) {
1712             Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);
1713         }
1714 
1715         mLastSwitchUserId = userId;
1716 
1717         // mSettings should be created before buildInputMethodListLocked
1718         mSettings = new InputMethodSettings(
1719                 mRes, context.getContentResolver(), mMethodMap, userId, !mSystemReady);
1720 
1721         updateCurrentProfileIds();
1722         AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId);
1723         mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
1724                 mSettings, context);
1725     }
1726 
resetDefaultImeLocked(Context context)1727     private void resetDefaultImeLocked(Context context) {
1728         // Do not reset the default (current) IME when it is a 3rd-party IME
1729         if (mCurMethodId != null && !mMethodMap.get(mCurMethodId).isSystem()) {
1730             return;
1731         }
1732         final List<InputMethodInfo> suitableImes = InputMethodUtils.getDefaultEnabledImes(
1733                 context, mSettings.getEnabledInputMethodListLocked());
1734         if (suitableImes.isEmpty()) {
1735             Slog.i(TAG, "No default found");
1736             return;
1737         }
1738         final InputMethodInfo defIm = suitableImes.get(0);
1739         if (DEBUG) {
1740             Slog.i(TAG, "Default found, using " + defIm.getId());
1741         }
1742         setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false);
1743     }
1744 
1745     @GuardedBy("mMethodMap")
switchUserOnHandlerLocked(@serIdInt int newUserId, IInputMethodClient clientToBeReset)1746     private void switchUserOnHandlerLocked(@UserIdInt int newUserId,
1747             IInputMethodClient clientToBeReset) {
1748         if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
1749                 + " currentUserId=" + mSettings.getCurrentUserId());
1750 
1751         // ContentObserver should be registered again when the user is changed
1752         mSettingsObserver.registerContentObserverLocked(newUserId);
1753 
1754         // If the system is not ready or the device is not yed unlocked by the user, then we use
1755         // copy-on-write settings.
1756         final boolean useCopyOnWriteSettings =
1757                 !mSystemReady || !mUserManagerInternal.isUserUnlockingOrUnlocked(newUserId);
1758         mSettings.switchCurrentUser(newUserId, useCopyOnWriteSettings);
1759         updateCurrentProfileIds();
1760         // Additional subtypes should be reset when the user is changed
1761         AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, newUserId);
1762         final String defaultImiId = mSettings.getSelectedInputMethod();
1763 
1764         if (DEBUG) Slog.d(TAG, "Switching user stage 2/3. newUserId=" + newUserId
1765                 + " defaultImiId=" + defaultImiId);
1766 
1767         // For secondary users, the list of enabled IMEs may not have been updated since the
1768         // callbacks to PackageMonitor are ignored for the secondary user. Here, defaultImiId may
1769         // not be empty even if the IME has been uninstalled by the primary user.
1770         // Even in such cases, IMMS works fine because it will find the most applicable
1771         // IME for that user.
1772         final boolean initialUserSwitch = TextUtils.isEmpty(defaultImiId);
1773         mLastSystemLocales = mRes.getConfiguration().getLocales();
1774 
1775         // TODO: Is it really possible that switchUserLocked() happens before system ready?
1776         if (mSystemReady) {
1777             hideCurrentInputLocked(
1778                     mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_SWITCH_USER);
1779 
1780             resetCurrentMethodAndClient(UnbindReason.SWITCH_USER);
1781             buildInputMethodListLocked(initialUserSwitch);
1782             if (TextUtils.isEmpty(mSettings.getSelectedInputMethod())) {
1783                 // This is the first time of the user switch and
1784                 // set the current ime to the proper one.
1785                 resetDefaultImeLocked(mContext);
1786             }
1787             updateFromSettingsLocked(true);
1788         }
1789 
1790         if (initialUserSwitch) {
1791             InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
1792                     mSettings.getEnabledInputMethodListLocked(), newUserId,
1793                     mContext.getBasePackageName());
1794         }
1795 
1796         if (DEBUG) Slog.d(TAG, "Switching user stage 3/3. newUserId=" + newUserId
1797                 + " selectedIme=" + mSettings.getSelectedInputMethod());
1798 
1799         mLastSwitchUserId = newUserId;
1800 
1801         if (mIsInteractive && clientToBeReset != null) {
1802             final ClientState cs = mClients.get(clientToBeReset.asBinder());
1803             if (cs == null) {
1804                 // The client is already gone.
1805                 return;
1806             }
1807             try {
1808                 cs.client.scheduleStartInputIfNecessary(mInFullscreenMode);
1809             } catch (RemoteException e) {
1810             }
1811         }
1812     }
1813 
updateCurrentProfileIds()1814     void updateCurrentProfileIds() {
1815         mSettings.setCurrentProfileIds(
1816                 mUserManager.getProfileIdsWithDisabled(mSettings.getCurrentUserId()));
1817     }
1818 
1819     @Override
onTransact(int code, Parcel data, Parcel reply, int flags)1820     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
1821             throws RemoteException {
1822         try {
1823             return super.onTransact(code, data, reply, flags);
1824         } catch (RuntimeException e) {
1825             // The input method manager only throws security exceptions, so let's
1826             // log all others.
1827             if (!(e instanceof SecurityException)) {
1828                 Slog.wtf(TAG, "Input Method Manager Crash", e);
1829             }
1830             throw e;
1831         }
1832     }
1833 
systemRunning(StatusBarManagerService statusBar)1834     public void systemRunning(StatusBarManagerService statusBar) {
1835         synchronized (mMethodMap) {
1836             if (DEBUG) {
1837                 Slog.d(TAG, "--- systemReady");
1838             }
1839             if (!mSystemReady) {
1840                 mSystemReady = true;
1841                 mLastSystemLocales = mRes.getConfiguration().getLocales();
1842                 final int currentUserId = mSettings.getCurrentUserId();
1843                 mSettings.switchCurrentUser(currentUserId,
1844                         !mUserManagerInternal.isUserUnlockingOrUnlocked(currentUserId));
1845                 mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
1846                 mNotificationManager = mContext.getSystemService(NotificationManager.class);
1847                 mStatusBar = statusBar;
1848                 if (mStatusBar != null) {
1849                     mStatusBar.setIconVisibility(mSlotIme, false);
1850                 }
1851                 updateSystemUiLocked(mImeWindowVis, mBackDisposition);
1852                 mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
1853                         com.android.internal.R.bool.show_ongoing_ime_switcher);
1854                 if (mShowOngoingImeSwitcherForPhones) {
1855                     mWindowManagerInternal.setOnHardKeyboardStatusChangeListener(
1856                             mHardKeyboardListener);
1857                 }
1858 
1859                 mMyPackageMonitor.register(mContext, null, UserHandle.ALL, true);
1860                 mSettingsObserver.registerContentObserverLocked(currentUserId);
1861 
1862                 final IntentFilter broadcastFilterForSystemUser = new IntentFilter();
1863                 broadcastFilterForSystemUser.addAction(Intent.ACTION_USER_ADDED);
1864                 broadcastFilterForSystemUser.addAction(Intent.ACTION_USER_REMOVED);
1865                 broadcastFilterForSystemUser.addAction(Intent.ACTION_LOCALE_CHANGED);
1866                 broadcastFilterForSystemUser.addAction(ACTION_SHOW_INPUT_METHOD_PICKER);
1867                 mContext.registerReceiver(new ImmsBroadcastReceiverForSystemUser(),
1868                         broadcastFilterForSystemUser);
1869 
1870                 final IntentFilter broadcastFilterForAllUsers = new IntentFilter();
1871                 broadcastFilterForAllUsers.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
1872                 mContext.registerReceiverAsUser(new ImmsBroadcastReceiverForAllUsers(),
1873                         UserHandle.ALL, broadcastFilterForAllUsers, null, null);
1874 
1875                 final String defaultImiId = mSettings.getSelectedInputMethod();
1876                 final boolean imeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
1877                 buildInputMethodListLocked(!imeSelectedOnBoot /* resetDefaultEnabledIme */);
1878                 updateFromSettingsLocked(true);
1879                 InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
1880                         mSettings.getEnabledInputMethodListLocked(), currentUserId,
1881                         mContext.getBasePackageName());
1882             }
1883         }
1884     }
1885 
1886     // ---------------------------------------------------------------------------------------
1887     // Check whether or not this is a valid IPC. Assumes an IPC is valid when either
1888     // 1) it comes from the system process
1889     // 2) the calling process' user id is identical to the current user id IMMS thinks.
1890     @GuardedBy("mMethodMap")
calledFromValidUserLocked()1891     private boolean calledFromValidUserLocked() {
1892         final int uid = Binder.getCallingUid();
1893         final int userId = UserHandle.getUserId(uid);
1894         if (DEBUG) {
1895             Slog.d(TAG, "--- calledFromForegroundUserOrSystemProcess ? "
1896                     + "calling uid = " + uid + " system uid = " + Process.SYSTEM_UID
1897                     + " calling userId = " + userId + ", foreground user id = "
1898                     + mSettings.getCurrentUserId() + ", calling pid = " + Binder.getCallingPid()
1899                     + InputMethodUtils.getApiCallStack());
1900         }
1901         if (uid == Process.SYSTEM_UID) {
1902             return true;
1903         }
1904         if (userId == mSettings.getCurrentUserId()) {
1905             return true;
1906         }
1907 
1908         // Caveat: A process which has INTERACT_ACROSS_USERS_FULL gets results for the
1909         // foreground user, not for the user of that process. Accordingly InputMethodManagerService
1910         // must not manage background users' states in any functions.
1911         // Note that privacy-sensitive IPCs, such as setInputMethod, are still securely guarded
1912         // by a token.
1913         if (mContext.checkCallingOrSelfPermission(
1914                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
1915                         == PackageManager.PERMISSION_GRANTED) {
1916             if (DEBUG) {
1917                 Slog.d(TAG, "--- Access granted because the calling process has "
1918                         + "the INTERACT_ACROSS_USERS_FULL permission");
1919             }
1920             return true;
1921         }
1922         // TODO(b/34886274): The semantics of this verification is actually not well-defined.
1923         Slog.w(TAG, "--- IPC called from background users. Ignore. callers="
1924                 + Debug.getCallers(10));
1925         return false;
1926     }
1927 
1928 
1929     /**
1930      * Returns true iff the caller is identified to be the current input method with the token.
1931      * @param token The window token given to the input method when it was started.
1932      * @return true if and only if non-null valid token is specified.
1933      */
1934     @GuardedBy("mMethodMap")
calledWithValidTokenLocked(@onNull IBinder token)1935     private boolean calledWithValidTokenLocked(@NonNull IBinder token) {
1936         if (token == null) {
1937             throw new InvalidParameterException("token must not be null.");
1938         }
1939         if (token != mCurToken) {
1940             Slog.e(TAG, "Ignoring " + Debug.getCaller() + " due to an invalid token."
1941                     + " uid:" + Binder.getCallingUid() + " token:" + token);
1942             return false;
1943         }
1944         return true;
1945     }
1946 
1947     @GuardedBy("mMethodMap")
bindCurrentInputMethodServiceLocked( Intent service, ServiceConnection conn, int flags)1948     private boolean bindCurrentInputMethodServiceLocked(
1949             Intent service, ServiceConnection conn, int flags) {
1950         if (service == null || conn == null) {
1951             Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn);
1952             return false;
1953         }
1954         return mContext.bindServiceAsUser(service, conn, flags,
1955                 new UserHandle(mSettings.getCurrentUserId()));
1956     }
1957 
1958     @Override
getInputMethodList(@serIdInt int userId)1959     public List<InputMethodInfo> getInputMethodList(@UserIdInt int userId) {
1960         if (UserHandle.getCallingUserId() != userId) {
1961             mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
1962         }
1963         synchronized (mMethodMap) {
1964             final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
1965                     mSettings.getCurrentUserId(), null);
1966             if (resolvedUserIds.length != 1) {
1967                 return Collections.emptyList();
1968             }
1969             final long ident = Binder.clearCallingIdentity();
1970             try {
1971                 return getInputMethodListLocked(resolvedUserIds[0]);
1972             } finally {
1973                 Binder.restoreCallingIdentity(ident);
1974             }
1975         }
1976     }
1977 
1978     @Override
getEnabledInputMethodList(@serIdInt int userId)1979     public List<InputMethodInfo> getEnabledInputMethodList(@UserIdInt int userId) {
1980         if (UserHandle.getCallingUserId() != userId) {
1981             mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
1982         }
1983         synchronized (mMethodMap) {
1984             final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
1985                     mSettings.getCurrentUserId(), null);
1986             if (resolvedUserIds.length != 1) {
1987                 return Collections.emptyList();
1988             }
1989             final long ident = Binder.clearCallingIdentity();
1990             try {
1991                 return getEnabledInputMethodListLocked(resolvedUserIds[0]);
1992             } finally {
1993                 Binder.restoreCallingIdentity(ident);
1994             }
1995         }
1996     }
1997 
1998     @GuardedBy("mMethodMap")
getInputMethodListLocked(@serIdInt int userId)1999     private List<InputMethodInfo> getInputMethodListLocked(@UserIdInt int userId) {
2000         final ArrayList<InputMethodInfo> methodList;
2001         if (userId == mSettings.getCurrentUserId()) {
2002             // Create a copy.
2003             methodList = new ArrayList<>(mMethodList);
2004         } else {
2005             final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
2006             methodList = new ArrayList<>();
2007             final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
2008                     new ArrayMap<>();
2009             AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
2010             queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
2011                     methodList);
2012         }
2013         return methodList;
2014     }
2015 
2016     @GuardedBy("mMethodMap")
getEnabledInputMethodListLocked(@serIdInt int userId)2017     private List<InputMethodInfo> getEnabledInputMethodListLocked(@UserIdInt int userId) {
2018         if (userId == mSettings.getCurrentUserId()) {
2019             return mSettings.getEnabledInputMethodListLocked();
2020         }
2021         final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
2022         final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
2023         final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
2024                 new ArrayMap<>();
2025         AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
2026         queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
2027                 methodList);
2028         final InputMethodSettings settings = new InputMethodSettings(mContext.getResources(),
2029                 mContext.getContentResolver(), methodMap, userId, true);
2030         return settings.getEnabledInputMethodListLocked();
2031     }
2032 
2033     @GuardedBy("mMethodMap")
onCreateInlineSuggestionsRequestLocked(@serIdInt int userId, InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback)2034     private void onCreateInlineSuggestionsRequestLocked(@UserIdInt int userId,
2035             InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback) {
2036         final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
2037         try {
2038             if (userId == mSettings.getCurrentUserId() && imi != null
2039                     && imi.isInlineSuggestionsEnabled() && mCurMethod != null) {
2040                 executeOrSendMessage(mCurMethod,
2041                         mCaller.obtainMessageOOO(MSG_INLINE_SUGGESTIONS_REQUEST, mCurMethod,
2042                                 requestInfo, new InlineSuggestionsRequestCallbackDecorator(callback,
2043                                         imi.getPackageName(), mCurTokenDisplayId, mCurToken,
2044                                         this)));
2045             } else {
2046                 callback.onInlineSuggestionsUnsupported();
2047             }
2048         } catch (RemoteException e) {
2049             Slog.w(TAG, "RemoteException calling onCreateInlineSuggestionsRequest(): " + e);
2050         }
2051     }
2052 
2053     /**
2054      * The decorator which validates the host package name in the
2055      * {@link InlineSuggestionsRequest} argument to make sure it matches the IME package name.
2056      */
2057     private static final class InlineSuggestionsRequestCallbackDecorator
2058             extends IInlineSuggestionsRequestCallback.Stub {
2059         @NonNull
2060         private final IInlineSuggestionsRequestCallback mCallback;
2061         @NonNull
2062         private final String mImePackageName;
2063 
2064         private final int mImeDisplayId;
2065 
2066         @NonNull
2067         private final IBinder mImeToken;
2068         @NonNull
2069         private final InputMethodManagerService mImms;
2070 
InlineSuggestionsRequestCallbackDecorator( @onNull IInlineSuggestionsRequestCallback callback, @NonNull String imePackageName, int displayId, @NonNull IBinder imeToken, @NonNull InputMethodManagerService imms)2071         InlineSuggestionsRequestCallbackDecorator(
2072                 @NonNull IInlineSuggestionsRequestCallback callback, @NonNull String imePackageName,
2073                 int displayId, @NonNull IBinder imeToken, @NonNull InputMethodManagerService imms) {
2074             mCallback = callback;
2075             mImePackageName = imePackageName;
2076             mImeDisplayId = displayId;
2077             mImeToken = imeToken;
2078             mImms = imms;
2079         }
2080 
2081         @Override
onInlineSuggestionsUnsupported()2082         public void onInlineSuggestionsUnsupported() throws RemoteException {
2083             mCallback.onInlineSuggestionsUnsupported();
2084         }
2085 
2086         @Override
onInlineSuggestionsRequest(InlineSuggestionsRequest request, IInlineSuggestionsResponseCallback callback)2087         public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
2088                 IInlineSuggestionsResponseCallback callback)
2089                 throws RemoteException {
2090             if (!mImePackageName.equals(request.getHostPackageName())) {
2091                 throw new SecurityException(
2092                         "Host package name in the provide request=[" + request.getHostPackageName()
2093                                 + "] doesn't match the IME package name=[" + mImePackageName
2094                                 + "].");
2095             }
2096             request.setHostDisplayId(mImeDisplayId);
2097             mImms.setCurHostInputToken(mImeToken, request.getHostInputToken());
2098             mCallback.onInlineSuggestionsRequest(request, callback);
2099         }
2100 
2101         @Override
onInputMethodStartInput(AutofillId imeFieldId)2102         public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException {
2103             mCallback.onInputMethodStartInput(imeFieldId);
2104         }
2105 
2106         @Override
onInputMethodShowInputRequested(boolean requestResult)2107         public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException {
2108             mCallback.onInputMethodShowInputRequested(requestResult);
2109         }
2110 
2111         @Override
onInputMethodStartInputView()2112         public void onInputMethodStartInputView() throws RemoteException {
2113             mCallback.onInputMethodStartInputView();
2114         }
2115 
2116         @Override
onInputMethodFinishInputView()2117         public void onInputMethodFinishInputView() throws RemoteException {
2118             mCallback.onInputMethodFinishInputView();
2119         }
2120 
2121         @Override
onInputMethodFinishInput()2122         public void onInputMethodFinishInput() throws RemoteException {
2123             mCallback.onInputMethodFinishInput();
2124         }
2125 
2126         @Override
onInlineSuggestionsSessionInvalidated()2127         public void onInlineSuggestionsSessionInvalidated() throws RemoteException {
2128             mCallback.onInlineSuggestionsSessionInvalidated();
2129         }
2130     }
2131 
2132     /**
2133      * Sets current host input token.
2134      *
2135      * @param callerImeToken the token has been made for the current active input method
2136      * @param hostInputToken the host input token of the current active input method
2137      */
setCurHostInputToken(@onNull IBinder callerImeToken, @Nullable IBinder hostInputToken)2138     void setCurHostInputToken(@NonNull IBinder callerImeToken, @Nullable IBinder hostInputToken) {
2139         synchronized (mMethodMap) {
2140             if (!calledWithValidTokenLocked(callerImeToken)) {
2141                 return;
2142             }
2143             mCurHostInputToken = hostInputToken;
2144         }
2145     }
2146 
2147     /**
2148      * @param imiId if null, returns enabled subtypes for the current imi
2149      * @return enabled subtypes of the specified imi
2150      */
2151     @Override
getEnabledInputMethodSubtypeList(String imiId, boolean allowsImplicitlySelectedSubtypes)2152     public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
2153             boolean allowsImplicitlySelectedSubtypes) {
2154         final int callingUserId = UserHandle.getCallingUserId();
2155         synchronized (mMethodMap) {
2156             final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
2157                     mSettings.getCurrentUserId(), null);
2158             if (resolvedUserIds.length != 1) {
2159                 return Collections.emptyList();
2160             }
2161             final long ident = Binder.clearCallingIdentity();
2162             try {
2163                 return getEnabledInputMethodSubtypeListLocked(imiId,
2164                         allowsImplicitlySelectedSubtypes, resolvedUserIds[0]);
2165             } finally {
2166                 Binder.restoreCallingIdentity(ident);
2167             }
2168         }
2169     }
2170 
2171     @GuardedBy("mMethodMap")
getEnabledInputMethodSubtypeListLocked(String imiId, boolean allowsImplicitlySelectedSubtypes, @UserIdInt int userId)2172     private List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(String imiId,
2173             boolean allowsImplicitlySelectedSubtypes, @UserIdInt int userId) {
2174         if (userId == mSettings.getCurrentUserId()) {
2175             final InputMethodInfo imi;
2176             if (imiId == null && mCurMethodId != null) {
2177                 imi = mMethodMap.get(mCurMethodId);
2178             } else {
2179                 imi = mMethodMap.get(imiId);
2180             }
2181             if (imi == null) {
2182                 return Collections.emptyList();
2183             }
2184             return mSettings.getEnabledInputMethodSubtypeListLocked(
2185                     mContext, imi, allowsImplicitlySelectedSubtypes);
2186         }
2187         final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
2188         final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
2189         final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
2190                 new ArrayMap<>();
2191         AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
2192         queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
2193                 methodList);
2194         final InputMethodInfo imi = methodMap.get(imiId);
2195         if (imi == null) {
2196             return Collections.emptyList();
2197         }
2198         final InputMethodSettings settings = new InputMethodSettings(mContext.getResources(),
2199                 mContext.getContentResolver(), methodMap, userId, true);
2200         return settings.getEnabledInputMethodSubtypeListLocked(
2201                 mContext, imi, allowsImplicitlySelectedSubtypes);
2202     }
2203 
2204     /**
2205      * Called by each application process as a preparation to start interacting with
2206      * {@link InputMethodManagerService}.
2207      *
2208      * <p>As a general principle, IPCs from the application process that take
2209      * {@link IInputMethodClient} will be rejected without this step.</p>
2210      *
2211      * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
2212      *               of {@link android.view.inputmethod.InputMethodManager} that runs on the client
2213      *               process
2214      * @param inputContext communication channel for the dummy
2215      *                     {@link android.view.inputmethod.InputConnection}
2216      * @param selfReportedDisplayId self-reported display ID to which the client is associated.
2217      *                              Whether the client is still allowed to access to this display
2218      *                              or not needs to be evaluated every time the client interacts
2219      *                              with the display
2220      */
2221     @Override
addClient(IInputMethodClient client, IInputContext inputContext, int selfReportedDisplayId)2222     public void addClient(IInputMethodClient client, IInputContext inputContext,
2223             int selfReportedDisplayId) {
2224         // Here there are two scenarios where this method is called:
2225         // A. IMM is being instantiated in a different process and this is an IPC from that process
2226         // B. IMM is being instantiated in the same process but Binder.clearCallingIdentity() is
2227         //    called in the caller side if necessary.
2228         // In either case the following UID/PID should be the ones where InputMethodManager is
2229         // actually running.
2230         final int callerUid = Binder.getCallingUid();
2231         final int callerPid = Binder.getCallingPid();
2232         synchronized (mMethodMap) {
2233             // TODO: Optimize this linear search.
2234             final int numClients = mClients.size();
2235             for (int i = 0; i < numClients; ++i) {
2236                 final ClientState state = mClients.valueAt(i);
2237                 if (state.uid == callerUid && state.pid == callerPid
2238                         && state.selfReportedDisplayId == selfReportedDisplayId) {
2239                     throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid
2240                             + "/displayId=" + selfReportedDisplayId + " is already registered.");
2241                 }
2242             }
2243             final ClientDeathRecipient deathRecipient = new ClientDeathRecipient(this, client);
2244             try {
2245                 client.asBinder().linkToDeath(deathRecipient, 0);
2246             } catch (RemoteException e) {
2247                 throw new IllegalStateException(e);
2248             }
2249             // We cannot fully avoid race conditions where the client UID already lost the access to
2250             // the given self-reported display ID, even if the client is not maliciously reporting
2251             // a fake display ID. Unconditionally returning SecurityException just because the
2252             // client doesn't pass display ID verification can cause many test failures hence not an
2253             // option right now.  At the same time
2254             //    context.getSystemService(InputMethodManager.class)
2255             // is expected to return a valid non-null instance at any time if we do not choose to
2256             // have the client crash.  Thus we do not verify the display ID at all here.  Instead we
2257             // later check the display ID every time the client needs to interact with the specified
2258             // display.
2259             mClients.put(client.asBinder(), new ClientState(client, inputContext, callerUid,
2260                     callerPid, selfReportedDisplayId, deathRecipient));
2261         }
2262     }
2263 
removeClient(IInputMethodClient client)2264     void removeClient(IInputMethodClient client) {
2265         synchronized (mMethodMap) {
2266             ClientState cs = mClients.remove(client.asBinder());
2267             if (cs != null) {
2268                 client.asBinder().unlinkToDeath(cs.clientDeathRecipient, 0);
2269                 clearClientSessionLocked(cs);
2270 
2271                 final int numItems = mActivityViewDisplayIdToParentMap.size();
2272                 for (int i = numItems - 1; i >= 0; --i) {
2273                     final ActivityViewInfo info = mActivityViewDisplayIdToParentMap.valueAt(i);
2274                     if (info.mParentClient == cs) {
2275                         mActivityViewDisplayIdToParentMap.removeAt(i);
2276                     }
2277                 }
2278 
2279                 if (mCurClient == cs) {
2280                     if (mBoundToMethod) {
2281                         mBoundToMethod = false;
2282                         if (mCurMethod != null) {
2283                             executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
2284                                     MSG_UNBIND_INPUT, mCurMethod));
2285                         }
2286                     }
2287                     mCurClient = null;
2288                     mCurActivityViewToScreenMatrix = null;
2289                 }
2290                 if (mCurFocusedWindowClient == cs) {
2291                     mCurFocusedWindowClient = null;
2292                 }
2293             }
2294         }
2295     }
2296 
executeOrSendMessage(IInterface target, Message msg)2297     void executeOrSendMessage(IInterface target, Message msg) {
2298          if (target.asBinder() instanceof Binder) {
2299              mCaller.sendMessage(msg);
2300          } else {
2301              handleMessage(msg);
2302              msg.recycle();
2303          }
2304     }
2305 
unbindCurrentClientLocked(@nbindReason int unbindClientReason)2306     void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {
2307         if (mCurClient != null) {
2308             if (DEBUG) Slog.v(TAG, "unbindCurrentInputLocked: client="
2309                     + mCurClient.client.asBinder());
2310             if (mBoundToMethod) {
2311                 mBoundToMethod = false;
2312                 if (mCurMethod != null) {
2313                     executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
2314                             MSG_UNBIND_INPUT, mCurMethod));
2315                 }
2316             }
2317 
2318             executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
2319                     MSG_SET_ACTIVE, 0, 0, mCurClient));
2320             executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
2321                     MSG_UNBIND_CLIENT, mCurSeq, unbindClientReason, mCurClient.client));
2322             mCurClient.sessionRequested = false;
2323             mCurClient = null;
2324             mCurActivityViewToScreenMatrix = null;
2325 
2326             hideInputMethodMenuLocked();
2327         }
2328     }
2329 
getImeShowFlags()2330     private int getImeShowFlags() {
2331         int flags = 0;
2332         if (mShowForced) {
2333             flags |= InputMethod.SHOW_FORCED
2334                     | InputMethod.SHOW_EXPLICIT;
2335         } else if (mShowExplicitlyRequested) {
2336             flags |= InputMethod.SHOW_EXPLICIT;
2337         }
2338         return flags;
2339     }
2340 
getAppShowFlags()2341     private int getAppShowFlags() {
2342         int flags = 0;
2343         if (mShowForced) {
2344             flags |= InputMethodManager.SHOW_FORCED;
2345         } else if (!mShowExplicitlyRequested) {
2346             flags |= InputMethodManager.SHOW_IMPLICIT;
2347         }
2348         return flags;
2349     }
2350 
2351     @GuardedBy("mMethodMap")
2352     @NonNull
attachNewInputLocked(@tartInputReason int startInputReason, boolean initial)2353     InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
2354         if (!mBoundToMethod) {
2355             executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
2356                     MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
2357             mBoundToMethod = true;
2358         }
2359 
2360         final Binder startInputToken = new Binder();
2361         final StartInputInfo info = new StartInputInfo(mSettings.getCurrentUserId(), mCurToken,
2362                 mCurTokenDisplayId, mCurId, startInputReason, !initial,
2363                 UserHandle.getUserId(mCurClient.uid), mCurClient.selfReportedDisplayId,
2364                 mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode, mCurSeq);
2365         mImeTargetWindowMap.put(startInputToken, mCurFocusedWindow);
2366         mStartInputHistory.addEntry(info);
2367 
2368         // Seems that PackageManagerInternal#grantImplicitAccess() doesn't handle cross-user
2369         // implicit visibility (e.g. IME[user=10] -> App[user=0]) thus we do this only for the
2370         // same-user scenarios.
2371         // That said ignoring cross-user scenario will never affect IMEs that do not have
2372         // INTERACT_ACROSS_USERS(_FULL) permissions, which is actually almost always the case.
2373         if (mSettings.getCurrentUserId() == UserHandle.getUserId(mCurClient.uid)) {
2374             mPackageManagerInternal.grantImplicitAccess(mSettings.getCurrentUserId(),
2375                     null /* intent */, UserHandle.getAppId(mCurMethodUid), mCurClient.uid, true);
2376         }
2377 
2378         final SessionState session = mCurClient.curSession;
2379         executeOrSendMessage(session.method, mCaller.obtainMessageIIOOOO(
2380                 MSG_START_INPUT, mCurInputContextMissingMethods, initial ? 0 : 1 /* restarting */,
2381                 startInputToken, session, mCurInputContext, mCurAttribute));
2382         if (mShowRequested) {
2383             if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
2384             showCurrentInputLocked(mCurFocusedWindow, getAppShowFlags(), null,
2385                     SoftInputShowHideReason.ATTACH_NEW_INPUT);
2386         }
2387         return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
2388                 session.session, (session.channel != null ? session.channel.dup() : null),
2389                 mCurId, mCurSeq, mCurActivityViewToScreenMatrix);
2390     }
2391 
2392     @Nullable
getActivityViewToScreenMatrixLocked(int clientDisplayId, int imeDisplayId)2393     private Matrix getActivityViewToScreenMatrixLocked(int clientDisplayId, int imeDisplayId) {
2394         if (clientDisplayId == imeDisplayId) {
2395             return null;
2396         }
2397         int displayId = clientDisplayId;
2398         Matrix matrix = null;
2399         while (true) {
2400             final ActivityViewInfo info = mActivityViewDisplayIdToParentMap.get(displayId);
2401             if (info == null) {
2402                 return null;
2403             }
2404             if (matrix == null) {
2405                 matrix = new Matrix(info.mMatrix);
2406             } else {
2407                 matrix.postConcat(info.mMatrix);
2408             }
2409             if (info.mParentClient.selfReportedDisplayId == imeDisplayId) {
2410                 return matrix;
2411             }
2412             displayId = info.mParentClient.selfReportedDisplayId;
2413         }
2414     }
2415 
2416     @GuardedBy("mMethodMap")
2417     @NonNull
startInputUncheckedLocked(@onNull ClientState cs, IInputContext inputContext, @MissingMethodFlags int missingMethods, @NonNull EditorInfo attribute, @StartInputFlags int startInputFlags, @StartInputReason int startInputReason)2418     InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
2419             @MissingMethodFlags int missingMethods, @NonNull EditorInfo attribute,
2420             @StartInputFlags int startInputFlags, @StartInputReason int startInputReason) {
2421         // If no method is currently selected, do nothing.
2422         if (mCurMethodId == null) {
2423             return InputBindResult.NO_IME;
2424         }
2425 
2426         if (!mSystemReady) {
2427             // If the system is not yet ready, we shouldn't be running third
2428             // party code.
2429             return new InputBindResult(
2430                     InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
2431                     null, null, mCurMethodId, mCurSeq, null);
2432         }
2433 
2434         if (!InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, cs.uid,
2435                 attribute.packageName)) {
2436             Slog.e(TAG, "Rejecting this client as it reported an invalid package name."
2437                     + " uid=" + cs.uid + " package=" + attribute.packageName);
2438             return InputBindResult.INVALID_PACKAGE_NAME;
2439         }
2440 
2441         if (!mWindowManagerInternal.isUidAllowedOnDisplay(cs.selfReportedDisplayId, cs.uid)) {
2442             // Wait, the client no longer has access to the display.
2443             return InputBindResult.INVALID_DISPLAY_ID;
2444         }
2445         // Compute the final shown display ID with validated cs.selfReportedDisplayId for this
2446         // session & other conditions.
2447         final int displayIdToShowIme = computeImeDisplayIdForTarget(cs.selfReportedDisplayId,
2448                 mImeDisplayValidator);
2449 
2450         if (mCurClient != cs) {
2451             // Was the keyguard locked when switching over to the new client?
2452             mCurClientInKeyguard = isKeyguardLocked();
2453             // If the client is changing, we need to switch over to the new
2454             // one.
2455             unbindCurrentClientLocked(UnbindReason.SWITCH_CLIENT);
2456             if (DEBUG) Slog.v(TAG, "switching to client: client="
2457                     + cs.client.asBinder() + " keyguard=" + mCurClientInKeyguard);
2458 
2459             // If the screen is on, inform the new client it is active
2460             if (mIsInteractive) {
2461                 executeOrSendMessage(cs.client, mCaller.obtainMessageIO(MSG_SET_ACTIVE, 1, cs));
2462             }
2463         }
2464 
2465         // Bump up the sequence for this client and attach it.
2466         mCurSeq++;
2467         if (mCurSeq <= 0) mCurSeq = 1;
2468         mCurClient = cs;
2469         mCurInputContext = inputContext;
2470         mCurActivityViewToScreenMatrix =
2471                 getActivityViewToScreenMatrixLocked(cs.selfReportedDisplayId, displayIdToShowIme);
2472         if (cs.selfReportedDisplayId != displayIdToShowIme
2473                 && mCurActivityViewToScreenMatrix == null) {
2474             // CursorAnchorInfo API does not work as-is for cross-display scenario.  Pretend that
2475             // InputConnection#requestCursorUpdates() is not implemented in the application so that
2476             // IMEs will always receive false from this API.
2477             missingMethods |= MissingMethodFlags.REQUEST_CURSOR_UPDATES;
2478         }
2479         mCurInputContextMissingMethods = missingMethods;
2480         mCurAttribute = attribute;
2481 
2482         // Check if the input method is changing.
2483         // We expect the caller has already verified that the client is allowed to access this
2484         // display ID.
2485         if (mCurId != null && mCurId.equals(mCurMethodId)
2486                 && displayIdToShowIme == mCurTokenDisplayId) {
2487             if (cs.curSession != null) {
2488                 // Fast case: if we are already connected to the input method,
2489                 // then just return it.
2490                 return attachNewInputLocked(startInputReason,
2491                         (startInputFlags & StartInputFlags.INITIAL_CONNECTION) != 0);
2492             }
2493             if (mHaveConnection) {
2494                 if (mCurMethod != null) {
2495                     // Return to client, and we will get back with it when
2496                     // we have had a session made for it.
2497                     requestClientSessionLocked(cs);
2498                     return new InputBindResult(
2499                             InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
2500                             null, null, mCurId, mCurSeq, null);
2501                 } else if (SystemClock.uptimeMillis()
2502                         < (mLastBindTime+TIME_TO_RECONNECT)) {
2503                     // In this case we have connected to the service, but
2504                     // don't yet have its interface.  If it hasn't been too
2505                     // long since we did the connection, we'll return to
2506                     // the client and wait to get the service interface so
2507                     // we can report back.  If it has been too long, we want
2508                     // to fall through so we can try a disconnect/reconnect
2509                     // to see if we can get back in touch with the service.
2510                     return new InputBindResult(
2511                             InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
2512                             null, null, mCurId, mCurSeq, null);
2513                 } else {
2514                     EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
2515                             mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
2516                 }
2517             }
2518         }
2519 
2520         InputMethodInfo info = mMethodMap.get(mCurMethodId);
2521         if (info == null) {
2522             throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
2523         }
2524 
2525         unbindCurrentMethodLocked();
2526 
2527         mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
2528         mCurIntent.setComponent(info.getComponent());
2529         mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
2530                 com.android.internal.R.string.input_method_binding_label);
2531         mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
2532                 mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
2533 
2534         if (bindCurrentInputMethodServiceLocked(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) {
2535             mLastBindTime = SystemClock.uptimeMillis();
2536             mHaveConnection = true;
2537             mCurId = info.getId();
2538             mCurToken = new Binder();
2539             mCurTokenDisplayId = displayIdToShowIme;
2540             try {
2541                 if (DEBUG) {
2542                     Slog.v(TAG, "Adding window token: " + mCurToken + " for display: "
2543                             + mCurTokenDisplayId);
2544                 }
2545                 mIWindowManager.addWindowToken(mCurToken, LayoutParams.TYPE_INPUT_METHOD,
2546                         mCurTokenDisplayId);
2547             } catch (RemoteException e) {
2548             }
2549             return new InputBindResult(
2550                     InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
2551                     null, null, mCurId, mCurSeq, null);
2552         }
2553         mCurIntent = null;
2554         Slog.w(TAG, "Failure connecting to input method service: " + mCurIntent);
2555         return InputBindResult.IME_NOT_CONNECTED;
2556     }
2557 
2558     @FunctionalInterface
2559     interface ImeDisplayValidator {
displayCanShowIme(int displayId)2560         boolean displayCanShowIme(int displayId);
2561     }
2562 
2563     /**
2564      * Find the display where the IME should be shown.
2565      *
2566      * @param displayId the ID of the display where the IME client target is.
2567      * @param checker instance of {@link ImeDisplayValidator} which is used for
2568      *                checking display config to adjust the final target display.
2569      * @return The ID of the display where the IME should be shown.
2570      */
computeImeDisplayIdForTarget(int displayId, @NonNull ImeDisplayValidator checker)2571     static int computeImeDisplayIdForTarget(int displayId, @NonNull ImeDisplayValidator checker) {
2572         if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) {
2573             return FALLBACK_DISPLAY_ID;
2574         }
2575 
2576         // Show IME window on fallback display when the display doesn't support system decorations
2577         // or the display is virtual and isn't owned by system for security concern.
2578         return checker.displayCanShowIme(displayId) ? displayId : FALLBACK_DISPLAY_ID;
2579     }
2580 
2581     @AnyThread
scheduleNotifyImeUidToAudioService(int uid)2582     private void scheduleNotifyImeUidToAudioService(int uid) {
2583         mCaller.removeMessages(MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE);
2584         mCaller.obtainMessageI(MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE, uid).sendToTarget();
2585     }
2586 
2587     @Override
onServiceConnected(ComponentName name, IBinder service)2588     public void onServiceConnected(ComponentName name, IBinder service) {
2589         synchronized (mMethodMap) {
2590             if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
2591                 mCurMethod = IInputMethod.Stub.asInterface(service);
2592                 final String curMethodPackage = mCurIntent.getComponent().getPackageName();
2593                 final int curMethodUid = mPackageManagerInternal.getPackageUidInternal(
2594                         curMethodPackage, 0 /* flags */, mSettings.getCurrentUserId());
2595                 if (curMethodUid < 0) {
2596                     Slog.e(TAG, "Failed to get UID for package=" + curMethodPackage);
2597                     mCurMethodUid = Process.INVALID_UID;
2598                 } else {
2599                     mCurMethodUid = curMethodUid;
2600                 }
2601                 if (mCurToken == null) {
2602                     Slog.w(TAG, "Service connected without a token!");
2603                     unbindCurrentMethodLocked();
2604                     return;
2605                 }
2606                 if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
2607                 // Dispatch display id for InputMethodService to update context display.
2608                 executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(
2609                         MSG_INITIALIZE_IME, mCurTokenDisplayId, mCurMethod, mCurToken));
2610                 scheduleNotifyImeUidToAudioService(mCurMethodUid);
2611                 if (mCurClient != null) {
2612                     clearClientSessionLocked(mCurClient);
2613                     requestClientSessionLocked(mCurClient);
2614                 }
2615             }
2616         }
2617     }
2618 
onSessionCreated(IInputMethod method, IInputMethodSession session, InputChannel channel)2619     void onSessionCreated(IInputMethod method, IInputMethodSession session,
2620             InputChannel channel) {
2621         synchronized (mMethodMap) {
2622             if (mUserSwitchHandlerTask != null) {
2623                 // We have a pending user-switching task so it's better to just ignore this session.
2624                 channel.dispose();
2625                 return;
2626             }
2627             if (mCurMethod != null && method != null
2628                     && mCurMethod.asBinder() == method.asBinder()) {
2629                 if (mCurClient != null) {
2630                     clearClientSessionLocked(mCurClient);
2631                     mCurClient.curSession = new SessionState(mCurClient,
2632                             method, session, channel);
2633                     InputBindResult res = attachNewInputLocked(
2634                             StartInputReason.SESSION_CREATED_BY_IME, true);
2635                     if (res.method != null) {
2636                         executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
2637                                 MSG_BIND_CLIENT, mCurClient.client, res));
2638                     }
2639                     return;
2640                 }
2641             }
2642         }
2643 
2644         // Session abandoned.  Close its associated input channel.
2645         channel.dispose();
2646     }
2647 
unbindCurrentMethodLocked()2648     void unbindCurrentMethodLocked() {
2649         if (mVisibleBound) {
2650             mContext.unbindService(mVisibleConnection);
2651             mVisibleBound = false;
2652         }
2653 
2654         if (mHaveConnection) {
2655             mContext.unbindService(this);
2656             mHaveConnection = false;
2657         }
2658 
2659         if (mCurToken != null) {
2660             try {
2661                 if (DEBUG) {
2662                     Slog.v(TAG, "Removing window token: " + mCurToken + " for display: "
2663                             + mCurTokenDisplayId);
2664                 }
2665                 mIWindowManager.removeWindowToken(mCurToken, mCurTokenDisplayId);
2666             } catch (RemoteException e) {
2667             }
2668             // Set IME window status as invisible when unbind current method.
2669             mImeWindowVis = 0;
2670             mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
2671             updateSystemUiLocked(mImeWindowVis, mBackDisposition);
2672             mCurToken = null;
2673             mCurTokenDisplayId = INVALID_DISPLAY;
2674             mCurHostInputToken = null;
2675         }
2676 
2677         mCurId = null;
2678         clearCurMethodLocked();
2679     }
2680 
resetCurrentMethodAndClient(@nbindReason int unbindClientReason)2681     void resetCurrentMethodAndClient(@UnbindReason int unbindClientReason) {
2682         mCurMethodId = null;
2683         unbindCurrentMethodLocked();
2684         unbindCurrentClientLocked(unbindClientReason);
2685     }
2686 
requestClientSessionLocked(ClientState cs)2687     void requestClientSessionLocked(ClientState cs) {
2688         if (!cs.sessionRequested) {
2689             if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
2690             InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString());
2691             cs.sessionRequested = true;
2692             executeOrSendMessage(mCurMethod, mCaller.obtainMessageOOO(
2693                     MSG_CREATE_SESSION, mCurMethod, channels[1],
2694                     new MethodCallback(this, mCurMethod, channels[0])));
2695         }
2696     }
2697 
clearClientSessionLocked(ClientState cs)2698     void clearClientSessionLocked(ClientState cs) {
2699         finishSessionLocked(cs.curSession);
2700         cs.curSession = null;
2701         cs.sessionRequested = false;
2702     }
2703 
finishSessionLocked(SessionState sessionState)2704     private void finishSessionLocked(SessionState sessionState) {
2705         if (sessionState != null) {
2706             if (sessionState.session != null) {
2707                 try {
2708                     sessionState.session.finishSession();
2709                 } catch (RemoteException e) {
2710                     Slog.w(TAG, "Session failed to close due to remote exception", e);
2711                     updateSystemUiLocked(0 /* vis */, mBackDisposition);
2712                 }
2713                 sessionState.session = null;
2714             }
2715             if (sessionState.channel != null) {
2716                 sessionState.channel.dispose();
2717                 sessionState.channel = null;
2718             }
2719         }
2720     }
2721 
clearCurMethodLocked()2722     void clearCurMethodLocked() {
2723         if (mCurMethod != null) {
2724             final int numClients = mClients.size();
2725             for (int i = 0; i < numClients; ++i) {
2726                 clearClientSessionLocked(mClients.valueAt(i));
2727             }
2728 
2729             finishSessionLocked(mEnabledSession);
2730             mEnabledSession = null;
2731             mCurMethod = null;
2732             mCurMethodUid = Process.INVALID_UID;
2733             scheduleNotifyImeUidToAudioService(mCurMethodUid);
2734         }
2735         if (mStatusBar != null) {
2736             mStatusBar.setIconVisibility(mSlotIme, false);
2737         }
2738         mInFullscreenMode = false;
2739     }
2740 
2741     @Override
onServiceDisconnected(ComponentName name)2742     public void onServiceDisconnected(ComponentName name) {
2743         // Note that mContext.unbindService(this) does not trigger this.  Hence if we are here the
2744         // disconnection is not intended by IMMS (e.g. triggered because the current IMS crashed),
2745         // which is irregular but can eventually happen for everyone just by continuing using the
2746         // device.  Thus it is important to make sure that all the internal states are properly
2747         // refreshed when this method is called back.  Running
2748         //    adb install -r <APK that implements the current IME>
2749         // would be a good way to trigger such a situation.
2750         synchronized (mMethodMap) {
2751             if (DEBUG) Slog.v(TAG, "Service disconnected: " + name
2752                     + " mCurIntent=" + mCurIntent);
2753             if (mCurMethod != null && mCurIntent != null
2754                     && name.equals(mCurIntent.getComponent())) {
2755                 clearCurMethodLocked();
2756                 // We consider this to be a new bind attempt, since the system
2757                 // should now try to restart the service for us.
2758                 mLastBindTime = SystemClock.uptimeMillis();
2759                 mShowRequested = mInputShown;
2760                 mInputShown = false;
2761                 unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME);
2762             }
2763         }
2764     }
2765 
2766     @BinderThread
updateStatusIcon(@onNull IBinder token, String packageName, @DrawableRes int iconId)2767     private void updateStatusIcon(@NonNull IBinder token, String packageName,
2768             @DrawableRes int iconId) {
2769         synchronized (mMethodMap) {
2770             if (!calledWithValidTokenLocked(token)) {
2771                 return;
2772             }
2773             final long ident = Binder.clearCallingIdentity();
2774             try {
2775                 if (iconId == 0) {
2776                     if (DEBUG) Slog.d(TAG, "hide the small icon for the input method");
2777                     if (mStatusBar != null) {
2778                         mStatusBar.setIconVisibility(mSlotIme, false);
2779                     }
2780                 } else if (packageName != null) {
2781                     if (DEBUG) Slog.d(TAG, "show a small icon for the input method");
2782                     CharSequence contentDescription = null;
2783                     try {
2784                         // Use PackageManager to load label
2785                         final PackageManager packageManager = mContext.getPackageManager();
2786                         contentDescription = packageManager.getApplicationLabel(
2787                                 mIPackageManager.getApplicationInfo(packageName, 0,
2788                                         mSettings.getCurrentUserId()));
2789                     } catch (RemoteException e) {
2790                         /* ignore */
2791                     }
2792                     if (mStatusBar != null) {
2793                         mStatusBar.setIcon(mSlotIme, packageName, iconId, 0,
2794                                 contentDescription  != null
2795                                         ? contentDescription.toString() : null);
2796                         mStatusBar.setIconVisibility(mSlotIme, true);
2797                     }
2798                 }
2799             } finally {
2800                 Binder.restoreCallingIdentity(ident);
2801             }
2802         }
2803     }
2804 
shouldShowImeSwitcherLocked(int visibility)2805     private boolean shouldShowImeSwitcherLocked(int visibility) {
2806         if (!mShowOngoingImeSwitcherForPhones) return false;
2807         if (mSwitchingDialog != null) return false;
2808         if (mWindowManagerInternal.isKeyguardShowingAndNotOccluded()
2809                 && mKeyguardManager != null && mKeyguardManager.isKeyguardSecure()) return false;
2810         if ((visibility & InputMethodService.IME_ACTIVE) == 0
2811                 || (visibility & InputMethodService.IME_INVISIBLE) != 0) {
2812             return false;
2813         }
2814         if (mWindowManagerInternal.isHardKeyboardAvailable()) {
2815             // When physical keyboard is attached, we show the ime switcher (or notification if
2816             // NavBar is not available) because SHOW_IME_WITH_HARD_KEYBOARD settings currently
2817             // exists in the IME switcher dialog.  Might be OK to remove this condition once
2818             // SHOW_IME_WITH_HARD_KEYBOARD settings finds a good place to live.
2819             return true;
2820         } else if ((visibility & InputMethodService.IME_VISIBLE) == 0) {
2821             return false;
2822         }
2823 
2824         List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
2825         final int N = imis.size();
2826         if (N > 2) return true;
2827         if (N < 1) return false;
2828         int nonAuxCount = 0;
2829         int auxCount = 0;
2830         InputMethodSubtype nonAuxSubtype = null;
2831         InputMethodSubtype auxSubtype = null;
2832         for(int i = 0; i < N; ++i) {
2833             final InputMethodInfo imi = imis.get(i);
2834             final List<InputMethodSubtype> subtypes =
2835                     mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true);
2836             final int subtypeCount = subtypes.size();
2837             if (subtypeCount == 0) {
2838                 ++nonAuxCount;
2839             } else {
2840                 for (int j = 0; j < subtypeCount; ++j) {
2841                     final InputMethodSubtype subtype = subtypes.get(j);
2842                     if (!subtype.isAuxiliary()) {
2843                         ++nonAuxCount;
2844                         nonAuxSubtype = subtype;
2845                     } else {
2846                         ++auxCount;
2847                         auxSubtype = subtype;
2848                     }
2849                 }
2850             }
2851         }
2852         if (nonAuxCount > 1 || auxCount > 1) {
2853             return true;
2854         } else if (nonAuxCount == 1 && auxCount == 1) {
2855             if (nonAuxSubtype != null && auxSubtype != null
2856                     && (nonAuxSubtype.getLocale().equals(auxSubtype.getLocale())
2857                             || auxSubtype.overridesImplicitlyEnabledSubtype()
2858                             || nonAuxSubtype.overridesImplicitlyEnabledSubtype())
2859                     && nonAuxSubtype.containsExtraValueKey(TAG_TRY_SUPPRESSING_IME_SWITCHER)) {
2860                 return false;
2861             }
2862             return true;
2863         }
2864         return false;
2865     }
2866 
isKeyguardLocked()2867     private boolean isKeyguardLocked() {
2868         return mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
2869     }
2870 
2871     @BinderThread
2872     @SuppressWarnings("deprecation")
setImeWindowStatus(@onNull IBinder token, int vis, int backDisposition)2873     private void setImeWindowStatus(@NonNull IBinder token, int vis, int backDisposition) {
2874         final int topFocusedDisplayId = mWindowManagerInternal.getTopFocusedDisplayId();
2875 
2876         synchronized (mMethodMap) {
2877             if (!calledWithValidTokenLocked(token)) {
2878                 return;
2879             }
2880             // Skip update IME status when current token display is not same as focused display.
2881             // Note that we still need to update IME status when focusing external display
2882             // that does not support system decoration and fallback to show IME on default
2883             // display since it is intentional behavior.
2884             if (mCurTokenDisplayId != topFocusedDisplayId
2885                     && mCurTokenDisplayId != FALLBACK_DISPLAY_ID) {
2886                 return;
2887             }
2888             mImeWindowVis = vis;
2889             mBackDisposition = backDisposition;
2890             updateSystemUiLocked(vis, backDisposition);
2891         }
2892 
2893         final boolean dismissImeOnBackKeyPressed;
2894         switch (backDisposition) {
2895             case InputMethodService.BACK_DISPOSITION_WILL_DISMISS:
2896                 dismissImeOnBackKeyPressed = true;
2897                 break;
2898             case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS:
2899                 dismissImeOnBackKeyPressed = false;
2900                 break;
2901             default:
2902             case InputMethodService.BACK_DISPOSITION_DEFAULT:
2903                 dismissImeOnBackKeyPressed = ((vis & InputMethodService.IME_VISIBLE) != 0);
2904                 break;
2905         }
2906         mWindowManagerInternal.updateInputMethodWindowStatus(token,
2907                 (vis & InputMethodService.IME_VISIBLE) != 0, dismissImeOnBackKeyPressed);
2908     }
2909 
2910     @BinderThread
reportStartInput(@onNull IBinder token, IBinder startInputToken)2911     private void reportStartInput(@NonNull IBinder token, IBinder startInputToken) {
2912         synchronized (mMethodMap) {
2913             if (!calledWithValidTokenLocked(token)) {
2914                 return;
2915             }
2916             final IBinder targetWindow = mImeTargetWindowMap.get(startInputToken);
2917             if (targetWindow != null && mLastImeTargetWindow != targetWindow) {
2918                 mWindowManagerInternal.updateInputMethodTargetWindow(token, targetWindow);
2919             }
2920             mLastImeTargetWindow = targetWindow;
2921         }
2922     }
2923 
2924     // Caution! This method is called in this class. Handle multi-user carefully
updateSystemUiLocked(int vis, int backDisposition)2925     private void updateSystemUiLocked(int vis, int backDisposition) {
2926         if (mCurToken == null) {
2927             return;
2928         }
2929         if (DEBUG) {
2930             Slog.d(TAG, "IME window vis: " + vis
2931                     + " active: " + (vis & InputMethodService.IME_ACTIVE)
2932                     + " inv: " + (vis & InputMethodService.IME_INVISIBLE)
2933                     + " displayId: " + mCurTokenDisplayId);
2934         }
2935 
2936         // TODO: Move this clearing calling identity block to setImeWindowStatus after making sure
2937         // all updateSystemUi happens on system previlege.
2938         final long ident = Binder.clearCallingIdentity();
2939         try {
2940             // apply policy for binder calls
2941             if (vis != 0 && isKeyguardLocked() && !mCurClientInKeyguard) {
2942                 vis = 0;
2943             }
2944             if (!mCurPerceptible) {
2945                 vis &= ~InputMethodService.IME_VISIBLE;
2946             }
2947             // mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked().
2948             final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
2949             if (mStatusBar != null) {
2950                 mStatusBar.setImeWindowStatus(mCurTokenDisplayId, mCurToken, vis, backDisposition,
2951                         needsToShowImeSwitcher, false /*isMultiClientImeEnabled*/);
2952             }
2953             final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
2954             if (imi != null && needsToShowImeSwitcher) {
2955                 // Used to load label
2956                 final CharSequence title = mRes.getText(
2957                         com.android.internal.R.string.select_input_method);
2958                 final CharSequence summary = InputMethodUtils.getImeAndSubtypeDisplayName(
2959                         mContext, imi, mCurrentSubtype);
2960                 mImeSwitcherNotification.setContentTitle(title)
2961                         .setContentText(summary)
2962                         .setContentIntent(mImeSwitchPendingIntent);
2963                 try {
2964                     // TODO(b/120076400): Figure out what is the best behavior
2965                     if ((mNotificationManager != null)
2966                             && !mIWindowManager.hasNavigationBar(DEFAULT_DISPLAY)) {
2967                         if (DEBUG) {
2968                             Slog.d(TAG, "--- show notification: label =  " + summary);
2969                         }
2970                         mNotificationManager.notifyAsUser(null,
2971                                 SystemMessage.NOTE_SELECT_INPUT_METHOD,
2972                                 mImeSwitcherNotification.build(), UserHandle.ALL);
2973                         mNotificationShown = true;
2974                     }
2975                 } catch (RemoteException e) {
2976                 }
2977             } else {
2978                 if (mNotificationShown && mNotificationManager != null) {
2979                     if (DEBUG) {
2980                         Slog.d(TAG, "--- hide notification");
2981                     }
2982                     mNotificationManager.cancelAsUser(null,
2983                             SystemMessage.NOTE_SELECT_INPUT_METHOD, UserHandle.ALL);
2984                     mNotificationShown = false;
2985                 }
2986             }
2987         } finally {
2988             Binder.restoreCallingIdentity(ident);
2989         }
2990     }
2991 
updateFromSettingsLocked(boolean enabledMayChange)2992     void updateFromSettingsLocked(boolean enabledMayChange) {
2993         updateInputMethodsFromSettingsLocked(enabledMayChange);
2994         updateKeyboardFromSettingsLocked();
2995     }
2996 
updateInputMethodsFromSettingsLocked(boolean enabledMayChange)2997     void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
2998         if (enabledMayChange) {
2999             List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
3000             for (int i=0; i<enabled.size(); i++) {
3001                 // We allow the user to select "disabled until used" apps, so if they
3002                 // are enabling one of those here we now need to make it enabled.
3003                 InputMethodInfo imm = enabled.get(i);
3004                 try {
3005                     ApplicationInfo ai = mIPackageManager.getApplicationInfo(imm.getPackageName(),
3006                             PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
3007                             mSettings.getCurrentUserId());
3008                     if (ai != null && ai.enabledSetting
3009                             == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
3010                         if (DEBUG) {
3011                             Slog.d(TAG, "Update state(" + imm.getId()
3012                                     + "): DISABLED_UNTIL_USED -> DEFAULT");
3013                         }
3014                         mIPackageManager.setApplicationEnabledSetting(imm.getPackageName(),
3015                                 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
3016                                 PackageManager.DONT_KILL_APP, mSettings.getCurrentUserId(),
3017                                 mContext.getBasePackageName());
3018                     }
3019                 } catch (RemoteException e) {
3020                 }
3021             }
3022         }
3023         // We are assuming that whoever is changing DEFAULT_INPUT_METHOD and
3024         // ENABLED_INPUT_METHODS is taking care of keeping them correctly in
3025         // sync, so we will never have a DEFAULT_INPUT_METHOD that is not
3026         // enabled.
3027         String id = mSettings.getSelectedInputMethod();
3028         // There is no input method selected, try to choose new applicable input method.
3029         if (TextUtils.isEmpty(id) && chooseNewDefaultIMELocked()) {
3030             id = mSettings.getSelectedInputMethod();
3031         }
3032         if (!TextUtils.isEmpty(id)) {
3033             try {
3034                 setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id));
3035             } catch (IllegalArgumentException e) {
3036                 Slog.w(TAG, "Unknown input method from prefs: " + id, e);
3037                 resetCurrentMethodAndClient(UnbindReason.SWITCH_IME_FAILED);
3038             }
3039         } else {
3040             // There is no longer an input method set, so stop any current one.
3041             resetCurrentMethodAndClient(UnbindReason.NO_IME);
3042         }
3043         // Here is not the perfect place to reset the switching controller. Ideally
3044         // mSwitchingController and mSettings should be able to share the same state.
3045         // TODO: Make sure that mSwitchingController and mSettings are sharing the
3046         // the same enabled IMEs list.
3047         mSwitchingController.resetCircularListLocked(mContext);
3048 
3049     }
3050 
updateKeyboardFromSettingsLocked()3051     public void updateKeyboardFromSettingsLocked() {
3052         mShowImeWithHardKeyboard = mSettings.isShowImeWithHardKeyboardEnabled();
3053         if (mSwitchingDialog != null
3054                 && mSwitchingDialogTitleView != null
3055                 && mSwitchingDialog.isShowing()) {
3056             final Switch hardKeySwitch = (Switch)mSwitchingDialogTitleView.findViewById(
3057                     com.android.internal.R.id.hard_keyboard_switch);
3058             hardKeySwitch.setChecked(mShowImeWithHardKeyboard);
3059         }
3060     }
3061 
setInputMethodLocked(String id, int subtypeId)3062     /* package */ void setInputMethodLocked(String id, int subtypeId) {
3063         InputMethodInfo info = mMethodMap.get(id);
3064         if (info == null) {
3065             throw new IllegalArgumentException("Unknown id: " + id);
3066         }
3067 
3068         // See if we need to notify a subtype change within the same IME.
3069         if (id.equals(mCurMethodId)) {
3070             final int subtypeCount = info.getSubtypeCount();
3071             if (subtypeCount <= 0) {
3072                 return;
3073             }
3074             final InputMethodSubtype oldSubtype = mCurrentSubtype;
3075             final InputMethodSubtype newSubtype;
3076             if (subtypeId >= 0 && subtypeId < subtypeCount) {
3077                 newSubtype = info.getSubtypeAt(subtypeId);
3078             } else {
3079                 // If subtype is null, try to find the most applicable one from
3080                 // getCurrentInputMethodSubtype.
3081                 newSubtype = getCurrentInputMethodSubtypeLocked();
3082             }
3083             if (newSubtype == null || oldSubtype == null) {
3084                 Slog.w(TAG, "Illegal subtype state: old subtype = " + oldSubtype
3085                         + ", new subtype = " + newSubtype);
3086                 return;
3087             }
3088             if (newSubtype != oldSubtype) {
3089                 setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
3090                 if (mCurMethod != null) {
3091                     try {
3092                         updateSystemUiLocked(mImeWindowVis, mBackDisposition);
3093                         mCurMethod.changeInputMethodSubtype(newSubtype);
3094                     } catch (RemoteException e) {
3095                         Slog.w(TAG, "Failed to call changeInputMethodSubtype");
3096                     }
3097                 }
3098             }
3099             return;
3100         }
3101 
3102         // Changing to a different IME.
3103         final long ident = Binder.clearCallingIdentity();
3104         try {
3105             // Set a subtype to this input method.
3106             // subtypeId the name of a subtype which will be set.
3107             setSelectedInputMethodAndSubtypeLocked(info, subtypeId, false);
3108             // mCurMethodId should be updated after setSelectedInputMethodAndSubtypeLocked()
3109             // because mCurMethodId is stored as a history in
3110             // setSelectedInputMethodAndSubtypeLocked().
3111             mCurMethodId = id;
3112 
3113             if (LocalServices.getService(ActivityManagerInternal.class).isSystemReady()) {
3114                 Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
3115                 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
3116                 intent.putExtra("input_method_id", id);
3117                 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
3118             }
3119             unbindCurrentClientLocked(UnbindReason.SWITCH_IME);
3120         } finally {
3121             Binder.restoreCallingIdentity(ident);
3122         }
3123     }
3124 
3125     @Override
showSoftInput(IInputMethodClient client, IBinder windowToken, int flags, ResultReceiver resultReceiver)3126     public boolean showSoftInput(IInputMethodClient client, IBinder windowToken, int flags,
3127             ResultReceiver resultReceiver) {
3128         int uid = Binder.getCallingUid();
3129         synchronized (mMethodMap) {
3130             if (!calledFromValidUserLocked()) {
3131                 return false;
3132             }
3133             final long ident = Binder.clearCallingIdentity();
3134             try {
3135                 if (mCurClient == null || client == null
3136                         || mCurClient.client.asBinder() != client.asBinder()) {
3137                     // We need to check if this is the current client with
3138                     // focus in the window manager, to allow this call to
3139                     // be made before input is started in it.
3140                     final ClientState cs = mClients.get(client.asBinder());
3141                     if (cs == null) {
3142                         throw new IllegalArgumentException("unknown client " + client.asBinder());
3143                     }
3144                     if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
3145                             cs.selfReportedDisplayId)) {
3146                         Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client);
3147                         return false;
3148                     }
3149                 }
3150                 if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
3151                 return showCurrentInputLocked(windowToken, flags, resultReceiver,
3152                         SoftInputShowHideReason.SHOW_SOFT_INPUT);
3153             } finally {
3154                 Binder.restoreCallingIdentity(ident);
3155             }
3156         }
3157     }
3158 
3159     @BinderThread
3160     @Override
reportPerceptible(IBinder windowToken, boolean perceptible)3161     public void reportPerceptible(IBinder windowToken, boolean perceptible) {
3162         Objects.requireNonNull(windowToken, "windowToken must not be null");
3163         int uid = Binder.getCallingUid();
3164         synchronized (mMethodMap) {
3165             if (!calledFromValidUserLocked()) {
3166                 return;
3167             }
3168             final long ident = Binder.clearCallingIdentity();
3169             try {
3170                 if (mCurFocusedWindow == windowToken
3171                         && mCurPerceptible != perceptible) {
3172                     mCurPerceptible = perceptible;
3173                     updateSystemUiLocked(mImeWindowVis, mBackDisposition);
3174                 }
3175             } finally {
3176                 Binder.restoreCallingIdentity(ident);
3177             }
3178         }
3179     }
3180 
3181     @GuardedBy("mMethodMap")
showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason)3182     boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
3183             @SoftInputShowHideReason int reason) {
3184         mShowRequested = true;
3185         if (mAccessibilityRequestingNoSoftKeyboard) {
3186             return false;
3187         }
3188 
3189         if ((flags&InputMethodManager.SHOW_FORCED) != 0) {
3190             mShowExplicitlyRequested = true;
3191             mShowForced = true;
3192         } else if ((flags&InputMethodManager.SHOW_IMPLICIT) == 0) {
3193             mShowExplicitlyRequested = true;
3194         }
3195 
3196         if (!mSystemReady) {
3197             return false;
3198         }
3199 
3200         boolean res = false;
3201         if (mCurMethod != null) {
3202             if (DEBUG) Slog.d(TAG, "showCurrentInputLocked: mCurToken=" + mCurToken);
3203             // create a dummy token for IMS so that IMS cannot inject windows into client app.
3204             Binder showInputToken = new Binder();
3205             mShowRequestWindowMap.put(showInputToken, windowToken);
3206             executeOrSendMessage(mCurMethod, mCaller.obtainMessageIIOOO(
3207                     MSG_SHOW_SOFT_INPUT, getImeShowFlags(), reason, mCurMethod, resultReceiver,
3208                     showInputToken));
3209             mInputShown = true;
3210             if (mHaveConnection && !mVisibleBound) {
3211                 bindCurrentInputMethodServiceLocked(
3212                         mCurIntent, mVisibleConnection, IME_VISIBLE_BIND_FLAGS);
3213                 mVisibleBound = true;
3214             }
3215             res = true;
3216         } else if (mHaveConnection && SystemClock.uptimeMillis()
3217                 >= (mLastBindTime+TIME_TO_RECONNECT)) {
3218             // The client has asked to have the input method shown, but
3219             // we have been sitting here too long with a connection to the
3220             // service and no interface received, so let's disconnect/connect
3221             // to try to prod things along.
3222             EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, mCurMethodId,
3223                     SystemClock.uptimeMillis()-mLastBindTime,1);
3224             Slog.w(TAG, "Force disconnect/connect to the IME in showCurrentInputLocked()");
3225             mContext.unbindService(this);
3226             bindCurrentInputMethodServiceLocked(mCurIntent, this, IME_CONNECTION_BIND_FLAGS);
3227         } else {
3228             if (DEBUG) {
3229                 Slog.d(TAG, "Can't show input: connection = " + mHaveConnection + ", time = "
3230                         + ((mLastBindTime+TIME_TO_RECONNECT) - SystemClock.uptimeMillis()));
3231             }
3232         }
3233 
3234         return res;
3235     }
3236 
3237     @Override
hideSoftInput(IInputMethodClient client, IBinder windowToken, int flags, ResultReceiver resultReceiver)3238     public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken, int flags,
3239             ResultReceiver resultReceiver) {
3240         int uid = Binder.getCallingUid();
3241         synchronized (mMethodMap) {
3242             if (!calledFromValidUserLocked()) {
3243                 return false;
3244             }
3245             final long ident = Binder.clearCallingIdentity();
3246             try {
3247                 if (mCurClient == null || client == null
3248                         || mCurClient.client.asBinder() != client.asBinder()) {
3249                     // We need to check if this is the current client with
3250                     // focus in the window manager, to allow this call to
3251                     // be made before input is started in it.
3252                     final ClientState cs = mClients.get(client.asBinder());
3253                     if (cs == null) {
3254                         throw new IllegalArgumentException("unknown client " + client.asBinder());
3255                     }
3256                     if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
3257                             cs.selfReportedDisplayId)) {
3258                         if (DEBUG) {
3259                             Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client);
3260                         }
3261                         return false;
3262                     }
3263                 }
3264 
3265                 if (DEBUG) Slog.v(TAG, "Client requesting input be hidden");
3266                 return hideCurrentInputLocked(windowToken, flags, resultReceiver,
3267                         SoftInputShowHideReason.HIDE_SOFT_INPUT);
3268             } finally {
3269                 Binder.restoreCallingIdentity(ident);
3270             }
3271         }
3272     }
3273 
hideCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason)3274     boolean hideCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
3275             @SoftInputShowHideReason int reason) {
3276         if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
3277                 && (mShowExplicitlyRequested || mShowForced)) {
3278             if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
3279             return false;
3280         }
3281         if (mShowForced && (flags&InputMethodManager.HIDE_NOT_ALWAYS) != 0) {
3282             if (DEBUG) Slog.v(TAG, "Not hiding: forced show not cancelled by not-always hide");
3283             return false;
3284         }
3285 
3286         // There is a chance that IMM#hideSoftInput() is called in a transient state where
3287         // IMMS#InputShown is already updated to be true whereas IMMS#mImeWindowVis is still waiting
3288         // to be updated with the new value sent from IME process.  Even in such a transient state
3289         // historically we have accepted an incoming call of IMM#hideSoftInput() from the
3290         // application process as a valid request, and have even promised such a behavior with CTS
3291         // since Android Eclair.  That's why we need to accept IMM#hideSoftInput() even when only
3292         // IMMS#InputShown indicates that the software keyboard is shown.
3293         // TODO: Clean up, IMMS#mInputShown, IMMS#mImeWindowVis and mShowRequested.
3294         final boolean shouldHideSoftInput = (mCurMethod != null) && (mInputShown ||
3295                 (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
3296         boolean res;
3297         if (shouldHideSoftInput) {
3298             final Binder hideInputToken = new Binder();
3299             mHideRequestWindowMap.put(hideInputToken, windowToken);
3300             // The IME will report its visible state again after the following message finally
3301             // delivered to the IME process as an IPC.  Hence the inconsistency between
3302             // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in
3303             // the final state.
3304             executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO(MSG_HIDE_SOFT_INPUT,
3305                     reason, mCurMethod, resultReceiver, hideInputToken));
3306             res = true;
3307         } else {
3308             res = false;
3309         }
3310         if (mHaveConnection && mVisibleBound) {
3311             mContext.unbindService(mVisibleConnection);
3312             mVisibleBound = false;
3313         }
3314         mInputShown = false;
3315         mShowRequested = false;
3316         mShowExplicitlyRequested = false;
3317         mShowForced = false;
3318         return res;
3319     }
3320 
3321     @NonNull
3322     @Override
startInputOrWindowGainedFocus( @tartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken, @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext, @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion)3323     public InputBindResult startInputOrWindowGainedFocus(
3324             @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
3325             @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
3326             int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
3327             @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) {
3328         if (windowToken == null) {
3329             Slog.e(TAG, "windowToken cannot be null.");
3330             return InputBindResult.NULL;
3331         }
3332         final int callingUserId = UserHandle.getCallingUserId();
3333         final int userId;
3334         if (attribute != null && attribute.targetInputMethodUser != null
3335                 && attribute.targetInputMethodUser.getIdentifier() != callingUserId) {
3336             mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
3337                     "Using EditorInfo.targetInputMethodUser requires INTERACT_ACROSS_USERS_FULL.");
3338             userId = attribute.targetInputMethodUser.getIdentifier();
3339             if (!mUserManagerInternal.isUserRunning(userId)) {
3340                 // There is a chance that we hit here because of race condition.  Let's just return
3341                 // an error code instead of crashing the caller process, which at least has
3342                 // INTERACT_ACROSS_USERS_FULL permission thus is likely to be an important process.
3343                 Slog.e(TAG, "User #" + userId + " is not running.");
3344                 return InputBindResult.INVALID_USER;
3345             }
3346         } else {
3347             userId = callingUserId;
3348         }
3349         final InputBindResult result;
3350         synchronized (mMethodMap) {
3351             final long ident = Binder.clearCallingIdentity();
3352             try {
3353                 result = startInputOrWindowGainedFocusInternalLocked(startInputReason, client,
3354                         windowToken, startInputFlags, softInputMode, windowFlags, attribute,
3355                         inputContext, missingMethods, unverifiedTargetSdkVersion, userId);
3356             } finally {
3357                 Binder.restoreCallingIdentity(ident);
3358             }
3359         }
3360         if (result == null) {
3361             // This must never happen, but just in case.
3362             Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
3363                     + InputMethodDebug.startInputReasonToString(startInputReason)
3364                     + " windowFlags=#" + Integer.toHexString(windowFlags)
3365                     + " editorInfo=" + attribute);
3366             return InputBindResult.NULL;
3367         }
3368         return result;
3369     }
3370 
3371     @NonNull
startInputOrWindowGainedFocusInternalLocked( @tartInputReason int startInputReason, IInputMethodClient client, @NonNull IBinder windowToken, @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute, IInputContext inputContext, @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion, @UserIdInt int userId)3372     private InputBindResult startInputOrWindowGainedFocusInternalLocked(
3373             @StartInputReason int startInputReason, IInputMethodClient client,
3374             @NonNull IBinder windowToken, @StartInputFlags int startInputFlags,
3375             @SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute,
3376             IInputContext inputContext, @MissingMethodFlags int missingMethods,
3377             int unverifiedTargetSdkVersion, @UserIdInt int userId) {
3378         if (DEBUG) {
3379             Slog.v(TAG, "startInputOrWindowGainedFocusInternalLocked: reason="
3380                     + InputMethodDebug.startInputReasonToString(startInputReason)
3381                     + " client=" + client.asBinder()
3382                     + " inputContext=" + inputContext
3383                     + " missingMethods="
3384                     + InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods)
3385                     + " attribute=" + attribute
3386                     + " startInputFlags="
3387                     + InputMethodDebug.startInputFlagsToString(startInputFlags)
3388                     + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
3389                     + " windowFlags=#" + Integer.toHexString(windowFlags)
3390                     + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion);
3391         }
3392 
3393         final int windowDisplayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken);
3394 
3395         final ClientState cs = mClients.get(client.asBinder());
3396         if (cs == null) {
3397             throw new IllegalArgumentException("unknown client " + client.asBinder());
3398         }
3399         if (cs.selfReportedDisplayId != windowDisplayId) {
3400             Slog.e(TAG, "startInputOrWindowGainedFocusInternal: display ID mismatch."
3401                     + " from client:" + cs.selfReportedDisplayId
3402                     + " from window:" + windowDisplayId);
3403             return InputBindResult.DISPLAY_ID_MISMATCH;
3404         }
3405 
3406         if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
3407                 cs.selfReportedDisplayId)) {
3408             // Check with the window manager to make sure this client actually
3409             // has a window with focus.  If not, reject.  This is thread safe
3410             // because if the focus changes some time before or after, the
3411             // next client receiving focus that has any interest in input will
3412             // be calling through here after that change happens.
3413             if (DEBUG) {
3414                 Slog.w(TAG, "Focus gain on non-focused client " + cs.client
3415                         + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
3416             }
3417             return InputBindResult.NOT_IME_TARGET_WINDOW;
3418         }
3419 
3420         if (mUserSwitchHandlerTask != null) {
3421             // There is already an on-going pending user switch task.
3422             final int nextUserId = mUserSwitchHandlerTask.mToUserId;
3423             if (userId == nextUserId) {
3424                 scheduleSwitchUserTaskLocked(userId, cs.client);
3425                 return InputBindResult.USER_SWITCHING;
3426             }
3427             for (int profileId : mUserManager.getProfileIdsWithDisabled(nextUserId)) {
3428                 if (profileId == userId) {
3429                     scheduleSwitchUserTaskLocked(userId, cs.client);
3430                     return InputBindResult.USER_SWITCHING;
3431                 }
3432             }
3433             return InputBindResult.INVALID_USER;
3434         }
3435 
3436         // cross-profile access is always allowed here to allow profile-switching.
3437         if (!mSettings.isCurrentProfile(userId)) {
3438             Slog.w(TAG, "A background user is requesting window. Hiding IME.");
3439             Slog.w(TAG, "If you need to impersonate a foreground user/profile from"
3440                     + " a background user, use EditorInfo.targetInputMethodUser with"
3441                     + " INTERACT_ACROSS_USERS_FULL permission.");
3442             hideCurrentInputLocked(
3443                     mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_INVALID_USER);
3444             return InputBindResult.INVALID_USER;
3445         }
3446 
3447         if (userId != mSettings.getCurrentUserId()) {
3448             scheduleSwitchUserTaskLocked(userId, cs.client);
3449             return InputBindResult.USER_SWITCHING;
3450         }
3451 
3452         // Master feature flag that overrides other conditions and forces IME preRendering.
3453         if (DEBUG) {
3454             Slog.v(TAG, "IME PreRendering MASTER flag: "
3455                     + DebugFlags.FLAG_PRE_RENDER_IME_VIEWS.value() + ", LowRam: " + mIsLowRam);
3456         }
3457         // pre-rendering not supported on low-ram devices.
3458         cs.shouldPreRenderIme = DebugFlags.FLAG_PRE_RENDER_IME_VIEWS.value() && !mIsLowRam;
3459 
3460         if (mCurFocusedWindow == windowToken) {
3461             if (DEBUG) {
3462                 Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client
3463                         + " attribute=" + attribute + ", token = " + windowToken
3464                         + ", startInputReason="
3465                         + InputMethodDebug.startInputReasonToString(startInputReason));
3466             }
3467             if (attribute != null) {
3468                 return startInputUncheckedLocked(cs, inputContext, missingMethods,
3469                         attribute, startInputFlags, startInputReason);
3470             }
3471             return new InputBindResult(
3472                     InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
3473                     null, null, null, -1, null);
3474         }
3475         mCurFocusedWindow = windowToken;
3476         mCurFocusedWindowSoftInputMode = softInputMode;
3477         mCurFocusedWindowClient = cs;
3478         mCurPerceptible = true;
3479 
3480         // Should we auto-show the IME even if the caller has not
3481         // specified what should be done with it?
3482         // We only do this automatically if the window can resize
3483         // to accommodate the IME (so what the user sees will give
3484         // them good context without input information being obscured
3485         // by the IME) or if running on a large screen where there
3486         // is more room for the target window + IME.
3487         final boolean doAutoShow =
3488                 (softInputMode & LayoutParams.SOFT_INPUT_MASK_ADJUST)
3489                         == LayoutParams.SOFT_INPUT_ADJUST_RESIZE
3490                 || mRes.getConfiguration().isLayoutSizeAtLeast(
3491                         Configuration.SCREENLAYOUT_SIZE_LARGE);
3492         final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0;
3493 
3494         // We want to start input before showing the IME, but after closing
3495         // it.  We want to do this after closing it to help the IME disappear
3496         // more quickly (not get stuck behind it initializing itself for the
3497         // new focused input, even if its window wants to hide the IME).
3498         boolean didStart = false;
3499 
3500         InputBindResult res = null;
3501         switch (softInputMode & LayoutParams.SOFT_INPUT_MASK_STATE) {
3502             case LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
3503                 if (!isTextEditor || !doAutoShow) {
3504                     if (LayoutParams.mayUseInputMethod(windowFlags)) {
3505                         // There is no focus view, and this window will
3506                         // be behind any soft input window, so hide the
3507                         // soft input window if it is shown.
3508                         if (DEBUG) Slog.v(TAG, "Unspecified window will hide input");
3509                         hideCurrentInputLocked(
3510                                 mCurFocusedWindow, InputMethodManager.HIDE_NOT_ALWAYS, null,
3511                                 SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW);
3512 
3513                         // If focused display changed, we should unbind current method
3514                         // to make app window in previous display relayout after Ime
3515                         // window token removed.
3516                         // Note that we can trust client's display ID as long as it matches
3517                         // to the display ID obtained from the window.
3518                         if (cs.selfReportedDisplayId != mCurTokenDisplayId) {
3519                             unbindCurrentMethodLocked();
3520                         }
3521                     }
3522                 } else if (isTextEditor && doAutoShow
3523                         && (softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
3524                     // There is a focus view, and we are navigating forward
3525                     // into the window, so show the input window for the user.
3526                     // We only do this automatically if the window can resize
3527                     // to accommodate the IME (so what the user sees will give
3528                     // them good context without input information being obscured
3529                     // by the IME) or if running on a large screen where there
3530                     // is more room for the target window + IME.
3531                     if (DEBUG) Slog.v(TAG, "Unspecified window will show input");
3532                     if (attribute != null) {
3533                         res = startInputUncheckedLocked(cs, inputContext, missingMethods,
3534                                 attribute, startInputFlags, startInputReason);
3535                         didStart = true;
3536                     }
3537                     showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
3538                             SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV);
3539                 }
3540                 break;
3541             case LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
3542                 // Do nothing.
3543                 break;
3544             case LayoutParams.SOFT_INPUT_STATE_HIDDEN:
3545                 if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
3546                     if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward");
3547                     hideCurrentInputLocked(mCurFocusedWindow, 0, null,
3548                             SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV);
3549                 }
3550                 break;
3551             case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
3552                 if (DEBUG) Slog.v(TAG, "Window asks to hide input");
3553                 hideCurrentInputLocked(mCurFocusedWindow, 0, null,
3554                         SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE);
3555                 break;
3556             case LayoutParams.SOFT_INPUT_STATE_VISIBLE:
3557                 if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
3558                     if (DEBUG) Slog.v(TAG, "Window asks to show input going forward");
3559                     if (InputMethodUtils.isSoftInputModeStateVisibleAllowed(
3560                             unverifiedTargetSdkVersion, startInputFlags)) {
3561                         if (attribute != null) {
3562                             res = startInputUncheckedLocked(cs, inputContext, missingMethods,
3563                                     attribute, startInputFlags, startInputReason);
3564                             didStart = true;
3565                         }
3566                         showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
3567                                 SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV);
3568                     } else {
3569                         Slog.e(TAG, "SOFT_INPUT_STATE_VISIBLE is ignored because"
3570                                 + " there is no focused view that also returns true from"
3571                                 + " View#onCheckIsTextEditor()");
3572                     }
3573                 }
3574                 break;
3575             case LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
3576                 if (DEBUG) Slog.v(TAG, "Window asks to always show input");
3577                 if (InputMethodUtils.isSoftInputModeStateVisibleAllowed(
3578                         unverifiedTargetSdkVersion, startInputFlags)) {
3579                     if (attribute != null) {
3580                         res = startInputUncheckedLocked(cs, inputContext, missingMethods,
3581                                 attribute, startInputFlags, startInputReason);
3582                         didStart = true;
3583                     }
3584                     showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
3585                             SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE);
3586                 } else {
3587                     Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because"
3588                             + " there is no focused view that also returns true from"
3589                             + " View#onCheckIsTextEditor()");
3590                 }
3591                 break;
3592         }
3593 
3594         if (!didStart) {
3595             if (attribute != null) {
3596                 if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
3597                         || (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) {
3598                     res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
3599                             startInputFlags, startInputReason);
3600                 } else {
3601                     res = InputBindResult.NO_EDITOR;
3602                 }
3603             } else {
3604                 res = InputBindResult.NULL_EDITOR_INFO;
3605             }
3606         }
3607         return res;
3608     }
3609 
canShowInputMethodPickerLocked(IInputMethodClient client)3610     private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
3611         // TODO(yukawa): multi-display support.
3612         final int uid = Binder.getCallingUid();
3613         if (mCurFocusedWindowClient != null && client != null
3614                 && mCurFocusedWindowClient.client.asBinder() == client.asBinder()) {
3615             return true;
3616         } else if (mCurIntent != null && InputMethodUtils.checkIfPackageBelongsToUid(
3617                 mAppOpsManager,
3618                 uid,
3619                 mCurIntent.getComponent().getPackageName())) {
3620             return true;
3621         }
3622         return false;
3623     }
3624 
3625     @Override
showInputMethodPickerFromClient( IInputMethodClient client, int auxiliarySubtypeMode)3626     public void showInputMethodPickerFromClient(
3627             IInputMethodClient client, int auxiliarySubtypeMode) {
3628         synchronized (mMethodMap) {
3629             if (!calledFromValidUserLocked()) {
3630                 return;
3631             }
3632             if(!canShowInputMethodPickerLocked(client)) {
3633                 Slog.w(TAG, "Ignoring showInputMethodPickerFromClient of uid "
3634                         + Binder.getCallingUid() + ": " + client);
3635                 return;
3636             }
3637 
3638             // Always call subtype picker, because subtype picker is a superset of input method
3639             // picker.
3640             mHandler.sendMessage(mCaller.obtainMessageII(
3641                     MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode,
3642                     (mCurClient != null) ? mCurClient.selfReportedDisplayId : DEFAULT_DISPLAY));
3643         }
3644     }
3645 
3646     @Override
showInputMethodPickerFromSystem(IInputMethodClient client, int auxiliarySubtypeMode, int displayId)3647     public void showInputMethodPickerFromSystem(IInputMethodClient client, int auxiliarySubtypeMode,
3648             int displayId) {
3649         if (mContext.checkCallingPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
3650                 != PackageManager.PERMISSION_GRANTED) {
3651             throw new SecurityException(
3652                     "showInputMethodPickerFromSystem requires WRITE_SECURE_SETTINGS permission");
3653         }
3654         // Always call subtype picker, because subtype picker is a superset of input method
3655         // picker.
3656         mHandler.sendMessage(mCaller.obtainMessageII(
3657                 MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode, displayId));
3658     }
3659 
isInputMethodPickerShownForTest()3660     public boolean isInputMethodPickerShownForTest() {
3661         synchronized(mMethodMap) {
3662             if (mSwitchingDialog == null) {
3663                 return false;
3664             }
3665             return mSwitchingDialog.isShowing();
3666         }
3667     }
3668 
3669     @BinderThread
setInputMethod(@onNull IBinder token, String id)3670     private void setInputMethod(@NonNull IBinder token, String id) {
3671         synchronized (mMethodMap) {
3672             if (!calledWithValidTokenLocked(token)) {
3673                 return;
3674             }
3675             setInputMethodWithSubtypeIdLocked(token, id, NOT_A_SUBTYPE_ID);
3676         }
3677     }
3678 
3679     @BinderThread
setInputMethodAndSubtype(@onNull IBinder token, String id, InputMethodSubtype subtype)3680     private void setInputMethodAndSubtype(@NonNull IBinder token, String id,
3681             InputMethodSubtype subtype) {
3682         synchronized (mMethodMap) {
3683             if (!calledWithValidTokenLocked(token)) {
3684                 return;
3685             }
3686             if (subtype != null) {
3687                 setInputMethodWithSubtypeIdLocked(token, id,
3688                         InputMethodUtils.getSubtypeIdFromHashCode(mMethodMap.get(id),
3689                                 subtype.hashCode()));
3690             } else {
3691                 setInputMethod(token, id);
3692             }
3693         }
3694     }
3695 
3696     @Override
showInputMethodAndSubtypeEnablerFromClient( IInputMethodClient client, String inputMethodId)3697     public void showInputMethodAndSubtypeEnablerFromClient(
3698             IInputMethodClient client, String inputMethodId) {
3699         synchronized (mMethodMap) {
3700             // TODO(yukawa): Should we verify the display ID?
3701             if (!calledFromValidUserLocked()) {
3702                 return;
3703             }
3704             executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
3705                     MSG_SHOW_IM_SUBTYPE_ENABLER, inputMethodId));
3706         }
3707     }
3708 
3709     @BinderThread
switchToPreviousInputMethod(@onNull IBinder token)3710     private boolean switchToPreviousInputMethod(@NonNull IBinder token) {
3711         synchronized (mMethodMap) {
3712             if (!calledWithValidTokenLocked(token)) {
3713                 return false;
3714             }
3715             final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
3716             final InputMethodInfo lastImi;
3717             if (lastIme != null) {
3718                 lastImi = mMethodMap.get(lastIme.first);
3719             } else {
3720                 lastImi = null;
3721             }
3722             String targetLastImiId = null;
3723             int subtypeId = NOT_A_SUBTYPE_ID;
3724             if (lastIme != null && lastImi != null) {
3725                 final boolean imiIdIsSame = lastImi.getId().equals(mCurMethodId);
3726                 final int lastSubtypeHash = Integer.parseInt(lastIme.second);
3727                 final int currentSubtypeHash = mCurrentSubtype == null ? NOT_A_SUBTYPE_ID
3728                         : mCurrentSubtype.hashCode();
3729                 // If the last IME is the same as the current IME and the last subtype is not
3730                 // defined, there is no need to switch to the last IME.
3731                 if (!imiIdIsSame || lastSubtypeHash != currentSubtypeHash) {
3732                     targetLastImiId = lastIme.first;
3733                     subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(lastImi, lastSubtypeHash);
3734                 }
3735             }
3736 
3737             if (TextUtils.isEmpty(targetLastImiId)
3738                     && !InputMethodUtils.canAddToLastInputMethod(mCurrentSubtype)) {
3739                 // This is a safety net. If the currentSubtype can't be added to the history
3740                 // and the framework couldn't find the last ime, we will make the last ime be
3741                 // the most applicable enabled keyboard subtype of the system imes.
3742                 final List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
3743                 if (enabled != null) {
3744                     final int N = enabled.size();
3745                     final String locale = mCurrentSubtype == null
3746                             ? mRes.getConfiguration().locale.toString()
3747                             : mCurrentSubtype.getLocale();
3748                     for (int i = 0; i < N; ++i) {
3749                         final InputMethodInfo imi = enabled.get(i);
3750                         if (imi.getSubtypeCount() > 0 && imi.isSystem()) {
3751                             InputMethodSubtype keyboardSubtype =
3752                                     InputMethodUtils.findLastResortApplicableSubtypeLocked(mRes,
3753                                             InputMethodUtils.getSubtypes(imi),
3754                                             InputMethodUtils.SUBTYPE_MODE_KEYBOARD, locale, true);
3755                             if (keyboardSubtype != null) {
3756                                 targetLastImiId = imi.getId();
3757                                 subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
3758                                         imi, keyboardSubtype.hashCode());
3759                                 if(keyboardSubtype.getLocale().equals(locale)) {
3760                                     break;
3761                                 }
3762                             }
3763                         }
3764                     }
3765                 }
3766             }
3767 
3768             if (!TextUtils.isEmpty(targetLastImiId)) {
3769                 if (DEBUG) {
3770                     Slog.d(TAG, "Switch to: " + lastImi.getId() + ", " + lastIme.second
3771                             + ", from: " + mCurMethodId + ", " + subtypeId);
3772                 }
3773                 setInputMethodWithSubtypeIdLocked(token, targetLastImiId, subtypeId);
3774                 return true;
3775             } else {
3776                 return false;
3777             }
3778         }
3779     }
3780 
3781     @BinderThread
switchToNextInputMethod(@onNull IBinder token, boolean onlyCurrentIme)3782     private boolean switchToNextInputMethod(@NonNull IBinder token, boolean onlyCurrentIme) {
3783         synchronized (mMethodMap) {
3784             if (!calledWithValidTokenLocked(token)) {
3785                 return false;
3786             }
3787             final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
3788                     onlyCurrentIme, mMethodMap.get(mCurMethodId), mCurrentSubtype);
3789             if (nextSubtype == null) {
3790                 return false;
3791             }
3792             setInputMethodWithSubtypeIdLocked(token, nextSubtype.mImi.getId(),
3793                     nextSubtype.mSubtypeId);
3794             return true;
3795         }
3796     }
3797 
3798     @BinderThread
shouldOfferSwitchingToNextInputMethod(@onNull IBinder token)3799     private boolean shouldOfferSwitchingToNextInputMethod(@NonNull IBinder token) {
3800         synchronized (mMethodMap) {
3801             if (!calledWithValidTokenLocked(token)) {
3802                 return false;
3803             }
3804             final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
3805                     false /* onlyCurrentIme */, mMethodMap.get(mCurMethodId), mCurrentSubtype);
3806             if (nextSubtype == null) {
3807                 return false;
3808             }
3809             return true;
3810         }
3811     }
3812 
3813     @Override
getLastInputMethodSubtype()3814     public InputMethodSubtype getLastInputMethodSubtype() {
3815         synchronized (mMethodMap) {
3816             if (!calledFromValidUserLocked()) {
3817                 return null;
3818             }
3819             final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
3820             // TODO: Handle the case of the last IME with no subtypes
3821             if (lastIme == null || TextUtils.isEmpty(lastIme.first)
3822                     || TextUtils.isEmpty(lastIme.second)) return null;
3823             final InputMethodInfo lastImi = mMethodMap.get(lastIme.first);
3824             if (lastImi == null) return null;
3825             try {
3826                 final int lastSubtypeHash = Integer.parseInt(lastIme.second);
3827                 final int lastSubtypeId =
3828                         InputMethodUtils.getSubtypeIdFromHashCode(lastImi, lastSubtypeHash);
3829                 if (lastSubtypeId < 0 || lastSubtypeId >= lastImi.getSubtypeCount()) {
3830                     return null;
3831                 }
3832                 return lastImi.getSubtypeAt(lastSubtypeId);
3833             } catch (NumberFormatException e) {
3834                 return null;
3835             }
3836         }
3837     }
3838 
3839     @Override
setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes)3840     public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
3841         // By this IPC call, only a process which shares the same uid with the IME can add
3842         // additional input method subtypes to the IME.
3843         if (TextUtils.isEmpty(imiId) || subtypes == null) return;
3844         final ArrayList<InputMethodSubtype> toBeAdded = new ArrayList<>();
3845         for (InputMethodSubtype subtype : subtypes) {
3846             if (!toBeAdded.contains(subtype)) {
3847                 toBeAdded.add(subtype);
3848             } else {
3849                 Slog.w(TAG, "Duplicated subtype definition found: "
3850                         + subtype.getLocale() + ", " + subtype.getMode());
3851             }
3852         }
3853         synchronized (mMethodMap) {
3854             if (!calledFromValidUserLocked()) {
3855                 return;
3856             }
3857             if (!mSystemReady) {
3858                 return;
3859             }
3860             final InputMethodInfo imi = mMethodMap.get(imiId);
3861             if (imi == null) return;
3862             final String[] packageInfos;
3863             try {
3864                 packageInfos = mIPackageManager.getPackagesForUid(Binder.getCallingUid());
3865             } catch (RemoteException e) {
3866                 Slog.e(TAG, "Failed to get package infos");
3867                 return;
3868             }
3869             if (packageInfos != null) {
3870                 final int packageNum = packageInfos.length;
3871                 for (int i = 0; i < packageNum; ++i) {
3872                     if (packageInfos[i].equals(imi.getPackageName())) {
3873                         if (subtypes.length > 0) {
3874                             mAdditionalSubtypeMap.put(imi.getId(), toBeAdded);
3875                         } else {
3876                             mAdditionalSubtypeMap.remove(imi.getId());
3877                         }
3878                         AdditionalSubtypeUtils.save(mAdditionalSubtypeMap, mMethodMap,
3879                                 mSettings.getCurrentUserId());
3880                         final long ident = Binder.clearCallingIdentity();
3881                         try {
3882                             buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
3883                         } finally {
3884                             Binder.restoreCallingIdentity(ident);
3885                         }
3886                         return;
3887                     }
3888                 }
3889             }
3890         }
3891         return;
3892     }
3893 
3894     /**
3895      * This is kept due to {@code @UnsupportedAppUsage} in
3896      * {@link InputMethodManager#getInputMethodWindowVisibleHeight()} and a dependency in
3897      * {@link InputMethodService#onCreate()}.
3898      *
3899      * <p>TODO(Bug 113914148): Check if we can remove this.</p>
3900      * @return {@link WindowManagerInternal#getInputMethodWindowVisibleHeight()}
3901      */
3902     @Override
getInputMethodWindowVisibleHeight()3903     public int getInputMethodWindowVisibleHeight() {
3904         // TODO(yukawa): Should we verify the display ID?
3905         return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId);
3906     }
3907 
3908     @Override
reportActivityView(IInputMethodClient parentClient, int childDisplayId, float[] matrixValues)3909     public void reportActivityView(IInputMethodClient parentClient, int childDisplayId,
3910             float[] matrixValues) {
3911         final DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(childDisplayId);
3912         if (displayInfo == null) {
3913             throw new IllegalArgumentException(
3914                     "Cannot find display for non-existent displayId: " + childDisplayId);
3915         }
3916         final int callingUid = Binder.getCallingUid();
3917         if (callingUid != displayInfo.ownerUid) {
3918             throw new SecurityException("The caller doesn't own the display.");
3919         }
3920 
3921         synchronized (mMethodMap) {
3922             final ClientState cs = mClients.get(parentClient.asBinder());
3923             if (cs == null) {
3924                 return;
3925             }
3926 
3927             // null matrixValues means that the entry needs to be removed.
3928             if (matrixValues == null) {
3929                 final ActivityViewInfo info = mActivityViewDisplayIdToParentMap.get(childDisplayId);
3930                 if (info == null) {
3931                     return;
3932                 }
3933                 if (info.mParentClient != cs) {
3934                     throw new SecurityException("Only the owner client can clear"
3935                             + " ActivityViewGeometry for display #" + childDisplayId);
3936                 }
3937                 mActivityViewDisplayIdToParentMap.remove(childDisplayId);
3938                 return;
3939             }
3940 
3941             ActivityViewInfo info = mActivityViewDisplayIdToParentMap.get(childDisplayId);
3942             if (info != null && info.mParentClient != cs) {
3943                 throw new InvalidParameterException("Display #" + childDisplayId
3944                         + " is already registered by " + info.mParentClient);
3945             }
3946             if (info == null) {
3947                 if (!mWindowManagerInternal.isUidAllowedOnDisplay(childDisplayId, cs.uid)) {
3948                     throw new SecurityException(cs + " cannot access to display #"
3949                             + childDisplayId);
3950                 }
3951                 info = new ActivityViewInfo(cs, new Matrix());
3952                 mActivityViewDisplayIdToParentMap.put(childDisplayId, info);
3953             }
3954             info.mMatrix.setValues(matrixValues);
3955 
3956             if (mCurClient == null || mCurClient.curSession == null) {
3957                 return;
3958             }
3959 
3960             Matrix matrix = null;
3961             int displayId = mCurClient.selfReportedDisplayId;
3962             boolean needToNotify = false;
3963             while (true) {
3964                 needToNotify |= (displayId == childDisplayId);
3965                 final ActivityViewInfo next = mActivityViewDisplayIdToParentMap.get(displayId);
3966                 if (next == null) {
3967                     break;
3968                 }
3969                 if (matrix == null) {
3970                     matrix = new Matrix(next.mMatrix);
3971                 } else {
3972                     matrix.postConcat(next.mMatrix);
3973                 }
3974                 if (next.mParentClient.selfReportedDisplayId == mCurTokenDisplayId) {
3975                     if (needToNotify) {
3976                         final float[] values = new float[9];
3977                         matrix.getValues(values);
3978                         try {
3979                             mCurClient.client.updateActivityViewToScreenMatrix(mCurSeq, values);
3980                         } catch (RemoteException e) {
3981                         }
3982                     }
3983                     break;
3984                 }
3985                 displayId = info.mParentClient.selfReportedDisplayId;
3986             }
3987         }
3988     }
3989 
3990     @Override
removeImeSurface()3991     public void removeImeSurface() {
3992         mContext.enforceCallingPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW, null);
3993         mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE));
3994     }
3995 
3996     @Override
removeImeSurfaceFromWindow(IBinder windowToken)3997     public void removeImeSurfaceFromWindow(IBinder windowToken) {
3998         // No permission check, because we'll only execute the request if the calling window is
3999         // also the current IME client.
4000         mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE_FROM_WINDOW, windowToken).sendToTarget();
4001     }
4002 
4003     @BinderThread
notifyUserAction(@onNull IBinder token)4004     private void notifyUserAction(@NonNull IBinder token) {
4005         if (DEBUG) {
4006             Slog.d(TAG, "Got the notification of a user action.");
4007         }
4008         synchronized (mMethodMap) {
4009             if (mCurToken != token) {
4010                 if (DEBUG) {
4011                     Slog.d(TAG, "Ignoring the user action notification from IMEs that are no longer"
4012                             + " active.");
4013                 }
4014                 return;
4015             }
4016             final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
4017             if (imi != null) {
4018                 mSwitchingController.onUserActionLocked(imi, mCurrentSubtype);
4019             }
4020         }
4021     }
4022 
4023     @BinderThread
reportPreRendered(IBinder token, EditorInfo info)4024     private void reportPreRendered(IBinder token, EditorInfo info) {
4025         synchronized (mMethodMap) {
4026             if (!calledWithValidTokenLocked(token)) {
4027                 return;
4028             }
4029             if (mCurClient != null && mCurClient.client != null) {
4030                 executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
4031                         MSG_REPORT_PRE_RENDERED, info, mCurClient));
4032             }
4033         }
4034     }
4035 
4036     @BinderThread
applyImeVisibility(IBinder token, IBinder windowToken, boolean setVisible)4037     private void applyImeVisibility(IBinder token, IBinder windowToken, boolean setVisible) {
4038         synchronized (mMethodMap) {
4039             if (!calledWithValidTokenLocked(token)) {
4040                 return;
4041             }
4042             if (!setVisible) {
4043                 if (mCurClient != null) {
4044                     // IMMS only knows of focused window, not the actual IME target.
4045                     // e.g. it isn't aware of any window that has both
4046                     // NOT_FOCUSABLE, ALT_FOCUSABLE_IM flags set and can the IME target.
4047                     // Send it to window manager to hide IME from IME target window.
4048                     // TODO(b/139861270): send to mCurClient.client once IMMS is aware of
4049                     // actual IME target.
4050                     mWindowManagerInternal.hideIme(
4051                             mHideRequestWindowMap.get(windowToken),
4052                             mCurClient.selfReportedDisplayId);
4053                 }
4054             } else {
4055                 // Send to window manager to show IME after IME layout finishes.
4056                 mWindowManagerInternal.showImePostLayout(mShowRequestWindowMap.get(windowToken));
4057             }
4058         }
4059     }
4060 
setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId)4061     private void setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId) {
4062         if (token == null) {
4063             if (mContext.checkCallingOrSelfPermission(
4064                     android.Manifest.permission.WRITE_SECURE_SETTINGS)
4065                     != PackageManager.PERMISSION_GRANTED) {
4066                 throw new SecurityException(
4067                         "Using null token requires permission "
4068                         + android.Manifest.permission.WRITE_SECURE_SETTINGS);
4069             }
4070         } else if (mCurToken != token) {
4071             Slog.w(TAG, "Ignoring setInputMethod of uid " + Binder.getCallingUid()
4072                     + " token: " + token);
4073             return;
4074         }
4075 
4076         final long ident = Binder.clearCallingIdentity();
4077         try {
4078             setInputMethodLocked(id, subtypeId);
4079         } finally {
4080             Binder.restoreCallingIdentity(ident);
4081         }
4082     }
4083 
4084     @BinderThread
hideMySoftInput(@onNull IBinder token, int flags)4085     private void hideMySoftInput(@NonNull IBinder token, int flags) {
4086         synchronized (mMethodMap) {
4087             if (!calledWithValidTokenLocked(token)) {
4088                 return;
4089             }
4090             long ident = Binder.clearCallingIdentity();
4091             try {
4092                 hideCurrentInputLocked(
4093                         mLastImeTargetWindow, flags, null,
4094                         SoftInputShowHideReason.HIDE_MY_SOFT_INPUT);
4095 
4096             } finally {
4097                 Binder.restoreCallingIdentity(ident);
4098             }
4099         }
4100     }
4101 
4102     @BinderThread
showMySoftInput(@onNull IBinder token, int flags)4103     private void showMySoftInput(@NonNull IBinder token, int flags) {
4104         synchronized (mMethodMap) {
4105             if (!calledWithValidTokenLocked(token)) {
4106                 return;
4107             }
4108             long ident = Binder.clearCallingIdentity();
4109             try {
4110                 showCurrentInputLocked(mLastImeTargetWindow, flags, null,
4111                         SoftInputShowHideReason.SHOW_MY_SOFT_INPUT);
4112             } finally {
4113                 Binder.restoreCallingIdentity(ident);
4114             }
4115         }
4116     }
4117 
setEnabledSessionInMainThread(SessionState session)4118     void setEnabledSessionInMainThread(SessionState session) {
4119         if (mEnabledSession != session) {
4120             if (mEnabledSession != null && mEnabledSession.session != null) {
4121                 try {
4122                     if (DEBUG) Slog.v(TAG, "Disabling: " + mEnabledSession);
4123                     mEnabledSession.method.setSessionEnabled(mEnabledSession.session, false);
4124                 } catch (RemoteException e) {
4125                 }
4126             }
4127             mEnabledSession = session;
4128             if (mEnabledSession != null && mEnabledSession.session != null) {
4129                 try {
4130                     if (DEBUG) Slog.v(TAG, "Enabling: " + mEnabledSession);
4131                     mEnabledSession.method.setSessionEnabled(mEnabledSession.session, true);
4132                 } catch (RemoteException e) {
4133                 }
4134             }
4135         }
4136     }
4137 
4138     @MainThread
4139     @Override
handleMessage(Message msg)4140     public boolean handleMessage(Message msg) {
4141         SomeArgs args;
4142         switch (msg.what) {
4143             case MSG_SHOW_IM_SUBTYPE_PICKER:
4144                 final boolean showAuxSubtypes;
4145                 final int displayId = msg.arg2;
4146                 switch (msg.arg1) {
4147                     case InputMethodManager.SHOW_IM_PICKER_MODE_AUTO:
4148                         // This is undocumented so far, but IMM#showInputMethodPicker() has been
4149                         // implemented so that auxiliary subtypes will be excluded when the soft
4150                         // keyboard is invisible.
4151                         showAuxSubtypes = mInputShown;
4152                         break;
4153                     case InputMethodManager.SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES:
4154                         showAuxSubtypes = true;
4155                         break;
4156                     case InputMethodManager.SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES:
4157                         showAuxSubtypes = false;
4158                         break;
4159                     default:
4160                         Slog.e(TAG, "Unknown subtype picker mode = " + msg.arg1);
4161                         return false;
4162                 }
4163                 showInputMethodMenu(showAuxSubtypes, displayId);
4164                 return true;
4165 
4166             case MSG_SHOW_IM_SUBTYPE_ENABLER:
4167                 showInputMethodAndSubtypeEnabler((String)msg.obj);
4168                 return true;
4169 
4170             case MSG_SHOW_IM_CONFIG:
4171                 showConfigureInputMethods();
4172                 return true;
4173 
4174             // ---------------------------------------------------------
4175 
4176             case MSG_UNBIND_INPUT:
4177                 try {
4178                     ((IInputMethod)msg.obj).unbindInput();
4179                 } catch (RemoteException e) {
4180                     // There is nothing interesting about the method dying.
4181                 }
4182                 return true;
4183             case MSG_BIND_INPUT:
4184                 args = (SomeArgs)msg.obj;
4185                 try {
4186                     ((IInputMethod)args.arg1).bindInput((InputBinding)args.arg2);
4187                 } catch (RemoteException e) {
4188                 }
4189                 args.recycle();
4190                 return true;
4191             case MSG_SHOW_SOFT_INPUT:
4192                 args = (SomeArgs) msg.obj;
4193                 try {
4194                     final @SoftInputShowHideReason int reason = msg.arg2;
4195                     if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput("
4196                             + args.arg3 + ", " + msg.arg1 + ", " + args.arg2 + ") for reason: "
4197                             + InputMethodDebug.softInputDisplayReasonToString(reason));
4198                     ((IInputMethod) args.arg1).showSoftInput(
4199                             (IBinder) args.arg3, msg.arg1, (ResultReceiver) args.arg2);
4200                     mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry(
4201                             mCurClient, mCurAttribute,
4202                             mWindowManagerInternal.getWindowName(mCurFocusedWindow),
4203                             mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode,
4204                             mWindowManagerInternal.getWindowName(
4205                                     mShowRequestWindowMap.get(args.arg3)),
4206                             mWindowManagerInternal.getImeControlTargetNameForLogging(
4207                                     mCurTokenDisplayId),
4208                             mWindowManagerInternal.getImeTargetNameForLogging(
4209                                     mCurTokenDisplayId)));
4210                 } catch (RemoteException e) {
4211                 }
4212                 args.recycle();
4213                 return true;
4214             case MSG_HIDE_SOFT_INPUT:
4215                 args = (SomeArgs) msg.obj;
4216                 try {
4217                     final @SoftInputShowHideReason int reason = msg.arg1;
4218                     if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, "
4219                             + args.arg3 + ", " + args.arg2 + ") for reason: "
4220                             + InputMethodDebug.softInputDisplayReasonToString(reason));
4221                     ((IInputMethod)args.arg1).hideSoftInput(
4222                             (IBinder) args.arg3, 0, (ResultReceiver)args.arg2);
4223                     mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry(
4224                             mCurClient, mCurAttribute,
4225                             mWindowManagerInternal.getWindowName(mCurFocusedWindow),
4226                             mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode,
4227                             mWindowManagerInternal.getWindowName(
4228                                     mHideRequestWindowMap.get(args.arg3)),
4229                             mWindowManagerInternal.getImeControlTargetNameForLogging(
4230                                     mCurTokenDisplayId),
4231                             mWindowManagerInternal.getImeTargetNameForLogging(
4232                                     mCurTokenDisplayId)));
4233                 } catch (RemoteException e) {
4234                 }
4235                 args.recycle();
4236                 return true;
4237             case MSG_HIDE_CURRENT_INPUT_METHOD:
4238                 synchronized (mMethodMap) {
4239                     final @SoftInputShowHideReason int reason = (int) msg.obj;
4240                     hideCurrentInputLocked(mCurFocusedWindow, 0, null, reason);
4241 
4242                 }
4243                 return true;
4244             case MSG_INITIALIZE_IME:
4245                 args = (SomeArgs)msg.obj;
4246                 try {
4247                     if (DEBUG) {
4248                         Slog.v(TAG, "Sending attach of token: " + args.arg2 + " for display: "
4249                                 + msg.arg1);
4250                     }
4251                     final IBinder token = (IBinder) args.arg2;
4252                     ((IInputMethod) args.arg1).initializeInternal(token, msg.arg1,
4253                             new InputMethodPrivilegedOperationsImpl(this, token));
4254                 } catch (RemoteException e) {
4255                 }
4256                 args.recycle();
4257                 return true;
4258             case MSG_CREATE_SESSION: {
4259                 args = (SomeArgs)msg.obj;
4260                 IInputMethod method = (IInputMethod)args.arg1;
4261                 InputChannel channel = (InputChannel)args.arg2;
4262                 try {
4263                     method.createSession(channel, (IInputSessionCallback)args.arg3);
4264                 } catch (RemoteException e) {
4265                 } finally {
4266                     // Dispose the channel if the input method is not local to this process
4267                     // because the remote proxy will get its own copy when unparceled.
4268                     if (channel != null && Binder.isProxy(method)) {
4269                         channel.dispose();
4270                     }
4271                 }
4272                 args.recycle();
4273                 return true;
4274             }
4275             case MSG_REMOVE_IME_SURFACE: {
4276                 synchronized (mMethodMap) {
4277                     try {
4278                         if (mEnabledSession != null && mEnabledSession.session != null
4279                                 && !mShowRequested) {
4280                             mEnabledSession.session.removeImeSurface();
4281                         }
4282                     } catch (RemoteException e) {
4283                     }
4284                 }
4285                 return true;
4286             }
4287             case MSG_REMOVE_IME_SURFACE_FROM_WINDOW: {
4288                 IBinder windowToken = (IBinder) msg.obj;
4289                 synchronized (mMethodMap) {
4290                     try {
4291                         if (windowToken == mCurFocusedWindow
4292                                 && mEnabledSession != null && mEnabledSession.session != null) {
4293                             mEnabledSession.session.removeImeSurface();
4294                         }
4295                     } catch (RemoteException e) {
4296                     }
4297                 }
4298                 return true;
4299             }
4300             // ---------------------------------------------------------
4301 
4302             case MSG_START_INPUT: {
4303                 final int missingMethods = msg.arg1;
4304                 final boolean restarting = msg.arg2 != 0;
4305                 args = (SomeArgs) msg.obj;
4306                 final IBinder startInputToken = (IBinder) args.arg1;
4307                 final SessionState session = (SessionState) args.arg2;
4308                 final IInputContext inputContext = (IInputContext) args.arg3;
4309                 final EditorInfo editorInfo = (EditorInfo) args.arg4;
4310                 try {
4311                     setEnabledSessionInMainThread(session);
4312                     session.method.startInput(startInputToken, inputContext, missingMethods,
4313                             editorInfo, restarting, session.client.shouldPreRenderIme);
4314                 } catch (RemoteException e) {
4315                 }
4316                 args.recycle();
4317                 return true;
4318             }
4319 
4320             // ---------------------------------------------------------
4321 
4322             case MSG_UNBIND_CLIENT:
4323                 try {
4324                     ((IInputMethodClient)msg.obj).onUnbindMethod(msg.arg1, msg.arg2);
4325                 } catch (RemoteException e) {
4326                     // There is nothing interesting about the last client dying.
4327                 }
4328                 return true;
4329             case MSG_BIND_CLIENT: {
4330                 args = (SomeArgs)msg.obj;
4331                 IInputMethodClient client = (IInputMethodClient)args.arg1;
4332                 InputBindResult res = (InputBindResult)args.arg2;
4333                 try {
4334                     client.onBindMethod(res);
4335                 } catch (RemoteException e) {
4336                     Slog.w(TAG, "Client died receiving input method " + args.arg2);
4337                 } finally {
4338                     // Dispose the channel if the input method is not local to this process
4339                     // because the remote proxy will get its own copy when unparceled.
4340                     if (res.channel != null && Binder.isProxy(client)) {
4341                         res.channel.dispose();
4342                     }
4343                 }
4344                 args.recycle();
4345                 return true;
4346             }
4347             case MSG_SET_ACTIVE:
4348                 try {
4349                     ((ClientState)msg.obj).client.setActive(msg.arg1 != 0, msg.arg2 != 0);
4350                 } catch (RemoteException e) {
4351                     Slog.w(TAG, "Got RemoteException sending setActive(false) notification to pid "
4352                             + ((ClientState)msg.obj).pid + " uid "
4353                             + ((ClientState)msg.obj).uid);
4354                 }
4355                 return true;
4356             case MSG_SET_INTERACTIVE:
4357                 handleSetInteractive(msg.arg1 != 0);
4358                 return true;
4359             case MSG_REPORT_FULLSCREEN_MODE: {
4360                 final boolean fullscreen = msg.arg1 != 0;
4361                 final ClientState clientState = (ClientState)msg.obj;
4362                 try {
4363                     clientState.client.reportFullscreenMode(fullscreen);
4364                 } catch (RemoteException e) {
4365                     Slog.w(TAG, "Got RemoteException sending "
4366                             + "reportFullscreen(" + fullscreen + ") notification to pid="
4367                             + clientState.pid + " uid=" + clientState.uid);
4368                 }
4369                 return true;
4370             }
4371             case MSG_REPORT_PRE_RENDERED: {
4372                 args = (SomeArgs) msg.obj;
4373                 final EditorInfo info = (EditorInfo) args.arg1;
4374                 final ClientState clientState = (ClientState) args.arg2;
4375                 try {
4376                     clientState.client.reportPreRendered(info);
4377                 } catch (RemoteException e) {
4378                     Slog.w(TAG, "Got RemoteException sending "
4379                             + "reportPreRendered(" + info + ") notification to pid="
4380                             + clientState.pid + " uid=" + clientState.uid);
4381                 }
4382                 args.recycle();
4383                 return true;
4384             }
4385             case MSG_APPLY_IME_VISIBILITY: {
4386                 final boolean setVisible = msg.arg1 != 0;
4387                 final ClientState clientState = (ClientState) msg.obj;
4388                 try {
4389                     clientState.client.applyImeVisibility(setVisible);
4390                 } catch (RemoteException e) {
4391                     Slog.w(TAG, "Got RemoteException sending "
4392                             + "applyImeVisibility(" + setVisible + ") notification to pid="
4393                             + clientState.pid + " uid=" + clientState.uid);
4394                 }
4395                 return true;
4396             }
4397 
4398             // --------------------------------------------------------------
4399             case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
4400                 mHardKeyboardListener.handleHardKeyboardStatusChange(msg.arg1 == 1);
4401                 return true;
4402             case MSG_SYSTEM_UNLOCK_USER: {
4403                 final int userId = msg.arg1;
4404                 onUnlockUser(userId);
4405                 return true;
4406             }
4407             case MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED: {
4408                 final int userId = msg.arg1;
4409                 final List<InputMethodInfo> imes = (List<InputMethodInfo>) msg.obj;
4410                 mInputMethodListListeners.forEach(
4411                         listener -> listener.onInputMethodListUpdated(imes, userId));
4412                 return true;
4413             }
4414 
4415             // ---------------------------------------------------------------
4416             case MSG_INLINE_SUGGESTIONS_REQUEST: {
4417                 args = (SomeArgs) msg.obj;
4418                 final InlineSuggestionsRequestInfo requestInfo =
4419                         (InlineSuggestionsRequestInfo) args.arg2;
4420                 final IInlineSuggestionsRequestCallback callback =
4421                         (IInlineSuggestionsRequestCallback) args.arg3;
4422                 try {
4423                     ((IInputMethod) args.arg1).onCreateInlineSuggestionsRequest(requestInfo,
4424                             callback);
4425                 } catch (RemoteException e) {
4426                     Slog.w(TAG, "RemoteException calling onCreateInlineSuggestionsRequest(): " + e);
4427                 }
4428                 args.recycle();
4429                 return true;
4430             }
4431 
4432             // ---------------------------------------------------------------
4433             case MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE: {
4434                 if (mAudioManagerInternal == null) {
4435                     mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
4436                 }
4437                 if (mAudioManagerInternal != null) {
4438                     mAudioManagerInternal.setInputMethodServiceUid(msg.arg1 /* uid */);
4439                 }
4440                 return true;
4441             }
4442         }
4443         return false;
4444     }
4445 
handleSetInteractive(final boolean interactive)4446     private void handleSetInteractive(final boolean interactive) {
4447         synchronized (mMethodMap) {
4448             mIsInteractive = interactive;
4449             updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition);
4450 
4451             // Inform the current client of the change in active status
4452             if (mCurClient != null && mCurClient.client != null) {
4453                 executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
4454                         MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, mInFullscreenMode ? 1 : 0,
4455                         mCurClient));
4456             }
4457         }
4458     }
4459 
chooseNewDefaultIMELocked()4460     private boolean chooseNewDefaultIMELocked() {
4461         final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME(
4462                 mSettings.getEnabledInputMethodListLocked());
4463         if (imi != null) {
4464             if (DEBUG) {
4465                 Slog.d(TAG, "New default IME was selected: " + imi.getId());
4466             }
4467             resetSelectedInputMethodAndSubtypeLocked(imi.getId());
4468             return true;
4469         }
4470 
4471         return false;
4472     }
4473 
queryInputMethodServicesInternal(Context context, @UserIdInt int userId, ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap, ArrayMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList)4474     static void queryInputMethodServicesInternal(Context context,
4475             @UserIdInt int userId, ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap,
4476             ArrayMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList) {
4477         methodList.clear();
4478         methodMap.clear();
4479 
4480         // Note: We do not specify PackageManager.MATCH_ENCRYPTION_* flags here because the default
4481         // behavior of PackageManager is exactly what we want.  It by default picks up appropriate
4482         // services depending on the unlock state for the specified user.
4483         final List<ResolveInfo> services = context.getPackageManager().queryIntentServicesAsUser(
4484                 new Intent(InputMethod.SERVICE_INTERFACE),
4485                 PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS,
4486                 userId);
4487 
4488         methodList.ensureCapacity(services.size());
4489         methodMap.ensureCapacity(services.size());
4490 
4491         for (int i = 0; i < services.size(); ++i) {
4492             ResolveInfo ri = services.get(i);
4493             ServiceInfo si = ri.serviceInfo;
4494             final String imeId = InputMethodInfo.computeId(ri);
4495             if (!android.Manifest.permission.BIND_INPUT_METHOD.equals(si.permission)) {
4496                 Slog.w(TAG, "Skipping input method " + imeId
4497                         + ": it does not require the permission "
4498                         + android.Manifest.permission.BIND_INPUT_METHOD);
4499                 continue;
4500             }
4501 
4502             if (DEBUG) Slog.d(TAG, "Checking " + imeId);
4503 
4504             try {
4505                 final InputMethodInfo imi = new InputMethodInfo(context, ri,
4506                         additionalSubtypeMap.get(imeId));
4507                 if (imi.isVrOnly()) {
4508                     continue;  // Skip VR-only IME, which isn't supported for now.
4509                 }
4510                 methodList.add(imi);
4511                 methodMap.put(imi.getId(), imi);
4512                 if (DEBUG) {
4513                     Slog.d(TAG, "Found an input method " + imi);
4514                 }
4515             } catch (Exception e) {
4516                 Slog.wtf(TAG, "Unable to load input method " + imeId, e);
4517             }
4518         }
4519     }
4520 
4521     @GuardedBy("mMethodMap")
buildInputMethodListLocked(boolean resetDefaultEnabledIme)4522     void buildInputMethodListLocked(boolean resetDefaultEnabledIme) {
4523         if (DEBUG) {
4524             Slog.d(TAG, "--- re-buildInputMethodList reset = " + resetDefaultEnabledIme
4525                     + " \n ------ caller=" + Debug.getCallers(10));
4526         }
4527         if (!mSystemReady) {
4528             Slog.e(TAG, "buildInputMethodListLocked is not allowed until system is ready");
4529             return;
4530         }
4531         mMethodMapUpdateCount++;
4532         mMyPackageMonitor.clearKnownImePackageNamesLocked();
4533 
4534         queryInputMethodServicesInternal(mContext, mSettings.getCurrentUserId(),
4535                 mAdditionalSubtypeMap, mMethodMap, mMethodList);
4536 
4537         // Construct the set of possible IME packages for onPackageChanged() to avoid false
4538         // negatives when the package state remains to be the same but only the component state is
4539         // changed.
4540         {
4541             // Here we intentionally use PackageManager.MATCH_DISABLED_COMPONENTS since the purpose
4542             // of this query is to avoid false negatives.  PackageManager.MATCH_ALL could be more
4543             // conservative, but it seems we cannot use it for now (Issue 35176630).
4544             final List<ResolveInfo> allInputMethodServices =
4545                     mContext.getPackageManager().queryIntentServicesAsUser(
4546                             new Intent(InputMethod.SERVICE_INTERFACE),
4547                             PackageManager.MATCH_DISABLED_COMPONENTS, mSettings.getCurrentUserId());
4548             final int N = allInputMethodServices.size();
4549             for (int i = 0; i < N; ++i) {
4550                 final ServiceInfo si = allInputMethodServices.get(i).serviceInfo;
4551                 if (android.Manifest.permission.BIND_INPUT_METHOD.equals(si.permission)) {
4552                     mMyPackageMonitor.addKnownImePackageNameLocked(si.packageName);
4553                 }
4554             }
4555         }
4556 
4557         boolean reenableMinimumNonAuxSystemImes = false;
4558         // TODO: The following code should find better place to live.
4559         if (!resetDefaultEnabledIme) {
4560             boolean enabledImeFound = false;
4561             boolean enabledNonAuxImeFound = false;
4562             final List<InputMethodInfo> enabledImes = mSettings.getEnabledInputMethodListLocked();
4563             final int N = enabledImes.size();
4564             for (int i = 0; i < N; ++i) {
4565                 final InputMethodInfo imi = enabledImes.get(i);
4566                 if (mMethodList.contains(imi)) {
4567                     enabledImeFound = true;
4568                     if (!imi.isAuxiliaryIme()) {
4569                         enabledNonAuxImeFound = true;
4570                         break;
4571                     }
4572                 }
4573             }
4574             if (!enabledImeFound) {
4575                 if (DEBUG) {
4576                     Slog.i(TAG, "All the enabled IMEs are gone. Reset default enabled IMEs.");
4577                 }
4578                 resetDefaultEnabledIme = true;
4579                 resetSelectedInputMethodAndSubtypeLocked("");
4580             } else if (!enabledNonAuxImeFound) {
4581                 if (DEBUG) {
4582                     Slog.i(TAG, "All the enabled non-Aux IMEs are gone. Do partial reset.");
4583                 }
4584                 reenableMinimumNonAuxSystemImes = true;
4585             }
4586         }
4587 
4588         if (resetDefaultEnabledIme || reenableMinimumNonAuxSystemImes) {
4589             final ArrayList<InputMethodInfo> defaultEnabledIme =
4590                     InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList,
4591                             reenableMinimumNonAuxSystemImes);
4592             final int N = defaultEnabledIme.size();
4593             for (int i = 0; i < N; ++i) {
4594                 final InputMethodInfo imi =  defaultEnabledIme.get(i);
4595                 if (DEBUG) {
4596                     Slog.d(TAG, "--- enable ime = " + imi);
4597                 }
4598                 setInputMethodEnabledLocked(imi.getId(), true);
4599             }
4600         }
4601 
4602         final String defaultImiId = mSettings.getSelectedInputMethod();
4603         if (!TextUtils.isEmpty(defaultImiId)) {
4604             if (!mMethodMap.containsKey(defaultImiId)) {
4605                 Slog.w(TAG, "Default IME is uninstalled. Choose new default IME.");
4606                 if (chooseNewDefaultIMELocked()) {
4607                     updateInputMethodsFromSettingsLocked(true);
4608                 }
4609             } else {
4610                 // Double check that the default IME is certainly enabled.
4611                 setInputMethodEnabledLocked(defaultImiId, true);
4612             }
4613         }
4614         // Here is not the perfect place to reset the switching controller. Ideally
4615         // mSwitchingController and mSettings should be able to share the same state.
4616         // TODO: Make sure that mSwitchingController and mSettings are sharing the
4617         // the same enabled IMEs list.
4618         mSwitchingController.resetCircularListLocked(mContext);
4619 
4620         // Notify InputMethodListListeners of the new installed InputMethods.
4621         final List<InputMethodInfo> inputMethodList = new ArrayList<>(mMethodList);
4622         mHandler.obtainMessage(MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED,
4623                 mSettings.getCurrentUserId(), 0 /* unused */, inputMethodList).sendToTarget();
4624     }
4625 
4626     // ----------------------------------------------------------------------
4627 
showInputMethodAndSubtypeEnabler(String inputMethodId)4628     private void showInputMethodAndSubtypeEnabler(String inputMethodId) {
4629         Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS);
4630         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
4631                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
4632                 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
4633         if (!TextUtils.isEmpty(inputMethodId)) {
4634             intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, inputMethodId);
4635         }
4636         final int userId;
4637         synchronized (mMethodMap) {
4638             userId = mSettings.getCurrentUserId();
4639         }
4640         mContext.startActivityAsUser(intent, null, UserHandle.of(userId));
4641     }
4642 
showConfigureInputMethods()4643     private void showConfigureInputMethods() {
4644         Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS);
4645         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
4646                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
4647                 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
4648         mContext.startActivityAsUser(intent, null, UserHandle.CURRENT);
4649     }
4650 
isScreenLocked()4651     private boolean isScreenLocked() {
4652         return mKeyguardManager != null
4653                 && mKeyguardManager.isKeyguardLocked() && mKeyguardManager.isKeyguardSecure();
4654     }
4655 
showInputMethodMenu(boolean showAuxSubtypes, int displayId)4656     private void showInputMethodMenu(boolean showAuxSubtypes, int displayId) {
4657         if (DEBUG) Slog.v(TAG, "Show switching menu. showAuxSubtypes=" + showAuxSubtypes);
4658 
4659         final boolean isScreenLocked = isScreenLocked();
4660 
4661         final String lastInputMethodId = mSettings.getSelectedInputMethod();
4662         int lastInputMethodSubtypeId = mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
4663         if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
4664 
4665         synchronized (mMethodMap) {
4666             final List<ImeSubtypeListItem> imList =
4667                     mSwitchingController.getSortedInputMethodAndSubtypeListLocked(
4668                             showAuxSubtypes, isScreenLocked);
4669             if (imList.isEmpty()) {
4670                 return;
4671             }
4672 
4673             hideInputMethodMenuLocked();
4674 
4675             if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
4676                 final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtypeLocked();
4677                 if (currentSubtype != null) {
4678                     final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
4679                     lastInputMethodSubtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
4680                             currentImi, currentSubtype.hashCode());
4681                 }
4682             }
4683 
4684             final int N = imList.size();
4685             mIms = new InputMethodInfo[N];
4686             mSubtypeIds = new int[N];
4687             int checkedItem = 0;
4688             for (int i = 0; i < N; ++i) {
4689                 final ImeSubtypeListItem item = imList.get(i);
4690                 mIms[i] = item.mImi;
4691                 mSubtypeIds[i] = item.mSubtypeId;
4692                 if (mIms[i].getId().equals(lastInputMethodId)) {
4693                     int subtypeId = mSubtypeIds[i];
4694                     if ((subtypeId == NOT_A_SUBTYPE_ID)
4695                             || (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID && subtypeId == 0)
4696                             || (subtypeId == lastInputMethodSubtypeId)) {
4697                         checkedItem = i;
4698                     }
4699                 }
4700             }
4701 
4702             final ActivityThread currentThread = ActivityThread.currentActivityThread();
4703             final Context settingsContext = new ContextThemeWrapper(
4704                     displayId == DEFAULT_DISPLAY ? currentThread.getSystemUiContext()
4705                             : currentThread.createSystemUiContext(displayId),
4706                     com.android.internal.R.style.Theme_DeviceDefault_Settings);
4707 
4708             mDialogBuilder = new AlertDialog.Builder(settingsContext);
4709             mDialogBuilder.setOnCancelListener(new OnCancelListener() {
4710                 @Override
4711                 public void onCancel(DialogInterface dialog) {
4712                     hideInputMethodMenu();
4713                 }
4714             });
4715 
4716             final Context dialogContext = mDialogBuilder.getContext();
4717             final TypedArray a = dialogContext.obtainStyledAttributes(null,
4718                     com.android.internal.R.styleable.DialogPreference,
4719                     com.android.internal.R.attr.alertDialogStyle, 0);
4720             final Drawable dialogIcon = a.getDrawable(
4721                     com.android.internal.R.styleable.DialogPreference_dialogIcon);
4722             a.recycle();
4723 
4724             mDialogBuilder.setIcon(dialogIcon);
4725 
4726             final LayoutInflater inflater = dialogContext.getSystemService(LayoutInflater.class);
4727             final View tv = inflater.inflate(
4728                     com.android.internal.R.layout.input_method_switch_dialog_title, null);
4729             mDialogBuilder.setCustomTitle(tv);
4730 
4731             // Setup layout for a toggle switch of the hardware keyboard
4732             mSwitchingDialogTitleView = tv;
4733             mSwitchingDialogTitleView
4734                     .findViewById(com.android.internal.R.id.hard_keyboard_section)
4735                     .setVisibility(mWindowManagerInternal.isHardKeyboardAvailable()
4736                             ? View.VISIBLE : View.GONE);
4737             final Switch hardKeySwitch = (Switch) mSwitchingDialogTitleView.findViewById(
4738                     com.android.internal.R.id.hard_keyboard_switch);
4739             hardKeySwitch.setChecked(mShowImeWithHardKeyboard);
4740             hardKeySwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
4741                 @Override
4742                 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
4743                     mSettings.setShowImeWithHardKeyboard(isChecked);
4744                     // Ensure that the input method dialog is dismissed when changing
4745                     // the hardware keyboard state.
4746                     hideInputMethodMenu();
4747                 }
4748             });
4749 
4750             final ImeSubtypeListAdapter adapter = new ImeSubtypeListAdapter(dialogContext,
4751                     com.android.internal.R.layout.input_method_switch_item, imList, checkedItem);
4752             final OnClickListener choiceListener = new OnClickListener() {
4753                 @Override
4754                 public void onClick(final DialogInterface dialog, final int which) {
4755                     synchronized (mMethodMap) {
4756                         if (mIms == null || mIms.length <= which || mSubtypeIds == null
4757                                 || mSubtypeIds.length <= which) {
4758                             return;
4759                         }
4760                         final InputMethodInfo im = mIms[which];
4761                         int subtypeId = mSubtypeIds[which];
4762                         adapter.mCheckedItem = which;
4763                         adapter.notifyDataSetChanged();
4764                         hideInputMethodMenu();
4765                         if (im != null) {
4766                             if (subtypeId < 0 || subtypeId >= im.getSubtypeCount()) {
4767                                 subtypeId = NOT_A_SUBTYPE_ID;
4768                             }
4769                             setInputMethodLocked(im.getId(), subtypeId);
4770                         }
4771                     }
4772                 }
4773             };
4774             mDialogBuilder.setSingleChoiceItems(adapter, checkedItem, choiceListener);
4775 
4776             mSwitchingDialog = mDialogBuilder.create();
4777             mSwitchingDialog.setCanceledOnTouchOutside(true);
4778             final Window w = mSwitchingDialog.getWindow();
4779             final LayoutParams attrs = w.getAttributes();
4780             w.setType(LayoutParams.TYPE_INPUT_METHOD_DIALOG);
4781             // Use an alternate token for the dialog for that window manager can group the token
4782             // with other IME windows based on type vs. grouping based on whichever token happens
4783             // to get selected by the system later on.
4784             attrs.token = mSwitchingDialogToken;
4785             attrs.privateFlags |= LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
4786             attrs.setTitle("Select input method");
4787             w.setAttributes(attrs);
4788             updateSystemUiLocked(mImeWindowVis, mBackDisposition);
4789             mSwitchingDialog.show();
4790         }
4791     }
4792 
4793     private static class ImeSubtypeListAdapter extends ArrayAdapter<ImeSubtypeListItem> {
4794         private final LayoutInflater mInflater;
4795         private final int mTextViewResourceId;
4796         private final List<ImeSubtypeListItem> mItemsList;
4797         public int mCheckedItem;
ImeSubtypeListAdapter(Context context, int textViewResourceId, List<ImeSubtypeListItem> itemsList, int checkedItem)4798         public ImeSubtypeListAdapter(Context context, int textViewResourceId,
4799                 List<ImeSubtypeListItem> itemsList, int checkedItem) {
4800             super(context, textViewResourceId, itemsList);
4801 
4802             mTextViewResourceId = textViewResourceId;
4803             mItemsList = itemsList;
4804             mCheckedItem = checkedItem;
4805             mInflater = context.getSystemService(LayoutInflater.class);
4806         }
4807 
4808         @Override
getView(int position, View convertView, ViewGroup parent)4809         public View getView(int position, View convertView, ViewGroup parent) {
4810             final View view = convertView != null ? convertView
4811                     : mInflater.inflate(mTextViewResourceId, null);
4812             if (position < 0 || position >= mItemsList.size()) return view;
4813             final ImeSubtypeListItem item = mItemsList.get(position);
4814             final CharSequence imeName = item.mImeName;
4815             final CharSequence subtypeName = item.mSubtypeName;
4816             final TextView firstTextView = (TextView)view.findViewById(android.R.id.text1);
4817             final TextView secondTextView = (TextView)view.findViewById(android.R.id.text2);
4818             if (TextUtils.isEmpty(subtypeName)) {
4819                 firstTextView.setText(imeName);
4820                 secondTextView.setVisibility(View.GONE);
4821             } else {
4822                 firstTextView.setText(subtypeName);
4823                 secondTextView.setText(imeName);
4824                 secondTextView.setVisibility(View.VISIBLE);
4825             }
4826             final RadioButton radioButton =
4827                     (RadioButton)view.findViewById(com.android.internal.R.id.radio);
4828             radioButton.setChecked(position == mCheckedItem);
4829             return view;
4830         }
4831     }
4832 
hideInputMethodMenu()4833     void hideInputMethodMenu() {
4834         synchronized (mMethodMap) {
4835             hideInputMethodMenuLocked();
4836         }
4837     }
4838 
hideInputMethodMenuLocked()4839     void hideInputMethodMenuLocked() {
4840         if (DEBUG) Slog.v(TAG, "Hide switching menu");
4841 
4842         if (mSwitchingDialog != null) {
4843             mSwitchingDialog.dismiss();
4844             mSwitchingDialog = null;
4845             mSwitchingDialogTitleView = null;
4846         }
4847 
4848         updateSystemUiLocked(mImeWindowVis, mBackDisposition);
4849         mDialogBuilder = null;
4850         mIms = null;
4851     }
4852 
4853     // ----------------------------------------------------------------------
4854 
4855     /**
4856      * Enable or disable the given IME by updating {@link Settings.Secure#ENABLED_INPUT_METHODS}.
4857      *
4858      * @param id ID of the IME is to be manipulated. It is OK to pass IME ID that is currently not
4859      *           recognized by the system.
4860      * @param enabled {@code true} if {@code id} needs to be enabled.
4861      * @return {@code true} if the IME was previously enabled. {@code false} otherwise.
4862      */
setInputMethodEnabledLocked(String id, boolean enabled)4863     private boolean setInputMethodEnabledLocked(String id, boolean enabled) {
4864         List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings
4865                 .getEnabledInputMethodsAndSubtypeListLocked();
4866 
4867         if (enabled) {
4868             for (Pair<String, ArrayList<String>> pair: enabledInputMethodsList) {
4869                 if (pair.first.equals(id)) {
4870                     // We are enabling this input method, but it is already enabled.
4871                     // Nothing to do. The previous state was enabled.
4872                     return true;
4873                 }
4874             }
4875             mSettings.appendAndPutEnabledInputMethodLocked(id, false);
4876             // Previous state was disabled.
4877             return false;
4878         } else {
4879             StringBuilder builder = new StringBuilder();
4880             if (mSettings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
4881                     builder, enabledInputMethodsList, id)) {
4882                 // Disabled input method is currently selected, switch to another one.
4883                 final String selId = mSettings.getSelectedInputMethod();
4884                 if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
4885                     Slog.i(TAG, "Can't find new IME, unsetting the current input method.");
4886                     resetSelectedInputMethodAndSubtypeLocked("");
4887                 }
4888                 // Previous state was enabled.
4889                 return true;
4890             } else {
4891                 // We are disabling the input method but it is already disabled.
4892                 // Nothing to do.  The previous state was disabled.
4893                 return false;
4894             }
4895         }
4896     }
4897 
setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId, boolean setSubtypeOnly)4898     private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
4899             boolean setSubtypeOnly) {
4900         mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype);
4901 
4902         // Set Subtype here
4903         if (imi == null || subtypeId < 0) {
4904             mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
4905             mCurrentSubtype = null;
4906         } else {
4907             if (subtypeId < imi.getSubtypeCount()) {
4908                 InputMethodSubtype subtype = imi.getSubtypeAt(subtypeId);
4909                 mSettings.putSelectedSubtype(subtype.hashCode());
4910                 mCurrentSubtype = subtype;
4911             } else {
4912                 mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
4913                 // If the subtype is not specified, choose the most applicable one
4914                 mCurrentSubtype = getCurrentInputMethodSubtypeLocked();
4915             }
4916         }
4917 
4918         if (!setSubtypeOnly) {
4919             // Set InputMethod here
4920             mSettings.putSelectedInputMethod(imi != null ? imi.getId() : "");
4921         }
4922     }
4923 
resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme)4924     private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) {
4925         InputMethodInfo imi = mMethodMap.get(newDefaultIme);
4926         int lastSubtypeId = NOT_A_SUBTYPE_ID;
4927         // newDefaultIme is empty when there is no candidate for the selected IME.
4928         if (imi != null && !TextUtils.isEmpty(newDefaultIme)) {
4929             String subtypeHashCode = mSettings.getLastSubtypeForInputMethodLocked(newDefaultIme);
4930             if (subtypeHashCode != null) {
4931                 try {
4932                     lastSubtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
4933                             imi, Integer.parseInt(subtypeHashCode));
4934                 } catch (NumberFormatException e) {
4935                     Slog.w(TAG, "HashCode for subtype looks broken: " + subtypeHashCode, e);
4936                 }
4937             }
4938         }
4939         setSelectedInputMethodAndSubtypeLocked(imi, lastSubtypeId, false);
4940     }
4941 
4942     /**
4943      * @return Return the current subtype of this input method.
4944      */
4945     @Override
getCurrentInputMethodSubtype()4946     public InputMethodSubtype getCurrentInputMethodSubtype() {
4947         synchronized (mMethodMap) {
4948             // TODO: Make this work even for non-current users?
4949             if (!calledFromValidUserLocked()) {
4950                 return null;
4951             }
4952             return getCurrentInputMethodSubtypeLocked();
4953         }
4954     }
4955 
getCurrentInputMethodSubtypeLocked()4956     private InputMethodSubtype getCurrentInputMethodSubtypeLocked() {
4957         if (mCurMethodId == null) {
4958             return null;
4959         }
4960         final boolean subtypeIsSelected = mSettings.isSubtypeSelected();
4961         final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
4962         if (imi == null || imi.getSubtypeCount() == 0) {
4963             return null;
4964         }
4965         if (!subtypeIsSelected || mCurrentSubtype == null
4966                 || !InputMethodUtils.isValidSubtypeId(imi, mCurrentSubtype.hashCode())) {
4967             int subtypeId = mSettings.getSelectedInputMethodSubtypeId(mCurMethodId);
4968             if (subtypeId == NOT_A_SUBTYPE_ID) {
4969                 // If there are no selected subtypes, the framework will try to find
4970                 // the most applicable subtype from explicitly or implicitly enabled
4971                 // subtypes.
4972                 List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes =
4973                         mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true);
4974                 // If there is only one explicitly or implicitly enabled subtype,
4975                 // just returns it.
4976                 if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) {
4977                     mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0);
4978                 } else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) {
4979                     mCurrentSubtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
4980                             mRes, explicitlyOrImplicitlyEnabledSubtypes,
4981                             InputMethodUtils.SUBTYPE_MODE_KEYBOARD, null, true);
4982                     if (mCurrentSubtype == null) {
4983                         mCurrentSubtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
4984                                 mRes, explicitlyOrImplicitlyEnabledSubtypes, null, null,
4985                                 true);
4986                     }
4987                 }
4988             } else {
4989                 mCurrentSubtype = InputMethodUtils.getSubtypes(imi).get(subtypeId);
4990             }
4991         }
4992         return mCurrentSubtype;
4993     }
4994 
getInputMethodListAsUser(@serIdInt int userId)4995     private List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
4996         synchronized (mMethodMap) {
4997             return getInputMethodListLocked(userId);
4998         }
4999     }
5000 
getEnabledInputMethodListAsUser(@serIdInt int userId)5001     private List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) {
5002         synchronized (mMethodMap) {
5003             return getEnabledInputMethodListLocked(userId);
5004         }
5005     }
5006 
onCreateInlineSuggestionsRequest(@serIdInt int userId, InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback)5007     private void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
5008             InlineSuggestionsRequestInfo requestInfo,
5009             IInlineSuggestionsRequestCallback callback) {
5010         synchronized (mMethodMap) {
5011             onCreateInlineSuggestionsRequestLocked(userId, requestInfo, callback);
5012         }
5013     }
5014 
switchToInputMethod(String imeId, @UserIdInt int userId)5015     private boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
5016         synchronized (mMethodMap) {
5017             if (userId == mSettings.getCurrentUserId()) {
5018                 if (!mMethodMap.containsKey(imeId)
5019                         || !mSettings.getEnabledInputMethodListLocked()
5020                                 .contains(mMethodMap.get(imeId))) {
5021                     return false; // IME is not is found or not enabled.
5022                 }
5023                 setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID);
5024                 return true;
5025             }
5026             final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
5027             final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
5028             final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
5029                     new ArrayMap<>();
5030             AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
5031             queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
5032                     methodMap, methodList);
5033             final InputMethodSettings settings = new InputMethodSettings(
5034                     mContext.getResources(), mContext.getContentResolver(), methodMap,
5035                     userId, false);
5036             if (!methodMap.containsKey(imeId)
5037                     || !settings.getEnabledInputMethodListLocked()
5038                             .contains(methodMap.get(imeId))) {
5039                 return false; // IME is not is found or not enabled.
5040             }
5041             settings.putSelectedInputMethod(imeId);
5042             settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
5043             return true;
5044         }
5045     }
5046 
transferTouchFocusToImeWindow(@onNull IBinder sourceInputToken, int displayId)5047     private boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken,
5048             int displayId) {
5049         //TODO(b/150843766): Check if Input Token is valid.
5050         final IBinder curHostInputToken;
5051         synchronized (mMethodMap) {
5052             if (displayId != mCurTokenDisplayId || mCurHostInputToken == null) {
5053                 return false;
5054             }
5055             curHostInputToken = mCurHostInputToken;
5056         }
5057         return mInputManagerInternal.transferTouchFocus(sourceInputToken, curHostInputToken);
5058     }
5059 
reportImeControl(@ullable IBinder windowToken)5060     private void reportImeControl(@Nullable IBinder windowToken) {
5061         synchronized (mMethodMap) {
5062             if (mCurFocusedWindow != windowToken) {
5063                 // mCurPerceptible was set by the focused window, but it is no longer in control,
5064                 // so we reset mCurPerceptible.
5065                 mCurPerceptible = true;
5066             }
5067         }
5068     }
5069 
5070     private static final class LocalServiceImpl extends InputMethodManagerInternal {
5071         @NonNull
5072         private final InputMethodManagerService mService;
5073 
LocalServiceImpl(@onNull InputMethodManagerService service)5074         LocalServiceImpl(@NonNull InputMethodManagerService service) {
5075             mService = service;
5076         }
5077 
5078         @Override
setInteractive(boolean interactive)5079         public void setInteractive(boolean interactive) {
5080             // Do everything in handler so as not to block the caller.
5081             mService.mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0)
5082                     .sendToTarget();
5083         }
5084 
5085         @Override
hideCurrentInputMethod(@oftInputShowHideReason int reason)5086         public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
5087             mService.mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
5088             mService.mHandler.obtainMessage(MSG_HIDE_CURRENT_INPUT_METHOD, reason).sendToTarget();
5089         }
5090 
5091         @Override
getInputMethodListAsUser(int userId)5092         public List<InputMethodInfo> getInputMethodListAsUser(int userId) {
5093             return mService.getInputMethodListAsUser(userId);
5094         }
5095 
5096         @Override
getEnabledInputMethodListAsUser(int userId)5097         public List<InputMethodInfo> getEnabledInputMethodListAsUser(int userId) {
5098             return mService.getEnabledInputMethodListAsUser(userId);
5099         }
5100 
5101         @Override
onCreateInlineSuggestionsRequest(int userId, InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb)5102         public void onCreateInlineSuggestionsRequest(int userId,
5103                 InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb) {
5104             mService.onCreateInlineSuggestionsRequest(userId, requestInfo, cb);
5105         }
5106 
5107         @Override
switchToInputMethod(String imeId, int userId)5108         public boolean switchToInputMethod(String imeId, int userId) {
5109             return mService.switchToInputMethod(imeId, userId);
5110         }
5111 
5112         @Override
registerInputMethodListListener(InputMethodListListener listener)5113         public void registerInputMethodListListener(InputMethodListListener listener) {
5114             mService.mInputMethodListListeners.addIfAbsent(listener);
5115         }
5116 
5117         @Override
transferTouchFocusToImeWindow(@onNull IBinder sourceInputToken, int displayId)5118         public boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken,
5119                 int displayId) {
5120             return mService.transferTouchFocusToImeWindow(sourceInputToken, displayId);
5121         }
5122 
5123         @Override
reportImeControl(@ullable IBinder windowToken)5124         public void reportImeControl(@Nullable IBinder windowToken) {
5125             mService.reportImeControl(windowToken);
5126         }
5127 
5128         @Override
removeImeSurface()5129         public void removeImeSurface() {
5130             mService.mHandler.sendMessage(mService.mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE));
5131         }
5132     }
5133 
5134     @BinderThread
createInputContentUriToken(@ullable IBinder token, @Nullable Uri contentUri, @Nullable String packageName)5135     private IInputContentUriToken createInputContentUriToken(@Nullable IBinder token,
5136             @Nullable Uri contentUri, @Nullable String packageName) {
5137         if (token == null) {
5138             throw new NullPointerException("token");
5139         }
5140         if (packageName == null) {
5141             throw new NullPointerException("packageName");
5142         }
5143         if (contentUri == null) {
5144             throw new NullPointerException("contentUri");
5145         }
5146         final String contentUriScheme = contentUri.getScheme();
5147         if (!"content".equals(contentUriScheme)) {
5148             throw new InvalidParameterException("contentUri must have content scheme");
5149         }
5150 
5151         synchronized (mMethodMap) {
5152             final int uid = Binder.getCallingUid();
5153             if (mCurMethodId == null) {
5154                 return null;
5155             }
5156             if (mCurToken != token) {
5157                 Slog.e(TAG, "Ignoring createInputContentUriToken mCurToken=" + mCurToken
5158                         + " token=" + token);
5159                 return null;
5160             }
5161             // We cannot simply distinguish a bad IME that reports an arbitrary package name from
5162             // an unfortunate IME whose internal state is already obsolete due to the asynchronous
5163             // nature of our system.  Let's compare it with our internal record.
5164             if (!TextUtils.equals(mCurAttribute.packageName, packageName)) {
5165                 Slog.e(TAG, "Ignoring createInputContentUriToken mCurAttribute.packageName="
5166                     + mCurAttribute.packageName + " packageName=" + packageName);
5167                 return null;
5168             }
5169             // This user ID can never bee spoofed.
5170             final int imeUserId = UserHandle.getUserId(uid);
5171             // This user ID can never bee spoofed.
5172             final int appUserId = UserHandle.getUserId(mCurClient.uid);
5173             // This user ID may be invalid if "contentUri" embedded an invalid user ID.
5174             final int contentUriOwnerUserId = ContentProvider.getUserIdFromUri(contentUri,
5175                     imeUserId);
5176             final Uri contentUriWithoutUserId = ContentProvider.getUriWithoutUserId(contentUri);
5177             // Note: InputContentUriTokenHandler.take() checks whether the IME (specified by "uid")
5178             // actually has the right to grant a read permission for "contentUriWithoutUserId" that
5179             // is claimed to belong to "contentUriOwnerUserId".  For example, specifying random
5180             // content URI and/or contentUriOwnerUserId just results in a SecurityException thrown
5181             // from InputContentUriTokenHandler.take() and can never be allowed beyond what is
5182             // actually allowed to "uid", which is guaranteed to be the IME's one.
5183             return new InputContentUriTokenHandler(contentUriWithoutUserId, uid,
5184                     packageName, contentUriOwnerUserId, appUserId);
5185         }
5186     }
5187 
5188     @BinderThread
reportFullscreenMode(@onNull IBinder token, boolean fullscreen)5189     private void reportFullscreenMode(@NonNull IBinder token, boolean fullscreen) {
5190         synchronized (mMethodMap) {
5191             if (!calledWithValidTokenLocked(token)) {
5192                 return;
5193             }
5194             if (mCurClient != null && mCurClient.client != null) {
5195                 mInFullscreenMode = fullscreen;
5196                 executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
5197                         MSG_REPORT_FULLSCREEN_MODE, fullscreen ? 1 : 0, mCurClient));
5198             }
5199         }
5200     }
5201 
5202     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)5203     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
5204         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
5205 
5206         IInputMethod method;
5207         ClientState client;
5208         ClientState focusedWindowClient;
5209 
5210         final Printer p = new PrintWriterPrinter(pw);
5211 
5212         synchronized (mMethodMap) {
5213             p.println("Current Input Method Manager state:");
5214             int N = mMethodList.size();
5215             p.println("  Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount);
5216             for (int i=0; i<N; i++) {
5217                 InputMethodInfo info = mMethodList.get(i);
5218                 p.println("  InputMethod #" + i + ":");
5219                 info.dump(p, "    ");
5220             }
5221             p.println("  Clients:");
5222             final int numClients = mClients.size();
5223             for (int i = 0; i < numClients; ++i) {
5224                 final ClientState ci = mClients.valueAt(i);
5225                 p.println("  Client " + ci + ":");
5226                 p.println("    client=" + ci.client);
5227                 p.println("    inputContext=" + ci.inputContext);
5228                 p.println("    sessionRequested=" + ci.sessionRequested);
5229                 p.println("    curSession=" + ci.curSession);
5230             }
5231             p.println("  mCurMethodId=" + mCurMethodId);
5232             client = mCurClient;
5233             p.println("  mCurClient=" + client + " mCurSeq=" + mCurSeq);
5234             p.println("  mCurPerceptible=" + mCurPerceptible);
5235             p.println("  mCurFocusedWindow=" + mCurFocusedWindow
5236                     + " softInputMode=" +
5237                     InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode)
5238                     + " client=" + mCurFocusedWindowClient);
5239             focusedWindowClient = mCurFocusedWindowClient;
5240             p.println("  mCurId=" + mCurId + " mHaveConnection=" + mHaveConnection
5241                     + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound=" + mVisibleBound);
5242             p.println("  mCurToken=" + mCurToken);
5243             p.println("  mCurTokenDisplayId=" + mCurTokenDisplayId);
5244             p.println("  mCurHostInputToken=" + mCurHostInputToken);
5245             p.println("  mCurIntent=" + mCurIntent);
5246             method = mCurMethod;
5247             p.println("  mCurMethod=" + mCurMethod);
5248             p.println("  mEnabledSession=" + mEnabledSession);
5249             p.println("  mShowRequested=" + mShowRequested
5250                     + " mShowExplicitlyRequested=" + mShowExplicitlyRequested
5251                     + " mShowForced=" + mShowForced
5252                     + " mInputShown=" + mInputShown);
5253             p.println("  mInFullscreenMode=" + mInFullscreenMode);
5254             p.println("  mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive);
5255             p.println("  mSettingsObserver=" + mSettingsObserver);
5256             p.println("  mSwitchingController:");
5257             mSwitchingController.dump(p);
5258             p.println("  mSettings:");
5259             mSettings.dumpLocked(p, "    ");
5260 
5261             p.println("  mStartInputHistory:");
5262             mStartInputHistory.dump(pw, "   ");
5263 
5264             p.println("  mSoftInputShowHideHistory:");
5265             mSoftInputShowHideHistory.dump(pw, "   ");
5266         }
5267 
5268         p.println(" ");
5269         if (client != null) {
5270             pw.flush();
5271             try {
5272                 TransferPipe.dumpAsync(client.client.asBinder(), fd, args);
5273             } catch (IOException | RemoteException e) {
5274                 p.println("Failed to dump input method client: " + e);
5275             }
5276         } else {
5277             p.println("No input method client.");
5278         }
5279 
5280         if (focusedWindowClient != null && client != focusedWindowClient) {
5281             p.println(" ");
5282             p.println("Warning: Current input method client doesn't match the last focused. "
5283                     + "window.");
5284             p.println("Dumping input method client in the last focused window just in case.");
5285             p.println(" ");
5286             pw.flush();
5287             try {
5288                 TransferPipe.dumpAsync(focusedWindowClient.client.asBinder(), fd, args);
5289             } catch (IOException | RemoteException e) {
5290                 p.println("Failed to dump input method client in focused window: " + e);
5291             }
5292         }
5293 
5294         p.println(" ");
5295         if (method != null) {
5296             pw.flush();
5297             try {
5298                 TransferPipe.dumpAsync(method.asBinder(), fd, args);
5299             } catch (IOException | RemoteException e) {
5300                 p.println("Failed to dump input method service: " + e);
5301             }
5302         } else {
5303             p.println("No input method service.");
5304         }
5305     }
5306 
5307     @BinderThread
5308     @Override
onShellCommand(@ullable FileDescriptor in, @Nullable FileDescriptor out, @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver)5309     public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
5310             @Nullable FileDescriptor err,
5311             @NonNull String[] args, @Nullable ShellCallback callback,
5312             @NonNull ResultReceiver resultReceiver) throws RemoteException {
5313         final int callingUid = Binder.getCallingUid();
5314         // Reject any incoming calls from non-shell users, including ones from the system user.
5315         if (callingUid != Process.ROOT_UID && callingUid != Process.SHELL_UID) {
5316             // Note that Binder#onTransact() will automatically close "in", "out", and "err" when
5317             // returned from this method, hence there is no need to close those FDs.
5318             // "resultReceiver" is the only thing that needs to be taken care of here.
5319             if (resultReceiver != null) {
5320                 resultReceiver.send(ShellCommandResult.FAILURE, null);
5321             }
5322             final String errorMsg = "InputMethodManagerService does not support shell commands from"
5323                     + " non-shell users. callingUid=" + callingUid
5324                     + " args=" + Arrays.toString(args);
5325             if (Process.isCoreUid(callingUid)) {
5326                 // Let's not crash the calling process if the caller is one of core components.
5327                 Slog.e(TAG, errorMsg);
5328                 return;
5329             }
5330             throw new SecurityException(errorMsg);
5331         }
5332         new ShellCommandImpl(this).exec(
5333                 this, in, out, err, args, callback, resultReceiver);
5334     }
5335 
5336     private static final class ShellCommandImpl extends ShellCommand {
5337         @NonNull
5338         final InputMethodManagerService mService;
5339 
ShellCommandImpl(InputMethodManagerService service)5340         ShellCommandImpl(InputMethodManagerService service) {
5341             mService = service;
5342         }
5343 
5344         @RequiresPermission(allOf = {
5345                 Manifest.permission.DUMP,
5346                 Manifest.permission.INTERACT_ACROSS_USERS_FULL,
5347                 Manifest.permission.WRITE_SECURE_SETTINGS,
5348         })
5349         @BinderThread
5350         @ShellCommandResult
5351         @Override
onCommand(@ullable String cmd)5352         public int onCommand(@Nullable String cmd) {
5353             // For shell command, require all the permissions here in favor of code simplicity.
5354             Arrays.asList(
5355                     Manifest.permission.DUMP,
5356                     Manifest.permission.INTERACT_ACROSS_USERS_FULL,
5357                     Manifest.permission.WRITE_SECURE_SETTINGS
5358             ).forEach(permission -> mService.mContext.enforceCallingPermission(permission, null));
5359 
5360             final long identity = Binder.clearCallingIdentity();
5361             try {
5362                 return onCommandWithSystemIdentity(cmd);
5363             } finally {
5364                 Binder.restoreCallingIdentity(identity);
5365             }
5366         }
5367 
5368         @BinderThread
5369         @ShellCommandResult
onCommandWithSystemIdentity(@ullable String cmd)5370         private int onCommandWithSystemIdentity(@Nullable String cmd) {
5371             if ("refresh_debug_properties".equals(cmd)) {
5372                 return refreshDebugProperties();
5373             }
5374 
5375             if ("get-last-switch-user-id".equals(cmd)) {
5376                 return mService.getLastSwitchUserId(this);
5377             }
5378 
5379             // For existing "adb shell ime <command>".
5380             if ("ime".equals(cmd)) {
5381                 final String imeCommand = getNextArg();
5382                 if (imeCommand == null || "help".equals(imeCommand) || "-h".equals(imeCommand)) {
5383                     onImeCommandHelp();
5384                     return ShellCommandResult.SUCCESS;
5385                 }
5386                 switch (imeCommand) {
5387                     case "list":
5388                         return mService.handleShellCommandListInputMethods(this);
5389                     case "enable":
5390                         return mService.handleShellCommandEnableDisableInputMethod(this, true);
5391                     case "disable":
5392                         return mService.handleShellCommandEnableDisableInputMethod(this, false);
5393                     case "set":
5394                         return mService.handleShellCommandSetInputMethod(this);
5395                     case "reset":
5396                         return mService.handleShellCommandResetInputMethod(this);
5397                     default:
5398                         getOutPrintWriter().println("Unknown command: " + imeCommand);
5399                         return ShellCommandResult.FAILURE;
5400                 }
5401             }
5402 
5403             return handleDefaultCommands(cmd);
5404         }
5405 
5406         @BinderThread
5407         @ShellCommandResult
refreshDebugProperties()5408         private int refreshDebugProperties() {
5409             DebugFlags.FLAG_OPTIMIZE_START_INPUT.refresh();
5410             DebugFlags.FLAG_PRE_RENDER_IME_VIEWS.refresh();
5411             return ShellCommandResult.SUCCESS;
5412         }
5413 
5414         @BinderThread
5415         @Override
onHelp()5416         public void onHelp() {
5417             try (PrintWriter pw = getOutPrintWriter()) {
5418                 pw.println("InputMethodManagerService commands:");
5419                 pw.println("  help");
5420                 pw.println("    Prints this help text.");
5421                 pw.println("  dump [options]");
5422                 pw.println("    Synonym of dumpsys.");
5423                 pw.println("  ime <command> [options]");
5424                 pw.println("    Manipulate IMEs.  Run \"ime help\" for details.");
5425             }
5426         }
5427 
onImeCommandHelp()5428         private void onImeCommandHelp() {
5429             try (IndentingPrintWriter pw =
5430                          new IndentingPrintWriter(getOutPrintWriter(), "  ", 100)) {
5431                 pw.println("ime <command>:");
5432                 pw.increaseIndent();
5433 
5434                 pw.println("list [-a] [-s]");
5435                 pw.increaseIndent();
5436                 pw.println("prints all enabled input methods.");
5437                 pw.increaseIndent();
5438                 pw.println("-a: see all input methods");
5439                 pw.println("-s: only a single summary line of each");
5440                 pw.decreaseIndent();
5441                 pw.decreaseIndent();
5442 
5443                 pw.println("enable [--user <USER_ID>] <ID>");
5444                 pw.increaseIndent();
5445                 pw.println("allows the given input method ID to be used.");
5446                 pw.increaseIndent();
5447                 pw.print("--user <USER_ID>: Specify which user to enable.");
5448                 pw.println(" Assumes the current user if not specified.");
5449                 pw.decreaseIndent();
5450                 pw.decreaseIndent();
5451 
5452                 pw.println("disable [--user <USER_ID>] <ID>");
5453                 pw.increaseIndent();
5454                 pw.println("disallows the given input method ID to be used.");
5455                 pw.increaseIndent();
5456                 pw.print("--user <USER_ID>: Specify which user to disable.");
5457                 pw.println(" Assumes the current user if not specified.");
5458                 pw.decreaseIndent();
5459                 pw.decreaseIndent();
5460 
5461                 pw.println("set [--user <USER_ID>] <ID>");
5462                 pw.increaseIndent();
5463                 pw.println("switches to the given input method ID.");
5464                 pw.increaseIndent();
5465                 pw.print("--user <USER_ID>: Specify which user to enable.");
5466                 pw.println(" Assumes the current user if not specified.");
5467                 pw.decreaseIndent();
5468                 pw.decreaseIndent();
5469 
5470                 pw.println("reset [--user <USER_ID>]");
5471                 pw.increaseIndent();
5472                 pw.println("reset currently selected/enabled IMEs to the default ones as if "
5473                         + "the device is initially booted with the current locale.");
5474                 pw.increaseIndent();
5475                 pw.print("--user <USER_ID>: Specify which user to reset.");
5476                 pw.println(" Assumes the current user if not specified.");
5477                 pw.decreaseIndent();
5478 
5479                 pw.decreaseIndent();
5480 
5481                 pw.decreaseIndent();
5482             }
5483         }
5484     }
5485 
5486     // ----------------------------------------------------------------------
5487     // Shell command handlers:
5488 
5489     @BinderThread
5490     @ShellCommandResult
getLastSwitchUserId(@onNull ShellCommand shellCommand)5491     private int getLastSwitchUserId(@NonNull ShellCommand shellCommand) {
5492         synchronized (mMethodMap) {
5493             shellCommand.getOutPrintWriter().println(mLastSwitchUserId);
5494             return ShellCommandResult.SUCCESS;
5495         }
5496     }
5497 
5498     /**
5499      * Handles {@code adb shell ime list}.
5500      * @param shellCommand {@link ShellCommand} object that is handling this command.
5501      * @return Exit code of the command.
5502      */
5503     @BinderThread
5504     @ShellCommandResult
handleShellCommandListInputMethods(@onNull ShellCommand shellCommand)5505     private int handleShellCommandListInputMethods(@NonNull ShellCommand shellCommand) {
5506         boolean all = false;
5507         boolean brief = false;
5508         int userIdToBeResolved = UserHandle.USER_CURRENT;
5509         while (true) {
5510             final String nextOption = shellCommand.getNextOption();
5511             if (nextOption == null) {
5512                 break;
5513             }
5514             switch (nextOption) {
5515                 case "-a":
5516                     all = true;
5517                     break;
5518                 case "-s":
5519                     brief = true;
5520                     break;
5521                 case "-u":
5522                 case "--user":
5523                     userIdToBeResolved = UserHandle.parseUserArg(shellCommand.getNextArgRequired());
5524                     break;
5525             }
5526         }
5527         synchronized (mMethodMap) {
5528             final PrintWriter pr = shellCommand.getOutPrintWriter();
5529             final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
5530                     mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
5531             for (int userId : userIds) {
5532                 final List<InputMethodInfo> methods = all
5533                         ? getInputMethodListLocked(userId)
5534                         : getEnabledInputMethodListLocked(userId);
5535                 if (userIds.length > 1) {
5536                     pr.print("User #");
5537                     pr.print(userId);
5538                     pr.println(":");
5539                 }
5540                 for (InputMethodInfo info : methods) {
5541                     if (brief) {
5542                         pr.println(info.getId());
5543                     } else {
5544                         pr.print(info.getId());
5545                         pr.println(":");
5546                         info.dump(pr::println, "  ");
5547                     }
5548                 }
5549             }
5550         }
5551         return ShellCommandResult.SUCCESS;
5552     }
5553 
5554     /**
5555      * Handles {@code adb shell ime enable} and {@code adb shell ime disable}.
5556      * @param shellCommand {@link ShellCommand} object that is handling this command.
5557      * @param enabled {@code true} if the command was {@code adb shell ime enable}.
5558      * @return Exit code of the command.
5559      */
5560     @BinderThread
5561     @ShellCommandResult
handleShellCommandEnableDisableInputMethod( @onNull ShellCommand shellCommand, boolean enabled)5562     private int handleShellCommandEnableDisableInputMethod(
5563             @NonNull ShellCommand shellCommand, boolean enabled) {
5564         final int userIdToBeResolved = handleOptionsForCommandsThatOnlyHaveUserOption(shellCommand);
5565         final String imeId = shellCommand.getNextArgRequired();
5566         final PrintWriter out = shellCommand.getOutPrintWriter();
5567         final PrintWriter error = shellCommand.getErrPrintWriter();
5568         synchronized (mMethodMap) {
5569             final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
5570                     mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
5571             for (int userId : userIds) {
5572                 if (!userHasDebugPriv(userId, shellCommand)) {
5573                     continue;
5574                 }
5575                 handleShellCommandEnableDisableInputMethodInternalLocked(userId, imeId, enabled,
5576                         out, error);
5577             }
5578         }
5579         return ShellCommandResult.SUCCESS;
5580     }
5581 
5582     /**
5583      * A special helper method for commands that only have {@code -u} and {@code --user} options.
5584      *
5585      * <p>You cannot use this helper method if the command has other options.</p>
5586      *
5587      * <p>CAVEAT: This method must be called only once before any other
5588      * {@link ShellCommand#getNextArg()} and {@link ShellCommand#getNextArgRequired()} for the
5589      * main arguments.</p>
5590      *
5591      * @param shellCommand {@link ShellCommand} from which options should be obtained.
5592      * @return User ID to be resolved. {@link UserHandle#CURRENT} if not specified.
5593      */
5594     @BinderThread
5595     @UserIdInt
handleOptionsForCommandsThatOnlyHaveUserOption(ShellCommand shellCommand)5596     private static int handleOptionsForCommandsThatOnlyHaveUserOption(ShellCommand shellCommand) {
5597         while (true) {
5598             final String nextOption = shellCommand.getNextOption();
5599             if (nextOption == null) {
5600                 break;
5601             }
5602             switch (nextOption) {
5603                 case "-u":
5604                 case "--user":
5605                     return UserHandle.parseUserArg(shellCommand.getNextArgRequired());
5606             }
5607         }
5608         return UserHandle.USER_CURRENT;
5609     }
5610 
5611     @BinderThread
handleShellCommandEnableDisableInputMethodInternalLocked( @serIdInt int userId, String imeId, boolean enabled, PrintWriter out, PrintWriter error)5612     private void handleShellCommandEnableDisableInputMethodInternalLocked(
5613             @UserIdInt int userId, String imeId, boolean enabled, PrintWriter out,
5614             PrintWriter error) {
5615         boolean failedToEnableUnknownIme = false;
5616         boolean previouslyEnabled = false;
5617         if (userId == mSettings.getCurrentUserId()) {
5618             if (enabled && !mMethodMap.containsKey(imeId)) {
5619                 failedToEnableUnknownIme = true;
5620             } else {
5621                 previouslyEnabled = setInputMethodEnabledLocked(imeId, enabled);
5622             }
5623         } else {
5624             final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
5625             final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
5626             final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
5627                     new ArrayMap<>();
5628             AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
5629             queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
5630                     methodMap, methodList);
5631             final InputMethodSettings settings = new InputMethodSettings(mContext.getResources(),
5632                     mContext.getContentResolver(), methodMap, userId, false);
5633             if (enabled) {
5634                 if (!methodMap.containsKey(imeId)) {
5635                     failedToEnableUnknownIme = true;
5636                 } else {
5637                     for (InputMethodInfo imi : settings.getEnabledInputMethodListLocked()) {
5638                         if (TextUtils.equals(imi.getId(), imeId)) {
5639                             previouslyEnabled = true;
5640                             break;
5641                         }
5642                     }
5643                     if (!previouslyEnabled) {
5644                         settings.appendAndPutEnabledInputMethodLocked(imeId, false);
5645                     }
5646                 }
5647             } else {
5648                 previouslyEnabled =
5649                         settings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
5650                                 new StringBuilder(),
5651                                 settings.getEnabledInputMethodsAndSubtypeListLocked(), imeId);
5652             }
5653         }
5654         if (failedToEnableUnknownIme) {
5655             error.print("Unknown input method ");
5656             error.print(imeId);
5657             error.println(" cannot be enabled for user #" + userId);
5658         } else {
5659             out.print("Input method ");
5660             out.print(imeId);
5661             out.print(": ");
5662             out.print((enabled == previouslyEnabled) ? "already " : "now ");
5663             out.print(enabled ? "enabled" : "disabled");
5664             out.print(" for user #");
5665             out.println(userId);
5666         }
5667     }
5668 
5669     /**
5670      * Handles {@code adb shell ime set}.
5671      * @param shellCommand {@link ShellCommand} object that is handling this command.
5672      * @return Exit code of the command.
5673      */
5674     @BinderThread
5675     @ShellCommandResult
handleShellCommandSetInputMethod(@onNull ShellCommand shellCommand)5676     private int handleShellCommandSetInputMethod(@NonNull ShellCommand shellCommand) {
5677         final int userIdToBeResolved = handleOptionsForCommandsThatOnlyHaveUserOption(shellCommand);
5678         final String imeId = shellCommand.getNextArgRequired();
5679         final PrintWriter out = shellCommand.getOutPrintWriter();
5680         final PrintWriter error = shellCommand.getErrPrintWriter();
5681         synchronized (mMethodMap) {
5682             final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
5683                     mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
5684             for (int userId : userIds) {
5685                 if (!userHasDebugPriv(userId, shellCommand)) {
5686                     continue;
5687                 }
5688                 boolean failedToSelectUnknownIme = !switchToInputMethod(imeId, userId);
5689                 if (failedToSelectUnknownIme) {
5690                     error.print("Unknown input method ");
5691                     error.print(imeId);
5692                     error.print(" cannot be selected for user #");
5693                     error.println(userId);
5694                 } else {
5695                     out.print("Input method ");
5696                     out.print(imeId);
5697                     out.print(" selected for user #");
5698                     out.println(userId);
5699                 }
5700             }
5701         }
5702         return ShellCommandResult.SUCCESS;
5703     }
5704 
5705     /**
5706      * Handles {@code adb shell ime reset-ime}.
5707      * @param shellCommand {@link ShellCommand} object that is handling this command.
5708      * @return Exit code of the command.
5709      */
5710     @BinderThread
5711     @ShellCommandResult
handleShellCommandResetInputMethod(@onNull ShellCommand shellCommand)5712     private int handleShellCommandResetInputMethod(@NonNull ShellCommand shellCommand) {
5713         final PrintWriter out = shellCommand.getOutPrintWriter();
5714         final int userIdToBeResolved = handleOptionsForCommandsThatOnlyHaveUserOption(shellCommand);
5715         synchronized (mMethodMap) {
5716             final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
5717                     mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
5718             for (int userId : userIds) {
5719                 if (!userHasDebugPriv(userId, shellCommand)) {
5720                     continue;
5721                 }
5722                 final String nextIme;
5723                 final List<InputMethodInfo> nextEnabledImes;
5724                 if (userId == mSettings.getCurrentUserId()) {
5725                     hideCurrentInputLocked(mCurFocusedWindow, 0, null,
5726                             SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND);
5727                     unbindCurrentMethodLocked();
5728                     // Reset the current IME
5729                     resetSelectedInputMethodAndSubtypeLocked(null);
5730                     // Also reset the settings of the current IME
5731                     mSettings.putSelectedInputMethod(null);
5732                     // Disable all enabled IMEs.
5733                     mSettings.getEnabledInputMethodListLocked().forEach(
5734                             imi -> setInputMethodEnabledLocked(imi.getId(), false));
5735                     // Re-enable with default enabled IMEs.
5736                     InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList).forEach(
5737                             imi -> setInputMethodEnabledLocked(imi.getId(), true));
5738                     updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
5739                     InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
5740                             mSettings.getEnabledInputMethodListLocked(),
5741                             mSettings.getCurrentUserId(),
5742                             mContext.getBasePackageName());
5743                     nextIme = mSettings.getSelectedInputMethod();
5744                     nextEnabledImes = mSettings.getEnabledInputMethodListLocked();
5745                 } else {
5746                     final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
5747                     final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
5748                     final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
5749                             new ArrayMap<>();
5750                     AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
5751                     queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
5752                             methodMap, methodList);
5753                     final InputMethodSettings settings = new InputMethodSettings(
5754                             mContext.getResources(), mContext.getContentResolver(), methodMap,
5755                             userId, false);
5756 
5757                     nextEnabledImes = InputMethodUtils.getDefaultEnabledImes(mContext, methodList);
5758                     nextIme = InputMethodUtils.getMostApplicableDefaultIME(nextEnabledImes).getId();
5759 
5760                     // Reset enabled IMEs.
5761                     settings.putEnabledInputMethodsStr("");
5762                     nextEnabledImes.forEach(imi -> settings.appendAndPutEnabledInputMethodLocked(
5763                             imi.getId(), false));
5764 
5765                     // Reset selected IME.
5766                     settings.putSelectedInputMethod(nextIme);
5767                     settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
5768                 }
5769                 out.println("Reset current and enabled IMEs for user #" + userId);
5770                 out.println("  Selected: " + nextIme);
5771                 nextEnabledImes.forEach(ime -> out.println("   Enabled: " + ime.getId()));
5772             }
5773         }
5774         return ShellCommandResult.SUCCESS;
5775     }
5776 
5777     /**
5778      * @param userId the actual user handle obtained by {@link UserHandle#getIdentifier()}
5779      * and *not* pseudo ids like {@link UserHandle#USER_ALL etc}.
5780      * @return {@code true} if userId has debugging privileges.
5781      * i.e. {@link UserManager#DISALLOW_DEBUGGING_FEATURES} is {@code false}.
5782      */
userHasDebugPriv(int userId, final ShellCommand shellCommand)5783     private boolean userHasDebugPriv(int userId, final ShellCommand shellCommand) {
5784         if (mUserManager.hasUserRestriction(
5785                 UserManager.DISALLOW_DEBUGGING_FEATURES, UserHandle.of(userId))) {
5786             shellCommand.getErrPrintWriter().println("User #" + userId
5787                     + " is restricted with DISALLOW_DEBUGGING_FEATURES.");
5788             return false;
5789         }
5790         return true;
5791     }
5792 
5793     private static final class InputMethodPrivilegedOperationsImpl
5794             extends IInputMethodPrivilegedOperations.Stub {
5795         private final InputMethodManagerService mImms;
5796         @NonNull
5797         private final IBinder mToken;
InputMethodPrivilegedOperationsImpl(InputMethodManagerService imms, @NonNull IBinder token)5798         InputMethodPrivilegedOperationsImpl(InputMethodManagerService imms,
5799                 @NonNull IBinder token) {
5800             mImms = imms;
5801             mToken = token;
5802         }
5803 
5804         @BinderThread
5805         @Override
setImeWindowStatus(int vis, int backDisposition)5806         public void setImeWindowStatus(int vis, int backDisposition) {
5807             mImms.setImeWindowStatus(mToken, vis, backDisposition);
5808         }
5809 
5810         @BinderThread
5811         @Override
reportStartInput(IBinder startInputToken)5812         public void reportStartInput(IBinder startInputToken) {
5813             mImms.reportStartInput(mToken, startInputToken);
5814         }
5815 
5816         @BinderThread
5817         @Override
createInputContentUriToken(Uri contentUri, String packageName)5818         public IInputContentUriToken createInputContentUriToken(Uri contentUri,
5819                 String packageName) {
5820             return mImms.createInputContentUriToken(mToken, contentUri, packageName);
5821         }
5822 
5823         @BinderThread
5824         @Override
reportFullscreenMode(boolean fullscreen)5825         public void reportFullscreenMode(boolean fullscreen) {
5826             mImms.reportFullscreenMode(mToken, fullscreen);
5827         }
5828 
5829         @BinderThread
5830         @Override
setInputMethod(String id)5831         public void setInputMethod(String id) {
5832             mImms.setInputMethod(mToken, id);
5833         }
5834 
5835         @BinderThread
5836         @Override
setInputMethodAndSubtype(String id, InputMethodSubtype subtype)5837         public void setInputMethodAndSubtype(String id, InputMethodSubtype subtype) {
5838             mImms.setInputMethodAndSubtype(mToken, id, subtype);
5839         }
5840 
5841         @BinderThread
5842         @Override
hideMySoftInput(int flags)5843         public void hideMySoftInput(int flags) {
5844             mImms.hideMySoftInput(mToken, flags);
5845         }
5846 
5847         @BinderThread
5848         @Override
showMySoftInput(int flags)5849         public void showMySoftInput(int flags) {
5850             mImms.showMySoftInput(mToken, flags);
5851         }
5852 
5853         @BinderThread
5854         @Override
updateStatusIcon(String packageName, @DrawableRes int iconId)5855         public void updateStatusIcon(String packageName, @DrawableRes int iconId) {
5856             mImms.updateStatusIcon(mToken, packageName, iconId);
5857         }
5858 
5859         @BinderThread
5860         @Override
switchToPreviousInputMethod()5861         public boolean switchToPreviousInputMethod() {
5862             return mImms.switchToPreviousInputMethod(mToken);
5863         }
5864 
5865         @BinderThread
5866         @Override
switchToNextInputMethod(boolean onlyCurrentIme)5867         public boolean switchToNextInputMethod(boolean onlyCurrentIme) {
5868             return mImms.switchToNextInputMethod(mToken, onlyCurrentIme);
5869         }
5870 
5871         @BinderThread
5872         @Override
shouldOfferSwitchingToNextInputMethod()5873         public boolean shouldOfferSwitchingToNextInputMethod() {
5874             return mImms.shouldOfferSwitchingToNextInputMethod(mToken);
5875         }
5876 
5877         @BinderThread
5878         @Override
notifyUserAction()5879         public void notifyUserAction() {
5880             mImms.notifyUserAction(mToken);
5881         }
5882 
5883         @BinderThread
5884         @Override
reportPreRendered(EditorInfo info)5885         public void reportPreRendered(EditorInfo info) {
5886             mImms.reportPreRendered(mToken, info);
5887         }
5888 
5889         @BinderThread
5890         @Override
applyImeVisibility(IBinder windowToken, boolean setVisible)5891         public void applyImeVisibility(IBinder windowToken, boolean setVisible) {
5892             mImms.applyImeVisibility(mToken, windowToken, setVisible);
5893         }
5894     }
5895 }
5896