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

Commit f1378742 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Ensure consistent surface visibility with window hierarchy

The candidates to update surface visibility:
- The visibility of activity or wallpaper is changed.
- All targets and participants of transition when the
  transition is finished.

And before applying surface visibility, it will check again to
skip if the window container is a participant of a collecting or
playing transition.

It would help:
- Reduce repeatedly visibility check in prepareSurface on every
  scheduled frame of WindowAnimator.
- Avoid disturbing visibility of container which is animating
  by shell transition.
- Avoid black screen, ANR, touch no response if any shell
  transition handler sets incorrect visibility.

mLastSurfaceShowing was no longer accurate since the surface
visibility can be set by WM shell.

Bug: 383241933
Flag: com.android.window.flags.respect_hierarchy_surface_visibility
Test: ActivityRecordTests
Change-Id: I616c2872baf9c3f1819f3a634e932b607891f5f6
parent 1b73388e
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -40,6 +40,17 @@ flag {
  }
}

flag {
  name: "respect_hierarchy_surface_visibility"
  namespace: "windowing_frontend"
  description: "Ensure consistent surface visibility with window hierarchy"
  bug: "383241933"
  is_fixed_read_only: true
  metadata {
    purpose: PURPOSE_BUGFIX
  }
}

flag {
  name: "use_cached_insets_for_display_switch"
  namespace: "windowing_frontend"
+27 −2
Original line number Diff line number Diff line
@@ -5574,6 +5574,7 @@ final class ActivityRecord extends WindowToken {
                mWmService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(
                        token);
            }
            mWmService.mAnimator.addSurfaceVisibilityUpdateIncludingAnimatableParents(this);
        }
    }

@@ -7221,6 +7222,15 @@ final class ActivityRecord extends WindowToken {

    @Override
    void prepareSurfaces() {
        if (mWmService.mFlags.mEnsureSurfaceVisibility) {
            // Input sink surface is not a part of animation, so apply in a steady state
            // (non-sync) with pending transaction.
            if (mVisible && mSyncState == SYNC_STATE_NONE) {
                mActivityRecordInputSink.applyChangesToSurfaceIfChanged(getPendingTransaction());
            }
            super.prepareSurfaces();
            return;
        }
        final boolean isDecorSurfaceBoosted =
                getTask() != null && getTask().isDecorSurfaceBoosted();
        final boolean show = (isVisible()
@@ -7246,6 +7256,15 @@ final class ActivityRecord extends WindowToken {
        super.prepareSurfaces();
    }

    @Override
    void updateSurfaceVisibility(Transaction t) {
        final boolean visible = mVisible
                // Ensure that the activity content is hidden when the decor surface is boosted to
                // prevent UI redressing attack.
                && (task == null || !task.isDecorSurfaceBoosted());
        t.setVisibility(mSurfaceControl, visible);
    }

    /**
     * @return Whether our {@link #getSurfaceControl} is currently showing.
     */
@@ -9495,9 +9514,15 @@ final class ActivityRecord extends WindowToken {
    }

    boolean canCaptureSnapshot() {
        if (mWmService.mFlags.mEnsureSurfaceVisibility) {
            if (!mVisible) {
                return false;
            }
        } else {
            if (!isSurfaceShowing() || findMainWindow() == null) {
                return false;
            }
        }
        return forAllWindows(
                // Ensure at least one window for the top app is visible before attempting to
                // take a screenshot. Visible here means that the WSA surface is shown and has
+2 −1
Original line number Diff line number Diff line
@@ -1895,7 +1895,8 @@ class BackNavigationController {
                    }
                }
                // Force update mLastSurfaceShowing for opening activity and its task.
                if (mWindowManagerService.mRoot.mTransitionController.isShellTransitionsEnabled()) {
                if (mWindowManagerService.mRoot.mTransitionController.isShellTransitionsEnabled()
                        && !mWindowManagerService.mFlags.mEnsureSurfaceVisibility) {
                    for (int i = visibleOpenActivities.length - 1; i >= 0; --i) {
                        WindowContainer.enforceSurfaceVisible(visibleOpenActivities[i]);
                    }
+20 −3
Original line number Diff line number Diff line
@@ -3320,6 +3320,10 @@ class Task extends TaskFragment {
            scheduleAnimation();
        }

        if (mWmService.mFlags.mEnsureSurfaceVisibility) {
            return;
        }

        // Let organizer manage task visibility for shell transition. So don't change it's
        // visibility during collecting.
        if (mTransitionController.isCollecting() && mCreatedByOrganizer) {
@@ -3343,6 +3347,11 @@ class Task extends TaskFragment {
        mLastSurfaceShowing = show;
    }

    @Override
    void updateSurfaceVisibility(SurfaceControl.Transaction t) {
        t.setVisibility(mSurfaceControl, isVisible());
    }

    /**
     * Fills in a {@link TaskInfo} with information from this task. Note that the base intent in the
     * task info will not include any extras or clip data.
@@ -3719,8 +3728,9 @@ class Task extends TaskFragment {
        }
        mDecorSurfaceContainer.commitBoostedState();

        // assignChildLayers() calls scheduleAnimation(), which calls prepareSurfaces()
        // to ensure child surface visibility.
        forAllActivities(mWmService.mAnimator::addSurfaceVisibilityUpdate,
                true /* traverseTopToBottom */);
        // This calls scheduleAnimation(), then WindowAnimator will update surface visibility.
        assignChildLayers();
    }

@@ -4812,7 +4822,14 @@ class Task extends TaskFragment {
                    // rotation change) after leaving this scope, the visibility operation will be
                    // put in sync transaction, then it is not synced with reparent.
                    if (lastParentBeforePip.mSyncState == SYNC_STATE_NONE) {
                        if (mWmService.mFlags.mEnsureSurfaceVisibility) {
                            if (lastParentBeforePip.isVisible()) {
                                lastParentBeforePip.getPendingTransaction().show(
                                        lastParentBeforePip.mSurfaceControl);
                            }
                        } else {
                            lastParentBeforePip.prepareSurfaces();
                        }
                        // If the moveToFront is a part of finishing transition, then make sure
                        // the z-order of tasks are up-to-date.
                        if (topActivity.mTransitionController.inFinishingTransition(topActivity)) {
+9 −1
Original line number Diff line number Diff line
@@ -1834,6 +1834,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
                }
                mConfigAtEndActivities = null;
            }
            ensureParticipantSurfaceVisibility();
            primaryDisplay.getPendingTransaction().merge(transaction);
            primaryDisplay.scheduleAnimation();
            mSyncId = -1;
@@ -2041,6 +2042,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
        }
    }

    void ensureParticipantSurfaceVisibility() {
        for (int i = mParticipants.size() - 1; i >= 0; i--) {
            mController.mAtm.mWindowManager.mAnimator.addSurfaceVisibilityUpdate(
                    mParticipants.valueAt(i));
        }
    }

    @VisibleForTesting
    void overrideAnimationOptionsToInfoIfNecessary(@NonNull TransitionInfo info) {
        if (mOverrideOptions == null) {
@@ -2526,7 +2534,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
    }

    /** Returns the parent that the remote animator can animate or control. */
    private static WindowContainer<?> getAnimatableParent(WindowContainer<?> wc) {
    static WindowContainer<?> getAnimatableParent(WindowContainer<?> wc) {
        WindowContainer<?> parent = wc.getParent();
        while (parent != null
                && (!parent.canCreateRemoteAnimationTarget() && !parent.isOrganized())) {
Loading