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