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