AccessibilityService.java revision a6b64f5099b7be6e8384958d8bcddb97bb06ec93
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.accessibilityservice.GestureDescription.MotionEventGenerator;
20import android.annotation.NonNull;
21import android.annotation.Nullable;
22import android.app.Service;
23import android.content.Context;
24import android.content.Intent;
25import android.content.pm.ParceledListSlice;
26import android.graphics.Region;
27import android.os.Handler;
28import android.os.IBinder;
29import android.os.Looper;
30import android.os.Message;
31import android.os.RemoteException;
32import android.util.ArrayMap;
33import android.util.Log;
34import android.util.Pair;
35import android.util.Slog;
36import android.util.SparseArray;
37import android.view.KeyEvent;
38import android.view.MotionEvent;
39import android.view.WindowManager;
40import android.view.WindowManagerImpl;
41import android.view.accessibility.AccessibilityEvent;
42import android.view.accessibility.AccessibilityInteractionClient;
43import android.view.accessibility.AccessibilityNodeInfo;
44import android.view.accessibility.AccessibilityWindowInfo;
45
46import com.android.internal.os.HandlerCaller;
47import com.android.internal.os.SomeArgs;
48
49import java.util.List;
50
51/**
52 * An accessibility service runs in the background and receives callbacks by the system
53 * when {@link AccessibilityEvent}s are fired. Such events denote some state transition
54 * in the user interface, for example, the focus has changed, a button has been clicked,
55 * etc. Such a service can optionally request the capability for querying the content
56 * of the active window. Development of an accessibility service requires extending this
57 * class and implementing its abstract methods.
58 *
59 * <div class="special reference">
60 * <h3>Developer Guides</h3>
61 * <p>For more information about creating AccessibilityServices, read the
62 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
63 * developer guide.</p>
64 * </div>
65 *
66 * <h3>Lifecycle</h3>
67 * <p>
68 * The lifecycle of an accessibility service is managed exclusively by the system and
69 * follows the established service life cycle. Additionally, starting or stopping an
70 * accessibility service is triggered exclusively by an explicit user action through
71 * enabling or disabling it in the device settings. After the system binds to a service it
72 * calls {@link AccessibilityService#onServiceConnected()}. This method can be
73 * overriden by clients that want to perform post binding setup.
74 * </p>
75 * <h3>Declaration</h3>
76 * <p>
77 * An accessibility is declared as any other service in an AndroidManifest.xml but it
78 * must also specify that it handles the "android.accessibilityservice.AccessibilityService"
79 * {@link android.content.Intent}. Failure to declare this intent will cause the system to
80 * ignore the accessibility service. Additionally an accessibility service must request the
81 * {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission to ensure
82 * that only the system
83 * can bind to it. Failure to declare this intent will cause the system to ignore the
84 * accessibility service. Following is an example declaration:
85 * </p>
86 * <pre> &lt;service android:name=".MyAccessibilityService"
87 *         android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"&gt;
88 *     &lt;intent-filter&gt;
89 *         &lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;
90 *     &lt;/intent-filter&gt;
91 *     . . .
92 * &lt;/service&gt;</pre>
93 * <h3>Configuration</h3>
94 * <p>
95 * An accessibility service can be configured to receive specific types of accessibility events,
96 * listen only to specific packages, get events from each type only once in a given time frame,
97 * retrieve window content, specify a settings activity, etc.
98 * </p>
99 * <p>
100 * There are two approaches for configuring an accessibility service:
101 * </p>
102 * <ul>
103 * <li>
104 * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring
105 * the service. A service declaration with a meta-data tag is presented below:
106 * <pre> &lt;service android:name=".MyAccessibilityService"&gt;
107 *     &lt;intent-filter&gt;
108 *         &lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;
109 *     &lt;/intent-filter&gt;
110 *     &lt;meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" /&gt;
111 * &lt;/service&gt;</pre>
112 * <p class="note">
113 * <strong>Note:</strong> This approach enables setting all properties.
114 * </p>
115 * <p>
116 * For more details refer to {@link #SERVICE_META_DATA} and
117 * <code>&lt;{@link android.R.styleable#AccessibilityService accessibility-service}&gt;</code>.
118 * </p>
119 * </li>
120 * <li>
121 * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note
122 * that this method can be called any time to dynamically change the service configuration.
123 * <p class="note">
124 * <strong>Note:</strong> This approach enables setting only dynamically configurable properties:
125 * {@link AccessibilityServiceInfo#eventTypes},
126 * {@link AccessibilityServiceInfo#feedbackType},
127 * {@link AccessibilityServiceInfo#flags},
128 * {@link AccessibilityServiceInfo#notificationTimeout},
129 * {@link AccessibilityServiceInfo#packageNames}
130 * </p>
131 * <p>
132 * For more details refer to {@link AccessibilityServiceInfo}.
133 * </p>
134 * </li>
135 * </ul>
136 * <h3>Retrieving window content</h3>
137 * <p>
138 * A service can specify in its declaration that it can retrieve the active window
139 * content which is represented as a tree of {@link AccessibilityNodeInfo}. Note that
140 * declaring this capability requires that the service declares its configuration via
141 * an XML resource referenced by {@link #SERVICE_META_DATA}.
142 * </p>
143 * <p>
144 * For security purposes an accessibility service can retrieve only the content of the
145 * currently active window. The currently active window is defined as the window from
146 * which was fired the last event of the following types:
147 * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED},
148 * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
149 * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT},
150 * In other words, the last window that was shown or the last window that the user has touched
151 * during touch exploration.
152 * </p>
153 * <p>
154 * The entry point for retrieving window content is through calling
155 * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()} of the last received
156 * event of the above types or a previous event from the same window
157 * (see {@link AccessibilityEvent#getWindowId() AccessibilityEvent.getWindowId()}). Invoking
158 * this method will return an {@link AccessibilityNodeInfo} that can be used to traverse the
159 * window content which represented as a tree of such objects.
160 * </p>
161 * <p class="note">
162 * <strong>Note</strong> An accessibility service may have requested to be notified for
163 * a subset of the event types, thus be unaware that the active window has changed. Therefore
164 * accessibility service that would like to retrieve window content should:
165 * <ul>
166 * <li>
167 * Register for all event types with no notification timeout and keep track for the active
168 * window by calling {@link AccessibilityEvent#getWindowId()} of the last received event and
169 * compare this with the {@link AccessibilityNodeInfo#getWindowId()} before calling retrieval
170 * methods on the latter.
171 * </li>
172 * <li>
173 * Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail since the
174 * active window has changed and the service did not get the accessibility event yet. Note
175 * that it is possible to have a retrieval method failing even adopting the strategy
176 * specified in the previous bullet because the accessibility event dispatch is asynchronous
177 * and crosses process boundaries.
178 * </li>
179 * </ul>
180 * </p>
181 * <h3>Notification strategy</h3>
182 * <p>
183 * All accessibility services are notified of all events they have requested, regardless of their
184 * feedback type.
185 * </p>
186 * <p class="note">
187 * <strong>Note:</strong> The event notification timeout is useful to avoid propagating
188 * events to the client too frequently since this is accomplished via an expensive
189 * interprocess call. One can think of the timeout as a criteria to determine when
190 * event generation has settled down.</p>
191 * <h3>Event types</h3>
192 * <ul>
193 * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}</li>
194 * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}</li>
195 * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}</li>
196 * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}</li>
197 * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}</li>
198 * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}</li>
199 * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}</li>
200 * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}</li>
201 * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}</li>
202 * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}</li>
203 * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}</li>
204 * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}</li>
205 * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}</li>
206 * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}</li>
207 * <li>{@link AccessibilityEvent#TYPE_ANNOUNCEMENT}</li>
208 * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_START}</li>
209 * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_END}</li>
210 * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_START}</li>
211 * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_END}</li>
212 * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED}</li>
213 * <li>{@link AccessibilityEvent#TYPE_WINDOWS_CHANGED}</li>
214 * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED}</li>
215 * </ul>
216 * <h3>Feedback types</h3>
217 * <ul>
218 * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li>
219 * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}</li>
220 * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li>
221 * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}</li>
222 * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}</li>
223 * <li>{@link AccessibilityServiceInfo#FEEDBACK_BRAILLE}</li>
224 * </ul>
225 * @see AccessibilityEvent
226 * @see AccessibilityServiceInfo
227 * @see android.view.accessibility.AccessibilityManager
228 */
229public abstract class AccessibilityService extends Service {
230
231    /**
232     * The user has performed a swipe up gesture on the touch screen.
233     */
234    public static final int GESTURE_SWIPE_UP = 1;
235
236    /**
237     * The user has performed a swipe down gesture on the touch screen.
238     */
239    public static final int GESTURE_SWIPE_DOWN = 2;
240
241    /**
242     * The user has performed a swipe left gesture on the touch screen.
243     */
244    public static final int GESTURE_SWIPE_LEFT = 3;
245
246    /**
247     * The user has performed a swipe right gesture on the touch screen.
248     */
249    public static final int GESTURE_SWIPE_RIGHT = 4;
250
251    /**
252     * The user has performed a swipe left and right gesture on the touch screen.
253     */
254    public static final int GESTURE_SWIPE_LEFT_AND_RIGHT = 5;
255
256    /**
257     * The user has performed a swipe right and left gesture on the touch screen.
258     */
259    public static final int GESTURE_SWIPE_RIGHT_AND_LEFT = 6;
260
261    /**
262     * The user has performed a swipe up and down gesture on the touch screen.
263     */
264    public static final int GESTURE_SWIPE_UP_AND_DOWN = 7;
265
266    /**
267     * The user has performed a swipe down and up gesture on the touch screen.
268     */
269    public static final int GESTURE_SWIPE_DOWN_AND_UP = 8;
270
271    /**
272     * The user has performed a left and up gesture on the touch screen.
273     */
274    public static final int GESTURE_SWIPE_LEFT_AND_UP = 9;
275
276    /**
277     * The user has performed a left and down gesture on the touch screen.
278     */
279    public static final int GESTURE_SWIPE_LEFT_AND_DOWN = 10;
280
281    /**
282     * The user has performed a right and up gesture on the touch screen.
283     */
284    public static final int GESTURE_SWIPE_RIGHT_AND_UP = 11;
285
286    /**
287     * The user has performed a right and down gesture on the touch screen.
288     */
289    public static final int GESTURE_SWIPE_RIGHT_AND_DOWN = 12;
290
291    /**
292     * The user has performed an up and left gesture on the touch screen.
293     */
294    public static final int GESTURE_SWIPE_UP_AND_LEFT = 13;
295
296    /**
297     * The user has performed an up and right gesture on the touch screen.
298     */
299    public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14;
300
301    /**
302     * The user has performed an down and left gesture on the touch screen.
303     */
304    public static final int GESTURE_SWIPE_DOWN_AND_LEFT = 15;
305
306    /**
307     * The user has performed an down and right gesture on the touch screen.
308     */
309    public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16;
310
311    /**
312     * The {@link Intent} that must be declared as handled by the service.
313     */
314    public static final String SERVICE_INTERFACE =
315        "android.accessibilityservice.AccessibilityService";
316
317    /**
318     * Name under which an AccessibilityService component publishes information
319     * about itself. This meta-data must reference an XML resource containing an
320     * <code>&lt;{@link android.R.styleable#AccessibilityService accessibility-service}&gt;</code>
321     * tag. This is a a sample XML file configuring an accessibility service:
322     * <pre> &lt;accessibility-service
323     *     android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
324     *     android:packageNames="foo.bar, foo.baz"
325     *     android:accessibilityFeedbackType="feedbackSpoken"
326     *     android:notificationTimeout="100"
327     *     android:accessibilityFlags="flagDefault"
328     *     android:settingsActivity="foo.bar.TestBackActivity"
329     *     android:canRetrieveWindowContent="true"
330     *     android:canRequestTouchExplorationMode="true"
331     *     android:canRequestEnhancedWebAccessibility="true"
332     *     . . .
333     * /&gt;</pre>
334     */
335    public static final String SERVICE_META_DATA = "android.accessibilityservice";
336
337    /**
338     * Action to go back.
339     */
340    public static final int GLOBAL_ACTION_BACK = 1;
341
342    /**
343     * Action to go home.
344     */
345    public static final int GLOBAL_ACTION_HOME = 2;
346
347    /**
348     * Action to open the recent apps.
349     */
350    public static final int GLOBAL_ACTION_RECENTS = 3;
351
352    /**
353     * Action to open the notifications.
354     */
355    public static final int GLOBAL_ACTION_NOTIFICATIONS = 4;
356
357    /**
358     * Action to open the quick settings.
359     */
360    public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5;
361
362    /**
363     * Action to open the power long-press dialog.
364     */
365    public static final int GLOBAL_ACTION_POWER_DIALOG = 6;
366
367    private static final String LOG_TAG = "AccessibilityService";
368
369    /**
370     * @hide
371     */
372    public interface Callbacks {
373        public void onAccessibilityEvent(AccessibilityEvent event);
374        public void onInterrupt();
375        public void onServiceConnected();
376        public void init(int connectionId, IBinder windowToken);
377        public boolean onGesture(int gestureId);
378        public boolean onKeyEvent(KeyEvent event);
379        public void onMagnificationChanged(@NonNull Region region,
380                float scale, float centerX, float centerY);
381        public void onPerformGestureResult(int sequence, boolean completedSuccessfully);
382    }
383
384    private int mConnectionId;
385
386    private AccessibilityServiceInfo mInfo;
387
388    private IBinder mWindowToken;
389
390    private WindowManager mWindowManager;
391
392    private MagnificationController mMagnificationController;
393
394    private int mGestureStatusCallbackSequence;
395
396    private SparseArray<GestureResultCallbackInfo> mGestureStatusCallbackInfos;
397
398    private final Object mLock = new Object();
399
400    /**
401     * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
402     *
403     * @param event An event.
404     */
405    public abstract void onAccessibilityEvent(AccessibilityEvent event);
406
407    /**
408     * Callback for interrupting the accessibility feedback.
409     */
410    public abstract void onInterrupt();
411
412    /**
413     * Dispatches service connection to internal components first, then the
414     * client code.
415     */
416    private void dispatchServiceConnected() {
417        if (mMagnificationController != null) {
418            mMagnificationController.onServiceConnected();
419        }
420
421        // The client gets to handle service connection last, after we've set
422        // up any state upon which their code may rely.
423        onServiceConnected();
424    }
425
426    /**
427     * This method is a part of the {@link AccessibilityService} lifecycle and is
428     * called after the system has successfully bound to the service. If is
429     * convenient to use this method for setting the {@link AccessibilityServiceInfo}.
430     *
431     * @see AccessibilityServiceInfo
432     * @see #setServiceInfo(AccessibilityServiceInfo)
433     */
434    protected void onServiceConnected() {
435
436    }
437
438    /**
439     * Called by the system when the user performs a specific gesture on the
440     * touch screen.
441     *
442     * <strong>Note:</strong> To receive gestures an accessibility service must
443     * request that the device is in touch exploration mode by setting the
444     * {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE}
445     * flag.
446     *
447     * @param gestureId The unique id of the performed gesture.
448     *
449     * @return Whether the gesture was handled.
450     *
451     * @see #GESTURE_SWIPE_UP
452     * @see #GESTURE_SWIPE_UP_AND_LEFT
453     * @see #GESTURE_SWIPE_UP_AND_DOWN
454     * @see #GESTURE_SWIPE_UP_AND_RIGHT
455     * @see #GESTURE_SWIPE_DOWN
456     * @see #GESTURE_SWIPE_DOWN_AND_LEFT
457     * @see #GESTURE_SWIPE_DOWN_AND_UP
458     * @see #GESTURE_SWIPE_DOWN_AND_RIGHT
459     * @see #GESTURE_SWIPE_LEFT
460     * @see #GESTURE_SWIPE_LEFT_AND_UP
461     * @see #GESTURE_SWIPE_LEFT_AND_RIGHT
462     * @see #GESTURE_SWIPE_LEFT_AND_DOWN
463     * @see #GESTURE_SWIPE_RIGHT
464     * @see #GESTURE_SWIPE_RIGHT_AND_UP
465     * @see #GESTURE_SWIPE_RIGHT_AND_LEFT
466     * @see #GESTURE_SWIPE_RIGHT_AND_DOWN
467     */
468    protected boolean onGesture(int gestureId) {
469        return false;
470    }
471
472    /**
473     * Callback that allows an accessibility service to observe the key events
474     * before they are passed to the rest of the system. This means that the events
475     * are first delivered here before they are passed to the device policy, the
476     * input method, or applications.
477     * <p>
478     * <strong>Note:</strong> It is important that key events are handled in such
479     * a way that the event stream that would be passed to the rest of the system
480     * is well-formed. For example, handling the down event but not the up event
481     * and vice versa would generate an inconsistent event stream.
482     * </p>
483     * <p>
484     * <strong>Note:</strong> The key events delivered in this method are copies
485     * and modifying them will have no effect on the events that will be passed
486     * to the system. This method is intended to perform purely filtering
487     * functionality.
488     * <p>
489     *
490     * @param event The event to be processed.
491     * @return If true then the event will be consumed and not delivered to
492     *         applications, otherwise it will be delivered as usual.
493     */
494    protected boolean onKeyEvent(KeyEvent event) {
495        return false;
496    }
497
498    /**
499     * Gets the windows on the screen. This method returns only the windows
500     * that a sighted user can interact with, as opposed to all windows.
501     * For example, if there is a modal dialog shown and the user cannot touch
502     * anything behind it, then only the modal window will be reported
503     * (assuming it is the top one). For convenience the returned windows
504     * are ordered in a descending layer order, which is the windows that
505     * are higher in the Z-order are reported first. Since the user can always
506     * interact with the window that has input focus by typing, the focused
507     * window is always returned (even if covered by a modal window).
508     * <p>
509     * <strong>Note:</strong> In order to access the windows your service has
510     * to declare the capability to retrieve window content by setting the
511     * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
512     * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
513     * Also the service has to opt-in to retrieve the interactive windows by
514     * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
515     * flag.
516     * </p>
517     *
518     * @return The windows if there are windows and the service is can retrieve
519     *         them, otherwise an empty list.
520     */
521    public List<AccessibilityWindowInfo> getWindows() {
522        return AccessibilityInteractionClient.getInstance().getWindows(mConnectionId);
523    }
524
525    /**
526     * Gets the root node in the currently active window if this service
527     * can retrieve window content. The active window is the one that the user
528     * is currently touching or the window with input focus, if the user is not
529     * touching any window.
530     * <p>
531     * <strong>Note:</strong> In order to access the root node your service has
532     * to declare the capability to retrieve window content by setting the
533     * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
534     * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
535     * </p>
536     *
537     * @return The root node if this service can retrieve window content.
538     */
539    public AccessibilityNodeInfo getRootInActiveWindow() {
540        return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId);
541    }
542
543    /**
544     * Returns the magnification controller, which may be used to query and
545     * modify the state of display magnification.
546     * <p>
547     * <strong>Note:</strong> In order to control magnification, your service
548     * must declare the capability by setting the
549     * {@link android.R.styleable#AccessibilityService_canControlMagnification}
550     * property in its meta-data. For more information, see
551     * {@link #SERVICE_META_DATA}.
552     *
553     * @return the magnification controller
554     */
555    @NonNull
556    public final MagnificationController getMagnificationController() {
557        if (mMagnificationController == null) {
558            mMagnificationController = new MagnificationController(this);
559        }
560        return mMagnificationController;
561    }
562
563    /**
564     * Dispatch a gesture to the touch screen. Any gestures currently in progress, whether from
565     * the user, this service, or another service, will be cancelled.
566     * <p>
567     * <strong>Note:</strong> In order to dispatch gestures, your service
568     * must declare the capability by setting the
569     * {@link android.R.styleable#AccessibilityService_canPerformGestures}
570     * property in its meta-data. For more information, see
571     * {@link #SERVICE_META_DATA}.
572     *
573     * @param gesture The gesture to dispatch
574     * @param callback The object to call back when the status of the gesture is known. If
575     * {@code null}, no status is reported.
576     * @param handler The handler on which to call back the {@code callback} object. If
577     * {@code null}, the object is called back on the service's main thread.
578     *
579     * @return {@code true} if the gesture is dispatched, {@code false} if not.
580     */
581    public final boolean dispatchGesture(@NonNull GestureDescription gesture,
582            @Nullable GestureResultCallback callback,
583            @Nullable Handler handler) {
584        final IAccessibilityServiceConnection connection =
585                AccessibilityInteractionClient.getInstance().getConnection(
586                        mConnectionId);
587        if (connection == null) {
588            return false;
589        }
590        List<MotionEvent> events = MotionEventGenerator.getMotionEventsFromGestureDescription(
591                gesture, 100);
592        try {
593            synchronized (mLock) {
594                connection.sendMotionEvents(++mGestureStatusCallbackSequence,
595                        new ParceledListSlice<>(events));
596                if (callback != null) {
597                    if (mGestureStatusCallbackInfos == null) {
598                        mGestureStatusCallbackInfos = new SparseArray<>();
599                    }
600                    GestureResultCallbackInfo callbackInfo = new GestureResultCallbackInfo(gesture,
601                            callback, handler);
602                    mGestureStatusCallbackInfos.put(mGestureStatusCallbackSequence, callbackInfo);
603                }
604            }
605        } catch (RemoteException re) {
606            throw new RuntimeException(re);
607        }
608        return true;
609    }
610
611    void onPerformGestureResult(int sequence, final boolean completedSuccessfully) {
612        if (mGestureStatusCallbackInfos == null) {
613            return;
614        }
615        GestureResultCallbackInfo callbackInfo;
616        synchronized (mLock) {
617            callbackInfo = mGestureStatusCallbackInfos.get(sequence);
618        }
619        final GestureResultCallbackInfo finalCallbackInfo = callbackInfo;
620        if ((callbackInfo != null) && (callbackInfo.gestureDescription != null)
621                && (callbackInfo.callback != null)) {
622            if (callbackInfo.handler != null) {
623                callbackInfo.handler.post(new Runnable() {
624                    @Override
625                    public void run() {
626                        if (completedSuccessfully) {
627                            finalCallbackInfo.callback
628                                    .onCompleted(finalCallbackInfo.gestureDescription);
629                        } else {
630                            finalCallbackInfo.callback
631                                    .onCancelled(finalCallbackInfo.gestureDescription);
632                        }
633                    }
634                });
635                return;
636            }
637            if (completedSuccessfully) {
638                callbackInfo.callback.onCompleted(callbackInfo.gestureDescription);
639            } else {
640                callbackInfo.callback.onCancelled(callbackInfo.gestureDescription);
641            }
642        }
643    }
644
645    private void onMagnificationChanged(@NonNull Region region, float scale,
646            float centerX, float centerY) {
647        if (mMagnificationController != null) {
648            mMagnificationController.dispatchMagnificationChanged(
649                    region, scale, centerX, centerY);
650        }
651    }
652
653    /**
654     * Used to control and query the state of display magnification.
655     */
656    public static final class MagnificationController {
657        private final AccessibilityService mService;
658
659        /**
660         * Map of listeners to their handlers. Lazily created when adding the
661         * first magnification listener.
662         */
663        private ArrayMap<OnMagnificationChangedListener, Handler> mListeners;
664
665        MagnificationController(@NonNull AccessibilityService service) {
666            mService = service;
667        }
668
669        /**
670         * Called when the service is connected.
671         */
672        void onServiceConnected() {
673            if (mListeners != null && !mListeners.isEmpty()) {
674                setMagnificationCallbackEnabled(true);
675            }
676        }
677
678        /**
679         * Adds the specified change listener to the list of magnification
680         * change listeners. The callback will occur on the service's main
681         * thread.
682         *
683         * @param listener the listener to add, must be non-{@code null}
684         */
685        public void addListener(@NonNull OnMagnificationChangedListener listener) {
686            addListener(listener, null);
687        }
688
689        /**
690         * Adds the specified change listener to the list of magnification
691         * change listeners. The callback will occur on the specified
692         * {@link Handler}'s thread, or on the service's main thread if the
693         * handler is {@code null}.
694         *
695         * @param listener the listener to add, must be non-null
696         * @param handler the handler on which the callback should execute, or
697         *        {@code null} to execute on the service's main thread
698         */
699        public void addListener(@NonNull OnMagnificationChangedListener listener,
700                @Nullable Handler handler) {
701            if (mListeners == null) {
702                mListeners = new ArrayMap<>();
703            }
704
705            final boolean shouldEnableCallback = mListeners.isEmpty();
706            mListeners.put(listener, handler);
707
708            if (shouldEnableCallback) {
709                // This may fail if the service is not connected yet, but if we
710                // still have listeners when it connects then we can try again.
711                setMagnificationCallbackEnabled(true);
712            }
713        }
714
715        /**
716         * Removes all instances of the specified change listener from the list
717         * of magnification change listeners.
718         *
719         * @param listener the listener to remove, must be non-null
720         * @return {@code true} if at least one instance of the listener was
721         *         removed
722         */
723        public boolean removeListener(@NonNull OnMagnificationChangedListener listener) {
724            if (mListeners == null) {
725                return false;
726            }
727
728            final int keyIndex = mListeners.indexOfKey(listener);
729            final boolean hasKey = keyIndex >= 0;
730            if (hasKey) {
731                mListeners.removeAt(keyIndex);
732            }
733
734            if (hasKey && mListeners.isEmpty()) {
735                // We just removed the last listener, so we don't need
736                // callbacks from the service anymore.
737                setMagnificationCallbackEnabled(false);
738            }
739
740            return hasKey;
741        }
742
743        private void setMagnificationCallbackEnabled(boolean enabled) {
744            final IAccessibilityServiceConnection connection =
745                    AccessibilityInteractionClient.getInstance().getConnection(
746                            mService.mConnectionId);
747            if (connection != null) {
748                try {
749                    connection.setMagnificationCallbackEnabled(enabled);
750                } catch (RemoteException re) {
751                    throw new RuntimeException(re);
752                }
753            }
754        }
755
756        /**
757         * Dispatches magnification changes to any registered listeners. This
758         * should be called on the service's main thread.
759         */
760        void dispatchMagnificationChanged(final @NonNull Region region, final float scale,
761                final float centerX, final float centerY) {
762            if (mListeners == null || mListeners.isEmpty()) {
763                Slog.d(LOG_TAG, "Received magnification changed "
764                        + "callback with no listeners registered!");
765                setMagnificationCallbackEnabled(false);
766                return;
767            }
768
769            // Listeners may remove themselves. Perform a shallow copy to avoid
770            // concurrent modification.
771            final ArrayMap<OnMagnificationChangedListener, Handler> entries =
772                    new ArrayMap<>(mListeners);
773
774            for (int i = 0, count = entries.size(); i < count; i++) {
775                final OnMagnificationChangedListener listener = entries.keyAt(i);
776                final Handler handler = entries.valueAt(i);
777                if (handler != null) {
778                    handler.post(new Runnable() {
779                        @Override
780                        public void run() {
781                            listener.onMagnificationChanged(MagnificationController.this,
782                                    region, scale, centerX, centerY);
783                        }
784                    });
785                } else {
786                    // We're already on the main thread, just run the listener.
787                    listener.onMagnificationChanged(this, region, scale, centerX, centerY);
788                }
789            }
790        }
791
792        /**
793         * Returns the current magnification scale.
794         * <p>
795         * <strong>Note:</strong> If the service is not yet connected (e.g.
796         * {@link AccessibilityService#onServiceConnected()} has not yet been
797         * called) or the service has been disconnected, this method will
798         * return a default value of {@code 1.0f}.
799         *
800         * @return the current magnification scale
801         */
802        public float getScale() {
803            final IAccessibilityServiceConnection connection =
804                    AccessibilityInteractionClient.getInstance().getConnection(
805                            mService.mConnectionId);
806            if (connection != null) {
807                try {
808                    return connection.getMagnificationScale();
809                } catch (RemoteException re) {
810                    Log.w(LOG_TAG, "Failed to obtain scale", re);
811                }
812            }
813            return 1.0f;
814        }
815
816        /**
817         * Returns the unscaled screen-relative X coordinate of the focal
818         * center of the magnified region. This is the point around which
819         * zooming occurs and is guaranteed to lie within the magnified
820         * region.
821         * <p>
822         * <strong>Note:</strong> If the service is not yet connected (e.g.
823         * {@link AccessibilityService#onServiceConnected()} has not yet been
824         * called) or the service has been disconnected, this method will
825         * return a default value of {@code 0.0f}.
826         *
827         * @return the unscaled screen-relative X coordinate of the center of
828         *         the magnified region
829         */
830        public float getCenterX() {
831            final IAccessibilityServiceConnection connection =
832                    AccessibilityInteractionClient.getInstance().getConnection(
833                            mService.mConnectionId);
834            if (connection != null) {
835                try {
836                    return connection.getMagnificationCenterX();
837                } catch (RemoteException re) {
838                    Log.w(LOG_TAG, "Failed to obtain center X", re);
839                }
840            }
841            return 0.0f;
842        }
843
844        /**
845         * Returns the unscaled screen-relative Y coordinate of the focal
846         * center of the magnified region. This is the point around which
847         * zooming occurs and is guaranteed to lie within the magnified
848         * region.
849         * <p>
850         * <strong>Note:</strong> If the service is not yet connected (e.g.
851         * {@link AccessibilityService#onServiceConnected()} has not yet been
852         * called) or the service has been disconnected, this method will
853         * return a default value of {@code 0.0f}.
854         *
855         * @return the unscaled screen-relative Y coordinate of the center of
856         *         the magnified region
857         */
858        public float getCenterY() {
859            final IAccessibilityServiceConnection connection =
860                    AccessibilityInteractionClient.getInstance().getConnection(
861                            mService.mConnectionId);
862            if (connection != null) {
863                try {
864                    return connection.getMagnificationCenterY();
865                } catch (RemoteException re) {
866                    Log.w(LOG_TAG, "Failed to obtain center Y", re);
867                }
868            }
869            return 0.0f;
870        }
871
872        /**
873         * Returns the region of the screen currently being magnified. If
874         * magnification is not enabled, the returned region will be empty.
875         * <p>
876         * <strong>Note:</strong> If the service is not yet connected (e.g.
877         * {@link AccessibilityService#onServiceConnected()} has not yet been
878         * called) or the service has been disconnected, this method will
879         * return an empty region.
880         *
881         * @return the screen-relative bounds of the magnified region
882         */
883        @NonNull
884        public Region getMagnifiedRegion() {
885            final IAccessibilityServiceConnection connection =
886                    AccessibilityInteractionClient.getInstance().getConnection(
887                            mService.mConnectionId);
888            if (connection != null) {
889                try {
890                    return connection.getMagnifiedRegion();
891                } catch (RemoteException re) {
892                    Log.w(LOG_TAG, "Failed to obtain magnified region", re);
893                }
894            }
895            return Region.obtain();
896        }
897
898        /**
899         * Resets magnification scale and center to their default (e.g. no
900         * magnification) values.
901         * <p>
902         * <strong>Note:</strong> If the service is not yet connected (e.g.
903         * {@link AccessibilityService#onServiceConnected()} has not yet been
904         * called) or the service has been disconnected, this method will have
905         * no effect and return {@code false}.
906         *
907         * @param animate {@code true} to animate from the current scale and
908         *                center or {@code false} to reset the scale and center
909         *                immediately
910         * @return {@code true} on success, {@code false} on failure
911         */
912        public boolean reset(boolean animate) {
913            final IAccessibilityServiceConnection connection =
914                    AccessibilityInteractionClient.getInstance().getConnection(
915                            mService.mConnectionId);
916            if (connection != null) {
917                try {
918                    return connection.resetMagnification(animate);
919                } catch (RemoteException re) {
920                    Log.w(LOG_TAG, "Failed to reset", re);
921                }
922            }
923            return false;
924        }
925
926        /**
927         * Sets the magnification scale.
928         * <p>
929         * <strong>Note:</strong> If the service is not yet connected (e.g.
930         * {@link AccessibilityService#onServiceConnected()} has not yet been
931         * called) or the service has been disconnected, this method will have
932         * no effect and return {@code false}.
933         *
934         * @param scale the magnification scale to set, must be >= 1 and <= 5
935         * @param animate {@code true} to animate from the current scale or
936         *                {@code false} to set the scale immediately
937         * @return {@code true} on success, {@code false} on failure
938         */
939        public boolean setScale(float scale, boolean animate) {
940            final IAccessibilityServiceConnection connection =
941                    AccessibilityInteractionClient.getInstance().getConnection(
942                            mService.mConnectionId);
943            if (connection != null) {
944                try {
945                    return connection.setMagnificationScaleAndCenter(
946                            scale, Float.NaN, Float.NaN, animate);
947                } catch (RemoteException re) {
948                    Log.w(LOG_TAG, "Failed to set scale", re);
949                }
950            }
951            return false;
952        }
953
954        /**
955         * Sets the center of the magnified viewport.
956         * <p>
957         * <strong>Note:</strong> If the service is not yet connected (e.g.
958         * {@link AccessibilityService#onServiceConnected()} has not yet been
959         * called) or the service has been disconnected, this method will have
960         * no effect and return {@code false}.
961         *
962         * @param centerX the unscaled screen-relative X coordinate on which to
963         *                center the viewport
964         * @param centerY the unscaled screen-relative Y coordinate on which to
965         *                center the viewport
966         * @param animate {@code true} to animate from the current viewport
967         *                center or {@code false} to set the center immediately
968         * @return {@code true} on success, {@code false} on failure
969         */
970        public boolean setCenter(float centerX, float centerY, boolean animate) {
971            final IAccessibilityServiceConnection connection =
972                    AccessibilityInteractionClient.getInstance().getConnection(
973                            mService.mConnectionId);
974            if (connection != null) {
975                try {
976                    return connection.setMagnificationScaleAndCenter(
977                            Float.NaN, centerX, centerY, animate);
978                } catch (RemoteException re) {
979                    Log.w(LOG_TAG, "Failed to set center", re);
980                }
981            }
982            return false;
983        }
984
985        /**
986         * Listener for changes in the state of magnification.
987         */
988        public interface OnMagnificationChangedListener {
989            /**
990             * Called when the magnified region, scale, or center changes.
991             *
992             * @param controller the magnification controller
993             * @param region the new magnified region, may be empty if
994             *               magnification is not enabled (e.g. scale is 1)
995             * @param scale the new scale
996             * @param centerX the new X coordinate around which magnification is focused
997             * @param centerY the new Y coordinate around which magnification is focused
998             */
999            void onMagnificationChanged(@NonNull MagnificationController controller,
1000                    @NonNull Region region, float scale, float centerX, float centerY);
1001        }
1002    }
1003
1004    /**
1005     * Performs a global action. Such an action can be performed
1006     * at any moment regardless of the current application or user
1007     * location in that application. For example going back, going
1008     * home, opening recents, etc.
1009     *
1010     * @param action The action to perform.
1011     * @return Whether the action was successfully performed.
1012     *
1013     * @see #GLOBAL_ACTION_BACK
1014     * @see #GLOBAL_ACTION_HOME
1015     * @see #GLOBAL_ACTION_NOTIFICATIONS
1016     * @see #GLOBAL_ACTION_RECENTS
1017     */
1018    public final boolean performGlobalAction(int action) {
1019        IAccessibilityServiceConnection connection =
1020            AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1021        if (connection != null) {
1022            try {
1023                return connection.performGlobalAction(action);
1024            } catch (RemoteException re) {
1025                Log.w(LOG_TAG, "Error while calling performGlobalAction", re);
1026            }
1027        }
1028        return false;
1029    }
1030
1031    /**
1032     * Find the view that has the specified focus type. The search is performed
1033     * across all windows.
1034     * <p>
1035     * <strong>Note:</strong> In order to access the windows your service has
1036     * to declare the capability to retrieve window content by setting the
1037     * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
1038     * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
1039     * Also the service has to opt-in to retrieve the interactive windows by
1040     * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
1041     * flag.Otherwise, the search will be performed only in the active window.
1042     * </p>
1043     *
1044     * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or
1045     *         {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}.
1046     * @return The node info of the focused view or null.
1047     *
1048     * @see AccessibilityNodeInfo#FOCUS_INPUT
1049     * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY
1050     */
1051    public AccessibilityNodeInfo findFocus(int focus) {
1052        return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId,
1053                AccessibilityNodeInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus);
1054    }
1055
1056    /**
1057     * Gets the an {@link AccessibilityServiceInfo} describing this
1058     * {@link AccessibilityService}. This method is useful if one wants
1059     * to change some of the dynamically configurable properties at
1060     * runtime.
1061     *
1062     * @return The accessibility service info.
1063     *
1064     * @see AccessibilityServiceInfo
1065     */
1066    public final AccessibilityServiceInfo getServiceInfo() {
1067        IAccessibilityServiceConnection connection =
1068            AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1069        if (connection != null) {
1070            try {
1071                return connection.getServiceInfo();
1072            } catch (RemoteException re) {
1073                Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re);
1074            }
1075        }
1076        return null;
1077    }
1078
1079    /**
1080     * Sets the {@link AccessibilityServiceInfo} that describes this service.
1081     * <p>
1082     * Note: You can call this method any time but the info will be picked up after
1083     *       the system has bound to this service and when this method is called thereafter.
1084     *
1085     * @param info The info.
1086     */
1087    public final void setServiceInfo(AccessibilityServiceInfo info) {
1088        mInfo = info;
1089        sendServiceInfo();
1090    }
1091
1092    /**
1093     * Sets the {@link AccessibilityServiceInfo} for this service if the latter is
1094     * properly set and there is an {@link IAccessibilityServiceConnection} to the
1095     * AccessibilityManagerService.
1096     */
1097    private void sendServiceInfo() {
1098        IAccessibilityServiceConnection connection =
1099            AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1100        if (mInfo != null && connection != null) {
1101            try {
1102                connection.setServiceInfo(mInfo);
1103                mInfo = null;
1104                AccessibilityInteractionClient.getInstance().clearCache();
1105            } catch (RemoteException re) {
1106                Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
1107            }
1108        }
1109    }
1110
1111    @Override
1112    public Object getSystemService(@ServiceName @NonNull String name) {
1113        if (getBaseContext() == null) {
1114            throw new IllegalStateException(
1115                    "System services not available to Activities before onCreate()");
1116        }
1117
1118        // Guarantee that we always return the same window manager instance.
1119        if (WINDOW_SERVICE.equals(name)) {
1120            if (mWindowManager == null) {
1121                mWindowManager = (WindowManager) getBaseContext().getSystemService(name);
1122            }
1123            return mWindowManager;
1124        }
1125        return super.getSystemService(name);
1126    }
1127
1128    /**
1129     * Implement to return the implementation of the internal accessibility
1130     * service interface.
1131     */
1132    @Override
1133    public final IBinder onBind(Intent intent) {
1134        return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {
1135            @Override
1136            public void onServiceConnected() {
1137                AccessibilityService.this.dispatchServiceConnected();
1138            }
1139
1140            @Override
1141            public void onInterrupt() {
1142                AccessibilityService.this.onInterrupt();
1143            }
1144
1145            @Override
1146            public void onAccessibilityEvent(AccessibilityEvent event) {
1147                AccessibilityService.this.onAccessibilityEvent(event);
1148            }
1149
1150            @Override
1151            public void init(int connectionId, IBinder windowToken) {
1152                mConnectionId = connectionId;
1153                mWindowToken = windowToken;
1154
1155                // The client may have already obtained the window manager, so
1156                // update the default token on whatever manager we gave them.
1157                final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE);
1158                wm.setDefaultToken(windowToken);
1159            }
1160
1161            @Override
1162            public boolean onGesture(int gestureId) {
1163                return AccessibilityService.this.onGesture(gestureId);
1164            }
1165
1166            @Override
1167            public boolean onKeyEvent(KeyEvent event) {
1168                return AccessibilityService.this.onKeyEvent(event);
1169            }
1170
1171            @Override
1172            public void onMagnificationChanged(@NonNull Region region,
1173                    float scale, float centerX, float centerY) {
1174                AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY);
1175            }
1176
1177            @Override
1178            public void onPerformGestureResult(int sequence, boolean completedSuccessfully) {
1179                AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully);
1180            }
1181        });
1182    }
1183
1184    /**
1185     * Implements the internal {@link IAccessibilityServiceClient} interface to convert
1186     * incoming calls to it back to calls on an {@link AccessibilityService}.
1187     *
1188     * @hide
1189     */
1190    public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
1191            implements HandlerCaller.Callback {
1192        private static final int DO_INIT = 1;
1193        private static final int DO_ON_INTERRUPT = 2;
1194        private static final int DO_ON_ACCESSIBILITY_EVENT = 3;
1195        private static final int DO_ON_GESTURE = 4;
1196        private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5;
1197        private static final int DO_ON_KEY_EVENT = 6;
1198        private static final int DO_ON_MAGNIFICATION_CHANGED = 7;
1199        private static final int DO_GESTURE_COMPLETE = 8;
1200
1201        private final HandlerCaller mCaller;
1202
1203        private final Callbacks mCallback;
1204
1205        private int mConnectionId;
1206
1207        public IAccessibilityServiceClientWrapper(Context context, Looper looper,
1208                Callbacks callback) {
1209            mCallback = callback;
1210            mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
1211        }
1212
1213        public void init(IAccessibilityServiceConnection connection, int connectionId,
1214                IBinder windowToken) {
1215            Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId,
1216                    connection, windowToken);
1217            mCaller.sendMessage(message);
1218        }
1219
1220        public void onInterrupt() {
1221            Message message = mCaller.obtainMessage(DO_ON_INTERRUPT);
1222            mCaller.sendMessage(message);
1223        }
1224
1225        public void onAccessibilityEvent(AccessibilityEvent event) {
1226            Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event);
1227            mCaller.sendMessage(message);
1228        }
1229
1230        public void onGesture(int gestureId) {
1231            Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId);
1232            mCaller.sendMessage(message);
1233        }
1234
1235        public void clearAccessibilityCache() {
1236            Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE);
1237            mCaller.sendMessage(message);
1238        }
1239
1240        @Override
1241        public void onKeyEvent(KeyEvent event, int sequence) {
1242            Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event);
1243            mCaller.sendMessage(message);
1244        }
1245
1246        public void onMagnificationChanged(@NonNull Region region,
1247                float scale, float centerX, float centerY) {
1248            final SomeArgs args = SomeArgs.obtain();
1249            args.arg1 = region;
1250            args.arg2 = scale;
1251            args.arg3 = centerX;
1252            args.arg4 = centerY;
1253
1254            final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args);
1255            mCaller.sendMessage(message);
1256        }
1257
1258        public void onPerformGestureResult(int sequence, boolean successfully) {
1259            Message message = mCaller.obtainMessageII(DO_GESTURE_COMPLETE, sequence,
1260                    successfully ? 1 : 0);
1261            mCaller.sendMessage(message);
1262        }
1263
1264        @Override
1265        public void executeMessage(Message message) {
1266            switch (message.what) {
1267                case DO_ON_ACCESSIBILITY_EVENT: {
1268                    AccessibilityEvent event = (AccessibilityEvent) message.obj;
1269                    if (event != null) {
1270                        AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);
1271                        mCallback.onAccessibilityEvent(event);
1272                        // Make sure the event is recycled.
1273                        try {
1274                            event.recycle();
1275                        } catch (IllegalStateException ise) {
1276                            /* ignore - best effort */
1277                        }
1278                    }
1279                } return;
1280
1281                case DO_ON_INTERRUPT: {
1282                    mCallback.onInterrupt();
1283                } return;
1284
1285                case DO_INIT: {
1286                    mConnectionId = message.arg1;
1287                    SomeArgs args = (SomeArgs) message.obj;
1288                    IAccessibilityServiceConnection connection =
1289                            (IAccessibilityServiceConnection) args.arg1;
1290                    IBinder windowToken = (IBinder) args.arg2;
1291                    args.recycle();
1292                    if (connection != null) {
1293                        AccessibilityInteractionClient.getInstance().addConnection(mConnectionId,
1294                                connection);
1295                        mCallback.init(mConnectionId, windowToken);
1296                        mCallback.onServiceConnected();
1297                    } else {
1298                        AccessibilityInteractionClient.getInstance().removeConnection(
1299                                mConnectionId);
1300                        mConnectionId = AccessibilityInteractionClient.NO_ID;
1301                        AccessibilityInteractionClient.getInstance().clearCache();
1302                        mCallback.init(AccessibilityInteractionClient.NO_ID, null);
1303                    }
1304                } return;
1305
1306                case DO_ON_GESTURE: {
1307                    final int gestureId = message.arg1;
1308                    mCallback.onGesture(gestureId);
1309                } return;
1310
1311                case DO_CLEAR_ACCESSIBILITY_CACHE: {
1312                    AccessibilityInteractionClient.getInstance().clearCache();
1313                } return;
1314
1315                case DO_ON_KEY_EVENT: {
1316                    KeyEvent event = (KeyEvent) message.obj;
1317                    try {
1318                        IAccessibilityServiceConnection connection = AccessibilityInteractionClient
1319                                .getInstance().getConnection(mConnectionId);
1320                        if (connection != null) {
1321                            final boolean result = mCallback.onKeyEvent(event);
1322                            final int sequence = message.arg1;
1323                            try {
1324                                connection.setOnKeyEventResult(result, sequence);
1325                            } catch (RemoteException re) {
1326                                /* ignore */
1327                            }
1328                        }
1329                    } finally {
1330                        // Make sure the event is recycled.
1331                        try {
1332                            event.recycle();
1333                        } catch (IllegalStateException ise) {
1334                            /* ignore - best effort */
1335                        }
1336                    }
1337                } return;
1338
1339                case DO_ON_MAGNIFICATION_CHANGED: {
1340                    final SomeArgs args = (SomeArgs) message.obj;
1341                    final Region region = (Region) args.arg1;
1342                    final float scale = (float) args.arg2;
1343                    final float centerX = (float) args.arg3;
1344                    final float centerY = (float) args.arg4;
1345                    mCallback.onMagnificationChanged(region, scale, centerX, centerY);
1346                } return;
1347
1348                case DO_GESTURE_COMPLETE: {
1349                    final boolean successfully = message.arg2 == 1;
1350                    mCallback.onPerformGestureResult(message.arg1, successfully);
1351                } return;
1352
1353                default :
1354                    Log.w(LOG_TAG, "Unknown message type " + message.what);
1355            }
1356        }
1357    }
1358
1359    /**
1360     * Class used to report status of dispatched gestures
1361     */
1362    public static abstract class GestureResultCallback {
1363        /** Called when the gesture has completed successfully
1364         *
1365         * @param gestureDescription The description of the gesture that completed.
1366         */
1367        public void onCompleted(GestureDescription gestureDescription) {
1368        }
1369
1370        /** Called when the gesture was cancelled
1371         *
1372         * @param gestureDescription The description of the gesture that was cancelled.
1373         */
1374        public void onCancelled(GestureDescription gestureDescription) {
1375        }
1376    }
1377
1378    /* Object to keep track of gesture result callbacks */
1379    private static class GestureResultCallbackInfo {
1380        GestureDescription gestureDescription;
1381        GestureResultCallback callback;
1382        Handler handler;
1383
1384        GestureResultCallbackInfo(GestureDescription gestureDescription,
1385                GestureResultCallback callback, Handler handler) {
1386            this.gestureDescription = gestureDescription;
1387            this.callback = callback;
1388            this.handler = handler;
1389        }
1390    }
1391}
1392