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.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.SdkConstant; 26 import android.annotation.SystemApi; 27 import android.annotation.SystemService; 28 import android.content.ComponentName; 29 import android.content.Context; 30 import android.content.pm.PackageManager; 31 import android.content.pm.ServiceInfo; 32 import android.content.res.Resources; 33 import android.os.Binder; 34 import android.os.Handler; 35 import android.os.IBinder; 36 import android.os.Looper; 37 import android.os.Message; 38 import android.os.Process; 39 import android.os.RemoteException; 40 import android.os.ServiceManager; 41 import android.os.SystemClock; 42 import android.os.UserHandle; 43 import android.util.ArrayMap; 44 import android.util.Log; 45 import android.view.IWindow; 46 import android.view.View; 47 48 import com.android.internal.annotations.VisibleForTesting; 49 import com.android.internal.util.IntPair; 50 51 import java.util.ArrayList; 52 import java.util.Collections; 53 import java.util.List; 54 55 /** 56 * System level service that serves as an event dispatch for {@link AccessibilityEvent}s, 57 * and provides facilities for querying the accessibility state of the system. 58 * Accessibility events are generated when something notable happens in the user interface, 59 * for example an {@link android.app.Activity} starts, the focus or selection of a 60 * {@link android.view.View} changes etc. Parties interested in handling accessibility 61 * events implement and register an accessibility service which extends 62 * {@link android.accessibilityservice.AccessibilityService}. 63 * 64 * @see AccessibilityEvent 65 * @see AccessibilityNodeInfo 66 * @see android.accessibilityservice.AccessibilityService 67 * @see Context#getSystemService 68 * @see Context#ACCESSIBILITY_SERVICE 69 */ 70 @SystemService(Context.ACCESSIBILITY_SERVICE) 71 public final class AccessibilityManager { 72 private static final boolean DEBUG = false; 73 74 private static final String LOG_TAG = "AccessibilityManager"; 75 76 /** @hide */ 77 public static final int STATE_FLAG_ACCESSIBILITY_ENABLED = 0x00000001; 78 79 /** @hide */ 80 public static final int STATE_FLAG_TOUCH_EXPLORATION_ENABLED = 0x00000002; 81 82 /** @hide */ 83 public static final int STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED = 0x00000004; 84 85 /** @hide */ 86 public static final int DALTONIZER_DISABLED = -1; 87 88 /** @hide */ 89 public static final int DALTONIZER_SIMULATE_MONOCHROMACY = 0; 90 91 /** @hide */ 92 public static final int DALTONIZER_CORRECT_DEUTERANOMALY = 12; 93 94 /** @hide */ 95 public static final int AUTOCLICK_DELAY_DEFAULT = 600; 96 97 /** 98 * Activity action: Launch UI to manage which accessibility service or feature is assigned 99 * to the navigation bar Accessibility button. 100 * <p> 101 * Input: Nothing. 102 * </p> 103 * <p> 104 * Output: Nothing. 105 * </p> 106 * 107 * @hide 108 */ 109 @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) 110 public static final String ACTION_CHOOSE_ACCESSIBILITY_BUTTON = 111 "com.android.internal.intent.action.CHOOSE_ACCESSIBILITY_BUTTON"; 112 113 static final Object sInstanceSync = new Object(); 114 115 private static AccessibilityManager sInstance; 116 117 private final Object mLock = new Object(); 118 119 private IAccessibilityManager mService; 120 121 final int mUserId; 122 123 final Handler mHandler; 124 125 final Handler.Callback mCallback; 126 127 boolean mIsEnabled; 128 129 int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK; 130 131 boolean mIsTouchExplorationEnabled; 132 133 boolean mIsHighTextContrastEnabled; 134 135 private final ArrayMap<AccessibilityStateChangeListener, Handler> 136 mAccessibilityStateChangeListeners = new ArrayMap<>(); 137 138 private final ArrayMap<TouchExplorationStateChangeListener, Handler> 139 mTouchExplorationStateChangeListeners = new ArrayMap<>(); 140 141 private final ArrayMap<HighTextContrastChangeListener, Handler> 142 mHighTextContrastStateChangeListeners = new ArrayMap<>(); 143 144 private final ArrayMap<AccessibilityServicesStateChangeListener, Handler> 145 mServicesStateChangeListeners = new ArrayMap<>(); 146 147 /** 148 * Listener for the system accessibility state. To listen for changes to the 149 * accessibility state on the device, implement this interface and register 150 * it with the system by calling {@link #addAccessibilityStateChangeListener}. 151 */ 152 public interface AccessibilityStateChangeListener { 153 154 /** 155 * Called when the accessibility enabled state changes. 156 * 157 * @param enabled Whether accessibility is enabled. 158 */ 159 void onAccessibilityStateChanged(boolean enabled); 160 } 161 162 /** 163 * Listener for the system touch exploration state. To listen for changes to 164 * the touch exploration state on the device, implement this interface and 165 * register it with the system by calling 166 * {@link #addTouchExplorationStateChangeListener}. 167 */ 168 public interface TouchExplorationStateChangeListener { 169 170 /** 171 * Called when the touch exploration enabled state changes. 172 * 173 * @param enabled Whether touch exploration is enabled. 174 */ 175 void onTouchExplorationStateChanged(boolean enabled); 176 } 177 178 /** 179 * Listener for changes to the state of accessibility services. Changes include services being 180 * enabled or disabled, or changes to the {@link AccessibilityServiceInfo} of a running service. 181 * {@see #addAccessibilityServicesStateChangeListener}. 182 * 183 * @hide 184 */ 185 public interface AccessibilityServicesStateChangeListener { 186 187 /** 188 * Called when the state of accessibility services changes. 189 * 190 * @param manager The manager that is calling back 191 */ 192 void onAccessibilityServicesStateChanged(AccessibilityManager manager); 193 } 194 195 /** 196 * Listener for the system high text contrast state. To listen for changes to 197 * the high text contrast state on the device, implement this interface and 198 * register it with the system by calling 199 * {@link #addHighTextContrastStateChangeListener}. 200 * 201 * @hide 202 */ 203 public interface HighTextContrastChangeListener { 204 205 /** 206 * Called when the high text contrast enabled state changes. 207 * 208 * @param enabled Whether high text contrast is enabled. 209 */ 210 void onHighTextContrastStateChanged(boolean enabled); 211 } 212 213 private final IAccessibilityManagerClient.Stub mClient = 214 new IAccessibilityManagerClient.Stub() { 215 @Override 216 public void setState(int state) { 217 // We do not want to change this immediately as the application may 218 // have already checked that accessibility is on and fired an event, 219 // that is now propagating up the view tree, Hence, if accessibility 220 // is now off an exception will be thrown. We want to have the exception 221 // enforcement to guard against apps that fire unnecessary accessibility 222 // events when accessibility is off. 223 mHandler.obtainMessage(MyCallback.MSG_SET_STATE, state, 0).sendToTarget(); 224 } 225 226 @Override 227 public void notifyServicesStateChanged() { 228 final ArrayMap<AccessibilityServicesStateChangeListener, Handler> listeners; 229 synchronized (mLock) { 230 if (mServicesStateChangeListeners.isEmpty()) { 231 return; 232 } 233 listeners = new ArrayMap<>(mServicesStateChangeListeners); 234 } 235 236 int numListeners = listeners.size(); 237 for (int i = 0; i < numListeners; i++) { 238 final AccessibilityServicesStateChangeListener listener = 239 mServicesStateChangeListeners.keyAt(i); 240 mServicesStateChangeListeners.valueAt(i).post(() -> listener 241 .onAccessibilityServicesStateChanged(AccessibilityManager.this)); 242 } 243 } 244 245 @Override 246 public void setRelevantEventTypes(int eventTypes) { 247 mRelevantEventTypes = eventTypes; 248 } 249 }; 250 251 /** 252 * Get an AccessibilityManager instance (create one if necessary). 253 * 254 * @param context Context in which this manager operates. 255 * 256 * @hide 257 */ 258 public static AccessibilityManager getInstance(Context context) { 259 synchronized (sInstanceSync) { 260 if (sInstance == null) { 261 final int userId; 262 if (Binder.getCallingUid() == Process.SYSTEM_UID 263 || context.checkCallingOrSelfPermission( 264 Manifest.permission.INTERACT_ACROSS_USERS) 265 == PackageManager.PERMISSION_GRANTED 266 || context.checkCallingOrSelfPermission( 267 Manifest.permission.INTERACT_ACROSS_USERS_FULL) 268 == PackageManager.PERMISSION_GRANTED) { 269 userId = UserHandle.USER_CURRENT; 270 } else { 271 userId = UserHandle.myUserId(); 272 } 273 sInstance = new AccessibilityManager(context, null, userId); 274 } 275 } 276 return sInstance; 277 } 278 279 /** 280 * Create an instance. 281 * 282 * @param context A {@link Context}. 283 * @param service An interface to the backing service. 284 * @param userId User id under which to run. 285 * 286 * @hide 287 */ 288 public AccessibilityManager(Context context, IAccessibilityManager service, int userId) { 289 // Constructor can't be chained because we can't create an instance of an inner class 290 // before calling another constructor. 291 mCallback = new MyCallback(); 292 mHandler = new Handler(context.getMainLooper(), mCallback); 293 mUserId = userId; 294 synchronized (mLock) { 295 tryConnectToServiceLocked(service); 296 } 297 } 298 299 /** 300 * Create an instance. 301 * 302 * @param handler The handler to use 303 * @param service An interface to the backing service. 304 * @param userId User id under which to run. 305 * 306 * @hide 307 */ 308 public AccessibilityManager(Handler handler, IAccessibilityManager service, int userId) { 309 mCallback = new MyCallback(); 310 mHandler = handler; 311 mUserId = userId; 312 synchronized (mLock) { 313 tryConnectToServiceLocked(service); 314 } 315 } 316 317 /** 318 * @hide 319 */ 320 public IAccessibilityManagerClient getClient() { 321 return mClient; 322 } 323 324 /** 325 * @hide 326 */ 327 @VisibleForTesting 328 public Handler.Callback getCallback() { 329 return mCallback; 330 } 331 332 /** 333 * Returns if the accessibility in the system is enabled. 334 * 335 * @return True if accessibility is enabled, false otherwise. 336 */ 337 public boolean isEnabled() { 338 synchronized (mLock) { 339 IAccessibilityManager service = getServiceLocked(); 340 if (service == null) { 341 return false; 342 } 343 return mIsEnabled; 344 } 345 } 346 347 /** 348 * Returns if the touch exploration in the system is enabled. 349 * 350 * @return True if touch exploration is enabled, false otherwise. 351 */ 352 public boolean isTouchExplorationEnabled() { 353 synchronized (mLock) { 354 IAccessibilityManager service = getServiceLocked(); 355 if (service == null) { 356 return false; 357 } 358 return mIsTouchExplorationEnabled; 359 } 360 } 361 362 /** 363 * Returns if the high text contrast in the system is enabled. 364 * <p> 365 * <strong>Note:</strong> You need to query this only if you application is 366 * doing its own rendering and does not rely on the platform rendering pipeline. 367 * </p> 368 * 369 * @return True if high text contrast is enabled, false otherwise. 370 * 371 * @hide 372 */ 373 public boolean isHighTextContrastEnabled() { 374 synchronized (mLock) { 375 IAccessibilityManager service = getServiceLocked(); 376 if (service == null) { 377 return false; 378 } 379 return mIsHighTextContrastEnabled; 380 } 381 } 382 383 /** 384 * Sends an {@link AccessibilityEvent}. 385 * 386 * @param event The event to send. 387 * 388 * @throws IllegalStateException if accessibility is not enabled. 389 * 390 * <strong>Note:</strong> The preferred mechanism for sending custom accessibility 391 * events is through calling 392 * {@link android.view.ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)} 393 * instead of this method to allow predecessors to augment/filter events sent by 394 * their descendants. 395 */ 396 public void sendAccessibilityEvent(AccessibilityEvent event) { 397 final IAccessibilityManager service; 398 final int userId; 399 synchronized (mLock) { 400 service = getServiceLocked(); 401 if (service == null) { 402 return; 403 } 404 if (!mIsEnabled) { 405 Looper myLooper = Looper.myLooper(); 406 if (myLooper == Looper.getMainLooper()) { 407 throw new IllegalStateException( 408 "Accessibility off. Did you forget to check that?"); 409 } else { 410 // If we're not running on the thread with the main looper, it's possible for 411 // the state of accessibility to change between checking isEnabled and 412 // calling this method. So just log the error rather than throwing the 413 // exception. 414 Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled"); 415 return; 416 } 417 } 418 if ((event.getEventType() & mRelevantEventTypes) == 0) { 419 if (DEBUG) { 420 Log.i(LOG_TAG, "Not dispatching irrelevant event: " + event 421 + " that is not among " 422 + AccessibilityEvent.eventTypeToString(mRelevantEventTypes)); 423 } 424 return; 425 } 426 userId = mUserId; 427 } 428 try { 429 event.setEventTime(SystemClock.uptimeMillis()); 430 // it is possible that this manager is in the same process as the service but 431 // client using it is called through Binder from another process. Example: MMS 432 // app adds a SMS notification and the NotificationManagerService calls this method 433 long identityToken = Binder.clearCallingIdentity(); 434 service.sendAccessibilityEvent(event, userId); 435 Binder.restoreCallingIdentity(identityToken); 436 if (DEBUG) { 437 Log.i(LOG_TAG, event + " sent"); 438 } 439 } catch (RemoteException re) { 440 Log.e(LOG_TAG, "Error during sending " + event + " ", re); 441 } finally { 442 event.recycle(); 443 } 444 } 445 446 /** 447 * Requests feedback interruption from all accessibility services. 448 */ 449 public void interrupt() { 450 final IAccessibilityManager service; 451 final int userId; 452 synchronized (mLock) { 453 service = getServiceLocked(); 454 if (service == null) { 455 return; 456 } 457 if (!mIsEnabled) { 458 Looper myLooper = Looper.myLooper(); 459 if (myLooper == Looper.getMainLooper()) { 460 throw new IllegalStateException( 461 "Accessibility off. Did you forget to check that?"); 462 } else { 463 // If we're not running on the thread with the main looper, it's possible for 464 // the state of accessibility to change between checking isEnabled and 465 // calling this method. So just log the error rather than throwing the 466 // exception. 467 Log.e(LOG_TAG, "Interrupt called with accessibility disabled"); 468 return; 469 } 470 } 471 userId = mUserId; 472 } 473 try { 474 service.interrupt(userId); 475 if (DEBUG) { 476 Log.i(LOG_TAG, "Requested interrupt from all services"); 477 } 478 } catch (RemoteException re) { 479 Log.e(LOG_TAG, "Error while requesting interrupt from all services. ", re); 480 } 481 } 482 483 /** 484 * Returns the {@link ServiceInfo}s of the installed accessibility services. 485 * 486 * @return An unmodifiable list with {@link ServiceInfo}s. 487 * 488 * @deprecated Use {@link #getInstalledAccessibilityServiceList()} 489 */ 490 @Deprecated 491 public List<ServiceInfo> getAccessibilityServiceList() { 492 List<AccessibilityServiceInfo> infos = getInstalledAccessibilityServiceList(); 493 List<ServiceInfo> services = new ArrayList<>(); 494 final int infoCount = infos.size(); 495 for (int i = 0; i < infoCount; i++) { 496 AccessibilityServiceInfo info = infos.get(i); 497 services.add(info.getResolveInfo().serviceInfo); 498 } 499 return Collections.unmodifiableList(services); 500 } 501 502 /** 503 * Returns the {@link AccessibilityServiceInfo}s of the installed accessibility services. 504 * 505 * @return An unmodifiable list with {@link AccessibilityServiceInfo}s. 506 */ 507 public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() { 508 final IAccessibilityManager service; 509 final int userId; 510 synchronized (mLock) { 511 service = getServiceLocked(); 512 if (service == null) { 513 return Collections.emptyList(); 514 } 515 userId = mUserId; 516 } 517 518 List<AccessibilityServiceInfo> services = null; 519 try { 520 services = service.getInstalledAccessibilityServiceList(userId); 521 if (DEBUG) { 522 Log.i(LOG_TAG, "Installed AccessibilityServices " + services); 523 } 524 } catch (RemoteException re) { 525 Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re); 526 } 527 if (services != null) { 528 return Collections.unmodifiableList(services); 529 } else { 530 return Collections.emptyList(); 531 } 532 } 533 534 /** 535 * Returns the {@link AccessibilityServiceInfo}s of the enabled accessibility services 536 * for a given feedback type. 537 * 538 * @param feedbackTypeFlags The feedback type flags. 539 * @return An unmodifiable list with {@link AccessibilityServiceInfo}s. 540 * 541 * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE 542 * @see AccessibilityServiceInfo#FEEDBACK_GENERIC 543 * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC 544 * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN 545 * @see AccessibilityServiceInfo#FEEDBACK_VISUAL 546 * @see AccessibilityServiceInfo#FEEDBACK_BRAILLE 547 */ 548 public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList( 549 int feedbackTypeFlags) { 550 final IAccessibilityManager service; 551 final int userId; 552 synchronized (mLock) { 553 service = getServiceLocked(); 554 if (service == null) { 555 return Collections.emptyList(); 556 } 557 userId = mUserId; 558 } 559 560 List<AccessibilityServiceInfo> services = null; 561 try { 562 services = service.getEnabledAccessibilityServiceList(feedbackTypeFlags, userId); 563 if (DEBUG) { 564 Log.i(LOG_TAG, "Installed AccessibilityServices " + services); 565 } 566 } catch (RemoteException re) { 567 Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re); 568 } 569 if (services != null) { 570 return Collections.unmodifiableList(services); 571 } else { 572 return Collections.emptyList(); 573 } 574 } 575 576 /** 577 * Registers an {@link AccessibilityStateChangeListener} for changes in 578 * the global accessibility state of the system. Equivalent to calling 579 * {@link #addAccessibilityStateChangeListener(AccessibilityStateChangeListener, Handler)} 580 * with a null handler. 581 * 582 * @param listener The listener. 583 * @return Always returns {@code true}. 584 */ 585 public boolean addAccessibilityStateChangeListener( 586 @NonNull AccessibilityStateChangeListener listener) { 587 addAccessibilityStateChangeListener(listener, null); 588 return true; 589 } 590 591 /** 592 * Registers an {@link AccessibilityStateChangeListener} for changes in 593 * the global accessibility state of the system. If the listener has already been registered, 594 * the handler used to call it back is updated. 595 * 596 * @param listener The listener. 597 * @param handler The handler on which the listener should be called back, or {@code null} 598 * for a callback on the process's main handler. 599 */ 600 public void addAccessibilityStateChangeListener( 601 @NonNull AccessibilityStateChangeListener listener, @Nullable Handler handler) { 602 synchronized (mLock) { 603 mAccessibilityStateChangeListeners 604 .put(listener, (handler == null) ? mHandler : handler); 605 } 606 } 607 608 /** 609 * Unregisters an {@link AccessibilityStateChangeListener}. 610 * 611 * @param listener The listener. 612 * @return True if the listener was previously registered. 613 */ 614 public boolean removeAccessibilityStateChangeListener( 615 @NonNull AccessibilityStateChangeListener listener) { 616 synchronized (mLock) { 617 int index = mAccessibilityStateChangeListeners.indexOfKey(listener); 618 mAccessibilityStateChangeListeners.remove(listener); 619 return (index >= 0); 620 } 621 } 622 623 /** 624 * Registers a {@link TouchExplorationStateChangeListener} for changes in 625 * the global touch exploration state of the system. Equivalent to calling 626 * {@link #addTouchExplorationStateChangeListener(TouchExplorationStateChangeListener, Handler)} 627 * with a null handler. 628 * 629 * @param listener The listener. 630 * @return Always returns {@code true}. 631 */ 632 public boolean addTouchExplorationStateChangeListener( 633 @NonNull TouchExplorationStateChangeListener listener) { 634 addTouchExplorationStateChangeListener(listener, null); 635 return true; 636 } 637 638 /** 639 * Registers an {@link TouchExplorationStateChangeListener} for changes in 640 * the global touch exploration state of the system. If the listener has already been 641 * registered, the handler used to call it back is updated. 642 * 643 * @param listener The listener. 644 * @param handler The handler on which the listener should be called back, or {@code null} 645 * for a callback on the process's main handler. 646 */ 647 public void addTouchExplorationStateChangeListener( 648 @NonNull TouchExplorationStateChangeListener listener, @Nullable Handler handler) { 649 synchronized (mLock) { 650 mTouchExplorationStateChangeListeners 651 .put(listener, (handler == null) ? mHandler : handler); 652 } 653 } 654 655 /** 656 * Unregisters a {@link TouchExplorationStateChangeListener}. 657 * 658 * @param listener The listener. 659 * @return True if listener was previously registered. 660 */ 661 public boolean removeTouchExplorationStateChangeListener( 662 @NonNull TouchExplorationStateChangeListener listener) { 663 synchronized (mLock) { 664 int index = mTouchExplorationStateChangeListeners.indexOfKey(listener); 665 mTouchExplorationStateChangeListeners.remove(listener); 666 return (index >= 0); 667 } 668 } 669 670 /** 671 * Registers a {@link AccessibilityServicesStateChangeListener}. 672 * 673 * @param listener The listener. 674 * @param handler The handler on which the listener should be called back, or {@code null} 675 * for a callback on the process's main handler. 676 * @hide 677 */ 678 public void addAccessibilityServicesStateChangeListener( 679 @NonNull AccessibilityServicesStateChangeListener listener, @Nullable Handler handler) { 680 synchronized (mLock) { 681 mServicesStateChangeListeners 682 .put(listener, (handler == null) ? mHandler : handler); 683 } 684 } 685 686 /** 687 * Unregisters a {@link AccessibilityServicesStateChangeListener}. 688 * 689 * @param listener The listener. 690 * 691 * @hide 692 */ 693 public void removeAccessibilityServicesStateChangeListener( 694 @NonNull AccessibilityServicesStateChangeListener listener) { 695 // Final CopyOnWriteArrayList - no lock needed. 696 mServicesStateChangeListeners.remove(listener); 697 } 698 699 /** 700 * Registers a {@link HighTextContrastChangeListener} for changes in 701 * the global high text contrast state of the system. 702 * 703 * @param listener The listener. 704 * 705 * @hide 706 */ 707 public void addHighTextContrastStateChangeListener( 708 @NonNull HighTextContrastChangeListener listener, @Nullable Handler handler) { 709 synchronized (mLock) { 710 mHighTextContrastStateChangeListeners 711 .put(listener, (handler == null) ? mHandler : handler); 712 } 713 } 714 715 /** 716 * Unregisters a {@link HighTextContrastChangeListener}. 717 * 718 * @param listener The listener. 719 * 720 * @hide 721 */ 722 public void removeHighTextContrastStateChangeListener( 723 @NonNull HighTextContrastChangeListener listener) { 724 synchronized (mLock) { 725 mHighTextContrastStateChangeListeners.remove(listener); 726 } 727 } 728 729 /** 730 * Check if the accessibility volume stream is active. 731 * 732 * @return True if accessibility volume is active (i.e. some service has requested it). False 733 * otherwise. 734 * @hide 735 */ 736 public boolean isAccessibilityVolumeStreamActive() { 737 List<AccessibilityServiceInfo> serviceInfos = 738 getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK); 739 for (int i = 0; i < serviceInfos.size(); i++) { 740 if ((serviceInfos.get(i).flags & FLAG_ENABLE_ACCESSIBILITY_VOLUME) != 0) { 741 return true; 742 } 743 } 744 return false; 745 } 746 747 /** 748 * Report a fingerprint gesture to accessibility. Only available for the system process. 749 * 750 * @param keyCode The key code of the gesture 751 * @return {@code true} if accessibility consumes the event. {@code false} if not. 752 * @hide 753 */ 754 public boolean sendFingerprintGesture(int keyCode) { 755 final IAccessibilityManager service; 756 synchronized (mLock) { 757 service = getServiceLocked(); 758 if (service == null) { 759 return false; 760 } 761 } 762 try { 763 return service.sendFingerprintGesture(keyCode); 764 } catch (RemoteException e) { 765 return false; 766 } 767 } 768 769 /** 770 * Sets the current state and notifies listeners, if necessary. 771 * 772 * @param stateFlags The state flags. 773 */ 774 private void setStateLocked(int stateFlags) { 775 final boolean enabled = (stateFlags & STATE_FLAG_ACCESSIBILITY_ENABLED) != 0; 776 final boolean touchExplorationEnabled = 777 (stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0; 778 final boolean highTextContrastEnabled = 779 (stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0; 780 781 final boolean wasEnabled = mIsEnabled; 782 final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled; 783 final boolean wasHighTextContrastEnabled = mIsHighTextContrastEnabled; 784 785 // Ensure listeners get current state from isZzzEnabled() calls. 786 mIsEnabled = enabled; 787 mIsTouchExplorationEnabled = touchExplorationEnabled; 788 mIsHighTextContrastEnabled = highTextContrastEnabled; 789 790 if (wasEnabled != enabled) { 791 notifyAccessibilityStateChanged(); 792 } 793 794 if (wasTouchExplorationEnabled != touchExplorationEnabled) { 795 notifyTouchExplorationStateChanged(); 796 } 797 798 if (wasHighTextContrastEnabled != highTextContrastEnabled) { 799 notifyHighTextContrastStateChanged(); 800 } 801 } 802 803 /** 804 * Find an installed service with the specified {@link ComponentName}. 805 * 806 * @param componentName The name to match to the service. 807 * 808 * @return The info corresponding to the installed service, or {@code null} if no such service 809 * is installed. 810 * @hide 811 */ 812 public AccessibilityServiceInfo getInstalledServiceInfoWithComponentName( 813 ComponentName componentName) { 814 final List<AccessibilityServiceInfo> installedServiceInfos = 815 getInstalledAccessibilityServiceList(); 816 if ((installedServiceInfos == null) || (componentName == null)) { 817 return null; 818 } 819 for (int i = 0; i < installedServiceInfos.size(); i++) { 820 if (componentName.equals(installedServiceInfos.get(i).getComponentName())) { 821 return installedServiceInfos.get(i); 822 } 823 } 824 return null; 825 } 826 827 /** 828 * Adds an accessibility interaction connection interface for a given window. 829 * @param windowToken The window token to which a connection is added. 830 * @param connection The connection. 831 * 832 * @hide 833 */ 834 public int addAccessibilityInteractionConnection(IWindow windowToken, 835 IAccessibilityInteractionConnection connection) { 836 final IAccessibilityManager service; 837 final int userId; 838 synchronized (mLock) { 839 service = getServiceLocked(); 840 if (service == null) { 841 return View.NO_ID; 842 } 843 userId = mUserId; 844 } 845 try { 846 return service.addAccessibilityInteractionConnection(windowToken, connection, userId); 847 } catch (RemoteException re) { 848 Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re); 849 } 850 return View.NO_ID; 851 } 852 853 /** 854 * Removed an accessibility interaction connection interface for a given window. 855 * @param windowToken The window token to which a connection is removed. 856 * 857 * @hide 858 */ 859 public void removeAccessibilityInteractionConnection(IWindow windowToken) { 860 final IAccessibilityManager service; 861 synchronized (mLock) { 862 service = getServiceLocked(); 863 if (service == null) { 864 return; 865 } 866 } 867 try { 868 service.removeAccessibilityInteractionConnection(windowToken); 869 } catch (RemoteException re) { 870 Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re); 871 } 872 } 873 874 /** 875 * Perform the accessibility shortcut if the caller has permission. 876 * 877 * @hide 878 */ 879 public void performAccessibilityShortcut() { 880 final IAccessibilityManager service; 881 synchronized (mLock) { 882 service = getServiceLocked(); 883 if (service == null) { 884 return; 885 } 886 } 887 try { 888 service.performAccessibilityShortcut(); 889 } catch (RemoteException re) { 890 Log.e(LOG_TAG, "Error performing accessibility shortcut. ", re); 891 } 892 } 893 894 /** 895 * Notifies that the accessibility button in the system's navigation area has been clicked 896 * 897 * @hide 898 */ 899 public void notifyAccessibilityButtonClicked() { 900 final IAccessibilityManager service; 901 synchronized (mLock) { 902 service = getServiceLocked(); 903 if (service == null) { 904 return; 905 } 906 } 907 try { 908 service.notifyAccessibilityButtonClicked(); 909 } catch (RemoteException re) { 910 Log.e(LOG_TAG, "Error while dispatching accessibility button click", re); 911 } 912 } 913 914 /** 915 * Notifies that the visibility of the accessibility button in the system's navigation area 916 * has changed. 917 * 918 * @param shown {@code true} if the accessibility button is visible within the system 919 * navigation area, {@code false} otherwise 920 * @hide 921 */ 922 public void notifyAccessibilityButtonVisibilityChanged(boolean shown) { 923 final IAccessibilityManager service; 924 synchronized (mLock) { 925 service = getServiceLocked(); 926 if (service == null) { 927 return; 928 } 929 } 930 try { 931 service.notifyAccessibilityButtonVisibilityChanged(shown); 932 } catch (RemoteException re) { 933 Log.e(LOG_TAG, "Error while dispatching accessibility button visibility change", re); 934 } 935 } 936 937 /** 938 * Set an IAccessibilityInteractionConnection to replace the actions of a picture-in-picture 939 * window. Intended for use by the System UI only. 940 * 941 * @param connection The connection to handle the actions. Set to {@code null} to avoid 942 * affecting the actions. 943 * 944 * @hide 945 */ 946 public void setPictureInPictureActionReplacingConnection( 947 @Nullable IAccessibilityInteractionConnection connection) { 948 final IAccessibilityManager service; 949 synchronized (mLock) { 950 service = getServiceLocked(); 951 if (service == null) { 952 return; 953 } 954 } 955 try { 956 service.setPictureInPictureActionReplacingConnection(connection); 957 } catch (RemoteException re) { 958 Log.e(LOG_TAG, "Error setting picture in picture action replacement", re); 959 } 960 } 961 962 private IAccessibilityManager getServiceLocked() { 963 if (mService == null) { 964 tryConnectToServiceLocked(null); 965 } 966 return mService; 967 } 968 969 private void tryConnectToServiceLocked(IAccessibilityManager service) { 970 if (service == null) { 971 IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE); 972 if (iBinder == null) { 973 return; 974 } 975 service = IAccessibilityManager.Stub.asInterface(iBinder); 976 } 977 978 try { 979 final long userStateAndRelevantEvents = service.addClient(mClient, mUserId); 980 setStateLocked(IntPair.first(userStateAndRelevantEvents)); 981 mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents); 982 mService = service; 983 } catch (RemoteException re) { 984 Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); 985 } 986 } 987 988 /** 989 * Notifies the registered {@link AccessibilityStateChangeListener}s. 990 */ 991 private void notifyAccessibilityStateChanged() { 992 final boolean isEnabled; 993 final ArrayMap<AccessibilityStateChangeListener, Handler> listeners; 994 synchronized (mLock) { 995 if (mAccessibilityStateChangeListeners.isEmpty()) { 996 return; 997 } 998 isEnabled = mIsEnabled; 999 listeners = new ArrayMap<>(mAccessibilityStateChangeListeners); 1000 } 1001 1002 int numListeners = listeners.size(); 1003 for (int i = 0; i < numListeners; i++) { 1004 final AccessibilityStateChangeListener listener = 1005 mAccessibilityStateChangeListeners.keyAt(i); 1006 mAccessibilityStateChangeListeners.valueAt(i) 1007 .post(() -> listener.onAccessibilityStateChanged(isEnabled)); 1008 } 1009 } 1010 1011 /** 1012 * Notifies the registered {@link TouchExplorationStateChangeListener}s. 1013 */ 1014 private void notifyTouchExplorationStateChanged() { 1015 final boolean isTouchExplorationEnabled; 1016 final ArrayMap<TouchExplorationStateChangeListener, Handler> listeners; 1017 synchronized (mLock) { 1018 if (mTouchExplorationStateChangeListeners.isEmpty()) { 1019 return; 1020 } 1021 isTouchExplorationEnabled = mIsTouchExplorationEnabled; 1022 listeners = new ArrayMap<>(mTouchExplorationStateChangeListeners); 1023 } 1024 1025 int numListeners = listeners.size(); 1026 for (int i = 0; i < numListeners; i++) { 1027 final TouchExplorationStateChangeListener listener = 1028 mTouchExplorationStateChangeListeners.keyAt(i); 1029 mTouchExplorationStateChangeListeners.valueAt(i) 1030 .post(() -> listener.onTouchExplorationStateChanged(isTouchExplorationEnabled)); 1031 } 1032 } 1033 1034 /** 1035 * Notifies the registered {@link HighTextContrastChangeListener}s. 1036 */ 1037 private void notifyHighTextContrastStateChanged() { 1038 final boolean isHighTextContrastEnabled; 1039 final ArrayMap<HighTextContrastChangeListener, Handler> listeners; 1040 synchronized (mLock) { 1041 if (mHighTextContrastStateChangeListeners.isEmpty()) { 1042 return; 1043 } 1044 isHighTextContrastEnabled = mIsHighTextContrastEnabled; 1045 listeners = new ArrayMap<>(mHighTextContrastStateChangeListeners); 1046 } 1047 1048 int numListeners = listeners.size(); 1049 for (int i = 0; i < numListeners; i++) { 1050 final HighTextContrastChangeListener listener = 1051 mHighTextContrastStateChangeListeners.keyAt(i); 1052 mHighTextContrastStateChangeListeners.valueAt(i) 1053 .post(() -> listener.onHighTextContrastStateChanged(isHighTextContrastEnabled)); 1054 } 1055 } 1056 1057 /** 1058 * Determines if the accessibility button within the system navigation area is supported. 1059 * 1060 * @return {@code true} if the accessibility button is supported on this device, 1061 * {@code false} otherwise 1062 * @hide 1063 */ 1064 @SystemApi 1065 public static boolean isAccessibilityButtonSupported() { 1066 final Resources res = Resources.getSystem(); 1067 return res.getBoolean(com.android.internal.R.bool.config_showNavigationBar); 1068 } 1069 1070 private final class MyCallback implements Handler.Callback { 1071 public static final int MSG_SET_STATE = 1; 1072 1073 @Override 1074 public boolean handleMessage(Message message) { 1075 switch (message.what) { 1076 case MSG_SET_STATE: { 1077 // See comment at mClient 1078 final int state = message.arg1; 1079 synchronized (mLock) { 1080 setStateLocked(state); 1081 } 1082 } break; 1083 } 1084 return true; 1085 } 1086 } 1087 } 1088