Merge "Fixes multiple activity snapshots can stay in cache." into main
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
index 24ed1bb..57a0bb5 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
@@ -18,6 +18,8 @@
 
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 
+import static com.android.server.wm.SnapshotPersistQueue.MAX_STORE_QUEUE_DEPTH;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -343,6 +345,11 @@
         if (DEBUG) {
             Slog.d(TAG, "ActivitySnapshotController#recordSnapshot " + activity);
         }
+        if (mPersister.mSnapshotPersistQueue.peekWriteQueueSize() >= MAX_STORE_QUEUE_DEPTH
+                || mPersister.mSnapshotPersistQueue.peekQueueSize() > MAX_PERSIST_SNAPSHOT_COUNT) {
+            Slog.w(TAG, "Skipping recording activity snapshot, too many requests!");
+            return;
+        }
         final int size = activity.size();
         final int[] mixedCode = new int[size];
         if (size == 1) {
@@ -432,7 +439,7 @@
             addBelowActivityIfExist(ar, mPendingLoadActivity, false, "load-snapshot");
         } else {
             // remove the snapshot for the one below close
-            addBelowActivityIfExist(ar, mPendingRemoveActivity, true, "remove-snapshot");
+            addBelowActivityIfExist(ar, mPendingRemoveActivity, false, "remove-snapshot");
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
index 8b63ecf7..a545454 100644
--- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
+++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
@@ -50,7 +50,7 @@
 class SnapshotPersistQueue {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotPersister" : TAG_WM;
     private static final long DELAY_MS = 100;
-    private static final int MAX_STORE_QUEUE_DEPTH = 2;
+    static final int MAX_STORE_QUEUE_DEPTH = 2;
     private static final int COMPRESS_QUALITY = 95;
 
     @GuardedBy("mLock")
@@ -154,7 +154,12 @@
         }
     }
 
-    @VisibleForTesting
+    int peekWriteQueueSize() {
+        synchronized (mLock) {
+            return mStoreQueueItems.size();
+        }
+    }
+
     int peekQueueSize() {
         synchronized (mLock) {
             return mWriteQueue.size();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
index 2a53df9..a7fc10f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
@@ -17,30 +17,23 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.SnapshotPersistQueue.MAX_STORE_QUEUE_DEPTH;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 
-import android.content.ComponentName;
-import android.graphics.ColorSpace;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.HardwareBuffer;
 import android.platform.test.annotations.Presubmit;
 import android.util.ArraySet;
-import android.view.Surface;
 import android.window.TaskSnapshot;
 
 import androidx.test.filters.SmallTest;
@@ -61,14 +54,20 @@
 @SmallTest
 @Presubmit
 @RunWith(WindowTestRunner.class)
-public class ActivitySnapshotControllerTests extends WindowTestsBase {
+public class ActivitySnapshotControllerTests extends TaskSnapshotPersisterTestBase {
 
     private ActivitySnapshotController mActivitySnapshotController;
 
+    public ActivitySnapshotControllerTests() {
+        super(0.8f /* highResScale */, 0.5f /* lowResScale */);
+    }
+
+    @Override
     @Before
-    public void setUp() throws Exception {
-        spyOn(mWm.mSnapshotController.mActivitySnapshotController);
-        mActivitySnapshotController = mWm.mSnapshotController.mActivitySnapshotController;
+    public void setUp() {
+        super.setUp();
+        mActivitySnapshotController = new ActivitySnapshotController(mWm, mSnapshotPersistQueue);
+        spyOn(mActivitySnapshotController);
         doReturn(false).when(mActivitySnapshotController).shouldDisableSnapshots();
         mActivitySnapshotController.resetTmpFields();
     }
@@ -92,12 +91,11 @@
         assertEquals(0, mActivitySnapshotController.mPendingRemoveActivity.size());
         mActivitySnapshotController.resetTmpFields();
 
-        // simulate three activity
+        // simulate three activity, the bottom activity won't participate in transition
         final WindowState belowClose = createAppWindow(task, ACTIVITY_TYPE_STANDARD,
                 "belowClose");
         belowClose.mActivityRecord.commitVisibility(
                 false /* visible */, true /* performLayout */);
-        windows.add(belowClose.mActivityRecord);
         mActivitySnapshotController.handleTransitionFinish(windows);
         assertEquals(1, mActivitySnapshotController.mPendingRemoveActivity.size());
         assertEquals(belowClose.mActivityRecord,
@@ -249,15 +247,28 @@
         assertEquals(taskSnapshot, mActivitySnapshotController.getSnapshot(activities));
     }
 
-    private TaskSnapshot createSnapshot() {
-        HardwareBuffer buffer = mock(HardwareBuffer.class);
-        doReturn(100).when(buffer).getWidth();
-        doReturn(100).when(buffer).getHeight();
-        return new TaskSnapshot(1, 0 /* captureTime */, new ComponentName("", ""), buffer,
-                ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
-                Surface.ROTATION_0, new Point(100, 100), new Rect() /* contentInsets */,
-                new Rect() /* letterboxInsets*/, false /* isLowResolution */,
-                true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, 0 /* mSystemUiVisibility */,
-                false /* isTranslucent */, false /* hasImeSurface */, 0 /* uiMode */);
+    /**
+     * Verifies that activity snapshot is skipped if the persister queue has too many pending write
+     * items.
+     */
+    @Test
+    public void testSkipRecordActivity() {
+        doReturn(createSnapshot()).when(mActivitySnapshotController).recordSnapshotInner(any());
+        final Task task = createTask(mDisplayContent);
+
+        mSnapshotPersistQueue.setPaused(true);
+        final ArrayList<ActivityRecord> tmpList = new ArrayList<>();
+        for (int i = 0; i < MAX_STORE_QUEUE_DEPTH; ++i) {
+            tmpList.clear();
+            final ActivityRecord activity = createActivityRecord(task);
+            tmpList.add(activity);
+            mActivitySnapshotController.recordSnapshot(tmpList);
+            assertNotNull(mActivitySnapshotController.findSavedFile(activity));
+        }
+        tmpList.clear();
+        final ActivityRecord activity = createActivityRecord(task);
+        tmpList.add(activity);
+        mActivitySnapshotController.recordSnapshot(tmpList);
+        assertNull(mActivitySnapshotController.findSavedFile(activity));
     }
 }