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