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

Commit a39ae3ea authored by Andrii Kulian's avatar Andrii Kulian
Browse files

Defer client visibility update when calculating new config

In ActivityStackSupervisor#ensureVisibilityAndConfig() we first
update visibility of all activities to be able to properly calculate
configuration on the next step. However, we need to make sure the
latest config is applied whenever a client becomes visible.
To prevent making making activities visible without latest config
this CL defers messages to client in first visibility calculation
pass.

Bug: 76011287
Test: ActivityLifecycleTests
Test: ActivityManagerAppConfigurationTests
Change-Id: I978fc800322fb502545650b9f2eece96cd9c7f40
parent a7988330
Loading
Loading
Loading
Loading
+20 −8
Original line number Diff line number Diff line
@@ -313,6 +313,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
                                        // process that it is hidden.
    boolean sleeping;       // have we told the activity to sleep?
    boolean nowVisible;     // is this activity's window visible?
    boolean mClientVisibilityDeferred;// was the visibility change message to client deferred?
    boolean idle;           // has the activity gone idle?
    boolean hasBeenLaunched;// has this activity ever been launched?
    boolean frozenBeforeDestroy;// has been frozen but not yet destroyed.
@@ -1710,7 +1711,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
        return !behindFullscreenActivity || mLaunchTaskBehind;
    }

    void makeVisibleIfNeeded(ActivityRecord starting) {
    void makeVisibleIfNeeded(ActivityRecord starting, boolean reportToClient) {
        // This activity is not currently visible, but is running. Tell it to become visible.
        if (mState == RESUMED || this == starting) {
            if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY,
@@ -1730,15 +1731,28 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
            setVisible(true);
            sleeping = false;
            app.pendingUiClean = true;
            service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
                    WindowVisibilityItem.obtain(true /* showWindow */));
            if (reportToClient) {
                makeClientVisible();
            } else {
                mClientVisibilityDeferred = true;
            }
            // The activity may be waiting for stop, but that is no longer appropriate for it.
            mStackSupervisor.mStoppingActivities.remove(this);
            mStackSupervisor.mGoingToSleepActivities.remove(this);
        } catch (Exception e) {
            // Just skip on any failure; we'll make it visible when it next restarts.
            Slog.w(TAG, "Exception thrown making visible: " + intent.getComponent(), e);
        }
        handleAlreadyVisible();
    }

    /** Send visibility change message to the client and pause if needed. */
    void makeClientVisible() {
        mClientVisibilityDeferred = false;
        try {
            service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
                    WindowVisibilityItem.obtain(true /* showWindow */));
            if (shouldPauseWhenBecomingVisible()) {
                // Capture reason before state change

                // An activity must be in the {@link PAUSING} state for the system to validate
                // the move to {@link PAUSED}.
                setState(PAUSING, "makeVisibleIfNeeded");
@@ -1747,10 +1761,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
                                configChangeFlags, false /* dontReport */));
            }
        } catch (Exception e) {
            // Just skip on any failure; we'll make it visible when it next restarts.
            Slog.w(TAG, "Exception thrown making visible: " + intent.getComponent(), e);
            Slog.w(TAG, "Exception thrown sending visibility update: " + intent.getComponent(), e);
        }
        handleAlreadyVisible();
    }

    /** Check if activity should be moved to PAUSED state when it becomes visible. */
+11 −8
Original line number Diff line number Diff line
@@ -134,7 +134,6 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.util.ArraySet;
@@ -1361,8 +1360,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai

    void goToSleep() {
        // Ensure visibility without updating configuration, as activities are about to sleep.
        ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */, !PRESERVE_WINDOWS,
                false /* updateConfiguration */);
        ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
                !PRESERVE_WINDOWS);

        // Make sure any paused or stopped but visible activities are now sleeping.
        // This ensures that the activity's onStop() is called.
@@ -1837,7 +1836,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
    final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
            boolean preserveWindows) {
        ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
                true /* updateConfiguration */);
                true /* notifyClients */);
    }

    /**
@@ -1847,7 +1846,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
     */
    // TODO: Should be re-worked based on the fact that each task as a stack in most cases.
    final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
            boolean preserveWindows, boolean updateConfiguration) {
            boolean preserveWindows, boolean notifyClients) {
        mTopActivityOccludesKeyguard = false;
        mTopDismissingKeyguardActivity = null;
        mStackSupervisor.getKeyguardController().beginActivityVisibilityUpdate();
@@ -1899,7 +1898,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
                                + " finishing=" + r.finishing + " state=" + r.getState());
                        // First: if this is not the current activity being started, make
                        // sure it matches the current configuration.
                        if (r != starting && updateConfiguration) {
                        if (r != starting && notifyClients) {
                            r.ensureActivityConfiguration(0 /* globalChanges */, preserveWindows,
                                    true /* ignoreStopState */);
                        }
@@ -1919,11 +1918,15 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
                            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
                                    "Skipping: already visible at " + r);

                            if (r.mClientVisibilityDeferred && notifyClients) {
                                r.makeClientVisible();
                            }

                            if (r.handleAlreadyVisible()) {
                                resumeNextActivity = false;
                            }
                        } else {
                            r.makeVisibleIfNeeded(starting);
                            r.makeVisibleIfNeeded(starting, notifyClients);
                        }
                        // Aggregate current change flags.
                        configChanges |= r.configChangeFlags;
@@ -3836,7 +3839,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
            if (finishingActivityInNonFocusedStack) {
                // Finishing activity that was in paused state and it was in not currently focused
                // stack, need to make something visible in its place.
                mStackSupervisor.ensureVisibilityAndConfig(null, mDisplayId,
                mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
                        false /* markFrozenIfConfigChanged */, true /* deferResume */);
            }
            if (activityRemoved) {
+19 −10
Original line number Diff line number Diff line
@@ -1628,27 +1628,36 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D

    /**
     * Ensure all activities visibility, update orientation and configuration.
     *
     * @param starting The currently starting activity or {@code null} if there is none.
     * @param displayId The id of the display where operation is executed.
     * @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to
     *                                  {@code true} if config changed.
     * @param deferResume Whether to defer resume while updating config.
     */
    boolean ensureVisibilityAndConfig(ActivityRecord r, int displayId,
    boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
            boolean markFrozenIfConfigChanged, boolean deferResume) {
        // First ensure visibility without updating the config just yet. We need this to know what
        // activities are affecting configuration now.
        // Passing null here for 'starting' param value, so that visibility of actual starting
        // activity will be properly updated.
        ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
                false /* preserveWindows */, false /* updateConfiguration */);
                false /* preserveWindows */, false /* notifyClients */);

        // Force-update the orientation from the WindowManager, since we need the true configuration
        // to send to the client now.
        final Configuration config = mWindowManager.updateOrientationFromAppTokens(
                getDisplayOverrideConfiguration(displayId),
                r != null && r.mayFreezeScreenLocked(r.app) ? r.appToken : null,
                starting != null && starting.mayFreezeScreenLocked(starting.app)
                        ? starting.appToken : null,
                displayId, true /* forceUpdate */);
        if (r != null && markFrozenIfConfigChanged && config != null) {
            r.frozenBeforeDestroy = true;
        if (starting != null && markFrozenIfConfigChanged && config != null) {
            starting.frozenBeforeDestroy = true;
        }

        // Update the configuration of the activities on the display.
        return mService.updateDisplayOverrideConfigurationLocked(config, r,
                deferResume, displayId);
        return mService.updateDisplayOverrideConfigurationLocked(config, starting, deferResume,
                displayId);
    }

    private void logIfTransactionTooLarge(Intent intent, Bundle icicle) {
@@ -3675,14 +3684,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
    void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
            boolean preserveWindows) {
        ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
                true /* updateConfiguration */);
                true /* notifyClients */);
    }

    /**
     * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
     */
    void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
            boolean preserveWindows, boolean updateConfiguration) {
            boolean preserveWindows, boolean notifyClients) {
        getKeyguardController().beginActivityVisibilityUpdate();
        try {
            // First the front stacks. In case any are not fullscreen and are in front of home.
@@ -3691,7 +3700,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                    final ActivityStack stack = display.getChildAt(stackNdx);
                    stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
                            updateConfiguration);
                            notifyClients);
                }
            }
        } finally {
+4 −4
Original line number Diff line number Diff line
@@ -131,7 +131,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
        mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped");

        // The activity is in the focused stack so it should not move to paused.
        mActivity.makeVisibleIfNeeded(null /* starting */);
        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
        assertTrue(mActivity.isState(STOPPED));
        assertFalse(pauseFound.value);

@@ -139,14 +139,14 @@ public class ActivityRecordTests extends ActivityTestsBase {
        mActivity.mStackSupervisor.mFocusedStack = null;

        // In the unfocused stack, the activity should move to paused.
        mActivity.makeVisibleIfNeeded(null /* starting */);
        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
        assertTrue(mActivity.isState(PAUSING));
        assertTrue(pauseFound.value);

        // Make sure that the state does not change for current non-stopping states.
        mActivity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped");

        mActivity.makeVisibleIfNeeded(null /* starting */);
        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);

        assertTrue(mActivity.isState(INITIALIZING));

@@ -156,7 +156,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
        // Make sure that the state does not change when we have an activity becoming translucent
        final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
        mStack.mTranslucentActivityWaiting = topActivity;
        mActivity.makeVisibleIfNeeded(null /* starting */);
        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);

        assertTrue(mActivity.isState(STOPPED));
    }