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

Commit 3095609f authored by Mark Renouf's avatar Mark Renouf Committed by Android (Google) Code Review
Browse files

Merge "Expose snapshotTask for use in WindowManager"

parents 41ee2c2d 98ba8e69
Loading
Loading
Loading
Loading
+107 −0
Original line number Diff line number Diff line
@@ -2097,6 +2097,113 @@ public class ActivityManager {
                return new TaskSnapshot[size];
            }
        };

        /** Builder for a {@link TaskSnapshot} object */
        public static final class Builder {
            private long mId;
            private ComponentName mTopActivity;
            private GraphicBuffer mSnapshot;
            private ColorSpace mColorSpace;
            private int mOrientation;
            private Rect mContentInsets;
            private boolean mReducedResolution;
            private float mScaleFraction;
            private boolean mIsRealSnapshot;
            private int mWindowingMode;
            private int mSystemUiVisibility;
            private boolean mIsTranslucent;
            private int mPixelFormat;

            public Builder setId(long id) {
                mId = id;
                return this;
            }

            public Builder setTopActivityComponent(ComponentName name) {
                mTopActivity = name;
                return this;
            }

            public Builder setSnapshot(GraphicBuffer buffer) {
                mSnapshot = buffer;
                return this;
            }

            public Builder setColorSpace(ColorSpace colorSpace) {
                mColorSpace = colorSpace;
                return this;
            }

            public Builder setOrientation(int orientation) {
                mOrientation = orientation;
                return this;
            }

            public Builder setContentInsets(Rect contentInsets) {
                mContentInsets = contentInsets;
                return this;
            }

            public Builder setReducedResolution(boolean reducedResolution) {
                mReducedResolution = reducedResolution;
                return this;
            }

            public float getScaleFraction() {
                return mScaleFraction;
            }

            public Builder setScaleFraction(float scaleFraction) {
                mScaleFraction = scaleFraction;
                return this;
            }

            public Builder setIsRealSnapshot(boolean realSnapshot) {
                mIsRealSnapshot = realSnapshot;
                return this;
            }

            public Builder setWindowingMode(int windowingMode) {
                mWindowingMode = windowingMode;
                return this;
            }

            public Builder setSystemUiVisibility(int systemUiVisibility) {
                mSystemUiVisibility = systemUiVisibility;
                return this;
            }

            public Builder setIsTranslucent(boolean isTranslucent) {
                mIsTranslucent = isTranslucent;
                return this;
            }

            public int getPixelFormat() {
                return mPixelFormat;
            }

            public Builder setPixelFormat(int pixelFormat) {
                mPixelFormat = pixelFormat;
                return this;
            }

            public TaskSnapshot build() {
                return new TaskSnapshot(
                        mId,
                        mTopActivity,
                        mSnapshot,
                        mColorSpace,
                        mOrientation,
                        mContentInsets,
                        mReducedResolution,
                        mScaleFraction,
                        mIsRealSnapshot,
                        mWindowingMode,
                        mSystemUiVisibility,
                        mIsTranslucent);

            }
        }
    }

    /** @hide */
+102 −48
Original line number Diff line number Diff line
@@ -89,6 +89,14 @@ class TaskSnapshotController {
    @VisibleForTesting
    static final int SNAPSHOT_MODE_NONE = 2;

    /**
     * Constant for <code>scaleFactor</code> when calling {@link #snapshotTask} which is
     * interpreted as using the most appropriate scale ratio for the system.
     * This may yield a smaller ratio on low memory devices.
     */
    @VisibleForTesting
    static final float SNAPSHOT_SCALE_AUTO = -1f;

    private final WindowManagerService mService;

    private final TaskSnapshotCache mCache;
@@ -260,6 +268,84 @@ class TaskSnapshotController {
        });
    }

    /**
     * Validates the state of the Task is appropriate to capture a snapshot, collects
     * information from the task and populates the builder.
     *
     * @param task the task to capture
     * @param scaleFraction the scale fraction between 0-1.0, or {@link #SNAPSHOT_SCALE_AUTO}
     *                      to automatically select
     * @param pixelFormat the desired pixel format, or {@link PixelFormat#UNKNOWN} to
     *                    automatically select
     * @param builder the snapshot builder to populate
     *
     * @return true if the state of the task is ok to proceed
     */
    private boolean prepareTaskSnapshot(Task task, float scaleFraction, int pixelFormat,
            TaskSnapshot.Builder builder) {
        if (!mService.mPolicy.isScreenOn()) {
            if (DEBUG_SCREENSHOT) {
                Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
            }
            return false;
        }
        final ActivityRecord activity = findAppTokenForSnapshot(task);
        if (activity == null) {
            if (DEBUG_SCREENSHOT) {
                Slog.w(TAG_WM, "Failed to take screenshot. No visible windows for " + task);
            }
            return false;
        }
        if (activity.hasCommittedReparentToAnimationLeash()) {
            if (DEBUG_SCREENSHOT) {
                Slog.w(TAG_WM, "Failed to take screenshot. App is animating " + activity);
            }
            return false;
        }

        final WindowState mainWindow = activity.findMainWindow();
        if (mainWindow == null) {
            Slog.w(TAG_WM, "Failed to take screenshot. No main window for " + task);
            return false;
        }

        builder.setIsRealSnapshot(true);
        builder.setId(System.currentTimeMillis());
        builder.setContentInsets(getInsets(mainWindow));

        final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();

        if (scaleFraction == SNAPSHOT_SCALE_AUTO) {
            builder.setScaleFraction(isLowRamDevice
                    ? mPersister.getReducedScale()
                    : mFullSnapshotScale);
            builder.setReducedResolution(isLowRamDevice);
        } else {
            builder.setScaleFraction(scaleFraction);
            builder.setReducedResolution(scaleFraction < 1.0f);
        }

        final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
        final boolean isShowWallpaper = (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) != 0;

        if (pixelFormat == PixelFormat.UNKNOWN) {
            pixelFormat = mPersister.use16BitFormat() && activity.fillsParent()
                    && !(isWindowTranslucent && isShowWallpaper)
                    ? PixelFormat.RGB_565
                    : PixelFormat.RGBA_8888;
        }

        final boolean isTranslucent = PixelFormat.formatHasAlpha(pixelFormat)
                && (!activity.fillsParent() || isWindowTranslucent);

        builder.setTopActivityComponent(activity.mActivityComponent);
        builder.setIsTranslucent(isTranslucent);
        builder.setOrientation(activity.getTask().getConfiguration().orientation);
        builder.setWindowingMode(task.getWindowingMode());
        builder.setSystemUiVisibility(getSystemUiVisibility(task));
        return true;
    }

    @Nullable
    SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
            float scaleFraction) {
@@ -288,63 +374,31 @@ class TaskSnapshotController {
        return screenshotBuffer;
    }

    @Nullable private TaskSnapshot snapshotTask(Task task) {
        if (!mService.mPolicy.isScreenOn()) {
            if (DEBUG_SCREENSHOT) {
                Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
            }
            return null;
        }

        final ActivityRecord activity = findAppTokenForSnapshot(task);
        if (activity == null) {
            if (DEBUG_SCREENSHOT) {
                Slog.w(TAG_WM, "Failed to take screenshot. No visible windows for " + task);
            }
            return null;
        }
        if (activity.hasCommittedReparentToAnimationLeash()) {
            if (DEBUG_SCREENSHOT) {
                Slog.w(TAG_WM, "Failed to take screenshot. App is animating " + activity);
            }
            return null;
    @Nullable
    TaskSnapshot snapshotTask(Task task) {
        return snapshotTask(task, SNAPSHOT_SCALE_AUTO, PixelFormat.UNKNOWN);
    }

        final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
        final float scaleFraction = isLowRamDevice
                ? mPersister.getReducedScale()
                : mFullSnapshotScale;
    @Nullable
    TaskSnapshot snapshotTask(Task task, float scaleFraction, int pixelFormat) {
        TaskSnapshot.Builder builder = new TaskSnapshot.Builder();

        final WindowState mainWindow = activity.findMainWindow();
        if (mainWindow == null) {
            Slog.w(TAG_WM, "Failed to take screenshot. No main window for " + task);
        if (!prepareTaskSnapshot(task, scaleFraction, pixelFormat, builder)) {
            // Failed some pre-req. Has been logged.
            return null;
        }
        final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
        final boolean isShowWallpaper = (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) != 0;
        final int pixelFormat = mPersister.use16BitFormat() && activity.fillsParent()
                && !(isWindowTranslucent && isShowWallpaper)
                ? PixelFormat.RGB_565
                : PixelFormat.RGBA_8888;

        final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
                createTaskSnapshot(task, scaleFraction, pixelFormat);
                createTaskSnapshot(task, builder.getScaleFraction(),
                builder.getPixelFormat());

        if (screenshotBuffer == null) {
            if (DEBUG_SCREENSHOT) {
                Slog.w(TAG_WM, "Failed to take screenshot for " + task);
            }
            // Failed to acquire image. Has been logged.
            return null;
        }
        final boolean isTranslucent = PixelFormat.formatHasAlpha(pixelFormat)
                && (!activity.fillsParent() || isWindowTranslucent);
        return new TaskSnapshot(
                System.currentTimeMillis() /* id */,
                activity.mActivityComponent, screenshotBuffer.getGraphicBuffer(),
                screenshotBuffer.getColorSpace(),
                activity.getTask().getConfiguration().orientation,
                getInsets(mainWindow), isLowRamDevice /* reduced */, scaleFraction /* scale */,
                true /* isRealSnapshot */, task.getWindowingMode(), getSystemUiVisibility(task),
                isTranslucent);
        builder.setSnapshot(screenshotBuffer.getGraphicBuffer());
        builder.setColorSpace(screenshotBuffer.getColorSpace());
        return builder.build();
    }

    private boolean shouldDisableSnapshots() {
+65 −0
Original line number Diff line number Diff line
@@ -23,9 +23,20 @@ import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_APP_THE
import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_REAL;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.res.Configuration;
import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
import android.view.View;

import androidx.test.filters.SmallTest;

@@ -33,6 +44,7 @@ import com.google.android.collect.Sets;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;

/**
 * Test class for {@link TaskSnapshotController}.
@@ -110,4 +122,57 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
        assertEquals(SNAPSHOT_MODE_APP_THEME,
                mWm.mTaskSnapshotController.getSnapshotMode(secureWindow.getTask()));
    }

    @Test
    public void testSnapshotBuilder() {
        final GraphicBuffer buffer = Mockito.mock(GraphicBuffer.class);
        final ColorSpace sRGB = ColorSpace.get(ColorSpace.Named.SRGB);
        final long id = 1234L;
        final ComponentName activityComponent = new ComponentName("package", ".Class");
        final int windowingMode = WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
        final int systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN;
        final int pixelFormat = PixelFormat.RGBA_8888;
        final int orientation = Configuration.ORIENTATION_PORTRAIT;
        final float scaleFraction = 0.25f;
        final Rect contentInsets = new Rect(1, 2, 3, 4);

        try {
            ActivityManager.TaskSnapshot.Builder builder =
                    new ActivityManager.TaskSnapshot.Builder();
            builder.setId(id);
            builder.setTopActivityComponent(activityComponent);
            builder.setSystemUiVisibility(systemUiVisibility);
            builder.setWindowingMode(windowingMode);
            builder.setColorSpace(sRGB);
            builder.setReducedResolution(true);
            builder.setOrientation(orientation);
            builder.setContentInsets(contentInsets);
            builder.setIsTranslucent(true);
            builder.setScaleFraction(0.25f);
            builder.setSnapshot(buffer);
            builder.setIsRealSnapshot(true);
            builder.setPixelFormat(pixelFormat);

            // Not part of TaskSnapshot itself, used in screenshot process
            assertEquals(pixelFormat, builder.getPixelFormat());

            ActivityManager.TaskSnapshot snapshot = builder.build();
            assertEquals(id, snapshot.getId());
            assertEquals(activityComponent, snapshot.getTopActivityComponent());
            assertEquals(systemUiVisibility, snapshot.getSystemUiVisibility());
            assertEquals(windowingMode, snapshot.getWindowingMode());
            assertEquals(sRGB, snapshot.getColorSpace());
            assertTrue(snapshot.isReducedResolution());
            assertEquals(orientation, snapshot.getOrientation());
            assertEquals(contentInsets, snapshot.getContentInsets());
            assertTrue(snapshot.isTranslucent());
            assertEquals(scaleFraction, builder.getScaleFraction(), 0.001f);
            assertSame(buffer, snapshot.getSnapshot());
            assertTrue(snapshot.isRealSnapshot());
        } finally {
            if (buffer != null) {
                buffer.destroy();
            }
        }
    }
}