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