Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e1b98d22 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Consider transition change info for task snapshot

For example, when launching an activity in a new task, the activity
requests to change orientation at runtime before it is drawn.
The transition info will be like:
 type=OPEN {
  All changes: sb=(0, 0 - 1080, 2340) eb=(0, 0 - 2340, 1080) r=0->1
  opening task: mode=OPEN
  closing task: mode=TO_BACK
  display: mode=CHANGE
 }

Because the configuration of tasks will be updated with display,
the closing task also has the configuration in new orientation.
But the activity in the closing task still keeps the appearance
in original orientation, so the attributes of snapshot should be
consistent with it. Otherwise the task snapshot may show a rotated
and cropped activity.

Also remove "inFinishingTransition" condition in createSnapshot
because currently RecentsTransitionHandler will take snapshot
from ATMS#takeTaskSnapshot before the transition is finished.

Bug: 301670257
Bug: 279678674
Test: TaskSnapshotControllerTest
Change-Id: If030f175bc60804d8b1f90fc88b670789fee4026
parent 53a1eff9
Loading
Loading
Loading
Loading
+42 −40
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.graphics.Point;
@@ -81,7 +82,14 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,

    protected final WindowManagerService mService;
    protected final float mHighResSnapshotScale;
    private final Rect mTmpRect = new Rect();

    /**
     * The transition change info of the target to capture screenshot. It is only non-null when
     * capturing a snapshot with a given change info. It must be cleared after
     * {@link #recordSnapshotInner} is done.
     */
    protected Transition.ChangeInfo mCurrentChangeInfo;

    /**
     * Flag indicating whether we are running on an Android TV device.
     */
@@ -203,12 +211,15 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
    @Nullable
    TaskSnapshot snapshot(TYPE source) {
        TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
        if (!prepareTaskSnapshot(source, builder)) {
        final Rect crop = prepareTaskSnapshot(source, builder);
        if (crop == null) {
            // Failed some pre-req. Has been logged.
            return null;
        }
        final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
                createSnapshot(source, builder);
        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "createSnapshot");
        final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = createSnapshot(source,
                mHighResSnapshotScale, crop, builder);
        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
        if (screenshotBuffer == null) {
            // Failed to acquire image. Has been logged.
            return null;
@@ -221,37 +232,13 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,

    @Nullable
    ScreenCapture.ScreenshotHardwareBuffer createSnapshot(@NonNull TYPE source,
            TaskSnapshot.Builder builder) {
        Point taskSize = new Point();
        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "createSnapshot");
        final ScreenCapture.ScreenshotHardwareBuffer taskSnapshot = createSnapshot(source,
                mHighResSnapshotScale, builder.getPixelFormat(), taskSize, builder);
        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
        builder.setTaskSize(taskSize);
        return taskSnapshot;
    }

    @Nullable
    ScreenCapture.ScreenshotHardwareBuffer createSnapshot(@NonNull TYPE source,
            float scaleFraction, int pixelFormat, Point outTaskSize, TaskSnapshot.Builder builder) {
            float scaleFraction, Rect crop, TaskSnapshot.Builder builder) {
        if (source.getSurfaceControl() == null) {
            if (DEBUG_SCREENSHOT) {
                Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + source);
            }
            return null;
        }
        mTmpRect.setEmpty();
        if (source.mTransitionController.inFinishingTransition(source)) {
            final Transition.ChangeInfo changeInfo = source.mTransitionController
                    .mFinishingTransition.mChanges.get(source);
            if (changeInfo != null) {
                mTmpRect.set(changeInfo.mAbsoluteBounds);
            }
        }
        if (mTmpRect.isEmpty()) {
            source.getBounds(mTmpRect);
        }
        mTmpRect.offsetTo(0, 0);
        SurfaceControl[] excludeLayers;
        final WindowState imeWindow = source.getDisplayContent().mInputMethodWindow;
        // Exclude IME window snapshot when IME isn't proper to attach to app.
@@ -277,12 +264,8 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
        builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isVisible());
        final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
                ScreenCapture.captureLayersExcluding(
                        source.getSurfaceControl(), mTmpRect, scaleFraction,
                        pixelFormat, excludeLayers);
        if (outTaskSize != null) {
            outTaskSize.x = mTmpRect.width();
            outTaskSize.y = mTmpRect.height();
        }
                        source.getSurfaceControl(), crop, scaleFraction,
                        builder.getPixelFormat(), excludeLayers);
        final HardwareBuffer buffer = screenshotBuffer == null ? null
                : screenshotBuffer.getHardwareBuffer();
        if (isInvalidHardwareBuffer(buffer)) {
@@ -306,10 +289,11 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
     * @return true if the state of the task is ok to proceed
     */
    @VisibleForTesting
    boolean prepareTaskSnapshot(TYPE source, TaskSnapshot.Builder builder) {
    @Nullable
    Rect prepareTaskSnapshot(TYPE source, TaskSnapshot.Builder builder) {
        final Pair<ActivityRecord, WindowState> result = checkIfReadyToSnapshot(source);
        if (result == null) {
            return false;
            return null;
        }
        final ActivityRecord activity = result.first;
        final WindowState mainWindow = result.second;
@@ -335,11 +319,29 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
        builder.setTopActivityComponent(activity.mActivityComponent);
        builder.setPixelFormat(pixelFormat);
        builder.setIsTranslucent(isTranslucent);
        builder.setOrientation(activity.getTask().getConfiguration().orientation);
        builder.setRotation(activity.getTask().getDisplayContent().getRotation());
        builder.setWindowingMode(source.getWindowingMode());
        builder.setAppearance(getAppearance(source));
        return true;

        final Configuration taskConfig = activity.getTask().getConfiguration();
        final int displayRotation = taskConfig.windowConfiguration.getDisplayRotation();
        final Rect outCrop = new Rect();
        final Transition.ChangeInfo changeInfo = mCurrentChangeInfo;
        if (changeInfo != null && changeInfo.mRotation != displayRotation) {
            // For example, the source is closing and display rotation changes at the same time.
            // The snapshot should record the state in previous rotation.
            outCrop.set(changeInfo.mAbsoluteBounds);
            builder.setRotation(changeInfo.mRotation);
            builder.setOrientation(changeInfo.mAbsoluteBounds.height()
                    >= changeInfo.mAbsoluteBounds.width()
                    ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE);
        } else {
            outCrop.set(taskConfig.windowConfiguration.getBounds());
            builder.setRotation(displayRotation);
            builder.setOrientation(taskConfig.orientation);
        }
        outCrop.offsetTo(0, 0);
        builder.setTaskSize(new Point(outCrop.right, outCrop.bottom));
        return outCrop;
    }

    /**
+1 −1
Original line number Diff line number Diff line
@@ -86,7 +86,7 @@ class SnapshotController {
            if (info.mContainer.isActivityTypeHome()) continue;
            final Task task = info.mContainer.asTask();
            if (task != null && !task.isVisibleRequested()) {
                mTaskSnapshotController.recordSnapshot(task);
                mTaskSnapshotController.recordSnapshot(task, info);
            }
            // Won't need to capture activity snapshot in close transition.
            if (isTransitionClose) {
+14 −0
Original line number Diff line number Diff line
@@ -150,6 +150,20 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot
        }
    }

    /**
     * The attributes of task snapshot are based on task configuration. But sometimes the
     * configuration may have been changed during a transition, so supply the ChangeInfo that
     * stored the previous appearance of the closing task.
     */
    void recordSnapshot(Task task, Transition.ChangeInfo changeInfo) {
        mCurrentChangeInfo = changeInfo;
        try {
            recordSnapshot(task);
        } finally {
            mCurrentChangeInfo = null;
        }
    }

    TaskSnapshot recordSnapshot(Task task) {
        final TaskSnapshot snapshot = recordSnapshotInner(task);
        if (snapshot != null && !task.isActivityTypeHome()) {
+4 −4
Original line number Diff line number Diff line
@@ -216,7 +216,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
        try {
            final TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
            mWm.mTaskSnapshotController.createSnapshot(mAppWindow.mActivityRecord.getTask(),
                    1f /* scaleFraction */, PixelFormat.UNKNOWN, null /* outTaskSize */, builder);
                    1f /* scaleFraction */, new Rect() /* crop */, builder);
        } catch (NullPointerException e) {
            fail("There should be no exception when calling createTaskSnapshot");
        }
@@ -238,7 +238,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
            spyOn(builder);
            mWm.mTaskSnapshotController.createSnapshot(
                    mAppWindow.mActivityRecord.getTask(), 1f /* scaleFraction */,
                    PixelFormat.UNKNOWN, null /* outTaskSize */, builder);
                    new Rect() /* crop */, builder);
            // Verify the builder should includes IME surface.
            verify(builder).setHasImeSurface(eq(true));
            builder.setColorSpace(ColorSpace.get(ColorSpace.Named.SRGB));
@@ -261,7 +261,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
        final TaskSnapshot.Builder builder =
                new TaskSnapshot.Builder();
        boolean success = mWm.mTaskSnapshotController.prepareTaskSnapshot(
                mAppWindow.mActivityRecord.getTask(), builder);
                mAppWindow.mActivityRecord.getTask(), builder) != null;

        assertTrue(success);
        // The pixel format should be selected automatically.
@@ -270,7 +270,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
        // Snapshot should not be taken while the rotation of activity and task are different.
        doReturn(true).when(mAppWindow.mActivityRecord).hasFixedRotationTransform();
        success = mWm.mTaskSnapshotController.prepareTaskSnapshot(
                mAppWindow.mActivityRecord.getTask(), builder);
                mAppWindow.mActivityRecord.getTask(), builder) != null;

        assertFalse(success);
    }