diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl index 0cd9b366bf5c2..1223d72f643ed 100644 --- a/core/java/android/window/IWindowOrganizerController.aidl +++ b/core/java/android/window/IWindowOrganizerController.aidl @@ -77,17 +77,6 @@ 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); - /** * Registers a transition player with Core. There is only one of these at a time and calling * this will replace the existing one if set. diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java index bcd0e8ddf2cd3..544d422400791 100644 --- a/core/java/android/window/WindowOrganizer.java +++ b/core/java/android/window/WindowOrganizer.java @@ -25,7 +25,6 @@ import android.app.ActivityTaskManager; import android.os.IBinder; import android.os.RemoteException; import android.util.Singleton; -import android.view.SurfaceControl; /** * Base class for organizing specific types of windows like Tasks and DisplayAreas @@ -111,28 +110,6 @@ 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 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(); - } - } - /** * Register an ITransitionPlayer to handle transition animations. * @hide diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java new file mode 100644 index 0000000000000..e59845c2a068b --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2021 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.wm.shell.common; + +import android.graphics.GraphicBuffer; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.view.SurfaceControl; + +/** + * Helpers for working with screenshots. + */ +public class ScreenshotUtils { + + /** + * Take a screenshot of the specified SurfaceControl. + * + * @param t the transaction used to set changes on the resulting screenshot. + * @param sc the SurfaceControl to take a screenshot of + * @param crop the crop to use when capturing the screenshot + * + * @return A SurfaceControl where the screenshot will be attached, or null if failed. + */ + public static SurfaceControl takeScreenshot(SurfaceControl.Transaction t, SurfaceControl sc, + Rect crop) { + final SurfaceControl.ScreenshotHardwareBuffer buffer = SurfaceControl.captureLayers( + new SurfaceControl.LayerCaptureArgs.Builder(sc) + .setSourceCrop(crop) + .setCaptureSecureLayers(true) + .setAllowProtected(true) + .build() + ); + if (buffer == null || buffer.getHardwareBuffer() == null) { + return null; + } + final GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer( + buffer.getHardwareBuffer()); + final SurfaceControl screenshot = new SurfaceControl.Builder() + .setName("ScreenshotUtils screenshot") + .setFormat(PixelFormat.TRANSLUCENT) + .setSecure(buffer.containsSecureLayers()) + .setCallsite("ScreenshotUtils.takeScreenshot") + .setBLASTLayer() + .build(); + + t.setBuffer(screenshot, graphicBuffer); + t.setColorSpace(screenshot, buffer.getColorSpace()); + t.reparent(screenshot, sc); + t.setLayer(screenshot, Integer.MAX_VALUE); + t.show(screenshot); + t.apply(); + return screenshot; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index b3528712db97e..c0708c2cecad8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -70,6 +70,7 @@ import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.ScreenshotUtils; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.annotations.ShellMainThread; @@ -1119,6 +1120,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds, @PipAnimationController.TransitionDirection int direction, @PipAnimationController.AnimationType int type) { + final Rect preResizeBounds = new Rect(mPipBoundsState.getBounds()); mPipBoundsState.setBounds(destinationBounds); if (direction == TRANSITION_DIRECTION_REMOVE_STACK) { removePipImmediately(); @@ -1142,23 +1144,26 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, && mPictureInPictureParams != null && !mPictureInPictureParams.isSeamlessResizeEnabled(); if (animateCrossFadeResize) { - // Take a snapshot of the PIP task and hide it. We'll show it and fade it out after - // the wct transaction is applied and the activity is laid out again. - final SurfaceControl snapshotSurface = mTaskOrganizer.takeScreenshot(mToken); - mSurfaceTransactionHelper.reparentAndShowSurfaceSnapshot( - mSurfaceControlTransactionFactory.getTransaction(), mLeash, snapshotSurface); - mSyncTransactionQueue.queue(wct); - mSyncTransactionQueue.runInSync(t -> { - // Scale the snapshot from its pre-resize bounds to the post-resize bounds. - final Rect snapshotSrc = new Rect(0, 0, snapshotSurface.getWidth(), - snapshotSurface.getHeight()); - final Rect snapshotDest = new Rect(0, 0, destinationBounds.width(), - destinationBounds.height()); - mSurfaceTransactionHelper.scale(t, snapshotSurface, snapshotSrc, snapshotDest); + // Take a snapshot of the PIP task and show it. We'll fade it out after the wct + // transaction is applied and the activity is laid out again. + preResizeBounds.offsetTo(0, 0); + final Rect snapshotDest = new Rect(0, 0, destinationBounds.width(), + destinationBounds.height()); + final SurfaceControl snapshotSurface = ScreenshotUtils.takeScreenshot( + mSurfaceControlTransactionFactory.getTransaction(), mLeash, preResizeBounds); + if (snapshotSurface != null) { + mSyncTransactionQueue.queue(wct); + mSyncTransactionQueue.runInSync(t -> { + // Scale the snapshot from its pre-resize bounds to the post-resize bounds. + mSurfaceTransactionHelper.scale(t, snapshotSurface, preResizeBounds, + snapshotDest); - // Start animation to fade out the snapshot. - fadeOutAndRemoveOverlay(snapshotSurface); - }); + // Start animation to fade out the snapshot. + fadeOutAndRemoveOverlay(snapshotSurface); + }); + } else { + applyFinishBoundsResize(wct, direction); + } } else { applyFinishBoundsResize(wct, direction); } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index b7ad820bbdc4a..a82a478f2a4f2 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; @@ -37,8 +36,6 @@ import android.annotation.Nullable; import android.app.WindowConfiguration; import android.content.pm.ActivityInfo; import android.content.res.Configuration; -import android.graphics.GraphicBuffer; -import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Binder; import android.os.Bundle; @@ -53,7 +50,6 @@ import android.window.ITaskOrganizerController; import android.window.ITransitionPlayer; import android.window.IWindowContainerTransactionCallback; import android.window.IWindowOrganizerController; -import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; @@ -786,44 +782,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub mTransactionCallbacksByPendingSyncId.remove(syncId); } - @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.ScreenshotHardwareBuffer buffer = SurfaceControl.captureLayers( - wc.getSurfaceControl(), bounds, 1); - - if (buffer == null || buffer.getHardwareBuffer() == null) { - return false; - } - - GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer( - buffer.getHardwareBuffer()); - SurfaceControl screenshot = mService.mWindowManager.mSurfaceControlFactory.apply(null) - .setName(wc.getName() + " - Organizer Screenshot") - .setFormat(PixelFormat.TRANSLUCENT) - .setParent(wc.getParentSurfaceControl()) - .setSecure(buffer.containsSecureLayers()) - .setCallsite("WindowOrganizerController.takeScreenshot") - .setBLASTLayer() - .build(); - - SurfaceControl.Transaction transaction = mService.mWindowManager.mTransactionFactory.get(); - transaction.setBuffer(screenshot, graphicBuffer); - transaction.setColorSpace(screenshot, buffer.getColorSpace()); - transaction.apply(); - - outSurfaceControl.copyFrom(screenshot, "WindowOrganizerController.takeScreenshot"); - return true; - } - @Override public void registerTransitionPlayer(ITransitionPlayer player) { enforceTaskPermission("registerTransitionPlayer()");