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