AccessibilityService.java revision 214fb68767502f5fede643a062c1dc5975d75b27
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.annotation.NonNull; 20import android.annotation.Nullable; 21import android.app.Service; 22import android.content.Context; 23import android.content.Intent; 24import android.graphics.Region; 25import android.os.Handler; 26import android.os.IBinder; 27import android.os.Looper; 28import android.os.Message; 29import android.os.RemoteException; 30import android.util.ArrayMap; 31import android.util.Log; 32import android.util.Slog; 33import android.view.KeyEvent; 34import android.view.WindowManager; 35import android.view.WindowManagerImpl; 36import android.view.accessibility.AccessibilityEvent; 37import android.view.accessibility.AccessibilityInteractionClient; 38import android.view.accessibility.AccessibilityNodeInfo; 39import android.view.accessibility.AccessibilityWindowInfo; 40 41import com.android.internal.os.HandlerCaller; 42import com.android.internal.os.SomeArgs; 43 44import java.util.ArrayList; 45import java.util.List; 46import java.util.Map.Entry; 47import java.util.Set; 48 49/** 50 * An accessibility service runs in the background and receives callbacks by the system 51 * when {@link AccessibilityEvent}s are fired. Such events denote some state transition 52 * in the user interface, for example, the focus has changed, a button has been clicked, 53 * etc. Such a service can optionally request the capability for querying the content 54 * of the active window. Development of an accessibility service requires extending this 55 * class and implementing its abstract methods. 56 * 57 * <div class="special reference"> 58 * <h3>Developer Guides</h3> 59 * <p>For more information about creating AccessibilityServices, read the 60 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a> 61 * developer guide.</p> 62 * </div> 63 * 64 * <h3>Lifecycle</h3> 65 * <p> 66 * The lifecycle of an accessibility service is managed exclusively by the system and 67 * follows the established service life cycle. Additionally, starting or stopping an 68 * accessibility service is triggered exclusively by an explicit user action through 69 * enabling or disabling it in the device settings. After the system binds to a service it 70 * calls {@link AccessibilityService#onServiceConnected()}. This method can be 71 * overriden by clients that want to perform post binding setup. 72 * </p> 73 * <h3>Declaration</h3> 74 * <p> 75 * An accessibility is declared as any other service in an AndroidManifest.xml but it 76 * must also specify that it handles the "android.accessibilityservice.AccessibilityService" 77 * {@link android.content.Intent}. Failure to declare this intent will cause the system to 78 * ignore the accessibility service. Additionally an accessibility service must request the 79 * {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission to ensure 80 * that only the system 81 * can bind to it. Failure to declare this intent will cause the system to ignore the 82 * accessibility service. Following is an example declaration: 83 * </p> 84 * <pre> <service android:name=".MyAccessibilityService" 85 * android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> 86 * <intent-filter> 87 * <action android:name="android.accessibilityservice.AccessibilityService" /> 88 * </intent-filter> 89 * . . . 90 * </service></pre> 91 * <h3>Configuration</h3> 92 * <p> 93 * An accessibility service can be configured to receive specific types of accessibility events, 94 * listen only to specific packages, get events from each type only once in a given time frame, 95 * retrieve window content, specify a settings activity, etc. 96 * </p> 97 * <p> 98 * There are two approaches for configuring an accessibility service: 99 * </p> 100 * <ul> 101 * <li> 102 * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring 103 * the service. A service declaration with a meta-data tag is presented below: 104 * <pre> <service android:name=".MyAccessibilityService"> 105 * <intent-filter> 106 * <action android:name="android.accessibilityservice.AccessibilityService" /> 107 * </intent-filter> 108 * <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" /> 109 * </service></pre> 110 * <p class="note"> 111 * <strong>Note:</strong> This approach enables setting all properties. 112 * </p> 113 * <p> 114 * For more details refer to {@link #SERVICE_META_DATA} and 115 * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code>. 116 * </p> 117 * </li> 118 * <li> 119 * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note 120 * that this method can be called any time to dynamically change the service configuration. 121 * <p class="note"> 122 * <strong>Note:</strong> This approach enables setting only dynamically configurable properties: 123 * {@link AccessibilityServiceInfo#eventTypes}, 124 * {@link AccessibilityServiceInfo#feedbackType}, 125 * {@link AccessibilityServiceInfo#flags}, 126 * {@link AccessibilityServiceInfo#notificationTimeout}, 127 * {@link AccessibilityServiceInfo#packageNames} 128 * </p> 129 * <p> 130 * For more details refer to {@link AccessibilityServiceInfo}. 131 * </p> 132 * </li> 133 * </ul> 134 * <h3>Retrieving window content</h3> 135 * <p> 136 * A service can specify in its declaration that it can retrieve the active window 137 * content which is represented as a tree of {@link AccessibilityNodeInfo}. Note that 138 * declaring this capability requires that the service declares its configuration via 139 * an XML resource referenced by {@link #SERVICE_META_DATA}. 140 * </p> 141 * <p> 142 * For security purposes an accessibility service can retrieve only the content of the 143 * currently active window. The currently active window is defined as the window from 144 * which was fired the last event of the following types: 145 * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}, 146 * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}, 147 * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}, 148 * In other words, the last window that was shown or the last window that the user has touched 149 * during touch exploration. 150 * </p> 151 * <p> 152 * The entry point for retrieving window content is through calling 153 * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()} of the last received 154 * event of the above types or a previous event from the same window 155 * (see {@link AccessibilityEvent#getWindowId() AccessibilityEvent.getWindowId()}). Invoking 156 * this method will return an {@link AccessibilityNodeInfo} that can be used to traverse the 157 * window content which represented as a tree of such objects. 158 * </p> 159 * <p class="note"> 160 * <strong>Note</strong> An accessibility service may have requested to be notified for 161 * a subset of the event types, thus be unaware that the active window has changed. Therefore 162 * accessibility service that would like to retrieve window content should: 163 * <ul> 164 * <li> 165 * Register for all event types with no notification timeout and keep track for the active 166 * window by calling {@link AccessibilityEvent#getWindowId()} of the last received event and 167 * compare this with the {@link AccessibilityNodeInfo#getWindowId()} before calling retrieval 168 * methods on the latter. 169 * </li> 170 * <li> 171 * Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail since the 172 * active window has changed and the service did not get the accessibility event yet. Note 173 * that it is possible to have a retrieval method failing even adopting the strategy 174 * specified in the previous bullet because the accessibility event dispatch is asynchronous 175 * and crosses process boundaries. 176 * </li> 177 * </ul> 178 * </p> 179 * <h3>Notification strategy</h3> 180 * <p> 181 * For each feedback type only one accessibility service is notified. Services are notified 182 * in the order of registration. Hence, if two services are registered for the same 183 * feedback type in the same package the first one wins. It is possible however, to 184 * register a service as the default one for a given feedback type. In such a case this 185 * service is invoked if no other service was interested in the event. In other words, default 186 * services do not compete with other services and are notified last regardless of the 187 * registration order. This enables "generic" accessibility services that work reasonably 188 * well with most applications to coexist with "polished" ones that are targeted for 189 * specific applications. 190 * </p> 191 * <p class="note"> 192 * <strong>Note:</strong> The event notification timeout is useful to avoid propagating 193 * events to the client too frequently since this is accomplished via an expensive 194 * interprocess call. One can think of the timeout as a criteria to determine when 195 * event generation has settled down.</p> 196 * <h3>Event types</h3> 197 * <ul> 198 * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}</li> 199 * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}</li> 200 * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}</li> 201 * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}</li> 202 * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}</li> 203 * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}</li> 204 * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}</li> 205 * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}</li> 206 * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}</li> 207 * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}</li> 208 * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}</li> 209 * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}</li> 210 * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}</li> 211 * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}</li> 212 * <li>{@link AccessibilityEvent#TYPE_ANNOUNCEMENT}</li> 213 * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_START}</li> 214 * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_END}</li> 215 * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_START}</li> 216 * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_END}</li> 217 * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED}</li> 218 * <li>{@link AccessibilityEvent#TYPE_WINDOWS_CHANGED}</li> 219 * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED}</li> 220 * </ul> 221 * <h3>Feedback types</h3> 222 * <ul> 223 * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li> 224 * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}</li> 225 * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li> 226 * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}</li> 227 * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}</li> 228 * <li>{@link AccessibilityServiceInfo#FEEDBACK_BRAILLE}</li> 229 * </ul> 230 * @see AccessibilityEvent 231 * @see AccessibilityServiceInfo 232 * @see android.view.accessibility.AccessibilityManager 233 */ 234public abstract class AccessibilityService extends Service { 235 236 /** 237 * The user has performed a swipe up gesture on the touch screen. 238 */ 239 public static final int GESTURE_SWIPE_UP = 1; 240 241 /** 242 * The user has performed a swipe down gesture on the touch screen. 243 */ 244 public static final int GESTURE_SWIPE_DOWN = 2; 245 246 /** 247 * The user has performed a swipe left gesture on the touch screen. 248 */ 249 public static final int GESTURE_SWIPE_LEFT = 3; 250 251 /** 252 * The user has performed a swipe right gesture on the touch screen. 253 */ 254 public static final int GESTURE_SWIPE_RIGHT = 4; 255 256 /** 257 * The user has performed a swipe left and right gesture on the touch screen. 258 */ 259 public static final int GESTURE_SWIPE_LEFT_AND_RIGHT = 5; 260 261 /** 262 * The user has performed a swipe right and left gesture on the touch screen. 263 */ 264 public static final int GESTURE_SWIPE_RIGHT_AND_LEFT = 6; 265 266 /** 267 * The user has performed a swipe up and down gesture on the touch screen. 268 */ 269 public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; 270 271 /** 272 * The user has performed a swipe down and up gesture on the touch screen. 273 */ 274 public static final int GESTURE_SWIPE_DOWN_AND_UP = 8; 275 276 /** 277 * The user has performed a left and up gesture on the touch screen. 278 */ 279 public static final int GESTURE_SWIPE_LEFT_AND_UP = 9; 280 281 /** 282 * The user has performed a left and down gesture on the touch screen. 283 */ 284 public static final int GESTURE_SWIPE_LEFT_AND_DOWN = 10; 285 286 /** 287 * The user has performed a right and up gesture on the touch screen. 288 */ 289 public static final int GESTURE_SWIPE_RIGHT_AND_UP = 11; 290 291 /** 292 * The user has performed a right and down gesture on the touch screen. 293 */ 294 public static final int GESTURE_SWIPE_RIGHT_AND_DOWN = 12; 295 296 /** 297 * The user has performed an up and left gesture on the touch screen. 298 */ 299 public static final int GESTURE_SWIPE_UP_AND_LEFT = 13; 300 301 /** 302 * The user has performed an up and right gesture on the touch screen. 303 */ 304 public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14; 305 306 /** 307 * The user has performed an down and left gesture on the touch screen. 308 */ 309 public static final int GESTURE_SWIPE_DOWN_AND_LEFT = 15; 310 311 /** 312 * The user has performed an down and right gesture on the touch screen. 313 */ 314 public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16; 315 316 /** 317 * The {@link Intent} that must be declared as handled by the service. 318 */ 319 public static final String SERVICE_INTERFACE = 320 "android.accessibilityservice.AccessibilityService"; 321 322 /** 323 * Name under which an AccessibilityService component publishes information 324 * about itself. This meta-data must reference an XML resource containing an 325 * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code> 326 * tag. This is a a sample XML file configuring an accessibility service: 327 * <pre> <accessibility-service 328 * android:accessibilityEventTypes="typeViewClicked|typeViewFocused" 329 * android:packageNames="foo.bar, foo.baz" 330 * android:accessibilityFeedbackType="feedbackSpoken" 331 * android:notificationTimeout="100" 332 * android:accessibilityFlags="flagDefault" 333 * android:settingsActivity="foo.bar.TestBackActivity" 334 * android:canRetrieveWindowContent="true" 335 * android:canRequestTouchExplorationMode="true" 336 * android:canRequestEnhancedWebAccessibility="true" 337 * . . . 338 * /></pre> 339 */ 340 public static final String SERVICE_META_DATA = "android.accessibilityservice"; 341 342 /** 343 * Action to go back. 344 */ 345 public static final int GLOBAL_ACTION_BACK = 1; 346 347 /** 348 * Action to go home. 349 */ 350 public static final int GLOBAL_ACTION_HOME = 2; 351 352 /** 353 * Action to open the recent apps. 354 */ 355 public static final int GLOBAL_ACTION_RECENTS = 3; 356 357 /** 358 * Action to open the notifications. 359 */ 360 public static final int GLOBAL_ACTION_NOTIFICATIONS = 4; 361 362 /** 363 * Action to open the quick settings. 364 */ 365 public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; 366 367 /** 368 * Action to open the power long-press dialog. 369 */ 370 public static final int GLOBAL_ACTION_POWER_DIALOG = 6; 371 372 private static final String LOG_TAG = "AccessibilityService"; 373 374 /** 375 * @hide 376 */ 377 public interface Callbacks { 378 public void onAccessibilityEvent(AccessibilityEvent event); 379 public void onInterrupt(); 380 public void onServiceConnected(); 381 public void init(int connectionId, IBinder windowToken); 382 public boolean onGesture(int gestureId); 383 public boolean onKeyEvent(KeyEvent event); 384 public void onMagnificationChanged(@NonNull Region region, 385 float scale, float centerX, float centerY); 386 } 387 388 private int mConnectionId; 389 390 private AccessibilityServiceInfo mInfo; 391 392 private IBinder mWindowToken; 393 394 private WindowManager mWindowManager; 395 396 private MagnificationController mMagnificationController; 397 398 /** 399 * Callback for {@link android.view.accessibility.AccessibilityEvent}s. 400 * 401 * @param event An event. 402 */ 403 public abstract void onAccessibilityEvent(AccessibilityEvent event); 404 405 /** 406 * Callback for interrupting the accessibility feedback. 407 */ 408 public abstract void onInterrupt(); 409 410 /** 411 * Dispatches service connection to internal components first, then the 412 * client code. 413 */ 414 private void dispatchServiceConnected() { 415 if (mMagnificationController != null) { 416 mMagnificationController.onServiceConnected(); 417 } 418 419 // The client gets to handle service connection last, after we've set 420 // up any state upon which their code may rely. 421 onServiceConnected(); 422 } 423 424 /** 425 * This method is a part of the {@link AccessibilityService} lifecycle and is 426 * called after the system has successfully bound to the service. If is 427 * convenient to use this method for setting the {@link AccessibilityServiceInfo}. 428 * 429 * @see AccessibilityServiceInfo 430 * @see #setServiceInfo(AccessibilityServiceInfo) 431 */ 432 protected void onServiceConnected() { 433 434 } 435 436 /** 437 * Called by the system when the user performs a specific gesture on the 438 * touch screen. 439 * 440 * <strong>Note:</strong> To receive gestures an accessibility service must 441 * request that the device is in touch exploration mode by setting the 442 * {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} 443 * flag. 444 * 445 * @param gestureId The unique id of the performed gesture. 446 * 447 * @return Whether the gesture was handled. 448 * 449 * @see #GESTURE_SWIPE_UP 450 * @see #GESTURE_SWIPE_UP_AND_LEFT 451 * @see #GESTURE_SWIPE_UP_AND_DOWN 452 * @see #GESTURE_SWIPE_UP_AND_RIGHT 453 * @see #GESTURE_SWIPE_DOWN 454 * @see #GESTURE_SWIPE_DOWN_AND_LEFT 455 * @see #GESTURE_SWIPE_DOWN_AND_UP 456 * @see #GESTURE_SWIPE_DOWN_AND_RIGHT 457 * @see #GESTURE_SWIPE_LEFT 458 * @see #GESTURE_SWIPE_LEFT_AND_UP 459 * @see #GESTURE_SWIPE_LEFT_AND_RIGHT 460 * @see #GESTURE_SWIPE_LEFT_AND_DOWN 461 * @see #GESTURE_SWIPE_RIGHT 462 * @see #GESTURE_SWIPE_RIGHT_AND_UP 463 * @see #GESTURE_SWIPE_RIGHT_AND_LEFT 464 * @see #GESTURE_SWIPE_RIGHT_AND_DOWN 465 */ 466 protected boolean onGesture(int gestureId) { 467 return false; 468 } 469 470 /** 471 * Callback that allows an accessibility service to observe the key events 472 * before they are passed to the rest of the system. This means that the events 473 * are first delivered here before they are passed to the device policy, the 474 * input method, or applications. 475 * <p> 476 * <strong>Note:</strong> It is important that key events are handled in such 477 * a way that the event stream that would be passed to the rest of the system 478 * is well-formed. For example, handling the down event but not the up event 479 * and vice versa would generate an inconsistent event stream. 480 * </p> 481 * <p> 482 * <strong>Note:</strong> The key events delivered in this method are copies 483 * and modifying them will have no effect on the events that will be passed 484 * to the system. This method is intended to perform purely filtering 485 * functionality. 486 * <p> 487 * 488 * @param event The event to be processed. 489 * @return If true then the event will be consumed and not delivered to 490 * applications, otherwise it will be delivered as usual. 491 */ 492 protected boolean onKeyEvent(KeyEvent event) { 493 return false; 494 } 495 496 /** 497 * Gets the windows on the screen. This method returns only the windows 498 * that a sighted user can interact with, as opposed to all windows. 499 * For example, if there is a modal dialog shown and the user cannot touch 500 * anything behind it, then only the modal window will be reported 501 * (assuming it is the top one). For convenience the returned windows 502 * are ordered in a descending layer order, which is the windows that 503 * are higher in the Z-order are reported first. Since the user can always 504 * interact with the window that has input focus by typing, the focused 505 * window is always returned (even if covered by a modal window). 506 * <p> 507 * <strong>Note:</strong> In order to access the windows your service has 508 * to declare the capability to retrieve window content by setting the 509 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 510 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 511 * Also the service has to opt-in to retrieve the interactive windows by 512 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} 513 * flag. 514 * </p> 515 * 516 * @return The windows if there are windows and the service is can retrieve 517 * them, otherwise an empty list. 518 */ 519 public List<AccessibilityWindowInfo> getWindows() { 520 return AccessibilityInteractionClient.getInstance().getWindows(mConnectionId); 521 } 522 523 /** 524 * Gets the root node in the currently active window if this service 525 * can retrieve window content. The active window is the one that the user 526 * is currently touching or the window with input focus, if the user is not 527 * touching any window. 528 * <p> 529 * <strong>Note:</strong> In order to access the root node your service has 530 * to declare the capability to retrieve window content by setting the 531 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 532 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 533 * </p> 534 * 535 * @return The root node if this service can retrieve window content. 536 */ 537 public AccessibilityNodeInfo getRootInActiveWindow() { 538 return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId); 539 } 540 541 /** 542 * Returns the magnification controller, which may be used to query and 543 * modify the state of display magnification. 544 * <p> 545 * <strong>Note:</strong> In order to control magnification, your service 546 * must declare the capability by setting the 547 * {@link android.R.styleable#AccessibilityService_canControlMagnification} 548 * property in its meta-data. For more information, see 549 * {@link #SERVICE_META_DATA}. 550 * 551 * @return the magnification controller 552 */ 553 @NonNull 554 public final MagnificationController getMagnificationController() { 555 if (mMagnificationController == null) { 556 mMagnificationController = new MagnificationController(this); 557 } 558 return mMagnificationController; 559 } 560 561 private void onMagnificationChanged(@NonNull Region region, float scale, 562 float centerX, float centerY) { 563 if (mMagnificationController != null) { 564 mMagnificationController.dispatchMagnificationChanged( 565 region, scale, centerX, centerY); 566 } 567 } 568 569 /** 570 * Used to control and query the state of display magnification. 571 */ 572 public static final class MagnificationController { 573 private final AccessibilityService mService; 574 575 /** 576 * Map of listeners to their handlers. Lazily created when adding the 577 * first magnification listener. 578 */ 579 private ArrayMap<OnMagnificationChangedListener, Handler> mListeners; 580 581 MagnificationController(@NonNull AccessibilityService service) { 582 mService = service; 583 } 584 585 /** 586 * Called when the service is connected. 587 */ 588 void onServiceConnected() { 589 if (mListeners != null && !mListeners.isEmpty()) { 590 setMagnificationCallbackEnabled(true); 591 } 592 } 593 594 /** 595 * Adds the specified change listener to the list of magnification 596 * change listeners. The callback will occur on the service's main 597 * thread. 598 * 599 * @param listener the listener to add, must be non-{@code null} 600 */ 601 public void addListener(@NonNull OnMagnificationChangedListener listener) { 602 addListener(listener, null); 603 } 604 605 /** 606 * Adds the specified change listener to the list of magnification 607 * change listeners. The callback will occur on the specified 608 * {@link Handler}'s thread, or on the service's main thread if the 609 * handler is {@code null}. 610 * 611 * @param listener the listener to add, must be non-null 612 * @param handler the handler on which the callback should execute, or 613 * {@code null} to execute on the service's main thread 614 */ 615 public void addListener(@NonNull OnMagnificationChangedListener listener, 616 @Nullable Handler handler) { 617 if (mListeners == null) { 618 mListeners = new ArrayMap<>(); 619 } 620 621 final boolean shouldEnableCallback = mListeners.isEmpty(); 622 mListeners.put(listener, handler); 623 624 if (shouldEnableCallback) { 625 // This may fail if the service is not connected yet, but if we 626 // still have listeners when it connects then we can try again. 627 setMagnificationCallbackEnabled(true); 628 } 629 } 630 631 /** 632 * Removes all instances of the specified change listener from the list 633 * of magnification change listeners. 634 * 635 * @param listener the listener to remove, must be non-null 636 * @return {@code true} if at least one instance of the listener was 637 * removed 638 */ 639 public boolean removeListener(@NonNull OnMagnificationChangedListener listener) { 640 if (mListeners == null) { 641 return false; 642 } 643 644 final int keyIndex = mListeners.indexOfKey(listener); 645 final boolean hasKey = keyIndex >= 0; 646 if (hasKey) { 647 mListeners.removeAt(keyIndex); 648 } 649 650 if (hasKey && mListeners.isEmpty()) { 651 // We just removed the last listener, so we don't need 652 // callbacks from the service anymore. 653 setMagnificationCallbackEnabled(false); 654 } 655 656 return hasKey; 657 } 658 659 private void setMagnificationCallbackEnabled(boolean enabled) { 660 final IAccessibilityServiceConnection connection = 661 AccessibilityInteractionClient.getInstance().getConnection( 662 mService.mConnectionId); 663 if (connection != null) { 664 try { 665 connection.setMagnificationCallbackEnabled(enabled); 666 } catch (RemoteException re) { 667 throw new RuntimeException(re); 668 } 669 } 670 } 671 672 /** 673 * Dispatches magnification changes to any registered listeners. This 674 * should be called on the service's main thread. 675 */ 676 void dispatchMagnificationChanged(final @NonNull Region region, final float scale, 677 final float centerX, final float centerY) { 678 if (mListeners == null || mListeners.isEmpty()) { 679 Slog.d(LOG_TAG, "Received magnification changed " 680 + "callback with no listeners registered!"); 681 setMagnificationCallbackEnabled(false); 682 return; 683 } 684 685 // Listeners may remove themselves. Perform a shallow copy to avoid 686 // concurrent modification. 687 final ArrayMap<OnMagnificationChangedListener, Handler> entries = 688 new ArrayMap<>(mListeners); 689 690 for (int i = 0, count = entries.size(); i < count; i++) { 691 final OnMagnificationChangedListener listener = entries.keyAt(i); 692 final Handler handler = entries.valueAt(i); 693 if (handler != null) { 694 handler.post(new Runnable() { 695 @Override 696 public void run() { 697 listener.onMagnificationChanged(MagnificationController.this, 698 region, scale, centerX, centerY); 699 } 700 }); 701 } else { 702 // We're already on the main thread, just run the listener. 703 listener.onMagnificationChanged(this, region, scale, centerX, centerY); 704 } 705 } 706 } 707 708 /** 709 * Returns the current magnification scale. 710 * <p> 711 * <strong>Note:</strong> If the service is not yet connected (e.g. 712 * {@link AccessibilityService#onServiceConnected()} has not yet been 713 * called) or the service has been disconnected, this method will 714 * return a default value of {@code 1.0f}. 715 * 716 * @return the current magnification scale 717 */ 718 public float getScale() { 719 final IAccessibilityServiceConnection connection = 720 AccessibilityInteractionClient.getInstance().getConnection( 721 mService.mConnectionId); 722 if (connection != null) { 723 try { 724 return connection.getMagnificationScale(); 725 } catch (RemoteException re) { 726 Log.w(LOG_TAG, "Failed to obtain scale", re); 727 } 728 } 729 return 1.0f; 730 } 731 732 /** 733 * Returns the unscaled screen-relative X coordinate of the focal 734 * center of the magnified region. This is the point around which 735 * zooming occurs and is guaranteed to lie within the magnified 736 * region. 737 * <p> 738 * <strong>Note:</strong> If the service is not yet connected (e.g. 739 * {@link AccessibilityService#onServiceConnected()} has not yet been 740 * called) or the service has been disconnected, this method will 741 * return a default value of {@code 0.0f}. 742 * 743 * @return the unscaled screen-relative X coordinate of the center of 744 * the magnified region 745 */ 746 public float getCenterX() { 747 final IAccessibilityServiceConnection connection = 748 AccessibilityInteractionClient.getInstance().getConnection( 749 mService.mConnectionId); 750 if (connection != null) { 751 try { 752 return connection.getMagnificationCenterX(); 753 } catch (RemoteException re) { 754 Log.w(LOG_TAG, "Failed to obtain center X", re); 755 } 756 } 757 return 0.0f; 758 } 759 760 /** 761 * Returns the unscaled screen-relative Y coordinate of the focal 762 * center of the magnified region. This is the point around which 763 * zooming occurs and is guaranteed to lie within the magnified 764 * region. 765 * <p> 766 * <strong>Note:</strong> If the service is not yet connected (e.g. 767 * {@link AccessibilityService#onServiceConnected()} has not yet been 768 * called) or the service has been disconnected, this method will 769 * return a default value of {@code 0.0f}. 770 * 771 * @return the unscaled screen-relative Y coordinate of the center of 772 * the magnified region 773 */ 774 public float getCenterY() { 775 final IAccessibilityServiceConnection connection = 776 AccessibilityInteractionClient.getInstance().getConnection( 777 mService.mConnectionId); 778 if (connection != null) { 779 try { 780 return connection.getMagnificationCenterY(); 781 } catch (RemoteException re) { 782 Log.w(LOG_TAG, "Failed to obtain center Y", re); 783 } 784 } 785 return 0.0f; 786 } 787 788 /** 789 * Returns the region of the screen currently being magnified. If 790 * magnification is not enabled, the returned region will be empty. 791 * <p> 792 * <strong>Note:</strong> If the service is not yet connected (e.g. 793 * {@link AccessibilityService#onServiceConnected()} has not yet been 794 * called) or the service has been disconnected, this method will 795 * return an empty region. 796 * 797 * @return the screen-relative bounds of the magnified region 798 */ 799 @NonNull 800 public Region getMagnifiedRegion() { 801 final IAccessibilityServiceConnection connection = 802 AccessibilityInteractionClient.getInstance().getConnection( 803 mService.mConnectionId); 804 if (connection != null) { 805 try { 806 return connection.getMagnifiedRegion(); 807 } catch (RemoteException re) { 808 Log.w(LOG_TAG, "Failed to obtain magnified region", re); 809 } 810 } 811 return Region.obtain(); 812 } 813 814 /** 815 * Resets magnification scale and center to their default (e.g. no 816 * magnification) values. 817 * <p> 818 * <strong>Note:</strong> If the service is not yet connected (e.g. 819 * {@link AccessibilityService#onServiceConnected()} has not yet been 820 * called) or the service has been disconnected, this method will have 821 * no effect and return {@code false}. 822 * 823 * @param animate {@code true} to animate from the current scale and 824 * center or {@code false} to reset the scale and center 825 * immediately 826 * @return {@code true} on success, {@code false} on failure 827 */ 828 public boolean reset(boolean animate) { 829 final IAccessibilityServiceConnection connection = 830 AccessibilityInteractionClient.getInstance().getConnection( 831 mService.mConnectionId); 832 if (connection != null) { 833 try { 834 return connection.resetMagnification(animate); 835 } catch (RemoteException re) { 836 Log.w(LOG_TAG, "Failed to reset", re); 837 } 838 } 839 return false; 840 } 841 842 /** 843 * Sets the magnification scale. 844 * <p> 845 * <strong>Note:</strong> If the service is not yet connected (e.g. 846 * {@link AccessibilityService#onServiceConnected()} has not yet been 847 * called) or the service has been disconnected, this method will have 848 * no effect and return {@code false}. 849 * 850 * @param scale the magnification scale to set, must be >= 1 and <= 5 851 * @param animate {@code true} to animate from the current scale or 852 * {@code false} to set the scale immediately 853 * @return {@code true} on success, {@code false} on failure 854 */ 855 public boolean setScale(float scale, boolean animate) { 856 final IAccessibilityServiceConnection connection = 857 AccessibilityInteractionClient.getInstance().getConnection( 858 mService.mConnectionId); 859 if (connection != null) { 860 try { 861 return connection.setMagnificationScaleAndCenter( 862 scale, Float.NaN, Float.NaN, animate); 863 } catch (RemoteException re) { 864 Log.w(LOG_TAG, "Failed to set scale", re); 865 } 866 } 867 return false; 868 } 869 870 /** 871 * Sets the center of the magnified viewport. 872 * <p> 873 * <strong>Note:</strong> If the service is not yet connected (e.g. 874 * {@link AccessibilityService#onServiceConnected()} has not yet been 875 * called) or the service has been disconnected, this method will have 876 * no effect and return {@code false}. 877 * 878 * @param centerX the unscaled screen-relative X coordinate on which to 879 * center the viewport 880 * @param centerY the unscaled screen-relative Y coordinate on which to 881 * center the viewport 882 * @param animate {@code true} to animate from the current viewport 883 * center or {@code false} to set the center immediately 884 * @return {@code true} on success, {@code false} on failure 885 */ 886 public boolean setCenter(float centerX, float centerY, boolean animate) { 887 final IAccessibilityServiceConnection connection = 888 AccessibilityInteractionClient.getInstance().getConnection( 889 mService.mConnectionId); 890 if (connection != null) { 891 try { 892 return connection.setMagnificationScaleAndCenter( 893 Float.NaN, centerX, centerY, animate); 894 } catch (RemoteException re) { 895 Log.w(LOG_TAG, "Failed to set center", re); 896 } 897 } 898 return false; 899 } 900 901 /** 902 * Listener for changes in the state of magnification. 903 */ 904 public interface OnMagnificationChangedListener { 905 /** 906 * Called when the magnified region, scale, or center changes. 907 * 908 * @param controller the magnification controller 909 * @param region the new magnified region, may be empty if 910 * magnification is not enabled (e.g. scale is 1) 911 * @param scale the new scale 912 * @param centerX the new X coordinate around which magnification is focused 913 * @param centerY the new Y coordinate around which magnification is focused 914 */ 915 void onMagnificationChanged(@NonNull MagnificationController controller, 916 @NonNull Region region, float scale, float centerX, float centerY); 917 } 918 } 919 920 /** 921 * Performs a global action. Such an action can be performed 922 * at any moment regardless of the current application or user 923 * location in that application. For example going back, going 924 * home, opening recents, etc. 925 * 926 * @param action The action to perform. 927 * @return Whether the action was successfully performed. 928 * 929 * @see #GLOBAL_ACTION_BACK 930 * @see #GLOBAL_ACTION_HOME 931 * @see #GLOBAL_ACTION_NOTIFICATIONS 932 * @see #GLOBAL_ACTION_RECENTS 933 */ 934 public final boolean performGlobalAction(int action) { 935 IAccessibilityServiceConnection connection = 936 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 937 if (connection != null) { 938 try { 939 return connection.performGlobalAction(action); 940 } catch (RemoteException re) { 941 Log.w(LOG_TAG, "Error while calling performGlobalAction", re); 942 } 943 } 944 return false; 945 } 946 947 /** 948 * Find the view that has the specified focus type. The search is performed 949 * across all windows. 950 * <p> 951 * <strong>Note:</strong> In order to access the windows your service has 952 * to declare the capability to retrieve window content by setting the 953 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 954 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 955 * Also the service has to opt-in to retrieve the interactive windows by 956 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} 957 * flag.Otherwise, the search will be performed only in the active window. 958 * </p> 959 * 960 * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or 961 * {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}. 962 * @return The node info of the focused view or null. 963 * 964 * @see AccessibilityNodeInfo#FOCUS_INPUT 965 * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY 966 */ 967 public AccessibilityNodeInfo findFocus(int focus) { 968 return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, 969 AccessibilityNodeInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus); 970 } 971 972 /** 973 * Gets the an {@link AccessibilityServiceInfo} describing this 974 * {@link AccessibilityService}. This method is useful if one wants 975 * to change some of the dynamically configurable properties at 976 * runtime. 977 * 978 * @return The accessibility service info. 979 * 980 * @see AccessibilityServiceInfo 981 */ 982 public final AccessibilityServiceInfo getServiceInfo() { 983 IAccessibilityServiceConnection connection = 984 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 985 if (connection != null) { 986 try { 987 return connection.getServiceInfo(); 988 } catch (RemoteException re) { 989 Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re); 990 } 991 } 992 return null; 993 } 994 995 /** 996 * Sets the {@link AccessibilityServiceInfo} that describes this service. 997 * <p> 998 * Note: You can call this method any time but the info will be picked up after 999 * the system has bound to this service and when this method is called thereafter. 1000 * 1001 * @param info The info. 1002 */ 1003 public final void setServiceInfo(AccessibilityServiceInfo info) { 1004 mInfo = info; 1005 sendServiceInfo(); 1006 } 1007 1008 /** 1009 * Sets the {@link AccessibilityServiceInfo} for this service if the latter is 1010 * properly set and there is an {@link IAccessibilityServiceConnection} to the 1011 * AccessibilityManagerService. 1012 */ 1013 private void sendServiceInfo() { 1014 IAccessibilityServiceConnection connection = 1015 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 1016 if (mInfo != null && connection != null) { 1017 try { 1018 connection.setServiceInfo(mInfo); 1019 mInfo = null; 1020 AccessibilityInteractionClient.getInstance().clearCache(); 1021 } catch (RemoteException re) { 1022 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re); 1023 } 1024 } 1025 } 1026 1027 @Override 1028 public Object getSystemService(@ServiceName @NonNull String name) { 1029 if (getBaseContext() == null) { 1030 throw new IllegalStateException( 1031 "System services not available to Activities before onCreate()"); 1032 } 1033 1034 // Guarantee that we always return the same window manager instance. 1035 if (WINDOW_SERVICE.equals(name)) { 1036 if (mWindowManager == null) { 1037 mWindowManager = (WindowManager) getBaseContext().getSystemService(name); 1038 } 1039 return mWindowManager; 1040 } 1041 return super.getSystemService(name); 1042 } 1043 1044 /** 1045 * Implement to return the implementation of the internal accessibility 1046 * service interface. 1047 */ 1048 @Override 1049 public final IBinder onBind(Intent intent) { 1050 return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() { 1051 @Override 1052 public void onServiceConnected() { 1053 AccessibilityService.this.dispatchServiceConnected(); 1054 } 1055 1056 @Override 1057 public void onInterrupt() { 1058 AccessibilityService.this.onInterrupt(); 1059 } 1060 1061 @Override 1062 public void onAccessibilityEvent(AccessibilityEvent event) { 1063 AccessibilityService.this.onAccessibilityEvent(event); 1064 } 1065 1066 @Override 1067 public void init(int connectionId, IBinder windowToken) { 1068 mConnectionId = connectionId; 1069 mWindowToken = windowToken; 1070 1071 // The client may have already obtained the window manager, so 1072 // update the default token on whatever manager we gave them. 1073 final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE); 1074 wm.setDefaultToken(windowToken); 1075 } 1076 1077 @Override 1078 public boolean onGesture(int gestureId) { 1079 return AccessibilityService.this.onGesture(gestureId); 1080 } 1081 1082 @Override 1083 public boolean onKeyEvent(KeyEvent event) { 1084 return AccessibilityService.this.onKeyEvent(event); 1085 } 1086 1087 @Override 1088 public void onMagnificationChanged(@NonNull Region region, 1089 float scale, float centerX, float centerY) { 1090 AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY); 1091 } 1092 }); 1093 } 1094 1095 /** 1096 * Implements the internal {@link IAccessibilityServiceClient} interface to convert 1097 * incoming calls to it back to calls on an {@link AccessibilityService}. 1098 * 1099 * @hide 1100 */ 1101 public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub 1102 implements HandlerCaller.Callback { 1103 private static final int DO_INIT = 1; 1104 private static final int DO_ON_INTERRUPT = 2; 1105 private static final int DO_ON_ACCESSIBILITY_EVENT = 3; 1106 private static final int DO_ON_GESTURE = 4; 1107 private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5; 1108 private static final int DO_ON_KEY_EVENT = 6; 1109 private static final int DO_ON_MAGNIFICATION_CHANGED = 7; 1110 1111 private final HandlerCaller mCaller; 1112 1113 private final Callbacks mCallback; 1114 1115 private int mConnectionId; 1116 1117 public IAccessibilityServiceClientWrapper(Context context, Looper looper, 1118 Callbacks callback) { 1119 mCallback = callback; 1120 mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/); 1121 } 1122 1123 public void init(IAccessibilityServiceConnection connection, int connectionId, 1124 IBinder windowToken) { 1125 Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId, 1126 connection, windowToken); 1127 mCaller.sendMessage(message); 1128 } 1129 1130 public void onInterrupt() { 1131 Message message = mCaller.obtainMessage(DO_ON_INTERRUPT); 1132 mCaller.sendMessage(message); 1133 } 1134 1135 public void onAccessibilityEvent(AccessibilityEvent event) { 1136 Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event); 1137 mCaller.sendMessage(message); 1138 } 1139 1140 public void onGesture(int gestureId) { 1141 Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId); 1142 mCaller.sendMessage(message); 1143 } 1144 1145 public void clearAccessibilityCache() { 1146 Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE); 1147 mCaller.sendMessage(message); 1148 } 1149 1150 @Override 1151 public void onKeyEvent(KeyEvent event, int sequence) { 1152 Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event); 1153 mCaller.sendMessage(message); 1154 } 1155 1156 public void onMagnificationChanged(@NonNull Region region, 1157 float scale, float centerX, float centerY) { 1158 final SomeArgs args = SomeArgs.obtain(); 1159 args.arg1 = region; 1160 args.arg2 = scale; 1161 args.arg3 = centerX; 1162 args.arg4 = centerY; 1163 1164 final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args); 1165 mCaller.sendMessage(message); 1166 } 1167 1168 @Override 1169 public void executeMessage(Message message) { 1170 switch (message.what) { 1171 case DO_ON_ACCESSIBILITY_EVENT: { 1172 AccessibilityEvent event = (AccessibilityEvent) message.obj; 1173 if (event != null) { 1174 AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event); 1175 mCallback.onAccessibilityEvent(event); 1176 // Make sure the event is recycled. 1177 try { 1178 event.recycle(); 1179 } catch (IllegalStateException ise) { 1180 /* ignore - best effort */ 1181 } 1182 } 1183 } return; 1184 1185 case DO_ON_INTERRUPT: { 1186 mCallback.onInterrupt(); 1187 } return; 1188 1189 case DO_INIT: { 1190 mConnectionId = message.arg1; 1191 SomeArgs args = (SomeArgs) message.obj; 1192 IAccessibilityServiceConnection connection = 1193 (IAccessibilityServiceConnection) args.arg1; 1194 IBinder windowToken = (IBinder) args.arg2; 1195 args.recycle(); 1196 if (connection != null) { 1197 AccessibilityInteractionClient.getInstance().addConnection(mConnectionId, 1198 connection); 1199 mCallback.init(mConnectionId, windowToken); 1200 mCallback.onServiceConnected(); 1201 } else { 1202 AccessibilityInteractionClient.getInstance().removeConnection( 1203 mConnectionId); 1204 mConnectionId = AccessibilityInteractionClient.NO_ID; 1205 AccessibilityInteractionClient.getInstance().clearCache(); 1206 mCallback.init(AccessibilityInteractionClient.NO_ID, null); 1207 } 1208 } return; 1209 1210 case DO_ON_GESTURE: { 1211 final int gestureId = message.arg1; 1212 mCallback.onGesture(gestureId); 1213 } return; 1214 1215 case DO_CLEAR_ACCESSIBILITY_CACHE: { 1216 AccessibilityInteractionClient.getInstance().clearCache(); 1217 } return; 1218 1219 case DO_ON_KEY_EVENT: { 1220 KeyEvent event = (KeyEvent) message.obj; 1221 try { 1222 IAccessibilityServiceConnection connection = AccessibilityInteractionClient 1223 .getInstance().getConnection(mConnectionId); 1224 if (connection != null) { 1225 final boolean result = mCallback.onKeyEvent(event); 1226 final int sequence = message.arg1; 1227 try { 1228 connection.setOnKeyEventResult(result, sequence); 1229 } catch (RemoteException re) { 1230 /* ignore */ 1231 } 1232 } 1233 } finally { 1234 // Make sure the event is recycled. 1235 try { 1236 event.recycle(); 1237 } catch (IllegalStateException ise) { 1238 /* ignore - best effort */ 1239 } 1240 } 1241 } return; 1242 1243 case DO_ON_MAGNIFICATION_CHANGED: { 1244 final SomeArgs args = (SomeArgs) message.obj; 1245 final Region region = (Region) args.arg1; 1246 final float scale = (float) args.arg2; 1247 final float centerX = (float) args.arg3; 1248 final float centerY = (float) args.arg4; 1249 mCallback.onMagnificationChanged(region, scale, centerX, centerY); 1250 } return; 1251 1252 default : 1253 Log.w(LOG_TAG, "Unknown message type " + message.what); 1254 } 1255 } 1256 } 1257} 1258