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