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