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