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

Commit 98ba8e69 authored by Mark Renouf's avatar Mark Renouf
Browse files

Expose snapshotTask for use in WindowManager

Exposes snapshotTask which will immediately take a task snapshot
instead of checking the cache. Also adds an overload which allows
specifying scaleRatio and pixelFormat

Change-Id: I0ee3d90ae0ff508e10a8b24bd47d593f28ab1ea8
parent b50bc1f6
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();
            }
        }
    }
}