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