Added takeScreenshot API to WindowOrganizer

Added requests to take screenshot of a Window that's organized
by the client. The resulting screenshot will be attached to a
SurfaceControl and reparented to the requested window's parent. The
client will be responsible for showing, setting z order, etc. They can
also re-parent to another place in the hierarchy. The default is to
parent to the window's parent since that's usually where a screenshot is
placed.

Test: Builds
Bug: 152114574
Change-Id: I5c829e029f3528fdb382842e9f0474097e01cb2e
Merged-In: I5c829e029f3528fdb382842e9f0474097e01cb2e
This commit is contained in:
chaviw
2020-04-21 12:55:37 -07:00
committed by Chavi Weingarten
parent 9d07e62689
commit 5ef1e93671
3 changed files with 76 additions and 0 deletions

View File

@@ -16,9 +16,12 @@
package android.window;
import android.view.SurfaceControl;
import android.window.IDisplayAreaOrganizerController;
import android.window.ITaskOrganizerController;
import android.window.IWindowContainerTransactionCallback;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
/** @hide */
@@ -47,4 +50,15 @@ interface IWindowOrganizerController {
/** @return An interface enabling the management of display area organizers. */
IDisplayAreaOrganizerController getDisplayAreaOrganizerController();
/**
* Take a screenshot of the requested Window token and place the content of the screenshot into
* outSurfaceControl. The SurfaceControl will be a child of the token's parent, so it will be
* a sibling of the token's window
* @param token The token for the WindowContainer that should get a screenshot taken.
* @param outSurfaceControl The SurfaceControl where the screenshot will be attached.
*
* @return true if the screenshot was successful, false otherwise.
*/
boolean takeScreenshot(in WindowContainerToken token, out SurfaceControl outSurfaceControl);
}

View File

@@ -17,11 +17,13 @@
package android.window;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.app.ActivityTaskManager;
import android.os.RemoteException;
import android.util.Singleton;
import android.view.SurfaceControl;
/**
* Base class for organizing specific types of windows like Tasks and DisplayAreas
@@ -63,6 +65,28 @@ public class WindowOrganizer {
}
}
/**
* Take a screenshot for a specified Window
* @param token The token for the WindowContainer that should get a screenshot taken.
* @return A SurfaceControl where the screenshot will be attached, or null if failed.
*
* @hide
*/
@Nullable
@RequiresPermission(android.Manifest.permission.READ_FRAME_BUFFER)
public static SurfaceControl takeScreenshot(@NonNull WindowContainerToken token) {
try {
SurfaceControl surfaceControl = new SurfaceControl();
if (getWindowOrganizerController().takeScreenshot(token, surfaceControl)) {
return surfaceControl;
} else {
return null;
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
static IWindowOrganizerController getWindowOrganizerController() {
return IWindowOrganizerControllerSingleton.get();

View File

@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
@@ -26,17 +27,20 @@ import static com.android.server.wm.WindowContainer.POSITION_TOP;
import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Slog;
import android.view.Surface;
import android.view.SurfaceControl;
import android.window.IDisplayAreaOrganizerController;
import android.window.ITaskOrganizerController;
import android.window.IWindowContainerTransactionCallback;
import android.window.IWindowOrganizerController;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.util.function.pooled.PooledConsumer;
@@ -372,6 +376,40 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
mTransactionCallbacksByPendingSyncId.remove(mSyncId);
}
@Override
public boolean takeScreenshot(WindowContainerToken token, SurfaceControl outSurfaceControl) {
mService.mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "takeScreenshot()");
final WindowContainer wc = WindowContainer.fromBinder(token.asBinder());
if (wc == null) {
throw new RuntimeException("Invalid token in screenshot transaction");
}
final Rect bounds = new Rect();
wc.getBounds(bounds);
bounds.offsetTo(0, 0);
SurfaceControl.ScreenshotGraphicBuffer buffer = SurfaceControl.captureLayers(
wc.getSurfaceControl(), bounds, 1);
if (buffer == null || buffer.getGraphicBuffer() == null) {
return false;
}
SurfaceControl screenshot = mService.mWindowManager.mSurfaceControlFactory.apply(null)
.setName(wc.getName() + " - Organizer Screenshot")
.setBufferSize(bounds.width(), bounds.height())
.setFormat(PixelFormat.TRANSLUCENT)
.setParent(wc.getParentSurfaceControl())
.build();
Surface surface = new Surface();
surface.copyFrom(screenshot);
surface.attachAndQueueBufferWithColorSpace(buffer.getGraphicBuffer(), null);
surface.release();
outSurfaceControl.copyFrom(screenshot);
return true;
}
private void enforceStackPermission(String func) {
mService.mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, func);
}