Introduce A11yService#takeScreenshotOfWindow()

This enables accessibility services to take a screenshot of
a window without any overlay windows drawn on top, which is
beneficial for ML-based understanding of the visual content
in the window.

Bug: 249830826
Test: atest AccessibilityTakeScreenshotTest
Test: use in an a11y service, observe screenshots
      contain only content from the requested windows
      and no overlays
Change-Id: Ic886aeace9cb2984a472273858281b4f2c3a0e46
This commit is contained in:
Daniel Norman
2022-08-24 12:00:14 -07:00
parent b091fb85de
commit 32afd29346
12 changed files with 315 additions and 3 deletions

View File

@@ -3112,10 +3112,13 @@ package android.accessibilityservice {
method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
method public void setTouchExplorationPassthroughRegion(int, @NonNull android.graphics.Region);
method public void takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull android.accessibilityservice.AccessibilityService.TakeScreenshotCallback);
method public void takeScreenshotOfWindow(int, @NonNull java.util.concurrent.Executor, @NonNull android.accessibilityservice.AccessibilityService.TakeScreenshotCallback);
field public static final int ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR = 1; // 0x1
field public static final int ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT = 3; // 0x3
field public static final int ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY = 4; // 0x4
field public static final int ERROR_TAKE_SCREENSHOT_INVALID_WINDOW = 5; // 0x5
field public static final int ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS = 2; // 0x2
field public static final int ERROR_TAKE_SCREENSHOT_SECURE_WINDOW = 6; // 0x6
field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
field public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; // 0x28
field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13

View File

@@ -696,7 +696,8 @@ public abstract class AccessibilityService extends Service {
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_DISPLAY,
ERROR_TAKE_SCREENSHOT_INVALID_WINDOW
})
public @interface ScreenshotErrorCode {}
@@ -727,6 +728,18 @@ public abstract class AccessibilityService extends Service {
*/
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.
@@ -2568,6 +2581,7 @@ public abstract class AccessibilityService extends Service {
* @param executor Executor on which to run the callback.
* @param callback The callback invoked when taking screenshot has succeeded or failed.
* See {@link TakeScreenshotCallback} for details.
* @see #takeScreenshotOfWindow
*/
public void takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor,
@NonNull TakeScreenshotCallback callback) {
@@ -2589,7 +2603,8 @@ public abstract class AccessibilityService extends Service {
final HardwareBuffer hardwareBuffer =
result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER, android.hardware.HardwareBuffer.class);
final ParcelableColorSpace colorSpace =
result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, android.graphics.ParcelableColorSpace.class);
result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE,
android.graphics.ParcelableColorSpace.class);
final ScreenshotResult screenshot = new ScreenshotResult(hardwareBuffer,
colorSpace.getColorSpace(),
result.getLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP));
@@ -2600,6 +2615,37 @@ public abstract class AccessibilityService extends Service {
}
}
/**
* Takes a screenshot of the specified window and returns it via an
* {@link AccessibilityService.ScreenshotResult}. You can use {@link Bitmap#wrapHardwareBuffer}
* to construct the bitmap from the ScreenshotResult's payload.
* <p>
* <strong>Note:</strong> 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}.
* </p>
* <p>
* Both this method and {@link #takeScreenshot} can be used for machine learning-based visual
* screen understanding. Use <code>takeScreenshotOfWindow</code> 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.
* </p>
*
* @param accessibilityWindowId The window id, from {@link AccessibilityWindowInfo#getId()}.
* @param executor Executor on which to run the callback.
* @param callback The callback invoked when taking screenshot has succeeded or failed.
* See {@link TakeScreenshotCallback} for details.
* @see #takeScreenshot
*/
public void takeScreenshotOfWindow(int accessibilityWindowId,
@NonNull @CallbackExecutor Executor executor,
@NonNull TakeScreenshotCallback callback) {
AccessibilityInteractionClient.getInstance(this).takeScreenshotOfWindow(
mConnectionId, accessibilityWindowId, executor, callback);
}
/**
* Sets the strokeWidth and color of the accessibility focus rectangle.
* <p>
@@ -3113,7 +3159,8 @@ public abstract class AccessibilityService extends Service {
private final @NonNull ColorSpace mColorSpace;
private final long mTimestamp;
private ScreenshotResult(@NonNull HardwareBuffer hardwareBuffer,
/** @hide */
public ScreenshotResult(@NonNull HardwareBuffer hardwareBuffer,
@NonNull ColorSpace colorSpace, long timestamp) {
Preconditions.checkNotNull(hardwareBuffer, "hardwareBuffer cannot be null");
Preconditions.checkNotNull(colorSpace, "colorSpace cannot be null");

View File

@@ -30,6 +30,7 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.accessibility.AccessibilityWindowInfo;
import java.util.List;
import android.window.ScreenCapture;
/**
* Interface given to an AccessibilitySerivce to talk to the AccessibilityManagerService.
@@ -122,6 +123,10 @@ interface IAccessibilityServiceConnection {
void takeScreenshot(int displayId, in RemoteCallback callback);
void takeScreenshotOfWindow(int accessibilityWindowId, int interactionId,
in ScreenCapture.ScreenCaptureListener listener,
IAccessibilityInteractionConnectionCallback callback);
void setGestureDetectionPassthroughRegion(int displayId, in Region region);
void setTouchExplorationPassthroughRegion(int displayId, in Region region);

View File

@@ -21,6 +21,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_A
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_REQUESTED_KEY;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
import android.accessibilityservice.AccessibilityService;
import android.annotation.NonNull;
import android.graphics.Matrix;
import android.graphics.Rect;
@@ -46,11 +47,13 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityRequestPreparer;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.window.ScreenCapture;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.function.pooled.PooledLambda;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -588,6 +591,43 @@ public final class AccessibilityInteractionController {
}
}
/**
* Take a screenshot using {@link ScreenCapture} of this {@link ViewRootImpl}'s {@link
* SurfaceControl}.
*/
public void takeScreenshotOfWindowClientThread(int interactionId,
ScreenCapture.ScreenCaptureListener listener,
IAccessibilityInteractionConnectionCallback callback) {
Message message = PooledLambda.obtainMessage(
AccessibilityInteractionController::takeScreenshotOfWindowUiThread,
this, interactionId, listener, callback);
// Screenshot results are returned to the service asynchronously, so the same-thread
// message wait logic from #scheduleMessage() is not needed.
mHandler.sendMessage(message);
}
private void takeScreenshotOfWindowUiThread(int interactionId,
ScreenCapture.ScreenCaptureListener listener,
IAccessibilityInteractionConnectionCallback callback) {
try {
if ((mViewRootImpl.getWindowFlags() & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
callback.sendTakeScreenshotOfWindowError(
AccessibilityService.ERROR_TAKE_SCREENSHOT_SECURE_WINDOW, interactionId);
return;
}
final ScreenCapture.LayerCaptureArgs captureArgs =
new ScreenCapture.LayerCaptureArgs.Builder(mViewRootImpl.getSurfaceControl())
.setChildrenOnly(false).setUid(Process.myUid()).build();
if (ScreenCapture.captureLayers(captureArgs, listener) != 0) {
callback.sendTakeScreenshotOfWindowError(
AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR, interactionId);
}
} catch (RemoteException re) {
/* ignore - the other side will time out */
}
}
public void findFocusClientThread(long accessibilityNodeId, int focusType,
Region interactiveRegion, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,

View File

@@ -88,6 +88,7 @@ import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodCl
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER;
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
import android.animation.AnimationHandler;
import android.animation.LayoutTransition;
import android.annotation.AnyThread;
@@ -198,6 +199,7 @@ import android.window.ClientWindowFrames;
import android.window.CompatOnBackInvokedCallback;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
import android.window.ScreenCapture;
import android.window.SurfaceSyncGroup;
import android.window.WindowOnBackInvokedDispatcher;
@@ -10691,6 +10693,25 @@ public final class ViewRootImpl implements ViewParent,
.notifyOutsideTouchClientThread();
}
}
@Override
public void takeScreenshotOfWindow(int interactionId,
ScreenCapture.ScreenCaptureListener listener,
IAccessibilityInteractionConnectionCallback callback) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.takeScreenshotOfWindowClientThread(interactionId, listener, callback);
} else {
try {
callback.sendTakeScreenshotOfWindowError(
AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR,
interactionId);
} catch (RemoteException re) {
/* best effort - ignore */
}
}
}
}
/**

View File

@@ -22,7 +22,9 @@ import static android.os.Build.VERSION_CODES.S;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_MASK;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_MASK;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -31,17 +33,21 @@ import android.content.Context;
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.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.Pair;
import android.util.SparseArray;
import android.util.SparseLongArray;
import android.view.Display;
import android.view.ViewConfiguration;
import android.window.ScreenCapture;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -53,6 +59,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -146,6 +153,10 @@ public final class AccessibilityInteractionClient
private boolean mPerformAccessibilityActionResult;
// SparseArray of interaction ID -> screenshot executor+callback.
private final SparseArray<Pair<Executor, AccessibilityService.TakeScreenshotCallback>>
mTakeScreenshotOfWindowCallbacks = new SparseArray<>();
private Message mSameThreadMessage;
private int mInteractionIdWaitingForPrefetchResult = -1;
@@ -778,6 +789,59 @@ public final class AccessibilityInteractionClient
return Collections.emptyList();
}
/**
* Takes a screenshot of the window with the provided {@code accessibilityWindowId} and
* returns the answer asynchronously. This async behavior is similar to {@link
* AccessibilityService#takeScreenshot} but unlike other methods in this class which perform
* synchronous waiting in the AccessibilityService client.
*
* @see AccessibilityService#takeScreenshotOfWindow
*/
public void takeScreenshotOfWindow(int connectionId, int accessibilityWindowId,
@NonNull @CallbackExecutor Executor executor,
@NonNull AccessibilityService.TakeScreenshotCallback callback) {
synchronized (mInstanceLock) {
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection == null) {
executor.execute(() -> callback.onFailure(
AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR));
return;
}
final long identityToken = Binder.clearCallingIdentity();
try {
final int interactionId = mInteractionIdCounter.getAndIncrement();
mTakeScreenshotOfWindowCallbacks.put(interactionId,
Pair.create(executor, callback));
// Create a ScreenCaptureListener to receive the screenshot directly from
// SurfaceFlinger instead of requiring an extra IPC from the app:
// A11yService -> App -> SurfaceFlinger -> A11yService
ScreenCapture.ScreenCaptureListener listener =
new ScreenCapture.ScreenCaptureListener(
screenshot -> sendWindowScreenshotSuccess(screenshot,
interactionId));
connection.takeScreenshotOfWindow(accessibilityWindowId, interactionId,
listener, this);
new Handler(Looper.getMainLooper()).postDelayed(() -> {
synchronized (mInstanceLock) {
// Notify failure if we still haven't sent a response after timeout.
if (mTakeScreenshotOfWindowCallbacks.contains(interactionId)) {
sendTakeScreenshotOfWindowError(
AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR,
interactionId);
}
}
}, TIMEOUT_INTERACTION_MILLIS);
} finally {
Binder.restoreCallingIdentity(identityToken);
}
} catch (RemoteException re) {
executor.execute(() -> callback.onFailure(
AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR));
}
}
}
/**
* Finds {@link AccessibilityNodeInfo}s by View text. The match is case
* insensitive containment. The search is performed in the window whose
@@ -1253,6 +1317,55 @@ public final class AccessibilityInteractionClient
}
}
/**
* Sends the result of a window screenshot request to the requesting client.
*
* {@link #takeScreenshotOfWindow} does not perform synchronous waiting, so this method
* does not notify any wait lock.
*/
private void sendWindowScreenshotSuccess(ScreenCapture.ScreenshotHardwareBuffer screenshot,
int interactionId) {
if (screenshot == null) {
sendTakeScreenshotOfWindowError(
AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR, interactionId);
return;
}
synchronized (mInstanceLock) {
if (mTakeScreenshotOfWindowCallbacks.contains(interactionId)) {
final AccessibilityService.ScreenshotResult result =
new AccessibilityService.ScreenshotResult(screenshot.getHardwareBuffer(),
screenshot.getColorSpace(), SystemClock.uptimeMillis());
final Pair<Executor, AccessibilityService.TakeScreenshotCallback> pair =
mTakeScreenshotOfWindowCallbacks.get(interactionId);
final Executor executor = pair.first;
final AccessibilityService.TakeScreenshotCallback callback = pair.second;
executor.execute(() -> callback.onSuccess(result));
mTakeScreenshotOfWindowCallbacks.remove(interactionId);
}
}
}
/**
* Sends an error code for a window screenshot request to the requesting client.
*
* @param errorCode The error code from {@link AccessibilityService.ScreenshotErrorCode}.
* @param interactionId The interaction id of the request.
*/
@Override
public void sendTakeScreenshotOfWindowError(
@AccessibilityService.ScreenshotErrorCode int errorCode, int interactionId) {
synchronized (mInstanceLock) {
if (mTakeScreenshotOfWindowCallbacks.contains(interactionId)) {
final Pair<Executor, AccessibilityService.TakeScreenshotCallback> pair =
mTakeScreenshotOfWindowCallbacks.get(interactionId);
final Executor executor = pair.first;
final AccessibilityService.TakeScreenshotCallback callback = pair.second;
executor.execute(() -> callback.onFailure(errorCode));
mTakeScreenshotOfWindowCallbacks.remove(interactionId);
}
}
}
/**
* Clears the result state.
*/

View File

@@ -22,6 +22,7 @@ import android.os.Bundle;
import android.view.MagnificationSpec;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.window.ScreenCapture;
/**
* Interface for interaction between the AccessibilityManagerService
@@ -60,4 +61,8 @@ oneway interface IAccessibilityInteractionConnection {
void clearAccessibilityFocus();
void notifyOutsideTouch();
void takeScreenshotOfWindow(int interactionId,
in ScreenCapture.ScreenCaptureListener listener,
IAccessibilityInteractionConnectionCallback callback);
}

View File

@@ -63,4 +63,9 @@ oneway interface IAccessibilityInteractionConnectionCallback {
*/
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
void setPerformAccessibilityActionResult(boolean succeeded, int interactionId);
/**
* Sends an error code for a window screenshot request to the requesting client.
*/
void sendTakeScreenshotOfWindowError(int errorCode, int interactionId);
}

View File

@@ -27,6 +27,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.window.ScreenCapture;
import java.util.Collections;
import java.util.List;
@@ -180,6 +181,10 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon
public void takeScreenshot(int displayId, RemoteCallback callback) {}
public void takeScreenshotOfWindow(int accessibilityWindowId, int interactionId,
ScreenCapture.ScreenCaptureListener listener,
IAccessibilityInteractionConnectionCallback callback) {}
public void setFocusAppearance(int strokeWidth, int color) {}
public void setCacheEnabled(boolean enabled) {}

View File

@@ -29,6 +29,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.window.ScreenCapture;
import androidx.annotation.BinderThread;
@@ -361,6 +362,15 @@ public class PipAccessibilityInteractionConnection {
});
}
@Override
public void takeScreenshotOfWindow(int interactionId,
ScreenCapture.ScreenCaptureListener listener,
IAccessibilityInteractionConnectionCallback callback) throws RemoteException {
// AbstractAccessibilityServiceConnection uses the standard
// IAccessibilityInteractionConnection for takeScreenshotOfWindow for Pip windows,
// so do nothing here.
}
@Override
public void clearAccessibilityFocus() throws RemoteException {
// Do nothing

View File

@@ -16,6 +16,7 @@
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;
@@ -83,6 +84,7 @@ 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;
@@ -211,6 +213,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
/** 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<>();
public interface SystemSupport {
/**
@@ -1251,6 +1258,51 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
mInvocationHandler.setSoftKeyboardCallbackEnabled(enabled);
}
@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;
}
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);
}
@Override
public void takeScreenshot(int displayId, RemoteCallback callback) {
if (svcConnTracingEnabled()) {

View File

@@ -167,6 +167,12 @@ public class ActionReplacingCallback extends IAccessibilityInteractionConnection
mServiceCallback.setPerformAccessibilityActionResult(succeeded, interactionId);
}
@Override
public void sendTakeScreenshotOfWindowError(int errorCode, int interactionId)
throws RemoteException {
mServiceCallback.sendTakeScreenshotOfWindowError(errorCode, interactionId);
}
private void replaceInfoActionsAndCallService() {
final AccessibilityNodeInfo nodeToReturn;
boolean doCallback = false;