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