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