/* * 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.accessibilityservice; import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import android.accessibilityservice.GestureDescription.MotionEventGenerator; import android.annotation.CallbackExecutor; import android.annotation.CheckResult; 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.TestApi; import android.app.Service; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.content.pm.ParceledListSlice; import android.graphics.Bitmap; import android.graphics.ColorSpace; import android.graphics.ParcelableColorSpace; import android.graphics.Region; import android.hardware.HardwareBuffer; import android.hardware.display.DisplayManager; 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.RemoteCallback; import android.os.RemoteException; import android.os.SystemClock; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.WindowManager; import android.view.WindowManagerImpl; import android.view.accessibility.AccessibilityCache; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.AccessibilityWindowInfo; import android.view.inputmethod.EditorInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.CancellationGroup; import com.android.internal.inputmethod.IAccessibilityInputMethodSession; import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback; import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; import com.android.internal.inputmethod.RemoteAccessibilityInputConnection; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.List; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.function.IntConsumer; /** * Accessibility services should only be used to assist users with disabilities in using * Android devices and apps. They run in the background and receive callbacks by the system * when {@link AccessibilityEvent}s are fired. Such events denote some state transition * in the user interface, for example, the focus has changed, a button has been clicked, * etc. Such a service can optionally request the capability for querying the content * of the active window. Development of an accessibility service requires extending this * class and implementing its abstract methods. * *
For more information about creating AccessibilityServices, read the * Accessibility * developer guide.
** The lifecycle of an accessibility service is managed exclusively by the system and * follows the established service life cycle. Starting an accessibility service is triggered * exclusively by the user explicitly turning the service on in device settings. After the system * binds to a service, it calls {@link AccessibilityService#onServiceConnected()}. This method can * be overridden by clients that want to perform post binding setup. *
** An accessibility service stops either when the user turns it off in device settings or when * it calls {@link AccessibilityService#disableSelf()}. *
** An accessibility is declared as any other service in an AndroidManifest.xml, but it * must do two things: *
<service android:name=".MyAccessibilityService" * android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> * <intent-filter> * <action android:name="android.accessibilityservice.AccessibilityService" /> * </intent-filter> * . . . * </service>*
* An accessibility service can be configured to receive specific types of accessibility events, * listen only to specific packages, get events from each type only once in a given time frame, * retrieve window content, specify a settings activity, etc. *
** There are two approaches for configuring an accessibility service: *
*<service android:name=".MyAccessibilityService"> * <intent-filter> * <action android:name="android.accessibilityservice.AccessibilityService" /> * </intent-filter> * <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" /> * </service>*
* Note: This approach enables setting all properties. *
*
* For more details refer to {@link #SERVICE_META_DATA} and
* <{@link android.R.styleable#AccessibilityService accessibility-service}>
.
*
* Note: This approach enables setting only dynamically configurable properties: * {@link AccessibilityServiceInfo#eventTypes}, * {@link AccessibilityServiceInfo#feedbackType}, * {@link AccessibilityServiceInfo#flags}, * {@link AccessibilityServiceInfo#notificationTimeout}, * {@link AccessibilityServiceInfo#packageNames} *
** For more details refer to {@link AccessibilityServiceInfo}. *
** A service can specify in its declaration that it can retrieve window * content which is represented as a tree of {@link AccessibilityWindowInfo} and * {@link AccessibilityNodeInfo} objects. Note that * declaring this capability requires that the service declares its configuration via * an XML resource referenced by {@link #SERVICE_META_DATA}. *
** Window content may be retrieved with * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()}, * {@link AccessibilityService#findFocus(int)}, * {@link AccessibilityService#getWindows()}, or * {@link AccessibilityService#getRootInActiveWindow()}. *
** Note An accessibility service may have requested to be notified for * a subset of the event types, and thus be unaware when the node hierarchy has changed. It is also * possible for a node to contain outdated information because the window content may change at any * time. *
*Accessibility services can draw overlays on top of existing screen contents. * Accessibility overlays can be used to visually highlight items on the screen * e.g. indicate the current item with accessibility focus. * Overlays can also offer the user a way to interact with the service directly and quickly * customize the service's behavior.
*Accessibility overlays can be attached to a particular window or to the display itself. * Attaching an overlay to a window allows the overly to move, grow and shrink as the window does. * The overlay will maintain the same relative position within the window bounds as the window * moves. The overlay will also maintain the same relative position within the window bounds if * the window is resized. * To attach an overlay to a window, use {@link #attachAccessibilityOverlayToWindow}. * Attaching an overlay to the display means that the overlay is independent of the active * windows on that display. * To attach an overlay to a display, use {@link #attachAccessibilityOverlayToDisplay}.
*When positioning an overlay that is attached to a window, the service must use window * coordinates. In order to position an overlay on top of an existing UI element it is necessary * to know the bounds of that element in window coordinates. To find the bounds in window * coordinates of an element, find the corresponding {@link AccessibilityNodeInfo} as discussed * above and call {@link AccessibilityNodeInfo#getBoundsInWindow}.
** All accessibility services are notified of all events they have requested, regardless of their * feedback type. *
** Note: The event notification timeout is useful to avoid propagating * events to the client too frequently since this is accomplished via an expensive * interprocess call. One can think of the timeout as a criteria to determine when * event generation has settled down.
*<{@link android.R.styleable#AccessibilityService accessibility-service}>
* tag. This is a sample XML file configuring an accessibility service:
* <accessibility-service * android:accessibilityEventTypes="typeViewClicked|typeViewFocused" * android:packageNames="foo.bar, foo.baz" * android:accessibilityFeedbackType="feedbackSpoken" * android:notificationTimeout="100" * android:accessibilityFlags="flagDefault" * android:settingsActivity="foo.bar.TestBackActivity" * android:canRetrieveWindowContent="true" * android:canRequestTouchExplorationMode="true" * . . . * />*/ public static final String SERVICE_META_DATA = "android.accessibilityservice"; /** * Action to go back. */ public static final int GLOBAL_ACTION_BACK = 1; /** * Action to go home. */ public static final int GLOBAL_ACTION_HOME = 2; /** * Action to toggle showing the overview of recent apps. Will fail on platforms that don't * show recent apps. */ public static final int GLOBAL_ACTION_RECENTS = 3; /** * Action to open the notifications. */ public static final int GLOBAL_ACTION_NOTIFICATIONS = 4; /** * Action to open the quick settings. */ public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; /** * Action to open the power long-press dialog. */ public static final int GLOBAL_ACTION_POWER_DIALOG = 6; /** * Action to toggle docking the current app's window. *
* Note: It is effective only if it appears in {@link #getSystemActions()}.
*/
public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7;
/**
* Action to lock the screen
*/
public static final int GLOBAL_ACTION_LOCK_SCREEN = 8;
/**
* Action to take a screenshot
*/
public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9;
/**
* Action to send the KEYCODE_HEADSETHOOK KeyEvent, which is used to answer and hang up calls
* and play and stop media. Calling takes priority. If there is an incoming call,
* this action can be used to answer that call, and if there is an ongoing call, to hang up on
* that call.
*/
public static final int GLOBAL_ACTION_KEYCODE_HEADSETHOOK = 10;
/**
* Action to trigger the Accessibility Button
*/
public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON = 11;
/**
* Action to bring up the Accessibility Button's chooser menu
*/
public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 12;
/**
* Action to trigger the Accessibility Shortcut. This shortcut has a hardware trigger and can
* be activated by holding down the two volume keys.
*/
public static final int GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT = 13;
/**
* Action to show Launcher's all apps.
*/
public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14;
/**
* Action to dismiss the notification shade
*/
public static final int GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE = 15;
/**
* Action to trigger dpad up keyevent.
*/
public static final int GLOBAL_ACTION_DPAD_UP = 16;
/**
* Action to trigger dpad down keyevent.
*/
public static final int GLOBAL_ACTION_DPAD_DOWN = 17;
/**
* Action to trigger dpad left keyevent.
*/
public static final int GLOBAL_ACTION_DPAD_LEFT = 18;
/**
* Action to trigger dpad right keyevent.
*/
public static final int GLOBAL_ACTION_DPAD_RIGHT = 19;
/**
* Action to trigger dpad center keyevent.
*/
public static final int GLOBAL_ACTION_DPAD_CENTER = 20;
private static final String LOG_TAG = "AccessibilityService";
/**
* Interface used by IAccessibilityServiceClientWrapper to call the service from its main
* thread.
* @hide
*/
public interface Callbacks {
void onAccessibilityEvent(AccessibilityEvent event);
void onInterrupt();
void onServiceConnected();
void init(int connectionId, IBinder windowToken);
/** The detected gesture information for different displays */
boolean onGesture(AccessibilityGestureEvent gestureInfo);
boolean onKeyEvent(KeyEvent event);
/** Magnification changed callbacks for different displays */
void onMagnificationChanged(int displayId, @NonNull Region region,
MagnificationConfig config);
/** Callbacks for receiving motion events. */
void onMotionEvent(MotionEvent event);
/** Callback for tuch state changes. */
void onTouchStateChanged(int displayId, int state);
void onSoftKeyboardShowModeChanged(int showMode);
void onPerformGestureResult(int sequence, boolean completedSuccessfully);
void onFingerprintCapturingGesturesChanged(boolean active);
void onFingerprintGesture(int gesture);
/** Accessbility button clicked callbacks for different displays */
void onAccessibilityButtonClicked(int displayId);
void onAccessibilityButtonAvailabilityChanged(boolean available);
/** This is called when the system action list is changed. */
void onSystemActionsChanged();
/** This is called when an app requests ime sessions or when the service is enabled. */
void createImeSession(IAccessibilityInputMethodSessionCallback callback);
/** This is called when an app starts input or when the service is enabled. */
void startInput(@Nullable RemoteAccessibilityInputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting);
}
/**
* Annotations for Soft Keyboard show modes so tools can catch invalid show modes.
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "SHOW_MODE_" }, value = {
SHOW_MODE_AUTO,
SHOW_MODE_HIDDEN,
SHOW_MODE_IGNORE_HARD_KEYBOARD
})
public @interface SoftKeyboardShowMode {}
/**
* Allow the system to control when the soft keyboard is shown.
* @see SoftKeyboardController
*/
public static final int SHOW_MODE_AUTO = 0;
/**
* Never show the soft keyboard.
* @see SoftKeyboardController
*/
public static final int SHOW_MODE_HIDDEN = 1;
/**
* Allow the soft keyboard to be shown, even if a hard keyboard is connected
* @see SoftKeyboardController
*/
public static final int SHOW_MODE_IGNORE_HARD_KEYBOARD = 2;
/**
* Mask used to cover the show modes supported in public API
* @hide
*/
public static final int SHOW_MODE_MASK = 0x03;
/**
* Bit used to hold the old value of the hard IME setting to restore when a service is shut
* down.
* @hide
*/
public static final int SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE = 0x20000000;
/**
* Bit for show mode setting to indicate that the user has overridden the hard keyboard
* behavior.
* @hide
*/
public static final int SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN = 0x40000000;
/**
* Annotations for error codes of taking screenshot.
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "TAKE_SCREENSHOT_" }, value = {
ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR,
ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS,
ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT,
ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
ERROR_TAKE_SCREENSHOT_INVALID_WINDOW
})
public @interface ScreenshotErrorCode {}
/**
* The status of taking screenshot is success.
* @hide
*/
public static final int TAKE_SCREENSHOT_SUCCESS = 0;
/**
* The status of taking screenshot is failure and the reason is internal error.
*/
public static final int ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR = 1;
/**
* The status of taking screenshot is failure and the reason is no accessibility access.
*/
public static final int ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS = 2;
/**
* The status of taking screenshot is failure and the reason is that too little time has
* elapsed since the last screenshot.
*/
public static final int ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT = 3;
/**
* The status of taking screenshot is failure and the reason is invalid display Id.
*/
public static final int ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY = 4;
/**
* The status of taking screenshot is failure and the reason is invalid accessibility window Id.
*/
public static final int ERROR_TAKE_SCREENSHOT_INVALID_WINDOW = 5;
/**
* The status of taking screenshot is failure and the reason is the window contains secure
* content.
* @see WindowManager.LayoutParams#FLAG_SECURE
*/
public static final int ERROR_TAKE_SCREENSHOT_SECURE_WINDOW = 6;
/**
* The interval time of calling
* {@link AccessibilityService#takeScreenshot(int, Executor, Consumer)} API.
* @hide
*/
@TestApi
public static final int ACCESSIBILITY_TAKE_SCREENSHOT_REQUEST_INTERVAL_TIMES_MS = 333;
/** @hide */
public static final String KEY_ACCESSIBILITY_SCREENSHOT_STATUS =
"screenshot_status";
/** @hide */
public static final String KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER =
"screenshot_hardwareBuffer";
/** @hide */
public static final String KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE =
"screenshot_colorSpace";
/** @hide */
public static final String KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP =
"screenshot_timestamp";
/**
* Annotations for result codes of attaching accessibility overlays.
*
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
@IntDef(
prefix = {"OVERLAY_RESULT_"},
value = {
OVERLAY_RESULT_SUCCESS,
OVERLAY_RESULT_INTERNAL_ERROR,
OVERLAY_RESULT_INVALID,
})
public @interface AttachOverlayResult {}
/** Result code indicating the overlay was successfully attached. */
@FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
public static final int OVERLAY_RESULT_SUCCESS = 0;
/**
* Result code indicating the overlay could not be attached due to an internal
* error and not
* because of problems with the input.
*/
@FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
public static final int OVERLAY_RESULT_INTERNAL_ERROR = 1;
/**
* Result code indicating the overlay could not be attached because the
* specified display or
* window id was invalid.
*/
@FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
public static final int OVERLAY_RESULT_INVALID = 2;
private int mConnectionId = AccessibilityInteractionClient.NO_ID;
@UnsupportedAppUsage
private AccessibilityServiceInfo mInfo;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private IBinder mWindowToken;
private WindowManager mWindowManager;
/** List of magnification controllers, mapping from displayId -> MagnificationController. */
private final SparseArray
* Note: To receive gestures an accessibility service must
* request that the device is in touch exploration mode by setting the
* {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE}
* flag.
*
* Note: The default implementation calls {@link #onGesture(int)} when the
* touch screen is default display.
*
* @param gestureEvent The information of gesture.
*
* @return Whether the gesture was handled.
*
*/
public boolean onGesture(@NonNull AccessibilityGestureEvent gestureEvent) {
if (gestureEvent.getDisplayId() == Display.DEFAULT_DISPLAY) {
onGesture(gestureEvent.getGestureId());
}
return false;
}
/**
* Callback that allows an accessibility service to observe the key events
* before they are passed to the rest of the system. This means that the events
* are first delivered here before they are passed to the device policy, the
* input method, or applications.
*
* Note: It is important that key events are handled in such
* a way that the event stream that would be passed to the rest of the system
* is well-formed. For example, handling the down event but not the up event
* and vice versa would generate an inconsistent event stream.
*
* Note: The key events delivered in this method are copies
* and modifying them will have no effect on the events that will be passed
* to the system. This method is intended to perform purely filtering
* functionality.
*
*
* @param event The event to be processed. This event is owned by the caller and cannot be used
* after this method returns. Services wishing to use the event after this method returns should
* make a copy.
* @return If true then the event will be consumed and not delivered to
* applications, otherwise it will be delivered as usual.
*/
protected boolean onKeyEvent(KeyEvent event) {
return false;
}
/**
* Callback that allows an accessibility service to observe generic {@link MotionEvent}s.
*
* Prefer {@link TouchInteractionController} to observe and control touchscreen events,
* including touch gestures. If this or any enabled service is using
* {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} then
* {@link #onMotionEvent} will not receive touchscreen events.
*
* Note: The service must first request to listen to events using
* {@link AccessibilityServiceInfo#setMotionEventSources}.
* {@link MotionEvent}s from sources in {@link AccessibilityServiceInfo#getMotionEventSources()}
* are not sent to the rest of the system. To stop listening to events from a given source, call
* {@link AccessibilityServiceInfo#setMotionEventSources} with that source removed.
*
* Note: In order to access the windows your service has
* to declare the capability to retrieve window content by setting the
* {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
* property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
* Also the service has to opt-in to retrieve the interactive windows by
* setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
* flag.
*
* Note: In order to access the windows your service has
* to declare the capability to retrieve window content by setting the
* {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
* property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
* Also the service has to opt-in to retrieve the interactive windows by
* setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
* flag.
*
* Note: In order to access the root node your service has
* to declare the capability to retrieve window content by setting the
* {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
* property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
*
* Note: In order to control magnification, your service
* must declare the capability by setting the
* {@link android.R.styleable#AccessibilityService_canControlMagnification}
* property in its meta-data. For more information, see
* {@link #SERVICE_META_DATA}.
*
* @return the magnification controller
*/
@NonNull
public final MagnificationController getMagnificationController() {
return getMagnificationController(Display.DEFAULT_DISPLAY);
}
/**
* Returns the magnification controller of specified logical display, which may be used to
* query and modify the state of display magnification.
*
* Note: In order to control magnification, your service
* must declare the capability by setting the
* {@link android.R.styleable#AccessibilityService_canControlMagnification}
* property in its meta-data. For more information, see
* {@link #SERVICE_META_DATA}.
*
* @param displayId The logic display id, use {@link Display#DEFAULT_DISPLAY} for
* default display.
* @return the magnification controller
*
* @hide
*/
@NonNull
public final MagnificationController getMagnificationController(int displayId) {
synchronized (mLock) {
MagnificationController controller = mMagnificationControllers.get(displayId);
if (controller == null) {
controller = new MagnificationController(this, mLock, displayId);
mMagnificationControllers.put(displayId, controller);
}
return controller;
}
}
/**
* Get the controller for fingerprint gestures. This feature requires {@link
* AccessibilityServiceInfo#CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES}.
*
*Note: The service must be connected before this method is called.
*
* @return The controller for fingerprint gestures, or {@code null} if gestures are unavailable.
*/
@RequiresPermission(android.Manifest.permission.USE_FINGERPRINT)
public final @NonNull FingerprintGestureController getFingerprintGestureController() {
if (mFingerprintGestureController == null) {
mFingerprintGestureController = new FingerprintGestureController(
AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId));
}
return mFingerprintGestureController;
}
/**
* Dispatch a gesture to the touch screen. Any gestures currently in progress, whether from
* the user, this service, or another service, will be cancelled.
*
* The gesture will be dispatched as if it were performed directly on the screen by a user, so
* the events may be affected by features such as magnification and explore by touch.
*
* Note: In order to dispatch gestures, your service
* must declare the capability by setting the
* {@link android.R.styleable#AccessibilityService_canPerformGestures}
* property in its meta-data. For more information, see
* {@link #SERVICE_META_DATA}.
* Since many apps do not appropriately support {@link AccessibilityAction#ACTION_CLICK},
* if this action fails on an element that should be clickable, a service that is not a screen
* reader may send a tap directly to the element as a fallback. The example below
* demonstrates this fallback using the gesture dispatch APIs:
*
* For gestures to be smooth they should line up with the refresh rate of the display.
* On versions of Android before R, the sample time was fixed to 100ms.
*/
private int calculateGestureSampleTimeMs(int displayId) {
if (getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.Q) {
return 100;
}
Display display = getSystemService(DisplayManager.class).getDisplay(
displayId);
if (display == null) {
return 100;
}
int msPerSecond = 1000;
int sampleTimeMs = (int) (msPerSecond / display.getRefreshRate());
if (sampleTimeMs < 1) {
// Should be impossible, but do not return 0.
return 100;
}
return sampleTimeMs;
}
void onPerformGestureResult(int sequence, final boolean completedSuccessfully) {
if (mGestureStatusCallbackInfos == null) {
return;
}
GestureResultCallbackInfo callbackInfo;
synchronized (mLock) {
callbackInfo = mGestureStatusCallbackInfos.get(sequence);
mGestureStatusCallbackInfos.remove(sequence);
}
final GestureResultCallbackInfo finalCallbackInfo = callbackInfo;
if ((callbackInfo != null) && (callbackInfo.gestureDescription != null)
&& (callbackInfo.callback != null)) {
if (callbackInfo.handler != null) {
callbackInfo.handler.post(new Runnable() {
@Override
public void run() {
if (completedSuccessfully) {
finalCallbackInfo.callback
.onCompleted(finalCallbackInfo.gestureDescription);
} else {
finalCallbackInfo.callback
.onCancelled(finalCallbackInfo.gestureDescription);
}
}
});
return;
}
if (completedSuccessfully) {
callbackInfo.callback.onCompleted(callbackInfo.gestureDescription);
} else {
callbackInfo.callback.onCancelled(callbackInfo.gestureDescription);
}
}
}
private void onMagnificationChanged(int displayId, @NonNull Region region,
MagnificationConfig config) {
MagnificationController controller;
synchronized (mLock) {
controller = mMagnificationControllers.get(displayId);
}
if (controller != null) {
controller.dispatchMagnificationChanged(region, config);
}
}
/**
* Callback for fingerprint gesture handling
* @param active If gesture detection is active
*/
private void onFingerprintCapturingGesturesChanged(boolean active) {
getFingerprintGestureController().onGestureDetectionActiveChanged(active);
}
/**
* Callback for fingerprint gesture handling
* @param gesture The identifier for the gesture performed
*/
private void onFingerprintGesture(int gesture) {
getFingerprintGestureController().onGesture(gesture);
}
/** @hide */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public int getConnectionId() {
return mConnectionId;
}
/**
* Used to control and query the state of display magnification.
*/
public static final class MagnificationController {
private final AccessibilityService mService;
private final int mDisplayId;
/**
* Map of listeners to their handlers. Lazily created when adding the
* first magnification listener.
*/
private ArrayMap
* Note: If the service is not yet connected (e.g.
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will
* return null.
*
* Note: If the service is not yet connected (e.g.
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will
* return a default value of {@code 1.0f}.
*
* Note: This legacy API gets the scale of full-screen
* magnification. To get the scale of the current controlling magnifier,
* use {@link #getMagnificationConfig} instead.
*
* Note: If the service is not yet connected (e.g.
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will
* return a default value of {@code 0.0f}.
*
* Note: This legacy API gets the center position of full-screen
* magnification. To get the magnification center of the current controlling magnifier,
* use {@link #getMagnificationConfig} instead.
*
* Note: If the service is not yet connected (e.g.
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will
* return a default value of {@code 0.0f}.
*
* Note: This legacy API gets the center position of full-screen
* magnification. To get the magnification center of the current controlling magnifier,
* use {@link #getMagnificationConfig} instead.
*
* The returned region will be empty if magnification is not active. Magnification is active
* if magnification gestures are enabled or if a service is running that can control
* magnification.
*
* Note: If the service is not yet connected (e.g.
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will
* return an empty region.
*
* Note: This legacy API gets the magnification region of full-screen
* magnification. To get the magnification region of the current controlling magnifier,
* use {@link #getCurrentMagnificationRegion()} instead.
*
* If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN},
* the returned region will be empty if the magnification is
* not active. And the magnification is active if magnification gestures are enabled
* or if a service is running that can control magnification.
*
* If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW},
* the returned region will be empty if the magnification is not activated.
*
* Note: If the service is not yet connected (e.g.
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will
* return an empty region.
*
* Note: If the service is not yet connected (e.g.
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will have
* no effect and return {@code false}.
*
* Note: This legacy API reset full-screen magnification.
* To reset the current controlling magnifier, use
* {@link #resetCurrentMagnification(boolean)} ()} instead.
*
* Note: If the service is not yet connected (e.g.
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will have
* no effect and return {@code false}.
*
* Note: If the service is not yet connected (e.g.
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will have
* no effect and return {@code false}.
*
* Note: If the service is not yet connected (e.g.
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will have
* no effect and return {@code false}.
*
* Note: This legacy API sets the scale of full-screen
* magnification. To set the scale of the specified magnifier,
* use {@link #setMagnificationConfig} instead.
*
* Note: If the service is not yet connected (e.g.
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will have
* no effect and return {@code false}.
*
* Note: This legacy API sets the center of full-screen
* magnification. To set the center of the specified magnifier,
* use {@link #setMagnificationConfig} instead.
*
* Note: This legacy callback notifies only full-screen
* magnification change.
*
* Note: This method can be overridden to listen to the
* magnification changes of all magnification modes then the legacy callback
* would not receive the notifications.
* Skipping calling super when overriding this method results in
* {@link #onMagnificationChanged(MagnificationController, Region, float, float, float)}
* not getting called.
*
* Accessibility services may request to override the decisions normally made about whether or
* not the soft keyboard is shown.
*
* If multiple services make conflicting requests, the last request is honored. A service may
* register a listener to find out if the mode has changed under it.
*
* If the user takes action to override the behavior behavior requested by an accessibility
* service, the user's request takes precendence, the show mode will be reset to
* {@link AccessibilityService#SHOW_MODE_AUTO}, and services will no longer be able to control
* that aspect of the soft keyboard's behavior.
*
* Note: Because soft keyboards are independent apps, the framework does not have total control
* over their behavior. They may choose to show themselves, or not, without regard to requests
* made here. So the framework will make a best effort to deliver the behavior requested, but
* cannot guarantee success.
*
* @see AccessibilityService#SHOW_MODE_AUTO
* @see AccessibilityService#SHOW_MODE_HIDDEN
* @see AccessibilityService#SHOW_MODE_IGNORE_HARD_KEYBOARD
*/
public static final class SoftKeyboardController {
private final AccessibilityService mService;
/**
* Map of listeners to their handlers. Lazily created when adding the first
* soft keyboard change listener.
*/
private ArrayMap
* Note: If the service is not yet connected (e.g.
* {@link AccessibilityService#onServiceConnected()} has not yet been called) or the
* service has been disconnected, this method will have no effect and return {@code false}.
*
* @param showMode the new show mode for the soft keyboard
* @return {@code true} on success
*
* @see AccessibilityService#SHOW_MODE_AUTO
* @see AccessibilityService#SHOW_MODE_HIDDEN
* @see AccessibilityService#SHOW_MODE_IGNORE_HARD_KEYBOARD
*/
public boolean setShowMode(@SoftKeyboardShowMode int showMode) {
final IAccessibilityServiceConnection connection =
AccessibilityInteractionClient.getInstance(mService).getConnection(
mService.mConnectionId);
if (connection != null) {
try {
return connection.setSoftKeyboardShowMode(showMode);
} catch (RemoteException re) {
Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re);
re.rethrowFromSystemServer();
}
}
return false;
}
/**
* Listener for changes in the soft keyboard show mode.
*/
public interface OnShowModeChangedListener {
/**
* Called when the soft keyboard behavior changes. The default show mode is
* {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
* focused. An AccessibilityService can also request the show mode
* {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
*
* @param controller the soft keyboard controller
* @param showMode the current soft keyboard show mode
*/
void onShowModeChanged(@NonNull SoftKeyboardController controller,
@SoftKeyboardShowMode int showMode);
}
/**
* Switches the current IME for the user for whom the service is enabled. The change will
* persist until the current IME is explicitly changed again, and may persist beyond the
* life cycle of the requesting service.
*
* @param imeId The ID of the input method to make current. This IME must be installed and
* enabled.
* @return {@code true} if the current input method was successfully switched to the input
* method by {@code imeId},
* {@code false} if the input method specified is not installed, not enabled, or
* otherwise not available to become the current IME
*
* @see android.view.inputmethod.InputMethodInfo#getId()
*/
public boolean switchToInputMethod(@NonNull String imeId) {
final IAccessibilityServiceConnection connection =
AccessibilityInteractionClient.getInstance(mService).getConnection(
mService.mConnectionId);
if (connection != null) {
try {
return connection.switchToInputMethod(imeId);
} catch (RemoteException re) {
throw new RuntimeException(re);
}
}
return false;
}
/**
* Enable or disable the specified IME for the user for whom the service is activated. The
* IME needs to be in the same package as the service and needs to be allowed by device
* policy, if there is one. The change will persist until the specified IME is next
* explicitly enabled or disabled by whatever means, such as user choice, and may persist
* beyond the life cycle of the requesting service.
*
* @param imeId The ID of the input method to enable or disable. This IME must be installed.
* @param enabled {@code true} if the input method associated with {@code imeId} should be
* enabled.
* @return status code for the result of enabling/disabling the input method associated
* with {@code imeId}.
* @throws SecurityException if the input method is not in the same package as the service.
*
* @see android.view.inputmethod.InputMethodInfo#getId()
*/
@CheckResult
@EnableImeResult
public int setInputMethodEnabled(@NonNull String imeId, boolean enabled)
throws SecurityException {
final IAccessibilityServiceConnection connection =
AccessibilityInteractionClient.getInstance(mService).getConnection(
mService.mConnectionId);
if (connection != null) {
try {
return connection.setInputMethodEnabled(imeId, enabled);
} catch (RemoteException re) {
throw new RuntimeException(re);
}
}
return ENABLE_IME_FAIL_UNKNOWN;
}
}
/**
* Returns the controller for the accessibility button within the system's navigation area.
* This instance may be used to query the accessibility button's state and register listeners
* for interactions with and state changes for the accessibility button when
* {@link AccessibilityServiceInfo#FLAG_REQUEST_ACCESSIBILITY_BUTTON} is set.
*
* Note: Not all devices are capable of displaying the accessibility button
* within a navigation area, and as such, use of this class should be considered only as an
* optional feature or shortcut on supported device implementations.
*
* Note: Not all devices are capable of displaying the accessibility button
* within a navigation area, and as such, use of this class should be considered only as an
* optional feature or shortcut on supported device implementations.
* If {@code enabled}, enable the cache and prefetching. Otherwise, disable the cache
* and prefetching.
* Note: By default the cache is enabled.
* @param enabled whether to enable or disable the cache.
* @return {@code true} if the cache and connection are not null, so the cache status is set.
*/
public boolean setCacheEnabled(boolean enabled) {
AccessibilityCache cache =
AccessibilityInteractionClient.getCache(mConnectionId);
if (cache == null) {
return false;
}
final IAccessibilityServiceConnection connection =
AccessibilityInteractionClient.getConnection(mConnectionId);
if (connection == null) {
return false;
}
try {
connection.setCacheEnabled(enabled);
cache.setEnabled(enabled);
return true;
} catch (RemoteException re) {
Log.w(LOG_TAG, "Error while setting status of cache", re);
re.rethrowFromSystemServer();
}
return false;
}
/** Invalidates {@code node} and its subtree in the cache.
* @param node the node to invalidate.
* @return {@code true} if the subtree rooted at {@code node} was invalidated.
*/
public boolean clearCachedSubtree(@NonNull AccessibilityNodeInfo node) {
AccessibilityCache cache =
AccessibilityInteractionClient.getCache(mConnectionId);
if (cache == null) {
return false;
}
return cache.clearSubTree(node);
}
/** Clears the cache.
* @return {@code true} if the cache was cleared
*/
public boolean clearCache() {
AccessibilityCache cache =
AccessibilityInteractionClient.getCache(mConnectionId);
if (cache == null) {
return false;
}
cache.clear();
return true;
}
/** Checks if {@code node} is in the cache.
* @param node the node to check.
* @return {@code true} if {@code node} is in the cache.
*/
public boolean isNodeInCache(@NonNull AccessibilityNodeInfo node) {
AccessibilityCache cache =
AccessibilityInteractionClient.getCache(mConnectionId);
if (cache == null) {
return false;
}
return cache.isNodeInCache(node);
}
/** Returns {@code true} if the cache is enabled. */
public boolean isCacheEnabled() {
AccessibilityCache cache =
AccessibilityInteractionClient.getCache(mConnectionId);
if (cache == null) {
return false;
}
return cache.isEnabled();
}
/** This is called when the system action list is changed. */
public void onSystemActionsChanged() {
}
/**
* Returns a list of system actions available in the system right now.
*
* System actions that correspond to the global action constants will have matching action IDs.
* For example, an with id {@link #GLOBAL_ACTION_BACK} will perform the back action.
*
* These actions should be called by {@link #performGlobalAction}.
*
* Note: The global action ids themselves give no information about the current availability
* of their corresponding actions. To determine if a global action is available, use
* {@link #getSystemActions()}
*
* @param action The action to perform.
* @return Whether the action was successfully performed.
*
* Perform actions using ids like the id constants referenced below:
* @see #GLOBAL_ACTION_BACK
* @see #GLOBAL_ACTION_HOME
* @see #GLOBAL_ACTION_NOTIFICATIONS
* @see #GLOBAL_ACTION_RECENTS
* @see #GLOBAL_ACTION_DPAD_UP
* @see #GLOBAL_ACTION_DPAD_DOWN
* @see #GLOBAL_ACTION_DPAD_LEFT
* @see #GLOBAL_ACTION_DPAD_RIGHT
* @see #GLOBAL_ACTION_DPAD_CENTER
*/
public final boolean performGlobalAction(int action) {
IAccessibilityServiceConnection connection =
AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId);
if (connection != null) {
try {
return connection.performGlobalAction(action);
} catch (RemoteException re) {
Log.w(LOG_TAG, "Error while calling performGlobalAction", re);
re.rethrowFromSystemServer();
}
}
return false;
}
/**
* Find the view that has the specified focus type. The search is performed
* across all windows.
*
* Note: In order to access the windows your service has
* to declare the capability to retrieve window content by setting the
* {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
* property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
* Also the service has to opt-in to retrieve the interactive windows by
* setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
* flag. Otherwise, the search will be performed only in the active window.
*
* Note: If the view with {@link AccessibilityNodeInfo#FOCUS_INPUT}
* is on an embedded view hierarchy which is embedded in a {@link android.view.SurfaceView} via
* {@link android.view.SurfaceView#setChildSurfacePackage}, there is a limitation that this API
* won't be able to find the node for the view. It's because views don't know about
* the embedded hierarchies. Instead, you could traverse all the nodes to find the
* focus.
*
* Note: You can call this method any time but the info will be picked up after
* the system has bound to this service and when this method is called thereafter.
*
* @param info The info.
*/
public final void setServiceInfo(AccessibilityServiceInfo info) {
mInfo = info;
updateInputMethod(info);
mMotionEventSources = info.getMotionEventSources();
sendServiceInfo();
}
/**
* Sets the {@link AccessibilityServiceInfo} for this service if the latter is
* properly set and there is an {@link IAccessibilityServiceConnection} to the
* AccessibilityManagerService.
*/
private void sendServiceInfo() {
IAccessibilityServiceConnection connection =
AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId);
if (mInfo != null && connection != null) {
try {
connection.setServiceInfo(mInfo);
mInfo = null;
AccessibilityInteractionClient.getInstance(this).clearCache(mConnectionId);
} catch (RemoteException re) {
Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
re.rethrowFromSystemServer();
}
}
}
@Override
public Object getSystemService(@ServiceName @NonNull String name) {
if (getBaseContext() == null) {
throw new IllegalStateException(
"System services not available to Activities before onCreate()");
}
// Guarantee that we always return the same window manager instance.
if (WINDOW_SERVICE.equals(name)) {
if (mWindowManager == null) {
mWindowManager = (WindowManager) getBaseContext().getSystemService(name);
final WindowManagerImpl wm = (WindowManagerImpl) mWindowManager;
// Set e default token obtained from the connection to ensure client could use
// accessibility overlay.
wm.setDefaultToken(mWindowToken);
}
return mWindowManager;
}
return super.getSystemService(name);
}
/**
* Takes a screenshot of the specified display and returns it via an
* {@link AccessibilityService.ScreenshotResult}. You can use {@link Bitmap#wrapHardwareBuffer}
* to construct the bitmap from the ScreenshotResult's payload.
*
* Note: In order to take screenshot your service has
* to declare the capability to take screenshot by setting the
* {@link android.R.styleable#AccessibilityService_canTakeScreenshot}
* property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
*
* Note: In order to take screenshots your service has
* to declare the capability to take screenshot by setting the
* {@link android.R.styleable#AccessibilityService_canTakeScreenshot}
* property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
*
* Both this method and {@link #takeScreenshot} can be used for machine learning-based visual
* screen understanding. Use
* Note: This setting persists until this or another active
* AccessibilityService changes it or the device reboots.
* This field must be set and cleared only from the binder thread(s), where the system
* guarantees that {@link #bindInput()},
* {@link #startInput(IRemoteAccessibilityInputConnection, EditorInfo, boolean)},
* and {@link #unbindInput()} are called with the same order as the original calls
* in {@link com.android.server.inputmethod.InputMethodManagerService}.
* See {@link IBinder#FLAG_ONEWAY} for detailed semantics.
* Note: The application should call {@link HardwareBuffer#close()} when
* the buffer is no longer needed to free the underlying resources.
* Generally speaking, an accessibility overlay will be a {@link android.view.View}. To embed
* the View into a {@link android.view.SurfaceControl}, create a {@link
* android.view.SurfaceControlViewHost} and attach the View using {@link
* android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by calling
* To remove this overlay and free the associated resources, use If the specified overlay has already been attached to the specified display this method
* does nothing. If the specified overlay has already been attached to a previous display this
* function will transfer the overlay to the new display. Services can attach multiple overlays.
* Use Generally speaking, an accessibility overlay will be a {@link android.view.View}. To embed
* the View into a {@link android.view.SurfaceControl}, create a {@link
* android.view.SurfaceControlViewHost} and attach the View using {@link
* android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by calling
* To remove this overlay and free the associated resources, use If the specified overlay has already been attached to the specified display this method
* does nothing. If the specified overlay has already been attached to a previous display this
* function will transfer the overlay to the new display. Services can attach multiple overlays.
* Use Generally speaking, an accessibility overlay will be a {@link android.view.View}. To embed
* the View into a {@link android.view.SurfaceControl}, create a {@link
* android.view.SurfaceControlViewHost} and attach the View using {@link
* android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by calling
* To remove this overlay and free the associated resources, use If the specified overlay has already been attached to the specified window this method
* does nothing. If the specified overlay has already been attached to a previous window this
* function will transfer the overlay to the new window. Services can attach multiple overlays.
* Use Generally speaking, an accessibility overlay will be a {@link android.view.View}. To embed
* the View into a {@link android.view.SurfaceControl}, create a {@link
* android.view.SurfaceControlViewHost} and attach the View using {@link
* android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by calling
* To remove this overlay and free the associated resources, use If the specified overlay has already been attached to the specified window this method
* does nothing. If the specified overlay has already been attached to a previous window this
* function will transfer the overlay to the new window. Services can attach multiple overlays.
* Use > getWindowsOnAllDisplays() {
return AccessibilityInteractionClient.getInstance(this).getWindowsOnAllDisplays(
mConnectionId);
}
/**
* Gets the root node in the currently active window if this service
* can retrieve window content. The active window is the one that the user
* is currently touching or the window with input focus, if the user is not
* touching any window. It could be from any logical display.
*
* @param gesture The gesture to dispatch
* @param callback The object to call back when the status of the gesture is known. If
* {@code null}, no status is reported.
* @param handler The handler on which to call back the {@code callback} object. If
* {@code null}, the object is called back on the service's main thread.
*
* @return {@code true} if the gesture is dispatched, {@code false} if not.
*/
public final boolean dispatchGesture(@NonNull GestureDescription gesture,
@Nullable GestureResultCallback callback,
@Nullable Handler handler) {
final IAccessibilityServiceConnection connection =
AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId);
if (connection == null) {
return false;
}
int sampleTimeMs = calculateGestureSampleTimeMs(gesture.getDisplayId());
List
* private void tap(PointF point) {
* StrokeDescription tap = new StrokeDescription(path(point), 0,
* ViewConfiguration.getTapTimeout());
* GestureDescription.Builder builder = new GestureDescription.Builder();
* builder.addStroke(tap);
* dispatchGesture(builder.build(), null, null);
* }
*
* takeScreenshotOfWindow
if your target window might be
* visually underneath an accessibility overlay (from your or another accessibility service) in
* order to capture the window contents without the screenshot being covered by the overlay
* contents drawn on the screen.
* 0
disables animations entirely. When
* animations are disabled services receive window change events more quickly which can reduce
* the potential by confusion by reducing the time during which windows are in transition.
*
* @see AccessibilityEvent#TYPE_WINDOWS_CHANGED
* @see AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED
* @see android.provider.Settings.Global#WINDOW_ANIMATION_SCALE
* @see android.provider.Settings.Global#TRANSITION_ANIMATION_SCALE
* @see android.provider.Settings.Global#ANIMATOR_DURATION_SCALE
* @param scale The scaling factor for all animations.
*/
public void setAnimationScale(float scale) {
final IAccessibilityServiceConnection connection =
AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId);
if (connection != null) {
try {
connection.setAnimationScale(scale);
} catch (RemoteException re) {
throw new RuntimeException(re);
}
}
}
private static class AccessibilityContext extends ContextWrapper {
private final int mConnectionId;
private AccessibilityContext(Context base, int connectionId) {
super(base);
mConnectionId = connectionId;
setDefaultTokenInternal(this, getDisplayId());
}
@NonNull
@Override
public Context createDisplayContext(Display display) {
return new AccessibilityContext(super.createDisplayContext(display), mConnectionId);
}
@NonNull
@Override
public Context createWindowContext(int type, @Nullable Bundle options) {
final Context context = super.createWindowContext(type, options);
if (type != TYPE_ACCESSIBILITY_OVERLAY) {
return context;
}
return new AccessibilityContext(context, mConnectionId);
}
@NonNull
@Override
public Context createWindowContext(@NonNull Display display, int type,
@Nullable Bundle options) {
final Context context = super.createWindowContext(display, type, options);
if (type != TYPE_ACCESSIBILITY_OVERLAY) {
return context;
}
return new AccessibilityContext(context, mConnectionId);
}
private void setDefaultTokenInternal(Context context, int displayId) {
final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(
WINDOW_SERVICE);
final IAccessibilityServiceConnection connection =
AccessibilityInteractionClient.getConnection(mConnectionId);
IBinder token = null;
if (connection != null) {
try {
token = connection.getOverlayWindowToken(displayId);
} catch (RemoteException re) {
Log.w(LOG_TAG, "Failed to get window token", re);
re.rethrowFromSystemServer();
}
wm.setDefaultToken(token);
}
}
}
/**
* Returns the touch interaction controller for the specified logical display, which may be used
* to detect gestures and otherwise control touch interactions. If
* {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is disabled the
* controller's methods will have no effect.
*
* @param displayId The logical display id, use {@link Display#DEFAULT_DISPLAY} for default
* display.
* @return the TouchExploration controller
*/
@NonNull
public final TouchInteractionController getTouchInteractionController(int displayId) {
synchronized (mLock) {
TouchInteractionController controller = mTouchInteractionControllers.get(displayId);
if (controller == null) {
controller = new TouchInteractionController(this, mLock, displayId);
mTouchInteractionControllers.put(displayId, controller);
}
return controller;
}
}
void sendMotionEventToCallback(MotionEvent event) {
boolean sendingTouchEventToTouchInteractionController = false;
if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
TouchInteractionController controller;
synchronized (mLock) {
int displayId = event.getDisplayId();
controller = mTouchInteractionControllers.get(displayId);
}
if (controller != null) {
sendingTouchEventToTouchInteractionController = true;
controller.onMotionEvent(event);
}
}
final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
if ((mMotionEventSources & eventSourceWithoutClass) != 0
&& !sendingTouchEventToTouchInteractionController) {
onMotionEvent(event);
}
}
void onTouchStateChanged(int displayId, int state) {
TouchInteractionController controller;
synchronized (mLock) {
controller = mTouchInteractionControllers.get(displayId);
}
if (controller != null) {
controller.onStateChanged(state);
}
}
/**
* Attaches a {@link android.view.SurfaceControl} containing an accessibility overlay to the
* specified display. This type of overlay should be used for content that does not need to
* track the location and size of Views in the currently active app e.g. service configuration
* or general service UI.
*
* viewHost.getSurfacePackage().getSurfaceControl()
.
*
*
* new SurfaceControl.Transaction().reparent(sc, null).apply();
.
*
* new SurfaceControl.Transaction().setLayer(sc, layer).apply();
. to
* coordinate the order of the overlays on screen.
*
* @param displayId the display to which the SurfaceControl should be attached.
* @param sc the SurfaceControl containing the overlay content
*
*/
public void attachAccessibilityOverlayToDisplay(int displayId, @NonNull SurfaceControl sc) {
Preconditions.checkNotNull(sc, "SurfaceControl cannot be null");
AccessibilityInteractionClient.getInstance(this)
.attachAccessibilityOverlayToDisplay(mConnectionId, displayId, sc, null, null);
}
/**
* Attaches a {@link android.view.SurfaceControl} containing an accessibility overlay to the
* specified display. This type of overlay should be used for content that does not need to
* track the location and size of Views in the currently active app e.g. service configuration
* or general service UI.
*
* viewHost.getSurfacePackage().getSurfaceControl()
.
*
*
* new SurfaceControl.Transaction().reparent(sc, null).apply();
.
*
* new SurfaceControl.Transaction().setLayer(sc, layer).apply();
. to
* coordinate the order of the overlays on screen.
*
* @param displayId the display to which the SurfaceControl should be attached.
* @param sc the SurfaceControl containing the overlay content
* @param executor Executor on which to run the callback.
* @param callback The callback invoked when attaching the overlay has succeeded or failed. The
* callback is a {@link java.util.function.IntConsumer} of the result status code.
* @see #OVERLAY_RESULT_SUCCESS
* @see #OVERLAY_RESULT_INVALID
* @see #OVERLAY_RESULT_INTERNAL_ERROR
*/
@FlaggedApi(android.view.accessibility.Flags.FLAG_A11Y_OVERLAY_CALLBACKS)
public final void attachAccessibilityOverlayToDisplay(
int displayId,
@NonNull SurfaceControl sc,
@NonNull @CallbackExecutor Executor executor,
@NonNull IntConsumer callback) {
Preconditions.checkNotNull(sc, "SurfaceControl cannot be null");
AccessibilityInteractionClient.getInstance(this)
.attachAccessibilityOverlayToDisplay(
mConnectionId, displayId, sc, executor, callback);
}
/**
* Attaches an accessibility overlay {@link android.view.SurfaceControl} to the specified
* window. This method should be used when you want the overlay to move and resize as the parent
* window moves and resizes.
*
* viewHost.getSurfacePackage().getSurfaceControl()
.
*
*
* new SurfaceControl.Transaction().reparent(sc, null).apply();
.
*
* new SurfaceControl.Transaction().setLayer(sc, layer).apply();
. to
* coordinate the order of the overlays on screen.
*
* @param accessibilityWindowId The window id, from {@link AccessibilityWindowInfo#getId()}.
* @param sc the SurfaceControl containing the overlay content
*
*/
public void attachAccessibilityOverlayToWindow(
int accessibilityWindowId, @NonNull SurfaceControl sc) {
Preconditions.checkNotNull(sc, "SurfaceControl cannot be null");
AccessibilityInteractionClient.getInstance(this)
.attachAccessibilityOverlayToWindow(
mConnectionId, accessibilityWindowId, sc, null, null);
}
/**
* Attaches an accessibility overlay {@link android.view.SurfaceControl} to the specified
* window. This method should be used when you want the overlay to move and resize as the parent
* window moves and resizes.
*
* viewHost.getSurfacePackage().getSurfaceControl()
.
*
*
* new SurfaceControl.Transaction().reparent(sc, null).apply();
.
*
* new SurfaceControl.Transaction().setLayer(sc, layer).apply();
. to
* coordinate the order of the overlays on screen.
*
* @param accessibilityWindowId The window id, from {@link AccessibilityWindowInfo#getId()}.
* @param sc the SurfaceControl containing the overlay content
* @param executor Executor on which to run the callback.
* @param callback The callback invoked when attaching the overlay has succeeded or failed. The
* callback is a {@link java.util.function.IntConsumer} of the result status code.
* @see #OVERLAY_RESULT_SUCCESS
* @see #OVERLAY_RESULT_INVALID
* @see #OVERLAY_RESULT_INTERNAL_ERROR
*/
@FlaggedApi(android.view.accessibility.Flags.FLAG_A11Y_OVERLAY_CALLBACKS)
public final void attachAccessibilityOverlayToWindow(
int accessibilityWindowId,
@NonNull SurfaceControl sc,
@NonNull @CallbackExecutor Executor executor,
@NonNull IntConsumer callback) {
Preconditions.checkNotNull(sc, "SurfaceControl cannot be null");
AccessibilityInteractionClient.getInstance(this)
.attachAccessibilityOverlayToWindow(
mConnectionId, accessibilityWindowId, sc, executor, callback);
}
/**
* Returns the {@link BrailleDisplayController} which may be used to communicate with
* refreshable Braille displays that provide USB or Bluetooth Braille display HID support.
*/
@FlaggedApi(android.view.accessibility.Flags.FLAG_BRAILLE_DISPLAY_HID)
@NonNull
public final BrailleDisplayController getBrailleDisplayController() {
BrailleDisplayController.checkApiFlagIsEnabled();
synchronized (mLock) {
if (mBrailleDisplayController == null) {
mBrailleDisplayController = new BrailleDisplayControllerImpl(this, mLock);
}
return mBrailleDisplayController;
}
}
}