• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.accessibilityservice;
18 
19 import android.accessibilityservice.GestureDescription.MotionEventGenerator;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.app.Service;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.ParceledListSlice;
27 import android.graphics.Region;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.Looper;
31 import android.os.Message;
32 import android.os.RemoteException;
33 import android.provider.Settings;
34 import android.util.ArrayMap;
35 import android.util.Log;
36 import android.util.Pair;
37 import android.util.Slog;
38 import android.util.SparseArray;
39 import android.view.KeyEvent;
40 import android.view.MotionEvent;
41 import android.view.WindowManager;
42 import android.view.WindowManagerImpl;
43 import android.view.accessibility.AccessibilityEvent;
44 import android.view.accessibility.AccessibilityInteractionClient;
45 import android.view.accessibility.AccessibilityNodeInfo;
46 import android.view.accessibility.AccessibilityWindowInfo;
47 
48 import com.android.internal.os.HandlerCaller;
49 import com.android.internal.os.SomeArgs;
50 
51 import java.lang.annotation.Retention;
52 import java.lang.annotation.RetentionPolicy;
53 import 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  */
219 public 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 {
onAccessibilityEvent(AccessibilityEvent event)367         public void onAccessibilityEvent(AccessibilityEvent event);
onInterrupt()368         public void onInterrupt();
onServiceConnected()369         public void onServiceConnected();
init(int connectionId, IBinder windowToken)370         public void init(int connectionId, IBinder windowToken);
onGesture(int gestureId)371         public boolean onGesture(int gestureId);
onKeyEvent(KeyEvent event)372         public boolean onKeyEvent(KeyEvent event);
onMagnificationChanged(@onNull Region region, float scale, float centerX, float centerY)373         public void onMagnificationChanged(@NonNull Region region,
374                 float scale, float centerX, float centerY);
onSoftKeyboardShowModeChanged(int showMode)375         public void onSoftKeyboardShowModeChanged(int showMode);
onPerformGestureResult(int sequence, boolean completedSuccessfully)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      */
onAccessibilityEvent(AccessibilityEvent event)413     public abstract void onAccessibilityEvent(AccessibilityEvent event);
414 
415     /**
416      * Callback for interrupting the accessibility feedback.
417      */
onInterrupt()418     public abstract void onInterrupt();
419 
420     /**
421      * Dispatches service connection to internal components first, then the
422      * client code.
423      */
dispatchServiceConnected()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      */
onServiceConnected()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      */
onGesture(int gestureId)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      */
onKeyEvent(KeyEvent event)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      */
getWindows()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      */
getRootInActiveWindow()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      */
disableSelf()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
getMagnificationController()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      */
dispatchGesture(@onNull GestureDescription gesture, @Nullable GestureResultCallback callback, @Nullable Handler handler)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 
onPerformGestureResult(int sequence, final boolean completedSuccessfully)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 
onMagnificationChanged(@onNull Region region, float scale, float centerX, float centerY)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 
MagnificationController(@onNull AccessibilityService service, @NonNull Object lock)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          */
onServiceConnected()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          */
addListener(@onNull OnMagnificationChangedListener listener)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          */
addListener(@onNull OnMagnificationChangedListener listener, @Nullable Handler handler)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          */
removeListener(@onNull OnMagnificationChangedListener listener)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 
setMagnificationCallbackEnabled(boolean enabled)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          */
dispatchMagnificationChanged(final @NonNull Region region, final float scale, final float centerX, final float centerY)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          */
getScale()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          */
getCenterX()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          */
getCenterY()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
getMagnificationRegion()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          */
reset(boolean animate)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          */
setScale(float scale, boolean animate)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          */
setCenter(float centerX, float centerY, boolean animate)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              */
onMagnificationChanged(@onNull MagnificationController controller, @NonNull Region region, float scale, float centerX, float centerY)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
getSoftKeyboardController()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 
onSoftKeyboardShowModeChanged(int showMode)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 
SoftKeyboardController(@onNull AccessibilityService service, @NonNull Object lock)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          */
onServiceConnected()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          */
addOnShowModeChangedListener(@onNull OnShowModeChangedListener listener)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          */
addOnShowModeChangedListener(@onNull OnShowModeChangedListener listener, @Nullable Handler handler)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          */
removeOnShowModeChangedListener(@onNull OnShowModeChangedListener listener)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 
setSoftKeyboardCallbackEnabled(boolean enabled)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          */
dispatchSoftKeyboardShowModeChanged(final int showMode)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
getShowMode()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          */
setShowMode(@oftKeyboardShowMode int showMode)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             */
onShowModeChanged(@onNull SoftKeyboardController controller, @SoftKeyboardShowMode int showMode)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      */
performGlobalAction(int action)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      */
findFocus(int focus)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      */
getServiceInfo()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      */
setServiceInfo(AccessibilityServiceInfo info)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      */
sendServiceInfo()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
getSystemService(@erviceName @onNull String name)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
onBind(Intent intent)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