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