Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f0fa481d authored by wilsonshih's avatar wilsonshih
Browse files

Allows continues persist snapshots to disk via ImageReader

Via creating Bitmap object via ImageReader, there won't take graphic
resources when persist snapshots to disk, so it won't slow down the
refresh rate anymore.
Second, when adding an item to mWriteQueue, remove any mutually
exclusive action items. When inserting a delete item, remove the
corresponding store item (and vice versa), as the previous action will
eventually be overridden.
Update the limitation for store queue depth:
- Hardware render involved items < MAX_HW_STORE_QUEUE_DEPTH
- Total (SW + HW) items < MaxRecentTasks

Flag: com.android.window.flags.extending_persistence_snapshot_queue_depth
Bug: 416144425
Test: atest TaskSnapshotPersisterLoaderTest ActivitySnapshotControllerTests
Test: swipe up to close desktop mode tasks, verify task snapshots can
persist in disk.
Test: run DisplayRefreshRateTest#testRefreshRate right after above
step, verify refresh rate won't drop down.

Change-Id: I0c370b63eed22007229fa26d695a31a93aef8acc
parent b18f46dc
Loading
Loading
Loading
Loading
+11 −0
Original line number Original line Diff line number Diff line
@@ -511,6 +511,17 @@ flag {
    }
    }
}
}


flag {
    name: "extending_persistence_snapshot_queue_depth"
    namespace: "windowing_frontend"
    description: "Use ImageReader to persist snapshot into disk, and extending the persistence snapshot queue depth."
    bug: "416144425"
    is_fixed_read_only: true
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}

flag {
flag {
    name: "intercept_motion_from_move_to_cancel"
    name: "intercept_motion_from_move_to_cancel"
    namespace: "windowing_frontend"
    namespace: "windowing_frontend"
+8 −4
Original line number Original line Diff line number Diff line
@@ -18,7 +18,7 @@ package com.android.server.wm;


import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;


import static com.android.server.wm.SnapshotPersistQueue.MAX_STORE_QUEUE_DEPTH;
import static com.android.server.wm.SnapshotPersistQueue.MAX_HW_STORE_QUEUE_DEPTH;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
@@ -34,6 +34,7 @@ import android.window.TaskSnapshot;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
import com.android.window.flags.Flags;


import java.io.File;
import java.io.File;
import java.io.PrintWriter;
import java.io.PrintWriter;
@@ -57,7 +58,7 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
    private static final boolean DEBUG = false;
    private static final boolean DEBUG = false;
    private static final String TAG = AbsAppSnapshotController.TAG;
    private static final String TAG = AbsAppSnapshotController.TAG;
    // Maximum persisted snapshot count on disk.
    // Maximum persisted snapshot count on disk.
    private static final int MAX_PERSIST_SNAPSHOT_COUNT = 20;
    static final int MAX_PERSIST_SNAPSHOT_COUNT = 20;


    static final String SNAPSHOTS_DIRNAME = "activity_snapshots";
    static final String SNAPSHOTS_DIRNAME = "activity_snapshots";


@@ -353,8 +354,11 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
        if (DEBUG) {
        if (DEBUG) {
            Slog.d(TAG, "ActivitySnapshotController#recordSnapshot " + activity);
            Slog.d(TAG, "ActivitySnapshotController#recordSnapshot " + activity);
        }
        }
        if (mPersister.mSnapshotPersistQueue.peekWriteQueueSize() >= MAX_STORE_QUEUE_DEPTH
        final int maxStoreQueue = Flags.extendingPersistenceSnapshotQueueDepth()
                || mPersister.mSnapshotPersistQueue.peekQueueSize() > MAX_PERSIST_SNAPSHOT_COUNT) {
                ? mSnapshotPersistQueue.mMaxTotalStoreQueue
                : MAX_HW_STORE_QUEUE_DEPTH;
        if (mSnapshotPersistQueue.peekWriteQueueSize() >= maxStoreQueue
                || mSnapshotPersistQueue.peekQueueSize() > MAX_PERSIST_SNAPSHOT_COUNT) {
            Slog.w(TAG, "Skipping recording activity snapshot, too many requests!");
            Slog.w(TAG, "Skipping recording activity snapshot, too many requests!");
            return;
            return;
        }
        }
+109 −10
Original line number Original line Diff line number Diff line
@@ -23,6 +23,7 @@ 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.app.ActivityTaskManager;
import android.graphics.Bitmap;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.graphics.PixelFormat;
import android.hardware.HardwareBuffer;
import android.hardware.HardwareBuffer;
@@ -49,6 +50,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayDeque;
import java.util.Iterator;


/**
/**
 * Singleton worker thread to queue up persist or delete tasks of {@link TaskSnapshot}s to disk.
 * Singleton worker thread to queue up persist or delete tasks of {@link TaskSnapshot}s to disk.
@@ -56,7 +58,7 @@ import java.util.ArrayDeque;
class SnapshotPersistQueue {
class SnapshotPersistQueue {
    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 long DELAY_MS = 100;
    private static final long DELAY_MS = 100;
    static final int MAX_STORE_QUEUE_DEPTH = 2;
    static final int MAX_HW_STORE_QUEUE_DEPTH = 2;
    private static final int COMPRESS_QUALITY = 95;
    private static final int COMPRESS_QUALITY = 95;


    @GuardedBy("mLock")
    @GuardedBy("mLock")
@@ -71,9 +73,11 @@ class SnapshotPersistQueue {
    private final Object mLock = new Object();
    private final Object mLock = new Object();
    private final UserManagerInternal mUserManagerInternal;
    private final UserManagerInternal mUserManagerInternal;
    private boolean mShutdown;
    private boolean mShutdown;
    final int mMaxTotalStoreQueue;


    SnapshotPersistQueue() {
    SnapshotPersistQueue() {
        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
        mMaxTotalStoreQueue = ActivityTaskManager.getMaxRecentTasksStatic();
    }
    }


    Object getLock() {
    Object getLock() {
@@ -173,7 +177,18 @@ class SnapshotPersistQueue {
    }
    }


    private void addToQueueInternal(WriteQueueItem item, boolean insertToFront) {
    private void addToQueueInternal(WriteQueueItem item, boolean insertToFront) {
        if (Flags.extendingPersistenceSnapshotQueueDepth()) {
            final Iterator<WriteQueueItem> iterator = mWriteQueue.iterator();
            while (iterator.hasNext()) {
                final WriteQueueItem next = iterator.next();
                if (item.isDuplicateOrExclusiveItem(next)) {
                    iterator.remove();
                    break;
                }
            }
        } else {
            mWriteQueue.removeFirstOccurrence(item);
            mWriteQueue.removeFirstOccurrence(item);
        }
        if (insertToFront) {
        if (insertToFront) {
            mWriteQueue.addFirst(item);
            mWriteQueue.addFirst(item);
        } else {
        } else {
@@ -200,11 +215,41 @@ class SnapshotPersistQueue {


    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private void ensureStoreQueueDepthLocked() {
    private void ensureStoreQueueDepthLocked() {
        while (mStoreQueueItems.size() > MAX_STORE_QUEUE_DEPTH) {
        if (!Flags.extendingPersistenceSnapshotQueueDepth()) {
            while (mStoreQueueItems.size() > MAX_HW_STORE_QUEUE_DEPTH) {
                final StoreWriteQueueItem item = mStoreQueueItems.poll();
                final StoreWriteQueueItem item = mStoreQueueItems.poll();
                mWriteQueue.remove(item);
                mWriteQueue.remove(item);
                Slog.i(TAG, "Queue is too deep! Purged item with index=" + item.mId);
                Slog.i(TAG, "Queue is too deep! Purged item with index=" + item.mId);
            }
            }
            return;
        }

        // Rules for store queue depth:
        //  - Hardware render involved items < MAX_HW_STORE_QUEUE_DEPTH
        //  - Total (SW + HW) items < mMaxTotalStoreQueue
        int hwStoreCount = 0;
        int totalStoreCount = 0;
        // Use descending iterator to keep the latest items.
        final Iterator<StoreWriteQueueItem> iterator = mStoreQueueItems.descendingIterator();
        while (iterator.hasNext() && mStoreQueueItems.size() > MAX_HW_STORE_QUEUE_DEPTH) {
            final StoreWriteQueueItem item = iterator.next();
            totalStoreCount++;
            boolean removeItem = false;
            if (mustPersistByHardwareRender(item.mSnapshot)) {
                hwStoreCount++;
                if (hwStoreCount > MAX_HW_STORE_QUEUE_DEPTH) {
                    removeItem = true;
                }
            }
            if (!removeItem && totalStoreCount > mMaxTotalStoreQueue) {
                removeItem = true;
            }
            if (removeItem) {
                iterator.remove();
                mWriteQueue.remove(item);
                Slog.i(TAG, "Queue is too deep! Purged item with index=" + item.mId);
            }
        }
    }
    }


    void deleteSnapshot(int index, int userId, PersistInfoProvider provider) {
    void deleteSnapshot(int index, int userId, PersistInfoProvider provider) {
@@ -302,6 +347,19 @@ class SnapshotPersistQueue {
         */
         */
        void onDequeuedLocked() {
        void onDequeuedLocked() {
        }
        }

        boolean isDuplicateOrExclusiveItem(WriteQueueItem testItem) {
            return false;
        }
    }

    static boolean mustPersistByHardwareRender(@NonNull TaskSnapshot snapshot) {
        final HardwareBuffer hwBuffer = snapshot.getHardwareBuffer();
        final int pixelFormat = hwBuffer.getFormat();
        return !Flags.extendingPersistenceSnapshotQueueDepth()
                || (pixelFormat != PixelFormat.RGB_565 && pixelFormat != PixelFormat.RGBA_8888)
                || !snapshot.isRealSnapshot()
                || TransitionAnimation.hasProtectedContent(hwBuffer);
    }
    }


    StoreWriteQueueItem createStoreWriteQueueItem(int id, int userId, TaskSnapshot snapshot,
    StoreWriteQueueItem createStoreWriteQueueItem(int id, int userId, TaskSnapshot snapshot,
@@ -411,10 +469,7 @@ class SnapshotPersistQueue {
            final int width = hwBuffer.getWidth();
            final int width = hwBuffer.getWidth();
            final int height = hwBuffer.getHeight();
            final int height = hwBuffer.getHeight();
            final int pixelFormat = hwBuffer.getFormat();
            final int pixelFormat = hwBuffer.getFormat();
            final Bitmap swBitmap = !Flags.reduceTaskSnapshotMemoryUsage()
            final Bitmap swBitmap = mustPersistByHardwareRender(mSnapshot)
                    || (pixelFormat != PixelFormat.RGB_565 && pixelFormat != PixelFormat.RGBA_8888)
                    || !mSnapshot.isRealSnapshot()
                    || TransitionAnimation.hasProtectedContent(hwBuffer)
                    ? copyToSwBitmapReadBack()
                    ? copyToSwBitmapReadBack()
                    : copyToSwBitmapDirect(width, height, pixelFormat);
                    : copyToSwBitmapDirect(width, height, pixelFormat);
            if (swBitmap == null) {
            if (swBitmap == null) {
@@ -511,6 +566,28 @@ class SnapshotPersistQueue {
                    && mPersistInfoProvider == other.mPersistInfoProvider;
                    && mPersistInfoProvider == other.mPersistInfoProvider;
        }
        }


        /** Called when the item is going to be removed from write queue. */
        void onRemovedFromWriteQueue() {
            mStoreQueueItems.remove(this);
            mSnapshot.removeReference(TaskSnapshot.REFERENCE_PERSIST);
        }

        @Override
        boolean isDuplicateOrExclusiveItem(WriteQueueItem testItem) {
            if (equals(testItem)) {
                final StoreWriteQueueItem swqi = (StoreWriteQueueItem) testItem;
                if (swqi.mSnapshot != mSnapshot) {
                    swqi.onRemovedFromWriteQueue();
                    return true;
                }
            } else if (testItem instanceof DeleteWriteQueueItem) {
                final DeleteWriteQueueItem dwqi = (DeleteWriteQueueItem) testItem;
                return dwqi.mId == mId && dwqi.mUserId == mUserId
                        && dwqi.mPersistInfoProvider == mPersistInfoProvider;
            }
            return false;
        }

        @Override
        @Override
        public String toString() {
        public String toString() {
            return "StoreWriteQueueItem{ID=" + mId + ", UserId=" + mUserId + "}";
            return "StoreWriteQueueItem{ID=" + mId + ", UserId=" + mUserId + "}";
@@ -541,6 +618,28 @@ class SnapshotPersistQueue {
        public String toString() {
        public String toString() {
            return "DeleteWriteQueueItem{ID=" + mId + ", UserId=" + mUserId + "}";
            return "DeleteWriteQueueItem{ID=" + mId + ", UserId=" + mUserId + "}";
        }
        }

        @Override
        public boolean equals(Object o) {
            if (o == null || getClass() != o.getClass()) return false;
            final DeleteWriteQueueItem other = (DeleteWriteQueueItem) o;
            return mId == other.mId && mUserId == other.mUserId
                    && mPersistInfoProvider == other.mPersistInfoProvider;
        }

        @Override
        boolean isDuplicateOrExclusiveItem(WriteQueueItem testItem) {
            if (testItem instanceof StoreWriteQueueItem) {
                final StoreWriteQueueItem swqi = (StoreWriteQueueItem) testItem;
                if (swqi.mId == mId && swqi.mUserId == mUserId
                        && swqi.mPersistInfoProvider == mPersistInfoProvider) {
                    swqi.onRemovedFromWriteQueue();
                    return true;
                }
                return false;
            }
            return equals(testItem);
        }
    }
    }


    void dump(PrintWriter pw, String prefix) {
    void dump(PrintWriter pw, String prefix) {
+8 −2
Original line number Original line Diff line number Diff line
@@ -21,7 +21,8 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
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.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.SnapshotPersistQueue.MAX_STORE_QUEUE_DEPTH;
import static com.android.server.wm.ActivitySnapshotController.MAX_PERSIST_SNAPSHOT_COUNT;
import static com.android.server.wm.SnapshotPersistQueue.MAX_HW_STORE_QUEUE_DEPTH;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertFalse;
@@ -39,6 +40,8 @@ import android.window.TaskSnapshot;


import androidx.test.filters.SmallTest;
import androidx.test.filters.SmallTest;


import com.android.window.flags.Flags;

import org.junit.Before;
import org.junit.Before;
import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;
@@ -275,7 +278,10 @@ public class ActivitySnapshotControllerTests extends TaskSnapshotPersisterTestBa


        mSnapshotPersistQueue.setPaused(true);
        mSnapshotPersistQueue.setPaused(true);
        final ArrayList<ActivityRecord> tmpList = new ArrayList<>();
        final ArrayList<ActivityRecord> tmpList = new ArrayList<>();
        for (int i = 0; i < MAX_STORE_QUEUE_DEPTH; ++i) {
        final int maxStoreQueue = Flags.extendingPersistenceSnapshotQueueDepth()
                ? MAX_PERSIST_SNAPSHOT_COUNT
                : MAX_HW_STORE_QUEUE_DEPTH;
        for (int i = 0; i < maxStoreQueue; ++i) {
            tmpList.clear();
            tmpList.clear();
            final ActivityRecord activity = createActivityRecord(task);
            final ActivityRecord activity = createActivityRecord(task);
            tmpList.add(activity);
            tmpList.add(activity);
+37 −0
Original line number Original line Diff line number Diff line
@@ -36,6 +36,8 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.SystemClock;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.util.ArraySet;
import android.util.ArraySet;
import android.view.Surface;
import android.view.Surface;
import android.window.TaskSnapshot;
import android.window.TaskSnapshot;
@@ -45,6 +47,7 @@ import androidx.test.filters.MediumTest;
import com.android.server.wm.AppSnapshotLoader.PreRLegacySnapshotConfig;
import com.android.server.wm.AppSnapshotLoader.PreRLegacySnapshotConfig;
import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem;
import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem;
import com.android.window.flags.Flags;


import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;
@@ -121,6 +124,7 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa
     * Tests that too many store write queue items are being purged.
     * Tests that too many store write queue items are being purged.
     */
     */
    @Test
    @Test
    @RequiresFlagsDisabled(Flags.FLAG_EXTENDING_PERSISTENCE_SNAPSHOT_QUEUE_DEPTH)
    public void testPurging() {
    public void testPurging() {
        mPersister.persistSnapshot(100, mTestUserId, createSnapshot());
        mPersister.persistSnapshot(100, mTestUserId, createSnapshot());
        mSnapshotPersistQueue.waitForQueueEmpty();
        mSnapshotPersistQueue.waitForQueueEmpty();
@@ -143,6 +147,39 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa
        assertTrueForFiles(existsFiles, File::exists, " must exist");
        assertTrueForFiles(existsFiles, File::exists, " must exist");
        assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
        assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
    }
    }
    /**
     * Tests that too many store write queue items are being purged.
     */
    @Test
    @RequiresFlagsEnabled(Flags.FLAG_EXTENDING_PERSISTENCE_SNAPSHOT_QUEUE_DEPTH)
    public void testPurging_HW() {
        mPersister.persistSnapshot(100, mTestUserId, createFakeSnapshot());
        mSnapshotPersistQueue.waitForQueueEmpty();
        mSnapshotPersistQueue.setPaused(true);
        mPersister.persistSnapshot(1, mTestUserId, createFakeSnapshot());
        mPersister.removeObsoleteFiles(new ArraySet<>(), new int[]{mTestUserId});
        mPersister.persistSnapshot(2, mTestUserId, createFakeSnapshot());
        mPersister.persistSnapshot(3, mTestUserId, createFakeSnapshot());
        mPersister.persistSnapshot(4, mTestUserId, createFakeSnapshot());
        // Verify there should only keep the latest request when received a duplicated id.
        mPersister.persistSnapshot(4, mTestUserId, createFakeSnapshot());
        // Expected 3: One remove obsolete request, two persist request.
        assertEquals(3, mSnapshotPersistQueue.peekQueueSize());
        mSnapshotPersistQueue.setPaused(false);
        mSnapshotPersistQueue.waitForQueueEmpty();

        // Make sure 1,2 were purged but removeObsoleteFiles wasn't.
        final File[] existsFiles = convertFilePath("3.proto", "4.proto");
        final File[] nonExistsFiles = convertFilePath("100.proto", "1.proto", "2.proto");
        assertTrueForFiles(existsFiles, File::exists, " must exist");
        assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
    }

    TaskSnapshot createFakeSnapshot() {
        return new TaskSnapshotBuilder().setTopActivityComponent(getUniqueComponentName())
                .setIsRealSnapshot(false).build();
    }



    @Test
    @Test
    public void testGetTaskId() {
    public void testGetTaskId() {