blob: 1d73843260cbb8d8e49bcc160986832c80919fdd [file] [log] [blame]
/*
** Copyright 2017, 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 com.android.server.accessibility;
import static android.accessibilityservice.AccessibilityService.ACCESSIBILITY_TAKE_SCREENSHOT_REQUEST_INTERVAL_TIMES_MS;
import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE;
import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER;
import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_STATUS;
import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP;
import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CONNECTION;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.accessibility.AccessibilityInteractionClient.CALL_STACK;
import static android.view.accessibility.AccessibilityInteractionClient.IGNORE_CALL_STACK;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
import static com.android.window.flags.Flags.deleteCaptureDisplay;
import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.accessibilityservice.MagnificationConfig;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.graphics.ParcelableColorSpace;
import android.graphics.Region;
import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
import android.view.accessibility.AccessibilityCache;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.inputmethod.EditorInfo;
import android.window.ScreenCapture;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
import com.android.server.accessibility.magnification.MagnificationProcessor;
import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* This class represents an accessibility client - either an AccessibilityService or a UiAutomation.
* It is responsible for behavior common to both types of clients.
*/
@SuppressWarnings("MissingPermissionAnnotation")
abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServiceConnection.Stub
implements ServiceConnection, IBinder.DeathRecipient, KeyEventDispatcher.KeyEventFilter,
FingerprintGestureDispatcher.FingerprintGestureClient {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "AbstractAccessibilityServiceConnection";
private static final String TRACE_SVC_CONN = LOG_TAG + ".IAccessibilityServiceConnection";
private static final String TRACE_SVC_CLIENT = LOG_TAG + ".IAccessibilityServiceClient";
private static final String TRACE_WM = "WindowManagerInternal";
private static final int WAIT_WINDOWS_TIMEOUT_MILLIS = 5000;
/** Display type for displays associated with the default user of the device. */
public static final int DISPLAY_TYPE_DEFAULT = 1 << 0;
/** Display type for displays associated with an AccessibilityDisplayProxy user. */
public static final int DISPLAY_TYPE_PROXY = 1 << 1;
protected static final String TAKE_SCREENSHOT = "takeScreenshot";
protected final Context mContext;
protected final SystemSupport mSystemSupport;
protected final WindowManagerInternal mWindowManagerService;
private final SystemActionPerformer mSystemActionPerformer;
final AccessibilityWindowManager mA11yWindowManager;
private final DisplayManager mDisplayManager;
private final PowerManager mPowerManager;
private final IPlatformCompat mIPlatformCompat;
private final Handler mMainHandler;
// Handler for scheduling method invocations on the main thread.
public final InvocationHandler mInvocationHandler;
final int mId;
protected final AccessibilityServiceInfo mAccessibilityServiceInfo;
// Lock must match the one used by AccessibilityManagerService
protected final Object mLock;
protected final AccessibilitySecurityPolicy mSecurityPolicy;
protected final AccessibilityTrace mTrace;
// The attribution tag set by the service that is bound to this instance
protected String mAttributionTag;
protected int mDisplayTypes = DISPLAY_TYPE_DEFAULT;
// The service that's bound to this instance. Whenever this value is non-null, this
// object is registered as a death recipient
IBinder mService;
IAccessibilityServiceClient mServiceInterface;
int mEventTypes;
int mFeedbackType;
Set<String> mPackageNames = new HashSet<>();
boolean mIsDefault;
boolean mRequestTouchExplorationMode;
private boolean mServiceHandlesDoubleTap;
private boolean mRequestMultiFingerGestures;
private boolean mRequestTwoFingerPassthrough;
private boolean mSendMotionEvents;
private SparseArray<Boolean> mServiceDetectsGestures = new SparseArray<>(0);
boolean mRequestFilterKeyEvents;
boolean mRetrieveInteractiveWindows;
boolean mCaptureFingerprintGestures;
boolean mRequestAccessibilityButton;
boolean mReceivedAccessibilityButtonCallbackSinceBind;
boolean mLastAccessibilityButtonCallbackState;
boolean mRequestImeApis;
int mFetchFlags;
long mNotificationTimeout;
final ComponentName mComponentName;
int mGenericMotionEventSources;
int mObservedMotionEventSources;
// the events pending events to be dispatched to this service
final SparseArray<AccessibilityEvent> mPendingEvents = new SparseArray<>();
/** Whether this service relies on its {@link AccessibilityCache} being up to date */
boolean mUsesAccessibilityCache = false;
// Handler only for dispatching accessibility events since we use event
// types as message types allowing us to remove messages per event type.
public Handler mEventDispatchHandler;
final SparseArray<IBinder> mOverlayWindowTokens = new SparseArray();
// All the embedded accessibility overlays that have been added by this service.
private List<SurfaceControl> mOverlays = new ArrayList<>();
/** The timestamp of requesting to take screenshot in milliseconds */
private long mRequestTakeScreenshotTimestampMs;
/**
* The timestamps of requesting to take a window screenshot in milliseconds,
* mapping from accessibility window id -> timestamp.
*/
private SparseArray<Long> mRequestTakeScreenshotOfWindowTimestampMs = new SparseArray<>();
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "DISPLAY_TYPE_" }, value = {
DISPLAY_TYPE_DEFAULT,
DISPLAY_TYPE_PROXY
})
public @interface DisplayTypes {}
public interface SystemSupport {
/**
* @return The current dispatcher for key events
*/
@NonNull KeyEventDispatcher getKeyEventDispatcher();
/**
* @param displayId The display id.
* @return The current injector of motion events used on the display, if one exists.
*/
@Nullable MotionEventInjector getMotionEventInjectorForDisplayLocked(int displayId);
/**
* @return The current dispatcher for fingerprint gestures, if one exists
*/
@Nullable FingerprintGestureDispatcher getFingerprintGestureDispatcher();
/**
* @return The magnification processor
*/
@NonNull
MagnificationProcessor getMagnificationProcessor();
/**
* Called back to notify system that the client has changed
* @param serviceInfoChanged True if the service's AccessibilityServiceInfo changed.
*/
void onClientChangeLocked(boolean serviceInfoChanged);
/**
* Called back to notify the system the proxy client for a device has changed.
*
* Changes include if the proxy is unregistered, if its service info list has changed, or if
* its focus appearance has changed.
*/
void onProxyChanged(int deviceId);
int getCurrentUserIdLocked();
Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec(
int windowId);
boolean isAccessibilityButtonShown();
/**
* Persists the component names in the specified setting in a
* colon separated fashion.
*
* @param settingName The setting name.
* @param componentNames The component names.
* @param userId The user id to persist the setting for.
*/
void persistComponentNamesToSettingLocked(String settingName,
Set<ComponentName> componentNames, int userId);
/* This is exactly PendingIntent.getActivity, separated out for testability */
PendingIntent getPendingIntentActivity(Context context, int requestCode, Intent intent,
int flags);
void setGestureDetectionPassthroughRegion(int displayId, Region region);
void setTouchExplorationPassthroughRegion(int displayId, Region region);
void setServiceDetectsGesturesEnabled(int displayId, boolean mode);
void requestTouchExploration(int displayId);
void requestDragging(int displayId, int pointerId);
void requestDelegating(int displayId);
void onDoubleTap(int displayId);
void onDoubleTapAndHold(int displayId);
void requestImeLocked(AbstractAccessibilityServiceConnection connection);
void unbindImeLocked(AbstractAccessibilityServiceConnection connection);
void attachAccessibilityOverlayToDisplay(
int interactionId,
int displayId,
SurfaceControl sc,
IAccessibilityInteractionConnectionCallback callback);
}
public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName,
AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport,
AccessibilityTrace trace, WindowManagerInternal windowManagerInternal,
SystemActionPerformer systemActionPerfomer,
AccessibilityWindowManager a11yWindowManager) {
mContext = context;
mWindowManagerService = windowManagerInternal;
mId = id;
mComponentName = componentName;
mAccessibilityServiceInfo = accessibilityServiceInfo;
mLock = lock;
mSecurityPolicy = securityPolicy;
mSystemActionPerformer = systemActionPerfomer;
mSystemSupport = systemSupport;
mTrace = trace;
mMainHandler = mainHandler;
mInvocationHandler = new InvocationHandler(mainHandler.getLooper());
mA11yWindowManager = a11yWindowManager;
mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mIPlatformCompat = IPlatformCompat.Stub.asInterface(
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
mEventDispatchHandler = new Handler(mainHandler.getLooper()) {
@Override
public void handleMessage(Message message) {
final int eventType = message.what;
AccessibilityEvent event = (AccessibilityEvent) message.obj;
boolean serviceWantsEvent = message.arg1 != 0;
notifyAccessibilityEventInternal(eventType, event, serviceWantsEvent);
}
};
setDynamicallyConfigurableProperties(accessibilityServiceInfo);
}
@Override
public boolean onKeyEvent(KeyEvent keyEvent, int sequenceNumber) {
if (!mRequestFilterKeyEvents || (mServiceInterface == null)) {
return false;
}
if((mAccessibilityServiceInfo.getCapabilities()
& AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) == 0) {
return false;
}
if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
return false;
}
try {
if (svcClientTracingEnabled()) {
logTraceSvcClient("onKeyEvent", keyEvent + ", " + sequenceNumber);
}
mServiceInterface.onKeyEvent(keyEvent, sequenceNumber);
} catch (RemoteException e) {
return false;
}
return true;
}
public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
mEventTypes = info.eventTypes;
mFeedbackType = info.feedbackType;
String[] packageNames = info.packageNames;
mPackageNames.clear();
if (packageNames != null) {
mPackageNames.addAll(Arrays.asList(packageNames));
}
mNotificationTimeout = info.notificationTimeout;
mIsDefault = (info.flags & DEFAULT) != 0;
mGenericMotionEventSources = info.getMotionEventSources();
if (android.view.accessibility.Flags.motionEventObserving()) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING)
== PackageManager.PERMISSION_GRANTED) {
mObservedMotionEventSources = info.getObservedMotionEventSources();
} else {
Slog.e(
LOG_TAG,
"Observing motion events requires"
+ " android.Manifest.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING.");
mObservedMotionEventSources = 0;
}
}
if (supportsFlagForNotImportantViews(info)) {
if ((info.flags & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0) {
mFetchFlags |=
AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS;
} else {
mFetchFlags &=
~AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS;
}
}
if ((info.flags & AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS) != 0) {
mFetchFlags |= AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS;
} else {
mFetchFlags &= ~AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS;
}
if (mAccessibilityServiceInfo.isAccessibilityTool()) {
mFetchFlags |= AccessibilityNodeInfo.FLAG_SERVICE_IS_ACCESSIBILITY_TOOL;
} else {
mFetchFlags &= ~AccessibilityNodeInfo.FLAG_SERVICE_IS_ACCESSIBILITY_TOOL;
}
mRequestTouchExplorationMode = (info.flags
& AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
mServiceHandlesDoubleTap = (info.flags
& AccessibilityServiceInfo.FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0;
mRequestMultiFingerGestures = (info.flags
& AccessibilityServiceInfo.FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0;
mRequestTwoFingerPassthrough =
(info.flags & AccessibilityServiceInfo.FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0;
mSendMotionEvents =
(info.flags & AccessibilityServiceInfo.FLAG_SEND_MOTION_EVENTS) != 0;
mRequestFilterKeyEvents =
(info.flags & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
mRetrieveInteractiveWindows = (info.flags
& AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
mCaptureFingerprintGestures = (info.flags
& AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES) != 0;
mRequestAccessibilityButton = (info.flags
& AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
mRequestImeApis = (info.flags
& AccessibilityServiceInfo.FLAG_INPUT_METHOD_EDITOR) != 0;
}
protected boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
return info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion
>= Build.VERSION_CODES.JELLY_BEAN;
}
public boolean canReceiveEventsLocked() {
return (mEventTypes != 0 && mService != null);
}
@Override
public void setOnKeyEventResult(boolean handled, int sequence) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("setOnKeyEventResult", "handled=" + handled + ";sequence=" + sequence);
}
final long identity = Binder.clearCallingIdentity();
try {
mSystemSupport.getKeyEventDispatcher().setOnKeyEventResult(this, handled, sequence);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public AccessibilityServiceInfo getServiceInfo() {
if (svcConnTracingEnabled()) {
logTraceSvcConn("getServiceInfo", "");
}
synchronized (mLock) {
return mAccessibilityServiceInfo;
}
}
public int getCapabilities() {
return mAccessibilityServiceInfo.getCapabilities();
}
int getRelevantEventTypes() {
return (mUsesAccessibilityCache ? AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK
: AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) | mEventTypes;
}
@Override
public void setServiceInfo(AccessibilityServiceInfo info) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("setServiceInfo", "info=" + info);
}
if (!info.isWithinParcelableSize()) {
throw new IllegalStateException(
"Cannot update service info: size is larger than safe parcelable limits.");
}
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
// If the XML manifest had data to configure the service its info
// should be already set. In such a case update only the dynamically
// configurable properties.
boolean oldRequestIme = mRequestImeApis;
AccessibilityServiceInfo oldInfo = mAccessibilityServiceInfo;
if (oldInfo != null) {
oldInfo.updateDynamicallyConfigurableProperties(mIPlatformCompat, info);
setDynamicallyConfigurableProperties(oldInfo);
} else {
setDynamicallyConfigurableProperties(info);
}
mSystemSupport.onClientChangeLocked(true);
if (!oldRequestIme && mRequestImeApis) {
mSystemSupport.requestImeLocked(this);
} else if (oldRequestIme && !mRequestImeApis) {
mSystemSupport.unbindImeLocked(this);
}
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void setInstalledAndEnabledServices(List<AccessibilityServiceInfo> infos) {
return;
}
@Override
public List<AccessibilityServiceInfo> getInstalledAndEnabledServices() {
return null;
}
@Override
public void setAttributionTag(String attributionTag) {
mAttributionTag = attributionTag;
}
String getAttributionTag() {
return mAttributionTag;
}
protected abstract boolean hasRightsToCurrentUserLocked();
@Nullable
@Override
public AccessibilityWindowInfo.WindowListSparseArray getWindows() {
if (svcConnTracingEnabled()) {
logTraceSvcConn("getWindows", "");
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return null;
}
final boolean permissionGranted =
mSecurityPolicy.canRetrieveWindowsLocked(this);
if (!permissionGranted) {
return null;
}
if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
return null;
}
final long identity = Binder.clearCallingIdentity();
try {
final AccessibilityWindowInfo.WindowListSparseArray allWindows =
new AccessibilityWindowInfo.WindowListSparseArray();
final ArrayList<Integer> displayList = mA11yWindowManager.getDisplayListLocked(
mDisplayTypes);
final int displayListCounts = displayList.size();
if (displayListCounts > 0) {
for (int i = 0; i < displayListCounts; i++) {
final int displayId = displayList.get(i);
ensureWindowsAvailableTimedLocked(displayId);
final List<AccessibilityWindowInfo> windowList = getWindowsByDisplayLocked(
displayId);
if (windowList != null) {
allWindows.put(displayId, windowList);
}
}
}
return allWindows;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
protected void setDisplayTypes(@DisplayTypes int displayTypes) {
mDisplayTypes = displayTypes;
}
@Override
public AccessibilityWindowInfo getWindow(int windowId) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("getWindow", "windowId=" + windowId);
}
synchronized (mLock) {
int displayId = Display.INVALID_DISPLAY;
if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
displayId = mA11yWindowManager.getDisplayIdByUserIdAndWindowIdLocked(
mSystemSupport.getCurrentUserIdLocked(), windowId);
}
ensureWindowsAvailableTimedLocked(displayId);
if (!hasRightsToCurrentUserLocked()) {
return null;
}
final boolean permissionGranted =
mSecurityPolicy.canRetrieveWindowsLocked(this);
if (!permissionGranted) {
return null;
}
if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
return null;
}
final long identity = Binder.clearCallingIdentity();
try {
AccessibilityWindowInfo window =
mA11yWindowManager.findA11yWindowInfoByIdLocked(windowId);
if (window != null) {
AccessibilityWindowInfo windowClone = AccessibilityWindowInfo.obtain(window);
windowClone.setConnectionId(mId);
return windowClone;
}
return null;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
@Override
public String[] findAccessibilityNodeInfosByViewId(int accessibilityWindowId,
long accessibilityNodeId, String viewIdResName, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
if (svcConnTracingEnabled()) {
logTraceSvcConn("findAccessibilityNodeInfosByViewId",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ accessibilityNodeId + ";viewIdResName=" + viewIdResName + ";interactionId="
+ interactionId + ";callback=" + callback + ";interrogatingTid="
+ interrogatingTid);
}
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
Region partialInteractiveRegion = Region.obtain();
synchronized (mLock) {
mUsesAccessibilityCache = true;
if (!hasRightsToCurrentUserLocked()) {
return null;
}
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(
mSystemSupport.getCurrentUserIdLocked(), this, resolvedWindowId);
if (!permissionGranted) {
return null;
} else {
connection = mA11yWindowManager.getConnectionLocked(
mSystemSupport.getCurrentUserIdLocked(), resolvedWindowId);
if (connection == null) {
return null;
}
}
if (!mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(
resolvedWindowId, partialInteractiveRegion)) {
partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
}
final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
final float[] transformMatrix = transformMatrixAndSpec.first;
final MagnificationSpec spec = transformMatrixAndSpec.second;
if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
return null;
}
final int interrogatingPid = Binder.getCallingPid();
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
if (intConnTracingEnabled()) {
logTraceIntConn("findAccessibilityNodeInfosByViewId",
accessibilityNodeId + ";" + viewIdResName + ";" + partialInteractiveRegion + ";"
+ interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
+ ";" + interrogatingTid + ";" + spec + ";" + Arrays.toString(transformMatrix));
}
try {
connection.getRemote().findAccessibilityNodeInfosByViewId(accessibilityNodeId,
viewIdResName, partialInteractiveRegion, interactionId, callback, mFetchFlags,
interrogatingPid, interrogatingTid, spec, transformMatrix);
return mSecurityPolicy.computeValidReportedPackages(
connection.getPackageName(), connection.getUid());
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error findAccessibilityNodeInfoByViewId().");
}
} finally {
Binder.restoreCallingIdentity(identityToken);
// Recycle if passed to another process.
if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) {
partialInteractiveRegion.recycle();
}
}
return null;
}
@Override
public String[] findAccessibilityNodeInfosByText(int accessibilityWindowId,
long accessibilityNodeId, String text, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
if (svcConnTracingEnabled()) {
logTraceSvcConn("findAccessibilityNodeInfosByText",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ accessibilityNodeId + ";text=" + text + ";interactionId=" + interactionId
+ ";callback=" + callback + ";interrogatingTid=" + interrogatingTid);
}
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
Region partialInteractiveRegion = Region.obtain();
synchronized (mLock) {
mUsesAccessibilityCache = true;
if (!hasRightsToCurrentUserLocked()) {
return null;
}
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(
mSystemSupport.getCurrentUserIdLocked(), this, resolvedWindowId);
if (!permissionGranted) {
return null;
} else {
connection = mA11yWindowManager.getConnectionLocked(
mSystemSupport.getCurrentUserIdLocked(), resolvedWindowId);
if (connection == null) {
return null;
}
}
if (!mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(
resolvedWindowId, partialInteractiveRegion)) {
partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
}
final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
final float[] transformMatrix = transformMatrixAndSpec.first;
final MagnificationSpec spec = transformMatrixAndSpec.second;
if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
return null;
}
final int interrogatingPid = Binder.getCallingPid();
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
if (intConnTracingEnabled()) {
logTraceIntConn("findAccessibilityNodeInfosByText",
accessibilityNodeId + ";" + text + ";" + partialInteractiveRegion + ";"
+ interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
+ ";" + interrogatingTid + ";" + spec + ";" + Arrays.toString(transformMatrix));
}
try {
connection.getRemote().findAccessibilityNodeInfosByText(accessibilityNodeId,
text, partialInteractiveRegion, interactionId, callback, mFetchFlags,
interrogatingPid, interrogatingTid, spec, transformMatrix);
return mSecurityPolicy.computeValidReportedPackages(
connection.getPackageName(), connection.getUid());
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()");
}
} finally {
Binder.restoreCallingIdentity(identityToken);
// Recycle if passed to another process.
if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) {
partialInteractiveRegion.recycle();
}
}
return null;
}
@Override
public String[] findAccessibilityNodeInfoByAccessibilityId(
int accessibilityWindowId, long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
long interrogatingTid, Bundle arguments) throws RemoteException {
if (svcConnTracingEnabled()) {
logTraceSvcConn("findAccessibilityNodeInfoByAccessibilityId",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ accessibilityNodeId + ";interactionId=" + interactionId + ";callback="
+ callback + ";flags=" + flags + ";interrogatingTid=" + interrogatingTid
+ ";arguments=" + arguments);
}
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
Region partialInteractiveRegion = Region.obtain();
synchronized (mLock) {
mUsesAccessibilityCache = true;
if (!hasRightsToCurrentUserLocked()) {
return null;
}
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(
mSystemSupport.getCurrentUserIdLocked(), this, resolvedWindowId);
if (!permissionGranted) {
return null;
} else {
connection = mA11yWindowManager.getConnectionLocked(
mSystemSupport.getCurrentUserIdLocked(), resolvedWindowId);
if (connection == null) {
return null;
}
}
if (!mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(
resolvedWindowId, partialInteractiveRegion)) {
partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
}
final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
final float[] transformMatrix = transformMatrixAndSpec.first;
final MagnificationSpec spec = transformMatrixAndSpec.second;
if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
return null;
}
final int interrogatingPid = Binder.getCallingPid();
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
if (intConnTracingEnabled()) {
logTraceIntConn("findAccessibilityNodeInfoByAccessibilityId",
accessibilityNodeId + ";" + partialInteractiveRegion + ";" + interactionId + ";"
+ callback + ";" + (mFetchFlags | flags) + ";" + interrogatingPid + ";"
+ interrogatingTid + ";" + spec + ";" + Arrays.toString(transformMatrix)
+ ";" + arguments);
}
try {
connection.getRemote().findAccessibilityNodeInfoByAccessibilityId(
accessibilityNodeId, partialInteractiveRegion, interactionId, callback,
mFetchFlags | flags, interrogatingPid, interrogatingTid, spec, transformMatrix,
arguments);
return mSecurityPolicy.computeValidReportedPackages(
connection.getPackageName(), connection.getUid());
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()");
}
} finally {
Binder.restoreCallingIdentity(identityToken);
// Recycle if passed to another process.
if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) {
partialInteractiveRegion.recycle();
}
}
return null;
}
@Override
public String[] findFocus(int accessibilityWindowId, long accessibilityNodeId,
int focusType, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
if (svcConnTracingEnabled()) {
logTraceSvcConn("findFocus",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ accessibilityNodeId + ";focusType=" + focusType + ";interactionId="
+ interactionId + ";callback=" + callback + ";interrogatingTid="
+ interrogatingTid);
}
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
Region partialInteractiveRegion = Region.obtain();
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return null;
}
resolvedWindowId = resolveAccessibilityWindowIdForFindFocusLocked(
accessibilityWindowId, focusType);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(
mSystemSupport.getCurrentUserIdLocked(), this, resolvedWindowId);
if (!permissionGranted) {
return null;
} else {
connection = mA11yWindowManager.getConnectionLocked(
mSystemSupport.getCurrentUserIdLocked(), resolvedWindowId);
if (connection == null) {
return null;
}
}
if (!mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(
resolvedWindowId, partialInteractiveRegion)) {
partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
}
final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
final float[] transformMatrix = transformMatrixAndSpec.first;
final MagnificationSpec spec = transformMatrixAndSpec.second;
if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
return null;
}
final int interrogatingPid = Binder.getCallingPid();
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
if (intConnTracingEnabled()) {
logTraceIntConn("findFocus",
accessibilityNodeId + ";" + focusType + ";" + partialInteractiveRegion + ";"
+ interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
+ ";" + interrogatingTid + ";" + spec + ";"
+ Arrays.toString(transformMatrix));
}
try {
connection.getRemote().findFocus(accessibilityNodeId, focusType,
partialInteractiveRegion, interactionId, callback, mFetchFlags,
interrogatingPid, interrogatingTid, spec, transformMatrix);
return mSecurityPolicy.computeValidReportedPackages(
connection.getPackageName(), connection.getUid());
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling findFocus()");
}
} finally {
Binder.restoreCallingIdentity(identityToken);
// Recycle if passed to another process.
if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) {
partialInteractiveRegion.recycle();
}
}
return null;
}
@Override
public String[] focusSearch(int accessibilityWindowId, long accessibilityNodeId,
int direction, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
if (svcConnTracingEnabled()) {
logTraceSvcConn("focusSearch",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ accessibilityNodeId + ";direction=" + direction + ";interactionId="
+ interactionId + ";callback=" + callback + ";interrogatingTid="
+ interrogatingTid);
}
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
Region partialInteractiveRegion = Region.obtain();
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return null;
}
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(
mSystemSupport.getCurrentUserIdLocked(), this, resolvedWindowId);
if (!permissionGranted) {
return null;
} else {
connection = mA11yWindowManager.getConnectionLocked(
mSystemSupport.getCurrentUserIdLocked(), resolvedWindowId);
if (connection == null) {
return null;
}
}
if (!mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(
resolvedWindowId, partialInteractiveRegion)) {
partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
}
final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
final float[] transformMatrix = transformMatrixAndSpec.first;
final MagnificationSpec spec = transformMatrixAndSpec.second;
if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
return null;
}
final int interrogatingPid = Binder.getCallingPid();
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
if (intConnTracingEnabled()) {
logTraceIntConn("focusSearch",
accessibilityNodeId + ";" + direction + ";" + partialInteractiveRegion
+ ";" + interactionId + ";" + callback + ";" + mFetchFlags + ";"
+ interrogatingPid + ";" + interrogatingTid + ";" + spec + ";"
+ Arrays.toString(transformMatrix));
}
try {
connection.getRemote().focusSearch(accessibilityNodeId, direction,
partialInteractiveRegion, interactionId, callback, mFetchFlags,
interrogatingPid, interrogatingTid, spec, transformMatrix);
return mSecurityPolicy.computeValidReportedPackages(
connection.getPackageName(), connection.getUid());
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling accessibilityFocusSearch()");
}
} finally {
Binder.restoreCallingIdentity(identityToken);
// Recycle if passed to another process.
if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) {
partialInteractiveRegion.recycle();
}
}
return null;
}
@Override
public void sendGesture(int sequence, ParceledListSlice gestureSteps) {
if (svcConnTracingEnabled()) {
logTraceSvcConn(
"sendGesture", "sequence=" + sequence + ";gestureSteps=" + gestureSteps);
}
}
@Override
public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("dispatchGesture", "sequence=" + sequence + ";gestureSteps="
+ gestureSteps + ";displayId=" + displayId);
}
}
@Override
public boolean performAccessibilityAction(int accessibilityWindowId,
long accessibilityNodeId, int action, Bundle arguments, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
if (svcConnTracingEnabled()) {
logTraceSvcConn("performAccessibilityAction",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ accessibilityNodeId + ";action=" + action + ";arguments=" + arguments
+ ";interactionId=" + interactionId + ";callback=" + callback
+ ";interrogatingTid=" + interrogatingTid);
}
final int resolvedWindowId;
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return false;
}
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
if (!mSecurityPolicy.canGetAccessibilityNodeInfoLocked(
mSystemSupport.getCurrentUserIdLocked(), this, resolvedWindowId)) {
return false;
}
}
if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
return false;
}
return performAccessibilityActionInternal(
mSystemSupport.getCurrentUserIdLocked(), resolvedWindowId, accessibilityNodeId,
action, arguments, interactionId, callback, mFetchFlags, interrogatingTid);
}
@Override
public boolean performGlobalAction(int action) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("performGlobalAction", "action=" + action);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return false;
}
}
final long identity = Binder.clearCallingIdentity();
try {
return mSystemActionPerformer.performSystemAction(action);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public @NonNull List<AccessibilityNodeInfo.AccessibilityAction> getSystemActions() {
if (svcConnTracingEnabled()) {
logTraceSvcConn("getSystemActions", "");
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return Collections.emptyList();
}
}
final long identity = Binder.clearCallingIdentity();
try {
return mSystemActionPerformer.getSystemActions();
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public boolean isFingerprintGestureDetectionAvailable() {
if (svcConnTracingEnabled()) {
logTraceSvcConn("isFingerprintGestureDetectionAvailable", "");
}
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
return false;
}
final long identity = Binder.clearCallingIdentity();
try {
if (isCapturingFingerprintGestures()) {
FingerprintGestureDispatcher dispatcher =
mSystemSupport.getFingerprintGestureDispatcher();
return (dispatcher != null) && dispatcher.isFingerprintGestureDetectionAvailable();
}
return false;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Nullable
@Override
public MagnificationConfig getMagnificationConfig(int displayId) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("getMagnificationConfig", "displayId=" + displayId);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return null;
}
}
final long identity = Binder.clearCallingIdentity();
try {
return mSystemSupport.getMagnificationProcessor().getMagnificationConfig(displayId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public float getMagnificationScale(int displayId) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("getMagnificationScale", "displayId=" + displayId);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return 1.0f;
}
}
final long identity = Binder.clearCallingIdentity();
try {
return mSystemSupport.getMagnificationProcessor().getScale(displayId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public Region getMagnificationRegion(int displayId) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("getMagnificationRegion", "displayId=" + displayId);
}
synchronized (mLock) {
final Region region = Region.obtain();
if (!hasRightsToCurrentUserLocked()) {
return region;
}
MagnificationProcessor magnificationProcessor =
mSystemSupport.getMagnificationProcessor();
final long identity = Binder.clearCallingIdentity();
try {
magnificationProcessor.getFullscreenMagnificationRegion(displayId,
region, mSecurityPolicy.canControlMagnification(this));
return region;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
@Override
public Region getCurrentMagnificationRegion(int displayId) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("getCurrentMagnificationRegion", "displayId=" + displayId);
}
synchronized (mLock) {
final Region region = Region.obtain();
if (!hasRightsToCurrentUserLocked()) {
return region;
}
MagnificationProcessor magnificationProcessor =
mSystemSupport.getMagnificationProcessor();
final long identity = Binder.clearCallingIdentity();
try {
magnificationProcessor.getCurrentMagnificationRegion(displayId,
region, mSecurityPolicy.canControlMagnification(this));
return region;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
@Override
public float getMagnificationCenterX(int displayId) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("getMagnificationCenterX", "displayId=" + displayId);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return 0.0f;
}
MagnificationProcessor magnificationProcessor =
mSystemSupport.getMagnificationProcessor();
final long identity = Binder.clearCallingIdentity();
try {
return magnificationProcessor.getCenterX(displayId,
mSecurityPolicy.canControlMagnification(this));
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
@Override
public float getMagnificationCenterY(int displayId) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("getMagnificationCenterY", "displayId=" + displayId);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return 0.0f;
}
MagnificationProcessor magnificationProcessor =
mSystemSupport.getMagnificationProcessor();
final long identity = Binder.clearCallingIdentity();
try {
return magnificationProcessor.getCenterY(displayId,
mSecurityPolicy.canControlMagnification(this));
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
@Override
public boolean resetMagnification(int displayId, boolean animate) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("resetMagnification", "displayId=" + displayId + ";animate=" + animate);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return false;
}
if (!mSecurityPolicy.canControlMagnification(this)) {
return false;
}
}
final long identity = Binder.clearCallingIdentity();
try {
MagnificationProcessor magnificationProcessor =
mSystemSupport.getMagnificationProcessor();
return (magnificationProcessor.resetFullscreenMagnification(displayId, animate)
|| !magnificationProcessor.isMagnifying(displayId));
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public boolean resetCurrentMagnification(int displayId, boolean animate) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("resetCurrentMagnification",
"displayId=" + displayId + ";animate=" + animate);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return false;
}
if (!mSecurityPolicy.canControlMagnification(this)) {
return false;
}
}
final long identity = Binder.clearCallingIdentity();
try {
MagnificationProcessor magnificationProcessor =
mSystemSupport.getMagnificationProcessor();
return (magnificationProcessor.resetCurrentMagnification(displayId, animate)
|| !magnificationProcessor.isMagnifying(displayId));
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public boolean setMagnificationConfig(int displayId,
@NonNull MagnificationConfig config, boolean animate) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("setMagnificationSpec",
"displayId=" + displayId + ", config=" + config.toString());
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return false;
}
if (!mSecurityPolicy.canControlMagnification(this)) {
return false;
}
final long identity = Binder.clearCallingIdentity();
try {
MagnificationProcessor magnificationProcessor =
mSystemSupport.getMagnificationProcessor();
return magnificationProcessor.setMagnificationConfig(displayId, config, animate,
mId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
@Override
public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("setMagnificationCallbackEnabled",
"displayId=" + displayId + ";enabled=" + enabled);
}
final long identity = Binder.clearCallingIdentity();
try {
mInvocationHandler.setMagnificationCallbackEnabled(displayId, enabled);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
public boolean isMagnificationCallbackEnabled(int displayId) {
return mInvocationHandler.isMagnificationCallbackEnabled(displayId);
}
@Override
public void setSoftKeyboardCallbackEnabled(boolean enabled) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("setSoftKeyboardCallbackEnabled", "enabled=" + enabled);
}
final long identity = Binder.clearCallingIdentity();
try {
mInvocationHandler.setSoftKeyboardCallbackEnabled(enabled);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void takeScreenshotOfWindow(int accessibilityWindowId, int interactionId,
ScreenCapture.ScreenCaptureListener listener,
IAccessibilityInteractionConnectionCallback callback) throws RemoteException {
final long currentTimestamp = SystemClock.uptimeMillis();
if ((currentTimestamp
- mRequestTakeScreenshotOfWindowTimestampMs.get(accessibilityWindowId, 0L))
<= ACCESSIBILITY_TAKE_SCREENSHOT_REQUEST_INTERVAL_TIMES_MS) {
callback.sendTakeScreenshotOfWindowError(
AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT, interactionId);
return;
}
mRequestTakeScreenshotOfWindowTimestampMs.put(accessibilityWindowId, currentTimestamp);
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
callback.sendTakeScreenshotOfWindowError(
AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR, interactionId);
return;
}
if (!mSecurityPolicy.canTakeScreenshotLocked(this)) {
callback.sendTakeScreenshotOfWindowError(
AccessibilityService.ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS,
interactionId);
return;
}
}
if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
callback.sendTakeScreenshotOfWindowError(
AccessibilityService.ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS,
interactionId);
return;
}
final long identity = Binder.clearCallingIdentity();
try {
RemoteAccessibilityConnection connection = mA11yWindowManager.getConnectionLocked(
mSystemSupport.getCurrentUserIdLocked(),
resolveAccessibilityWindowIdLocked(accessibilityWindowId));
if (connection == null) {
callback.sendTakeScreenshotOfWindowError(
AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_WINDOW, interactionId);
return;
}
connection.getRemote().takeScreenshotOfWindow(interactionId, listener, callback);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void takeScreenshot(int displayId, RemoteCallback callback) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("takeScreenshot", "displayId=" + displayId + ";callback=" + callback);
}
final long currentTimestamp = SystemClock.uptimeMillis();
if (mRequestTakeScreenshotTimestampMs != 0
&& (currentTimestamp - mRequestTakeScreenshotTimestampMs)
<= AccessibilityService.ACCESSIBILITY_TAKE_SCREENSHOT_REQUEST_INTERVAL_TIMES_MS) {
sendScreenshotFailure(AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT,
callback);
return;
}
mRequestTakeScreenshotTimestampMs = currentTimestamp;
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
sendScreenshotFailure(AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR,
callback);
return;
}
if (!mSecurityPolicy.canTakeScreenshotLocked(this)) {
throw new SecurityException("Services don't have the capability of taking"
+ " the screenshot.");
}
}
if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
sendScreenshotFailure(
AccessibilityService.ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS,
callback);
return;
}
// Private virtual displays are created by the ap and is not allowed to access by other
// aps. We assume the contents on this display should not be captured.
final DisplayManager displayManager =
(DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
final Display display = displayManager.getDisplay(displayId);
if ((display == null) || (display.getType() == Display.TYPE_VIRTUAL
&& (display.getFlags() & Display.FLAG_PRIVATE) != 0)) {
sendScreenshotFailure(
AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, callback);
return;
}
final long identity = Binder.clearCallingIdentity();
if (deleteCaptureDisplay()) {
try {
ScreenCapture.ScreenCaptureListener screenCaptureListener = new
ScreenCapture.ScreenCaptureListener(
(screenshotBuffer, result) -> {
if (screenshotBuffer != null && result == 0) {
sendScreenshotSuccess(screenshotBuffer, callback);
} else {
sendScreenshotFailure(
AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
callback);
}
}
);
mWindowManagerService.captureDisplay(displayId, null, screenCaptureListener);
} catch (Exception e) {
sendScreenshotFailure(AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
callback);
} finally {
Binder.restoreCallingIdentity(identity);
}
} else {
try {
mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
final ScreenshotHardwareBuffer screenshotBuffer = LocalServices
.getService(DisplayManagerInternal.class).userScreenshot(displayId);
if (screenshotBuffer != null) {
sendScreenshotSuccess(screenshotBuffer, callback);
} else {
sendScreenshotFailure(
AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
callback);
}
}, null).recycleOnUse());
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
private void sendScreenshotSuccess(ScreenshotHardwareBuffer screenshotBuffer,
RemoteCallback callback) {
if (deleteCaptureDisplay()) {
mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
final ParcelableColorSpace colorSpace =
new ParcelableColorSpace(screenshotBuffer.getColorSpace());
final Bundle payload = new Bundle();
payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS,
AccessibilityService.TAKE_SCREENSHOT_SUCCESS);
payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER,
hardwareBuffer);
payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace);
payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP,
SystemClock.uptimeMillis());
// Send back the result.
callback.sendResult(payload);
hardwareBuffer.close();
}, null).recycleOnUse());
} else {
final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
final ParcelableColorSpace colorSpace =
new ParcelableColorSpace(screenshotBuffer.getColorSpace());
final Bundle payload = new Bundle();
payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS,
AccessibilityService.TAKE_SCREENSHOT_SUCCESS);
payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER,
hardwareBuffer);
payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace);
payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP,
SystemClock.uptimeMillis());
// Send back the result.
callback.sendResult(payload);
hardwareBuffer.close();
}
}
private void sendScreenshotFailure(@AccessibilityService.ScreenshotErrorCode int errorCode,
RemoteCallback callback) {
mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
final Bundle payload = new Bundle();
payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS, errorCode);
// Send back the result.
callback.sendResult(payload);
}, null).recycleOnUse());
}
@Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
synchronized (mLock) {
pw.append("Service[label=" + mAccessibilityServiceInfo.getResolveInfo()
.loadLabel(mContext.getPackageManager()));
pw.append(", feedbackType"
+ AccessibilityServiceInfo.feedbackTypeToString(mFeedbackType));
pw.append(", capabilities=" + mAccessibilityServiceInfo.getCapabilities());
pw.append(", eventTypes="
+ AccessibilityEvent.eventTypeToString(mEventTypes));
pw.append(", notificationTimeout=" + mNotificationTimeout);
pw.append(", requestA11yBtn=" + mRequestAccessibilityButton);
pw.append("]");
}
}
/**
* Called when the connection is first created. Add a window token for all known displays.
* <p>
* <strong>Note:</strong> Should not be called while holding the AccessibilityManagerService
* lock because this calls out to WindowManagerService.
*/
void addWindowTokensForAllDisplays() {
final Display[] displays = mDisplayManager.getDisplays();
for (int i = 0; i < displays.length; i++) {
final int displayId = displays[i].getDisplayId();
addWindowTokenForDisplay(displayId);
}
}
/**
* Called whenever a logical display has been added to the system. Add a window token for adding
* an accessibility overlay.
*
* <p>
* <strong>Note:</strong> Should not be called while holding the AccessibilityManagerService
* lock because this calls out to WindowManagerService.
*
* @param displayId The id of the logical display that was added.
*/
void addWindowTokenForDisplay(int displayId) {
final long identity = Binder.clearCallingIdentity();
try {
final IBinder overlayWindowToken = new Binder();
if (wmTracingEnabled()) {
logTraceWM("addWindowToken",
overlayWindowToken + ";TYPE_ACCESSIBILITY_OVERLAY;" + displayId + ";null");
}
mWindowManagerService.addWindowToken(overlayWindowToken, TYPE_ACCESSIBILITY_OVERLAY,
displayId, null /* options */);
synchronized (mLock) {
mOverlayWindowTokens.put(displayId, overlayWindowToken);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
public void onRemoved() {
final Display[] displays = mDisplayManager.getDisplays();
for (int i = 0; i < displays.length; i++) {
final int displayId = displays[i].getDisplayId();
onDisplayRemoved(displayId);
}
if (com.android.server.accessibility.Flags.cleanupA11yOverlays()) {
detachAllOverlays();
}
}
/**
* Called whenever a logical display has been removed from the system. Remove a window token for
* removing an accessibility overlay.
*
* @param displayId The id of the logical display that was added.
*/
public void onDisplayRemoved(int displayId) {
final long identity = Binder.clearCallingIdentity();
if (wmTracingEnabled()) {
logTraceWM(
"addWindowToken", mOverlayWindowTokens.get(displayId) + ";true;" + displayId);
}
try {
mWindowManagerService.removeWindowToken(mOverlayWindowTokens.get(displayId), true,
displayId);
synchronized (mLock) {
mOverlayWindowTokens.remove(displayId);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/**
* Gets overlay window token by the display Id.
*
* @param displayId The id of the logical display that was added.
* @return window token.
*/
@Override
public IBinder getOverlayWindowToken(int displayId) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("getOverlayWindowToken", "displayId=" + displayId);
}
synchronized (mLock) {
final long identity = Binder.clearCallingIdentity();
try {
return mOverlayWindowTokens.get(displayId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
/**
* Gets windowId of given token.
*
* @param token The token
* @return window id
*/
@Override
public int getWindowIdForLeashToken(@NonNull IBinder token) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("getWindowIdForLeashToken", "token=" + token);
}
synchronized (mLock) {
final long identity = Binder.clearCallingIdentity();
try {
return mA11yWindowManager.getWindowIdLocked(token);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
public void resetLocked() {
mSystemSupport.getKeyEventDispatcher().flush(this);
try {
// Clear the proxy in the other process so this
// IAccessibilityServiceConnection can be garbage collected.
if (mServiceInterface != null) {
if (svcClientTracingEnabled()) {
logTraceSvcClient("init", "null, " + mId + ", null");
}
mServiceInterface.init(null, mId, null);
}
} catch (RemoteException re) {
/* ignore */
}
if (mService != null) {
try {
mService.unlinkToDeath(this, 0);
} catch (NoSuchElementException e) {
Slog.e(LOG_TAG, "Failed unregistering death link");
}
mService = null;
}
mServiceInterface = null;
mReceivedAccessibilityButtonCallbackSinceBind = false;
}
public boolean isConnectedLocked() {
return (mService != null);
}
public void notifyAccessibilityEvent(AccessibilityEvent event) {
synchronized (mLock) {
final int eventType = event.getEventType();
final boolean serviceWantsEvent = wantsEventLocked(event);
final boolean requiredForCacheConsistency = mUsesAccessibilityCache
&& ((AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK & eventType) != 0);
if (!serviceWantsEvent && !requiredForCacheConsistency) {
return;
}
if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
return;
}
// Make a copy since during dispatch it is possible the event to
// be modified to remove its source if the receiving service does
// not have permission to access the window content.
AccessibilityEvent newEvent = AccessibilityEvent.obtain(event);
Message message;
if ((mNotificationTimeout > 0)
&& (eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)) {
// Allow at most one pending event
final AccessibilityEvent oldEvent = mPendingEvents.get(eventType);
mPendingEvents.put(eventType, newEvent);
if (oldEvent != null) {
mEventDispatchHandler.removeMessages(eventType);
oldEvent.recycle();
}
message = mEventDispatchHandler.obtainMessage(eventType);
} else {
// Send all messages, bypassing mPendingEvents
message = mEventDispatchHandler.obtainMessage(eventType, newEvent);
}
message.arg1 = serviceWantsEvent ? 1 : 0;
mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout);
}
}
/**
* Determines if given event can be dispatched to a service based on the package of the
* event source. Specifically, a service is notified if it is interested in events from the
* package.
*
* @param event The event.
* @return True if the listener should be notified, false otherwise.
*/
private boolean wantsEventLocked(AccessibilityEvent event) {
if (!canReceiveEventsLocked()) {
return false;
}
final boolean includeNotImportantViews = (mFetchFlags
& AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
if ((event.getWindowId() != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)
&& !event.isImportantForAccessibility()
&& !includeNotImportantViews) {
return false;
}
if (event.isAccessibilityDataSensitive()
&& (mFetchFlags & AccessibilityNodeInfo.FLAG_SERVICE_IS_ACCESSIBILITY_TOOL) == 0) {
return false;
}
int eventType = event.getEventType();
if ((mEventTypes & eventType) != eventType) {
return false;
}
Set<String> packageNames = mPackageNames;
String packageName = (event.getPackageName() != null)
? event.getPackageName().toString() : null;
return (packageNames.isEmpty() || packageNames.contains(packageName));
}
/**
* Notifies an accessibility service client for a scheduled event given the event type.
*
* @param eventType The type of the event to dispatch.
*/
private void notifyAccessibilityEventInternal(
int eventType,
AccessibilityEvent event,
boolean serviceWantsEvent) {
IAccessibilityServiceClient listener;
synchronized (mLock) {
listener = mServiceInterface;
// If the service died/was disabled while the message for dispatching
// the accessibility event was propagating the listener may be null.
if (listener == null) {
return;
}
// There are two ways we notify for events, throttled AND non-throttled. If we
// are not throttling, then messages come with events, which we handle with
// minimal fuss.
if (event == null) {
// We are throttling events, so we'll send the event for this type in
// mPendingEvents as long as it it's null. It can only null due to a race
// condition:
//
// 1) A binder thread calls notifyAccessibilityServiceDelayedLocked
// which posts a message for dispatching an event and stores the event
// in mPendingEvents.
// 2) The message is pulled from the queue by the handler on the service
// thread and this method is just about to acquire the lock.
// 3) Another binder thread acquires the lock in notifyAccessibilityEvent
// 4) notifyAccessibilityEvent recycles the event that this method was about
// to process, replaces it with a new one, and posts a second message
// 5) This method grabs the new event, processes it, and removes it from
// mPendingEvents
// 6) The second message dispatched in (4) arrives, but the event has been
// remvoved in (5).
event = mPendingEvents.get(eventType);
if (event == null) {
return;
}
mPendingEvents.remove(eventType);
}
if (mSecurityPolicy.canRetrieveWindowContentLocked(this)) {
event.setConnectionId(mId);
} else {
event.setSource((View) null);
}
event.setSealed(true);
}
try {
if (svcClientTracingEnabled()) {
logTraceSvcClient("onAccessibilityEvent", event + ";" + serviceWantsEvent);
}
listener.onAccessibilityEvent(event, serviceWantsEvent);
if (DEBUG) {
Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
}
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error during sending " + event + " to " + listener, re);
} finally {
event.recycle();
}
}
public void notifyGesture(AccessibilityGestureEvent gestureEvent) {
if (android.view.accessibility.Flags.copyEventsForGestureDetection()) {
// We will use this event async, so copy it because it contains MotionEvents.
mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE,
gestureEvent.copyForAsync()).sendToTarget();
} else {
mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE,
gestureEvent).sendToTarget();
}
}
public void notifySystemActionsChangedLocked() {
mInvocationHandler.sendEmptyMessage(
InvocationHandler.MSG_ON_SYSTEM_ACTIONS_CHANGED);
}
public void notifyClearAccessibilityNodeInfoCache() {
mInvocationHandler.sendEmptyMessage(
InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE);
}
public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
@NonNull MagnificationConfig config) {
mInvocationHandler
.notifyMagnificationChangedLocked(displayId, region, config);
}
public void notifySoftKeyboardShowModeChangedLocked(int showState) {
mInvocationHandler.notifySoftKeyboardShowModeChangedLocked(showState);
}
public void notifyAccessibilityButtonClickedLocked(int displayId) {
mInvocationHandler.notifyAccessibilityButtonClickedLocked(displayId);
}
public void notifyAccessibilityButtonAvailabilityChangedLocked(boolean available) {
mInvocationHandler.notifyAccessibilityButtonAvailabilityChangedLocked(available);
}
public void createImeSessionLocked() {
mInvocationHandler.createImeSessionLocked();
}
public void setImeSessionEnabledLocked(IAccessibilityInputMethodSession session,
boolean enabled) {
mInvocationHandler.setImeSessionEnabledLocked(session, enabled);
}
public void bindInputLocked() {
mInvocationHandler.bindInputLocked();
}
public void unbindInputLocked() {
mInvocationHandler.unbindInputLocked();
}
public void startInputLocked(IRemoteAccessibilityInputConnection connection,
EditorInfo editorInfo, boolean restarting) {
mInvocationHandler.startInputLocked(connection, editorInfo, restarting);
}
@Nullable
private Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec(
int resolvedWindowId) {
return mSystemSupport.getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
}
public boolean wantsGenericMotionEvent(MotionEvent event) {
final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
return (mGenericMotionEventSources & eventSourceWithoutClass) != 0;
}
/**
* Called by the invocation handler to notify the service that the
* state of magnification has changed.
*/
private void notifyMagnificationChangedInternal(int displayId, @NonNull Region region,
@NonNull MagnificationConfig config) {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
if (svcClientTracingEnabled()) {
logTraceSvcClient("onMagnificationChanged", displayId + ", " + region + ", "
+ config.toString());
}
listener.onMagnificationChanged(displayId, region, config);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error sending magnification changes to " + mService, re);
}
}
}
/**
* Called by the invocation handler to notify the service that the state of the soft
* keyboard show mode has changed.
*/
private void notifySoftKeyboardShowModeChangedInternal(int showState) {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
if (svcClientTracingEnabled()) {
logTraceSvcClient("onSoftKeyboardShowModeChanged", String.valueOf(showState));
}
listener.onSoftKeyboardShowModeChanged(showState);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error sending soft keyboard show mode changes to " + mService,
re);
}
}
}
private void notifyAccessibilityButtonClickedInternal(int displayId) {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
if (svcClientTracingEnabled()) {
logTraceSvcClient("onAccessibilityButtonClicked", String.valueOf(displayId));
}
listener.onAccessibilityButtonClicked(displayId);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error sending accessibility button click to " + mService, re);
}
}
}
private void notifyAccessibilityButtonAvailabilityChangedInternal(boolean available) {
// Only notify the service if it's not been notified or the state has changed
if (mReceivedAccessibilityButtonCallbackSinceBind
&& (mLastAccessibilityButtonCallbackState == available)) {
return;
}
mReceivedAccessibilityButtonCallbackSinceBind = true;
mLastAccessibilityButtonCallbackState = available;
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
if (svcClientTracingEnabled()) {
logTraceSvcClient("onAccessibilityButtonAvailabilityChanged",
String.valueOf(available));
}
listener.onAccessibilityButtonAvailabilityChanged(available);
} catch (RemoteException re) {
Slog.e(LOG_TAG,
"Error sending accessibility button availability change to " + mService,
re);
}
}
}
private void notifyGestureInternal(AccessibilityGestureEvent gestureInfo) {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
if (svcClientTracingEnabled()) {
logTraceSvcClient("onGesture", gestureInfo.toString());
}
listener.onGesture(gestureInfo);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error during sending gesture " + gestureInfo
+ " to " + mService, re);
}
}
}
private void notifySystemActionsChangedInternal() {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
if (svcClientTracingEnabled()) {
logTraceSvcClient("onSystemActionsChanged", "");
}
listener.onSystemActionsChanged();
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error sending system actions change to " + mService,
re);
}
}
}
private void notifyClearAccessibilityCacheInternal() {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
if (svcClientTracingEnabled()) {
logTraceSvcClient("clearAccessibilityCache", "");
}
listener.clearAccessibilityCache();
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error during requesting accessibility info cache"
+ " to be cleared.", re);
}
}
}
protected void createImeSessionInternal() {
}
private void setImeSessionEnabledInternal(IAccessibilityInputMethodSession session,
boolean enabled) {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null && session != null) {
try {
if (svcClientTracingEnabled()) {
logTraceSvcClient("createImeSession", "");
}
listener.setImeSessionEnabled(session, enabled);
} catch (RemoteException re) {
Slog.e(LOG_TAG,
"Error requesting IME session from " + mService, re);
}
}
}
private void bindInputInternal() {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
if (svcClientTracingEnabled()) {
logTraceSvcClient("bindInput", "");
}
listener.bindInput();
} catch (RemoteException re) {
Slog.e(LOG_TAG,
"Error binding input to " + mService, re);
}
}
}
private void unbindInputInternal() {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
if (svcClientTracingEnabled()) {
logTraceSvcClient("unbindInput", "");
}
listener.unbindInput();
} catch (RemoteException re) {
Slog.e(LOG_TAG,
"Error unbinding input to " + mService, re);
}
}
}
private void startInputInternal(IRemoteAccessibilityInputConnection connection,
EditorInfo editorInfo, boolean restarting) {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
if (svcClientTracingEnabled()) {
logTraceSvcClient("startInput", "editorInfo=" + editorInfo
+ " restarting=" + restarting);
}
listener.startInput(connection, editorInfo, restarting);
} catch (RemoteException re) {
Slog.e(LOG_TAG,
"Error starting input to " + mService, re);
}
}
}
protected IAccessibilityServiceClient getServiceInterfaceSafely() {
synchronized (mLock) {
return mServiceInterface;
}
}
private int resolveAccessibilityWindowIdLocked(int accessibilityWindowId) {
if (accessibilityWindowId == AccessibilityWindowInfo.ACTIVE_WINDOW_ID) {
final int focusedWindowId =
mA11yWindowManager.getActiveWindowId(mSystemSupport.getCurrentUserIdLocked());
if (!mA11yWindowManager.windowIdBelongsToDisplayType(focusedWindowId, mDisplayTypes)) {
return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
}
return focusedWindowId;
}
return accessibilityWindowId;
}
int resolveAccessibilityWindowIdForFindFocusLocked(int windowId, int focusType) {
if (windowId == AccessibilityWindowInfo.ANY_WINDOW_ID) {
final int focusedWindowId = mA11yWindowManager.getFocusedWindowId(focusType);
// If the caller is a proxy and the found window doesn't belong to a proxy display
// (or vice versa), then return null. This doesn't work if there are multiple active
// proxys, but in the future this code shouldn't be needed if input focus are
// properly split. (so we will deal with the issues if we see them).
//TODO(254545943): Remove this when there is user and proxy separation of input focus
if (!mA11yWindowManager.windowIdBelongsToDisplayType(focusedWindowId, mDisplayTypes)) {
return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
}
return focusedWindowId;
}
return windowId;
}
/**
* Request that the system make sure windows are available to interrogate.
*
* @param displayId The logical display id.
*/
private void ensureWindowsAvailableTimedLocked(int displayId) {
if (displayId == Display.INVALID_DISPLAY) {
return;
}
if (mA11yWindowManager.getWindowListLocked(displayId) != null) {
return;
}
// If we have no registered callback, update the state we
// we may have to register one but it didn't happen yet.
if (!mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
// Invokes client change to make sure tracking window enabled.
mSystemSupport.onClientChangeLocked(false);
}
// We have no windows but do not care about them, done.
if (!mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
return;
}
// Wait for the windows with a timeout.
final long startMillis = SystemClock.uptimeMillis();
while (mA11yWindowManager.getWindowListLocked(displayId) == null) {
final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
final long remainMillis = WAIT_WINDOWS_TIMEOUT_MILLIS - elapsedMillis;
if (remainMillis <= 0) {
return;
}
try {
mLock.wait(remainMillis);
} catch (InterruptedException ie) {
/* ignore */
}
}
}
/**
* Perform the specified accessibility action
*
* @param resolvedWindowId The window ID
* [Other parameters match the method on IAccessibilityServiceConnection]
*
* @return Whether or not the action could be sent to the app process
*/
private boolean performAccessibilityActionInternal(int userId, int resolvedWindowId,
long accessibilityNodeId, int action, Bundle arguments, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int fetchFlags,
long interrogatingTid) {
RemoteAccessibilityConnection connection;
IBinder windowToken = null;
synchronized (mLock) {
connection = mA11yWindowManager.getConnectionLocked(userId, resolvedWindowId);
if (connection == null) {
return false;
}
final boolean isA11yFocusAction = (action == ACTION_ACCESSIBILITY_FOCUS)
|| (action == ACTION_CLEAR_ACCESSIBILITY_FOCUS);
if (!isA11yFocusAction) {
windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(
userId, resolvedWindowId);
}
final AccessibilityWindowInfo a11yWindowInfo =
mA11yWindowManager.findA11yWindowInfoByIdLocked(resolvedWindowId);
if (a11yWindowInfo != null && a11yWindowInfo.isInPictureInPictureMode()
&& mA11yWindowManager.getPictureInPictureActionReplacingConnection() != null
&& !isA11yFocusAction) {
connection = mA11yWindowManager.getPictureInPictureActionReplacingConnection();
}
}
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
try {
// Regardless of whether or not the action succeeds, it was generated by an
// accessibility service that is driven by user actions, so note user activity.
mPowerManager.userActivity(SystemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0);
if (action == ACTION_CLICK || action == ACTION_LONG_CLICK) {
mA11yWindowManager.notifyOutsideTouch(userId, resolvedWindowId);
}
if (windowToken != null) {
mWindowManagerService.requestWindowFocus(windowToken);
}
if (intConnTracingEnabled()) {
logTraceIntConn("performAccessibilityAction",
accessibilityNodeId + ";" + action + ";" + arguments + ";" + interactionId
+ ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid + ";"
+ interrogatingTid);
}
connection.getRemote().performAccessibilityAction(accessibilityNodeId, action,
arguments, interactionId, callback, fetchFlags, interrogatingPid,
interrogatingTid);
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling performAccessibilityAction: " + re);
}
return false;
} finally {
Binder.restoreCallingIdentity(identityToken);
}
return true;
}
/**
* Replace the interaction callback if needed, for example if the window is in picture-
* in-picture mode and needs its nodes replaced.
*
* @param originalCallback The callback we were planning to use
* @param resolvedWindowId The ID of the window we're calling
* @param interactionId The id for the original callback
* @param interrogatingPid Process ID of requester
* @param interrogatingTid Thread ID of requester
*
* @return The callback to use, which may be the original one.
*/
private IAccessibilityInteractionConnectionCallback replaceCallbackIfNeeded(
IAccessibilityInteractionConnectionCallback originalCallback, int resolvedWindowId,
int interactionId, int interrogatingPid, long interrogatingTid) {
final RemoteAccessibilityConnection pipActionReplacingConnection =
mA11yWindowManager.getPictureInPictureActionReplacingConnection();
synchronized (mLock) {
final AccessibilityWindowInfo windowInfo =
mA11yWindowManager.findA11yWindowInfoByIdLocked(resolvedWindowId);
if ((windowInfo == null) || !windowInfo.isInPictureInPictureMode()
|| (pipActionReplacingConnection == null)) {
return originalCallback;
}
}
return new ActionReplacingCallback(originalCallback,
pipActionReplacingConnection.getRemote(), interactionId,
interrogatingPid, interrogatingTid);
}
private List<AccessibilityWindowInfo> getWindowsByDisplayLocked(int displayId) {
final List<AccessibilityWindowInfo> internalWindowList =
mA11yWindowManager.getWindowListLocked(displayId);
if (internalWindowList == null) {
return null;
}
final List<AccessibilityWindowInfo> returnedWindowList = new ArrayList<>();
final int windowCount = internalWindowList.size();
for (int i = 0; i < windowCount; i++) {
AccessibilityWindowInfo window = internalWindowList.get(i);
AccessibilityWindowInfo windowClone =
AccessibilityWindowInfo.obtain(window);
windowClone.setConnectionId(mId);
returnedWindowList.add(windowClone);
}
return returnedWindowList;
}
public ComponentName getComponentName() {
return mComponentName;
}
private final class InvocationHandler extends Handler {
public static final int MSG_ON_GESTURE = 1;
public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 2;
private static final int MSG_ON_MAGNIFICATION_CHANGED = 5;
private static final int MSG_ON_SOFT_KEYBOARD_STATE_CHANGED = 6;
private static final int MSG_ON_ACCESSIBILITY_BUTTON_CLICKED = 7;
private static final int MSG_ON_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 8;
private static final int MSG_ON_SYSTEM_ACTIONS_CHANGED = 9;
private static final int MSG_CREATE_IME_SESSION = 10;
private static final int MSG_SET_IME_SESSION_ENABLED = 11;
private static final int MSG_BIND_INPUT = 12;
private static final int MSG_UNBIND_INPUT = 13;
private static final int MSG_START_INPUT = 14;
/** List of magnification callback states, mapping from displayId -> Boolean */
@GuardedBy("mlock")
private final SparseArray<Boolean> mMagnificationCallbackState = new SparseArray<>(0);
private boolean mIsSoftKeyboardCallbackEnabled = false;
public InvocationHandler(Looper looper) {
super(looper, null, true);
}
@Override
public void handleMessage(Message message) {
final int type = message.what;
switch (type) {
case MSG_ON_GESTURE: {
if (message.obj instanceof AccessibilityGestureEvent gesture) {
notifyGestureInternal(gesture);
if (android.view.accessibility.Flags.copyEventsForGestureDetection()) {
gesture.recycle();
}
}
} break;
case MSG_CLEAR_ACCESSIBILITY_CACHE: {
notifyClearAccessibilityCacheInternal();
} break;
case MSG_ON_MAGNIFICATION_CHANGED: {
final SomeArgs args = (SomeArgs) message.obj;
final Region region = (Region) args.arg1;
final MagnificationConfig config = (MagnificationConfig) args.arg2;
final int displayId = args.argi1;
notifyMagnificationChangedInternal(displayId, region, config);
args.recycle();
} break;
case MSG_ON_SOFT_KEYBOARD_STATE_CHANGED: {
final int showState = (int) message.arg1;
notifySoftKeyboardShowModeChangedInternal(showState);
} break;
case MSG_ON_ACCESSIBILITY_BUTTON_CLICKED: {
final int displayId = (int) message.arg1;
notifyAccessibilityButtonClickedInternal(displayId);
} break;
case MSG_ON_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED: {
final boolean available = (message.arg1 != 0);
notifyAccessibilityButtonAvailabilityChangedInternal(available);
} break;
case MSG_ON_SYSTEM_ACTIONS_CHANGED: {
notifySystemActionsChangedInternal();
break;
}
case MSG_CREATE_IME_SESSION:
createImeSessionInternal();
break;
case MSG_SET_IME_SESSION_ENABLED:
final boolean enabled = (message.arg1 != 0);
final IAccessibilityInputMethodSession session =
(IAccessibilityInputMethodSession) message.obj;
setImeSessionEnabledInternal(session, enabled);
break;
case MSG_BIND_INPUT:
bindInputInternal();
break;
case MSG_UNBIND_INPUT:
unbindInputInternal();
break;
case MSG_START_INPUT:
final boolean restarting = (message.arg1 != 0);
final SomeArgs args = (SomeArgs) message.obj;
final IRemoteAccessibilityInputConnection connection =
(IRemoteAccessibilityInputConnection) args.arg1;
final EditorInfo editorInfo = (EditorInfo) args.arg2;
startInputInternal(connection, editorInfo, restarting);
args.recycle();
break;
default: {
throw new IllegalArgumentException("Unknown message: " + type);
}
}
}
public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
@NonNull MagnificationConfig config) {
synchronized (mLock) {
if (mMagnificationCallbackState.get(displayId) == null) {
return;
}
}
final SomeArgs args = SomeArgs.obtain();
args.arg1 = region;
args.arg2 = config;
args.argi1 = displayId;
final Message msg = obtainMessage(MSG_ON_MAGNIFICATION_CHANGED, args);
msg.sendToTarget();
}
public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {
synchronized (mLock) {
if (enabled) {
mMagnificationCallbackState.put(displayId, true);
} else {
mMagnificationCallbackState.remove(displayId);
}
}
}
public boolean isMagnificationCallbackEnabled(int displayId) {
synchronized (mLock) {
return mMagnificationCallbackState.get(displayId) != null;
}
}
public void notifySoftKeyboardShowModeChangedLocked(int showState) {
if (!mIsSoftKeyboardCallbackEnabled) {
return;
}
final Message msg = obtainMessage(MSG_ON_SOFT_KEYBOARD_STATE_CHANGED, showState, 0);
msg.sendToTarget();
}
public void setSoftKeyboardCallbackEnabled(boolean enabled) {
mIsSoftKeyboardCallbackEnabled = enabled;
}
public void notifyAccessibilityButtonClickedLocked(int displayId) {
final Message msg = obtainMessage(MSG_ON_ACCESSIBILITY_BUTTON_CLICKED, displayId, 0);
msg.sendToTarget();
}
public void notifyAccessibilityButtonAvailabilityChangedLocked(boolean available) {
final Message msg = obtainMessage(MSG_ON_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED,
(available ? 1 : 0), 0);
msg.sendToTarget();
}
public void createImeSessionLocked() {
final Message msg = obtainMessage(MSG_CREATE_IME_SESSION);
msg.sendToTarget();
}
public void setImeSessionEnabledLocked(IAccessibilityInputMethodSession session,
boolean enabled) {
final Message msg = obtainMessage(MSG_SET_IME_SESSION_ENABLED, (enabled ? 1 : 0),
0, session);
msg.sendToTarget();
}
public void bindInputLocked() {
final Message msg = obtainMessage(MSG_BIND_INPUT);
msg.sendToTarget();
}
public void unbindInputLocked() {
final Message msg = obtainMessage(MSG_UNBIND_INPUT);
msg.sendToTarget();
}
public void startInputLocked(
IRemoteAccessibilityInputConnection connection,
EditorInfo editorInfo, boolean restarting) {
final SomeArgs args = SomeArgs.obtain();
args.arg1 = connection;
args.arg2 = editorInfo;
final Message msg = obtainMessage(MSG_START_INPUT, restarting ? 1 : 0, 0, args);
msg.sendToTarget();
}
}
public boolean isServiceHandlesDoubleTapEnabled() {
return mServiceHandlesDoubleTap;
}
public boolean isMultiFingerGesturesEnabled() {
return mRequestMultiFingerGestures;
}
public boolean isTwoFingerPassthroughEnabled() {
return mRequestTwoFingerPassthrough;
}
public boolean isSendMotionEventsEnabled() {
return mSendMotionEvents;
}
@Override
public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("setGestureDetectionPassthroughRegion",
"displayId=" + displayId + ";region=" + region);
}
final long identity = Binder.clearCallingIdentity();
try {
mSystemSupport.setGestureDetectionPassthroughRegion(displayId, region);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void setTouchExplorationPassthroughRegion(int displayId, Region region) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("setTouchExplorationPassthroughRegion",
"displayId=" + displayId + ";region=" + region);
}
final long identity = Binder.clearCallingIdentity();
try {
mSystemSupport.setTouchExplorationPassthroughRegion(displayId, region);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void setFocusAppearance(int strokeWidth, int color) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("setFocusAppearance", "strokeWidth=" + strokeWidth + ";color=" + color);
}
}
@Override
public void setCacheEnabled(boolean enabled) {
if (svcConnTracingEnabled()) {
logTraceSvcConn("setCacheEnabled", "enabled=" + enabled);
}
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
mUsesAccessibilityCache = enabled;
mSystemSupport.onClientChangeLocked(true);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
int processId, long threadId, int callingUid, Bundle callingStack) {
final long identity = Binder.clearCallingIdentity();
try {
if (mTrace.isA11yTracingEnabledForTypes(loggingTypes)) {
ArrayList<StackTraceElement> list =
(ArrayList<StackTraceElement>) callingStack.getSerializable(CALL_STACK,
java.util.ArrayList.class);
HashSet<String> ignoreList =
(HashSet<String>) callingStack.getSerializable(IGNORE_CALL_STACK,
java.util.HashSet.class);
mTrace.logTrace(timestamp, where, loggingTypes, callingParams, processId, threadId,
callingUid, list.toArray(new StackTraceElement[list.size()]), ignoreList);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
protected boolean svcClientTracingEnabled() {
return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CLIENT);
}
protected void logTraceSvcClient(String methodName, String params) {
mTrace.logTrace(TRACE_SVC_CLIENT + "." + methodName,
FLAGS_ACCESSIBILITY_SERVICE_CLIENT, params);
}
protected boolean svcConnTracingEnabled() {
return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CONNECTION);
}
protected void logTraceSvcConn(String methodName, String params) {
mTrace.logTrace(TRACE_SVC_CONN + "." + methodName,
FLAGS_ACCESSIBILITY_SERVICE_CONNECTION, params);
}
protected boolean intConnTracingEnabled() {
return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION);
}
protected void logTraceIntConn(String methodName, String params) {
mTrace.logTrace(LOG_TAG + "." + methodName,
FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION, params);
}
protected boolean wmTracingEnabled() {
return mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL);
}
protected void logTraceWM(String methodName, String params) {
mTrace.logTrace(TRACE_WM + "." + methodName, FLAGS_WINDOW_MANAGER_INTERNAL, params);
}
@Override
public void setServiceDetectsGesturesEnabled(int displayId, boolean mode) {
final long identity = Binder.clearCallingIdentity();
try {
mServiceDetectsGestures.put(displayId, mode);
mSystemSupport.setServiceDetectsGesturesEnabled(displayId, mode);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
public boolean isServiceDetectsGesturesEnabled(int displayId) {
if (mServiceDetectsGestures.contains(displayId)) {
return mServiceDetectsGestures.get(displayId);
}
return false;
}
@Override
public void requestTouchExploration(int displayId) {
final long identity = Binder.clearCallingIdentity();
try {
mSystemSupport.requestTouchExploration(displayId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void requestDragging(int displayId, int pointerId) {
final long identity = Binder.clearCallingIdentity();
try {
mSystemSupport.requestDragging(displayId, pointerId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void requestDelegating(int displayId) {
final long identity = Binder.clearCallingIdentity();
try {
mSystemSupport.requestDelegating(displayId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void onDoubleTap(int displayId) {
final long identity = Binder.clearCallingIdentity();
try {
mSystemSupport.onDoubleTap(displayId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void onDoubleTapAndHold(int displayId) {
final long identity = Binder.clearCallingIdentity();
try {
mSystemSupport.onDoubleTapAndHold(displayId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/**
* Sets the scaling factor for animations.
*/
@Override
public void setAnimationScale(float scale) {
final long identity = Binder.clearCallingIdentity();
try {
Settings.Global.putFloat(
mContext.getContentResolver(), Settings.Global.WINDOW_ANIMATION_SCALE, scale);
Settings.Global.putFloat(
mContext.getContentResolver(),
Settings.Global.TRANSITION_ANIMATION_SCALE,
scale);
Settings.Global.putFloat(
mContext.getContentResolver(), Settings.Global.ANIMATOR_DURATION_SCALE, scale);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void attachAccessibilityOverlayToDisplay(
int interactionId,
int displayId,
SurfaceControl sc,
IAccessibilityInteractionConnectionCallback callback) {
final long identity = Binder.clearCallingIdentity();
try {
mSystemSupport.attachAccessibilityOverlayToDisplay(
interactionId, displayId, sc, callback);
mOverlays.add(sc);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void attachAccessibilityOverlayToWindow(
int interactionId,
int accessibilityWindowId,
SurfaceControl sc,
IAccessibilityInteractionConnectionCallback callback)
throws RemoteException {
final long identity = Binder.clearCallingIdentity();
try {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
t.setTrustedOverlay(sc, true).apply();
t.close();
synchronized (mLock) {
RemoteAccessibilityConnection connection =
mA11yWindowManager.getConnectionLocked(
mSystemSupport.getCurrentUserIdLocked(),
resolveAccessibilityWindowIdLocked(accessibilityWindowId));
if (connection == null) {
callback.sendAttachOverlayResult(
AccessibilityService.OVERLAY_RESULT_INVALID, interactionId);
return;
}
connection
.getRemote()
.attachAccessibilityOverlayToWindow(sc, interactionId, callback);
mOverlays.add(sc);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
protected void detachAllOverlays() {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
for (SurfaceControl sc : mOverlays) {
if (sc.isValid()) {
t.reparent(sc, null);
}
}
t.apply();
t.close();
mOverlays.clear();
}
}