AccessibilityService.java revision 688a6977cf7673ed0542ab3d839053c4e38a4dbd
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
17package android.accessibilityservice;
18
19import android.app.Service;
20import android.content.Context;
21import android.content.Intent;
22import android.os.IBinder;
23import android.os.Looper;
24import android.os.Message;
25import android.os.RemoteException;
26import android.util.Log;
27import android.view.KeyEvent;
28import android.view.accessibility.AccessibilityEvent;
29import android.view.accessibility.AccessibilityInteractionClient;
30import android.view.accessibility.AccessibilityNodeInfo;
31
32import 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 */
210public 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