Merge "Supporting multi-display for takeScreenshot()" into rvc-dev am: aa8f5cb671 am: ba452787c7 am: 8baf0cebbe am: f08a1a7d2f
Change-Id: I6be761c26c509a7c7cef216d097ac6f6756b772c
This commit is contained in:
@@ -47,7 +47,6 @@ import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Display;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerImpl;
|
||||
@@ -1981,8 +1980,6 @@ public abstract class AccessibilityService extends Service {
|
||||
* 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}.
|
||||
* This API only will support {@link Display#DEFAULT_DISPLAY} until {@link SurfaceControl}
|
||||
* supports non-default displays.
|
||||
* </p>
|
||||
*
|
||||
* @param displayId The logic display id, must be {@link Display#DEFAULT_DISPLAY} for
|
||||
@@ -1990,18 +1987,11 @@ 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.
|
||||
*
|
||||
* @throws IllegalArgumentException if displayId is not {@link Display#DEFAULT_DISPLAY}.
|
||||
*/
|
||||
public void takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull TakeScreenshotCallback callback) {
|
||||
Preconditions.checkNotNull(executor, "executor cannot be null");
|
||||
Preconditions.checkNotNull(callback, "callback cannot be null");
|
||||
|
||||
if (displayId != Display.DEFAULT_DISPLAY) {
|
||||
throw new IllegalArgumentException("DisplayId isn't the default display");
|
||||
}
|
||||
|
||||
final IAccessibilityServiceConnection connection =
|
||||
AccessibilityInteractionClient.getInstance().getConnection(
|
||||
mConnectionId);
|
||||
|
||||
@@ -71,6 +71,15 @@ public abstract class DisplayManagerInternal {
|
||||
*/
|
||||
public abstract SurfaceControl.ScreenshotGraphicBuffer screenshot(int displayId);
|
||||
|
||||
/**
|
||||
* Take a screenshot without secure layer of the specified display and return a buffer.
|
||||
*
|
||||
* @param displayId The display id to take the screenshot of.
|
||||
* @return The buffer or null if we have failed.
|
||||
*/
|
||||
public abstract SurfaceControl.ScreenshotGraphicBuffer screenshotWithoutSecureLayer(
|
||||
int displayId);
|
||||
|
||||
/**
|
||||
* Returns information about the specified logical display.
|
||||
*
|
||||
|
||||
@@ -43,12 +43,10 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.graphics.GraphicBuffer;
|
||||
import android.graphics.ParcelableColorSpace;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Region;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.display.DisplayManagerGlobal;
|
||||
import android.hardware.display.DisplayManagerInternal;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -66,7 +64,6 @@ import android.util.SparseArray;
|
||||
import android.view.Display;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MagnificationSpec;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceControl.ScreenshotGraphicBuffer;
|
||||
import android.view.View;
|
||||
import android.view.WindowInfo;
|
||||
@@ -1010,53 +1007,29 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
|
||||
return;
|
||||
}
|
||||
|
||||
final Display display = DisplayManagerGlobal.getInstance()
|
||||
.getRealDisplay(displayId);
|
||||
if (display == null) {
|
||||
sendScreenshotFailure(AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
|
||||
callback);
|
||||
// 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;
|
||||
}
|
||||
|
||||
sendScreenshotSuccess(display, callback);
|
||||
}
|
||||
|
||||
private ScreenshotGraphicBuffer takeScreenshotBuffer(Display display) {
|
||||
final Point displaySize = new Point();
|
||||
// TODO (b/145893483): calling new API with the display as a parameter
|
||||
// when surface control supported.
|
||||
final IBinder token = SurfaceControl.getInternalDisplayToken();
|
||||
final Rect crop = new Rect(0, 0, displaySize.x, displaySize.y);
|
||||
final int rotation = display.getRotation();
|
||||
display.getRealSize(displaySize);
|
||||
|
||||
return SurfaceControl.screenshotToBuffer(token, crop, displaySize.x, displaySize.y,
|
||||
false, rotation);
|
||||
}
|
||||
|
||||
private void sendScreenshotSuccess(Display display, RemoteCallback callback) {
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
|
||||
final ScreenshotGraphicBuffer screenshotBuffer = takeScreenshotBuffer(display);
|
||||
final GraphicBuffer graphicBuffer = screenshotBuffer.getGraphicBuffer();
|
||||
try (HardwareBuffer hardwareBuffer =
|
||||
HardwareBuffer.createFromGraphicBuffer(graphicBuffer)) {
|
||||
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();
|
||||
final ScreenshotGraphicBuffer screenshotBuffer = LocalServices
|
||||
.getService(DisplayManagerInternal.class)
|
||||
.screenshotWithoutSecureLayer(displayId);
|
||||
if (screenshotBuffer != null) {
|
||||
sendScreenshotSuccess(screenshotBuffer, callback);
|
||||
} else {
|
||||
sendScreenshotFailure(
|
||||
AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, callback);
|
||||
}
|
||||
}, null).recycleOnUse());
|
||||
} finally {
|
||||
@@ -1064,6 +1037,29 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
|
||||
}
|
||||
}
|
||||
|
||||
private void sendScreenshotSuccess(ScreenshotGraphicBuffer screenshotBuffer,
|
||||
RemoteCallback callback) {
|
||||
final GraphicBuffer graphicBuffer = screenshotBuffer.getGraphicBuffer();
|
||||
try (HardwareBuffer hardwareBuffer =
|
||||
HardwareBuffer.createFromGraphicBuffer(graphicBuffer)) {
|
||||
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) -> {
|
||||
|
||||
@@ -1362,7 +1362,8 @@ public final class DisplayManagerService extends SystemService {
|
||||
return null;
|
||||
}
|
||||
|
||||
private SurfaceControl.ScreenshotGraphicBuffer screenshotInternal(int displayId) {
|
||||
private SurfaceControl.ScreenshotGraphicBuffer screenshotInternal(int displayId,
|
||||
boolean captureSecureLayer) {
|
||||
synchronized (mSyncRoot) {
|
||||
final IBinder token = getDisplayToken(displayId);
|
||||
if (token == null) {
|
||||
@@ -1374,9 +1375,15 @@ public final class DisplayManagerService extends SystemService {
|
||||
}
|
||||
|
||||
final DisplayInfo displayInfo = logicalDisplay.getDisplayInfoLocked();
|
||||
return SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(token, new Rect(),
|
||||
displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight(),
|
||||
false /* useIdentityTransform */, 0 /* rotation */);
|
||||
if (captureSecureLayer) {
|
||||
return SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(token, new Rect(),
|
||||
displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight(),
|
||||
false /* useIdentityTransform */, 0 /* rotation */);
|
||||
} else {
|
||||
return SurfaceControl.screenshotToBuffer(token, new Rect(),
|
||||
displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight(),
|
||||
false /* useIdentityTransform */, 0 /* rotation */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2494,7 +2501,12 @@ public final class DisplayManagerService extends SystemService {
|
||||
|
||||
@Override
|
||||
public SurfaceControl.ScreenshotGraphicBuffer screenshot(int displayId) {
|
||||
return screenshotInternal(displayId);
|
||||
return screenshotInternal(displayId, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SurfaceControl.ScreenshotGraphicBuffer screenshotWithoutSecureLayer(int displayId) {
|
||||
return screenshotInternal(displayId, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
package com.android.server.accessibility;
|
||||
|
||||
import static android.accessibilityservice.AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY;
|
||||
import static android.accessibilityservice.AccessibilityService.ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS;
|
||||
import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_HOME;
|
||||
import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_STATUS;
|
||||
import static android.accessibilityservice.AccessibilityServiceInfo.CAPABILITY_CAN_CONTROL_MAGNIFICATION;
|
||||
import static android.accessibilityservice.AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES;
|
||||
import static android.accessibilityservice.AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS;
|
||||
@@ -69,6 +72,7 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.graphics.Region;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
@@ -93,6 +97,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.Spy;
|
||||
|
||||
@@ -162,6 +167,7 @@ public class AbstractAccessibilityServiceConnectionTest {
|
||||
@Mock private IAccessibilityInteractionConnectionCallback mMockCallback;
|
||||
@Mock private FingerprintGestureDispatcher mMockFingerprintGestureDispatcher;
|
||||
@Mock private MagnificationController mMockMagnificationController;
|
||||
@Mock private RemoteCallback.OnResultListener mMockListener;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
@@ -705,6 +711,38 @@ public class AbstractAccessibilityServiceConnectionTest {
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void takeScreenshot_NoA11yAccess_returnErrorCode() throws InterruptedException {
|
||||
// no checkAccessibilityAccess, should return error code.
|
||||
when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(true);
|
||||
when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(false);
|
||||
|
||||
mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY,
|
||||
new RemoteCallback(mMockListener));
|
||||
mHandler.sendLastMessage();
|
||||
|
||||
verify(mMockListener).onResult(Mockito.argThat(
|
||||
bundle -> ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS
|
||||
== bundle.getInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void takeScreenshot_invalidDisplay_returnErrorCode() throws InterruptedException {
|
||||
when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(true);
|
||||
when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(true);
|
||||
|
||||
final DisplayManager displayManager = new DisplayManager(mMockContext);
|
||||
when(mMockContext.getSystemService(Context.DISPLAY_SERVICE)).thenReturn(displayManager);
|
||||
|
||||
mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY + 1,
|
||||
new RemoteCallback(mMockListener));
|
||||
mHandler.sendLastMessage();
|
||||
|
||||
verify(mMockListener).onResult(Mockito.argThat(
|
||||
bundle -> ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY
|
||||
== bundle.getInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS)));
|
||||
}
|
||||
|
||||
private void updateServiceInfo(AccessibilityServiceInfo serviceInfo, int eventType,
|
||||
int feedbackType, int flags, String[] packageNames, int notificationTimeout) {
|
||||
serviceInfo.eventTypes = eventType;
|
||||
|
||||
Reference in New Issue
Block a user