1 /*
2  * Copyright (C) 2022 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 android.view.inputmethod;
18 
19 import android.Manifest;
20 import android.annotation.AnyThread;
21 import android.annotation.DurationMillisLong;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.RequiresNoPermission;
25 import android.annotation.RequiresPermission;
26 import android.annotation.UserIdInt;
27 import android.content.Context;
28 import android.os.Binder;
29 import android.os.IBinder;
30 import android.os.RemoteException;
31 import android.os.ResultReceiver;
32 import android.os.ServiceManager;
33 import android.view.WindowManager;
34 import android.window.ImeOnBackInvokedDispatcher;
35 
36 import com.android.internal.inputmethod.DirectBootAwareness;
37 import com.android.internal.inputmethod.IImeTracker;
38 import com.android.internal.inputmethod.IInputMethodClient;
39 import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
40 import com.android.internal.inputmethod.IRemoteInputConnection;
41 import com.android.internal.inputmethod.InputBindResult;
42 import com.android.internal.inputmethod.SoftInputShowHideReason;
43 import com.android.internal.inputmethod.StartInputFlags;
44 import com.android.internal.inputmethod.StartInputReason;
45 import com.android.internal.view.IInputMethodManager;
46 
47 import java.util.ArrayList;
48 import java.util.List;
49 import java.util.function.Consumer;
50 
51 /**
52  * A global wrapper to directly invoke {@link IInputMethodManager} IPCs.
53  *
54  * <p>All public static methods are guaranteed to be thread-safe.</p>
55  *
56  * <p>All public methods are guaranteed to do nothing when {@link IInputMethodManager} is
57  * unavailable.</p>
58  *
59  * <p>If you want to use any of this method outside of {@code android.view.inputmethod}, create
60  * a wrapper method in {@link InputMethodManagerGlobal} instead of making this class public.</p>
61  */
62 final class IInputMethodManagerGlobalInvoker {
63     @Nullable
64     private static volatile IInputMethodManager sServiceCache = null;
65 
66     @Nullable
67     private static volatile IImeTracker sTrackerServiceCache = null;
68 
69     /**
70      * @return {@code true} if {@link IInputMethodManager} is available.
71      */
72     @AnyThread
isAvailable()73     static boolean isAvailable() {
74         return getService() != null;
75     }
76 
77     @AnyThread
78     @Nullable
getService()79     static IInputMethodManager getService() {
80         IInputMethodManager service = sServiceCache;
81         if (service == null) {
82             if (InputMethodManager.isInEditModeInternal()) {
83                 return null;
84             }
85             service = IInputMethodManager.Stub.asInterface(
86                     ServiceManager.getService(Context.INPUT_METHOD_SERVICE));
87             if (service == null) {
88                 return null;
89             }
90             sServiceCache = service;
91         }
92         return service;
93     }
94 
95     @AnyThread
handleRemoteExceptionOrRethrow(@onNull RemoteException e, @Nullable Consumer<RemoteException> exceptionHandler)96     private static void handleRemoteExceptionOrRethrow(@NonNull RemoteException e,
97             @Nullable Consumer<RemoteException> exceptionHandler) {
98         if (exceptionHandler != null) {
99             exceptionHandler.accept(e);
100         } else {
101             throw e.rethrowFromSystemServer();
102         }
103     }
104 
105     /**
106      * Invokes {@link IInputMethodManager#startProtoDump(byte[], int, String)}.
107      *
108      * @param protoDump client or service side information to be stored by the server
109      * @param source where the information is coming from, refer to
110      *               {@link com.android.internal.inputmethod.ImeTracing#IME_TRACING_FROM_CLIENT} and
111      *               {@link com.android.internal.inputmethod.ImeTracing#IME_TRACING_FROM_IMS}
112      * @param where where the information is coming from.
113      * @param exceptionHandler an optional {@link RemoteException} handler.
114      */
115     @AnyThread
116     @RequiresNoPermission
startProtoDump(byte[] protoDump, int source, String where, @Nullable Consumer<RemoteException> exceptionHandler)117     static void startProtoDump(byte[] protoDump, int source, String where,
118             @Nullable Consumer<RemoteException> exceptionHandler) {
119         final IInputMethodManager service = getService();
120         if (service == null) {
121             return;
122         }
123         try {
124             service.startProtoDump(protoDump, source, where);
125         } catch (RemoteException e) {
126             handleRemoteExceptionOrRethrow(e, exceptionHandler);
127         }
128     }
129 
130     /**
131      * Invokes {@link IInputMethodManager#startImeTrace()}.
132      *
133      * @param exceptionHandler an optional {@link RemoteException} handler.
134      */
135     @AnyThread
136     @RequiresPermission(Manifest.permission.CONTROL_UI_TRACING)
startImeTrace(@ullable Consumer<RemoteException> exceptionHandler)137     static void startImeTrace(@Nullable Consumer<RemoteException> exceptionHandler) {
138         final IInputMethodManager service = getService();
139         if (service == null) {
140             return;
141         }
142         try {
143             service.startImeTrace();
144         } catch (RemoteException e) {
145             handleRemoteExceptionOrRethrow(e, exceptionHandler);
146         }
147     }
148 
149     /**
150      * Invokes {@link IInputMethodManager#stopImeTrace()}.
151      *
152      * @param exceptionHandler an optional {@link RemoteException} handler.
153      */
154     @AnyThread
155     @RequiresPermission(Manifest.permission.CONTROL_UI_TRACING)
stopImeTrace(@ullable Consumer<RemoteException> exceptionHandler)156     static void stopImeTrace(@Nullable Consumer<RemoteException> exceptionHandler) {
157         final IInputMethodManager service = getService();
158         if (service == null) {
159             return;
160         }
161         try {
162             service.stopImeTrace();
163         } catch (RemoteException e) {
164             handleRemoteExceptionOrRethrow(e, exceptionHandler);
165         }
166     }
167 
168     /**
169      * Invokes {@link IInputMethodManager#isImeTraceEnabled()}.
170      *
171      * @return The return value of {@link IInputMethodManager#isImeTraceEnabled()}.
172      */
173     @AnyThread
174     @RequiresNoPermission
isImeTraceEnabled()175     static boolean isImeTraceEnabled() {
176         final IInputMethodManager service = getService();
177         if (service == null) {
178             return false;
179         }
180         try {
181             return service.isImeTraceEnabled();
182         } catch (RemoteException e) {
183             throw e.rethrowFromSystemServer();
184         }
185     }
186 
187     /**
188      * Invokes {@link IInputMethodManager#removeImeSurface()}
189      */
190     @AnyThread
191     @RequiresPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
removeImeSurface(@ullable Consumer<RemoteException> exceptionHandler)192     static void removeImeSurface(@Nullable Consumer<RemoteException> exceptionHandler) {
193         final IInputMethodManager service = getService();
194         if (service == null) {
195             return;
196         }
197         try {
198             service.removeImeSurface();
199         } catch (RemoteException e) {
200             handleRemoteExceptionOrRethrow(e, exceptionHandler);
201         }
202     }
203 
204     @AnyThread
addClient(@onNull IInputMethodClient client, @NonNull IRemoteInputConnection fallbackInputConnection, int untrustedDisplayId)205     static void addClient(@NonNull IInputMethodClient client,
206             @NonNull IRemoteInputConnection fallbackInputConnection, int untrustedDisplayId) {
207         final IInputMethodManager service = getService();
208         if (service == null) {
209             return;
210         }
211         try {
212             service.addClient(client, fallbackInputConnection, untrustedDisplayId);
213         } catch (RemoteException e) {
214             throw e.rethrowFromSystemServer();
215         }
216     }
217 
218     @AnyThread
219     @Nullable
220     @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
getCurrentInputMethodInfoAsUser(@serIdInt int userId)221     static InputMethodInfo getCurrentInputMethodInfoAsUser(@UserIdInt int userId) {
222         final IInputMethodManager service = getService();
223         if (service == null) {
224             return null;
225         }
226         try {
227             return service.getCurrentInputMethodInfoAsUser(userId);
228         } catch (RemoteException e) {
229             throw e.rethrowFromSystemServer();
230         }
231     }
232 
233     @AnyThread
234     @NonNull
235     @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
getInputMethodList(@serIdInt int userId, @DirectBootAwareness int directBootAwareness)236     static List<InputMethodInfo> getInputMethodList(@UserIdInt int userId,
237             @DirectBootAwareness int directBootAwareness) {
238         final IInputMethodManager service = getService();
239         if (service == null) {
240             return new ArrayList<>();
241         }
242         try {
243             return service.getInputMethodList(userId, directBootAwareness);
244         } catch (RemoteException e) {
245             throw e.rethrowFromSystemServer();
246         }
247     }
248 
249     @AnyThread
250     @NonNull
251     @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
getEnabledInputMethodList(@serIdInt int userId)252     static List<InputMethodInfo> getEnabledInputMethodList(@UserIdInt int userId) {
253         final IInputMethodManager service = getService();
254         if (service == null) {
255             return new ArrayList<>();
256         }
257         try {
258             return service.getEnabledInputMethodList(userId);
259         } catch (RemoteException e) {
260             throw e.rethrowFromSystemServer();
261         }
262     }
263 
264     @AnyThread
265     @NonNull
266     @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
getEnabledInputMethodSubtypeList(@ullable String imiId, boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId)267     static List<InputMethodSubtype> getEnabledInputMethodSubtypeList(@Nullable String imiId,
268             boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId) {
269         final IInputMethodManager service = getService();
270         if (service == null) {
271             return new ArrayList<>();
272         }
273         try {
274             return service.getEnabledInputMethodSubtypeList(imiId,
275                     allowsImplicitlyEnabledSubtypes, userId);
276         } catch (RemoteException e) {
277             throw e.rethrowFromSystemServer();
278         }
279     }
280 
281     @AnyThread
282     @Nullable
283     @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
getLastInputMethodSubtype(@serIdInt int userId)284     static InputMethodSubtype getLastInputMethodSubtype(@UserIdInt int userId) {
285         final IInputMethodManager service = getService();
286         if (service == null) {
287             return null;
288         }
289         try {
290             return service.getLastInputMethodSubtype(userId);
291         } catch (RemoteException e) {
292             throw e.rethrowFromSystemServer();
293         }
294     }
295 
296     @AnyThread
showSoftInput(@onNull IInputMethodClient client, @Nullable IBinder windowToken, @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags, int lastClickToolType, @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason)297     static boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
298             @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
299             int lastClickToolType, @Nullable ResultReceiver resultReceiver,
300             @SoftInputShowHideReason int reason) {
301         final IInputMethodManager service = getService();
302         if (service == null) {
303             return false;
304         }
305         try {
306             return service.showSoftInput(client, windowToken, statsToken, flags, lastClickToolType,
307                     resultReceiver, reason);
308         } catch (RemoteException e) {
309             throw e.rethrowFromSystemServer();
310         }
311     }
312 
313     @AnyThread
hideSoftInput(@onNull IInputMethodClient client, @Nullable IBinder windowToken, @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags, @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason)314     static boolean hideSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
315             @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
316             @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
317         final IInputMethodManager service = getService();
318         if (service == null) {
319             return false;
320         }
321         try {
322             return service.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver,
323                     reason);
324         } catch (RemoteException e) {
325             throw e.rethrowFromSystemServer();
326         }
327     }
328 
329     @AnyThread
330     @NonNull
331     @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
startInputOrWindowGainedFocus(@tartInputReason int startInputReason, @NonNull IInputMethodClient client, @Nullable IBinder windowToken, @StartInputFlags int startInputFlags, @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo, @Nullable IRemoteInputConnection remoteInputConnection, @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection, int unverifiedTargetSdkVersion, @UserIdInt int userId, @NonNull ImeOnBackInvokedDispatcher imeDispatcher)332     static InputBindResult startInputOrWindowGainedFocus(@StartInputReason int startInputReason,
333             @NonNull IInputMethodClient client, @Nullable IBinder windowToken,
334             @StartInputFlags int startInputFlags,
335             @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
336             @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
337             @Nullable IRemoteInputConnection remoteInputConnection,
338             @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
339             int unverifiedTargetSdkVersion, @UserIdInt int userId,
340             @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
341         final IInputMethodManager service = getService();
342         if (service == null) {
343             return InputBindResult.NULL;
344         }
345         try {
346             return service.startInputOrWindowGainedFocus(startInputReason, client, windowToken,
347                     startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection,
348                     remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId,
349                     imeDispatcher);
350         } catch (RemoteException e) {
351             throw e.rethrowFromSystemServer();
352         }
353     }
354 
355     @AnyThread
showInputMethodPickerFromClient(@onNull IInputMethodClient client, int auxiliarySubtypeMode)356     static void showInputMethodPickerFromClient(@NonNull IInputMethodClient client,
357             int auxiliarySubtypeMode) {
358         final IInputMethodManager service = getService();
359         if (service == null) {
360             return;
361         }
362         try {
363             service.showInputMethodPickerFromClient(client, auxiliarySubtypeMode);
364         } catch (RemoteException e) {
365             throw e.rethrowFromSystemServer();
366         }
367     }
368 
369     @AnyThread
370     @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId)371     static void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId) {
372         final IInputMethodManager service = getService();
373         if (service == null) {
374             return;
375         }
376         try {
377             service.showInputMethodPickerFromSystem(auxiliarySubtypeMode, displayId);
378         } catch (RemoteException e) {
379             throw e.rethrowFromSystemServer();
380         }
381     }
382 
383     @AnyThread
384     @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
isInputMethodPickerShownForTest()385     static boolean isInputMethodPickerShownForTest() {
386         final IInputMethodManager service = getService();
387         if (service == null) {
388             return false;
389         }
390         try {
391             return service.isInputMethodPickerShownForTest();
392         } catch (RemoteException e) {
393             throw e.rethrowFromSystemServer();
394         }
395     }
396 
397     @AnyThread
398     @Nullable
399     @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
getCurrentInputMethodSubtype(@serIdInt int userId)400     static InputMethodSubtype getCurrentInputMethodSubtype(@UserIdInt int userId) {
401         final IInputMethodManager service = getService();
402         if (service == null) {
403             return null;
404         }
405         try {
406             return service.getCurrentInputMethodSubtype(userId);
407         } catch (RemoteException e) {
408             throw e.rethrowFromSystemServer();
409         }
410     }
411 
412     @AnyThread
413     @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
setAdditionalInputMethodSubtypes(@onNull String imeId, @NonNull InputMethodSubtype[] subtypes, @UserIdInt int userId)414     static void setAdditionalInputMethodSubtypes(@NonNull String imeId,
415             @NonNull InputMethodSubtype[] subtypes, @UserIdInt int userId) {
416         final IInputMethodManager service = getService();
417         if (service == null) {
418             return;
419         }
420         try {
421             service.setAdditionalInputMethodSubtypes(imeId, subtypes, userId);
422         } catch (RemoteException e) {
423             throw e.rethrowFromSystemServer();
424         }
425     }
426 
427     @AnyThread
428     @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
setExplicitlyEnabledInputMethodSubtypes(@onNull String imeId, @NonNull int[] subtypeHashCodes, @UserIdInt int userId)429     static void setExplicitlyEnabledInputMethodSubtypes(@NonNull String imeId,
430             @NonNull int[] subtypeHashCodes, @UserIdInt int userId) {
431         final IInputMethodManager service = getService();
432         if (service == null) {
433             return;
434         }
435         try {
436             service.setExplicitlyEnabledInputMethodSubtypes(imeId, subtypeHashCodes, userId);
437         } catch (RemoteException e) {
438             throw e.rethrowFromSystemServer();
439         }
440     }
441 
442     @AnyThread
getInputMethodWindowVisibleHeight(@onNull IInputMethodClient client)443     static int getInputMethodWindowVisibleHeight(@NonNull IInputMethodClient client) {
444         final IInputMethodManager service = getService();
445         if (service == null) {
446             return 0;
447         }
448         try {
449             return service.getInputMethodWindowVisibleHeight(client);
450         } catch (RemoteException e) {
451             throw e.rethrowFromSystemServer();
452         }
453     }
454 
455     @AnyThread
reportVirtualDisplayGeometryAsync(@onNull IInputMethodClient client, int childDisplayId, @Nullable float[] matrixValues)456     static void reportVirtualDisplayGeometryAsync(@NonNull IInputMethodClient client,
457             int childDisplayId, @Nullable float[] matrixValues) {
458         final IInputMethodManager service = getService();
459         if (service == null) {
460             return;
461         }
462         try {
463             service.reportVirtualDisplayGeometryAsync(client, childDisplayId, matrixValues);
464         } catch (RemoteException e) {
465             throw e.rethrowFromSystemServer();
466         }
467     }
468 
469     @AnyThread
reportPerceptibleAsync(@onNull IBinder windowToken, boolean perceptible)470     static void reportPerceptibleAsync(@NonNull IBinder windowToken, boolean perceptible) {
471         final IInputMethodManager service = getService();
472         if (service == null) {
473             return;
474         }
475         try {
476             service.reportPerceptibleAsync(windowToken, perceptible);
477         } catch (RemoteException e) {
478             throw e.rethrowFromSystemServer();
479         }
480     }
481 
482     @AnyThread
removeImeSurfaceFromWindowAsync(@onNull IBinder windowToken)483     static void removeImeSurfaceFromWindowAsync(@NonNull IBinder windowToken) {
484         final IInputMethodManager service = getService();
485         if (service == null) {
486             return;
487         }
488         try {
489             service.removeImeSurfaceFromWindowAsync(windowToken);
490         } catch (RemoteException e) {
491             throw e.rethrowFromSystemServer();
492         }
493     }
494 
495     @AnyThread
startStylusHandwriting(@onNull IInputMethodClient client)496     static void startStylusHandwriting(@NonNull IInputMethodClient client) {
497         final IInputMethodManager service = getService();
498         if (service == null) {
499             return;
500         }
501         try {
502             service.startStylusHandwriting(client);
503         } catch (RemoteException e) {
504             throw e.rethrowFromSystemServer();
505         }
506     }
507 
508     @AnyThread
prepareStylusHandwritingDelegation( @onNull IInputMethodClient client, @UserIdInt int userId, @NonNull String delegatePackageName, @NonNull String delegatorPackageName)509     static void prepareStylusHandwritingDelegation(
510             @NonNull IInputMethodClient client,
511             @UserIdInt int userId,
512             @NonNull String delegatePackageName,
513             @NonNull String delegatorPackageName) {
514         final IInputMethodManager service = getService();
515         if (service == null) {
516             return;
517         }
518         try {
519             service.prepareStylusHandwritingDelegation(
520                     client, userId, delegatePackageName, delegatorPackageName);
521         } catch (RemoteException e) {
522             throw e.rethrowFromSystemServer();
523         }
524     }
525 
526     @AnyThread
acceptStylusHandwritingDelegation( @onNull IInputMethodClient client, @UserIdInt int userId, @NonNull String delegatePackageName, @NonNull String delegatorPackageName)527     static boolean acceptStylusHandwritingDelegation(
528             @NonNull IInputMethodClient client,
529             @UserIdInt int userId,
530             @NonNull String delegatePackageName,
531             @NonNull String delegatorPackageName) {
532         final IInputMethodManager service = getService();
533         if (service == null) {
534             return false;
535         }
536         try {
537             return service.acceptStylusHandwritingDelegation(
538                     client, userId, delegatePackageName, delegatorPackageName);
539         } catch (RemoteException e) {
540             throw e.rethrowFromSystemServer();
541         }
542     }
543 
544     @AnyThread
545     @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
isStylusHandwritingAvailableAsUser(@serIdInt int userId)546     static boolean isStylusHandwritingAvailableAsUser(@UserIdInt int userId) {
547         final IInputMethodManager service = getService();
548         if (service == null) {
549             return false;
550         }
551         try {
552             return service.isStylusHandwritingAvailableAsUser(userId);
553         } catch (RemoteException e) {
554             throw e.rethrowFromSystemServer();
555         }
556     }
557 
558     @AnyThread
559     @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
addVirtualStylusIdForTestSession(IInputMethodClient client)560     static void addVirtualStylusIdForTestSession(IInputMethodClient client) {
561         final IInputMethodManager service = getService();
562         if (service == null) {
563             return;
564         }
565         try {
566             service.addVirtualStylusIdForTestSession(client);
567         } catch (RemoteException e) {
568             throw e.rethrowFromSystemServer();
569         }
570     }
571 
572     @AnyThread
573     @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
setStylusWindowIdleTimeoutForTest( IInputMethodClient client, @DurationMillisLong long timeout)574     static void setStylusWindowIdleTimeoutForTest(
575             IInputMethodClient client, @DurationMillisLong long timeout) {
576         final IInputMethodManager service = getService();
577         if (service == null) {
578             return;
579         }
580         try {
581             service.setStylusWindowIdleTimeoutForTest(client, timeout);
582         } catch (RemoteException e) {
583             throw e.rethrowFromSystemServer();
584         }
585     }
586 
587     /** @see com.android.server.inputmethod.ImeTrackerService#onRequestShow */
588     @AnyThread
589     @NonNull
onRequestShow(@onNull String tag, int uid, @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason)590     static ImeTracker.Token onRequestShow(@NonNull String tag, int uid,
591             @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
592         final IImeTracker service = getImeTrackerService();
593         if (service == null) {
594             // Create token with "fake" binder if the service was not found.
595             return new ImeTracker.Token(new Binder(), tag);
596         }
597         try {
598             return service.onRequestShow(tag, uid, origin, reason);
599         } catch (RemoteException e) {
600             throw e.rethrowFromSystemServer();
601         }
602     }
603 
604     /** @see com.android.server.inputmethod.ImeTrackerService#onRequestHide */
605     @AnyThread
606     @NonNull
onRequestHide(@onNull String tag, int uid, @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason)607     static ImeTracker.Token onRequestHide(@NonNull String tag, int uid,
608             @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
609         final IImeTracker service = getImeTrackerService();
610         if (service == null) {
611             // Create token with "fake" binder if the service was not found.
612             return new ImeTracker.Token(new Binder(), tag);
613         }
614         try {
615             return service.onRequestHide(tag, uid, origin, reason);
616         } catch (RemoteException e) {
617             throw e.rethrowFromSystemServer();
618         }
619     }
620 
621     /** @see com.android.server.inputmethod.ImeTrackerService#onProgress */
622     @AnyThread
onProgress(@onNull IBinder binder, @ImeTracker.Phase int phase)623     static void onProgress(@NonNull IBinder binder, @ImeTracker.Phase int phase) {
624         final IImeTracker service = getImeTrackerService();
625         if (service == null) {
626             return;
627         }
628         try {
629             service.onProgress(binder, phase);
630         } catch (RemoteException e) {
631             throw e.rethrowFromSystemServer();
632         }
633     }
634 
635     /** @see com.android.server.inputmethod.ImeTrackerService#onFailed */
636     @AnyThread
onFailed(@onNull ImeTracker.Token statsToken, @ImeTracker.Phase int phase)637     static void onFailed(@NonNull ImeTracker.Token statsToken, @ImeTracker.Phase int phase) {
638         final IImeTracker service = getImeTrackerService();
639         if (service == null) {
640             return;
641         }
642         try {
643             service.onFailed(statsToken, phase);
644         } catch (RemoteException e) {
645             throw e.rethrowFromSystemServer();
646         }
647     }
648 
649     /** @see com.android.server.inputmethod.ImeTrackerService#onCancelled */
650     @AnyThread
onCancelled(@onNull ImeTracker.Token statsToken, @ImeTracker.Phase int phase)651     static void onCancelled(@NonNull ImeTracker.Token statsToken, @ImeTracker.Phase int phase) {
652         final IImeTracker service = getImeTrackerService();
653         if (service == null) {
654             return;
655         }
656         try {
657             service.onCancelled(statsToken, phase);
658         } catch (RemoteException e) {
659             throw e.rethrowFromSystemServer();
660         }
661     }
662 
663     /** @see com.android.server.inputmethod.ImeTrackerService#onShown */
664     @AnyThread
onShown(@onNull ImeTracker.Token statsToken)665     static void onShown(@NonNull ImeTracker.Token statsToken) {
666         final IImeTracker service = getImeTrackerService();
667         if (service == null) {
668             return;
669         }
670         try {
671             service.onShown(statsToken);
672         } catch (RemoteException e) {
673             throw e.rethrowFromSystemServer();
674         }
675     }
676 
677     /** @see com.android.server.inputmethod.ImeTrackerService#onHidden */
678     @AnyThread
onHidden(@onNull ImeTracker.Token statsToken)679     static void onHidden(@NonNull ImeTracker.Token statsToken) {
680         final IImeTracker service = getImeTrackerService();
681         if (service == null) {
682             return;
683         }
684         try {
685             service.onHidden(statsToken);
686         } catch (RemoteException e) {
687             throw e.rethrowFromSystemServer();
688         }
689     }
690 
691     /** @see com.android.server.inputmethod.ImeTrackerService#hasPendingImeVisibilityRequests */
692     @AnyThread
693     @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
hasPendingImeVisibilityRequests()694     static boolean hasPendingImeVisibilityRequests() {
695         final var service = getImeTrackerService();
696         if (service == null) {
697             return true;
698         }
699         try {
700             return service.hasPendingImeVisibilityRequests();
701         } catch (RemoteException e) {
702             throw e.rethrowFromSystemServer();
703         }
704     }
705 
706     @AnyThread
707     @Nullable
getImeTrackerService()708     private static IImeTracker getImeTrackerService() {
709         var trackerService = sTrackerServiceCache;
710         if (trackerService == null) {
711             final var service = getService();
712             if (service == null) {
713                 return null;
714             }
715 
716             try {
717                 trackerService = service.getImeTrackerService();
718                 if (trackerService == null) {
719                     return null;
720                 }
721 
722                 sTrackerServiceCache = trackerService;
723             } catch (RemoteException e) {
724                 throw e.rethrowFromSystemServer();
725             }
726         }
727         return trackerService;
728     }
729 }
730