Move ensure activity visibility processing to helper class (72/n)

Reduces the amount of cruft in ActivityStack class and makes it
helper to refactor later.

Bug: 80414790
Test: Existing tests pass
Change-Id: I937b7083348fbd860d70d2c80714a5491dc1e213
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index ebfc65e..5258357 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -1408,7 +1408,7 @@
             boolean preserveWindows, boolean notifyClients) {
         for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
             final ActivityStack stack = getChildAt(stackNdx);
-            stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
+            stack.ensureActivitiesVisible(starting, configChanges, preserveWindows,
                     notifyClients);
         }
     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 210a3c7..ba8212b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4569,7 +4569,7 @@
         // If the activity is stopped, stopping, cycle to an active state. We avoid doing
         // this when there is an activity waiting to become translucent as the extra binder
         // calls will lead to noticeable jank. A later call to
-        // ActivityStack#ensureActivitiesVisibleLocked will bring the activity to a proper
+        // ActivityStack#ensureActivitiesVisible will bring the activity to a proper
         // active state.
         if (!isState(STARTED, RESUMED, PAUSED, STOPPED, STOPPING)
                 || getActivityStack().mTranslucentActivityWaiting != null) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index d5350eb..f1c47eb 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -213,7 +213,7 @@
     static final String TAG_TASKS = TAG + POSTFIX_TASKS;
     private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION;
     private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
-    private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
+    static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
 
     // Ticks during which we check progress while waiting for an app to launch.
     private static final int LAUNCH_TICK = 500;
@@ -515,6 +515,8 @@
     }
 
     private static final ResetTargetTaskHelper sResetTargetTaskHelper = new ResetTargetTaskHelper();
+    private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
+            new EnsureActivitiesVisibleHelper(this);
 
     int numActivities() {
         int count = 0;
@@ -1492,8 +1494,7 @@
 
     void goToSleep() {
         // Ensure visibility without updating configuration, as activities are about to sleep.
-        ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
-                !PRESERVE_WINDOWS);
+        ensureActivitiesVisible(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.
@@ -1979,127 +1980,25 @@
      * Make sure that all activities that need to be visible in the stack (that is, they
      * currently can be seen by the user) actually are and update their configuration.
      */
-    final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
+    void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
             boolean preserveWindows) {
-        ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
-                true /* notifyClients */);
+        ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
     }
 
     /**
      * Ensure visibility with an option to also update the configuration of visible activities.
-     * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
+     * @see #ensureActivitiesVisible(ActivityRecord, int, boolean)
      * @see RootActivityContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
      */
     // 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,
+    void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
             boolean preserveWindows, boolean notifyClients) {
         mTopActivityOccludesKeyguard = false;
         mTopDismissingKeyguardActivity = null;
         mStackSupervisor.getKeyguardController().beginActivityVisibilityUpdate();
         try {
-            ActivityRecord top = topRunningActivityLocked();
-            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + top
-                    + " configChanges=0x" + Integer.toHexString(configChanges));
-            if (top != null) {
-                checkTranslucentActivityWaiting(top);
-            }
-
-            // If the top activity is not fullscreen, then we need to
-            // make sure any activities under it are now visible.
-            boolean aboveTop = top != null;
-            final boolean stackShouldBeVisible = shouldBeVisible(starting);
-            boolean behindFullscreenActivity = !stackShouldBeVisible;
-            // We should not resume activities that being launched behind because these
-            // activities are actually behind other fullscreen activities, but still required
-            // to be visible (such as performing Recents animation).
-            final boolean resumeTopActivity = isFocusable() && isInStackLocked(starting) == null
-                    && top != null && !top.mLaunchTaskBehind;
-            for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-                final Task task = getChildAt(taskNdx);
-                for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
-                    final ActivityRecord r = task.getChildAt(activityNdx);
-                    final boolean isTop = r == top;
-                    if (aboveTop && !isTop) {
-                        continue;
-                    }
-                    aboveTop = false;
-
-                    final boolean reallyVisible = r.shouldBeVisible(behindFullscreenActivity,
-                            false /* ignoringKeyguard */);
-                    // Check whether activity should be visible without Keyguard influence
-                    if (r.visibleIgnoringKeyguard) {
-                        if (r.occludesParent()) {
-                            // At this point, nothing else needs to be shown in this task.
-                            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
-                                    + " stackVisible=" + stackShouldBeVisible
-                                    + " behindFullscreen=" + behindFullscreenActivity);
-                            behindFullscreenActivity = true;
-                        } else {
-                            behindFullscreenActivity = false;
-                        }
-                    }
-
-                    if (reallyVisible) {
-                        if (r.finishing) {
-                            continue;
-                        }
-                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make visible? " + r
-                                + " 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 && notifyClients) {
-                            r.ensureActivityConfiguration(0 /* globalChanges */, preserveWindows,
-                                    true /* ignoreVisibility */);
-                        }
-
-                        if (!r.attachedToProcess()) {
-                            makeVisibleAndRestartIfNeeded(starting, configChanges, isTop,
-                                    resumeTopActivity && isTop, r);
-                        } else if (r.mVisibleRequested) {
-                            // If this activity is already visible, then there is nothing to do here.
-                            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
-                                    "Skipping: already visible at " + r);
-
-                            if (r.mClientVisibilityDeferred && notifyClients) {
-                                r.makeClientVisible();
-                            }
-
-                            r.handleAlreadyVisible();
-                            if (notifyClients) {
-                                r.makeActiveIfNeeded(starting);
-                            }
-                        } else {
-                            r.makeVisibleIfNeeded(starting, notifyClients);
-                        }
-                        // Aggregate current change flags.
-                        configChanges |= r.configChangeFlags;
-                    } else {
-                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make invisible? " + r
-                                + " finishing=" + r.finishing + " state=" + r.getState()
-                                + " stackShouldBeVisible=" + stackShouldBeVisible
-                                + " behindFullscreenActivity=" + behindFullscreenActivity
-                                + " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
-                        r.makeInvisible();
-                    }
-                }
-                final int windowingMode = getWindowingMode();
-                if (windowingMode == WINDOWING_MODE_FREEFORM) {
-                    // The visibility of tasks and the activities they contain in freeform stack are
-                    // determined individually unlike other stacks where the visibility or fullscreen
-                    // status of an activity in a previous task affects other.
-                    behindFullscreenActivity = !stackShouldBeVisible;
-                } else if (isActivityTypeHome()) {
-                    if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + task
-                            + " stackShouldBeVisible=" + stackShouldBeVisible
-                            + " behindFullscreenActivity=" + behindFullscreenActivity);
-                    // No other task in the home stack should be visible behind the home activity.
-                    // Home activities is usually a translucent activity with the wallpaper behind
-                    // them. However, when they don't have the wallpaper behind them, we want to
-                    // show activities in the next application stack behind them vs. another
-                    // task in the home stack like recents.
-                    behindFullscreenActivity = true;
-                }
-            }
+            mEnsureActivitiesVisibleHelper.process(
+                    starting, configChanges, preserveWindows, notifyClients);
 
             if (mTranslucentActivityWaiting != null &&
                     mUndrawnActivitiesBelowTopTranslucent.isEmpty()) {
@@ -2214,7 +2113,7 @@
         return (flags & FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0;
     }
 
-    private void checkTranslucentActivityWaiting(ActivityRecord top) {
+    void checkTranslucentActivityWaiting(ActivityRecord top) {
         if (mTranslucentActivityWaiting != top) {
             mUndrawnActivitiesBelowTopTranslucent.clear();
             if (mTranslucentActivityWaiting != null) {
@@ -2226,31 +2125,6 @@
         }
     }
 
-    private boolean makeVisibleAndRestartIfNeeded(ActivityRecord starting, int configChanges,
-            boolean isTop, boolean andResume, ActivityRecord r) {
-        // We need to make sure the app is running if it's the top, or it is just made visible from
-        // invisible. If the app is already visible, it must have died while it was visible. In this
-        // case, we'll show the dead window but will not restart the app. Otherwise we could end up
-        // thrashing.
-        if (isTop || !r.mVisibleRequested) {
-            // This activity needs to be visible, but isn't even running...
-            // get it started and resume if no other stack in this stack is resumed.
-            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Start and freeze screen for " + r);
-            if (r != starting) {
-                r.startFreezingScreenLocked(configChanges);
-            }
-            if (!r.mVisibleRequested || r.mLaunchTaskBehind) {
-                if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Starting and making visible: " + r);
-                r.setVisibility(true);
-            }
-            if (r != starting) {
-                mStackSupervisor.startSpecificActivityLocked(r, andResume, true /* checkConfig */);
-                return true;
-            }
-        }
-        return false;
-    }
-
     void convertActivityToTranslucent(ActivityRecord r) {
         mTranslucentActivityWaiting = r;
         mUndrawnActivitiesBelowTopTranslucent.clear();
@@ -2490,7 +2364,7 @@
                         && next.containsDismissKeyguardWindow();
 
                 if (canShowWhenLocked || mayDismissKeyguard) {
-                    ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
+                    ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
                             !PRESERVE_WINDOWS);
                     nothingToResume = shouldSleepActivities();
                 }
@@ -2819,7 +2693,7 @@
                     next.showStartingWindow(null /* prev */, false /* newTask */,
                             false /* taskSwitch */);
                 }
-                mStackSupervisor.startSpecificActivityLocked(next, true, false);
+                mStackSupervisor.startSpecificActivity(next, true, false);
                 return true;
             }
 
@@ -2846,7 +2720,7 @@
                 if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
             }
             if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Restarting " + next);
-            mStackSupervisor.startSpecificActivityLocked(next, true, true);
+            mStackSupervisor.startSpecificActivity(next, true, true);
         }
 
         return true;
@@ -2991,7 +2865,7 @@
                 // Don't do a starting window for mLaunchTaskBehind. More importantly make sure we
                 // tell WindowManager that r is visible even though it is at the back of the stack.
                 r.setVisibility(true);
-                ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
                 // Go ahead to execute app transition for this activity since the app transition
                 // will not be triggered through the resume channel.
                 getDisplay().mDisplayContent.executeAppTransition();
@@ -4381,7 +4255,7 @@
 
         // The task might have already been running and its visibility needs to be synchronized with
         // the visibility of the stack / windows.
-        ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+        ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
         mRootActivityContainer.resumeFocusedStacksTopActivities();
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 6ea80d2..304f230 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -975,7 +975,7 @@
         }
     }
 
-    void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
+    void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
         // Is this activity's application already running?
         final WindowProcessController wpc =
                 mService.getProcessController(r.processName, r.info.applicationInfo.uid);
@@ -1742,12 +1742,12 @@
              * to the fullscreen stack.  This is to guarantee that when we are removing a stack,
              * that the client receives onStop() before it is reparented.  We do this by detaching
              * the stack from the display so that it will be considered invisible when
-             * ensureActivitiesVisibleLocked() is called, and all of its activitys will be marked
+             * ensureActivitiesVisible() is called, and all of its activitys will be marked
              * invisible as well and added to the stopping list.  After which we process the
              * stopping list by handling the idle.
              */
             stack.mForceHidden = true;
-            stack.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+            stack.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
             stack.mForceHidden = false;
             activityIdleInternalLocked(null, false /* fromTimeout */,
                     true /* processPausingActivites */, null /* configuration */);
@@ -2072,7 +2072,7 @@
 
         mRecentTasks.add(task);
         mService.getTaskChangeNotificationController().notifyTaskStackChanged();
-        stack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+        stack.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
 
         // When launching tasks behind, update the last active time of the top task after the new
         // task has been shown briefly
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 21d5861..8455c6d 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1574,7 +1574,7 @@
                 // Also, we don't want to resume activities in a task that currently has an overlay
                 // as the starting activity just needs to be in the visible paused state until the
                 // over is removed.
-                mTargetStack.ensureActivitiesVisibleLocked(mStartActivity, 0, !PRESERVE_WINDOWS);
+                mTargetStack.ensureActivitiesVisible(mStartActivity, 0, !PRESERVE_WINDOWS);
                 // Go ahead and tell window manager to execute app transition for this activity
                 // since the app transition will not be triggered through the resume channel.
                 mTargetStack.getDisplay().mDisplayContent.executeAppTransition();
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 104805f..320ca65 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -346,7 +346,7 @@
                         false /* preserveWindow */);
                 // And we need to make sure at this point that all other activities
                 // are made visible with the correct configuration.
-                stack.ensureActivitiesVisibleLocked(starting, 0, !PRESERVE_WINDOWS);
+                stack.ensureActivitiesVisible(starting, 0, !PRESERVE_WINDOWS);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
new file mode 100644
index 0000000..3aae1b1
--- /dev/null
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+
+import static com.android.server.wm.ActivityStack.TAG_VISIBILITY;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
+
+import android.util.Slog;
+
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+/** Helper class to ensure activities are in the right visible state for a container. */
+class EnsureActivitiesVisibleHelper {
+    private final ActivityStack mContiner;
+    private ActivityRecord mTop;
+    private ActivityRecord mStarting;
+    private boolean mAboveTop;
+    private boolean mContainerShouldBeVisible;
+    private boolean mBehindFullscreenActivity;
+    private int mConfigChanges;
+    private boolean mPreserveWindows;
+    private boolean mNotifyClients;
+
+    EnsureActivitiesVisibleHelper(ActivityStack container) {
+        mContiner = container;
+    }
+
+    void reset(ActivityRecord starting, int configChanges, boolean preserveWindows,
+            boolean notifyClients) {
+        mStarting = starting;
+        mTop = mContiner.topRunningActivityLocked();
+        // If the top activity is not fullscreen, then we need to make sure any activities under it
+        // are now visible.
+        mAboveTop = mTop != null;
+        mContainerShouldBeVisible = mContiner.shouldBeVisible(mStarting);
+        mBehindFullscreenActivity = !mContainerShouldBeVisible;
+        mConfigChanges = configChanges;
+        mPreserveWindows = preserveWindows;
+        mNotifyClients = notifyClients;
+    }
+
+    /**
+     * Ensure visibility with an option to also update the configuration of visible activities.
+     * @see ActivityStack#ensureActivitiesVisible(ActivityRecord, int, boolean)
+     * @see RootActivityContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
+     */
+    void process(ActivityRecord starting, int configChanges, boolean preserveWindows,
+            boolean notifyClients) {
+        reset(starting, configChanges, preserveWindows, notifyClients);
+
+        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + mTop
+                + " configChanges=0x" + Integer.toHexString(configChanges));
+        if (mTop != null) {
+            mContiner.checkTranslucentActivityWaiting(mTop);
+        }
+
+        // We should not resume activities that being launched behind because these
+        // activities are actually behind other fullscreen activities, but still required
+        // to be visible (such as performing Recents animation).
+        final boolean resumeTopActivity = mTop != null && !mTop.mLaunchTaskBehind
+                && mContiner.isFocusable() && mContiner.isInStackLocked(starting) == null;
+
+        final PooledConsumer f = PooledLambda.obtainConsumer(
+                EnsureActivitiesVisibleHelper::setActivityVisibilityState, this,
+                PooledLambda.__(ActivityRecord.class), resumeTopActivity);
+        mContiner.forAllActivities(f);
+        f.recycle();
+    }
+
+    private void setActivityVisibilityState(ActivityRecord r, final boolean resumeTopActivity) {
+        final boolean isTop = r == mTop;
+        if (mAboveTop && !isTop) {
+            return;
+        }
+        mAboveTop = false;
+
+        final boolean reallyVisible = r.shouldBeVisible(
+                mBehindFullscreenActivity, false /* ignoringKeyguard */);
+
+        // Check whether activity should be visible without Keyguard influence
+        if (r.visibleIgnoringKeyguard) {
+            if (r.occludesParent()) {
+                // At this point, nothing else needs to be shown in this task.
+                if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
+                        + " stackVisible=" + mContainerShouldBeVisible
+                        + " behindFullscreen=" + mBehindFullscreenActivity);
+                mBehindFullscreenActivity = true;
+            } else {
+                mBehindFullscreenActivity = false;
+            }
+        }
+
+        if (reallyVisible) {
+            if (r.finishing) {
+                return;
+            }
+            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make visible? " + r
+                    + " 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 != mStarting && mNotifyClients) {
+                r.ensureActivityConfiguration(0 /* globalChanges */, mPreserveWindows,
+                        true /* ignoreVisibility */);
+            }
+
+            if (!r.attachedToProcess()) {
+                makeVisibleAndRestartIfNeeded(mStarting, mConfigChanges, isTop,
+                        resumeTopActivity && isTop, r);
+            } else if (r.mVisibleRequested) {
+                // If this activity is already visible, then there is nothing to do here.
+                if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+                        "Skipping: already visible at " + r);
+
+                if (r.mClientVisibilityDeferred && mNotifyClients) {
+                    r.makeClientVisible();
+                }
+
+                r.handleAlreadyVisible();
+                if (mNotifyClients) {
+                    r.makeActiveIfNeeded(mStarting);
+                }
+            } else {
+                r.makeVisibleIfNeeded(mStarting, mNotifyClients);
+            }
+            // Aggregate current change flags.
+            mConfigChanges |= r.configChangeFlags;
+        } else {
+            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make invisible? " + r
+                    + " finishing=" + r.finishing + " state=" + r.getState()
+                    + " stackShouldBeVisible=" + mContainerShouldBeVisible
+                    + " behindFullscreenActivity=" + mBehindFullscreenActivity
+                    + " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
+            r.makeInvisible();
+        }
+
+        final int windowingMode = mContiner.getWindowingMode();
+        if (windowingMode == WINDOWING_MODE_FREEFORM) {
+            // The visibility of tasks and the activities they contain in freeform stack are
+            // determined individually unlike other stacks where the visibility or fullscreen
+            // status of an activity in a previous task affects other.
+            mBehindFullscreenActivity = !mContainerShouldBeVisible;
+        } else if (mContiner.isActivityTypeHome()) {
+            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + mContiner
+                    + " stackShouldBeVisible=" + mContainerShouldBeVisible
+                    + " behindFullscreenActivity=" + mBehindFullscreenActivity);
+            // No other task in the home stack should be visible behind the home activity.
+            // Home activities is usually a translucent activity with the wallpaper behind
+            // them. However, when they don't have the wallpaper behind them, we want to
+            // show activities in the next application stack behind them vs. another
+            // task in the home stack like recents.
+            mBehindFullscreenActivity = true;
+        }
+    }
+
+    private void makeVisibleAndRestartIfNeeded(ActivityRecord starting, int configChanges,
+            boolean isTop, boolean andResume, ActivityRecord r) {
+        // We need to make sure the app is running if it's the top, or it is just made visible from
+        // invisible. If the app is already visible, it must have died while it was visible. In this
+        // case, we'll show the dead window but will not restart the app. Otherwise we could end up
+        // thrashing.
+        if (!isTop && r.mVisibleRequested) {
+            return;
+        }
+
+        // This activity needs to be visible, but isn't even running...
+        // get it started and resume if no other stack in this stack is resumed.
+        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Start and freeze screen for " + r);
+        if (r != starting) {
+            r.startFreezingScreenLocked(configChanges);
+        }
+        if (!r.mVisibleRequested || r.mLaunchTaskBehind) {
+            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Starting and making visible: " + r);
+            r.setVisibility(true);
+        }
+        if (r != starting) {
+            mContiner.mStackSupervisor.startSpecificActivity(r, andResume, true /* checkConfig */);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index a17bd24..824a3c8 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -134,7 +134,7 @@
 
         if (!targetActivity.attachedToProcess()) {
             ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Real start recents");
-            mStackSupervisor.startSpecificActivityLocked(targetActivity, false /* andResume */,
+            mStackSupervisor.startSpecificActivity(targetActivity, false /* andResume */,
                     false /* checkConfig */);
             // Make sure the activity won't be involved in transition.
             if (targetActivity.getDisplayContent() != null) {
@@ -368,7 +368,7 @@
                         // transition (the target activity will be one of closing apps).
                         if (!controller.shouldDeferCancelWithScreenshot()
                                 && !targetStack.isFocusedStackOnDisplay()) {
-                            targetStack.ensureActivitiesVisibleLocked(null /* starting */,
+                            targetStack.ensureActivitiesVisible(null /* starting */,
                                     0 /* starting */, false /* preserveWindows */);
                         }
                         // Keep target stack in place, nothing changes, so ignore the transition
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index b71659e..ba54859 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1118,7 +1118,7 @@
             r.setState(ActivityStack.ActivityState.INITIALIZING, "test");
             // Ensure precondition that the activity is opaque.
             assertTrue(r.occludesParent());
-            mSupervisor.startSpecificActivityLocked(r, false /* andResume */,
+            mSupervisor.startSpecificActivity(r, false /* andResume */,
                     false /* checkConfig */);
         }
         mSupervisor.endDeferResume();
@@ -1145,12 +1145,12 @@
         new ActivityBuilder(mService).setTask(mTask).build();
         doReturn(false).when(nonTopVisibleActivity).attachedToProcess();
         doReturn(true).when(nonTopVisibleActivity).shouldBeVisible(anyBoolean(), anyBoolean());
-        doNothing().when(mSupervisor).startSpecificActivityLocked(any(), anyBoolean(),
+        doNothing().when(mSupervisor).startSpecificActivity(any(), anyBoolean(),
                 anyBoolean());
 
-        mStack.ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
+        mStack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
                 false /* preserveWindows */);
-        verify(mSupervisor).startSpecificActivityLocked(any(), eq(false) /* andResume */,
+        verify(mSupervisor).startSpecificActivity(any(), eq(false) /* andResume */,
                 anyBoolean());
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index d48b9d7..e67380c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -200,7 +200,7 @@
         startRecentsActivity();
 
         // Recents activity must be restarted, but not be resumed while running recents animation.
-        verify(mRootActivityContainer.mStackSupervisor).startSpecificActivityLocked(
+        verify(mRootActivityContainer.mStackSupervisor).startSpecificActivity(
                 eq(recentActivity), eq(false), anyBoolean());
         assertThat(recentActivity.getState()).isEqualTo(PAUSED);
     }