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