Merge "Allow stacks to hold tasks on various sizes."
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index b2f068b..5974fcf 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1789,6 +1789,7 @@
         public Rect bounds = new Rect();
         public int[] taskIds;
         public String[] taskNames;
+        public Rect[] taskBounds;
         public int displayId;
 
         @Override
@@ -1805,6 +1806,14 @@
             dest.writeInt(bounds.bottom);
             dest.writeIntArray(taskIds);
             dest.writeStringArray(taskNames);
+            final int boundsCount = taskBounds == null ? 0 : taskBounds.length;
+            dest.writeInt(boundsCount);
+            for (int i = 0; i < boundsCount; i++) {
+                dest.writeInt(taskBounds[i].left);
+                dest.writeInt(taskBounds[i].top);
+                dest.writeInt(taskBounds[i].right);
+                dest.writeInt(taskBounds[i].bottom);
+            }
             dest.writeInt(displayId);
         }
 
@@ -1814,6 +1823,17 @@
                     source.readInt(), source.readInt(), source.readInt(), source.readInt());
             taskIds = source.createIntArray();
             taskNames = source.createStringArray();
+            final int boundsCount = source.readInt();
+            if (boundsCount > 0) {
+                taskBounds = new Rect[boundsCount];
+                for (int i = 0; i < boundsCount; i++) {
+                    taskBounds[i] = new Rect();
+                    taskBounds[i].set(
+                            source.readInt(), source.readInt(), source.readInt(), source.readInt());
+                }
+            } else {
+                taskBounds = null;
+            }
             displayId = source.readInt();
         }
 
@@ -1844,7 +1864,11 @@
             prefix = prefix + "  ";
             for (int i = 0; i < taskIds.length; ++i) {
                 sb.append(prefix); sb.append("taskId="); sb.append(taskIds[i]);
-                        sb.append(": "); sb.append(taskNames[i]); sb.append("\n");
+                        sb.append(": "); sb.append(taskNames[i]);
+                        if (taskBounds != null) {
+                            sb.append(" bounds="); sb.append(taskBounds[i].toShortString());
+                        }
+                        sb.append("\n");
             }
             return sb.toString();
         }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index e83d635..facaee4 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -799,6 +799,14 @@
             return true;
         }
 
+        case SET_FOCUSED_TASK_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            int taskId = data.readInt();
+            setFocusedStack(taskId);
+            reply.writeNoException();
+            return true;
+        }
+
         case REGISTER_TASK_STACK_LISTENER_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
@@ -2429,6 +2437,15 @@
             return true;
         }
 
+        case GET_TASK_BOUNDS_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            int taskId = data.readInt();
+            Rect r = getTaskBounds(taskId);
+            reply.writeNoException();
+            r.writeToParcel(reply, 0);
+            return true;
+        }
+
         case GET_TASK_DESCRIPTION_ICON_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             String filename = data.readString();
@@ -3520,6 +3537,18 @@
         return focusedStackId;
     }
     @Override
+    public void setFocusedTask(int taskId) throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(taskId);
+        mRemote.transact(SET_FOCUSED_TASK_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+    @Override
     public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException
     {
         Parcel data = Parcel.obtain();
@@ -5790,6 +5819,21 @@
     }
 
     @Override
+    public Rect getTaskBounds(int taskId) throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(taskId);
+        mRemote.transact(GET_TASK_BOUNDS_TRANSACTION, data, reply, 0);
+        reply.readException();
+        Rect rect = Rect.CREATOR.createFromParcel(reply);
+        data.recycle();
+        reply.recycle();
+        return rect;
+    }
+
+    @Override
     public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 55cf3462..c3e55f1 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -144,6 +144,7 @@
     public boolean isInHomeStack(int taskId) throws RemoteException;
     public void setFocusedStack(int stackId) throws RemoteException;
     public int getFocusedStackId() throws RemoteException;
+    public void setFocusedTask(int taskId) throws RemoteException;
     public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException;
     public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException;
     public ContentProviderHolder getContentProvider(IApplicationThread caller,
@@ -486,6 +487,7 @@
             throws RemoteException;
     public void setTaskResizeable(int taskId, boolean resizeable) throws RemoteException;
     public void resizeTask(int taskId, Rect bounds) throws RemoteException;
+    public Rect getTaskBounds(int taskId) throws RemoteException;
     public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException;
 
     public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts)
@@ -752,7 +754,7 @@
     int GET_PACKAGE_ASK_SCREEN_COMPAT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+127;
     int SET_PACKAGE_ASK_SCREEN_COMPAT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+128;
     int SWITCH_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+129;
-    int ___AVAILABLE_1___ = IBinder.FIRST_CALL_TRANSACTION+130;
+    int SET_FOCUSED_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+130;
     int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+131;
     int REGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+132;
     int UNREGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+133;
@@ -804,7 +806,7 @@
     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;
-    // Available
+    int GET_TASK_BOUNDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+183;
     int GET_ACTIVITY_DISPLAY_ID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+184;
     int DELETE_ACTIVITY_CONTAINER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+185;
     int SET_PROCESS_MEMORY_TRIM_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+186;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 17db471..6e3a29d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -70,6 +70,7 @@
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsAppWidgetHost;
+import com.android.systemui.recents.RecentsConfiguration;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -255,12 +256,16 @@
         return false;
     }
 
-    /** Get the bounds of a stack / task. */
-    public Rect getTaskBounds(int stackId) {
-        ActivityManager.StackInfo info = getAllStackInfos().get(stackId);
-        if (info != null)
-          return info.bounds;
-        return new Rect();
+    /** Get the bounds of a task. */
+    public Rect getTaskBounds(int taskId) {
+        if (mIam == null) return null;
+
+        try {
+            return mIam.getTaskBounds(taskId);
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+        return null;
     }
 
     /** Resize a given task. */
@@ -268,32 +273,17 @@
         if (mIam == null) return;
 
         try {
+            if (RecentsConfiguration.getInstance().multiStackEnabled) {
+                // In debug mode, we force all task to be resizeable regardless of the
+                // current app configuration.
+                mIam.setTaskResizeable(taskId, true);
+            }
             mIam.resizeTask(taskId, bounds);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
     }
 
-    /** Returns the stack info for all stacks. */
-    public SparseArray<ActivityManager.StackInfo> getAllStackInfos() {
-        if (mIam == null) return new SparseArray<ActivityManager.StackInfo>();
-
-        try {
-            SparseArray<ActivityManager.StackInfo> stacks =
-                    new SparseArray<ActivityManager.StackInfo>();
-            List<ActivityManager.StackInfo> infos = mIam.getAllStackInfos();
-            int stackCount = infos.size();
-            for (int i = 0; i < stackCount; i++) {
-                ActivityManager.StackInfo info = infos.get(i);
-                stacks.put(info.stackId, info);
-            }
-            return stacks;
-        } catch (RemoteException e) {
-            e.printStackTrace();
-            return new SparseArray<ActivityManager.StackInfo>();
-        }
-    }
-
     /** Returns the focused stack id. */
     public int getFocusedStack() {
         if (mIam == null) return -1;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index f40c58d..b8015c0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -172,19 +172,13 @@
         }
 
         // Initialize the stacks
-        SparseArray<ActivityManager.StackInfo> stackInfos = mSystemServicesProxy.getAllStackInfos();
         mStacks.clear();
         int stackCount = stacksTasks.size();
         for (int i = 0; i < stackCount; i++) {
             int stackId = stacksTasks.keyAt(i);
-            ActivityManager.StackInfo info = stackInfos.get(stackId);
             ArrayList<Task> stackTasks = stacksTasks.valueAt(i);
             TaskStack stack = new TaskStack(stackId);
-            if (Constants.DebugFlags.App.EnableMultiStackToSingleStack) {
-                stack.setBounds(displayBounds, displayBounds);
-            } else {
-                stack.setBounds(info.bounds, displayBounds);
-            }
+            stack.setBounds(displayBounds, displayBounds);
             stack.setTasks(stackTasks);
             stack.createAffiliatedGroupings(mConfig);
             mStacks.put(stackId, stack);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 6db4020..353bcbe 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -224,7 +224,7 @@
     /** Updates the resize task bar button. */
     void updateResizeTaskBarIcon(Task t) {
         Rect display = mSsp.getWindowRect();
-        Rect taskRect = mSsp.getTaskBounds(t.key.stackId);
+        Rect taskRect = mSsp.getTaskBounds(t.key.id);
         int resId = R.drawable.star;
         if (display.equals(taskRect) || taskRect.isEmpty()) {
             resId = R.drawable.vector_drawable_place_fullscreen;
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index daf1751..13b3f8d 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -158,7 +158,7 @@
 # Task removed with source explanation.
 31003 wm_task_removed (TaskId|1|5),(Reason|3)
 # Stack created.
-31004 wm_stack_created (StackId|1|5),(RelativeBoxId|1|5),(Position|1),(Weight|1|6)
+31004 wm_stack_created (StackId|1|5)
 # Home stack moved to top (1) or bottom (0).
 31005 wm_home_stack_moved (ToTop|1)
 # Stack removed.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 755d7a0..266e3c7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2695,6 +2695,21 @@
         }
     }
 
+    @Override
+    public void setFocusedTask(int taskId) {
+        if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
+        synchronized (ActivityManagerService.this) {
+            TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+            if (task != null) {
+                ActivityRecord r = task.topRunningActivityLocked(null);
+                if (r != null) {
+                    setFocusedActivityLocked(r, "setFocusedTask");
+                    mStackSupervisor.resumeTopActivitiesLocked(task.stack, null, null);
+                }
+            }
+        }
+    }
+
     /** Sets the task stack listener that gets callbacks when a task stack changes. */
     @Override
     public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException {
@@ -8575,6 +8590,27 @@
     }
 
     @Override
+    public Rect getTaskBounds(int taskId) {
+        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                "getTaskBounds()");
+        long ident = Binder.clearCallingIdentity();
+        Rect rect = new Rect();
+        try {
+            synchronized (this) {
+                TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task == null) {
+                    Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found");
+                    return rect;
+                }
+                mWindowManager.getTaskBounds(task.taskId, rect);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return rect;
+    }
+
+    @Override
     public Bitmap getTaskDescriptionIcon(String filename) {
         if (!FileUtils.isValidExtFilename(filename)
                 || !filename.contains(ActivityRecord.ACTIVITY_ICON_SUFFIX)) {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 0957eb5..9b1044c 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -130,10 +130,10 @@
     long pauseTime;         // last time we started pausing the activity
     long launchTickTime;    // base time for launch tick messages
     Configuration configuration; // configuration activity was last running in
-    // Overridden configuration by the activity stack
-    // WARNING: Reference points to {@link ActivityStack#mOverrideConfig}, so its internal state
+    // Overridden configuration by the activity task
+    // WARNING: Reference points to {@link TaskRecord#mOverrideConfig}, so its internal state
     // should never be altered directly.
-    Configuration stackConfigOverride;
+    Configuration taskConfigOverride;
     CompatibilityInfo compat;// last used compatibility mode
     ActivityRecord resultTo; // who started this entry, so will get our reply
     final String resultWho; // additional identifier for use by resultTo.
@@ -212,7 +212,7 @@
                 pw.print(" icon=0x"); pw.print(Integer.toHexString(icon));
                 pw.print(" theme=0x"); pw.println(Integer.toHexString(theme));
         pw.print(prefix); pw.print("config="); pw.println(configuration);
-        pw.print(prefix); pw.print("stackConfigOverride="); pw.println(stackConfigOverride);
+        pw.print(prefix); pw.print("taskConfigOverride="); pw.println(taskConfigOverride);
         if (resultTo != null || resultWho != null) {
             pw.print(prefix); pw.print("resultTo="); pw.print(resultTo);
                     pw.print(" resultWho="); pw.print(resultWho);
@@ -445,8 +445,7 @@
         resolvedType = _resolvedType;
         componentSpecified = _componentSpecified;
         configuration = _configuration;
-        stackConfigOverride = (container != null)
-                ? container.mStack.mOverrideConfig : Configuration.EMPTY;
+        taskConfigOverride = Configuration.EMPTY;
         resultTo = _resultTo;
         resultWho = _resultWho;
         requestCode = _reqCode;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 2b763ed..431b433 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -113,11 +113,11 @@
     // How long we wait for the activity to tell us it has stopped before
     // giving up.  This is a good amount of time because we really need this
     // from the application in order to get its saved state.
-    static final int STOP_TIMEOUT = 10*1000;
+    static final int STOP_TIMEOUT = 10 * 1000;
 
     // How long we wait until giving up on an activity telling us it has
     // finished destroying itself.
-    static final int DESTROY_TIMEOUT = 10*1000;
+    static final int DESTROY_TIMEOUT = 10 * 1000;
 
     // How long until we reset a task when the user returns to it.  Currently
     // disabled.
@@ -125,7 +125,7 @@
 
     // How long between activity launches that we consider safe to not warn
     // the user about an unexpected activity being launched on top.
-    static final long START_WARN_TIME = 5*1000;
+    static final long START_WARN_TIME = 5 * 1000;
 
     // Set to false to disable the preview that is shown while a new activity
     // is being started.
@@ -214,8 +214,7 @@
     // Activity.onTranslucentConversionComplete(false). If a timeout occurs prior to the last
     // background activity being drawn then the same call will be made with a true value.
     ActivityRecord mTranslucentActivityWaiting = null;
-    private ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent =
-            new ArrayList<ActivityRecord>();
+    private ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent = new ArrayList<>();
 
     /**
      * Set when we know we are going to be calling updateConfiguration()
@@ -223,7 +222,7 @@
      */
     boolean mConfigWillChange;
 
-    // Whether or not this stack covers the entire screen; by default stacks are full screen
+    // Whether or not this stack covers the entire screen; by default stacks are fullscreen
     boolean mFullscreen = true;
 
     long mLaunchStartTime = 0;
@@ -241,11 +240,6 @@
     /** Run all ActivityStacks through this */
     final ActivityStackSupervisor mStackSupervisor;
 
-    Configuration mOverrideConfig;
-    /** True if the stack was forced to full screen because {@link TaskRecord#mResizeable} is false
-     * and the stack was previously resized. */
-    private boolean mForcedFullscreen = false;
-
     static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
     static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
     static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
@@ -361,7 +355,6 @@
         mStackId = activityContainer.mStackId;
         mCurrentUser = mService.mCurrentUserId;
         mRecentTasks = recentTasks;
-        mOverrideConfig = Configuration.EMPTY;
     }
 
     boolean okToShowLocked(ActivityRecord r) {
@@ -1165,16 +1158,18 @@
 
         final int numStacks = mStacks.size();
         while (stackNdx < numStacks) {
-            ActivityStack historyStack = mStacks.get(stackNdx);
+            final ActivityStack historyStack = mStacks.get(stackNdx);
             tasks = historyStack.mTaskHistory;
             final int numTasks = tasks.size();
             while (taskNdx < numTasks) {
-                activities = tasks.get(taskNdx).mActivities;
+                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 && activity.fullscreen ? null : activity;
+                        return historyStack.mFullscreen
+                                && currentTask.mFullscreen && activity.fullscreen ? null : activity;
                     }
                     ++activityNdx;
                 }
@@ -1222,16 +1217,18 @@
          * wallpaper to be shown behind it.
          */
         for (int i = mStacks.indexOf(this) + 1; i < mStacks.size(); i++) {
-            ActivityStack stack = mStacks.get(i);
-            // stack above isn't full screen, so, we assume we're still visible. at some point
-            // we should look at the stack bounds to see if we're occluded even if the stack
-            // isn't fullscreen
+            final ActivityStack stack = mStacks.get(i);
+            // stack above isn't fullscreen, so, we assume we're still visible.
             if (!stack.mFullscreen) {
                 continue;
             }
             final ArrayList<TaskRecord> tasks = stack.getAllTasks();
             for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
                 final TaskRecord task = tasks.get(taskNdx);
+                // task above isn't fullscreen, so, we assume we're still visible.
+                if (!task.mFullscreen) {
+                    continue;
+                }
                 final ArrayList<ActivityRecord> activities = task.mActivities;
                 for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                     final ActivityRecord r = activities.get(activityNdx);
@@ -1285,6 +1282,10 @@
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
             final ArrayList<ActivityRecord> activities = task.mActivities;
+            // Set to true if an activity in this task is fullscreen thereby hiding other
+            // activities in the same task. Initialized to the same value as behindFullscreen
+            // which represent if the entire task/stack is behind another fullscreen task/stack.
+            boolean behindFullscreenActivity = behindFullscreen;
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord r = activities.get(activityNdx);
                 if (r.finishing) {
@@ -1296,7 +1297,7 @@
                 aboveTop = false;
                 // mLaunchingBehind: Activities launching behind are at the back of the task stack
                 // but must be drawn initially for the animation as though they were visible.
-                if (!behindFullscreen || r.mLaunchTaskBehind) {
+                if (!behindFullscreenActivity || r.mLaunchTaskBehind) {
                     if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
                             "Make visible? " + r + " finishing=" + r.finishing
                             + " state=" + r.state);
@@ -1379,17 +1380,22 @@
                     configChanges |= r.configChangeFlags;
 
                     if (r.fullscreen) {
-                        // At this point, nothing else needs to be shown
-                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r);
-                        behindFullscreen = true;
+                        // At this point, nothing else needs to be shown in this task.
+                        behindFullscreenActivity = true;
+                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
+                                + " behindFullscreen=" + behindFullscreen
+                                + " behindFullscreenActivity=" + behindFullscreenActivity);
                     } else if (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()) {
-                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Showing home: at " + r);
-                        behindFullscreen = true;
+                        behindFullscreenActivity = true;
+                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Showing home: at " + r
+                                + " behindFullscreen=" + behindFullscreen
+                                + " behindFullscreenActivity=" + behindFullscreenActivity);
                     }
                 } else {
                     if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
-                        "Make invisible? " + r + " finishing=" + r.finishing
-                        + " state=" + r.state + " behindFullscreen=" + behindFullscreen);
+                            "Make invisible? " + r + " finishing=" + r.finishing
+                            + " state=" + r.state + " behindFullscreen=" + behindFullscreen
+                            + " behindFullscreenActivity=" + behindFullscreenActivity);
                     // Now for any activities that aren't visible to the user, make
                     // sure they no longer are keeping the screen frozen.
                     if (r.visible) {
@@ -1436,6 +1442,9 @@
                     }
                 }
             }
+            // Factoring if the previous task is fullscreen there by affecting the visibility of
+            // task behind it.
+            behindFullscreen |= task.mFullscreen;
         }
 
         if (mTranslucentActivityWaiting != null &&
@@ -3818,29 +3827,12 @@
         if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                 "Ensuring correct configuration: " + r);
 
-        // Make sure the current stack override configuration is supported by the top task
-        // before continuing.
-        final TaskRecord topTask = topTask();
-        if (topTask != null && ((topTask.mResizeable && mForcedFullscreen)
-                    || (!topTask.mResizeable && !mFullscreen))) {
-            final boolean prevFullscreen = mFullscreen;
-            final Configuration newOverrideConfig =
-                    mWindowManager.forceStackToFullscreen(mStackId, !topTask.mResizeable);
-            updateOverrideConfiguration(newOverrideConfig);
-            mForcedFullscreen = !prevFullscreen && mFullscreen;
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Updated stack config to support task=" + topTask
-                            + " resizeable=" + topTask.mResizeable
-                            + " mForcedFullscreen=" + mForcedFullscreen
-                            + " prevFullscreen=" + prevFullscreen
-                            + " mFullscreen=" + mFullscreen);
-        }
-
         // Short circuit: if the two configurations are the exact same
         // object (the common case), then there is nothing to do.
-        Configuration newConfig = mService.mConfiguration;
+        final Configuration newConfig = mService.mConfiguration;
+        final Configuration taskConfig = r.task.mOverrideConfig;
         if (r.configuration == newConfig
-                && r.stackConfigOverride == mOverrideConfig
+                && r.taskConfigOverride == taskConfig
                 && !r.forceNewConfig) {
             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                     "Configuration unchanged in " + r);
@@ -3858,30 +3850,30 @@
         // Okay we now are going to make this activity have the new config.
         // But then we need to figure out how it needs to deal with that.
         final Configuration oldConfig = r.configuration;
-        final Configuration oldStackOverride = r.stackConfigOverride;
+        final Configuration oldTaskOverride = r.taskConfigOverride;
         r.configuration = newConfig;
-        r.stackConfigOverride = mOverrideConfig;
+        r.taskConfigOverride = taskConfig;
 
         // Determine what has changed.  May be nothing, if this is a config
         // that has come back from the app after going idle.  In that case
         // we just want to leave the official config object now in the
         // activity and do nothing else.
-        int stackChanges = oldStackOverride.diff(mOverrideConfig);
-        if (stackChanges == 0) {
+        int taskChanges = oldTaskOverride.diff(taskConfig);
+        if (taskChanges == 0) {
             // {@link Configuration#diff} doesn't catch changes from unset values.
             // Check for changes we care about.
-            if (oldStackOverride.orientation != mOverrideConfig.orientation) {
-                stackChanges |= ActivityInfo.CONFIG_ORIENTATION;
+            if (oldTaskOverride.orientation != taskConfig.orientation) {
+                taskChanges |= ActivityInfo.CONFIG_ORIENTATION;
             }
-            if (oldStackOverride.screenHeightDp != mOverrideConfig.screenHeightDp
-                    || oldStackOverride.screenWidthDp != mOverrideConfig.screenWidthDp) {
-                stackChanges |= ActivityInfo.CONFIG_SCREEN_SIZE;
+            if (oldTaskOverride.screenHeightDp != taskConfig.screenHeightDp
+                    || oldTaskOverride.screenWidthDp != taskConfig.screenWidthDp) {
+                taskChanges |= ActivityInfo.CONFIG_SCREEN_SIZE;
             }
-            if (oldStackOverride.smallestScreenWidthDp != mOverrideConfig.smallestScreenWidthDp) {
-                stackChanges |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+            if (oldTaskOverride.smallestScreenWidthDp != taskConfig.smallestScreenWidthDp) {
+                taskChanges |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
             }
         }
-        final int changes = oldConfig.diff(newConfig) | stackChanges;
+        final int changes = oldConfig.diff(newConfig) | taskChanges;
         if (changes == 0 && !r.forceNewConfig) {
             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                     "Configuration no differences in " + r);
@@ -3943,14 +3935,14 @@
         }
 
         // Default case: the activity can handle this new configuration, so hand it over.
-        // NOTE: We only forward the stack override configuration as the system level configuration
+        // NOTE: We only forward the task override configuration as the system level configuration
         // changes is always sent to all processes when they happen so it can just use whatever
         // system level configuration it last got.
         if (r.app != null && r.app.thread != null) {
             try {
                 if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending new config to " + r);
                 r.app.thread.scheduleActivityConfigurationChanged(
-                        r.appToken, new Configuration(mOverrideConfig));
+                        r.appToken, new Configuration(taskConfig));
             } catch (RemoteException e) {
                 // If process died, whatever.
             }
@@ -3984,7 +3976,7 @@
             r.forceNewConfig = false;
             r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes,
                     !andResume, new Configuration(mService.mConfiguration),
-                    new Configuration(mOverrideConfig));
+                    new Configuration(r.task.mOverrideConfig));
             // Note: don't need to call pauseIfSleepingLocked() here, because
             // the caller will only pass in 'andResume' if this activity is
             // currently resumed, which implies we aren't sleeping.
@@ -4381,16 +4373,6 @@
                 + " stackId=" + mStackId + ", " + mTaskHistory.size() + " tasks}";
     }
 
-    boolean updateOverrideConfiguration(Configuration newConfig) {
-        Configuration oldConfig = mOverrideConfig;
-        mOverrideConfig = (newConfig == null) ? Configuration.EMPTY : newConfig;
-        // We override the configuration only when the stack's dimensions are different from
-        // the display. In this manner, we know that if the override configuration is empty,
-        // the stack is necessarily full screen.
-        mFullscreen = Configuration.EMPTY.equals(mOverrideConfig);
-        return !mOverrideConfig.equals(oldConfig);
-    }
-
     void onLockTaskPackagesUpdatedLocked() {
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             mTaskHistory.get(taskNdx).setLockTaskAuth();
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index b1ac7ee..e7f28c5 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -99,6 +99,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.EventLog;
+import android.util.IntArray;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -1283,7 +1284,7 @@
             app.forceProcessStateUpTo(mService.mTopProcessState);
             app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                     System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
-                    new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage,
+                    new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage,
                     task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
                     newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
 
@@ -2871,9 +2872,54 @@
             return;
         }
 
-        final Configuration overrideConfig = mWindowManager.resizeStack(stackId, bounds);
-        if (stack.updateOverrideConfiguration(overrideConfig)) {
+        final IntArray changedTaskIds = new IntArray(stack.numTasks());
+        final List<Configuration> newTaskConfigs = new ArrayList<>(stack.numTasks());
+        stack.mFullscreen =
+                mWindowManager.resizeStack(stackId, bounds, changedTaskIds, newTaskConfigs);
+        for (int i = changedTaskIds.size() - 1; i >= 0; i--) {
+            final TaskRecord task = anyTaskForIdLocked(changedTaskIds.get(i), false);
+            if (task == null) {
+                Slog.wtf(TAG, "Task in WindowManager, but not in ActivityManager???");
+                continue;
+            }
+            task.updateOverrideConfiguration(newTaskConfigs.get(i));
+        }
+
+        if (r != null) {
+            final boolean updated = stack.ensureActivityConfigurationLocked(r, 0);
+            // And we need to make sure at this point that all other activities
+            // are made visible with the correct configuration.
+            ensureActivitiesVisibleLocked(r, 0);
+            if (!updated) {
+                resumeTopActivitiesLocked(stack, null, null);
+            }
+        }
+    }
+
+    void resizeTaskLocked(TaskRecord task, Rect bounds) {
+        if (!task.mResizeable) {
+            Slog.w(TAG, "resizeTask: task " + task + " not resizeable.");
+            return;
+        }
+
+        if (task.mBounds != null && task.mBounds.equals(bounds)) {
+            // Nothing to do here...
+            return;
+        }
+
+        task.mBounds = new Rect(bounds);
+
+        if (!mWindowManager.isValidTaskId(task.taskId)) {
+            // Task doesn't exist in window manager yet (e.g. was restored from recents).
+            // No need to do anything else until we add the task to window manager.
+            return;
+        }
+
+        final Configuration overrideConfig = mWindowManager.resizeTask(task.taskId, bounds);
+        if (task.updateOverrideConfiguration(overrideConfig)) {
+            ActivityRecord r = task.topRunningActivityLocked(null);
             if (r != null) {
+                final ActivityStack stack = task.stack;
                 final boolean updated = stack.ensureActivityConfigurationLocked(r, 0);
                 // And we need to make sure at this point that all other activities
                 // are made visible with the correct configuration.
@@ -2885,48 +2931,6 @@
         }
     }
 
-    /** Makes sure the input task is in a stack with the specified bounds by either resizing the
-     * current task stack if it only has one entry, moving the task to a stack that matches the
-     * bounds, or creating a new stack with the required bounds. Also, makes the task resizeable.*/
-    void resizeTaskLocked(TaskRecord task, Rect bounds) {
-        task.mResizeable = true;
-        final ActivityStack currentStack = task.stack;
-        if (currentStack.isHomeStack()) {
-            // Can't move task off the home stack. Sorry!
-            return;
-        }
-
-        final int matchingStackId = mWindowManager.getStackIdWithBounds(bounds);
-        if (matchingStackId != -1) {
-            // There is already a stack with the right bounds!
-            if (currentStack != null && currentStack.mStackId == matchingStackId) {
-                // Nothing to do here. Already in the right stack...
-                return;
-            }
-            // Move task to stack with matching bounds.
-            moveTaskToStackLocked(task.taskId, matchingStackId, true);
-            return;
-        }
-
-        if (currentStack != null && currentStack.numTasks() == 1) {
-            // Just resize the current stack since this is the task in it.
-            resizeStackLocked(currentStack.mStackId, bounds);
-            return;
-        }
-
-        // Create new stack and move the task to it.
-        final int displayId = (currentStack != null && currentStack.mDisplayId != -1)
-                ? currentStack.mDisplayId : Display.DEFAULT_DISPLAY;
-        ActivityStack newStack = createStackOnDisplay(getNextStackId(), displayId);
-
-        if (newStack == null) {
-            Slog.e(TAG, "resizeTaskLocked: Can't create stack for task=" + task);
-            return;
-        }
-        moveTaskToStackLocked(task.taskId, newStack.mStackId, true);
-        resizeStackLocked(newStack.mStackId, bounds);
-    }
-
     ActivityStack createStackOnDisplay(int stackId, int displayId) {
         ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
         if (activityDisplay == null) {
@@ -2962,7 +2966,7 @@
             final ArrayList<ActivityStack> homeDisplayStacks = mHomeStack.mStacks;
             for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack tmpStack = homeDisplayStacks.get(stackNdx);
-                if (!tmpStack.isHomeStack() && tmpStack.mFullscreen) {
+                if (!tmpStack.isHomeStack()) {
                     stack = tmpStack;
                     break;
                 }
@@ -3789,6 +3793,7 @@
         final int numTasks = tasks.size();
         int[] taskIds = new int[numTasks];
         String[] taskNames = new String[numTasks];
+        Rect[] taskBounds = new Rect[numTasks];
         for (int i = 0; i < numTasks; ++i) {
             final TaskRecord task = tasks.get(i);
             taskIds[i] = task.taskId;
@@ -3796,6 +3801,8 @@
                     : task.realActivity != null ? task.realActivity.flattenToString()
                     : task.getTopActivity() != null ? task.getTopActivity().packageName
                     : "unknown";
+            taskBounds[i] = new Rect();
+            mWindowManager.getTaskBounds(task.taskId, taskBounds[i]);
         }
         info.taskIds = taskIds;
         info.taskNames = taskNames;
@@ -3811,7 +3818,7 @@
     }
 
     ArrayList<StackInfo> getAllStackInfosLocked() {
-        ArrayList<StackInfo> list = new ArrayList<StackInfo>();
+        ArrayList<StackInfo> list = new ArrayList<>();
         for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
             ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
             for (int ndx = stacks.size() - 1; ndx >= 0; --ndx) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 7e2ad29..a892c7d 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -40,7 +40,9 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.os.Debug;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -92,6 +94,7 @@
     private static final String ATTR_CALLING_PACKAGE = "calling_package";
     private static final String ATTR_RESIZEABLE = "resizeable";
     private static final String ATTR_PRIVILEGED = "privileged";
+    private static final String ATTR_BOUNDS = "bounds";
 
     private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail";
 
@@ -199,6 +202,14 @@
 
     final ActivityManagerService mService;
 
+    // Whether or not this task covers the entire screen; by default tasks are fullscreen.
+    boolean mFullscreen = true;
+
+    // Bounds of the Task. null for fullscreen tasks.
+    Rect mBounds = null;
+
+    Configuration mOverrideConfig = Configuration.EMPTY;
+
     TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
             IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
         mService = service;
@@ -252,7 +263,8 @@
             long _firstActiveTime, long _lastActiveTime, long lastTimeMoved,
             boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription,
             int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor,
-            int callingUid, String callingPackage, boolean resizeable, boolean privileged) {
+            int callingUid, String callingPackage, boolean resizeable, boolean privileged,
+            Rect bounds) {
         mService = service;
         mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
                 TaskPersister.IMAGE_EXTENSION;
@@ -289,6 +301,7 @@
         mCallingPackage = callingPackage;
         mResizeable = resizeable;
         mPrivileged = privileged;
+        mBounds = bounds;
     }
 
     void touchActiveTime() {
@@ -950,6 +963,9 @@
         out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
         out.attribute(null, ATTR_RESIZEABLE, String.valueOf(mResizeable));
         out.attribute(null, ATTR_PRIVILEGED, String.valueOf(mPrivileged));
+        if (mBounds != null) {
+            out.attribute(null, ATTR_BOUNDS, mBounds.flattenToString());
+        }
 
         if (affinityIntent != null) {
             out.startTag(null, TAG_AFFINITYINTENT);
@@ -1010,6 +1026,7 @@
         String callingPackage = "";
         boolean resizeable = false;
         boolean privileged = false;
+        Rect bounds = null;
 
         for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
             final String attrName = in.getAttributeName(attrNdx);
@@ -1067,6 +1084,8 @@
                 resizeable = Boolean.valueOf(attrValue);
             } else if (ATTR_PRIVILEGED.equals(attrName)) {
                 privileged = Boolean.valueOf(attrValue);
+            } else if (ATTR_BOUNDS.equals(attrName)) {
+                bounds = Rect.unflattenFromString(attrValue);
             } else {
                 Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
             }
@@ -1126,7 +1145,7 @@
                 autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription,
                 activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
                 taskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor,
-                callingUid, callingPackage, resizeable, privileged);
+                callingUid, callingPackage, resizeable, privileged, bounds);
 
         for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
             activities.get(activityNdx).task = task;
@@ -1136,6 +1155,16 @@
         return task;
     }
 
+    boolean updateOverrideConfiguration(Configuration newConfig) {
+        Configuration oldConfig = mOverrideConfig;
+        mOverrideConfig = (newConfig == null) ? Configuration.EMPTY : newConfig;
+        // We override the configuration only when the task's dimensions are different from the
+        // display. In this manner, we know that if the override configuration is empty, the task
+        // is necessarily fullscreen.
+        mFullscreen = Configuration.EMPTY.equals(mOverrideConfig);
+        return !mOverrideConfig.equals(oldConfig);
+    }
+
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("userId="); pw.print(userId);
                 pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index e385be3..538d6b84 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -29,9 +29,6 @@
     private static final String TAG = "DimLayer";
     private static final boolean DEBUG = false;
 
-    /** Reference to the owner of this object. */
-    final DisplayContent mDisplayContent;
-
     /** Actual surface that dims */
     SurfaceControl mDimSurface;
 
@@ -62,13 +59,18 @@
     /** Time in milliseconds to take to transition from mStartAlpha to mTargetAlpha */
     long mDuration;
 
-    /** Owning stack */
-    final TaskStack mStack;
+    /** Interface implemented by users of the dim layer */
+    interface DimLayerUser {
+        /** Returns true if the user of the dim layer is fullscreen. */
+        boolean isFullscreen();
+        /** Returns the display info. of the dim layer user. */
+        DisplayInfo getDisplayInfo();
+    }
+    /** The user of this dim layer. */
+    final DimLayerUser mUser;
 
-    DimLayer(WindowManagerService service, TaskStack stack, DisplayContent displayContent) {
-        mStack = stack;
-        mDisplayContent = displayContent;
-        final int displayId = mDisplayContent.getDisplayId();
+    DimLayer(WindowManagerService service, DimLayerUser user, int displayId) {
+        mUser = user;
         if (DEBUG) Slog.v(TAG, "Ctor: displayId=" + displayId);
         SurfaceControl.openTransaction();
         try {
@@ -145,14 +147,14 @@
     private void adjustBounds() {
         final int dw, dh;
         final float xPos, yPos;
-        if (!mStack.isFullscreen()) {
+        if (!mUser.isFullscreen()) {
             dw = mBounds.width();
             dh = mBounds.height();
             xPos = mBounds.left;
             yPos = mBounds.top;
         } else {
             // Set surface size to screen size.
-            final DisplayInfo info = mDisplayContent.getDisplayInfo();
+            final DisplayInfo info = mUser.getDisplayInfo();
             // Multiply by 1.5 so that rotating a frozen surface that includes this does not expose
             // a corner.
             dw = (int) (info.logicalWidth * 1.5);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5d6df26..d9786c8 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -89,8 +89,8 @@
      * (except a future lockscreen TaskStack) moves to the top. */
     private TaskStack mHomeStack = null;
 
-    /** Detect user tapping outside of current focused stack bounds .*/
-    StackTapPointerEventListener mTapDetector;
+    /** Detect user tapping outside of current focused task bounds .*/
+    TaskTapPointerEventListener mTapDetector;
 
     /** Detect user tapping outside of current focused stack bounds .*/
     Region mTouchExcludeRegion = new Region();
@@ -219,24 +219,27 @@
         mContentRect.set(contentRect);
     }
 
-    int stackIdFromPoint(int x, int y) {
+    int taskIdFromPoint(int x, int y) {
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final TaskStack stack = mStacks.get(stackNdx);
-            stack.getBounds(mTmpRect);
-            if (mTmpRect.contains(x, y)) {
-                return stack.mStackId;
+            final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
+            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+                final Task task = tasks.get(taskNdx);
+                task.getBounds(mTmpRect);
+                if (mTmpRect.contains(x, y)) {
+                    return task.mTaskId;
+                }
             }
         }
         return -1;
     }
 
-    void setTouchExcludeRegion(TaskStack focusedStack) {
+    void setTouchExcludeRegion(Task focusedTask) {
         mTouchExcludeRegion.set(mBaseDisplayRect);
         WindowList windows = getWindowList();
         for (int i = windows.size() - 1; i >= 0; --i) {
             final WindowState win = windows.get(i);
-            final TaskStack stack = win.getStack();
-            if (win.isVisibleLw() && stack != null && stack != focusedStack) {
+            final Task task = win.getTask();
+            if (win.isVisibleLw() && task != null && task != focusedTask) {
                 mTmpRect.set(win.mVisibleFrame);
                 // If no intersection, we need mTmpRect to be unmodified.
                 mTmpRect.intersect(win.mVisibleInsets);
@@ -273,21 +276,37 @@
     boolean animateDimLayers() {
         boolean result = false;
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            result |= mStacks.get(stackNdx).animateDimLayers();
+            final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
+            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+                final Task task = tasks.get(taskNdx);
+                result |= task.animateDimLayers();
+                if (task.isFullscreen()) {
+                    // No point in continuing as this task covers the entire screen.
+                    // Also, fullscreen tasks all share the same dim layer, so we don't want
+                    // processing of fullscreen task below this one affecting the dim layer state.
+                    return result;
+                }
+            }
         }
         return result;
     }
 
     void resetDimming() {
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            mStacks.get(stackNdx).resetDimmingTag();
+            final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
+            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+                tasks.get(taskNdx).clearContinueDimming();
+            }
         }
     }
 
     boolean isDimming() {
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            if (mStacks.get(stackNdx).isDimming()) {
-                return true;
+            final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
+            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+                if (tasks.get(taskNdx).isDimming()) {
+                    return true;
+                }
             }
         }
         return false;
@@ -295,7 +314,10 @@
 
     void stopDimmingIfNeeded() {
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            mStacks.get(stackNdx).stopDimmingIfNeeded();
+            final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
+            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+                tasks.get(taskNdx).stopDimmingIfNeeded();
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 1a125d4..222945c 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -413,7 +413,7 @@
                 continue;
             }
 
-            child.getStackBounds(mTmpRect);
+            child.getTaskBounds(mTmpRect);
             if (!mTmpRect.contains(x, y)) {
                 // outside of this window's activity stack == don't tell about drags
                 continue;
diff --git a/services/core/java/com/android/server/wm/FocusedStackFrame.java b/services/core/java/com/android/server/wm/FocusedTaskFrame.java
similarity index 91%
rename from services/core/java/com/android/server/wm/FocusedStackFrame.java
rename to services/core/java/com/android/server/wm/FocusedTaskFrame.java
index 826fe97..d8e1095 100644
--- a/services/core/java/com/android/server/wm/FocusedStackFrame.java
+++ b/services/core/java/com/android/server/wm/FocusedTaskFrame.java
@@ -33,8 +33,8 @@
 
 import com.android.server.wm.WindowStateAnimator.SurfaceTrace;
 
-class FocusedStackFrame {
-    private static final String TAG = "FocusedStackFrame";
+class FocusedTaskFrame {
+    private static final String TAG = "FocusedTaskFrame";
     private static final boolean DEBUG = false;
     private static final int THICKNESS = 2;
     private static final float ALPHA = 0.3f;
@@ -47,14 +47,14 @@
     private final Rect mLastBounds = new Rect();
     private int mLayer = -1;
 
-    public FocusedStackFrame(Display display, SurfaceSession session) {
+    public FocusedTaskFrame(Display display, SurfaceSession session) {
         SurfaceControl ctrl = null;
         try {
             if (DEBUG_SURFACE_TRACE) {
-                ctrl = new SurfaceTrace(session, "FocusedStackFrame",
+                ctrl = new SurfaceTrace(session, "FocusedTaskFrame",
                     1, 1, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
             } else {
-                ctrl = new SurfaceControl(session, "FocusedStackFrame",
+                ctrl = new SurfaceControl(session, "FocusedTaskFrame",
                     1, 1, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
             }
             ctrl.setLayerStack(display.getLayerStack());
@@ -122,11 +122,11 @@
         }
     }
 
-    void setVisibility(TaskStack stack) {
-        if (stack == null || stack.isFullscreen()) {
+    void setVisibility(Task task) {
+        if (task == null || task.isFullscreen()) {
             setupSurface(false);
         } else {
-            stack.getBounds(mBounds);
+            task.getBounds(mBounds);
             setupSurface(true);
             if (!mBounds.equals(mLastBounds)) {
                 draw();
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index ae442e5..21e92c9 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -177,7 +177,7 @@
         if (modal && child.mAppToken != null) {
             // Limit the outer touch to the activity stack region.
             flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-            child.getStackBounds(mTmpRect);
+            child.getTaskBounds(mTmpRect);
             inputWindowHandle.touchableRegion.set(mTmpRect);
         } else {
             // Not modal or full screen modal
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 0c3cf65..09d70d8 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -17,13 +17,29 @@
 package com.android.server.wm;
 
 import static com.android.server.wm.WindowManagerService.TAG;
+import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
 import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
 
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TypedValue;
+import android.view.DisplayInfo;
+import android.view.Surface;
+
 import com.android.server.EventLogTags;
 
-class Task {
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+class Task implements DimLayer.DimLayerUser {
+    /** Amount of time in milliseconds to animate the dim surface from one value to another,
+     * when no window animation is driving it. */
+    private static final int DEFAULT_DIM_DURATION = 200;
+
     TaskStack mStack;
     final AppTokenList mAppTokens = new AppTokenList();
     final int mTaskId;
@@ -31,11 +47,41 @@
     boolean mDeferRemoval = false;
     final WindowManagerService mService;
 
+    // Content limits relative to the DisplayContent this sits in.
+    private Rect mBounds = new Rect();
+
+    // Device rotation as of the last time {@link #mBounds} was set.
+    int mRotation;
+
+    // Whether mBounds is fullscreen
+    private boolean mFullscreen = true;
+
+    // Contains configurations settings that are different from the global configuration due to
+    // stack specific operations. E.g. {@link #setBounds}.
+    Configuration mOverrideConfig;
+
+    // For comparison with DisplayContent bounds.
+    private Rect mTmpRect = new Rect();
+    // For handling display rotations.
+    private Rect mTmpRect2 = new Rect();
+
+    // The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer.
+    WindowStateAnimator mDimWinAnimator;
+    // Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
+    private DimLayer mDimLayer;
+    // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the end
+    // then stop any dimming.
+    private boolean mContinueDimming;
+    // Shared dim layer for fullscreen tasks. {@link #mDimLayer} will point to this instead
+    // of creating a new object per fullscreen task on a display.
+    private static final SparseArray<DimLayer> sSharedFullscreenDimLayers = new SparseArray<>();
+
     Task(int taskId, TaskStack stack, int userId, WindowManagerService service) {
         mTaskId = taskId;
         mStack = stack;
         mUserId = userId;
         mService = service;
+        mOverrideConfig = Configuration.EMPTY;
     }
 
     DisplayContent getDisplayContent() {
@@ -107,13 +153,290 @@
         }
     }
 
+    /** Set the task bounds. Passing in null sets the bounds to fullscreen. */
+    boolean setBounds(Rect bounds) {
+        boolean oldFullscreen = mFullscreen;
+        int rotation = Surface.ROTATION_0;
+        final DisplayContent displayContent = mStack.getDisplayContent();
+        if (displayContent != null) {
+            displayContent.getLogicalDisplayRect(mTmpRect);
+            rotation = displayContent.getDisplayInfo().rotation;
+            if (bounds == null) {
+                bounds = mTmpRect;
+                mFullscreen = true;
+            } else {
+                // ensure bounds are entirely within the display rect
+                if (!bounds.intersect(mTmpRect)) {
+                    // Can't set bounds outside the containing display...Sorry!
+                    return false;
+                }
+                mFullscreen = mTmpRect.equals(bounds);
+            }
+        }
+
+        if (bounds == null) {
+            // Can't set to fullscreen if we don't have a display to get bounds from...
+            return false;
+        }
+        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
+            return false;
+        }
+
+        mBounds.set(bounds);
+        mRotation = rotation;
+        updateDimLayer();
+        updateOverrideConfiguration();
+        return true;
+    }
+
+    void getBounds(Rect out) {
+        out.set(mBounds);
+    }
+
+    private void updateOverrideConfiguration() {
+        final Configuration serviceConfig = mService.mCurConfiguration;
+        if (mFullscreen) {
+            mOverrideConfig = Configuration.EMPTY;
+            return;
+        }
+
+        if (mOverrideConfig == Configuration.EMPTY) {
+            mOverrideConfig  = new Configuration();
+        }
+
+        // TODO(multidisplay): Update Dp to that of display stack is on.
+        final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+        mOverrideConfig.screenWidthDp =
+                Math.min((int)(mBounds.width() / density), serviceConfig.screenWidthDp);
+        mOverrideConfig.screenHeightDp =
+                Math.min((int)(mBounds.height() / density), serviceConfig.screenHeightDp);
+        mOverrideConfig.smallestScreenWidthDp =
+                Math.min(mOverrideConfig.screenWidthDp, mOverrideConfig.screenHeightDp);
+        mOverrideConfig.orientation =
+                (mOverrideConfig.screenWidthDp <= mOverrideConfig.screenHeightDp)
+                        ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
+    }
+
+    void updateDisplayInfo(final DisplayContent displayContent) {
+        if (displayContent == null) {
+            return;
+        }
+        if (mFullscreen) {
+            setBounds(null);
+            return;
+        }
+        final int newRotation = displayContent.getDisplayInfo().rotation;
+        if (mRotation == newRotation) {
+            return;
+        }
+
+        // Device rotation changed. We don't want the task to move around on the screen when
+        // this happens, so update the task bounds so it stays in the same place.
+        final int rotationDelta = DisplayContent.deltaRotation(mRotation, newRotation);
+        displayContent.getLogicalDisplayRect(mTmpRect);
+        switch (rotationDelta) {
+            case Surface.ROTATION_0:
+                mTmpRect2.set(mBounds);
+                break;
+            case Surface.ROTATION_90:
+                mTmpRect2.top = mTmpRect.bottom - mBounds.right;
+                mTmpRect2.left = mBounds.top;
+                mTmpRect2.right = mTmpRect2.left + mBounds.height();
+                mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
+                break;
+            case Surface.ROTATION_180:
+                mTmpRect2.top = mTmpRect.bottom - mBounds.bottom;
+                mTmpRect2.left = mTmpRect.right - mBounds.right;
+                mTmpRect2.right = mTmpRect2.left + mBounds.width();
+                mTmpRect2.bottom = mTmpRect2.top + mBounds.height();
+                break;
+            case Surface.ROTATION_270:
+                mTmpRect2.top = mBounds.left;
+                mTmpRect2.left = mTmpRect.right - mBounds.bottom;
+                mTmpRect2.right = mTmpRect2.left + mBounds.height();
+                mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
+                break;
+        }
+        setBounds(mTmpRect2);
+    }
+
+    /** Updates the dim layer bounds, recreating it if needed. */
+    private void updateDimLayer() {
+        DimLayer newDimLayer;
+        final boolean previousFullscreen =
+                mDimLayer != null && sSharedFullscreenDimLayers.indexOfValue(mDimLayer) > -1;
+        final int displayId = mStack.getDisplayContent().getDisplayId();
+        if (mFullscreen) {
+            if (previousFullscreen) {
+                // Nothing to do here...
+                return;
+            }
+            // Use shared fullscreen dim layer
+            newDimLayer = sSharedFullscreenDimLayers.get(displayId);
+            if (newDimLayer == null) {
+                if (mDimLayer != null) {
+                    // Re-purpose the previous dim layer.
+                    newDimLayer = mDimLayer;
+                } else {
+                    // Create new full screen dim layer.
+                    newDimLayer = new DimLayer(mService, this, displayId);
+                }
+                newDimLayer.setBounds(mBounds);
+                sSharedFullscreenDimLayers.put(displayId, newDimLayer);
+            } else if (mDimLayer != null) {
+                mDimLayer.destroySurface();
+            }
+        } else {
+            newDimLayer = (mDimLayer == null || previousFullscreen)
+                    ? new DimLayer(mService, this, displayId) : mDimLayer;
+            newDimLayer.setBounds(mBounds);
+        }
+        mDimLayer = newDimLayer;
+    }
+
+    boolean animateDimLayers() {
+        final int dimLayer;
+        final float dimAmount;
+        if (mDimWinAnimator == null) {
+            dimLayer = mDimLayer.getLayer();
+            dimAmount = 0;
+        } else {
+            dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
+            dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
+        }
+        final float targetAlpha = mDimLayer.getTargetAlpha();
+        if (targetAlpha != dimAmount) {
+            if (mDimWinAnimator == null) {
+                mDimLayer.hide(DEFAULT_DIM_DURATION);
+            } else {
+                long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
+                        ? mDimWinAnimator.mAnimation.computeDurationHint()
+                        : DEFAULT_DIM_DURATION;
+                if (targetAlpha > dimAmount) {
+                    duration = getDimBehindFadeDuration(duration);
+                }
+                mDimLayer.show(dimLayer, dimAmount, duration);
+            }
+        } else if (mDimLayer.getLayer() != dimLayer) {
+            mDimLayer.setLayer(dimLayer);
+        }
+        if (mDimLayer.isAnimating()) {
+            if (!mService.okToDisplay()) {
+                // Jump to the end of the animation.
+                mDimLayer.show();
+            } else {
+                return mDimLayer.stepAnimation();
+            }
+        }
+        return false;
+    }
+
+    private long getDimBehindFadeDuration(long duration) {
+        TypedValue tv = new TypedValue();
+        mService.mContext.getResources().getValue(
+                com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
+        if (tv.type == TypedValue.TYPE_FRACTION) {
+            duration = (long)tv.getFraction(duration, duration);
+        } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
+            duration = tv.data;
+        }
+        return duration;
+    }
+
+    void clearContinueDimming() {
+        mContinueDimming = false;
+    }
+
+    void setContinueDimming() {
+        mContinueDimming = true;
+    }
+
+    boolean getContinueDimming() {
+        return mContinueDimming;
+    }
+
+    boolean isDimming() {
+        return mDimLayer.isDimming();
+    }
+
+    boolean isDimming(WindowStateAnimator winAnimator) {
+        return mDimWinAnimator == winAnimator && isDimming();
+    }
+
+    void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
+        // Only set dim params on the highest dimmed layer.
+        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
+        if (newWinAnimator.mSurfaceShown && (mDimWinAnimator == null
+                || !mDimWinAnimator.mSurfaceShown
+                || mDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
+            mDimWinAnimator = newWinAnimator;
+            if (mDimWinAnimator.mWin.mAppToken == null
+                    && !mFullscreen && mStack.getDisplayContent() != null) {
+                // Dim should cover the entire screen for system windows.
+                mStack.getDisplayContent().getLogicalDisplayRect(mTmpRect);
+                mDimLayer.setBounds(mTmpRect);
+            }
+        }
+    }
+
+    void stopDimmingIfNeeded() {
+        if (!mContinueDimming && isDimming()) {
+            mDimWinAnimator = null;
+            mDimLayer.setBounds(mBounds);
+        }
+    }
+
+    void close() {
+        if (mDimLayer != null) {
+            mDimLayer.destroySurface();
+            mDimLayer = null;
+        }
+    }
+
+    void resizeWindows() {
+        final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
+        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
+            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
+            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+                final WindowState win = windows.get(winNdx);
+                if (!resizingWindows.contains(win)) {
+                    if (DEBUG_RESIZE) Slog.d(TAG, "setBounds: Resizing " + win);
+                    resizingWindows.add(win);
+                }
+            }
+        }
+    }
+
     boolean showForAllUsers() {
         final int tokensCount = mAppTokens.size();
         return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
     }
 
     @Override
+    public boolean isFullscreen() {
+        return mFullscreen;
+    }
+
+    @Override
+    public DisplayInfo getDisplayInfo() {
+        return mStack.getDisplayContent().getDisplayInfo();
+    }
+
+    @Override
     public String toString() {
         return "{taskId=" + mTaskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}";
     }
+
+    public void printTo(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.print("taskId="); pw.print(mTaskId);
+                pw.print(prefix); pw.print("appTokens="); pw.print(mAppTokens);
+                pw.print(prefix); pw.print("mdr="); pw.println(mDeferRemoval);
+        if (mDimLayer.isDimming()) {
+            pw.print(prefix); pw.println("mDimLayer:");
+            mDimLayer.printTo(prefix + " ", pw);
+            pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
+        } else {
+            pw.println();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 4545032..90a173a 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -22,22 +22,18 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Debug;
-import android.util.DisplayMetrics;
 import android.util.EventLog;
+import android.util.IntArray;
 import android.util.Slog;
-import android.util.TypedValue;
-import android.view.Surface;
+import android.view.DisplayInfo;
 
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 
-public class TaskStack {
-    /** Amount of time in milliseconds to animate the dim surface from one value to another,
-     * when no window animation is driving it. */
-    private static final int DEFAULT_DIM_DURATION = 200;
-
+public class TaskStack implements DimLayer.DimLayerUser {
     /** Unique identifier */
     final int mStackId;
 
@@ -49,12 +45,10 @@
 
     /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match
      * mTaskHistory in the ActivityStack with the same mStackId */
-    private final ArrayList<Task> mTasks = new ArrayList<Task>();
+    private final ArrayList<Task> mTasks = new ArrayList<>();
 
     /** For comparison with DisplayContent bounds. */
     private Rect mTmpRect = new Rect();
-    /** For handling display rotations. */
-    private Rect mTmpRect2 = new Rect();
 
     /** Content limits relative to the DisplayContent this sits in. */
     private Rect mBounds = new Rect();
@@ -62,49 +56,22 @@
     /** Whether mBounds is fullscreen */
     private boolean mFullscreen = true;
 
-    /** Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND} */
-    private DimLayer mDimLayer;
-
-    /** The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer. */
-    WindowStateAnimator mDimWinAnimator;
-
     /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
     DimLayer mAnimationBackgroundSurface;
 
     /** The particular window with an Animation with non-zero background color. */
     WindowStateAnimator mAnimationBackgroundAnimator;
 
-    /** Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the end
-     * then stop any dimming. */
-    boolean mDimmingTag;
-
     /** Application tokens that are exiting, but still on screen for animations. */
     final AppTokenList mExitingAppTokens = new AppTokenList();
 
     /** Detach this stack from its display when animation completes. */
     boolean mDeferDetach;
 
-    // Contains configurations settings that are different from the global configuration due to
-    // stack specific operations. E.g. {@link #setBounds}.
-    Configuration mOverrideConfig;
-    // True if the stack was forced to fullscreen disregarding the override configuration.
-    private boolean mForceFullscreen;
-    // The {@link #mBounds} before the stack was forced to fullscreen. Will be restored as the
-    // stack bounds once the stack is no longer forced to fullscreen.
-    final private Rect mPreForceFullscreenBounds;
-
-    // Device rotation as of the last time {@link #mBounds} was set.
-    int mRotation;
-
     TaskStack(WindowManagerService service, int stackId) {
         mService = service;
         mStackId = stackId;
-        mOverrideConfig = Configuration.EMPTY;
-        mForceFullscreen = false;
-        mPreForceFullscreenBounds = new Rect();
-        // TODO: remove bounds from log, they are always 0.
-        EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId, mBounds.left, mBounds.top,
-                mBounds.right, mBounds.bottom);
+        EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId);
     }
 
     DisplayContent getDisplayContent() {
@@ -116,30 +83,39 @@
     }
 
     void resizeWindows() {
-        final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
-                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-                    final WindowState win = windows.get(winNdx);
-                    if (!resizingWindows.contains(win)) {
-                        if (WindowManagerService.DEBUG_RESIZE) Slog.d(TAG,
-                                "setBounds: Resizing " + win);
-                        resizingWindows.add(win);
-                    }
-                }
-            }
+            mTasks.get(taskNdx).resizeWindows();
         }
     }
 
-    /** Set the stack bounds. Passing in null sets the bounds to fullscreen. */
-    boolean setBounds(Rect bounds) {
+    /**
+     * Set the bounds of the stack and its containing tasks.
+     * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen.
+     * @param changedTaskIds Output list of Ids of tasks that changed in bounds.
+     * @param newTaskConfigs Output list of new Configuation of the tasks that changed.
+     * @return True if the stack bounds was changed.
+     * */
+    boolean setBounds(Rect bounds, IntArray changedTaskIds, List<Configuration> newTaskConfigs) {
+        if (!setBounds(bounds)) {
+            return false;
+        }
+
+        // Update bounds of containing tasks.
+        final Rect newBounds = mFullscreen ? null : mBounds;
+        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+            final Task task = mTasks.get(taskNdx);
+            if (task.setBounds(newBounds)) {
+                changedTaskIds.add(task.mTaskId);
+                newTaskConfigs.add(task.mOverrideConfig);
+            }
+        }
+        return true;
+    }
+
+    private boolean setBounds(Rect bounds) {
         boolean oldFullscreen = mFullscreen;
-        int rotation = Surface.ROTATION_0;
         if (mDisplayContent != null) {
             mDisplayContent.getLogicalDisplayRect(mTmpRect);
-            rotation = mDisplayContent.getDisplayInfo().rotation;
             if (bounds == null) {
                 bounds = mTmpRect;
                 mFullscreen = true;
@@ -157,15 +133,12 @@
             // Can't set to fullscreen if we don't have a display to get bounds from...
             return false;
         }
-        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
+        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen) {
             return false;
         }
 
-        mDimLayer.setBounds(bounds);
         mAnimationBackgroundSurface.setBounds(bounds);
         mBounds.set(bounds);
-        mRotation = rotation;
-        updateOverrideConfiguration();
         return true;
     }
 
@@ -173,93 +146,13 @@
         out.set(mBounds);
     }
 
-    private void updateOverrideConfiguration() {
-        final Configuration serviceConfig = mService.mCurConfiguration;
-        if (mFullscreen) {
-            mOverrideConfig = Configuration.EMPTY;
-            return;
-        }
-
-        if (mOverrideConfig == Configuration.EMPTY) {
-            mOverrideConfig  = new Configuration();
-        }
-
-        // TODO(multidisplay): Update Dp to that of display stack is on.
-        final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
-        mOverrideConfig.screenWidthDp =
-                Math.min((int)(mBounds.width() / density), serviceConfig.screenWidthDp);
-        mOverrideConfig.screenHeightDp =
-                Math.min((int)(mBounds.height() / density), serviceConfig.screenHeightDp);
-        mOverrideConfig.smallestScreenWidthDp =
-                Math.min(mOverrideConfig.screenWidthDp, mOverrideConfig.screenHeightDp);
-        mOverrideConfig.orientation =
-                (mOverrideConfig.screenWidthDp <= mOverrideConfig.screenHeightDp)
-                        ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
-    }
-
     void updateDisplayInfo() {
-        if (mFullscreen) {
-            setBounds(null);
-        } else if (mDisplayContent != null) {
-            final int newRotation = mDisplayContent.getDisplayInfo().rotation;
-            if (mRotation == newRotation) {
-                return;
-            }
-
-            // Device rotation changed. We don't want the stack to move around on the screen when
-            // this happens, so update the stack bounds so it stays in the same place.
-            final int rotationDelta = DisplayContent.deltaRotation(mRotation, newRotation);
+        if (mDisplayContent != null) {
             mDisplayContent.getLogicalDisplayRect(mTmpRect);
-            switch (rotationDelta) {
-                case Surface.ROTATION_0:
-                    mTmpRect2.set(mBounds);
-                    break;
-                case Surface.ROTATION_90:
-                    mTmpRect2.top = mTmpRect.bottom - mBounds.right;
-                    mTmpRect2.left = mBounds.top;
-                    mTmpRect2.right = mTmpRect2.left + mBounds.height();
-                    mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
-                    break;
-                case Surface.ROTATION_180:
-                    mTmpRect2.top = mTmpRect.bottom - mBounds.bottom;
-                    mTmpRect2.left = mTmpRect.right - mBounds.right;
-                    mTmpRect2.right = mTmpRect2.left + mBounds.width();
-                    mTmpRect2.bottom = mTmpRect2.top + mBounds.height();
-                    break;
-                case Surface.ROTATION_270:
-                    mTmpRect2.top = mBounds.left;
-                    mTmpRect2.left = mTmpRect.right - mBounds.bottom;
-                    mTmpRect2.right = mTmpRect2.left + mBounds.height();
-                    mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
-                    break;
+            mAnimationBackgroundSurface.setBounds(mTmpRect);
+            for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+                mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent);
             }
-            setBounds(mTmpRect2);
-        }
-    }
-
-    boolean isFullscreen() {
-        return mFullscreen;
-    }
-
-    /** Forces the stack to fullscreen if input is true, else un-forces the stack from fullscreen.
-     * Returns true if something happened.
-     */
-    boolean forceFullscreen(boolean forceFullscreen) {
-        if (mForceFullscreen == forceFullscreen) {
-            return false;
-        }
-        mForceFullscreen = forceFullscreen;
-        if (forceFullscreen) {
-            if (mFullscreen) {
-                return false;
-            }
-            mPreForceFullscreenBounds.set(mBounds);
-            return setBounds(null);
-        } else {
-            if (!mFullscreen || mPreForceFullscreenBounds.isEmpty()) {
-                return false;
-            }
-            return setBounds(mPreForceFullscreenBounds);
         }
     }
 
@@ -313,6 +206,7 @@
         mTasks.add(stackNdx, task);
 
         task.mStack = this;
+        task.updateDisplayInfo(mDisplayContent);
         if (toTop) {
             mDisplayContent.moveStack(this, true);
         }
@@ -361,8 +255,7 @@
         }
 
         mDisplayContent = displayContent;
-        mDimLayer = new DimLayer(mService, this, displayContent);
-        mAnimationBackgroundSurface = new DimLayer(mService, this, displayContent);
+        mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId());
         updateDisplayInfo();
     }
 
@@ -394,98 +287,6 @@
         mAnimationBackgroundSurface.hide();
     }
 
-    private long getDimBehindFadeDuration(long duration) {
-        TypedValue tv = new TypedValue();
-        mService.mContext.getResources().getValue(
-                com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
-        if (tv.type == TypedValue.TYPE_FRACTION) {
-            duration = (long)tv.getFraction(duration, duration);
-        } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
-            duration = tv.data;
-        }
-        return duration;
-    }
-
-    boolean animateDimLayers() {
-        final int dimLayer;
-        final float dimAmount;
-        if (mDimWinAnimator == null) {
-            dimLayer = mDimLayer.getLayer();
-            dimAmount = 0;
-        } else {
-            dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
-            dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
-        }
-        final float targetAlpha = mDimLayer.getTargetAlpha();
-        if (targetAlpha != dimAmount) {
-            if (mDimWinAnimator == null) {
-                mDimLayer.hide(DEFAULT_DIM_DURATION);
-            } else {
-                long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
-                        ? mDimWinAnimator.mAnimation.computeDurationHint()
-                        : DEFAULT_DIM_DURATION;
-                if (targetAlpha > dimAmount) {
-                    duration = getDimBehindFadeDuration(duration);
-                }
-                mDimLayer.show(dimLayer, dimAmount, duration);
-            }
-        } else if (mDimLayer.getLayer() != dimLayer) {
-            mDimLayer.setLayer(dimLayer);
-        }
-        if (mDimLayer.isAnimating()) {
-            if (!mService.okToDisplay()) {
-                // Jump to the end of the animation.
-                mDimLayer.show();
-            } else {
-                return mDimLayer.stepAnimation();
-            }
-        }
-        return false;
-    }
-
-    void resetDimmingTag() {
-        mDimmingTag = false;
-    }
-
-    void setDimmingTag() {
-        mDimmingTag = true;
-    }
-
-    boolean testDimmingTag() {
-        return mDimmingTag;
-    }
-
-    boolean isDimming() {
-        return mDimLayer.isDimming();
-    }
-
-    boolean isDimming(WindowStateAnimator winAnimator) {
-        return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
-    }
-
-    void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
-        // Only set dim params on the highest dimmed layer.
-        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
-        if (newWinAnimator.mSurfaceShown && (mDimWinAnimator == null
-                || !mDimWinAnimator.mSurfaceShown
-                || mDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
-            mDimWinAnimator = newWinAnimator;
-            if (mDimWinAnimator.mWin.mAppToken == null
-                    && !mFullscreen && mDisplayContent != null) {
-                // Dim should cover the entire screen for system windows.
-                mDisplayContent.getLogicalDisplayRect(mTmpRect);
-                mDimLayer.setBounds(mTmpRect);
-            }
-        }
-    }
-
-    void stopDimmingIfNeeded() {
-        if (!mDimmingTag && isDimming()) {
-            mDimWinAnimator = null;
-            mDimLayer.setBounds(mBounds);
-        }
-    }
-
     void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
         int animLayer = winAnimator.mAnimLayer;
         if (mAnimationBackgroundAnimator == null
@@ -514,9 +315,8 @@
             mAnimationBackgroundSurface.destroySurface();
             mAnimationBackgroundSurface = null;
         }
-        if (mDimLayer != null) {
-            mDimLayer.destroySurface();
-            mDimLayer = null;
+        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+            mTasks.get(taskNdx).close();
         }
     }
 
@@ -524,21 +324,17 @@
         pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
         pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach);
         for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
-            pw.print(prefix); pw.println(mTasks.get(taskNdx));
+            pw.print(prefix);
+            mTasks.get(taskNdx).printTo(prefix + " ", pw);
         }
         if (mAnimationBackgroundSurface.isDimming()) {
             pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
             mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
         }
-        if (mDimLayer.isDimming()) {
-            pw.print(prefix); pw.println("mDimLayer:");
-            mDimLayer.printTo(prefix + " ", pw);
-            pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
-        }
         if (!mExitingAppTokens.isEmpty()) {
             pw.println();
             pw.println("  Exiting application tokens:");
-            for (int i=mExitingAppTokens.size()-1; i>=0; i--) {
+            for (int i = mExitingAppTokens.size() - 1; i >= 0; i--) {
                 WindowToken token = mExitingAppTokens.get(i);
                 pw.print("  Exiting App #"); pw.print(i);
                 pw.print(' '); pw.print(token);
@@ -549,6 +345,16 @@
     }
 
     @Override
+    public boolean isFullscreen() {
+        return mFullscreen;
+    }
+
+    @Override
+    public DisplayInfo getDisplayInfo() {
+        return mDisplayContent.getDisplayInfo();
+    }
+
+    @Override
     public String toString() {
         return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
     }
diff --git a/services/core/java/com/android/server/wm/StackTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
similarity index 95%
rename from services/core/java/com/android/server/wm/StackTapPointerEventListener.java
rename to services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index 1a85993..c97d12f 100644
--- a/services/core/java/com/android/server/wm/StackTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -23,7 +23,7 @@
 
 import com.android.server.wm.WindowManagerService.H;
 
-public class StackTapPointerEventListener implements PointerEventListener {
+public class TaskTapPointerEventListener implements PointerEventListener {
     private static final int TAP_TIMEOUT_MSEC = 300;
     private static final float TAP_MOTION_SLOP_INCHES = 0.125f;
 
@@ -35,7 +35,7 @@
     private final WindowManagerService mService;
     private final DisplayContent mDisplayContent;
 
-    public StackTapPointerEventListener(WindowManagerService service,
+    public TaskTapPointerEventListener(WindowManagerService service,
             DisplayContent displayContent) {
         mService = service;
         mDisplayContent = displayContent;
@@ -77,7 +77,7 @@
                                 && Math.abs(x - mDownX) < mMotionSlop
                                 && Math.abs(y - mDownY) < mMotionSlop
                                 && !mTouchExcludeRegion.contains(x, y)) {
-                            mService.mH.obtainMessage(H.TAP_OUTSIDE_STACK, x, y,
+                            mService.mH.obtainMessage(H.TAP_OUTSIDE_TASK, x, y,
                                     mDisplayContent).sendToTarget();
                         }
                     }
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 85a9624..b9740af 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -709,7 +709,7 @@
                 mService.scheduleAnimationLocked();
             }
 
-            mService.setFocusedStackLayer();
+            mService.setFocusedTaskLayer();
 
             if (mService.mWatermark != null) {
                 mService.mWatermark.drawIfNeeded();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2647244..320edbe 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -70,6 +70,7 @@
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
@@ -155,7 +156,6 @@
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
-import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
@@ -184,7 +184,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
 
 /** {@hide} */
 public class WindowManagerService extends IWindowManager.Stub
@@ -251,9 +250,9 @@
     static final int LAYER_OFFSET_DIM = 1;
 
     /**
-     * FocusedStackFrame layer is immediately above focused window.
+     * FocusedTaskFrame layer is immediately above focused window.
      */
-    static final int LAYER_OFFSET_FOCUSED_STACK = 1;
+    static final int LAYER_OFFSET_FOCUSED_TASK = 1;
 
     /**
      * Animation thumbnail is as far as possible below the window above
@@ -446,9 +445,9 @@
     StrictModeFlash mStrictModeFlash;
     CircularDisplayMask mCircularDisplayMask;
     EmulatorDisplayOverlay mEmulatorDisplayOverlay;
-    FocusedStackFrame mFocusedStackFrame;
+    FocusedTaskFrame mFocusedTaskFrame;
 
-    int mFocusedStackLayer;
+    int mFocusedTaskLayer;
 
     final float[] mTmpFloats = new float[9];
     final Rect mTmpContentRect = new Rect();
@@ -991,7 +990,7 @@
         SurfaceControl.openTransaction();
         try {
             createWatermarkInTransaction();
-            mFocusedStackFrame = new FocusedStackFrame(
+            mFocusedTaskFrame = new FocusedTaskFrame(
                     getDefaultDisplayContentLocked().getDisplay(), mFxSession);
         } finally {
             SurfaceControl.closeTransaction();
@@ -4063,37 +4062,33 @@
     }
 
     /** Call while in a Surface transaction. */
-    void setFocusedStackLayer() {
-        mFocusedStackLayer = 0;
+    void setFocusedTaskLayer() {
+        mFocusedTaskLayer = 0;
         if (mFocusedApp != null) {
             final WindowList windows = mFocusedApp.allAppWindows;
             for (int i = windows.size() - 1; i >= 0; --i) {
                 final WindowState win = windows.get(i);
                 final int animLayer = win.mWinAnimator.mAnimLayer;
                 if (win.mAttachedWindow == null && win.isVisibleLw() &&
-                        animLayer > mFocusedStackLayer) {
-                    mFocusedStackLayer = animLayer + LAYER_OFFSET_FOCUSED_STACK;
+                        animLayer > mFocusedTaskLayer) {
+                    mFocusedTaskLayer = animLayer + LAYER_OFFSET_FOCUSED_TASK;
                 }
             }
         }
-        if (DEBUG_LAYERS) Slog.v(TAG, "Setting FocusedStackFrame to layer=" +
-                mFocusedStackLayer);
-        mFocusedStackFrame.setLayer(mFocusedStackLayer);
+        if (DEBUG_LAYERS) Slog.v(TAG, "Setting FocusedTaskFrame to layer=" + mFocusedTaskLayer);
+        mFocusedTaskFrame.setLayer(mFocusedTaskLayer);
     }
 
-    void setFocusedStackFrame() {
-        final TaskStack stack;
+    void setFocusedTaskFrame() {
+        Task task = null;
         if (mFocusedApp != null) {
-            final Task task = mFocusedApp.mTask;
-            stack = task.mStack;
+            task = mFocusedApp.mTask;
             final DisplayContent displayContent = task.getDisplayContent();
             if (displayContent != null) {
-                displayContent.setTouchExcludeRegion(stack);
+                displayContent.setTouchExcludeRegion(task);
             }
-        } else {
-            stack = null;
         }
-        mFocusedStackFrame.setVisibility(stack);
+        mFocusedTaskFrame.setVisibility(task);
     }
 
     @Override
@@ -4121,11 +4116,11 @@
             if (changed) {
                 mFocusedApp = newFocus;
                 mInputMonitor.setFocusedAppLw(newFocus);
-                setFocusedStackFrame();
+                setFocusedTaskFrame();
                 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setFocusedApp");
                 SurfaceControl.openTransaction();
                 try {
-                    setFocusedStackLayer();
+                    setFocusedTaskLayer();
                 } finally {
                     SurfaceControl.closeTransaction();
                     if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> CLOSE TRANSACTION setFocusedApp");
@@ -5233,71 +5228,78 @@
         }
     }
 
-    /**
-     * Re-sizes the specified stack and its containing windows.
-     * Returns a {@link Configuration} object that contains configurations settings
-     * that should be overridden due to the operation.
-     */
-    public Configuration resizeStack(int stackId, Rect bounds) {
-        synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(stackId);
-            if (stack == null) {
-                throw new IllegalArgumentException("resizeStack: stackId " + stackId
-                        + " not found.");
-            }
-            if (stack.setBounds(bounds)) {
-                stack.resizeWindows();
-                stack.getDisplayContent().layoutNeeded = true;
-                performLayoutAndPlaceSurfacesLocked();
-            }
-            return new Configuration(stack.mOverrideConfig);
-        }
-    }
-
     public void getStackBounds(int stackId, Rect bounds) {
-        final TaskStack stack = mStackIdToStack.get(stackId);
-        if (stack != null) {
-            stack.getBounds(bounds);
-            return;
-        }
-        bounds.setEmpty();
-    }
-
-    /** Returns the id of an application (non-home stack) stack that match the input bounds.
-     * -1 if no stack matches.*/
-    public int getStackIdWithBounds(Rect bounds) {
-        Rect stackBounds = new Rect();
         synchronized (mWindowMap) {
-            for (int i = mStackIdToStack.size() - 1; i >= 0; --i) {
-                TaskStack stack = mStackIdToStack.valueAt(i);
-                if (stack.mStackId != HOME_STACK_ID) {
-                    stack.getBounds(stackBounds);
-                    if (stackBounds.equals(bounds)) {
-                        return stack.mStackId;
-                    }
-                }
+            final TaskStack stack = mStackIdToStack.get(stackId);
+            if (stack != null) {
+                stack.getBounds(bounds);
+                return;
             }
+            bounds.setEmpty();
         }
-        return -1;
     }
 
-    /** Forces the stack to fullscreen if input is true, else un-forces the stack from fullscreen.
-     * Returns a {@link Configuration} object that contains configurations settings
-     * that should be overridden due to the operation.
-     */
-    public Configuration forceStackToFullscreen(int stackId, boolean forceFullscreen) {
+    /**
+     * Re-sizes a stack and its containing tasks.
+     * @param stackId Id of stack to resize.
+     * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen.
+     * @param changedTaskIds Output list of Ids of tasks that changed in bounds due to resize.
+     * @param newTaskConfigs Output list of new Configuation of the tasks that changed.
+     * @return True if the stack is now fullscreen.
+     * */
+    public boolean resizeStack(
+            int stackId, Rect bounds, IntArray changedTaskIds, List<Configuration> newTaskConfigs) {
         synchronized (mWindowMap) {
             final TaskStack stack = mStackIdToStack.get(stackId);
             if (stack == null) {
                 throw new IllegalArgumentException("resizeStack: stackId " + stackId
                         + " not found.");
             }
-            if (stack.forceFullscreen(forceFullscreen)) {
+            if (stack.setBounds(bounds, changedTaskIds, newTaskConfigs)) {
                 stack.resizeWindows();
                 stack.getDisplayContent().layoutNeeded = true;
                 performLayoutAndPlaceSurfacesLocked();
             }
-            return new Configuration(stack.mOverrideConfig);
+            return stack.isFullscreen();
+        }
+    }
+
+    /**
+     * Re-sizes the specified task and its containing windows.
+     * Returns a {@link Configuration} object that contains configurations settings
+     * that should be overridden due to the operation.
+     */
+    public Configuration resizeTask(int taskId, Rect bounds) {
+        synchronized (mWindowMap) {
+            Task task = mTaskIdToTask.get(taskId);
+            if (task == null) {
+                throw new IllegalArgumentException("resizeTask: taskId " + taskId
+                        + " not found.");
+            }
+            if (task.setBounds(bounds)) {
+                task.resizeWindows();
+                task.getDisplayContent().layoutNeeded = true;
+                performLayoutAndPlaceSurfacesLocked();
+            }
+            return new Configuration(task.mOverrideConfig);
+        }
+    }
+
+    public void getTaskBounds(int taskId, Rect bounds) {
+        synchronized (mWindowMap) {
+            Task task = mTaskIdToTask.get(taskId);
+            if (task != null) {
+                task.getBounds(bounds);
+                return;
+            }
+            bounds.setEmpty();
+        }
+    }
+
+    /** Return true if the input task id represents a valid window manager task. */
+    public boolean isValidTaskId(int taskId) {
+        synchronized (mWindowMap) {
+            return mTaskIdToTask.get(taskId) != null;
         }
     }
 
@@ -6249,7 +6251,7 @@
                         int right = wf.right - cr.right;
                         int bottom = wf.bottom - cr.bottom;
                         frame.union(left, top, right, bottom);
-                        ws.getStackBounds(stackBounds);
+                        ws.getTaskBounds(stackBounds);
                         if (!frame.intersect(stackBounds)) {
                             // Set frame empty if there's no intersection.
                             frame.setEmpty();
@@ -7708,7 +7710,7 @@
         public static final int DO_DISPLAY_CHANGED = 29;
 
         public static final int CLIENT_FREEZE_TIMEOUT = 30;
-        public static final int TAP_OUTSIDE_STACK = 31;
+        public static final int TAP_OUTSIDE_TASK = 31;
         public static final int NOTIFY_ACTIVITY_DRAWN = 32;
 
         public static final int ALL_WINDOWS_DRAWN = 33;
@@ -8170,14 +8172,14 @@
                     }
                     break;
 
-                case TAP_OUTSIDE_STACK: {
-                    int stackId;
+                case TAP_OUTSIDE_TASK: {
+                    int taskId;
                     synchronized (mWindowMap) {
-                        stackId = ((DisplayContent)msg.obj).stackIdFromPoint(msg.arg1, msg.arg2);
+                        taskId = ((DisplayContent)msg.obj).taskIdFromPoint(msg.arg1, msg.arg2);
                     }
-                    if (stackId >= 0) {
+                    if (taskId >= 0) {
                         try {
-                            mActivityManager.setFocusedStack(stackId);
+                            mActivityManager.setFocusedTask(taskId);
                         } catch (RemoteException e) {
                         }
                     }
@@ -8877,8 +8879,8 @@
                 layerChanged = true;
                 anyLayerChanged = true;
             }
-            final TaskStack stack = w.getStack();
-            if (layerChanged && stack != null && stack.isDimming(winAnimator)) {
+            final Task task = w.getTask();
+            if (layerChanged && task != null && task.isDimming(winAnimator)) {
                 // Force an animation pass just to update the mDimLayer layer.
                 scheduleAnimationLocked();
             }
@@ -9768,14 +9770,14 @@
                 && w.isDisplayedLw()
                 && !w.mExiting) {
             final WindowStateAnimator winAnimator = w.mWinAnimator;
-            final TaskStack stack = w.getStack();
-            if (stack == null) {
+            final Task task = w.getTask();
+            if (task == null) {
                 return;
             }
-            stack.setDimmingTag();
-            if (!stack.isDimming(winAnimator)) {
+            task.setContinueDimming();
+            if (!task.isDimming(winAnimator)) {
                 if (localLOGV) Slog.v(TAG, "Win " + w + " start dimming.");
-                stack.startDimmingIfNeeded(winAnimator);
+                task.startDimmingIfNeeded(winAnimator);
             }
         }
     }
@@ -9927,7 +9929,7 @@
                     }
 
                     // FIRST LOOP: Perform a layout, if needed.
-                    if (repeats < 4) {
+                    if (repeats < LAYOUT_REPEAT_THRESHOLD) {
                         performLayoutLockedInner(displayContent, repeats == 1,
                                 false /*updateInputWindows*/);
                     } else {
@@ -9962,8 +9964,8 @@
                 final int N = windows.size();
                 for (i=N-1; i>=0; i--) {
                     WindowState w = windows.get(i);
-                    final TaskStack stack = w.getStack();
-                    if (stack == null && w.getAttrs().type != TYPE_PRIVATE_PRESENTATION) {
+                    final Task task = w.getTask();
+                    if (task == null && w.getAttrs().type != TYPE_PRIVATE_PRESENTATION) {
                         continue;
                     }
 
@@ -9975,7 +9977,7 @@
                         handleNotObscuredLocked(w, innerDw, innerDh);
                     }
 
-                    if (stack != null && !stack.testDimmingTag()) {
+                    if (task != null && !task.getContinueDimming()) {
                         handleFlagDimBehind(w);
                     }
 
@@ -10369,7 +10371,7 @@
         if (updateInputWindowsNeeded) {
             mInputMonitor.updateInputWindowsLw(false /*force*/);
         }
-        setFocusedStackFrame();
+        setFocusedTaskFrame();
 
         // Check to see if we are now in a state where the screen should
         // be enabled, because the window obscured flags have changed.
@@ -11428,7 +11430,8 @@
     boolean dumpWindows(PrintWriter pw, String name, String[] args,
             int opti, boolean dumpAll) {
         WindowList windows = new WindowList();
-        if ("visible".equals(name)) {
+        if ("visible".equals(name) || "visible-apps".equals(name)) {
+            final boolean appsOnly = "visible-apps".equals(name);
             synchronized(mWindowMap) {
                 final int numDisplays = mDisplayContents.size();
                 for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
@@ -11436,7 +11439,8 @@
                             mDisplayContents.valueAt(displayNdx).getWindowList();
                     for (int winNdx = windowList.size() - 1; winNdx >= 0; --winNdx) {
                         final WindowState w = windowList.get(winNdx);
-                        if (w.mWinAnimator.mSurfaceShown) {
+                        if (w.mWinAnimator.mSurfaceShown
+                                && (!appsOnly || (appsOnly && w.mAppToken != null))) {
                             windows.add(w);
                         }
                     }
@@ -11561,6 +11565,7 @@
                 pw.println("    Window hex object identifier, or");
                 pw.println("    \"all\" for all windows, or");
                 pw.println("    \"visible\" for the visible windows.");
+                pw.println("    \"visible-apps\" for the visible app windows.");
                 pw.println("  -a: include all available server state.");
                 return;
             } else {
@@ -11711,7 +11716,7 @@
 
         // TODO: Create an input channel for each display with touch capability.
         if (displayId == Display.DEFAULT_DISPLAY) {
-            displayContent.mTapDetector = new StackTapPointerEventListener(this, displayContent);
+            displayContent.mTapDetector = new TaskTapPointerEventListener(this, displayContent);
             registerPointerEventListener(displayContent.mTapDetector);
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c2548de..f7bf6e4 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -535,10 +535,10 @@
             Rect osf) {
         mHaveFrame = true;
 
-        final TaskStack stack = mAppToken != null ? getStack() : null;
-        final boolean nonFullscreenStack = stack != null && !stack.isFullscreen();
-        if (nonFullscreenStack) {
-            stack.getBounds(mContainingFrame);
+        final Task task = mAppToken != null ? getTask() : null;
+        final boolean nonFullscreenTask = task != null && !task.isFullscreen();
+        if (nonFullscreenTask) {
+            task.getBounds(mContainingFrame);
             final WindowState imeWin = mService.mInputMethodWindow;
             if (imeWin != null && imeWin.isVisibleNow() && mService.mInputMethodTarget == this
                     && mContainingFrame.bottom > cf.bottom) {
@@ -626,7 +626,7 @@
             y = mAttrs.y;
         }
 
-        if (nonFullscreenStack) {
+        if (nonFullscreenTask) {
             // Make sure window fits in containing frame since it is in a non-fullscreen stack as
             // required by {@link Gravity#apply} call.
             w = Math.min(w, pw);
@@ -856,27 +856,33 @@
         return displayContent.getDisplayId();
     }
 
-    TaskStack getStack() {
+    Task getTask() {
         AppWindowToken wtoken = mAppToken == null ? mService.mFocusedApp : mAppToken;
-        if (wtoken != null) {
-            Task task = wtoken.mTask;
-            if (task != null) {
-                if (task.mStack != null) {
-                    return task.mStack;
-                }
-                Slog.e(TAG, "getStack: mStack null for task=" + task);
-            } else {
-                Slog.e(TAG, "getStack: " + this + " couldn't find task for " + wtoken
-                    + " Callers=" + Debug.getCallers(4));
+        if (wtoken == null) {
+            Slog.e(TAG, "getTask: " + this + " null wtoken " + " Callers=" + Debug.getCallers(5));
+            return null;
+        }
+        final Task task = wtoken.mTask;
+        if (task == null) Slog.e(TAG, "getStack: " + this + " couldn't find task for " + wtoken
+                    + " Callers=" + Debug.getCallers(5));
+        return task;
+    }
+
+    TaskStack getStack() {
+        Task task = getTask();
+        if (task != null) {
+            if (task.mStack != null) {
+                return task.mStack;
             }
+            Slog.e(TAG, "getStack: mStack null for task=" + task);
         }
         return mDisplayContent.getHomeStack();
     }
 
-    void getStackBounds(Rect bounds) {
-        final TaskStack stack = getStack();
-        if (stack != null) {
-            stack.getBounds(bounds);
+    void getTaskBounds(Rect bounds) {
+        final Task task = getTask();
+        if (task != null) {
+            task.getBounds(bounds);
             return;
         }
         bounds.set(mFrame);
@@ -1139,9 +1145,9 @@
     }
 
     boolean isConfigChanged() {
-        final TaskStack stack = getStack();
+        final Task task = getTask();
         final Configuration overrideConfig =
-                (stack != null) ? stack.mOverrideConfig : Configuration.EMPTY;
+                (task != null) ? task.mOverrideConfig : Configuration.EMPTY;
         final Configuration serviceConfig = mService.mCurConfiguration;
         boolean configChanged =
                 (mConfiguration != serviceConfig && mConfiguration.diff(serviceConfig) != 0)
@@ -1387,11 +1393,11 @@
 
     @Override
     public boolean isDimming() {
-        TaskStack stack = getStack();
-        if (stack == null) {
+        Task task = getTask();
+        if (task == null) {
             return false;
         }
-        return stack.isDimming(mWinAnimator);
+        return task.isDimming(mWinAnimator);
     }
 
     public void setShowToOwnerOnlyLocked(boolean showToOwnerOnly) {
@@ -1489,9 +1495,9 @@
             if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
                     + ": " + mCompatFrame);
             boolean configChanged = isConfigChanged();
-            final TaskStack stack = getStack();
+            final Task task = getTask();
             final Configuration overrideConfig =
-                    (stack != null) ? stack.mOverrideConfig : Configuration.EMPTY;
+                    (task != null) ? task.mOverrideConfig : Configuration.EMPTY;
             if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION) && configChanged) {
                 Slog.i(TAG, "Sending new config to window " + this + ": "
                         + mWinAnimator.mSurfaceW + "x" + mWinAnimator.mSurfaceH + " / config="
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 3251a56..31ee869 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -169,9 +169,6 @@
     /** Set when the window has been shown in the screen the first time. */
     static final int HAS_DRAWN = 4;
 
-    private static final int SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN =
-            View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-
     String drawStateToString() {
         switch (mDrawState) {
             case NO_SURFACE: return "NO_SURFACE";
@@ -1418,9 +1415,9 @@
                 mAnimator.setPendingLayoutChanges(w.getDisplayId(),
                         WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
                 if ((w.mAttrs.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) {
-                    final TaskStack stack = w.getStack();
-                    if (stack != null) {
-                        stack.startDimmingIfNeeded(this);
+                    final Task task = w.getTask();
+                    if (task != null) {
+                        task.startDimmingIfNeeded(this);
                     }
                 }
             } catch (RuntimeException e) {