1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.inputmethod; 18 19 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED; 20 import static android.content.Context.DEVICE_ID_DEFAULT; 21 import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT; 22 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; 23 import static android.view.Display.INVALID_DISPLAY; 24 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.annotation.UserIdInt; 28 import android.app.ActivityOptions; 29 import android.app.PendingIntent; 30 import android.content.ComponentName; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.ServiceConnection; 34 import android.content.pm.PackageManagerInternal; 35 import android.inputmethodservice.InputMethodService; 36 import android.inputmethodservice.InputMethodService.BackDispositionMode; 37 import android.inputmethodservice.InputMethodService.ImeWindowVisibility; 38 import android.os.Binder; 39 import android.os.IBinder; 40 import android.os.Process; 41 import android.os.SystemClock; 42 import android.os.Trace; 43 import android.os.UserHandle; 44 import android.provider.Settings; 45 import android.util.EventLog; 46 import android.util.Slog; 47 import android.view.Display; 48 import android.view.WindowManager; 49 import android.view.inputmethod.InputMethod; 50 import android.view.inputmethod.InputMethodInfo; 51 import android.view.inputmethod.InputMethodManager; 52 import android.view.inputmethod.InputMethodSubtype; 53 54 import com.android.internal.annotations.GuardedBy; 55 import com.android.internal.annotations.VisibleForTesting; 56 import com.android.internal.inputmethod.IInputMethod; 57 import com.android.internal.inputmethod.InlineSuggestionsRequestCallback; 58 import com.android.internal.inputmethod.InlineSuggestionsRequestInfo; 59 import com.android.internal.inputmethod.InputBindResult; 60 import com.android.internal.inputmethod.UnbindReason; 61 import com.android.server.EventLogTags; 62 import com.android.server.wm.WindowManagerInternal; 63 64 import java.io.PrintWriter; 65 import java.util.concurrent.CountDownLatch; 66 67 /** 68 * A controller managing the state of the input method binding. 69 */ 70 final class InputMethodBindingController { 71 static final boolean DEBUG = false; 72 private static final String TAG = InputMethodBindingController.class.getSimpleName(); 73 74 /** Time in milliseconds that the IME service has to bind before it is reconnected. */ 75 static final long TIME_TO_RECONNECT = 3 * 1000; 76 77 @UserIdInt private final int mUserId; 78 @NonNull private final InputMethodManagerService mService; 79 @NonNull private final Context mContext; 80 @NonNull private final AutofillSuggestionsController mAutofillController; 81 @NonNull private final PackageManagerInternal mPackageManagerInternal; 82 @NonNull private final WindowManagerInternal mWindowManagerInternal; 83 84 @GuardedBy("ImfLock.class") private long mLastBindTime; 85 @GuardedBy("ImfLock.class") private boolean mHasMainConnection; 86 @GuardedBy("ImfLock.class") @Nullable private String mCurId; 87 @GuardedBy("ImfLock.class") @Nullable private String mSelectedMethodId; 88 @GuardedBy("ImfLock.class") @Nullable private Intent mCurIntent; 89 @GuardedBy("ImfLock.class") @Nullable private IInputMethodInvoker mCurMethod; 90 @GuardedBy("ImfLock.class") private int mCurMethodUid = Process.INVALID_UID; 91 @GuardedBy("ImfLock.class") @Nullable private IBinder mCurToken; 92 @GuardedBy("ImfLock.class") @Nullable private InputMethodSubtype mCurrentSubtype; 93 @GuardedBy("ImfLock.class") private int mCurTokenDisplayId = INVALID_DISPLAY; 94 @GuardedBy("ImfLock.class") private int mCurSeq; 95 @GuardedBy("ImfLock.class") private boolean mVisibleBound; 96 @GuardedBy("ImfLock.class") private boolean mSupportsStylusHw; 97 @GuardedBy("ImfLock.class") private boolean mSupportsConnectionlessStylusHw; 98 99 /** The display id for which the latest startInput was called. */ 100 @GuardedBy("ImfLock.class") private int mDisplayIdToShowIme = INVALID_DISPLAY; 101 @GuardedBy("ImfLock.class") private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT; 102 103 /** 104 * A set of status bits regarding the active IME. 105 * 106 * <em>Do not update this value outside of {@link #setImeWindowVis} and 107 * {@link InputMethodBindingController#unbindCurrentMethod}.</em> 108 */ 109 @ImeWindowVisibility 110 @GuardedBy("ImfLock.class") 111 private int mImeWindowVis; 112 113 @BackDispositionMode 114 @GuardedBy("ImfLock.class") 115 private int mBackDisposition = BACK_DISPOSITION_DEFAULT; 116 117 @Nullable private CountDownLatch mLatchForTesting; 118 119 /** 120 * Binding flags for establishing connection to the {@link InputMethodService}. 121 */ 122 @VisibleForTesting 123 static final int IME_CONNECTION_BIND_FLAGS; 124 static { 125 if (android.view.inputmethod.Flags.lowerImeOomImportance()) { 126 IME_CONNECTION_BIND_FLAGS = Context.BIND_AUTO_CREATE 127 | Context.BIND_ALMOST_PERCEPTIBLE 128 | Context.BIND_IMPORTANT_BACKGROUND 129 | Context.BIND_SCHEDULE_LIKE_TOP_APP; 130 } else { 131 IME_CONNECTION_BIND_FLAGS = Context.BIND_AUTO_CREATE 132 | Context.BIND_NOT_VISIBLE 133 | Context.BIND_NOT_FOREGROUND 134 | Context.BIND_IMPORTANT_BACKGROUND 135 | Context.BIND_SCHEDULE_LIKE_TOP_APP; 136 } 137 } 138 139 private final int mImeConnectionBindFlags; 140 141 /** 142 * Binding flags used only while the {@link InputMethodService} is showing window. 143 */ 144 @VisibleForTesting 145 static final int IME_VISIBLE_BIND_FLAGS = 146 Context.BIND_AUTO_CREATE 147 | Context.BIND_TREAT_LIKE_ACTIVITY 148 | Context.BIND_FOREGROUND_SERVICE 149 | Context.BIND_INCLUDE_CAPABILITIES 150 | Context.BIND_SHOWING_UI; 151 InputMethodBindingController(@serIdInt int userId, @NonNull InputMethodManagerService service)152 InputMethodBindingController(@UserIdInt int userId, 153 @NonNull InputMethodManagerService service) { 154 this(userId, service, IME_CONNECTION_BIND_FLAGS, null /* latchForTesting */); 155 } 156 InputMethodBindingController(@serIdInt int userId, @NonNull InputMethodManagerService service, int imeConnectionBindFlags, CountDownLatch latchForTesting)157 InputMethodBindingController(@UserIdInt int userId, 158 @NonNull InputMethodManagerService service, int imeConnectionBindFlags, 159 CountDownLatch latchForTesting) { 160 mUserId = userId; 161 mService = service; 162 mContext = mService.mContext; 163 mAutofillController = new AutofillSuggestionsController(this); 164 mPackageManagerInternal = mService.mPackageManagerInternal; 165 mWindowManagerInternal = mService.mWindowManagerInternal; 166 mImeConnectionBindFlags = imeConnectionBindFlags; 167 mLatchForTesting = latchForTesting; 168 } 169 170 /** 171 * Time that we last initiated a bind to the input method, to determine 172 * if we should try to disconnect and reconnect to it. 173 */ 174 @GuardedBy("ImfLock.class") getLastBindTime()175 long getLastBindTime() { 176 return mLastBindTime; 177 } 178 179 /** 180 * Set to true if our ServiceConnection is currently actively bound to 181 * a service (whether or not we have gotten its IBinder back yet). 182 */ 183 @GuardedBy("ImfLock.class") hasMainConnection()184 boolean hasMainConnection() { 185 return mHasMainConnection; 186 } 187 188 /** 189 * Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently 190 * connected to or in the process of connecting to. 191 * 192 * <p>This can be {@code null} when no input method is connected.</p> 193 * 194 * @see #getSelectedMethodId() 195 */ 196 @GuardedBy("ImfLock.class") 197 @Nullable getCurId()198 String getCurId() { 199 return mCurId; 200 } 201 202 /** 203 * Id obtained with {@link InputMethodInfo#getId()} for the currently selected input method. 204 * This is to be synchronized with the secure settings keyed with 205 * {@link android.provider.Settings.Secure#DEFAULT_INPUT_METHOD}. 206 * 207 * <p>This can be transiently {@code null} when the system is re-initializing input method 208 * settings, e.g., the system locale is just changed.</p> 209 * 210 * <p>Note that {@link #getCurId()} is used to track which IME is being connected to 211 * {@link com.android.server.inputmethod.InputMethodManagerService}.</p> 212 * 213 * @see #getCurId() 214 */ 215 @GuardedBy("ImfLock.class") 216 @Nullable getSelectedMethodId()217 String getSelectedMethodId() { 218 return mSelectedMethodId; 219 } 220 221 @GuardedBy("ImfLock.class") setSelectedMethodId(@ullable String selectedMethodId)222 void setSelectedMethodId(@Nullable String selectedMethodId) { 223 mSelectedMethodId = selectedMethodId; 224 } 225 226 /** 227 * Returns {@link InputMethodInfo} that is queried from {@link #getSelectedMethodId()}. 228 * 229 * @return {@link InputMethodInfo} whose IME ID is the same as {@link #getSelectedMethodId()}. 230 * {@code null} otherwise 231 */ 232 @GuardedBy("ImfLock.class") 233 @Nullable getSelectedMethod()234 InputMethodInfo getSelectedMethod() { 235 return InputMethodSettingsRepository.get(mUserId).getMethodMap().get(mSelectedMethodId); 236 } 237 238 /** 239 * The token we have made for the currently active input method, to 240 * identify it in the future. 241 */ 242 @GuardedBy("ImfLock.class") 243 @Nullable getCurToken()244 IBinder getCurToken() { 245 return mCurToken; 246 } 247 248 /** 249 * The current {@link InputMethodSubtype} of the current input method. 250 * 251 * @return the current {@link InputMethodSubtype} of the current input method. {@code null} 252 * means that there is no {@link InputMethodSubtype} currently selected 253 */ 254 @GuardedBy("ImfLock.class") 255 @Nullable getCurrentSubtype()256 InputMethodSubtype getCurrentSubtype() { 257 return mCurrentSubtype; 258 } 259 260 /** 261 * Sets the current {@link InputMethodSubtype} of the current input method. 262 * 263 * @param currentSubtype the current {@link InputMethodSubtype} of the current input method 264 */ 265 @GuardedBy("ImfLock.class") setCurrentSubtype(@ullable InputMethodSubtype currentSubtype)266 void setCurrentSubtype(@Nullable InputMethodSubtype currentSubtype) { 267 mCurrentSubtype = currentSubtype; 268 } 269 270 /** 271 * Returns the displayId associated with {@link #getCurToken()}. 272 * 273 * @return the displayId associated with {@link #getCurToken()}. {@link Display#INVALID_DISPLAY} 274 * while {@link #getCurToken()} returns {@code null} 275 */ 276 @GuardedBy("ImfLock.class") getCurTokenDisplayId()277 int getCurTokenDisplayId() { 278 return mCurTokenDisplayId; 279 } 280 281 /** 282 * The Intent used to connect to the current input method. 283 */ 284 @GuardedBy("ImfLock.class") 285 @Nullable getCurIntent()286 Intent getCurIntent() { 287 return mCurIntent; 288 } 289 290 /** 291 * The current binding sequence number, incremented every time there is 292 * a new bind performed. 293 */ 294 @GuardedBy("ImfLock.class") getSequenceNumber()295 int getSequenceNumber() { 296 return mCurSeq; 297 } 298 299 /** 300 * Increase the current binding sequence number by one. 301 * Reset to 1 on overflow. 302 */ 303 @GuardedBy("ImfLock.class") advanceSequenceNumber()304 void advanceSequenceNumber() { 305 mCurSeq += 1; 306 if (mCurSeq <= 0) { 307 mCurSeq = 1; 308 } 309 } 310 311 /** 312 * If non-null, this is the input method service we are currently connected 313 * to. 314 */ 315 @GuardedBy("ImfLock.class") 316 @Nullable getCurMethod()317 IInputMethodInvoker getCurMethod() { 318 return mCurMethod; 319 } 320 321 /** 322 * If not {@link Process#INVALID_UID}, then the UID of {@link #getCurIntent()}. 323 */ 324 @GuardedBy("ImfLock.class") getCurMethodUid()325 int getCurMethodUid() { 326 return mCurMethodUid; 327 } 328 329 /** 330 * Indicates whether {@link #mVisibleConnection} is currently in use. 331 */ 332 @GuardedBy("ImfLock.class") isVisibleBound()333 boolean isVisibleBound() { 334 return mVisibleBound; 335 } 336 337 /** 338 * Returns {@code true} if current IME supports Stylus Handwriting. 339 */ 340 @GuardedBy("ImfLock.class") supportsStylusHandwriting()341 boolean supportsStylusHandwriting() { 342 return mSupportsStylusHw; 343 } 344 345 /** Returns whether the current IME supports connectionless stylus handwriting sessions. */ 346 @GuardedBy("ImfLock.class") supportsConnectionlessStylusHandwriting()347 boolean supportsConnectionlessStylusHandwriting() { 348 return mSupportsConnectionlessStylusHw; 349 } 350 351 /** 352 * Used to bring IME service up to visible adjustment while it is being shown. 353 */ 354 @GuardedBy("ImfLock.class") 355 private final ServiceConnection mVisibleConnection = new ServiceConnection() { 356 @Override public void onBindingDied(ComponentName name) { 357 synchronized (ImfLock.class) { 358 mAutofillController.invalidateAutofillSession(); 359 if (isVisibleBound()) { 360 unbindVisibleConnection(); 361 } 362 } 363 } 364 365 @Override public void onServiceConnected(ComponentName name, IBinder service) { 366 } 367 368 @Override public void onServiceDisconnected(ComponentName name) { 369 synchronized (ImfLock.class) { 370 mAutofillController.invalidateAutofillSession(); 371 } 372 } 373 }; 374 375 /** 376 * Used to bind the IME while it is not currently being shown. 377 */ 378 @GuardedBy("ImfLock.class") 379 private final ServiceConnection mMainConnection = new ServiceConnection() { 380 @Override 381 public void onServiceConnected(ComponentName name, IBinder service) { 382 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.onServiceConnected"); 383 synchronized (ImfLock.class) { 384 if (mCurIntent != null && name.equals(mCurIntent.getComponent())) { 385 mCurMethod = IInputMethodInvoker.create(IInputMethod.Stub.asInterface(service)); 386 updateCurrentMethodUid(); 387 if (mCurToken == null) { 388 Slog.w(TAG, "Service connected without a token!"); 389 unbindCurrentMethod(); 390 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 391 return; 392 } 393 if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken); 394 final InputMethodInfo info = 395 InputMethodSettingsRepository.get(mUserId).getMethodMap().get( 396 mSelectedMethodId); 397 boolean supportsStylusHwChanged = 398 mSupportsStylusHw != info.supportsStylusHandwriting(); 399 mSupportsStylusHw = info.supportsStylusHandwriting(); 400 if (supportsStylusHwChanged) { 401 InputMethodManager.invalidateLocalStylusHandwritingAvailabilityCaches(); 402 } 403 boolean supportsConnectionlessStylusHwChanged = 404 mSupportsConnectionlessStylusHw 405 != info.supportsConnectionlessStylusHandwriting(); 406 if (supportsConnectionlessStylusHwChanged) { 407 mSupportsConnectionlessStylusHw = 408 info.supportsConnectionlessStylusHandwriting(); 409 InputMethodManager 410 .invalidateLocalConnectionlessStylusHandwritingAvailabilityCaches(); 411 } 412 mService.initializeImeLocked(mCurMethod, mCurToken, 413 InputMethodBindingController.this); 414 mService.scheduleNotifyImeUidToAudioService(mCurMethodUid); 415 mService.reRequestCurrentClientSessionLocked(mUserId); 416 mAutofillController.performOnCreateInlineSuggestionsRequest(); 417 } 418 419 // reset Handwriting event receiver. 420 // always call this as it handles changes in mSupportsStylusHw. It is a noop 421 // if unchanged. 422 mService.scheduleResetStylusHandwriting(); 423 } 424 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 425 426 if (mLatchForTesting != null) { 427 mLatchForTesting.countDown(); // Notify the finish to tests 428 } 429 } 430 431 @GuardedBy("ImfLock.class") 432 private void updateCurrentMethodUid() { 433 final String curMethodPackage = mCurIntent.getComponent().getPackageName(); 434 final int curMethodUid = mPackageManagerInternal.getPackageUid( 435 curMethodPackage, 0 /* flags */, mUserId); 436 if (curMethodUid < 0) { 437 Slog.e(TAG, "Failed to get UID for package=" + curMethodPackage); 438 mCurMethodUid = Process.INVALID_UID; 439 } else { 440 mCurMethodUid = curMethodUid; 441 } 442 } 443 444 @Override 445 public void onServiceDisconnected(@NonNull ComponentName name) { 446 // Note that mContext.unbindService(this) does not trigger this. Hence if we are 447 // here the 448 // disconnection is not intended by IMMS (e.g. triggered because the current IMS 449 // crashed), 450 // which is irregular but can eventually happen for everyone just by continuing 451 // using the 452 // device. Thus it is important to make sure that all the internal states are 453 // properly 454 // refreshed when this method is called back. Running 455 // adb install -r <APK that implements the current IME> 456 // would be a good way to trigger such a situation. 457 synchronized (ImfLock.class) { 458 if (DEBUG) { 459 Slog.v(TAG, "Service disconnected: " + name + " mCurIntent=" + mCurIntent); 460 } 461 if (mCurMethod != null && mCurIntent != null 462 && name.equals(mCurIntent.getComponent())) { 463 // We consider this to be a new bind attempt, since the system 464 // should now try to restart the service for us. 465 mLastBindTime = SystemClock.uptimeMillis(); 466 clearCurMethodAndSessions(); 467 final var userData = mService.getUserData(mUserId); 468 userData.mVisibilityStateComputer.setInputShown(false); 469 mService.unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME, mUserId); 470 } 471 } 472 } 473 }; 474 475 @GuardedBy("ImfLock.class") invalidateAutofillSession()476 void invalidateAutofillSession() { 477 mAutofillController.invalidateAutofillSession(); 478 } 479 480 @GuardedBy("ImfLock.class") onCreateInlineSuggestionsRequest(InlineSuggestionsRequestInfo requestInfo, InlineSuggestionsRequestCallback callback, boolean touchExplorationEnabled)481 void onCreateInlineSuggestionsRequest(InlineSuggestionsRequestInfo requestInfo, 482 InlineSuggestionsRequestCallback callback, boolean touchExplorationEnabled) { 483 mAutofillController.onCreateInlineSuggestionsRequest(requestInfo, callback, 484 touchExplorationEnabled); 485 } 486 487 @GuardedBy("ImfLock.class") 488 @Nullable getCurHostInputToken()489 IBinder getCurHostInputToken() { 490 return mAutofillController.getCurHostInputToken(); 491 } 492 493 @GuardedBy("ImfLock.class") unbindCurrentMethod()494 void unbindCurrentMethod() { 495 if (isVisibleBound()) { 496 unbindVisibleConnection(); 497 } 498 499 if (hasMainConnection()) { 500 unbindMainConnection(); 501 } 502 503 if (getCurToken() != null) { 504 mService.resetSystemUiLocked(this); 505 removeCurrentToken(); 506 mAutofillController.onResetSystemUi(); 507 } 508 509 mCurId = null; 510 clearCurMethodAndSessions(); 511 } 512 513 @GuardedBy("ImfLock.class") clearCurMethodAndSessions()514 private void clearCurMethodAndSessions() { 515 mService.clearClientSessionsLocked(this); 516 mCurMethod = null; 517 mCurMethodUid = Process.INVALID_UID; 518 } 519 520 @GuardedBy("ImfLock.class") removeCurrentToken()521 private void removeCurrentToken() { 522 if (DEBUG) { 523 Slog.v(TAG, 524 "Removing window token: " + mCurToken + " for display: " + mCurTokenDisplayId); 525 } 526 mWindowManagerInternal.removeWindowToken(mCurToken, true /* removeWindows */, 527 false /* animateExit */, mCurTokenDisplayId); 528 mCurToken = null; 529 mCurTokenDisplayId = INVALID_DISPLAY; 530 } 531 532 @GuardedBy("ImfLock.class") 533 @NonNull bindCurrentMethod()534 InputBindResult bindCurrentMethod() { 535 if (mSelectedMethodId == null) { 536 Slog.e(TAG, "mSelectedMethodId is null!"); 537 return InputBindResult.NO_IME; 538 } 539 540 InputMethodInfo info = InputMethodSettingsRepository.get(mUserId).getMethodMap().get( 541 mSelectedMethodId); 542 if (info == null) { 543 throw new IllegalArgumentException("Unknown id: " + mSelectedMethodId); 544 } 545 546 mCurIntent = createImeBindingIntent(info.getComponent()); 547 548 if (bindCurrentInputMethodServiceMainConnection()) { 549 mCurId = info.getId(); 550 mLastBindTime = SystemClock.uptimeMillis(); 551 552 mCurToken = new Binder(); 553 mCurTokenDisplayId = mDisplayIdToShowIme; 554 if (DEBUG) { 555 Slog.v(TAG, "Adding window token: " + mCurToken + " for display: " 556 + mDisplayIdToShowIme); 557 } 558 mWindowManagerInternal.addWindowToken(mCurToken, 559 WindowManager.LayoutParams.TYPE_INPUT_METHOD, 560 mDisplayIdToShowIme, null /* options */); 561 return new InputBindResult( 562 InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING, 563 null, null, null, mCurId, mCurSeq, false); 564 } 565 566 Slog.w(InputMethodManagerService.TAG, 567 "Failure connecting to input method service: " + mCurIntent); 568 mCurIntent = null; 569 return InputBindResult.IME_NOT_CONNECTED; 570 } 571 572 @NonNull createImeBindingIntent(ComponentName component)573 private Intent createImeBindingIntent(ComponentName component) { 574 Intent intent = new Intent(InputMethod.SERVICE_INTERFACE); 575 intent.setComponent(component); 576 intent.putExtra(Intent.EXTRA_CLIENT_LABEL, 577 com.android.internal.R.string.input_method_binding_label); 578 var options = ActivityOptions.makeBasic() 579 .setPendingIntentCreatorBackgroundActivityStartMode( 580 MODE_BACKGROUND_ACTIVITY_START_DENIED); 581 intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( 582 mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 583 PendingIntent.FLAG_IMMUTABLE, options.toBundle())); 584 return intent; 585 } 586 587 @GuardedBy("ImfLock.class") unbindMainConnection()588 private void unbindMainConnection() { 589 mContext.unbindService(mMainConnection); 590 mHasMainConnection = false; 591 } 592 593 @GuardedBy("ImfLock.class") unbindVisibleConnection()594 void unbindVisibleConnection() { 595 mContext.unbindService(mVisibleConnection); 596 mVisibleBound = false; 597 } 598 599 @GuardedBy("ImfLock.class") bindCurrentInputMethodService(ServiceConnection conn, int flags)600 private boolean bindCurrentInputMethodService(ServiceConnection conn, int flags) { 601 if (mCurIntent == null || conn == null) { 602 Slog.e(TAG, "--- bind failed: service = " + mCurIntent + ", conn = " + conn); 603 return false; 604 } 605 return mContext.bindServiceAsUser(mCurIntent, conn, flags, new UserHandle(mUserId)); 606 } 607 608 @GuardedBy("ImfLock.class") bindCurrentInputMethodServiceMainConnection()609 private boolean bindCurrentInputMethodServiceMainConnection() { 610 mHasMainConnection = bindCurrentInputMethodService(mMainConnection, 611 mImeConnectionBindFlags); 612 return mHasMainConnection; 613 } 614 615 /** 616 * Bind the IME so that it can be shown. 617 * 618 * <p> 619 * Performs a rebind if no binding is achieved in {@link #TIME_TO_RECONNECT} milliseconds. 620 */ 621 @GuardedBy("ImfLock.class") setCurrentMethodVisible()622 void setCurrentMethodVisible() { 623 if (mCurMethod != null) { 624 if (DEBUG) Slog.d(TAG, "setCurrentMethodVisible: mCurToken=" + mCurToken); 625 if (hasMainConnection() && !isVisibleBound()) { 626 mVisibleBound = bindCurrentInputMethodService(mVisibleConnection, 627 IME_VISIBLE_BIND_FLAGS); 628 } 629 return; 630 } 631 632 // No IME is currently connected. Reestablish the main connection. 633 if (!hasMainConnection()) { 634 if (DEBUG) { 635 Slog.d(TAG, "Cannot show input: no IME bound. Rebinding."); 636 } 637 bindCurrentMethod(); 638 return; 639 } 640 641 long bindingDuration = SystemClock.uptimeMillis() - mLastBindTime; 642 if (bindingDuration >= TIME_TO_RECONNECT) { 643 // The client has asked to have the input method shown, but 644 // we have been sitting here too long with a connection to the 645 // service and no interface received, so let's disconnect/connect 646 // to try to prod things along. 647 EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, getSelectedMethodId(), 648 bindingDuration, 1); 649 Slog.w(TAG, "Force disconnect/connect to the IME in setCurrentMethodVisible()"); 650 unbindMainConnection(); 651 bindCurrentInputMethodServiceMainConnection(); 652 } else { 653 if (DEBUG) { 654 Slog.d(TAG, "Can't show input: connection = " + mHasMainConnection + ", time = " 655 + (TIME_TO_RECONNECT - bindingDuration)); 656 } 657 } 658 } 659 660 /** 661 * Remove the binding needed for the IME to be shown. 662 */ 663 @GuardedBy("ImfLock.class") setCurrentMethodNotVisible()664 void setCurrentMethodNotVisible() { 665 if (isVisibleBound()) { 666 unbindVisibleConnection(); 667 } 668 } 669 670 /** 671 * Returns the current {@link InputMethodSubtype}. 672 * 673 * <p>Also this method has had questionable behaviors:</p> 674 * <ul> 675 * <li>Calling this method can update {@link #mCurrentSubtype}.</li> 676 * <li>This method may return {@link #mCurrentSubtype} as-is, even if it does not belong to 677 * the current IME.</li> 678 * </ul> 679 * <p>TODO(b/347083680): Address above issues.</p> 680 */ 681 @GuardedBy("ImfLock.class") 682 @Nullable getCurrentInputMethodSubtype()683 InputMethodSubtype getCurrentInputMethodSubtype() { 684 final var selectedMethodId = getSelectedMethodId(); 685 if (selectedMethodId == null) { 686 return null; 687 } 688 final InputMethodSettings settings = InputMethodSettingsRepository.get(mUserId); 689 final InputMethodInfo imi = settings.getMethodMap().get(selectedMethodId); 690 if (imi == null || imi.getSubtypeCount() == 0) { 691 return null; 692 } 693 final var subtype = SubtypeUtils.getCurrentInputMethodSubtype(imi, settings, 694 mCurrentSubtype); 695 mCurrentSubtype = subtype; 696 return subtype; 697 } 698 699 700 @GuardedBy("ImfLock.class") setDisplayIdToShowIme(int displayId)701 void setDisplayIdToShowIme(int displayId) { 702 mDisplayIdToShowIme = displayId; 703 } 704 705 @GuardedBy("ImfLock.class") getDisplayIdToShowIme()706 int getDisplayIdToShowIme() { 707 return mDisplayIdToShowIme; 708 } 709 710 @GuardedBy("ImfLock.class") setDeviceIdToShowIme(int deviceId)711 void setDeviceIdToShowIme(int deviceId) { 712 mDeviceIdToShowIme = deviceId; 713 } 714 715 @GuardedBy("ImfLock.class") getDeviceIdToShowIme()716 int getDeviceIdToShowIme() { 717 return mDeviceIdToShowIme; 718 } 719 720 @UserIdInt getUserId()721 int getUserId() { 722 return mUserId; 723 } 724 725 @GuardedBy("ImfLock.class") setImeWindowVis(@meWindowVisibility int imeWindowVis)726 void setImeWindowVis(@ImeWindowVisibility int imeWindowVis) { 727 mImeWindowVis = imeWindowVis; 728 } 729 730 @ImeWindowVisibility 731 @GuardedBy("ImfLock.class") getImeWindowVis()732 int getImeWindowVis() { 733 return mImeWindowVis; 734 } 735 736 @BackDispositionMode 737 @GuardedBy("ImfLock.class") getBackDisposition()738 int getBackDisposition() { 739 return mBackDisposition; 740 } 741 742 @GuardedBy("ImfLock.class") setBackDisposition(@ackDispositionMode int backDisposition)743 void setBackDisposition(@BackDispositionMode int backDisposition) { 744 mBackDisposition = backDisposition; 745 } 746 747 @GuardedBy("ImfLock.class") dump(@onNull PrintWriter pw, @NonNull String prefix)748 void dump(@NonNull PrintWriter pw, @NonNull String prefix) { 749 pw.println(prefix + "mSelectedMethodId=" + mSelectedMethodId); 750 pw.println(prefix + "mCurrentSubtype=" + mCurrentSubtype); 751 pw.println(prefix + "mCurSeq=" + mCurSeq); 752 pw.println(prefix + "mCurId=" + mCurId); 753 pw.println(prefix + "mHasMainConnection=" + mHasMainConnection); 754 pw.println(prefix + "mVisibleBound=" + mVisibleBound); 755 pw.println(prefix + "mCurToken=" + mCurToken); 756 pw.println(prefix + "mCurTokenDisplayId=" + mCurTokenDisplayId); 757 pw.println(prefix + "mCurHostInputToken=" + getCurHostInputToken()); 758 pw.println(prefix + "mCurIntent=" + mCurIntent); 759 pw.println(prefix + "mCurMethod=" + mCurMethod); 760 pw.println(prefix + "mImeWindowVis=" + mImeWindowVis); 761 pw.println(prefix + "mBackDisposition=" + mBackDisposition); 762 pw.println(prefix + "mDisplayIdToShowIme=" + mDisplayIdToShowIme); 763 pw.println(prefix + "mDeviceIdToShowIme=" + mDeviceIdToShowIme); 764 pw.println(prefix + "mSupportsStylusHw=" + mSupportsStylusHw); 765 pw.println(prefix + "mSupportsConnectionlessStylusHw=" + mSupportsConnectionlessStylusHw); 766 } 767 } 768