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} */
|
/** See {@link android.view.Surface.Rotation} */
|
||||||
@Surface.Rotation
|
@Surface.Rotation
|
||||||
private int mRotation;
|
private int mRotation;
|
||||||
|
/** The size of the snapshot before scaling */
|
||||||
|
private final Point mTaskSize;
|
||||||
private final Rect mContentInsets;
|
private final Rect mContentInsets;
|
||||||
// Whether this snapshot is a down-sampled version of the full resolution, used mainly for
|
// Whether this snapshot is a down-sampled version of the high resolution snapshot, used
|
||||||
// low-ram devices
|
// mainly for loading snapshots quickly from disk when user is flinging fast
|
||||||
private final boolean mIsLowResolution;
|
private final boolean mIsLowResolution;
|
||||||
// Whether or not the snapshot is a real snapshot or an app-theme generated snapshot due to
|
// 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
|
// the task having a secure window or having previews disabled
|
||||||
private final boolean mIsRealSnapshot;
|
private final boolean mIsRealSnapshot;
|
||||||
private final int mWindowingMode;
|
private final int mWindowingMode;
|
||||||
private final float mScale;
|
|
||||||
private final int mSystemUiVisibility;
|
private final int mSystemUiVisibility;
|
||||||
private final boolean mIsTranslucent;
|
private final boolean mIsTranslucent;
|
||||||
// Must be one of the named color spaces, otherwise, always use SRGB color space.
|
// 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,
|
public TaskSnapshot(long id,
|
||||||
@NonNull ComponentName topActivityComponent, GraphicBuffer snapshot,
|
@NonNull ComponentName topActivityComponent, GraphicBuffer snapshot,
|
||||||
@NonNull ColorSpace colorSpace, int orientation, int rotation, Rect contentInsets,
|
@NonNull ColorSpace colorSpace, int orientation, int rotation, Point taskSize,
|
||||||
boolean isLowResolution, float scale, boolean isRealSnapshot, int windowingMode,
|
Rect contentInsets, boolean isLowResolution, boolean isRealSnapshot,
|
||||||
int systemUiVisibility, boolean isTranslucent) {
|
int windowingMode, int systemUiVisibility, boolean isTranslucent) {
|
||||||
mId = id;
|
mId = id;
|
||||||
mTopActivityComponent = topActivityComponent;
|
mTopActivityComponent = topActivityComponent;
|
||||||
mSnapshot = snapshot;
|
mSnapshot = snapshot;
|
||||||
@@ -2038,9 +2039,9 @@ public class ActivityManager {
|
|||||||
? ColorSpace.get(ColorSpace.Named.SRGB) : colorSpace;
|
? ColorSpace.get(ColorSpace.Named.SRGB) : colorSpace;
|
||||||
mOrientation = orientation;
|
mOrientation = orientation;
|
||||||
mRotation = rotation;
|
mRotation = rotation;
|
||||||
|
mTaskSize = new Point(taskSize);
|
||||||
mContentInsets = new Rect(contentInsets);
|
mContentInsets = new Rect(contentInsets);
|
||||||
mIsLowResolution = isLowResolution;
|
mIsLowResolution = isLowResolution;
|
||||||
mScale = scale;
|
|
||||||
mIsRealSnapshot = isRealSnapshot;
|
mIsRealSnapshot = isRealSnapshot;
|
||||||
mWindowingMode = windowingMode;
|
mWindowingMode = windowingMode;
|
||||||
mSystemUiVisibility = systemUiVisibility;
|
mSystemUiVisibility = systemUiVisibility;
|
||||||
@@ -2057,9 +2058,9 @@ public class ActivityManager {
|
|||||||
: ColorSpace.get(ColorSpace.Named.SRGB);
|
: ColorSpace.get(ColorSpace.Named.SRGB);
|
||||||
mOrientation = source.readInt();
|
mOrientation = source.readInt();
|
||||||
mRotation = source.readInt();
|
mRotation = source.readInt();
|
||||||
|
mTaskSize = source.readParcelable(null /* classLoader */);
|
||||||
mContentInsets = source.readParcelable(null /* classLoader */);
|
mContentInsets = source.readParcelable(null /* classLoader */);
|
||||||
mIsLowResolution = source.readBoolean();
|
mIsLowResolution = source.readBoolean();
|
||||||
mScale = source.readFloat();
|
|
||||||
mIsRealSnapshot = source.readBoolean();
|
mIsRealSnapshot = source.readBoolean();
|
||||||
mWindowingMode = source.readInt();
|
mWindowingMode = source.readInt();
|
||||||
mSystemUiVisibility = source.readInt();
|
mSystemUiVisibility = source.readInt();
|
||||||
@@ -2110,6 +2111,14 @@ public class ActivityManager {
|
|||||||
return mRotation;
|
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
|
* @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.
|
* remove any areas behind system bars in the snapshot.
|
||||||
@@ -2159,14 +2168,6 @@ public class ActivityManager {
|
|||||||
return mSystemUiVisibility;
|
return mSystemUiVisibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The scale this snapshot was taken in.
|
|
||||||
*/
|
|
||||||
@UnsupportedAppUsage
|
|
||||||
public float getScale() {
|
|
||||||
return mScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -2180,9 +2181,9 @@ public class ActivityManager {
|
|||||||
dest.writeInt(mColorSpace.getId());
|
dest.writeInt(mColorSpace.getId());
|
||||||
dest.writeInt(mOrientation);
|
dest.writeInt(mOrientation);
|
||||||
dest.writeInt(mRotation);
|
dest.writeInt(mRotation);
|
||||||
|
dest.writeParcelable(mTaskSize, 0);
|
||||||
dest.writeParcelable(mContentInsets, 0);
|
dest.writeParcelable(mContentInsets, 0);
|
||||||
dest.writeBoolean(mIsLowResolution);
|
dest.writeBoolean(mIsLowResolution);
|
||||||
dest.writeFloat(mScale);
|
|
||||||
dest.writeBoolean(mIsRealSnapshot);
|
dest.writeBoolean(mIsRealSnapshot);
|
||||||
dest.writeInt(mWindowingMode);
|
dest.writeInt(mWindowingMode);
|
||||||
dest.writeInt(mSystemUiVisibility);
|
dest.writeInt(mSystemUiVisibility);
|
||||||
@@ -2200,9 +2201,11 @@ public class ActivityManager {
|
|||||||
+ " mColorSpace=" + mColorSpace.toString()
|
+ " mColorSpace=" + mColorSpace.toString()
|
||||||
+ " mOrientation=" + mOrientation
|
+ " mOrientation=" + mOrientation
|
||||||
+ " mRotation=" + mRotation
|
+ " mRotation=" + mRotation
|
||||||
|
+ " mTaskSize=" + mTaskSize.toString()
|
||||||
+ " mContentInsets=" + mContentInsets.toShortString()
|
+ " mContentInsets=" + mContentInsets.toShortString()
|
||||||
+ " mIsLowResolution=" + mIsLowResolution + " mScale=" + mScale
|
+ " mIsLowResolution=" + mIsLowResolution
|
||||||
+ " mIsRealSnapshot=" + mIsRealSnapshot + " mWindowingMode=" + mWindowingMode
|
+ " mIsRealSnapshot=" + mIsRealSnapshot
|
||||||
|
+ " mWindowingMode=" + mWindowingMode
|
||||||
+ " mSystemUiVisibility=" + mSystemUiVisibility
|
+ " mSystemUiVisibility=" + mSystemUiVisibility
|
||||||
+ " mIsTranslucent=" + mIsTranslucent;
|
+ " mIsTranslucent=" + mIsTranslucent;
|
||||||
}
|
}
|
||||||
@@ -2224,9 +2227,8 @@ public class ActivityManager {
|
|||||||
private ColorSpace mColorSpace;
|
private ColorSpace mColorSpace;
|
||||||
private int mOrientation;
|
private int mOrientation;
|
||||||
private int mRotation;
|
private int mRotation;
|
||||||
|
private Point mTaskSize;
|
||||||
private Rect mContentInsets;
|
private Rect mContentInsets;
|
||||||
private boolean mIsLowResolution;
|
|
||||||
private float mScaleFraction;
|
|
||||||
private boolean mIsRealSnapshot;
|
private boolean mIsRealSnapshot;
|
||||||
private int mWindowingMode;
|
private int mWindowingMode;
|
||||||
private int mSystemUiVisibility;
|
private int mSystemUiVisibility;
|
||||||
@@ -2263,28 +2265,19 @@ public class ActivityManager {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the original size of the task
|
||||||
|
*/
|
||||||
|
public Builder setTaskSize(Point size) {
|
||||||
|
mTaskSize = size;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder setContentInsets(Rect contentInsets) {
|
public Builder setContentInsets(Rect contentInsets) {
|
||||||
mContentInsets = contentInsets;
|
mContentInsets = contentInsets;
|
||||||
return this;
|
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) {
|
public Builder setIsRealSnapshot(boolean realSnapshot) {
|
||||||
mIsRealSnapshot = realSnapshot;
|
mIsRealSnapshot = realSnapshot;
|
||||||
return this;
|
return this;
|
||||||
@@ -2322,9 +2315,12 @@ public class ActivityManager {
|
|||||||
mColorSpace,
|
mColorSpace,
|
||||||
mOrientation,
|
mOrientation,
|
||||||
mRotation,
|
mRotation,
|
||||||
|
mTaskSize,
|
||||||
mContentInsets,
|
mContentInsets,
|
||||||
mIsLowResolution,
|
// When building a TaskSnapshot with the Builder class, isLowResolution
|
||||||
mScaleFraction,
|
// is always false. Low-res snapshots are only created when loading from
|
||||||
|
// disk.
|
||||||
|
false /* isLowResolution */,
|
||||||
mIsRealSnapshot,
|
mIsRealSnapshot,
|
||||||
mWindowingMode,
|
mWindowingMode,
|
||||||
mSystemUiVisibility,
|
mSystemUiVisibility,
|
||||||
|
|||||||
@@ -2752,7 +2752,9 @@
|
|||||||
|
|
||||||
<!-- The amount to scale reduced scale snapshots for Overview and snapshot starting windows.
|
<!-- 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
|
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>
|
<item name="config_lowResTaskSnapshotScale" format="float" type="dimen">0.5</item>
|
||||||
|
|
||||||
<!-- Feature flag to store TaskSnapshot in 16 bit pixel format to save memory. -->
|
<!-- Feature flag to store TaskSnapshot in 16 bit pixel format to save memory. -->
|
||||||
|
|||||||
@@ -62,7 +62,9 @@ public class ThumbnailData {
|
|||||||
orientation = snapshot.getOrientation();
|
orientation = snapshot.getOrientation();
|
||||||
rotation = snapshot.getRotation();
|
rotation = snapshot.getRotation();
|
||||||
reducedResolution = snapshot.isLowResolution();
|
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();
|
isRealSnapshot = snapshot.isRealSnapshot();
|
||||||
isTranslucent = snapshot.isTranslucent();
|
isTranslucent = snapshot.isTranslucent();
|
||||||
windowingMode = snapshot.getWindowingMode();
|
windowingMode = snapshot.getWindowingMode();
|
||||||
|
|||||||
@@ -32,7 +32,12 @@
|
|||||||
int32 system_ui_visibility = 8;
|
int32 system_ui_visibility = 8;
|
||||||
bool is_translucent = 9;
|
bool is_translucent = 9;
|
||||||
string top_activity_component = 10;
|
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;
|
int64 id = 12;
|
||||||
int32 rotation = 13;
|
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 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.DEBUG_SCREENSHOT;
|
||||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
|
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
|
||||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.ActivityManager;
|
|
||||||
import android.app.ActivityManager.TaskSnapshot;
|
import android.app.ActivityManager.TaskSnapshot;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.GraphicBuffer;
|
import android.graphics.GraphicBuffer;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
|
import android.graphics.Point;
|
||||||
import android.graphics.RecordingCanvas;
|
import android.graphics.RecordingCanvas;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.RenderNode;
|
import android.graphics.RenderNode;
|
||||||
@@ -89,14 +88,6 @@ class TaskSnapshotController {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final int SNAPSHOT_MODE_NONE = 2;
|
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 WindowManagerService mService;
|
||||||
|
|
||||||
private final TaskSnapshotCache mCache;
|
private final TaskSnapshotCache mCache;
|
||||||
@@ -229,7 +220,7 @@ class TaskSnapshotController {
|
|||||||
@Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
|
@Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
|
||||||
boolean isLowResolution) {
|
boolean isLowResolution) {
|
||||||
return mCache.getSnapshot(taskId, userId, restoreFromDisk, 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.
|
* information from the task and populates the builder.
|
||||||
*
|
*
|
||||||
* @param task the task to capture
|
* @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
|
* @param pixelFormat the desired pixel format, or {@link PixelFormat#UNKNOWN} to
|
||||||
* automatically select
|
* automatically select
|
||||||
* @param builder the snapshot builder to populate
|
* @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
|
* @return true if the state of the task is ok to proceed
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean prepareTaskSnapshot(Task task, float scaleFraction, int pixelFormat,
|
boolean prepareTaskSnapshot(Task task, int pixelFormat, TaskSnapshot.Builder builder) {
|
||||||
TaskSnapshot.Builder builder) {
|
|
||||||
if (!mService.mPolicy.isScreenOn()) {
|
if (!mService.mPolicy.isScreenOn()) {
|
||||||
if (DEBUG_SCREENSHOT) {
|
if (DEBUG_SCREENSHOT) {
|
||||||
Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
|
Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
|
||||||
@@ -314,18 +302,6 @@ class TaskSnapshotController {
|
|||||||
builder.setId(System.currentTimeMillis());
|
builder.setId(System.currentTimeMillis());
|
||||||
builder.setContentInsets(getInsets(mainWindow));
|
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 isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
|
||||||
final boolean isShowWallpaper = (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) != 0;
|
final boolean isShowWallpaper = (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) != 0;
|
||||||
|
|
||||||
@@ -351,13 +327,23 @@ class TaskSnapshotController {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
|
SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
|
||||||
float scaleFraction) {
|
TaskSnapshot.Builder builder) {
|
||||||
return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888);
|
Point taskSize = new Point();
|
||||||
|
final SurfaceControl.ScreenshotGraphicBuffer taskSnapshot = createTaskSnapshot(task,
|
||||||
|
mHighResTaskSnapshotScale, builder.getPixelFormat(), taskSize);
|
||||||
|
builder.setTaskSize(taskSize);
|
||||||
|
return taskSnapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
|
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 (task.getSurfaceControl() == null) {
|
||||||
if (DEBUG_SCREENSHOT) {
|
if (DEBUG_SCREENSHOT) {
|
||||||
Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + task);
|
Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + task);
|
||||||
@@ -369,6 +355,10 @@ class TaskSnapshotController {
|
|||||||
final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
|
final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
|
||||||
SurfaceControl.captureLayers(
|
SurfaceControl.captureLayers(
|
||||||
task.getSurfaceControl(), mTmpRect, scaleFraction, pixelFormat);
|
task.getSurfaceControl(), mTmpRect, scaleFraction, pixelFormat);
|
||||||
|
if (outTaskSize != null) {
|
||||||
|
outTaskSize.x = mTmpRect.width();
|
||||||
|
outTaskSize.y = mTmpRect.height();
|
||||||
|
}
|
||||||
final GraphicBuffer buffer = screenshotBuffer != null ? screenshotBuffer.getGraphicBuffer()
|
final GraphicBuffer buffer = screenshotBuffer != null ? screenshotBuffer.getGraphicBuffer()
|
||||||
: null;
|
: null;
|
||||||
if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
|
if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
|
||||||
@@ -379,21 +369,20 @@ class TaskSnapshotController {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
TaskSnapshot snapshotTask(Task task) {
|
TaskSnapshot snapshotTask(Task task) {
|
||||||
return snapshotTask(task, SNAPSHOT_SCALE_AUTO, PixelFormat.UNKNOWN);
|
return snapshotTask(task, PixelFormat.UNKNOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
TaskSnapshot snapshotTask(Task task, float scaleFraction, int pixelFormat) {
|
TaskSnapshot snapshotTask(Task task, int pixelFormat) {
|
||||||
TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
|
TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
|
||||||
|
|
||||||
if (!prepareTaskSnapshot(task, scaleFraction, pixelFormat, builder)) {
|
if (!prepareTaskSnapshot(task, pixelFormat, builder)) {
|
||||||
// Failed some pre-req. Has been logged.
|
// Failed some pre-req. Has been logged.
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
|
final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
|
||||||
createTaskSnapshot(task, builder.getScaleFraction(),
|
createTaskSnapshot(task, builder);
|
||||||
builder.getPixelFormat());
|
|
||||||
|
|
||||||
if (screenshotBuffer == null) {
|
if (screenshotBuffer == null) {
|
||||||
// Failed to acquire image. Has been logged.
|
// Failed to acquire image. Has been logged.
|
||||||
@@ -472,8 +461,10 @@ class TaskSnapshotController {
|
|||||||
final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
|
final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
|
||||||
attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(),
|
attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(),
|
||||||
mHighResTaskSnapshotScale, mainWindow.getRequestedInsetsState());
|
mHighResTaskSnapshotScale, mainWindow.getRequestedInsetsState());
|
||||||
final int width = (int) (task.getBounds().width() * mHighResTaskSnapshotScale);
|
final int taskWidth = task.getBounds().width();
|
||||||
final int height = (int) (task.getBounds().height() * mHighResTaskSnapshotScale);
|
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);
|
final RenderNode node = RenderNode.create("TaskSnapshotController", null);
|
||||||
node.setLeftTopRightBottom(0, 0, width, height);
|
node.setLeftTopRightBottom(0, 0, width, height);
|
||||||
@@ -494,9 +485,9 @@ class TaskSnapshotController {
|
|||||||
System.currentTimeMillis() /* id */,
|
System.currentTimeMillis() /* id */,
|
||||||
topChild.mActivityComponent, hwBitmap.createGraphicBufferHandle(),
|
topChild.mActivityComponent, hwBitmap.createGraphicBufferHandle(),
|
||||||
hwBitmap.getColorSpace(), mainWindow.getConfiguration().orientation,
|
hwBitmap.getColorSpace(), mainWindow.getConfiguration().orientation,
|
||||||
mainWindow.getWindowConfiguration().getRotation(),
|
mainWindow.getWindowConfiguration().getRotation(), new Point(taskWidth, taskHeight),
|
||||||
getInsets(mainWindow), ActivityManager.isLowRamDeviceStatic() /* isLowResolution */,
|
getInsets(mainWindow), false /* isLowResolution */,
|
||||||
mHighResTaskSnapshotScale, false /* isRealSnapshot */, task.getWindowingMode(),
|
false /* isRealSnapshot */, task.getWindowingMode(),
|
||||||
getSystemUiVisibility(task), false);
|
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_WITH_CLASS_NAME;
|
||||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
||||||
|
|
||||||
|
import android.app.ActivityManager;
|
||||||
import android.app.ActivityManager.TaskSnapshot;
|
import android.app.ActivityManager.TaskSnapshot;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
@@ -26,6 +27,7 @@ import android.graphics.Bitmap.Config;
|
|||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.BitmapFactory.Options;
|
import android.graphics.BitmapFactory.Options;
|
||||||
import android.graphics.GraphicBuffer;
|
import android.graphics.GraphicBuffer;
|
||||||
|
import android.graphics.Point;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
@@ -52,6 +54,76 @@ class TaskSnapshotLoader {
|
|||||||
mPersister = persister;
|
mPersister = persister;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class PreRLegacySnapshotConfig {
|
||||||
|
/**
|
||||||
|
* If isPreRLegacy is {@code true}, specifies the scale the snapshot was taken at
|
||||||
|
*/
|
||||||
|
final float mScale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If {@code true}, always load *_reduced.jpg file, no matter what was requested
|
||||||
|
*/
|
||||||
|
final boolean mForceLoadReducedJpeg;
|
||||||
|
|
||||||
|
PreRLegacySnapshotConfig(float scale, boolean forceLoadReducedJpeg) {
|
||||||
|
mScale = scale;
|
||||||
|
mForceLoadReducedJpeg = forceLoadReducedJpeg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When device is upgraded, we might be loading a legacy snapshot. In those cases,
|
||||||
|
* restore the scale based on how it was configured historically. See history of
|
||||||
|
* TaskSnapshotPersister for more information.
|
||||||
|
*
|
||||||
|
* | low_ram=false | low_ram=true
|
||||||
|
* +------------------------------------------------------------------------------+
|
||||||
|
* O | *.jpg = 100%, *_reduced.jpg = 50% |
|
||||||
|
* | +-----------------------------------------|
|
||||||
|
* P | | *.jpg = NONE, *_reduced.jpg = 60% |
|
||||||
|
* +------------------------------------+-----------------------------------------+
|
||||||
|
* Q | *.jpg = proto.scale, | *.jpg = NONE, |
|
||||||
|
* | *_reduced.jpg = 50% * proto.scale | *_reduced.jpg = proto.scale |
|
||||||
|
* +------------------------------------+-----------------------------------------+
|
||||||
|
*
|
||||||
|
* @return null if Android R, otherwise a PreRLegacySnapshotConfig object
|
||||||
|
*/
|
||||||
|
PreRLegacySnapshotConfig getLegacySnapshotConfig(int taskWidth, float legacyScale,
|
||||||
|
boolean highResFileExists, boolean loadLowResolutionBitmap) {
|
||||||
|
float preRLegacyScale = 0;
|
||||||
|
boolean forceLoadReducedJpeg = false;
|
||||||
|
boolean isPreRLegacySnapshot = (taskWidth == 0);
|
||||||
|
if (!isPreRLegacySnapshot) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final boolean isPreQLegacyProto = isPreRLegacySnapshot
|
||||||
|
&& (Float.compare(legacyScale, 0f) == 0);
|
||||||
|
|
||||||
|
if (isPreQLegacyProto) {
|
||||||
|
// Android O or Android P
|
||||||
|
if (ActivityManager.isLowRamDeviceStatic() && !highResFileExists) {
|
||||||
|
// Android P w/ low_ram=true
|
||||||
|
preRLegacyScale = 0.6f;
|
||||||
|
// Force bitmapFile to always be *_reduced.jpg
|
||||||
|
forceLoadReducedJpeg = true;
|
||||||
|
} else {
|
||||||
|
// Android O, OR Android P w/ low_ram=false
|
||||||
|
preRLegacyScale = loadLowResolutionBitmap ? 0.5f : 1.0f;
|
||||||
|
}
|
||||||
|
} else if (isPreRLegacySnapshot) {
|
||||||
|
// If not pre-Q but is pre-R, then it must be Android Q
|
||||||
|
if (ActivityManager.isLowRamDeviceStatic()) {
|
||||||
|
preRLegacyScale = legacyScale;
|
||||||
|
// Force bitmapFile to always be *_reduced.jpg
|
||||||
|
forceLoadReducedJpeg = true;
|
||||||
|
} else {
|
||||||
|
preRLegacyScale =
|
||||||
|
loadLowResolutionBitmap ? 0.5f * legacyScale : legacyScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new PreRLegacySnapshotConfig(preRLegacyScale, forceLoadReducedJpeg);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a task from the disk.
|
* Loads a task from the disk.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -60,20 +132,32 @@ class TaskSnapshotLoader {
|
|||||||
*
|
*
|
||||||
* @param taskId The id of the task to load.
|
* @param taskId The id of the task to load.
|
||||||
* @param userId The id of the user the task belonged to.
|
* @param userId The id of the user the task belonged to.
|
||||||
* @param isLowResolution Whether to load a reduced resolution version of the snapshot.
|
* @param loadLowResolutionBitmap Whether to load a low resolution resolution version of the
|
||||||
|
* snapshot.
|
||||||
* @return The loaded {@link TaskSnapshot} or {@code null} if it couldn't be loaded.
|
* @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 protoFile = mPersister.getProtoFile(taskId, userId);
|
||||||
final File bitmapFile = isLowResolution
|
if (!protoFile.exists()) {
|
||||||
? mPersister.getLowResolutionBitmapFile(taskId, userId)
|
|
||||||
: mPersister.getHighResolutionBitmapFile(taskId, userId);
|
|
||||||
if (bitmapFile == null || !protoFile.exists() || !bitmapFile.exists()) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final byte[] bytes = Files.readAllBytes(protoFile.toPath());
|
final byte[] bytes = Files.readAllBytes(protoFile.toPath());
|
||||||
final TaskSnapshotProto proto = TaskSnapshotProto.parseFrom(bytes);
|
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();
|
final Options options = new Options();
|
||||||
options.inPreferredConfig = mPersister.use16BitFormat() && !proto.isTranslucent
|
options.inPreferredConfig = mPersister.use16BitFormat() && !proto.isTranslucent
|
||||||
? Config.RGB_565
|
? Config.RGB_565
|
||||||
@@ -99,13 +183,20 @@ class TaskSnapshotLoader {
|
|||||||
|
|
||||||
final ComponentName topActivityComponent = ComponentName.unflattenFromString(
|
final ComponentName topActivityComponent = ComponentName.unflattenFromString(
|
||||||
proto.topActivityComponent);
|
proto.topActivityComponent);
|
||||||
// For legacy snapshots, restore the scale based on the reduced resolution state
|
|
||||||
final float legacyScale = isLowResolution ? mPersister.getLowResScale() : 1f;
|
Point taskSize;
|
||||||
final float scale = Float.compare(proto.scale, 0f) != 0 ? proto.scale : legacyScale;
|
if (legacyConfig != null) {
|
||||||
return new TaskSnapshot(proto.id, topActivityComponent, buffer, hwBitmap.getColorSpace(),
|
int taskWidth = (int) ((float) hwBitmap.getWidth() / legacyConfig.mScale);
|
||||||
proto.orientation, proto.rotation,
|
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),
|
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);
|
proto.systemUiVisibility, proto.isTranslucent);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Slog.w(TAG, "Unable to load task snapshot data for taskId=" + taskId);
|
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_WITH_CLASS_NAME;
|
||||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
import android.annotation.TestApi;
|
import android.annotation.TestApi;
|
||||||
import android.app.ActivityManager;
|
|
||||||
import android.app.ActivityManager.TaskSnapshot;
|
import android.app.ActivityManager.TaskSnapshot;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Bitmap.Config;
|
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 TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotPersister" : TAG_WM;
|
||||||
private static final String SNAPSHOTS_DIRNAME = "snapshots";
|
private static final String SNAPSHOTS_DIRNAME = "snapshots";
|
||||||
private static final String LOW_RES_FILE_POSTFIX = "_reduced";
|
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 long DELAY_MS = 100;
|
||||||
private static final int QUALITY = 95;
|
private static final int QUALITY = 95;
|
||||||
private static final String PROTO_EXTENSION = ".proto";
|
private static final String PROTO_EXTENSION = ".proto";
|
||||||
@@ -71,7 +69,8 @@ class TaskSnapshotPersister {
|
|||||||
private boolean mStarted;
|
private boolean mStarted;
|
||||||
private final Object mLock = new Object();
|
private final Object mLock = new Object();
|
||||||
private final DirectoryResolver mDirectoryResolver;
|
private final DirectoryResolver mDirectoryResolver;
|
||||||
private final float mLowResScale;
|
private final float mLowResScaleFactor;
|
||||||
|
private boolean mEnableLowResSnapshots;
|
||||||
private final boolean mUse16BitFormat;
|
private final boolean mUse16BitFormat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,13 +82,29 @@ class TaskSnapshotPersister {
|
|||||||
|
|
||||||
TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) {
|
TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) {
|
||||||
mDirectoryResolver = resolver;
|
mDirectoryResolver = resolver;
|
||||||
|
final float highResTaskSnapshotScale = service.mContext.getResources().getFloat(
|
||||||
if (ActivityManager.isLowRamDeviceStatic()) {
|
com.android.internal.R.dimen.config_highResTaskSnapshotScale);
|
||||||
mLowResScale = LOW_RAM_REDUCED_SCALE;
|
final float lowResTaskSnapshotScale = service.mContext.getResources().getFloat(
|
||||||
} else {
|
|
||||||
mLowResScale = service.mContext.getResources().getFloat(
|
|
||||||
com.android.internal.R.dimen.config_lowResTaskSnapshotScale);
|
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(
|
mUse16BitFormat = service.mContext.getResources().getBoolean(
|
||||||
com.android.internal.R.bool.config_use16BitTaskSnapshotPixelFormat);
|
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.
|
* Not to be used. Only here for testing.
|
||||||
*
|
|
||||||
* @return the lowResBitmap scale of task snapshots when they are set to be low res
|
|
||||||
*/
|
*/
|
||||||
float getLowResScale() {
|
@VisibleForTesting
|
||||||
return mLowResScale;
|
void setEnableLowResSnapshots(boolean enabled) {
|
||||||
|
mEnableLowResSnapshots = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,14 +231,10 @@ class TaskSnapshotPersister {
|
|||||||
}
|
}
|
||||||
|
|
||||||
File getHighResolutionBitmapFile(int taskId, int userId) {
|
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);
|
return new File(getDirectory(userId), taskId + BITMAP_EXTENSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
File getLowResolutionBitmapFile(int taskId, int userId) {
|
File getLowResolutionBitmapFile(int taskId, int userId) {
|
||||||
return new File(getDirectory(userId), taskId + LOW_RES_FILE_POSTFIX + BITMAP_EXTENSION);
|
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 protoFile = getProtoFile(taskId, userId);
|
||||||
final File bitmapLowResFile = getLowResolutionBitmapFile(taskId, userId);
|
final File bitmapLowResFile = getLowResolutionBitmapFile(taskId, userId);
|
||||||
protoFile.delete();
|
protoFile.delete();
|
||||||
|
if (bitmapLowResFile.exists()) {
|
||||||
bitmapLowResFile.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);
|
final File bitmapFile = getHighResolutionBitmapFile(taskId, userId);
|
||||||
|
if (bitmapFile.exists()) {
|
||||||
bitmapFile.delete();
|
bitmapFile.delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -343,6 +357,8 @@ class TaskSnapshotPersister {
|
|||||||
final TaskSnapshotProto proto = new TaskSnapshotProto();
|
final TaskSnapshotProto proto = new TaskSnapshotProto();
|
||||||
proto.orientation = mSnapshot.getOrientation();
|
proto.orientation = mSnapshot.getOrientation();
|
||||||
proto.rotation = mSnapshot.getRotation();
|
proto.rotation = mSnapshot.getRotation();
|
||||||
|
proto.taskWidth = mSnapshot.getTaskSize().x;
|
||||||
|
proto.taskHeight = mSnapshot.getTaskSize().y;
|
||||||
proto.insetLeft = mSnapshot.getContentInsets().left;
|
proto.insetLeft = mSnapshot.getContentInsets().left;
|
||||||
proto.insetTop = mSnapshot.getContentInsets().top;
|
proto.insetTop = mSnapshot.getContentInsets().top;
|
||||||
proto.insetRight = mSnapshot.getContentInsets().right;
|
proto.insetRight = mSnapshot.getContentInsets().right;
|
||||||
@@ -352,7 +368,6 @@ class TaskSnapshotPersister {
|
|||||||
proto.systemUiVisibility = mSnapshot.getSystemUiVisibility();
|
proto.systemUiVisibility = mSnapshot.getSystemUiVisibility();
|
||||||
proto.isTranslucent = mSnapshot.isTranslucent();
|
proto.isTranslucent = mSnapshot.isTranslucent();
|
||||||
proto.topActivityComponent = mSnapshot.getTopActivityComponent().flattenToString();
|
proto.topActivityComponent = mSnapshot.getTopActivityComponent().flattenToString();
|
||||||
proto.scale = mSnapshot.getScale();
|
|
||||||
proto.id = mSnapshot.getId();
|
proto.id = mSnapshot.getId();
|
||||||
final byte[] bytes = TaskSnapshotProto.toByteArray(proto);
|
final byte[] bytes = TaskSnapshotProto.toByteArray(proto);
|
||||||
final File file = getProtoFile(mTaskId, mUserId);
|
final File file = getProtoFile(mTaskId, mUserId);
|
||||||
@@ -379,11 +394,26 @@ class TaskSnapshotPersister {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Bitmap swBitmap = bitmap.copy(Config.ARGB_8888, false /* isMutable */);
|
final Bitmap swBitmap = bitmap.copy(Config.ARGB_8888, false /* isMutable */);
|
||||||
final Bitmap lowResBitmap = mSnapshot.isLowResolution()
|
|
||||||
? swBitmap
|
final File file = getHighResolutionBitmapFile(mTaskId, mUserId);
|
||||||
: Bitmap.createScaledBitmap(swBitmap,
|
try {
|
||||||
(int) (bitmap.getWidth() * mLowResScale),
|
FileOutputStream fos = new FileOutputStream(file);
|
||||||
(int) (bitmap.getHeight() * mLowResScale), true /* filter */);
|
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);
|
final File lowResFile = getLowResolutionBitmapFile(mTaskId, mUserId);
|
||||||
try {
|
try {
|
||||||
@@ -396,22 +426,6 @@ class TaskSnapshotPersister {
|
|||||||
}
|
}
|
||||||
lowResBitmap.recycle();
|
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;
|
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.FLAG_WATCH_OUTSIDE_TOUCH;
|
||||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
|
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
|
||||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
|
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.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
|
||||||
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
|
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
|
||||||
import static com.android.internal.policy.DecorView.getColorViewLeftInset;
|
import static com.android.internal.policy.DecorView.getColorViewLeftInset;
|
||||||
@@ -53,9 +54,11 @@ import android.content.Context;
|
|||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.GraphicBuffer;
|
import android.graphics.GraphicBuffer;
|
||||||
|
import android.graphics.Matrix;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.RectF;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
@@ -131,6 +134,8 @@ class TaskSnapshotSurface implements StartingSurface {
|
|||||||
private final Rect mContentInsets = new Rect();
|
private final Rect mContentInsets = new Rect();
|
||||||
private final Rect mFrame = new Rect();
|
private final Rect mFrame = new Rect();
|
||||||
private TaskSnapshot mSnapshot;
|
private TaskSnapshot mSnapshot;
|
||||||
|
private final RectF mTmpSnapshotSize = new RectF();
|
||||||
|
private final RectF mTmpDstFrame = new RectF();
|
||||||
private final CharSequence mTitle;
|
private final CharSequence mTitle;
|
||||||
private boolean mHasDrawn;
|
private boolean mHasDrawn;
|
||||||
private long mShownTime;
|
private long mShownTime;
|
||||||
@@ -141,6 +146,8 @@ class TaskSnapshotSurface implements StartingSurface {
|
|||||||
@VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
|
@VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
|
||||||
private final int mOrientationOnCreation;
|
private final int mOrientationOnCreation;
|
||||||
private final SurfaceControl.Transaction mTransaction;
|
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,
|
static TaskSnapshotSurface create(WindowManagerService service, ActivityRecord activity,
|
||||||
TaskSnapshot snapshot) {
|
TaskSnapshot snapshot) {
|
||||||
@@ -365,13 +372,17 @@ class TaskSnapshotSurface implements StartingSurface {
|
|||||||
frame = calculateSnapshotFrame(crop);
|
frame = calculateSnapshotFrame(crop);
|
||||||
mTransaction.setWindowCrop(mChildSurfaceControl, crop);
|
mTransaction.setWindowCrop(mChildSurfaceControl, crop);
|
||||||
mTransaction.setPosition(mChildSurfaceControl, frame.left, frame.top);
|
mTransaction.setPosition(mChildSurfaceControl, frame.left, frame.top);
|
||||||
|
mTmpDstFrame.set(frame);
|
||||||
} else {
|
} else {
|
||||||
frame = null;
|
frame = null;
|
||||||
|
mTmpDstFrame.set(mFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scale the mismatch dimensions to fill the task bounds
|
// Scale the mismatch dimensions to fill the task bounds
|
||||||
final float scale = 1 / mSnapshot.getScale();
|
mTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight());
|
||||||
mTransaction.setMatrix(mChildSurfaceControl, scale, 0, 0, scale);
|
mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL);
|
||||||
|
mTransaction.setMatrix(mChildSurfaceControl, mSnapshotMatrix, mTmpFloat9);
|
||||||
|
|
||||||
mTransaction.apply();
|
mTransaction.apply();
|
||||||
surface.attachAndQueueBufferWithColorSpace(buffer, mSnapshot.getColorSpace());
|
surface.attachAndQueueBufferWithColorSpace(buffer, mSnapshot.getColorSpace());
|
||||||
surface.release();
|
surface.release();
|
||||||
@@ -395,13 +406,17 @@ class TaskSnapshotSurface implements StartingSurface {
|
|||||||
rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight());
|
rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight());
|
||||||
final Rect insets = mSnapshot.getContentInsets();
|
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
|
// Let's remove all system decorations except the status bar, but only if the task is at the
|
||||||
// very top of the screen.
|
// very top of the screen.
|
||||||
final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0;
|
final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0;
|
||||||
rect.inset((int) (insets.left * mSnapshot.getScale()),
|
rect.inset((int) (insets.left * scaleX),
|
||||||
isTop ? 0 : (int) (insets.top * mSnapshot.getScale()),
|
isTop ? 0 : (int) (insets.top * scaleY),
|
||||||
(int) (insets.right * mSnapshot.getScale()),
|
(int) (insets.right * scaleX),
|
||||||
(int) (insets.bottom * mSnapshot.getScale()));
|
(int) (insets.bottom * scaleY));
|
||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,14 +427,20 @@ class TaskSnapshotSurface implements StartingSurface {
|
|||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
Rect calculateSnapshotFrame(Rect crop) {
|
Rect calculateSnapshotFrame(Rect crop) {
|
||||||
final Rect frame = new Rect(crop);
|
final float scaleX = (float) mSnapshot.getSnapshot().getWidth() / mSnapshot.getTaskSize().x;
|
||||||
final float scale = mSnapshot.getScale();
|
final float scaleY =
|
||||||
|
(float) mSnapshot.getSnapshot().getHeight() / mSnapshot.getTaskSize().y;
|
||||||
|
|
||||||
// Rescale the frame from snapshot to window coordinate space
|
// 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
|
// 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.
|
// However, we also need to make space for the navigation bar on the left side.
|
||||||
final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left,
|
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 com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_REAL;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotEquals;
|
import static org.junit.Assert.assertNotEquals;
|
||||||
import static org.junit.Assert.assertSame;
|
import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@@ -36,6 +37,7 @@ import android.content.res.Configuration;
|
|||||||
import android.graphics.ColorSpace;
|
import android.graphics.ColorSpace;
|
||||||
import android.graphics.GraphicBuffer;
|
import android.graphics.GraphicBuffer;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
|
import android.graphics.Point;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.platform.test.annotations.Presubmit;
|
import android.platform.test.annotations.Presubmit;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
@@ -138,6 +140,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
|
|||||||
final int orientation = Configuration.ORIENTATION_PORTRAIT;
|
final int orientation = Configuration.ORIENTATION_PORTRAIT;
|
||||||
final float scaleFraction = 0.25f;
|
final float scaleFraction = 0.25f;
|
||||||
final Rect contentInsets = new Rect(1, 2, 3, 4);
|
final Rect contentInsets = new Rect(1, 2, 3, 4);
|
||||||
|
final Point taskSize = new Point(5, 6);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ActivityManager.TaskSnapshot.Builder builder =
|
ActivityManager.TaskSnapshot.Builder builder =
|
||||||
@@ -147,14 +150,13 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
|
|||||||
builder.setSystemUiVisibility(systemUiVisibility);
|
builder.setSystemUiVisibility(systemUiVisibility);
|
||||||
builder.setWindowingMode(windowingMode);
|
builder.setWindowingMode(windowingMode);
|
||||||
builder.setColorSpace(sRGB);
|
builder.setColorSpace(sRGB);
|
||||||
builder.setIsLowResolution(true);
|
|
||||||
builder.setOrientation(orientation);
|
builder.setOrientation(orientation);
|
||||||
builder.setContentInsets(contentInsets);
|
builder.setContentInsets(contentInsets);
|
||||||
builder.setIsTranslucent(true);
|
builder.setIsTranslucent(true);
|
||||||
builder.setScaleFraction(0.25f);
|
|
||||||
builder.setSnapshot(buffer);
|
builder.setSnapshot(buffer);
|
||||||
builder.setIsRealSnapshot(true);
|
builder.setIsRealSnapshot(true);
|
||||||
builder.setPixelFormat(pixelFormat);
|
builder.setPixelFormat(pixelFormat);
|
||||||
|
builder.setTaskSize(taskSize);
|
||||||
|
|
||||||
// Not part of TaskSnapshot itself, used in screenshot process
|
// Not part of TaskSnapshot itself, used in screenshot process
|
||||||
assertEquals(pixelFormat, builder.getPixelFormat());
|
assertEquals(pixelFormat, builder.getPixelFormat());
|
||||||
@@ -165,13 +167,15 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
|
|||||||
assertEquals(systemUiVisibility, snapshot.getSystemUiVisibility());
|
assertEquals(systemUiVisibility, snapshot.getSystemUiVisibility());
|
||||||
assertEquals(windowingMode, snapshot.getWindowingMode());
|
assertEquals(windowingMode, snapshot.getWindowingMode());
|
||||||
assertEquals(sRGB, snapshot.getColorSpace());
|
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(orientation, snapshot.getOrientation());
|
||||||
assertEquals(contentInsets, snapshot.getContentInsets());
|
assertEquals(contentInsets, snapshot.getContentInsets());
|
||||||
assertTrue(snapshot.isTranslucent());
|
assertTrue(snapshot.isTranslucent());
|
||||||
assertEquals(scaleFraction, builder.getScaleFraction(), 0.001f);
|
|
||||||
assertSame(buffer, snapshot.getSnapshot());
|
assertSame(buffer, snapshot.getSnapshot());
|
||||||
assertTrue(snapshot.isRealSnapshot());
|
assertTrue(snapshot.isRealSnapshot());
|
||||||
|
assertEquals(taskSize, snapshot.getTaskSize());
|
||||||
} finally {
|
} finally {
|
||||||
if (buffer != null) {
|
if (buffer != null) {
|
||||||
buffer.destroy();
|
buffer.destroy();
|
||||||
@@ -188,11 +192,9 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
|
|||||||
|
|
||||||
final ActivityManager.TaskSnapshot.Builder builder =
|
final ActivityManager.TaskSnapshot.Builder builder =
|
||||||
new ActivityManager.TaskSnapshot.Builder();
|
new ActivityManager.TaskSnapshot.Builder();
|
||||||
final float scaleFraction = 0.8f;
|
|
||||||
mWm.mTaskSnapshotController.prepareTaskSnapshot(mAppWindow.mActivityRecord.getTask(),
|
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.
|
// The pixel format should be selected automatically.
|
||||||
assertNotEquals(PixelFormat.UNKNOWN, builder.getPixelFormat());
|
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_FULLSCREEN;
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
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.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.app.ActivityManager;
|
||||||
import android.app.ActivityManager.TaskSnapshot;
|
import android.app.ActivityManager.TaskSnapshot;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
@@ -36,10 +40,12 @@ import android.view.View;
|
|||||||
|
|
||||||
import androidx.test.filters.MediumTest;
|
import androidx.test.filters.MediumTest;
|
||||||
|
|
||||||
|
import com.android.server.wm.TaskSnapshotLoader.PreRLegacySnapshotConfig;
|
||||||
import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem;
|
import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.MockitoSession;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
@@ -55,6 +61,8 @@ import java.util.function.Predicate;
|
|||||||
@RunWith(WindowTestRunner.class)
|
@RunWith(WindowTestRunner.class)
|
||||||
public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBase {
|
public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBase {
|
||||||
|
|
||||||
|
private static final float DELTA = 0.00001f;
|
||||||
|
|
||||||
private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40);
|
private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -148,29 +156,172 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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()
|
TaskSnapshot a = new TaskSnapshotBuilder()
|
||||||
.setScale(0.5f)
|
.setScaleFraction(0.5f)
|
||||||
.setIsLowResolution(true)
|
.setIsLowResolution(true)
|
||||||
.build();
|
.build();
|
||||||
assertTrue(a.isLowResolution());
|
assertTrue(a.isLowResolution());
|
||||||
mPersister.persistSnapshot(1, mTestUserId, a);
|
mPersister.persistSnapshot(1, mTestUserId, a);
|
||||||
mPersister.waitForQueueEmpty();
|
mPersister.waitForQueueEmpty();
|
||||||
final File[] files = new File[]{new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
|
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[]{
|
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(files, File::exists, " must exist");
|
||||||
assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not 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);
|
assertNotNull(snapshot);
|
||||||
assertEquals(TEST_INSETS, snapshot.getContentInsets());
|
assertEquals(TEST_INSETS, snapshot.getContentInsets());
|
||||||
assertNotNull(snapshot.getSnapshot());
|
assertNotNull(snapshot.getSnapshot());
|
||||||
assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation());
|
assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation());
|
||||||
|
|
||||||
final TaskSnapshot snapshotNotExist = mLoader.loadTask(1, mTestUserId,
|
final TaskSnapshot snapshotNotExist = mLoader.loadTask(1, mTestUserId,
|
||||||
false /* isLowResolution */);
|
true /* isLowResolution */);
|
||||||
assertNull(snapshotNotExist);
|
assertNull(snapshotNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,13 +422,11 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa
|
|||||||
@Test
|
@Test
|
||||||
public void testScalePersistAndLoadSnapshot() {
|
public void testScalePersistAndLoadSnapshot() {
|
||||||
TaskSnapshot a = new TaskSnapshotBuilder()
|
TaskSnapshot a = new TaskSnapshotBuilder()
|
||||||
.setScale(0.25f)
|
.setScaleFraction(0.25f)
|
||||||
.build();
|
.build();
|
||||||
TaskSnapshot b = new TaskSnapshotBuilder()
|
TaskSnapshot b = new TaskSnapshotBuilder()
|
||||||
.setScale(0.75f)
|
.setScaleFraction(0.75f)
|
||||||
.build();
|
.build();
|
||||||
assertEquals(0.25f, a.getScale(), 1E-5);
|
|
||||||
assertEquals(0.75f, b.getScale(), 1E-5);
|
|
||||||
mPersister.persistSnapshot(1, mTestUserId, a);
|
mPersister.persistSnapshot(1, mTestUserId, a);
|
||||||
mPersister.persistSnapshot(2, mTestUserId, b);
|
mPersister.persistSnapshot(2, mTestUserId, b);
|
||||||
mPersister.waitForQueueEmpty();
|
mPersister.waitForQueueEmpty();
|
||||||
@@ -287,8 +436,6 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa
|
|||||||
false /* isLowResolution */);
|
false /* isLowResolution */);
|
||||||
assertNotNull(snapshotA);
|
assertNotNull(snapshotA);
|
||||||
assertNotNull(snapshotB);
|
assertNotNull(snapshotB);
|
||||||
assertEquals(0.25f, snapshotA.getScale(), 1E-5);
|
|
||||||
assertEquals(0.75f, snapshotB.getScale(), 1E-5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import android.graphics.Color;
|
|||||||
import android.graphics.ColorSpace;
|
import android.graphics.ColorSpace;
|
||||||
import android.graphics.GraphicBuffer;
|
import android.graphics.GraphicBuffer;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
|
import android.graphics.Point;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
@@ -87,8 +88,10 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase {
|
|||||||
* Builds a TaskSnapshot.
|
* Builds a TaskSnapshot.
|
||||||
*/
|
*/
|
||||||
static class TaskSnapshotBuilder {
|
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 mIsLowResolution = false;
|
||||||
private boolean mIsRealSnapshot = true;
|
private boolean mIsRealSnapshot = true;
|
||||||
private boolean mIsTranslucent = false;
|
private boolean mIsTranslucent = false;
|
||||||
@@ -96,8 +99,11 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase {
|
|||||||
private int mSystemUiVisibility = 0;
|
private int mSystemUiVisibility = 0;
|
||||||
private int mRotation = Surface.ROTATION_0;
|
private int mRotation = Surface.ROTATION_0;
|
||||||
|
|
||||||
TaskSnapshotBuilder setScale(float scale) {
|
TaskSnapshotBuilder() {
|
||||||
mScale = scale;
|
}
|
||||||
|
|
||||||
|
TaskSnapshotBuilder setScaleFraction(float scale) {
|
||||||
|
mScaleFraction = scale;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,15 +138,20 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TaskSnapshot build() {
|
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);
|
USAGE_HW_TEXTURE | USAGE_SW_READ_RARELY | USAGE_SW_READ_RARELY);
|
||||||
Canvas c = buffer.lockCanvas();
|
Canvas c = buffer.lockCanvas();
|
||||||
c.drawColor(Color.RED);
|
c.drawColor(Color.RED);
|
||||||
buffer.unlockCanvasAndPost(c);
|
buffer.unlockCanvasAndPost(c);
|
||||||
return new TaskSnapshot(MOCK_SNAPSHOT_ID, new ComponentName("", ""), buffer,
|
return new TaskSnapshot(MOCK_SNAPSHOT_ID, new ComponentName("", ""), buffer,
|
||||||
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
|
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
|
||||||
mRotation, TEST_INSETS,
|
mRotation, taskSize, TEST_INSETS,
|
||||||
mIsLowResolution, mScale, mIsRealSnapshot,
|
mIsLowResolution, mIsRealSnapshot,
|
||||||
mWindowingMode, mSystemUiVisibility, mIsTranslucent);
|
mWindowingMode, mSystemUiVisibility, mIsTranslucent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import android.graphics.Color;
|
|||||||
import android.graphics.ColorSpace;
|
import android.graphics.ColorSpace;
|
||||||
import android.graphics.GraphicBuffer;
|
import android.graphics.GraphicBuffer;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
|
import android.graphics.Point;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.platform.test.annotations.Presubmit;
|
import android.platform.test.annotations.Presubmit;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
@@ -67,12 +68,22 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase {
|
|||||||
int windowFlags, Rect taskBounds) {
|
int windowFlags, Rect taskBounds) {
|
||||||
final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888,
|
final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888,
|
||||||
GraphicBuffer.USAGE_SW_READ_RARELY | GraphicBuffer.USAGE_SW_WRITE_NEVER);
|
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(
|
final TaskSnapshot snapshot = new TaskSnapshot(
|
||||||
System.currentTimeMillis(),
|
System.currentTimeMillis(),
|
||||||
new ComponentName("", ""), buffer,
|
new ComponentName("", ""), buffer,
|
||||||
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
|
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
|
||||||
Surface.ROTATION_0, contentInsets, false,
|
Surface.ROTATION_0, taskSize, contentInsets, false,
|
||||||
1.0f, true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
|
true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
|
||||||
0 /* systemUiVisibility */, false /* isTranslucent */);
|
0 /* systemUiVisibility */, false /* isTranslucent */);
|
||||||
mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test",
|
mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test",
|
||||||
createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), sysuiVis, windowFlags, 0,
|
createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), sysuiVis, windowFlags, 0,
|
||||||
@@ -152,7 +163,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCalculateSnapshotCrop_taskNotOnTop() {
|
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());
|
assertEquals(new Rect(0, 10, 100, 90), mSurface.calculateSnapshotCrop());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user