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.app.Service;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.os.IBinder;
     23 import android.os.Looper;
     24 import android.os.Message;
     25 import android.os.RemoteException;
     26 import android.util.Log;
     27 import android.view.KeyEvent;
     28 import android.view.accessibility.AccessibilityEvent;
     29 import android.view.accessibility.AccessibilityInteractionClient;
     30 import android.view.accessibility.AccessibilityNodeInfo;
     31 
     32 import com.android.internal.os.HandlerCaller;
     33 
     34 /**
     35  * An accessibility service runs in the background and receives callbacks by the system
     36  * when {@link AccessibilityEvent}s are fired. Such events denote some state transition
     37  * in the user interface, for example, the focus has changed, a button has been clicked,
     38  * etc. Such a service can optionally request the capability for querying the content
     39  * of the active window. Development of an accessibility service requires extending this
     40  * class and implementing its abstract methods.
     41  *
     42  * <div class="special reference">
     43  * <h3>Developer Guides</h3>
     44  * <p>For more information about creating AccessibilityServices, read the
     45  * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
     46  * developer guide.</p>
     47  * </div>
     48  *
     49  * <h3>Lifecycle</h3>
     50  * <p>
     51  * The lifecycle of an accessibility service is managed exclusively by the system and
     52  * follows the established service life cycle. Additionally, starting or stopping an
     53  * accessibility service is triggered exclusively by an explicit user action through
     54  * enabling or disabling it in the device settings. After the system binds to a service it
     55  * calls {@link AccessibilityService#onServiceConnected()}. This method can be
     56  * overriden by clients that want to perform post binding setup.
     57  * </p>
     58  * <h3>Declaration</h3>
     59  * <p>
     60  * An accessibility is declared as any other service in an AndroidManifest.xml but it
     61  * must also specify that it handles the "android.accessibilityservice.AccessibilityService"
     62  * {@link android.content.Intent}. Failure to declare this intent will cause the system to
     63  * ignore the accessibility service. Additionally an accessibility service must request the
     64  * {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission to ensure
     65  * that only the system
     66  * can bind to it. Failure to declare this intent will cause the system to ignore the
     67  * accessibility service. Following is an example declaration:
     68  * </p>
     69  * <pre> &lt;service android:name=".MyAccessibilityService"
     70  *         android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"&gt;
     71  *     &lt;intent-filter&gt;
     72  *         &lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;
     73  *     &lt;/intent-filter&gt;
     74  *     . . .
     75  * &lt;/service&gt;</pre>
     76  * <h3>Configuration</h3>
     77  * <p>
     78  * An accessibility service can be configured to receive specific types of accessibility events,
     79  * listen only to specific packages, get events from each type only once in a given time frame,
     80  * retrieve window content, specify a settings activity, etc.
     81  * </p>
     82  * <p>
     83  * There are two approaches for configuring an accessibility service:
     84  * </p>
     85  * <ul>
     86  * <li>
     87  * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring
     88  * the service. A service declaration with a meta-data tag is presented below:
     89  * <pre> &lt;service android:name=".MyAccessibilityService"&gt;
     90  *     &lt;intent-filter&gt;
     91  *         &lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;
     92  *     &lt;/intent-filter&gt;
     93  *     &lt;meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" /&gt;
     94  * &lt;/service&gt;</pre>
     95  * <p class="note">
     96  * <strong>Note:</strong> This approach enables setting all properties.
     97  * </p>
     98  * <p>
     99  * For more details refer to {@link #SERVICE_META_DATA} and
    100  * <code>&lt;{@link android.R.styleable#AccessibilityService accessibility-service}&gt;</code>.
    101  * </p>
    102  * </li>
    103  * <li>
    104  * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note
    105  * that this method can be called any time to dynamically change the service configuration.
    106  * <p class="note">
    107  * <strong>Note:</strong> This approach enables setting only dynamically configurable properties:
    108  * {@link AccessibilityServiceInfo#eventTypes},
    109  * {@link AccessibilityServiceInfo#feedbackType},
    110  * {@link AccessibilityServiceInfo#flags},
    111  * {@link AccessibilityServiceInfo#notificationTimeout},
    112  * {@link AccessibilityServiceInfo#packageNames}
    113  * </p>
    114  * <p>
    115  * For more details refer to {@link AccessibilityServiceInfo}.
    116  * </p>
    117  * </li>
    118  * </ul>
    119  * <h3>Retrieving window content</h3>
    120  * <p>
    121  * A service can specify in its declaration that it can retrieve the active window
    122  * content which is represented as a tree of {@link AccessibilityNodeInfo}. Note that
    123  * declaring this capability requires that the service declares its configuration via
    124  * an XML resource referenced by {@link #SERVICE_META_DATA}.
    125  * </p>
    126  * <p>
    127  * For security purposes an accessibility service can retrieve only the content of the
    128  * currently active window. The currently active window is defined as the window from
    129  * which was fired the last event of the following types:
    130  * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED},
    131  * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
    132  * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT},
    133  * In other words, the last window that was shown or the last window that the user has touched
    134  * during touch exploration.
    135  * </p>
    136  * <p>
    137  * The entry point for retrieving window content is through calling
    138  * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()} of the last received
    139  * event of the above types or a previous event from the same window
    140  * (see {@link AccessibilityEvent#getWindowId() AccessibilityEvent.getWindowId()}). Invoking
    141  * this method will return an {@link AccessibilityNodeInfo} that can be used to traverse the
    142  * window content which represented as a tree of such objects.
    143  * </p>
    144  * <p class="note">
    145  * <strong>Note</strong> An accessibility service may have requested to be notified for
    146  * a subset of the event types, thus be unaware that the active window has changed. Therefore
    147  * accessibility service that would like to retrieve window content should:
    148  * <ul>
    149  * <li>
    150  * Register for all event types with no notification timeout and keep track for the active
    151  * window by calling {@link AccessibilityEvent#getWindowId()} of the last received event and
    152  * compare this with the {@link AccessibilityNodeInfo#getWindowId()} before calling retrieval
    153  * methods on the latter.
    154  * </li>
    155  * <li>
    156  * Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail since the
    157  * active window has changed and the service did not get the accessibility event yet. Note
    158  * that it is possible to have a retrieval method failing even adopting the strategy
    159  * specified in the previous bullet because the accessibility event dispatch is asynchronous
    160  * and crosses process boundaries.
    161  * </li>
    162  * </ul>
    163  * </p>
    164  * <h3>Notification strategy</h3>
    165  * <p>
    166  * For each feedback type only one accessibility service is notified. Services are notified
    167  * in the order of registration. Hence, if two services are registered for the same
    168  * feedback type in the same package the first one wins. It is possible however, to
    169  * register a service as the default one for a given feedback type. In such a case this
    170  * service is invoked if no other service was interested in the event. In other words, default
    171  * services do not compete with other services and are notified last regardless of the
    172  * registration order. This enables "generic" accessibility services that work reasonably
    173  * well with most applications to coexist with "polished" ones that are targeted for
    174  * specific applications.
    175  * </p>
    176  * <p class="note">
    177  * <strong>Note:</strong> The event notification timeout is useful to avoid propagating
    178  * events to the client too frequently since this is accomplished via an expensive
    179  * interprocess call. One can think of the timeout as a criteria to determine when
    180  * event generation has settled down.</p>
    181  * <h3>Event types</h3>
    182  * <ul>
    183  * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}
    184  * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}
    185  * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}
    186  * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}
    187  * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}
    188  * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}
    189  * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}
    190  * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}
    191  * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}
    192  * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}
    193  * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}
    194  * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}
    195  * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}
    196  * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}
    197  * </ul>
    198  * <h3>Feedback types</h3>
    199  * <ul>
    200  * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
    201  * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}
    202  * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
    203  * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}
    204  * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}
    205  * </ul>
    206  * @see AccessibilityEvent
    207  * @see AccessibilityServiceInfo
    208  * @see android.view.accessibility.AccessibilityManager
    209  */
    210 public abstract class AccessibilityService extends Service {
    211 
    212     /**
    213      * The user has performed a swipe up gesture on the touch screen.
    214      */
    215     public static final int GESTURE_SWIPE_UP = 1;
    216 
    217     /**
    218      * The user has performed a swipe down gesture on the touch screen.
    219      */
    220     public static final int GESTURE_SWIPE_DOWN = 2;
    221 
    222     /**
    223      * The user has performed a swipe left gesture on the touch screen.
    224      */
    225     public static final int GESTURE_SWIPE_LEFT = 3;
    226 
    227     /**
    228      * The user has performed a swipe right gesture on the touch screen.
    229      */
    230     public static final int GESTURE_SWIPE_RIGHT = 4;
    231 
    232     /**
    233      * The user has performed a swipe left and right gesture on the touch screen.
    234      */
    235     public static final int GESTURE_SWIPE_LEFT_AND_RIGHT = 5;
    236 
    237     /**
    238      * The user has performed a swipe right and left gesture on the touch screen.
    239      */
    240     public static final int GESTURE_SWIPE_RIGHT_AND_LEFT = 6;
    241 
    242     /**
    243      * The user has performed a swipe up and down gesture on the touch screen.
    244      */
    245     public static final int GESTURE_SWIPE_UP_AND_DOWN = 7;
    246 
    247     /**
    248      * The user has performed a swipe down and up gesture on the touch screen.
    249      */
    250     public static final int GESTURE_SWIPE_DOWN_AND_UP = 8;
    251 
    252     /**
    253      * The user has performed a left and up gesture on the touch screen.
    254      */
    255     public static final int GESTURE_SWIPE_LEFT_AND_UP = 9;
    256 
    257     /**
    258      * The user has performed a left and down gesture on the touch screen.
    259      */
    260     public static final int GESTURE_SWIPE_LEFT_AND_DOWN = 10;
    261 
    262     /**
    263      * The user has performed a right and up gesture on the touch screen.
    264      */
    265     public static final int GESTURE_SWIPE_RIGHT_AND_UP = 11;
    266 
    267     /**
    268      * The user has performed a right and down gesture on the touch screen.
    269      */
    270     public static final int GESTURE_SWIPE_RIGHT_AND_DOWN = 12;
    271 
    272     /**
    273      * The user has performed an up and left gesture on the touch screen.
    274      */
    275     public static final int GESTURE_SWIPE_UP_AND_LEFT = 13;
    276 
    277     /**
    278      * The user has performed an up and right gesture on the touch screen.
    279      */
    280     public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14;
    281 
    282     /**
    283      * The user has performed an down and left gesture on the touch screen.
    284      */
    285     public static final int GESTURE_SWIPE_DOWN_AND_LEFT = 15;
    286 
    287     /**
    288      * The user has performed an down and right gesture on the touch screen.
    289      */
    290     public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16;
    291 
    292     /**
    293      * The {@link Intent} that must be declared as handled by the service.
    294      */
    295     public static final String SERVICE_INTERFACE =
    296         "android.accessibilityservice.AccessibilityService";
    297 
    298     /**
    299      * Name under which an AccessibilityService component publishes information
    300      * about itself. This meta-data must reference an XML resource containing an
    301      * <code>&lt;{@link android.R.styleable#AccessibilityService accessibility-service}&gt;</code>
    302      * tag. This is a a sample XML file configuring an accessibility service:
    303      * <pre> &lt;accessibility-service
    304      *     android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
    305      *     android:packageNames="foo.bar, foo.baz"
    306      *     android:accessibilityFeedbackType="feedbackSpoken"
    307      *     android:notificationTimeout="100"
    308      *     android:accessibilityFlags="flagDefault"
    309      *     android:settingsActivity="foo.bar.TestBackActivity"
    310      *     android:canRetrieveWindowContent="true"
    311      *     android:canRequestTouchExplorationMode="true"
    312      *     android:canRequestEnhancedWebAccessibility="true"
    313      *     . . .
    314      * /&gt;</pre>
    315      */
    316     public static final String SERVICE_META_DATA = "android.accessibilityservice";
    317 
    318     /**
    319      * Action to go back.
    320      */
    321     public static final int GLOBAL_ACTION_BACK = 1;
    322 
    323     /**
    324      * Action to go home.
    325      */
    326     public static final int GLOBAL_ACTION_HOME = 2;
    327 
    328     /**
    329      * Action to open the recent apps.
    330      */
    331     public static final int GLOBAL_ACTION_RECENTS = 3;
    332 
    333     /**
    334      * Action to open the notifications.
    335      */
    336     public static final int GLOBAL_ACTION_NOTIFICATIONS = 4;
    337 
    338     /**
    339      * Action to open the quick settings.
    340      */
    341     public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5;
    342 
    343     private static final String LOG_TAG = "AccessibilityService";
    344 
    345     /**
    346      * @hide
    347      */
    348     public interface Callbacks {
    349         public void onAccessibilityEvent(AccessibilityEvent event);
    350         public void onInterrupt();
    351         public void onServiceConnected();
    352         public void onSetConnectionId(int connectionId);
    353         public boolean onGesture(int gestureId);
    354         public boolean onKeyEvent(KeyEvent event);
    355     }
    356 
    357     private int mConnectionId;
    358 
    359     private AccessibilityServiceInfo mInfo;
    360 
    361     /**
    362      * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
    363      *
    364      * @param event An event.
    365      */
    366     public abstract void onAccessibilityEvent(AccessibilityEvent event);
    367 
    368     /**
    369      * Callback for interrupting the accessibility feedback.
    370      */
    371     public abstract void onInterrupt();
    372 
    373     /**
    374      * This method is a part of the {@link AccessibilityService} lifecycle and is
    375      * called after the system has successfully bound to the service. If is
    376      * convenient to use this method for setting the {@link AccessibilityServiceInfo}.
    377      *
    378      * @see AccessibilityServiceInfo
    379      * @see #setServiceInfo(AccessibilityServiceInfo)
    380      */
    381     protected void onServiceConnected() {
    382 
    383     }
    384 
    385     /**
    386      * Called by the system when the user performs a specific gesture on the
    387      * touch screen.
    388      *
    389      * <strong>Note:</strong> To receive gestures an accessibility service must
    390      * request that the device is in touch exploration mode by setting the
    391      * {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE}
    392      * flag.
    393      *
    394      * @param gestureId The unique id of the performed gesture.
    395      *
    396      * @return Whether the gesture was handled.
    397      *
    398      * @see #GESTURE_SWIPE_UP
    399      * @see #GESTURE_SWIPE_UP_AND_LEFT
    400      * @see #GESTURE_SWIPE_UP_AND_DOWN
    401      * @see #GESTURE_SWIPE_UP_AND_RIGHT
    402      * @see #GESTURE_SWIPE_DOWN
    403      * @see #GESTURE_SWIPE_DOWN_AND_LEFT
    404      * @see #GESTURE_SWIPE_DOWN_AND_UP
    405      * @see #GESTURE_SWIPE_DOWN_AND_RIGHT
    406      * @see #GESTURE_SWIPE_LEFT
    407      * @see #GESTURE_SWIPE_LEFT_AND_UP
    408      * @see #GESTURE_SWIPE_LEFT_AND_RIGHT
    409      * @see #GESTURE_SWIPE_LEFT_AND_DOWN
    410      * @see #GESTURE_SWIPE_RIGHT
    411      * @see #GESTURE_SWIPE_RIGHT_AND_UP
    412      * @see #GESTURE_SWIPE_RIGHT_AND_LEFT
    413      * @see #GESTURE_SWIPE_RIGHT_AND_DOWN
    414      */
    415     protected boolean onGesture(int gestureId) {
    416         return false;
    417     }
    418 
    419     /**
    420      * Callback that allows an accessibility service to observe the key events
    421      * before they are passed to the rest of the system. This means that the events
    422      * are first delivered here before they are passed to the device policy, the
    423      * input method, or applications.
    424      * <p>
    425      * <strong>Note:</strong> It is important that key events are handled in such
    426      * a way that the event stream that would be passed to the rest of the system
    427      * is well-formed. For example, handling the down event but not the up event
    428      * and vice versa would generate an inconsistent event stream.
    429      * </p>
    430      * <p>
    431      * <strong>Note:</strong> The key events delivered in this method are copies
    432      * and modifying them will have no effect on the events that will be passed
    433      * to the system. This method is intended to perform purely filtering
    434      * functionality.
    435      * <p>
    436      *
    437      * @param event The event to be processed.
    438      * @return If true then the event will be consumed and not delivered to
    439      *         applications, otherwise it will be delivered as usual.
    440      */
    441     protected boolean onKeyEvent(KeyEvent event) {
    442         return false;
    443     }
    444 
    445     /**
    446      * Gets the root node in the currently active window if this service
    447      * can retrieve window content.
    448      *
    449      * @return The root node if this service can retrieve window content.
    450      */
    451     public AccessibilityNodeInfo getRootInActiveWindow() {
    452         return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId);
    453     }
    454 
    455     /**
    456      * Performs a global action. Such an action can be performed
    457      * at any moment regardless of the current application or user
    458      * location in that application. For example going back, going
    459      * home, opening recents, etc.
    460      *
    461      * @param action The action to perform.
    462      * @return Whether the action was successfully performed.
    463      *
    464      * @see #GLOBAL_ACTION_BACK
    465      * @see #GLOBAL_ACTION_HOME
    466      * @see #GLOBAL_ACTION_NOTIFICATIONS
    467      * @see #GLOBAL_ACTION_RECENTS
    468      */
    469     public final boolean performGlobalAction(int action) {
    470         IAccessibilityServiceConnection connection =
    471             AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
    472         if (connection != null) {
    473             try {
    474                 return connection.performGlobalAction(action);
    475             } catch (RemoteException re) {
    476                 Log.w(LOG_TAG, "Error while calling performGlobalAction", re);
    477             }
    478         }
    479         return false;
    480     }
    481 
    482     /**
    483      * Gets the an {@link AccessibilityServiceInfo} describing this
    484      * {@link AccessibilityService}. This method is useful if one wants
    485      * to change some of the dynamically configurable properties at
    486      * runtime.
    487      *
    488      * @return The accessibility service info.
    489      *
    490      * @see AccessibilityServiceInfo
    491      */
    492     public final AccessibilityServiceInfo getServiceInfo() {
    493         IAccessibilityServiceConnection connection =
    494             AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
    495         if (connection != null) {
    496             try {
    497                 return connection.getServiceInfo();
    498             } catch (RemoteException re) {
    499                 Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re);
    500             }
    501         }
    502         return null;
    503     }
    504 
    505     /**
    506      * Sets the {@link AccessibilityServiceInfo} that describes this service.
    507      * <p>
    508      * Note: You can call this method any time but the info will be picked up after
    509      *       the system has bound to this service and when this method is called thereafter.
    510      *
    511      * @param info The info.
    512      */
    513     public final void setServiceInfo(AccessibilityServiceInfo info) {
    514         mInfo = info;
    515         sendServiceInfo();
    516     }
    517 
    518     /**
    519      * Sets the {@link AccessibilityServiceInfo} for this service if the latter is
    520      * properly set and there is an {@link IAccessibilityServiceConnection} to the
    521      * AccessibilityManagerService.
    522      */
    523     private void sendServiceInfo() {
    524         IAccessibilityServiceConnection connection =
    525             AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
    526         if (mInfo != null && connection != null) {
    527             try {
    528                 connection.setServiceInfo(mInfo);
    529                 mInfo = null;
    530                 AccessibilityInteractionClient.getInstance().clearCache();
    531             } catch (RemoteException re) {
    532                 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
    533             }
    534         }
    535     }
    536 
    537     /**
    538      * Implement to return the implementation of the internal accessibility
    539      * service interface.
    540      */
    541     @Override
    542     public final IBinder onBind(Intent intent) {
    543         return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {
    544             @Override
    545             public void onServiceConnected() {
    546                 AccessibilityService.this.onServiceConnected();
    547             }
    548 
    549             @Override
    550             public void onInterrupt() {
    551                 AccessibilityService.this.onInterrupt();
    552             }
    553 
    554             @Override
    555             public void onAccessibilityEvent(AccessibilityEvent event) {
    556                 AccessibilityService.this.onAccessibilityEvent(event);
    557             }
    558 
    559             @Override
    560             public void onSetConnectionId( int connectionId) {
    561                 mConnectionId = connectionId;
    562             }
    563 
    564             @Override
    565             public boolean onGesture(int gestureId) {
    566                 return AccessibilityService.this.onGesture(gestureId);
    567             }
    568 
    569             @Override
    570             public boolean onKeyEvent(KeyEvent event) {
    571                 return AccessibilityService.this.onKeyEvent(event);
    572             }
    573         });
    574     }
    575 
    576     /**
    577      * Implements the internal {@link IAccessibilityServiceClient} interface to convert
    578      * incoming calls to it back to calls on an {@link AccessibilityService}.
    579      *
    580      * @hide
    581      */
    582     public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
    583             implements HandlerCaller.Callback {
    584 
    585         static final int NO_ID = -1;
    586 
    587         private static final int DO_SET_SET_CONNECTION = 10;
    588         private static final int DO_ON_INTERRUPT = 20;
    589         private static final int DO_ON_ACCESSIBILITY_EVENT = 30;
    590         private static final int DO_ON_GESTURE = 40;
    591         private static final int DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE = 50;
    592         private static final int DO_ON_KEY_EVENT = 60;
    593 
    594         private final HandlerCaller mCaller;
    595 
    596         private final Callbacks mCallback;
    597 
    598         private int mConnectionId;
    599 
    600         public IAccessibilityServiceClientWrapper(Context context, Looper looper,
    601                 Callbacks callback) {
    602             mCallback = callback;
    603             mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
    604         }
    605 
    606         public void setConnection(IAccessibilityServiceConnection connection, int connectionId) {
    607             Message message = mCaller.obtainMessageIO(DO_SET_SET_CONNECTION, connectionId,
    608                     connection);
    609             mCaller.sendMessage(message);
    610         }
    611 
    612         public void onInterrupt() {
    613             Message message = mCaller.obtainMessage(DO_ON_INTERRUPT);
    614             mCaller.sendMessage(message);
    615         }
    616 
    617         public void onAccessibilityEvent(AccessibilityEvent event) {
    618             Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event);
    619             mCaller.sendMessage(message);
    620         }
    621 
    622         public void onGesture(int gestureId) {
    623             Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId);
    624             mCaller.sendMessage(message);
    625         }
    626 
    627         public void clearAccessibilityNodeInfoCache() {
    628             Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE);
    629             mCaller.sendMessage(message);
    630         }
    631 
    632         @Override
    633         public void onKeyEvent(KeyEvent event, int sequence) {
    634             Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event);
    635             mCaller.sendMessage(message);
    636         }
    637 
    638         public void executeMessage(Message message) {
    639             switch (message.what) {
    640                 case DO_ON_ACCESSIBILITY_EVENT: {
    641                     AccessibilityEvent event = (AccessibilityEvent) message.obj;
    642                     if (event != null) {
    643                         AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);
    644                         mCallback.onAccessibilityEvent(event);
    645                         event.recycle();
    646                     }
    647                 } return;
    648                 case DO_ON_INTERRUPT: {
    649                     mCallback.onInterrupt();
    650                 } return;
    651                 case DO_SET_SET_CONNECTION: {
    652                     mConnectionId = message.arg1;
    653                     IAccessibilityServiceConnection connection =
    654                         (IAccessibilityServiceConnection) message.obj;
    655                     if (connection != null) {
    656                         AccessibilityInteractionClient.getInstance().addConnection(mConnectionId,
    657                                 connection);
    658                         mCallback.onSetConnectionId(mConnectionId);
    659                         mCallback.onServiceConnected();
    660                     } else {
    661                         AccessibilityInteractionClient.getInstance().removeConnection(mConnectionId);
    662                         AccessibilityInteractionClient.getInstance().clearCache();
    663                         mCallback.onSetConnectionId(AccessibilityInteractionClient.NO_ID);
    664                     }
    665                 } return;
    666                 case DO_ON_GESTURE: {
    667                     final int gestureId = message.arg1;
    668                     mCallback.onGesture(gestureId);
    669                 } return;
    670                 case DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE: {
    671                     AccessibilityInteractionClient.getInstance().clearCache();
    672                 } return;
    673                 case DO_ON_KEY_EVENT: {
    674                     KeyEvent event = (KeyEvent) message.obj;
    675                     try {
    676                         IAccessibilityServiceConnection connection = AccessibilityInteractionClient
    677                                 .getInstance().getConnection(mConnectionId);
    678                         if (connection != null) {
    679                             final boolean result = mCallback.onKeyEvent(event);
    680                             final int sequence = message.arg1;
    681                             try {
    682                                 connection.setOnKeyEventResult(result, sequence);
    683                             } catch (RemoteException re) {
    684                                 /* ignore */
    685                             }
    686                         }
    687                     } finally {
    688                         event.recycle();
    689                     }
    690                 } return;
    691                 default :
    692                     Log.w(LOG_TAG, "Unknown message type " + message.what);
    693             }
    694         }
    695     }
    696 }
    697