Add config to control full task snapshot scale

- Also save the scale with the snapshot data instead of trying to infer it
  from the state
- Also recycle the reduced resolution snapshot copy after writing it

Bug: 135150767
Test: atest TaskSnapshotPersisterLoaderTest
Change-Id: I64a9ccf16f3966ad5c6d07ff0b6ce8e9dd710e40
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index fa73758..7bfed91 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -380,7 +380,7 @@
         // don't want the navigation bar background be moving around when resizing in docked mode.
         // However, we need it for the transitions into/out of docked mode.
         if (mNavigationBarColor != null && fullscreen) {
-            DecorView.getNavigationBarRect(width, height, stableInsets, systemInsets, mTmpRect);
+            DecorView.getNavigationBarRect(width, height, stableInsets, systemInsets, mTmpRect, 1f);
             mNavigationBarColor.setBounds(mTmpRect);
             mNavigationBarColor.draw(canvas);
         }
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 723f161..afe7954 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1080,10 +1080,13 @@
     }
 
     public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect stableInsets,
-            Rect contentInsets, Rect outRect) {
-        final int bottomInset = getColorViewBottomInset(stableInsets.bottom, contentInsets.bottom);
-        final int leftInset = getColorViewLeftInset(stableInsets.left, contentInsets.left);
-        final int rightInset = getColorViewLeftInset(stableInsets.right, contentInsets.right);
+            Rect contentInsets, Rect outRect, float scale) {
+        final int bottomInset =
+                (int) (getColorViewBottomInset(stableInsets.bottom, contentInsets.bottom) * scale);
+        final int leftInset =
+                (int) (getColorViewLeftInset(stableInsets.left, contentInsets.left) * scale);
+        final int rightInset =
+                (int) (getColorViewLeftInset(stableInsets.right, contentInsets.right) * scale);
         final int size = getNavBarSize(bottomInset, rightInset, leftInset);
         if (isNavBarToRightEdge(bottomInset, rightInset)) {
             outRect.set(canvasWidth - size, 0, canvasWidth, canvasHeight);
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 14abb77..dd75c56 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2787,6 +2787,9 @@
          screen. -->
     <bool name="config_lowRamTaskSnapshotsAndRecents">false</bool>
 
+    <!-- The amount to scale fullscreen snapshots for Overview and snapshot starting windows. -->
+    <item name="config_fullTaskSnapshotScale" format="float" type="dimen">1.0</item>
+
     <!-- Determines whether recent tasks are provided to the user. Default device has recents
          property. If this is false, then the following recents config flags are ignored. -->
     <bool name="config_hasRecents">true</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9d75654..50814c5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -355,6 +355,7 @@
   <java-symbol type="bool" name="config_enableMultiUserUI"/>
   <java-symbol type="bool" name="config_enableNewAutoSelectNetworkUI"/>
   <java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
+  <java-symbol type="dimen" name="config_fullTaskSnapshotScale" />
   <java-symbol type="bool" name="config_lowRamTaskSnapshotsAndRecents" />
   <java-symbol type="bool" name="config_hasRecents" />
   <java-symbol type="string" name="config_recentsComponentName" />
diff --git a/proto/src/task_snapshot.proto b/proto/src/task_snapshot.proto
index a1bbe52..381d983 100644
--- a/proto/src/task_snapshot.proto
+++ b/proto/src/task_snapshot.proto
@@ -32,4 +32,5 @@
      int32 system_ui_visibility = 8;
      bool is_translucent = 9;
      string top_activity_component = 10;
+     float scale = 11;
  }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index d526e7c..6e7ab2e3 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -95,6 +95,7 @@
     private final ArraySet<Task> mSkipClosingAppSnapshotTasks = new ArraySet<>();
     private final ArraySet<Task> mTmpTasks = new ArraySet<>();
     private final Handler mHandler = new Handler();
+    private final float mFullSnapshotScale;
 
     private final Rect mTmpRect = new Rect();
 
@@ -124,6 +125,8 @@
                 PackageManager.FEATURE_EMBEDDED);
         mIsRunningOnWear = mService.mContext.getPackageManager().hasSystemFeature(
             PackageManager.FEATURE_WATCH);
+        mFullSnapshotScale = mService.mContext.getResources().getFloat(
+                com.android.internal.R.dimen.config_fullTaskSnapshotScale);
     }
 
     void systemReady() {
@@ -287,7 +290,9 @@
         }
 
         final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
-        final float scaleFraction = isLowRamDevice ? mPersister.getReducedScale() : 1f;
+        final float scaleFraction = isLowRamDevice
+                ? mPersister.getReducedScale()
+                : mFullSnapshotScale;
 
         final WindowState mainWindow = appWindowToken.findMainWindow();
         if (mainWindow == null) {
@@ -378,9 +383,10 @@
                 task.getTaskDescription().getBackgroundColor(), 255);
         final LayoutParams attrs = mainWindow.getAttrs();
         final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
-                attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription());
-        final int width = task.getBounds().width();
-        final int height = task.getBounds().height();
+                attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(),
+                mFullSnapshotScale);
+        final int width = (int) (task.getBounds().width() * mFullSnapshotScale);
+        final int height = (int) (task.getBounds().height() * mFullSnapshotScale);
 
         final RenderNode node = RenderNode.create("TaskSnapshotController", null);
         node.setLeftTopRightBottom(0, 0, width, height);
@@ -399,7 +405,7 @@
         return new TaskSnapshot(topChild.mActivityComponent, hwBitmap.createGraphicBufferHandle(),
                 hwBitmap.getColorSpace(), topChild.getConfiguration().orientation,
                 getInsets(mainWindow), ActivityManager.isLowRamDeviceStatic() /* reduced */,
-                1.0f /* scale */, false /* isRealSnapshot */, task.getWindowingMode(),
+                mFullSnapshotScale, false /* isRealSnapshot */, task.getWindowingMode(),
                 getSystemUiVisibility(task), false);
     }
 
@@ -481,6 +487,7 @@
     }
 
     void dump(PrintWriter pw, String prefix) {
+        pw.println(prefix + "mFullSnapshotScale=" + mFullSnapshotScale);
         mCache.dump(pw, prefix);
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
index f7b8945..fcd97c1 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
@@ -87,14 +87,16 @@
                         + bitmapFile.getPath());
                 return null;
             }
-            ComponentName topActivityComponent = ComponentName.unflattenFromString(
+            final ComponentName topActivityComponent = ComponentName.unflattenFromString(
                     proto.topActivityComponent);
+            // For legacy snapshots, restore the scale based on the reduced resolution state
+            final float legacyScale = reducedResolution ? mPersister.getReducedScale() : 1f;
+            final float scale = Float.compare(proto.scale, 0f) != 0 ? proto.scale : legacyScale;
             return new TaskSnapshot(topActivityComponent, buffer, bitmap.getColorSpace(),
                     proto.orientation,
                     new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom),
-                    reducedResolution, reducedResolution ? mPersister.getReducedScale() : 1f,
-                    proto.isRealSnapshot, proto.windowingMode, proto.systemUiVisibility,
-                    proto.isTranslucent);
+                    reducedResolution, scale, proto.isRealSnapshot, proto.windowingMode,
+                    proto.systemUiVisibility, proto.isTranslucent);
         } catch (IOException e) {
             Slog.w(TAG, "Unable to load task snapshot data for taskId=" + taskId);
             return null;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 0b63f48..72fc189 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -342,6 +342,7 @@
             proto.systemUiVisibility = mSnapshot.getSystemUiVisibility();
             proto.isTranslucent = mSnapshot.isTranslucent();
             proto.topActivityComponent = mSnapshot.getTopActivityComponent().flattenToString();
+            proto.scale = mSnapshot.getScale();
             final byte[] bytes = TaskSnapshotProto.toByteArray(proto);
             final File file = getProtoFile(mTaskId, mUserId);
             final AtomicFile atomicFile = new AtomicFile(file);
@@ -369,12 +370,13 @@
             }
 
             final Bitmap swBitmap = bitmap.copy(Config.ARGB_8888, false /* isMutable */);
-            final File reducedFile = getReducedResolutionBitmapFile(mTaskId, mUserId);
             final Bitmap reduced = mSnapshot.isReducedResolution()
                     ? swBitmap
                     : Bitmap.createScaledBitmap(swBitmap,
                             (int) (bitmap.getWidth() * mReducedScale),
                             (int) (bitmap.getHeight() * mReducedScale), true /* filter */);
+
+            final File reducedFile = getReducedResolutionBitmapFile(mTaskId, mUserId);
             try {
                 FileOutputStream reducedFos = new FileOutputStream(reducedFile);
                 reduced.compress(JPEG, QUALITY, reducedFos);
@@ -383,6 +385,7 @@
                 Slog.e(TAG, "Unable to open " + reducedFile +" for persisting.", e);
                 return false;
             }
+            reduced.recycle();
 
             // For snapshots with reduced resolution, do not create or save full sized bitmaps
             if (mSnapshot.isReducedResolution()) {
@@ -399,7 +402,6 @@
                 Slog.e(TAG, "Unable to open " + file + " for persisting.", e);
                 return false;
             }
-            reduced.recycle();
             swBitmap.recycle();
             return true;
         }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 5d99db5..a7760a86 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -248,7 +248,7 @@
         mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
         mTaskBounds = taskBounds;
         mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
-                windowPrivateFlags, sysUiVis, taskDescription);
+                windowPrivateFlags, sysUiVis, taskDescription, 1f);
         mStatusBarColor = taskDescription.getStatusBarColor();
         mOrientationOnCreation = currentOrientation;
     }
@@ -488,12 +488,14 @@
         private final int mWindowFlags;
         private final int mWindowPrivateFlags;
         private final int mSysUiVis;
+        private final float mScale;
 
-        SystemBarBackgroundPainter( int windowFlags, int windowPrivateFlags, int sysUiVis,
-                TaskDescription taskDescription) {
+        SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int sysUiVis,
+                TaskDescription taskDescription, float scale) {
             mWindowFlags = windowFlags;
             mWindowPrivateFlags = windowPrivateFlags;
             mSysUiVis = sysUiVis;
+            mScale = scale;
             final Context context = ActivityThread.currentActivityThread().getSystemUiContext();
             final int semiTransparent = context.getColor(
                     R.color.system_bar_background_semi_transparent);
@@ -521,7 +523,7 @@
                     (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
             if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
                     mSysUiVis, mStatusBarColor, mWindowFlags, forceBarBackground)) {
-                return getColorViewTopInset(mStableInsets.top, mContentInsets.top);
+                return (int) (getColorViewTopInset(mStableInsets.top, mContentInsets.top) * mScale);
             } else {
                 return 0;
             }
@@ -544,8 +546,8 @@
                 int statusBarHeight) {
             if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0
                     && (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) {
-                final int rightInset = DecorView.getColorViewRightInset(mStableInsets.right,
-                        mContentInsets.right);
+                final int rightInset = (int) (DecorView.getColorViewRightInset(mStableInsets.right,
+                        mContentInsets.right) * mScale);
                 final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0;
                 c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint);
             }
@@ -555,7 +557,7 @@
         void drawNavigationBarBackground(Canvas c) {
             final Rect navigationBarRect = new Rect();
             getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets,
-                    navigationBarRect);
+                    navigationBarRect, mScale);
             final boolean visible = isNavigationBarColorViewVisible();
             if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) {
                 c.drawRect(navigationBarRect, mNavigationBarPaint);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index b0eafee..f958867 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -145,7 +145,10 @@
 
     @Test
     public void testLowResolutionPersistAndLoadSnapshot() {
-        TaskSnapshot a = createSnapshot(0.5f /* reducedResolution */);
+        TaskSnapshot a = new TaskSnapshotBuilder()
+                .setScale(0.5f)
+                .setReducedResolution(true)
+                .build();
         assertTrue(a.isReducedResolution());
         mPersister.persistSnapshot(1 , mTestUserId, a);
         mPersister.waitForQueueEmpty();
@@ -253,6 +256,27 @@
     }
 
     @Test
+    public void testScalePersistAndLoadSnapshot() {
+        TaskSnapshot a = new TaskSnapshotBuilder()
+                .setScale(0.25f)
+                .build();
+        TaskSnapshot b = new TaskSnapshotBuilder()
+                .setScale(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();
+        final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, false /* reduced */);
+        final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, false /* reduced */);
+        assertNotNull(snapshotA);
+        assertNotNull(snapshotB);
+        assertEquals(0.25f, snapshotA.getScale(), 1E-5);
+        assertEquals(0.75f, snapshotB.getScale(), 1E-5);
+    }
+
+    @Test
     public void testRemoveObsoleteFiles() {
         mPersister.persistSnapshot(1, mTestUserId, createSnapshot());
         mPersister.persistSnapshot(2, mTestUserId, createSnapshot());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index c3b0a67..e004cd3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -77,12 +77,7 @@
     }
 
     TaskSnapshot createSnapshot() {
-        return createSnapshot(1f /* scale */);
-    }
-
-    TaskSnapshot createSnapshot(float scale) {
         return new TaskSnapshotBuilder()
-                .setScale(scale)
                 .build();
     }
 
@@ -92,6 +87,7 @@
     static class TaskSnapshotBuilder {
 
         private float mScale = 1f;
+        private boolean mReducedResolution = false;
         private boolean mIsRealSnapshot = true;
         private boolean mIsTranslucent = false;
         private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
@@ -102,6 +98,11 @@
             return this;
         }
 
+        TaskSnapshotBuilder setReducedResolution(boolean reducedResolution) {
+            mReducedResolution = reducedResolution;
+            return this;
+        }
+
         TaskSnapshotBuilder setIsRealSnapshot(boolean isRealSnapshot) {
             mIsRealSnapshot = isRealSnapshot;
             return this;
@@ -130,7 +131,7 @@
             buffer.unlockCanvasAndPost(c);
             return new TaskSnapshot(new ComponentName("", ""), buffer,
                     ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT, TEST_INSETS,
-                    mScale < 1f /* reducedResolution */, mScale, mIsRealSnapshot,
+                    mReducedResolution, mScale, mIsRealSnapshot,
                     mWindowingMode, mSystemUiVisibility, mIsTranslucent);
         }
     }