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.CallbackExecutor; 27 import android.annotation.ColorInt; 28 import android.annotation.IntDef; 29 import android.annotation.NonNull; 30 import android.annotation.Nullable; 31 import android.annotation.RequiresPermission; 32 import android.annotation.SdkConstant; 33 import android.annotation.SystemApi; 34 import android.annotation.SystemService; 35 import android.annotation.TestApi; 36 import android.annotation.UserIdInt; 37 import android.app.RemoteAction; 38 import android.compat.annotation.UnsupportedAppUsage; 39 import android.content.ComponentName; 40 import android.content.Context; 41 import android.content.Intent; 42 import android.content.pm.ActivityInfo; 43 import android.content.pm.PackageManager; 44 import android.content.pm.ResolveInfo; 45 import android.content.pm.ServiceInfo; 46 import android.content.res.Resources; 47 import android.os.Binder; 48 import android.os.Build; 49 import android.os.Handler; 50 import android.os.HandlerExecutor; 51 import android.os.IBinder; 52 import android.os.Looper; 53 import android.os.Message; 54 import android.os.Process; 55 import android.os.RemoteException; 56 import android.os.ServiceManager; 57 import android.os.SystemClock; 58 import android.os.UserHandle; 59 import android.util.ArrayMap; 60 import android.util.Log; 61 import android.util.SparseArray; 62 import android.view.IWindow; 63 import android.view.View; 64 import android.view.accessibility.AccessibilityEvent.EventType; 65 66 import com.android.internal.R; 67 import com.android.internal.annotations.VisibleForTesting; 68 import com.android.internal.util.IntPair; 69 70 import org.xmlpull.v1.XmlPullParserException; 71 72 import java.io.IOException; 73 import java.lang.annotation.Retention; 74 import java.lang.annotation.RetentionPolicy; 75 import java.util.ArrayList; 76 import java.util.Collections; 77 import java.util.List; 78 import java.util.concurrent.Executor; 79 80 /** 81 * System level service that serves as an event dispatch for {@link AccessibilityEvent}s, 82 * and provides facilities for querying the accessibility state of the system. 83 * Accessibility events are generated when something notable happens in the user interface, 84 * for example an {@link android.app.Activity} starts, the focus or selection of a 85 * {@link android.view.View} changes etc. Parties interested in handling accessibility 86 * events implement and register an accessibility service which extends 87 * {@link android.accessibilityservice.AccessibilityService}. 88 * 89 * @see AccessibilityEvent 90 * @see AccessibilityNodeInfo 91 * @see android.accessibilityservice.AccessibilityService 92 * @see Context#getSystemService 93 * @see Context#ACCESSIBILITY_SERVICE 94 */ 95 @SystemService(Context.ACCESSIBILITY_SERVICE) 96 public final class AccessibilityManager { 97 private static final boolean DEBUG = false; 98 99 private static final String LOG_TAG = "AccessibilityManager"; 100 101 /** @hide */ 102 public static final int STATE_FLAG_ACCESSIBILITY_ENABLED = 0x00000001; 103 104 /** @hide */ 105 public static final int STATE_FLAG_TOUCH_EXPLORATION_ENABLED = 0x00000002; 106 107 /** @hide */ 108 public static final int STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED = 0x00000004; 109 110 /** @hide */ 111 public static final int STATE_FLAG_DISPATCH_DOUBLE_TAP = 0x00000008; 112 113 /** @hide */ 114 public static final int STATE_FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000010; 115 116 /** @hide */ 117 public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED = 0x00000100; 118 /** @hide */ 119 public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED = 0x00000200; 120 /** @hide */ 121 public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED = 0x00000400; 122 /** @hide */ 123 public static final int STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED = 0x00000800; 124 /** @hide */ 125 public static final int STATE_FLAG_AUDIO_DESCRIPTION_BY_DEFAULT_ENABLED = 0x00001000; 126 127 /** @hide */ 128 public static final int DALTONIZER_DISABLED = -1; 129 130 /** @hide */ 131 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 132 public static final int DALTONIZER_SIMULATE_MONOCHROMACY = 0; 133 134 /** @hide */ 135 public static final int DALTONIZER_CORRECT_DEUTERANOMALY = 12; 136 137 /** @hide */ 138 public static final int AUTOCLICK_DELAY_DEFAULT = 600; 139 140 /** 141 * Activity action: Launch UI to manage which accessibility service or feature is assigned 142 * to the navigation bar Accessibility button. 143 * <p> 144 * Input: Nothing. 145 * </p> 146 * <p> 147 * Output: Nothing. 148 * </p> 149 * 150 * @hide 151 */ 152 @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) 153 public static final String ACTION_CHOOSE_ACCESSIBILITY_BUTTON = 154 "com.android.internal.intent.action.CHOOSE_ACCESSIBILITY_BUTTON"; 155 156 /** 157 * Used as an int value for accessibility chooser activity to represent the accessibility button 158 * shortcut type. 159 * 160 * @hide 161 */ 162 public static final int ACCESSIBILITY_BUTTON = 0; 163 164 /** 165 * Used as an int value for accessibility chooser activity to represent hardware key shortcut, 166 * such as volume key button. 167 * 168 * @hide 169 */ 170 public static final int ACCESSIBILITY_SHORTCUT_KEY = 1; 171 172 /** @hide */ 173 public static final int FLASH_REASON_CALL = 1; 174 175 /** @hide */ 176 public static final int FLASH_REASON_ALARM = 2; 177 178 /** @hide */ 179 public static final int FLASH_REASON_NOTIFICATION = 3; 180 181 /** @hide */ 182 public static final int FLASH_REASON_PREVIEW = 4; 183 184 /** 185 * Annotations for the shortcut type. 186 * @hide 187 */ 188 @Retention(RetentionPolicy.SOURCE) 189 @IntDef(value = { 190 ACCESSIBILITY_BUTTON, 191 ACCESSIBILITY_SHORTCUT_KEY 192 }) 193 public @interface ShortcutType {} 194 195 /** 196 * Annotations for content flag of UI. 197 * @hide 198 */ 199 @Retention(RetentionPolicy.SOURCE) 200 @IntDef(flag = true, prefix = { "FLAG_CONTENT_" }, value = { 201 FLAG_CONTENT_ICONS, 202 FLAG_CONTENT_TEXT, 203 FLAG_CONTENT_CONTROLS 204 }) 205 public @interface ContentFlag {} 206 207 /** 208 * Annotations for reason of Flash notification. 209 * @hide 210 */ 211 @Retention(RetentionPolicy.SOURCE) 212 @IntDef(prefix = { "FLASH_REASON_" }, value = { 213 FLASH_REASON_CALL, 214 FLASH_REASON_ALARM, 215 FLASH_REASON_NOTIFICATION, 216 FLASH_REASON_PREVIEW 217 }) 218 public @interface FlashNotificationReason {} 219 220 /** 221 * Use this flag to indicate the content of a UI that times out contains icons. 222 * 223 * @see #getRecommendedTimeoutMillis(int, int) 224 */ 225 public static final int FLAG_CONTENT_ICONS = 1; 226 227 /** 228 * Use this flag to indicate the content of a UI that times out contains text. 229 * 230 * @see #getRecommendedTimeoutMillis(int, int) 231 */ 232 public static final int FLAG_CONTENT_TEXT = 2; 233 234 /** 235 * Use this flag to indicate the content of a UI that times out contains interactive controls. 236 * 237 * @see #getRecommendedTimeoutMillis(int, int) 238 */ 239 public static final int FLAG_CONTENT_CONTROLS = 4; 240 241 @UnsupportedAppUsage 242 static final Object sInstanceSync = new Object(); 243 244 @UnsupportedAppUsage 245 private static AccessibilityManager sInstance; 246 247 @UnsupportedAppUsage 248 private final Object mLock = new Object(); 249 250 @UnsupportedAppUsage 251 private IAccessibilityManager mService; 252 253 @UnsupportedAppUsage 254 final int mUserId; 255 256 @UnsupportedAppUsage 257 final Handler mHandler; 258 259 final Handler.Callback mCallback; 260 261 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) 262 boolean mIsEnabled; 263 264 int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK; 265 266 int mInteractiveUiTimeout; 267 int mNonInteractiveUiTimeout; 268 269 boolean mIsTouchExplorationEnabled; 270 271 @UnsupportedAppUsage(trackingBug = 123768939L) 272 boolean mIsHighTextContrastEnabled; 273 274 boolean mIsAudioDescriptionByDefaultRequested; 275 276 // accessibility tracing state 277 int mAccessibilityTracingState = 0; 278 279 AccessibilityPolicy mAccessibilityPolicy; 280 281 private int mPerformingAction = 0; 282 283 /** The stroke width of the focus rectangle in pixels */ 284 private int mFocusStrokeWidth; 285 /** The color of the focus rectangle */ 286 private int mFocusColor; 287 288 @UnsupportedAppUsage 289 private final ArrayMap<AccessibilityStateChangeListener, Handler> 290 mAccessibilityStateChangeListeners = new ArrayMap<>(); 291 292 private final ArrayMap<TouchExplorationStateChangeListener, Handler> 293 mTouchExplorationStateChangeListeners = new ArrayMap<>(); 294 295 private final ArrayMap<HighTextContrastChangeListener, Handler> 296 mHighTextContrastStateChangeListeners = new ArrayMap<>(); 297 298 private final ArrayMap<AccessibilityServicesStateChangeListener, Executor> 299 mServicesStateChangeListeners = new ArrayMap<>(); 300 301 private final ArrayMap<AudioDescriptionRequestedChangeListener, Executor> 302 mAudioDescriptionRequestedChangeListeners = new ArrayMap<>(); 303 304 private boolean mRequestFromAccessibilityTool; 305 306 /** 307 * Map from a view's accessibility id to the list of request preparers set for that view 308 */ 309 private SparseArray<List<AccessibilityRequestPreparer>> mRequestPreparerLists; 310 311 /** 312 * Binder for flash notification. 313 * 314 * @see #startFlashNotificationSequence(Context, int) 315 */ 316 private final Binder mBinder = new Binder(); 317 318 /** 319 * Listener for the system accessibility state. To listen for changes to the 320 * accessibility state on the device, implement this interface and register 321 * it with the system by calling {@link #addAccessibilityStateChangeListener}. 322 */ 323 public interface AccessibilityStateChangeListener { 324 325 /** 326 * Called when the accessibility enabled state changes. 327 * 328 * @param enabled Whether accessibility is enabled. 329 */ onAccessibilityStateChanged(boolean enabled)330 void onAccessibilityStateChanged(boolean enabled); 331 } 332 333 /** 334 * Listener for the system touch exploration state. To listen for changes to 335 * the touch exploration state on the device, implement this interface and 336 * register it with the system by calling 337 * {@link #addTouchExplorationStateChangeListener}. 338 */ 339 public interface TouchExplorationStateChangeListener { 340 341 /** 342 * Called when the touch exploration enabled state changes. 343 * 344 * @param enabled Whether touch exploration is enabled. 345 */ onTouchExplorationStateChanged(boolean enabled)346 void onTouchExplorationStateChanged(boolean enabled); 347 } 348 349 /** 350 * Listener for changes to the state of accessibility services. 351 * 352 * <p> 353 * This refers to changes to {@link AccessibilityServiceInfo}, including: 354 * <ul> 355 * <li>Whenever a service is enabled or disabled, or its info has been set or removed.</li> 356 * <li>Whenever a metadata attribute of any running service's info changes.</li> 357 * </ul> 358 * 359 * @see #getEnabledAccessibilityServiceList for a list of infos of the enabled accessibility 360 * services. 361 * @see #addAccessibilityServicesStateChangeListener 362 * 363 */ 364 public interface AccessibilityServicesStateChangeListener { 365 366 /** 367 * Called when the state of accessibility services changes. 368 * 369 * @param manager The manager that is calling back 370 */ onAccessibilityServicesStateChanged(@onNull AccessibilityManager manager)371 void onAccessibilityServicesStateChanged(@NonNull AccessibilityManager manager); 372 } 373 374 /** 375 * Listener for the system high text contrast state. To listen for changes to 376 * the high text contrast state on the device, implement this interface and 377 * register it with the system by calling 378 * {@link #addHighTextContrastStateChangeListener}. 379 * 380 * @hide 381 */ 382 public interface HighTextContrastChangeListener { 383 384 /** 385 * Called when the high text contrast enabled state changes. 386 * 387 * @param enabled Whether high text contrast is enabled. 388 */ onHighTextContrastStateChanged(boolean enabled)389 void onHighTextContrastStateChanged(boolean enabled); 390 } 391 392 /** 393 * Listener for the audio description by default state. To listen for 394 * changes to the audio description by default state on the device, 395 * implement this interface and register it with the system by calling 396 * {@link #addAudioDescriptionRequestedChangeListener}. 397 */ 398 public interface AudioDescriptionRequestedChangeListener { 399 /** 400 * Called when the audio description enabled state changes. 401 * 402 * @param enabled Whether audio description by default is enabled. 403 */ onAudioDescriptionRequestedChanged(boolean enabled)404 void onAudioDescriptionRequestedChanged(boolean enabled); 405 } 406 407 /** 408 * Policy to inject behavior into the accessibility manager. 409 * 410 * @hide 411 */ 412 public interface AccessibilityPolicy { 413 /** 414 * Checks whether accessibility is enabled. 415 * 416 * @param accessibilityEnabled Whether the accessibility layer is enabled. 417 * @return whether accessibility is enabled. 418 */ isEnabled(boolean accessibilityEnabled)419 boolean isEnabled(boolean accessibilityEnabled); 420 421 /** 422 * Notifies the policy for an accessibility event. 423 * 424 * @param event The event. 425 * @param accessibilityEnabled Whether the accessibility layer is enabled. 426 * @param relevantEventTypes The events relevant events. 427 * @return The event to dispatch or null. 428 */ onAccessibilityEvent(@onNull AccessibilityEvent event, boolean accessibilityEnabled, @EventType int relevantEventTypes)429 @Nullable AccessibilityEvent onAccessibilityEvent(@NonNull AccessibilityEvent event, 430 boolean accessibilityEnabled, @EventType int relevantEventTypes); 431 432 /** 433 * Gets the list of relevant events. 434 * 435 * @param relevantEventTypes The relevant events. 436 * @return The relevant events to report. 437 */ getRelevantEventTypes(@ventType int relevantEventTypes)438 @EventType int getRelevantEventTypes(@EventType int relevantEventTypes); 439 440 /** 441 * Gets the list of installed services to report. 442 * 443 * @param installedService The installed services. 444 * @return The services to report. 445 */ getInstalledAccessibilityServiceList( @ullable List<AccessibilityServiceInfo> installedService)446 @NonNull List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList( 447 @Nullable List<AccessibilityServiceInfo> installedService); 448 449 /** 450 * Gets the list of enabled accessibility services. 451 * 452 * @param feedbackTypeFlags The feedback type to query for. 453 * @param enabledService The enabled services. 454 * @return The services to report. 455 */ getEnabledAccessibilityServiceList( @eedbackType int feedbackTypeFlags, @Nullable List<AccessibilityServiceInfo> enabledService)456 @Nullable List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList( 457 @FeedbackType int feedbackTypeFlags, 458 @Nullable List<AccessibilityServiceInfo> enabledService); 459 } 460 461 private final IAccessibilityManagerClient.Stub mClient = 462 new IAccessibilityManagerClient.Stub() { 463 @Override 464 public void setState(int state) { 465 // We do not want to change this immediately as the application may 466 // have already checked that accessibility is on and fired an event, 467 // that is now propagating up the view tree, Hence, if accessibility 468 // is now off an exception will be thrown. We want to have the exception 469 // enforcement to guard against apps that fire unnecessary accessibility 470 // events when accessibility is off. 471 mHandler.obtainMessage(MyCallback.MSG_SET_STATE, state, 0).sendToTarget(); 472 } 473 474 @Override 475 public void notifyServicesStateChanged(long updatedUiTimeout) { 476 updateUiTimeout(updatedUiTimeout); 477 478 final ArrayMap<AccessibilityServicesStateChangeListener, Executor> listeners; 479 synchronized (mLock) { 480 if (mServicesStateChangeListeners.isEmpty()) { 481 return; 482 } 483 listeners = new ArrayMap<>(mServicesStateChangeListeners); 484 } 485 486 int numListeners = listeners.size(); 487 for (int i = 0; i < numListeners; i++) { 488 final AccessibilityServicesStateChangeListener listener = 489 mServicesStateChangeListeners.keyAt(i); 490 mServicesStateChangeListeners.valueAt(i).execute(() -> listener 491 .onAccessibilityServicesStateChanged(AccessibilityManager.this)); 492 } 493 } 494 495 @Override 496 public void setRelevantEventTypes(int eventTypes) { 497 mRelevantEventTypes = eventTypes; 498 } 499 500 @Override 501 public void setFocusAppearance(int strokeWidth, int color) { 502 synchronized (mLock) { 503 updateFocusAppearanceLocked(strokeWidth, color); 504 } 505 } 506 }; 507 508 /** 509 * Get an AccessibilityManager instance (create one if necessary). 510 * 511 * @param context Context in which this manager operates. 512 * 513 * @hide 514 */ 515 @UnsupportedAppUsage getInstance(Context context)516 public static AccessibilityManager getInstance(Context context) { 517 synchronized (sInstanceSync) { 518 if (sInstance == null) { 519 final int userId; 520 if (Binder.getCallingUid() == Process.SYSTEM_UID 521 || context.checkCallingOrSelfPermission( 522 Manifest.permission.INTERACT_ACROSS_USERS) 523 == PackageManager.PERMISSION_GRANTED 524 || context.checkCallingOrSelfPermission( 525 Manifest.permission.INTERACT_ACROSS_USERS_FULL) 526 == PackageManager.PERMISSION_GRANTED) { 527 userId = UserHandle.USER_CURRENT; 528 } else { 529 userId = context.getUserId(); 530 } 531 sInstance = new AccessibilityManager(context, null, userId); 532 } 533 } 534 return sInstance; 535 } 536 537 /** 538 * Create an instance. 539 * 540 * @param context A {@link Context}. 541 * @param service An interface to the backing service. 542 * @param userId User id under which to run. 543 * 544 * @hide 545 */ AccessibilityManager(Context context, IAccessibilityManager service, int userId)546 public AccessibilityManager(Context context, IAccessibilityManager service, int userId) { 547 // Constructor can't be chained because we can't create an instance of an inner class 548 // before calling another constructor. 549 mCallback = new MyCallback(); 550 mHandler = new Handler(context.getMainLooper(), mCallback); 551 mUserId = userId; 552 synchronized (mLock) { 553 initialFocusAppearanceLocked(context.getResources()); 554 tryConnectToServiceLocked(service); 555 } 556 } 557 558 /** 559 * Create an instance. 560 * 561 * @param context A {@link Context}. 562 * @param handler The handler to use 563 * @param service An interface to the backing service. 564 * @param userId User id under which to run. 565 * @param serviceConnect {@code true} to connect the service or 566 * {@code false} not to connect the service. 567 * 568 * @hide 569 */ 570 @VisibleForTesting AccessibilityManager(Context context, Handler handler, IAccessibilityManager service, int userId, boolean serviceConnect)571 public AccessibilityManager(Context context, Handler handler, IAccessibilityManager service, 572 int userId, boolean serviceConnect) { 573 mCallback = new MyCallback(); 574 mHandler = handler; 575 mUserId = userId; 576 synchronized (mLock) { 577 initialFocusAppearanceLocked(context.getResources()); 578 if (serviceConnect) { 579 tryConnectToServiceLocked(service); 580 } 581 } 582 } 583 584 /** 585 * @hide 586 */ getClient()587 public IAccessibilityManagerClient getClient() { 588 return mClient; 589 } 590 591 /** 592 * Unregisters the IAccessibilityManagerClient from the backing service 593 * @hide 594 */ removeClient()595 public boolean removeClient() { 596 synchronized (mLock) { 597 IAccessibilityManager service = getServiceLocked(); 598 if (service == null) { 599 return false; 600 } 601 try { 602 return service.removeClient(mClient, mUserId); 603 } catch (RemoteException re) { 604 Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); 605 } 606 } 607 return false; 608 } 609 610 /** 611 * @hide 612 */ 613 @VisibleForTesting getCallback()614 public Handler.Callback getCallback() { 615 return mCallback; 616 } 617 618 /** 619 * Returns if the accessibility in the system is enabled. 620 * <p> 621 * <b>Note:</b> This query is used for sending {@link AccessibilityEvent}s, since events are 622 * only needed if accessibility is on. Avoid changing UI or app behavior based on the state of 623 * accessibility. While well-intentioned, doing this creates brittle, less 624 * well-maintained code that works for some users but not others. Shared code leads to more 625 * equitable experiences and less technical debt. 626 * 627 *<p> 628 * For example, if you want to expose a unique interaction with your app, use 629 * ViewCompat#addAccessibilityAction in AndroidX to make this interaction - ideally 630 * with the same code path used for non-accessibility users - available to accessibility 631 * services. Services can then expose this action in the way best fit for their users. 632 * 633 * @return True if accessibility is enabled, false otherwise. 634 */ isEnabled()635 public boolean isEnabled() { 636 synchronized (mLock) { 637 return mIsEnabled || hasAnyDirectConnection() 638 || (mAccessibilityPolicy != null && mAccessibilityPolicy.isEnabled(mIsEnabled)); 639 } 640 } 641 642 /** 643 * @see AccessibilityInteractionClient#hasAnyDirectConnection 644 * @hide 645 */ 646 @TestApi hasAnyDirectConnection()647 public boolean hasAnyDirectConnection() { 648 return AccessibilityInteractionClient.hasAnyDirectConnection(); 649 } 650 651 /** 652 * Returns if the touch exploration in the system is enabled. 653 * <p> 654 * <b>Note:</b> This query is used for dispatching hover events, such as 655 * {@link android.view.MotionEvent#ACTION_HOVER_ENTER}, to accessibility services to manage 656 * touch exploration. Avoid changing UI or app behavior based on the state of accessibility. 657 * While well-intentioned, doing this creates brittle, less well-maintained code that works for 658 * som users but not others. Shared code leads to more equitable experiences and less technical 659 * debt. 660 * 661 * @return True if touch exploration is enabled, false otherwise. 662 */ isTouchExplorationEnabled()663 public boolean isTouchExplorationEnabled() { 664 synchronized (mLock) { 665 IAccessibilityManager service = getServiceLocked(); 666 if (service == null) { 667 return false; 668 } 669 return mIsTouchExplorationEnabled; 670 } 671 } 672 673 /** 674 * Returns if the high text contrast in the system is enabled. 675 * <p> 676 * <strong>Note:</strong> You need to query this only if you application is 677 * doing its own rendering and does not rely on the platform rendering pipeline. 678 * </p> 679 * 680 * @return True if high text contrast is enabled, false otherwise. 681 * 682 * @hide 683 */ 684 @UnsupportedAppUsage isHighTextContrastEnabled()685 public boolean isHighTextContrastEnabled() { 686 synchronized (mLock) { 687 IAccessibilityManager service = getServiceLocked(); 688 if (service == null) { 689 return false; 690 } 691 return mIsHighTextContrastEnabled; 692 } 693 } 694 695 /** 696 * Sends an {@link AccessibilityEvent}. 697 * 698 * @param event The event to send. 699 * 700 * @throws IllegalStateException if accessibility is not enabled. 701 * 702 * <strong>Note:</strong> The preferred mechanism for sending custom accessibility 703 * events is through calling 704 * {@link android.view.ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)} 705 * instead of this method to allow predecessors to augment/filter events sent by 706 * their descendants. 707 */ sendAccessibilityEvent(AccessibilityEvent event)708 public void sendAccessibilityEvent(AccessibilityEvent event) { 709 final IAccessibilityManager service; 710 final int userId; 711 final AccessibilityEvent dispatchedEvent; 712 synchronized (mLock) { 713 service = getServiceLocked(); 714 if (service == null) { 715 return; 716 } 717 event.setEventTime(SystemClock.uptimeMillis()); 718 if (event.getAction() == 0) { 719 event.setAction(mPerformingAction); 720 } 721 if (mAccessibilityPolicy != null) { 722 dispatchedEvent = mAccessibilityPolicy.onAccessibilityEvent(event, 723 mIsEnabled, mRelevantEventTypes); 724 if (dispatchedEvent == null) { 725 return; 726 } 727 } else { 728 dispatchedEvent = event; 729 } 730 if (!isEnabled()) { 731 Looper myLooper = Looper.myLooper(); 732 if (myLooper == Looper.getMainLooper()) { 733 throw new IllegalStateException( 734 "Accessibility off. Did you forget to check that?"); 735 } else { 736 // If we're not running on the thread with the main looper, it's possible for 737 // the state of accessibility to change between checking isEnabled and 738 // calling this method. So just log the error rather than throwing the 739 // exception. 740 Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled"); 741 return; 742 } 743 } 744 if ((dispatchedEvent.getEventType() & mRelevantEventTypes) == 0) { 745 if (DEBUG) { 746 Log.i(LOG_TAG, "Not dispatching irrelevant event: " + dispatchedEvent 747 + " that is not among " 748 + AccessibilityEvent.eventTypeToString(mRelevantEventTypes)); 749 } 750 return; 751 } 752 userId = mUserId; 753 } 754 try { 755 // it is possible that this manager is in the same process as the service but 756 // client using it is called through Binder from another process. Example: MMS 757 // app adds a SMS notification and the NotificationManagerService calls this method 758 final long identityToken = Binder.clearCallingIdentity(); 759 try { 760 service.sendAccessibilityEvent(dispatchedEvent, userId); 761 } finally { 762 Binder.restoreCallingIdentity(identityToken); 763 } 764 if (DEBUG) { 765 Log.i(LOG_TAG, dispatchedEvent + " sent"); 766 } 767 } catch (RemoteException re) { 768 Log.e(LOG_TAG, "Error during sending " + dispatchedEvent + " ", re); 769 } finally { 770 if (event != dispatchedEvent) { 771 event.recycle(); 772 } 773 dispatchedEvent.recycle(); 774 } 775 } 776 777 /** 778 * Requests feedback interruption from all accessibility services. 779 */ interrupt()780 public void interrupt() { 781 final IAccessibilityManager service; 782 final int userId; 783 synchronized (mLock) { 784 service = getServiceLocked(); 785 if (service == null) { 786 return; 787 } 788 if (!isEnabled()) { 789 Looper myLooper = Looper.myLooper(); 790 if (myLooper == Looper.getMainLooper()) { 791 throw new IllegalStateException( 792 "Accessibility off. Did you forget to check that?"); 793 } else { 794 // If we're not running on the thread with the main looper, it's possible for 795 // the state of accessibility to change between checking isEnabled and 796 // calling this method. So just log the error rather than throwing the 797 // exception. 798 Log.e(LOG_TAG, "Interrupt called with accessibility disabled"); 799 return; 800 } 801 } 802 userId = mUserId; 803 } 804 try { 805 service.interrupt(userId); 806 if (DEBUG) { 807 Log.i(LOG_TAG, "Requested interrupt from all services"); 808 } 809 } catch (RemoteException re) { 810 Log.e(LOG_TAG, "Error while requesting interrupt from all services. ", re); 811 } 812 } 813 814 /** 815 * Returns the {@link ServiceInfo}s of the installed accessibility services. 816 * 817 * @return An unmodifiable list with {@link ServiceInfo}s. 818 * 819 * @deprecated Use {@link #getInstalledAccessibilityServiceList()} 820 */ 821 @Deprecated getAccessibilityServiceList()822 public List<ServiceInfo> getAccessibilityServiceList() { 823 List<AccessibilityServiceInfo> infos = getInstalledAccessibilityServiceList(); 824 List<ServiceInfo> services = new ArrayList<>(); 825 final int infoCount = infos.size(); 826 for (int i = 0; i < infoCount; i++) { 827 AccessibilityServiceInfo info = infos.get(i); 828 services.add(info.getResolveInfo().serviceInfo); 829 } 830 return Collections.unmodifiableList(services); 831 } 832 833 /** 834 * Returns the {@link AccessibilityServiceInfo}s of the installed accessibility services. 835 * 836 * @return An unmodifiable list with {@link AccessibilityServiceInfo}s. 837 */ getInstalledAccessibilityServiceList()838 public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() { 839 final IAccessibilityManager service; 840 final int userId; 841 synchronized (mLock) { 842 service = getServiceLocked(); 843 if (service == null) { 844 return Collections.emptyList(); 845 } 846 userId = mUserId; 847 } 848 849 List<AccessibilityServiceInfo> services = null; 850 try { 851 services = service.getInstalledAccessibilityServiceList(userId); 852 if (DEBUG) { 853 Log.i(LOG_TAG, "Installed AccessibilityServices " + services); 854 } 855 } catch (RemoteException re) { 856 Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re); 857 } 858 if (mAccessibilityPolicy != null) { 859 services = mAccessibilityPolicy.getInstalledAccessibilityServiceList(services); 860 } 861 if (services != null) { 862 return Collections.unmodifiableList(services); 863 } else { 864 return Collections.emptyList(); 865 } 866 } 867 868 /** 869 * Returns the {@link AccessibilityServiceInfo}s of the enabled accessibility services 870 * for a given feedback type. 871 * 872 * @param feedbackTypeFlags The feedback type flags. 873 * @return An unmodifiable list with {@link AccessibilityServiceInfo}s. 874 * 875 * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE 876 * @see AccessibilityServiceInfo#FEEDBACK_GENERIC 877 * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC 878 * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN 879 * @see AccessibilityServiceInfo#FEEDBACK_VISUAL 880 * @see AccessibilityServiceInfo#FEEDBACK_BRAILLE 881 */ getEnabledAccessibilityServiceList( int feedbackTypeFlags)882 public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList( 883 int feedbackTypeFlags) { 884 final IAccessibilityManager service; 885 final int userId; 886 synchronized (mLock) { 887 service = getServiceLocked(); 888 if (service == null) { 889 return Collections.emptyList(); 890 } 891 userId = mUserId; 892 } 893 894 List<AccessibilityServiceInfo> services = null; 895 try { 896 services = service.getEnabledAccessibilityServiceList(feedbackTypeFlags, userId); 897 if (DEBUG) { 898 Log.i(LOG_TAG, "Enabled AccessibilityServices " + services); 899 } 900 } catch (RemoteException re) { 901 Log.e(LOG_TAG, "Error while obtaining the enabled AccessibilityServices. ", re); 902 } 903 if (mAccessibilityPolicy != null) { 904 services = mAccessibilityPolicy.getEnabledAccessibilityServiceList( 905 feedbackTypeFlags, services); 906 } 907 if (services != null) { 908 return Collections.unmodifiableList(services); 909 } else { 910 return Collections.emptyList(); 911 } 912 } 913 914 /** 915 * Registers an {@link AccessibilityStateChangeListener} for changes in 916 * the global accessibility state of the system. Equivalent to calling 917 * {@link #addAccessibilityStateChangeListener(AccessibilityStateChangeListener, Handler)} 918 * with a null handler. 919 * 920 * @param listener The listener. 921 * @return Always returns {@code true}. 922 */ addAccessibilityStateChangeListener( @onNull AccessibilityStateChangeListener listener)923 public boolean addAccessibilityStateChangeListener( 924 @NonNull AccessibilityStateChangeListener listener) { 925 addAccessibilityStateChangeListener(listener, null); 926 return true; 927 } 928 929 /** 930 * Registers an {@link AccessibilityStateChangeListener} for changes in 931 * the global accessibility state of the system. If the listener has already been registered, 932 * the handler used to call it back is updated. 933 * 934 * @param listener The listener. 935 * @param handler The handler on which the listener should be called back, or {@code null} 936 * for a callback on the process's main handler. 937 */ addAccessibilityStateChangeListener( @onNull AccessibilityStateChangeListener listener, @Nullable Handler handler)938 public void addAccessibilityStateChangeListener( 939 @NonNull AccessibilityStateChangeListener listener, @Nullable Handler handler) { 940 synchronized (mLock) { 941 mAccessibilityStateChangeListeners 942 .put(listener, (handler == null) ? mHandler : handler); 943 } 944 } 945 946 /** 947 * Unregisters an {@link AccessibilityStateChangeListener}. 948 * 949 * @param listener The listener. 950 * @return True if the listener was previously registered. 951 */ removeAccessibilityStateChangeListener( @onNull AccessibilityStateChangeListener listener)952 public boolean removeAccessibilityStateChangeListener( 953 @NonNull AccessibilityStateChangeListener listener) { 954 synchronized (mLock) { 955 int index = mAccessibilityStateChangeListeners.indexOfKey(listener); 956 mAccessibilityStateChangeListeners.remove(listener); 957 return (index >= 0); 958 } 959 } 960 961 /** 962 * Registers a {@link TouchExplorationStateChangeListener} for changes in 963 * the global touch exploration state of the system. Equivalent to calling 964 * {@link #addTouchExplorationStateChangeListener(TouchExplorationStateChangeListener, Handler)} 965 * with a null handler. 966 * 967 * @param listener The listener. 968 * @return Always returns {@code true}. 969 */ addTouchExplorationStateChangeListener( @onNull TouchExplorationStateChangeListener listener)970 public boolean addTouchExplorationStateChangeListener( 971 @NonNull TouchExplorationStateChangeListener listener) { 972 addTouchExplorationStateChangeListener(listener, null); 973 return true; 974 } 975 976 /** 977 * Registers an {@link TouchExplorationStateChangeListener} for changes in 978 * the global touch exploration state of the system. If the listener has already been 979 * registered, the handler used to call it back is updated. 980 * 981 * @param listener The listener. 982 * @param handler The handler on which the listener should be called back, or {@code null} 983 * for a callback on the process's main handler. 984 */ addTouchExplorationStateChangeListener( @onNull TouchExplorationStateChangeListener listener, @Nullable Handler handler)985 public void addTouchExplorationStateChangeListener( 986 @NonNull TouchExplorationStateChangeListener listener, @Nullable Handler handler) { 987 synchronized (mLock) { 988 mTouchExplorationStateChangeListeners 989 .put(listener, (handler == null) ? mHandler : handler); 990 } 991 } 992 993 /** 994 * Unregisters a {@link TouchExplorationStateChangeListener}. 995 * 996 * @param listener The listener. 997 * @return True if listener was previously registered. 998 */ removeTouchExplorationStateChangeListener( @onNull TouchExplorationStateChangeListener listener)999 public boolean removeTouchExplorationStateChangeListener( 1000 @NonNull TouchExplorationStateChangeListener listener) { 1001 synchronized (mLock) { 1002 int index = mTouchExplorationStateChangeListeners.indexOfKey(listener); 1003 mTouchExplorationStateChangeListeners.remove(listener); 1004 return (index >= 0); 1005 } 1006 } 1007 1008 /** 1009 * Registers a {@link AccessibilityServicesStateChangeListener}. 1010 * 1011 * @param executor The executor. 1012 * @param listener The listener. 1013 */ addAccessibilityServicesStateChangeListener( @onNull @allbackExecutor Executor executor, @NonNull AccessibilityServicesStateChangeListener listener)1014 public void addAccessibilityServicesStateChangeListener( 1015 @NonNull @CallbackExecutor Executor executor, 1016 @NonNull AccessibilityServicesStateChangeListener listener) { 1017 synchronized (mLock) { 1018 mServicesStateChangeListeners.put(listener, executor); 1019 } 1020 } 1021 1022 /** 1023 * Registers a {@link AccessibilityServicesStateChangeListener}. This will execute a callback on 1024 * the process's main handler. 1025 * 1026 * @param listener The listener. 1027 * 1028 */ addAccessibilityServicesStateChangeListener( @onNull AccessibilityServicesStateChangeListener listener)1029 public void addAccessibilityServicesStateChangeListener( 1030 @NonNull AccessibilityServicesStateChangeListener listener) { 1031 addAccessibilityServicesStateChangeListener(new HandlerExecutor(mHandler), listener); 1032 } 1033 1034 /** 1035 * Unregisters a {@link AccessibilityServicesStateChangeListener}. 1036 * 1037 * @param listener The listener. 1038 * @return {@code true} if the listener was previously registered. 1039 */ removeAccessibilityServicesStateChangeListener( @onNull AccessibilityServicesStateChangeListener listener)1040 public boolean removeAccessibilityServicesStateChangeListener( 1041 @NonNull AccessibilityServicesStateChangeListener listener) { 1042 synchronized (mLock) { 1043 return mServicesStateChangeListeners.remove(listener) != null; 1044 } 1045 } 1046 1047 /** 1048 * Whether the current accessibility request comes from an 1049 * {@link AccessibilityService} with the {@link AccessibilityServiceInfo#isAccessibilityTool} 1050 * property set to true. 1051 * 1052 * <p> 1053 * You can use this method inside {@link AccessibilityNodeProvider} to decide how to populate 1054 * your nodes. 1055 * </p> 1056 * 1057 * <p> 1058 * <strong>Note:</strong> The return value is valid only when an {@link AccessibilityNodeInfo} 1059 * request is in progress, can change from one request to another, and has no meaning when a 1060 * request is not in progress. 1061 * </p> 1062 * 1063 * @return True if the current request is from a tool that sets isAccessibilityTool. 1064 */ isRequestFromAccessibilityTool()1065 public boolean isRequestFromAccessibilityTool() { 1066 return mRequestFromAccessibilityTool; 1067 } 1068 1069 /** 1070 * Specifies whether the current accessibility request comes from an 1071 * {@link AccessibilityService} with the {@link AccessibilityServiceInfo#isAccessibilityTool} 1072 * property set to true. 1073 * 1074 * @hide 1075 */ setRequestFromAccessibilityTool(boolean requestFromAccessibilityTool)1076 public void setRequestFromAccessibilityTool(boolean requestFromAccessibilityTool) { 1077 mRequestFromAccessibilityTool = requestFromAccessibilityTool; 1078 } 1079 1080 /** 1081 * Registers a {@link AccessibilityRequestPreparer}. 1082 */ addAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer)1083 public void addAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer) { 1084 if (mRequestPreparerLists == null) { 1085 mRequestPreparerLists = new SparseArray<>(1); 1086 } 1087 int id = preparer.getAccessibilityViewId(); 1088 List<AccessibilityRequestPreparer> requestPreparerList = mRequestPreparerLists.get(id); 1089 if (requestPreparerList == null) { 1090 requestPreparerList = new ArrayList<>(1); 1091 mRequestPreparerLists.put(id, requestPreparerList); 1092 } 1093 requestPreparerList.add(preparer); 1094 } 1095 1096 /** 1097 * Unregisters a {@link AccessibilityRequestPreparer}. 1098 */ removeAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer)1099 public void removeAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer) { 1100 if (mRequestPreparerLists == null) { 1101 return; 1102 } 1103 int viewId = preparer.getAccessibilityViewId(); 1104 List<AccessibilityRequestPreparer> requestPreparerList = mRequestPreparerLists.get(viewId); 1105 if (requestPreparerList != null) { 1106 requestPreparerList.remove(preparer); 1107 if (requestPreparerList.isEmpty()) { 1108 mRequestPreparerLists.remove(viewId); 1109 } 1110 } 1111 } 1112 1113 /** 1114 * Get the recommended timeout for changes to the UI needed by this user. Controls should remain 1115 * on the screen for at least this long to give users time to react. Some users may need 1116 * extra time to review the controls, or to reach them, or to activate assistive technology 1117 * to activate the controls automatically. 1118 * <p> 1119 * Use the combination of content flags to indicate contents of UI. For example, use 1120 * {@code FLAG_CONTENT_ICONS | FLAG_CONTENT_TEXT} for message notification which contains 1121 * icons and text, or use {@code FLAG_CONTENT_TEXT | FLAG_CONTENT_CONTROLS} for button dialog 1122 * which contains text and button controls. 1123 * <p/> 1124 * 1125 * @param originalTimeout The timeout appropriate for users with no accessibility needs. 1126 * @param uiContentFlags The combination of flags {@link #FLAG_CONTENT_ICONS}, 1127 * {@link #FLAG_CONTENT_TEXT} or {@link #FLAG_CONTENT_CONTROLS} to 1128 * indicate the contents of UI. 1129 * @return The recommended UI timeout for the current user in milliseconds. 1130 */ getRecommendedTimeoutMillis(int originalTimeout, @ContentFlag int uiContentFlags)1131 public int getRecommendedTimeoutMillis(int originalTimeout, @ContentFlag int uiContentFlags) { 1132 boolean hasControls = (uiContentFlags & FLAG_CONTENT_CONTROLS) != 0; 1133 boolean hasIconsOrText = (uiContentFlags & FLAG_CONTENT_ICONS) != 0 1134 || (uiContentFlags & FLAG_CONTENT_TEXT) != 0; 1135 int recommendedTimeout = originalTimeout; 1136 if (hasControls) { 1137 recommendedTimeout = Math.max(recommendedTimeout, mInteractiveUiTimeout); 1138 } 1139 if (hasIconsOrText) { 1140 recommendedTimeout = Math.max(recommendedTimeout, mNonInteractiveUiTimeout); 1141 } 1142 return recommendedTimeout; 1143 } 1144 1145 /** 1146 * Gets the strokeWidth of the focus rectangle. This value can be set by 1147 * {@link AccessibilityService}. 1148 * 1149 * @return The strokeWidth of the focus rectangle in pixels. 1150 * 1151 */ getAccessibilityFocusStrokeWidth()1152 public int getAccessibilityFocusStrokeWidth() { 1153 synchronized (mLock) { 1154 return mFocusStrokeWidth; 1155 } 1156 } 1157 1158 /** 1159 * Gets the color of the focus rectangle. This value can be set by 1160 * {@link AccessibilityService}. 1161 * 1162 * @return The color of the focus rectangle. 1163 * 1164 */ getAccessibilityFocusColor()1165 public @ColorInt int getAccessibilityFocusColor() { 1166 synchronized (mLock) { 1167 return mFocusColor; 1168 } 1169 } 1170 1171 /** 1172 * Gets accessibility interaction connection tracing enabled state. 1173 * 1174 * @hide 1175 */ isA11yInteractionConnectionTraceEnabled()1176 public boolean isA11yInteractionConnectionTraceEnabled() { 1177 synchronized (mLock) { 1178 return ((mAccessibilityTracingState 1179 & STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED) != 0); 1180 } 1181 } 1182 1183 /** 1184 * Gets accessibility interaction connection callback tracing enabled state. 1185 * 1186 * @hide 1187 */ isA11yInteractionConnectionCBTraceEnabled()1188 public boolean isA11yInteractionConnectionCBTraceEnabled() { 1189 synchronized (mLock) { 1190 return ((mAccessibilityTracingState 1191 & STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED) != 0); 1192 } 1193 } 1194 1195 /** 1196 * Gets accessibility interaction client tracing enabled state. 1197 * 1198 * @hide 1199 */ isA11yInteractionClientTraceEnabled()1200 public boolean isA11yInteractionClientTraceEnabled() { 1201 synchronized (mLock) { 1202 return ((mAccessibilityTracingState 1203 & STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED) != 0); 1204 } 1205 } 1206 1207 /** 1208 * Gets accessibility service tracing enabled state. 1209 * 1210 * @hide 1211 */ isA11yServiceTraceEnabled()1212 public boolean isA11yServiceTraceEnabled() { 1213 synchronized (mLock) { 1214 return ((mAccessibilityTracingState 1215 & STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED) != 0); 1216 } 1217 } 1218 1219 /** 1220 * Get the preparers that are registered for an accessibility ID 1221 * 1222 * @param id The ID of interest 1223 * @return The list of preparers, or {@code null} if there are none. 1224 * 1225 * @hide 1226 */ getRequestPreparersForAccessibilityId(int id)1227 public List<AccessibilityRequestPreparer> getRequestPreparersForAccessibilityId(int id) { 1228 if (mRequestPreparerLists == null) { 1229 return null; 1230 } 1231 return mRequestPreparerLists.get(id); 1232 } 1233 1234 /** 1235 * Set the currently performing accessibility action in views. 1236 * 1237 * @param actionId the action id of {@link AccessibilityNodeInfo.AccessibilityAction}. 1238 * @hide 1239 */ notifyPerformingAction(int actionId)1240 public void notifyPerformingAction(int actionId) { 1241 mPerformingAction = actionId; 1242 } 1243 1244 /** 1245 * Get the id of {@link AccessibilityNodeInfo.AccessibilityAction} currently being performed. 1246 * 1247 * @hide 1248 */ getPerformingAction()1249 public int getPerformingAction() { 1250 return mPerformingAction; 1251 } 1252 1253 /** 1254 * Registers a {@link HighTextContrastChangeListener} for changes in 1255 * the global high text contrast state of the system. 1256 * 1257 * @param listener The listener. 1258 * 1259 * @hide 1260 */ addHighTextContrastStateChangeListener( @onNull HighTextContrastChangeListener listener, @Nullable Handler handler)1261 public void addHighTextContrastStateChangeListener( 1262 @NonNull HighTextContrastChangeListener listener, @Nullable Handler handler) { 1263 synchronized (mLock) { 1264 mHighTextContrastStateChangeListeners 1265 .put(listener, (handler == null) ? mHandler : handler); 1266 } 1267 } 1268 1269 /** 1270 * Unregisters a {@link HighTextContrastChangeListener}. 1271 * 1272 * @param listener The listener. 1273 * 1274 * @hide 1275 */ removeHighTextContrastStateChangeListener( @onNull HighTextContrastChangeListener listener)1276 public void removeHighTextContrastStateChangeListener( 1277 @NonNull HighTextContrastChangeListener listener) { 1278 synchronized (mLock) { 1279 mHighTextContrastStateChangeListeners.remove(listener); 1280 } 1281 } 1282 1283 /** 1284 * Registers a {@link AudioDescriptionRequestedChangeListener} 1285 * for changes in the audio description by default state of the system. 1286 * The value could be read via {@link #isAudioDescriptionRequested}. 1287 * 1288 * @param executor The executor on which the listener should be called back. 1289 * @param listener The listener. 1290 */ addAudioDescriptionRequestedChangeListener( @onNull Executor executor, @NonNull AudioDescriptionRequestedChangeListener listener)1291 public void addAudioDescriptionRequestedChangeListener( 1292 @NonNull Executor executor, 1293 @NonNull AudioDescriptionRequestedChangeListener listener) { 1294 synchronized (mLock) { 1295 mAudioDescriptionRequestedChangeListeners.put(listener, executor); 1296 } 1297 } 1298 1299 /** 1300 * Unregisters a {@link AudioDescriptionRequestedChangeListener}. 1301 * 1302 * @param listener The listener. 1303 * @return True if listener was previously registered. 1304 */ removeAudioDescriptionRequestedChangeListener( @onNull AudioDescriptionRequestedChangeListener listener)1305 public boolean removeAudioDescriptionRequestedChangeListener( 1306 @NonNull AudioDescriptionRequestedChangeListener listener) { 1307 synchronized (mLock) { 1308 return (mAudioDescriptionRequestedChangeListeners.remove(listener) != null); 1309 } 1310 } 1311 1312 /** 1313 * Sets the {@link AccessibilityPolicy} controlling this manager. 1314 * 1315 * @param policy The policy. 1316 * 1317 * @hide 1318 */ setAccessibilityPolicy(@ullable AccessibilityPolicy policy)1319 public void setAccessibilityPolicy(@Nullable AccessibilityPolicy policy) { 1320 synchronized (mLock) { 1321 mAccessibilityPolicy = policy; 1322 } 1323 } 1324 1325 /** 1326 * Check if the accessibility volume stream is active. 1327 * 1328 * @return True if accessibility volume is active (i.e. some service has requested it). False 1329 * otherwise. 1330 * @hide 1331 */ isAccessibilityVolumeStreamActive()1332 public boolean isAccessibilityVolumeStreamActive() { 1333 List<AccessibilityServiceInfo> serviceInfos = 1334 getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK); 1335 for (int i = 0; i < serviceInfos.size(); i++) { 1336 if ((serviceInfos.get(i).flags & FLAG_ENABLE_ACCESSIBILITY_VOLUME) != 0) { 1337 return true; 1338 } 1339 } 1340 return false; 1341 } 1342 1343 /** 1344 * Report a fingerprint gesture to accessibility. Only available for the system process. 1345 * 1346 * @param keyCode The key code of the gesture 1347 * @return {@code true} if accessibility consumes the event. {@code false} if not. 1348 * @hide 1349 */ sendFingerprintGesture(int keyCode)1350 public boolean sendFingerprintGesture(int keyCode) { 1351 final IAccessibilityManager service; 1352 synchronized (mLock) { 1353 service = getServiceLocked(); 1354 if (service == null) { 1355 return false; 1356 } 1357 } 1358 try { 1359 return service.sendFingerprintGesture(keyCode); 1360 } catch (RemoteException e) { 1361 return false; 1362 } 1363 } 1364 1365 /** 1366 * Returns accessibility window id from window token. Accessibility window id is the one 1367 * returned from AccessibilityWindowInfo.getId(). Only available for the system process. 1368 * 1369 * @param windowToken Window token to find accessibility window id. 1370 * @return Accessibility window id for the window token. 1371 * AccessibilityWindowInfo.UNDEFINED_WINDOW_ID if accessibility window id not available for 1372 * the token. 1373 * @hide 1374 */ 1375 @SystemApi getAccessibilityWindowId(@ullable IBinder windowToken)1376 public int getAccessibilityWindowId(@Nullable IBinder windowToken) { 1377 if (windowToken == null) { 1378 return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 1379 } 1380 1381 final IAccessibilityManager service; 1382 synchronized (mLock) { 1383 service = getServiceLocked(); 1384 if (service == null) { 1385 return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 1386 } 1387 } 1388 try { 1389 return service.getAccessibilityWindowId(windowToken); 1390 } catch (RemoteException e) { 1391 return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 1392 } 1393 } 1394 1395 /** 1396 * Associate the connection between the host View and the embedded SurfaceControlViewHost. 1397 * 1398 * @hide 1399 */ associateEmbeddedHierarchy(@onNull IBinder host, @NonNull IBinder embedded)1400 public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) { 1401 final IAccessibilityManager service; 1402 synchronized (mLock) { 1403 service = getServiceLocked(); 1404 if (service == null) { 1405 return; 1406 } 1407 } 1408 try { 1409 service.associateEmbeddedHierarchy(host, embedded); 1410 } catch (RemoteException e) { 1411 return; 1412 } 1413 } 1414 1415 /** 1416 * Disassociate the connection between the host View and the embedded SurfaceControlViewHost. 1417 * The given token could be either from host side or embedded side. 1418 * 1419 * @hide 1420 */ disassociateEmbeddedHierarchy(@onNull IBinder token)1421 public void disassociateEmbeddedHierarchy(@NonNull IBinder token) { 1422 if (token == null) { 1423 return; 1424 } 1425 final IAccessibilityManager service; 1426 synchronized (mLock) { 1427 service = getServiceLocked(); 1428 if (service == null) { 1429 return; 1430 } 1431 } 1432 try { 1433 service.disassociateEmbeddedHierarchy(token); 1434 } catch (RemoteException e) { 1435 return; 1436 } 1437 } 1438 1439 /** 1440 * Sets the current state and notifies listeners, if necessary. 1441 * 1442 * @param stateFlags The state flags. 1443 */ 1444 @UnsupportedAppUsage setStateLocked(int stateFlags)1445 private void setStateLocked(int stateFlags) { 1446 final boolean enabled = (stateFlags & STATE_FLAG_ACCESSIBILITY_ENABLED) != 0; 1447 final boolean touchExplorationEnabled = 1448 (stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0; 1449 final boolean highTextContrastEnabled = 1450 (stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0; 1451 final boolean audioDescriptionEnabled = 1452 (stateFlags & STATE_FLAG_AUDIO_DESCRIPTION_BY_DEFAULT_ENABLED) != 0; 1453 1454 final boolean wasEnabled = isEnabled(); 1455 final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled; 1456 final boolean wasHighTextContrastEnabled = mIsHighTextContrastEnabled; 1457 final boolean wasAudioDescriptionByDefaultRequested = mIsAudioDescriptionByDefaultRequested; 1458 1459 // Ensure listeners get current state from isZzzEnabled() calls. 1460 mIsEnabled = enabled; 1461 mIsTouchExplorationEnabled = touchExplorationEnabled; 1462 mIsHighTextContrastEnabled = highTextContrastEnabled; 1463 mIsAudioDescriptionByDefaultRequested = audioDescriptionEnabled; 1464 1465 if (wasEnabled != isEnabled()) { 1466 notifyAccessibilityStateChanged(); 1467 } 1468 1469 if (wasTouchExplorationEnabled != touchExplorationEnabled) { 1470 notifyTouchExplorationStateChanged(); 1471 } 1472 1473 if (wasHighTextContrastEnabled != highTextContrastEnabled) { 1474 notifyHighTextContrastStateChanged(); 1475 } 1476 1477 if (wasAudioDescriptionByDefaultRequested 1478 != audioDescriptionEnabled) { 1479 notifyAudioDescriptionbyDefaultStateChanged(); 1480 } 1481 1482 updateAccessibilityTracingState(stateFlags); 1483 } 1484 1485 /** 1486 * Find an installed service with the specified {@link ComponentName}. 1487 * 1488 * @param componentName The name to match to the service. 1489 * 1490 * @return The info corresponding to the installed service, or {@code null} if no such service 1491 * is installed. 1492 * @hide 1493 */ getInstalledServiceInfoWithComponentName( ComponentName componentName)1494 public AccessibilityServiceInfo getInstalledServiceInfoWithComponentName( 1495 ComponentName componentName) { 1496 final List<AccessibilityServiceInfo> installedServiceInfos = 1497 getInstalledAccessibilityServiceList(); 1498 if ((installedServiceInfos == null) || (componentName == null)) { 1499 return null; 1500 } 1501 for (int i = 0; i < installedServiceInfos.size(); i++) { 1502 if (componentName.equals(installedServiceInfos.get(i).getComponentName())) { 1503 return installedServiceInfos.get(i); 1504 } 1505 } 1506 return null; 1507 } 1508 1509 /** 1510 * Adds an accessibility interaction connection interface for a given window. 1511 * @param windowToken The window token to which a connection is added. 1512 * @param leashToken The leash token to which a connection is added. 1513 * @param connection The connection. 1514 * 1515 * @hide 1516 */ addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken, String packageName, IAccessibilityInteractionConnection connection)1517 public int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken, 1518 String packageName, IAccessibilityInteractionConnection connection) { 1519 final IAccessibilityManager service; 1520 final int userId; 1521 synchronized (mLock) { 1522 service = getServiceLocked(); 1523 if (service == null) { 1524 return View.NO_ID; 1525 } 1526 userId = mUserId; 1527 } 1528 try { 1529 return service.addAccessibilityInteractionConnection(windowToken, leashToken, 1530 connection, packageName, userId); 1531 } catch (RemoteException re) { 1532 Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re); 1533 } 1534 return View.NO_ID; 1535 } 1536 1537 /** 1538 * Removed an accessibility interaction connection interface for a given window. 1539 * @param windowToken The window token to which a connection is removed. 1540 * 1541 * @hide 1542 */ removeAccessibilityInteractionConnection(IWindow windowToken)1543 public void removeAccessibilityInteractionConnection(IWindow windowToken) { 1544 final IAccessibilityManager service; 1545 synchronized (mLock) { 1546 service = getServiceLocked(); 1547 if (service == null) { 1548 return; 1549 } 1550 } 1551 try { 1552 service.removeAccessibilityInteractionConnection(windowToken); 1553 } catch (RemoteException re) { 1554 Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re); 1555 } 1556 } 1557 1558 /** 1559 * Perform the accessibility shortcut if the caller has permission. 1560 * 1561 * @hide 1562 */ 1563 @SystemApi 1564 @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) performAccessibilityShortcut()1565 public void performAccessibilityShortcut() { 1566 performAccessibilityShortcut(null); 1567 } 1568 1569 /** 1570 * Perform the accessibility shortcut for the given target which is assigned to the shortcut. 1571 * 1572 * @param targetName The flattened {@link ComponentName} string or the class name of a system 1573 * class implementing a supported accessibility feature, or {@code null} if there's no 1574 * specified target. 1575 * @hide 1576 */ 1577 @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) performAccessibilityShortcut(@ullable String targetName)1578 public void performAccessibilityShortcut(@Nullable String targetName) { 1579 final IAccessibilityManager service; 1580 synchronized (mLock) { 1581 service = getServiceLocked(); 1582 if (service == null) { 1583 return; 1584 } 1585 } 1586 try { 1587 service.performAccessibilityShortcut(targetName); 1588 } catch (RemoteException re) { 1589 Log.e(LOG_TAG, "Error performing accessibility shortcut. ", re); 1590 } 1591 } 1592 1593 /** 1594 * Register the provided {@link RemoteAction} with the given actionId 1595 * <p> 1596 * To perform established system actions, an accessibility service uses the GLOBAL_ACTION 1597 * constants in {@link android.accessibilityservice.AccessibilityService}. To provide a 1598 * customized implementation for one of these actions, the id of the registered system action 1599 * must match that of the corresponding GLOBAL_ACTION constant. For example, to register a 1600 * Back action, {@code actionId} must be 1601 * {@link android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_BACK} 1602 * </p> 1603 * @param action The remote action to be registered with the given actionId as system action. 1604 * @param actionId The id uniquely identify the system action. 1605 * @hide 1606 */ 1607 @SystemApi 1608 @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) registerSystemAction(@onNull RemoteAction action, int actionId)1609 public void registerSystemAction(@NonNull RemoteAction action, int actionId) { 1610 final IAccessibilityManager service; 1611 synchronized (mLock) { 1612 service = getServiceLocked(); 1613 if (service == null) { 1614 return; 1615 } 1616 } 1617 try { 1618 service.registerSystemAction(action, actionId); 1619 1620 if (DEBUG) { 1621 Log.i(LOG_TAG, "System action " + action.getTitle() + " is registered."); 1622 } 1623 } catch (RemoteException re) { 1624 Log.e(LOG_TAG, "Error registering system action " + action.getTitle() + " ", re); 1625 } 1626 } 1627 1628 /** 1629 * Unregister a system action with the given actionId 1630 * 1631 * @param actionId The id uniquely identify the system action to be unregistered. 1632 * @hide 1633 */ 1634 @SystemApi 1635 @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) unregisterSystemAction(int actionId)1636 public void unregisterSystemAction(int actionId) { 1637 final IAccessibilityManager service; 1638 synchronized (mLock) { 1639 service = getServiceLocked(); 1640 if (service == null) { 1641 return; 1642 } 1643 } 1644 try { 1645 service.unregisterSystemAction(actionId); 1646 1647 if (DEBUG) { 1648 Log.i(LOG_TAG, "System action with actionId " + actionId + " is unregistered."); 1649 } 1650 } catch (RemoteException re) { 1651 Log.e(LOG_TAG, "Error unregistering system action with actionId " + actionId + " ", re); 1652 } 1653 } 1654 1655 /** 1656 * Notifies that the accessibility button in the system's navigation area has been clicked 1657 * 1658 * @param displayId The logical display id. 1659 * @hide 1660 */ 1661 @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) notifyAccessibilityButtonClicked(int displayId)1662 public void notifyAccessibilityButtonClicked(int displayId) { 1663 notifyAccessibilityButtonClicked(displayId, null); 1664 } 1665 1666 /** 1667 * Perform the accessibility button for the given target which is assigned to the button. 1668 * 1669 * @param displayId displayId The logical display id. 1670 * @param targetName The flattened {@link ComponentName} string or the class name of a system 1671 * class implementing a supported accessibility feature, or {@code null} if there's no 1672 * specified target. 1673 * @hide 1674 */ 1675 @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) notifyAccessibilityButtonClicked(int displayId, @Nullable String targetName)1676 public void notifyAccessibilityButtonClicked(int displayId, @Nullable String targetName) { 1677 final IAccessibilityManager service; 1678 synchronized (mLock) { 1679 service = getServiceLocked(); 1680 if (service == null) { 1681 return; 1682 } 1683 } 1684 try { 1685 service.notifyAccessibilityButtonClicked(displayId, targetName); 1686 } catch (RemoteException re) { 1687 Log.e(LOG_TAG, "Error while dispatching accessibility button click", re); 1688 } 1689 } 1690 1691 /** 1692 * Notifies that the visibility of the accessibility button in the system's navigation area 1693 * has changed. 1694 * 1695 * @param shown {@code true} if the accessibility button is visible within the system 1696 * navigation area, {@code false} otherwise 1697 * @hide 1698 */ notifyAccessibilityButtonVisibilityChanged(boolean shown)1699 public void notifyAccessibilityButtonVisibilityChanged(boolean shown) { 1700 final IAccessibilityManager service; 1701 synchronized (mLock) { 1702 service = getServiceLocked(); 1703 if (service == null) { 1704 return; 1705 } 1706 } 1707 try { 1708 service.notifyAccessibilityButtonVisibilityChanged(shown); 1709 } catch (RemoteException re) { 1710 Log.e(LOG_TAG, "Error while dispatching accessibility button visibility change", re); 1711 } 1712 } 1713 1714 /** 1715 * Set an IAccessibilityInteractionConnection to replace the actions of a picture-in-picture 1716 * window. Intended for use by the System UI only. 1717 * 1718 * @param connection The connection to handle the actions. Set to {@code null} to avoid 1719 * affecting the actions. 1720 * 1721 * @hide 1722 */ setPictureInPictureActionReplacingConnection( @ullable IAccessibilityInteractionConnection connection)1723 public void setPictureInPictureActionReplacingConnection( 1724 @Nullable IAccessibilityInteractionConnection connection) { 1725 final IAccessibilityManager service; 1726 synchronized (mLock) { 1727 service = getServiceLocked(); 1728 if (service == null) { 1729 return; 1730 } 1731 } 1732 try { 1733 service.setPictureInPictureActionReplacingConnection(connection); 1734 } catch (RemoteException re) { 1735 Log.e(LOG_TAG, "Error setting picture in picture action replacement", re); 1736 } 1737 } 1738 1739 /** 1740 * Returns the list of shortcut target names currently assigned to the given shortcut. 1741 * 1742 * @param shortcutType The shortcut type. 1743 * @return The list of shortcut target names. 1744 * @hide 1745 */ 1746 @TestApi 1747 @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) 1748 @NonNull getAccessibilityShortcutTargets(@hortcutType int shortcutType)1749 public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) { 1750 final IAccessibilityManager service; 1751 synchronized (mLock) { 1752 service = getServiceLocked(); 1753 } 1754 if (service != null) { 1755 try { 1756 return service.getAccessibilityShortcutTargets(shortcutType); 1757 } catch (RemoteException re) { 1758 re.rethrowFromSystemServer(); 1759 } 1760 } 1761 return Collections.emptyList(); 1762 } 1763 1764 /** 1765 * Returns the {@link AccessibilityShortcutInfo}s of the installed accessibility shortcut 1766 * targets, for specific user. 1767 * 1768 * @param context The context of the application. 1769 * @param userId The user id. 1770 * @return A list with {@link AccessibilityShortcutInfo}s. 1771 * @hide 1772 */ 1773 @NonNull getInstalledAccessibilityShortcutListAsUser( @onNull Context context, @UserIdInt int userId)1774 public List<AccessibilityShortcutInfo> getInstalledAccessibilityShortcutListAsUser( 1775 @NonNull Context context, @UserIdInt int userId) { 1776 final List<AccessibilityShortcutInfo> shortcutInfos = new ArrayList<>(); 1777 final int flags = PackageManager.GET_ACTIVITIES 1778 | PackageManager.GET_META_DATA 1779 | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS 1780 | PackageManager.MATCH_DIRECT_BOOT_AWARE 1781 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; 1782 final Intent actionMain = new Intent(Intent.ACTION_MAIN); 1783 actionMain.addCategory(Intent.CATEGORY_ACCESSIBILITY_SHORTCUT_TARGET); 1784 1785 final PackageManager packageManager = context.getPackageManager(); 1786 final List<ResolveInfo> installedShortcutList = 1787 packageManager.queryIntentActivitiesAsUser(actionMain, flags, userId); 1788 for (int i = 0; i < installedShortcutList.size(); i++) { 1789 final AccessibilityShortcutInfo shortcutInfo = 1790 getShortcutInfo(context, installedShortcutList.get(i)); 1791 if (shortcutInfo != null) { 1792 shortcutInfos.add(shortcutInfo); 1793 } 1794 } 1795 return shortcutInfos; 1796 } 1797 1798 /** 1799 * Returns an {@link AccessibilityShortcutInfo} according to the given {@link ResolveInfo} of 1800 * an activity. 1801 * 1802 * @param context The context of the application. 1803 * @param resolveInfo The resolve info of an activity. 1804 * @return The AccessibilityShortcutInfo. 1805 */ 1806 @Nullable getShortcutInfo(@onNull Context context, @NonNull ResolveInfo resolveInfo)1807 private AccessibilityShortcutInfo getShortcutInfo(@NonNull Context context, 1808 @NonNull ResolveInfo resolveInfo) { 1809 final ActivityInfo activityInfo = resolveInfo.activityInfo; 1810 if (activityInfo == null || activityInfo.metaData == null 1811 || activityInfo.metaData.getInt(AccessibilityShortcutInfo.META_DATA) == 0) { 1812 return null; 1813 } 1814 try { 1815 return new AccessibilityShortcutInfo(context, activityInfo); 1816 } catch (XmlPullParserException | IOException exp) { 1817 Log.e(LOG_TAG, "Error while initializing AccessibilityShortcutInfo", exp); 1818 } 1819 return null; 1820 } 1821 1822 /** 1823 * 1824 * Sets an {@link IWindowMagnificationConnection} that manipulates window magnification. 1825 * 1826 * @param connection The connection that manipulates window magnification. 1827 * @hide 1828 */ setWindowMagnificationConnection(@ullable IWindowMagnificationConnection connection)1829 public void setWindowMagnificationConnection(@Nullable 1830 IWindowMagnificationConnection connection) { 1831 final IAccessibilityManager service; 1832 synchronized (mLock) { 1833 service = getServiceLocked(); 1834 if (service == null) { 1835 return; 1836 } 1837 } 1838 try { 1839 service.setWindowMagnificationConnection(connection); 1840 } catch (RemoteException re) { 1841 Log.e(LOG_TAG, "Error setting window magnfication connection", re); 1842 } 1843 } 1844 1845 /** 1846 * Determines if users want to select sound track with audio description by default. 1847 * <p> 1848 * Audio description, also referred to as a video description, described video, or 1849 * more precisely called a visual description, is a form of narration used to provide 1850 * information surrounding key visual elements in a media work for the benefit of 1851 * blind and visually impaired consumers. 1852 * </p> 1853 * <p> 1854 * The method provides the preference value to content provider apps to select the 1855 * default sound track during playing a video or movie. 1856 * </p> 1857 * <p> 1858 * Add listener to detect the state change via 1859 * {@link #addAudioDescriptionRequestedChangeListener} 1860 * </p> 1861 * @return {@code true} if the audio description is enabled, {@code false} otherwise. 1862 */ isAudioDescriptionRequested()1863 public boolean isAudioDescriptionRequested() { 1864 synchronized (mLock) { 1865 IAccessibilityManager service = getServiceLocked(); 1866 if (service == null) { 1867 return false; 1868 } 1869 return mIsAudioDescriptionByDefaultRequested; 1870 } 1871 } 1872 1873 /** 1874 * Sets the system audio caption enabled state. 1875 * 1876 * @param isEnabled The system audio captioning enabled state. 1877 * @param userId The user Id. 1878 * @hide 1879 */ setSystemAudioCaptioningEnabled(boolean isEnabled, int userId)1880 public void setSystemAudioCaptioningEnabled(boolean isEnabled, int userId) { 1881 final IAccessibilityManager service; 1882 synchronized (mLock) { 1883 service = getServiceLocked(); 1884 if (service == null) { 1885 return; 1886 } 1887 } 1888 try { 1889 service.setSystemAudioCaptioningEnabled(isEnabled, userId); 1890 } catch (RemoteException re) { 1891 throw re.rethrowFromSystemServer(); 1892 } 1893 } 1894 1895 /** 1896 * Gets the system audio caption UI enabled state. 1897 * 1898 * @param userId The user Id. 1899 * @return the system audio caption UI enabled state. 1900 * @hide 1901 */ isSystemAudioCaptioningUiEnabled(int userId)1902 public boolean isSystemAudioCaptioningUiEnabled(int userId) { 1903 final IAccessibilityManager service; 1904 synchronized (mLock) { 1905 service = getServiceLocked(); 1906 if (service == null) { 1907 return false; 1908 } 1909 } 1910 try { 1911 return service.isSystemAudioCaptioningUiEnabled(userId); 1912 } catch (RemoteException re) { 1913 throw re.rethrowFromSystemServer(); 1914 } 1915 } 1916 1917 /** 1918 * Sets the system audio caption UI enabled state. 1919 * 1920 * @param isEnabled The system audio captioning UI enabled state. 1921 * @param userId The user Id. 1922 * @hide 1923 */ setSystemAudioCaptioningUiEnabled(boolean isEnabled, int userId)1924 public void setSystemAudioCaptioningUiEnabled(boolean isEnabled, int userId) { 1925 final IAccessibilityManager service; 1926 synchronized (mLock) { 1927 service = getServiceLocked(); 1928 if (service == null) { 1929 return; 1930 } 1931 } 1932 try { 1933 service.setSystemAudioCaptioningUiEnabled(isEnabled, userId); 1934 } catch (RemoteException re) { 1935 throw re.rethrowFromSystemServer(); 1936 } 1937 } 1938 1939 1940 /** 1941 * Sets the {@link AccessibilityWindowAttributes} to the window associated with the given 1942 * window id. 1943 * 1944 * @param displayId The display id of the window. 1945 * @param windowId The id of the window. 1946 * @param attributes The accessibility window attributes. 1947 * @hide 1948 */ setAccessibilityWindowAttributes(int displayId, int windowId, AccessibilityWindowAttributes attributes)1949 public void setAccessibilityWindowAttributes(int displayId, int windowId, 1950 AccessibilityWindowAttributes attributes) { 1951 final IAccessibilityManager service; 1952 synchronized (mLock) { 1953 service = getServiceLocked(); 1954 if (service == null) { 1955 return; 1956 } 1957 } 1958 try { 1959 service.setAccessibilityWindowAttributes(displayId, windowId, mUserId, attributes); 1960 } catch (RemoteException re) { 1961 re.rethrowFromSystemServer(); 1962 } 1963 } 1964 1965 /** 1966 * Registers an {@link AccessibilityDisplayProxy}, so this proxy can access UI content specific 1967 * to its display. 1968 * 1969 * @param proxy the {@link AccessibilityDisplayProxy} to register. 1970 * @return {@code true} if the proxy is successfully registered. 1971 * 1972 * @throws IllegalArgumentException if the proxy's display is not currently tracked by a11y, is 1973 * {@link android.view.Display#DEFAULT_DISPLAY}, is or lower than 1974 * {@link android.view.Display#INVALID_DISPLAY}, or is already being proxy-ed. 1975 * 1976 * @throws SecurityException if the app does not hold the 1977 * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the 1978 * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission. 1979 * 1980 * @hide 1981 */ 1982 @SystemApi 1983 @RequiresPermission(allOf = {Manifest.permission.MANAGE_ACCESSIBILITY, 1984 Manifest.permission.CREATE_VIRTUAL_DEVICE}) registerDisplayProxy(@onNull AccessibilityDisplayProxy proxy)1985 public boolean registerDisplayProxy(@NonNull AccessibilityDisplayProxy proxy) { 1986 final IAccessibilityManager service; 1987 synchronized (mLock) { 1988 service = getServiceLocked(); 1989 if (service == null) { 1990 return false; 1991 } 1992 } 1993 1994 try { 1995 return service.registerProxyForDisplay(proxy.mServiceClient, proxy.getDisplayId()); 1996 } catch (RemoteException re) { 1997 throw re.rethrowFromSystemServer(); 1998 } 1999 } 2000 2001 /** 2002 * Unregisters an {@link AccessibilityDisplayProxy}. 2003 * 2004 * @return {@code true} if the proxy is successfully unregistered. 2005 * 2006 * @throws SecurityException if the app does not hold the 2007 * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the 2008 * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission. 2009 * 2010 * @hide 2011 */ 2012 @SystemApi 2013 @RequiresPermission(allOf = {Manifest.permission.MANAGE_ACCESSIBILITY, 2014 Manifest.permission.CREATE_VIRTUAL_DEVICE}) unregisterDisplayProxy(@onNull AccessibilityDisplayProxy proxy)2015 public boolean unregisterDisplayProxy(@NonNull AccessibilityDisplayProxy proxy) { 2016 final IAccessibilityManager service; 2017 synchronized (mLock) { 2018 service = getServiceLocked(); 2019 if (service == null) { 2020 return false; 2021 } 2022 } 2023 try { 2024 return service.unregisterProxyForDisplay(proxy.getDisplayId()); 2025 } catch (RemoteException re) { 2026 throw re.rethrowFromSystemServer(); 2027 } 2028 } 2029 2030 /** 2031 * Start sequence (infinite) type of flash notification. Use 2032 * {@code Context.getOpPackageName()} as the identifier of this flash notification. 2033 * The notification can be cancelled later by calling {@link #stopFlashNotificationSequence} 2034 * with same {@code Context.getOpPackageName()}. 2035 * If the binder associated with this {@link AccessibilityManager} instance dies then the 2036 * sequence will stop automatically. It is strongly recommended to call 2037 * {@link #stopFlashNotificationSequence} within a reasonable amount of time after calling 2038 * this method. 2039 * 2040 * @param context The context in which this manager operates. 2041 * @param reason The triggering reason of flash notification. 2042 * @return {@code true} if flash notification works properly. 2043 * @hide 2044 */ startFlashNotificationSequence(@onNull Context context, @FlashNotificationReason int reason)2045 public boolean startFlashNotificationSequence(@NonNull Context context, 2046 @FlashNotificationReason int reason) { 2047 final IAccessibilityManager service; 2048 synchronized (mLock) { 2049 service = getServiceLocked(); 2050 if (service == null) { 2051 return false; 2052 } 2053 } 2054 2055 try { 2056 return service.startFlashNotificationSequence(context.getOpPackageName(), 2057 reason, mBinder); 2058 } catch (RemoteException re) { 2059 Log.e(LOG_TAG, "Error while start flash notification sequence", re); 2060 return false; 2061 } 2062 } 2063 2064 /** 2065 * Stop sequence (infinite) type of flash notification. The flash notification with 2066 * {@code Context.getOpPackageName()} as identifier will be stopped if exist. 2067 * It is strongly recommended to call this method within a reasonable amount of time after 2068 * calling {@link #startFlashNotificationSequence} method. 2069 * 2070 * @param context The context in which this manager operates. 2071 * @return {@code true} if flash notification stops properly. 2072 * @hide 2073 */ stopFlashNotificationSequence(@onNull Context context)2074 public boolean stopFlashNotificationSequence(@NonNull Context context) { 2075 final IAccessibilityManager service; 2076 synchronized (mLock) { 2077 service = getServiceLocked(); 2078 if (service == null) { 2079 return false; 2080 } 2081 } 2082 2083 try { 2084 return service.stopFlashNotificationSequence(context.getOpPackageName()); 2085 } catch (RemoteException re) { 2086 Log.e(LOG_TAG, "Error while stop flash notification sequence", re); 2087 return false; 2088 } 2089 } 2090 2091 /** 2092 * Start event (finite) type of flash notification. 2093 * 2094 * @param context The context in which this manager operates. 2095 * @param reason The triggering reason of flash notification. 2096 * @param reasonPkg The package that trigger the flash notification. 2097 * @return {@code true} if flash notification works properly. 2098 * @hide 2099 */ startFlashNotificationEvent(@onNull Context context, @FlashNotificationReason int reason, @Nullable String reasonPkg)2100 public boolean startFlashNotificationEvent(@NonNull Context context, 2101 @FlashNotificationReason int reason, @Nullable String reasonPkg) { 2102 final IAccessibilityManager service; 2103 synchronized (mLock) { 2104 service = getServiceLocked(); 2105 if (service == null) { 2106 return false; 2107 } 2108 } 2109 2110 try { 2111 return service.startFlashNotificationEvent(context.getOpPackageName(), 2112 reason, reasonPkg); 2113 } catch (RemoteException re) { 2114 Log.e(LOG_TAG, "Error while start flash notification event", re); 2115 return false; 2116 } 2117 } 2118 2119 /** 2120 * Determines if the accessibility target is allowed. 2121 * 2122 * @param packageName The name of the application attempting to perform the operation. 2123 * @param uid The user id of the application attempting to perform the operation. 2124 * @param userId The id of the user for whom to perform the operation. 2125 * @return {@code true} the accessibility target is allowed. 2126 * @hide 2127 */ isAccessibilityTargetAllowed(String packageName, int uid, int userId)2128 public boolean isAccessibilityTargetAllowed(String packageName, int uid, int userId) { 2129 final IAccessibilityManager service; 2130 synchronized (mLock) { 2131 service = getServiceLocked(); 2132 if (service == null) { 2133 return false; 2134 } 2135 } 2136 2137 try { 2138 return service.isAccessibilityTargetAllowed(packageName, uid, userId); 2139 } catch (RemoteException re) { 2140 Log.e(LOG_TAG, "Error while check accessibility target status", re); 2141 return false; 2142 } 2143 } 2144 2145 /** 2146 * Sends restricted dialog intent if the accessibility target is disallowed. 2147 * 2148 * @param packageName The name of the application attempting to perform the operation. 2149 * @param uid The user id of the application attempting to perform the operation. 2150 * @param userId The id of the user for whom to perform the operation. 2151 * @return {@code true} if the restricted dialog is shown. 2152 * @hide 2153 */ sendRestrictedDialogIntent(String packageName, int uid, int userId)2154 public boolean sendRestrictedDialogIntent(String packageName, int uid, int userId) { 2155 final IAccessibilityManager service; 2156 synchronized (mLock) { 2157 service = getServiceLocked(); 2158 if (service == null) { 2159 return false; 2160 } 2161 } 2162 2163 try { 2164 return service.sendRestrictedDialogIntent(packageName, uid, userId); 2165 } catch (RemoteException re) { 2166 Log.e(LOG_TAG, "Error while show restricted dialog", re); 2167 return false; 2168 } 2169 } 2170 getServiceLocked()2171 private IAccessibilityManager getServiceLocked() { 2172 if (mService == null) { 2173 tryConnectToServiceLocked(null); 2174 } 2175 return mService; 2176 } 2177 tryConnectToServiceLocked(IAccessibilityManager service)2178 private void tryConnectToServiceLocked(IAccessibilityManager service) { 2179 if (service == null) { 2180 IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE); 2181 if (iBinder == null) { 2182 return; 2183 } 2184 service = IAccessibilityManager.Stub.asInterface(iBinder); 2185 } 2186 2187 try { 2188 final long userStateAndRelevantEvents = service.addClient(mClient, mUserId); 2189 setStateLocked(IntPair.first(userStateAndRelevantEvents)); 2190 mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents); 2191 updateUiTimeout(service.getRecommendedTimeoutMillis()); 2192 updateFocusAppearanceLocked(service.getFocusStrokeWidth(), service.getFocusColor()); 2193 mService = service; 2194 } catch (RemoteException re) { 2195 Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); 2196 } 2197 } 2198 2199 /** 2200 * Notifies the registered {@link AccessibilityStateChangeListener}s. 2201 * 2202 * Note: this method notifies only the listeners of this single instance. 2203 * AccessibilityManagerService is responsible for calling this method on all of 2204 * its AccessibilityManager clients in order to notify all listeners. 2205 * @hide 2206 */ notifyAccessibilityStateChanged()2207 public void notifyAccessibilityStateChanged() { 2208 final boolean isEnabled; 2209 final ArrayMap<AccessibilityStateChangeListener, Handler> listeners; 2210 synchronized (mLock) { 2211 if (mAccessibilityStateChangeListeners.isEmpty()) { 2212 return; 2213 } 2214 isEnabled = isEnabled(); 2215 listeners = new ArrayMap<>(mAccessibilityStateChangeListeners); 2216 } 2217 2218 final int numListeners = listeners.size(); 2219 for (int i = 0; i < numListeners; i++) { 2220 final AccessibilityStateChangeListener listener = listeners.keyAt(i); 2221 listeners.valueAt(i).post(() -> 2222 listener.onAccessibilityStateChanged(isEnabled)); 2223 } 2224 } 2225 2226 /** 2227 * Notifies the registered {@link TouchExplorationStateChangeListener}s. 2228 */ notifyTouchExplorationStateChanged()2229 private void notifyTouchExplorationStateChanged() { 2230 final boolean isTouchExplorationEnabled; 2231 final ArrayMap<TouchExplorationStateChangeListener, Handler> listeners; 2232 synchronized (mLock) { 2233 if (mTouchExplorationStateChangeListeners.isEmpty()) { 2234 return; 2235 } 2236 isTouchExplorationEnabled = mIsTouchExplorationEnabled; 2237 listeners = new ArrayMap<>(mTouchExplorationStateChangeListeners); 2238 } 2239 2240 final int numListeners = listeners.size(); 2241 for (int i = 0; i < numListeners; i++) { 2242 final TouchExplorationStateChangeListener listener = listeners.keyAt(i); 2243 listeners.valueAt(i).post(() -> 2244 listener.onTouchExplorationStateChanged(isTouchExplorationEnabled)); 2245 } 2246 } 2247 2248 /** 2249 * Notifies the registered {@link HighTextContrastChangeListener}s. 2250 */ notifyHighTextContrastStateChanged()2251 private void notifyHighTextContrastStateChanged() { 2252 final boolean isHighTextContrastEnabled; 2253 final ArrayMap<HighTextContrastChangeListener, Handler> listeners; 2254 synchronized (mLock) { 2255 if (mHighTextContrastStateChangeListeners.isEmpty()) { 2256 return; 2257 } 2258 isHighTextContrastEnabled = mIsHighTextContrastEnabled; 2259 listeners = new ArrayMap<>(mHighTextContrastStateChangeListeners); 2260 } 2261 2262 final int numListeners = listeners.size(); 2263 for (int i = 0; i < numListeners; i++) { 2264 final HighTextContrastChangeListener listener = listeners.keyAt(i); 2265 listeners.valueAt(i).post(() -> 2266 listener.onHighTextContrastStateChanged(isHighTextContrastEnabled)); 2267 } 2268 } 2269 2270 /** 2271 * Notifies the registered {@link AudioDescriptionStateChangeListener}s. 2272 */ notifyAudioDescriptionbyDefaultStateChanged()2273 private void notifyAudioDescriptionbyDefaultStateChanged() { 2274 final boolean isAudioDescriptionByDefaultRequested; 2275 final ArrayMap<AudioDescriptionRequestedChangeListener, Executor> listeners; 2276 synchronized (mLock) { 2277 if (mAudioDescriptionRequestedChangeListeners.isEmpty()) { 2278 return; 2279 } 2280 isAudioDescriptionByDefaultRequested = mIsAudioDescriptionByDefaultRequested; 2281 listeners = new ArrayMap<>(mAudioDescriptionRequestedChangeListeners); 2282 } 2283 2284 final int numListeners = listeners.size(); 2285 for (int i = 0; i < numListeners; i++) { 2286 final AudioDescriptionRequestedChangeListener listener = listeners.keyAt(i); 2287 listeners.valueAt(i).execute(() -> 2288 listener.onAudioDescriptionRequestedChanged( 2289 isAudioDescriptionByDefaultRequested)); 2290 } 2291 } 2292 2293 /** 2294 * Update mAccessibilityTracingState. 2295 */ updateAccessibilityTracingState(int stateFlag)2296 private void updateAccessibilityTracingState(int stateFlag) { 2297 synchronized (mLock) { 2298 mAccessibilityTracingState = stateFlag; 2299 } 2300 } 2301 2302 /** 2303 * Update interactive and non-interactive UI timeout. 2304 * 2305 * @param uiTimeout A pair of {@code int}s. First integer for interactive one, and second 2306 * integer for non-interactive one. 2307 */ updateUiTimeout(long uiTimeout)2308 private void updateUiTimeout(long uiTimeout) { 2309 mInteractiveUiTimeout = IntPair.first(uiTimeout); 2310 mNonInteractiveUiTimeout = IntPair.second(uiTimeout); 2311 } 2312 2313 /** 2314 * Updates the stroke width and color of the focus rectangle. 2315 * 2316 * @param strokeWidth The strokeWidth of the focus rectangle. 2317 * @param color The color of the focus rectangle. 2318 */ updateFocusAppearanceLocked(int strokeWidth, int color)2319 private void updateFocusAppearanceLocked(int strokeWidth, int color) { 2320 if (mFocusStrokeWidth == strokeWidth && mFocusColor == color) { 2321 return; 2322 } 2323 mFocusStrokeWidth = strokeWidth; 2324 mFocusColor = color; 2325 } 2326 2327 /** 2328 * Sets the stroke width and color of the focus rectangle to default value. 2329 * 2330 * @param resource The resources. 2331 */ initialFocusAppearanceLocked(Resources resource)2332 private void initialFocusAppearanceLocked(Resources resource) { 2333 try { 2334 mFocusStrokeWidth = resource.getDimensionPixelSize( 2335 R.dimen.accessibility_focus_highlight_stroke_width); 2336 mFocusColor = resource.getColor(R.color.accessibility_focus_highlight_color); 2337 } catch (Resources.NotFoundException re) { 2338 // Sets the stroke width and color to default value by hardcoded for making 2339 // the Talkback can work normally. 2340 mFocusStrokeWidth = (int) (4 * resource.getDisplayMetrics().density); 2341 mFocusColor = 0xbf39b500; 2342 Log.e(LOG_TAG, "Error while initialing the focus appearance data then setting to" 2343 + " default value by hardcoded", re); 2344 } 2345 } 2346 2347 /** 2348 * Determines if the accessibility button within the system navigation area is supported. 2349 * 2350 * @return {@code true} if the accessibility button is supported on this device, 2351 * {@code false} otherwise 2352 */ isAccessibilityButtonSupported()2353 public static boolean isAccessibilityButtonSupported() { 2354 final Resources res = Resources.getSystem(); 2355 return res.getBoolean(com.android.internal.R.bool.config_showNavigationBar); 2356 } 2357 2358 private final class MyCallback implements Handler.Callback { 2359 public static final int MSG_SET_STATE = 1; 2360 2361 @Override handleMessage(Message message)2362 public boolean handleMessage(Message message) { 2363 switch (message.what) { 2364 case MSG_SET_STATE: { 2365 // See comment at mClient 2366 final int state = message.arg1; 2367 synchronized (mLock) { 2368 setStateLocked(state); 2369 } 2370 } break; 2371 } 2372 return true; 2373 } 2374 } 2375 2376 /** 2377 * Retrieves the window's transformation matrix and magnification spec. 2378 * 2379 * <p> 2380 * Used by callers outside of the AccessibilityManagerService process which need 2381 * this information, like {@link android.view.accessibility.DirectAccessibilityConnection}. 2382 * </p> 2383 * 2384 * @return The transformation spec 2385 * @hide 2386 */ getWindowTransformationSpec( int windowId)2387 public IAccessibilityManager.WindowTransformationSpec getWindowTransformationSpec( 2388 int windowId) { 2389 final IAccessibilityManager service; 2390 synchronized (mLock) { 2391 service = getServiceLocked(); 2392 if (service == null) { 2393 return null; 2394 } 2395 } 2396 try { 2397 return service.getWindowTransformationSpec(windowId); 2398 } catch (RemoteException re) { 2399 throw re.rethrowFromSystemServer(); 2400 } 2401 } 2402 } 2403