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