diff options
5 files changed, 105 insertions, 60 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index f3759fd611df..d4f511280553 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -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); diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index ea2b9e79d99c..571537c3cc22 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -72,6 +72,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. * * @param displayId The logical display id. diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index c8b6d8dfd1b7..132b6927badd 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -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) -> { diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index a23205124f74..27375a3ab1e9 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1361,7 +1361,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) { @@ -1373,9 +1374,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 */); + } } } @@ -2474,7 +2481,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 diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java index 7bf1d98d9a3f..0445bff8fd0d 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java @@ -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; |