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