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