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