Home | History | Annotate | Download | only in accessibilityservice
      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 {
    378         void onAccessibilityEvent(AccessibilityEvent event);
    379         void onInterrupt();
    380         void onServiceConnected();
    381         void init(int connectionId, IBinder windowToken);
    382         boolean onGesture(int gestureId);
    383         boolean onKeyEvent(KeyEvent event);
    384         void onMagnificationChanged(@NonNull Region region,
    385                 float scale, float centerX, float centerY);
    386         void onSoftKeyboardShowModeChanged(int showMode);
    387         void onPerformGestureResult(int sequence, boolean completedSuccessfully);
    388         void onFingerprintCapturingGesturesChanged(boolean active);
    389         void onFingerprintGesture(int gesture);
    390         void onAccessibilityButtonClicked();
    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      */
    435     public abstract void onAccessibilityEvent(AccessibilityEvent event);
    436 
    437     /**
    438      * Callback for interrupting the accessibility feedback.
    439      */
    440     public abstract void onInterrupt();
    441 
    442     /**
    443      * Dispatches service connection to internal components first, then the
    444      * client code.
    445      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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)
    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      */
    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 
    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 
    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      */
    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      */
    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 
    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          */
    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          */
    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          */
    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          */
    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 
    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          */
    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          */
    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          */
    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          */
    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
   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          */
   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          */
   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          */
   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              */
   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
   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 
   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 
   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          */
   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          */
   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          */
   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          */
   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 
   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          */
   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
   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          */
   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             */
   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
   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 
   1383     private void onAccessibilityButtonClicked() {
   1384         getAccessibilityButtonController().dispatchAccessibilityButtonClicked();
   1385     }
   1386 
   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      */
   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      */
   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      */
   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      */
   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      */
   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
   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
   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