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

Commit 68d6a906 authored by wilsonshih's avatar wilsonshih
Browse files

Exclude non-main windows during their removal

...from being captured in the snapshot.
If a window that is being removed is captured in a snapshot, the
snapshot will definitely be inconsistent with the activity upon the
next launch. Therefore, it is recommended to either exclude non-main
windows from being captured in a snapshot, or to remove the activity
snapshot if a window is removed after the snapshot was captured.

Flag: com.android.window.flags.exclude_non_main_window_from_snapshot
Bug: 412295455
Test: Launch an activity from a popup window. Then, verify either that the
popup window won't be captured in the snapshot, or that the activity
snapshot is removed after the popup window is removed.

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

flag {
    name: "exclude_non_main_window_from_snapshot"
    namespace: "windowing_frontend"
    description: "Exclude non-main windows during their removal from being captured in the snapshot."
    bug: "412295455"
    is_fixed_read_only: true
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "current_animator_scale_uses_shared_memory"
    namespace: "wear_system_health"
+18 −13
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;

import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -54,6 +55,7 @@ import com.android.server.wm.utils.InsetUtils;
import com.android.window.flags.Flags;

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

@@ -62,7 +64,7 @@ import java.util.function.Supplier;
 * @param <TYPE> The basic type, either Task or ActivityRecord
 * @param <CACHE> The basic cache for either Task or ActivityRecord
 */
abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
abstract class AbsAppSnapshotController<TYPE extends WindowContainer<?>,
        CACHE extends SnapshotCache<TYPE>> {
    static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotController" : TAG_WM;
    /**
@@ -267,28 +269,31 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
            }
            return null;
        }
        SurfaceControl[] excludeLayers;
        final WindowState imeWindow = source.getDisplayContent().mInputMethodWindow;
        // Exclude IME window snapshot when IME isn't proper to attach to app.
        final boolean excludeIme = imeWindow != null && imeWindow.getSurfaceControl() != null
                && !source.getDisplayContent().shouldImeAttachedToApp();
        final WindowState navWindow =
                source.getDisplayContent().getDisplayPolicy().getNavigationBar();
        final ArrayList<SurfaceControl> excludeSurfaces = new ArrayList<>();
        if (excludeIme) {
            excludeSurfaces.add(imeWindow.getSurfaceControl());
        }
        // If config_attachNavBarToAppDuringTransition is true, the nav bar will be reparent to the
        // the swiped app when entering recent app, therefore the task will contain the navigation
        // bar and we should exclude it from snapshot.
        final boolean excludeNavBar = navWindow != null;
        if (excludeIme && excludeNavBar) {
            excludeLayers = new SurfaceControl[2];
            excludeLayers[0] = imeWindow.getSurfaceControl();
            excludeLayers[1] = navWindow.getSurfaceControl();
        } else if (excludeIme || excludeNavBar) {
            excludeLayers = new SurfaceControl[1];
            excludeLayers[0] =
                    excludeIme ? imeWindow.getSurfaceControl() : navWindow.getSurfaceControl();
        } else {
            excludeLayers = new SurfaceControl[0];
        if (navWindow != null) {
            excludeSurfaces.add(navWindow.getSurfaceControl());
        }
        if (Flags.excludeNonMainWindowFromSnapshot()) {
            source.forAllWindows(w -> {
                if (w.mAnimatingExit && !w.mRemoved && w.mAttrs.type != TYPE_BASE_APPLICATION) {
                    excludeSurfaces.add(w.getSurfaceControl());
                }
            }, true /* traverseTopToBottom */);
        }
        final SurfaceControl[] excludeLayers =
                excludeSurfaces.toArray(new SurfaceControl[excludeSurfaces.size()]);
        builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isVisible());
        final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
                ScreenCapture.captureLayersExcluding(
+12 −5
Original line number Diff line number Diff line
@@ -574,12 +574,18 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
        }, false /* traverseTopToBottom */);
    }

    boolean invalidateSnapshot(ActivityRecord activity) {
        if (shouldDisableSnapshots()) {
            return false;
        }
        return removeIfUserSavedFileExist(activity);
    }

    @Override
    void onAppRemoved(ActivityRecord activity) {
        if (shouldDisableSnapshots()) {
        if (!invalidateSnapshot(activity)) {
            return;
        }
        removeIfUserSavedFileExist(activity);
        if (DEBUG) {
            Slog.d(TAG, "ActivitySnapshotController#onAppRemoved delete snapshot " + activity);
        }
@@ -587,10 +593,9 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord

    @Override
    void onAppDied(ActivityRecord activity) {
        if (shouldDisableSnapshots()) {
        if (!invalidateSnapshot(activity)) {
            return;
        }
        removeIfUserSavedFileExist(activity);
        if (DEBUG) {
            Slog.d(TAG, "ActivitySnapshotController#onAppDied delete snapshot " + activity);
        }
@@ -659,7 +664,7 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
        }
    }

    private void removeIfUserSavedFileExist(ActivityRecord ar) {
    private boolean removeIfUserSavedFileExist(ActivityRecord ar) {
        final UserSavedFile usf = findSavedFile(ar);
        if (usf != null) {
            final SparseArray<UserSavedFile> usfs = getUserFiles(ar.mUserId);
@@ -671,7 +676,9 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
            }
            mSavedFilesInOrder.remove(usf);
            mPersister.removeSnapshot(usf.mFileId, ar.mUserId);
            return true;
        }
        return false;
    }

    @VisibleForTesting
+9 −0
Original line number Diff line number Diff line
@@ -2285,6 +2285,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
                    mActivityRecord != null && mActivityRecord.inTransition(),
                    Debug.getCallers(6));

            if (Flags.excludeNonMainWindowFromSnapshot()
                    && mAttrs.type != TYPE_BASE_APPLICATION && mHasSurface
                    && mActivityRecord != null && !mActivityRecord.isVisibleRequested()
                    && mWinAnimator.getShown()) {
                // Only remove the activity snapshot, because the user might still want to see the
                // task snapshot during the recents animation.
                mWmService.mSnapshotController.mActivitySnapshotController
                        .invalidateSnapshot(mActivityRecord);
            }
            // First, see if we need to run an animation. If we do, we have to hold off on removing the
            // window until the animation is done. If the display is frozen, just remove immediately,
            // since the animation wouldn't be seen.