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