diff options
| author | 2020-02-25 18:29:50 +0000 | |
|---|---|---|
| committer | 2020-02-25 18:29:50 +0000 | |
| commit | 81a320279e8f917c143b79aed5c8fd83d9456777 (patch) | |
| tree | 96cf5111343d79679081f49de3de64aeba844b05 | |
| parent | 7def6932cb9734626310c41ca538243e743f589d (diff) | |
| parent | 84f02a81e865cecf42e938b00b28c10091708373 (diff) | |
Merge changes from topic "new-task-snapshots-rvc-dev" into rvc-dev
* changes:
Disable reduced scale if reduced scale config is 0
Store original task width and height instead of scale
12 files changed, 467 insertions, 174 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 82fdb90be165..b51bbdf62286 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -2012,15 +2012,16 @@ public class ActivityManager { /** See {@link android.view.Surface.Rotation} */ @Surface.Rotation private int mRotation; + /** 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 private final boolean mIsRealSnapshot; private final int mWindowingMode; - private final float mScale; private final int mSystemUiVisibility; private final boolean mIsTranslucent; // Must be one of the named color spaces, otherwise, always use SRGB color space. @@ -2028,9 +2029,9 @@ public class ActivityManager { public TaskSnapshot(long id, @NonNull ComponentName topActivityComponent, GraphicBuffer snapshot, - @NonNull ColorSpace colorSpace, int orientation, int rotation, Rect contentInsets, - boolean isLowResolution, float scale, boolean isRealSnapshot, int windowingMode, - int systemUiVisibility, boolean isTranslucent) { + @NonNull ColorSpace colorSpace, int orientation, int rotation, Point taskSize, + Rect contentInsets, boolean isLowResolution, boolean isRealSnapshot, + int windowingMode, int systemUiVisibility, boolean isTranslucent) { mId = id; mTopActivityComponent = topActivityComponent; mSnapshot = snapshot; @@ -2038,9 +2039,9 @@ public class ActivityManager { ? ColorSpace.get(ColorSpace.Named.SRGB) : colorSpace; mOrientation = orientation; mRotation = rotation; + mTaskSize = new Point(taskSize); mContentInsets = new Rect(contentInsets); mIsLowResolution = isLowResolution; - mScale = scale; mIsRealSnapshot = isRealSnapshot; mWindowingMode = windowingMode; mSystemUiVisibility = systemUiVisibility; @@ -2057,9 +2058,9 @@ public class ActivityManager { : ColorSpace.get(ColorSpace.Named.SRGB); mOrientation = source.readInt(); mRotation = source.readInt(); + mTaskSize = source.readParcelable(null /* classLoader */); mContentInsets = source.readParcelable(null /* classLoader */); mIsLowResolution = source.readBoolean(); - mScale = source.readFloat(); mIsRealSnapshot = source.readBoolean(); mWindowingMode = source.readInt(); mSystemUiVisibility = source.readInt(); @@ -2111,6 +2112,14 @@ public class ActivityManager { } /** + * @return The size of the task at the point this snapshot was taken. + */ + @UnsupportedAppUsage + public Point getTaskSize() { + return mTaskSize; + } + + /** * @return The system/content insets on the snapshot. These can be clipped off in order to * remove any areas behind system bars in the snapshot. */ @@ -2159,14 +2168,6 @@ public class ActivityManager { return mSystemUiVisibility; } - /** - * @return The scale this snapshot was taken in. - */ - @UnsupportedAppUsage - public float getScale() { - return mScale; - } - @Override public int describeContents() { return 0; @@ -2180,9 +2181,9 @@ public class ActivityManager { dest.writeInt(mColorSpace.getId()); dest.writeInt(mOrientation); dest.writeInt(mRotation); + dest.writeParcelable(mTaskSize, 0); dest.writeParcelable(mContentInsets, 0); dest.writeBoolean(mIsLowResolution); - dest.writeFloat(mScale); dest.writeBoolean(mIsRealSnapshot); dest.writeInt(mWindowingMode); dest.writeInt(mSystemUiVisibility); @@ -2200,9 +2201,11 @@ public class ActivityManager { + " mColorSpace=" + mColorSpace.toString() + " mOrientation=" + mOrientation + " mRotation=" + mRotation + + " mTaskSize=" + mTaskSize.toString() + " mContentInsets=" + mContentInsets.toShortString() - + " mIsLowResolution=" + mIsLowResolution + " mScale=" + mScale - + " mIsRealSnapshot=" + mIsRealSnapshot + " mWindowingMode=" + mWindowingMode + + " mIsLowResolution=" + mIsLowResolution + + " mIsRealSnapshot=" + mIsRealSnapshot + + " mWindowingMode=" + mWindowingMode + " mSystemUiVisibility=" + mSystemUiVisibility + " mIsTranslucent=" + mIsTranslucent; } @@ -2224,9 +2227,8 @@ public class ActivityManager { private ColorSpace mColorSpace; private int mOrientation; private int mRotation; + private Point mTaskSize; private Rect mContentInsets; - private boolean mIsLowResolution; - private float mScaleFraction; private boolean mIsRealSnapshot; private int mWindowingMode; private int mSystemUiVisibility; @@ -2263,25 +2265,16 @@ public class ActivityManager { return this; } - public Builder setContentInsets(Rect contentInsets) { - mContentInsets = contentInsets; - return this; - } - /** - * Set to true if this is a low-resolution snapshot stored in *_reduced.jpg. + * Sets the original size of the task */ - public Builder setIsLowResolution(boolean isLowResolution) { - mIsLowResolution = isLowResolution; + public Builder setTaskSize(Point size) { + mTaskSize = size; return this; } - public float getScaleFraction() { - return mScaleFraction; - } - - public Builder setScaleFraction(float scaleFraction) { - mScaleFraction = scaleFraction; + public Builder setContentInsets(Rect contentInsets) { + mContentInsets = contentInsets; return this; } @@ -2322,9 +2315,12 @@ public class ActivityManager { mColorSpace, mOrientation, mRotation, + mTaskSize, mContentInsets, - mIsLowResolution, - mScaleFraction, + // 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, diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 490892ed9310..fb9158fb2e98 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -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. --> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java index 4474a49e55b8..eca6ebf7f8e5 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java @@ -62,7 +62,9 @@ public class ThumbnailData { orientation = snapshot.getOrientation(); rotation = snapshot.getRotation(); reducedResolution = snapshot.isLowResolution(); - scale = snapshot.getScale(); + // TODO(b/149579527): Pass task size instead of computing scale. + // Assume width and height were scaled the same; compute scale only for width + scale = (float) thumbnail.getWidth() / snapshot.getTaskSize().x; isRealSnapshot = snapshot.isRealSnapshot(); isTranslucent = snapshot.isTranslucent(); windowingMode = snapshot.getWindowingMode(); diff --git a/proto/src/task_snapshot.proto b/proto/src/task_snapshot.proto index 789019ce8b75..2006fb3d7bf1 100644 --- a/proto/src/task_snapshot.proto +++ b/proto/src/task_snapshot.proto @@ -32,7 +32,12 @@ int32 system_ui_visibility = 8; bool is_translucent = 9; string top_activity_component = 10; - float scale = 11; + // deprecated because original width and height are stored now instead of the scale. + float legacy_scale = 11 [deprecated=true]; int64 id = 12; int32 rotation = 13; + // The task width when the snapshot was taken + int32 task_width = 14; + // The task height when the snapshot was taken + int32 task_height = 15; } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 5c73f92ee6cd..f83b0522846c 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -18,19 +18,18 @@ 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; import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RenderNode; @@ -89,14 +88,6 @@ 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; @@ -229,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()); } /** @@ -273,8 +264,6 @@ class TaskSnapshotController { * 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 @@ -282,8 +271,7 @@ class TaskSnapshotController { * @return true if the state of the task is ok to proceed */ @VisibleForTesting - boolean prepareTaskSnapshot(Task task, float scaleFraction, int pixelFormat, - TaskSnapshot.Builder builder) { + boolean prepareTaskSnapshot(Task task, int pixelFormat, TaskSnapshot.Builder builder) { if (!mService.mPolicy.isScreenOn()) { if (DEBUG_SCREENSHOT) { Slog.i(TAG_WM, "Attempted to take screenshot while display was off."); @@ -314,18 +302,6 @@ class TaskSnapshotController { builder.setId(System.currentTimeMillis()); builder.setContentInsets(getInsets(mainWindow)); - final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic(); - - if (scaleFraction == SNAPSHOT_SCALE_AUTO) { - builder.setScaleFraction(isLowRamDevice - ? mPersister.getLowResScale() - : mHighResTaskSnapshotScale); - builder.setIsLowResolution(isLowRamDevice); - } else { - builder.setScaleFraction(scaleFraction); - builder.setIsLowResolution(scaleFraction < 1.0f); - } - final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE; final boolean isShowWallpaper = (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) != 0; @@ -351,13 +327,23 @@ class TaskSnapshotController { @Nullable SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task, + TaskSnapshot.Builder builder) { + Point taskSize = new Point(); + final SurfaceControl.ScreenshotGraphicBuffer taskSnapshot = createTaskSnapshot(task, + mHighResTaskSnapshotScale, builder.getPixelFormat(), taskSize); + builder.setTaskSize(taskSize); + return taskSnapshot; + } + + @Nullable + SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task, float scaleFraction) { - return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888); + return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888, null); } @Nullable SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task, - float scaleFraction, int pixelFormat) { + float scaleFraction, int pixelFormat, Point outTaskSize) { if (task.getSurfaceControl() == null) { if (DEBUG_SCREENSHOT) { Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + task); @@ -369,6 +355,10 @@ class TaskSnapshotController { final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer = SurfaceControl.captureLayers( task.getSurfaceControl(), mTmpRect, scaleFraction, pixelFormat); + if (outTaskSize != null) { + outTaskSize.x = mTmpRect.width(); + outTaskSize.y = mTmpRect.height(); + } final GraphicBuffer buffer = screenshotBuffer != null ? screenshotBuffer.getGraphicBuffer() : null; if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) { @@ -379,21 +369,20 @@ class TaskSnapshotController { @Nullable TaskSnapshot snapshotTask(Task task) { - return snapshotTask(task, SNAPSHOT_SCALE_AUTO, PixelFormat.UNKNOWN); + return snapshotTask(task, PixelFormat.UNKNOWN); } @Nullable - TaskSnapshot snapshotTask(Task task, float scaleFraction, int pixelFormat) { + TaskSnapshot snapshotTask(Task task, int pixelFormat) { TaskSnapshot.Builder builder = new TaskSnapshot.Builder(); - if (!prepareTaskSnapshot(task, scaleFraction, pixelFormat, builder)) { + if (!prepareTaskSnapshot(task, pixelFormat, builder)) { // Failed some pre-req. Has been logged. return null; } final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer = - createTaskSnapshot(task, builder.getScaleFraction(), - builder.getPixelFormat()); + createTaskSnapshot(task, builder); if (screenshotBuffer == null) { // Failed to acquire image. Has been logged. @@ -472,8 +461,10 @@ class TaskSnapshotController { final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags, attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(), mHighResTaskSnapshotScale, mainWindow.getRequestedInsetsState()); - final int width = (int) (task.getBounds().width() * mHighResTaskSnapshotScale); - final int height = (int) (task.getBounds().height() * mHighResTaskSnapshotScale); + final int taskWidth = task.getBounds().width(); + final int taskHeight = task.getBounds().height(); + final int width = (int) (taskWidth * mHighResTaskSnapshotScale); + final int height = (int) (taskHeight * mHighResTaskSnapshotScale); final RenderNode node = RenderNode.create("TaskSnapshotController", null); node.setLeftTopRightBottom(0, 0, width, height); @@ -494,9 +485,9 @@ class TaskSnapshotController { System.currentTimeMillis() /* id */, topChild.mActivityComponent, hwBitmap.createGraphicBufferHandle(), hwBitmap.getColorSpace(), mainWindow.getConfiguration().orientation, - mainWindow.getWindowConfiguration().getRotation(), - getInsets(mainWindow), ActivityManager.isLowRamDeviceStatic() /* isLowResolution */, - mHighResTaskSnapshotScale, false /* isRealSnapshot */, task.getWindowingMode(), + mainWindow.getWindowConfiguration().getRotation(), new Point(taskWidth, taskHeight), + getInsets(mainWindow), false /* isLowResolution */, + false /* isRealSnapshot */, task.getWindowingMode(), getSystemUiVisibility(task), false); } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java index 01f3427d78e1..c20ce5f40bc2 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.app.ActivityManager; import android.app.ActivityManager.TaskSnapshot; import android.content.ComponentName; import android.graphics.Bitmap; @@ -26,6 +27,7 @@ import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; import android.graphics.GraphicBuffer; +import android.graphics.Point; import android.graphics.Rect; import android.util.Slog; @@ -52,28 +54,110 @@ class TaskSnapshotLoader { 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> * Do not hold the window manager lock when calling this method, as we directly read data from * disk here, which might be slow. * - * @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 taskId The id of the task to load. + * @param userId The id of the user the task belonged to. + * @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 @@ -99,13 +183,20 @@ class TaskSnapshotLoader { final ComponentName topActivityComponent = ComponentName.unflattenFromString( proto.topActivityComponent); - // For legacy snapshots, restore the scale based on the reduced resolution state - final float legacyScale = isLowResolution ? mPersister.getLowResScale() : 1f; - final float scale = Float.compare(proto.scale, 0f) != 0 ? proto.scale : legacyScale; - return new TaskSnapshot(proto.id, topActivityComponent, buffer, hwBitmap.getColorSpace(), - proto.orientation, proto.rotation, + + Point taskSize; + 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, new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom), - isLowResolution, scale, 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); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java index 31212b8a6bd9..164d3e055e94 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java @@ -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 (ActivityManager.isLowRamDeviceStatic()) { - mLowResScale = LOW_RAM_REDUCED_SCALE; + 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 (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(); - 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 (bitmapLowResFile.exists()) { + bitmapLowResFile.delete(); + } + final File bitmapFile = getHighResolutionBitmapFile(taskId, userId); + if (bitmapFile.exists()) { bitmapFile.delete(); } } @@ -343,6 +357,8 @@ class TaskSnapshotPersister { final TaskSnapshotProto proto = new TaskSnapshotProto(); proto.orientation = mSnapshot.getOrientation(); proto.rotation = mSnapshot.getRotation(); + proto.taskWidth = mSnapshot.getTaskSize().x; + proto.taskHeight = mSnapshot.getTaskSize().y; proto.insetLeft = mSnapshot.getContentInsets().left; proto.insetTop = mSnapshot.getContentInsets().top; proto.insetRight = mSnapshot.getContentInsets().right; @@ -352,7 +368,6 @@ class TaskSnapshotPersister { proto.systemUiVisibility = mSnapshot.getSystemUiVisibility(); proto.isTranslucent = mSnapshot.isTranslucent(); proto.topActivityComponent = mSnapshot.getTopActivityComponent().flattenToString(); - proto.scale = mSnapshot.getScale(); proto.id = mSnapshot.getId(); final byte[] bytes = TaskSnapshotProto.toByteArray(proto); final File file = getProtoFile(mTaskId, mUserId); @@ -379,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; } } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index f4e42455087d..eb005e0f7eda 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -36,6 +36,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; + import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES; import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES; import static com.android.internal.policy.DecorView.getColorViewLeftInset; @@ -53,9 +54,11 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.GraphicBuffer; +import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.RectF; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -131,6 +134,8 @@ class TaskSnapshotSurface implements StartingSurface { private final Rect mContentInsets = new Rect(); private final Rect mFrame = new Rect(); private TaskSnapshot mSnapshot; + private final RectF mTmpSnapshotSize = new RectF(); + private final RectF mTmpDstFrame = new RectF(); private final CharSequence mTitle; private boolean mHasDrawn; private long mShownTime; @@ -141,6 +146,8 @@ class TaskSnapshotSurface implements StartingSurface { @VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter; private final int mOrientationOnCreation; private final SurfaceControl.Transaction mTransaction; + private final Matrix mSnapshotMatrix = new Matrix(); + private final float[] mTmpFloat9 = new float[9]; static TaskSnapshotSurface create(WindowManagerService service, ActivityRecord activity, TaskSnapshot snapshot) { @@ -365,13 +372,17 @@ class TaskSnapshotSurface implements StartingSurface { frame = calculateSnapshotFrame(crop); mTransaction.setWindowCrop(mChildSurfaceControl, crop); mTransaction.setPosition(mChildSurfaceControl, frame.left, frame.top); + mTmpDstFrame.set(frame); } else { frame = null; + mTmpDstFrame.set(mFrame); } // Scale the mismatch dimensions to fill the task bounds - final float scale = 1 / mSnapshot.getScale(); - mTransaction.setMatrix(mChildSurfaceControl, scale, 0, 0, scale); + mTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight()); + mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL); + mTransaction.setMatrix(mChildSurfaceControl, mSnapshotMatrix, mTmpFloat9); + mTransaction.apply(); surface.attachAndQueueBufferWithColorSpace(buffer, mSnapshot.getColorSpace()); surface.release(); @@ -395,13 +406,17 @@ class TaskSnapshotSurface implements StartingSurface { rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight()); final Rect insets = mSnapshot.getContentInsets(); + final float scaleX = (float) mSnapshot.getSnapshot().getWidth() / mSnapshot.getTaskSize().x; + final float scaleY = + (float) mSnapshot.getSnapshot().getHeight() / mSnapshot.getTaskSize().y; + // Let's remove all system decorations except the status bar, but only if the task is at the // very top of the screen. final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0; - rect.inset((int) (insets.left * mSnapshot.getScale()), - isTop ? 0 : (int) (insets.top * mSnapshot.getScale()), - (int) (insets.right * mSnapshot.getScale()), - (int) (insets.bottom * mSnapshot.getScale())); + rect.inset((int) (insets.left * scaleX), + isTop ? 0 : (int) (insets.top * scaleY), + (int) (insets.right * scaleX), + (int) (insets.bottom * scaleY)); return rect; } @@ -412,14 +427,20 @@ class TaskSnapshotSurface implements StartingSurface { */ @VisibleForTesting Rect calculateSnapshotFrame(Rect crop) { - final Rect frame = new Rect(crop); - final float scale = mSnapshot.getScale(); + final float scaleX = (float) mSnapshot.getSnapshot().getWidth() / mSnapshot.getTaskSize().x; + final float scaleY = + (float) mSnapshot.getSnapshot().getHeight() / mSnapshot.getTaskSize().y; // Rescale the frame from snapshot to window coordinate space - frame.scale(1 / scale); + final Rect frame = new Rect( + (int) (crop.left / scaleX + 0.5f), + (int) (crop.top / scaleY + 0.5f), + (int) (crop.right / scaleX + 0.5f), + (int) (crop.bottom / scaleY + 0.5f) + ); // By default, offset it to to top/left corner - frame.offsetTo((int) (-crop.left / scale), (int) (-crop.top / scale)); + frame.offsetTo((int) (-crop.left / scaleX), (int) (-crop.top / scaleY)); // However, we also need to make space for the navigation bar on the left side. final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left, diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java index bd8aacb6cb96..20d9aff5f3bf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -25,6 +25,7 @@ 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.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; @@ -36,6 +37,7 @@ import android.content.res.Configuration; import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.util.ArraySet; @@ -138,6 +140,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { final int orientation = Configuration.ORIENTATION_PORTRAIT; final float scaleFraction = 0.25f; final Rect contentInsets = new Rect(1, 2, 3, 4); + final Point taskSize = new Point(5, 6); try { ActivityManager.TaskSnapshot.Builder builder = @@ -147,14 +150,13 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { builder.setSystemUiVisibility(systemUiVisibility); builder.setWindowingMode(windowingMode); builder.setColorSpace(sRGB); - builder.setIsLowResolution(true); builder.setOrientation(orientation); builder.setContentInsets(contentInsets); builder.setIsTranslucent(true); - builder.setScaleFraction(0.25f); builder.setSnapshot(buffer); builder.setIsRealSnapshot(true); builder.setPixelFormat(pixelFormat); + builder.setTaskSize(taskSize); // Not part of TaskSnapshot itself, used in screenshot process assertEquals(pixelFormat, builder.getPixelFormat()); @@ -165,13 +167,15 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { assertEquals(systemUiVisibility, snapshot.getSystemUiVisibility()); assertEquals(windowingMode, snapshot.getWindowingMode()); assertEquals(sRGB, snapshot.getColorSpace()); - assertTrue(snapshot.isLowResolution()); + // Snapshots created with the Builder class are always high-res. The only way to get a + // low-res snapshot is to load it from the disk in TaskSnapshotLoader. + assertFalse(snapshot.isLowResolution()); assertEquals(orientation, snapshot.getOrientation()); assertEquals(contentInsets, snapshot.getContentInsets()); assertTrue(snapshot.isTranslucent()); - assertEquals(scaleFraction, builder.getScaleFraction(), 0.001f); assertSame(buffer, snapshot.getSnapshot()); assertTrue(snapshot.isRealSnapshot()); + assertEquals(taskSize, snapshot.getTaskSize()); } finally { if (buffer != null) { buffer.destroy(); @@ -188,11 +192,9 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { final ActivityManager.TaskSnapshot.Builder builder = new ActivityManager.TaskSnapshot.Builder(); - final float scaleFraction = 0.8f; mWm.mTaskSnapshotController.prepareTaskSnapshot(mAppWindow.mActivityRecord.getTask(), - scaleFraction, PixelFormat.UNKNOWN, builder); + PixelFormat.UNKNOWN, builder); - assertEquals(scaleFraction, builder.getScaleFraction(), 0 /* delta */); // The pixel format should be selected automatically. assertNotEquals(PixelFormat.UNKNOWN, builder.getPixelFormat()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java index 0b16e5ce8b97..40f15b7e9d39 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java @@ -19,12 +19,16 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; +import android.app.ActivityManager; import android.app.ActivityManager.TaskSnapshot; import android.content.res.Configuration; import android.graphics.Rect; @@ -36,10 +40,12 @@ import android.view.View; import androidx.test.filters.MediumTest; +import com.android.server.wm.TaskSnapshotLoader.PreRLegacySnapshotConfig; import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.MockitoSession; import java.io.File; import java.util.function.Predicate; @@ -55,6 +61,8 @@ import java.util.function.Predicate; @RunWith(WindowTestRunner.class) public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBase { + private static final float DELTA = 0.00001f; + private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40); @Test @@ -148,29 +156,172 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa } @Test - public void testLowResolutionPersistAndLoadSnapshot() { + public void testLegacyPLowRamConfig() throws Exception { + MockitoSession mockSession = mockitoSession() + .initMocks(this) + .mockStatic(ActivityManager.class) + .startMocking(); + + when(ActivityManager.isLowRamDeviceStatic()).thenReturn(true); + + // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, + // for any P low_ram device + final int taskWidth = 0; + final float legacyScale = 0f; + final boolean hasHighResFile = false; + + PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); + assertNotNull(highResConf); + assertEquals(highResConf.mScale, 0.6f, DELTA); + assertTrue(highResConf.mForceLoadReducedJpeg); + + PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); + assertNotNull(lowResConf); + assertEquals(lowResConf.mScale, 0.6f, DELTA); + assertTrue(lowResConf.mForceLoadReducedJpeg); + + mockSession.finishMocking(); + } + + @Test + public void testLegacyPNonLowRamConfig() throws Exception { + MockitoSession mockSession = mockitoSession() + .initMocks(this) + .mockStatic(ActivityManager.class) + .startMocking(); + + when(ActivityManager.isLowRamDeviceStatic()).thenReturn(false); + + // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, + // for any O device, or a P non-low_ram device + final int taskWidth = 0; + final float legacyScale = 0f; + final boolean hasHighResFile = true; + + PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); + assertNotNull(highResConf); + assertEquals(highResConf.mScale, 1.0f, DELTA); + assertFalse(highResConf.mForceLoadReducedJpeg); + + PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); + assertNotNull(lowResConf); + assertEquals(lowResConf.mScale, 0.5f, DELTA); + assertFalse(lowResConf.mForceLoadReducedJpeg); + + mockSession.finishMocking(); + } + + @Test + public void testLegacyQLowRamConfig() throws Exception { + MockitoSession mockSession = mockitoSession() + .initMocks(this) + .mockStatic(ActivityManager.class) + .startMocking(); + + when(ActivityManager.isLowRamDeviceStatic()).thenReturn(true); + + // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, + // for any Q low_ram device + final int taskWidth = 0; + final float legacyScale = 0.6f; + final boolean hasHighResFile = false; + + PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); + assertNotNull(highResConf); + assertEquals(highResConf.mScale, legacyScale, DELTA); + assertEquals(highResConf.mScale, 0.6f, DELTA); + assertTrue(highResConf.mForceLoadReducedJpeg); + + PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); + assertNotNull(lowResConf); + assertEquals(lowResConf.mScale, legacyScale, DELTA); + assertEquals(lowResConf.mScale, 0.6f, DELTA); + assertTrue(lowResConf.mForceLoadReducedJpeg); + + mockSession.finishMocking(); + } + + @Test + public void testLegacyQNonLowRamConfig() throws Exception { + MockitoSession mockSession = mockitoSession() + .initMocks(this) + .mockStatic(ActivityManager.class) + .startMocking(); + + when(ActivityManager.isLowRamDeviceStatic()).thenReturn(false); + + // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, + // for any Q non-low_ram device + final int taskWidth = 0; + final float legacyScale = 0.8f; + final boolean hasHighResFile = true; + + PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); + assertNotNull(highResConf); + assertEquals(highResConf.mScale, legacyScale, DELTA); + assertEquals(highResConf.mScale, 0.8f, DELTA); + assertFalse(highResConf.mForceLoadReducedJpeg); + + PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); + assertNotNull(lowResConf); + assertEquals(lowResConf.mScale, 0.5f * legacyScale, DELTA); + assertEquals(lowResConf.mScale, 0.5f * 0.8f, DELTA); + assertFalse(lowResConf.mForceLoadReducedJpeg); + + mockSession.finishMocking(); + } + + @Test + public void testNonLegacyRConfig() throws Exception { + // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, + // for any R device + final int taskWidth = 1440; + final float legacyScale = 0f; + final boolean hasHighResFile = true; + + PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); + assertNull(highResConf); + + PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); + assertNull(lowResConf); + } + + @Test + public void testDisabledLowResolutionPersistAndLoadSnapshot() { + mPersister.setEnableLowResSnapshots(false); + TaskSnapshot a = new TaskSnapshotBuilder() - .setScale(0.5f) + .setScaleFraction(0.5f) .setIsLowResolution(true) .build(); assertTrue(a.isLowResolution()); mPersister.persistSnapshot(1, mTestUserId, a); mPersister.waitForQueueEmpty(); final File[] files = new File[]{new File(FILES_DIR.getPath() + "/snapshots/1.proto"), - new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")}; + new File(FILES_DIR.getPath() + "/snapshots/1.jpg")}; final File[] nonExistsFiles = new File[]{ - new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), + new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"), }; assertTrueForFiles(files, File::exists, " must exist"); assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist"); - final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, true /* isLowResolution */); + final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */); assertNotNull(snapshot); assertEquals(TEST_INSETS, snapshot.getContentInsets()); assertNotNull(snapshot.getSnapshot()); assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation()); final TaskSnapshot snapshotNotExist = mLoader.loadTask(1, mTestUserId, - false /* isLowResolution */); + true /* isLowResolution */); assertNull(snapshotNotExist); } @@ -271,13 +422,11 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa @Test public void testScalePersistAndLoadSnapshot() { TaskSnapshot a = new TaskSnapshotBuilder() - .setScale(0.25f) + .setScaleFraction(0.25f) .build(); TaskSnapshot b = new TaskSnapshotBuilder() - .setScale(0.75f) + .setScaleFraction(0.75f) .build(); - assertEquals(0.25f, a.getScale(), 1E-5); - assertEquals(0.75f, b.getScale(), 1E-5); mPersister.persistSnapshot(1, mTestUserId, a); mPersister.persistSnapshot(2, mTestUserId, b); mPersister.waitForQueueEmpty(); @@ -287,8 +436,6 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa false /* isLowResolution */); assertNotNull(snapshotA); assertNotNull(snapshotB); - assertEquals(0.25f, snapshotA.getScale(), 1E-5); - assertEquals(0.75f, snapshotB.getScale(), 1E-5); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java index 4612dbab0a59..fa6663c06371 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java @@ -30,6 +30,7 @@ import android.graphics.Color; import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.Rect; import android.os.UserManager; import android.view.Surface; @@ -87,8 +88,10 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase { * Builds a TaskSnapshot. */ static class TaskSnapshotBuilder { + private static final int SNAPSHOT_WIDTH = 100; + private static final int SNAPSHOT_HEIGHT = 100; - private float mScale = 1f; + private float mScaleFraction = 1f; private boolean mIsLowResolution = false; private boolean mIsRealSnapshot = true; private boolean mIsTranslucent = false; @@ -96,8 +99,11 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase { private int mSystemUiVisibility = 0; private int mRotation = Surface.ROTATION_0; - TaskSnapshotBuilder setScale(float scale) { - mScale = scale; + TaskSnapshotBuilder() { + } + + TaskSnapshotBuilder setScaleFraction(float scale) { + mScaleFraction = scale; return this; } @@ -132,15 +138,20 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase { } TaskSnapshot build() { - final GraphicBuffer buffer = GraphicBuffer.create(100, 100, PixelFormat.RGBA_8888, + // To satisfy existing tests, ensure the graphics buffer is always 100x100, and + // compute the ize of the task according to mScaleFraction. + Point taskSize = new Point((int) (SNAPSHOT_WIDTH / mScaleFraction), + (int) (SNAPSHOT_HEIGHT / mScaleFraction)); + final GraphicBuffer buffer = GraphicBuffer.create(SNAPSHOT_WIDTH, SNAPSHOT_HEIGHT, + PixelFormat.RGBA_8888, USAGE_HW_TEXTURE | USAGE_SW_READ_RARELY | USAGE_SW_READ_RARELY); Canvas c = buffer.lockCanvas(); c.drawColor(Color.RED); buffer.unlockCanvasAndPost(c); return new TaskSnapshot(MOCK_SNAPSHOT_ID, new ComponentName("", ""), buffer, ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT, - mRotation, TEST_INSETS, - mIsLowResolution, mScale, mIsRealSnapshot, + mRotation, taskSize, TEST_INSETS, + mIsLowResolution, mIsRealSnapshot, mWindowingMode, mSystemUiVisibility, mIsTranslucent); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java index bb0e5aec8e2e..2164de9ea191 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java @@ -38,6 +38,7 @@ import android.graphics.Color; import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.view.Surface; @@ -67,12 +68,22 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { int windowFlags, Rect taskBounds) { final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888, GraphicBuffer.USAGE_SW_READ_RARELY | GraphicBuffer.USAGE_SW_WRITE_NEVER); + + // Previously when constructing TaskSnapshots for this test, scale was 1.0f, so to mimic + // this behavior set the taskSize to be the same as the taskBounds width and height. The + // taskBounds passed here are assumed to be the same task bounds as when the snapshot was + // taken. We assume there is no aspect ratio mismatch between the screenshot and the + // taskBounds + assertEquals(width, taskBounds.width()); + assertEquals(height, taskBounds.height()); + Point taskSize = new Point(taskBounds.width(), taskBounds.height()); + final TaskSnapshot snapshot = new TaskSnapshot( System.currentTimeMillis(), new ComponentName("", ""), buffer, ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT, - Surface.ROTATION_0, contentInsets, false, - 1.0f, true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, + Surface.ROTATION_0, taskSize, contentInsets, false, + true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */); mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test", createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), sysuiVis, windowFlags, 0, @@ -152,7 +163,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { @Test public void testCalculateSnapshotCrop_taskNotOnTop() { - setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 100)); + setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 150)); assertEquals(new Rect(0, 10, 100, 90), mSurface.calculateSnapshotCrop()); } |