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