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