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