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

Commit 9b2f7b6b authored by wilsonshih's avatar wilsonshih
Browse files

Fixes multiple activity snapshots can stay in cache.

1. Fix snaphsot isn't remove from cache because there didn't search
the activity outside of transition. It's a regression from ag/23898517
2. Skip record activity snapshot if the persist queue is too deep.
The StoreWriteQueueItem will hold a snapshot reference and waiting
to write on disk in perisist queue, skip record the snapshot if there
already have too many store items, since those will be purge anyway.

Bug: 382014256
Flag: EXEMPT bugfix
Test: atest ActivitySnapshotControllerTests
Test: Launch activity continuously like stress test, monitor the
activity snapshot won't increase repidly within a short of time, and
there should always only one snapshot left in activity snapshot cache.

Change-Id: I65b245713682bc7796eb132050bc5bcebb0972dd
parent 055e721b
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ 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 android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -343,6 +345,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) {
            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 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
            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");
        }
    }

+7 −2
Original line number Diff line number Diff line
@@ -50,7 +50,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;
    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 @@ class SnapshotPersistQueue {
        }
    }

    @VisibleForTesting
    int peekWriteQueueSize() {
        synchronized (mLock) {
            return mStoreQueueItems.size();
        }
    }

    int peekQueueSize() {
        synchronized (mLock) {
            return mWriteQueue.size();
+36 −25
Original line number Diff line number Diff line
@@ -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 @@ import java.util.Arrays;
@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 @@ public class ActivitySnapshotControllerTests extends WindowTestsBase {
        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 @@ public class ActivitySnapshotControllerTests extends WindowTestsBase {
        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));
    }
}