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