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