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