Pair ActivityStacks with Displays

- Introduce concept of ActivityStacks residing on Displays and able
to be decoupled and moved around.
- Add a new interface, IActivityContainer for clients to handle
ActivityStacks.
- Abandon ordering of stacks based on mStackState and instead use
ActivityDisplayInfo.stacks<ActivityStack> ordering.

Progress towards closing bug 12078972.

Change-Id: I7785b61c26dc17f432a4803eebee07c7415fcc1f
diff --git a/Android.mk b/Android.mk
index 3a3169c..ade5e38 100644
--- a/Android.mk
+++ b/Android.mk
@@ -59,6 +59,8 @@
 	core/java/android/accounts/IAccountManagerResponse.aidl \
 	core/java/android/accounts/IAccountAuthenticator.aidl \
 	core/java/android/accounts/IAccountAuthenticatorResponse.aidl \
+	core/java/android/app/IActivityContainer.aidl \
+	core/java/android/app/IActivityContainerCallback.aidl \
 	core/java/android/app/IActivityController.aidl \
 	core/java/android/app/IActivityPendingResult.aidl \
 	core/java/android/app/IAlarmManager.aidl \
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index f3432a0..7adf5ec 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -21,6 +21,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityManagerNative;
+import android.app.IActivityContainer;
 import android.app.IActivityController;
 import android.app.IActivityManager;
 import android.app.IInstrumentationWatcher;
@@ -35,6 +36,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -107,7 +109,7 @@
                 "       am to-intent-uri [INTENT]\n" +
                 "       am switch-user <USER_ID>\n" +
                 "       am stop-user <USER_ID>\n" +
-                "       am stack create <TASK_ID>\n" +
+                "       am stack create <TASK_ID> <DISPLAY_ID>\n" +
                 "       am stack movetask <TASK_ID> <STACK_ID> [true|false]\n" +
                 "       am stack resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
                 "       am stack list\n" +
@@ -1558,10 +1560,16 @@
     private void runStackCreate() throws Exception {
         String taskIdStr = nextArgRequired();
         int taskId = Integer.valueOf(taskIdStr);
+        String displayIdStr = nextArgRequired();
+        int displayId = Integer.valueOf(displayIdStr);
 
         try {
-            int stackId = mAm.createStack(taskId);
-            System.out.println("createStack returned new stackId=" + stackId + "\n\n");
+            IBinder homeActivityToken = mAm.getHomeActivityToken();
+            IActivityContainer container = mAm.createActivityContainer(homeActivityToken, null);
+            final int stackId = container.getStackId();
+            System.out.println("createStack returned new stackId=" + stackId + "\n");
+            container.attachToDisplay(displayId);
+            mAm.moveTaskToStack(taskId, stackId, true);
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index f0c4c93..0f38095 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -612,15 +612,6 @@
             return true;
         }
 
-        case CREATE_STACK_TRANSACTION: {
-            data.enforceInterface(IActivityManager.descriptor);
-            int taskId = data.readInt();
-            int res = createStack(taskId);
-            reply.writeNoException();
-            reply.writeInt(res);
-            return true;
-        }
-
         case MOVE_TASK_TO_STACK_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             int taskId = data.readInt();
@@ -2027,6 +2018,26 @@
             reply.writeNoException();
             return true;
         }
+
+        case CREATE_ACTIVITY_CONTAINER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder parentActivityToken = data.readStrongBinder();
+            IActivityContainerCallback callback =
+                    (IActivityContainerCallback) data.readStrongBinder();
+            IActivityContainer activityContainer =
+                    createActivityContainer(parentActivityToken, callback);
+            reply.writeNoException();
+            reply.writeStrongBinder(activityContainer.asBinder());
+            return true;
+        }
+
+        case GET_HOME_ACTIVITY_TOKEN_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder homeActivityToken = getHomeActivityToken();
+            reply.writeNoException();
+            reply.writeStrongBinder(homeActivityToken);
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -2714,20 +2725,6 @@
         reply.recycle();
     }
     @Override
-    public int createStack(int taskId) throws RemoteException
-    {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeInterfaceToken(IActivityManager.descriptor);
-        data.writeInt(taskId);
-        mRemote.transact(CREATE_STACK_TRANSACTION, data, reply, 0);
-        reply.readException();
-        int res = reply.readInt();
-        data.recycle();
-        reply.recycle();
-        return res;
-    }
-    @Override
     public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException
     {
         Parcel data = Parcel.obtain();
@@ -4655,5 +4652,33 @@
         reply.recycle();
     }
 
+    public IActivityContainer createActivityContainer(IBinder parentActivityToken,
+            IActivityContainerCallback callback) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(parentActivityToken);
+        data.writeStrongBinder((IBinder)callback);
+        mRemote.transact(CREATE_ACTIVITY_CONTAINER_TRANSACTION, data, reply, 0);
+        reply.readException();
+        IActivityContainer res =
+                IActivityContainer.Stub.asInterface(reply.readStrongBinder());
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
+
+    public IBinder getHomeActivityToken() throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        mRemote.transact(GET_HOME_ACTIVITY_TOKEN_TRANSACTION, data, reply, 0);
+        reply.readException();
+        IBinder res = reply.readStrongBinder();
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/IActivityContainer.aidl b/core/java/android/app/IActivityContainer.aidl
new file mode 100644
index 0000000..2883d25
--- /dev/null
+++ b/core/java/android/app/IActivityContainer.aidl
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2013, 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 android.app;
+
+import android.app.IActivityContainerCallback;
+import android.content.Intent;
+import android.os.IBinder;
+
+/** @hide */
+interface IActivityContainer {
+    void attachToDisplay(int displayId);
+    int getStackId();
+    void detachFromDisplay();
+    void startActivity(in Intent intent);
+}
diff --git a/core/java/android/app/IActivityContainerCallback.aidl b/core/java/android/app/IActivityContainerCallback.aidl
new file mode 100644
index 0000000..55c2001
--- /dev/null
+++ b/core/java/android/app/IActivityContainerCallback.aidl
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2013, 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 android.app;
+
+import android.os.IBinder;
+
+/** @hide */
+interface IActivityContainerCallback {
+    oneway void onLastActivityRemoved(IBinder container);
+}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 631d9b1..4be1945 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -118,7 +118,6 @@
     public void moveTaskToBack(int task) throws RemoteException;
     public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException;
     public void moveTaskBackwards(int task) throws RemoteException;
-    public int createStack(int taskId) throws RemoteException;
     public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException;
     public void resizeStack(int stackId, Rect bounds) throws RemoteException;
     public List<StackInfo> getAllStackInfos() throws RemoteException;
@@ -408,6 +407,11 @@
 
     public void performIdleMaintenance() throws RemoteException;
 
+    public IActivityContainer createActivityContainer(IBinder parentActivityToken,
+            IActivityContainerCallback callback) throws RemoteException;
+
+    public IBinder getHomeActivityToken() throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -678,7 +682,7 @@
     int KILL_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+164;
     int SET_USER_IS_MONKEY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+165;
     int HANG_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+166;
-    int CREATE_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+167;
+    int CREATE_ACTIVITY_CONTAINER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+167;
     int MOVE_TASK_TO_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+168;
     int RESIZE_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+169;
     int GET_ALL_STACK_INFOS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+170;
@@ -694,4 +698,5 @@
     int RELEASE_PERSISTABLE_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+180;
     int GET_PERSISTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+181;
     int APP_NOT_RESPONDING_VIA_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+182;
+    int GET_HOME_ACTIVITY_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+183;
 }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 05666b4..4540c1c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -28,13 +28,15 @@
 import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
 
 import android.app.AppOpsManager;
+import android.app.IActivityContainer;
+import android.app.IActivityContainerCallback;
 import android.appwidget.AppWidgetManager;
 import android.graphics.Rect;
 import android.util.ArrayMap;
-import android.view.Display;
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.ProcessMap;
 import com.android.internal.app.ProcessStats;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BatteryStatsImpl;
@@ -47,7 +49,6 @@
 import com.android.server.AppOpsService;
 import com.android.server.AttributeCache;
 import com.android.server.IntentResolver;
-import com.android.internal.app.ProcessMap;
 import com.android.server.SystemServer;
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityStack.ActivityState;
@@ -1770,7 +1771,6 @@
     public void setWindowManager(WindowManagerService wm) {
         mWindowManager = wm;
         mStackSupervisor.setWindowManager(wm);
-        wm.createStack(HOME_STACK_ID, Display.DEFAULT_DISPLAY);
     }
 
     public void startObservingNativeCrashes() {
@@ -1801,7 +1801,7 @@
         m.mFactoryTest = factoryTest;
         m.mIntentFirewall = new IntentFirewall(m.new IntentFirewallInterface());
 
-        m.mStackSupervisor = new ActivityStackSupervisor(m, context, thr.mLooper);
+        m.mStackSupervisor = new ActivityStackSupervisor(m);
 
         m.mBatteryStatsService.publish(context);
         m.mUsageStatsService.publish(context);
@@ -7096,22 +7096,28 @@
     }
 
     @Override
-    public int createStack(int taskId) {
+    public IBinder getHomeActivityToken() throws RemoteException {
         enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "createStack()");
-        if (DEBUG_STACK) Slog.d(TAG, "createStack: taskId=" + taskId);
+                "getHomeActivityToken()");
         synchronized (this) {
-            long ident = Binder.clearCallingIdentity();
-            try {
-                int stackId = mStackSupervisor.createStack();
-                mWindowManager.createStack(stackId, Display.DEFAULT_DISPLAY);
-                if (taskId > 0) {
-                    moveTaskToStack(taskId, stackId, true);
-                }
-                return stackId;
-            } finally {
-                Binder.restoreCallingIdentity(ident);
+            return mStackSupervisor.getHomeActivityToken();
+        }
+    }
+
+    @Override
+    public IActivityContainer createActivityContainer(IBinder parentActivityToken,
+            IActivityContainerCallback callback) throws RemoteException {
+        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                "createActivityContainer()");
+        synchronized (this) {
+            if (parentActivityToken == null) {
+                throw new IllegalArgumentException("parent token must not be null");
             }
+            ActivityRecord r = ActivityRecord.forToken(parentActivityToken);
+            if (r == null) {
+                return null;
+            }
+            return mStackSupervisor.createActivityContainer(r, callback);
         }
     }
 
@@ -7150,11 +7156,11 @@
     @Override
     public List<StackInfo> getAllStackInfos() {
         enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "getStackBoxes()");
+                "getAllStackInfos()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                return mStackSupervisor.getAllStackInfos();
+                return mStackSupervisor.getAllStackInfosLocked();
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -7168,7 +7174,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                return mStackSupervisor.getStackInfo(stackId);
+                return mStackSupervisor.getStackInfoLocked(stackId);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -14049,18 +14055,21 @@
 
         boolean kept = true;
         final ActivityStack mainStack = mStackSupervisor.getFocusedStack();
-        if (changes != 0 && starting == null) {
-            // If the configuration changed, and the caller is not already
-            // in the process of starting an activity, then find the top
-            // activity to check if its configuration needs to change.
-            starting = mainStack.topRunningActivityLocked(null);
-        }
+        // mainStack is null during startup.
+        if (mainStack != null) {
+            if (changes != 0 && starting == null) {
+                // If the configuration changed, and the caller is not already
+                // in the process of starting an activity, then find the top
+                // activity to check if its configuration needs to change.
+                starting = mainStack.topRunningActivityLocked(null);
+            }
 
-        if (starting != null) {
-            kept = mainStack.ensureActivityConfigurationLocked(starting, changes);
-            // And we need to make sure at this point that all other activities
-            // are made visible with the correct configuration.
-            mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);
+            if (starting != null) {
+                kept = mainStack.ensureActivityConfigurationLocked(starting, changes);
+                // And we need to make sure at this point that all other activities
+                // are made visible with the correct configuration.
+                mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);
+            }
         }
 
         if (values != null && mWindowManager != null) {
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 49f29fe..38b01df 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -21,6 +21,7 @@
 import com.android.internal.app.ResolverActivity;
 import com.android.server.AttributeCache;
 import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
 
 import android.app.ActivityOptions;
 import android.app.ResultInfo;
@@ -138,6 +139,7 @@
     boolean forceNewConfig; // force re-create with new config next time
     int launchCount;        // count of launches since last state
     long lastLaunchTime;    // time of last lauch of this activity
+    ArrayList<ActivityStack> mChildContainers = new ArrayList<ActivityStack>();
 
     String stringName;      // for caching of toString().
 
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 82d29a9..690574c 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -39,6 +39,7 @@
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityManagerService.ItemMatcher;
+import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
 import com.android.server.wm.AppTransition;
 import com.android.server.wm.TaskGroup;
 import com.android.server.wm.WindowManagerService;
@@ -52,7 +53,6 @@
 import android.app.ResultInfo;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
@@ -139,8 +139,6 @@
     final ActivityManagerService mService;
     final WindowManagerService mWindowManager;
 
-    final Context mContext;
-
     /**
      * The back history of all previous (and possibly still
      * running) activities.  It contains #TaskRecord objects.
@@ -230,6 +228,8 @@
 
     final int mStackId;
 
+    final ActivityContainer mActivityContainer;
+
     /** Run all ActivityStacks through this */
     final ActivityStackSupervisor mStackSupervisor;
 
@@ -327,14 +327,14 @@
         return count;
     }
 
-    ActivityStack(ActivityManagerService service, Context context, Looper looper, int stackId) {
-        mHandler = new ActivityStackHandler(looper);
-        mService = service;
-        mWindowManager = service.mWindowManager;
-        mStackSupervisor = service.mStackSupervisor;
-        mContext = context;
-        mStackId = stackId;
-        mCurrentUser = service.mCurrentUserId;
+    ActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer) {
+        mActivityContainer = activityContainer;
+        mStackSupervisor = activityContainer.getOuter();
+        mService = mStackSupervisor.mService;
+        mHandler = new ActivityStackHandler(mService.mHandler.getLooper());
+        mWindowManager = mService.mWindowManager;
+        mStackId = activityContainer.mStackId;
+        mCurrentUser = mService.mCurrentUserId;
     }
 
     boolean okToShow(ActivityRecord r) {
@@ -436,25 +436,6 @@
         return null;
     }
 
-    boolean containsApp(ProcessRecord app) {
-        if (app == null) {
-            return false;
-        }
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
-                if (r.finishing) {
-                    continue;
-                }
-                if (r.app == app) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
     final boolean updateLRUListLocked(ActivityRecord r) {
         final boolean hadit = mLRUActivities.remove(r);
         mLRUActivities.add(r);
@@ -465,6 +446,13 @@
         return mStackId == HOME_STACK_ID;
     }
 
+    ArrayList<ActivityStack> getStacksLocked() {
+        if (mActivityContainer.isAttached()) {
+            return mActivityContainer.mActivityDisplayInfo.stacks;
+        }
+        return null;
+    }
+
     /**
      * Returns the top activity in any existing task matching the given
      * Intent.  Returns null if no such task is found.
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index 4a46288..f1ac7fe 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -37,6 +37,8 @@
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
+import android.app.IActivityContainer;
+import android.app.IActivityContainerCallback;
 import android.app.IActivityManager;
 import android.app.IApplicationThread;
 import android.app.IThumbnailReceiver;
@@ -54,6 +56,9 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
+import android.graphics.Point;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Debug;
@@ -69,9 +74,11 @@
 import android.os.UserHandle;
 import android.util.EventLog;
 import android.util.Slog;
-import android.util.SparseIntArray;
+import android.util.SparseArray;
 
+import android.util.SparseIntArray;
 import android.view.Display;
+import android.view.DisplayInfo;
 import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.os.TransferPipe;
 import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
@@ -84,7 +91,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-public final class ActivityStackSupervisor {
+public final class ActivityStackSupervisor implements DisplayListener {
     static final boolean DEBUG = ActivityManagerService.DEBUG || false;
     static final boolean DEBUG_ADD_REMOVE = DEBUG || false;
     static final boolean DEBUG_APP = DEBUG || false;
@@ -108,19 +115,22 @@
     static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_STACK_MSG + 2;
     static final int SLEEP_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 3;
     static final int LAUNCH_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 4;
+    static final int HANDLE_DISPLAY_ADDED = FIRST_SUPERVISOR_STACK_MSG + 5;
+    static final int HANDLE_DISPLAY_CHANGED = FIRST_SUPERVISOR_STACK_MSG + 6;
+    static final int HANDLE_DISPLAY_REMOVED = FIRST_SUPERVISOR_STACK_MSG + 7;
+
 
     // For debugging to make sure the caller when acquiring/releasing our
     // wake lock is the system process.
     static final boolean VALIDATE_WAKE_LOCK_CALLER = false;
 
     final ActivityManagerService mService;
-    final Context mContext;
-    final Looper mLooper;
 
     final ActivityStackSupervisorHandler mHandler;
 
     /** Short cut */
     WindowManagerService mWindowManager;
+    DisplayManager mDisplayManager;
 
     /** Dismiss the keyguard after the next activity is displayed? */
     boolean mDismissKeyguardOnNextActivity = false;
@@ -143,14 +153,10 @@
      * DO NOT ACCESS DIRECTLY - It may be null, use getFocusedStack() */
     private ActivityStack mFocusedStack;
 
-    /** All the non-launcher stacks */
-    private ArrayList<ActivityStack> mStacks = new ArrayList<ActivityStack>();
-
-    private static final int STACK_STATE_HOME_IN_FRONT = 0;
-    private static final int STACK_STATE_HOME_TO_BACK = 1;
-    private static final int STACK_STATE_HOME_IN_BACK = 2;
-    private static final int STACK_STATE_HOME_TO_FRONT = 3;
-    private int mStackState = STACK_STATE_HOME_IN_FRONT;
+    /** If this is the same as mFocusedStack then the activity on the top of the focused stack has
+     * been resumed. If stacks are changing position this will hold the old stack until the new
+     * stack becomes resumed after which it will be set to the new stack. */
+    private ActivityStack mLastFocusedStack;
 
     /** List of activities that are waiting for a new activity to become visible before completing
      * whatever operation they are supposed to do. */
@@ -207,14 +213,17 @@
     /** Stack id of the front stack when user switched, indexed by userId. */
     SparseIntArray mUserStackInFront = new SparseIntArray(2);
 
-    public ActivityStackSupervisor(ActivityManagerService service, Context context,
-            Looper looper) {
+    /** Mapping from (ActivityStack/TaskStack).mStackId to their current state */
+    SparseArray<ActivityContainer> mActivityContainers = new SparseArray<ActivityContainer>();
+
+    /** Mapping from displayId to display current state */
+    SparseArray<ActivityDisplayInfo> mDisplayInfos = new SparseArray<ActivityDisplayInfo>();
+
+    public ActivityStackSupervisor(ActivityManagerService service) {
         mService = service;
-        mContext = context;
-        mLooper = looper;
-        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+        PowerManager pm = (PowerManager)mService.mContext.getSystemService(Context.POWER_SERVICE);
         mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
-        mHandler = new ActivityStackSupervisorHandler(looper);
+        mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
         if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
             throw new IllegalStateException("Calling must be system uid");
         }
@@ -224,9 +233,23 @@
     }
 
     void setWindowManager(WindowManagerService wm) {
-        mWindowManager = wm;
-        mHomeStack = new ActivityStack(mService, mContext, mLooper, HOME_STACK_ID);
-        mStacks.add(mHomeStack);
+        synchronized (mService) {
+            mWindowManager = wm;
+
+            mDisplayManager =
+                    (DisplayManager)mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
+            mDisplayManager.registerDisplayListener(this, null);
+
+            Display[] displays = mDisplayManager.getDisplays();
+            for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
+                final int displayId = displays[displayNdx].getDisplayId();
+                ActivityDisplayInfo info = new ActivityDisplayInfo(displayId);
+                mDisplayInfos.put(displayId, info);
+            }
+
+            createStackOnDisplay(null, HOME_STACK_ID, Display.DEFAULT_DISPLAY);
+            mHomeStack = mFocusedStack = mLastFocusedStack = getStack(HOME_STACK_ID);
+        }
     }
 
     void dismissKeyguard() {
@@ -238,43 +261,38 @@
     }
 
     ActivityStack getFocusedStack() {
-        if (mFocusedStack == null) {
-            return mHomeStack;
-        }
-        switch (mStackState) {
-            case STACK_STATE_HOME_IN_FRONT:
-            case STACK_STATE_HOME_TO_FRONT:
-                return mHomeStack;
-            case STACK_STATE_HOME_IN_BACK:
-            case STACK_STATE_HOME_TO_BACK:
-            default:
-                return mFocusedStack;
-        }
+        return mFocusedStack;
     }
 
     ActivityStack getLastStack() {
-        switch (mStackState) {
-            case STACK_STATE_HOME_IN_FRONT:
-            case STACK_STATE_HOME_TO_BACK:
-                return mHomeStack;
-            case STACK_STATE_HOME_TO_FRONT:
-            case STACK_STATE_HOME_IN_BACK:
-            default:
-                return mFocusedStack;
-        }
+        return mLastFocusedStack;
     }
 
+    // TODO: Split into two methods isFrontStack for any visible stack and isFrontmostStack for the
+    // top of all visible stacks.
     boolean isFrontStack(ActivityStack stack) {
-        return !(stack.isHomeStack() ^ getFocusedStack().isHomeStack());
+        ArrayList<ActivityStack> stacks = stack.getStacksLocked();
+        if (stacks != null && !stacks.isEmpty()) {
+            return stack == stacks.get(stacks.size() - 1);
+        }
+        return false;
     }
 
     void moveHomeStack(boolean toFront) {
-        final boolean homeInFront = isFrontStack(mHomeStack);
-        if (homeInFront ^ toFront) {
-            if (DEBUG_STACK) Slog.d(TAG, "moveHomeTask: mStackState old=" +
-                    stackStateToString(mStackState) + " new=" + stackStateToString(homeInFront ?
-                    STACK_STATE_HOME_TO_BACK : STACK_STATE_HOME_TO_FRONT));
-            mStackState = homeInFront ? STACK_STATE_HOME_TO_BACK : STACK_STATE_HOME_TO_FRONT;
+        ArrayList<ActivityStack> stacks = mHomeStack.getStacksLocked();
+        int topNdx = stacks.size() - 1;
+        if (topNdx <= 0) {
+            return;
+        }
+        ActivityStack topStack = stacks.get(topNdx);
+        final boolean homeInFront = topStack == mHomeStack;
+        if (homeInFront != toFront) {
+            mLastFocusedStack = topStack;
+            stacks.remove(mHomeStack);
+            stacks.add(toFront ? topNdx : 0, mHomeStack);
+            mFocusedStack = stacks.get(topNdx);
+            if (DEBUG_STACK) Slog.d(TAG, "moveHomeTask: topStack old=" + topStack + " new="
+                    + mFocusedStack);
         }
     }
 
@@ -302,21 +320,29 @@
     }
 
     TaskRecord anyTaskForIdLocked(int id) {
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            ActivityStack stack = mStacks.get(stackNdx);
-            TaskRecord task = stack.taskForIdLocked(id);
-            if (task != null) {
-                return task;
+        int numDisplays = mDisplayInfos.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                ActivityStack stack = stacks.get(stackNdx);
+                TaskRecord task = stack.taskForIdLocked(id);
+                if (task != null) {
+                    return task;
+                }
             }
         }
         return null;
     }
 
     ActivityRecord isInAnyStackLocked(IBinder token) {
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityRecord r = mStacks.get(stackNdx).isInStackLocked(token);
-            if (r != null) {
-                return r;
+        int numDisplays = mDisplayInfos.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityRecord r = stacks.get(stackNdx).isInStackLocked(token);
+                if (r != null) {
+                    return r;
+                }
             }
         }
         return null;
@@ -341,14 +367,11 @@
         }
         if (stack.removeTask(task) && !stack.isHomeStack()) {
             if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing stack " + stack);
-            mStacks.remove(stack);
+            stack.mActivityContainer.detachLocked();
             final int stackId = stack.mStackId;
             final int nextStackId = mWindowManager.removeStack(stackId);
             // TODO: Perhaps we need to let the ActivityManager determine the next focus...
-            if (mFocusedStack == null || mFocusedStack.mStackId == stackId) {
-                // If this is the last app stack, set mFocusedStack to null.
-                mFocusedStack = nextStackId == HOME_STACK_ID ? null : getStack(nextStackId);
-            }
+            mFocusedStack = getStack(nextStackId);
         }
     }
 
@@ -368,25 +391,28 @@
     }
 
     boolean attachApplicationLocked(ProcessRecord app) throws Exception {
-        boolean didSomething = false;
         final String processName = app.processName;
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            if (!isFrontStack(stack)) {
-                continue;
-            }
-            ActivityRecord hr = stack.topRunningActivityLocked(null);
-            if (hr != null) {
-                if (hr.app == null && app.uid == hr.info.applicationInfo.uid
-                        && processName.equals(hr.processName)) {
-                    try {
-                        if (realStartActivityLocked(hr, app, true, true)) {
-                            didSomething = true;
+        boolean didSomething = false;
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                if (!isFrontStack(stack)) {
+                    continue;
+                }
+                ActivityRecord hr = stack.topRunningActivityLocked(null);
+                if (hr != null) {
+                    if (hr.app == null && app.uid == hr.info.applicationInfo.uid
+                            && processName.equals(hr.processName)) {
+                        try {
+                            if (realStartActivityLocked(hr, app, true, true)) {
+                                didSomething = true;
+                            }
+                        } catch (Exception e) {
+                            Slog.w(TAG, "Exception in new application when starting activity "
+                                  + hr.intent.getComponent().flattenToShortString(), e);
+                            throw e;
                         }
-                    } catch (Exception e) {
-                        Slog.w(TAG, "Exception in new application when starting activity "
-                              + hr.intent.getComponent().flattenToShortString(), e);
-                        throw e;
                     }
                 }
             }
@@ -398,53 +424,52 @@
     }
 
     boolean allResumedActivitiesIdle() {
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            if (!isFrontStack(stack)) {
-                continue;
-            }
-            final ActivityRecord resumedActivity = stack.mResumedActivity;
-            if (resumedActivity == null || !resumedActivity.idle) {
-                return false;
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                if (!isFrontStack(stack)) {
+                    continue;
+                }
+                final ActivityRecord resumedActivity = stack.mResumedActivity;
+                if (resumedActivity == null || !resumedActivity.idle) {
+                    return false;
+                }
             }
         }
         return true;
     }
 
     boolean allResumedActivitiesComplete() {
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            if (isFrontStack(stack)) {
-                final ActivityRecord r = stack.mResumedActivity;
-                if (r != null && r.state != ActivityState.RESUMED) {
-                    return false;
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                if (isFrontStack(stack)) {
+                    final ActivityRecord r = stack.mResumedActivity;
+                    if (r != null && r.state != ActivityState.RESUMED) {
+                        return false;
+                    }
                 }
             }
         }
         // TODO: Not sure if this should check if all Paused are complete too.
-        switch (mStackState) {
-            case STACK_STATE_HOME_TO_BACK:
-                if (DEBUG_STACK) Slog.d(TAG, "allResumedActivitiesComplete: mStackState old=" +
-                        stackStateToString(STACK_STATE_HOME_TO_BACK) + " new=" +
-                        stackStateToString(STACK_STATE_HOME_IN_BACK));
-                mStackState = STACK_STATE_HOME_IN_BACK;
-                break;
-            case STACK_STATE_HOME_TO_FRONT:
-                if (DEBUG_STACK) Slog.d(TAG, "allResumedActivitiesComplete: mStackState old=" +
-                        stackStateToString(STACK_STATE_HOME_TO_FRONT) + " new=" +
-                        stackStateToString(STACK_STATE_HOME_IN_FRONT));
-                mStackState = STACK_STATE_HOME_IN_FRONT;
-                break;
-        }
+        if (DEBUG_STACK) Slog.d(TAG,
+                "allResumedActivitiesComplete: mLastFocusedStack changing from=" +
+                mLastFocusedStack + " to=" + mFocusedStack);
+        mLastFocusedStack = mFocusedStack;
         return true;
     }
 
     boolean allResumedActivitiesVisible() {
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            final ActivityRecord r = stack.mResumedActivity;
-            if (r != null && (!r.nowVisible || r.waitingVisible)) {
-                return false;
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                final ActivityRecord r = stack.mResumedActivity;
+                if (r != null && (!r.nowVisible || r.waitingVisible)) {
+                    return false;
+                }
             }
         }
         return true;
@@ -457,13 +482,16 @@
      */
     boolean pauseBackStacks(boolean userLeaving) {
         boolean someActivityPaused = false;
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            if (!isFrontStack(stack) && stack.mResumedActivity != null) {
-                if (DEBUG_STATES) Slog.d(TAG, "pauseBackStacks: stack=" + stack +
-                        " mResumedActivity=" + stack.mResumedActivity);
-                stack.startPausingLocked(userLeaving, false);
-                someActivityPaused = true;
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                if (!isFrontStack(stack) && stack.mResumedActivity != null) {
+                    if (DEBUG_STATES) Slog.d(TAG, "pauseBackStacks: stack=" + stack +
+                            " mResumedActivity=" + stack.mResumedActivity);
+                    stack.startPausingLocked(userLeaving, false);
+                    someActivityPaused = true;
+                }
             }
         }
         return someActivityPaused;
@@ -471,17 +499,20 @@
 
     boolean allPausedActivitiesComplete() {
         boolean pausing = true;
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            final ActivityRecord r = stack.mPausingActivity;
-            if (r != null && r.state != ActivityState.PAUSED
-                    && r.state != ActivityState.STOPPED
-                    && r.state != ActivityState.STOPPING) {
-                if (DEBUG_STATES) {
-                    Slog.d(TAG, "allPausedActivitiesComplete: r=" + r + " state=" + r.state);
-                    pausing = false;
-                } else {
-                    return false;
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                final ActivityRecord r = stack.mPausingActivity;
+                if (r != null && r.state != ActivityState.PAUSED
+                        && r.state != ActivityState.STOPPED
+                        && r.state != ActivityState.STOPPING) {
+                    if (DEBUG_STATES) {
+                        Slog.d(TAG, "allPausedActivitiesComplete: r=" + r + " state=" + r.state);
+                        pausing = false;
+                    } else {
+                        return false;
+                    }
                 }
             }
         }
@@ -523,8 +554,10 @@
             return r;
         }
 
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
+        // Return to the home stack.
+        final ArrayList<ActivityStack> stacks = mHomeStack.getStacksLocked();
+        for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = stacks.get(stackNdx);
             if (stack != focusedStack && isFrontStack(stack)) {
                 r = stack.topRunningActivityLocked(null);
                 if (r != null) {
@@ -540,15 +573,19 @@
         ActivityRecord r = null;
 
         // Gather all of the running tasks for each stack into runningTaskLists.
-        final int numStacks = mStacks.size();
-        ArrayList<RunningTaskInfo>[] runningTaskLists = new ArrayList[numStacks];
-        for (int stackNdx = numStacks - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            ArrayList<RunningTaskInfo> stackTaskList = new ArrayList<RunningTaskInfo>();
-            runningTaskLists[stackNdx] = stackTaskList;
-            final ActivityRecord ar = stack.getTasksLocked(receiver, pending, stackTaskList);
-            if (isFrontStack(stack)) {
-                r = ar;
+        ArrayList<ArrayList<RunningTaskInfo>> runningTaskLists =
+                new ArrayList<ArrayList<RunningTaskInfo>>();
+        final int numDisplays = mDisplayInfos.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                ArrayList<RunningTaskInfo> stackTaskList = new ArrayList<RunningTaskInfo>();
+                runningTaskLists.add(stackTaskList);
+                final ActivityRecord ar = stack.getTasksLocked(receiver, pending, stackTaskList);
+                if (r == null && isFrontStack(stack)) {
+                    r = ar;
+                }
             }
         }
 
@@ -557,8 +594,9 @@
         while (maxNum > 0) {
             long mostRecentActiveTime = Long.MIN_VALUE;
             ArrayList<RunningTaskInfo> selectedStackList = null;
-            for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-                ArrayList<RunningTaskInfo> stackTaskList = runningTaskLists[stackNdx];
+            final int numTaskLists = runningTaskLists.size();
+            for (int stackNdx = 0; stackNdx < numTaskLists; ++stackNdx) {
+                ArrayList<RunningTaskInfo> stackTaskList = runningTaskLists.get(stackNdx);
                 if (!stackTaskList.isEmpty()) {
                     final long lastActiveTime = stackTaskList.get(0).lastActiveTime;
                     if (lastActiveTime > mostRecentActiveTime) {
@@ -1264,7 +1302,7 @@
                 if (mFocusedStack != taskStack) {
                     if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
                             "adjustStackFocus: Setting focused stack to r=" + r + " task=" + task);
-                    mFocusedStack = taskStack.isHomeStack() ? null : taskStack;
+                    mFocusedStack = taskStack;
                 } else {
                     if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
                         "adjustStackFocus: Focused stack already=" + mFocusedStack);
@@ -1272,24 +1310,28 @@
                 return taskStack;
             }
 
-            if (mFocusedStack != null) {
+            if (mFocusedStack != mHomeStack) {
                 if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
                         "adjustStackFocus: Have a focused stack=" + mFocusedStack);
                 return mFocusedStack;
             }
 
-            for (int stackNdx = mStacks.size() - 1; stackNdx > 0; --stackNdx) {
-                ActivityStack stack = mStacks.get(stackNdx);
-                if (!stack.isHomeStack()) {
-                    if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
-                            "adjustStackFocus: Setting focused stack=" + stack);
-                    mFocusedStack = stack;
-                    return mFocusedStack;
+            int numDisplays = mDisplayInfos.size();
+            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+                for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                    ActivityStack stack = stacks.get(stackNdx);
+                    if (!stack.isHomeStack()) {
+                        if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
+                                "adjustStackFocus: Setting focused stack=" + stack);
+                        mFocusedStack = stack;
+                        return mFocusedStack;
+                    }
                 }
             }
 
-            // Time to create the first app stack for this user.
-            int stackId = mService.createStack(-1);
+            // Need to create an app stack for this user.
+            int stackId = createStackOnDisplay(null, getNextStackId(), Display.DEFAULT_DISPLAY);
             if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG, "adjustStackFocus: New stack r=" + r +
                     " stackId=" + stackId);
             mFocusedStack = getStack(stackId);
@@ -1299,30 +1341,10 @@
     }
 
     void setFocusedStack(ActivityRecord r) {
-        if (r == null) {
-            return;
-        }
-        if (!r.isApplicationActivity() || (r.task != null && !r.task.isApplicationTask())) {
-            if (mStackState != STACK_STATE_HOME_IN_FRONT) {
-                if (DEBUG_STACK || DEBUG_FOCUS) Slog.d(TAG, "setFocusedStack: mStackState old=" +
-                        stackStateToString(mStackState) + " new=" +
-                        stackStateToString(STACK_STATE_HOME_TO_FRONT) +
-                        " Callers=" + Debug.getCallers(3));
-                mStackState = STACK_STATE_HOME_TO_FRONT;
-            }
-        } else {
-            if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
-                    "setFocusedStack: Setting focused stack to r=" + r + " task=" + r.task +
-                    " Callers=" + Debug.getCallers(3));
-            final ActivityStack taskStack = r.task.stack;
-            mFocusedStack = taskStack.isHomeStack() ? null : taskStack;
-            if (mStackState != STACK_STATE_HOME_IN_BACK) {
-                if (DEBUG_STACK) Slog.d(TAG, "setFocusedStack: mStackState old=" +
-                        stackStateToString(mStackState) + " new=" +
-                        stackStateToString(STACK_STATE_HOME_TO_BACK) +
-                        " Callers=" + Debug.getCallers(3));
-                mStackState = STACK_STATE_HOME_TO_BACK;
-            }
+        if (r != null) {
+            final boolean isHomeActivity =
+                    !r.isApplicationActivity() || (r.task != null && !r.task.isApplicationTask());
+            moveHomeStack(isHomeActivity);
         }
     }
 
@@ -1938,17 +1960,21 @@
 
     boolean handleAppDiedLocked(ProcessRecord app) {
         boolean hasVisibleActivities = false;
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            hasVisibleActivities |= mStacks.get(stackNdx).handleAppDiedLocked(app);
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                hasVisibleActivities |= stacks.get(stackNdx).handleAppDiedLocked(app);
+            }
         }
         return hasVisibleActivities;
     }
 
     void closeSystemDialogsLocked() {
-        final int numStacks = mStacks.size();
-        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            stack.closeSystemDialogsLocked();
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                stacks.get(stackNdx).closeSystemDialogsLocked();
+            }
         }
     }
 
@@ -1961,11 +1987,14 @@
      */
     boolean forceStopPackageLocked(String name, boolean doit, boolean evenPersistent, int userId) {
         boolean didSomething = false;
-        final int numStacks = mStacks.size();
-        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            if (stack.forceStopPackageLocked(name, doit, evenPersistent, userId)) {
-                didSomething = true;
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            final int numStacks = stacks.size();
+            for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                if (stack.forceStopPackageLocked(name, doit, evenPersistent, userId)) {
+                    didSomething = true;
+                }
             }
         }
         return didSomething;
@@ -1980,15 +2009,18 @@
         // we don't blow away the previous app if this activity is being
         // hosted by the process that is actually still the foreground.
         ProcessRecord fgApp = null;
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            if (isFrontStack(stack)) {
-                if (stack.mResumedActivity != null) {
-                    fgApp = stack.mResumedActivity.app;
-                } else if (stack.mPausingActivity != null) {
-                    fgApp = stack.mPausingActivity.app;
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                if (isFrontStack(stack)) {
+                    if (stack.mResumedActivity != null) {
+                        fgApp = stack.mResumedActivity.app;
+                    } else if (stack.mPausingActivity != null) {
+                        fgApp = stack.mPausingActivity.app;
+                    }
+                    break;
                 }
-                break;
             }
         }
 
@@ -2012,13 +2044,16 @@
             targetStack = getFocusedStack();
         }
         boolean result = false;
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            if (isFrontStack(stack)) {
-                if (stack == targetStack) {
-                    result = stack.resumeTopActivityLocked(target, targetOptions);
-                } else {
-                    stack.resumeTopActivityLocked(null);
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                if (isFrontStack(stack)) {
+                    if (stack == targetStack) {
+                        result = stack.resumeTopActivityLocked(target, targetOptions);
+                    } else {
+                        stack.resumeTopActivityLocked(null);
+                    }
                 }
             }
         }
@@ -2026,38 +2061,91 @@
     }
 
     void finishTopRunningActivityLocked(ProcessRecord app) {
-        final int numStacks = mStacks.size();
-        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            stack.finishTopRunningActivityLocked(app);
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            final int numStacks = stacks.size();
+            for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                stack.finishTopRunningActivityLocked(app);
+            }
         }
     }
 
     void findTaskToMoveToFrontLocked(int taskId, int flags, Bundle options) {
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            if (mStacks.get(stackNdx).findTaskToMoveToFrontLocked(taskId, flags, options)) {
-                if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack=" +
-                        mStacks.get(stackNdx));
-                return;
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                if (stacks.get(stackNdx).findTaskToMoveToFrontLocked(taskId, flags, options)) {
+                    if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack="
+                            + stacks.get(stackNdx));
+                    return;
+                }
             }
         }
     }
 
     ActivityStack getStack(int stackId) {
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            if (stack.getStackId() == stackId) {
-                return stack;
-            }
+        ActivityContainer activityContainer = mActivityContainers.get(stackId);
+        if (activityContainer != null) {
+            return activityContainer.mStack;
         }
         return null;
     }
 
     ArrayList<ActivityStack> getStacks() {
-        return new ArrayList<ActivityStack>(mStacks);
+        ArrayList<ActivityStack> allStacks = new ArrayList<ActivityStack>();
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            allStacks.addAll(mDisplayInfos.valueAt(displayNdx).stacks);
+        }
+        return allStacks;
     }
 
-    int createStack() {
+    IBinder getHomeActivityToken() {
+        final ArrayList<TaskRecord> tasks = mHomeStack.getAllTasks();
+        for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = tasks.get(taskNdx);
+            if (task.isHomeTask()) {
+                final ArrayList<ActivityRecord> activities = task.mActivities;
+                for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                    final ActivityRecord r = activities.get(activityNdx);
+                    if (r.isHomeActivity()) {
+                        return r.appToken;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    ActivityContainer createActivityContainer(ActivityRecord parentActivity, int stackId,
+            IActivityContainerCallback callback) {
+        ActivityContainer activityContainer = new ActivityContainer(parentActivity, stackId,
+                callback);
+        mActivityContainers.put(stackId, activityContainer);
+        if (parentActivity != null) {
+            parentActivity.mChildContainers.add(activityContainer.mStack);
+        }
+        return activityContainer;
+    }
+
+    ActivityContainer createActivityContainer(ActivityRecord parentActivity,
+            IActivityContainerCallback callback) {
+        return createActivityContainer(parentActivity, getNextStackId(), callback);
+    }
+
+    private int createStackOnDisplay(ActivityRecord parentActivity, int stackId, int displayId) {
+        ActivityDisplayInfo displayInfo = mDisplayInfos.get(displayId);
+        if (displayInfo == null) {
+            return -1;
+        }
+
+        ActivityContainer activityContainer =
+                createActivityContainer(parentActivity, stackId, null);
+        activityContainer.attachToDisplayLocked(displayInfo);
+        return stackId;
+    }
+
+    int getNextStackId() {
         while (true) {
             if (++mLastStackId <= HOME_STACK_ID) {
                 mLastStackId = HOME_STACK_ID + 1;
@@ -2066,7 +2154,6 @@
                 break;
             }
         }
-        mStacks.add(new ActivityStack(mService, mContext, mLooper, mLastStackId));
         return mLastStackId;
     }
 
@@ -2088,15 +2175,18 @@
 
     ActivityRecord findTaskLocked(ActivityRecord r) {
         if (DEBUG_TASKS) Slog.d(TAG, "Looking for task of " + r);
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            if (!r.isApplicationActivity() && !stack.isHomeStack()) {
-                if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: " + stack);
-                continue;
-            }
-            final ActivityRecord ar = stack.findTaskLocked(r);
-            if (ar != null) {
-                return ar;
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                if (!r.isApplicationActivity() && !stack.isHomeStack()) {
+                    if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: " + stack);
+                    continue;
+                }
+                final ActivityRecord ar = stack.findTaskLocked(r);
+                if (ar != null) {
+                    return ar;
+                }
             }
         }
         if (DEBUG_TASKS) Slog.d(TAG, "No task found");
@@ -2104,10 +2194,13 @@
     }
 
     ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityRecord ar = mStacks.get(stackNdx).findActivityLocked(intent, info);
-            if (ar != null) {
-                return ar;
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityRecord ar = stacks.get(stackNdx).findActivityLocked(intent, info);
+                if (ar != null) {
+                    return ar;
+                }
             }
         }
         return null;
@@ -2135,8 +2228,11 @@
         final long endTime = System.currentTimeMillis() + timeout;
         while (true) {
             boolean cantShutdown = false;
-            for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-                cantShutdown |= mStacks.get(stackNdx).checkReadyForSleepLocked();
+            for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+                final ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+                for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                    cantShutdown |= stacks.get(stackNdx).checkReadyForSleepLocked();
+                }
             }
             if (cantShutdown) {
                 long timeRemaining = endTime - System.currentTimeMillis();
@@ -2167,11 +2263,14 @@
         if (mGoingToSleep.isHeld()) {
             mGoingToSleep.release();
         }
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            stack.awakeFromSleepingLocked();
-            if (isFrontStack(stack)) {
-                resumeTopActivitiesLocked();
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                stack.awakeFromSleepingLocked();
+                if (isFrontStack(stack)) {
+                    resumeTopActivitiesLocked();
+                }
             }
         }
         mGoingToSleepActivities.clear();
@@ -2190,8 +2289,11 @@
 
         if (!mSleepTimeout) {
             boolean dontSleep = false;
-            for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-                dontSleep |= mStacks.get(stackNdx).checkReadyForSleepLocked();
+            for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+                final ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+                for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                    dontSleep |= stacks.get(stackNdx).checkReadyForSleepLocked();
+                }
             }
 
             if (mStoppingActivities.size() > 0) {
@@ -2214,8 +2316,11 @@
             }
         }
 
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            mStacks.get(stackNdx).goToSleep();
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                stacks.get(stackNdx).goToSleep();
+            }
         }
 
         removeSleepTimeouts();
@@ -2242,37 +2347,45 @@
     }
 
     void handleAppCrashLocked(ProcessRecord app) {
-        final int numStacks = mStacks.size();
-        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            stack.handleAppCrashLocked(app);
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            final int numStacks = stacks.size();
+            for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                stack.handleAppCrashLocked(app);
+            }
         }
     }
 
     void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) {
         // First the front stacks. In case any are not fullscreen and are in front of home.
         boolean showHomeBehindStack = false;
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            if (isFrontStack(stack)) {
-                showHomeBehindStack =
-                        stack.ensureActivitiesVisibleLocked(starting, configChanges);
-            }
-        }
-        // Now do back stacks.
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            if (!isFrontStack(stack)) {
-                stack.ensureActivitiesVisibleLocked(starting, configChanges, showHomeBehindStack);
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            final int topStackNdx = stacks.size() - 1;
+            for (int stackNdx = topStackNdx; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                if (stackNdx == topStackNdx) {
+                    // Top stack.
+                    showHomeBehindStack =
+                            stack.ensureActivitiesVisibleLocked(starting, configChanges);
+                } else {
+                    // Back stack.
+                    stack.ensureActivitiesVisibleLocked(starting, configChanges,
+                            showHomeBehindStack);
+                }
             }
         }
     }
 
     void scheduleDestroyAllActivities(ProcessRecord app, String reason) {
-        final int numStacks = mStacks.size();
-        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            stack.scheduleDestroyActivities(app, false, reason);
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            final int numStacks = stacks.size();
+            for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                stack.scheduleDestroyActivities(app, false, reason);
+            }
         }
     }
 
@@ -2282,8 +2395,11 @@
         mCurrentUser = userId;
 
         mStartingUsers.add(uss);
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            mStacks.get(stackNdx).switchUserLocked(userId);
+        for (int displayNdx = mDisplayInfos.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                stacks.get(stackNdx).switchUserLocked(userId);
+            }
         }
 
         ActivityStack stack = getStack(restoreStackId);
@@ -2337,8 +2453,9 @@
     }
 
     void validateTopActivitiesLocked() {
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
+        // FIXME
+/*        for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = stacks.get(stackNdx);
             final ActivityRecord r = stack.topRunningActivityLocked(null);
             final ActivityState state = r == null ? ActivityState.DESTROYED : r.state;
             if (isFrontStack(stack)) {
@@ -2368,23 +2485,14 @@
                 }
             }
         }
-    }
-
-    private static String stackStateToString(int stackState) {
-        switch (stackState) {
-            case STACK_STATE_HOME_IN_FRONT: return "STACK_STATE_HOME_IN_FRONT";
-            case STACK_STATE_HOME_TO_BACK: return "STACK_STATE_HOME_TO_BACK";
-            case STACK_STATE_HOME_IN_BACK: return "STACK_STATE_HOME_IN_BACK";
-            case STACK_STATE_HOME_TO_FRONT: return "STACK_STATE_HOME_TO_FRONT";
-            default: return "Unknown stackState=" + stackState;
-        }
+*/
     }
 
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mDismissKeyguardOnNextActivity=");
                 pw.println(mDismissKeyguardOnNextActivity);
         pw.print(prefix); pw.print("mFocusedStack=" + mFocusedStack);
-                pw.print(" mStackState="); pw.println(stackStateToString(mStackState));
+                pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack);
         pw.print(prefix); pw.println("mSleepTimeout=" + mSleepTimeout);
         pw.print(prefix); pw.println("mCurTaskId=" + mCurTaskId);
         pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
@@ -2413,42 +2521,48 @@
             boolean dumpClient, String dumpPackage) {
         boolean printed = false;
         boolean needSep = false;
-        final int numStacks = mStacks.size();
-        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            StringBuilder stackHeader = new StringBuilder(128);
-            stackHeader.append("  Stack #");
-            stackHeader.append(mStacks.indexOf(stack));
-            stackHeader.append(":");
-            printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage, needSep,
-                    stackHeader.toString());
-            printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, "    ", "Run", false, !dumpAll,
-                    false, dumpPackage, true, "    Running activities (most recent first):", null);
+        for (int displayNdx = 0; displayNdx < mDisplayInfos.size(); ++displayNdx) {
+            ActivityDisplayInfo info = mDisplayInfos.valueAt(displayNdx);
+            pw.print("Display #"); pw.println(info.mDisplayId);
+            ArrayList<ActivityStack> stacks = info.stacks;
+            final int numStacks = stacks.size();
+            for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                StringBuilder stackHeader = new StringBuilder(128);
+                stackHeader.append("  Stack #");
+                stackHeader.append(stack.mStackId);
+                stackHeader.append(":");
+                printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
+                        needSep, stackHeader.toString());
+                printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, "    ", "Run", false,
+                        !dumpAll, false, dumpPackage, true,
+                        "    Running activities (most recent first):", null);
 
-            needSep = printed;
-            boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep,
-                    "    mPausingActivity: ");
-            if (pr) {
-                printed = true;
-                needSep = false;
-            }
-            pr = printThisActivity(pw, stack.mResumedActivity, dumpPackage, needSep,
-                    "    mResumedActivity: ");
-            if (pr) {
-                printed = true;
-                needSep = false;
-            }
-            if (dumpAll) {
-                pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep,
-                        "    mLastPausedActivity: ");
+                needSep = printed;
+                boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep,
+                        "    mPausingActivity: ");
                 if (pr) {
                     printed = true;
-                    needSep = true;
+                    needSep = false;
                 }
-                printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage,
-                        needSep, "    mLastNoHistoryActivity: ");
+                pr = printThisActivity(pw, stack.mResumedActivity, dumpPackage, needSep,
+                        "    mResumedActivity: ");
+                if (pr) {
+                    printed = true;
+                    needSep = false;
+                }
+                if (dumpAll) {
+                    pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep,
+                            "    mLastPausedActivity: ");
+                    if (pr) {
+                        printed = true;
+                        needSep = true;
+                    }
+                    printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage,
+                            needSep, "    mLastNoHistoryActivity: ");
+                }
+                needSep = printed;
             }
-            needSep = printed;
         }
 
         printed |= dumpHistoryList(fd, pw, mFinishingActivities, "  ", "Fin", false, !dumpAll,
@@ -2578,6 +2692,95 @@
         mHandler.sendEmptyMessageDelayed(SLEEP_TIMEOUT_MSG, SLEEP_TIMEOUT);
     }
 
+    @Override
+    public void onDisplayAdded(int displayId) {
+        mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_ADDED, displayId, 0));
+    }
+
+    @Override
+    public void onDisplayRemoved(int displayId) {
+        mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_REMOVED, displayId, 0));
+    }
+
+    @Override
+    public void onDisplayChanged(int displayId) {
+        mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_CHANGED, displayId, 0));
+    }
+
+    public void handleDisplayAddedLocked(int displayId) {
+        synchronized (mService) {
+            ActivityDisplayInfo info = new ActivityDisplayInfo(displayId);
+            mDisplayInfos.put(displayId, info);
+        }
+        mWindowManager.onDisplayAdded(displayId);
+    }
+
+    public void handleDisplayRemovedLocked(int displayId) {
+        synchronized (mService) {
+            ActivityDisplayInfo info = mDisplayInfos.get(displayId);
+            if (info != null) {
+                ArrayList<ActivityStack> stacks = info.stacks;
+                for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                    info.detachActivities(stacks.get(stackNdx));
+                }
+                mDisplayInfos.remove(displayId);
+            }
+        }
+        mWindowManager.onDisplayRemoved(displayId);
+    }
+
+    public void handleDisplayChangedLocked(int displayId) {
+        synchronized (mService) {
+            ActivityDisplayInfo info = mDisplayInfos.get(displayId);
+            if (info != null) {
+                // TODO: Update the bounds.
+            }
+        }
+        mWindowManager.onDisplayChanged(displayId);
+    }
+
+    StackInfo getStackInfo(ActivityStack stack) {
+        StackInfo info = new StackInfo();
+        mWindowManager.getStackBounds(stack.mStackId, info.bounds);
+        info.displayId = Display.DEFAULT_DISPLAY;
+        info.stackId = stack.mStackId;
+
+        ArrayList<TaskRecord> tasks = stack.getAllTasks();
+        final int numTasks = tasks.size();
+        int[] taskIds = new int[numTasks];
+        String[] taskNames = new String[numTasks];
+        for (int i = 0; i < numTasks; ++i) {
+            final TaskRecord task = tasks.get(i);
+            taskIds[i] = task.taskId;
+            taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
+                    : task.realActivity != null ? task.realActivity.flattenToString()
+                    : task.getTopActivity() != null ? task.getTopActivity().packageName
+                    : "unknown";
+        }
+        info.taskIds = taskIds;
+        info.taskNames = taskNames;
+        return info;
+    }
+
+    StackInfo getStackInfoLocked(int stackId) {
+        ActivityStack stack = getStack(stackId);
+        if (stack != null) {
+            return getStackInfo(stack);
+        }
+        return null;
+    }
+
+    ArrayList<StackInfo> getAllStackInfosLocked() {
+        ArrayList<StackInfo> list = new ArrayList<StackInfo>();
+        for (int displayNdx = 0; displayNdx < mDisplayInfos.size(); ++displayNdx) {
+            ArrayList<ActivityStack> stacks = mDisplayInfos.valueAt(displayNdx).stacks;
+            for (int ndx = stacks.size() - 1; ndx >= 0; --ndx) {
+                list.add(getStackInfo(stacks.get(ndx)));
+            }
+        }
+        return list;
+    }
+
     private final class ActivityStackSupervisorHandler extends Handler {
 
         public ActivityStackSupervisorHandler(Looper looper) {
@@ -2641,46 +2844,131 @@
                         }
                     }
                 } break;
+                case HANDLE_DISPLAY_ADDED: {
+                    handleDisplayAddedLocked(msg.arg1);
+                } break;
+                case HANDLE_DISPLAY_CHANGED: {
+                    handleDisplayChangedLocked(msg.arg1);
+                } break;
+                case HANDLE_DISPLAY_REMOVED: {
+                    handleDisplayRemovedLocked(msg.arg1);
+                } break;
             }
         }
     }
 
-    StackInfo getStackInfo(ActivityStack stack) {
-        StackInfo info = new StackInfo();
-        mWindowManager.getStackBounds(stack.mStackId, info.bounds);
-        info.displayId = Display.DEFAULT_DISPLAY;
-        info.stackId = stack.mStackId;
+    class ActivityContainer extends IActivityContainer.Stub {
+        final int mStackId;
+        final IActivityContainerCallback mCallback;
+        final ActivityStack mStack;
+        final ActivityRecord mParentActivity;
 
-        ArrayList<TaskRecord> tasks = stack.getAllTasks();
-        final int numTasks = tasks.size();
-        int[] taskIds = new int[numTasks];
-        String[] taskNames = new String[numTasks];
-        for (int i = 0; i < numTasks; ++i) {
-            final TaskRecord task = tasks.get(i);
-            taskIds[i] = task.taskId;
-            taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
-                    : task.realActivity != null ? task.realActivity.flattenToString()
-                    : task.getTopActivity() != null ? task.getTopActivity().packageName
-                    : "unknown";
+        /** Display this ActivityStack is currently on. Null if not attached to a Display. */
+        ActivityDisplayInfo mActivityDisplayInfo;
+
+        ActivityContainer(ActivityRecord parentActivity, int stackId,
+                IActivityContainerCallback callback) {
+            synchronized (mService) {
+                mStackId = stackId;
+                mStack = new ActivityStack(this);
+                mParentActivity = parentActivity;
+                mCallback = callback;
+            }
         }
-        info.taskIds = taskIds;
-        info.taskNames = taskNames;
-        return info;
+
+        void attachToDisplayLocked(ActivityDisplayInfo displayInfo) {
+            mActivityDisplayInfo = displayInfo;
+            displayInfo.attachActivities(mStack);
+            mWindowManager.createStack(mStackId, displayInfo.mDisplayId);
+        }
+
+        @Override
+        public void attachToDisplay(int displayId) throws RemoteException {
+            synchronized (mService) {
+                ActivityDisplayInfo displayInfo = mDisplayInfos.get(displayId);
+                if (displayInfo == null) {
+                    return;
+                }
+                attachToDisplayLocked(displayInfo);
+            }
+        }
+
+        @Override
+        public int getStackId() throws RemoteException {
+            return mStack.mStackId;
+        }
+
+        void detachLocked() {
+            if (mActivityDisplayInfo != null) {
+                mActivityDisplayInfo.detachActivities(mStack);
+                mActivityDisplayInfo = null;
+            }
+        }
+
+        @Override
+        public void detachFromDisplay() throws RemoteException {
+            synchronized (mService) {
+                detachLocked();
+            }
+        }
+
+        @Override
+        public void startActivity(Intent intent) throws RemoteException {
+
+        }
+
+        @Override
+        public IBinder asBinder() {
+            return this;
+        }
+
+        ActivityStackSupervisor getOuter() {
+            return ActivityStackSupervisor.this;
+        }
+
+        boolean isAttached() {
+            return mActivityDisplayInfo != null;
+        }
+
+        void getBounds(Point outBounds) {
+            if (mActivityDisplayInfo != null) {
+                mActivityDisplayInfo.getBounds(outBounds);
+            } else {
+                outBounds.set(0, 0);
+            }
+        }
     }
 
-    StackInfo getStackInfo(int stackId) {
-        ActivityStack stack = getStack(stackId);
-        if (stack != null) {
-            return getStackInfo(stack);
-        }
-        return null;
-    }
+    /** Exactly one of these classes per Display in the system. Capable of holding zero or more
+     * attached {@link ActivityStack}s */
+    final class ActivityDisplayInfo {
+        /** Actual Display this object tracks. */
+        final int mDisplayId;
+        final Display mDisplay;
+        final DisplayInfo mDisplayInfo = new DisplayInfo();
 
-    ArrayList<StackInfo> getAllStackInfos() {
-        ArrayList<StackInfo> list = new ArrayList<StackInfo>();
-        for (int ndx = mStacks.size() - 1; ndx >= 0; --ndx) {
-            list.add(getStackInfo(mStacks.get(ndx)));
+        /** 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> stacks = new ArrayList<ActivityStack>();
+
+        ActivityDisplayInfo(int displayId) {
+            mDisplayId = displayId;
+            mDisplay = mDisplayManager.getDisplay(displayId);
+            mDisplay.getDisplayInfo(mDisplayInfo);
         }
-        return list;
+
+        void attachActivities(ActivityStack stack) {
+            stacks.add(stack);
+        }
+
+        void detachActivities(ActivityStack stack) {
+            stacks.remove(stack);
+        }
+
+        void getBounds(Point bounds) {
+            mDisplay.getDisplayInfo(mDisplayInfo);
+            bounds.x = mDisplayInfo.appWidth;
+            bounds.y = mDisplayInfo.appHeight;
+        }
     }
 }
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index 4352ece..4064377 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -27,6 +27,7 @@
 import android.util.Slog;
 import android.view.Display;
 import android.view.DisplayInfo;
+import android.view.Surface;
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
@@ -211,10 +212,15 @@
     void getLogicalDisplayRect(Rect out) {
         updateDisplayInfo();
         // Uses same calculation as in LogicalDisplay#configureDisplayInTransactionLocked.
+        final int orientation = mDisplayInfo.rotation;
+        boolean rotated = (orientation == Surface.ROTATION_90
+                || orientation == Surface.ROTATION_270);
+        final int physWidth = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
+        final int physHeight = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;
         int width = mDisplayInfo.logicalWidth;
-        int left = (mBaseDisplayWidth - width) / 2;
+        int left = (physWidth - width) / 2;
         int height = mDisplayInfo.logicalHeight;
-        int top = (mBaseDisplayHeight - height) / 2;
+        int top = (physHeight - height) / 2;
         out.set(left, top, left + width, top + height);
     }
 
@@ -247,8 +253,12 @@
         mStacks.add(toTop ? mStacks.size() : 0, stack);
     }
 
-    TaskStack topStack() {
-        return mStacks.get(mStacks.size() - 1);
+    TaskStack removeStack(TaskStack stack) {
+        mStacks.remove(stack);
+        if (!mStacks.isEmpty()) {
+            return mStacks.get(mStacks.size() - 1);
+        }
+        return null;
     }
 
     /**
diff --git a/services/java/com/android/server/wm/TaskStack.java b/services/java/com/android/server/wm/TaskStack.java
index df1d108..ef245f9 100644
--- a/services/java/com/android/server/wm/TaskStack.java
+++ b/services/java/com/android/server/wm/TaskStack.java
@@ -205,7 +205,11 @@
         mAnimationBackgroundSurface.destroySurface();
         mDimLayer.destroySurface();
         EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
-        return mDisplayContent.topStack().mStackId;
+        TaskStack next = mDisplayContent.removeStack(this);
+        if (next != null) {
+            return next.mStackId;
+        }
+        return -1;
     }
 
     void resetAnimationBackgroundAnimator() {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index e50d61f..04129e2 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -4842,7 +4842,9 @@
                 int nextStackId = stack.remove();
                 stack.getDisplayContent().layoutNeeded = true;
                 requestTraversalLocked();
-                return nextStackId;
+                if (nextStackId > HOME_STACK_ID) {
+                    return nextStackId;
+                }
             }
             if (DEBUG_STACK) Slog.i(TAG, "removeStack: could not find stackId=" + stackId);
         }
@@ -8778,7 +8780,8 @@
             if (canBeSeen) {
                 // This function assumes that the contents of the default display are
                 // processed first before secondary displays.
-                if (w.mDisplayContent.isDefaultDisplay) {
+                final DisplayContent displayContent = w.mDisplayContent;
+                if (displayContent.isDefaultDisplay) {
                     // While a dream or keyguard is showing, obscure ordinary application
                     // content on secondary displays (by forcibly enabling mirroring unless
                     // there is other content we want to show) but still allow opaque