/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view.accessibility; import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME; import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; import android.Manifest; import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType; import android.accessibilityservice.AccessibilityShortcutInfo; import android.annotation.CallbackExecutor; import android.annotation.ColorInt; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UserIdInt; import android.app.RemoteAction; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.HandlerExecutor; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.util.ArrayMap; import android.util.Log; import android.util.SparseArray; import android.view.IWindow; import android.view.SurfaceControl; import android.view.View; import android.view.accessibility.AccessibilityEvent.EventType; import com.android.internal.R; import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IntPair; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; /** * System level service that serves as an event dispatch for {@link AccessibilityEvent}s, * and provides facilities for querying the accessibility state of the system. * Accessibility events are generated when something notable happens in the user interface, * for example an {@link android.app.Activity} starts, the focus or selection of a * {@link android.view.View} changes etc. Parties interested in handling accessibility * events implement and register an accessibility service which extends * {@link android.accessibilityservice.AccessibilityService}. * * @see AccessibilityEvent * @see AccessibilityNodeInfo * @see android.accessibilityservice.AccessibilityService * @see Context#getSystemService * @see Context#ACCESSIBILITY_SERVICE */ @SystemService(Context.ACCESSIBILITY_SERVICE) public final class AccessibilityManager { private static final boolean DEBUG = false; private static final String LOG_TAG = "AccessibilityManager"; /** @hide */ public static final int STATE_FLAG_ACCESSIBILITY_ENABLED = 1 /* << 0 */; /** @hide */ public static final int STATE_FLAG_TOUCH_EXPLORATION_ENABLED = 1 << 1; /** @hide */ public static final int STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED = 1 << 2; /** @hide */ public static final int STATE_FLAG_DISPATCH_DOUBLE_TAP = 1 << 3; /** @hide */ public static final int STATE_FLAG_REQUEST_MULTI_FINGER_GESTURES = 1 << 4; /** @hide */ public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED = 1 << 8; /** @hide */ public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED = 1 << 9; /** @hide */ public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED = 1 << 10; /** @hide */ public static final int STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED = 1 << 11; /** @hide */ public static final int STATE_FLAG_AUDIO_DESCRIPTION_BY_DEFAULT_ENABLED = 1 << 12; /** @hide */ public static final int DALTONIZER_DISABLED = -1; /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int DALTONIZER_SIMULATE_MONOCHROMACY = 0; /** @hide */ public static final int DALTONIZER_CORRECT_DEUTERANOMALY = 12; /** @hide */ public static final int AUTOCLICK_DELAY_DEFAULT = 600; /** * Activity action: Launch UI to manage which accessibility service or feature is assigned * to the navigation bar Accessibility button. *
* Input: Nothing. *
** Output: Nothing. *
* * @hide */ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_CHOOSE_ACCESSIBILITY_BUTTON = "com.android.internal.intent.action.CHOOSE_ACCESSIBILITY_BUTTON"; /** @hide */ public static final int FLASH_REASON_CALL = 1; /** @hide */ public static final int FLASH_REASON_ALARM = 2; /** @hide */ public static final int FLASH_REASON_NOTIFICATION = 3; /** @hide */ public static final int FLASH_REASON_PREVIEW = 4; /** * Annotations for content flag of UI. * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = { "FLAG_CONTENT_" }, value = { FLAG_CONTENT_ICONS, FLAG_CONTENT_TEXT, FLAG_CONTENT_CONTROLS }) public @interface ContentFlag {} /** * Annotations for reason of Flash notification. * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "FLASH_REASON_" }, value = { FLASH_REASON_CALL, FLASH_REASON_ALARM, FLASH_REASON_NOTIFICATION, FLASH_REASON_PREVIEW }) public @interface FlashNotificationReason {} /** * Use this flag to indicate the content of a UI that times out contains icons. * * @see #getRecommendedTimeoutMillis(int, int) */ public static final int FLAG_CONTENT_ICONS = 1; /** * Use this flag to indicate the content of a UI that times out contains text. * * @see #getRecommendedTimeoutMillis(int, int) */ public static final int FLAG_CONTENT_TEXT = 2; /** * Use this flag to indicate the content of a UI that times out contains interactive controls. * * @see #getRecommendedTimeoutMillis(int, int) */ public static final int FLAG_CONTENT_CONTROLS = 4; @UnsupportedAppUsage static final Object sInstanceSync = new Object(); @UnsupportedAppUsage private static AccessibilityManager sInstance; @UnsupportedAppUsage private final Object mLock = new Object(); @UnsupportedAppUsage private IAccessibilityManager mService; @UnsupportedAppUsage final int mUserId; @UnsupportedAppUsage final Handler mHandler; final Handler.Callback mCallback; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) boolean mIsEnabled; int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK; int mInteractiveUiTimeout; int mNonInteractiveUiTimeout; boolean mIsTouchExplorationEnabled; @UnsupportedAppUsage(trackingBug = 123768939L) boolean mIsHighTextContrastEnabled; boolean mIsAudioDescriptionByDefaultRequested; // accessibility tracing state int mAccessibilityTracingState = 0; AccessibilityPolicy mAccessibilityPolicy; private int mPerformingAction = 0; /** The stroke width of the focus rectangle in pixels */ private int mFocusStrokeWidth; /** The color of the focus rectangle */ private int mFocusColor; @UnsupportedAppUsage private final ArrayMap* This refers to changes to {@link AccessibilityServiceInfo}, including: *
* Note: This query is used for sending {@link AccessibilityEvent}s, since events are * only needed if accessibility is on. Avoid changing UI or app behavior based on the state of * accessibility. While well-intentioned, doing this creates brittle, less * well-maintained code that works for some users but not others. Shared code leads to more * equitable experiences and less technical debt. * *
* For example, if you want to expose a unique interaction with your app, use * ViewCompat#addAccessibilityAction in AndroidX to make this interaction - ideally * with the same code path used for non-accessibility users - available to accessibility * services. Services can then expose this action in the way best fit for their users. * * @return True if accessibility is enabled, false otherwise. */ public boolean isEnabled() { synchronized (mLock) { return mIsEnabled || hasAnyDirectConnection() || (mAccessibilityPolicy != null && mAccessibilityPolicy.isEnabled(mIsEnabled)); } } /** * @see AccessibilityInteractionClient#hasAnyDirectConnection * @hide */ @TestApi public boolean hasAnyDirectConnection() { return AccessibilityInteractionClient.hasAnyDirectConnection(); } /** * Returns if the touch exploration in the system is enabled. *
* Note: This query is used for dispatching hover events, such as * {@link android.view.MotionEvent#ACTION_HOVER_ENTER}, to accessibility services to manage * touch exploration. Avoid changing UI or app behavior based on the state of accessibility. * While well-intentioned, doing this creates brittle, less well-maintained code that works for * som users but not others. Shared code leads to more equitable experiences and less technical * debt. * * @return True if touch exploration is enabled, false otherwise. */ public boolean isTouchExplorationEnabled() { synchronized (mLock) { IAccessibilityManager service = getServiceLocked(); if (service == null) { return false; } return mIsTouchExplorationEnabled; } } /** * Returns if the high text contrast in the system is enabled. *
* Note: You need to query this only if you application is * doing its own rendering and does not rely on the platform rendering pipeline. *
* * @return True if high text contrast is enabled, false otherwise. * * @hide */ @UnsupportedAppUsage public boolean isHighTextContrastEnabled() { synchronized (mLock) { IAccessibilityManager service = getServiceLocked(); if (service == null) { return false; } return mIsHighTextContrastEnabled; } } /** * Sends an {@link AccessibilityEvent}. * * @param event The event to send. * * @throws IllegalStateException if accessibility is not enabled. * * Note: The preferred mechanism for sending custom accessibility * events is through calling * {@link android.view.ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)} * instead of this method to allow predecessors to augment/filter events sent by * their descendants. */ public void sendAccessibilityEvent(AccessibilityEvent event) { final IAccessibilityManager service; final int userId; final AccessibilityEvent dispatchedEvent; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return; } event.setEventTime(SystemClock.uptimeMillis()); if (event.getAction() == 0) { event.setAction(mPerformingAction); } if (mAccessibilityPolicy != null) { dispatchedEvent = mAccessibilityPolicy.onAccessibilityEvent(event, mIsEnabled, mRelevantEventTypes); if (dispatchedEvent == null) { return; } } else { dispatchedEvent = event; } if (!isEnabled()) { Looper myLooper = Looper.myLooper(); if (myLooper == Looper.getMainLooper()) { throw new IllegalStateException( "Accessibility off. Did you forget to check that?"); } else { // If we're not running on the thread with the main looper, it's possible for // the state of accessibility to change between checking isEnabled and // calling this method. So just log the error rather than throwing the // exception. Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled"); return; } } if ((dispatchedEvent.getEventType() & mRelevantEventTypes) == 0) { if (DEBUG) { Log.i(LOG_TAG, "Not dispatching irrelevant event: " + dispatchedEvent + " that is not among " + AccessibilityEvent.eventTypeToString(mRelevantEventTypes)); } return; } userId = mUserId; } try { // it is possible that this manager is in the same process as the service but // client using it is called through Binder from another process. Example: MMS // app adds a SMS notification and the NotificationManagerService calls this method final long identityToken = Binder.clearCallingIdentity(); try { service.sendAccessibilityEvent(dispatchedEvent, userId); } finally { Binder.restoreCallingIdentity(identityToken); } if (DEBUG) { Log.i(LOG_TAG, dispatchedEvent + " sent"); } } catch (RemoteException re) { Log.e(LOG_TAG, "Error during sending " + dispatchedEvent + " ", re); } finally { if (event != dispatchedEvent) { event.recycle(); } dispatchedEvent.recycle(); } } /** * Requests feedback interruption from all accessibility services. */ public void interrupt() { final IAccessibilityManager service; final int userId; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return; } if (!isEnabled()) { Looper myLooper = Looper.myLooper(); if (myLooper == Looper.getMainLooper()) { throw new IllegalStateException( "Accessibility off. Did you forget to check that?"); } else { // If we're not running on the thread with the main looper, it's possible for // the state of accessibility to change between checking isEnabled and // calling this method. So just log the error rather than throwing the // exception. Log.e(LOG_TAG, "Interrupt called with accessibility disabled"); return; } } userId = mUserId; } try { service.interrupt(userId); if (DEBUG) { Log.i(LOG_TAG, "Requested interrupt from all services"); } } catch (RemoteException re) { Log.e(LOG_TAG, "Error while requesting interrupt from all services. ", re); } } /** * Returns the {@link ServiceInfo}s of the installed accessibility services. * * @return An unmodifiable list with {@link ServiceInfo}s. * * @deprecated Use {@link #getInstalledAccessibilityServiceList()} */ @Deprecated public List* You can use this method inside {@link AccessibilityNodeProvider} to decide how to populate * your nodes. *
* ** Note: The return value is valid only when an {@link AccessibilityNodeInfo} * request is in progress, can change from one request to another, and has no meaning when a * request is not in progress. *
* * @return True if the current request is from a tool that sets isAccessibilityTool. */ public boolean isRequestFromAccessibilityTool() { return mRequestFromAccessibilityTool; } /** * Specifies whether the current accessibility request comes from an * {@link AccessibilityService} with the {@link AccessibilityServiceInfo#isAccessibilityTool} * property set to true. * * @hide */ public void setRequestFromAccessibilityTool(boolean requestFromAccessibilityTool) { mRequestFromAccessibilityTool = requestFromAccessibilityTool; } /** * Registers a {@link AccessibilityRequestPreparer}. */ public void addAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer) { if (mRequestPreparerLists == null) { mRequestPreparerLists = new SparseArray<>(1); } int id = preparer.getAccessibilityViewId(); List* Use the combination of content flags to indicate contents of UI. For example, use * {@code FLAG_CONTENT_ICONS | FLAG_CONTENT_TEXT} for message notification which contains * icons and text, or use {@code FLAG_CONTENT_TEXT | FLAG_CONTENT_CONTROLS} for button dialog * which contains text and button controls. *
* * @param originalTimeout The timeout appropriate for users with no accessibility needs. * @param uiContentFlags The combination of flags {@link #FLAG_CONTENT_ICONS}, * {@link #FLAG_CONTENT_TEXT} or {@link #FLAG_CONTENT_CONTROLS} to * indicate the contents of UI. * @return The recommended UI timeout for the current user in milliseconds. */ public int getRecommendedTimeoutMillis(int originalTimeout, @ContentFlag int uiContentFlags) { boolean hasControls = (uiContentFlags & FLAG_CONTENT_CONTROLS) != 0; boolean hasIconsOrText = (uiContentFlags & FLAG_CONTENT_ICONS) != 0 || (uiContentFlags & FLAG_CONTENT_TEXT) != 0; int recommendedTimeout = originalTimeout; if (hasControls) { recommendedTimeout = Math.max(recommendedTimeout, mInteractiveUiTimeout); } if (hasIconsOrText) { recommendedTimeout = Math.max(recommendedTimeout, mNonInteractiveUiTimeout); } return recommendedTimeout; } /** * Gets the strokeWidth of the focus rectangle. This value can be set by * {@link AccessibilityService}. * * @return The strokeWidth of the focus rectangle in pixels. * */ public int getAccessibilityFocusStrokeWidth() { synchronized (mLock) { return mFocusStrokeWidth; } } /** * Gets the color of the focus rectangle. This value can be set by * {@link AccessibilityService}. * * @return The color of the focus rectangle. * */ public @ColorInt int getAccessibilityFocusColor() { synchronized (mLock) { return mFocusColor; } } /** * Gets accessibility interaction connection tracing enabled state. * * @hide */ public boolean isA11yInteractionConnectionTraceEnabled() { synchronized (mLock) { return ((mAccessibilityTracingState & STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED) != 0); } } /** * Gets accessibility interaction connection callback tracing enabled state. * * @hide */ public boolean isA11yInteractionConnectionCBTraceEnabled() { synchronized (mLock) { return ((mAccessibilityTracingState & STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED) != 0); } } /** * Gets accessibility interaction client tracing enabled state. * * @hide */ public boolean isA11yInteractionClientTraceEnabled() { synchronized (mLock) { return ((mAccessibilityTracingState & STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED) != 0); } } /** * Gets accessibility service tracing enabled state. * * @hide */ public boolean isA11yServiceTraceEnabled() { synchronized (mLock) { return ((mAccessibilityTracingState & STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED) != 0); } } /** * Get the preparers that are registered for an accessibility ID * * @param id The ID of interest * @return The list of preparers, or {@code null} if there are none. * * @hide */ public List* To perform established system actions, an accessibility service uses the GLOBAL_ACTION * constants in {@link android.accessibilityservice.AccessibilityService}. To provide a * customized implementation for one of these actions, the id of the registered system action * must match that of the corresponding GLOBAL_ACTION constant. For example, to register a * Back action, {@code actionId} must be * {@link android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_BACK} *
* @param action The remote action to be registered with the given actionId as system action. * @param actionId The id uniquely identify the system action. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) public void registerSystemAction(@NonNull RemoteAction action, int actionId) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return; } } try { service.registerSystemAction(action, actionId); if (DEBUG) { Log.i(LOG_TAG, "System action " + action.getTitle() + " is registered."); } } catch (RemoteException re) { Log.e(LOG_TAG, "Error registering system action " + action.getTitle() + " ", re); } } /** * Unregister a system action with the given actionId * * @param actionId The id uniquely identify the system action to be unregistered. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) public void unregisterSystemAction(int actionId) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return; } } try { service.unregisterSystemAction(actionId); if (DEBUG) { Log.i(LOG_TAG, "System action with actionId " + actionId + " is unregistered."); } } catch (RemoteException re) { Log.e(LOG_TAG, "Error unregistering system action with actionId " + actionId + " ", re); } } /** * Notifies that the accessibility button in the system's navigation area has been clicked * * @param displayId The logical display id. * @hide */ @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public void notifyAccessibilityButtonClicked(int displayId) { notifyAccessibilityButtonClicked(displayId, null); } /** * Perform the accessibility button for the given target which is assigned to the button. * * @param displayId displayId The logical display id. * @param targetName The flattened {@link ComponentName} string or the class name of a system * class implementing a supported accessibility feature, or {@code null} if there's no * specified target. * @hide */ @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public void notifyAccessibilityButtonClicked(int displayId, @Nullable String targetName) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return; } } try { service.notifyAccessibilityButtonClicked(displayId, targetName); } catch (RemoteException re) { Log.e(LOG_TAG, "Error while dispatching accessibility button click", re); } } /** * Notifies that the visibility of the accessibility button in the system's navigation area * has changed. * * @param shown {@code true} if the accessibility button is visible within the system * navigation area, {@code false} otherwise * @hide */ public void notifyAccessibilityButtonVisibilityChanged(boolean shown) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return; } } try { service.notifyAccessibilityButtonVisibilityChanged(shown); } catch (RemoteException re) { Log.e(LOG_TAG, "Error while dispatching accessibility button visibility change", re); } } /** * Set an IAccessibilityInteractionConnection to replace the actions of a picture-in-picture * window. Intended for use by the System UI only. * * @param connection The connection to handle the actions. Set to {@code null} to avoid * affecting the actions. * * @hide */ public void setPictureInPictureActionReplacingConnection( @Nullable IAccessibilityInteractionConnection connection) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return; } } try { service.setPictureInPictureActionReplacingConnection(connection); } catch (RemoteException re) { Log.e(LOG_TAG, "Error setting picture in picture action replacement", re); } } /** * Returns the list of shortcut target names currently assigned to the given shortcut. * * @param shortcutType The shortcut type. * @return The list of shortcut target names. * @hide */ @TestApi @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) @NonNull public List* Audio description, also referred to as a video description, described video, or * more precisely called a visual description, is a form of narration used to provide * information surrounding key visual elements in a media work for the benefit of * blind and visually impaired consumers. *
** The method provides the preference value to content provider apps to select the * default sound track during playing a video or movie. *
** Add listener to detect the state change via * {@link #addAudioDescriptionRequestedChangeListener} *
* @return {@code true} if the audio description is enabled, {@code false} otherwise. */ public boolean isAudioDescriptionRequested() { synchronized (mLock) { IAccessibilityManager service = getServiceLocked(); if (service == null) { return false; } return mIsAudioDescriptionByDefaultRequested; } } /** * Sets the system audio caption enabled state. * * @param isEnabled The system audio captioning enabled state. * @param userId The user Id. * @hide */ public void setSystemAudioCaptioningEnabled(boolean isEnabled, int userId) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return; } } try { service.setSystemAudioCaptioningEnabled(isEnabled, userId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** * Gets the system audio caption UI enabled state. * * @param userId The user Id. * @return the system audio caption UI enabled state. * @hide */ public boolean isSystemAudioCaptioningUiEnabled(int userId) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return false; } } try { return service.isSystemAudioCaptioningUiEnabled(userId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** * Sets the system audio caption UI enabled state. * * @param isEnabled The system audio captioning UI enabled state. * @param userId The user Id. * @hide */ public void setSystemAudioCaptioningUiEnabled(boolean isEnabled, int userId) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return; } } try { service.setSystemAudioCaptioningUiEnabled(isEnabled, userId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** * Sets the {@link AccessibilityWindowAttributes} to the window associated with the given * window id. * * @param displayId The display id of the window. * @param windowId The id of the window. * @param attributes The accessibility window attributes. * @hide */ public void setAccessibilityWindowAttributes(int displayId, int windowId, AccessibilityWindowAttributes attributes) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return; } } try { service.setAccessibilityWindowAttributes(displayId, windowId, mUserId, attributes); } catch (RemoteException re) { re.rethrowFromSystemServer(); } } /** * Registers an {@link AccessibilityDisplayProxy}, so this proxy can access UI content specific * to its display. * * @param proxy the {@link AccessibilityDisplayProxy} to register. * @return {@code true} if the proxy is successfully registered. * * @throws IllegalArgumentException if the proxy's display is not currently tracked by a11y, is * {@link android.view.Display#DEFAULT_DISPLAY}, is or lower than * {@link android.view.Display#INVALID_DISPLAY}, or is already being proxy-ed. * * @throws SecurityException if the app does not hold the * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission. * * @hide */ @SystemApi @RequiresPermission(allOf = {Manifest.permission.MANAGE_ACCESSIBILITY, Manifest.permission.CREATE_VIRTUAL_DEVICE}) public boolean registerDisplayProxy(@NonNull AccessibilityDisplayProxy proxy) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return false; } } try { return service.registerProxyForDisplay(proxy.mServiceClient, proxy.getDisplayId()); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** * Unregisters an {@link AccessibilityDisplayProxy}. * * @return {@code true} if the proxy is successfully unregistered. * * @throws SecurityException if the app does not hold the * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission. * * @hide */ @SystemApi @RequiresPermission(allOf = {Manifest.permission.MANAGE_ACCESSIBILITY, Manifest.permission.CREATE_VIRTUAL_DEVICE}) public boolean unregisterDisplayProxy(@NonNull AccessibilityDisplayProxy proxy) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return false; } } try { return service.unregisterProxyForDisplay(proxy.getDisplayId()); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** * Start sequence (infinite) type of flash notification. Use {@code Context} to retrieve the * package name as the identifier of this flash notification. * The notification can be cancelled later by calling {@link #stopFlashNotificationSequence} * with same {@code Context}. * If the binder associated with this {@link AccessibilityManager} instance dies then the * sequence will stop automatically. It is strongly recommended to call * {@link #stopFlashNotificationSequence} within a reasonable amount of time after calling * this method. * * @param context The context in which this manager operates. * @param reason The triggering reason of flash notification. * @return {@code true} if flash notification works properly. * @hide */ @FlaggedApi(Flags.FLAG_FLASH_NOTIFICATION_SYSTEM_API) @TestApi @SystemApi(client = MODULE_LIBRARIES) public boolean startFlashNotificationSequence(@NonNull Context context, @FlashNotificationReason int reason) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return false; } } try { return service.startFlashNotificationSequence(context.getOpPackageName(), reason, mBinder); } catch (RemoteException re) { Log.e(LOG_TAG, "Error while start flash notification sequence", re); return false; } } /** * Stop sequence (infinite) type of flash notification. The flash notification with the * package name retrieved from {@code Context} as identifier will be stopped if exist. * It is strongly recommended to call this method within a reasonable amount of time after * calling {@link #startFlashNotificationSequence} method. * * @param context The context in which this manager operates. * @return {@code true} if flash notification stops properly. * @hide */ @FlaggedApi(Flags.FLAG_FLASH_NOTIFICATION_SYSTEM_API) @TestApi @SystemApi(client = MODULE_LIBRARIES) public boolean stopFlashNotificationSequence(@NonNull Context context) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return false; } } try { return service.stopFlashNotificationSequence(context.getOpPackageName()); } catch (RemoteException re) { Log.e(LOG_TAG, "Error while stop flash notification sequence", re); return false; } } /** * Start event (finite) type of flash notification. * * @param context The context in which this manager operates. * @param reason The triggering reason of flash notification. * @param reasonPkg The package that trigger the flash notification. * @return {@code true} if flash notification works properly. * @hide */ public boolean startFlashNotificationEvent(@NonNull Context context, @FlashNotificationReason int reason, @Nullable String reasonPkg) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return false; } } try { return service.startFlashNotificationEvent(context.getOpPackageName(), reason, reasonPkg); } catch (RemoteException re) { Log.e(LOG_TAG, "Error while start flash notification event", re); return false; } } /** * Determines if the accessibility target is allowed. * * @param packageName The name of the application attempting to perform the operation. * @param uid The user id of the application attempting to perform the operation. * @param userId The id of the user for whom to perform the operation. * @return {@code true} the accessibility target is allowed. * @hide */ public boolean isAccessibilityTargetAllowed(String packageName, int uid, int userId) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return false; } } try { return service.isAccessibilityTargetAllowed(packageName, uid, userId); } catch (RemoteException re) { Log.e(LOG_TAG, "Error while check accessibility target status", re); return false; } } /** * Sends restricted dialog intent if the accessibility target is disallowed. * * @param packageName The name of the application attempting to perform the operation. * @param uid The user id of the application attempting to perform the operation. * @param userId The id of the user for whom to perform the operation. * @return {@code true} if the restricted dialog is shown. * @hide */ public boolean sendRestrictedDialogIntent(String packageName, int uid, int userId) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return false; } } try { return service.sendRestrictedDialogIntent(packageName, uid, userId); } catch (RemoteException re) { Log.e(LOG_TAG, "Error while show restricted dialog", re); return false; } } private IAccessibilityManager getServiceLocked() { if (mService == null) { tryConnectToServiceLocked(null); } return mService; } private void tryConnectToServiceLocked(IAccessibilityManager service) { if (service == null) { IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE); if (iBinder == null) { return; } service = IAccessibilityManager.Stub.asInterface(iBinder); } try { final long userStateAndRelevantEvents = service.addClient(mClient, mUserId); setStateLocked(IntPair.first(userStateAndRelevantEvents)); mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents); updateUiTimeout(service.getRecommendedTimeoutMillis()); updateFocusAppearanceLocked(service.getFocusStrokeWidth(), service.getFocusColor()); mService = service; } catch (RemoteException re) { Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); } } /** * Notifies the registered {@link AccessibilityStateChangeListener}s. * * Note: this method notifies only the listeners of this single instance. * AccessibilityManagerService is responsible for calling this method on all of * its AccessibilityManager clients in order to notify all listeners. * @hide */ public void notifyAccessibilityStateChanged() { final boolean isEnabled; final ArrayMap* Used by callers outside of the AccessibilityManagerService process which need * this information, like {@link android.view.accessibility.DirectAccessibilityConnection}. *
* * @return The transformation spec * @hide */ public IAccessibilityManager.WindowTransformationSpec getWindowTransformationSpec( int windowId) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return null; } } try { return service.getWindowTransformationSpec(windowId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** * Attaches a {@link android.view.SurfaceControl} containing an accessibility overlay to the * specified display. * * @hide */ @RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW) public void attachAccessibilityOverlayToDisplay( int displayId, @NonNull SurfaceControl surfaceControl) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); if (service == null) { return; } } try { service.attachAccessibilityOverlayToDisplay( displayId, surfaceControl); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** * Notifies that the current a11y tiles in QuickSettings Panel has been changed * * @param userId The userId of the user attempts to change the qs panel. * @param tileComponentNames A list of Accessibility feature's TileServices' component names * and the a11y platform tiles' component names * @hide */ @RequiresPermission(Manifest.permission.STATUS_BAR_SERVICE) public void notifyQuickSettingsTilesChanged( @UserIdInt int userId, List