• 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     /**
362      * Action to lock the screen
363      */
364     public static final int GLOBAL_ACTION_LOCK_SCREEN = 8;
365 
366     /**
367      * Action to take a screenshot
368      */
369     public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9;
370 
371     private static final String LOG_TAG = "AccessibilityService";
372 
373     /**
374      * Interface used by IAccessibilityServiceWrapper to call the service from its main thread.
375      * @hide
376      */
377     public interface Callbacks {
onAccessibilityEvent(AccessibilityEvent event)378         void onAccessibilityEvent(AccessibilityEvent event);
onInterrupt()379         void onInterrupt();
onServiceConnected()380         void onServiceConnected();
init(int connectionId, IBinder windowToken)381         void init(int connectionId, IBinder windowToken);
onGesture(int gestureId)382         boolean onGesture(int gestureId);
onKeyEvent(KeyEvent event)383         boolean onKeyEvent(KeyEvent event);
onMagnificationChanged(@onNull Region region, float scale, float centerX, float centerY)384         void onMagnificationChanged(@NonNull Region region,
385                 float scale, float centerX, float centerY);
onSoftKeyboardShowModeChanged(int showMode)386         void onSoftKeyboardShowModeChanged(int showMode);
onPerformGestureResult(int sequence, boolean completedSuccessfully)387         void onPerformGestureResult(int sequence, boolean completedSuccessfully);
onFingerprintCapturingGesturesChanged(boolean active)388         void onFingerprintCapturingGesturesChanged(boolean active);
onFingerprintGesture(int gesture)389         void onFingerprintGesture(int gesture);
onAccessibilityButtonClicked()390         void onAccessibilityButtonClicked();
onAccessibilityButtonAvailabilityChanged(boolean available)391         void onAccessibilityButtonAvailabilityChanged(boolean available);
392     }
393 
394     /**
395      * Annotations for Soft Keyboard show modes so tools can catch invalid show modes.
396      * @hide
397      */
398     @Retention(RetentionPolicy.SOURCE)
399     @IntDef(prefix = { "SHOW_MODE_" }, value = {
400             SHOW_MODE_AUTO,
401             SHOW_MODE_HIDDEN
402     })
403     public @interface SoftKeyboardShowMode {}
404 
405     public static final int SHOW_MODE_AUTO = 0;
406     public static final int SHOW_MODE_HIDDEN = 1;
407 
408     private int mConnectionId = AccessibilityInteractionClient.NO_ID;
409 
410     private AccessibilityServiceInfo mInfo;
411 
412     private IBinder mWindowToken;
413 
414     private WindowManager mWindowManager;
415 
416     private MagnificationController mMagnificationController;
417     private SoftKeyboardController mSoftKeyboardController;
418     private AccessibilityButtonController mAccessibilityButtonController;
419 
420     private int mGestureStatusCallbackSequence;
421 
422     private SparseArray<GestureResultCallbackInfo> mGestureStatusCallbackInfos;
423 
424     private final Object mLock = new Object();
425 
426     private FingerprintGestureController mFingerprintGestureController;
427 
428     /**
429      * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
430      *
431      * @param event The new event. This event is owned by the caller and cannot be used after
432      * this method returns. Services wishing to use the event after this method returns should
433      * make a copy.
434      */
onAccessibilityEvent(AccessibilityEvent event)435     public abstract void onAccessibilityEvent(AccessibilityEvent event);
436 
437     /**
438      * Callback for interrupting the accessibility feedback.
439      */
onInterrupt()440     public abstract void onInterrupt();
441 
442     /**
443      * Dispatches service connection to internal components first, then the
444      * client code.
445      */
dispatchServiceConnected()446     private void dispatchServiceConnected() {
447         if (mMagnificationController != null) {
448             mMagnificationController.onServiceConnected();
449         }
450         if (mSoftKeyboardController != null) {
451             mSoftKeyboardController.onServiceConnected();
452         }
453 
454         // The client gets to handle service connection last, after we've set
455         // up any state upon which their code may rely.
456         onServiceConnected();
457     }
458 
459     /**
460      * This method is a part of the {@link AccessibilityService} lifecycle and is
461      * called after the system has successfully bound to the service. If is
462      * convenient to use this method for setting the {@link AccessibilityServiceInfo}.
463      *
464      * @see AccessibilityServiceInfo
465      * @see #setServiceInfo(AccessibilityServiceInfo)
466      */
onServiceConnected()467     protected void onServiceConnected() {
468 
469     }
470 
471     /**
472      * Called by the system when the user performs a specific gesture on the
473      * touch screen.
474      *
475      * <strong>Note:</strong> To receive gestures an accessibility service must
476      * request that the device is in touch exploration mode by setting the
477      * {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE}
478      * flag.
479      *
480      * @param gestureId The unique id of the performed gesture.
481      *
482      * @return Whether the gesture was handled.
483      *
484      * @see #GESTURE_SWIPE_UP
485      * @see #GESTURE_SWIPE_UP_AND_LEFT
486      * @see #GESTURE_SWIPE_UP_AND_DOWN
487      * @see #GESTURE_SWIPE_UP_AND_RIGHT
488      * @see #GESTURE_SWIPE_DOWN
489      * @see #GESTURE_SWIPE_DOWN_AND_LEFT
490      * @see #GESTURE_SWIPE_DOWN_AND_UP
491      * @see #GESTURE_SWIPE_DOWN_AND_RIGHT
492      * @see #GESTURE_SWIPE_LEFT
493      * @see #GESTURE_SWIPE_LEFT_AND_UP
494      * @see #GESTURE_SWIPE_LEFT_AND_RIGHT
495      * @see #GESTURE_SWIPE_LEFT_AND_DOWN
496      * @see #GESTURE_SWIPE_RIGHT
497      * @see #GESTURE_SWIPE_RIGHT_AND_UP
498      * @see #GESTURE_SWIPE_RIGHT_AND_LEFT
499      * @see #GESTURE_SWIPE_RIGHT_AND_DOWN
500      */
onGesture(int gestureId)501     protected boolean onGesture(int gestureId) {
502         return false;
503     }
504 
505     /**
506      * Callback that allows an accessibility service to observe the key events
507      * before they are passed to the rest of the system. This means that the events
508      * are first delivered here before they are passed to the device policy, the
509      * input method, or applications.
510      * <p>
511      * <strong>Note:</strong> It is important that key events are handled in such
512      * a way that the event stream that would be passed to the rest of the system
513      * is well-formed. For example, handling the down event but not the up event
514      * and vice versa would generate an inconsistent event stream.
515      * </p>
516      * <p>
517      * <strong>Note:</strong> The key events delivered in this method are copies
518      * and modifying them will have no effect on the events that will be passed
519      * to the system. This method is intended to perform purely filtering
520      * functionality.
521      * <p>
522      *
523      * @param event The event to be processed. This event is owned by the caller and cannot be used
524      * after this method returns. Services wishing to use the event after this method returns should
525      * make a copy.
526      * @return If true then the event will be consumed and not delivered to
527      *         applications, otherwise it will be delivered as usual.
528      */
onKeyEvent(KeyEvent event)529     protected boolean onKeyEvent(KeyEvent event) {
530         return false;
531     }
532 
533     /**
534      * Gets the windows on the screen. This method returns only the windows
535      * that a sighted user can interact with, as opposed to all windows.
536      * For example, if there is a modal dialog shown and the user cannot touch
537      * anything behind it, then only the modal window will be reported
538      * (assuming it is the top one). For convenience the returned windows
539      * are ordered in a descending layer order, which is the windows that
540      * are on top are reported first. Since the user can always
541      * interact with the window that has input focus by typing, the focused
542      * window is always returned (even if covered by a modal window).
543      * <p>
544      * <strong>Note:</strong> In order to access the windows your service has
545      * to declare the capability to retrieve window content by setting the
546      * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
547      * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
548      * Also the service has to opt-in to retrieve the interactive windows by
549      * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
550      * flag.
551      * </p>
552      *
553      * @return The windows if there are windows and the service is can retrieve
554      *         them, otherwise an empty list.
555      */
getWindows()556     public List<AccessibilityWindowInfo> getWindows() {
557         return AccessibilityInteractionClient.getInstance().getWindows(mConnectionId);
558     }
559 
560     /**
561      * Gets the root node in the currently active window if this service
562      * can retrieve window content. The active window is the one that the user
563      * is currently touching or the window with input focus, if the user is not
564      * touching any window.
565      * <p>
566      * The currently active window is defined as the window that most recently fired one
567      * of the following events:
568      * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED},
569      * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
570      * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}.
571      * In other words, the last window shown that also has input focus.
572      * </p>
573      * <p>
574      * <strong>Note:</strong> In order to access the root node your service has
575      * to declare the capability to retrieve window content by setting the
576      * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
577      * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
578      * </p>
579      *
580      * @return The root node if this service can retrieve window content.
581      */
getRootInActiveWindow()582     public AccessibilityNodeInfo getRootInActiveWindow() {
583         return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId);
584     }
585 
586     /**
587      * Disables the service. After calling this method, the service will be disabled and settings
588      * will show that it is turned off.
589      */
disableSelf()590     public final void disableSelf() {
591         final IAccessibilityServiceConnection connection =
592                 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
593         if (connection != null) {
594             try {
595                 connection.disableSelf();
596             } catch (RemoteException re) {
597                 throw new RuntimeException(re);
598             }
599         }
600     }
601 
602     /**
603      * Returns the magnification controller, which may be used to query and
604      * modify the state of display magnification.
605      * <p>
606      * <strong>Note:</strong> In order to control magnification, your service
607      * must declare the capability by setting the
608      * {@link android.R.styleable#AccessibilityService_canControlMagnification}
609      * property in its meta-data. For more information, see
610      * {@link #SERVICE_META_DATA}.
611      *
612      * @return the magnification controller
613      */
614     @NonNull
getMagnificationController()615     public final MagnificationController getMagnificationController() {
616         synchronized (mLock) {
617             if (mMagnificationController == null) {
618                 mMagnificationController = new MagnificationController(this, mLock);
619             }
620             return mMagnificationController;
621         }
622     }
623 
624     /**
625      * Get the controller for fingerprint gestures. This feature requires {@link
626      * AccessibilityServiceInfo#CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES}.
627      *
628      *<strong>Note: </strong> The service must be connected before this method is called.
629      *
630      * @return The controller for fingerprint gestures, or {@code null} if gestures are unavailable.
631      */
632     @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT)
getFingerprintGestureController()633     public final @NonNull FingerprintGestureController getFingerprintGestureController() {
634         if (mFingerprintGestureController == null) {
635             mFingerprintGestureController = new FingerprintGestureController(
636                     AccessibilityInteractionClient.getInstance().getConnection(mConnectionId));
637         }
638         return mFingerprintGestureController;
639     }
640 
641     /**
642      * Dispatch a gesture to the touch screen. Any gestures currently in progress, whether from
643      * the user, this service, or another service, will be cancelled.
644      * <p>
645      * The gesture will be dispatched as if it were performed directly on the screen by a user, so
646      * the events may be affected by features such as magnification and explore by touch.
647      * </p>
648      * <p>
649      * <strong>Note:</strong> In order to dispatch gestures, your service
650      * must declare the capability by setting the
651      * {@link android.R.styleable#AccessibilityService_canPerformGestures}
652      * property in its meta-data. For more information, see
653      * {@link #SERVICE_META_DATA}.
654      * </p>
655      *
656      * @param gesture The gesture to dispatch
657      * @param callback The object to call back when the status of the gesture is known. If
658      * {@code null}, no status is reported.
659      * @param handler The handler on which to call back the {@code callback} object. If
660      * {@code null}, the object is called back on the service's main thread.
661      *
662      * @return {@code true} if the gesture is dispatched, {@code false} if not.
663      */
dispatchGesture(@onNull GestureDescription gesture, @Nullable GestureResultCallback callback, @Nullable Handler handler)664     public final boolean dispatchGesture(@NonNull GestureDescription gesture,
665             @Nullable GestureResultCallback callback,
666             @Nullable Handler handler) {
667         final IAccessibilityServiceConnection connection =
668                 AccessibilityInteractionClient.getInstance().getConnection(
669                         mConnectionId);
670         if (connection == null) {
671             return false;
672         }
673         List<GestureDescription.GestureStep> steps =
674                 MotionEventGenerator.getGestureStepsFromGestureDescription(gesture, 100);
675         try {
676             synchronized (mLock) {
677                 mGestureStatusCallbackSequence++;
678                 if (callback != null) {
679                     if (mGestureStatusCallbackInfos == null) {
680                         mGestureStatusCallbackInfos = new SparseArray<>();
681                     }
682                     GestureResultCallbackInfo callbackInfo = new GestureResultCallbackInfo(gesture,
683                             callback, handler);
684                     mGestureStatusCallbackInfos.put(mGestureStatusCallbackSequence, callbackInfo);
685                 }
686                 connection.sendGesture(mGestureStatusCallbackSequence,
687                         new ParceledListSlice<>(steps));
688             }
689         } catch (RemoteException re) {
690             throw new RuntimeException(re);
691         }
692         return true;
693     }
694 
onPerformGestureResult(int sequence, final boolean completedSuccessfully)695     void onPerformGestureResult(int sequence, final boolean completedSuccessfully) {
696         if (mGestureStatusCallbackInfos == null) {
697             return;
698         }
699         GestureResultCallbackInfo callbackInfo;
700         synchronized (mLock) {
701             callbackInfo = mGestureStatusCallbackInfos.get(sequence);
702         }
703         final GestureResultCallbackInfo finalCallbackInfo = callbackInfo;
704         if ((callbackInfo != null) && (callbackInfo.gestureDescription != null)
705                 && (callbackInfo.callback != null)) {
706             if (callbackInfo.handler != null) {
707                 callbackInfo.handler.post(new Runnable() {
708                     @Override
709                     public void run() {
710                         if (completedSuccessfully) {
711                             finalCallbackInfo.callback
712                                     .onCompleted(finalCallbackInfo.gestureDescription);
713                         } else {
714                             finalCallbackInfo.callback
715                                     .onCancelled(finalCallbackInfo.gestureDescription);
716                         }
717                     }
718                 });
719                 return;
720             }
721             if (completedSuccessfully) {
722                 callbackInfo.callback.onCompleted(callbackInfo.gestureDescription);
723             } else {
724                 callbackInfo.callback.onCancelled(callbackInfo.gestureDescription);
725             }
726         }
727     }
728 
onMagnificationChanged(@onNull Region region, float scale, float centerX, float centerY)729     private void onMagnificationChanged(@NonNull Region region, float scale,
730             float centerX, float centerY) {
731         if (mMagnificationController != null) {
732             mMagnificationController.dispatchMagnificationChanged(
733                     region, scale, centerX, centerY);
734         }
735     }
736 
737     /**
738      * Callback for fingerprint gesture handling
739      * @param active If gesture detection is active
740      */
onFingerprintCapturingGesturesChanged(boolean active)741     private void onFingerprintCapturingGesturesChanged(boolean active) {
742         getFingerprintGestureController().onGestureDetectionActiveChanged(active);
743     }
744 
745     /**
746      * Callback for fingerprint gesture handling
747      * @param gesture The identifier for the gesture performed
748      */
onFingerprintGesture(int gesture)749     private void onFingerprintGesture(int gesture) {
750         getFingerprintGestureController().onGesture(gesture);
751     }
752 
753     /**
754      * Used to control and query the state of display magnification.
755      */
756     public static final class MagnificationController {
757         private final AccessibilityService mService;
758 
759         /**
760          * Map of listeners to their handlers. Lazily created when adding the
761          * first magnification listener.
762          */
763         private ArrayMap<OnMagnificationChangedListener, Handler> mListeners;
764         private final Object mLock;
765 
MagnificationController(@onNull AccessibilityService service, @NonNull Object lock)766         MagnificationController(@NonNull AccessibilityService service, @NonNull Object lock) {
767             mService = service;
768             mLock = lock;
769         }
770 
771         /**
772          * Called when the service is connected.
773          */
onServiceConnected()774         void onServiceConnected() {
775             synchronized (mLock) {
776                 if (mListeners != null && !mListeners.isEmpty()) {
777                     setMagnificationCallbackEnabled(true);
778                 }
779             }
780         }
781 
782         /**
783          * Adds the specified change listener to the list of magnification
784          * change listeners. The callback will occur on the service's main
785          * thread.
786          *
787          * @param listener the listener to add, must be non-{@code null}
788          */
addListener(@onNull OnMagnificationChangedListener listener)789         public void addListener(@NonNull OnMagnificationChangedListener listener) {
790             addListener(listener, null);
791         }
792 
793         /**
794          * Adds the specified change listener to the list of magnification
795          * change listeners. The callback will occur on the specified
796          * {@link Handler}'s thread, or on the service's main thread if the
797          * handler is {@code null}.
798          *
799          * @param listener the listener to add, must be non-null
800          * @param handler the handler on which the callback should execute, or
801          *        {@code null} to execute on the service's main thread
802          */
addListener(@onNull OnMagnificationChangedListener listener, @Nullable Handler handler)803         public void addListener(@NonNull OnMagnificationChangedListener listener,
804                 @Nullable Handler handler) {
805             synchronized (mLock) {
806                 if (mListeners == null) {
807                     mListeners = new ArrayMap<>();
808                 }
809 
810                 final boolean shouldEnableCallback = mListeners.isEmpty();
811                 mListeners.put(listener, handler);
812 
813                 if (shouldEnableCallback) {
814                     // This may fail if the service is not connected yet, but if we
815                     // still have listeners when it connects then we can try again.
816                     setMagnificationCallbackEnabled(true);
817                 }
818             }
819         }
820 
821         /**
822          * Removes the specified change listener from the list of magnification change listeners.
823          *
824          * @param listener the listener to remove, must be non-null
825          * @return {@code true} if the listener was removed, {@code false} otherwise
826          */
removeListener(@onNull OnMagnificationChangedListener listener)827         public boolean removeListener(@NonNull OnMagnificationChangedListener listener) {
828             if (mListeners == null) {
829                 return false;
830             }
831 
832             synchronized (mLock) {
833                 final int keyIndex = mListeners.indexOfKey(listener);
834                 final boolean hasKey = keyIndex >= 0;
835                 if (hasKey) {
836                     mListeners.removeAt(keyIndex);
837                 }
838 
839                 if (hasKey && mListeners.isEmpty()) {
840                     // We just removed the last listener, so we don't need
841                     // callbacks from the service anymore.
842                     setMagnificationCallbackEnabled(false);
843                 }
844 
845                 return hasKey;
846             }
847         }
848 
setMagnificationCallbackEnabled(boolean enabled)849         private void setMagnificationCallbackEnabled(boolean enabled) {
850             final IAccessibilityServiceConnection connection =
851                     AccessibilityInteractionClient.getInstance().getConnection(
852                             mService.mConnectionId);
853             if (connection != null) {
854                 try {
855                     connection.setMagnificationCallbackEnabled(enabled);
856                 } catch (RemoteException re) {
857                     throw new RuntimeException(re);
858                 }
859             }
860         }
861 
862         /**
863          * Dispatches magnification changes to any registered listeners. This
864          * should be called on the service's main thread.
865          */
dispatchMagnificationChanged(final @NonNull Region region, final float scale, final float centerX, final float centerY)866         void dispatchMagnificationChanged(final @NonNull Region region, final float scale,
867                 final float centerX, final float centerY) {
868             final ArrayMap<OnMagnificationChangedListener, Handler> entries;
869             synchronized (mLock) {
870                 if (mListeners == null || mListeners.isEmpty()) {
871                     Slog.d(LOG_TAG, "Received magnification changed "
872                             + "callback with no listeners registered!");
873                     setMagnificationCallbackEnabled(false);
874                     return;
875                 }
876 
877                 // Listeners may remove themselves. Perform a shallow copy to avoid concurrent
878                 // modification.
879                 entries = new ArrayMap<>(mListeners);
880             }
881 
882             for (int i = 0, count = entries.size(); i < count; i++) {
883                 final OnMagnificationChangedListener listener = entries.keyAt(i);
884                 final Handler handler = entries.valueAt(i);
885                 if (handler != null) {
886                     handler.post(new Runnable() {
887                         @Override
888                         public void run() {
889                             listener.onMagnificationChanged(MagnificationController.this,
890                                     region, scale, centerX, centerY);
891                         }
892                     });
893                 } else {
894                     // We're already on the main thread, just run the listener.
895                     listener.onMagnificationChanged(this, region, scale, centerX, centerY);
896                 }
897             }
898         }
899 
900         /**
901          * Returns the current magnification scale.
902          * <p>
903          * <strong>Note:</strong> If the service is not yet connected (e.g.
904          * {@link AccessibilityService#onServiceConnected()} has not yet been
905          * called) or the service has been disconnected, this method will
906          * return a default value of {@code 1.0f}.
907          *
908          * @return the current magnification scale
909          */
getScale()910         public float getScale() {
911             final IAccessibilityServiceConnection connection =
912                     AccessibilityInteractionClient.getInstance().getConnection(
913                             mService.mConnectionId);
914             if (connection != null) {
915                 try {
916                     return connection.getMagnificationScale();
917                 } catch (RemoteException re) {
918                     Log.w(LOG_TAG, "Failed to obtain scale", re);
919                     re.rethrowFromSystemServer();
920                 }
921             }
922             return 1.0f;
923         }
924 
925         /**
926          * Returns the unscaled screen-relative X coordinate of the focal
927          * center of the magnified region. This is the point around which
928          * zooming occurs and is guaranteed to lie within the magnified
929          * region.
930          * <p>
931          * <strong>Note:</strong> If the service is not yet connected (e.g.
932          * {@link AccessibilityService#onServiceConnected()} has not yet been
933          * called) or the service has been disconnected, this method will
934          * return a default value of {@code 0.0f}.
935          *
936          * @return the unscaled screen-relative X coordinate of the center of
937          *         the magnified region
938          */
getCenterX()939         public float getCenterX() {
940             final IAccessibilityServiceConnection connection =
941                     AccessibilityInteractionClient.getInstance().getConnection(
942                             mService.mConnectionId);
943             if (connection != null) {
944                 try {
945                     return connection.getMagnificationCenterX();
946                 } catch (RemoteException re) {
947                     Log.w(LOG_TAG, "Failed to obtain center X", re);
948                     re.rethrowFromSystemServer();
949                 }
950             }
951             return 0.0f;
952         }
953 
954         /**
955          * Returns the unscaled screen-relative Y coordinate of the focal
956          * center of the magnified region. This is the point around which
957          * zooming occurs and is guaranteed to lie within the magnified
958          * region.
959          * <p>
960          * <strong>Note:</strong> If the service is not yet connected (e.g.
961          * {@link AccessibilityService#onServiceConnected()} has not yet been
962          * called) or the service has been disconnected, this method will
963          * return a default value of {@code 0.0f}.
964          *
965          * @return the unscaled screen-relative Y coordinate of the center of
966          *         the magnified region
967          */
getCenterY()968         public float getCenterY() {
969             final IAccessibilityServiceConnection connection =
970                     AccessibilityInteractionClient.getInstance().getConnection(
971                             mService.mConnectionId);
972             if (connection != null) {
973                 try {
974                     return connection.getMagnificationCenterY();
975                 } catch (RemoteException re) {
976                     Log.w(LOG_TAG, "Failed to obtain center Y", re);
977                     re.rethrowFromSystemServer();
978                 }
979             }
980             return 0.0f;
981         }
982 
983         /**
984          * Returns the region of the screen currently active for magnification. Changes to
985          * magnification scale and center only affect this portion of the screen. The rest of the
986          * screen, for example input methods, cannot be magnified. This region is relative to the
987          * unscaled screen and is independent of the scale and center point.
988          * <p>
989          * The returned region will be empty if magnification is not active. Magnification is active
990          * if magnification gestures are enabled or if a service is running that can control
991          * magnification.
992          * <p>
993          * <strong>Note:</strong> If the service is not yet connected (e.g.
994          * {@link AccessibilityService#onServiceConnected()} has not yet been
995          * called) or the service has been disconnected, this method will
996          * return an empty region.
997          *
998          * @return the region of the screen currently active for magnification, or an empty region
999          * if magnification is not active.
1000          */
1001         @NonNull
getMagnificationRegion()1002         public Region getMagnificationRegion() {
1003             final IAccessibilityServiceConnection connection =
1004                     AccessibilityInteractionClient.getInstance().getConnection(
1005                             mService.mConnectionId);
1006             if (connection != null) {
1007                 try {
1008                     return connection.getMagnificationRegion();
1009                 } catch (RemoteException re) {
1010                     Log.w(LOG_TAG, "Failed to obtain magnified region", re);
1011                     re.rethrowFromSystemServer();
1012                 }
1013             }
1014             return Region.obtain();
1015         }
1016 
1017         /**
1018          * Resets magnification scale and center to their default (e.g. no
1019          * magnification) values.
1020          * <p>
1021          * <strong>Note:</strong> If the service is not yet connected (e.g.
1022          * {@link AccessibilityService#onServiceConnected()} has not yet been
1023          * called) or the service has been disconnected, this method will have
1024          * no effect and return {@code false}.
1025          *
1026          * @param animate {@code true} to animate from the current scale and
1027          *                center or {@code false} to reset the scale and center
1028          *                immediately
1029          * @return {@code true} on success, {@code false} on failure
1030          */
reset(boolean animate)1031         public boolean reset(boolean animate) {
1032             final IAccessibilityServiceConnection connection =
1033                     AccessibilityInteractionClient.getInstance().getConnection(
1034                             mService.mConnectionId);
1035             if (connection != null) {
1036                 try {
1037                     return connection.resetMagnification(animate);
1038                 } catch (RemoteException re) {
1039                     Log.w(LOG_TAG, "Failed to reset", re);
1040                     re.rethrowFromSystemServer();
1041                 }
1042             }
1043             return false;
1044         }
1045 
1046         /**
1047          * Sets the magnification scale.
1048          * <p>
1049          * <strong>Note:</strong> If the service is not yet connected (e.g.
1050          * {@link AccessibilityService#onServiceConnected()} has not yet been
1051          * called) or the service has been disconnected, this method will have
1052          * no effect and return {@code false}.
1053          *
1054          * @param scale the magnification scale to set, must be >= 1 and <= 5
1055          * @param animate {@code true} to animate from the current scale or
1056          *                {@code false} to set the scale immediately
1057          * @return {@code true} on success, {@code false} on failure
1058          */
setScale(float scale, boolean animate)1059         public boolean setScale(float scale, boolean animate) {
1060             final IAccessibilityServiceConnection connection =
1061                     AccessibilityInteractionClient.getInstance().getConnection(
1062                             mService.mConnectionId);
1063             if (connection != null) {
1064                 try {
1065                     return connection.setMagnificationScaleAndCenter(
1066                             scale, Float.NaN, Float.NaN, animate);
1067                 } catch (RemoteException re) {
1068                     Log.w(LOG_TAG, "Failed to set scale", re);
1069                     re.rethrowFromSystemServer();
1070                 }
1071             }
1072             return false;
1073         }
1074 
1075         /**
1076          * Sets the center of the magnified viewport.
1077          * <p>
1078          * <strong>Note:</strong> If the service is not yet connected (e.g.
1079          * {@link AccessibilityService#onServiceConnected()} has not yet been
1080          * called) or the service has been disconnected, this method will have
1081          * no effect and return {@code false}.
1082          *
1083          * @param centerX the unscaled screen-relative X coordinate on which to
1084          *                center the viewport
1085          * @param centerY the unscaled screen-relative Y coordinate on which to
1086          *                center the viewport
1087          * @param animate {@code true} to animate from the current viewport
1088          *                center or {@code false} to set the center immediately
1089          * @return {@code true} on success, {@code false} on failure
1090          */
setCenter(float centerX, float centerY, boolean animate)1091         public boolean setCenter(float centerX, float centerY, boolean animate) {
1092             final IAccessibilityServiceConnection connection =
1093                     AccessibilityInteractionClient.getInstance().getConnection(
1094                             mService.mConnectionId);
1095             if (connection != null) {
1096                 try {
1097                     return connection.setMagnificationScaleAndCenter(
1098                             Float.NaN, centerX, centerY, animate);
1099                 } catch (RemoteException re) {
1100                     Log.w(LOG_TAG, "Failed to set center", re);
1101                     re.rethrowFromSystemServer();
1102                 }
1103             }
1104             return false;
1105         }
1106 
1107         /**
1108          * Listener for changes in the state of magnification.
1109          */
1110         public interface OnMagnificationChangedListener {
1111             /**
1112              * Called when the magnified region, scale, or center changes.
1113              *
1114              * @param controller the magnification controller
1115              * @param region the magnification region
1116              * @param scale the new scale
1117              * @param centerX the new X coordinate, in unscaled coordinates, around which
1118              * magnification is focused
1119              * @param centerY the new Y coordinate, in unscaled coordinates, around which
1120              * magnification is focused
1121              */
onMagnificationChanged(@onNull MagnificationController controller, @NonNull Region region, float scale, float centerX, float centerY)1122             void onMagnificationChanged(@NonNull MagnificationController controller,
1123                     @NonNull Region region, float scale, float centerX, float centerY);
1124         }
1125     }
1126 
1127     /**
1128      * Returns the soft keyboard controller, which may be used to query and modify the soft keyboard
1129      * show mode.
1130      *
1131      * @return the soft keyboard controller
1132      */
1133     @NonNull
getSoftKeyboardController()1134     public final SoftKeyboardController getSoftKeyboardController() {
1135         synchronized (mLock) {
1136             if (mSoftKeyboardController == null) {
1137                 mSoftKeyboardController = new SoftKeyboardController(this, mLock);
1138             }
1139             return mSoftKeyboardController;
1140         }
1141     }
1142 
onSoftKeyboardShowModeChanged(int showMode)1143     private void onSoftKeyboardShowModeChanged(int showMode) {
1144         if (mSoftKeyboardController != null) {
1145             mSoftKeyboardController.dispatchSoftKeyboardShowModeChanged(showMode);
1146         }
1147     }
1148 
1149     /**
1150      * Used to control and query the soft keyboard show mode.
1151      */
1152     public static final class SoftKeyboardController {
1153         private final AccessibilityService mService;
1154 
1155         /**
1156          * Map of listeners to their handlers. Lazily created when adding the first
1157          * soft keyboard change listener.
1158          */
1159         private ArrayMap<OnShowModeChangedListener, Handler> mListeners;
1160         private final Object mLock;
1161 
SoftKeyboardController(@onNull AccessibilityService service, @NonNull Object lock)1162         SoftKeyboardController(@NonNull AccessibilityService service, @NonNull Object lock) {
1163             mService = service;
1164             mLock = lock;
1165         }
1166 
1167         /**
1168          * Called when the service is connected.
1169          */
onServiceConnected()1170         void onServiceConnected() {
1171             synchronized(mLock) {
1172                 if (mListeners != null && !mListeners.isEmpty()) {
1173                     setSoftKeyboardCallbackEnabled(true);
1174                 }
1175             }
1176         }
1177 
1178         /**
1179          * Adds the specified change listener to the list of show mode change listeners. The
1180          * callback will occur on the service's main thread. Listener is not called on registration.
1181          */
addOnShowModeChangedListener(@onNull OnShowModeChangedListener listener)1182         public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) {
1183             addOnShowModeChangedListener(listener, null);
1184         }
1185 
1186         /**
1187          * Adds the specified change listener to the list of soft keyboard show mode change
1188          * listeners. The callback will occur on the specified {@link Handler}'s thread, or on the
1189          * services's main thread if the handler is {@code null}.
1190          *
1191          * @param listener the listener to add, must be non-null
1192          * @param handler the handler on which to callback should execute, or {@code null} to
1193          *        execute on the service's main thread
1194          */
addOnShowModeChangedListener(@onNull OnShowModeChangedListener listener, @Nullable Handler handler)1195         public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener,
1196                 @Nullable Handler handler) {
1197             synchronized (mLock) {
1198                 if (mListeners == null) {
1199                     mListeners = new ArrayMap<>();
1200                 }
1201 
1202                 final boolean shouldEnableCallback = mListeners.isEmpty();
1203                 mListeners.put(listener, handler);
1204 
1205                 if (shouldEnableCallback) {
1206                     // This may fail if the service is not connected yet, but if we still have
1207                     // listeners when it connects, we can try again.
1208                     setSoftKeyboardCallbackEnabled(true);
1209                 }
1210             }
1211         }
1212 
1213         /**
1214          * Removes the specified change listener from the list of keyboard show mode change
1215          * listeners.
1216          *
1217          * @param listener the listener to remove, must be non-null
1218          * @return {@code true} if the listener was removed, {@code false} otherwise
1219          */
removeOnShowModeChangedListener(@onNull OnShowModeChangedListener listener)1220         public boolean removeOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) {
1221             if (mListeners == null) {
1222                 return false;
1223             }
1224 
1225             synchronized (mLock) {
1226                 final int keyIndex = mListeners.indexOfKey(listener);
1227                 final boolean hasKey = keyIndex >= 0;
1228                 if (hasKey) {
1229                     mListeners.removeAt(keyIndex);
1230                 }
1231 
1232                 if (hasKey && mListeners.isEmpty()) {
1233                     // We just removed the last listener, so we don't need callbacks from the
1234                     // service anymore.
1235                     setSoftKeyboardCallbackEnabled(false);
1236                 }
1237 
1238                 return hasKey;
1239             }
1240         }
1241 
setSoftKeyboardCallbackEnabled(boolean enabled)1242         private void setSoftKeyboardCallbackEnabled(boolean enabled) {
1243             final IAccessibilityServiceConnection connection =
1244                     AccessibilityInteractionClient.getInstance().getConnection(
1245                             mService.mConnectionId);
1246             if (connection != null) {
1247                 try {
1248                     connection.setSoftKeyboardCallbackEnabled(enabled);
1249                 } catch (RemoteException re) {
1250                     throw new RuntimeException(re);
1251                 }
1252             }
1253         }
1254 
1255         /**
1256          * Dispatches the soft keyboard show mode change to any registered listeners. This should
1257          * be called on the service's main thread.
1258          */
dispatchSoftKeyboardShowModeChanged(final int showMode)1259         void dispatchSoftKeyboardShowModeChanged(final int showMode) {
1260             final ArrayMap<OnShowModeChangedListener, Handler> entries;
1261             synchronized (mLock) {
1262                 if (mListeners == null || mListeners.isEmpty()) {
1263                     Slog.w(LOG_TAG, "Received soft keyboard show mode changed callback"
1264                             + " with no listeners registered!");
1265                     setSoftKeyboardCallbackEnabled(false);
1266                     return;
1267                 }
1268 
1269                 // Listeners may remove themselves. Perform a shallow copy to avoid concurrent
1270                 // modification.
1271                 entries = new ArrayMap<>(mListeners);
1272             }
1273 
1274             for (int i = 0, count = entries.size(); i < count; i++) {
1275                 final OnShowModeChangedListener listener = entries.keyAt(i);
1276                 final Handler handler = entries.valueAt(i);
1277                 if (handler != null) {
1278                     handler.post(new Runnable() {
1279                         @Override
1280                         public void run() {
1281                             listener.onShowModeChanged(SoftKeyboardController.this, showMode);
1282                         }
1283                     });
1284                 } else {
1285                     // We're already on the main thread, just run the listener.
1286                     listener.onShowModeChanged(this, showMode);
1287                 }
1288             }
1289         }
1290 
1291         /**
1292          * Returns the show mode of the soft keyboard. The default show mode is
1293          * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
1294          * focused. An AccessibilityService can also request the show mode
1295          * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
1296          *
1297          * @return the current soft keyboard show mode
1298          */
1299         @SoftKeyboardShowMode
getShowMode()1300         public int getShowMode() {
1301            try {
1302                return Settings.Secure.getInt(mService.getContentResolver(),
1303                        Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
1304            } catch (Settings.SettingNotFoundException e) {
1305                Log.v(LOG_TAG, "Failed to obtain the soft keyboard mode", e);
1306                // The settings hasn't been changed yet, so it's value is null. Return the default.
1307                return 0;
1308            }
1309         }
1310 
1311         /**
1312          * Sets the soft keyboard show mode. The default show mode is
1313          * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
1314          * focused. An AccessibilityService can also request the show mode
1315          * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. The
1316          * The lastto this method will be honored, regardless of any previous calls (including those
1317          * made by other AccessibilityServices).
1318          * <p>
1319          * <strong>Note:</strong> If the service is not yet connected (e.g.
1320          * {@link AccessibilityService#onServiceConnected()} has not yet been called) or the
1321          * service has been disconnected, this method will have no effect and return {@code false}.
1322          *
1323          * @param showMode the new show mode for the soft keyboard
1324          * @return {@code true} on success
1325          */
setShowMode(@oftKeyboardShowMode int showMode)1326         public boolean setShowMode(@SoftKeyboardShowMode int showMode) {
1327            final IAccessibilityServiceConnection connection =
1328                    AccessibilityInteractionClient.getInstance().getConnection(
1329                            mService.mConnectionId);
1330            if (connection != null) {
1331                try {
1332                    return connection.setSoftKeyboardShowMode(showMode);
1333                } catch (RemoteException re) {
1334                    Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re);
1335                    re.rethrowFromSystemServer();
1336                }
1337            }
1338            return false;
1339         }
1340 
1341         /**
1342          * Listener for changes in the soft keyboard show mode.
1343          */
1344         public interface OnShowModeChangedListener {
1345            /**
1346             * Called when the soft keyboard behavior changes. The default show mode is
1347             * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
1348             * focused. An AccessibilityService can also request the show mode
1349             * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
1350             *
1351             * @param controller the soft keyboard controller
1352             * @param showMode the current soft keyboard show mode
1353             */
onShowModeChanged(@onNull SoftKeyboardController controller, @SoftKeyboardShowMode int showMode)1354             void onShowModeChanged(@NonNull SoftKeyboardController controller,
1355                     @SoftKeyboardShowMode int showMode);
1356         }
1357     }
1358 
1359     /**
1360      * Returns the controller for the accessibility button within the system's navigation area.
1361      * This instance may be used to query the accessibility button's state and register listeners
1362      * for interactions with and state changes for the accessibility button when
1363      * {@link AccessibilityServiceInfo#FLAG_REQUEST_ACCESSIBILITY_BUTTON} is set.
1364      * <p>
1365      * <strong>Note:</strong> Not all devices are capable of displaying the accessibility button
1366      * within a navigation area, and as such, use of this class should be considered only as an
1367      * optional feature or shortcut on supported device implementations.
1368      * </p>
1369      *
1370      * @return the accessibility button controller for this {@link AccessibilityService}
1371      */
1372     @NonNull
getAccessibilityButtonController()1373     public final AccessibilityButtonController getAccessibilityButtonController() {
1374         synchronized (mLock) {
1375             if (mAccessibilityButtonController == null) {
1376                 mAccessibilityButtonController = new AccessibilityButtonController(
1377                         AccessibilityInteractionClient.getInstance().getConnection(mConnectionId));
1378             }
1379             return mAccessibilityButtonController;
1380         }
1381     }
1382 
onAccessibilityButtonClicked()1383     private void onAccessibilityButtonClicked() {
1384         getAccessibilityButtonController().dispatchAccessibilityButtonClicked();
1385     }
1386 
onAccessibilityButtonAvailabilityChanged(boolean available)1387     private void onAccessibilityButtonAvailabilityChanged(boolean available) {
1388         getAccessibilityButtonController().dispatchAccessibilityButtonAvailabilityChanged(
1389                 available);
1390     }
1391 
1392     /**
1393      * Performs a global action. Such an action can be performed
1394      * at any moment regardless of the current application or user
1395      * location in that application. For example going back, going
1396      * home, opening recents, etc.
1397      *
1398      * @param action The action to perform.
1399      * @return Whether the action was successfully performed.
1400      *
1401      * @see #GLOBAL_ACTION_BACK
1402      * @see #GLOBAL_ACTION_HOME
1403      * @see #GLOBAL_ACTION_NOTIFICATIONS
1404      * @see #GLOBAL_ACTION_RECENTS
1405      */
performGlobalAction(int action)1406     public final boolean performGlobalAction(int action) {
1407         IAccessibilityServiceConnection connection =
1408             AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1409         if (connection != null) {
1410             try {
1411                 return connection.performGlobalAction(action);
1412             } catch (RemoteException re) {
1413                 Log.w(LOG_TAG, "Error while calling performGlobalAction", re);
1414                 re.rethrowFromSystemServer();
1415             }
1416         }
1417         return false;
1418     }
1419 
1420     /**
1421      * Find the view that has the specified focus type. The search is performed
1422      * across all windows.
1423      * <p>
1424      * <strong>Note:</strong> In order to access the windows your service has
1425      * to declare the capability to retrieve window content by setting the
1426      * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
1427      * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
1428      * Also the service has to opt-in to retrieve the interactive windows by
1429      * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
1430      * flag. Otherwise, the search will be performed only in the active window.
1431      * </p>
1432      *
1433      * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or
1434      *         {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}.
1435      * @return The node info of the focused view or null.
1436      *
1437      * @see AccessibilityNodeInfo#FOCUS_INPUT
1438      * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY
1439      */
findFocus(int focus)1440     public AccessibilityNodeInfo findFocus(int focus) {
1441         return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId,
1442                 AccessibilityWindowInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus);
1443     }
1444 
1445     /**
1446      * Gets the an {@link AccessibilityServiceInfo} describing this
1447      * {@link AccessibilityService}. This method is useful if one wants
1448      * to change some of the dynamically configurable properties at
1449      * runtime.
1450      *
1451      * @return The accessibility service info.
1452      *
1453      * @see AccessibilityServiceInfo
1454      */
getServiceInfo()1455     public final AccessibilityServiceInfo getServiceInfo() {
1456         IAccessibilityServiceConnection connection =
1457             AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1458         if (connection != null) {
1459             try {
1460                 return connection.getServiceInfo();
1461             } catch (RemoteException re) {
1462                 Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re);
1463                 re.rethrowFromSystemServer();
1464             }
1465         }
1466         return null;
1467     }
1468 
1469     /**
1470      * Sets the {@link AccessibilityServiceInfo} that describes this service.
1471      * <p>
1472      * Note: You can call this method any time but the info will be picked up after
1473      *       the system has bound to this service and when this method is called thereafter.
1474      *
1475      * @param info The info.
1476      */
setServiceInfo(AccessibilityServiceInfo info)1477     public final void setServiceInfo(AccessibilityServiceInfo info) {
1478         mInfo = info;
1479         sendServiceInfo();
1480     }
1481 
1482     /**
1483      * Sets the {@link AccessibilityServiceInfo} for this service if the latter is
1484      * properly set and there is an {@link IAccessibilityServiceConnection} to the
1485      * AccessibilityManagerService.
1486      */
sendServiceInfo()1487     private void sendServiceInfo() {
1488         IAccessibilityServiceConnection connection =
1489             AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1490         if (mInfo != null && connection != null) {
1491             try {
1492                 connection.setServiceInfo(mInfo);
1493                 mInfo = null;
1494                 AccessibilityInteractionClient.getInstance().clearCache();
1495             } catch (RemoteException re) {
1496                 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
1497                 re.rethrowFromSystemServer();
1498             }
1499         }
1500     }
1501 
1502     @Override
getSystemService(@erviceName @onNull String name)1503     public Object getSystemService(@ServiceName @NonNull String name) {
1504         if (getBaseContext() == null) {
1505             throw new IllegalStateException(
1506                     "System services not available to Activities before onCreate()");
1507         }
1508 
1509         // Guarantee that we always return the same window manager instance.
1510         if (WINDOW_SERVICE.equals(name)) {
1511             if (mWindowManager == null) {
1512                 mWindowManager = (WindowManager) getBaseContext().getSystemService(name);
1513             }
1514             return mWindowManager;
1515         }
1516         return super.getSystemService(name);
1517     }
1518 
1519     /**
1520      * Implement to return the implementation of the internal accessibility
1521      * service interface.
1522      */
1523     @Override
onBind(Intent intent)1524     public final IBinder onBind(Intent intent) {
1525         return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {
1526             @Override
1527             public void onServiceConnected() {
1528                 AccessibilityService.this.dispatchServiceConnected();
1529             }
1530 
1531             @Override
1532             public void onInterrupt() {
1533                 AccessibilityService.this.onInterrupt();
1534             }
1535 
1536             @Override
1537             public void onAccessibilityEvent(AccessibilityEvent event) {
1538                 AccessibilityService.this.onAccessibilityEvent(event);
1539             }
1540 
1541             @Override
1542             public void init(int connectionId, IBinder windowToken) {
1543                 mConnectionId = connectionId;
1544                 mWindowToken = windowToken;
1545 
1546                 // The client may have already obtained the window manager, so
1547                 // update the default token on whatever manager we gave them.
1548                 final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE);
1549                 wm.setDefaultToken(windowToken);
1550             }
1551 
1552             @Override
1553             public boolean onGesture(int gestureId) {
1554                 return AccessibilityService.this.onGesture(gestureId);
1555             }
1556 
1557             @Override
1558             public boolean onKeyEvent(KeyEvent event) {
1559                 return AccessibilityService.this.onKeyEvent(event);
1560             }
1561 
1562             @Override
1563             public void onMagnificationChanged(@NonNull Region region,
1564                     float scale, float centerX, float centerY) {
1565                 AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY);
1566             }
1567 
1568             @Override
1569             public void onSoftKeyboardShowModeChanged(int showMode) {
1570                 AccessibilityService.this.onSoftKeyboardShowModeChanged(showMode);
1571             }
1572 
1573             @Override
1574             public void onPerformGestureResult(int sequence, boolean completedSuccessfully) {
1575                 AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully);
1576             }
1577 
1578             @Override
1579             public void onFingerprintCapturingGesturesChanged(boolean active) {
1580                 AccessibilityService.this.onFingerprintCapturingGesturesChanged(active);
1581             }
1582 
1583             @Override
1584             public void onFingerprintGesture(int gesture) {
1585                 AccessibilityService.this.onFingerprintGesture(gesture);
1586             }
1587 
1588             @Override
1589             public void onAccessibilityButtonClicked() {
1590                 AccessibilityService.this.onAccessibilityButtonClicked();
1591             }
1592 
1593             @Override
1594             public void onAccessibilityButtonAvailabilityChanged(boolean available) {
1595                 AccessibilityService.this.onAccessibilityButtonAvailabilityChanged(available);
1596             }
1597         });
1598     }
1599 
1600     /**
1601      * Implements the internal {@link IAccessibilityServiceClient} interface to convert
1602      * incoming calls to it back to calls on an {@link AccessibilityService}.
1603      *
1604      * @hide
1605      */
1606     public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
1607             implements HandlerCaller.Callback {
1608         private static final int DO_INIT = 1;
1609         private static final int DO_ON_INTERRUPT = 2;
1610         private static final int DO_ON_ACCESSIBILITY_EVENT = 3;
1611         private static final int DO_ON_GESTURE = 4;
1612         private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5;
1613         private static final int DO_ON_KEY_EVENT = 6;
1614         private static final int DO_ON_MAGNIFICATION_CHANGED = 7;
1615         private static final int DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED = 8;
1616         private static final int DO_GESTURE_COMPLETE = 9;
1617         private static final int DO_ON_FINGERPRINT_ACTIVE_CHANGED = 10;
1618         private static final int DO_ON_FINGERPRINT_GESTURE = 11;
1619         private static final int DO_ACCESSIBILITY_BUTTON_CLICKED = 12;
1620         private static final int DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 13;
1621 
1622         private final HandlerCaller mCaller;
1623 
1624         private final Callbacks mCallback;
1625 
1626         private int mConnectionId = AccessibilityInteractionClient.NO_ID;
1627 
1628         public IAccessibilityServiceClientWrapper(Context context, Looper looper,
1629                 Callbacks callback) {
1630             mCallback = callback;
1631             mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
1632         }
1633 
1634         public void init(IAccessibilityServiceConnection connection, int connectionId,
1635                 IBinder windowToken) {
1636             Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId,
1637                     connection, windowToken);
1638             mCaller.sendMessage(message);
1639         }
1640 
1641         public void onInterrupt() {
1642             Message message = mCaller.obtainMessage(DO_ON_INTERRUPT);
1643             mCaller.sendMessage(message);
1644         }
1645 
1646         public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) {
1647             Message message = mCaller.obtainMessageBO(
1648                     DO_ON_ACCESSIBILITY_EVENT, serviceWantsEvent, event);
1649             mCaller.sendMessage(message);
1650         }
1651 
1652         public void onGesture(int gestureId) {
1653             Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId);
1654             mCaller.sendMessage(message);
1655         }
1656 
1657         public void clearAccessibilityCache() {
1658             Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE);
1659             mCaller.sendMessage(message);
1660         }
1661 
1662         @Override
1663         public void onKeyEvent(KeyEvent event, int sequence) {
1664             Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event);
1665             mCaller.sendMessage(message);
1666         }
1667 
1668         public void onMagnificationChanged(@NonNull Region region,
1669                 float scale, float centerX, float centerY) {
1670             final SomeArgs args = SomeArgs.obtain();
1671             args.arg1 = region;
1672             args.arg2 = scale;
1673             args.arg3 = centerX;
1674             args.arg4 = centerY;
1675 
1676             final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args);
1677             mCaller.sendMessage(message);
1678         }
1679 
1680         public void onSoftKeyboardShowModeChanged(int showMode) {
1681           final Message message =
1682                   mCaller.obtainMessageI(DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED, showMode);
1683           mCaller.sendMessage(message);
1684         }
1685 
1686         public void onPerformGestureResult(int sequence, boolean successfully) {
1687             Message message = mCaller.obtainMessageII(DO_GESTURE_COMPLETE, sequence,
1688                     successfully ? 1 : 0);
1689             mCaller.sendMessage(message);
1690         }
1691 
1692         public void onFingerprintCapturingGesturesChanged(boolean active) {
1693             mCaller.sendMessage(mCaller.obtainMessageI(
1694                     DO_ON_FINGERPRINT_ACTIVE_CHANGED, active ? 1 : 0));
1695         }
1696 
1697         public void onFingerprintGesture(int gesture) {
1698             mCaller.sendMessage(mCaller.obtainMessageI(DO_ON_FINGERPRINT_GESTURE, gesture));
1699         }
1700 
1701         public void onAccessibilityButtonClicked() {
1702             final Message message = mCaller.obtainMessage(DO_ACCESSIBILITY_BUTTON_CLICKED);
1703             mCaller.sendMessage(message);
1704         }
1705 
1706         public void onAccessibilityButtonAvailabilityChanged(boolean available) {
1707             final Message message = mCaller.obtainMessageI(
1708                     DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED, (available ? 1 : 0));
1709             mCaller.sendMessage(message);
1710         }
1711 
1712         @Override
1713         public void executeMessage(Message message) {
1714             switch (message.what) {
1715                 case DO_ON_ACCESSIBILITY_EVENT: {
1716                     AccessibilityEvent event = (AccessibilityEvent) message.obj;
1717                     boolean serviceWantsEvent = message.arg1 != 0;
1718                     if (event != null) {
1719                         // Send the event to AccessibilityCache via AccessibilityInteractionClient
1720                         AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);
1721                         if (serviceWantsEvent
1722                                 && (mConnectionId != AccessibilityInteractionClient.NO_ID)) {
1723                             // Send the event to AccessibilityService
1724                             mCallback.onAccessibilityEvent(event);
1725                         }
1726                         // Make sure the event is recycled.
1727                         try {
1728                             event.recycle();
1729                         } catch (IllegalStateException ise) {
1730                             /* ignore - best effort */
1731                         }
1732                     }
1733                 } return;
1734 
1735                 case DO_ON_INTERRUPT: {
1736                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1737                         mCallback.onInterrupt();
1738                     }
1739                 } return;
1740 
1741                 case DO_INIT: {
1742                     mConnectionId = message.arg1;
1743                     SomeArgs args = (SomeArgs) message.obj;
1744                     IAccessibilityServiceConnection connection =
1745                             (IAccessibilityServiceConnection) args.arg1;
1746                     IBinder windowToken = (IBinder) args.arg2;
1747                     args.recycle();
1748                     if (connection != null) {
1749                         AccessibilityInteractionClient.getInstance().addConnection(mConnectionId,
1750                                 connection);
1751                         mCallback.init(mConnectionId, windowToken);
1752                         mCallback.onServiceConnected();
1753                     } else {
1754                         AccessibilityInteractionClient.getInstance().removeConnection(
1755                                 mConnectionId);
1756                         mConnectionId = AccessibilityInteractionClient.NO_ID;
1757                         AccessibilityInteractionClient.getInstance().clearCache();
1758                         mCallback.init(AccessibilityInteractionClient.NO_ID, null);
1759                     }
1760                 } return;
1761 
1762                 case DO_ON_GESTURE: {
1763                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1764                         final int gestureId = message.arg1;
1765                         mCallback.onGesture(gestureId);
1766                     }
1767                 } return;
1768 
1769                 case DO_CLEAR_ACCESSIBILITY_CACHE: {
1770                     AccessibilityInteractionClient.getInstance().clearCache();
1771                 } return;
1772 
1773                 case DO_ON_KEY_EVENT: {
1774                     KeyEvent event = (KeyEvent) message.obj;
1775                     try {
1776                         IAccessibilityServiceConnection connection = AccessibilityInteractionClient
1777                                 .getInstance().getConnection(mConnectionId);
1778                         if (connection != null) {
1779                             final boolean result = mCallback.onKeyEvent(event);
1780                             final int sequence = message.arg1;
1781                             try {
1782                                 connection.setOnKeyEventResult(result, sequence);
1783                             } catch (RemoteException re) {
1784                                 /* ignore */
1785                             }
1786                         }
1787                     } finally {
1788                         // Make sure the event is recycled.
1789                         try {
1790                             event.recycle();
1791                         } catch (IllegalStateException ise) {
1792                             /* ignore - best effort */
1793                         }
1794                     }
1795                 } return;
1796 
1797                 case DO_ON_MAGNIFICATION_CHANGED: {
1798                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1799                         final SomeArgs args = (SomeArgs) message.obj;
1800                         final Region region = (Region) args.arg1;
1801                         final float scale = (float) args.arg2;
1802                         final float centerX = (float) args.arg3;
1803                         final float centerY = (float) args.arg4;
1804                         mCallback.onMagnificationChanged(region, scale, centerX, centerY);
1805                     }
1806                 } return;
1807 
1808                 case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: {
1809                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1810                         final int showMode = (int) message.arg1;
1811                         mCallback.onSoftKeyboardShowModeChanged(showMode);
1812                     }
1813                 } return;
1814 
1815                 case DO_GESTURE_COMPLETE: {
1816                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1817                         final boolean successfully = message.arg2 == 1;
1818                         mCallback.onPerformGestureResult(message.arg1, successfully);
1819                     }
1820                 } return;
1821                 case DO_ON_FINGERPRINT_ACTIVE_CHANGED: {
1822                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1823                         mCallback.onFingerprintCapturingGesturesChanged(message.arg1 == 1);
1824                     }
1825                 } return;
1826                 case DO_ON_FINGERPRINT_GESTURE: {
1827                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1828                         mCallback.onFingerprintGesture(message.arg1);
1829                     }
1830                 } return;
1831 
1832                 case (DO_ACCESSIBILITY_BUTTON_CLICKED): {
1833                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1834                         mCallback.onAccessibilityButtonClicked();
1835                     }
1836                 } return;
1837 
1838                 case (DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED): {
1839                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
1840                         final boolean available = (message.arg1 != 0);
1841                         mCallback.onAccessibilityButtonAvailabilityChanged(available);
1842                     }
1843                 } return;
1844 
1845                 default :
1846                     Log.w(LOG_TAG, "Unknown message type " + message.what);
1847             }
1848         }
1849     }
1850 
1851     /**
1852      * Class used to report status of dispatched gestures
1853      */
1854     public static abstract class GestureResultCallback {
1855         /** Called when the gesture has completed successfully
1856          *
1857          * @param gestureDescription The description of the gesture that completed.
1858          */
1859         public void onCompleted(GestureDescription gestureDescription) {
1860         }
1861 
1862         /** Called when the gesture was cancelled
1863          *
1864          * @param gestureDescription The description of the gesture that was cancelled.
1865          */
1866         public void onCancelled(GestureDescription gestureDescription) {
1867         }
1868     }
1869 
1870     /* Object to keep track of gesture result callbacks */
1871     private static class GestureResultCallbackInfo {
1872         GestureDescription gestureDescription;
1873         GestureResultCallback callback;
1874         Handler handler;
1875 
1876         GestureResultCallbackInfo(GestureDescription gestureDescription,
1877                 GestureResultCallback callback, Handler handler) {
1878             this.gestureDescription = gestureDescription;
1879             this.callback = callback;
1880             this.handler = handler;
1881         }
1882     }
1883 }
1884