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
This commit is contained in:
committed by
Android (Google) Code Review
commit
81a320279e
@@ -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();
|
||||
@@ -2110,6 +2111,14 @@ public class ActivityManager {
|
||||
return mRotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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,28 +2265,19 @@ public class ActivityManager {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the original size of the task
|
||||
*/
|
||||
public Builder setTaskSize(Point size) {
|
||||
mTaskSize = size;
|
||||
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.
|
||||
*/
|
||||
public Builder setIsLowResolution(boolean isLowResolution) {
|
||||
mIsLowResolution = isLowResolution;
|
||||
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;
|
||||
@@ -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,
|
||||
|
||||
@@ -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. -->
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
float scaleFraction) {
|
||||
return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888);
|
||||
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, int pixelFormat) {
|
||||
float scaleFraction) {
|
||||
return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
} else {
|
||||
mLowResScale = 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 (lowResTaskSnapshotScale > 0) {
|
||||
mLowResScaleFactor = lowResTaskSnapshotScale / highResTaskSnapshotScale;
|
||||
setEnableLowResSnapshots(true);
|
||||
} else {
|
||||
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,11 +394,26 @@ 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 file = getHighResolutionBitmapFile(mTaskId, mUserId);
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
swBitmap.compress(JPEG, QUALITY, fos);
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to open " + file + " for persisting.", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!enableLowResSnapshots()) {
|
||||
swBitmap.recycle();
|
||||
return true;
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -396,22 +426,6 @@ class TaskSnapshotPersister {
|
||||
}
|
||||
lowResBitmap.recycle();
|
||||
|
||||
// For snapshots with lowResBitmap resolution, do not create or save full sized bitmaps
|
||||
if (mSnapshot.isLowResolution()) {
|
||||
swBitmap.recycle();
|
||||
return true;
|
||||
}
|
||||
|
||||
final File file = getHighResolutionBitmapFile(mTaskId, mUserId);
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
swBitmap.compress(JPEG, QUALITY, fos);
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to open " + file + " for persisting.", e);
|
||||
return false;
|
||||
}
|
||||
swBitmap.recycle();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user