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