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

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

Merge "Drawing app theme snapshot without hold wm lock." into main

parents 166ad296 09efebff
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -431,3 +431,14 @@ flag {
  bug: "277292497"
  is_fixed_read_only: true
}

flag {
    name: "exclude_drawing_app_theme_snapshot_from_lock"
    namespace: "windowing_frontend"
    description: "Do not hold wm lock when drawing app theme snapshot."
    is_fixed_read_only: true
    bug: "373502791"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
 No newline at end of file
+119 −38
Original line number Diff line number Diff line
@@ -51,8 +51,11 @@ import android.window.TaskSnapshot;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.server.wm.utils.InsetUtils;
import com.android.window.flags.Flags;

import java.io.PrintWriter;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * Base class for a Snapshot controller
@@ -148,43 +151,60 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
    protected abstract Rect getLetterboxInsets(ActivityRecord topActivity);

    /**
     * This is different than {@link #recordSnapshotInner(TYPE)} because it doesn't store
     * the snapshot to the cache and returns the TaskSnapshot immediately.
     *
     * This is only used for testing so the snapshot content can be verified.
     * This is different than {@link #recordSnapshotInner(TYPE, boolean, Consumer)}  because it
     * doesn't store the snapshot to the cache and returns the TaskSnapshot immediately.
     */
    @VisibleForTesting
    TaskSnapshot captureSnapshot(TYPE source) {
        final TaskSnapshot snapshot;
    SnapshotSupplier captureSnapshot(TYPE source, boolean allowAppTheme) {
        final SnapshotSupplier supplier = new SnapshotSupplier();
        switch (getSnapshotMode(source)) {
            case SNAPSHOT_MODE_NONE:
                return null;
            case SNAPSHOT_MODE_APP_THEME:
                snapshot = drawAppThemeSnapshot(source);
                Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "drawAppThemeSnapshot");
                if (Flags.excludeDrawingAppThemeSnapshotFromLock()) {
                    if (allowAppTheme) {
                        supplier.setSupplier(drawAppThemeSnapshot(source));
                    }
                } else {
                    final Supplier<TaskSnapshot> original = drawAppThemeSnapshot(source);
                    final TaskSnapshot snapshot = original != null ? original.get() : null;
                    supplier.setSnapshot(snapshot);
                }
                Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
                break;
            case SNAPSHOT_MODE_REAL:
                snapshot = snapshot(source);
                supplier.setSnapshot(snapshot(source));
                break;
            default:
                snapshot = null;
                break;
        }
        return snapshot;
        return supplier;
    }

    final TaskSnapshot recordSnapshotInner(TYPE source) {
    /**
     * @param allowAppTheme If true, allows to draw app theme snapshot when it's not allowed to take
     *                      a real screenshot, but create a fake representation of the app.
     * @param inLockConsumer Extra task to do in WM lock when first get the snapshot object.
     */
    final SnapshotSupplier recordSnapshotInner(TYPE source, boolean allowAppTheme,
            @Nullable Consumer<TaskSnapshot> inLockConsumer) {
        if (shouldDisableSnapshots()) {
            return null;
        }
        final TaskSnapshot snapshot = captureSnapshot(source);
        if (snapshot == null) {
            return null;
        final SnapshotSupplier supplier = captureSnapshot(source, allowAppTheme);
        supplier.setConsumer(t -> {
            synchronized (mService.mGlobalLock) {
                if (!source.isAttached()) {
                    return;
                }
        mCache.putSnapshot(source, snapshot);
        return snapshot;
                mCache.putSnapshot(source, t);
                if (inLockConsumer != null) {
                    inLockConsumer.accept(t);
                }
            }
        });
        return supplier;
    }

    @VisibleForTesting
    int getSnapshotMode(TYPE source) {
        final int type = source.getActivityType();
        if (type == ACTIVITY_TYPE_RECENTS || type == ACTIVITY_TYPE_DREAM) {
@@ -400,7 +420,7 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
     * If we are not allowed to take a real screenshot, this attempts to represent the app as best
     * as possible by using the theme's window background.
     */
    private TaskSnapshot drawAppThemeSnapshot(TYPE source) {
    private Supplier<TaskSnapshot> drawAppThemeSnapshot(TYPE source) {
        final ActivityRecord topActivity = getTopActivity(source);
        if (topActivity == null) {
            return null;
@@ -432,26 +452,46 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
        decorPainter.setInsets(systemBarInsets);
        decorPainter.drawDecors(c /* statusBarExcludeFrame */, null /* alreadyDrawFrame */);
        node.end(c);
        final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
        if (hwBitmap == null) {
            return null;
        }

        final Rect contentInsets = new Rect(systemBarInsets);
        final Rect letterboxInsets = getLetterboxInsets(topActivity);
        InsetUtils.addInsets(contentInsets, letterboxInsets);
        // Note, the app theme snapshot is never translucent because we enforce a non-translucent
        // color above
        final TaskSnapshot taskSnapshot = new TaskSnapshot(
                System.currentTimeMillis() /* id */,
                SystemClock.elapsedRealtimeNanos() /* captureTime */,
                topActivity.mActivityComponent, hwBitmap.getHardwareBuffer(),
                hwBitmap.getColorSpace(), mainWindow.getConfiguration().orientation,
                mainWindow.getWindowConfiguration().getRotation(), new Point(taskWidth, taskHeight),
                contentInsets, letterboxInsets, false /* isLowResolution */,
                false /* isRealSnapshot */, source.getWindowingMode(),
                attrs.insetsFlags.appearance, false /* isTranslucent */, false /* hasImeSurface */,
                topActivity.getConfiguration().uiMode /* uiMode */);
        return validateSnapshot(taskSnapshot);

        final TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
        builder.setIsRealSnapshot(false);
        builder.setId(System.currentTimeMillis());
        builder.setContentInsets(contentInsets);
        builder.setLetterboxInsets(letterboxInsets);

        builder.setTopActivityComponent(topActivity.mActivityComponent);
        // Note, the app theme snapshot is never translucent because we enforce a
        // non-translucent color above.
        builder.setIsTranslucent(false);
        builder.setWindowingMode(source.getWindowingMode());
        builder.setAppearance(attrs.insetsFlags.appearance);
        builder.setUiMode(topActivity.getConfiguration().uiMode);

        builder.setRotation(mainWindow.getWindowConfiguration().getRotation());
        builder.setOrientation(mainWindow.getConfiguration().orientation);
        builder.setTaskSize(new Point(taskWidth, taskHeight));
        builder.setCaptureTime(SystemClock.elapsedRealtimeNanos());

        return () -> {
            try {
                Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "drawAppThemeSnapshot_acquire");
                // Do not hold WM lock when calling to render thread.
                final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width,
                        height);
                if (hwBitmap == null) {
                    return null;
                }
                builder.setSnapshot(hwBitmap.getHardwareBuffer());
                builder.setColorSpace(hwBitmap.getColorSpace());
                return validateSnapshot(builder.build());
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
            }
        };
    }

    static Rect getSystemBarInsets(Rect frame, InsetsState state) {
@@ -482,4 +522,45 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
        pw.println(prefix + "mSnapshotEnabled=" + mSnapshotEnabled);
        mCache.dump(pw, prefix);
    }

    static class SnapshotSupplier implements Supplier<TaskSnapshot> {

        private TaskSnapshot mSnapshot;
        private boolean mHasSet;
        private Consumer<TaskSnapshot> mConsumer;
        private Supplier<TaskSnapshot> mSupplier;

        /** Callback when the snapshot is get for the first time. */
        void setConsumer(@NonNull Consumer<TaskSnapshot> consumer) {
            mConsumer = consumer;
        }

        void setSupplier(@NonNull Supplier<TaskSnapshot> createSupplier) {
            mSupplier = createSupplier;
        }

        void setSnapshot(TaskSnapshot snapshot) {
            mSnapshot = snapshot;
        }

        void handleSnapshot() {
            if (mHasSet) {
                return;
            }
            mHasSet = true;
            if (mSnapshot == null) {
                mSnapshot = mSupplier != null ? mSupplier.get() : null;
            }
            if (mConsumer != null && mSnapshot != null) {
                mConsumer.accept(mSnapshot);
            }
        }

        @Override
        @Nullable
        public TaskSnapshot get() {
            handleSnapshot();
            return mSnapshot;
        }
    }
}
+4 −1
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
import java.io.File;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.function.Supplier;

/**
 * When an app token becomes invisible, we take a snapshot (bitmap) and put it into our cache.
@@ -355,7 +356,9 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
        final int[] mixedCode = new int[size];
        if (size == 1) {
            final ActivityRecord singleActivity = activity.get(0);
            final TaskSnapshot snapshot = recordSnapshotInner(singleActivity);
            final Supplier<TaskSnapshot> supplier = recordSnapshotInner(singleActivity,
                    false /* allowAppTheme */, null /* inLockConsumer */);
            final TaskSnapshot snapshot = supplier != null ? supplier.get() : null;
            if (snapshot != null) {
                mixedCode[0] = getSystemHashCode(singleActivity);
                addUserSavedFile(singleActivity.mUserId, snapshot, mixedCode);
+6 −1
Original line number Diff line number Diff line
@@ -314,6 +314,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;

/**
 * System service for managing activities and their containers (task, displays,... ).
@@ -4038,6 +4039,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "takeTaskSnapshot()");
        final long ident = Binder.clearCallingIdentity();
        try {
            final Supplier<TaskSnapshot> supplier;
            synchronized (mGlobalLock) {
                final Task task = mRootWindowContainer.anyTaskForId(taskId,
                        MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
@@ -4050,11 +4052,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
                // be retrieved by recents. While if updateCache is false, the real snapshot will
                // always be taken and the snapshot won't be put into SnapshotPersister.
                if (updateCache) {
                    return mWindowManager.mTaskSnapshotController.recordSnapshot(task);
                    supplier = mWindowManager.mTaskSnapshotController
                            .getRecordSnapshotSupplier(task);
                } else {
                    return mWindowManager.mTaskSnapshotController.snapshot(task);
                }
            }
            return supplier != null ? supplier.get() : null;
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
@@ -6403,6 +6407,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        @Override
        public boolean shuttingDown(boolean booted, int timeout) {
            mShuttingDown = true;
            mWindowManager.mSnapshotController.mTaskSnapshotController.prepareShutdown();
            synchronized (mGlobalLock) {
                mRootWindowContainer.prepareForShutdown();
                updateEventDispatchingLocked(booted);
+0 −1
Original line number Diff line number Diff line
@@ -2926,7 +2926,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
    }

    void prepareForShutdown() {
        mWindowManager.mSnapshotController.mTaskSnapshotController.prepareShutdown();
        for (int i = 0; i < getChildCount(); i++) {
            createSleepToken("shutdown", getChildAt(i).mDisplayId);
        }
Loading