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