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

Commit 8e2b8372 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Remove unknown visibility record when finishing activity

In AR#isSyncFinished, isVisibilityUnknown has a higher priority
than isVisibleRequested. If the unknown record is not cleared, the
transition may wait for an actual invisible activity.

An example could be launching activities from keyguard with several
activity embedded operations (so the transition may not be ready
directly by starting window). A launching activity is set to
invisible before keyguardGoingAway is called. And then the activity
requests to finish, that may cause it to remain in unknown visibility
record. And because activity won't be stopped/destroyed until
transition is finished, the transition will time out.

Previously, finishing -> invisible can be handled, but invisible
-> finishing is not. So now always remove the unknown visibility
record when the state changes to finishing or stopped.

Bug: 305200092
Test: atest UnknownAppVisibilityControllerTest# \
      testRemoveFinishingInvisibleActivityFromUnknown
Change-Id: I221c72194e80eb26ac0f994904799e98f5354931
parent 52d4cfa2
Loading
Loading
Loading
Loading
+6 −7
Original line number Diff line number Diff line
@@ -4030,6 +4030,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        if (mAppStopped) {
            abortAndClearOptionsAnimation();
        }
        if (mDisplayContent != null) {
            mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(this);
        }
    }

    boolean isFinishing() {
@@ -5388,12 +5391,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        mLastDeferHidingClient = deferHidingClient;

        if (!visible) {
            // If this activity is about to finish/stopped and now becomes invisible, remove it
            // from the unknownApp list in case the activity does not want to draw anything, which
            // keep the user waiting for the next transition to start.
            if (finishing || isState(STOPPED)) {
                displayContent.mUnknownAppVisibilityController.appRemovedOrHidden(this);
            }
            // Because starting window was transferred, this activity may be a trampoline which has
            // been occluded by next activity. If it has added windows, set client visibility
            // immediately to avoid the client getting RELAYOUT_RES_FIRST_TIME from relayout and
@@ -5837,6 +5834,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                break;
            case STOPPED:
                mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_STOPPED);
                if (mDisplayContent != null) {
                    mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(this);
                }
                break;
            case DESTROYED:
                if (app != null && (mVisible || mVisibleRequested)) {
@@ -6517,7 +6517,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        }
        // Reset the last saved PiP snap fraction on app stop.
        mDisplayContent.mPinnedTaskController.onActivityHidden(mActivityComponent);
        mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(this);
        if (isClientVisible()) {
            // Though this is usually unlikely to happen, still make sure the client is invisible.
            setClientVisible(false);
+15 −0
Original line number Diff line number Diff line
@@ -378,8 +378,23 @@ class BLASTSyncEngine {
                if (!wc.isSyncFinished(this)) {
                    allFinished = false;
                    Slog.i(TAG, "Unfinished container: " + wc);
                    wc.forAllActivities(a -> {
                        if (a.isVisibleRequested()) {
                            if (a.isRelaunching()) {
                                Slog.i(TAG, "  " + a + " is relaunching");
                            }
                            a.forAllWindows(w -> {
                                Slog.i(TAG, "  " + w + " " + w.mWinAnimator.drawStateToString());
                            }, true /* traverseTopToBottom */);
                        } else if (a.mDisplayContent != null && !a.mDisplayContent
                                .mUnknownAppVisibilityController.allResolved()) {
                            Slog.i(TAG, "  UnknownAppVisibility: " + a.mDisplayContent
                                    .mUnknownAppVisibilityController.getDebugMessage());
                        }
                    });
                }
            }

            for (int i = mDependencies.size() - 1; i >= 0; --i) {
                allFinished = false;
                Slog.i(TAG, "Unfinished dependency: " + mDependencies.get(i).mSyncId);
+16 −4
Original line number Diff line number Diff line
@@ -70,6 +70,9 @@ class UnknownAppVisibilityController {
    }

    boolean isVisibilityUnknown(ActivityRecord r) {
        if (mUnknownApps.isEmpty()) {
            return false;
        }
        return mUnknownApps.containsKey(r);
    }

@@ -90,6 +93,9 @@ class UnknownAppVisibilityController {
    }

    void appRemovedOrHidden(@NonNull ActivityRecord activity) {
        if (mUnknownApps.isEmpty()) {
            return;
        }
        if (DEBUG_UNKNOWN_APP_VISIBILITY) {
            Slog.d(TAG, "App removed or hidden activity=" + activity);
        }
@@ -117,8 +123,11 @@ class UnknownAppVisibilityController {
     * Notifies that {@param activity} has finished resuming.
     */
    void notifyAppResumedFinished(@NonNull ActivityRecord activity) {
        if (mUnknownApps.containsKey(activity)
                && mUnknownApps.get(activity) == UNKNOWN_STATE_WAITING_RESUME) {
        if (mUnknownApps.isEmpty()) {
            return;
        }
        final Integer state = mUnknownApps.get(activity);
        if (state != null && state == UNKNOWN_STATE_WAITING_RESUME) {
            if (DEBUG_UNKNOWN_APP_VISIBILITY) {
                Slog.d(TAG, "App resume finished activity=" + activity);
            }
@@ -130,13 +139,16 @@ class UnknownAppVisibilityController {
     * Notifies that {@param activity} has relaid out.
     */
    void notifyRelayouted(@NonNull ActivityRecord activity) {
        if (!mUnknownApps.containsKey(activity)) {
        if (mUnknownApps.isEmpty()) {
            return;
        }
        final Integer state = mUnknownApps.get(activity);
        if (state == null) {
            return;
        }
        if (DEBUG_UNKNOWN_APP_VISIBILITY) {
            Slog.d(TAG, "App relayouted appWindow=" + activity);
        }
        int state = mUnknownApps.get(activity);
        if (state == UNKNOWN_STATE_WAITING_RELAYOUT || activity.mStartingWindow != null) {
            mUnknownApps.put(activity, UNKNOWN_STATE_WAITING_VISIBILITY_UPDATE);
            mDisplayContent.notifyKeyguardFlagsChanged();
+8 −3
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.wm;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import android.platform.test.annotations.Presubmit;
@@ -94,10 +95,14 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase {
    public void testRemoveFinishingInvisibleActivityFromUnknown() {
        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
        mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity);
        activity.finishing = true;
        activity.setVisibleRequested(true);
        activity.setVisibility(false);
        assertFalse(mDisplayContent.mUnknownAppVisibilityController.allResolved());
        activity.makeFinishingLocked();
        assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());

        mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity);
        assertTrue(mDisplayContent.mUnknownAppVisibilityController.isVisibilityUnknown(activity));
        activity.setState(ActivityRecord.State.STOPPED, "test");
        assertFalse(mDisplayContent.mUnknownAppVisibilityController.isVisibilityUnknown(activity));
    }

    @Test