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