AccessibilityService.java revision 78d2e2ddf07414eb342094ad8cf9d830b2220528
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 open the 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    private static final String LOG_TAG = "AccessibilityService";
357
358    /**
359     * @hide
360     */
361    public interface Callbacks {
362        public void onAccessibilityEvent(AccessibilityEvent event);
363        public void onInterrupt();
364        public void onServiceConnected();
365        public void init(int connectionId, IBinder windowToken);
366        public boolean onGesture(int gestureId);
367        public boolean onKeyEvent(KeyEvent event);
368        public void onMagnificationChanged(@NonNull Region region,
369                float scale, float centerX, float centerY);
370        public void onSoftKeyboardShowModeChanged(int showMode);
371        public void onPerformGestureResult(int sequence, boolean completedSuccessfully);
372    }
373
374    /**
375     * Annotations for Soft Keyboard show modes so tools can catch invalid show modes.
376     * @hide
377     */
378    @Retention(RetentionPolicy.SOURCE)
379    @IntDef({SHOW_MODE_AUTO, SHOW_MODE_HIDDEN})
380    public @interface SoftKeyboardShowMode {};
381    /**
382     * @hide
383     */
384    public static final int SHOW_MODE_AUTO = 0;
385    /**
386     * @hide
387     */
388    public static final int SHOW_MODE_HIDDEN = 1;
389
390    private int mConnectionId;
391
392    private AccessibilityServiceInfo mInfo;
393
394    private IBinder mWindowToken;
395
396    private WindowManager mWindowManager;
397
398    private MagnificationController mMagnificationController;
399    private SoftKeyboardController mSoftKeyboardController;
400
401    private int mGestureStatusCallbackSequence;
402
403    private SparseArray<GestureResultCallbackInfo> mGestureStatusCallbackInfos;
404
405    private final Object mLock = new Object();
406
407    /**
408     * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
409     *
410     * @param event An event.
411     */
412    public abstract void onAccessibilityEvent(AccessibilityEvent event);
413
414    /**
415     * Callback for interrupting the accessibility feedback.
416     */
417    public abstract void onInterrupt();
418
419    /**
420     * Dispatches service connection to internal components first, then the
421     * client code.
422     */
423    private void dispatchServiceConnected() {
424        if (mMagnificationController != null) {
425            mMagnificationController.onServiceConnected();
426        }
427
428        // The client gets to handle service connection last, after we've set
429        // up any state upon which their code may rely.
430        onServiceConnected();
431    }
432
433    /**
434     * This method is a part of the {@link AccessibilityService} lifecycle and is
435     * called after the system has successfully bound to the service. If is
436     * convenient to use this method for setting the {@link AccessibilityServiceInfo}.
437     *
438     * @see AccessibilityServiceInfo
439     * @see #setServiceInfo(AccessibilityServiceInfo)
440     */
441    protected void onServiceConnected() {
442
443    }
444
445    /**
446     * Called by the system when the user performs a specific gesture on the
447     * touch screen.
448     *
449     * <strong>Note:</strong> To receive gestures an accessibility service must
450     * request that the device is in touch exploration mode by setting the
451     * {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE}
452     * flag.
453     *
454     * @param gestureId The unique id of the performed gesture.
455     *
456     * @return Whether the gesture was handled.
457     *
458     * @see #GESTURE_SWIPE_UP
459     * @see #GESTURE_SWIPE_UP_AND_LEFT
460     * @see #GESTURE_SWIPE_UP_AND_DOWN
461     * @see #GESTURE_SWIPE_UP_AND_RIGHT
462     * @see #GESTURE_SWIPE_DOWN
463     * @see #GESTURE_SWIPE_DOWN_AND_LEFT
464     * @see #GESTURE_SWIPE_DOWN_AND_UP
465     * @see #GESTURE_SWIPE_DOWN_AND_RIGHT
466     * @see #GESTURE_SWIPE_LEFT
467     * @see #GESTURE_SWIPE_LEFT_AND_UP
468     * @see #GESTURE_SWIPE_LEFT_AND_RIGHT
469     * @see #GESTURE_SWIPE_LEFT_AND_DOWN
470     * @see #GESTURE_SWIPE_RIGHT
471     * @see #GESTURE_SWIPE_RIGHT_AND_UP
472     * @see #GESTURE_SWIPE_RIGHT_AND_LEFT
473     * @see #GESTURE_SWIPE_RIGHT_AND_DOWN
474     */
475    protected boolean onGesture(int gestureId) {
476        return false;
477    }
478
479    /**
480     * Callback that allows an accessibility service to observe the key events
481     * before they are passed to the rest of the system. This means that the events
482     * are first delivered here before they are passed to the device policy, the
483     * input method, or applications.
484     * <p>
485     * <strong>Note:</strong> It is important that key events are handled in such
486     * a way that the event stream that would be passed to the rest of the system
487     * is well-formed. For example, handling the down event but not the up event
488     * and vice versa would generate an inconsistent event stream.
489     * </p>
490     * <p>
491     * <strong>Note:</strong> The key events delivered in this method are copies
492     * and modifying them will have no effect on the events that will be passed
493     * to the system. This method is intended to perform purely filtering
494     * functionality.
495     * <p>
496     *
497     * @param event The event to be processed.
498     * @return If true then the event will be consumed and not delivered to
499     *         applications, otherwise it will be delivered as usual.
500     */
501    protected boolean onKeyEvent(KeyEvent event) {
502        return false;
503    }
504
505    /**
506     * Gets the windows on the screen. This method returns only the windows
507     * that a sighted user can interact with, as opposed to all windows.
508     * For example, if there is a modal dialog shown and the user cannot touch
509     * anything behind it, then only the modal window will be reported
510     * (assuming it is the top one). For convenience the returned windows
511     * are ordered in a descending layer order, which is the windows that
512     * are higher in the Z-order are reported first. Since the user can always
513     * interact with the window that has input focus by typing, the focused
514     * window is always returned (even if covered by a modal window).
515     * <p>
516     * <strong>Note:</strong> In order to access the windows your service has
517     * to declare the capability to retrieve window content by setting the
518     * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
519     * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
520     * Also the service has to opt-in to retrieve the interactive windows by
521     * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
522     * flag.
523     * </p>
524     *
525     * @return The windows if there are windows and the service is can retrieve
526     *         them, otherwise an empty list.
527     */
528    public List<AccessibilityWindowInfo> getWindows() {
529        return AccessibilityInteractionClient.getInstance().getWindows(mConnectionId);
530    }
531
532    /**
533     * Gets the root node in the currently active window if this service
534     * can retrieve window content. The active window is the one that the user
535     * is currently touching or the window with input focus, if the user is not
536     * touching any window.
537     * <p>
538     * The currently active window is defined as the window that most recently fired one
539     * of the following events:
540     * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED},
541     * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
542     * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}.
543     * In other words, the last window shown that also has input focus.
544     * </p>
545     * <p>
546     * <strong>Note:</strong> In order to access the root node your service has
547     * to declare the capability to retrieve window content by setting the
548     * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
549     * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
550     * </p>
551     *
552     * @return The root node if this service can retrieve window content.
553     */
554    public AccessibilityNodeInfo getRootInActiveWindow() {
555        return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId);
556    }
557
558    /**
559     * Disables the service. After calling this method, the service will be disabled and settings
560     * will show that it is turned off.
561     */
562    public final void disableSelf() {
563        final IAccessibilityServiceConnection connection =
564                AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
565        if (connection != null) {
566            try {
567                connection.disableSelf();
568            } catch (RemoteException re) {
569                throw new RuntimeException(re);
570            }
571        }
572    }
573
574    /**
575     * Returns the magnification controller, which may be used to query and
576     * modify the state of display magnification.
577     * <p>
578     * <strong>Note:</strong> In order to control magnification, your service
579     * must declare the capability by setting the
580     * {@link android.R.styleable#AccessibilityService_canControlMagnification}
581     * property in its meta-data. For more information, see
582     * {@link #SERVICE_META_DATA}.
583     *
584     * @return the magnification controller
585     */
586    @NonNull
587    public final MagnificationController getMagnificationController() {
588        synchronized (mLock) {
589            if (mMagnificationController == null) {
590                mMagnificationController = new MagnificationController(this, mLock);
591            }
592            return mMagnificationController;
593        }
594    }
595
596    /**
597     * Dispatch a gesture to the touch screen. Any gestures currently in progress, whether from
598     * the user, this service, or another service, will be cancelled.
599     * <p>
600     * <strong>Note:</strong> In order to dispatch gestures, your service
601     * must declare the capability by setting the
602     * {@link android.R.styleable#AccessibilityService_canPerformGestures}
603     * property in its meta-data. For more information, see
604     * {@link #SERVICE_META_DATA}.
605     *
606     * @param gesture The gesture to dispatch
607     * @param callback The object to call back when the status of the gesture is known. If
608     * {@code null}, no status is reported.
609     * @param handler The handler on which to call back the {@code callback} object. If
610     * {@code null}, the object is called back on the service's main thread.
611     *
612     * @return {@code true} if the gesture is dispatched, {@code false} if not.
613     */
614    public final boolean dispatchGesture(@NonNull GestureDescription gesture,
615            @Nullable GestureResultCallback callback,
616            @Nullable Handler handler) {
617        final IAccessibilityServiceConnection connection =
618                AccessibilityInteractionClient.getInstance().getConnection(
619                        mConnectionId);
620        if (connection == null) {
621            return false;
622        }
623        List<MotionEvent> events = MotionEventGenerator.getMotionEventsFromGestureDescription(
624                gesture, 100);
625        try {
626            synchronized (mLock) {
627                mGestureStatusCallbackSequence++;
628                if (callback != null) {
629                    if (mGestureStatusCallbackInfos == null) {
630                        mGestureStatusCallbackInfos = new SparseArray<>();
631                    }
632                    GestureResultCallbackInfo callbackInfo = new GestureResultCallbackInfo(gesture,
633                            callback, handler);
634                    mGestureStatusCallbackInfos.put(mGestureStatusCallbackSequence, callbackInfo);
635                }
636                connection.sendMotionEvents(mGestureStatusCallbackSequence,
637                        new ParceledListSlice<>(events));
638            }
639        } catch (RemoteException re) {
640            throw new RuntimeException(re);
641        }
642        return true;
643    }
644
645    void onPerformGestureResult(int sequence, final boolean completedSuccessfully) {
646        if (mGestureStatusCallbackInfos == null) {
647            return;
648        }
649        GestureResultCallbackInfo callbackInfo;
650        synchronized (mLock) {
651            callbackInfo = mGestureStatusCallbackInfos.get(sequence);
652        }
653        final GestureResultCallbackInfo finalCallbackInfo = callbackInfo;
654        if ((callbackInfo != null) && (callbackInfo.gestureDescription != null)
655                && (callbackInfo.callback != null)) {
656            if (callbackInfo.handler != null) {
657                callbackInfo.handler.post(new Runnable() {
658                    @Override
659                    public void run() {
660                        if (completedSuccessfully) {
661                            finalCallbackInfo.callback
662                                    .onCompleted(finalCallbackInfo.gestureDescription);
663                        } else {
664                            finalCallbackInfo.callback
665                                    .onCancelled(finalCallbackInfo.gestureDescription);
666                        }
667                    }
668                });
669                return;
670            }
671            if (completedSuccessfully) {
672                callbackInfo.callback.onCompleted(callbackInfo.gestureDescription);
673            } else {
674                callbackInfo.callback.onCancelled(callbackInfo.gestureDescription);
675            }
676        }
677    }
678
679    private void onMagnificationChanged(@NonNull Region region, float scale,
680            float centerX, float centerY) {
681        if (mMagnificationController != null) {
682            mMagnificationController.dispatchMagnificationChanged(
683                    region, scale, centerX, centerY);
684        }
685    }
686
687    /**
688     * Used to control and query the state of display magnification.
689     */
690    public static final class MagnificationController {
691        private final AccessibilityService mService;
692
693        /**
694         * Map of listeners to their handlers. Lazily created when adding the
695         * first magnification listener.
696         */
697        private ArrayMap<OnMagnificationChangedListener, Handler> mListeners;
698        private final Object mLock;
699
700        MagnificationController(@NonNull AccessibilityService service, @NonNull Object lock) {
701            mService = service;
702            mLock = lock;
703        }
704
705        /**
706         * Called when the service is connected.
707         */
708        void onServiceConnected() {
709            synchronized (mLock) {
710                if (mListeners != null && !mListeners.isEmpty()) {
711                    setMagnificationCallbackEnabled(true);
712                }
713            }
714        }
715
716        /**
717         * Adds the specified change listener to the list of magnification
718         * change listeners. The callback will occur on the service's main
719         * thread.
720         *
721         * @param listener the listener to add, must be non-{@code null}
722         */
723        public void addListener(@NonNull OnMagnificationChangedListener listener) {
724            addListener(listener, null);
725        }
726
727        /**
728         * Adds the specified change listener to the list of magnification
729         * change listeners. The callback will occur on the specified
730         * {@link Handler}'s thread, or on the service's main thread if the
731         * handler is {@code null}.
732         *
733         * @param listener the listener to add, must be non-null
734         * @param handler the handler on which the callback should execute, or
735         *        {@code null} to execute on the service's main thread
736         */
737        public void addListener(@NonNull OnMagnificationChangedListener listener,
738                @Nullable Handler handler) {
739            synchronized (mLock) {
740                if (mListeners == null) {
741                    mListeners = new ArrayMap<>();
742                }
743
744                final boolean shouldEnableCallback = mListeners.isEmpty();
745                mListeners.put(listener, handler);
746
747                if (shouldEnableCallback) {
748                    // This may fail if the service is not connected yet, but if we
749                    // still have listeners when it connects then we can try again.
750                    setMagnificationCallbackEnabled(true);
751                }
752            }
753        }
754
755        /**
756         * Removes all instances of the specified change listener from the list
757         * of magnification change listeners.
758         *
759         * @param listener the listener to remove, must be non-null
760         * @return {@code true} if at least one instance of the listener was
761         *         removed
762         */
763        public boolean removeListener(@NonNull OnMagnificationChangedListener listener) {
764            if (mListeners == null) {
765                return false;
766            }
767
768            synchronized (mLock) {
769                final int keyIndex = mListeners.indexOfKey(listener);
770                final boolean hasKey = keyIndex >= 0;
771                if (hasKey) {
772                    mListeners.removeAt(keyIndex);
773                }
774
775                if (hasKey && mListeners.isEmpty()) {
776                    // We just removed the last listener, so we don't need
777                    // callbacks from the service anymore.
778                    setMagnificationCallbackEnabled(false);
779                }
780
781                return hasKey;
782            }
783        }
784
785        private void setMagnificationCallbackEnabled(boolean enabled) {
786            final IAccessibilityServiceConnection connection =
787                    AccessibilityInteractionClient.getInstance().getConnection(
788                            mService.mConnectionId);
789            if (connection != null) {
790                try {
791                    connection.setMagnificationCallbackEnabled(enabled);
792                } catch (RemoteException re) {
793                    throw new RuntimeException(re);
794                }
795            }
796        }
797
798        /**
799         * Dispatches magnification changes to any registered listeners. This
800         * should be called on the service's main thread.
801         */
802        void dispatchMagnificationChanged(final @NonNull Region region, final float scale,
803                final float centerX, final float centerY) {
804            final ArrayMap<OnMagnificationChangedListener, Handler> entries;
805            synchronized (mLock) {
806                if (mListeners == null || mListeners.isEmpty()) {
807                    Slog.d(LOG_TAG, "Received magnification changed "
808                            + "callback with no listeners registered!");
809                    setMagnificationCallbackEnabled(false);
810                    return;
811                }
812
813                // Listeners may remove themselves. Perform a shallow copy to avoid concurrent
814                // modification.
815                entries = new ArrayMap<>(mListeners);
816            }
817
818            for (int i = 0, count = entries.size(); i < count; i++) {
819                final OnMagnificationChangedListener listener = entries.keyAt(i);
820                final Handler handler = entries.valueAt(i);
821                if (handler != null) {
822                    handler.post(new Runnable() {
823                        @Override
824                        public void run() {
825                            listener.onMagnificationChanged(MagnificationController.this,
826                                    region, scale, centerX, centerY);
827                        }
828                    });
829                } else {
830                    // We're already on the main thread, just run the listener.
831                    listener.onMagnificationChanged(this, region, scale, centerX, centerY);
832                }
833            }
834        }
835
836        /**
837         * Returns the current magnification scale.
838         * <p>
839         * <strong>Note:</strong> If the service is not yet connected (e.g.
840         * {@link AccessibilityService#onServiceConnected()} has not yet been
841         * called) or the service has been disconnected, this method will
842         * return a default value of {@code 1.0f}.
843         *
844         * @return the current magnification scale
845         */
846        public float getScale() {
847            final IAccessibilityServiceConnection connection =
848                    AccessibilityInteractionClient.getInstance().getConnection(
849                            mService.mConnectionId);
850            if (connection != null) {
851                try {
852                    return connection.getMagnificationScale();
853                } catch (RemoteException re) {
854                    Log.w(LOG_TAG, "Failed to obtain scale", re);
855                }
856            }
857            return 1.0f;
858        }
859
860        /**
861         * Returns the unscaled screen-relative X coordinate of the focal
862         * center of the magnified region. This is the point around which
863         * zooming occurs and is guaranteed to lie within the magnified
864         * region.
865         * <p>
866         * <strong>Note:</strong> If the service is not yet connected (e.g.
867         * {@link AccessibilityService#onServiceConnected()} has not yet been
868         * called) or the service has been disconnected, this method will
869         * return a default value of {@code 0.0f}.
870         *
871         * @return the unscaled screen-relative X coordinate of the center of
872         *         the magnified region
873         */
874        public float getCenterX() {
875            final IAccessibilityServiceConnection connection =
876                    AccessibilityInteractionClient.getInstance().getConnection(
877                            mService.mConnectionId);
878            if (connection != null) {
879                try {
880                    return connection.getMagnificationCenterX();
881                } catch (RemoteException re) {
882                    Log.w(LOG_TAG, "Failed to obtain center X", re);
883                }
884            }
885            return 0.0f;
886        }
887
888        /**
889         * Returns the unscaled screen-relative Y coordinate of the focal
890         * center of the magnified region. This is the point around which
891         * zooming occurs and is guaranteed to lie within the magnified
892         * region.
893         * <p>
894         * <strong>Note:</strong> If the service is not yet connected (e.g.
895         * {@link AccessibilityService#onServiceConnected()} has not yet been
896         * called) or the service has been disconnected, this method will
897         * return a default value of {@code 0.0f}.
898         *
899         * @return the unscaled screen-relative Y coordinate of the center of
900         *         the magnified region
901         */
902        public float getCenterY() {
903            final IAccessibilityServiceConnection connection =
904                    AccessibilityInteractionClient.getInstance().getConnection(
905                            mService.mConnectionId);
906            if (connection != null) {
907                try {
908                    return connection.getMagnificationCenterY();
909                } catch (RemoteException re) {
910                    Log.w(LOG_TAG, "Failed to obtain center Y", re);
911                }
912            }
913            return 0.0f;
914        }
915
916        /**
917         * Returns the region of the screen currently being magnified. If
918         * magnification is not enabled, the returned region will be empty.
919         * <p>
920         * <strong>Note:</strong> If the service is not yet connected (e.g.
921         * {@link AccessibilityService#onServiceConnected()} has not yet been
922         * called) or the service has been disconnected, this method will
923         * return an empty region.
924         *
925         * @return the screen-relative bounds of the magnified region
926         */
927        @NonNull
928        public Region getMagnifiedRegion() {
929            final IAccessibilityServiceConnection connection =
930                    AccessibilityInteractionClient.getInstance().getConnection(
931                            mService.mConnectionId);
932            if (connection != null) {
933                try {
934                    return connection.getMagnifiedRegion();
935                } catch (RemoteException re) {
936                    Log.w(LOG_TAG, "Failed to obtain magnified region", re);
937                }
938            }
939            return Region.obtain();
940        }
941
942        /**
943         * Resets magnification scale and center to their default (e.g. no
944         * magnification) values.
945         * <p>
946         * <strong>Note:</strong> If the service is not yet connected (e.g.
947         * {@link AccessibilityService#onServiceConnected()} has not yet been
948         * called) or the service has been disconnected, this method will have
949         * no effect and return {@code false}.
950         *
951         * @param animate {@code true} to animate from the current scale and
952         *                center or {@code false} to reset the scale and center
953         *                immediately
954         * @return {@code true} on success, {@code false} on failure
955         */
956        public boolean reset(boolean animate) {
957            final IAccessibilityServiceConnection connection =
958                    AccessibilityInteractionClient.getInstance().getConnection(
959                            mService.mConnectionId);
960            if (connection != null) {
961                try {
962                    return connection.resetMagnification(animate);
963                } catch (RemoteException re) {
964                    Log.w(LOG_TAG, "Failed to reset", re);
965                }
966            }
967            return false;
968        }
969
970        /**
971         * Sets the magnification scale.
972         * <p>
973         * <strong>Note:</strong> If the service is not yet connected (e.g.
974         * {@link AccessibilityService#onServiceConnected()} has not yet been
975         * called) or the service has been disconnected, this method will have
976         * no effect and return {@code false}.
977         *
978         * @param scale the magnification scale to set, must be >= 1 and <= 5
979         * @param animate {@code true} to animate from the current scale or
980         *                {@code false} to set the scale immediately
981         * @return {@code true} on success, {@code false} on failure
982         */
983        public boolean setScale(float scale, boolean animate) {
984            final IAccessibilityServiceConnection connection =
985                    AccessibilityInteractionClient.getInstance().getConnection(
986                            mService.mConnectionId);
987            if (connection != null) {
988                try {
989                    return connection.setMagnificationScaleAndCenter(
990                            scale, Float.NaN, Float.NaN, animate);
991                } catch (RemoteException re) {
992                    Log.w(LOG_TAG, "Failed to set scale", re);
993                }
994            }
995            return false;
996        }
997
998        /**
999         * Sets the center of the magnified viewport.
1000         * <p>
1001         * <strong>Note:</strong> If the service is not yet connected (e.g.
1002         * {@link AccessibilityService#onServiceConnected()} has not yet been
1003         * called) or the service has been disconnected, this method will have
1004         * no effect and return {@code false}.
1005         *
1006         * @param centerX the unscaled screen-relative X coordinate on which to
1007         *                center the viewport
1008         * @param centerY the unscaled screen-relative Y coordinate on which to
1009         *                center the viewport
1010         * @param animate {@code true} to animate from the current viewport
1011         *                center or {@code false} to set the center immediately
1012         * @return {@code true} on success, {@code false} on failure
1013         */
1014        public boolean setCenter(float centerX, float centerY, boolean animate) {
1015            final IAccessibilityServiceConnection connection =
1016                    AccessibilityInteractionClient.getInstance().getConnection(
1017                            mService.mConnectionId);
1018            if (connection != null) {
1019                try {
1020                    return connection.setMagnificationScaleAndCenter(
1021                            Float.NaN, centerX, centerY, animate);
1022                } catch (RemoteException re) {
1023                    Log.w(LOG_TAG, "Failed to set center", re);
1024                }
1025            }
1026            return false;
1027        }
1028
1029        /**
1030         * Listener for changes in the state of magnification.
1031         */
1032        public interface OnMagnificationChangedListener {
1033            /**
1034             * Called when the magnified region, scale, or center changes.
1035             *
1036             * @param controller the magnification controller
1037             * @param region the new magnified region, may be empty if
1038             *               magnification is not enabled (e.g. scale is 1)
1039             * @param scale the new scale
1040             * @param centerX the new X coordinate around which magnification is focused
1041             * @param centerY the new Y coordinate around which magnification is focused
1042             */
1043            void onMagnificationChanged(@NonNull MagnificationController controller,
1044                    @NonNull Region region, float scale, float centerX, float centerY);
1045        }
1046    }
1047
1048    /**
1049     * Returns the soft keyboard controller, which may be used to query and modify the soft keyboard
1050     * show mode.
1051     *
1052     * @return the soft keyboard controller
1053     */
1054    @NonNull
1055    public final SoftKeyboardController getSoftKeyboardController() {
1056        synchronized (mLock) {
1057            if (mSoftKeyboardController == null) {
1058                mSoftKeyboardController = new SoftKeyboardController(this, mLock);
1059            }
1060            return mSoftKeyboardController;
1061        }
1062    }
1063
1064    private void onSoftKeyboardShowModeChanged(int showMode) {
1065        if (mSoftKeyboardController != null) {
1066            mSoftKeyboardController.dispatchSoftKeyboardShowModeChanged(showMode);
1067        }
1068    }
1069
1070    /**
1071     * Used to control and query the soft keyboard show mode.
1072     */
1073    public static final class SoftKeyboardController {
1074        private final AccessibilityService mService;
1075
1076        /**
1077         * Map of listeners to their handlers. Lazily created when adding the first
1078         * soft keyboard change listener.
1079         */
1080        private ArrayMap<OnShowModeChangedListener, Handler> mListeners;
1081        private final Object mLock;
1082
1083        SoftKeyboardController(@NonNull AccessibilityService service, @NonNull Object lock) {
1084            mService = service;
1085            mLock = lock;
1086        }
1087
1088        /**
1089         * Called when the service is connected.
1090         */
1091        void onServiceConnected() {
1092            synchronized(mLock) {
1093                if (mListeners != null && !mListeners.isEmpty()) {
1094                    setSoftKeyboardCallbackEnabled(true);
1095                }
1096            }
1097        }
1098
1099        /**
1100         * Adds the specified change listener to the list of show mode change listeners. The
1101         * callback will occur on the service's main thread. Listener is not called on registration.
1102         */
1103        public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) {
1104            addOnShowModeChangedListener(listener, null);
1105        }
1106
1107        /**
1108         * Adds the specified change listener to the list of soft keyboard show mode change
1109         * listeners. The callback will occur on the specified {@link Handler}'s thread, or on the
1110         * services's main thread if the handler is {@code null}.
1111         *
1112         * @param listener the listener to add, must be non-null
1113         * @param handler the handler on which to callback should execute, or {@code null} to
1114         *        execute on the service's main thread
1115         */
1116        public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener,
1117                @Nullable Handler handler) {
1118            synchronized (mLock) {
1119                if (mListeners == null) {
1120                    mListeners = new ArrayMap<>();
1121                }
1122
1123                final boolean shouldEnableCallback = mListeners.isEmpty();
1124                mListeners.put(listener, handler);
1125
1126                if (shouldEnableCallback) {
1127                    // This may fail if the service is not connected yet, but if we still have
1128                    // listeners when it connects, we can try again.
1129                    setSoftKeyboardCallbackEnabled(true);
1130                }
1131            }
1132        }
1133
1134        /**
1135         * Removes all instances of the specified change listener from teh list of magnification
1136         * change listeners.
1137         *
1138         * @param listener the listener to remove, must be non-null
1139         * @return {@code true} if at least one instance of the listener was removed
1140         */
1141        public boolean removeOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) {
1142            if (mListeners == null) {
1143                return false;
1144            }
1145
1146            synchronized (mLock) {
1147                final int keyIndex = mListeners.indexOfKey(listener);
1148                final boolean hasKey = keyIndex >= 0;
1149                if (hasKey) {
1150                    mListeners.removeAt(keyIndex);
1151                }
1152
1153                if (hasKey && mListeners.isEmpty()) {
1154                    // We just removed the last listener, so we don't need callbacks from the
1155                    // service anymore.
1156                    setSoftKeyboardCallbackEnabled(false);
1157                }
1158
1159                return hasKey;
1160            }
1161        }
1162
1163        private void setSoftKeyboardCallbackEnabled(boolean enabled) {
1164            final IAccessibilityServiceConnection connection =
1165                    AccessibilityInteractionClient.getInstance().getConnection(
1166                            mService.mConnectionId);
1167            if (connection != null) {
1168                try {
1169                    connection.setSoftKeyboardCallbackEnabled(enabled);
1170                } catch (RemoteException re) {
1171                    throw new RuntimeException(re);
1172                }
1173            }
1174        }
1175
1176        /**
1177         * Dispatches the soft keyboard show mode change to any registered listeners. This should
1178         * be called on the service's main thread.
1179         */
1180        void dispatchSoftKeyboardShowModeChanged(final int showMode) {
1181            final ArrayMap<OnShowModeChangedListener, Handler> entries;
1182            synchronized (mLock) {
1183                if (mListeners == null || mListeners.isEmpty()) {
1184                    Slog.d(LOG_TAG, "Received soft keyboard show mode changed callback"
1185                            + " with no listeners registered!");
1186                    setSoftKeyboardCallbackEnabled(false);
1187                    return;
1188                }
1189
1190                // Listeners may remove themselves. Perform a shallow copy to avoid concurrent
1191                // modification.
1192                entries = new ArrayMap<>(mListeners);
1193            }
1194
1195            for (int i = 0, count = entries.size(); i < count; i++) {
1196                final OnShowModeChangedListener listener = entries.keyAt(i);
1197                final Handler handler = entries.valueAt(i);
1198                if (handler != null) {
1199                    handler.post(new Runnable() {
1200                        @Override
1201                        public void run() {
1202                            listener.onShowModeChanged(SoftKeyboardController.this, showMode);
1203                        }
1204                    });
1205                } else {
1206                    // We're already on the main thread, just run the listener.
1207                    listener.onShowModeChanged(this, showMode);
1208                }
1209            }
1210        }
1211
1212        /**
1213         * Returns the show mode of the soft keyboard. The default show mode is
1214         * {@code Settings.Secure.SHOW_MODE_AUTO}, where the soft keyboard is shown when a text
1215         * input field is focused. An AccessibilityService can also request the show mode
1216         * {@code Settings.Secure.SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
1217         *
1218         * @return the current soft keyboard show mode
1219         *
1220         * @see Settings#Secure#SHOW_MODE_AUTO
1221         * @see Settings#Secure#SHOW_MODE_HIDDEN
1222         */
1223        @SoftKeyboardShowMode
1224        public int getShowMode() {
1225           try {
1226               return Settings.Secure.getInt(mService.getContentResolver(),
1227                       Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
1228           } catch (Settings.SettingNotFoundException e) {
1229               Log.v(LOG_TAG, "Failed to obtain the soft keyboard mode", e);
1230               // The settings hasn't been changed yet, so it's value is null. Return the default.
1231               return 0;
1232           }
1233        }
1234
1235        /**
1236         * Sets the soft keyboard show mode. The default show mode is
1237         * {@code Settings.Secure.SHOW_MODE_AUTO}, where the soft keyboard is shown when a text
1238         * input field is focused. An AccessibilityService can also request the show mode
1239         * {@code Settings.Secure.SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. The
1240         * The lastto this method will be honored, regardless of any previous calls (including those
1241         * made by other AccessibilityServices).
1242         * <p>
1243         * <strong>Note:</strong> If the service is not yet conected (e.g.
1244         * {@link AccessibilityService#onServiceConnected()} has not yet been called) or the
1245         * service has been disconnected, this method will hav no effect and return {@code false}.
1246         *
1247         * @param showMode the new show mode for the soft keyboard
1248         * @return {@code true} on success
1249         *
1250         * @see Settings#Secure#SHOW_MODE_AUTO
1251         * @see Settings#Secure#SHOW_MODE_HIDDEN
1252         */
1253        public boolean setShowMode(@SoftKeyboardShowMode int showMode) {
1254           final IAccessibilityServiceConnection connection =
1255                   AccessibilityInteractionClient.getInstance().getConnection(
1256                           mService.mConnectionId);
1257           if (connection != null) {
1258               try {
1259                   return connection.setSoftKeyboardShowMode(showMode);
1260               } catch (RemoteException re) {
1261                   Log.w(LOG_TAG, "Falied to set soft keyboard behavior", re);
1262               }
1263           }
1264           return false;
1265        }
1266
1267        /**
1268         * Listener for changes in the soft keyboard show mode.
1269         */
1270        public interface OnShowModeChangedListener {
1271           /**
1272            * Called when the soft keyboard behavior changes. The default show mode is
1273            * {@code Settings.Secure.SHOW_MODE_AUTO}, where the soft keyboard is shown when a text
1274            * input field is focused. An AccessibilityService can also request the show mode
1275            * {@code Settings.Secure.SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
1276            *
1277            * @param controller the soft keyboard controller
1278            * @param showMode the current soft keyboard show mode
1279            */
1280            void onShowModeChanged(@NonNull SoftKeyboardController controller,
1281                    @SoftKeyboardShowMode int showMode);
1282        }
1283    }
1284
1285    /**
1286     * Performs a global action. Such an action can be performed
1287     * at any moment regardless of the current application or user
1288     * location in that application. For example going back, going
1289     * home, opening recents, etc.
1290     *
1291     * @param action The action to perform.
1292     * @return Whether the action was successfully performed.
1293     *
1294     * @see #GLOBAL_ACTION_BACK
1295     * @see #GLOBAL_ACTION_HOME
1296     * @see #GLOBAL_ACTION_NOTIFICATIONS
1297     * @see #GLOBAL_ACTION_RECENTS
1298     */
1299    public final boolean performGlobalAction(int action) {
1300        IAccessibilityServiceConnection connection =
1301            AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1302        if (connection != null) {
1303            try {
1304                return connection.performGlobalAction(action);
1305            } catch (RemoteException re) {
1306                Log.w(LOG_TAG, "Error while calling performGlobalAction", re);
1307            }
1308        }
1309        return false;
1310    }
1311
1312    /**
1313     * Find the view that has the specified focus type. The search is performed
1314     * across all windows.
1315     * <p>
1316     * <strong>Note:</strong> In order to access the windows your service has
1317     * to declare the capability to retrieve window content by setting the
1318     * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
1319     * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
1320     * Also the service has to opt-in to retrieve the interactive windows by
1321     * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
1322     * flag. Otherwise, the search will be performed only in the active window.
1323     * </p>
1324     *
1325     * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or
1326     *         {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}.
1327     * @return The node info of the focused view or null.
1328     *
1329     * @see AccessibilityNodeInfo#FOCUS_INPUT
1330     * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY
1331     */
1332    public AccessibilityNodeInfo findFocus(int focus) {
1333        return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId,
1334                AccessibilityNodeInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus);
1335    }
1336
1337    /**
1338     * Gets the an {@link AccessibilityServiceInfo} describing this
1339     * {@link AccessibilityService}. This method is useful if one wants
1340     * to change some of the dynamically configurable properties at
1341     * runtime.
1342     *
1343     * @return The accessibility service info.
1344     *
1345     * @see AccessibilityServiceInfo
1346     */
1347    public final AccessibilityServiceInfo getServiceInfo() {
1348        IAccessibilityServiceConnection connection =
1349            AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1350        if (connection != null) {
1351            try {
1352                return connection.getServiceInfo();
1353            } catch (RemoteException re) {
1354                Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re);
1355            }
1356        }
1357        return null;
1358    }
1359
1360    /**
1361     * Sets the {@link AccessibilityServiceInfo} that describes this service.
1362     * <p>
1363     * Note: You can call this method any time but the info will be picked up after
1364     *       the system has bound to this service and when this method is called thereafter.
1365     *
1366     * @param info The info.
1367     */
1368    public final void setServiceInfo(AccessibilityServiceInfo info) {
1369        mInfo = info;
1370        sendServiceInfo();
1371    }
1372
1373    /**
1374     * Sets the {@link AccessibilityServiceInfo} for this service if the latter is
1375     * properly set and there is an {@link IAccessibilityServiceConnection} to the
1376     * AccessibilityManagerService.
1377     */
1378    private void sendServiceInfo() {
1379        IAccessibilityServiceConnection connection =
1380            AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1381        if (mInfo != null && connection != null) {
1382            try {
1383                connection.setServiceInfo(mInfo);
1384                mInfo = null;
1385                AccessibilityInteractionClient.getInstance().clearCache();
1386            } catch (RemoteException re) {
1387                Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
1388            }
1389        }
1390    }
1391
1392    @Override
1393    public Object getSystemService(@ServiceName @NonNull String name) {
1394        if (getBaseContext() == null) {
1395            throw new IllegalStateException(
1396                    "System services not available to Activities before onCreate()");
1397        }
1398
1399        // Guarantee that we always return the same window manager instance.
1400        if (WINDOW_SERVICE.equals(name)) {
1401            if (mWindowManager == null) {
1402                mWindowManager = (WindowManager) getBaseContext().getSystemService(name);
1403            }
1404            return mWindowManager;
1405        }
1406        return super.getSystemService(name);
1407    }
1408
1409    /**
1410     * Implement to return the implementation of the internal accessibility
1411     * service interface.
1412     */
1413    @Override
1414    public final IBinder onBind(Intent intent) {
1415        return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {
1416            @Override
1417            public void onServiceConnected() {
1418                AccessibilityService.this.dispatchServiceConnected();
1419            }
1420
1421            @Override
1422            public void onInterrupt() {
1423                AccessibilityService.this.onInterrupt();
1424            }
1425
1426            @Override
1427            public void onAccessibilityEvent(AccessibilityEvent event) {
1428                AccessibilityService.this.onAccessibilityEvent(event);
1429            }
1430
1431            @Override
1432            public void init(int connectionId, IBinder windowToken) {
1433                mConnectionId = connectionId;
1434                mWindowToken = windowToken;
1435
1436                // The client may have already obtained the window manager, so
1437                // update the default token on whatever manager we gave them.
1438                final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE);
1439                wm.setDefaultToken(windowToken);
1440            }
1441
1442            @Override
1443            public boolean onGesture(int gestureId) {
1444                return AccessibilityService.this.onGesture(gestureId);
1445            }
1446
1447            @Override
1448            public boolean onKeyEvent(KeyEvent event) {
1449                return AccessibilityService.this.onKeyEvent(event);
1450            }
1451
1452            @Override
1453            public void onMagnificationChanged(@NonNull Region region,
1454                    float scale, float centerX, float centerY) {
1455                AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY);
1456            }
1457
1458            @Override
1459            public void onSoftKeyboardShowModeChanged(int showMode) {
1460                AccessibilityService.this.onSoftKeyboardShowModeChanged(showMode);
1461            }
1462
1463            @Override
1464            public void onPerformGestureResult(int sequence, boolean completedSuccessfully) {
1465                AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully);
1466            }
1467        });
1468    }
1469
1470    /**
1471     * Implements the internal {@link IAccessibilityServiceClient} interface to convert
1472     * incoming calls to it back to calls on an {@link AccessibilityService}.
1473     *
1474     * @hide
1475     */
1476    public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
1477            implements HandlerCaller.Callback {
1478        private static final int DO_INIT = 1;
1479        private static final int DO_ON_INTERRUPT = 2;
1480        private static final int DO_ON_ACCESSIBILITY_EVENT = 3;
1481        private static final int DO_ON_GESTURE = 4;
1482        private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5;
1483        private static final int DO_ON_KEY_EVENT = 6;
1484        private static final int DO_ON_MAGNIFICATION_CHANGED = 7;
1485        private static final int DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED = 8;
1486        private static final int DO_GESTURE_COMPLETE = 9;
1487
1488        private final HandlerCaller mCaller;
1489
1490        private final Callbacks mCallback;
1491
1492        private int mConnectionId;
1493
1494        public IAccessibilityServiceClientWrapper(Context context, Looper looper,
1495                Callbacks callback) {
1496            mCallback = callback;
1497            mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
1498        }
1499
1500        public void init(IAccessibilityServiceConnection connection, int connectionId,
1501                IBinder windowToken) {
1502            Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId,
1503                    connection, windowToken);
1504            mCaller.sendMessage(message);
1505        }
1506
1507        public void onInterrupt() {
1508            Message message = mCaller.obtainMessage(DO_ON_INTERRUPT);
1509            mCaller.sendMessage(message);
1510        }
1511
1512        public void onAccessibilityEvent(AccessibilityEvent event) {
1513            Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event);
1514            mCaller.sendMessage(message);
1515        }
1516
1517        public void onGesture(int gestureId) {
1518            Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId);
1519            mCaller.sendMessage(message);
1520        }
1521
1522        public void clearAccessibilityCache() {
1523            Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE);
1524            mCaller.sendMessage(message);
1525        }
1526
1527        @Override
1528        public void onKeyEvent(KeyEvent event, int sequence) {
1529            Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event);
1530            mCaller.sendMessage(message);
1531        }
1532
1533        public void onMagnificationChanged(@NonNull Region region,
1534                float scale, float centerX, float centerY) {
1535            final SomeArgs args = SomeArgs.obtain();
1536            args.arg1 = region;
1537            args.arg2 = scale;
1538            args.arg3 = centerX;
1539            args.arg4 = centerY;
1540
1541            final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args);
1542            mCaller.sendMessage(message);
1543        }
1544
1545        public void onSoftKeyboardShowModeChanged(int showMode) {
1546          final Message message =
1547                  mCaller.obtainMessageI(DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED, showMode);
1548          mCaller.sendMessage(message);
1549        }
1550
1551        public void onPerformGestureResult(int sequence, boolean successfully) {
1552            Message message = mCaller.obtainMessageII(DO_GESTURE_COMPLETE, sequence,
1553                    successfully ? 1 : 0);
1554            mCaller.sendMessage(message);
1555        }
1556
1557        @Override
1558        public void executeMessage(Message message) {
1559            switch (message.what) {
1560                case DO_ON_ACCESSIBILITY_EVENT: {
1561                    AccessibilityEvent event = (AccessibilityEvent) message.obj;
1562                    if (event != null) {
1563                        AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);
1564                        mCallback.onAccessibilityEvent(event);
1565                        // Make sure the event is recycled.
1566                        try {
1567                            event.recycle();
1568                        } catch (IllegalStateException ise) {
1569                            /* ignore - best effort */
1570                        }
1571                    }
1572                } return;
1573
1574                case DO_ON_INTERRUPT: {
1575                    mCallback.onInterrupt();
1576                } return;
1577
1578                case DO_INIT: {
1579                    mConnectionId = message.arg1;
1580                    SomeArgs args = (SomeArgs) message.obj;
1581                    IAccessibilityServiceConnection connection =
1582                            (IAccessibilityServiceConnection) args.arg1;
1583                    IBinder windowToken = (IBinder) args.arg2;
1584                    args.recycle();
1585                    if (connection != null) {
1586                        AccessibilityInteractionClient.getInstance().addConnection(mConnectionId,
1587                                connection);
1588                        mCallback.init(mConnectionId, windowToken);
1589                        mCallback.onServiceConnected();
1590                    } else {
1591                        AccessibilityInteractionClient.getInstance().removeConnection(
1592                                mConnectionId);
1593                        mConnectionId = AccessibilityInteractionClient.NO_ID;
1594                        AccessibilityInteractionClient.getInstance().clearCache();
1595                        mCallback.init(AccessibilityInteractionClient.NO_ID, null);
1596                    }
1597                } return;
1598
1599                case DO_ON_GESTURE: {
1600                    final int gestureId = message.arg1;
1601                    mCallback.onGesture(gestureId);
1602                } return;
1603
1604                case DO_CLEAR_ACCESSIBILITY_CACHE: {
1605                    AccessibilityInteractionClient.getInstance().clearCache();
1606                } return;
1607
1608                case DO_ON_KEY_EVENT: {
1609                    KeyEvent event = (KeyEvent) message.obj;
1610                    try {
1611                        IAccessibilityServiceConnection connection = AccessibilityInteractionClient
1612                                .getInstance().getConnection(mConnectionId);
1613                        if (connection != null) {
1614                            final boolean result = mCallback.onKeyEvent(event);
1615                            final int sequence = message.arg1;
1616                            try {
1617                                connection.setOnKeyEventResult(result, sequence);
1618                            } catch (RemoteException re) {
1619                                /* ignore */
1620                            }
1621                        }
1622                    } finally {
1623                        // Make sure the event is recycled.
1624                        try {
1625                            event.recycle();
1626                        } catch (IllegalStateException ise) {
1627                            /* ignore - best effort */
1628                        }
1629                    }
1630                } return;
1631
1632                case DO_ON_MAGNIFICATION_CHANGED: {
1633                    final SomeArgs args = (SomeArgs) message.obj;
1634                    final Region region = (Region) args.arg1;
1635                    final float scale = (float) args.arg2;
1636                    final float centerX = (float) args.arg3;
1637                    final float centerY = (float) args.arg4;
1638                    mCallback.onMagnificationChanged(region, scale, centerX, centerY);
1639                } return;
1640
1641                case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: {
1642                    final int showMode = (int) message.arg1;
1643                    mCallback.onSoftKeyboardShowModeChanged(showMode);
1644                } return;
1645
1646                case DO_GESTURE_COMPLETE: {
1647                    final boolean successfully = message.arg2 == 1;
1648                    mCallback.onPerformGestureResult(message.arg1, successfully);
1649                } return;
1650
1651                default :
1652                    Log.w(LOG_TAG, "Unknown message type " + message.what);
1653            }
1654        }
1655    }
1656
1657    /**
1658     * Class used to report status of dispatched gestures
1659     */
1660    public static abstract class GestureResultCallback {
1661        /** Called when the gesture has completed successfully
1662         *
1663         * @param gestureDescription The description of the gesture that completed.
1664         */
1665        public void onCompleted(GestureDescription gestureDescription) {
1666        }
1667
1668        /** Called when the gesture was cancelled
1669         *
1670         * @param gestureDescription The description of the gesture that was cancelled.
1671         */
1672        public void onCancelled(GestureDescription gestureDescription) {
1673        }
1674    }
1675
1676    /* Object to keep track of gesture result callbacks */
1677    private static class GestureResultCallbackInfo {
1678        GestureDescription gestureDescription;
1679        GestureResultCallback callback;
1680        Handler handler;
1681
1682        GestureResultCallbackInfo(GestureDescription gestureDescription,
1683                GestureResultCallback callback, Handler handler) {
1684            this.gestureDescription = gestureDescription;
1685            this.callback = callback;
1686            this.handler = handler;
1687        }
1688    }
1689}
1690