Purge StoreWriteQueue items to avoid system health issues

If queue gets too deep we may run out of memory or cause other
system health issues.

Test: TaskSnapshotPersisterLoaderTest
Bug: 38416992
Bug: 37631016
Change-Id: I725c9a458f78af2e625f2451bb0030176035f596
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 866bfc0..297e288 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -39,6 +39,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayDeque;
+import java.util.ArrayList;
 
 /**
  * Persists {@link TaskSnapshot}s to disk.
@@ -55,10 +56,13 @@
     private static final int QUALITY = 95;
     private static final String PROTO_EXTENSION = ".proto";
     private static final String BITMAP_EXTENSION = ".jpg";
+    private static final int MAX_STORE_QUEUE_DEPTH = 2;
 
     @GuardedBy("mLock")
     private final ArrayDeque<WriteQueueItem> mWriteQueue = new ArrayDeque<>();
     @GuardedBy("mLock")
+    private final ArrayDeque<StoreWriteQueueItem> mStoreQueueItems = new ArrayDeque<>();
+    @GuardedBy("mLock")
     private boolean mQueueIdling;
     @GuardedBy("mLock")
     private boolean mPaused;
@@ -153,11 +157,22 @@
     @GuardedBy("mLock")
     private void sendToQueueLocked(WriteQueueItem item) {
         mWriteQueue.offer(item);
+        item.onQueuedLocked();
+        ensureStoreQueueDepthLocked();
         if (!mPaused) {
             mLock.notifyAll();
         }
     }
 
+    @GuardedBy("mLock")
+    private void ensureStoreQueueDepthLocked() {
+        while (mStoreQueueItems.size() > MAX_STORE_QUEUE_DEPTH) {
+            final StoreWriteQueueItem item = mStoreQueueItems.poll();
+            mWriteQueue.remove(item);
+            Slog.i(TAG, "Queue is too deep! Purged item with taskid=" + item.mTaskId);
+        }
+    }
+
     private File getDirectory(int userId) {
         return new File(mDirectoryResolver.getSystemDirectoryForUser(userId), SNAPSHOTS_DIRNAME);
     }
@@ -202,6 +217,9 @@
                         next = null;
                     } else {
                         next = mWriteQueue.poll();
+                        if (next != null) {
+                            next.onDequeuedLocked();
+                        }
                     }
                 }
                 if (next != null) {
@@ -226,6 +244,18 @@
 
     private abstract class WriteQueueItem {
         abstract void write();
+
+        /**
+         * Called when this queue item has been put into the queue.
+         */
+        void onQueuedLocked() {
+        }
+
+        /**
+         * Called when this queue item has been taken out of the queue.
+         */
+        void onDequeuedLocked() {
+        }
     }
 
     private class StoreWriteQueueItem extends WriteQueueItem {
@@ -240,6 +270,16 @@
         }
 
         @Override
+        void onQueuedLocked() {
+            mStoreQueueItems.offer(this);
+        }
+
+        @Override
+        void onDequeuedLocked() {
+            mStoreQueueItems.remove(this);
+        }
+
+        @Override
         void write() {
             if (!createDirectory(mUserId)) {
                 Slog.e(TAG, "Unable to create snapshot directory for user dir="