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