2 * Copyright (C) 2009 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.accessibilityservice;
19 import android.accessibilityservice.GestureDescription.MotionEventGenerator;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.RequiresPermission;
24 import android.app.Service;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.ParceledListSlice;
28 import android.graphics.Region;
29 import android.hardware.fingerprint.FingerprintManager;
30 import android.os.Handler;
31 import android.os.IBinder;
32 import android.os.Looper;
33 import android.os.Message;
34 import android.os.RemoteException;
35 import android.provider.Settings;
36 import android.util.ArrayMap;
37 import android.util.Log;
38 import android.util.Slog;
39 import android.util.SparseArray;
40 import android.view.KeyEvent;
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;
48 import com.android.internal.os.HandlerCaller;
49 import com.android.internal.os.SomeArgs;
51 import java.lang.annotation.Retention;
52 import java.lang.annotation.RetentionPolicy;
53 import java.util.List;
55 import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
58 * Accessibility services should only be used to assist users with disabilities in using
59 * Android devices and apps. They run in the background and receive callbacks by the system
60 * when {@link AccessibilityEvent}s are fired. Such events denote some state transition
61 * in the user interface, for example, the focus has changed, a button has been clicked,
62 * etc. Such a service can optionally request the capability for querying the content
63 * of the active window. Development of an accessibility service requires extending this
64 * class and implementing its abstract methods.
66 * <div class="special reference">
67 * <h3>Developer Guides</h3>
68 * <p>For more information about creating AccessibilityServices, read the
69 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
70 * developer guide.</p>
75 * The lifecycle of an accessibility service is managed exclusively by the system and
76 * follows the established service life cycle. Starting an accessibility service is triggered
77 * exclusively by the user explicitly turning the service on in device settings. After the system
78 * binds to a service, it calls {@link AccessibilityService#onServiceConnected()}. This method can
79 * be overriden by clients that want to perform post binding setup.
82 * An accessibility service stops either when the user turns it off in device settings or when
83 * it calls {@link AccessibilityService#disableSelf()}.
85 * <h3>Declaration</h3>
87 * An accessibility is declared as any other service in an AndroidManifest.xml, but it
91 * Specify that it handles the "android.accessibilityservice.AccessibilityService"
92 * {@link android.content.Intent}.
95 * Request the {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission to
96 * ensure that only the system can bind to it.
99 * If either of these items is missing, the system will ignore the accessibility service.
100 * Following is an example declaration:
102 * <pre> <service android:name=".MyAccessibilityService"
103 * android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
104 * <intent-filter>
105 * <action android:name="android.accessibilityservice.AccessibilityService" />
106 * </intent-filter>
108 * </service></pre>
109 * <h3>Configuration</h3>
111 * An accessibility service can be configured to receive specific types of accessibility events,
112 * listen only to specific packages, get events from each type only once in a given time frame,
113 * retrieve window content, specify a settings activity, etc.
116 * There are two approaches for configuring an accessibility service:
120 * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring
121 * the service. A service declaration with a meta-data tag is presented below:
122 * <pre> <service android:name=".MyAccessibilityService">
123 * <intent-filter>
124 * <action android:name="android.accessibilityservice.AccessibilityService" />
125 * </intent-filter>
126 * <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" />
127 * </service></pre>
129 * <strong>Note:</strong> This approach enables setting all properties.
132 * For more details refer to {@link #SERVICE_META_DATA} and
133 * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code>.
137 * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note
138 * that this method can be called any time to dynamically change the service configuration.
140 * <strong>Note:</strong> This approach enables setting only dynamically configurable properties:
141 * {@link AccessibilityServiceInfo#eventTypes},
142 * {@link AccessibilityServiceInfo#feedbackType},
143 * {@link AccessibilityServiceInfo#flags},
144 * {@link AccessibilityServiceInfo#notificationTimeout},
145 * {@link AccessibilityServiceInfo#packageNames}
148 * For more details refer to {@link AccessibilityServiceInfo}.
152 * <h3>Retrieving window content</h3>
154 * A service can specify in its declaration that it can retrieve window
155 * content which is represented as a tree of {@link AccessibilityWindowInfo} and
156 * {@link AccessibilityNodeInfo} objects. Note that
157 * declaring this capability requires that the service declares its configuration via
158 * an XML resource referenced by {@link #SERVICE_META_DATA}.
161 * Window content may be retrieved with
162 * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()},
163 * {@link AccessibilityService#findFocus(int)},
164 * {@link AccessibilityService#getWindows()}, or
165 * {@link AccessibilityService#getRootInActiveWindow()}.
168 * <strong>Note</strong> An accessibility service may have requested to be notified for
169 * a subset of the event types, and thus be unaware when the node hierarchy has changed. It is also
170 * possible for a node to contain outdated information because the window content may change at any
173 * <h3>Notification strategy</h3>
175 * All accessibility services are notified of all events they have requested, regardless of their
179 * <strong>Note:</strong> The event notification timeout is useful to avoid propagating
180 * events to the client too frequently since this is accomplished via an expensive
181 * interprocess call. One can think of the timeout as a criteria to determine when
182 * event generation has settled down.</p>
183 * <h3>Event types</h3>
185 * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}</li>
186 * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}</li>
187 * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}</li>
188 * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}</li>
189 * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}</li>
190 * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}</li>
191 * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}</li>
192 * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}</li>
193 * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}</li>
194 * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}</li>
195 * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}</li>
196 * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}</li>
197 * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}</li>
198 * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}</li>
199 * <li>{@link AccessibilityEvent#TYPE_ANNOUNCEMENT}</li>
200 * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_START}</li>
201 * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_END}</li>
202 * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_START}</li>
203 * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_END}</li>
204 * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED}</li>
205 * <li>{@link AccessibilityEvent#TYPE_WINDOWS_CHANGED}</li>
206 * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED}</li>
208 * <h3>Feedback types</h3>
210 * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li>
211 * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}</li>
212 * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li>
213 * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}</li>
214 * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}</li>
215 * <li>{@link AccessibilityServiceInfo#FEEDBACK_BRAILLE}</li>
217 * @see AccessibilityEvent
218 * @see AccessibilityServiceInfo
219 * @see android.view.accessibility.AccessibilityManager
221 public abstract class AccessibilityService extends Service {
224 * The user has performed a swipe up gesture on the touch screen.
226 public static final int GESTURE_SWIPE_UP = 1;
229 * The user has performed a swipe down gesture on the touch screen.
231 public static final int GESTURE_SWIPE_DOWN = 2;
234 * The user has performed a swipe left gesture on the touch screen.
236 public static final int GESTURE_SWIPE_LEFT = 3;
239 * The user has performed a swipe right gesture on the touch screen.
241 public static final int GESTURE_SWIPE_RIGHT = 4;
244 * The user has performed a swipe left and right gesture on the touch screen.
246 public static final int GESTURE_SWIPE_LEFT_AND_RIGHT = 5;
249 * The user has performed a swipe right and left gesture on the touch screen.
251 public static final int GESTURE_SWIPE_RIGHT_AND_LEFT = 6;
254 * The user has performed a swipe up and down gesture on the touch screen.
256 public static final int GESTURE_SWIPE_UP_AND_DOWN = 7;
259 * The user has performed a swipe down and up gesture on the touch screen.
261 public static final int GESTURE_SWIPE_DOWN_AND_UP = 8;
264 * The user has performed a left and up gesture on the touch screen.
266 public static final int GESTURE_SWIPE_LEFT_AND_UP = 9;
269 * The user has performed a left and down gesture on the touch screen.
271 public static final int GESTURE_SWIPE_LEFT_AND_DOWN = 10;
274 * The user has performed a right and up gesture on the touch screen.
276 public static final int GESTURE_SWIPE_RIGHT_AND_UP = 11;
279 * The user has performed a right and down gesture on the touch screen.
281 public static final int GESTURE_SWIPE_RIGHT_AND_DOWN = 12;
284 * The user has performed an up and left gesture on the touch screen.
286 public static final int GESTURE_SWIPE_UP_AND_LEFT = 13;
289 * The user has performed an up and right gesture on the touch screen.
291 public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14;
294 * The user has performed an down and left gesture on the touch screen.
296 public static final int GESTURE_SWIPE_DOWN_AND_LEFT = 15;
299 * The user has performed an down and right gesture on the touch screen.
301 public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16;
304 * The {@link Intent} that must be declared as handled by the service.
306 public static final String SERVICE_INTERFACE =
307 "android.accessibilityservice.AccessibilityService";
310 * Name under which an AccessibilityService component publishes information
311 * about itself. This meta-data must reference an XML resource containing an
312 * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code>
313 * tag. This is a a sample XML file configuring an accessibility service:
314 * <pre> <accessibility-service
315 * android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
316 * android:packageNames="foo.bar, foo.baz"
317 * android:accessibilityFeedbackType="feedbackSpoken"
318 * android:notificationTimeout="100"
319 * android:accessibilityFlags="flagDefault"
320 * android:settingsActivity="foo.bar.TestBackActivity"
321 * android:canRetrieveWindowContent="true"
322 * android:canRequestTouchExplorationMode="true"
326 public static final String SERVICE_META_DATA = "android.accessibilityservice";
331 public static final int GLOBAL_ACTION_BACK = 1;
336 public static final int GLOBAL_ACTION_HOME = 2;
339 * Action to toggle showing the overview of recent apps. Will fail on platforms that don't
342 public static final int GLOBAL_ACTION_RECENTS = 3;
345 * Action to open the notifications.
347 public static final int GLOBAL_ACTION_NOTIFICATIONS = 4;
350 * Action to open the quick settings.
352 public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5;
355 * Action to open the power long-press dialog.
357 public static final int GLOBAL_ACTION_POWER_DIALOG = 6;
360 * Action to toggle docking the current app's window
362 public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7;
364 private static final String LOG_TAG = "AccessibilityService";
367 * Interface used by IAccessibilityServiceWrapper to call the service from its main thread.
370 public interface Callbacks {
371 void onAccessibilityEvent(AccessibilityEvent event);
373 void onServiceConnected();
374 void init(int connectionId, IBinder windowToken);
375 boolean onGesture(int gestureId);
376 boolean onKeyEvent(KeyEvent event);
377 void onMagnificationChanged(@NonNull Region region,
378 float scale, float centerX, float centerY);
379 void onSoftKeyboardShowModeChanged(int showMode);
380 void onPerformGestureResult(int sequence, boolean completedSuccessfully);
381 void onFingerprintCapturingGesturesChanged(boolean active);
382 void onFingerprintGesture(int gesture);
383 void onAccessibilityButtonClicked();
384 void onAccessibilityButtonAvailabilityChanged(boolean available);
388 * Annotations for Soft Keyboard show modes so tools can catch invalid show modes.
391 @Retention(RetentionPolicy.SOURCE)
392 @IntDef({SHOW_MODE_AUTO, SHOW_MODE_HIDDEN})
393 public @interface SoftKeyboardShowMode {};
394 public static final int SHOW_MODE_AUTO = 0;
395 public static final int SHOW_MODE_HIDDEN = 1;
397 private int mConnectionId;
399 private AccessibilityServiceInfo mInfo;
401 private IBinder mWindowToken;
403 private WindowManager mWindowManager;
405 private MagnificationController mMagnificationController;
406 private SoftKeyboardController mSoftKeyboardController;
407 private AccessibilityButtonController mAccessibilityButtonController;
409 private int mGestureStatusCallbackSequence;
411 private SparseArray<GestureResultCallbackInfo> mGestureStatusCallbackInfos;
413 private final Object mLock = new Object();
415 private FingerprintGestureController mFingerprintGestureController;
418 * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
420 * @param event The new event. This event is owned by the caller and cannot be used after
421 * this method returns. Services wishing to use the event after this method returns should
424 public abstract void onAccessibilityEvent(AccessibilityEvent event);
427 * Callback for interrupting the accessibility feedback.
429 public abstract void onInterrupt();
432 * Dispatches service connection to internal components first, then the
435 private void dispatchServiceConnected() {
436 if (mMagnificationController != null) {
437 mMagnificationController.onServiceConnected();
439 if (mSoftKeyboardController != null) {
440 mSoftKeyboardController.onServiceConnected();
443 // The client gets to handle service connection last, after we've set
444 // up any state upon which their code may rely.
445 onServiceConnected();
449 * This method is a part of the {@link AccessibilityService} lifecycle and is
450 * called after the system has successfully bound to the service. If is
451 * convenient to use this method for setting the {@link AccessibilityServiceInfo}.
453 * @see AccessibilityServiceInfo
454 * @see #setServiceInfo(AccessibilityServiceInfo)
456 protected void onServiceConnected() {
461 * Called by the system when the user performs a specific gesture on the
464 * <strong>Note:</strong> To receive gestures an accessibility service must
465 * request that the device is in touch exploration mode by setting the
466 * {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE}
469 * @param gestureId The unique id of the performed gesture.
471 * @return Whether the gesture was handled.
473 * @see #GESTURE_SWIPE_UP
474 * @see #GESTURE_SWIPE_UP_AND_LEFT
475 * @see #GESTURE_SWIPE_UP_AND_DOWN
476 * @see #GESTURE_SWIPE_UP_AND_RIGHT
477 * @see #GESTURE_SWIPE_DOWN
478 * @see #GESTURE_SWIPE_DOWN_AND_LEFT
479 * @see #GESTURE_SWIPE_DOWN_AND_UP
480 * @see #GESTURE_SWIPE_DOWN_AND_RIGHT
481 * @see #GESTURE_SWIPE_LEFT
482 * @see #GESTURE_SWIPE_LEFT_AND_UP
483 * @see #GESTURE_SWIPE_LEFT_AND_RIGHT
484 * @see #GESTURE_SWIPE_LEFT_AND_DOWN
485 * @see #GESTURE_SWIPE_RIGHT
486 * @see #GESTURE_SWIPE_RIGHT_AND_UP
487 * @see #GESTURE_SWIPE_RIGHT_AND_LEFT
488 * @see #GESTURE_SWIPE_RIGHT_AND_DOWN
490 protected boolean onGesture(int gestureId) {
495 * Callback that allows an accessibility service to observe the key events
496 * before they are passed to the rest of the system. This means that the events
497 * are first delivered here before they are passed to the device policy, the
498 * input method, or applications.
500 * <strong>Note:</strong> It is important that key events are handled in such
501 * a way that the event stream that would be passed to the rest of the system
502 * is well-formed. For example, handling the down event but not the up event
503 * and vice versa would generate an inconsistent event stream.
506 * <strong>Note:</strong> The key events delivered in this method are copies
507 * and modifying them will have no effect on the events that will be passed
508 * to the system. This method is intended to perform purely filtering
512 * @param event The event to be processed. This event is owned by the caller and cannot be used
513 * after this method returns. Services wishing to use the event after this method returns should
515 * @return If true then the event will be consumed and not delivered to
516 * applications, otherwise it will be delivered as usual.
518 protected boolean onKeyEvent(KeyEvent event) {
523 * Gets the windows on the screen. This method returns only the windows
524 * that a sighted user can interact with, as opposed to all windows.
525 * For example, if there is a modal dialog shown and the user cannot touch
526 * anything behind it, then only the modal window will be reported
527 * (assuming it is the top one). For convenience the returned windows
528 * are ordered in a descending layer order, which is the windows that
529 * are higher in the Z-order are reported first. Since the user can always
530 * interact with the window that has input focus by typing, the focused
531 * window is always returned (even if covered by a modal window).
533 * <strong>Note:</strong> In order to access the windows your service has
534 * to declare the capability to retrieve window content by setting the
535 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
536 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
537 * Also the service has to opt-in to retrieve the interactive windows by
538 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
542 * @return The windows if there are windows and the service is can retrieve
543 * them, otherwise an empty list.
545 public List<AccessibilityWindowInfo> getWindows() {
546 return AccessibilityInteractionClient.getInstance().getWindows(mConnectionId);
550 * Gets the root node in the currently active window if this service
551 * can retrieve window content. The active window is the one that the user
552 * is currently touching or the window with input focus, if the user is not
553 * touching any window.
555 * The currently active window is defined as the window that most recently fired one
556 * of the following events:
557 * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED},
558 * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
559 * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}.
560 * In other words, the last window shown that also has input focus.
563 * <strong>Note:</strong> In order to access the root node your service has
564 * to declare the capability to retrieve window content by setting the
565 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
566 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
569 * @return The root node if this service can retrieve window content.
571 public AccessibilityNodeInfo getRootInActiveWindow() {
572 return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId);
576 * Disables the service. After calling this method, the service will be disabled and settings
577 * will show that it is turned off.
579 public final void disableSelf() {
580 final IAccessibilityServiceConnection connection =
581 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
582 if (connection != null) {
584 connection.disableSelf();
585 } catch (RemoteException re) {
586 throw new RuntimeException(re);
592 * Returns the magnification controller, which may be used to query and
593 * modify the state of display magnification.
595 * <strong>Note:</strong> In order to control magnification, your service
596 * must declare the capability by setting the
597 * {@link android.R.styleable#AccessibilityService_canControlMagnification}
598 * property in its meta-data. For more information, see
599 * {@link #SERVICE_META_DATA}.
601 * @return the magnification controller
604 public final MagnificationController getMagnificationController() {
605 synchronized (mLock) {
606 if (mMagnificationController == null) {
607 mMagnificationController = new MagnificationController(this, mLock);
609 return mMagnificationController;
614 * Get the controller for fingerprint gestures. This feature requires {@link
615 * AccessibilityServiceInfo#CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES}.
617 *<strong>Note: </strong> The service must be connected before this method is called.
619 * @return The controller for fingerprint gestures, or {@code null} if gestures are unavailable.
621 @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT)
622 public final @NonNull FingerprintGestureController getFingerprintGestureController() {
623 if (mFingerprintGestureController == null) {
624 mFingerprintGestureController = new FingerprintGestureController(
625 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId));
627 return mFingerprintGestureController;
631 * Dispatch a gesture to the touch screen. Any gestures currently in progress, whether from
632 * the user, this service, or another service, will be cancelled.
634 * The gesture will be dispatched as if it were performed directly on the screen by a user, so
635 * the events may be affected by features such as magnification and explore by touch.
638 * <strong>Note:</strong> In order to dispatch gestures, your service
639 * must declare the capability by setting the
640 * {@link android.R.styleable#AccessibilityService_canPerformGestures}
641 * property in its meta-data. For more information, see
642 * {@link #SERVICE_META_DATA}.
645 * @param gesture The gesture to dispatch
646 * @param callback The object to call back when the status of the gesture is known. If
647 * {@code null}, no status is reported.
648 * @param handler The handler on which to call back the {@code callback} object. If
649 * {@code null}, the object is called back on the service's main thread.
651 * @return {@code true} if the gesture is dispatched, {@code false} if not.
653 public final boolean dispatchGesture(@NonNull GestureDescription gesture,
654 @Nullable GestureResultCallback callback,
655 @Nullable Handler handler) {
656 final IAccessibilityServiceConnection connection =
657 AccessibilityInteractionClient.getInstance().getConnection(
659 if (connection == null) {
662 List<GestureDescription.GestureStep> steps =
663 MotionEventGenerator.getGestureStepsFromGestureDescription(gesture, 100);
665 synchronized (mLock) {
666 mGestureStatusCallbackSequence++;
667 if (callback != null) {
668 if (mGestureStatusCallbackInfos == null) {
669 mGestureStatusCallbackInfos = new SparseArray<>();
671 GestureResultCallbackInfo callbackInfo = new GestureResultCallbackInfo(gesture,
673 mGestureStatusCallbackInfos.put(mGestureStatusCallbackSequence, callbackInfo);
675 connection.sendGesture(mGestureStatusCallbackSequence,
676 new ParceledListSlice<>(steps));
678 } catch (RemoteException re) {
679 throw new RuntimeException(re);
684 void onPerformGestureResult(int sequence, final boolean completedSuccessfully) {
685 if (mGestureStatusCallbackInfos == null) {
688 GestureResultCallbackInfo callbackInfo;
689 synchronized (mLock) {
690 callbackInfo = mGestureStatusCallbackInfos.get(sequence);
692 final GestureResultCallbackInfo finalCallbackInfo = callbackInfo;
693 if ((callbackInfo != null) && (callbackInfo.gestureDescription != null)
694 && (callbackInfo.callback != null)) {
695 if (callbackInfo.handler != null) {
696 callbackInfo.handler.post(new Runnable() {
699 if (completedSuccessfully) {
700 finalCallbackInfo.callback
701 .onCompleted(finalCallbackInfo.gestureDescription);
703 finalCallbackInfo.callback
704 .onCancelled(finalCallbackInfo.gestureDescription);
710 if (completedSuccessfully) {
711 callbackInfo.callback.onCompleted(callbackInfo.gestureDescription);
713 callbackInfo.callback.onCancelled(callbackInfo.gestureDescription);
718 private void onMagnificationChanged(@NonNull Region region, float scale,
719 float centerX, float centerY) {
720 if (mMagnificationController != null) {
721 mMagnificationController.dispatchMagnificationChanged(
722 region, scale, centerX, centerY);
727 * Callback for fingerprint gesture handling
728 * @param active If gesture detection is active
730 private void onFingerprintCapturingGesturesChanged(boolean active) {
731 getFingerprintGestureController().onGestureDetectionActiveChanged(active);
735 * Callback for fingerprint gesture handling
736 * @param gesture The identifier for the gesture performed
738 private void onFingerprintGesture(int gesture) {
739 getFingerprintGestureController().onGesture(gesture);
743 * Used to control and query the state of display magnification.
745 public static final class MagnificationController {
746 private final AccessibilityService mService;
749 * Map of listeners to their handlers. Lazily created when adding the
750 * first magnification listener.
752 private ArrayMap<OnMagnificationChangedListener, Handler> mListeners;
753 private final Object mLock;
755 MagnificationController(@NonNull AccessibilityService service, @NonNull Object lock) {
761 * Called when the service is connected.
763 void onServiceConnected() {
764 synchronized (mLock) {
765 if (mListeners != null && !mListeners.isEmpty()) {
766 setMagnificationCallbackEnabled(true);
772 * Adds the specified change listener to the list of magnification
773 * change listeners. The callback will occur on the service's main
776 * @param listener the listener to add, must be non-{@code null}
778 public void addListener(@NonNull OnMagnificationChangedListener listener) {
779 addListener(listener, null);
783 * Adds the specified change listener to the list of magnification
784 * change listeners. The callback will occur on the specified
785 * {@link Handler}'s thread, or on the service's main thread if the
786 * handler is {@code null}.
788 * @param listener the listener to add, must be non-null
789 * @param handler the handler on which the callback should execute, or
790 * {@code null} to execute on the service's main thread
792 public void addListener(@NonNull OnMagnificationChangedListener listener,
793 @Nullable Handler handler) {
794 synchronized (mLock) {
795 if (mListeners == null) {
796 mListeners = new ArrayMap<>();
799 final boolean shouldEnableCallback = mListeners.isEmpty();
800 mListeners.put(listener, handler);
802 if (shouldEnableCallback) {
803 // This may fail if the service is not connected yet, but if we
804 // still have listeners when it connects then we can try again.
805 setMagnificationCallbackEnabled(true);
811 * Removes the specified change listener from the list of magnification change listeners.
813 * @param listener the listener to remove, must be non-null
814 * @return {@code true} if the listener was removed, {@code false} otherwise
816 public boolean removeListener(@NonNull OnMagnificationChangedListener listener) {
817 if (mListeners == null) {
821 synchronized (mLock) {
822 final int keyIndex = mListeners.indexOfKey(listener);
823 final boolean hasKey = keyIndex >= 0;
825 mListeners.removeAt(keyIndex);
828 if (hasKey && mListeners.isEmpty()) {
829 // We just removed the last listener, so we don't need
830 // callbacks from the service anymore.
831 setMagnificationCallbackEnabled(false);
838 private void setMagnificationCallbackEnabled(boolean enabled) {
839 final IAccessibilityServiceConnection connection =
840 AccessibilityInteractionClient.getInstance().getConnection(
841 mService.mConnectionId);
842 if (connection != null) {
844 connection.setMagnificationCallbackEnabled(enabled);
845 } catch (RemoteException re) {
846 throw new RuntimeException(re);
852 * Dispatches magnification changes to any registered listeners. This
853 * should be called on the service's main thread.
855 void dispatchMagnificationChanged(final @NonNull Region region, final float scale,
856 final float centerX, final float centerY) {
857 final ArrayMap<OnMagnificationChangedListener, Handler> entries;
858 synchronized (mLock) {
859 if (mListeners == null || mListeners.isEmpty()) {
860 Slog.d(LOG_TAG, "Received magnification changed "
861 + "callback with no listeners registered!");
862 setMagnificationCallbackEnabled(false);
866 // Listeners may remove themselves. Perform a shallow copy to avoid concurrent
868 entries = new ArrayMap<>(mListeners);
871 for (int i = 0, count = entries.size(); i < count; i++) {
872 final OnMagnificationChangedListener listener = entries.keyAt(i);
873 final Handler handler = entries.valueAt(i);
874 if (handler != null) {
875 handler.post(new Runnable() {
878 listener.onMagnificationChanged(MagnificationController.this,
879 region, scale, centerX, centerY);
883 // We're already on the main thread, just run the listener.
884 listener.onMagnificationChanged(this, region, scale, centerX, centerY);
890 * Returns the current magnification scale.
892 * <strong>Note:</strong> If the service is not yet connected (e.g.
893 * {@link AccessibilityService#onServiceConnected()} has not yet been
894 * called) or the service has been disconnected, this method will
895 * return a default value of {@code 1.0f}.
897 * @return the current magnification scale
899 public float getScale() {
900 final IAccessibilityServiceConnection connection =
901 AccessibilityInteractionClient.getInstance().getConnection(
902 mService.mConnectionId);
903 if (connection != null) {
905 return connection.getMagnificationScale();
906 } catch (RemoteException re) {
907 Log.w(LOG_TAG, "Failed to obtain scale", re);
908 re.rethrowFromSystemServer();
915 * Returns the unscaled screen-relative X coordinate of the focal
916 * center of the magnified region. This is the point around which
917 * zooming occurs and is guaranteed to lie within the magnified
920 * <strong>Note:</strong> If the service is not yet connected (e.g.
921 * {@link AccessibilityService#onServiceConnected()} has not yet been
922 * called) or the service has been disconnected, this method will
923 * return a default value of {@code 0.0f}.
925 * @return the unscaled screen-relative X coordinate of the center of
926 * the magnified region
928 public float getCenterX() {
929 final IAccessibilityServiceConnection connection =
930 AccessibilityInteractionClient.getInstance().getConnection(
931 mService.mConnectionId);
932 if (connection != null) {
934 return connection.getMagnificationCenterX();
935 } catch (RemoteException re) {
936 Log.w(LOG_TAG, "Failed to obtain center X", re);
937 re.rethrowFromSystemServer();
944 * Returns the unscaled screen-relative Y coordinate of the focal
945 * center of the magnified region. This is the point around which
946 * zooming occurs and is guaranteed to lie within the magnified
949 * <strong>Note:</strong> If the service is not yet connected (e.g.
950 * {@link AccessibilityService#onServiceConnected()} has not yet been
951 * called) or the service has been disconnected, this method will
952 * return a default value of {@code 0.0f}.
954 * @return the unscaled screen-relative Y coordinate of the center of
955 * the magnified region
957 public float getCenterY() {
958 final IAccessibilityServiceConnection connection =
959 AccessibilityInteractionClient.getInstance().getConnection(
960 mService.mConnectionId);
961 if (connection != null) {
963 return connection.getMagnificationCenterY();
964 } catch (RemoteException re) {
965 Log.w(LOG_TAG, "Failed to obtain center Y", re);
966 re.rethrowFromSystemServer();
973 * Returns the region of the screen currently active for magnification. Changes to
974 * magnification scale and center only affect this portion of the screen. The rest of the
975 * screen, for example input methods, cannot be magnified. This region is relative to the
976 * unscaled screen and is independent of the scale and center point.
978 * The returned region will be empty if magnification is not active. Magnification is active
979 * if magnification gestures are enabled or if a service is running that can control
982 * <strong>Note:</strong> If the service is not yet connected (e.g.
983 * {@link AccessibilityService#onServiceConnected()} has not yet been
984 * called) or the service has been disconnected, this method will
985 * return an empty region.
987 * @return the region of the screen currently active for magnification, or an empty region
988 * if magnification is not active.
991 public Region getMagnificationRegion() {
992 final IAccessibilityServiceConnection connection =
993 AccessibilityInteractionClient.getInstance().getConnection(
994 mService.mConnectionId);
995 if (connection != null) {
997 return connection.getMagnificationRegion();
998 } catch (RemoteException re) {
999 Log.w(LOG_TAG, "Failed to obtain magnified region", re);
1000 re.rethrowFromSystemServer();
1003 return Region.obtain();
1007 * Resets magnification scale and center to their default (e.g. no
1008 * magnification) values.
1010 * <strong>Note:</strong> If the service is not yet connected (e.g.
1011 * {@link AccessibilityService#onServiceConnected()} has not yet been
1012 * called) or the service has been disconnected, this method will have
1013 * no effect and return {@code false}.
1015 * @param animate {@code true} to animate from the current scale and
1016 * center or {@code false} to reset the scale and center
1018 * @return {@code true} on success, {@code false} on failure
1020 public boolean reset(boolean animate) {
1021 final IAccessibilityServiceConnection connection =
1022 AccessibilityInteractionClient.getInstance().getConnection(
1023 mService.mConnectionId);
1024 if (connection != null) {
1026 return connection.resetMagnification(animate);
1027 } catch (RemoteException re) {
1028 Log.w(LOG_TAG, "Failed to reset", re);
1029 re.rethrowFromSystemServer();
1036 * Sets the magnification scale.
1038 * <strong>Note:</strong> If the service is not yet connected (e.g.
1039 * {@link AccessibilityService#onServiceConnected()} has not yet been
1040 * called) or the service has been disconnected, this method will have
1041 * no effect and return {@code false}.
1043 * @param scale the magnification scale to set, must be >= 1 and <= 5
1044 * @param animate {@code true} to animate from the current scale or
1045 * {@code false} to set the scale immediately
1046 * @return {@code true} on success, {@code false} on failure
1048 public boolean setScale(float scale, boolean animate) {
1049 final IAccessibilityServiceConnection connection =
1050 AccessibilityInteractionClient.getInstance().getConnection(
1051 mService.mConnectionId);
1052 if (connection != null) {
1054 return connection.setMagnificationScaleAndCenter(
1055 scale, Float.NaN, Float.NaN, animate);
1056 } catch (RemoteException re) {
1057 Log.w(LOG_TAG, "Failed to set scale", re);
1058 re.rethrowFromSystemServer();
1065 * Sets the center of the magnified viewport.
1067 * <strong>Note:</strong> If the service is not yet connected (e.g.
1068 * {@link AccessibilityService#onServiceConnected()} has not yet been
1069 * called) or the service has been disconnected, this method will have
1070 * no effect and return {@code false}.
1072 * @param centerX the unscaled screen-relative X coordinate on which to
1073 * center the viewport
1074 * @param centerY the unscaled screen-relative Y coordinate on which to
1075 * center the viewport
1076 * @param animate {@code true} to animate from the current viewport
1077 * center or {@code false} to set the center immediately
1078 * @return {@code true} on success, {@code false} on failure
1080 public boolean setCenter(float centerX, float centerY, boolean animate) {
1081 final IAccessibilityServiceConnection connection =
1082 AccessibilityInteractionClient.getInstance().getConnection(
1083 mService.mConnectionId);
1084 if (connection != null) {
1086 return connection.setMagnificationScaleAndCenter(
1087 Float.NaN, centerX, centerY, animate);
1088 } catch (RemoteException re) {
1089 Log.w(LOG_TAG, "Failed to set center", re);
1090 re.rethrowFromSystemServer();
1097 * Listener for changes in the state of magnification.
1099 public interface OnMagnificationChangedListener {
1101 * Called when the magnified region, scale, or center changes.
1103 * @param controller the magnification controller
1104 * @param region the magnification region
1105 * @param scale the new scale
1106 * @param centerX the new X coordinate, in unscaled coordinates, around which
1107 * magnification is focused
1108 * @param centerY the new Y coordinate, in unscaled coordinates, around which
1109 * magnification is focused
1111 void onMagnificationChanged(@NonNull MagnificationController controller,
1112 @NonNull Region region, float scale, float centerX, float centerY);
1117 * Returns the soft keyboard controller, which may be used to query and modify the soft keyboard
1120 * @return the soft keyboard controller
1123 public final SoftKeyboardController getSoftKeyboardController() {
1124 synchronized (mLock) {
1125 if (mSoftKeyboardController == null) {
1126 mSoftKeyboardController = new SoftKeyboardController(this, mLock);
1128 return mSoftKeyboardController;
1132 private void onSoftKeyboardShowModeChanged(int showMode) {
1133 if (mSoftKeyboardController != null) {
1134 mSoftKeyboardController.dispatchSoftKeyboardShowModeChanged(showMode);
1139 * Used to control and query the soft keyboard show mode.
1141 public static final class SoftKeyboardController {
1142 private final AccessibilityService mService;
1145 * Map of listeners to their handlers. Lazily created when adding the first
1146 * soft keyboard change listener.
1148 private ArrayMap<OnShowModeChangedListener, Handler> mListeners;
1149 private final Object mLock;
1151 SoftKeyboardController(@NonNull AccessibilityService service, @NonNull Object lock) {
1157 * Called when the service is connected.
1159 void onServiceConnected() {
1160 synchronized(mLock) {
1161 if (mListeners != null && !mListeners.isEmpty()) {
1162 setSoftKeyboardCallbackEnabled(true);
1168 * Adds the specified change listener to the list of show mode change listeners. The
1169 * callback will occur on the service's main thread. Listener is not called on registration.
1171 public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) {
1172 addOnShowModeChangedListener(listener, null);
1176 * Adds the specified change listener to the list of soft keyboard show mode change
1177 * listeners. The callback will occur on the specified {@link Handler}'s thread, or on the
1178 * services's main thread if the handler is {@code null}.
1180 * @param listener the listener to add, must be non-null
1181 * @param handler the handler on which to callback should execute, or {@code null} to
1182 * execute on the service's main thread
1184 public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener,
1185 @Nullable Handler handler) {
1186 synchronized (mLock) {
1187 if (mListeners == null) {
1188 mListeners = new ArrayMap<>();
1191 final boolean shouldEnableCallback = mListeners.isEmpty();
1192 mListeners.put(listener, handler);
1194 if (shouldEnableCallback) {
1195 // This may fail if the service is not connected yet, but if we still have
1196 // listeners when it connects, we can try again.
1197 setSoftKeyboardCallbackEnabled(true);
1203 * Removes the specified change listener from the list of keyboard show mode change
1206 * @param listener the listener to remove, must be non-null
1207 * @return {@code true} if the listener was removed, {@code false} otherwise
1209 public boolean removeOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) {
1210 if (mListeners == null) {
1214 synchronized (mLock) {
1215 final int keyIndex = mListeners.indexOfKey(listener);
1216 final boolean hasKey = keyIndex >= 0;
1218 mListeners.removeAt(keyIndex);
1221 if (hasKey && mListeners.isEmpty()) {
1222 // We just removed the last listener, so we don't need callbacks from the
1224 setSoftKeyboardCallbackEnabled(false);
1231 private void setSoftKeyboardCallbackEnabled(boolean enabled) {
1232 final IAccessibilityServiceConnection connection =
1233 AccessibilityInteractionClient.getInstance().getConnection(
1234 mService.mConnectionId);
1235 if (connection != null) {
1237 connection.setSoftKeyboardCallbackEnabled(enabled);
1238 } catch (RemoteException re) {
1239 throw new RuntimeException(re);
1245 * Dispatches the soft keyboard show mode change to any registered listeners. This should
1246 * be called on the service's main thread.
1248 void dispatchSoftKeyboardShowModeChanged(final int showMode) {
1249 final ArrayMap<OnShowModeChangedListener, Handler> entries;
1250 synchronized (mLock) {
1251 if (mListeners == null || mListeners.isEmpty()) {
1252 Slog.w(LOG_TAG, "Received soft keyboard show mode changed callback"
1253 + " with no listeners registered!");
1254 setSoftKeyboardCallbackEnabled(false);
1258 // Listeners may remove themselves. Perform a shallow copy to avoid concurrent
1260 entries = new ArrayMap<>(mListeners);
1263 for (int i = 0, count = entries.size(); i < count; i++) {
1264 final OnShowModeChangedListener listener = entries.keyAt(i);
1265 final Handler handler = entries.valueAt(i);
1266 if (handler != null) {
1267 handler.post(new Runnable() {
1270 listener.onShowModeChanged(SoftKeyboardController.this, showMode);
1274 // We're already on the main thread, just run the listener.
1275 listener.onShowModeChanged(this, showMode);
1281 * Returns the show mode of the soft keyboard. The default show mode is
1282 * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
1283 * focused. An AccessibilityService can also request the show mode
1284 * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
1286 * @return the current soft keyboard show mode
1288 @SoftKeyboardShowMode
1289 public int getShowMode() {
1291 return Settings.Secure.getInt(mService.getContentResolver(),
1292 Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
1293 } catch (Settings.SettingNotFoundException e) {
1294 Log.v(LOG_TAG, "Failed to obtain the soft keyboard mode", e);
1295 // The settings hasn't been changed yet, so it's value is null. Return the default.
1301 * Sets the soft keyboard show mode. The default show mode is
1302 * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
1303 * focused. An AccessibilityService can also request the show mode
1304 * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. The
1305 * The lastto this method will be honored, regardless of any previous calls (including those
1306 * made by other AccessibilityServices).
1308 * <strong>Note:</strong> If the service is not yet connected (e.g.
1309 * {@link AccessibilityService#onServiceConnected()} has not yet been called) or the
1310 * service has been disconnected, this method will have no effect and return {@code false}.
1312 * @param showMode the new show mode for the soft keyboard
1313 * @return {@code true} on success
1315 public boolean setShowMode(@SoftKeyboardShowMode int showMode) {
1316 final IAccessibilityServiceConnection connection =
1317 AccessibilityInteractionClient.getInstance().getConnection(
1318 mService.mConnectionId);
1319 if (connection != null) {
1321 return connection.setSoftKeyboardShowMode(showMode);
1322 } catch (RemoteException re) {
1323 Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re);
1324 re.rethrowFromSystemServer();
1331 * Listener for changes in the soft keyboard show mode.
1333 public interface OnShowModeChangedListener {
1335 * Called when the soft keyboard behavior changes. The default show mode is
1336 * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
1337 * focused. An AccessibilityService can also request the show mode
1338 * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
1340 * @param controller the soft keyboard controller
1341 * @param showMode the current soft keyboard show mode
1343 void onShowModeChanged(@NonNull SoftKeyboardController controller,
1344 @SoftKeyboardShowMode int showMode);
1349 * Returns the controller for the accessibility button within the system's navigation area.
1350 * This instance may be used to query the accessibility button's state and register listeners
1351 * for interactions with and state changes for the accessibility button when
1352 * {@link AccessibilityServiceInfo#FLAG_REQUEST_ACCESSIBILITY_BUTTON} is set.
1354 * <strong>Note:</strong> Not all devices are capable of displaying the accessibility button
1355 * within a navigation area, and as such, use of this class should be considered only as an
1356 * optional feature or shortcut on supported device implementations.
1359 * @return the accessibility button controller for this {@link AccessibilityService}
1362 public final AccessibilityButtonController getAccessibilityButtonController() {
1363 synchronized (mLock) {
1364 if (mAccessibilityButtonController == null) {
1365 mAccessibilityButtonController = new AccessibilityButtonController(
1366 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId));
1368 return mAccessibilityButtonController;
1372 private void onAccessibilityButtonClicked() {
1373 getAccessibilityButtonController().dispatchAccessibilityButtonClicked();
1376 private void onAccessibilityButtonAvailabilityChanged(boolean available) {
1377 getAccessibilityButtonController().dispatchAccessibilityButtonAvailabilityChanged(
1382 * Performs a global action. Such an action can be performed
1383 * at any moment regardless of the current application or user
1384 * location in that application. For example going back, going
1385 * home, opening recents, etc.
1387 * @param action The action to perform.
1388 * @return Whether the action was successfully performed.
1390 * @see #GLOBAL_ACTION_BACK
1391 * @see #GLOBAL_ACTION_HOME
1392 * @see #GLOBAL_ACTION_NOTIFICATIONS
1393 * @see #GLOBAL_ACTION_RECENTS
1395 public final boolean performGlobalAction(int action) {
1396 IAccessibilityServiceConnection connection =
1397 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1398 if (connection != null) {
1400 return connection.performGlobalAction(action);
1401 } catch (RemoteException re) {
1402 Log.w(LOG_TAG, "Error while calling performGlobalAction", re);
1403 re.rethrowFromSystemServer();
1410 * Find the view that has the specified focus type. The search is performed
1411 * across all windows.
1413 * <strong>Note:</strong> In order to access the windows your service has
1414 * to declare the capability to retrieve window content by setting the
1415 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
1416 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
1417 * Also the service has to opt-in to retrieve the interactive windows by
1418 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
1419 * flag. Otherwise, the search will be performed only in the active window.
1422 * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or
1423 * {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}.
1424 * @return The node info of the focused view or null.
1426 * @see AccessibilityNodeInfo#FOCUS_INPUT
1427 * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY
1429 public AccessibilityNodeInfo findFocus(int focus) {
1430 return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId,
1431 AccessibilityWindowInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus);
1435 * Gets the an {@link AccessibilityServiceInfo} describing this
1436 * {@link AccessibilityService}. This method is useful if one wants
1437 * to change some of the dynamically configurable properties at
1440 * @return The accessibility service info.
1442 * @see AccessibilityServiceInfo
1444 public final AccessibilityServiceInfo getServiceInfo() {
1445 IAccessibilityServiceConnection connection =
1446 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1447 if (connection != null) {
1449 return connection.getServiceInfo();
1450 } catch (RemoteException re) {
1451 Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re);
1452 re.rethrowFromSystemServer();
1459 * Sets the {@link AccessibilityServiceInfo} that describes this service.
1461 * Note: You can call this method any time but the info will be picked up after
1462 * the system has bound to this service and when this method is called thereafter.
1464 * @param info The info.
1466 public final void setServiceInfo(AccessibilityServiceInfo info) {
1472 * Sets the {@link AccessibilityServiceInfo} for this service if the latter is
1473 * properly set and there is an {@link IAccessibilityServiceConnection} to the
1474 * AccessibilityManagerService.
1476 private void sendServiceInfo() {
1477 IAccessibilityServiceConnection connection =
1478 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1479 if (mInfo != null && connection != null) {
1481 connection.setServiceInfo(mInfo);
1483 AccessibilityInteractionClient.getInstance().clearCache();
1484 } catch (RemoteException re) {
1485 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
1486 re.rethrowFromSystemServer();
1492 public Object getSystemService(@ServiceName @NonNull String name) {
1493 if (getBaseContext() == null) {
1494 throw new IllegalStateException(
1495 "System services not available to Activities before onCreate()");
1498 // Guarantee that we always return the same window manager instance.
1499 if (WINDOW_SERVICE.equals(name)) {
1500 if (mWindowManager == null) {
1501 mWindowManager = (WindowManager) getBaseContext().getSystemService(name);
1503 return mWindowManager;
1505 return super.getSystemService(name);
1509 * Implement to return the implementation of the internal accessibility
1510 * service interface.
1513 public final IBinder onBind(Intent intent) {
1514 return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {
1516 public void onServiceConnected() {
1517 AccessibilityService.this.dispatchServiceConnected();
1521 public void onInterrupt() {
1522 AccessibilityService.this.onInterrupt();
1526 public void onAccessibilityEvent(AccessibilityEvent event) {
1527 AccessibilityService.this.onAccessibilityEvent(event);
1531 public void init(int connectionId, IBinder windowToken) {
1532 mConnectionId = connectionId;
1533 mWindowToken = windowToken;
1535 // The client may have already obtained the window manager, so
1536 // update the default token on whatever manager we gave them.
1537 final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE);
1538 wm.setDefaultToken(windowToken);
1542 public boolean onGesture(int gestureId) {
1543 return AccessibilityService.this.onGesture(gestureId);
1547 public boolean onKeyEvent(KeyEvent event) {
1548 return AccessibilityService.this.onKeyEvent(event);
1552 public void onMagnificationChanged(@NonNull Region region,
1553 float scale, float centerX, float centerY) {
1554 AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY);
1558 public void onSoftKeyboardShowModeChanged(int showMode) {
1559 AccessibilityService.this.onSoftKeyboardShowModeChanged(showMode);
1563 public void onPerformGestureResult(int sequence, boolean completedSuccessfully) {
1564 AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully);
1568 public void onFingerprintCapturingGesturesChanged(boolean active) {
1569 AccessibilityService.this.onFingerprintCapturingGesturesChanged(active);
1573 public void onFingerprintGesture(int gesture) {
1574 AccessibilityService.this.onFingerprintGesture(gesture);
1578 public void onAccessibilityButtonClicked() {
1579 AccessibilityService.this.onAccessibilityButtonClicked();
1583 public void onAccessibilityButtonAvailabilityChanged(boolean available) {
1584 AccessibilityService.this.onAccessibilityButtonAvailabilityChanged(available);
1590 * Implements the internal {@link IAccessibilityServiceClient} interface to convert
1591 * incoming calls to it back to calls on an {@link AccessibilityService}.
1595 public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
1596 implements HandlerCaller.Callback {
1597 private static final int DO_INIT = 1;
1598 private static final int DO_ON_INTERRUPT = 2;
1599 private static final int DO_ON_ACCESSIBILITY_EVENT = 3;
1600 private static final int DO_ON_GESTURE = 4;
1601 private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5;
1602 private static final int DO_ON_KEY_EVENT = 6;
1603 private static final int DO_ON_MAGNIFICATION_CHANGED = 7;
1604 private static final int DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED = 8;
1605 private static final int DO_GESTURE_COMPLETE = 9;
1606 private static final int DO_ON_FINGERPRINT_ACTIVE_CHANGED = 10;
1607 private static final int DO_ON_FINGERPRINT_GESTURE = 11;
1608 private static final int DO_ACCESSIBILITY_BUTTON_CLICKED = 12;
1609 private static final int DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 13;
1611 private final HandlerCaller mCaller;
1613 private final Callbacks mCallback;
1615 private int mConnectionId;
1617 public IAccessibilityServiceClientWrapper(Context context, Looper looper,
1618 Callbacks callback) {
1619 mCallback = callback;
1620 mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
1623 public void init(IAccessibilityServiceConnection connection, int connectionId,
1624 IBinder windowToken) {
1625 Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId,
1626 connection, windowToken);
1627 mCaller.sendMessage(message);
1630 public void onInterrupt() {
1631 Message message = mCaller.obtainMessage(DO_ON_INTERRUPT);
1632 mCaller.sendMessage(message);
1635 public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) {
1636 Message message = mCaller.obtainMessageBO(
1637 DO_ON_ACCESSIBILITY_EVENT, serviceWantsEvent, event);
1638 mCaller.sendMessage(message);
1641 public void onGesture(int gestureId) {
1642 Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId);
1643 mCaller.sendMessage(message);
1646 public void clearAccessibilityCache() {
1647 Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE);
1648 mCaller.sendMessage(message);
1652 public void onKeyEvent(KeyEvent event, int sequence) {
1653 Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event);
1654 mCaller.sendMessage(message);
1657 public void onMagnificationChanged(@NonNull Region region,
1658 float scale, float centerX, float centerY) {
1659 final SomeArgs args = SomeArgs.obtain();
1662 args.arg3 = centerX;
1663 args.arg4 = centerY;
1665 final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args);
1666 mCaller.sendMessage(message);
1669 public void onSoftKeyboardShowModeChanged(int showMode) {
1670 final Message message =
1671 mCaller.obtainMessageI(DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED, showMode);
1672 mCaller.sendMessage(message);
1675 public void onPerformGestureResult(int sequence, boolean successfully) {
1676 Message message = mCaller.obtainMessageII(DO_GESTURE_COMPLETE, sequence,
1677 successfully ? 1 : 0);
1678 mCaller.sendMessage(message);
1681 public void onFingerprintCapturingGesturesChanged(boolean active) {
1682 mCaller.sendMessage(mCaller.obtainMessageI(
1683 DO_ON_FINGERPRINT_ACTIVE_CHANGED, active ? 1 : 0));
1686 public void onFingerprintGesture(int gesture) {
1687 mCaller.sendMessage(mCaller.obtainMessageI(DO_ON_FINGERPRINT_GESTURE, gesture));
1690 public void onAccessibilityButtonClicked() {
1691 final Message message = mCaller.obtainMessage(DO_ACCESSIBILITY_BUTTON_CLICKED);
1692 mCaller.sendMessage(message);
1695 public void onAccessibilityButtonAvailabilityChanged(boolean available) {
1696 final Message message = mCaller.obtainMessageI(
1697 DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED, (available ? 1 : 0));
1698 mCaller.sendMessage(message);
1702 public void executeMessage(Message message) {
1703 switch (message.what) {
1704 case DO_ON_ACCESSIBILITY_EVENT: {
1705 AccessibilityEvent event = (AccessibilityEvent) message.obj;
1706 boolean serviceWantsEvent = message.arg1 != 0;
1707 if (event != null) {
1708 // Send the event to AccessibilityCache via AccessibilityInteractionClient
1709 AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);
1710 if (serviceWantsEvent) {
1711 // Send the event to AccessibilityService
1712 mCallback.onAccessibilityEvent(event);
1714 // Make sure the event is recycled.
1717 } catch (IllegalStateException ise) {
1718 /* ignore - best effort */
1723 case DO_ON_INTERRUPT: {
1724 mCallback.onInterrupt();
1728 mConnectionId = message.arg1;
1729 SomeArgs args = (SomeArgs) message.obj;
1730 IAccessibilityServiceConnection connection =
1731 (IAccessibilityServiceConnection) args.arg1;
1732 IBinder windowToken = (IBinder) args.arg2;
1734 if (connection != null) {
1735 AccessibilityInteractionClient.getInstance().addConnection(mConnectionId,
1737 mCallback.init(mConnectionId, windowToken);
1738 mCallback.onServiceConnected();
1740 AccessibilityInteractionClient.getInstance().removeConnection(
1742 mConnectionId = AccessibilityInteractionClient.NO_ID;
1743 AccessibilityInteractionClient.getInstance().clearCache();
1744 mCallback.init(AccessibilityInteractionClient.NO_ID, null);
1748 case DO_ON_GESTURE: {
1749 final int gestureId = message.arg1;
1750 mCallback.onGesture(gestureId);
1753 case DO_CLEAR_ACCESSIBILITY_CACHE: {
1754 AccessibilityInteractionClient.getInstance().clearCache();
1757 case DO_ON_KEY_EVENT: {
1758 KeyEvent event = (KeyEvent) message.obj;
1760 IAccessibilityServiceConnection connection = AccessibilityInteractionClient
1761 .getInstance().getConnection(mConnectionId);
1762 if (connection != null) {
1763 final boolean result = mCallback.onKeyEvent(event);
1764 final int sequence = message.arg1;
1766 connection.setOnKeyEventResult(result, sequence);
1767 } catch (RemoteException re) {
1772 // Make sure the event is recycled.
1775 } catch (IllegalStateException ise) {
1776 /* ignore - best effort */
1781 case DO_ON_MAGNIFICATION_CHANGED: {
1782 final SomeArgs args = (SomeArgs) message.obj;
1783 final Region region = (Region) args.arg1;
1784 final float scale = (float) args.arg2;
1785 final float centerX = (float) args.arg3;
1786 final float centerY = (float) args.arg4;
1787 mCallback.onMagnificationChanged(region, scale, centerX, centerY);
1790 case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: {
1791 final int showMode = (int) message.arg1;
1792 mCallback.onSoftKeyboardShowModeChanged(showMode);
1795 case DO_GESTURE_COMPLETE: {
1796 final boolean successfully = message.arg2 == 1;
1797 mCallback.onPerformGestureResult(message.arg1, successfully);
1799 case DO_ON_FINGERPRINT_ACTIVE_CHANGED: {
1800 mCallback.onFingerprintCapturingGesturesChanged(message.arg1 == 1);
1802 case DO_ON_FINGERPRINT_GESTURE: {
1803 mCallback.onFingerprintGesture(message.arg1);
1806 case (DO_ACCESSIBILITY_BUTTON_CLICKED): {
1807 mCallback.onAccessibilityButtonClicked();
1810 case (DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED): {
1811 final boolean available = (message.arg1 != 0);
1812 mCallback.onAccessibilityButtonAvailabilityChanged(available);
1816 Log.w(LOG_TAG, "Unknown message type " + message.what);
1822 * Class used to report status of dispatched gestures
1824 public static abstract class GestureResultCallback {
1825 /** Called when the gesture has completed successfully
1827 * @param gestureDescription The description of the gesture that completed.
1829 public void onCompleted(GestureDescription gestureDescription) {
1832 /** Called when the gesture was cancelled
1834 * @param gestureDescription The description of the gesture that was cancelled.
1836 public void onCancelled(GestureDescription gestureDescription) {
1840 /* Object to keep track of gesture result callbacks */
1841 private static class GestureResultCallbackInfo {
1842 GestureDescription gestureDescription;
1843 GestureResultCallback callback;
1846 GestureResultCallbackInfo(GestureDescription gestureDescription,
1847 GestureResultCallback callback, Handler handler) {
1848 this.gestureDescription = gestureDescription;
1849 this.callback = callback;
1850 this.handler = handler;