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

Commit b34b6e8e authored by Wei Sheng Shih's avatar Wei Sheng Shih Committed by Android (Google) Code Review
Browse files

Merge "Allows continues persist snapshots to disk via ImageReader" into main

parents 2ff65fe5 f0fa481d
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -470,6 +470,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 {
    name: "intercept_motion_from_move_to_cancel"
    namespace: "windowing_frontend"
+8 −4
Original line number 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 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.Nullable;
@@ -34,6 +34,7 @@ import android.window.TaskSnapshot;

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

import java.io.File;
import java.io.PrintWriter;
@@ -57,7 +58,7 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
    private static final boolean DEBUG = false;
    private static final String TAG = AbsAppSnapshotController.TAG;
    // 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";

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

/**
 * 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 {
    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotPersister" : TAG_WM;
    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;

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

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

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

    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);
        }
        if (insertToFront) {
            mWriteQueue.addFirst(item);
        } else {
@@ -200,11 +215,41 @@ class SnapshotPersistQueue {

    @GuardedBy("mLock")
    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();
                mWriteQueue.remove(item);
                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) {
@@ -302,6 +347,19 @@ class SnapshotPersistQueue {
         */
        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,
@@ -411,10 +469,7 @@ class SnapshotPersistQueue {
            final int width = hwBuffer.getWidth();
            final int height = hwBuffer.getHeight();
            final int pixelFormat = hwBuffer.getFormat();
            final Bitmap swBitmap = !Flags.reduceTaskSnapshotMemoryUsage()
                    || (pixelFormat != PixelFormat.RGB_565 && pixelFormat != PixelFormat.RGBA_8888)
                    || !mSnapshot.isRealSnapshot()
                    || TransitionAnimation.hasProtectedContent(hwBuffer)
            final Bitmap swBitmap = mustPersistByHardwareRender(mSnapshot)
                    ? copyToSwBitmapReadBack()
                    : copyToSwBitmapDirect(width, height, pixelFormat);
            if (swBitmap == null) {
@@ -511,6 +566,28 @@ class SnapshotPersistQueue {
                    && 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
        public String toString() {
            return "StoreWriteQueueItem{ID=" + mId + ", UserId=" + mUserId + "}";
@@ -541,6 +618,28 @@ class SnapshotPersistQueue {
        public String toString() {
            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) {
+8 −2
Original line number 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.spyOn;
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.assertFalse;
@@ -39,6 +40,8 @@ import android.window.TaskSnapshot;

import androidx.test.filters.SmallTest;

import com.android.window.flags.Flags;

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

        mSnapshotPersistQueue.setPaused(true);
        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();
            final ActivityRecord activity = createActivityRecord(task);
            tmpList.add(activity);
+37 −0
Original line number Diff line number Diff line
@@ -36,6 +36,8 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.util.ArraySet;
import android.view.Surface;
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.BaseAppSnapshotPersister.PersistInfoProvider;
import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem;
import com.android.window.flags.Flags;

import org.junit.Test;
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.
     */
    @Test
    @RequiresFlagsDisabled(Flags.FLAG_EXTENDING_PERSISTENCE_SNAPSHOT_QUEUE_DEPTH)
    public void testPurging() {
        mPersister.persistSnapshot(100, mTestUserId, createSnapshot());
        mSnapshotPersistQueue.waitForQueueEmpty();
@@ -143,6 +147,39 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa
        assertTrueForFiles(existsFiles, File::exists, " must 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
    public void testGetTaskId() {