Merge "Moved ActivityDisplay to its own class file."
diff --git a/api/test-current.txt b/api/test-current.txt
index 2c6dcaa..90ecb8c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3866,6 +3866,8 @@
     method public deprecated void restartPackage(java.lang.String);
     method public static void setVrThread(int);
     method public void setWatchHeapLimit(long);
+    method public static boolean supportsMultiWindow(android.content.Context);
+    method public static boolean supportsSplitScreenMultiWindow(android.content.Context);
     field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT";
     field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1
     field public static final int LOCK_TASK_MODE_NONE = 0; // 0x0
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 78d05f5..5e2e333 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1152,6 +1152,7 @@
      * E.g. freeform, split-screen, picture-in-picture.
      * @hide
      */
+    @TestApi
     static public boolean supportsMultiWindow(Context context) {
         // On watches, multi-window is used to present essential system UI, and thus it must be
         // supported regardless of device memory characteristics.
@@ -1166,6 +1167,7 @@
      * Returns true if the system supports split screen multi-window.
      * @hide
      */
+    @TestApi
     static public boolean supportsSplitScreenMultiWindow(Context context) {
         return supportsMultiWindow(context)
                 && Resources.getSystem().getBoolean(
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
new file mode 100644
index 0000000..5da9fb2
--- /dev/null
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2017 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.am;
+
+import static android.view.Display.FLAG_PRIVATE;
+import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.proto.ActivityDisplayProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.proto.ActivityDisplayProto.STACKS;
+import static com.android.server.am.proto.ActivityDisplayProto.ID;
+
+import android.app.ActivityManagerInternal;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+import android.view.Display;
+import com.android.server.wm.ConfigurationContainer;
+
+import java.util.ArrayList;
+
+/**
+ * Exactly one of these classes per Display in the system. Capable of holding zero or more
+ * attached {@link ActivityStack}s.
+ */
+class ActivityDisplay extends ConfigurationContainer {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_AM;
+    private static final String TAG_STACK = TAG + POSTFIX_STACK;
+
+    static final int POSITION_TOP = Integer.MAX_VALUE;
+    static final int POSITION_BOTTOM = Integer.MIN_VALUE;
+
+    private ActivityStackSupervisor mSupervisor;
+    /** Actual Display this object tracks. */
+    int mDisplayId;
+    Display mDisplay;
+
+    /** All of the stacks on this display. Order matters, topmost stack is in front of all other
+     * stacks, bottommost behind. Accessed directly by ActivityManager package classes */
+    final ArrayList<ActivityStack> mStacks = new ArrayList<>();
+
+    /** Array of all UIDs that are present on the display. */
+    private IntArray mDisplayAccessUIDs = new IntArray();
+
+    /** All tokens used to put activities on this stack to sleep (including mOffToken) */
+    final ArrayList<ActivityManagerInternal.SleepToken> mAllSleepTokens = new ArrayList<>();
+    /** The token acquired by ActivityStackSupervisor to put stacks on the display to sleep */
+    ActivityManagerInternal.SleepToken mOffToken;
+
+    private boolean mSleeping;
+
+    ActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
+        mSupervisor = supervisor;
+        mDisplayId = displayId;
+        final Display display = supervisor.mDisplayManager.getDisplay(displayId);
+        if (display == null) {
+            throw new IllegalStateException("Display does not exist displayId=" + displayId);
+        }
+        mDisplay = display;
+    }
+
+    void addChild(ActivityStack stack, int position) {
+        if (position == POSITION_BOTTOM) {
+            position = 0;
+        } else if (position == POSITION_TOP) {
+            position = mStacks.size();
+        }
+        if (DEBUG_STACK) Slog.v(TAG_STACK, "addChild: attaching " + stack
+                + " to displayId=" + mDisplayId + " position=" + position);
+        positionChildAt(stack, position);
+        mSupervisor.mService.updateSleepIfNeededLocked();
+    }
+
+    void removeChild(ActivityStack stack) {
+        if (DEBUG_STACK) Slog.v(TAG_STACK, "removeChild: detaching " + stack
+                + " from displayId=" + mDisplayId);
+        mStacks.remove(stack);
+        mSupervisor.mService.updateSleepIfNeededLocked();
+    }
+
+    void positionChildAtTop(ActivityStack stack) {
+        positionChildAt(stack, mStacks.size());
+    }
+
+    void positionChildAtBottom(ActivityStack stack) {
+        positionChildAt(stack, 0);
+    }
+
+    private void positionChildAt(ActivityStack stack, int position) {
+        mStacks.remove(stack);
+        mStacks.add(getTopInsertPosition(stack, position), stack);
+    }
+
+    private int getTopInsertPosition(ActivityStack stack, int candidatePosition) {
+        int position = mStacks.size();
+        if (position > 0) {
+            final ActivityStack topStack = mStacks.get(position - 1);
+            if (topStack.getWindowConfiguration().isAlwaysOnTop() && topStack != stack) {
+                // If the top stack is always on top, we move this stack just below it.
+                position--;
+            }
+        }
+        return Math.min(position, candidatePosition);
+    }
+
+    <T extends ActivityStack> T getStack(int stackId) {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final ActivityStack stack = mStacks.get(i);
+            if (stack.mStackId == stackId) {
+                return (T) stack;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
+    }
+
+    @Override
+    protected int getChildCount() {
+        return mStacks.size();
+    }
+
+    @Override
+    protected ConfigurationContainer getChildAt(int index) {
+        return mStacks.get(index);
+    }
+
+    @Override
+    protected ConfigurationContainer getParent() {
+        return mSupervisor;
+    }
+
+    boolean isPrivate() {
+        return (mDisplay.getFlags() & FLAG_PRIVATE) != 0;
+    }
+
+    boolean isUidPresent(int uid) {
+        for (ActivityStack stack : mStacks) {
+            if (stack.isUidPresent(uid)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** Update and get all UIDs that are present on the display and have access to it. */
+    IntArray getPresentUIDs() {
+        mDisplayAccessUIDs.clear();
+        for (ActivityStack stack : mStacks) {
+            stack.getPresentUIDs(mDisplayAccessUIDs);
+        }
+        return mDisplayAccessUIDs;
+    }
+
+    boolean shouldDestroyContentOnRemove() {
+        return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
+    }
+
+    boolean shouldSleep() {
+        return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
+                && (mSupervisor.mService.mRunningVoice == null);
+    }
+
+    boolean isSleeping() {
+        return mSleeping;
+    }
+
+    void setIsSleeping(boolean asleep) {
+        mSleeping = asleep;
+    }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        super.writeToProto(proto, CONFIGURATION_CONTAINER);
+        proto.write(ID, mDisplayId);
+        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = mStacks.get(stackNdx);
+            stack.writeToProto(proto, STACKS);
+        }
+        proto.end(token);
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 12778d8..6df5fb7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -20060,7 +20060,10 @@
         synchronized (this) {
             final long origId = Binder.clearCallingIdentity();
             try {
-                mStackSupervisor.moveTasksToFullscreenStackLocked(fromStackId, onTop);
+                final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
+                if (stack != null){
+                    mStackSupervisor.moveTasksToFullscreenStackLocked(stack, onTop);
+                }
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 142c97b..2241ed6 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -2312,7 +2312,7 @@
         // be visible based on the stack, task, and lockscreen state and use that here instead. The
         // method should be based on the logic in ActivityStack.ensureActivitiesVisibleLocked().
         // Skip updating configuration for activity is a stack that shouldn't be visible.
-        if (stack.shouldBeVisible(null /* starting */) == STACK_INVISIBLE) {
+        if (!stack.shouldBeVisible(null /* starting */)) {
             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                     "Skipping config check invisible stack: " + this);
             return true;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8d21862..361d91a 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -29,6 +29,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
 import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
@@ -37,6 +38,8 @@
 import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
 
 import static android.view.Display.INVALID_DISPLAY;
+import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
+import static com.android.server.am.ActivityDisplay.POSITION_TOP;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_APP;
@@ -267,7 +270,6 @@
     final ActivityManagerService mService;
     private final WindowManagerService mWindowManager;
     T mWindowContainerController;
-    private final RecentTasks mRecentTasks;
 
     /**
      * The back history of all previous (and possibly still
@@ -350,9 +352,6 @@
     int mCurrentUser;
 
     final int mStackId;
-    /** The other stacks, in order, on the attached display. Updated at attach/detach time. */
-    // TODO: This list doesn't belong here...
-    ArrayList<ActivityStack> mStacks;
     /** The attached Display's unique identifier, or -1 if detached */
     int mDisplayId;
 
@@ -461,22 +460,20 @@
         return count;
     }
 
-    ActivityStack(ActivityStackSupervisor.ActivityDisplay display, int stackId,
-            ActivityStackSupervisor supervisor, RecentTasks recentTasks, boolean onTop) {
+    ActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
+            boolean onTop) {
         mStackSupervisor = supervisor;
         mService = supervisor.mService;
         mHandler = new ActivityStackHandler(mService.mHandler.getLooper());
         mWindowManager = mService.mWindowManager;
         mStackId = stackId;
         mCurrentUser = mService.mUserController.getCurrentUserIdLocked();
-        mRecentTasks = recentTasks;
         mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
                 ? new LaunchingTaskPositioner() : null;
         mTmpRect2.setEmpty();
         updateOverrideConfiguration();
         mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
                 mTmpRect2);
-        mStackSupervisor.mStacks.put(mStackId, this);
         postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
     }
 
@@ -503,7 +500,7 @@
     }
 
     /** Adds the stack to specified display and calls WindowManager to do the same. */
-    void reparent(ActivityStackSupervisor.ActivityDisplay activityDisplay, boolean onTop) {
+    void reparent(ActivityDisplay activityDisplay, boolean onTop) {
         removeFromDisplay();
         mTmpRect2.setEmpty();
         postAddToDisplay(activityDisplay, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
@@ -521,10 +518,9 @@
      * @param activityDisplay New display to which this stack was attached.
      * @param bounds Updated bounds.
      */
-    private void postAddToDisplay(ActivityStackSupervisor.ActivityDisplay activityDisplay,
+    private void postAddToDisplay(ActivityDisplay activityDisplay,
             Rect bounds, boolean onTop) {
         mDisplayId = activityDisplay.mDisplayId;
-        mStacks = activityDisplay.mStacks;
         mBounds = bounds != null ? new Rect(bounds) : null;
         mFullscreen = mBounds == null;
         if (mTaskPositioner != null) {
@@ -533,7 +529,7 @@
         }
         onParentChanged();
 
-        activityDisplay.attachStack(this, findStackInsertIndex(onTop));
+        activityDisplay.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
         if (mStackId == DOCKED_STACK_ID) {
             // If we created a docked stack we want to resize it so it resizes all other stacks
             // in the system.
@@ -547,33 +543,31 @@
      * either destroyed completely or re-parented.
      */
     private void removeFromDisplay() {
-        final ActivityStackSupervisor.ActivityDisplay display = getDisplay();
-        if (display != null) {
-            display.detachStack(this);
-        }
-        mDisplayId = INVALID_DISPLAY;
-        mStacks = null;
-        if (mTaskPositioner != null) {
-            mTaskPositioner.reset();
-        }
-        if (mStackId == DOCKED_STACK_ID) {
+        if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
             // If we removed a docked stack we want to resize it so it resizes all other stacks
             // in the system to fullscreen.
             mStackSupervisor.resizeDockedStackLocked(
                     null, null, null, null, null, PRESERVE_WINDOWS);
         }
+        final ActivityDisplay display = getDisplay();
+        if (display != null) {
+            display.removeChild(this);
+        }
+        mDisplayId = INVALID_DISPLAY;
+        if (mTaskPositioner != null) {
+            mTaskPositioner.reset();
+        }
     }
 
     /** Removes the stack completely. Also calls WindowManager to do the same on its side. */
     void remove() {
         removeFromDisplay();
-        mStackSupervisor.mStacks.remove(mStackId);
         mWindowContainerController.removeContainer();
         mWindowContainerController = null;
         onParentChanged();
     }
 
-    ActivityStackSupervisor.ActivityDisplay getDisplay() {
+    ActivityDisplay getDisplay() {
         return mStackSupervisor.getActivityDisplay(mDisplayId);
     }
 
@@ -858,7 +852,7 @@
     }
 
     final boolean isOnHomeDisplay() {
-        return isAttached() && mDisplayId == DEFAULT_DISPLAY;
+        return mDisplayId == DEFAULT_DISPLAY;
     }
 
     void moveToFront(String reason) {
@@ -874,8 +868,7 @@
             return;
         }
 
-        mStacks.remove(this);
-        mStacks.add(findStackInsertIndex(ON_TOP), this);
+        getDisplay().positionChildAtTop(this);
         mStackSupervisor.setFocusStackUnchecked(reason, this);
         if (task != null) {
             insertTaskAtTop(task, null);
@@ -889,45 +882,6 @@
         }
     }
 
-    /**
-     * @param task If non-null, the task will be moved to the back of the stack.
-     * */
-    private void moveToBack(TaskRecord task) {
-        if (!isAttached()) {
-            return;
-        }
-
-        mStacks.remove(this);
-        mStacks.add(0, this);
-
-        if (task != null) {
-            mTaskHistory.remove(task);
-            mTaskHistory.add(0, task);
-            updateTaskMovement(task, false);
-            mWindowContainerController.positionChildAtBottom(task.getWindowContainerController());
-        }
-    }
-
-    /**
-     * @return the index to insert a new stack into, taking the always-on-top stacks into account.
-     */
-    private int findStackInsertIndex(boolean onTop) {
-        if (onTop) {
-            int addIndex = mStacks.size();
-            if (addIndex > 0) {
-                final ActivityStack topStack = mStacks.get(addIndex - 1);
-                if (topStack.getWindowConfiguration().isAlwaysOnTop()
-                        && topStack != this) {
-                    // If the top stack is always on top, we move this stack just below it.
-                    addIndex--;
-                }
-            }
-            return addIndex;
-        } else {
-            return 0;
-        }
-    }
-
     boolean isFocusable() {
         if (getWindowConfiguration().canReceiveKeys()) {
             return true;
@@ -939,7 +893,7 @@
     }
 
     final boolean isAttached() {
-        return mStacks != null;
+        return getParent() != null;
     }
 
     /**
@@ -1114,13 +1068,6 @@
                 "Launch completed; removing icicle of " + r.icicle);
     }
 
-    void addRecentActivityLocked(ActivityRecord r) {
-        if (r != null) {
-            final TaskRecord task = r.getTask();
-            mRecentTasks.addLocked(task);
-            task.touchActiveTime();
-        }
-    }
 
     private void startLaunchTraces(String packageName) {
         if (mFullyDrawnStartTime != 0)  {
@@ -1568,54 +1515,6 @@
         }
     }
 
-    // Find the first visible activity above the passed activity and if it is translucent return it
-    // otherwise return null;
-    ActivityRecord findNextTranslucentActivity(ActivityRecord r) {
-        TaskRecord task = r.getTask();
-        if (task == null) {
-            return null;
-        }
-
-        final ActivityStack stack = task.getStack();
-        if (stack == null) {
-            return null;
-        }
-
-        int stackNdx = mStacks.indexOf(stack);
-
-        ArrayList<TaskRecord> tasks = stack.mTaskHistory;
-        int taskNdx = tasks.indexOf(task);
-
-        ArrayList<ActivityRecord> activities = task.mActivities;
-        int activityNdx = activities.indexOf(r) + 1;
-
-        final int numStacks = mStacks.size();
-        while (stackNdx < numStacks) {
-            final ActivityStack historyStack = mStacks.get(stackNdx);
-            tasks = historyStack.mTaskHistory;
-            final int numTasks = tasks.size();
-            while (taskNdx < numTasks) {
-                final TaskRecord currentTask = tasks.get(taskNdx);
-                activities = currentTask.mActivities;
-                final int numActivities = activities.size();
-                while (activityNdx < numActivities) {
-                    final ActivityRecord activity = activities.get(activityNdx);
-                    if (!activity.finishing) {
-                        return historyStack.mFullscreen
-                                && currentTask.mFullscreen && activity.fullscreen ? null : activity;
-                    }
-                    ++activityNdx;
-                }
-                activityNdx = 0;
-                ++taskNdx;
-            }
-            taskNdx = 0;
-            ++stackNdx;
-        }
-
-        return null;
-    }
-
     /** Returns true if the stack contains a fullscreen task. */
     private boolean hasFullscreenTask() {
         for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
@@ -1679,26 +1578,26 @@
     }
 
     /**
-     * Returns what the stack visibility should be: {@link #STACK_INVISIBLE} or
-     * {@link #STACK_VISIBLE}.
+     * Returns true if the stack should be visible.
      *
      * @param starting The currently starting activity or null if there is none.
      */
-    int shouldBeVisible(ActivityRecord starting) {
+    boolean shouldBeVisible(ActivityRecord starting) {
         if (!isAttached() || mForceHidden) {
-            return STACK_INVISIBLE;
+            return false;
         }
 
         if (mStackSupervisor.isFrontStackOnDisplay(this) || mStackSupervisor.isFocusedStack(this)) {
-            return STACK_VISIBLE;
+            return true;
         }
 
-        final int stackIndex = mStacks.indexOf(this);
+        final ArrayList<ActivityStack> displayStacks = getDisplay().mStacks;
+        final int stackIndex = displayStacks.indexOf(this);
 
-        if (stackIndex == mStacks.size() - 1) {
+        if (stackIndex == displayStacks.size() - 1) {
             Slog.wtf(TAG,
                     "Stack=" + this + " isn't front stack but is at the top of the stack list");
-            return STACK_INVISIBLE;
+            return false;
         }
 
         // Check position and visibility of this stack relative to the front stack on its display.
@@ -1709,43 +1608,43 @@
             // If the assistant stack is focused and translucent, then the docked stack is always
             // visible
             if (topStack.isActivityTypeAssistant()) {
-                return (topStack.isStackTranslucent(starting, DOCKED_STACK_ID)) ? STACK_VISIBLE
-                        : STACK_INVISIBLE;
+                return topStack.isStackTranslucent(starting, DOCKED_STACK_ID);
             }
-            return STACK_VISIBLE;
+            return true;
         }
 
         // Set home stack to invisible when it is below but not immediately below the docked stack
         // A case would be if recents stack exists but has no tasks and is below the docked stack
         // and home stack is below recents
         if (mStackId == HOME_STACK_ID) {
-            int dockedStackIndex = mStacks.indexOf(mStackSupervisor.getStack(DOCKED_STACK_ID));
+            int dockedStackIndex = displayStacks.indexOf
+                    (mStackSupervisor.getStack(DOCKED_STACK_ID));
             if (dockedStackIndex > stackIndex && stackIndex != dockedStackIndex - 1) {
-                return STACK_INVISIBLE;
+                return false;
             }
         }
 
         // Find the first stack behind front stack that actually got something visible.
-        int stackBehindTopIndex = mStacks.indexOf(topStack) - 1;
+        int stackBehindTopIndex = displayStacks.indexOf(topStack) - 1;
         while (stackBehindTopIndex >= 0 &&
-                mStacks.get(stackBehindTopIndex).topRunningActivityLocked() == null) {
+                displayStacks.get(stackBehindTopIndex).topRunningActivityLocked() == null) {
             stackBehindTopIndex--;
         }
         final int stackBehindTopId = (stackBehindTopIndex >= 0)
-                ? mStacks.get(stackBehindTopIndex).mStackId : INVALID_STACK_ID;
+                ? displayStacks.get(stackBehindTopIndex).mStackId : INVALID_STACK_ID;
         final boolean alwaysOnTop = topStack.getWindowConfiguration().isAlwaysOnTop();
         if (topStackId == DOCKED_STACK_ID || alwaysOnTop) {
             if (stackIndex == stackBehindTopIndex) {
                 // Stacks directly behind the docked or pinned stack are always visible.
-                return STACK_VISIBLE;
+                return true;
             } else if (alwaysOnTop && stackIndex == stackBehindTopIndex - 1) {
                 // Otherwise, this stack can also be visible if it is directly behind a docked stack
                 // or translucent assistant stack behind an always-on-top top-most stack
                 if (stackBehindTopId == DOCKED_STACK_ID) {
-                    return STACK_VISIBLE;
+                    return true;
                 } else if (stackBehindTopId == ASSISTANT_STACK_ID) {
-                    return mStacks.get(stackBehindTopIndex).isStackTranslucent(starting, mStackId)
-                            ? STACK_VISIBLE : STACK_INVISIBLE;
+                    return displayStacks.get(stackBehindTopIndex).isStackTranslucent(
+                            starting, mStackId);
                 }
             }
         }
@@ -1756,7 +1655,7 @@
             // always visible so they can act as a backdrop to the translucent activity.
             // For example, dialog activities
             if (stackIndex == stackBehindTopIndex) {
-                return STACK_VISIBLE;
+                return true;
             }
             if (stackBehindTopIndex >= 0) {
                 if ((stackBehindTopId == DOCKED_STACK_ID
@@ -1764,18 +1663,18 @@
                         && stackIndex == (stackBehindTopIndex - 1)) {
                     // The stack behind the docked or pinned stack is also visible so we can have a
                     // complete backdrop to the translucent activity when the docked stack is up.
-                    return STACK_VISIBLE;
+                    return true;
                 }
             }
         }
 
         if (StackId.isStaticStack(mStackId)) {
             // Visibility of any static stack should have been determined by the conditions above.
-            return STACK_INVISIBLE;
+            return false;
         }
 
-        for (int i = stackIndex + 1; i < mStacks.size(); i++) {
-            final ActivityStack stack = mStacks.get(i);
+        for (int i = stackIndex + 1; i < displayStacks.size(); i++) {
+            final ActivityStack stack = displayStacks.get(i);
 
             if (!stack.mFullscreen && !stack.hasFullscreenTask()) {
                 continue;
@@ -1783,15 +1682,15 @@
 
             if (!StackId.isDynamicStacksVisibleBehindAllowed(stack.mStackId)) {
                 // These stacks can't have any dynamic stacks visible behind them.
-                return STACK_INVISIBLE;
+                return false;
             }
 
             if (!stack.isStackTranslucent(starting, INVALID_STACK_ID)) {
-                return STACK_INVISIBLE;
+                return false;
             }
         }
 
-        return STACK_VISIBLE;
+        return true;
     }
 
     final int rankTaskLayers(int baseLayer) {
@@ -1828,12 +1727,10 @@
             // 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 int stackVisibility = shouldBeVisible(starting);
-            final boolean stackInvisible = stackVisibility != STACK_VISIBLE;
-            boolean behindFullscreenActivity = stackInvisible;
+            final boolean stackVisible = shouldBeVisible(starting);
+            boolean behindFullscreenActivity = !stackVisible;
             boolean resumeNextActivity = mStackSupervisor.isFocusedStack(this)
                     && (isInStackLocked(starting) == null);
-            boolean behindTranslucentActivity = false;
             for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
                 final TaskRecord task = mTaskHistory.get(taskNdx);
                 final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -1857,11 +1754,8 @@
                     final boolean reallyVisible = checkKeyguardVisibility(r,
                             visibleIgnoringKeyguard, isTop);
                     if (visibleIgnoringKeyguard) {
-                        behindFullscreenActivity = updateBehindFullscreen(stackInvisible,
+                        behindFullscreenActivity = updateBehindFullscreen(!stackVisible,
                                 behindFullscreenActivity, task, r);
-                        if (behindFullscreenActivity && !r.fullscreen) {
-                            behindTranslucentActivity = true;
-                        }
                     }
                     if (reallyVisible) {
                         if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make visible? " + r
@@ -1897,10 +1791,10 @@
                         configChanges |= r.configChangeFlags;
                     } else {
                         if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make invisible? " + r
-                                + " finishing=" + r.finishing + " state=" + r.state + " stackInvisible="
-                                + stackInvisible + " behindFullscreenActivity="
-                                + behindFullscreenActivity + " mLaunchTaskBehind="
-                                + r.mLaunchTaskBehind);
+                                + " finishing=" + r.finishing + " state=" + r.state
+                                + " stackVisible=" + stackVisible
+                                + " behindFullscreenActivity=" + behindFullscreenActivity
+                                + " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
                         makeInvisible(r);
                     }
                 }
@@ -1908,10 +1802,10 @@
                     // 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 = stackVisibility == STACK_INVISIBLE;
+                    behindFullscreenActivity = !stackVisible;
                 } else if (mStackId == HOME_STACK_ID) {
                     if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + task
-                            + " stackInvisible=" + stackInvisible
+                            + " stackVisible=" + stackVisible
                             + " 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
@@ -2012,7 +1906,7 @@
      * {@link Display#FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD} applied.
      */
     private boolean canShowWithInsecureKeyguard() {
-        final ActivityStackSupervisor.ActivityDisplay activityDisplay = getDisplay();
+        final ActivityDisplay activityDisplay = getDisplay();
         if (activityDisplay == null) {
             throw new IllegalStateException("Stack is not attached to any display, stackId="
                     + mStackId);
@@ -2192,7 +2086,7 @@
         // activities as we need to display their starting window until they are done initializing.
         boolean behindFullscreenActivity = false;
 
-        if (shouldBeVisible(null) == STACK_INVISIBLE) {
+        if (!shouldBeVisible(null)) {
             // The stack is not visible, so no activity in it should be displaying a starting
             // window. Mark all activities below top and behind fullscreen.
             aboveTop = false;
@@ -2268,9 +2162,7 @@
         mResumedActivity = r;
         r.state = ActivityState.RESUMED;
         mService.setResumedActivityUncheckLocked(r, reason);
-        final TaskRecord task = r.getTask();
-        task.touchActiveTime();
-        mRecentTasks.addLocked(task);
+        mStackSupervisor.addRecentActivity(r);
     }
 
     private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
@@ -4558,7 +4450,7 @@
         // Don't refocus if invisible to current user
         final ActivityRecord top = tr.getTopActivity();
         if (top == null || !top.okToShowLocked()) {
-            addRecentActivityLocked(top);
+            mStackSupervisor.addRecentActivity(top);
             ActivityOptions.abort(options);
             return;
         }
@@ -5162,8 +5054,7 @@
             if (task.autoRemoveFromRecents() || isVoiceSession) {
                 // Task creator asked to remove this when done, or this task was a voice
                 // interaction, so it should not remain on the recent tasks list.
-                mRecentTasks.remove(task);
-                task.removedFromRecents();
+                mStackSupervisor.removeTaskFromRecents(task);
             }
 
             task.removeWindowContainer();
@@ -5180,9 +5071,8 @@
                     mStackSupervisor.moveHomeStackToFront(myReason);
                 }
             }
-            if (mStacks != null) {
-                mStacks.remove(this);
-                mStacks.add(0, this);
+            if (isAttached()) {
+                getDisplay().positionChildAtBottom(this);
             }
             if (!isHomeOrRecentsStack()) {
                 remove();
@@ -5359,7 +5249,7 @@
     }
 
     boolean shouldSleepActivities() {
-        final ActivityStackSupervisor.ActivityDisplay display = getDisplay();
+        final ActivityDisplay display = getDisplay();
         return display != null ? display.isSleeping() : mService.isSleepingLocked();
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 45c4df9..bf0c3a4 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -52,9 +52,7 @@
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.FLAG_PRIVATE;
 import static android.view.Display.INVALID_DISPLAY;
-import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
 import static android.view.Display.TYPE_VIRTUAL;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
@@ -393,15 +391,11 @@
      * They are used by components that may hide and block interaction with underlying
      * activities.
      */
-    final ArrayList<SleepToken> mSleepTokens = new ArrayList<SleepToken>();
+    final ArrayList<SleepToken> mSleepTokens = new ArrayList<>();
 
     /** Stack id of the front stack when user switched, indexed by userId. */
     SparseIntArray mUserStackInFront = new SparseIntArray(2);
 
-    // TODO: Add listener for removal of references.
-    /** Mapping from (ActivityStack/TaskStack).mStackId to their current state */
-    SparseArray<ActivityStack> mStacks = new SparseArray<>();
-
     // TODO: There should be an ActivityDisplayController coordinating am/wm interaction.
     /** Mapping from displayId to display current state */
     private final SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<>();
@@ -620,10 +614,7 @@
             Display[] displays = mDisplayManager.getDisplays();
             for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
                 final int displayId = displays[displayNdx].getDisplayId();
-                ActivityDisplay activityDisplay = new ActivityDisplay(displayId);
-                if (activityDisplay.mDisplay == null) {
-                    throw new IllegalStateException("Default Display does not exist");
-                }
+                ActivityDisplay activityDisplay = new ActivityDisplay(this, displayId);
                 mActivityDisplays.put(displayId, activityDisplay);
                 calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
             }
@@ -2160,12 +2151,18 @@
     }
 
     protected <T extends ActivityStack> T getStack(int stackId) {
-        return getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP);
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final T stack = mActivityDisplays.valueAt(i).getStack(stackId);
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return null;
     }
 
     protected <T extends ActivityStack> T getStack(int stackId, boolean createStaticStackIfNeeded,
             boolean createOnTop) {
-        final ActivityStack stack = mStacks.get(stackId);
+        final ActivityStack stack = getStack(stackId);
         if (stack != null) {
             return (T) stack;
         }
@@ -2443,8 +2440,7 @@
             final List<ActivityStack> stacks = getActivityDisplayOrCreateLocked(displayId).mStacks;
             for (int j = stacks.size() - 1; j >= 0; --j) {
                 final ActivityStack stack = stacks.get(j);
-                if (stack != currentFocus && stack.isFocusable()
-                        && stack.shouldBeVisible(null) != STACK_INVISIBLE) {
+                if (stack != currentFocus && stack.isFocusable() && stack.shouldBeVisible(null)) {
                     return stack;
                 }
             }
@@ -2571,16 +2567,12 @@
         mResizingTasksDuringAnimation.clear();
     }
 
-    private void moveTasksToFullscreenStackInSurfaceTransaction(int fromStackId,
+    private void moveTasksToFullscreenStackInSurfaceTransaction(ActivityStack fromStack,
             boolean onTop) {
 
-        final ActivityStack stack = getStack(fromStackId);
-        if (stack == null) {
-            return;
-        }
-
         mWindowManager.deferSurfaceLayout();
         try {
+            final int fromStackId = fromStack.mStackId;
             if (fromStackId == DOCKED_STACK_ID) {
                 // We are moving all tasks from the docked stack to the fullscreen stack,
                 // which is dismissing the docked stack, so resize all other stacks to
@@ -2610,12 +2602,12 @@
                 }
             }
             ActivityStack fullscreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
-            final boolean isFullscreenStackVisible = fullscreenStack != null &&
-                    fullscreenStack.shouldBeVisible(null) == STACK_VISIBLE;
+            final boolean isFullscreenStackVisible = fullscreenStack != null
+                    && fullscreenStack.shouldBeVisible(null);
             // If we are moving from the pinned stack, then the animation takes care of updating
             // the picture-in-picture mode.
             final boolean schedulePictureInPictureModeChange = (fromStackId == PINNED_STACK_ID);
-            final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+            final ArrayList<TaskRecord> tasks = fromStack.getAllTasks();
             final int size = tasks.size();
             if (onTop) {
                 for (int i = 0; i < size; i++) {
@@ -2655,9 +2647,9 @@
         }
     }
 
-    void moveTasksToFullscreenStackLocked(int fromStackId, boolean onTop) {
+    void moveTasksToFullscreenStackLocked(ActivityStack fromStack, boolean onTop) {
         mWindowManager.inSurfaceTransaction(
-                () -> moveTasksToFullscreenStackInSurfaceTransaction(fromStackId, onTop));
+                () -> moveTasksToFullscreenStackInSurfaceTransaction(fromStack, onTop));
     }
 
     void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
@@ -2697,7 +2689,7 @@
                 // The dock stack either was dismissed or went fullscreen, which is kinda the same.
                 // In this case we make all other static stacks fullscreen and move all
                 // docked stack tasks to the fullscreen stack.
-                moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, ON_TOP);
+                moveTasksToFullscreenStackLocked(stack, ON_TOP);
 
                 // stack shouldn't contain anymore activities, so nothing to resume.
                 r = null;
@@ -2789,9 +2781,9 @@
     ActivityStack createStack(int stackId, ActivityDisplay display, boolean onTop) {
         switch (stackId) {
             case PINNED_STACK_ID:
-                return new PinnedActivityStack(display, stackId, this, mRecentTasks, onTop);
+                return new PinnedActivityStack(display, stackId, this, onTop);
             default:
-                return new ActivityStack(display, stackId, this, mRecentTasks, onTop);
+                return new ActivityStack(display, stackId, this, onTop);
         }
     }
 
@@ -2820,7 +2812,7 @@
                     true /* processPausingActivites */, null /* configuration */);
 
             // Move all the tasks to the bottom of the fullscreen stack
-            moveTasksToFullscreenStackLocked(PINNED_STACK_ID, !ON_TOP);
+            moveTasksToFullscreenStackLocked(pinnedStack, !ON_TOP);
         } else {
             for (int i = tasks.size() - 1; i >= 0; i--) {
                 removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
@@ -2871,10 +2863,23 @@
         return false;
     }
 
+    void addRecentActivity(ActivityRecord r) {
+        if (r == null) {
+            return;
+        }
+        final TaskRecord task = r.getTask();
+        mRecentTasks.addLocked(task);
+        task.touchActiveTime();
+    }
+
+    void removeTaskFromRecents(TaskRecord task) {
+        mRecentTasks.remove(task);
+        task.removedFromRecents();
+    }
+
     void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess, boolean removeFromRecents) {
         if (removeFromRecents) {
-            mRecentTasks.remove(tr);
-            tr.removedFromRecents();
+            removeTaskFromRecents(tr);
         }
         ComponentName component = tr.getBaseIntent().getComponent();
         if (component == null) {
@@ -3000,7 +3005,7 @@
             throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown displayId="
                     + displayId);
         }
-        final ActivityStack stack = mStacks.get(stackId);
+        final ActivityStack stack = getStack(stackId);
         if (stack == null) {
             throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown stackId="
                     + stackId);
@@ -3109,12 +3114,16 @@
 
         mWindowManager.deferSurfaceLayout();
 
+        PinnedActivityStack stack = getStack(PINNED_STACK_ID);
+
         // This will clear the pinned stack by moving an existing task to the full screen stack,
         // ensuring only one task is present.
-        moveTasksToFullscreenStackLocked(PINNED_STACK_ID, !ON_TOP);
+        if (stack != null) {
+            moveTasksToFullscreenStackLocked(stack, !ON_TOP);
+        }
 
         // Need to make sure the pinned stack exist so we can resize it below...
-        final PinnedActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+        stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
 
         try {
             final TaskRecord task = r.getTask();
@@ -3608,7 +3617,10 @@
     boolean switchUserLocked(int userId, UserState uss) {
         final int focusStackId = mFocusedStack.getStackId();
         // We dismiss the docked stack whenever we switch users.
-        moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, focusStackId == DOCKED_STACK_ID);
+        final ActivityStack dockedStack = getStack(DOCKED_STACK_ID);
+        if (dockedStack != null) {
+            moveTasksToFullscreenStackLocked(dockedStack, mFocusedStack == dockedStack);
+        }
         // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
         // also cause all tasks to be moved to the fullscreen stack at a position that is
         // appropriate.
@@ -3755,7 +3767,10 @@
         pw.print(prefix);
         pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
         pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
-        pw.print(prefix); pw.println("mStacks=" + mStacks);
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.valueAt(i);
+            pw.println(prefix + "displayId=" + display.mDisplayId + " mStacks=" + display.mStacks);
+        }
         if (!mWaitingForActivityVisible.isEmpty()) {
             pw.print(prefix); pw.println("mWaitingForActivityVisible=");
             for (int i = 0; i < mWaitingForActivityVisible.size(); ++i) {
@@ -3816,8 +3831,7 @@
                 ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
                 for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                     ActivityStack stack = stacks.get(stackNdx);
-                    if (!dumpVisibleStacksOnly ||
-                            stack.shouldBeVisible(null) == STACK_VISIBLE) {
+                    if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
                         activities.addAll(stack.getDumpActivitiesLocked(name));
                     }
                 }
@@ -4066,17 +4080,18 @@
             return null;
         }
         // The display hasn't been added to ActivityManager yet, create a new record now.
-        activityDisplay = new ActivityDisplay(displayId);
-        if (activityDisplay.mDisplay == null) {
-            Slog.w(TAG, "Display " + displayId + " gone before initialization complete");
-            return null;
-        }
-        mActivityDisplays.put(displayId, activityDisplay);
+        activityDisplay = new ActivityDisplay(this, displayId);
+        attachDisplay(activityDisplay);
         calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
         mWindowManager.onDisplayAdded(displayId);
         return activityDisplay;
     }
 
+    @VisibleForTesting
+    void attachDisplay(ActivityDisplay display) {
+        mActivityDisplays.put(display.mDisplayId, display);
+    }
+
     private void calculateDefaultMinimalSizeOfResizeableTasks(ActivityDisplay display) {
         mDefaultMinSizeOfResizeableTask =
                 mService.mContext.getResources().getDimensionPixelSize(
@@ -4104,7 +4119,7 @@
                         // Moving all tasks to fullscreen stack, because it's guaranteed to be
                         // a valid launch stack for all activities. This way the task history from
                         // external display will be preserved on primary after move.
-                        moveTasksToFullscreenStackLocked(stack.getStackId(), true /* onTop */);
+                        moveTasksToFullscreenStackLocked(stack, true /* onTop */);
                     }
                 }
 
@@ -4166,7 +4181,7 @@
         if (display.mAllSleepTokens.isEmpty()) {
             return;
         }
-        for (SleepTokenImpl token : display.mAllSleepTokens) {
+        for (SleepToken token : display.mAllSleepTokens) {
             mSleepTokens.remove(token);
         }
         display.mAllSleepTokens.clear();
@@ -4182,7 +4197,7 @@
         info.displayId = displayId;
         info.stackId = stack.mStackId;
         info.userId = stack.mCurrentUser;
-        info.visible = stack.shouldBeVisible(null) == STACK_VISIBLE;
+        info.visible = stack.shouldBeVisible(null);
         // A stack might be not attached to a display.
         info.position = display != null
                 ? display.mStacks.indexOf(stack)
@@ -4289,7 +4304,11 @@
 
             // Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
             // we need to move it to top of fullscreen stack, otherwise it will be covered.
-            moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, actualStackId == DOCKED_STACK_ID);
+            final ActivityStack dockedStack = getStack(DOCKED_STACK_ID);
+            if (dockedStack != null) {
+                moveTasksToFullscreenStackLocked(dockedStack,
+                        actualStackId == dockedStack.getStackId());
+            }
         } else if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
                 && !topActivity.noDisplay) {
             final String packageName = topActivity.appInfo.packageName;
@@ -4494,133 +4513,6 @@
         }
     }
 
-    // TODO: Move to its own file.
-    /** Exactly one of these classes per Display in the system. Capable of holding zero or more
-     * attached {@link ActivityStack}s */
-    class ActivityDisplay extends ConfigurationContainer {
-        /** Actual Display this object tracks. */
-        int mDisplayId;
-        Display mDisplay;
-
-        /** All of the stacks on this display. Order matters, topmost stack is in front of all other
-         * stacks, bottommost behind. Accessed directly by ActivityManager package classes */
-        final ArrayList<ActivityStack> mStacks = new ArrayList<>();
-
-        /** Array of all UIDs that are present on the display. */
-        private IntArray mDisplayAccessUIDs = new IntArray();
-
-        /** All tokens used to put activities on this stack to sleep (including mOffToken) */
-        final ArrayList<SleepTokenImpl> mAllSleepTokens = new ArrayList<>();
-        /** The token acquired by ActivityStackSupervisor to put stacks on the display to sleep */
-        SleepToken mOffToken;
-
-        private boolean mSleeping;
-
-        @VisibleForTesting
-        ActivityDisplay() {
-            mActivityDisplays.put(mDisplayId, this);
-        }
-
-        // After instantiation, check that mDisplay is not null before using this. The alternative
-        // is for this to throw an exception if mDisplayManager.getDisplay() returns null.
-        ActivityDisplay(int displayId) {
-            final Display display = mDisplayManager.getDisplay(displayId);
-            if (display == null) {
-                return;
-            }
-            init(display);
-        }
-
-        void init(Display display) {
-            mDisplay = display;
-            mDisplayId = display.getDisplayId();
-        }
-
-        void attachStack(ActivityStack stack, int position) {
-            if (DEBUG_STACK) Slog.v(TAG_STACK, "attachStack: attaching " + stack
-                    + " to displayId=" + mDisplayId + " position=" + position);
-            mStacks.add(position, stack);
-            mService.updateSleepIfNeededLocked();
-        }
-
-        void detachStack(ActivityStack stack) {
-            if (DEBUG_STACK) Slog.v(TAG_STACK, "detachStack: detaching " + stack
-                    + " from displayId=" + mDisplayId);
-            mStacks.remove(stack);
-            mService.updateSleepIfNeededLocked();
-        }
-
-        @Override
-        public String toString() {
-            return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
-        }
-
-        @Override
-        protected int getChildCount() {
-            return mStacks.size();
-        }
-
-        @Override
-        protected ConfigurationContainer getChildAt(int index) {
-            return mStacks.get(index);
-        }
-
-        @Override
-        protected ConfigurationContainer getParent() {
-            return ActivityStackSupervisor.this;
-        }
-
-        boolean isPrivate() {
-            return (mDisplay.getFlags() & FLAG_PRIVATE) != 0;
-        }
-
-        boolean isUidPresent(int uid) {
-            for (ActivityStack stack : mStacks) {
-                if (stack.isUidPresent(uid)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /** Update and get all UIDs that are present on the display and have access to it. */
-        private IntArray getPresentUIDs() {
-            mDisplayAccessUIDs.clear();
-            for (ActivityStack stack : mStacks) {
-                stack.getPresentUIDs(mDisplayAccessUIDs);
-            }
-            return mDisplayAccessUIDs;
-        }
-
-        boolean shouldDestroyContentOnRemove() {
-            return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
-        }
-
-        boolean shouldSleep() {
-            return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
-                    && (mService.mRunningVoice == null);
-        }
-
-        boolean isSleeping() {
-            return mSleeping;
-        }
-
-        void setIsSleeping(boolean asleep) {
-            mSleeping = asleep;
-        }
-
-        public void writeToProto(ProtoOutputStream proto, long fieldId) {
-            final long token = proto.start(fieldId);
-            super.writeToProto(proto, ActivityDisplayProto.CONFIGURATION_CONTAINER);
-            proto.write(ID, mDisplayId);
-            for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = mStacks.get(stackNdx);
-                stack.writeToProto(proto, STACKS);
-            }
-            proto.end(token);
-        }
-    }
-
     ActivityStack findStackBehind(ActivityStack stack) {
         // TODO(multi-display): We are only looking for stacks on the default display.
         final ActivityDisplay display = mActivityDisplays.get(DEFAULT_DISPLAY);
@@ -4756,8 +4648,7 @@
             for (int j = display.mStacks.size() - 1; j >= 0; j--) {
                 final ActivityStack stack = display.mStacks.get(j);
                 // Get top activity from a visible stack and add it to the list.
-                if (stack.shouldBeVisible(null /* starting */)
-                        == ActivityStack.STACK_VISIBLE) {
+                if (stack.shouldBeVisible(null /* starting */)) {
                     final ActivityRecord top = stack.topActivity();
                     if (top != null) {
                         if (stack == mFocusedStack) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index fab4d0d..1a2b46c 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -39,7 +39,6 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
@@ -78,7 +77,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityManagerService.ANIMATE;
 import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
 import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
 import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
@@ -1241,7 +1239,7 @@
                         mOptions);
             }
         } else {
-            mTargetStack.addRecentActivityLocked(mStartActivity);
+            mSupervisor.addRecentActivity(mStartActivity);
         }
         mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
 
@@ -2107,7 +2105,8 @@
         }
         if (stack == null) {
             // We first try to put the task in the first dynamic stack on home display.
-            final ArrayList<ActivityStack> homeDisplayStacks = mSupervisor.mHomeStack.mStacks;
+            final ArrayList<ActivityStack> homeDisplayStacks =
+                    mSupervisor.getStacksOnDefaultDisplay();
             for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
                 stack = homeDisplayStacks.get(stackNdx);
                 if (isDynamicStack(stack.mStackId)) {
@@ -2245,8 +2244,7 @@
                 // and if yes, we will launch into that stack. If not, we just put the new
                 // activity into parent's stack, because we can't find a better place.
                 final ActivityStack dockedStack = mSupervisor.getStack(DOCKED_STACK_ID);
-                if (dockedStack != null
-                        && dockedStack.shouldBeVisible(r) == STACK_INVISIBLE) {
+                if (dockedStack != null && !dockedStack.shouldBeVisible(r)) {
                     // There is a docked stack, but it isn't visible, so we can't launch into that.
                     return null;
                 } else {
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index e03c530..7101fc4 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -342,8 +342,12 @@
             // show on top of the lock screen. In this can we want to dismiss the docked
             // stack since it will be complicated/risky to try to put the activity on top
             // of the lock screen in the right fullscreen configuration.
-            mStackSupervisor.moveTasksToFullscreenStackLocked(DOCKED_STACK_ID,
-                    mStackSupervisor.mFocusedStack.getStackId() == DOCKED_STACK_ID);
+            final ActivityStack stack = mStackSupervisor.getStack(DOCKED_STACK_ID);
+            if (stack == null) {
+                return;
+            }
+            mStackSupervisor.moveTasksToFullscreenStackLocked(stack,
+                    mStackSupervisor.mFocusedStack == stack);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
index a601ee1..33f5664 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -32,9 +32,9 @@
 class PinnedActivityStack extends ActivityStack<PinnedStackWindowController>
         implements PinnedStackWindowListener {
 
-    PinnedActivityStack(ActivityStackSupervisor.ActivityDisplay display, int stackId,
-            ActivityStackSupervisor supervisor, RecentTasks recentTasks, boolean onTop) {
-        super(display, stackId, supervisor, recentTasks, onTop);
+    PinnedActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
+            boolean onTop) {
+        super(display, stackId, supervisor, onTop);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 1da94da..b7b1f7c 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -225,9 +225,15 @@
         /*@WindowConfiguration.ActivityType*/ int thisType = getActivityType();
         /*@WindowConfiguration.ActivityType*/ int otherType = other.getActivityType();
 
-        return thisType == otherType
-                || thisType == ACTIVITY_TYPE_UNDEFINED
-                || otherType == ACTIVITY_TYPE_UNDEFINED;
+        if (thisType == otherType) {
+            return true;
+        }
+        if (thisType == ACTIVITY_TYPE_ASSISTANT) {
+            // Assistant activities are only compatible with themselves...
+            return false;
+        }
+        // Otherwise we are compatible if us or other is not currently defined.
+        return thisType == ACTIVITY_TYPE_UNDEFINED || otherType == ACTIVITY_TYPE_UNDEFINED;
     }
 
     public void registerConfigurationChangeListener(ConfigurationContainerListener listener) {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 0cf1df8..6caa60b 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.any;
@@ -24,7 +25,6 @@
 
 import org.mockito.invocation.InvocationOnMock;
 
-import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -32,6 +32,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.support.test.InstrumentationRegistry;
@@ -167,8 +168,11 @@
 
         public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) {
             super(service, looper);
+            mDisplayManager =
+                    (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
             mWindowManager = prepareMockWindowManager();
-            mDisplay = new ActivityDisplay();
+            mDisplay = new ActivityDisplay(this, DEFAULT_DISPLAY);
+            attachDisplay(mDisplay);
         }
 
         // TODO: Use Mockito spy instead. Currently not possible due to TestActivityStackSupervisor
@@ -220,17 +224,15 @@
 
         @Override
         ActivityStack createStack(int stackId, ActivityDisplay display, boolean onTop) {
-            final RecentTasks recents =
-                    new RecentTasks(mService, mService.mStackSupervisor);
             if (stackId == PINNED_STACK_ID) {
-                return new PinnedActivityStack(display, stackId, this, recents, onTop) {
+                return new PinnedActivityStack(display, stackId, this, onTop) {
                     @Override
                     Rect getDefaultPictureInPictureBounds(float aspectRatio) {
                         return new Rect(50, 50, 100, 100);
                     }
                 };
             } else {
-                return new TestActivityStack(display, stackId, this, recents, onTop);
+                return new TestActivityStack(display, stackId, this, onTop);
             }
         }
 
@@ -280,9 +282,9 @@
             extends ActivityStack<T> implements ActivityStackReporter {
         private int mOnActivityRemovedFromStackCount = 0;
         private T mContainerController;
-        TestActivityStack(ActivityStackSupervisor.ActivityDisplay display, int stackId,
-                ActivityStackSupervisor supervisor, RecentTasks recentTasks, boolean onTop) {
-            super(display, stackId, supervisor, recentTasks, onTop);
+        TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
+                boolean onTop) {
+            super(display, stackId, supervisor, onTop);
         }
 
         @Override