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