• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.accessibilityservice;
18 
19 import android.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 {
onAccessibilityEvent(AccessibilityEvent event)349         public void onAccessibilityEvent(AccessibilityEvent event);
onInterrupt()350         public void onInterrupt();
onServiceConnected()351         public void onServiceConnected();
onSetConnectionId(int connectionId)352         public void onSetConnectionId(int connectionId);
onGesture(int gestureId)353         public boolean onGesture(int gestureId);
onKeyEvent(KeyEvent event)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      */
onAccessibilityEvent(AccessibilityEvent event)366     public abstract void onAccessibilityEvent(AccessibilityEvent event);
367 
368     /**
369      * Callback for interrupting the accessibility feedback.
370      */
onInterrupt()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      */
onServiceConnected()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      */
onGesture(int gestureId)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      */
onKeyEvent(KeyEvent event)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      */
getRootInActiveWindow()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      */
performGlobalAction(int action)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      */
getServiceInfo()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      */
setServiceInfo(AccessibilityServiceInfo info)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      */
sendServiceInfo()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
onBind(Intent intent)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