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