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 17 package android.view.accessibility; 18 19 import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME; 20 21 import android.Manifest; 22 import android.accessibilityservice.AccessibilityService; 23 import android.accessibilityservice.AccessibilityServiceInfo; 24 import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType; 25 import android.accessibilityservice.AccessibilityShortcutInfo; 26 import android.annotation.ColorInt; 27 import android.annotation.IntDef; 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.annotation.RequiresPermission; 31 import android.annotation.SdkConstant; 32 import android.annotation.SystemApi; 33 import android.annotation.SystemService; 34 import android.annotation.TestApi; 35 import android.annotation.UserIdInt; 36 import android.app.RemoteAction; 37 import android.compat.annotation.UnsupportedAppUsage; 38 import android.content.ComponentName; 39 import android.content.Context; 40 import android.content.Intent; 41 import android.content.pm.ActivityInfo; 42 import android.content.pm.PackageManager; 43 import android.content.pm.ResolveInfo; 44 import android.content.pm.ServiceInfo; 45 import android.content.res.Resources; 46 import android.os.Binder; 47 import android.os.Build; 48 import android.os.Handler; 49 import android.os.IBinder; 50 import android.os.Looper; 51 import android.os.Message; 52 import android.os.Process; 53 import android.os.RemoteException; 54 import android.os.ServiceManager; 55 import android.os.SystemClock; 56 import android.os.UserHandle; 57 import android.util.ArrayMap; 58 import android.util.Log; 59 import android.util.SparseArray; 60 import android.view.IWindow; 61 import android.view.View; 62 import android.view.accessibility.AccessibilityEvent.EventType; 63 64 import com.android.internal.R; 65 import com.android.internal.annotations.VisibleForTesting; 66 import com.android.internal.util.IntPair; 67 68 import org.xmlpull.v1.XmlPullParserException; 69 70 import java.io.IOException; 71 import java.lang.annotation.Retention; 72 import java.lang.annotation.RetentionPolicy; 73 import java.util.ArrayList; 74 import java.util.Collections; 75 import java.util.List; 76 77 /** 78 * System level service that serves as an event dispatch for {@link AccessibilityEvent}s, 79 * and provides facilities for querying the accessibility state of the system. 80 * Accessibility events are generated when something notable happens in the user interface, 81 * for example an {@link android.app.Activity} starts, the focus or selection of a 82 * {@link android.view.View} changes etc. Parties interested in handling accessibility 83 * events implement and register an accessibility service which extends 84 * {@link android.accessibilityservice.AccessibilityService}. 85 * 86 * @see AccessibilityEvent 87 * @see AccessibilityNodeInfo 88 * @see android.accessibilityservice.AccessibilityService 89 * @see Context#getSystemService 90 * @see Context#ACCESSIBILITY_SERVICE 91 */ 92 @SystemService(Context.ACCESSIBILITY_SERVICE) 93 public final class AccessibilityManager { 94 private static final boolean DEBUG = false; 95 96 private static final String LOG_TAG = "AccessibilityManager"; 97 98 /** @hide */ 99 public static final int STATE_FLAG_ACCESSIBILITY_ENABLED = 0x00000001; 100 101 /** @hide */ 102 public static final int STATE_FLAG_TOUCH_EXPLORATION_ENABLED = 0x00000002; 103 104 /** @hide */ 105 public static final int STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED = 0x00000004; 106 107 /** @hide */ 108 public static final int STATE_FLAG_DISPATCH_DOUBLE_TAP = 0x00000008; 109 110 /** @hide */ 111 public static final int STATE_FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000010; 112 113 /** @hide */ 114 public static final int STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED = 0x00000020; 115 116 /** @hide */ 117 public static final int DALTONIZER_DISABLED = -1; 118 119 /** @hide */ 120 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 121 public static final int DALTONIZER_SIMULATE_MONOCHROMACY = 0; 122 123 /** @hide */ 124 public static final int DALTONIZER_CORRECT_DEUTERANOMALY = 12; 125 126 /** @hide */ 127 public static final int AUTOCLICK_DELAY_DEFAULT = 600; 128 129 /** 130 * Activity action: Launch UI to manage which accessibility service or feature is assigned 131 * to the navigation bar Accessibility button. 132 * <p> 133 * Input: Nothing. 134 * </p> 135 * <p> 136 * Output: Nothing. 137 * </p> 138 * 139 * @hide 140 */ 141 @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) 142 public static final String ACTION_CHOOSE_ACCESSIBILITY_BUTTON = 143 "com.android.internal.intent.action.CHOOSE_ACCESSIBILITY_BUTTON"; 144 145 /** 146 * Used as an int value for accessibility chooser activity to represent the accessibility button 147 * shortcut type. 148 * 149 * @hide 150 */ 151 public static final int ACCESSIBILITY_BUTTON = 0; 152 153 /** 154 * Used as an int value for accessibility chooser activity to represent hardware key shortcut, 155 * such as volume key button. 156 * 157 * @hide 158 */ 159 public static final int ACCESSIBILITY_SHORTCUT_KEY = 1; 160 161 /** 162 * Annotations for the shortcut type. 163 * @hide 164 */ 165 @Retention(RetentionPolicy.SOURCE) 166 @IntDef(value = { 167 ACCESSIBILITY_BUTTON, 168 ACCESSIBILITY_SHORTCUT_KEY 169 }) 170 public @interface ShortcutType {} 171 172 /** 173 * Annotations for content flag of UI. 174 * @hide 175 */ 176 @Retention(RetentionPolicy.SOURCE) 177 @IntDef(flag = true, prefix = { "FLAG_CONTENT_" }, value = { 178 FLAG_CONTENT_ICONS, 179 FLAG_CONTENT_TEXT, 180 FLAG_CONTENT_CONTROLS 181 }) 182 public @interface ContentFlag {} 183 184 /** 185 * Use this flag to indicate the content of a UI that times out contains icons. 186 * 187 * @see #getRecommendedTimeoutMillis(int, int) 188 */ 189 public static final int FLAG_CONTENT_ICONS = 1; 190 191 /** 192 * Use this flag to indicate the content of a UI that times out contains text. 193 * 194 * @see #getRecommendedTimeoutMillis(int, int) 195 */ 196 public static final int FLAG_CONTENT_TEXT = 2; 197 198 /** 199 * Use this flag to indicate the content of a UI that times out contains interactive controls. 200 * 201 * @see #getRecommendedTimeoutMillis(int, int) 202 */ 203 public static final int FLAG_CONTENT_CONTROLS = 4; 204 205 @UnsupportedAppUsage 206 static final Object sInstanceSync = new Object(); 207 208 @UnsupportedAppUsage 209 private static AccessibilityManager sInstance; 210 211 @UnsupportedAppUsage 212 private final Object mLock = new Object(); 213 214 @UnsupportedAppUsage 215 private IAccessibilityManager mService; 216 217 @UnsupportedAppUsage 218 final int mUserId; 219 220 @UnsupportedAppUsage 221 final Handler mHandler; 222 223 final Handler.Callback mCallback; 224 225 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) 226 boolean mIsEnabled; 227 228 int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK; 229 230 int mInteractiveUiTimeout; 231 int mNonInteractiveUiTimeout; 232 233 boolean mIsTouchExplorationEnabled; 234 235 @UnsupportedAppUsage(trackingBug = 123768939L) 236 boolean mIsHighTextContrastEnabled; 237 238 // Whether accessibility tracing is enabled or not 239 boolean mIsAccessibilityTracingEnabled = false; 240 241 AccessibilityPolicy mAccessibilityPolicy; 242 243 private int mPerformingAction = 0; 244 245 /** The stroke width of the focus rectangle in pixels */ 246 private int mFocusStrokeWidth; 247 /** The color of the focus rectangle */ 248 private int mFocusColor; 249 250 @UnsupportedAppUsage 251 private final ArrayMap<AccessibilityStateChangeListener, Handler> 252 mAccessibilityStateChangeListeners = new ArrayMap<>(); 253 254 private final ArrayMap<TouchExplorationStateChangeListener, Handler> 255 mTouchExplorationStateChangeListeners = new ArrayMap<>(); 256 257 private final ArrayMap<HighTextContrastChangeListener, Handler> 258 mHighTextContrastStateChangeListeners = new ArrayMap<>(); 259 260 private final ArrayMap<AccessibilityServicesStateChangeListener, Handler> 261 mServicesStateChangeListeners = new ArrayMap<>(); 262 263 /** 264 * Map from a view's accessibility id to the list of request preparers set for that view 265 */ 266 private SparseArray<List<AccessibilityRequestPreparer>> mRequestPreparerLists; 267 268 /** 269 * Listener for the system accessibility state. To listen for changes to the 270 * accessibility state on the device, implement this interface and register 271 * it with the system by calling {@link #addAccessibilityStateChangeListener}. 272 */ 273 public interface AccessibilityStateChangeListener { 274 275 /** 276 * Called when the accessibility enabled state changes. 277 * 278 * @param enabled Whether accessibility is enabled. 279 */ onAccessibilityStateChanged(boolean enabled)280 void onAccessibilityStateChanged(boolean enabled); 281 } 282 283 /** 284 * Listener for the system touch exploration state. To listen for changes to 285 * the touch exploration state on the device, implement this interface and 286 * register it with the system by calling 287 * {@link #addTouchExplorationStateChangeListener}. 288 */ 289 public interface TouchExplorationStateChangeListener { 290 291 /** 292 * Called when the touch exploration enabled state changes. 293 * 294 * @param enabled Whether touch exploration is enabled. 295 */ onTouchExplorationStateChanged(boolean enabled)296 void onTouchExplorationStateChanged(boolean enabled); 297 } 298 299 /** 300 * Listener for changes to the state of accessibility services. Changes include services being 301 * enabled or disabled, or changes to the {@link AccessibilityServiceInfo} of a running service. 302 * {@see #addAccessibilityServicesStateChangeListener}. 303 * 304 * @hide 305 */ 306 @TestApi 307 public interface AccessibilityServicesStateChangeListener { 308 309 /** 310 * Called when the state of accessibility services changes. 311 * 312 * @param manager The manager that is calling back 313 */ onAccessibilityServicesStateChanged(AccessibilityManager manager)314 void onAccessibilityServicesStateChanged(AccessibilityManager manager); 315 } 316 317 /** 318 * Listener for the system high text contrast state. To listen for changes to 319 * the high text contrast state on the device, implement this interface and 320 * register it with the system by calling 321 * {@link #addHighTextContrastStateChangeListener}. 322 * 323 * @hide 324 */ 325 public interface HighTextContrastChangeListener { 326 327 /** 328 * Called when the high text contrast enabled state changes. 329 * 330 * @param enabled Whether high text contrast is enabled. 331 */ onHighTextContrastStateChanged(boolean enabled)332 void onHighTextContrastStateChanged(boolean enabled); 333 } 334 335 /** 336 * Policy to inject behavior into the accessibility manager. 337 * 338 * @hide 339 */ 340 public interface AccessibilityPolicy { 341 /** 342 * Checks whether accessibility is enabled. 343 * 344 * @param accessibilityEnabled Whether the accessibility layer is enabled. 345 * @return whether accessibility is enabled. 346 */ isEnabled(boolean accessibilityEnabled)347 boolean isEnabled(boolean accessibilityEnabled); 348 349 /** 350 * Notifies the policy for an accessibility event. 351 * 352 * @param event The event. 353 * @param accessibilityEnabled Whether the accessibility layer is enabled. 354 * @param relevantEventTypes The events relevant events. 355 * @return The event to dispatch or null. 356 */ onAccessibilityEvent(@onNull AccessibilityEvent event, boolean accessibilityEnabled, @EventType int relevantEventTypes)357 @Nullable AccessibilityEvent onAccessibilityEvent(@NonNull AccessibilityEvent event, 358 boolean accessibilityEnabled, @EventType int relevantEventTypes); 359 360 /** 361 * Gets the list of relevant events. 362 * 363 * @param relevantEventTypes The relevant events. 364 * @return The relevant events to report. 365 */ getRelevantEventTypes(@ventType int relevantEventTypes)366 @EventType int getRelevantEventTypes(@EventType int relevantEventTypes); 367 368 /** 369 * Gets the list of installed services to report. 370 * 371 * @param installedService The installed services. 372 * @return The services to report. 373 */ getInstalledAccessibilityServiceList( @ullable List<AccessibilityServiceInfo> installedService)374 @NonNull List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList( 375 @Nullable List<AccessibilityServiceInfo> installedService); 376 377 /** 378 * Gets the list of enabled accessibility services. 379 * 380 * @param feedbackTypeFlags The feedback type to query for. 381 * @param enabledService The enabled services. 382 * @return The services to report. 383 */ getEnabledAccessibilityServiceList( @eedbackType int feedbackTypeFlags, @Nullable List<AccessibilityServiceInfo> enabledService)384 @Nullable List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList( 385 @FeedbackType int feedbackTypeFlags, 386 @Nullable List<AccessibilityServiceInfo> enabledService); 387 } 388 389 private final IAccessibilityManagerClient.Stub mClient = 390 new IAccessibilityManagerClient.Stub() { 391 @Override 392 public void setState(int state) { 393 // We do not want to change this immediately as the application may 394 // have already checked that accessibility is on and fired an event, 395 // that is now propagating up the view tree, Hence, if accessibility 396 // is now off an exception will be thrown. We want to have the exception 397 // enforcement to guard against apps that fire unnecessary accessibility 398 // events when accessibility is off. 399 mHandler.obtainMessage(MyCallback.MSG_SET_STATE, state, 0).sendToTarget(); 400 } 401 402 @Override 403 public void notifyServicesStateChanged(long updatedUiTimeout) { 404 updateUiTimeout(updatedUiTimeout); 405 406 final ArrayMap<AccessibilityServicesStateChangeListener, Handler> listeners; 407 synchronized (mLock) { 408 if (mServicesStateChangeListeners.isEmpty()) { 409 return; 410 } 411 listeners = new ArrayMap<>(mServicesStateChangeListeners); 412 } 413 414 int numListeners = listeners.size(); 415 for (int i = 0; i < numListeners; i++) { 416 final AccessibilityServicesStateChangeListener listener = 417 mServicesStateChangeListeners.keyAt(i); 418 mServicesStateChangeListeners.valueAt(i).post(() -> listener 419 .onAccessibilityServicesStateChanged(AccessibilityManager.this)); 420 } 421 } 422 423 @Override 424 public void setRelevantEventTypes(int eventTypes) { 425 mRelevantEventTypes = eventTypes; 426 } 427 428 @Override 429 public void setFocusAppearance(int strokeWidth, int color) { 430 synchronized (mLock) { 431 updateFocusAppearanceLocked(strokeWidth, color); 432 } 433 } 434 }; 435 436 /** 437 * Get an AccessibilityManager instance (create one if necessary). 438 * 439 * @param context Context in which this manager operates. 440 * 441 * @hide 442 */ 443 @UnsupportedAppUsage getInstance(Context context)444 public static AccessibilityManager getInstance(Context context) { 445 synchronized (sInstanceSync) { 446 if (sInstance == null) { 447 final int userId; 448 if (Binder.getCallingUid() == Process.SYSTEM_UID 449 || context.checkCallingOrSelfPermission( 450 Manifest.permission.INTERACT_ACROSS_USERS) 451 == PackageManager.PERMISSION_GRANTED 452 || context.checkCallingOrSelfPermission( 453 Manifest.permission.INTERACT_ACROSS_USERS_FULL) 454 == PackageManager.PERMISSION_GRANTED) { 455 userId = UserHandle.USER_CURRENT; 456 } else { 457 userId = context.getUserId(); 458 } 459 sInstance = new AccessibilityManager(context, null, userId); 460 } 461 } 462 return sInstance; 463 } 464 465 /** 466 * Create an instance. 467 * 468 * @param context A {@link Context}. 469 * @param service An interface to the backing service. 470 * @param userId User id under which to run. 471 * 472 * @hide 473 */ AccessibilityManager(Context context, IAccessibilityManager service, int userId)474 public AccessibilityManager(Context context, IAccessibilityManager service, int userId) { 475 // Constructor can't be chained because we can't create an instance of an inner class 476 // before calling another constructor. 477 mCallback = new MyCallback(); 478 mHandler = new Handler(context.getMainLooper(), mCallback); 479 mUserId = userId; 480 synchronized (mLock) { 481 initialFocusAppearanceLocked(context.getResources()); 482 tryConnectToServiceLocked(service); 483 } 484 } 485 486 /** 487 * Create an instance. 488 * 489 * @param context A {@link Context}. 490 * @param handler The handler to use 491 * @param service An interface to the backing service. 492 * @param userId User id under which to run. 493 * @param serviceConnect {@code true} to connect the service or 494 * {@code false} not to connect the service. 495 * 496 * @hide 497 */ 498 @VisibleForTesting AccessibilityManager(Context context, Handler handler, IAccessibilityManager service, int userId, boolean serviceConnect)499 public AccessibilityManager(Context context, Handler handler, IAccessibilityManager service, 500 int userId, boolean serviceConnect) { 501 mCallback = new MyCallback(); 502 mHandler = handler; 503 mUserId = userId; 504 synchronized (mLock) { 505 initialFocusAppearanceLocked(context.getResources()); 506 if (serviceConnect) { 507 tryConnectToServiceLocked(service); 508 } 509 } 510 } 511 512 /** 513 * @hide 514 */ getClient()515 public IAccessibilityManagerClient getClient() { 516 return mClient; 517 } 518 519 /** 520 * Unregisters the IAccessibilityManagerClient from the backing service 521 * @hide 522 */ removeClient()523 public boolean removeClient() { 524 synchronized (mLock) { 525 IAccessibilityManager service = getServiceLocked(); 526 if (service == null) { 527 return false; 528 } 529 try { 530 return service.removeClient(mClient, mUserId); 531 } catch (RemoteException re) { 532 Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); 533 } 534 } 535 return false; 536 } 537 538 /** 539 * @hide 540 */ 541 @VisibleForTesting getCallback()542 public Handler.Callback getCallback() { 543 return mCallback; 544 } 545 546 /** 547 * Returns if the accessibility in the system is enabled. 548 * 549 * @return True if accessibility is enabled, false otherwise. 550 */ isEnabled()551 public boolean isEnabled() { 552 synchronized (mLock) { 553 return mIsEnabled || (mAccessibilityPolicy != null 554 && mAccessibilityPolicy.isEnabled(mIsEnabled)); 555 } 556 } 557 558 /** 559 * Returns if the touch exploration in the system is enabled. 560 * 561 * @return True if touch exploration is enabled, false otherwise. 562 */ isTouchExplorationEnabled()563 public boolean isTouchExplorationEnabled() { 564 synchronized (mLock) { 565 IAccessibilityManager service = getServiceLocked(); 566 if (service == null) { 567 return false; 568 } 569 return mIsTouchExplorationEnabled; 570 } 571 } 572 573 /** 574 * Returns if the high text contrast in the system is enabled. 575 * <p> 576 * <strong>Note:</strong> You need to query this only if you application is 577 * doing its own rendering and does not rely on the platform rendering pipeline. 578 * </p> 579 * 580 * @return True if high text contrast is enabled, false otherwise. 581 * 582 * @hide 583 */ 584 @UnsupportedAppUsage isHighTextContrastEnabled()585 public boolean isHighTextContrastEnabled() { 586 synchronized (mLock) { 587 IAccessibilityManager service = getServiceLocked(); 588 if (service == null) { 589 return false; 590 } 591 return mIsHighTextContrastEnabled; 592 } 593 } 594 595 /** 596 * Sends an {@link AccessibilityEvent}. 597 * 598 * @param event The event to send. 599 * 600 * @throws IllegalStateException if accessibility is not enabled. 601 * 602 * <strong>Note:</strong> The preferred mechanism for sending custom accessibility 603 * events is through calling 604 * {@link android.view.ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)} 605 * instead of this method to allow predecessors to augment/filter events sent by 606 * their descendants. 607 */ sendAccessibilityEvent(AccessibilityEvent event)608 public void sendAccessibilityEvent(AccessibilityEvent event) { 609 final IAccessibilityManager service; 610 final int userId; 611 final AccessibilityEvent dispatchedEvent; 612 synchronized (mLock) { 613 service = getServiceLocked(); 614 if (service == null) { 615 return; 616 } 617 event.setEventTime(SystemClock.uptimeMillis()); 618 if (event.getAction() == 0) { 619 event.setAction(mPerformingAction); 620 } 621 if (mAccessibilityPolicy != null) { 622 dispatchedEvent = mAccessibilityPolicy.onAccessibilityEvent(event, 623 mIsEnabled, mRelevantEventTypes); 624 if (dispatchedEvent == null) { 625 return; 626 } 627 } else { 628 dispatchedEvent = event; 629 } 630 if (!isEnabled()) { 631 Looper myLooper = Looper.myLooper(); 632 if (myLooper == Looper.getMainLooper()) { 633 throw new IllegalStateException( 634 "Accessibility off. Did you forget to check that?"); 635 } else { 636 // If we're not running on the thread with the main looper, it's possible for 637 // the state of accessibility to change between checking isEnabled and 638 // calling this method. So just log the error rather than throwing the 639 // exception. 640 Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled"); 641 return; 642 } 643 } 644 if ((dispatchedEvent.getEventType() & mRelevantEventTypes) == 0) { 645 if (DEBUG) { 646 Log.i(LOG_TAG, "Not dispatching irrelevant event: " + dispatchedEvent 647 + " that is not among " 648 + AccessibilityEvent.eventTypeToString(mRelevantEventTypes)); 649 } 650 return; 651 } 652 userId = mUserId; 653 } 654 try { 655 // it is possible that this manager is in the same process as the service but 656 // client using it is called through Binder from another process. Example: MMS 657 // app adds a SMS notification and the NotificationManagerService calls this method 658 final long identityToken = Binder.clearCallingIdentity(); 659 try { 660 service.sendAccessibilityEvent(dispatchedEvent, userId); 661 } finally { 662 Binder.restoreCallingIdentity(identityToken); 663 } 664 if (DEBUG) { 665 Log.i(LOG_TAG, dispatchedEvent + " sent"); 666 } 667 } catch (RemoteException re) { 668 Log.e(LOG_TAG, "Error during sending " + dispatchedEvent + " ", re); 669 } finally { 670 if (event != dispatchedEvent) { 671 event.recycle(); 672 } 673 dispatchedEvent.recycle(); 674 } 675 } 676 677 /** 678 * Requests feedback interruption from all accessibility services. 679 */ interrupt()680 public void interrupt() { 681 final IAccessibilityManager service; 682 final int userId; 683 synchronized (mLock) { 684 service = getServiceLocked(); 685 if (service == null) { 686 return; 687 } 688 if (!isEnabled()) { 689 Looper myLooper = Looper.myLooper(); 690 if (myLooper == Looper.getMainLooper()) { 691 throw new IllegalStateException( 692 "Accessibility off. Did you forget to check that?"); 693 } else { 694 // If we're not running on the thread with the main looper, it's possible for 695 // the state of accessibility to change between checking isEnabled and 696 // calling this method. So just log the error rather than throwing the 697 // exception. 698 Log.e(LOG_TAG, "Interrupt called with accessibility disabled"); 699 return; 700 } 701 } 702 userId = mUserId; 703 } 704 try { 705 service.interrupt(userId); 706 if (DEBUG) { 707 Log.i(LOG_TAG, "Requested interrupt from all services"); 708 } 709 } catch (RemoteException re) { 710 Log.e(LOG_TAG, "Error while requesting interrupt from all services. ", re); 711 } 712 } 713 714 /** 715 * Returns the {@link ServiceInfo}s of the installed accessibility services. 716 * 717 * @return An unmodifiable list with {@link ServiceInfo}s. 718 * 719 * @deprecated Use {@link #getInstalledAccessibilityServiceList()} 720 */ 721 @Deprecated getAccessibilityServiceList()722 public List<ServiceInfo> getAccessibilityServiceList() { 723 List<AccessibilityServiceInfo> infos = getInstalledAccessibilityServiceList(); 724 List<ServiceInfo> services = new ArrayList<>(); 725 final int infoCount = infos.size(); 726 for (int i = 0; i < infoCount; i++) { 727 AccessibilityServiceInfo info = infos.get(i); 728 services.add(info.getResolveInfo().serviceInfo); 729 } 730 return Collections.unmodifiableList(services); 731 } 732 733 /** 734 * Returns the {@link AccessibilityServiceInfo}s of the installed accessibility services. 735 * 736 * @return An unmodifiable list with {@link AccessibilityServiceInfo}s. 737 */ getInstalledAccessibilityServiceList()738 public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() { 739 final IAccessibilityManager service; 740 final int userId; 741 synchronized (mLock) { 742 service = getServiceLocked(); 743 if (service == null) { 744 return Collections.emptyList(); 745 } 746 userId = mUserId; 747 } 748 749 List<AccessibilityServiceInfo> services = null; 750 try { 751 services = service.getInstalledAccessibilityServiceList(userId); 752 if (DEBUG) { 753 Log.i(LOG_TAG, "Installed AccessibilityServices " + services); 754 } 755 } catch (RemoteException re) { 756 Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re); 757 } 758 if (mAccessibilityPolicy != null) { 759 services = mAccessibilityPolicy.getInstalledAccessibilityServiceList(services); 760 } 761 if (services != null) { 762 return Collections.unmodifiableList(services); 763 } else { 764 return Collections.emptyList(); 765 } 766 } 767 768 /** 769 * Returns the {@link AccessibilityServiceInfo}s of the enabled accessibility services 770 * for a given feedback type. 771 * 772 * @param feedbackTypeFlags The feedback type flags. 773 * @return An unmodifiable list with {@link AccessibilityServiceInfo}s. 774 * 775 * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE 776 * @see AccessibilityServiceInfo#FEEDBACK_GENERIC 777 * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC 778 * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN 779 * @see AccessibilityServiceInfo#FEEDBACK_VISUAL 780 * @see AccessibilityServiceInfo#FEEDBACK_BRAILLE 781 */ getEnabledAccessibilityServiceList( int feedbackTypeFlags)782 public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList( 783 int feedbackTypeFlags) { 784 final IAccessibilityManager service; 785 final int userId; 786 synchronized (mLock) { 787 service = getServiceLocked(); 788 if (service == null) { 789 return Collections.emptyList(); 790 } 791 userId = mUserId; 792 } 793 794 List<AccessibilityServiceInfo> services = null; 795 try { 796 services = service.getEnabledAccessibilityServiceList(feedbackTypeFlags, userId); 797 if (DEBUG) { 798 Log.i(LOG_TAG, "Enabled AccessibilityServices " + services); 799 } 800 } catch (RemoteException re) { 801 Log.e(LOG_TAG, "Error while obtaining the enabled AccessibilityServices. ", re); 802 } 803 if (mAccessibilityPolicy != null) { 804 services = mAccessibilityPolicy.getEnabledAccessibilityServiceList( 805 feedbackTypeFlags, services); 806 } 807 if (services != null) { 808 return Collections.unmodifiableList(services); 809 } else { 810 return Collections.emptyList(); 811 } 812 } 813 814 /** 815 * Registers an {@link AccessibilityStateChangeListener} for changes in 816 * the global accessibility state of the system. Equivalent to calling 817 * {@link #addAccessibilityStateChangeListener(AccessibilityStateChangeListener, Handler)} 818 * with a null handler. 819 * 820 * @param listener The listener. 821 * @return Always returns {@code true}. 822 */ addAccessibilityStateChangeListener( @onNull AccessibilityStateChangeListener listener)823 public boolean addAccessibilityStateChangeListener( 824 @NonNull AccessibilityStateChangeListener listener) { 825 addAccessibilityStateChangeListener(listener, null); 826 return true; 827 } 828 829 /** 830 * Registers an {@link AccessibilityStateChangeListener} for changes in 831 * the global accessibility state of the system. If the listener has already been registered, 832 * the handler used to call it back is updated. 833 * 834 * @param listener The listener. 835 * @param handler The handler on which the listener should be called back, or {@code null} 836 * for a callback on the process's main handler. 837 */ addAccessibilityStateChangeListener( @onNull AccessibilityStateChangeListener listener, @Nullable Handler handler)838 public void addAccessibilityStateChangeListener( 839 @NonNull AccessibilityStateChangeListener listener, @Nullable Handler handler) { 840 synchronized (mLock) { 841 mAccessibilityStateChangeListeners 842 .put(listener, (handler == null) ? mHandler : handler); 843 } 844 } 845 846 /** 847 * Unregisters an {@link AccessibilityStateChangeListener}. 848 * 849 * @param listener The listener. 850 * @return True if the listener was previously registered. 851 */ removeAccessibilityStateChangeListener( @onNull AccessibilityStateChangeListener listener)852 public boolean removeAccessibilityStateChangeListener( 853 @NonNull AccessibilityStateChangeListener listener) { 854 synchronized (mLock) { 855 int index = mAccessibilityStateChangeListeners.indexOfKey(listener); 856 mAccessibilityStateChangeListeners.remove(listener); 857 return (index >= 0); 858 } 859 } 860 861 /** 862 * Registers a {@link TouchExplorationStateChangeListener} for changes in 863 * the global touch exploration state of the system. Equivalent to calling 864 * {@link #addTouchExplorationStateChangeListener(TouchExplorationStateChangeListener, Handler)} 865 * with a null handler. 866 * 867 * @param listener The listener. 868 * @return Always returns {@code true}. 869 */ addTouchExplorationStateChangeListener( @onNull TouchExplorationStateChangeListener listener)870 public boolean addTouchExplorationStateChangeListener( 871 @NonNull TouchExplorationStateChangeListener listener) { 872 addTouchExplorationStateChangeListener(listener, null); 873 return true; 874 } 875 876 /** 877 * Registers an {@link TouchExplorationStateChangeListener} for changes in 878 * the global touch exploration state of the system. If the listener has already been 879 * registered, the handler used to call it back is updated. 880 * 881 * @param listener The listener. 882 * @param handler The handler on which the listener should be called back, or {@code null} 883 * for a callback on the process's main handler. 884 */ addTouchExplorationStateChangeListener( @onNull TouchExplorationStateChangeListener listener, @Nullable Handler handler)885 public void addTouchExplorationStateChangeListener( 886 @NonNull TouchExplorationStateChangeListener listener, @Nullable Handler handler) { 887 synchronized (mLock) { 888 mTouchExplorationStateChangeListeners 889 .put(listener, (handler == null) ? mHandler : handler); 890 } 891 } 892 893 /** 894 * Unregisters a {@link TouchExplorationStateChangeListener}. 895 * 896 * @param listener The listener. 897 * @return True if listener was previously registered. 898 */ removeTouchExplorationStateChangeListener( @onNull TouchExplorationStateChangeListener listener)899 public boolean removeTouchExplorationStateChangeListener( 900 @NonNull TouchExplorationStateChangeListener listener) { 901 synchronized (mLock) { 902 int index = mTouchExplorationStateChangeListeners.indexOfKey(listener); 903 mTouchExplorationStateChangeListeners.remove(listener); 904 return (index >= 0); 905 } 906 } 907 908 /** 909 * Registers a {@link AccessibilityServicesStateChangeListener}. 910 * 911 * @param listener The listener. 912 * @param handler The handler on which the listener should be called back, or {@code null} 913 * for a callback on the process's main handler. 914 * @hide 915 */ 916 @TestApi addAccessibilityServicesStateChangeListener( @onNull AccessibilityServicesStateChangeListener listener, @Nullable Handler handler)917 public void addAccessibilityServicesStateChangeListener( 918 @NonNull AccessibilityServicesStateChangeListener listener, @Nullable Handler handler) { 919 synchronized (mLock) { 920 mServicesStateChangeListeners 921 .put(listener, (handler == null) ? mHandler : handler); 922 } 923 } 924 925 /** 926 * Unregisters a {@link AccessibilityServicesStateChangeListener}. 927 * 928 * @param listener The listener. 929 * 930 * @hide 931 */ 932 @TestApi removeAccessibilityServicesStateChangeListener( @onNull AccessibilityServicesStateChangeListener listener)933 public void removeAccessibilityServicesStateChangeListener( 934 @NonNull AccessibilityServicesStateChangeListener listener) { 935 synchronized (mLock) { 936 mServicesStateChangeListeners.remove(listener); 937 } 938 } 939 940 /** 941 * Registers a {@link AccessibilityRequestPreparer}. 942 */ addAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer)943 public void addAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer) { 944 if (mRequestPreparerLists == null) { 945 mRequestPreparerLists = new SparseArray<>(1); 946 } 947 int id = preparer.getAccessibilityViewId(); 948 List<AccessibilityRequestPreparer> requestPreparerList = mRequestPreparerLists.get(id); 949 if (requestPreparerList == null) { 950 requestPreparerList = new ArrayList<>(1); 951 mRequestPreparerLists.put(id, requestPreparerList); 952 } 953 requestPreparerList.add(preparer); 954 } 955 956 /** 957 * Unregisters a {@link AccessibilityRequestPreparer}. 958 */ removeAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer)959 public void removeAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer) { 960 if (mRequestPreparerLists == null) { 961 return; 962 } 963 int viewId = preparer.getAccessibilityViewId(); 964 List<AccessibilityRequestPreparer> requestPreparerList = mRequestPreparerLists.get(viewId); 965 if (requestPreparerList != null) { 966 requestPreparerList.remove(preparer); 967 if (requestPreparerList.isEmpty()) { 968 mRequestPreparerLists.remove(viewId); 969 } 970 } 971 } 972 973 /** 974 * Get the recommended timeout for changes to the UI needed by this user. Controls should remain 975 * on the screen for at least this long to give users time to react. Some users may need 976 * extra time to review the controls, or to reach them, or to activate assistive technology 977 * to activate the controls automatically. 978 * <p> 979 * Use the combination of content flags to indicate contents of UI. For example, use 980 * {@code FLAG_CONTENT_ICONS | FLAG_CONTENT_TEXT} for message notification which contains 981 * icons and text, or use {@code FLAG_CONTENT_TEXT | FLAG_CONTENT_CONTROLS} for button dialog 982 * which contains text and button controls. 983 * <p/> 984 * 985 * @param originalTimeout The timeout appropriate for users with no accessibility needs. 986 * @param uiContentFlags The combination of flags {@link #FLAG_CONTENT_ICONS}, 987 * {@link #FLAG_CONTENT_TEXT} or {@link #FLAG_CONTENT_CONTROLS} to 988 * indicate the contents of UI. 989 * @return The recommended UI timeout for the current user in milliseconds. 990 */ getRecommendedTimeoutMillis(int originalTimeout, @ContentFlag int uiContentFlags)991 public int getRecommendedTimeoutMillis(int originalTimeout, @ContentFlag int uiContentFlags) { 992 boolean hasControls = (uiContentFlags & FLAG_CONTENT_CONTROLS) != 0; 993 boolean hasIconsOrText = (uiContentFlags & FLAG_CONTENT_ICONS) != 0 994 || (uiContentFlags & FLAG_CONTENT_TEXT) != 0; 995 int recommendedTimeout = originalTimeout; 996 if (hasControls) { 997 recommendedTimeout = Math.max(recommendedTimeout, mInteractiveUiTimeout); 998 } 999 if (hasIconsOrText) { 1000 recommendedTimeout = Math.max(recommendedTimeout, mNonInteractiveUiTimeout); 1001 } 1002 return recommendedTimeout; 1003 } 1004 1005 /** 1006 * Gets the strokeWidth of the focus rectangle. This value can be set by 1007 * {@link AccessibilityService}. 1008 * 1009 * @return The strokeWidth of the focus rectangle in pixels. 1010 * 1011 */ getAccessibilityFocusStrokeWidth()1012 public int getAccessibilityFocusStrokeWidth() { 1013 synchronized (mLock) { 1014 return mFocusStrokeWidth; 1015 } 1016 } 1017 1018 /** 1019 * Gets the color of the focus rectangle. This value can be set by 1020 * {@link AccessibilityService}. 1021 * 1022 * @return The color of the focus rectangle. 1023 * 1024 */ getAccessibilityFocusColor()1025 public @ColorInt int getAccessibilityFocusColor() { 1026 synchronized (mLock) { 1027 return mFocusColor; 1028 } 1029 } 1030 1031 /** 1032 * Gets accessibility tracing enabled state. 1033 * 1034 * @hide 1035 */ isAccessibilityTracingEnabled()1036 public boolean isAccessibilityTracingEnabled() { 1037 synchronized (mLock) { 1038 return mIsAccessibilityTracingEnabled; 1039 } 1040 } 1041 1042 /** 1043 * Get the preparers that are registered for an accessibility ID 1044 * 1045 * @param id The ID of interest 1046 * @return The list of preparers, or {@code null} if there are none. 1047 * 1048 * @hide 1049 */ getRequestPreparersForAccessibilityId(int id)1050 public List<AccessibilityRequestPreparer> getRequestPreparersForAccessibilityId(int id) { 1051 if (mRequestPreparerLists == null) { 1052 return null; 1053 } 1054 return mRequestPreparerLists.get(id); 1055 } 1056 1057 /** 1058 * Set the currently performing accessibility action in views. 1059 * 1060 * @param actionId the action id of {@link AccessibilityNodeInfo.AccessibilityAction}. 1061 * @hide 1062 */ notifyPerformingAction(int actionId)1063 public void notifyPerformingAction(int actionId) { 1064 mPerformingAction = actionId; 1065 } 1066 1067 /** 1068 * Registers a {@link HighTextContrastChangeListener} for changes in 1069 * the global high text contrast state of the system. 1070 * 1071 * @param listener The listener. 1072 * 1073 * @hide 1074 */ addHighTextContrastStateChangeListener( @onNull HighTextContrastChangeListener listener, @Nullable Handler handler)1075 public void addHighTextContrastStateChangeListener( 1076 @NonNull HighTextContrastChangeListener listener, @Nullable Handler handler) { 1077 synchronized (mLock) { 1078 mHighTextContrastStateChangeListeners 1079 .put(listener, (handler == null) ? mHandler : handler); 1080 } 1081 } 1082 1083 /** 1084 * Unregisters a {@link HighTextContrastChangeListener}. 1085 * 1086 * @param listener The listener. 1087 * 1088 * @hide 1089 */ removeHighTextContrastStateChangeListener( @onNull HighTextContrastChangeListener listener)1090 public void removeHighTextContrastStateChangeListener( 1091 @NonNull HighTextContrastChangeListener listener) { 1092 synchronized (mLock) { 1093 mHighTextContrastStateChangeListeners.remove(listener); 1094 } 1095 } 1096 1097 /** 1098 * Sets the {@link AccessibilityPolicy} controlling this manager. 1099 * 1100 * @param policy The policy. 1101 * 1102 * @hide 1103 */ setAccessibilityPolicy(@ullable AccessibilityPolicy policy)1104 public void setAccessibilityPolicy(@Nullable AccessibilityPolicy policy) { 1105 synchronized (mLock) { 1106 mAccessibilityPolicy = policy; 1107 } 1108 } 1109 1110 /** 1111 * Check if the accessibility volume stream is active. 1112 * 1113 * @return True if accessibility volume is active (i.e. some service has requested it). False 1114 * otherwise. 1115 * @hide 1116 */ isAccessibilityVolumeStreamActive()1117 public boolean isAccessibilityVolumeStreamActive() { 1118 List<AccessibilityServiceInfo> serviceInfos = 1119 getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK); 1120 for (int i = 0; i < serviceInfos.size(); i++) { 1121 if ((serviceInfos.get(i).flags & FLAG_ENABLE_ACCESSIBILITY_VOLUME) != 0) { 1122 return true; 1123 } 1124 } 1125 return false; 1126 } 1127 1128 /** 1129 * Report a fingerprint gesture to accessibility. Only available for the system process. 1130 * 1131 * @param keyCode The key code of the gesture 1132 * @return {@code true} if accessibility consumes the event. {@code false} if not. 1133 * @hide 1134 */ sendFingerprintGesture(int keyCode)1135 public boolean sendFingerprintGesture(int keyCode) { 1136 final IAccessibilityManager service; 1137 synchronized (mLock) { 1138 service = getServiceLocked(); 1139 if (service == null) { 1140 return false; 1141 } 1142 } 1143 try { 1144 return service.sendFingerprintGesture(keyCode); 1145 } catch (RemoteException e) { 1146 return false; 1147 } 1148 } 1149 1150 /** 1151 * Returns accessibility window id from window token. Accessibility window id is the one 1152 * returned from AccessibilityWindowInfo.getId(). Only available for the system process. 1153 * 1154 * @param windowToken Window token to find accessibility window id. 1155 * @return Accessibility window id for the window token. 1156 * AccessibilityWindowInfo.UNDEFINED_WINDOW_ID if accessibility window id not available for 1157 * the token. 1158 * @hide 1159 */ 1160 @SystemApi getAccessibilityWindowId(@ullable IBinder windowToken)1161 public int getAccessibilityWindowId(@Nullable IBinder windowToken) { 1162 if (windowToken == null) { 1163 return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 1164 } 1165 1166 final IAccessibilityManager service; 1167 synchronized (mLock) { 1168 service = getServiceLocked(); 1169 if (service == null) { 1170 return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 1171 } 1172 } 1173 try { 1174 return service.getAccessibilityWindowId(windowToken); 1175 } catch (RemoteException e) { 1176 return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 1177 } 1178 } 1179 1180 /** 1181 * Associate the connection between the host View and the embedded SurfaceControlViewHost. 1182 * 1183 * @hide 1184 */ associateEmbeddedHierarchy(@onNull IBinder host, @NonNull IBinder embedded)1185 public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) { 1186 final IAccessibilityManager service; 1187 synchronized (mLock) { 1188 service = getServiceLocked(); 1189 if (service == null) { 1190 return; 1191 } 1192 } 1193 try { 1194 service.associateEmbeddedHierarchy(host, embedded); 1195 } catch (RemoteException e) { 1196 return; 1197 } 1198 } 1199 1200 /** 1201 * Disassociate the connection between the host View and the embedded SurfaceControlViewHost. 1202 * The given token could be either from host side or embedded side. 1203 * 1204 * @hide 1205 */ disassociateEmbeddedHierarchy(@onNull IBinder token)1206 public void disassociateEmbeddedHierarchy(@NonNull IBinder token) { 1207 if (token == null) { 1208 return; 1209 } 1210 final IAccessibilityManager service; 1211 synchronized (mLock) { 1212 service = getServiceLocked(); 1213 if (service == null) { 1214 return; 1215 } 1216 } 1217 try { 1218 service.disassociateEmbeddedHierarchy(token); 1219 } catch (RemoteException e) { 1220 return; 1221 } 1222 } 1223 1224 /** 1225 * Sets the current state and notifies listeners, if necessary. 1226 * 1227 * @param stateFlags The state flags. 1228 */ 1229 @UnsupportedAppUsage setStateLocked(int stateFlags)1230 private void setStateLocked(int stateFlags) { 1231 final boolean enabled = (stateFlags & STATE_FLAG_ACCESSIBILITY_ENABLED) != 0; 1232 final boolean touchExplorationEnabled = 1233 (stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0; 1234 final boolean highTextContrastEnabled = 1235 (stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0; 1236 final boolean accessibilityTracingEnabled = 1237 (stateFlags & STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED) != 0; 1238 1239 final boolean wasEnabled = isEnabled(); 1240 final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled; 1241 final boolean wasHighTextContrastEnabled = mIsHighTextContrastEnabled; 1242 1243 // Ensure listeners get current state from isZzzEnabled() calls. 1244 mIsEnabled = enabled; 1245 mIsTouchExplorationEnabled = touchExplorationEnabled; 1246 mIsHighTextContrastEnabled = highTextContrastEnabled; 1247 1248 if (wasEnabled != isEnabled()) { 1249 notifyAccessibilityStateChanged(); 1250 } 1251 1252 if (wasTouchExplorationEnabled != touchExplorationEnabled) { 1253 notifyTouchExplorationStateChanged(); 1254 } 1255 1256 if (wasHighTextContrastEnabled != highTextContrastEnabled) { 1257 notifyHighTextContrastStateChanged(); 1258 } 1259 1260 updateAccessibilityTracingState(accessibilityTracingEnabled); 1261 } 1262 1263 /** 1264 * Find an installed service with the specified {@link ComponentName}. 1265 * 1266 * @param componentName The name to match to the service. 1267 * 1268 * @return The info corresponding to the installed service, or {@code null} if no such service 1269 * is installed. 1270 * @hide 1271 */ getInstalledServiceInfoWithComponentName( ComponentName componentName)1272 public AccessibilityServiceInfo getInstalledServiceInfoWithComponentName( 1273 ComponentName componentName) { 1274 final List<AccessibilityServiceInfo> installedServiceInfos = 1275 getInstalledAccessibilityServiceList(); 1276 if ((installedServiceInfos == null) || (componentName == null)) { 1277 return null; 1278 } 1279 for (int i = 0; i < installedServiceInfos.size(); i++) { 1280 if (componentName.equals(installedServiceInfos.get(i).getComponentName())) { 1281 return installedServiceInfos.get(i); 1282 } 1283 } 1284 return null; 1285 } 1286 1287 /** 1288 * Adds an accessibility interaction connection interface for a given window. 1289 * @param windowToken The window token to which a connection is added. 1290 * @param leashToken The leash token to which a connection is added. 1291 * @param connection The connection. 1292 * 1293 * @hide 1294 */ addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken, String packageName, IAccessibilityInteractionConnection connection)1295 public int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken, 1296 String packageName, IAccessibilityInteractionConnection connection) { 1297 final IAccessibilityManager service; 1298 final int userId; 1299 synchronized (mLock) { 1300 service = getServiceLocked(); 1301 if (service == null) { 1302 return View.NO_ID; 1303 } 1304 userId = mUserId; 1305 } 1306 try { 1307 return service.addAccessibilityInteractionConnection(windowToken, leashToken, 1308 connection, packageName, userId); 1309 } catch (RemoteException re) { 1310 Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re); 1311 } 1312 return View.NO_ID; 1313 } 1314 1315 /** 1316 * Removed an accessibility interaction connection interface for a given window. 1317 * @param windowToken The window token to which a connection is removed. 1318 * 1319 * @hide 1320 */ removeAccessibilityInteractionConnection(IWindow windowToken)1321 public void removeAccessibilityInteractionConnection(IWindow windowToken) { 1322 final IAccessibilityManager service; 1323 synchronized (mLock) { 1324 service = getServiceLocked(); 1325 if (service == null) { 1326 return; 1327 } 1328 } 1329 try { 1330 service.removeAccessibilityInteractionConnection(windowToken); 1331 } catch (RemoteException re) { 1332 Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re); 1333 } 1334 } 1335 1336 /** 1337 * Perform the accessibility shortcut if the caller has permission. 1338 * 1339 * @hide 1340 */ 1341 @SystemApi 1342 @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) performAccessibilityShortcut()1343 public void performAccessibilityShortcut() { 1344 performAccessibilityShortcut(null); 1345 } 1346 1347 /** 1348 * Perform the accessibility shortcut for the given target which is assigned to the shortcut. 1349 * 1350 * @param targetName The flattened {@link ComponentName} string or the class name of a system 1351 * class implementing a supported accessibility feature, or {@code null} if there's no 1352 * specified target. 1353 * @hide 1354 */ 1355 @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) performAccessibilityShortcut(@ullable String targetName)1356 public void performAccessibilityShortcut(@Nullable String targetName) { 1357 final IAccessibilityManager service; 1358 synchronized (mLock) { 1359 service = getServiceLocked(); 1360 if (service == null) { 1361 return; 1362 } 1363 } 1364 try { 1365 service.performAccessibilityShortcut(targetName); 1366 } catch (RemoteException re) { 1367 Log.e(LOG_TAG, "Error performing accessibility shortcut. ", re); 1368 } 1369 } 1370 1371 /** 1372 * Register the provided {@link RemoteAction} with the given actionId 1373 * <p> 1374 * To perform established system actions, an accessibility service uses the GLOBAL_ACTION 1375 * constants in {@link android.accessibilityservice.AccessibilityService}. To provide a 1376 * customized implementation for one of these actions, the id of the registered system action 1377 * must match that of the corresponding GLOBAL_ACTION constant. For example, to register a 1378 * Back action, {@code actionId} must be 1379 * {@link android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_BACK} 1380 * </p> 1381 * @param action The remote action to be registered with the given actionId as system action. 1382 * @param actionId The id uniquely identify the system action. 1383 * @hide 1384 */ 1385 @SystemApi 1386 @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) registerSystemAction(@onNull RemoteAction action, int actionId)1387 public void registerSystemAction(@NonNull RemoteAction action, int actionId) { 1388 final IAccessibilityManager service; 1389 synchronized (mLock) { 1390 service = getServiceLocked(); 1391 if (service == null) { 1392 return; 1393 } 1394 } 1395 try { 1396 service.registerSystemAction(action, actionId); 1397 1398 if (DEBUG) { 1399 Log.i(LOG_TAG, "System action " + action.getTitle() + " is registered."); 1400 } 1401 } catch (RemoteException re) { 1402 Log.e(LOG_TAG, "Error registering system action " + action.getTitle() + " ", re); 1403 } 1404 } 1405 1406 /** 1407 * Unregister a system action with the given actionId 1408 * 1409 * @param actionId The id uniquely identify the system action to be unregistered. 1410 * @hide 1411 */ 1412 @SystemApi 1413 @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) unregisterSystemAction(int actionId)1414 public void unregisterSystemAction(int actionId) { 1415 final IAccessibilityManager service; 1416 synchronized (mLock) { 1417 service = getServiceLocked(); 1418 if (service == null) { 1419 return; 1420 } 1421 } 1422 try { 1423 service.unregisterSystemAction(actionId); 1424 1425 if (DEBUG) { 1426 Log.i(LOG_TAG, "System action with actionId " + actionId + " is unregistered."); 1427 } 1428 } catch (RemoteException re) { 1429 Log.e(LOG_TAG, "Error unregistering system action with actionId " + actionId + " ", re); 1430 } 1431 } 1432 1433 /** 1434 * Notifies that the accessibility button in the system's navigation area has been clicked 1435 * 1436 * @param displayId The logical display id. 1437 * @hide 1438 */ 1439 @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) notifyAccessibilityButtonClicked(int displayId)1440 public void notifyAccessibilityButtonClicked(int displayId) { 1441 notifyAccessibilityButtonClicked(displayId, null); 1442 } 1443 1444 /** 1445 * Perform the accessibility button for the given target which is assigned to the button. 1446 * 1447 * @param displayId displayId The logical display id. 1448 * @param targetName The flattened {@link ComponentName} string or the class name of a system 1449 * class implementing a supported accessibility feature, or {@code null} if there's no 1450 * specified target. 1451 * @hide 1452 */ 1453 @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) notifyAccessibilityButtonClicked(int displayId, @Nullable String targetName)1454 public void notifyAccessibilityButtonClicked(int displayId, @Nullable String targetName) { 1455 final IAccessibilityManager service; 1456 synchronized (mLock) { 1457 service = getServiceLocked(); 1458 if (service == null) { 1459 return; 1460 } 1461 } 1462 try { 1463 service.notifyAccessibilityButtonClicked(displayId, targetName); 1464 } catch (RemoteException re) { 1465 Log.e(LOG_TAG, "Error while dispatching accessibility button click", re); 1466 } 1467 } 1468 1469 /** 1470 * Notifies that the visibility of the accessibility button in the system's navigation area 1471 * has changed. 1472 * 1473 * @param shown {@code true} if the accessibility button is visible within the system 1474 * navigation area, {@code false} otherwise 1475 * @hide 1476 */ notifyAccessibilityButtonVisibilityChanged(boolean shown)1477 public void notifyAccessibilityButtonVisibilityChanged(boolean shown) { 1478 final IAccessibilityManager service; 1479 synchronized (mLock) { 1480 service = getServiceLocked(); 1481 if (service == null) { 1482 return; 1483 } 1484 } 1485 try { 1486 service.notifyAccessibilityButtonVisibilityChanged(shown); 1487 } catch (RemoteException re) { 1488 Log.e(LOG_TAG, "Error while dispatching accessibility button visibility change", re); 1489 } 1490 } 1491 1492 /** 1493 * Set an IAccessibilityInteractionConnection to replace the actions of a picture-in-picture 1494 * window. Intended for use by the System UI only. 1495 * 1496 * @param connection The connection to handle the actions. Set to {@code null} to avoid 1497 * affecting the actions. 1498 * 1499 * @hide 1500 */ setPictureInPictureActionReplacingConnection( @ullable IAccessibilityInteractionConnection connection)1501 public void setPictureInPictureActionReplacingConnection( 1502 @Nullable IAccessibilityInteractionConnection connection) { 1503 final IAccessibilityManager service; 1504 synchronized (mLock) { 1505 service = getServiceLocked(); 1506 if (service == null) { 1507 return; 1508 } 1509 } 1510 try { 1511 service.setPictureInPictureActionReplacingConnection(connection); 1512 } catch (RemoteException re) { 1513 Log.e(LOG_TAG, "Error setting picture in picture action replacement", re); 1514 } 1515 } 1516 1517 /** 1518 * Returns the list of shortcut target names currently assigned to the given shortcut. 1519 * 1520 * @param shortcutType The shortcut type. 1521 * @return The list of shortcut target names. 1522 * @hide 1523 */ 1524 @TestApi 1525 @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) 1526 @NonNull getAccessibilityShortcutTargets(@hortcutType int shortcutType)1527 public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) { 1528 final IAccessibilityManager service; 1529 synchronized (mLock) { 1530 service = getServiceLocked(); 1531 } 1532 if (service != null) { 1533 try { 1534 return service.getAccessibilityShortcutTargets(shortcutType); 1535 } catch (RemoteException re) { 1536 re.rethrowFromSystemServer(); 1537 } 1538 } 1539 return Collections.emptyList(); 1540 } 1541 1542 /** 1543 * Returns the {@link AccessibilityShortcutInfo}s of the installed accessibility shortcut 1544 * targets, for specific user. 1545 * 1546 * @param context The context of the application. 1547 * @param userId The user id. 1548 * @return A list with {@link AccessibilityShortcutInfo}s. 1549 * @hide 1550 */ 1551 @NonNull getInstalledAccessibilityShortcutListAsUser( @onNull Context context, @UserIdInt int userId)1552 public List<AccessibilityShortcutInfo> getInstalledAccessibilityShortcutListAsUser( 1553 @NonNull Context context, @UserIdInt int userId) { 1554 final List<AccessibilityShortcutInfo> shortcutInfos = new ArrayList<>(); 1555 final int flags = PackageManager.GET_ACTIVITIES 1556 | PackageManager.GET_META_DATA 1557 | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS 1558 | PackageManager.MATCH_DIRECT_BOOT_AWARE 1559 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; 1560 final Intent actionMain = new Intent(Intent.ACTION_MAIN); 1561 actionMain.addCategory(Intent.CATEGORY_ACCESSIBILITY_SHORTCUT_TARGET); 1562 1563 final PackageManager packageManager = context.getPackageManager(); 1564 final List<ResolveInfo> installedShortcutList = 1565 packageManager.queryIntentActivitiesAsUser(actionMain, flags, userId); 1566 for (int i = 0; i < installedShortcutList.size(); i++) { 1567 final AccessibilityShortcutInfo shortcutInfo = 1568 getShortcutInfo(context, installedShortcutList.get(i)); 1569 if (shortcutInfo != null) { 1570 shortcutInfos.add(shortcutInfo); 1571 } 1572 } 1573 return shortcutInfos; 1574 } 1575 1576 /** 1577 * Returns an {@link AccessibilityShortcutInfo} according to the given {@link ResolveInfo} of 1578 * an activity. 1579 * 1580 * @param context The context of the application. 1581 * @param resolveInfo The resolve info of an activity. 1582 * @return The AccessibilityShortcutInfo. 1583 */ 1584 @Nullable getShortcutInfo(@onNull Context context, @NonNull ResolveInfo resolveInfo)1585 private AccessibilityShortcutInfo getShortcutInfo(@NonNull Context context, 1586 @NonNull ResolveInfo resolveInfo) { 1587 final ActivityInfo activityInfo = resolveInfo.activityInfo; 1588 if (activityInfo == null || activityInfo.metaData == null 1589 || activityInfo.metaData.getInt(AccessibilityShortcutInfo.META_DATA) == 0) { 1590 return null; 1591 } 1592 try { 1593 return new AccessibilityShortcutInfo(context, activityInfo); 1594 } catch (XmlPullParserException | IOException exp) { 1595 Log.e(LOG_TAG, "Error while initializing AccessibilityShortcutInfo", exp); 1596 } 1597 return null; 1598 } 1599 1600 /** 1601 * 1602 * Sets an {@link IWindowMagnificationConnection} that manipulates window magnification. 1603 * 1604 * @param connection The connection that manipulates window magnification. 1605 * @hide 1606 */ setWindowMagnificationConnection(@ullable IWindowMagnificationConnection connection)1607 public void setWindowMagnificationConnection(@Nullable 1608 IWindowMagnificationConnection connection) { 1609 final IAccessibilityManager service; 1610 synchronized (mLock) { 1611 service = getServiceLocked(); 1612 if (service == null) { 1613 return; 1614 } 1615 } 1616 try { 1617 service.setWindowMagnificationConnection(connection); 1618 } catch (RemoteException re) { 1619 Log.e(LOG_TAG, "Error setting window magnfication connection", re); 1620 } 1621 } 1622 getServiceLocked()1623 private IAccessibilityManager getServiceLocked() { 1624 if (mService == null) { 1625 tryConnectToServiceLocked(null); 1626 } 1627 return mService; 1628 } 1629 tryConnectToServiceLocked(IAccessibilityManager service)1630 private void tryConnectToServiceLocked(IAccessibilityManager service) { 1631 if (service == null) { 1632 IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE); 1633 if (iBinder == null) { 1634 return; 1635 } 1636 service = IAccessibilityManager.Stub.asInterface(iBinder); 1637 } 1638 1639 try { 1640 final long userStateAndRelevantEvents = service.addClient(mClient, mUserId); 1641 setStateLocked(IntPair.first(userStateAndRelevantEvents)); 1642 mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents); 1643 updateUiTimeout(service.getRecommendedTimeoutMillis()); 1644 updateFocusAppearanceLocked(service.getFocusStrokeWidth(), service.getFocusColor()); 1645 mService = service; 1646 } catch (RemoteException re) { 1647 Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); 1648 } 1649 } 1650 1651 /** 1652 * Notifies the registered {@link AccessibilityStateChangeListener}s. 1653 */ notifyAccessibilityStateChanged()1654 private void notifyAccessibilityStateChanged() { 1655 final boolean isEnabled; 1656 final ArrayMap<AccessibilityStateChangeListener, Handler> listeners; 1657 synchronized (mLock) { 1658 if (mAccessibilityStateChangeListeners.isEmpty()) { 1659 return; 1660 } 1661 isEnabled = isEnabled(); 1662 listeners = new ArrayMap<>(mAccessibilityStateChangeListeners); 1663 } 1664 1665 final int numListeners = listeners.size(); 1666 for (int i = 0; i < numListeners; i++) { 1667 final AccessibilityStateChangeListener listener = listeners.keyAt(i); 1668 listeners.valueAt(i).post(() -> 1669 listener.onAccessibilityStateChanged(isEnabled)); 1670 } 1671 } 1672 1673 /** 1674 * Notifies the registered {@link TouchExplorationStateChangeListener}s. 1675 */ notifyTouchExplorationStateChanged()1676 private void notifyTouchExplorationStateChanged() { 1677 final boolean isTouchExplorationEnabled; 1678 final ArrayMap<TouchExplorationStateChangeListener, Handler> listeners; 1679 synchronized (mLock) { 1680 if (mTouchExplorationStateChangeListeners.isEmpty()) { 1681 return; 1682 } 1683 isTouchExplorationEnabled = mIsTouchExplorationEnabled; 1684 listeners = new ArrayMap<>(mTouchExplorationStateChangeListeners); 1685 } 1686 1687 final int numListeners = listeners.size(); 1688 for (int i = 0; i < numListeners; i++) { 1689 final TouchExplorationStateChangeListener listener = listeners.keyAt(i); 1690 listeners.valueAt(i).post(() -> 1691 listener.onTouchExplorationStateChanged(isTouchExplorationEnabled)); 1692 } 1693 } 1694 1695 /** 1696 * Notifies the registered {@link HighTextContrastChangeListener}s. 1697 */ notifyHighTextContrastStateChanged()1698 private void notifyHighTextContrastStateChanged() { 1699 final boolean isHighTextContrastEnabled; 1700 final ArrayMap<HighTextContrastChangeListener, Handler> listeners; 1701 synchronized (mLock) { 1702 if (mHighTextContrastStateChangeListeners.isEmpty()) { 1703 return; 1704 } 1705 isHighTextContrastEnabled = mIsHighTextContrastEnabled; 1706 listeners = new ArrayMap<>(mHighTextContrastStateChangeListeners); 1707 } 1708 1709 final int numListeners = listeners.size(); 1710 for (int i = 0; i < numListeners; i++) { 1711 final HighTextContrastChangeListener listener = listeners.keyAt(i); 1712 listeners.valueAt(i).post(() -> 1713 listener.onHighTextContrastStateChanged(isHighTextContrastEnabled)); 1714 } 1715 } 1716 1717 /** 1718 * Update mIsAccessibilityTracingEnabled. 1719 */ updateAccessibilityTracingState(boolean enabled)1720 private void updateAccessibilityTracingState(boolean enabled) { 1721 synchronized (mLock) { 1722 mIsAccessibilityTracingEnabled = enabled; 1723 } 1724 } 1725 1726 /** 1727 * Update interactive and non-interactive UI timeout. 1728 * 1729 * @param uiTimeout A pair of {@code int}s. First integer for interactive one, and second 1730 * integer for non-interactive one. 1731 */ updateUiTimeout(long uiTimeout)1732 private void updateUiTimeout(long uiTimeout) { 1733 mInteractiveUiTimeout = IntPair.first(uiTimeout); 1734 mNonInteractiveUiTimeout = IntPair.second(uiTimeout); 1735 } 1736 1737 /** 1738 * Updates the stroke width and color of the focus rectangle. 1739 * 1740 * @param strokeWidth The strokeWidth of the focus rectangle. 1741 * @param color The color of the focus rectangle. 1742 */ updateFocusAppearanceLocked(int strokeWidth, int color)1743 private void updateFocusAppearanceLocked(int strokeWidth, int color) { 1744 if (mFocusStrokeWidth == strokeWidth && mFocusColor == color) { 1745 return; 1746 } 1747 mFocusStrokeWidth = strokeWidth; 1748 mFocusColor = color; 1749 } 1750 1751 /** 1752 * Sets the stroke width and color of the focus rectangle to default value. 1753 * 1754 * @param resource The resources. 1755 */ initialFocusAppearanceLocked(Resources resource)1756 private void initialFocusAppearanceLocked(Resources resource) { 1757 try { 1758 mFocusStrokeWidth = resource.getDimensionPixelSize( 1759 R.dimen.accessibility_focus_highlight_stroke_width); 1760 mFocusColor = resource.getColor(R.color.accessibility_focus_highlight_color); 1761 } catch (Resources.NotFoundException re) { 1762 // Sets the stroke width and color to default value by hardcoded for making 1763 // the Talkback can work normally. 1764 mFocusStrokeWidth = (int) (4 * resource.getDisplayMetrics().density); 1765 mFocusColor = 0xbf39b500; 1766 Log.e(LOG_TAG, "Error while initialing the focus appearance data then setting to" 1767 + " default value by hardcoded", re); 1768 } 1769 } 1770 1771 /** 1772 * Determines if the accessibility button within the system navigation area is supported. 1773 * 1774 * @return {@code true} if the accessibility button is supported on this device, 1775 * {@code false} otherwise 1776 */ isAccessibilityButtonSupported()1777 public static boolean isAccessibilityButtonSupported() { 1778 final Resources res = Resources.getSystem(); 1779 return res.getBoolean(com.android.internal.R.bool.config_showNavigationBar); 1780 } 1781 1782 private final class MyCallback implements Handler.Callback { 1783 public static final int MSG_SET_STATE = 1; 1784 1785 @Override handleMessage(Message message)1786 public boolean handleMessage(Message message) { 1787 switch (message.what) { 1788 case MSG_SET_STATE: { 1789 // See comment at mClient 1790 final int state = message.arg1; 1791 synchronized (mLock) { 1792 setStateLocked(state); 1793 } 1794 } break; 1795 } 1796 return true; 1797 } 1798 } 1799 } 1800