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