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

Commit 84f02a81 authored by Peter Kalauskas's avatar Peter Kalauskas
Browse files

Disable reduced scale if reduced scale config is 0

Change reduced scale implementation to toggle on/off based on
config_lowResTaskSnapshotScale=0 instead of ro.config.low_ram=true

Also, only use _reduced.jpg if reduced scale is enabled.  Previously,
for task snapshots, [0-9]+_reduced.jpg would be used if reduced scale
was disabled, and [0-9]+.jpg would never be used. This patch swaps that
behavior to make the underlying system more intuitive.  Now, if reduced
snapshots are disabled, store the task snapshot in [0-9]+.jpg and never
use [0-9]+_reduced.jpg.

Also, store low-res snapshots at config_lowResTaskSnapshotScale.
Prevously, low-res snapshots were stored at lowResScale * highResScale

Test: TaskSnapshotCacheTest
Test: TaskSnapshotControllerTest
Test: TaskSnapshotPersisterLoaderTest
Test: TaskSnapshotSurfaceTest
Bug: 148099851
Bug: 142063079
Change-Id: I5a0d58766347d875eaec138820323063aa1c2988
parent fe0a413a
Loading
Loading
Loading
Loading
+6 −19
Original line number Diff line number Diff line
@@ -2015,8 +2015,8 @@ public class ActivityManager {
        /** The size of the snapshot before scaling */
        private final Point mTaskSize;
        private final Rect mContentInsets;
        // Whether this snapshot is a down-sampled version of the full resolution, used mainly for
        // low-ram devices
        // Whether this snapshot is a down-sampled version of the high resolution snapshot, used
        // mainly for loading snapshots quickly from disk when user is flinging fast
        private final boolean mIsLowResolution;
        // Whether or not the snapshot is a real snapshot or an app-theme generated snapshot due to
        // the task having a secure window or having previews disabled
@@ -2229,7 +2229,6 @@ public class ActivityManager {
            private int mRotation;
            private Point mTaskSize;
            private Rect mContentInsets;
            private boolean mIsLowResolution;
            private boolean mIsRealSnapshot;
            private int mWindowingMode;
            private int mSystemUiVisibility;
@@ -2279,21 +2278,6 @@ public class ActivityManager {
                return this;
            }

            /**
             * Returns {@code true} if this is meant to be a low-resolution
             */
            public boolean isLowResolution() {
                return mIsLowResolution;
            }

            /**
             * Set to {@code true} if this is a low-resolution snapshot stored in *_reduced.jpg.
             */
            public Builder setIsLowResolution(boolean isLowResolution) {
                mIsLowResolution = isLowResolution;
                return this;
            }

            public Builder setIsRealSnapshot(boolean realSnapshot) {
                mIsRealSnapshot = realSnapshot;
                return this;
@@ -2333,7 +2317,10 @@ public class ActivityManager {
                        mRotation,
                        mTaskSize,
                        mContentInsets,
                        mIsLowResolution,
                        // When building a TaskSnapshot with the Builder class, isLowResolution
                        // is always false. Low-res snapshots are only created when loading from
                        // disk.
                        false /* isLowResolution */,
                        mIsRealSnapshot,
                        mWindowingMode,
                        mSystemUiVisibility,
+3 −1
Original line number Diff line number Diff line
@@ -2752,7 +2752,9 @@

    <!-- The amount to scale reduced scale snapshots for Overview and snapshot starting windows.
         Reduced scale snapshots are loaded before full screen snapshots to improve load times and
         minimize the chance the user will see an empty task card. -->
         minimize the chance the user will see an empty task card. If set to 0, reduced scale
         snapshots are disabled, and snapshots will only be stored at config_highResTaskSnapshotScale
         -->
    <item name="config_lowResTaskSnapshotScale" format="float" type="dimen">0.5</item>

    <!-- Feature flag to store TaskSnapshot in 16 bit pixel format to save memory. -->
+4 −11
Original line number Diff line number Diff line
@@ -18,14 +18,12 @@ package com.android.server.wm;

import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;

import static com.android.server.wm.TaskSnapshotPersister.DISABLE_HIGH_RES_BITMAPS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskSnapshot;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
@@ -222,7 +220,7 @@ class TaskSnapshotController {
    @Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
            boolean isLowResolution) {
        return mCache.getSnapshot(taskId, userId, restoreFromDisk, isLowResolution
                || DISABLE_HIGH_RES_BITMAPS);
                && mPersister.enableLowResSnapshots());
    }

    /**
@@ -300,12 +298,9 @@ class TaskSnapshotController {
            return false;
        }

        final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();

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

        final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
        final boolean isShowWallpaper = (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) != 0;
@@ -334,10 +329,8 @@ class TaskSnapshotController {
    SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
            TaskSnapshot.Builder builder) {
        Point taskSize = new Point();
        float scale = builder.isLowResolution()
                ? mPersister.getLowResScale() : mHighResTaskSnapshotScale;
        final SurfaceControl.ScreenshotGraphicBuffer taskSnapshot = createTaskSnapshot(task, scale,
                builder.getPixelFormat(), taskSize);
        final SurfaceControl.ScreenshotGraphicBuffer taskSnapshot = createTaskSnapshot(task,
                mHighResTaskSnapshotScale, builder.getPixelFormat(), taskSize);
        builder.setTaskSize(taskSize);
        return taskSnapshot;
    }
@@ -493,7 +486,7 @@ class TaskSnapshotController {
                topChild.mActivityComponent, hwBitmap.createGraphicBufferHandle(),
                hwBitmap.getColorSpace(), mainWindow.getConfiguration().orientation,
                mainWindow.getWindowConfiguration().getRotation(), new Point(taskWidth, taskHeight),
                getInsets(mainWindow), ActivityManager.isLowRamDeviceStatic() /* isLowResolution */,
                getInsets(mainWindow), false /* isLowResolution */,
                false /* isRealSnapshot */, task.getWindowingMode(),
                getSystemUiVisibility(task), false);
    }
+96 −22
Original line number Diff line number Diff line
@@ -48,15 +48,82 @@ class TaskSnapshotLoader {

    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotLoader" : TAG_WM;

    private static final float LEGACY_REDUCED_SCALE =
            ActivityManager.isLowRamDeviceStatic() ? 0.6f : 0.5f;

    private final TaskSnapshotPersister mPersister;

    TaskSnapshotLoader(TaskSnapshotPersister persister) {
        mPersister = persister;
    }

    static class PreRLegacySnapshotConfig {
        /**
         * If isPreRLegacy is {@code true}, specifies the scale the snapshot was taken at
         */
        final float mScale;

        /**
         * If {@code true}, always load *_reduced.jpg file, no matter what was requested
         */
        final boolean mForceLoadReducedJpeg;

        PreRLegacySnapshotConfig(float scale, boolean forceLoadReducedJpeg) {
            mScale = scale;
            mForceLoadReducedJpeg = forceLoadReducedJpeg;
        }
    }

    /**
     * When device is upgraded, we might be loading a legacy snapshot. In those cases,
     * restore the scale based on how it was configured historically. See history of
     * TaskSnapshotPersister for more information.
     *
     *   | low_ram=false                      | low_ram=true
     *   +------------------------------------------------------------------------------+
     * O | *.jpg = 100%, *_reduced.jpg = 50%                                            |
     *   |                                    +-----------------------------------------|
     * P |                                    | *.jpg = NONE, *_reduced.jpg = 60%       |
     *   +------------------------------------+-----------------------------------------+
     * Q | *.jpg = proto.scale,               | *.jpg = NONE,                           |
     *   | *_reduced.jpg = 50% * proto.scale  | *_reduced.jpg = proto.scale             |
     *   +------------------------------------+-----------------------------------------+
     *
     * @return null if Android R, otherwise a PreRLegacySnapshotConfig object
     */
    PreRLegacySnapshotConfig getLegacySnapshotConfig(int taskWidth, float legacyScale,
            boolean highResFileExists, boolean loadLowResolutionBitmap) {
        float preRLegacyScale = 0;
        boolean forceLoadReducedJpeg = false;
        boolean isPreRLegacySnapshot = (taskWidth == 0);
        if (!isPreRLegacySnapshot) {
            return null;
        }
        final boolean isPreQLegacyProto = isPreRLegacySnapshot
                && (Float.compare(legacyScale, 0f) == 0);

        if (isPreQLegacyProto) {
            // Android O or Android P
            if (ActivityManager.isLowRamDeviceStatic() && !highResFileExists) {
                // Android P w/ low_ram=true
                preRLegacyScale = 0.6f;
                // Force bitmapFile to always be *_reduced.jpg
                forceLoadReducedJpeg = true;
            } else {
                // Android O, OR Android P w/ low_ram=false
                preRLegacyScale = loadLowResolutionBitmap ? 0.5f : 1.0f;
            }
        } else if (isPreRLegacySnapshot) {
            // If not pre-Q but is pre-R, then it must be Android Q
            if (ActivityManager.isLowRamDeviceStatic()) {
                preRLegacyScale = legacyScale;
                // Force bitmapFile to always be *_reduced.jpg
                forceLoadReducedJpeg = true;
            } else {
                preRLegacyScale =
                        loadLowResolutionBitmap ? 0.5f * legacyScale : legacyScale;
            }
        }
        return new PreRLegacySnapshotConfig(preRLegacyScale, forceLoadReducedJpeg);
    }

    /**
     * Loads a task from the disk.
     * <p>
@@ -65,20 +132,32 @@ class TaskSnapshotLoader {
     *
     * @param taskId                  The id of the task to load.
     * @param userId                  The id of the user the task belonged to.
     * @param isLowResolution Whether to load a reduced resolution version of the snapshot.
     * @param loadLowResolutionBitmap Whether to load a low resolution resolution version of the
     *                                snapshot.
     * @return The loaded {@link TaskSnapshot} or {@code null} if it couldn't be loaded.
     */
    TaskSnapshot loadTask(int taskId, int userId, boolean isLowResolution) {
    TaskSnapshot loadTask(int taskId, int userId, boolean loadLowResolutionBitmap) {
        final File protoFile = mPersister.getProtoFile(taskId, userId);
        final File bitmapFile = isLowResolution
                ? mPersister.getLowResolutionBitmapFile(taskId, userId)
                : mPersister.getHighResolutionBitmapFile(taskId, userId);
        if (bitmapFile == null || !protoFile.exists() || !bitmapFile.exists()) {
        if (!protoFile.exists()) {
            return null;
        }
        try {
            final byte[] bytes = Files.readAllBytes(protoFile.toPath());
            final TaskSnapshotProto proto = TaskSnapshotProto.parseFrom(bytes);
            final File highResBitmap = mPersister.getHighResolutionBitmapFile(taskId, userId);

            PreRLegacySnapshotConfig legacyConfig = getLegacySnapshotConfig(proto.taskWidth,
                    proto.legacyScale, highResBitmap.exists(), loadLowResolutionBitmap);

            boolean forceLoadReducedJpeg =
                    legacyConfig != null && legacyConfig.mForceLoadReducedJpeg;
            File bitmapFile = (loadLowResolutionBitmap || forceLoadReducedJpeg)
                    ? mPersister.getLowResolutionBitmapFile(taskId, userId) : highResBitmap;

            if (!bitmapFile.exists()) {
                return null;
            }

            final Options options = new Options();
            options.inPreferredConfig = mPersister.use16BitFormat() && !proto.isTranslucent
                    ? Config.RGB_565
@@ -105,24 +184,19 @@ class TaskSnapshotLoader {
            final ComponentName topActivityComponent = ComponentName.unflattenFromString(
                    proto.topActivityComponent);

            // For legacy snapshots, restore the scale based on the reduced resolution state
            Point taskSize;
            if (proto.taskWidth == 0) {
                // For legacy snapshots, restore the scale based on the reduced resolution state
                final float preQLegacyScale = isLowResolution ? LEGACY_REDUCED_SCALE : 1f;
                final float scale = Float.compare(proto.legacyScale, 0f) != 0
                        ? proto.legacyScale : preQLegacyScale;
                int taskWidth = (int) ((float) hwBitmap.getWidth() / scale);
                int taskHeight = (int) ((float) hwBitmap.getHeight() / scale);
            if (legacyConfig != null) {
                int taskWidth = (int) ((float) hwBitmap.getWidth() / legacyConfig.mScale);
                int taskHeight = (int) ((float) hwBitmap.getHeight() / legacyConfig.mScale);
                taskSize = new Point(taskWidth, taskHeight);
            } else {
                taskSize = new Point(proto.taskWidth, proto.taskHeight);
            }

            return new TaskSnapshot(proto.id, topActivityComponent, buffer, hwBitmap.getColorSpace(),
                    proto.orientation, proto.rotation, taskSize,
            return new TaskSnapshot(proto.id, topActivityComponent, buffer,
                    hwBitmap.getColorSpace(), proto.orientation, proto.rotation, taskSize,
                    new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom),
                    isLowResolution, proto.isRealSnapshot, proto.windowingMode,
                    loadLowResolutionBitmap, proto.isRealSnapshot, proto.windowingMode,
                    proto.systemUiVisibility, proto.isTranslucent);
        } catch (IOException e) {
            Slog.w(TAG, "Unable to load task snapshot data for taskId=" + taskId);
+55 −42
Original line number Diff line number Diff line
@@ -21,8 +21,8 @@ import static android.graphics.Bitmap.CompressFormat.JPEG;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;

import android.annotation.NonNull;
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskSnapshot;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
@@ -52,8 +52,6 @@ class TaskSnapshotPersister {
    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotPersister" : TAG_WM;
    private static final String SNAPSHOTS_DIRNAME = "snapshots";
    private static final String LOW_RES_FILE_POSTFIX = "_reduced";
    private static final float LOW_RAM_REDUCED_SCALE = .8f;
    static final boolean DISABLE_HIGH_RES_BITMAPS = ActivityManager.isLowRamDeviceStatic();
    private static final long DELAY_MS = 100;
    private static final int QUALITY = 95;
    private static final String PROTO_EXTENSION = ".proto";
@@ -71,7 +69,8 @@ class TaskSnapshotPersister {
    private boolean mStarted;
    private final Object mLock = new Object();
    private final DirectoryResolver mDirectoryResolver;
    private final float mLowResScale;
    private final float mLowResScaleFactor;
    private boolean mEnableLowResSnapshots;
    private final boolean mUse16BitFormat;

    /**
@@ -83,13 +82,29 @@ class TaskSnapshotPersister {

    TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) {
        mDirectoryResolver = resolver;
        final float highResTaskSnapshotScale = service.mContext.getResources().getFloat(
                com.android.internal.R.dimen.config_highResTaskSnapshotScale);
        final float lowResTaskSnapshotScale = service.mContext.getResources().getFloat(
                com.android.internal.R.dimen.config_lowResTaskSnapshotScale);

        if (lowResTaskSnapshotScale < 0 || 1 <= lowResTaskSnapshotScale) {
            throw new RuntimeException("Low-res scale must be between 0 and 1");
        }
        if (highResTaskSnapshotScale <= 0 || 1 < highResTaskSnapshotScale) {
            throw new RuntimeException("High-res scale must be between 0 and 1");
        }
        if (highResTaskSnapshotScale <= lowResTaskSnapshotScale) {
            throw new RuntimeException("High-res scale must be greater than low-res scale");
        }

        if (ActivityManager.isLowRamDeviceStatic()) {
            mLowResScale = LOW_RAM_REDUCED_SCALE;
        if (lowResTaskSnapshotScale > 0) {
            mLowResScaleFactor = lowResTaskSnapshotScale / highResTaskSnapshotScale;
            setEnableLowResSnapshots(true);
        } else {
            mLowResScale = service.mContext.getResources().getFloat(
                    com.android.internal.R.dimen.config_lowResTaskSnapshotScale);
            mLowResScaleFactor = 0;
            setEnableLowResSnapshots(false);
        }

        mUse16BitFormat = service.mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_use16BitTaskSnapshotPixelFormat);
    }
@@ -155,13 +170,16 @@ class TaskSnapshotPersister {
        }
    }

    boolean enableLowResSnapshots() {
        return mEnableLowResSnapshots;
    }

    /**
     * Gets the scaling the persister uses for low resolution task snapshots.
     *
     * @return the lowResBitmap scale of task snapshots when they are set to be low res
     * Not to be used. Only here for testing.
     */
    float getLowResScale() {
        return mLowResScale;
    @VisibleForTesting
    void setEnableLowResSnapshots(boolean enabled) {
        mEnableLowResSnapshots = enabled;
    }

    /**
@@ -213,14 +231,10 @@ class TaskSnapshotPersister {
    }

    File getHighResolutionBitmapFile(int taskId, int userId) {
        // Full sized bitmaps are disabled on low ram devices
        if (DISABLE_HIGH_RES_BITMAPS) {
            Slog.wtf(TAG, "This device does not support full sized resolution bitmaps.");
            return null;
        }
        return new File(getDirectory(userId), taskId + BITMAP_EXTENSION);
    }

    @NonNull
    File getLowResolutionBitmapFile(int taskId, int userId) {
        return new File(getDirectory(userId), taskId + LOW_RES_FILE_POSTFIX + BITMAP_EXTENSION);
    }
@@ -234,11 +248,11 @@ class TaskSnapshotPersister {
        final File protoFile = getProtoFile(taskId, userId);
        final File bitmapLowResFile = getLowResolutionBitmapFile(taskId, userId);
        protoFile.delete();
        if (bitmapLowResFile.exists()) {
            bitmapLowResFile.delete();

        // Low ram devices do not have a full sized file to delete
        if (!DISABLE_HIGH_RES_BITMAPS) {
        }
        final File bitmapFile = getHighResolutionBitmapFile(taskId, userId);
        if (bitmapFile.exists()) {
            bitmapFile.delete();
        }
    }
@@ -380,39 +394,38 @@ class TaskSnapshotPersister {
            }

            final Bitmap swBitmap = bitmap.copy(Config.ARGB_8888, false /* isMutable */);
            final Bitmap lowResBitmap = mSnapshot.isLowResolution()
                    ? swBitmap
                    : Bitmap.createScaledBitmap(swBitmap,
                            (int) (bitmap.getWidth() * mLowResScale),
                            (int) (bitmap.getHeight() * mLowResScale), true /* filter */);

            final File lowResFile = getLowResolutionBitmapFile(mTaskId, mUserId);
            final File file = getHighResolutionBitmapFile(mTaskId, mUserId);
            try {
                FileOutputStream lowResFos = new FileOutputStream(lowResFile);
                lowResBitmap.compress(JPEG, QUALITY, lowResFos);
                lowResFos.close();
                FileOutputStream fos = new FileOutputStream(file);
                swBitmap.compress(JPEG, QUALITY, fos);
                fos.close();
            } catch (IOException e) {
                Slog.e(TAG, "Unable to open " + lowResFile + " for persisting.", e);
                Slog.e(TAG, "Unable to open " + file + " for persisting.", e);
                return false;
            }
            lowResBitmap.recycle();

            // For snapshots with lowResBitmap resolution, do not create or save full sized bitmaps
            if (mSnapshot.isLowResolution()) {
            if (!enableLowResSnapshots()) {
                swBitmap.recycle();
                return true;
            }

            final File file = getHighResolutionBitmapFile(mTaskId, mUserId);
            final Bitmap lowResBitmap = Bitmap.createScaledBitmap(swBitmap,
                    (int) (bitmap.getWidth() * mLowResScaleFactor),
                    (int) (bitmap.getHeight() * mLowResScaleFactor), true /* filter */);
            swBitmap.recycle();

            final File lowResFile = getLowResolutionBitmapFile(mTaskId, mUserId);
            try {
                FileOutputStream fos = new FileOutputStream(file);
                swBitmap.compress(JPEG, QUALITY, fos);
                fos.close();
                FileOutputStream lowResFos = new FileOutputStream(lowResFile);
                lowResBitmap.compress(JPEG, QUALITY, lowResFos);
                lowResFos.close();
            } catch (IOException e) {
                Slog.e(TAG, "Unable to open " + file + " for persisting.", e);
                Slog.e(TAG, "Unable to open " + lowResFile + " for persisting.", e);
                return false;
            }
            swBitmap.recycle();
            lowResBitmap.recycle();

            return true;
        }
    }
Loading