AccessibilityService.java revision f7174e87b6007000777b0124de9cef70d8618788
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.app.Service; 20import android.content.Context; 21import android.content.Intent; 22import android.os.IBinder; 23import android.os.Looper; 24import android.os.Message; 25import android.os.RemoteException; 26import android.os.SystemClock; 27import android.util.Log; 28import android.view.KeyEvent; 29import android.view.accessibility.AccessibilityEvent; 30import android.view.accessibility.AccessibilityInteractionClient; 31import android.view.accessibility.AccessibilityNodeInfo; 32import android.view.accessibility.AccessibilityWindowInfo; 33 34import com.android.internal.os.HandlerCaller; 35 36import java.util.List; 37 38/** 39 * An accessibility service runs in the background and receives callbacks by the system 40 * when {@link AccessibilityEvent}s are fired. Such events denote some state transition 41 * in the user interface, for example, the focus has changed, a button has been clicked, 42 * etc. Such a service can optionally request the capability for querying the content 43 * of the active window. Development of an accessibility service requires extending this 44 * class and implementing its abstract methods. 45 * 46 * <div class="special reference"> 47 * <h3>Developer Guides</h3> 48 * <p>For more information about creating AccessibilityServices, read the 49 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a> 50 * developer guide.</p> 51 * </div> 52 * 53 * <h3>Lifecycle</h3> 54 * <p> 55 * The lifecycle of an accessibility service is managed exclusively by the system and 56 * follows the established service life cycle. Additionally, starting or stopping an 57 * accessibility service is triggered exclusively by an explicit user action through 58 * enabling or disabling it in the device settings. After the system binds to a service it 59 * calls {@link AccessibilityService#onServiceConnected()}. This method can be 60 * overriden by clients that want to perform post binding setup. 61 * </p> 62 * <h3>Declaration</h3> 63 * <p> 64 * An accessibility is declared as any other service in an AndroidManifest.xml but it 65 * must also specify that it handles the "android.accessibilityservice.AccessibilityService" 66 * {@link android.content.Intent}. Failure to declare this intent will cause the system to 67 * ignore the accessibility service. Additionally an accessibility service must request the 68 * {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission to ensure 69 * that only the system 70 * can bind to it. Failure to declare this intent will cause the system to ignore the 71 * accessibility service. Following is an example declaration: 72 * </p> 73 * <pre> <service android:name=".MyAccessibilityService" 74 * android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> 75 * <intent-filter> 76 * <action android:name="android.accessibilityservice.AccessibilityService" /> 77 * </intent-filter> 78 * . . . 79 * </service></pre> 80 * <h3>Configuration</h3> 81 * <p> 82 * An accessibility service can be configured to receive specific types of accessibility events, 83 * listen only to specific packages, get events from each type only once in a given time frame, 84 * retrieve window content, specify a settings activity, etc. 85 * </p> 86 * <p> 87 * There are two approaches for configuring an accessibility service: 88 * </p> 89 * <ul> 90 * <li> 91 * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring 92 * the service. A service declaration with a meta-data tag is presented below: 93 * <pre> <service android:name=".MyAccessibilityService"> 94 * <intent-filter> 95 * <action android:name="android.accessibilityservice.AccessibilityService" /> 96 * </intent-filter> 97 * <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" /> 98 * </service></pre> 99 * <p class="note"> 100 * <strong>Note:</strong> This approach enables setting all properties. 101 * </p> 102 * <p> 103 * For more details refer to {@link #SERVICE_META_DATA} and 104 * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code>. 105 * </p> 106 * </li> 107 * <li> 108 * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note 109 * that this method can be called any time to dynamically change the service configuration. 110 * <p class="note"> 111 * <strong>Note:</strong> This approach enables setting only dynamically configurable properties: 112 * {@link AccessibilityServiceInfo#eventTypes}, 113 * {@link AccessibilityServiceInfo#feedbackType}, 114 * {@link AccessibilityServiceInfo#flags}, 115 * {@link AccessibilityServiceInfo#notificationTimeout}, 116 * {@link AccessibilityServiceInfo#packageNames} 117 * </p> 118 * <p> 119 * For more details refer to {@link AccessibilityServiceInfo}. 120 * </p> 121 * </li> 122 * </ul> 123 * <h3>Retrieving window content</h3> 124 * <p> 125 * A service can specify in its declaration that it can retrieve the active window 126 * content which is represented as a tree of {@link AccessibilityNodeInfo}. Note that 127 * declaring this capability requires that the service declares its configuration via 128 * an XML resource referenced by {@link #SERVICE_META_DATA}. 129 * </p> 130 * <p> 131 * For security purposes an accessibility service can retrieve only the content of the 132 * currently active window. The currently active window is defined as the window from 133 * which was fired the last event of the following types: 134 * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}, 135 * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}, 136 * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}, 137 * In other words, the last window that was shown or the last window that the user has touched 138 * during touch exploration. 139 * </p> 140 * <p> 141 * The entry point for retrieving window content is through calling 142 * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()} of the last received 143 * event of the above types or a previous event from the same window 144 * (see {@link AccessibilityEvent#getWindowId() AccessibilityEvent.getWindowId()}). Invoking 145 * this method will return an {@link AccessibilityNodeInfo} that can be used to traverse the 146 * window content which represented as a tree of such objects. 147 * </p> 148 * <p class="note"> 149 * <strong>Note</strong> An accessibility service may have requested to be notified for 150 * a subset of the event types, thus be unaware that the active window has changed. Therefore 151 * accessibility service that would like to retrieve window content should: 152 * <ul> 153 * <li> 154 * Register for all event types with no notification timeout and keep track for the active 155 * window by calling {@link AccessibilityEvent#getWindowId()} of the last received event and 156 * compare this with the {@link AccessibilityNodeInfo#getWindowId()} before calling retrieval 157 * methods on the latter. 158 * </li> 159 * <li> 160 * Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail since the 161 * active window has changed and the service did not get the accessibility event yet. Note 162 * that it is possible to have a retrieval method failing even adopting the strategy 163 * specified in the previous bullet because the accessibility event dispatch is asynchronous 164 * and crosses process boundaries. 165 * </li> 166 * </ul> 167 * </p> 168 * <h3>Notification strategy</h3> 169 * <p> 170 * For each feedback type only one accessibility service is notified. Services are notified 171 * in the order of registration. Hence, if two services are registered for the same 172 * feedback type in the same package the first one wins. It is possible however, to 173 * register a service as the default one for a given feedback type. In such a case this 174 * service is invoked if no other service was interested in the event. In other words, default 175 * services do not compete with other services and are notified last regardless of the 176 * registration order. This enables "generic" accessibility services that work reasonably 177 * well with most applications to coexist with "polished" ones that are targeted for 178 * specific applications. 179 * </p> 180 * <p class="note"> 181 * <strong>Note:</strong> The event notification timeout is useful to avoid propagating 182 * events to the client too frequently since this is accomplished via an expensive 183 * interprocess call. One can think of the timeout as a criteria to determine when 184 * event generation has settled down.</p> 185 * <h3>Event types</h3> 186 * <ul> 187 * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}</li> 188 * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}</li> 189 * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}</li> 190 * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}</li> 191 * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}</li> 192 * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}</li> 193 * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}</li> 194 * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}</li> 195 * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}</li> 196 * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}</li> 197 * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}</li> 198 * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}</li> 199 * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}</li> 200 * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}</li> 201 * <li>{@link AccessibilityEvent#TYPE_ANNOUNCEMENT}</li> 202 * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_START}</li> 203 * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_END}</li> 204 * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_START}</li> 205 * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_END}</li> 206 * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED}</li> 207 * <li>{@link AccessibilityEvent#TYPE_WINDOWS_CHANGED}</li> 208 * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED}</li> 209 * </ul> 210 * <h3>Feedback types</h3> 211 * <ul> 212 * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li> 213 * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}</li> 214 * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li> 215 * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}</li> 216 * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}</li> 217 * <li>{@link AccessibilityServiceInfo#FEEDBACK_BRAILLE}</li> 218 * </ul> 219 * @see AccessibilityEvent 220 * @see AccessibilityServiceInfo 221 * @see android.view.accessibility.AccessibilityManager 222 */ 223public abstract class AccessibilityService extends Service { 224 225 /** 226 * The user has performed a swipe up gesture on the touch screen. 227 */ 228 public static final int GESTURE_SWIPE_UP = 1; 229 230 /** 231 * The user has performed a swipe down gesture on the touch screen. 232 */ 233 public static final int GESTURE_SWIPE_DOWN = 2; 234 235 /** 236 * The user has performed a swipe left gesture on the touch screen. 237 */ 238 public static final int GESTURE_SWIPE_LEFT = 3; 239 240 /** 241 * The user has performed a swipe right gesture on the touch screen. 242 */ 243 public static final int GESTURE_SWIPE_RIGHT = 4; 244 245 /** 246 * The user has performed a swipe left and right gesture on the touch screen. 247 */ 248 public static final int GESTURE_SWIPE_LEFT_AND_RIGHT = 5; 249 250 /** 251 * The user has performed a swipe right and left gesture on the touch screen. 252 */ 253 public static final int GESTURE_SWIPE_RIGHT_AND_LEFT = 6; 254 255 /** 256 * The user has performed a swipe up and down gesture on the touch screen. 257 */ 258 public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; 259 260 /** 261 * The user has performed a swipe down and up gesture on the touch screen. 262 */ 263 public static final int GESTURE_SWIPE_DOWN_AND_UP = 8; 264 265 /** 266 * The user has performed a left and up gesture on the touch screen. 267 */ 268 public static final int GESTURE_SWIPE_LEFT_AND_UP = 9; 269 270 /** 271 * The user has performed a left and down gesture on the touch screen. 272 */ 273 public static final int GESTURE_SWIPE_LEFT_AND_DOWN = 10; 274 275 /** 276 * The user has performed a right and up gesture on the touch screen. 277 */ 278 public static final int GESTURE_SWIPE_RIGHT_AND_UP = 11; 279 280 /** 281 * The user has performed a right and down gesture on the touch screen. 282 */ 283 public static final int GESTURE_SWIPE_RIGHT_AND_DOWN = 12; 284 285 /** 286 * The user has performed an up and left gesture on the touch screen. 287 */ 288 public static final int GESTURE_SWIPE_UP_AND_LEFT = 13; 289 290 /** 291 * The user has performed an up and right gesture on the touch screen. 292 */ 293 public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14; 294 295 /** 296 * The user has performed an down and left gesture on the touch screen. 297 */ 298 public static final int GESTURE_SWIPE_DOWN_AND_LEFT = 15; 299 300 /** 301 * The user has performed an down and right gesture on the touch screen. 302 */ 303 public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16; 304 305 /** 306 * The {@link Intent} that must be declared as handled by the service. 307 */ 308 public static final String SERVICE_INTERFACE = 309 "android.accessibilityservice.AccessibilityService"; 310 311 /** 312 * Name under which an AccessibilityService component publishes information 313 * about itself. This meta-data must reference an XML resource containing an 314 * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code> 315 * tag. This is a a sample XML file configuring an accessibility service: 316 * <pre> <accessibility-service 317 * android:accessibilityEventTypes="typeViewClicked|typeViewFocused" 318 * android:packageNames="foo.bar, foo.baz" 319 * android:accessibilityFeedbackType="feedbackSpoken" 320 * android:notificationTimeout="100" 321 * android:accessibilityFlags="flagDefault" 322 * android:settingsActivity="foo.bar.TestBackActivity" 323 * android:canRetrieveWindowContent="true" 324 * android:canRequestTouchExplorationMode="true" 325 * android:canRequestEnhancedWebAccessibility="true" 326 * . . . 327 * /></pre> 328 */ 329 public static final String SERVICE_META_DATA = "android.accessibilityservice"; 330 331 /** 332 * Action to go back. 333 */ 334 public static final int GLOBAL_ACTION_BACK = 1; 335 336 /** 337 * Action to go home. 338 */ 339 public static final int GLOBAL_ACTION_HOME = 2; 340 341 /** 342 * Action to open the recent apps. 343 */ 344 public static final int GLOBAL_ACTION_RECENTS = 3; 345 346 /** 347 * Action to open the notifications. 348 */ 349 public static final int GLOBAL_ACTION_NOTIFICATIONS = 4; 350 351 /** 352 * Action to open the quick settings. 353 */ 354 public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; 355 356 private static final String LOG_TAG = "AccessibilityService"; 357 358 /** 359 * @hide 360 */ 361 public interface Callbacks { 362 public void onAccessibilityEvent(AccessibilityEvent event); 363 public void onInterrupt(); 364 public void onServiceConnected(); 365 public void onSetConnectionId(int connectionId); 366 public boolean onGesture(int gestureId); 367 public boolean onKeyEvent(KeyEvent event); 368 } 369 370 private int mConnectionId; 371 372 private AccessibilityServiceInfo mInfo; 373 374 /** 375 * Callback for {@link android.view.accessibility.AccessibilityEvent}s. 376 * 377 * @param event An event. 378 */ 379 public abstract void onAccessibilityEvent(AccessibilityEvent event); 380 381 /** 382 * Callback for interrupting the accessibility feedback. 383 */ 384 public abstract void onInterrupt(); 385 386 /** 387 * This method is a part of the {@link AccessibilityService} lifecycle and is 388 * called after the system has successfully bound to the service. If is 389 * convenient to use this method for setting the {@link AccessibilityServiceInfo}. 390 * 391 * @see AccessibilityServiceInfo 392 * @see #setServiceInfo(AccessibilityServiceInfo) 393 */ 394 protected void onServiceConnected() { 395 396 } 397 398 /** 399 * Called by the system when the user performs a specific gesture on the 400 * touch screen. 401 * 402 * <strong>Note:</strong> To receive gestures an accessibility service must 403 * request that the device is in touch exploration mode by setting the 404 * {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} 405 * flag. 406 * 407 * @param gestureId The unique id of the performed gesture. 408 * 409 * @return Whether the gesture was handled. 410 * 411 * @see #GESTURE_SWIPE_UP 412 * @see #GESTURE_SWIPE_UP_AND_LEFT 413 * @see #GESTURE_SWIPE_UP_AND_DOWN 414 * @see #GESTURE_SWIPE_UP_AND_RIGHT 415 * @see #GESTURE_SWIPE_DOWN 416 * @see #GESTURE_SWIPE_DOWN_AND_LEFT 417 * @see #GESTURE_SWIPE_DOWN_AND_UP 418 * @see #GESTURE_SWIPE_DOWN_AND_RIGHT 419 * @see #GESTURE_SWIPE_LEFT 420 * @see #GESTURE_SWIPE_LEFT_AND_UP 421 * @see #GESTURE_SWIPE_LEFT_AND_RIGHT 422 * @see #GESTURE_SWIPE_LEFT_AND_DOWN 423 * @see #GESTURE_SWIPE_RIGHT 424 * @see #GESTURE_SWIPE_RIGHT_AND_UP 425 * @see #GESTURE_SWIPE_RIGHT_AND_LEFT 426 * @see #GESTURE_SWIPE_RIGHT_AND_DOWN 427 */ 428 protected boolean onGesture(int gestureId) { 429 return false; 430 } 431 432 /** 433 * Callback that allows an accessibility service to observe the key events 434 * before they are passed to the rest of the system. This means that the events 435 * are first delivered here before they are passed to the device policy, the 436 * input method, or applications. 437 * <p> 438 * <strong>Note:</strong> It is important that key events are handled in such 439 * a way that the event stream that would be passed to the rest of the system 440 * is well-formed. For example, handling the down event but not the up event 441 * and vice versa would generate an inconsistent event stream. 442 * </p> 443 * <p> 444 * <strong>Note:</strong> The key events delivered in this method are copies 445 * and modifying them will have no effect on the events that will be passed 446 * to the system. This method is intended to perform purely filtering 447 * functionality. 448 * <p> 449 * 450 * @param event The event to be processed. 451 * @return If true then the event will be consumed and not delivered to 452 * applications, otherwise it will be delivered as usual. 453 */ 454 protected boolean onKeyEvent(KeyEvent event) { 455 return false; 456 } 457 458 /** 459 * Gets the windows on the screen. This method returns only the windows 460 * that a sighted user can interact with, as opposed to all windows. 461 * For example, if there is a modal dialog shown and the user cannot touch 462 * anything behind it, then only the modal window will be reported 463 * (assuming it is the top one). For convenience the returned windows 464 * are ordered in a descending layer order, which is the windows that 465 * are higher in the Z-order are reported first. Since the user can always 466 * interact with the window that has input focus by typing, the focused 467 * window is always returned (even if covered by a modal window). 468 * <p> 469 * <strong>Note:</strong> In order to access the windows your service has 470 * to declare the capability to retrieve window content by setting the 471 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 472 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 473 * Also the service has to opt-in to retrieve the interactive windows by 474 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} 475 * flag. 476 * </p> 477 * 478 * @return The windows if there are windows and the service is can retrieve 479 * them, otherwise an empty list. 480 */ 481 public List<AccessibilityWindowInfo> getWindows() { 482 return AccessibilityInteractionClient.getInstance().getWindows(mConnectionId); 483 } 484 485 /** 486 * Gets the root node in the currently active window if this service 487 * can retrieve window content. The active window is the one that the user 488 * is currently touching or the window with input focus, if the user is not 489 * touching any window. 490 * <p> 491 * <strong>Note:</strong> In order to access the root node your service has 492 * to declare the capability to retrieve window content by setting the 493 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 494 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 495 * </p> 496 * 497 * @return The root node if this service can retrieve window content. 498 */ 499 public AccessibilityNodeInfo getRootInActiveWindow() { 500 return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId); 501 } 502 503 /** 504 * Performs a global action. Such an action can be performed 505 * at any moment regardless of the current application or user 506 * location in that application. For example going back, going 507 * home, opening recents, etc. 508 * 509 * @param action The action to perform. 510 * @return Whether the action was successfully performed. 511 * 512 * @see #GLOBAL_ACTION_BACK 513 * @see #GLOBAL_ACTION_HOME 514 * @see #GLOBAL_ACTION_NOTIFICATIONS 515 * @see #GLOBAL_ACTION_RECENTS 516 */ 517 public final boolean performGlobalAction(int action) { 518 IAccessibilityServiceConnection connection = 519 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 520 if (connection != null) { 521 try { 522 return connection.performGlobalAction(action); 523 } catch (RemoteException re) { 524 Log.w(LOG_TAG, "Error while calling performGlobalAction", re); 525 } 526 } 527 return false; 528 } 529 530 /** 531 * Find the view that has the specified focus type. The search is performed 532 * across all windows. 533 * <p> 534 * <strong>Note:</strong> In order to access the windows your service has 535 * to declare the capability to retrieve window content by setting the 536 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 537 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 538 * Also the service has to opt-in to retrieve the interactive windows by 539 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} 540 * flag.Otherwise, the search will be performed only in the active window. 541 * </p> 542 * 543 * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or 544 * {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}. 545 * @return The node info of the focused view or null. 546 * 547 * @see AccessibilityNodeInfo#FOCUS_INPUT 548 * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY 549 */ 550 public AccessibilityNodeInfo findFocus(int focus) { 551 return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, 552 AccessibilityNodeInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus); 553 } 554 555 /** 556 * Gets the an {@link AccessibilityServiceInfo} describing this 557 * {@link AccessibilityService}. This method is useful if one wants 558 * to change some of the dynamically configurable properties at 559 * runtime. 560 * 561 * @return The accessibility service info. 562 * 563 * @see AccessibilityServiceInfo 564 */ 565 public final AccessibilityServiceInfo getServiceInfo() { 566 IAccessibilityServiceConnection connection = 567 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 568 if (connection != null) { 569 try { 570 return connection.getServiceInfo(); 571 } catch (RemoteException re) { 572 Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re); 573 } 574 } 575 return null; 576 } 577 578 /** 579 * Sets the {@link AccessibilityServiceInfo} that describes this service. 580 * <p> 581 * Note: You can call this method any time but the info will be picked up after 582 * the system has bound to this service and when this method is called thereafter. 583 * 584 * @param info The info. 585 */ 586 public final void setServiceInfo(AccessibilityServiceInfo info) { 587 mInfo = info; 588 sendServiceInfo(); 589 } 590 591 /** 592 * Sets the {@link AccessibilityServiceInfo} for this service if the latter is 593 * properly set and there is an {@link IAccessibilityServiceConnection} to the 594 * AccessibilityManagerService. 595 */ 596 private void sendServiceInfo() { 597 IAccessibilityServiceConnection connection = 598 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 599 if (mInfo != null && connection != null) { 600 try { 601 connection.setServiceInfo(mInfo); 602 mInfo = null; 603 AccessibilityInteractionClient.getInstance().clearCache(); 604 } catch (RemoteException re) { 605 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re); 606 } 607 } 608 } 609 610 /** 611 * Implement to return the implementation of the internal accessibility 612 * service interface. 613 */ 614 @Override 615 public final IBinder onBind(Intent intent) { 616 return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() { 617 @Override 618 public void onServiceConnected() { 619 AccessibilityService.this.onServiceConnected(); 620 } 621 622 @Override 623 public void onInterrupt() { 624 AccessibilityService.this.onInterrupt(); 625 } 626 627 @Override 628 public void onAccessibilityEvent(AccessibilityEvent event) { 629 AccessibilityService.this.onAccessibilityEvent(event); 630 } 631 632 @Override 633 public void onSetConnectionId( int connectionId) { 634 mConnectionId = connectionId; 635 } 636 637 @Override 638 public boolean onGesture(int gestureId) { 639 return AccessibilityService.this.onGesture(gestureId); 640 } 641 642 @Override 643 public boolean onKeyEvent(KeyEvent event) { 644 return AccessibilityService.this.onKeyEvent(event); 645 } 646 }); 647 } 648 649 /** 650 * Implements the internal {@link IAccessibilityServiceClient} interface to convert 651 * incoming calls to it back to calls on an {@link AccessibilityService}. 652 * 653 * @hide 654 */ 655 public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub 656 implements HandlerCaller.Callback { 657 658 static final int NO_ID = -1; 659 660 private static final int DO_SET_SET_CONNECTION = 1; 661 private static final int DO_ON_INTERRUPT = 2; 662 private static final int DO_ON_ACCESSIBILITY_EVENT = 3; 663 private static final int DO_ON_GESTURE = 4; 664 private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5; 665 private static final int DO_ON_KEY_EVENT = 6; 666 private static final int DO_ON_WINDOWS_CHANGED = 7; 667 668 private final HandlerCaller mCaller; 669 670 private final Callbacks mCallback; 671 672 private int mConnectionId; 673 674 public IAccessibilityServiceClientWrapper(Context context, Looper looper, 675 Callbacks callback) { 676 mCallback = callback; 677 mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/); 678 } 679 680 public void setConnection(IAccessibilityServiceConnection connection, int connectionId) { 681 Message message = mCaller.obtainMessageIO(DO_SET_SET_CONNECTION, connectionId, 682 connection); 683 mCaller.sendMessage(message); 684 } 685 686 public void onInterrupt() { 687 Message message = mCaller.obtainMessage(DO_ON_INTERRUPT); 688 mCaller.sendMessage(message); 689 } 690 691 public void onAccessibilityEvent(AccessibilityEvent event) { 692 Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event); 693 mCaller.sendMessage(message); 694 } 695 696 public void onGesture(int gestureId) { 697 Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId); 698 mCaller.sendMessage(message); 699 } 700 701 public void clearAccessibilityCache() { 702 Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE); 703 mCaller.sendMessage(message); 704 } 705 706 @Override 707 public void onKeyEvent(KeyEvent event, int sequence) { 708 Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event); 709 mCaller.sendMessage(message); 710 } 711 712 @Override 713 public void onWindowsChanged(int[] windowIds) { 714 Message message = mCaller.obtainMessageO(DO_ON_WINDOWS_CHANGED, windowIds); 715 mCaller.sendMessage(message); 716 } 717 718 @Override 719 public void executeMessage(Message message) { 720 switch (message.what) { 721 case DO_ON_ACCESSIBILITY_EVENT: { 722 AccessibilityEvent event = (AccessibilityEvent) message.obj; 723 if (event != null) { 724 AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event); 725 mCallback.onAccessibilityEvent(event); 726 // Make sure the event is recycled. 727 try { 728 event.recycle(); 729 } catch (IllegalStateException ise) { 730 /* ignore - best effort */ 731 } 732 } 733 } return; 734 735 case DO_ON_INTERRUPT: { 736 mCallback.onInterrupt(); 737 } return; 738 739 case DO_SET_SET_CONNECTION: { 740 mConnectionId = message.arg1; 741 IAccessibilityServiceConnection connection = 742 (IAccessibilityServiceConnection) message.obj; 743 if (connection != null) { 744 AccessibilityInteractionClient.getInstance().addConnection(mConnectionId, 745 connection); 746 mCallback.onSetConnectionId(mConnectionId); 747 mCallback.onServiceConnected(); 748 } else { 749 AccessibilityInteractionClient.getInstance().removeConnection( 750 mConnectionId); 751 AccessibilityInteractionClient.getInstance().clearCache(); 752 mCallback.onSetConnectionId(AccessibilityInteractionClient.NO_ID); 753 } 754 } return; 755 756 case DO_ON_GESTURE: { 757 final int gestureId = message.arg1; 758 mCallback.onGesture(gestureId); 759 } return; 760 761 case DO_CLEAR_ACCESSIBILITY_CACHE: { 762 AccessibilityInteractionClient.getInstance().clearCache(); 763 } return; 764 765 case DO_ON_KEY_EVENT: { 766 KeyEvent event = (KeyEvent) message.obj; 767 try { 768 IAccessibilityServiceConnection connection = AccessibilityInteractionClient 769 .getInstance().getConnection(mConnectionId); 770 if (connection != null) { 771 final boolean result = mCallback.onKeyEvent(event); 772 final int sequence = message.arg1; 773 try { 774 connection.setOnKeyEventResult(result, sequence); 775 } catch (RemoteException re) { 776 /* ignore */ 777 } 778 } 779 } finally { 780 // Make sure the event is recycled. 781 try { 782 event.recycle(); 783 } catch (IllegalStateException ise) { 784 /* ignore - best effort */ 785 } 786 } 787 } return; 788 789 case DO_ON_WINDOWS_CHANGED: { 790 final int[] windowIds = (int[]) message.obj; 791 792 // Update the cached windows first. 793 // NOTE: The cache will hold on to the windows so do not recycle. 794 if (windowIds != null) { 795 AccessibilityInteractionClient.getInstance().removeWindows(windowIds); 796 } 797 798 // Let the client know the windows changed. 799 AccessibilityEvent event = AccessibilityEvent.obtain( 800 AccessibilityEvent.TYPE_WINDOWS_CHANGED); 801 event.setEventTime(SystemClock.uptimeMillis()); 802 event.setSealed(true); 803 804 mCallback.onAccessibilityEvent(event); 805 806 // Make sure the event is recycled. 807 try { 808 event.recycle(); 809 } catch (IllegalStateException ise) { 810 /* ignore - best effort */ 811 } 812 } break; 813 814 default : 815 Log.w(LOG_TAG, "Unknown message type " + message.what); 816 } 817 } 818 } 819} 820