• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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.accessibilityservice;
18 
19 import android.accessibilityservice.GestureDescription.MotionEventGenerator;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.RequiresPermission;
24 import android.app.Service;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.ParceledListSlice;
28 import android.graphics.Region;
29 import android.os.Handler;
30 import android.os.IBinder;
31 import android.os.Looper;
32 import android.os.Message;
33 import android.os.RemoteException;
34 import android.provider.Settings;
35 import android.util.ArrayMap;
36 import android.util.Log;
37 import android.util.Slog;
38 import android.util.SparseArray;
39 import android.view.KeyEvent;
40 import android.view.WindowManager;
41 import android.view.WindowManagerImpl;
42 import android.view.accessibility.AccessibilityEvent;
43 import android.view.accessibility.AccessibilityInteractionClient;
44 import android.view.accessibility.AccessibilityNodeInfo;
45 import android.view.accessibility.AccessibilityWindowInfo;
46 
47 import com.android.internal.os.HandlerCaller;
48 import com.android.internal.os.SomeArgs;
49 
50 import java.lang.annotation.Retention;
51 import java.lang.annotation.RetentionPolicy;
52 import java.util.List;
53 
54 /**
55  * Accessibility services should only be used to assist users with disabilities in using
56  * Android devices and apps. They run in the background and receive callbacks by the system
57  * when {@link AccessibilityEvent}s are fired. Such events denote some state transition
58  * in the user interface, for example, the focus has changed, a button has been clicked,
59  * etc. Such a service can optionally request the capability for querying the content
60  * of the active window. Development of an accessibility service requires extending this
61  * class and implementing its abstract methods.
62  *
63  * <div class="special reference">
64  * <h3>Developer Guides</h3>
65  * <p>For more information about creating AccessibilityServices, read the
66  * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
67  * developer guide.</p>
68  * </div>
69  *
70  * <h3>Lifecycle</h3>
71  * <p>
72  * The lifecycle of an accessibility service is managed exclusively by the system and
73  * follows the established service life cycle. Starting an accessibility service is triggered
74  * exclusively by the user explicitly turning the service on in device settings. After the system
75  * binds to a service, it calls {@link AccessibilityService#onServiceConnected()}. This method can
76  * be overriden by clients that want to perform post binding setup.
77  * </p>
78  * <p>
79  * An accessibility service stops either when the user turns it off in device settings or when
80  * it calls {@link AccessibilityService#disableSelf()}.
81  * </p>
82  * <h3>Declaration</h3>
83  * <p>
84  * An accessibility is declared as any other service in an AndroidManifest.xml, but it
85  * must do two things:
86  * <ul>
87  *     <ol>
88  *         Specify that it handles the "android.accessibilityservice.AccessibilityService"
89  *         {@link android.content.Intent}.
90  *     </ol>
91  *     <ol>
92  *         Request the {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission to
93  *         ensure that only the system can bind to it.
94  *     </ol>
95  * </ul>
96  * If either of these items is missing, the system will ignore the accessibility service.
97  * Following is an example declaration:
98  * </p>
99  * <pre> &lt;service android:name=".MyAccessibilityService"
100  *         android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"&gt;
101  *     &lt;intent-filter&gt;
102  *         &lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;
103  *     &lt;/intent-filter&gt;
104  *     . . .
105  * &lt;/service&gt;</pre>
106  * <h3>Configuration</h3>
107  * <p>
108  * An accessibility service can be configured to receive specific types of accessibility events,
109  * listen only to specific packages, get events from each type only once in a given time frame,
110  * retrieve window content, specify a settings activity, etc.
111  * </p>
112  * <p>
113  * There are two approaches for configuring an accessibility service:
114  * </p>
115  * <ul>
116  * <li>
117  * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring
118  * the service. A service declaration with a meta-data tag is presented below:
119  * <pre> &lt;service android:name=".MyAccessibilityService"&gt;
120  *     &lt;intent-filter&gt;
121  *         &lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;
122  *     &lt;/intent-filter&gt;
123  *     &lt;meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" /&gt;
124  * &lt;/service&gt;</pre>
125  * <p class="note">
126  * <strong>Note:</strong> This approach enables setting all properties.
127  * </p>
128  * <p>
129  * For more details refer to {@link #SERVICE_META_DATA} and
130  * <code>&lt;{@link android.R.styleable#AccessibilityService accessibility-service}&gt;</code>.
131  * </p>
132  * </li>
133  * <li>
134  * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note
135  * that this method can be called any time to dynamically change the service configuration.
136  * <p class="note">
137  * <strong>Note:</strong> This approach enables setting only dynamically configurable properties:
138  * {@link AccessibilityServiceInfo#eventTypes},
139  * {@link AccessibilityServiceInfo#feedbackType},
140  * {@link AccessibilityServiceInfo#flags},
141  * {@link AccessibilityServiceInfo#notificationTimeout},
142  * {@link AccessibilityServiceInfo#packageNames}
143  * </p>
144  * <p>
145  * For more details refer to {@link AccessibilityServiceInfo}.
146  * </p>
147  * </li>
148  * </ul>
149  * <h3>Retrieving window content</h3>
150  * <p>
151  * A service can specify in its declaration that it can retrieve window
152  * content which is represented as a tree of {@link AccessibilityWindowInfo} and
153  * {@link AccessibilityNodeInfo} objects. Note that
154  * declaring this capability requires that the service declares its configuration via
155  * an XML resource referenced by {@link #SERVICE_META_DATA}.
156  * </p>
157  * <p>
158  * Window content may be retrieved with
159  * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()},
160  * {@link AccessibilityService#findFocus(int)},
161  * {@link AccessibilityService#getWindows()}, or
162  * {@link AccessibilityService#getRootInActiveWindow()}.
163  * </p>
164  * <p class="note">
165  * <strong>Note</strong> An accessibility service may have requested to be notified for
166  * a subset of the event types, and thus be unaware when the node hierarchy has changed. It is also
167  * possible for a node to contain outdated information because the window content may change at any
168  * time.
169  * </p>
170  * <h3>Notification strategy</h3>
171  * <p>
172  * All accessibility services are notified of all events they have requested, regardless of their
173  * feedback type.
174  * </p>
175  * <p class="note">
176  * <strong>Note:</strong> The event notification timeout is useful to avoid propagating
177  * events to the client too frequently since this is accomplished via an expensive
178  * interprocess call. One can think of the timeout as a criteria to determine when
179  * event generation has settled down.</p>
180  * <h3>Event types</h3>
181  * <ul>
182  * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}</li>
183  * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}</li>
184  * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}</li>
185  * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}</li>
186  * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}</li>
187  * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}</li>
188  * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}</li>
189  * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}</li>
190  * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}</li>
191  * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}</li>
192  * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}</li>
193  * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}</li>
194  * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}</li>
195  * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}</li>
196  * <li>{@link AccessibilityEvent#TYPE_ANNOUNCEMENT}</li>
197  * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_START}</li>
198  * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_END}</li>
199  * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_START}</li>
200  * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_END}</li>
201  * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED}</li>
202  * <li>{@link AccessibilityEvent#TYPE_WINDOWS_CHANGED}</li>
203  * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED}</li>
204  * </ul>
205  * <h3>Feedback types</h3>
206  * <ul>
207  * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li>
208  * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}</li>
209  * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li>
210  * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}</li>
211  * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}</li>
212  * <li>{@link AccessibilityServiceInfo#FEEDBACK_BRAILLE}</li>
213  * </ul>
214  * @see AccessibilityEvent
215  * @see AccessibilityServiceInfo
216  * @see android.view.accessibility.AccessibilityManager
217  */
218 public abstract class AccessibilityService extends Service {
219 
220     /**
221      * The user has performed a swipe up gesture on the touch screen.
222      */
223     public static final int GESTURE_SWIPE_UP = 1;
224 
225     /**
226      * The user has performed a swipe down gesture on the touch screen.
227      */
228     public static final int GESTURE_SWIPE_DOWN = 2;
229 
230     /**
231      * The user has performed a swipe left gesture on the touch screen.
232      */
233     public static final int GESTURE_SWIPE_LEFT = 3;
234 
235     /**
236      * The user has performed a swipe right gesture on the touch screen.
237      */
238     public static final int GESTURE_SWIPE_RIGHT = 4;
239 
240     /**
241      * The user has performed a swipe left and right gesture on the touch screen.
242      */
243     public static final int GESTURE_SWIPE_LEFT_AND_RIGHT = 5;
244 
245     /**
246      * The user has performed a swipe right and left gesture on the touch screen.
247      */
248     public static final int GESTURE_SWIPE_RIGHT_AND_LEFT = 6;
249 
250     /**
251      * The user has performed a swipe up and down gesture on the touch screen.
252      */
253     public static final int GESTURE_SWIPE_UP_AND_DOWN = 7;
254 
255     /**
256      * The user has performed a swipe down and up gesture on the touch screen.
257      */
258     public static final int GESTURE_SWIPE_DOWN_AND_UP = 8;
259 
260     /**
261      * The user has performed a left and up gesture on the touch screen.
262      */
263     public static final int GESTURE_SWIPE_LEFT_AND_UP = 9;
264 
265     /**
266      * The user has performed a left and down gesture on the touch screen.
267      */
268     public static final int GESTURE_SWIPE_LEFT_AND_DOWN = 10;
269 
270     /**
271      * The user has performed a right and up gesture on the touch screen.
272      */
273     public static final int GESTURE_SWIPE_RIGHT_AND_UP = 11;
274 
275     /**
276      * The user has performed a right and down gesture on the touch screen.
277      */
278     public static final int GESTURE_SWIPE_RIGHT_AND_DOWN = 12;
279 
280     /**
281      * The user has performed an up and left gesture on the touch screen.
282      */
283     public static final int GESTURE_SWIPE_UP_AND_LEFT = 13;
284 
285     /**
286      * The user has performed an up and right gesture on the touch screen.
287      */
288     public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14;
289 
290     /**
291      * The user has performed an down and left gesture on the touch screen.
292      */
293     public static final int GESTURE_SWIPE_DOWN_AND_LEFT = 15;
294 
295     /**
296      * The user has performed an down and right gesture on the touch screen.
297      */
298     public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16;
299 
300     /**
301      * The {@link Intent} that must be declared as handled by the service.
302      */
303     public static final String SERVICE_INTERFACE =
304         "android.accessibilityservice.AccessibilityService";
305 
306     /**
307      * Name under which an AccessibilityService component publishes information
308      * about itself. This meta-data must reference an XML resource containing an
309      * <code>&lt;{@link android.R.styleable#AccessibilityService accessibility-service}&gt;</code>
310      * tag. This is a a sample XML file configuring an accessibility service:
311      * <pre> &lt;accessibility-service
312      *     android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
313      *     android:packageNames="foo.bar, foo.baz"
314      *     android:accessibilityFeedbackType="feedbackSpoken"
315      *     android:notificationTimeout="100"
316      *     android:accessibilityFlags="flagDefault"
317      *     android:settingsActivity="foo.bar.TestBackActivity"
318      *     android:canRetrieveWindowContent="true"
319      *     android:canRequestTouchExplorationMode="true"
320      *     . . .
321      * /&gt;</pre>
322      */
323     public static final String SERVICE_META_DATA = "android.accessibilityservice";
324 
325     /**
326      * Action to go back.
327      */
328     public static final int GLOBAL_ACTION_BACK = 1;
329 
330     /**
331      * Action to go home.
332      */
333     public static final int GLOBAL_ACTION_HOME = 2;
334 
335     /**
336      * Action to toggle showing the overview of recent apps. Will fail on platforms that don't
337      * show recent apps.
338      */
339     public static final int GLOBAL_ACTION_RECENTS = 3;
340 
341     /**
342      * Action to open the notifications.
343      */
344     public static final int GLOBAL_ACTION_NOTIFICATIONS = 4;
345 
346     /**
347      * Action to open the quick settings.
348      */
349     public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5;
350 
351     /**
352      * Action to open the power long-press dialog.
353      */
354     public static final int GLOBAL_ACTION_POWER_DIALOG = 6;
355 
356     /**
357      * Action to toggle docking the current app's window
358      */
359     public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7;
360 
361     private static final String LOG_TAG = "AccessibilityService";
362 
363     /**
364      * Interface used by IAccessibilityServiceWrapper to call the service from its main thread.
365      * @hide
366      */
367     public interface Callbacks {
onAccessibilityEvent(AccessibilityEvent event)368         void onAccessibilityEvent(AccessibilityEvent event);
onInterrupt()369         void onInterrupt();
onServiceConnected()370         void onServiceConnected();
init(int connectionId, IBinder windowToken)371         void init(int connectionId, IBinder windowToken);
onGesture(int gestureId)372         boolean onGesture(int gestureId);
onKeyEvent(KeyEvent event)373         boolean onKeyEvent(KeyEvent event);
onMagnificationChanged(@onNull Region region, float scale, float centerX, float centerY)374         void onMagnificationChanged(@NonNull Region region,
375                 float scale, float centerX, float centerY);
onSoftKeyboardShowModeChanged(int showMode)376         void onSoftKeyboardShowModeChanged(int showMode);
onPerformGestureResult(int sequence, boolean completedSuccessfully)377         void onPerformGestureResult(int sequence, boolean completedSuccessfully);
onFingerprintCapturingGesturesChanged(boolean active)378         void onFingerprintCapturingGesturesChanged(boolean active);
onFingerprintGesture(int gesture)379         void onFingerprintGesture(int gesture);
onAccessibilityButtonClicked()380         void onAccessibilityButtonClicked();
onAccessibilityButtonAvailabilityChanged(boolean available)381         void onAccessibilityButtonAvailabilityChanged(boolean available);
382     }
383 
384     /**
385      * Annotations for Soft Keyboard show modes so tools can catch invalid show modes.
386      * @hide
387      */
388     @Retention(RetentionPolicy.SOURCE)
389     @IntDef({SHOW_MODE_AUTO, SHOW_MODE_HIDDEN})
390     public @interface SoftKeyboardShowMode {};
391     public static final int SHOW_MODE_AUTO = 0;
392     public static final int SHOW_MODE_HIDDEN = 1;
393 
394     private int mConnectionId = AccessibilityInteractionClient.NO_ID;
395 
396     private AccessibilityServiceInfo mInfo;
397 
398     private IBinder mWindowToken;
399 
400     private WindowManager mWindowManager;
401 
402     private MagnificationController mMagnificationController;
403     private SoftKeyboardController mSoftKeyboardController;
404     private AccessibilityButtonController mAccessibilityButtonController;
405 
406     private int mGestureStatusCallbackSequence;
407 
408     private SparseArray<GestureResultCallbackInfo> mGestureStatusCallbackInfos;
409 
410     private final Object mLock = new Object();
411 
412     private FingerprintGestureController mFingerprintGestureController;
413 
414     /**
415      * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
416      *
417      * @param event The new event. This event is owned by the caller and cannot be used after
418      * this method returns. Services wishing to use the event after this method returns should
419      * make a copy.
420      */
onAccessibilityEvent(AccessibilityEvent event)421     public abstract void onAccessibilityEvent(AccessibilityEvent event);
422 
423     /**
424      * Callback for interrupting the accessibility feedback.
425      */
onInterrupt()426     public abstract void onInterrupt();
427 
428     /**
429      * Dispatches service connection to internal components first, then the
430      * client code.
431      */
dispatchServiceConnected()432     private void dispatchServiceConnected() {
433         if (mMagnificationController != null) {
434             mMagnificationController.onServiceConnected();
435         }
436         if (mSoftKeyboardController != null) {
437             mSoftKeyboardController.onServiceConnected();
438         }
439 
440         // The client gets to handle service connection last, after we've set
441         // up any state upon which their code may rely.
442         onServiceConnected();
443     }
444 
445     /**
446      * This method is a part of the {@link AccessibilityService} lifecycle and is
447      * called after the system has successfully bound to the service. If is
448      * convenient to use this method for setting the {@link AccessibilityServiceInfo}.
449      *
450      * @see AccessibilityServiceInfo
451      * @see #setServiceInfo(AccessibilityServiceInfo)
452      */
onServiceConnected()453     protected void onServiceConnected() {
454 
455     }
456 
457     /**
458      * Called by the system when the user performs a specific gesture on the
459      * touch screen.
460      *
461      * <strong>Note:</strong> To receive gestures an accessibility service must
462      * request that the device is in touch exploration mode by setting the
463      * {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE}
464      * flag.
465      *
466      * @param gestureId The unique id of the performed gesture.
467      *
468      * @return Whether the gesture was handled.
469      *
470      * @see #GESTURE_SWIPE_UP
471      * @see #GESTURE_SWIPE_UP_AND_LEFT
472      * @see #GESTURE_SWIPE_UP_AND_DOWN
473      * @see #GESTURE_SWIPE_UP_AND_RIGHT
474      * @see #GESTURE_SWIPE_DOWN
475      * @see #GESTURE_SWIPE_DOWN_AND_LEFT
476      * @see #GESTURE_SWIPE_DOWN_AND_UP
477      * @see #GESTURE_SWIPE_DOWN_AND_RIGHT
478      * @see #GESTURE_SWIPE_LEFT
479      * @see #GESTURE_SWIPE_LEFT_AND_UP
480      * @see #GESTURE_SWIPE_LEFT_AND_RIGHT
481      * @see #GESTURE_SWIPE_LEFT_AND_DOWN
482      * @see #GESTURE_SWIPE_RIGHT
483      * @see #GESTURE_SWIPE_RIGHT_AND_UP
484      * @see #GESTURE_SWIPE_RIGHT_AND_LEFT
485      * @see #GESTURE_SWIPE_RIGHT_AND_DOWN
486      */
onGesture(int gestureId)487     protected boolean onGesture(int gestureId) {
488         return false;
489     }
490 
491     /**
492      * Callback that allows an accessibility service to observe the key events
493      * before they are passed to the rest of the system. This means that the events
494      * are first delivered here before they are passed to the device policy, the
495      * input method, or applications.
496      * <p>
497      * <strong>Note:</strong> It is important that key events are handled in such
498      * a way that the event stream that would be passed to the rest of the system
499      * is well-formed. For example, handling the down event but not the up event
500      * and vice versa would generate an inconsistent event stream.
501      * </p>
502      * <p>
503      * <strong>Note:</strong> The key events delivered in this method are copies
504      * and modifying them will have no effect on the events that will be passed
505      * to the system. This method is intended to perform purely filtering
506      * functionality.
507      * <p>
508      *
509      * @param event The event to be processed. This event is owned by the caller and cannot be used
510      * after this method returns. Services wishing to use the event after this method returns should
511      * make a copy.
512      * @return If true then the event will be consumed and not delivered to
513      *         applications, otherwise it will be delivered as usual.
514      */
onKeyEvent(KeyEvent event)515     protected boolean onKeyEvent(KeyEvent event) {
516         return false;
517     }
518 
519     /**
520      * Gets the windows on the screen. This method returns only the windows
521      * that a sighted user can interact with, as opposed to all windows.
522      * For example, if there is a modal dialog shown and the user cannot touch
523      * anything behind it, then only the modal window will be reported
524      * (assuming it is the top one). For convenience the returned windows
525      * are ordered in a descending layer order, which is the windows that
526      * are higher in the Z-order are reported first. Since the user can always
527      * interact with the window that has input focus by typing, the focused
528      * window is always returned (even if covered by a modal window).
529      * <p>
530      * <strong>Note:</strong> In order to access the windows your service has
531      * to declare the capability to retrieve window content by setting the
532      * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
533      * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
534      * Also the service has to opt-in to retrieve the interactive windows by
535      * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
536      * flag.
537      * </p>
538      *
539      * @return The windows if there are windows and the service is can retrieve
540      *         them, otherwise an empty list.
541      */
getWindows()542     public List<AccessibilityWindowInfo> getWindows() {
543         return AccessibilityInteractionClient.getInstance().getWindows(mConnectionId);
544     }
545 
546     /**
547      * Gets the root node in the currently active window if this service
548      * can retrieve window content. The active window is the one that the user
549      * is currently touching or the window with input focus, if the user is not
550      * touching any window.
551      * <p>
552      * The currently active window is defined as the window that most recently fired one
553      * of the following events:
554      * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED},
555      * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
556      * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}.
557      * In other words, the last window shown that also has input focus.
558      * </p>
559      * <p>
560      * <strong>Note:</strong> In order to access the root node your service has
561      * to declare the capability to retrieve window content by setting the
562      * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
563      * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
564      * </p>
565      *
566      * @return The root node if this service can retrieve window content.
567      */
getRootInActiveWindow()568     public AccessibilityNodeInfo getRootInActiveWindow() {
569         return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId);
570     }
571 
572     /**
573      * Disables the service. After calling this method, the service will be disabled and settings
574      * will show that it is turned off.
575      */
disableSelf()576     public final void disableSelf() {
577         final IAccessibilityServiceConnection connection =
578                 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
579         if (connection != null) {
580             try {
581                 connection.disableSelf();
582             } catch (RemoteException re) {
583                 throw new RuntimeException(re);
584             }
585         }
586     }
587 
588     /**
589      * Returns the magnification controller, which may be used to query and
590      * modify the state of display magnification.
591      * <p>
592      * <strong>Note:</strong> In order to control magnification, your service
593      * must declare the capability by setting the
594      * {@link android.R.styleable#AccessibilityService_canControlMagnification}
595      * property in its meta-data. For more information, see
596      * {@link #SERVICE_META_DATA}.
597      *
598      * @return the magnification controller
599      */
600     @NonNull
getMagnificationController()601     public final MagnificationController getMagnificationController() {
602         synchronized (mLock) {
603             if (mMagnificationController == null) {
604                 mMagnificationController = new MagnificationController(this, mLock);
605             }
606             return mMagnificationController;
607         }
608     }
609 
610     /**
611      * Get the controller for fingerprint gestures. This feature requires {@link
612      * AccessibilityServiceInfo#CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES}.
613      *
614      *<strong>Note: </strong> The service must be connected before this method is called.
615      *
616      * @return The controller for fingerprint gestures, or {@code null} if gestures are unavailable.
617      */
618     @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT)
getFingerprintGestureController()619     public final @NonNull FingerprintGestureController getFingerprintGestureController() {
620         if (mFingerprintGestureController == null) {
621             mFingerprintGestureController = new FingerprintGestureController(
622                     AccessibilityInteractionClient.getInstance().getConnection(mConnectionId));
623         }
624         return mFingerprintGestureController;
625     }
626 
627     /**
628      * Dispatch a gesture to the touch screen. Any gestures currently in progress, whether from
629      * the user, this service, or another service, will be cancelled.
630      * <p>
631      * The gesture will be dispatched as if it were performed directly on the screen by a user, so
632      * the events may be affected by features such as magnification and explore by touch.
633      * </p>
634      * <p>
635      * <strong>Note:</strong> In order to dispatch gestures, your service
636      * must declare the capability by setting the
637      * {@link android.R.styleable#AccessibilityService_canPerformGestures}
638      * property in its meta-data. For more information, see
639      * {@link #SERVICE_META_DATA}.
640      * </p>
641      *
642      * @param gesture The gesture to dispatch
643      * @param callback The object to call back when the status of the gesture is known. If
644      * {@code null}, no status is reported.
645      * @param handler The handler on which to call back the {@code callback} object. If
646      * {@code null}, the object is called back on the service's main thread.
647      *
648      * @return {@code true} if the gesture is dispatched, {@code false} if not.
649      */
dispatchGesture(@onNull GestureDescription gesture, @Nullable GestureResultCallback callback, @Nullable Handler handler)650     public final boolean dispatchGesture(@NonNull GestureDescription gesture,
651             @Nullable GestureResultCallback callback,
652             @Nullable Handler handler) {
653         final IAccessibilityServiceConnection connection =
654                 AccessibilityInteractionClient.getInstance().getConnection(
655                         mConnectionId);
656         if (connection == null) {
657             return false;
658         }
659         List<GestureDescription.GestureStep> steps =
660                 MotionEventGenerator.getGestureStepsFromGestureDescription(gesture, 100);
661         try {
662             synchronized (mLock) {
663                 mGestureStatusCallbackSequence++;
664                 if (callback != null) {
665                     if (mGestureStatusCallbackInfos == null) {
666                         mGestureStatusCallbackInfos = new SparseArray<>();
667                     }
668                     GestureResultCallbackInfo callbackInfo = new GestureResultCallbackInfo(gesture,
669                             callback, handler);
670                     mGestureStatusCallbackInfos.put(mGestureStatusCallbackSequence, callbackInfo);
671                 }
672                 connection.sendGesture(mGestureStatusCallbackSequence,
673                         new ParceledListSlice<>(steps));
674             }
675         } catch (RemoteException re) {
676             throw new RuntimeException(re);
677         }
678         return true;
679     }
680 
onPerformGestureResult(int sequence, final boolean completedSuccessfully)681     void onPerformGestureResult(int sequence, final boolean completedSuccessfully) {
682         if (mGestureStatusCallbackInfos == null) {
683             return;
684         }
685         GestureResultCallbackInfo callbackInfo;
686         synchronized (mLock) {
687             callbackInfo = mGestureStatusCallbackInfos.get(sequence);
688         }
689         final GestureResultCallbackInfo finalCallbackInfo = callbackInfo;
690         if ((callbackInfo != null) && (callbackInfo.gestureDescription != null)
691                 && (callbackInfo.callback != null)) {
692             if (callbackInfo.handler != null) {
693                 callbackInfo.handler.post(new Runnable() {
694                     @Override
695                     public void run() {
696                         if (completedSuccessfully) {
697                             finalCallbackInfo.callback
698                                     .onCompleted(finalCallbackInfo.gestureDescription);
699                         } else {
700                             finalCallbackInfo.callback
701                                     .onCancelled(finalCallbackInfo.gestureDescription);
702                         }
703                     }
704                 });
705                 return;
706             }
707             if (completedSuccessfully) {
708                 callbackInfo.callback.onCompleted(callbackInfo.gestureDescription);
709             } else {
710                 callbackInfo.callback.onCancelled(callbackInfo.gestureDescription);
711             }
712         }
713     }
714 
onMagnificationChanged(@onNull Region region, float scale, float centerX, float centerY)715     private void onMagnificationChanged(@NonNull Region region, float scale,
716             float centerX, float centerY) {
717         if (mMagnificationController != null) {
718             mMagnificationController.dispatchMagnificationChanged(
719                     region, scale, centerX, centerY);
720         }
721     }
722 
723     /**
724      * Callback for fingerprint gesture handling
725      * @param active If gesture detection is active
726      */
onFingerprintCapturingGesturesChanged(boolean active)727     private void onFingerprintCapturingGesturesChanged(boolean active) {
728         getFingerprintGestureController().onGestureDetectionActiveChanged(active);
729     }
730 
731     /**
732      * Callback for fingerprint gesture handling
733      * @param gesture The identifier for the gesture performed
734      */
onFingerprintGesture(int gesture)735     private void onFingerprintGesture(int gesture) {
736         getFingerprintGestureController().onGesture(gesture);
737     }
738 
739     /**
740      * Used to control and query the state of display magnification.
741      */
742     public static final class MagnificationController {
743         private final AccessibilityService mService;
744 
745         /**
746          * Map of listeners to their handlers. Lazily created when adding the
747          * first magnification listener.
748          */
749         private ArrayMap<OnMagnificationChangedListener, Handler> mListeners;
750         private final Object mLock;
751 
MagnificationController(@onNull AccessibilityService service, @NonNull Object lock)752         MagnificationController(@NonNull AccessibilityService service, @NonNull Object lock) {
753             mService = service;
754             mLock = lock;
755         }
756 
757         /**
758          * Called when the service is connected.
759          */
onServiceConnected()760         void onServiceConnected() {
761             synchronized (mLock) {
762                 if (mListeners != null && !mListeners.isEmpty()) {
763                     setMagnificationCallbackEnabled(true);
764                 }
765             }
766         }
767 
768         /**
769          * Adds the specified change listener to the list of magnification
770          * change listeners. The callback will occur on the service's main
771          * thread.
772          *
773          * @param listener the listener to add, must be non-{@code null}
774          */
addListener(@onNull OnMagnificationChangedListener listener)775         public void addListener(@NonNull OnMagnificationChangedListener listener) {
776             addListener(listener, null);
777         }
778 
779         /**
780          * Adds the specified change listener to the list of magnification
781          * change listeners. The callback will occur on the specified
782          * {@link Handler}'s thread, or on the service's main thread if the
783          * handler is {@code null}.
784          *
785          * @param listener the listener to add, must be non-null
786          * @param handler the handler on which the callback should execute, or
787          *        {@code null} to execute on the service's main thread
788          */
addListener(@onNull OnMagnificationChangedListener listener, @Nullable Handler handler)789         public void addListener(@NonNull OnMagnificationChangedListener listener,
790                 @Nullable Handler handler) {
791             synchronized (mLock) {
792                 if (mListeners == null) {
793                     mListeners = new ArrayMap<>();
794                 }
795 
796                 final boolean shouldEnableCallback = mListeners.isEmpty();
797                 mListeners.put(listener, handler);
798 
799                 if (shouldEnableCallback) {
800                     // This may fail if the service is not connected yet, but if we
801                     // still have listeners when it connects then we can try again.
802                     setMagnificationCallbackEnabled(true);
803                 }
804             }
805         }
806 
807         /**
808          * Removes the specified change listener from the list of magnification change listeners.
809          *
810          * @param listener the listener to remove, must be non-null
811          * @return {@code true} if the listener was removed, {@code false} otherwise
812          */
removeListener(@onNull OnMagnificationChangedListener listener)813         public boolean removeListener(@NonNull OnMagnificationChangedListener listener) {
814             if (mListeners == null) {
815                 return false;
816             }
817 
818             synchronized (mLock) {
819                 final int keyIndex = mListeners.indexOfKey(listener);
820                 final boolean hasKey = keyIndex >= 0;
821                 if (hasKey) {
822                     mListeners.removeAt(keyIndex);
823                 }
824 
825                 if (hasKey && mListeners.isEmpty()) {
826                     // We just removed the last listener, so we don't need
827                     // callbacks from the service anymore.
828                     setMagnificationCallbackEnabled(false);
829                 }
830 
831                 return hasKey;
832             }
833         }
834 
setMagnificationCallbackEnabled(boolean enabled)835         private void setMagnificationCallbackEnabled(boolean enabled) {
836             final IAccessibilityServiceConnection connection =
837                     AccessibilityInteractionClient.getInstance().getConnection(
838                             mService.mConnectionId);
839             if (connection != null) {
840                 try {
841                     connection.setMagnificationCallbackEnabled(enabled);
842                 } catch (RemoteException re) {
843                     throw new RuntimeException(re);
844                 }
845             }
846         }
847 
848         /**
849          * Dispatches magnification changes to any registered listeners. This
850          * should be called on the service's main thread.
851          */
dispatchMagnificationChanged(final @NonNull Region region, final float scale, final float centerX, final float centerY)852         void dispatchMagnificationChanged(final @NonNull Region region, final float scale,
853                 final float centerX, final float centerY) {
854             final ArrayMap<OnMagnificationChangedListener, Handler> entries;
855             synchronized (mLock) {
856                 if (mListeners == null || mListeners.isEmpty()) {
857                     Slog.d(LOG_TAG, "Received magnification changed "
858                             + "callback with no listeners registered!");
859                     setMagnificationCallbackEnabled(false);
860                     return;
861                 }
862 
863                 // Listeners may remove themselves. Perform a shallow copy to avoid concurrent
864                 // modification.
865                 entries = new ArrayMap<>(mListeners);
866             }
867 
868             for (int i = 0, count = entries.size(); i < count; i++) {
869                 final OnMagnificationChangedListener listener = entries.keyAt(i);
870                 final Handler handler = entries.valueAt(i);
871                 if (handler != null) {
872                     handler.post(new Runnable() {
873                         @Override
874                         public void run() {
875                             listener.onMagnificationChanged(MagnificationController.this,
876                                     region, scale, centerX, centerY);
877                         }
878                     });
879                 } else {
880                     // We're already on the main thread, just run the listener.
881                     listener.onMagnificationChanged(this, region, scale, centerX, centerY);
882                 }
883             }
884         }
885 
886         /**
887          * Returns the current magnification scale.
888          * <p>
889          * <strong>Note:</strong> If the service is not yet connected (e.g.
890          * {@link AccessibilityService#onServiceConnected()} has not yet been
891          * called) or the service has been disconnected, this method will
892          * return a default value of {@code 1.0f}.
893          *
894          * @return the current magnification scale
895          */
getScale()896         public float getScale() {
897             final IAccessibilityServiceConnection connection =
898                     AccessibilityInteractionClient.getInstance().getConnection(
899                             mService.mConnectionId);
900             if (connection != null) {
901                 try {
902                     return connection.getMagnificationScale();
903                 } catch (RemoteException re) {
904                     Log.w(LOG_TAG, "Failed to obtain scale", re);
905                     re.rethrowFromSystemServer();
906                 }
907             }
908             return 1.0f;
909         }
910 
911         /**
912          * Returns the unscaled screen-relative X coordinate of the focal
913          * center of the magnified region. This is the point around which
914          * zooming occurs and is guaranteed to lie within the magnified
915          * region.
916          * <p>
917          * <strong>Note:</strong> If the service is not yet connected (e.g.
918          * {@link AccessibilityService#onServiceConnected()} has not yet been
919          * called) or the service has been disconnected, this method will
920          * return a default value of {@code 0.0f}.
921          *
922          * @return the unscaled screen-relative X coordinate of the center of
923          *         the magnified region
924          */
getCenterX()925         public float getCenterX() {
926             final IAccessibilityServiceConnection connection =
927                     AccessibilityInteractionClient.getInstance().getConnection(
928                             mService.mConnectionId);
929             if (connection != null) {
930                 try {
931                     return connection.getMagnificationCenterX();
932                 } catch (RemoteException re) {
933                     Log.w(LOG_TAG, "Failed to obtain center X", re);
934                     re.rethrowFromSystemServer();
935                 }
936             }
937             return 0.0f;
938         }
939 
940         /**
941          * Returns the unscaled screen-relative Y coordinate of the focal
942          * center of the magnified region. This is the point around which
943          * zooming occurs and is guaranteed to lie within the magnified
944          * region.
945          * <p>
946          * <strong>Note:</strong> If the service is not yet connected (e.g.
947          * {@link AccessibilityService#onServiceConnected()} has not yet been
948          * called) or the service has been disconnected, this method will
949          * return a default value of {@code 0.0f}.
950          *
951          * @return the unscaled screen-relative Y coordinate of the center of
952          *         the magnified region
953          */
getCenterY()954         public float getCenterY() {
955             final IAccessibilityServiceConnection connection =
956                     AccessibilityInteractionClient.getInstance().getConnection(
957                             mService.mConnectionId);
958             if (connection != null) {
959                 try {
960                     return connection.getMagnificationCenterY();
961                 } catch (RemoteException re) {
962                     Log.w(LOG_TAG, "Failed to obtain center Y", re);
963                     re.rethrowFromSystemServer();
964                 }
965             }
966             return 0.0f;
967         }
968 
969         /**
970          * Returns the region of the screen currently active for magnification. Changes to
971          * magnification scale and center only affect this portion of the screen. The rest of the
972          * screen, for example input methods, cannot be magnified. This region is relative to the
973          * unscaled screen and is independent of the scale and center point.
974          * <p>
975          * The returned region will be empty if magnification is not active. Magnification is active
976          * if magnification gestures are enabled or if a service is running that can control
977          * magnification.
978          * <p>
979          * <strong>Note:</strong> If the service is not yet connected (e.g.
980          * {@link AccessibilityService#onServiceConnected()} has not yet been
981          * called) or the service has been disconnected, this method will
982          * return an empty region.
983          *
984          * @return the region of the screen currently active for magnification, or an empty region
985          * if magnification is not active.
986          */
987         @NonNull
getMagnificationRegion()988         public Region getMagnificationRegion() {
989             final IAccessibilityServiceConnection connection =
990                     AccessibilityInteractionClient.getInstance().getConnection(
991                             mService.mConnectionId);
992             if (connection != null) {
993                 try {
994                     return connection.getMagnificationRegion();
995                 } catch (RemoteException re) {
996                     Log.w(LOG_TAG, "Failed to obtain magnified region", re);
997                     re.rethrowFromSystemServer();
998                 }
999             }
1000             return Region.obtain();
1001         }
1002 
1003         /**
1004          * Resets magnification scale and center to their default (e.g. no
1005          * magnification) values.
1006          * <p>
1007          * <strong>Note:</strong> If the service is not yet connected (e.g.
1008          * {@link AccessibilityService#onServiceConnected()} has not yet been
1009          * called) or the service has been disconnected, this method will have
1010          * no effect and return {@code false}.
1011          *
1012          * @param animate {@code true} to animate from the current scale and
1013          *                center or {@code false} to reset the scale and center
1014          *                immediately
1015          * @return {@code true} on success, {@code false} on failure
1016          */
reset(boolean animate)1017         public boolean reset(boolean animate) {
1018             final IAccessibilityServiceConnection connection =
1019                     AccessibilityInteractionClient.getInstance().getConnection(
1020                             mService.mConnectionId);
1021             if (connection != null) {
1022                 try {
1023                     return connection.resetMagnification(animate);
1024                 } catch (RemoteException re) {
1025                     Log.w(LOG_TAG, "Failed to reset", re);
1026                     re.rethrowFromSystemServer();
1027                 }
1028             }
1029             return false;
1030         }
1031 
1032         /**
1033          * Sets the magnification scale.
1034          * <p>
1035          * <strong>Note:</strong> If the service is not yet connected (e.g.
1036          * {@link AccessibilityService#onServiceConnected()} has not yet been
1037          * called) or the service has been disconnected, this method will have
1038          * no effect and return {@code false}.
1039          *
1040          * @param scale the magnification scale to set, must be >= 1 and <= 5
1041          * @param animate {@code true} to animate from the current scale or
1042          *                {@code false} to set the scale immediately
1043          * @return {@code true} on success, {@code false} on failure
1044          */
setScale(float scale, boolean animate)1045         public boolean setScale(float scale, boolean animate) {
1046             final IAccessibilityServiceConnection connection =
1047                     AccessibilityInteractionClient.getInstance().getConnection(
1048                             mService.mConnectionId);
1049             if (connection != null) {
1050                 try {
1051                     return connection.setMagnificationScaleAndCenter(
1052                             scale, Float.NaN, Float.NaN, animate);
1053                 } catch (RemoteException re) {
1054                     Log.w(LOG_TAG, "Failed to set scale", re);
1055                     re.rethrowFromSystemServer();
1056                 }
1057             }
1058             return false;
1059         }
1060 
1061         /**
1062          * Sets the center of the magnified viewport.
1063          * <p>
1064          * <strong>Note:</strong> If the service is not yet connected (e.g.
1065          * {@link AccessibilityService#onServiceConnected()} has not yet been
1066          * called) or the service has been disconnected, this method will have
1067          * no effect and return {@code false}.
1068          *
1069          * @param centerX the unscaled screen-relative X coordinate on which to
1070          *                center the viewport
1071          * @param centerY the unscaled screen-relative Y coordinate on which to
1072          *                center the viewport
1073          * @param animate {@code true} to animate from the current viewport
1074          *                center or {@code false} to set the center immediately
1075          * @return {@code true} on success, {@code false} on failure
1076          */
setCenter(float centerX, float centerY, boolean animate)1077         public boolean setCenter(float centerX, float centerY, boolean animate) {
1078             final IAccessibilityServiceConnection connection =
1079                     AccessibilityInteractionClient.getInstance().getConnection(
1080                             mService.mConnectionId);
1081             if (connection != null) {
1082                 try {
1083                     return connection.setMagnificationScaleAndCenter(
1084                             Float.NaN, centerX, centerY, animate);
1085                 } catch (RemoteException re) {
1086                     Log.w(LOG_TAG, "Failed to set center", re);
1087                     re.rethrowFromSystemServer();
1088                 }
1089             }
1090             return false;
1091         }
1092 
1093         /**
1094          * Listener for changes in the state of magnification.
1095          */
1096         public interface OnMagnificationChangedListener {
1097             /**
1098              * Called when the magnified region, scale, or center changes.
1099              *
1100              * @param controller the magnification controller
1101              * @param region the magnification region
1102              * @param scale the new scale
1103              * @param centerX the new X coordinate, in unscaled coordinates, around which
1104              * magnification is focused
1105              * @param centerY the new Y coordinate, in unscaled coordinates, around which
1106              * magnification is focused
1107              */
onMagnificationChanged(@onNull MagnificationController controller, @NonNull Region region, float scale, float centerX, float centerY)1108             void onMagnificationChanged(@NonNull MagnificationController controller,
1109                     @NonNull Region region, float scale, float centerX, float centerY);
1110         }
1111     }
1112 
1113     /**
1114      * Returns the soft keyboard controller, which may be used to query and modify the soft keyboard
1115      * show mode.
1116      *
1117      * @return the soft keyboard controller
1118      */
1119     @NonNull
getSoftKeyboardController()1120     public final SoftKeyboardController getSoftKeyboardController() {
1121         synchronized (mLock) {
1122             if (mSoftKeyboardController == null) {
1123                 mSoftKeyboardController = new SoftKeyboardController(this, mLock);
1124             }
1125             return mSoftKeyboardController;
1126         }
1127     }
1128 
onSoftKeyboardShowModeChanged(int showMode)1129     private void onSoftKeyboardShowModeChanged(int showMode) {
1130         if (mSoftKeyboardController != null) {
1131             mSoftKeyboardController.dispatchSoftKeyboardShowModeChanged(showMode);
1132         }
1133     }
1134 
1135     /**
1136      * Used to control and query the soft keyboard show mode.
1137      */
1138     public static final class SoftKeyboardController {
1139         private final AccessibilityService mService;
1140 
1141         /**
1142          * Map of listeners to their handlers. Lazily created when adding the first
1143          * soft keyboard change listener.
1144          */
1145         private ArrayMap<OnShowModeChangedListener, Handler> mListeners;
1146         private final Object mLock;
1147 
SoftKeyboardController(@onNull AccessibilityService service, @NonNull Object lock)1148         SoftKeyboardController(@NonNull AccessibilityService service, @NonNull Object lock) {
1149             mService = service;
1150             mLock = lock;
1151         }
1152 
1153         /**
1154          * Called when the service is connected.
1155          */
onServiceConnected()1156         void onServiceConnected() {
1157             synchronized(mLock) {
1158                 if (mListeners != null && !mListeners.isEmpty()) {
1159                     setSoftKeyboardCallbackEnabled(true);
1160                 }
1161             }
1162         }
1163 
1164         /**
1165          * Adds the specified change listener to the list of show mode change listeners. The
1166          * callback will occur on the service's main thread. Listener is not called on registration.
1167          */
addOnShowModeChangedListener(@onNull OnShowModeChangedListener listener)1168         public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) {
1169             addOnShowModeChangedListener(listener, null);
1170         }
1171 
1172         /**
1173          * Adds the specified change listener to the list of soft keyboard show mode change
1174          * listeners. The callback will occur on the specified {@link Handler}'s thread, or on the
1175          * services's main thread if the handler is {@code null}.
1176          *
1177          * @param listener the listener to add, must be non-null
1178          * @param handler the handler on which to callback should execute, or {@code null} to
1179          *        execute on the service's main thread
1180          */
addOnShowModeChangedListener(@onNull OnShowModeChangedListener listener, @Nullable Handler handler)1181         public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener,
1182                 @Nullable Handler handler) {
1183             synchronized (mLock) {
1184                 if (mListeners == null) {
1185                     mListeners = new ArrayMap<>();
1186                 }
1187 
1188                 final boolean shouldEnableCallback = mListeners.isEmpty();
1189                 mListeners.put(listener, handler);
1190 
1191                 if (shouldEnableCallback) {
1192                     // This may fail if the service is not connected yet, but if we still have
1193                     // listeners when it connects, we can try again.
1194                     setSoftKeyboardCallbackEnabled(true);
1195                 }
1196             }
1197         }
1198 
1199         /**
1200          * Removes the specified change listener from the list of keyboard show mode change
1201          * listeners.
1202          *
1203          * @param listener the listener to remove, must be non-null
1204          * @return {@code true} if the listener was removed, {@code false} otherwise
1205          */
removeOnShowModeChangedListener(@onNull OnShowModeChangedListener listener)1206         public boolean removeOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) {
1207             if (mListeners == null) {
1208                 return false;
1209             }
1210 
1211             synchronized (mLock) {
1212                 final int keyIndex = mListeners.indexOfKey(listener);
1213                 final boolean hasKey = keyIndex >= 0;
1214                 if (hasKey) {
1215                     mListeners.removeAt(keyIndex);
1216                 }
1217 
1218                 if (hasKey && mListeners.isEmpty()) {
1219                     // We just removed the last listener, so we don't need callbacks from the
1220                     // service anymore.
1221                     setSoftKeyboardCallbackEnabled(false);
1222                 }
1223 
1224                 return hasKey;
1225             }
1226         }
1227 
setSoftKeyboardCallbackEnabled(boolean enabled)1228         private void setSoftKeyboardCallbackEnabled(boolean enabled) {
1229             final IAccessibilityServiceConnection connection =
1230                     AccessibilityInteractionClient.getInstance().getConnection(
1231                             mService.mConnectionId);
1232             if (connection != null) {
1233                 try {
1234                     connection.setSoftKeyboardCallbackEnabled(enabled);
1235                 } catch (RemoteException re) {
1236                     throw new RuntimeException(re);
1237                 }
1238             }
1239         }
1240 
1241         /**
1242          * Dispatches the soft keyboard show mode change to any registered listeners. This should
1243          * be called on the service's main thread.
1244          */
dispatchSoftKeyboardShowModeChanged(final int showMode)1245         void dispatchSoftKeyboardShowModeChanged(final int showMode) {
1246             final ArrayMap<OnShowModeChangedListener, Handler> entries;
1247             synchronized (mLock) {
1248                 if (mListeners == null || mListeners.isEmpty()) {
1249                     Slog.w(LOG_TAG, "Received soft keyboard show mode changed callback"
1250                             + " with no listeners registered!");
1251                     setSoftKeyboardCallbackEnabled(false);
1252                     return;
1253                 }
1254 
1255                 // Listeners may remove themselves. Perform a shallow copy to avoid concurrent
1256                 // modification.
1257                 entries = new ArrayMap<>(mListeners);
1258             }
1259 
1260             for (int i = 0, count = entries.size(); i < count; i++) {
1261                 final OnShowModeChangedListener listener = entries.keyAt(i);
1262                 final Handler handler = entries.valueAt(i);
1263                 if (handler != null) {
1264                     handler.post(new Runnable() {
1265                         @Override
1266                         public void run() {
1267                             listener.onShowModeChanged(SoftKeyboardController.this, showMode);
1268                         }
1269                     });
1270                 } else {
1271                     // We're already on the main thread, just run the listener.
1272                     listener.onShowModeChanged(this, showMode);
1273                 }
1274             }
1275         }
1276 
1277         /**
1278          * Returns the show mode of the soft keyboard. The default show mode is
1279          * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
1280          * focused. An AccessibilityService can also request the show mode
1281          * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
1282          *
1283          * @return the current soft keyboard show mode
1284          */
1285         @SoftKeyboardShowMode
getShowMode()1286         public int getShowMode() {
1287            try {
1288                return Settings.Secure.getInt(mService.getContentResolver(),
1289                        Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
1290            } catch (Settings.SettingNotFoundException e) {
1291                Log.v(LOG_TAG, "Failed to obtain the soft keyboard mode", e);
1292                // The settings hasn't been changed yet, so it's value is null. Return the default.
1293                return 0;
1294            }
1295         }
1296 
1297         /**
1298          * Sets the soft keyboard show mode. The default show mode is
1299          * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
1300          * focused. An AccessibilityService can also request the show mode
1301          * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. The
1302          * The lastto this method will be honored, regardless of any previous calls (including those
1303          * made by other AccessibilityServices).
1304          * <p>
1305          * <strong>Note:</strong> If the service is not yet connected (e.g.
1306          * {@link AccessibilityService#onServiceConnected()} has not yet been called) or the
1307          * service has been disconnected, this method will have no effect and return {@code false}.
1308          *
1309          * @param showMode the new show mode for the soft keyboard
1310          * @return {@code true} on success
1311          */
setShowMode(@oftKeyboardShowMode int showMode)1312         public boolean setShowMode(@SoftKeyboardShowMode int showMode) {
1313            final IAccessibilityServiceConnection connection =
1314                    AccessibilityInteractionClient.getInstance().getConnection(
1315                            mService.mConnectionId);
1316            if (connection != null) {
1317                try {
1318                    return connection.setSoftKeyboardShowMode(showMode);
1319                } catch (RemoteException re) {
1320                    Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re);
1321                    re.rethrowFromSystemServer();
1322                }
1323            }
1324            return false;
1325         }
1326 
1327         /**
1328          * Listener for changes in the soft keyboard show mode.
1329          */
1330         public interface OnShowModeChangedListener {
1331            /**
1332             * Called when the soft keyboard behavior changes. The default show mode is
1333             * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
1334             * focused. An AccessibilityService can also request the show mode
1335             * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
1336             *
1337             * @param controller the soft keyboard controller
1338             * @param showMode the current soft keyboard show mode
1339             */
onShowModeChanged(@onNull SoftKeyboardController controller, @SoftKeyboardShowMode int showMode)1340             void onShowModeChanged(@NonNull SoftKeyboardController controller,
1341                     @SoftKeyboardShowMode int showMode);
1342         }
1343     }
1344 
1345     /**
1346      * Returns the controller for the accessibility button within the system's navigation area.
1347      * This instance may be used to query the accessibility button's state and register listeners
1348      * for interactions with and state changes for the accessibility button when
1349      * {@link AccessibilityServiceInfo#FLAG_REQUEST_ACCESSIBILITY_BUTTON} is set.
1350      * <p>
1351      * <strong>Note:</strong> Not all devices are capable of displaying the accessibility button
1352      * within a navigation area, and as such, use of this class should be considered only as an
1353      * optional feature or shortcut on supported device implementations.
1354      * </p>
1355      *
1356      * @return the accessibility button controller for this {@link AccessibilityService}
1357      */
1358     @NonNull
getAccessibilityButtonController()1359     public final AccessibilityButtonController getAccessibilityButtonController() {
1360         synchronized (mLock) {
1361             if (mAccessibilityButtonController == null) {
1362                 mAccessibilityButtonController = new AccessibilityButtonController(
1363                         AccessibilityInteractionClient.getInstance().getConnection(mConnectionId));
1364             }
1365             return mAccessibilityButtonController;
1366         }
1367     }
1368 
onAccessibilityButtonClicked()1369     private void onAccessibilityButtonClicked() {
1370         getAccessibilityButtonController().dispatchAccessibilityButtonClicked();
1371     }
1372 
onAccessibilityButtonAvailabilityChanged(boolean available)1373     private void onAccessibilityButtonAvailabilityChanged(boolean available) {
1374         getAccessibilityButtonController().dispatchAccessibilityButtonAvailabilityChanged(
1375                 available);
1376     }
1377 
1378     /**
1379      * Performs a global action. Such an action can be performed
1380      * at any moment regardless of the current application or user
1381      * location in that application. For example going back, going
1382      * home, opening recents, etc.
1383      *
1384      * @param action The action to perform.
1385      * @return Whether the action was successfully performed.
1386      *
1387      * @see #GLOBAL_ACTION_BACK
1388      * @see #GLOBAL_ACTION_HOME
1389      * @see #GLOBAL_ACTION_NOTIFICATIONS
1390      * @see #GLOBAL_ACTION_RECENTS
1391      */
performGlobalAction(int action)1392     public final boolean performGlobalAction(int action) {
1393         IAccessibilityServiceConnection connection =
1394             AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1395         if (connection != null) {
1396             try {
1397                 return connection.performGlobalAction(action);
1398             } catch (RemoteException re) {
1399                 Log.w(LOG_TAG, "Error while calling performGlobalAction", re);
1400                 re.rethrowFromSystemServer();
1401             }
1402         }
1403         return false;
1404     }
1405 
1406     /**
1407      * Find the view that has the specified focus type. The search is performed
1408      * across all windows.
1409      * <p>
1410      * <strong>Note:</strong> In order to access the windows your service has
1411      * to declare the capability to retrieve window content by setting the
1412      * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
1413      * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
1414      * Also the service has to opt-in to retrieve the interactive windows by
1415      * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
1416      * flag. Otherwise, the search will be performed only in the active window.
1417      * </p>
1418      *
1419      * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or
1420      *         {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}.
1421      * @return The node info of the focused view or null.
1422      *
1423      * @see AccessibilityNodeInfo#FOCUS_INPUT
1424      * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY
1425      */
findFocus(int focus)1426     public AccessibilityNodeInfo findFocus(int focus) {
1427         return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId,
1428                 AccessibilityWindowInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus);
1429     }
1430 
1431     /**
1432      * Gets the an {@link AccessibilityServiceInfo} describing this
1433      * {@link AccessibilityService}. This method is useful if one wants
1434      * to change some of the dynamically configurable properties at
1435      * runtime.
1436      *
1437      * @return The accessibility service info.
1438      *
1439      * @see AccessibilityServiceInfo
1440      */
getServiceInfo()1441     public final AccessibilityServiceInfo getServiceInfo() {
1442         IAccessibilityServiceConnection connection =
1443             AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1444         if (connection != null) {
1445             try {
1446                 return connection.getServiceInfo();
1447             } catch (RemoteException re) {
1448                 Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re);
1449                 re.rethrowFromSystemServer();
1450             }
1451         }
1452         return null;
1453     }
1454 
1455     /**
1456      * Sets the {@link AccessibilityServiceInfo} that describes this service.
1457      * <p>
1458      * Note: You can call this method any time but the info will be picked up after
1459      *       the system has bound to this service and when this method is called thereafter.
1460      *
1461      * @param info The info.
1462      */
setServiceInfo(AccessibilityServiceInfo info)1463     public final void setServiceInfo(AccessibilityServiceInfo info) {
1464         mInfo = info;
1465         sendServiceInfo();
1466     }
1467 
1468     /**
1469      * Sets the {@link AccessibilityServiceInfo} for this service if the latter is
1470      * properly set and there is an {@link IAccessibilityServiceConnection} to the
1471      * AccessibilityManagerService.
1472      */
sendServiceInfo()1473     private void sendServiceInfo() {
1474         IAccessibilityServiceConnection connection =
1475             AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1476         if (mInfo != null && connection != null) {
1477             try {
1478                 connection.setServiceInfo(mInfo);
1479                 mInfo = null;
1480                 AccessibilityInteractionClient.getInstance().clearCache();
1481             } catch (RemoteException re) {
1482                 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
1483                 re.rethrowFromSystemServer();
1484             }
1485         }
1486     }
1487 
1488     @Override
getSystemService(@erviceName @onNull String name)1489     public Object getSystemService(@ServiceName @NonNull String name) {
1490         if (getBaseContext() == null) {
1491             throw new IllegalStateException(
1492                     "System services not available to Activities before onCreate()");
1493         }
1494 
1495         // Guarantee that we always return the same window manager instance.
1496         if (WINDOW_SERVICE.equals(name)) {
1497             if (mWindowManager == null) {
1498                 mWindowManager = (WindowManager) getBaseContext().getSystemService(name);
1499             }
1500             return mWindowManager;
1501         }
1502         return super.getSystemService(name);
1503     }
1504 
1505     /**
1506      * Implement to return the implementation of the internal accessibility
1507      * service interface.
1508      */
1509     @Override
onBind(Intent intent)1510     public final IBinder onBind(Intent intent) {
1511         return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {
1512             @Override
1513             public void onServiceConnected() {
1514                 AccessibilityService.this.dispatchServiceConnected();
1515             }
1516 
1517             @Override
1518             public void onInterrupt() {
1519                 AccessibilityService.this.onInterrupt();
1520             }
1521 
1522             @Override
1523             public void onAccessibilityEvent(AccessibilityEvent event) {
1524                 AccessibilityService.this.onAccessibilityEvent(event);
1525             }
1526 
1527             @Override
1528             public void init(int connectionId, IBinder windowToken) {
1529                 mConnectionId = connectionId;
1530                 mWindowToken = windowToken;
1531 
1532                 // The client may have already obtained the window manager, so
1533                 // update the default token on whatever manager we gave them.
1534                 final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE);
1535                 wm.setDefaultToken(windowToken);
1536             }
1537 
1538             @Override
1539             public boolean onGesture(int gestureId) {
1540                 return AccessibilityService.this.onGesture(gestureId);
1541             }
1542 
1543             @Override
1544             public boolean onKeyEvent(KeyEvent event) {
1545                 return AccessibilityService.this.onKeyEvent(event);
1546             }
1547 
1548             @Override
1549             public void onMagnificationChanged(@NonNull Region region,
1550                     float scale, float centerX, float centerY) {
1551                 AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY);
1552             }
1553 
1554             @Override
1555             public void onSoftKeyboardShowModeChanged(int showMode) {
1556                 AccessibilityService.this.onSoftKeyboardShowModeChanged(showMode);
1557             }
1558 
1559             @Override
1560             public void onPerformGestureResult(int sequence, boolean completedSuccessfully) {
1561                 AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully);
1562             }
1563 
1564             @Override
1565             public void onFingerprintCapturingGesturesChanged(boolean active) {
1566                 AccessibilityService.this.onFingerprintCapturingGesturesChanged(active);
1567             }
1568 
1569             @Override
1570             public void onFingerprintGesture(int gesture) {
1571                 AccessibilityService.this.onFingerprintGesture(gesture);
1572             }
1573 
1574             @Override
1575             public void onAccessibilityButtonClicked() {
1576                 AccessibilityService.this.onAccessibilityButtonClicked();
1577             }
1578 
1579             @Override
1580             public void onAccessibilityButtonAvailabilityChanged(boolean available) {
1581                 AccessibilityService.this.onAccessibilityButtonAvailabilityChanged(available);
1582             }
1583         });
1584     }
1585 
1586     /**
1587      * Implements the internal {@link IAccessibilityServiceClient} interface to convert
1588      * incoming calls to it back to calls on an {@link AccessibilityService}.
1589      *
1590      * @hide
1591      */
1592     public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
1593             implements HandlerCaller.Callback {
1594         private static final int DO_INIT = 1;
1595         private static final int DO_ON_INTERRUPT = 2;
1596         private static final int DO_ON_ACCESSIBILITY_EVENT = 3;
1597         private static final int DO_ON_GESTURE = 4;
1598         private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5;
1599         private static final int DO_ON_KEY_EVENT = 6;
1600         private static final int DO_ON_MAGNIFICATION_CHANGED = 7;
1601         private static final int DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED = 8;
1602         private static final int DO_GESTURE_COMPLETE = 9;
1603         private static final int DO_ON_FINGERPRINT_ACTIVE_CHANGED = 10;
1604         private static final int DO_ON_FINGERPRINT_GESTURE = 11;
1605         private static final int DO_ACCESSIBILITY_BUTTON_CLICKED = 12;
1606         private static final int DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 13;
1607 
1608         private final HandlerCaller mCaller;
1609 
1610         private final Callbacks mCallback;
1611 
1612         private int mConnectionId = AccessibilityInteractionClient.NO_ID;
1613 
1614         public IAccessibilityServiceClientWrapper(Context context, Looper looper,
1615                 Callbacks callback) {
1616             mCallback = callback;
1617             mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
1618         }
1619 
1620         public void init(IAccessibilityServiceConnection connection, int connectionId,
1621                 IBinder windowToken) {
1622             Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId,
1623                     connection, windowToken);
1624             mCaller.sendMessage(message);
1625         }
1626 
1627         public void onInterrupt() {
1628             Message message = mCaller.obtainMessage(DO_ON_INTERRUPT);
1629             mCaller.sendMessage(message);
1630         }
1631 
1632         public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) {
1633             Message message = mCaller.obtainMessageBO(
1634                     DO_ON_ACCESSIBILITY_EVENT, serviceWantsEvent, event);
1635             mCaller.sendMessage(message);
1636         }
1637 
1638         public void onGesture(int gestureId) {
1639             Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId);
1640             mCaller.sendMessage(message);
1641         }
1642 
1643         public void clearAccessibilityCache() {
1644             Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE);
1645             mCaller.sendMessage(message);
1646         }
1647 
1648         @Override
1649         public void onKeyEvent(KeyEvent event, int sequence) {
1650             Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event);
1651             mCaller.sendMessage(message);
1652         }
1653 
1654         public void onMagnificationChanged(@NonNull Region region,
1655                 float scale, float centerX, float centerY) {
1656             final SomeArgs args = SomeArgs.obtain();
1657             args.arg1 = region;
1658             args.arg2 = scale;
1659             args.arg3 = centerX;
1660             args.arg4 = centerY;
1661 
1662             final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args);
1663             mCaller.sendMessage(message);
1664         }
1665 
1666         public void onSoftKeyboardShowModeChanged(int showMode) {
1667           final Message message =
1668                   mCaller.obtainMessageI(DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED, showMode);
1669           mCaller.sendMessage(message);
1670         }
1671 
1672         public void onPerformGestureResult(int sequence, boolean successfully) {
1673             Message message = mCaller.obtainMessageII(DO_GESTURE_COMPLETE, sequence,
1674                     successfully ? 1 : 0);
1675             mCaller.sendMessage(message);
1676         }
1677 
1678         public void onFingerprintCapturingGesturesChanged(boolean active) {
1679             mCaller.sendMessage(mCaller.obtainMessageI(
1680                     DO_ON_FINGERPRINT_ACTIVE_CHANGED, active ? 1 : 0));
1681         }
1682 
1683         public void onFingerprintGesture(int gesture) {
1684             mCaller.sendMessage(mCaller.obtainMessageI(DO_ON_FINGERPRINT_GESTURE, gesture));
1685         }
1686 
1687         public void onAccessibilityButtonClicked() {
1688             final Message message = mCaller.obtainMessage(DO_ACCESSIBILITY_BUTTON_CLICKED);
1689             mCaller.sendMessage(message);
1690         }
1691 
1692         public void onAccessibilityButtonAvailabilityChanged(boolean available) {
1693             final Message message = mCaller.obtainMessageI(
1694                     DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED, (available ? 1 : 0));
1695             mCaller.sendMessage(message);
1696         }
1697 
1698         @Override
1699         public void executeMessage(Message message) {
1700             switch (message.what) {
1701                 case DO_ON_ACCESSIBILITY_EVENT: {
1702                     AccessibilityEvent event = (AccessibilityEvent) message.obj;
1703                     boolean serviceWantsEvent = message.arg1 != 0;
1704                     if (event != null) {
1705                         // Send the event to AccessibilityCache via AccessibilityInteractionClient
1706                         AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);
1707                         if (serviceWantsEvent
1708                                 && (mConnectionId != AccessibilityInteractionClient.NO_ID)) {
1709                             // Send the event to AccessibilityService
1710                             mCallback.onAccessibilityEvent(event);
1711                         }
1712                         // Make sure the event is recycled.
1713                         try {
1714                             event.recycle();
1715                         } catch (IllegalStateException ise) {
1716                             /* ignore - best effort */
1717                         }
1718                     }
1719                 } return;
1720 
1721                 case DO_ON_INTERRUPT: {
1722                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1723                         mCallback.onInterrupt();
1724                     }
1725                 } return;
1726 
1727                 case DO_INIT: {
1728                     mConnectionId = message.arg1;
1729                     SomeArgs args = (SomeArgs) message.obj;
1730                     IAccessibilityServiceConnection connection =
1731                             (IAccessibilityServiceConnection) args.arg1;
1732                     IBinder windowToken = (IBinder) args.arg2;
1733                     args.recycle();
1734                     if (connection != null) {
1735                         AccessibilityInteractionClient.getInstance().addConnection(mConnectionId,
1736                                 connection);
1737                         mCallback.init(mConnectionId, windowToken);
1738                         mCallback.onServiceConnected();
1739                     } else {
1740                         AccessibilityInteractionClient.getInstance().removeConnection(
1741                                 mConnectionId);
1742                         mConnectionId = AccessibilityInteractionClient.NO_ID;
1743                         AccessibilityInteractionClient.getInstance().clearCache();
1744                         mCallback.init(AccessibilityInteractionClient.NO_ID, null);
1745                     }
1746                 } return;
1747 
1748                 case DO_ON_GESTURE: {
1749                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1750                         final int gestureId = message.arg1;
1751                         mCallback.onGesture(gestureId);
1752                     }
1753                 } return;
1754 
1755                 case DO_CLEAR_ACCESSIBILITY_CACHE: {
1756                     AccessibilityInteractionClient.getInstance().clearCache();
1757                 } return;
1758 
1759                 case DO_ON_KEY_EVENT: {
1760                     KeyEvent event = (KeyEvent) message.obj;
1761                     try {
1762                         IAccessibilityServiceConnection connection = AccessibilityInteractionClient
1763                                 .getInstance().getConnection(mConnectionId);
1764                         if (connection != null) {
1765                             final boolean result = mCallback.onKeyEvent(event);
1766                             final int sequence = message.arg1;
1767                             try {
1768                                 connection.setOnKeyEventResult(result, sequence);
1769                             } catch (RemoteException re) {
1770                                 /* ignore */
1771                             }
1772                         }
1773                     } finally {
1774                         // Make sure the event is recycled.
1775                         try {
1776                             event.recycle();
1777                         } catch (IllegalStateException ise) {
1778                             /* ignore - best effort */
1779                         }
1780                     }
1781                 } return;
1782 
1783                 case DO_ON_MAGNIFICATION_CHANGED: {
1784                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1785                         final SomeArgs args = (SomeArgs) message.obj;
1786                         final Region region = (Region) args.arg1;
1787                         final float scale = (float) args.arg2;
1788                         final float centerX = (float) args.arg3;
1789                         final float centerY = (float) args.arg4;
1790                         mCallback.onMagnificationChanged(region, scale, centerX, centerY);
1791                     }
1792                 } return;
1793 
1794                 case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: {
1795                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1796                         final int showMode = (int) message.arg1;
1797                         mCallback.onSoftKeyboardShowModeChanged(showMode);
1798                     }
1799                 } return;
1800 
1801                 case DO_GESTURE_COMPLETE: {
1802                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1803                         final boolean successfully = message.arg2 == 1;
1804                         mCallback.onPerformGestureResult(message.arg1, successfully);
1805                     }
1806                 } return;
1807                 case DO_ON_FINGERPRINT_ACTIVE_CHANGED: {
1808                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1809                         mCallback.onFingerprintCapturingGesturesChanged(message.arg1 == 1);
1810                     }
1811                 } return;
1812                 case DO_ON_FINGERPRINT_GESTURE: {
1813                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1814                         mCallback.onFingerprintGesture(message.arg1);
1815                     }
1816                 } return;
1817 
1818                 case (DO_ACCESSIBILITY_BUTTON_CLICKED): {
1819                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1820                         mCallback.onAccessibilityButtonClicked();
1821                     }
1822                 } return;
1823 
1824                 case (DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED): {
1825                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1826                         final boolean available = (message.arg1 != 0);
1827                         mCallback.onAccessibilityButtonAvailabilityChanged(available);
1828                     }
1829                 } return;
1830 
1831                 default :
1832                     Log.w(LOG_TAG, "Unknown message type " + message.what);
1833             }
1834         }
1835     }
1836 
1837     /**
1838      * Class used to report status of dispatched gestures
1839      */
1840     public static abstract class GestureResultCallback {
1841         /** Called when the gesture has completed successfully
1842          *
1843          * @param gestureDescription The description of the gesture that completed.
1844          */
1845         public void onCompleted(GestureDescription gestureDescription) {
1846         }
1847 
1848         /** Called when the gesture was cancelled
1849          *
1850          * @param gestureDescription The description of the gesture that was cancelled.
1851          */
1852         public void onCancelled(GestureDescription gestureDescription) {
1853         }
1854     }
1855 
1856     /* Object to keep track of gesture result callbacks */
1857     private static class GestureResultCallbackInfo {
1858         GestureDescription gestureDescription;
1859         GestureResultCallback callback;
1860         Handler handler;
1861 
1862         GestureResultCallbackInfo(GestureDescription gestureDescription,
1863                 GestureResultCallback callback, Handler handler) {
1864             this.gestureDescription = gestureDescription;
1865             this.callback = callback;
1866             this.handler = handler;
1867         }
1868     }
1869 }
1870