Merge "Fixing regression with launch target task."
diff --git a/api/current.txt b/api/current.txt
index f75ef0b..5cee2a3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -321,7 +321,7 @@
     field public static final int buttonBarNeutralButtonStyle = 16843914; // 0x101048a
     field public static final int buttonBarPositiveButtonStyle = 16843913; // 0x1010489
     field public static final int buttonBarStyle = 16843566; // 0x101032e
-    field public static final int buttonGravity = 16844030; // 0x10104fe
+    field public static final int buttonGravity = 16844031; // 0x10104ff
     field public static final int buttonStyle = 16842824; // 0x1010048
     field public static final int buttonStyleInset = 16842826; // 0x101004a
     field public static final int buttonStyleSmall = 16842825; // 0x1010049
@@ -371,7 +371,7 @@
     field public static final int codes = 16843330; // 0x1010242
     field public static final int collapseColumns = 16843083; // 0x101014b
     field public static final int collapseContentDescription = 16843984; // 0x10104d0
-    field public static final int collapseIcon = 16844031; // 0x10104ff
+    field public static final int collapseIcon = 16844032; // 0x1010500
     field public static final int color = 16843173; // 0x10101a5
     field public static final int colorAccent = 16843829; // 0x1010435
     field public static final int colorActivatedHighlight = 16843664; // 0x1010390
@@ -412,7 +412,7 @@
     field public static final int contentInsetRight = 16843862; // 0x1010456
     field public static final int contentInsetStart = 16843859; // 0x1010453
     field public static final int contextClickable = 16844007; // 0x10104e7
-    field public static final int contextPopupMenuStyle = 16844033; // 0x1010501
+    field public static final int contextPopupMenuStyle = 16844034; // 0x1010502
     field public static final int controlX1 = 16843772; // 0x10103fc
     field public static final int controlX2 = 16843774; // 0x10103fe
     field public static final int controlY1 = 16843773; // 0x10103fd
@@ -780,7 +780,7 @@
     field public static final int layout_y = 16843136; // 0x1010180
     field public static final int left = 16843181; // 0x10101ad
     field public static final int letterSpacing = 16843958; // 0x10104b6
-    field public static final int level = 16844032; // 0x1010500
+    field public static final int level = 16844033; // 0x1010501
     field public static final int lineSpacingExtra = 16843287; // 0x1010217
     field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
     field public static final int lines = 16843092; // 0x1010154
@@ -813,7 +813,7 @@
     field public static final int marqueeRepeatLimit = 16843293; // 0x101021d
     field public static final int matchOrder = 16843855; // 0x101044f
     field public static final int max = 16843062; // 0x1010136
-    field public static final int maxButtonHeight = 16844029; // 0x10104fd
+    field public static final int maxButtonHeight = 16844030; // 0x10104fe
     field public static final int maxDate = 16843584; // 0x1010340
     field public static final int maxEms = 16843095; // 0x1010157
     field public static final int maxHeight = 16843040; // 0x1010120
@@ -1174,6 +1174,7 @@
     field public static final int summaryOn = 16843247; // 0x10101ef
     field public static final int supportsAssist = 16844016; // 0x10104f0
     field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
+    field public static final int supportsPictureInPicture = 16844024; // 0x10104f8
     field public static final int supportsRtl = 16843695; // 0x10103af
     field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
     field public static final int supportsUploading = 16843419; // 0x101029b
@@ -1222,7 +1223,7 @@
     field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f
     field public static final int textAppearanceMedium = 16842817; // 0x1010041
     field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044
-    field public static final int textAppearancePopupMenuHeader = 16844034; // 0x1010502
+    field public static final int textAppearancePopupMenuHeader = 16844035; // 0x1010503
     field public static final int textAppearanceSearchResultSubtitle = 16843424; // 0x10102a0
     field public static final int textAppearanceSearchResultTitle = 16843425; // 0x10102a1
     field public static final int textAppearanceSmall = 16842818; // 0x1010042
@@ -1291,11 +1292,11 @@
     field public static final int tintMode = 16843771; // 0x10103fb
     field public static final int title = 16843233; // 0x10101e1
     field public static final int titleCondensed = 16843234; // 0x10101e2
-    field public static final int titleMargin = 16844024; // 0x10104f8
-    field public static final int titleMarginBottom = 16844028; // 0x10104fc
-    field public static final int titleMarginEnd = 16844026; // 0x10104fa
-    field public static final int titleMarginStart = 16844025; // 0x10104f9
-    field public static final int titleMarginTop = 16844027; // 0x10104fb
+    field public static final int titleMargin = 16844025; // 0x10104f9
+    field public static final int titleMarginBottom = 16844029; // 0x10104fd
+    field public static final int titleMarginEnd = 16844027; // 0x10104fb
+    field public static final int titleMarginStart = 16844026; // 0x10104fa
+    field public static final int titleMarginTop = 16844028; // 0x10104fc
     field public static final int titleTextAppearance = 16843822; // 0x101042e
     field public static final int titleTextColor = 16844003; // 0x10104e3
     field public static final int titleTextStyle = 16843512; // 0x10102f8
@@ -1395,7 +1396,7 @@
     field public static final int windowAllowReturnTransitionOverlap = 16843835; // 0x101043b
     field public static final int windowAnimationStyle = 16842926; // 0x10100ae
     field public static final int windowBackground = 16842836; // 0x1010054
-    field public static final int windowBackgroundFallback = 16844035; // 0x1010503
+    field public static final int windowBackgroundFallback = 16844036; // 0x1010504
     field public static final int windowClipToOutline = 16843947; // 0x10104ab
     field public static final int windowCloseOnTouchOutside = 16843611; // 0x101035b
     field public static final int windowContentOverlay = 16842841; // 0x1010059
@@ -22152,6 +22153,7 @@
     field public static final int GL_ACTIVE_PROGRAM = 33369; // 0x8259
     field public static final int GL_ACTIVE_RESOURCES = 37621; // 0x92f5
     field public static final int GL_ACTIVE_VARIABLES = 37637; // 0x9305
+    field public static final int GL_ALL_BARRIER_BITS = -1; // 0xffffffff
     field public static final int GL_ALL_SHADER_BITS = -1; // 0xffffffff
     field public static final int GL_ARRAY_SIZE = 37627; // 0x92fb
     field public static final int GL_ARRAY_STRIDE = 37630; // 0x92fe
@@ -22175,6 +22177,7 @@
     field public static final int GL_DISPATCH_INDIRECT_BUFFER_BINDING = 37103; // 0x90ef
     field public static final int GL_DRAW_INDIRECT_BUFFER = 36671; // 0x8f3f
     field public static final int GL_DRAW_INDIRECT_BUFFER_BINDING = 36675; // 0x8f43
+    field public static final int GL_ELEMENT_ARRAY_BARRIER_BIT = 2; // 0x2
     field public static final int GL_FRAGMENT_SHADER_BIT = 2; // 0x2
     field public static final int GL_FRAMEBUFFER_BARRIER_BIT = 1024; // 0x400
     field public static final int GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS = 37652; // 0x9314
@@ -22264,6 +22267,7 @@
     field public static final int GL_SAMPLE_MASK = 36433; // 0x8e51
     field public static final int GL_SAMPLE_MASK_VALUE = 36434; // 0x8e52
     field public static final int GL_SAMPLE_POSITION = 36432; // 0x8e50
+    field public static final int GL_SHADER_IMAGE_ACCESS_BARRIER_BIT = 32; // 0x20
     field public static final int GL_SHADER_STORAGE_BARRIER_BIT = 8192; // 0x2000
     field public static final int GL_SHADER_STORAGE_BLOCK = 37606; // 0x92e6
     field public static final int GL_SHADER_STORAGE_BUFFER = 37074; // 0x90d2
@@ -22309,6 +22313,7 @@
     field public static final int GL_UNSIGNED_INT_IMAGE_3D = 36964; // 0x9064
     field public static final int GL_UNSIGNED_INT_IMAGE_CUBE = 36966; // 0x9066
     field public static final int GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE = 37130; // 0x910a
+    field public static final int GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT = 1; // 0x1
     field public static final int GL_VERTEX_ATTRIB_BINDING = 33492; // 0x82d4
     field public static final int GL_VERTEX_ATTRIB_RELATIVE_OFFSET = 33493; // 0x82d5
     field public static final int GL_VERTEX_BINDING_BUFFER = 36687; // 0x8f4f
diff --git a/api/system-current.txt b/api/system-current.txt
index c4c6f1c..bdba865 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -413,7 +413,7 @@
     field public static final int buttonBarNeutralButtonStyle = 16843914; // 0x101048a
     field public static final int buttonBarPositiveButtonStyle = 16843913; // 0x1010489
     field public static final int buttonBarStyle = 16843566; // 0x101032e
-    field public static final int buttonGravity = 16844030; // 0x10104fe
+    field public static final int buttonGravity = 16844031; // 0x10104ff
     field public static final int buttonStyle = 16842824; // 0x1010048
     field public static final int buttonStyleInset = 16842826; // 0x101004a
     field public static final int buttonStyleSmall = 16842825; // 0x1010049
@@ -463,7 +463,7 @@
     field public static final int codes = 16843330; // 0x1010242
     field public static final int collapseColumns = 16843083; // 0x101014b
     field public static final int collapseContentDescription = 16843984; // 0x10104d0
-    field public static final int collapseIcon = 16844031; // 0x10104ff
+    field public static final int collapseIcon = 16844032; // 0x1010500
     field public static final int color = 16843173; // 0x10101a5
     field public static final int colorAccent = 16843829; // 0x1010435
     field public static final int colorActivatedHighlight = 16843664; // 0x1010390
@@ -504,7 +504,7 @@
     field public static final int contentInsetRight = 16843862; // 0x1010456
     field public static final int contentInsetStart = 16843859; // 0x1010453
     field public static final int contextClickable = 16844007; // 0x10104e7
-    field public static final int contextPopupMenuStyle = 16844033; // 0x1010501
+    field public static final int contextPopupMenuStyle = 16844034; // 0x1010502
     field public static final int controlX1 = 16843772; // 0x10103fc
     field public static final int controlX2 = 16843774; // 0x10103fe
     field public static final int controlY1 = 16843773; // 0x10103fd
@@ -872,7 +872,7 @@
     field public static final int layout_y = 16843136; // 0x1010180
     field public static final int left = 16843181; // 0x10101ad
     field public static final int letterSpacing = 16843958; // 0x10104b6
-    field public static final int level = 16844032; // 0x1010500
+    field public static final int level = 16844033; // 0x1010501
     field public static final int lineSpacingExtra = 16843287; // 0x1010217
     field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
     field public static final int lines = 16843092; // 0x1010154
@@ -905,7 +905,7 @@
     field public static final int marqueeRepeatLimit = 16843293; // 0x101021d
     field public static final int matchOrder = 16843855; // 0x101044f
     field public static final int max = 16843062; // 0x1010136
-    field public static final int maxButtonHeight = 16844029; // 0x10104fd
+    field public static final int maxButtonHeight = 16844030; // 0x10104fe
     field public static final int maxDate = 16843584; // 0x1010340
     field public static final int maxEms = 16843095; // 0x1010157
     field public static final int maxHeight = 16843040; // 0x1010120
@@ -1270,6 +1270,7 @@
     field public static final int summaryOn = 16843247; // 0x10101ef
     field public static final int supportsAssist = 16844016; // 0x10104f0
     field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
+    field public static final int supportsPictureInPicture = 16844024; // 0x10104f8
     field public static final int supportsRtl = 16843695; // 0x10103af
     field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
     field public static final int supportsUploading = 16843419; // 0x101029b
@@ -1318,7 +1319,7 @@
     field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f
     field public static final int textAppearanceMedium = 16842817; // 0x1010041
     field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044
-    field public static final int textAppearancePopupMenuHeader = 16844034; // 0x1010502
+    field public static final int textAppearancePopupMenuHeader = 16844035; // 0x1010503
     field public static final int textAppearanceSearchResultSubtitle = 16843424; // 0x10102a0
     field public static final int textAppearanceSearchResultTitle = 16843425; // 0x10102a1
     field public static final int textAppearanceSmall = 16842818; // 0x1010042
@@ -1387,11 +1388,11 @@
     field public static final int tintMode = 16843771; // 0x10103fb
     field public static final int title = 16843233; // 0x10101e1
     field public static final int titleCondensed = 16843234; // 0x10101e2
-    field public static final int titleMargin = 16844024; // 0x10104f8
-    field public static final int titleMarginBottom = 16844028; // 0x10104fc
-    field public static final int titleMarginEnd = 16844026; // 0x10104fa
-    field public static final int titleMarginStart = 16844025; // 0x10104f9
-    field public static final int titleMarginTop = 16844027; // 0x10104fb
+    field public static final int titleMargin = 16844025; // 0x10104f9
+    field public static final int titleMarginBottom = 16844029; // 0x10104fd
+    field public static final int titleMarginEnd = 16844027; // 0x10104fb
+    field public static final int titleMarginStart = 16844026; // 0x10104fa
+    field public static final int titleMarginTop = 16844028; // 0x10104fc
     field public static final int titleTextAppearance = 16843822; // 0x101042e
     field public static final int titleTextColor = 16844003; // 0x10104e3
     field public static final int titleTextStyle = 16843512; // 0x10102f8
@@ -1491,7 +1492,7 @@
     field public static final int windowAllowReturnTransitionOverlap = 16843835; // 0x101043b
     field public static final int windowAnimationStyle = 16842926; // 0x10100ae
     field public static final int windowBackground = 16842836; // 0x1010054
-    field public static final int windowBackgroundFallback = 16844035; // 0x1010503
+    field public static final int windowBackgroundFallback = 16844036; // 0x1010504
     field public static final int windowClipToOutline = 16843947; // 0x10104ab
     field public static final int windowCloseOnTouchOutside = 16843611; // 0x101035b
     field public static final int windowContentOverlay = 16842841; // 0x1010059
@@ -24097,6 +24098,7 @@
     field public static final int GL_ACTIVE_PROGRAM = 33369; // 0x8259
     field public static final int GL_ACTIVE_RESOURCES = 37621; // 0x92f5
     field public static final int GL_ACTIVE_VARIABLES = 37637; // 0x9305
+    field public static final int GL_ALL_BARRIER_BITS = -1; // 0xffffffff
     field public static final int GL_ALL_SHADER_BITS = -1; // 0xffffffff
     field public static final int GL_ARRAY_SIZE = 37627; // 0x92fb
     field public static final int GL_ARRAY_STRIDE = 37630; // 0x92fe
@@ -24120,6 +24122,7 @@
     field public static final int GL_DISPATCH_INDIRECT_BUFFER_BINDING = 37103; // 0x90ef
     field public static final int GL_DRAW_INDIRECT_BUFFER = 36671; // 0x8f3f
     field public static final int GL_DRAW_INDIRECT_BUFFER_BINDING = 36675; // 0x8f43
+    field public static final int GL_ELEMENT_ARRAY_BARRIER_BIT = 2; // 0x2
     field public static final int GL_FRAGMENT_SHADER_BIT = 2; // 0x2
     field public static final int GL_FRAMEBUFFER_BARRIER_BIT = 1024; // 0x400
     field public static final int GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS = 37652; // 0x9314
@@ -24209,6 +24212,7 @@
     field public static final int GL_SAMPLE_MASK = 36433; // 0x8e51
     field public static final int GL_SAMPLE_MASK_VALUE = 36434; // 0x8e52
     field public static final int GL_SAMPLE_POSITION = 36432; // 0x8e50
+    field public static final int GL_SHADER_IMAGE_ACCESS_BARRIER_BIT = 32; // 0x20
     field public static final int GL_SHADER_STORAGE_BARRIER_BIT = 8192; // 0x2000
     field public static final int GL_SHADER_STORAGE_BLOCK = 37606; // 0x92e6
     field public static final int GL_SHADER_STORAGE_BUFFER = 37074; // 0x90d2
@@ -24254,6 +24258,7 @@
     field public static final int GL_UNSIGNED_INT_IMAGE_3D = 36964; // 0x9064
     field public static final int GL_UNSIGNED_INT_IMAGE_CUBE = 36966; // 0x9066
     field public static final int GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE = 37130; // 0x910a
+    field public static final int GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT = 1; // 0x1
     field public static final int GL_VERTEX_ATTRIB_BINDING = 33492; // 0x82d4
     field public static final int GL_VERTEX_ATTRIB_RELATIVE_OFFSET = 33493; // 0x82d5
     field public static final int GL_VERTEX_BINDING_BUFFER = 36687; // 0x8f4f
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 1e2e33d..12780a8 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -154,7 +154,7 @@
                 "       am stack movetask <TASK_ID> <STACK_ID> [true|false]\n" +
                 "       am stack resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
                 "       am stack size-docked-stack-test: <STEP_SIZE> <l|t|r|b> [DELAY_MS]\n" +
-                "       am stack split <STACK_ID> <v|h> [INTENT]\n" +
+                "       am stack move-top-activity-to-pinned-stack: <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
                 "       am stack positiontask <TASK_ID> <STACK_ID> <POSITION>\n" +
                 "       am stack list\n" +
                 "       am stack info <STACK_ID>\n" +
@@ -297,11 +297,9 @@
                 "   <STEP_SIZE> increments from the side <l>eft, <t>op, <r>ight, or <b>ottom\n" +
                 "   applying the optional [DELAY_MS] between each step.\n" +
                 "\n" +
-                "am stack split: split <STACK_ID> into 2 stacks <v>ertically or <h>orizontally\n" +
-                "   starting the new stack with [INTENT] if specified. If [INTENT] isn't\n" +
-                "   specified and the current stack has more than one task, then the top task\n" +
-                "   of the current task will be moved to the new stack. Command will also force\n" +
-                "   all current tasks in both stacks to be resizeable.\n" +
+                "am stack move-top-activity-to-pinned-stack: moves the top activity from\n" +
+                "   <STACK_ID> to the pinned stack using <LEFT,TOP,RIGHT,BOTTOM> for the\n" +
+                "   bounds of the pinned stack.\n" +
                 "\n" +
                 "am stack positiontask: place <TASK_ID> in <STACK_ID> at <POSITION>" +
                 "\n" +
@@ -1954,25 +1952,34 @@
 
     private void runStack() throws Exception {
         String op = nextArgRequired();
-        if (op.equals("start")) {
-            runStackStart();
-        } else if (op.equals("movetask")) {
-            runStackMoveTask();
-        } else if (op.equals("resize")) {
-            runStackResize();
-        } else if (op.equals("positiontask")) {
-            runStackPositionTask();
-        } else if (op.equals("list")) {
-            runStackList();
-        } else if (op.equals("info")) {
-            runStackInfo();
-        } else if (op.equals("split")) {
-            runStackSplit();
-        } else if (op.equals("size-docked-stack-test")) {
-            runStackSizeDockedStackTest();
-        } else {
-            showError("Error: unknown command '" + op + "'");
-            return;
+        switch (op) {
+            case "start":
+                runStackStart();
+                break;
+            case "movetask":
+                runStackMoveTask();
+                break;
+            case "resize":
+                runStackResize();
+                break;
+            case "positiontask":
+                runStackPositionTask();
+                break;
+            case "list":
+                runStackList();
+                break;
+            case "info":
+                runStackInfo();
+                break;
+            case "move-top-activity-to-pinned-stack":
+                runMoveTopActivityToPinnedStack();
+                break;
+            case "size-docked-stack-test":
+                runStackSizeDockedStackTest();
+                break;
+            default:
+                showError("Error: unknown command '" + op + "'");
+                break;
         }
     }
 
@@ -2072,61 +2079,21 @@
         }
     }
 
-    private void runStackSplit() throws Exception {
-        final int stackId = Integer.valueOf(nextArgRequired());
-        final String splitDirection = nextArgRequired();
-        Intent intent = null;
-        try {
-            intent = makeIntent(UserHandle.USER_CURRENT);
-        } catch (IllegalArgumentException e) {
-            // no intent supplied.
+    private void runMoveTopActivityToPinnedStack() throws Exception {
+        int stackId = Integer.valueOf(nextArgRequired());
+        final Rect bounds = getBounds();
+        if (bounds == null) {
+            System.err.println("Error: invalid input bounds");
+            return;
         }
 
         try {
-            final StackInfo currentStackInfo = mAm.getStackInfo(stackId);
-            // Calculate bounds for new and current stack.
-            final Rect currentStackBounds = new Rect(currentStackInfo.bounds);
-            final Rect newStackBounds = new Rect(currentStackInfo.bounds);
-            if ("v".equals(splitDirection)) {
-                currentStackBounds.right = newStackBounds.left = currentStackInfo.bounds.centerX();
-            } else if ("h".equals(splitDirection)) {
-                currentStackBounds.bottom = newStackBounds.top = currentStackInfo.bounds.centerY();
-            } else {
-                showError("Error: unknown split direction '" + splitDirection + "'");
-                return;
+            if (!mAm.moveTopActivityToPinnedStack(stackId, bounds)) {
+                showError("Didn't move top activity to pinned stack.");
             }
-
-            // Create new stack
-            IActivityContainer container = mAm.createStackOnDisplay(currentStackInfo.displayId);
-            if (container == null) {
-                showError("Error: Unable to create new stack...");
-            }
-
-            final int newStackId = container.getStackId();
-
-            if (intent != null) {
-                container.startActivity(intent);
-            } else if (currentStackInfo.taskIds != null && currentStackInfo.taskIds.length > 1) {
-                // Move top task over to new stack
-                mAm.moveTaskToStack(currentStackInfo.taskIds[currentStackInfo.taskIds.length - 1],
-                        newStackId, true);
-            }
-
-            final StackInfo newStackInfo = mAm.getStackInfo(newStackId);
-
-            // Make all tasks in the stacks resizeable.
-            for (int taskId : currentStackInfo.taskIds) {
-                mAm.setTaskResizeable(taskId, true);
-            }
-
-            for (int taskId : newStackInfo.taskIds) {
-                mAm.setTaskResizeable(taskId, true);
-            }
-
-            // Resize stacks
-            mAm.resizeStack(currentStackInfo.stackId, currentStackBounds, false);
-            mAm.resizeStack(newStackInfo.stackId, newStackBounds, false);
         } catch (RemoteException e) {
+            showError("Unable to move top activity: " + e);
+            return;
         }
     }
 
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 3bfeff0..6ae32d0 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -434,10 +434,17 @@
     public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
 
     /**
+     * ID of stack that always on top (always visible) when it exist.
+     * Mainly used for this in Picture-in-Picture mode.
+     * @hide
+     */
+    public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
+
+    /**
      * Last static stack stack ID.
      * @hide
      */
-    public static final int LAST_STATIC_STACK_ID = DOCKED_STACK_ID;
+    public static final int LAST_STATIC_STACK_ID = PINNED_STACK_ID;
 
     /**
      * Start of ID range used by stacks that are created dynamically.
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index b97f947..70f1bcc 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -755,6 +755,16 @@
             return true;
         }
 
+        case MOVE_TOP_ACTIVITY_TO_PINNED_STACK: {
+            data.enforceInterface(IActivityManager.descriptor);
+            final int stackId = data.readInt();
+            final Rect r = Rect.CREATOR.createFromParcel(data);
+            final boolean res = moveTopActivityToPinnedStack(stackId, r);
+            reply.writeNoException();
+            reply.writeInt(res ? 1 : 0);
+            return true;
+        }
+
         case RESIZE_STACK_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             final int stackId = data.readInt();
@@ -3555,6 +3565,22 @@
         reply.recycle();
     }
     @Override
+    public boolean moveTopActivityToPinnedStack(int stackId, Rect r)
+        throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(stackId);
+        r.writeToParcel(data, 0);
+        mRemote.transact(MOVE_TOP_ACTIVITY_TO_PINNED_STACK, data, reply, 0);
+        reply.readException();
+        final boolean res = reply.readInt() != 0;
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
+    @Override
     public void resizeStack(int stackId, Rect r, boolean allowResizeInDockedMode)
             throws RemoteException
     {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index c26a44c..ebdd302 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -143,6 +143,7 @@
     public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException;
     public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop)
             throws RemoteException;
+    public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) throws RemoteException;
     public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode) throws RemoteException;
     public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException;
     public List<StackInfo> getAllStackInfos() throws RemoteException;
@@ -898,4 +899,5 @@
     int MOVE_TASK_TO_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346;
     int SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 347;
     int REMOVE_STACK = IBinder.FIRST_CALL_TRANSACTION + 348;
+    int MOVE_TOP_ACTIVITY_TO_PINNED_STACK = IBinder.FIRST_CALL_TRANSACTION + 349;
 }
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 3853772..7ca39cb 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -671,6 +671,13 @@
      */
     public boolean resizeable;
 
+    /**
+     * Value indicating if the activity is supports picture-in-picture form of multi-window mode.
+     * See {@link android.R.attr#supportsPictureInPicture}.
+     * @hide
+     */
+    public boolean supportsPip;
+
     /** @hide */
     public static final int LOCK_TASK_LAUNCH_MODE_DEFAULT = 0;
     /** @hide */
@@ -723,6 +730,7 @@
         parentActivityName = orig.parentActivityName;
         maxRecents = orig.maxRecents;
         resizeable = orig.resizeable;
+        supportsPip = orig.supportsPip;
         lockTaskLaunchMode = orig.lockTaskLaunchMode;
         layout = orig.layout;
     }
@@ -769,8 +777,8 @@
         if (uiOptions != 0) {
             pw.println(prefix + " uiOptions=0x" + Integer.toHexString(uiOptions));
         }
-        pw.println(prefix + "resizeable=" + resizeable + " lockTaskLaunchMode="
-                + lockTaskLaunchModeToString(lockTaskLaunchMode));
+        pw.println(prefix + "resizeable=" + resizeable + " supportsPip=" + supportsPip);
+        pw.println(prefix + "lockTaskLaunchMode=" + lockTaskLaunchModeToString(lockTaskLaunchMode));
         if (layout != null) {
             pw.println(prefix + "initialLayout=" + layout.width + "|"
                     + layout.widthFraction + ", " + layout.height + "|"
@@ -806,6 +814,7 @@
         dest.writeInt(persistableMode);
         dest.writeInt(maxRecents);
         dest.writeInt(resizeable ? 1 : 0);
+        dest.writeInt(supportsPip ? 1 : 0);
         dest.writeInt(lockTaskLaunchMode);
         if (layout != null) {
             dest.writeInt(1);
@@ -847,6 +856,7 @@
         persistableMode = source.readInt();
         maxRecents = source.readInt();
         resizeable = (source.readInt() == 1);
+        supportsPip = (source.readInt() == 1);
         lockTaskLaunchMode = source.readInt();
         if (source.readInt() == 1) {
             layout = new Layout(source);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 1e85dfb..5d73b06 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3220,6 +3220,9 @@
                     R.styleable.AndroidManifestActivity_resizeableActivity,
                     owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N);
 
+            a.info.supportsPip = a.info.resizeable ? sa.getBoolean(
+                    R.styleable.AndroidManifestActivity_supportsPictureInPicture, false) : false;
+
             a.info.screenOrientation = sa.getInt(
                     R.styleable.AndroidManifestActivity_screenOrientation,
                     ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 11effd0..a1f9743 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6277,6 +6277,15 @@
         */
        public static final String FORCE_ALLOW_ON_EXTERNAL = "force_allow_on_external";
 
+        /**
+         * Whether any activity can be resized. When this is true, any
+         * activity, regardless of manifest values, can be resized for multi-window.
+         * (0 = false, 1 = true)
+         * @hide
+         */
+        public static final String DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES
+                = "force_resizable_activities";
+
        /**
         * Whether user has enabled development settings.
         */
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index de509b2..2459cfa 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -238,6 +238,7 @@
             initialScrollY = Touch.getInitialScrollY(widget, buffer);
         }
 
+        boolean wasTouchSelecting = isTouchSelecting(isMouse, buffer);
         boolean handled = Touch.onTouchEvent(widget, buffer, event);
 
         if (widget.didTouchFocusSelect() && !isMouse) {
@@ -267,9 +268,9 @@
                 // Cursor can be active at any location in the text while mouse pointer can start
                 // selection from a totally different location. Use LAST_TAP_DOWN span to ensure
                 // text selection will start from mouse pointer location.
+                final int startOffset = buffer.getSpanStart(LAST_TAP_DOWN);
                 if (isMouse && Touch.isSelectionStarted(buffer)) {
-                    int offset = buffer.getSpanStart(LAST_TAP_DOWN);
-                    Selection.setSelection(buffer, offset);
+                    Selection.setSelection(buffer, startOffset);
                 }
 
                 if (isTouchSelecting(isMouse, buffer) && handled) {
@@ -284,9 +285,9 @@
                     // Update selection as we're moving the selection area.
 
                     // Get the current touch position
-                    int offset = widget.getOffsetForPosition(event.getX(), event.getY());
-
-                    Selection.extendSelection(buffer, offset);
+                    final int offset = widget.getOffsetForPosition(event.getX(), event.getY());
+                    Selection.setSelection(buffer, Math.min(startOffset, offset),
+                            Math.max(startOffset, offset));
                     return true;
                 }
             } else if (action == MotionEvent.ACTION_UP) {
@@ -300,10 +301,12 @@
                     return true;
                 }
 
-                int offset = widget.getOffsetForPosition(event.getX(), event.getY());
-                if (isTouchSelecting(isMouse, buffer)) {
+                if (wasTouchSelecting) {
+                    final int startOffset = buffer.getSpanStart(LAST_TAP_DOWN);
+                    final int endOffset = widget.getOffsetForPosition(event.getX(), event.getY());
+                    Selection.setSelection(buffer, Math.min(startOffset, endOffset),
+                            Math.max(startOffset, endOffset));
                     buffer.removeSpan(LAST_TAP_DOWN);
-                    Selection.extendSelection(buffer, offset);
                 }
 
                 MetaKeyKeyListener.adjustMetaAfterKeypress(buffer);
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index c1eb80d..37d6757 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -343,15 +343,15 @@
         int seconds = (int) Math.floor(duration / 1000);
         int days = 0, hours = 0, minutes = 0;
 
-        if (seconds > SECONDS_PER_DAY) {
+        if (seconds >= SECONDS_PER_DAY) {
             days = seconds / SECONDS_PER_DAY;
             seconds -= days * SECONDS_PER_DAY;
         }
-        if (seconds > SECONDS_PER_HOUR) {
+        if (seconds >= SECONDS_PER_HOUR) {
             hours = seconds / SECONDS_PER_HOUR;
             seconds -= hours * SECONDS_PER_HOUR;
         }
-        if (seconds > SECONDS_PER_MINUTE) {
+        if (seconds >= SECONDS_PER_MINUTE) {
             minutes = seconds / SECONDS_PER_MINUTE;
             seconds -= minutes * SECONDS_PER_MINUTE;
         }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7d39a0c..e17bdd7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4191,7 +4191,10 @@
 
                     if (mPointerIconShape != pointerShape) {
                         mPointerIconShape = pointerShape;
-                        event.getDevice().setPointerShape(pointerShape);
+                        final InputDevice inputDevice = event.getDevice();
+                        if (inputDevice != null) {
+                            inputDevice.setPointerShape(pointerShape);
+                        }
                     }
                 } else if (event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) {
                     mPointerIconShape = PointerIcon.STYLE_NOT_SPECIFIED;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 4644211..afef763 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -20,6 +20,7 @@
 import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.INVALID_STACK_ID;
+import static android.app.ActivityManager.PINNED_STACK_ID;
 import static android.view.View.MeasureSpec.AT_MOST;
 import static android.view.View.MeasureSpec.EXACTLY;
 import static android.view.View.MeasureSpec.getMode;
@@ -5408,7 +5409,7 @@
      * @Return Returns true if the window should show a non client decor.
      **/
     private static boolean hasNonClientDecor(int workspaceId) {
-        return workspaceId == FREEFORM_WORKSPACE_STACK_ID;
+        return workspaceId == FREEFORM_WORKSPACE_STACK_ID || workspaceId == PINNED_STACK_ID;
     }
 
     /**
@@ -5417,7 +5418,7 @@
      * @Return Returns true if the window should show a shadow.
      **/
     private static boolean nonClientDecorHasShadow(int workspaceId) {
-        return workspaceId == FREEFORM_WORKSPACE_STACK_ID;
+        return workspaceId == FREEFORM_WORKSPACE_STACK_ID || workspaceId == PINNED_STACK_ID;
     }
 
     @Override
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 322ac4f..07ac471 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1059,7 +1059,7 @@
          at the same time.
 
          <p>The default value is <code>false</code> for applications with
-         <code>targetSdkVersion</code> lesser than {@link android.os.Build.VERSION_CODES#M} and
+         <code>targetSdkVersion</code> lesser than {@link android.os.Build.VERSION_CODES#N} and
          <code>true</code> otherwise.
 
          <p>NOTE: A task's root activity value is applied to all additional activities launched in
@@ -1067,11 +1067,24 @@
          all other activities in the task as resizeable and will not if the root activity isn't
          resizeable.
 
-         <p>NOTE: The value of {@link android.R.attr#screenOrientation} will be ignored for
-         resizeable activities as the system doesn't support fixed orientation on a resizeable
-         activity. -->
+         <p>NOTE: The value of {@link android.R.attr#screenOrientation} is ignored for
+         resizeable activities when in multi-window mode. -->
     <attr name="resizeableActivity" format="boolean" />
 
+    <!-- Indicates that the activity supports the picture-in-picture (PiP) form of multi-window.
+         While it makes sense to be able to resize most activities types in multi-window mode when
+         {@link android.R.attr#resizeableActivity} is set. It only makes sense to put specific types
+         of activities in PiP mode of multi-window. For example, activities that play video. When
+         set the activity will be allowed to enter PiP mode when the system deems it appropriate on
+         devices that support PiP.
+
+         <p>The default value is <code>false</code> for applications with
+         <code>targetSdkVersion</code> lesser than {@link android.os.Build.VERSION_CODES#N} and
+         <code>true</code> otherwise.
+
+         <p>NOTE: Attribute is only used if {@link android.R.attr#resizeableActivity} is true. -->
+    <attr name="supportsPictureInPicture" format="boolean" />
+
     <!-- This value indicates how tasks rooted at this activity will behave in lockTask mode.
          While in lockTask mode the system will not launch non-permitted tasks until
          lockTask mode is disabled.
@@ -1826,6 +1839,7 @@
         <attr name="relinquishTaskIdentity" />
         <attr name="resumeWhilePausing" />
         <attr name="resizeableActivity" />
+        <attr name="supportsPictureInPicture" />
         <attr name="lockTaskMode" />
         <attr name="showForAllUsers" />
     </declare-styleable>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index c05b585..037f1c4 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2666,6 +2666,7 @@
     <public type="attr" name="initialHeight" />
     <public type="attr" name="minimalSize" />
     <public type="attr" name="resizeableActivity" />
+    <public type="attr" name="supportsPictureInPicture" />
     <public type="attr" name="titleMargin" />
     <public type="attr" name="titleMarginStart" />
     <public type="attr" name="titleMarginEnd" />
diff --git a/core/tests/coretests/src/android/util/TimeUtilsTest.java b/core/tests/coretests/src/android/util/TimeUtilsTest.java
index 74c8e04..2370627 100644
--- a/core/tests/coretests/src/android/util/TimeUtilsTest.java
+++ b/core/tests/coretests/src/android/util/TimeUtilsTest.java
@@ -436,15 +436,17 @@
         assertFormatDuration("+100ms", 100);
         assertFormatDuration("+101ms", 101);
         assertFormatDuration("+330ms", 330);
+        assertFormatDuration("+1s0ms", 1000);
         assertFormatDuration("+1s330ms", 1330);
         assertFormatDuration("+10s24ms", 10024);
+        assertFormatDuration("+1m0s30ms", 60030);
+        assertFormatDuration("+1h0m0s30ms", 3600030);
+        assertFormatDuration("+1d0h0m0s30ms", 86400030);
     }
 
     public void testFormatHugeDuration() {
-        //assertFormatDuration("+15542d1h11m11s555ms", 1342833071555L);
-        // TODO: improve formatDuration() API
-        assertFormatDuration("+999d23h59m59s999ms", 1342833071555L);
-        assertFormatDuration("-999d23h59m59s999ms", -1342833071555L);
+        assertFormatDuration("+15542d1h11m11s555ms", 1342833071555L);
+        assertFormatDuration("-15542d1h11m11s555ms", -1342833071555L);
     }
 
     private void assertFormatDuration(String expected, long duration) {
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
new file mode 100644
index 0000000..c5e2ae6
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.widget;
+
+import static android.widget.espresso.TextViewActions.mouseDragOnText;
+import static android.widget.espresso.TextViewAssertions.hasSelection;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.typeTextIntoFocusedView;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+import com.android.frameworks.coretests.R;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * Tests mouse interaction of the TextView widget from an Activity
+ */
+public class TextViewActivityMouseTest extends ActivityInstrumentationTestCase2<TextViewActivity>{
+
+    public TextViewActivityMouseTest() {
+        super(TextViewActivity.class);
+    }
+
+    @SmallTest
+    public void testSelectTextByDrag() throws Exception {
+        getActivity();
+
+        final String helloWorld = "Hello world!";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+        onView(withId(R.id.textview)).perform(
+                mouseDragOnText(helloWorld.indexOf("llo"), helloWorld.indexOf("ld!")));
+
+        onView(withId(R.id.textview)).check(hasSelection("llo wor"));
+    }
+
+    @SmallTest
+    public void testSelectTextByDrag_reverse() throws Exception {
+        getActivity();
+
+        final String helloWorld = "Hello world!";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+        onView(withId(R.id.textview)).perform(
+                mouseDragOnText( helloWorld.indexOf("ld!"), helloWorld.indexOf("llo")));
+
+        onView(withId(R.id.textview)).check(hasSelection("llo wor"));
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java b/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java
index a0cd848..9ff8e82 100644
--- a/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java
@@ -20,25 +20,22 @@
 import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static org.hamcrest.Matchers.allOf;
-
 import android.annotation.Nullable;
 import android.os.SystemClock;
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.PerformException;
 import android.support.test.espresso.ViewAction;
 import android.support.test.espresso.action.CoordinatesProvider;
-import android.support.test.espresso.action.GeneralClickAction;
 import android.support.test.espresso.action.MotionEvents;
 import android.support.test.espresso.action.PrecisionDescriber;
-import android.support.test.espresso.action.Press;
 import android.support.test.espresso.action.Swiper;
-import android.support.test.espresso.action.Tap;
 import android.support.test.espresso.util.HumanReadables;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.widget.TextView;
+
 import org.hamcrest.Matcher;
 
 
@@ -51,11 +48,48 @@
  * <ul>
  */
 public final class DragOnTextViewActions implements ViewAction {
+    public interface Dragger extends Swiper {
+        UiController wrapUiController(UiController uiController);
+    }
 
     /**
      * Executes different "drag on text" types to given positions.
      */
-    public enum Drag implements Swiper {
+    public enum Drag implements Dragger {
+
+        /**
+         * Starts a drag with a mouse down.
+         */
+        MOUSE_DOWN {
+            private DownMotionPerformer downMotion = new DownMotionPerformer() {
+                @Override
+                public MotionEvent perform(
+                        UiController uiController, float[] coordinates, float[] precision) {
+                    MotionEvent downEvent = MotionEvents.sendDown(
+                            uiController, coordinates, precision)
+                            .down;
+                    return downEvent;
+                }
+            };
+
+            @Override
+            public Status sendSwipe(
+                    UiController uiController,
+                    float[] startCoordinates, float[] endCoordinates, float[] precision) {
+                return sendLinearDrag(
+                        uiController, downMotion, startCoordinates, endCoordinates, precision);
+            }
+
+            @Override
+            public String toString() {
+                return "mouse down and drag to select";
+            }
+
+            @Override
+            public UiController wrapUiController(UiController uiController) {
+                return new MouseUiController(uiController);
+            }
+        },
 
         /**
          * Starts a drag with a long-press.
@@ -197,6 +231,11 @@
 
             return res;
         }
+
+        @Override
+        public UiController wrapUiController(UiController uiController) {
+            return uiController;
+        }
     }
 
     /**
@@ -215,13 +254,13 @@
         MotionEvent perform(UiController uiController, float[] coordinates, float[] precision);
     }
 
-    private final Swiper mDragger;
+    private final Dragger mDragger;
     private final CoordinatesProvider mStartCoordinatesProvider;
     private final CoordinatesProvider mEndCoordinatesProvider;
     private final PrecisionDescriber mPrecisionDescriber;
 
     public DragOnTextViewActions(
-            Swiper dragger,
+            Dragger dragger,
             CoordinatesProvider startCoordinatesProvider,
             CoordinatesProvider endCoordinatesProvider,
             PrecisionDescriber precisionDescriber) {
@@ -242,6 +281,8 @@
         checkNotNull(uiController);
         checkNotNull(view);
 
+        uiController = mDragger.wrapUiController(uiController);
+
         float[] startCoordinates = mStartCoordinatesProvider.calculateCoordinates(view);
         float[] endCoordinates = mEndCoordinatesProvider.calculateCoordinates(view);
         float[] precision = mPrecisionDescriber.describePrecision();
diff --git a/core/tests/coretests/src/android/widget/espresso/MouseUiController.java b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
new file mode 100644
index 0000000..f1387f8
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.widget.espresso;
+
+import android.support.test.espresso.InjectEventSecurityException;
+import android.support.test.espresso.UiController;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+/**
+ * Class to wrap an UiController to overwrite source of motion events to SOURCE_MOUSE.
+ * Note that this doesn't change the tool type.
+ */
+public class MouseUiController implements UiController {
+    private final UiController mUiController;
+
+    public MouseUiController(UiController uiController) {
+        mUiController = uiController;
+    }
+
+    @Override
+    public boolean injectKeyEvent(KeyEvent event) throws InjectEventSecurityException {
+        return mUiController.injectKeyEvent(event);
+    }
+
+    @Override
+    public boolean injectMotionEvent(MotionEvent event) throws InjectEventSecurityException {
+        // Modify the event to mimic mouse primary button event.
+        event.setSource(InputDevice.SOURCE_MOUSE);
+        event.setButtonState(MotionEvent.BUTTON_PRIMARY);
+        return mUiController.injectMotionEvent(event);
+    }
+
+    @Override
+    public boolean injectString(String str) throws InjectEventSecurityException {
+        return mUiController.injectString(str);
+    }
+
+    @Override
+    public void loopMainThreadForAtLeast(long millisDelay) {
+        mUiController.loopMainThreadForAtLeast(millisDelay);
+    }
+
+    @Override
+    public void loopMainThreadUntilIdle() {
+        mUiController.loopMainThreadUntilIdle();
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
index 835b1b9..4f5a72b 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
@@ -124,6 +124,27 @@
     }
 
     /**
+     * Returns an action that click then drags by mouse on text from startIndex to endIndex on the
+     * TextView.<br>
+     * <br>
+     * View constraints:
+     * <ul>
+     * <li>must be a TextView displayed on screen
+     * <ul>
+     *
+     * @param startIndex The index of the TextView's text to start a drag from
+     * @param endIndex The index of the TextView's text to end the drag at
+     */
+    public static ViewAction mouseDragOnText(int startIndex, int endIndex) {
+        return actionWithAssertions(
+                new DragOnTextViewActions(
+                        DragOnTextViewActions.Drag.MOUSE_DOWN,
+                        new TextCoordinates(startIndex),
+                        new TextCoordinates(endIndex),
+                        Press.PINPOINT));
+    }
+
+    /**
      * A provider of the x, y coordinates of the text at the specified index in a text view.
      */
     private static final class TextCoordinates implements CoordinatesProvider {
diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java
index 3bd17fa..abcccbd 100644
--- a/graphics/java/android/graphics/Point.java
+++ b/graphics/java/android/graphics/Point.java
@@ -99,7 +99,7 @@
 
     /** @hide */
     public void printShortString(PrintWriter pw) {
-        pw.println("["); pw.print(x); pw.print(","); pw.print(y); pw.print("]");
+        pw.print("["); pw.print(x); pw.print(","); pw.print(y); pw.print("]");
     }
 
     /**
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 94806ca..7dbba25 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -25,6 +25,15 @@
 namespace android {
 namespace uirenderer {
 
+void BakedOpRenderer::Info::setViewport(uint32_t width, uint32_t height) {
+    viewportWidth = width;
+    viewportHeight = height;
+    orthoMatrix.loadOrtho(viewportWidth, viewportHeight);
+
+    renderState.setViewport(width, height);
+    renderState.blend().syncEnabled();
+}
+
 Texture* BakedOpRenderer::Info::getTexture(const SkBitmap* bitmap) {
     Texture* texture = renderState.assetAtlas().getEntryTexture(bitmap);
     if (!texture) {
@@ -45,9 +54,54 @@
     didDraw = true;
 }
 
-void BakedOpRenderer::startFrame(Info& info) {
-    info.renderState.setViewport(info.viewportWidth, info.viewportHeight);
-    info.renderState.blend().syncEnabled();
+Layer* BakedOpRenderer::startLayer(Info& info, uint32_t width, uint32_t height) {
+    info.caches.textureState().activateTexture(0);
+    Layer* layer = info.caches.layerCache.get(info.renderState, width, height);
+    LOG_ALWAYS_FATAL_IF(!layer, "need layer...");
+
+    info.layer = layer;
+    layer->texCoords.set(0.0f, width / float(layer->getHeight()),
+            height / float(layer->getWidth()), 0.0f);
+
+    layer->setFbo(info.renderState.genFramebuffer());
+    info.renderState.bindFramebuffer(layer->getFbo());
+    layer->bindTexture();
+
+    // Initialize the texture if needed
+    if (layer->isEmpty()) {
+        layer->allocateTexture();
+        layer->setEmpty(false);
+    }
+
+    // attach the texture to the FBO
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+            layer->getTextureId(), 0);
+    LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "startLayer FAILED");
+    LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
+            "framebuffer incomplete!");
+
+    // Clear the FBO
+    info.renderState.scissor().setEnabled(false);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    // Change the viewport & ortho projection
+    info.setViewport(width, height);
+    return layer;
+}
+
+void BakedOpRenderer::endLayer(Info& info) {
+    Layer* layer = info.layer;
+    info.layer = nullptr;
+
+    // Detach the texture from the FBO
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
+    LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "endLayer FAILED");
+    layer->removeFbo(false);
+}
+
+void BakedOpRenderer::startFrame(Info& info, uint32_t width, uint32_t height) {
+    info.renderState.bindFramebuffer(0);
+    info.setViewport(width, height);
     Caches::getInstance().clearGarbage();
 
     if (!info.opaque) {
@@ -130,7 +184,31 @@
 }
 
 void BakedOpRenderer::onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) {
-    LOG_ALWAYS_FATAL("unsupported operation");
+    Layer* layer = *op.layerHandle;
+
+    // TODO: make this work for HW layers
+    layer->setPaint(op.paint);
+    layer->setBlend(true);
+    float layerAlpha = (layer->getAlpha() / 255.0f) * state.alpha;
+
+    const bool tryToSnap = state.computedState.transform.isPureTranslate();
+    Glop glop;
+    GlopBuilder(info.renderState, info.caches, &glop)
+            .setRoundRectClipState(state.roundRectClipState)
+            .setMeshTexturedUvQuad(nullptr, layer->texCoords)
+            .setFillLayer(layer->getTexture(), layer->getColorFilter(), layerAlpha, layer->getMode(), Blend::ModeOrderSwap::NoSwap)
+            .setTransform(state.computedState.transform, TransformFlags::None)
+            .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
+            .build();
+    info.renderGlop(state, glop);
+
+    // return layer to cache, since each clipped savelayer is only drawn once.
+    layer->setConvexMask(nullptr);
+    if (!info.caches.layerCache.put(layer)) {
+        // Failing to add the layer to the cache should happen only if the layer is too large
+        LAYER_LOGD("Deleting layer");
+        layer->decStrong(nullptr);
+    }
 }
 
 } // namespace uirenderer
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index f45dbe4..616adde 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -25,21 +25,21 @@
 
 class Caches;
 struct Glop;
+class Layer;
 class RenderState;
 
 class BakedOpRenderer {
 public:
     class Info {
     public:
-        Info(Caches& caches, RenderState& renderState, int viewportWidth, int viewportHeight, bool opaque)
+        Info(Caches& caches, RenderState& renderState, bool opaque)
                 : renderState(renderState)
                 , caches(caches)
-                , opaque(opaque)
-                , viewportWidth(viewportWidth)
-                , viewportHeight(viewportHeight) {
-            orthoMatrix.loadOrtho(viewportWidth, viewportHeight);
+                , opaque(opaque) {
         }
 
+        void setViewport(uint32_t width, uint32_t height);
+
         Texture* getTexture(const SkBitmap* bitmap);
 
         void renderGlop(const BakedOpState& state, const Glop& glop);
@@ -47,16 +47,19 @@
         Caches& caches;
 
         bool didDraw = false;
-        bool opaque;
 
+        Layer* layer = nullptr;
 
         // where should these live? layer state object?
-        int viewportWidth;
-        int viewportHeight;
+        bool opaque;
+        uint32_t viewportWidth = 0;
+        uint32_t viewportHeight = 0;
         Matrix4 orthoMatrix;
     };
 
-    static void startFrame(Info& info);
+    static Layer* startLayer(Info& info, uint32_t width, uint32_t height);
+    static void endLayer(Info& info);
+    static void startFrame(Info& info, uint32_t width, uint32_t height);
     static void endFrame(Info& info);
 
     /**
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index f99d92b..489ebc1 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -155,8 +155,7 @@
 
     if (fbo) {
         if (flush) LayerRenderer::flushLayer(renderState, this);
-        // If put fails the cache will delete the FBO
-        caches.fboCache.put(fbo);
+        renderState.deleteFramebuffer(fbo);
         fbo = 0;
     }
 }
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 227271d..e9e5d81 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -189,7 +189,7 @@
     LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height);
 
     Caches& caches = Caches::getInstance();
-    GLuint fbo = caches.fboCache.get();
+    GLuint fbo = renderState.genFramebuffer();
     if (!fbo) {
         ALOGW("Could not obtain an FBO");
         return nullptr;
@@ -204,7 +204,7 @@
 
     // We first obtain a layer before comparing against the max texture size
     // because layers are not allocated at the exact desired size. They are
-    // always created slighly larger to improve recycling
+    // always created slightly larger to improve recycling
     const uint32_t maxTextureSize = caches.maxTextureSize;
     if (layer->getWidth() > maxTextureSize || layer->getHeight() > maxTextureSize) {
         ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)",
@@ -357,7 +357,7 @@
             && bitmap->width() <= caches.maxTextureSize
             && bitmap->height() <= caches.maxTextureSize) {
 
-        GLuint fbo = caches.fboCache.get();
+        GLuint fbo = renderState.getFramebuffer();
         if (!fbo) {
             ALOGW("Could not obtain an FBO");
             return false;
@@ -465,7 +465,7 @@
         layer->setAlpha(alpha, mode);
         layer->setFbo(previousLayerFbo);
         caches.textureState().deleteTexture(texture);
-        caches.fboCache.put(fbo);
+        renderState.deleteFramebuffer(fbo);
         renderState.setViewport(previousViewportWidth, previousViewportHeight);
 
         return status;
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index c1417c4..cde42f8 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -203,7 +203,7 @@
 };
 
 // iterate back toward target to see if anything drawn since should overlap the new op
-// if no target, merging ops still interate to find similar batch to insert after
+// if no target, merging ops still iterate to find similar batch to insert after
 void OpReorderer::LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
         BatchBase** targetBatch, size_t* insertBatchIndex) const {
     for (int i = mBatches.size() - 1; i >= 0; i--) {
@@ -292,18 +292,14 @@
     }
 }
 
-OpReorderer::OpReorderer()
+OpReorderer::OpReorderer(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight,
+        const std::vector< sp<RenderNode> >& nodes)
         : mCanvasState(*this) {
-    mLayerReorderers.emplace_back();
+    ATRACE_NAME("prepare drawing commands");
+
+    mLayerReorderers.emplace_back(viewportWidth, viewportHeight);
     mLayerStack.push_back(0);
-}
 
-void OpReorderer::onViewportInitialized() {}
-
-void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
-
-void OpReorderer::defer(const SkRect& clip, int viewportWidth, int viewportHeight,
-        const std::vector< sp<RenderNode> >& nodes) {
     mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
             clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
             Vector3());
@@ -321,13 +317,22 @@
     }
 }
 
-void OpReorderer::defer(int viewportWidth, int viewportHeight, const DisplayList& displayList) {
+OpReorderer::OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList)
+        : mCanvasState(*this) {
     ATRACE_NAME("prepare drawing commands");
+
+    mLayerReorderers.emplace_back(viewportWidth, viewportHeight);
+    mLayerStack.push_back(0);
+
     mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
             0, 0, viewportWidth, viewportHeight, Vector3());
     deferImpl(displayList);
 }
 
+void OpReorderer::onViewportInitialized() {}
+
+void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
+
 /**
  * Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() methods.
  *
@@ -350,11 +355,6 @@
 
 void OpReorderer::replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) {
     ATRACE_NAME("flush drawing commands");
-    // Relay through layers in reverse order, since layers
-    // later in the list will be drawn by earlier ones
-    for (int i = mLayerReorderers.size() - 1; i >= 0; i--) {
-        mLayerReorderers[i].replayBakedOpsImpl(arg, receivers);
-    }
 }
 
 void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
@@ -405,15 +405,17 @@
 
 // TODO: test rejection at defer time, where the bounds become empty
 void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
+    const uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
+    const uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
+
     mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
     mCanvasState.writableSnapshot()->transform->loadIdentity();
-    mCanvasState.writableSnapshot()->initializeViewport(
-            (int) op.unmappedBounds.getWidth(), (int) op.unmappedBounds.getHeight());
+    mCanvasState.writableSnapshot()->initializeViewport(layerWidth, layerHeight);
     mCanvasState.writableSnapshot()->roundRectClipState = nullptr;
 
     // create a new layer, and push its index on the stack
     mLayerStack.push_back(mLayerReorderers.size());
-    mLayerReorderers.emplace_back();
+    mLayerReorderers.emplace_back(layerWidth, layerHeight);
     mLayerReorderers.back().beginLayerOp = &op;
 }
 
@@ -432,7 +434,8 @@
             beginLayerOp.unmappedBounds,
             beginLayerOp.localMatrix,
             beginLayerOp.localClipRect,
-            beginLayerOp.paint);
+            beginLayerOp.paint,
+            &mLayerReorderers[finishedLayerIndex].layer);
     BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
 
     if (bakedOpState) {
diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h
index 73dc9af..f32b858 100644
--- a/libs/hwui/OpReorderer.h
+++ b/libs/hwui/OpReorderer.h
@@ -63,6 +63,10 @@
      */
     class LayerReorderer {
     public:
+        LayerReorderer(uint32_t width, uint32_t height)
+                : width(width)
+                , height(height) {}
+
         // iterate back toward target to see if anything drawn since should overlap the new op
         // if no target, merging ops still iterate to find similar batch to insert after
         void locateInsertIndex(int batchId, const Rect& clippedBounds,
@@ -77,15 +81,22 @@
 
         void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) const;
 
+        bool empty() const {
+            return mBatches.empty();
+        }
+
         void clear() {
             mBatches.clear();
         }
 
         void dump() const;
 
+        Layer* layer = nullptr;
         const BeginLayerOp* beginLayerOp = nullptr;
-
+        const uint32_t width;
+        const uint32_t height;
     private:
+
         std::vector<BatchBase*> mBatches;
 
         /**
@@ -100,14 +111,13 @@
 
     };
 public:
-    OpReorderer();
-    virtual ~OpReorderer() {}
-
     // TODO: not final, just presented this way for simplicity. Layers too?
-    void defer(const SkRect& clip, int viewportWidth, int viewportHeight,
+    OpReorderer(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight,
             const std::vector< sp<RenderNode> >& nodes);
 
-    void defer(int viewportWidth, int viewportHeight, const DisplayList& displayList);
+    OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList);
+
+    virtual ~OpReorderer() {}
 
     /**
      * replayBakedOps() is templated based on what class will receive ops being replayed.
@@ -128,8 +138,21 @@
         static BakedOpReceiver receivers[] = {
             MAP_OPS(BAKED_OP_RECEIVER)
         };
-        StaticReceiver::startFrame(arg);
-        replayBakedOpsImpl((void*)&arg, receivers);
+
+        // Relay through layers in reverse order, since layers
+        // later in the list will be drawn by earlier ones
+        for (int i = mLayerReorderers.size() - 1; i >= 1; i--) {
+            LayerReorderer& layer = mLayerReorderers[i];
+            if (!layer.empty()) {
+                layer.layer = StaticReceiver::startLayer(arg, layer.width, layer.height);
+                layer.replayBakedOpsImpl((void*)&arg, receivers);
+                StaticReceiver::endLayer(arg);
+            }
+        }
+
+        const LayerReorderer& fbo0 = mLayerReorderers[0];
+        StaticReceiver::startFrame(arg, fbo0.width, fbo0.height);
+        fbo0.replayBakedOpsImpl((void*)&arg, receivers);
         StaticReceiver::endFrame(arg);
     }
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index d4f65b6..8c3603b 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -673,7 +673,7 @@
 
 bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
     layer->clipRect.set(clip);
-    layer->setFbo(mCaches.fboCache.get());
+    layer->setFbo(mRenderState.genFramebuffer());
 
     writableSnapshot()->region = &writableSnapshot()->layer->region;
     writableSnapshot()->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer;
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index dd01637..6c31b42 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -29,6 +29,7 @@
 namespace android {
 namespace uirenderer {
 
+class Layer;
 class RenderNode;
 struct Vertex;
 
@@ -136,8 +137,12 @@
 };
 
 struct LayerOp : RecordedOp {
-    LayerOp(BASE_PARAMS)
-            : SUPER(LayerOp) {}
+    LayerOp(BASE_PARAMS, Layer** layerHandle)
+            : SUPER(LayerOp)
+            , layerHandle(layerHandle) {}
+    // Records a handle to the Layer object, since the Layer itself won't be
+    // constructed until after this operation is constructed.
+    Layer** layerHandle;
 };
 
 }; // namespace uirenderer
diff --git a/libs/hwui/microbench/OpReordererBench.cpp b/libs/hwui/microbench/OpReordererBench.cpp
index cf96d44..382c0bd 100644
--- a/libs/hwui/microbench/OpReordererBench.cpp
+++ b/libs/hwui/microbench/OpReordererBench.cpp
@@ -48,8 +48,7 @@
 void BM_OpReorderer_defer::Run(int iters) {
     StartBenchmarkTiming();
     for (int i = 0; i < iters; i++) {
-        OpReorderer reorderer;
-        reorderer.defer(200, 200, *sReorderingDisplayList);
+        OpReorderer reorderer(200, 200, *sReorderingDisplayList);
         MicroBench::DoNotOptimize(&reorderer);
     }
     StopBenchmarkTiming();
@@ -60,11 +59,10 @@
     TestUtils::runOnRenderThread([this, iters](RenderState& renderState, Caches& caches) {
         StartBenchmarkTiming();
         for (int i = 0; i < iters; i++) {
-            OpReorderer reorderer;
-            reorderer.defer(200, 200, *sReorderingDisplayList);
+            OpReorderer reorderer(200, 200, *sReorderingDisplayList);
             MicroBench::DoNotOptimize(&reorderer);
 
-            BakedOpRenderer::Info info(caches, renderState, 200, 200, true);
+            BakedOpRenderer::Info info(caches, renderState, true);
             reorderer.replayBakedOps<BakedOpRenderer>(info);
         }
         StopBenchmarkTiming();
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index dfa70ac..9637117 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -125,6 +125,21 @@
     }
 }
 
+GLuint RenderState::genFramebuffer() {
+    GLuint ret;
+    glGenFramebuffers(1, &ret);
+    return ret;
+}
+
+void RenderState::deleteFramebuffer(GLuint fbo) {
+    if (mFramebuffer == fbo) {
+        // GL defines that deleting the currently bound FBO rebinds FBO 0.
+        // Reflect this in our cached value.
+        mFramebuffer = 0;
+    }
+    glDeleteFramebuffers(1, &fbo);
+}
+
 void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) {
     if (mode == DrawGlInfo::kModeProcessNoContext) {
         // If there's no context we don't need to interrupt as there's
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index 9ae0845..87a7996 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -57,7 +57,10 @@
     void getViewport(GLsizei* outWidth, GLsizei* outHeight);
 
     void bindFramebuffer(GLuint fbo);
-    GLint getFramebuffer() { return mFramebuffer; }
+    GLuint getFramebuffer() { return mFramebuffer; }
+    GLuint genFramebuffer();
+    void deleteFramebuffer(GLuint fbo);
+
 
     void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info);
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f571426..7c0f0b6 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -318,10 +318,8 @@
     mEglManager.damageFrame(frame, dirty);
 
 #if HWUI_NEW_OPS
-    OpReorderer reorderer;
-    reorderer.defer(dirty, frame.width(), frame.height(), mRenderNodes);
-    BakedOpRenderer::Info info(Caches::getInstance(), mRenderThread.renderState(),
-            frame.width(), frame.height(), mOpaque);
+    OpReorderer reorderer(dirty, frame.width(), frame.height(), mRenderNodes);
+    BakedOpRenderer::Info info(Caches::getInstance(), mRenderThread.renderState(), mOpaque);
     // TODO: profiler().draw(mCanvas);
     reorderer.replayBakedOps<BakedOpRenderer>(info);
 
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index b353ae6..485759b 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -333,7 +333,7 @@
     if (CC_LIKELY(err == EGL_SUCCESS)) {
         return true;
     }
-    if (err == EGL_BAD_SURFACE) {
+    if (err == EGL_BAD_SURFACE || err == EGL_BAD_NATIVE_WINDOW) {
         // For some reason our surface was destroyed out from under us
         // This really shouldn't happen, but if it does we can recover easily
         // by just not trying to use the surface anymore
diff --git a/libs/hwui/tests/TreeContentAnimation.cpp b/libs/hwui/tests/TreeContentAnimation.cpp
index 891af91..2eefd37 100644
--- a/libs/hwui/tests/TreeContentAnimation.cpp
+++ b/libs/hwui/tests/TreeContentAnimation.cpp
@@ -54,15 +54,10 @@
     }
 };
 
-static TestCanvas* startRecording(RenderNode* node) {
-    TestCanvas* renderer = new TestCanvas(
-            node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
-    return renderer;
-}
-
-static void endRecording(TestCanvas* renderer, RenderNode* node) {
-    node->setStagingDisplayList(renderer->finishRecording());
-    delete renderer;
+static void recordNode(RenderNode& node, std::function<void(TestCanvas&)> contentCallback) {
+    TestCanvas canvas(node.stagingProperties().getWidth(), node.stagingProperties().getHeight());
+    contentCallback(canvas);
+    node.setStagingDisplayList(canvas.finishRecording());
 }
 
 class TreeContentAnimation {
@@ -75,7 +70,7 @@
             frameCount = fc;
         }
     }
-    virtual void createContent(int width, int height, TestCanvas* renderer) = 0;
+    virtual void createContent(int width, int height, TestCanvas* canvas) = 0;
     virtual void doFrame(int frameNr) = 0;
 
     template <class T>
@@ -108,11 +103,9 @@
         proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
         proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
 
-        android::uirenderer::Rect DUMMY;
-
-        TestCanvas* renderer = startRecording(rootNode);
-        animation.createContent(width, height, renderer);
-        endRecording(renderer, rootNode);
+        recordNode(*rootNode, [&animation, width, height](TestCanvas& canvas) {
+            animation.createContent(width, height, &canvas); //TODO: no&
+        });
 
         // Do a few cold runs then reset the stats so that the caches are all hot
         for (int i = 0; i < 3; i++) {
@@ -140,19 +133,19 @@
 class ShadowGridAnimation : public TreeContentAnimation {
 public:
     std::vector< sp<RenderNode> > cards;
-    void createContent(int width, int height, TestCanvas* renderer) override {
-        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
-        renderer->insertReorderBarrier(true);
+    void createContent(int width, int height, TestCanvas* canvas) override {
+        canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+        canvas->insertReorderBarrier(true);
 
         for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
             for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
                 sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
-                renderer->drawRenderNode(card.get());
+                canvas->drawRenderNode(card.get());
                 cards.push_back(card);
             }
         }
 
-        renderer->insertReorderBarrier(false);
+        canvas->insertReorderBarrier(false);
     }
     void doFrame(int frameNr) override {
         int curFrame = frameNr % 150;
@@ -171,9 +164,9 @@
         node->mutateStagingProperties().mutableOutline().setShouldClip(true);
         node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
 
-        TestCanvas* renderer = startRecording(node.get());
-        renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
-        endRecording(renderer, node.get());
+        recordNode(*node, [](TestCanvas& canvas) {
+            canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+        });
         return node;
     }
 };
@@ -187,19 +180,19 @@
 class ShadowGrid2Animation : public TreeContentAnimation {
 public:
     std::vector< sp<RenderNode> > cards;
-    void createContent(int width, int height, TestCanvas* renderer) override {
-        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
-        renderer->insertReorderBarrier(true);
+    void createContent(int width, int height, TestCanvas* canvas) override {
+        canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+        canvas->insertReorderBarrier(true);
 
         for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
             for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
                 sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
-                renderer->drawRenderNode(card.get());
+                canvas->drawRenderNode(card.get());
                 cards.push_back(card);
             }
         }
 
-        renderer->insertReorderBarrier(false);
+        canvas->insertReorderBarrier(false);
     }
     void doFrame(int frameNr) override {
         int curFrame = frameNr % 150;
@@ -218,9 +211,9 @@
         node->mutateStagingProperties().mutableOutline().setShouldClip(true);
         node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
 
-        TestCanvas* renderer = startRecording(node.get());
-        renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
-        endRecording(renderer, node.get());
+        recordNode(*node, [](TestCanvas& canvas) {
+            canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+        });
         return node;
     }
 };
@@ -233,15 +226,30 @@
 
 class RectGridAnimation : public TreeContentAnimation {
 public:
-    sp<RenderNode> card;
-    void createContent(int width, int height, TestCanvas* renderer) override {
-        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
-        renderer->insertReorderBarrier(true);
+    sp<RenderNode> card = new RenderNode();
+    void createContent(int width, int height, TestCanvas* canvas) override {
+        canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+        canvas->insertReorderBarrier(true);
 
-        card = createCard(40, 40, 200, 200);
-        renderer->drawRenderNode(card.get());
+        card->mutateStagingProperties().setLeftTopRightBottom(50, 50, 250, 250);
+        card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+        recordNode(*card, [](TestCanvas& canvas) {
+            canvas.drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
 
-        renderer->insertReorderBarrier(false);
+            SkRegion region;
+            for (int xOffset = 0; xOffset < 200; xOffset+=2) {
+                for (int yOffset = 0; yOffset < 200; yOffset+=2) {
+                    region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
+                }
+            }
+
+            SkPaint paint;
+            paint.setColor(0xff00ffff);
+            canvas.drawRegion(region, paint);
+        });
+        canvas->drawRenderNode(card.get());
+
+        canvas->insertReorderBarrier(false);
     }
     void doFrame(int frameNr) override {
         int curFrame = frameNr % 150;
@@ -249,29 +257,6 @@
         card->mutateStagingProperties().setTranslationY(curFrame);
         card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
     }
-private:
-    sp<RenderNode> createCard(int x, int y, int width, int height) {
-        sp<RenderNode> node = new RenderNode();
-        node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
-        node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
-        TestCanvas* renderer = startRecording(node.get());
-        renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
-
-        SkRegion region;
-        for (int xOffset = 0; xOffset < width; xOffset+=2) {
-            for (int yOffset = 0; yOffset < height; yOffset+=2) {
-                region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
-            }
-        }
-
-        SkPaint paint;
-        paint.setColor(0xff00ffff);
-        renderer->drawRegion(region, paint);
-
-        endRecording(renderer, node.get());
-        return node;
-    }
 };
 static Benchmark _RectGrid(BenchmarkInfo{
     "rectgrid",
@@ -282,15 +267,22 @@
 
 class OvalAnimation : public TreeContentAnimation {
 public:
-    sp<RenderNode> card;
-    void createContent(int width, int height, TestCanvas* renderer) override {
-        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
-        renderer->insertReorderBarrier(true);
+    sp<RenderNode> card = new RenderNode();
+    void createContent(int width, int height, TestCanvas* canvas) override {
+        canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+        canvas->insertReorderBarrier(true);
 
-        card = createCard(40, 40, 400, 400);
-        renderer->drawRenderNode(card.get());
+        card->mutateStagingProperties().setLeftTopRightBottom(0, 0, 200, 200);
+        card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+        recordNode(*card, [](TestCanvas& canvas) {
+            SkPaint paint;
+            paint.setAntiAlias(true);
+            paint.setColor(0xFF000000);
+            canvas.drawOval(0, 0, 200, 200, paint);
+        });
+        canvas->drawRenderNode(card.get());
 
-        renderer->insertReorderBarrier(false);
+        canvas->insertReorderBarrier(false);
     }
 
     void doFrame(int frameNr) override {
@@ -299,22 +291,6 @@
         card->mutateStagingProperties().setTranslationY(curFrame);
         card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
     }
-private:
-    sp<RenderNode> createCard(int x, int y, int width, int height) {
-        sp<RenderNode> node = new RenderNode();
-        node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
-        node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
-        TestCanvas* renderer = startRecording(node.get());
-
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setColor(0xFF000000);
-        renderer->drawOval(0, 0, width, height, paint);
-
-        endRecording(renderer, node.get());
-        return node;
-    }
 };
 static Benchmark _Oval(BenchmarkInfo{
     "oval",
@@ -325,7 +301,7 @@
 class PartialDamageTest : public TreeContentAnimation {
 public:
     std::vector< sp<RenderNode> > cards;
-    void createContent(int width, int height, TestCanvas* renderer) override {
+    void createContent(int width, int height, TestCanvas* canvas) override {
         static SkColor COLORS[] = {
                 0xFFF44336,
                 0xFF9C27B0,
@@ -333,13 +309,13 @@
                 0xFF4CAF50,
         };
 
-        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+        canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
 
         for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
             for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
                 sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
                         COLORS[static_cast<int>((y / dp(116))) % 4]);
-                renderer->drawRenderNode(card.get());
+                canvas->drawRenderNode(card.get());
                 cards.push_back(card);
             }
         }
@@ -350,10 +326,10 @@
         cards[0]->mutateStagingProperties().setTranslationY(curFrame);
         cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
 
-        TestCanvas* renderer = startRecording(cards[0].get());
-        renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
-                SkXfermode::kSrcOver_Mode);
-        endRecording(renderer, cards[0].get());
+        recordNode(*cards[0], [curFrame](TestCanvas& canvas) {
+            canvas.drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
+                    SkXfermode::kSrcOver_Mode);
+        });
     }
 
     static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
@@ -378,9 +354,9 @@
         node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
         node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
 
-        TestCanvas* renderer = startRecording(node.get());
-        renderer->drawColor(color, SkXfermode::kSrcOver_Mode);
-        endRecording(renderer, node.get());
+        recordNode(*node, [color](TestCanvas& canvas) {
+            canvas.drawColor(color, SkXfermode::kSrcOver_Mode);
+        });
         return node;
     }
 };
@@ -393,16 +369,24 @@
 });
 
 
-class SimpleRectGridAnimation : public TreeContentAnimation {
+class SaveLayerAnimation : public TreeContentAnimation {
 public:
-    sp<RenderNode> card;
-    void createContent(int width, int height, TestCanvas* renderer) override {
-        SkPaint paint;
-        paint.setColor(0xFF00FFFF);
-        renderer->drawRect(0, 0, width, height, paint);
+    sp<RenderNode> card = new RenderNode();
+    void createContent(int width, int height, TestCanvas* canvas) override {
+        canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
 
-        card = createCard(40, 40, 200, 200);
-        renderer->drawRenderNode(card.get());
+        card->mutateStagingProperties().setLeftTopRightBottom(0, 0, 200, 200);
+        card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+        recordNode(*card, [](TestCanvas& canvas) {
+            canvas.saveLayerAlpha(0, 0, 200, 200, 128, SkCanvas::kClipToLayer_SaveFlag);
+            canvas.drawColor(0xFF00FF00, SkXfermode::kSrcOver_Mode); // outer, unclipped
+            canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
+            canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode); // inner, clipped
+            canvas.restore();
+            canvas.restore();
+        });
+
+        canvas->drawRenderNode(card.get());
     }
     void doFrame(int frameNr) override {
         int curFrame = frameNr % 150;
@@ -410,24 +394,10 @@
         card->mutateStagingProperties().setTranslationY(curFrame);
         card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
     }
-private:
-    sp<RenderNode> createCard(int x, int y, int width, int height) {
-        sp<RenderNode> node = new RenderNode();
-        node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
-        node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
-        TestCanvas* renderer = startRecording(node.get());
-        SkPaint paint;
-        paint.setColor(0xFFFF00FF);
-        renderer->drawRect(0, 0, width, height, paint);
-
-        endRecording(renderer, node.get());
-        return node;
-    }
 };
-static Benchmark _SimpleRectGrid(BenchmarkInfo{
-    "simplerectgrid",
-    "A simple collection of rects. "
-    "Low CPU/GPU load.",
-    TreeContentAnimation::run<SimpleRectGridAnimation>
+static Benchmark _SaveLayer(BenchmarkInfo{
+    "savelayer",
+    "A nested pair of clipped saveLayer operations. "
+    "Tests the clipped saveLayer codepath. Draws content into offscreen buffers and back again.",
+    TreeContentAnimation::run<SaveLayerAnimation>
 });
diff --git a/libs/hwui/unit_tests/BakedOpStateTests.cpp b/libs/hwui/unit_tests/BakedOpStateTests.cpp
index 82aebea..bc1b69f 100644
--- a/libs/hwui/unit_tests/BakedOpStateTests.cpp
+++ b/libs/hwui/unit_tests/BakedOpStateTests.cpp
@@ -85,13 +85,5 @@
     }
 }
 
-#define UNSUPPORTED_OP(Info, Type) \
-        static void on##Type(Info*, const Type&, const BakedOpState&) { FAIL(); }
-
-class Info {
-public:
-    int index = 0;
-};
-
 }
 }
diff --git a/libs/hwui/unit_tests/OpReordererTests.cpp b/libs/hwui/unit_tests/OpReordererTests.cpp
index d02f89d..af9c3f2 100644
--- a/libs/hwui/unit_tests/OpReordererTests.cpp
+++ b/libs/hwui/unit_tests/OpReordererTests.cpp
@@ -35,19 +35,22 @@
  * for every test.
  *
  * onXXXOp methods fail by default - tests should override ops they expect
+ * startLayer fails by default - tests should override if expected
  * startFrame/endFrame do nothing by default - tests should override to intercept
  */
 template<class CustomClient, class Arg>
 class TestReceiver {
 public:
 #define CLIENT_METHOD(Type) \
-    virtual void on##Type(Arg&, const Type&, const BakedOpState&) { FAIL(); }
+    virtual void on##Type(Arg&, const Type&, const BakedOpState&) { ADD_FAILURE(); }
     class Client {
     public:
         virtual ~Client() {};
         MAP_OPS(CLIENT_METHOD)
 
-        virtual void startFrame(Arg& info) {}
+        virtual Layer* startLayer(Arg& info, uint32_t width, uint32_t height) { ADD_FAILURE(); return nullptr; }
+        virtual void endLayer(Arg& info) { ADD_FAILURE(); }
+        virtual void startFrame(Arg& info, uint32_t width, uint32_t height) {}
         virtual void endFrame(Arg& info) {}
     };
 
@@ -57,11 +60,18 @@
     }
     MAP_OPS(DISPATCHER_METHOD)
 
-    static void startFrame(Arg& info) {
+    static Layer* startLayer(Arg& info, uint32_t width, uint32_t height) {
         CustomClient client;
-        client.startFrame(info);
+        return client.startLayer(info, width, height);
     }
-
+    static void endLayer(Arg& info) {
+        CustomClient client;
+        client.endLayer(info);
+    }
+    static void startFrame(Arg& info, uint32_t width, uint32_t height) {
+        CustomClient client;
+        client.startFrame(info, width, height);
+    }
     static void endFrame(Arg& info) {
         CustomClient client;
         client.endFrame(info);
@@ -78,8 +88,10 @@
 
 class SimpleReceiver : public TestReceiver<SimpleReceiver, Info>::Client {
 public:
-    void startFrame(Info& info) override {
+    void startFrame(Info& info, uint32_t width, uint32_t height) override {
         EXPECT_EQ(0, info.index++);
+        EXPECT_EQ(100u, width);
+        EXPECT_EQ(200u, height);
     }
     void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
         EXPECT_EQ(1, info.index++);
@@ -97,8 +109,7 @@
         canvas.drawRect(0, 0, 100, 200, SkPaint());
         canvas.drawBitmap(bitmap, 10, 10, nullptr);
     });
-    OpReorderer reorderer;
-    reorderer.defer(200, 200, *dl);
+    OpReorderer reorderer(100, 200, *dl);
 
     Info info;
     reorderer.replayBakedOps<TestReceiver<SimpleReceiver, Info>>(info);
@@ -113,8 +124,7 @@
         canvas.drawRect(0, 0, 400, 400, SkPaint());
         canvas.restore();
     });
-    OpReorderer reorderer;
-    reorderer.defer(200, 200, *dl);
+    OpReorderer reorderer(200, 200, *dl);
 
     Info info;
     reorderer.replayBakedOps<TestReceiver<FailReceiver, Info>>(info);
@@ -146,8 +156,7 @@
         canvas.restore();
     });
 
-    OpReorderer reorderer;
-    reorderer.defer(200, 200, *dl);
+    OpReorderer reorderer(200, 200, *dl);
 
     Info info;
     reorderer.replayBakedOps<TestReceiver<SimpleBatchingReceiver, Info>>(info);
@@ -167,7 +176,7 @@
             EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
             break;
         default:
-            FAIL();
+            ADD_FAILURE();
         }
     }
 };
@@ -196,8 +205,7 @@
     std::vector< sp<RenderNode> > nodes;
     nodes.push_back(parent.get());
 
-    OpReorderer reorderer;
-    reorderer.defer(SkRect::MakeWH(200, 200), 200, 200, nodes);
+    OpReorderer reorderer(SkRect::MakeWH(200, 200), 200, 200, nodes);
 
     Info info;
     reorderer.replayBakedOps<TestReceiver<RenderNodeReceiver, Info>>(info);
@@ -221,8 +229,7 @@
     std::vector< sp<RenderNode> > nodes;
     nodes.push_back(node.get());
 
-    OpReorderer reorderer;
-    reorderer.defer(SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
+    OpReorderer reorderer(SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
             200, 200, nodes);
 
     Info info;
@@ -232,8 +239,17 @@
 
 class SaveLayerSimpleReceiver : public TestReceiver<SaveLayerSimpleReceiver, Info>::Client {
 public:
-    void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+    Layer* startLayer(Info& info, uint32_t width, uint32_t height) override {
         EXPECT_EQ(0, info.index++);
+        EXPECT_EQ(180u, width);
+        EXPECT_EQ(180u, height);
+        return nullptr;
+    }
+    void endLayer(Info& info) override {
+        EXPECT_EQ(2, info.index++);
+    }
+    void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+        EXPECT_EQ(1, info.index++);
         EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
         EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clippedBounds);
         EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clipRect);
@@ -243,7 +259,7 @@
         EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
     }
     void onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) override {
-        EXPECT_EQ(1, info.index++);
+        EXPECT_EQ(3, info.index++);
         EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
         EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clipRect);
         EXPECT_TRUE(state.computedState.transform.isIdentity());
@@ -256,33 +272,61 @@
         canvas.restore();
     });
 
-    OpReorderer reorderer;
-    reorderer.defer(200, 200, *dl);
+    OpReorderer reorderer(200, 200, *dl);
 
     Info info;
     reorderer.replayBakedOps<TestReceiver<SaveLayerSimpleReceiver, Info>>(info);
-    EXPECT_EQ(2, info.index);
+    EXPECT_EQ(4, info.index);
 }
 
 
-// saveLayer1 {rect1, saveLayer2 { rect2 } } will play back as rect2, rect1, layerOp2, layerOp1
+/* saveLayer1 {rect1, saveLayer2 { rect2 } } will play back as:
+ * - startLayer2, rect2 endLayer2
+ * - startLayer1, rect1, drawLayer2, endLayer1
+ * - startFrame, layerOp1, endFrame
+ */
 class SaveLayerNestedReceiver : public TestReceiver<SaveLayerNestedReceiver, Info>::Client {
 public:
-    void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+    Layer* startLayer(Info& info, uint32_t width, uint32_t height) override {
         const int index = info.index++;
         if (index == 0) {
+            EXPECT_EQ(400u, width);
+            EXPECT_EQ(400u, height);
+            return (Layer*) 0x400;
+        } else if (index == 3) {
+            EXPECT_EQ(800u, width);
+            EXPECT_EQ(800u, height);
+            return (Layer*) 0x800;
+        } else { ADD_FAILURE(); }
+        return (Layer*) nullptr;
+    }
+    void endLayer(Info& info) override {
+        int index = info.index++;
+        EXPECT_TRUE(index == 2 || index == 6);
+    }
+    void startFrame(Info& info, uint32_t width, uint32_t height) override {
+        EXPECT_EQ(7, info.index++);
+    }
+    void endFrame(Info& info) override {
+        EXPECT_EQ(9, info.index++);
+    }
+    void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+        const int index = info.index++;
+        if (index == 1) {
             EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner rect
-        } else if (index == 1) {
+        } else if (index == 4) {
             EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer rect
-        } else { FAIL(); }
+        } else { ADD_FAILURE(); }
     }
     void onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) override {
         const int index = info.index++;
-        if (index == 2) {
+        if (index == 5) {
+            EXPECT_EQ((Layer*)0x400, *op.layerHandle);
             EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner layer
-        } else if (index == 3) {
+        } else if (index == 8) {
+            EXPECT_EQ((Layer*)0x800, *op.layerHandle);
             EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer layer
-        } else { FAIL(); }
+        } else { ADD_FAILURE(); }
     }
 };
 TEST(OpReorderer, saveLayerNested) {
@@ -299,12 +343,11 @@
         canvas.restore();
     });
 
-    OpReorderer reorderer;
-    reorderer.defer(800, 800, *dl);
+    OpReorderer reorderer(800, 800, *dl);
 
     Info info;
     reorderer.replayBakedOps<TestReceiver<SaveLayerNestedReceiver, Info>>(info);
-    EXPECT_EQ(4, info.index);
+    EXPECT_EQ(10, info.index);
 }
 
 TEST(OpReorderer, saveLayerContentRejection) {
@@ -319,8 +362,7 @@
         canvas.restore();
         canvas.restore();
     });
-    OpReorderer reorderer;
-    reorderer.defer(200, 200, *dl);
+    OpReorderer reorderer(200, 200, *dl);
     Info info;
 
     // should see no ops, even within the layer, since the layer should be rejected
diff --git a/libs/hwui/unit_tests/RecordingCanvasTests.cpp b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
index c023123..e8cdf46 100644
--- a/libs/hwui/unit_tests/RecordingCanvasTests.cpp
+++ b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
@@ -38,7 +38,7 @@
         canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
         canvas.restore();
     });
-    playbackOps(*dl, [](const RecordedOp& op) { FAIL(); });
+    playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
 }
 
 TEST(RecordingCanvas, testSimpleRectRecord) {
@@ -135,7 +135,7 @@
             // TODO: add asserts
             break;
         default:
-            FAIL();
+            ADD_FAILURE();
         }
     });
     EXPECT_EQ(3, count);
diff --git a/media/java/android/media/midi/package.html b/media/java/android/media/midi/package.html
index 45fb579..93dbf3f 100644
--- a/media/java/android/media/midi/package.html
+++ b/media/java/android/media/midi/package.html
@@ -180,7 +180,8 @@
             Log.e(TAG, "could not open device " + info);
         } else {
             ...
-        }, new Handler(Looper.getMainLooper())
+        }
+    }, new Handler(Looper.getMainLooper())
     );
 </pre>
 
diff --git a/opengl/java/android/opengl/GLES31.java b/opengl/java/android/opengl/GLES31.java
index 3cbaa60..805930e 100644
--- a/opengl/java/android/opengl/GLES31.java
+++ b/opengl/java/android/opengl/GLES31.java
@@ -24,9 +24,14 @@
 
     public static final int GL_VERTEX_SHADER_BIT                            = 0x00000001;
     public static final int GL_FRAGMENT_SHADER_BIT                          = 0x00000002;
+    public static final int GL_COMPUTE_SHADER_BIT                           = 0x00000020;
+    public static final int GL_ALL_SHADER_BITS                              = -1; // 0xFFFFFFFF
+
+    public static final int GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT              = 0x00000001;
+    public static final int GL_ELEMENT_ARRAY_BARRIER_BIT                    = 0x00000002;
     public static final int GL_UNIFORM_BARRIER_BIT                          = 0x00000004;
     public static final int GL_TEXTURE_FETCH_BARRIER_BIT                    = 0x00000008;
-    public static final int GL_COMPUTE_SHADER_BIT                           = 0x00000020;
+    public static final int GL_SHADER_IMAGE_ACCESS_BARRIER_BIT              = 0x00000020;
     public static final int GL_COMMAND_BARRIER_BIT                          = 0x00000040;
     public static final int GL_PIXEL_BUFFER_BARRIER_BIT                     = 0x00000080;
     public static final int GL_TEXTURE_UPDATE_BARRIER_BIT                   = 0x00000100;
@@ -35,7 +40,8 @@
     public static final int GL_TRANSFORM_FEEDBACK_BARRIER_BIT               = 0x00000800;
     public static final int GL_ATOMIC_COUNTER_BARRIER_BIT                   = 0x00001000;
     public static final int GL_SHADER_STORAGE_BARRIER_BIT                   = 0x00002000;
-    public static final int GL_ALL_SHADER_BITS                              = -1; // 0xFFFFFFFF
+    public static final int GL_ALL_BARRIER_BITS                             = -1; // 0xFFFFFFFF
+
 
     public static final int GL_TEXTURE_WIDTH                                = 0x1000;
     public static final int GL_TEXTURE_HEIGHT                               = 0x1001;
diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml
index 085df35..eba00a6 100644
--- a/packages/DocumentsUI/res/layout/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_list.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 
-<com.android.documentsui.DocListItem xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.documentsui.ListItem xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:background="@drawable/item_doc_list_background"
@@ -137,4 +137,4 @@
 
     </LinearLayout>
 
-</com.android.documentsui.DocListItem>
+</com.android.documentsui.ListItem>
diff --git a/packages/DocumentsUI/res/values/config.xml b/packages/DocumentsUI/res/values/config.xml
index 73538278..ad419aa 100644
--- a/packages/DocumentsUI/res/values/config.xml
+++ b/packages/DocumentsUI/res/values/config.xml
@@ -16,6 +16,6 @@
 
 <resources>
     <bool name="productivity_device">true</bool>
-    <!-- intentionally unset. Vendors should set this in an overlay. -->
+    <!-- Intentionally unset. Vendors should set this in an overlay. -->
     <string name="trusted_quick_viewer_package"></string>
 </resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 3b985ec..627ba75 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -334,12 +334,12 @@
                 startActivity(intent);
                 return;
             } catch (SecurityException e) {
-                // carry on to regular view mode.
+                // Carry on to regular view mode.
                 Log.e(TAG, "Caught security error: " + e.getLocalizedMessage());
             }
         }
 
-        // fallback to traditional VIEW action...
+        // Fallback to traditional VIEW action...
         intent = new Intent(Intent.ACTION_VIEW);
         intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
         intent.setData(doc.derivedUri);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
index 6a424a6..605c530 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
@@ -92,6 +92,8 @@
                 intent.setClipData(mClipData);
 
                 return intent;
+            } else {
+                Log.e(TAG, "Can't resolve trusted quick view package: " + trustedPkg);
             }
         }
 
diff --git a/packages/DocumentsUI/tests/Android.mk b/packages/DocumentsUI/tests/Android.mk
index 3f191a9..2a540d4 100644
--- a/packages/DocumentsUI/tests/Android.mk
+++ b/packages/DocumentsUI/tests/Android.mk
@@ -3,11 +3,12 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
+#LOCAL_SDK_VERSION := current
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 mockito-target guava
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 mockito-target guava ub-uiautomator
 
 LOCAL_PACKAGE_NAME := DocumentsUITests
 LOCAL_INSTRUMENTATION_FOR := DocumentsUI
@@ -15,3 +16,4 @@
 LOCAL_CERTIFICATE := platform
 
 include $(BUILD_PACKAGE)
+
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
new file mode 100644
index 0000000..1f4b751
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.Configurator;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.test.InstrumentationTestCase;
+import android.view.MotionEvent;
+
+import java.util.concurrent.TimeoutException;
+
+public class FilesActivityUiTest extends InstrumentationTestCase {
+
+    private static final String TAG = "FilesActivityUiTest";
+    private static final String TARGET_PKG = "com.android.documentsui";
+    private static final String LAUNCHER_PKG = "com.android.launcher";
+    private static final int ONE_SECOND = 1000;
+    private static final int FIVE_SECONDS = 5 * ONE_SECOND;
+
+    private ActionBar mBar;
+    private UiDevice mDevice;
+    private Context mContext;
+
+    public void setUp() throws TimeoutException {
+        // Initialize UiDevice instance.
+        mDevice = UiDevice.getInstance(getInstrumentation());
+
+        Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE);
+
+        // Start from the home screen.
+        mDevice.pressHome();
+        mDevice.wait(Until.hasObject(By.pkg(LAUNCHER_PKG).depth(0)), FIVE_SECONDS);
+
+        // Launch app.
+        mContext = getInstrumentation().getContext();
+        Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(TARGET_PKG);
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        mContext.startActivity(intent);
+
+        // Wait for the app to appear.
+        mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), FIVE_SECONDS);
+        mDevice.waitForIdle();
+
+        mBar = new ActionBar();
+    }
+
+    public void testSwitchMode() throws Exception {
+        UiObject2 mode = mBar.gridMode(100);
+        if (mode != null) {
+            mode.click();
+            assertNotNull(mBar.listMode(ONE_SECOND));
+        } else {
+            mBar.listMode(100).click();
+            assertNotNull(mBar.gridMode(ONE_SECOND));
+        }
+    }
+
+    private class ActionBar {
+
+        public UiObject2 gridMode(int timeout) {
+            // Note that we're using By.desc rather than By.res, because of b/25285770
+            BySelector selector = By.desc("Grid view");
+            if (timeout > 0) {
+                mDevice.wait(Until.findObject(selector), timeout);
+            }
+            return mDevice.findObject(selector);
+        }
+
+        public UiObject2 listMode(int timeout) {
+            // Note that we're using By.desc rather than By.res, because of b/25285770
+            BySelector selector = By.desc("List view");
+            if (timeout > 0) {
+                mDevice.wait(Until.findObject(selector), timeout);
+            }
+            return mDevice.findObject(selector);
+        }
+    }
+}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index 11e937b..df9d44a 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -2,6 +2,7 @@
 
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.res.Resources;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
@@ -30,10 +31,9 @@
 
     private static final String TABLE_MTP_DOCUMENTS = "MtpDocuments";
 
-    static final String COLUMN_DEVICE_ID = "deviceId";
-    static final String COLUMN_STORAGE_ID = "storageId";
-    static final String COLUMN_OBJECT_HANDLE = "objectHandle";
-    static final String COLUMN_FULL_PATH = "fullPath";
+    static final String COLUMN_DEVICE_ID = "device_id";
+    static final String COLUMN_STORAGE_ID = "storage_id";
+    static final String COLUMN_OBJECT_HANDLE = "object_handle";
 
     private static class OpenHelper extends SQLiteOpenHelper {
         private static final String CREATE_TABLE_QUERY =
@@ -43,7 +43,6 @@
                 COLUMN_DEVICE_ID + " INTEGER NOT NULL," +
                 COLUMN_STORAGE_ID + " INTEGER NOT NULL," +
                 COLUMN_OBJECT_HANDLE + " INTEGER," +
-                COLUMN_FULL_PATH + " TEXT NOT NULL," +
                 DocumentsContract.Document.COLUMN_MIME_TYPE + " TEXT," +
                 DocumentsContract.Document.COLUMN_DISPLAY_NAME + " TEXT NOT NULL," +
                 DocumentsContract.Document.COLUMN_SUMMARY + " TEXT," +
@@ -86,17 +85,15 @@
     }
 
     @VisibleForTesting
-    void putRootDocument(MtpRoot root) throws Exception {
+    void putRootDocument(Resources resources, MtpRoot root) throws Exception {
         database.beginTransaction();
         try {
             final ContentValues values = new ContentValues();
             values.put(COLUMN_DEVICE_ID, root.mDeviceId);
             values.put(COLUMN_STORAGE_ID, root.mStorageId);
             values.putNull(COLUMN_OBJECT_HANDLE);
-            values.put(
-                    COLUMN_FULL_PATH, "/" + root.mDeviceId + "/" + escape(root.mDescription));
             values.put(Document.COLUMN_MIME_TYPE, DocumentsContract.Document.MIME_TYPE_DIR);
-            values.put(Document.COLUMN_DISPLAY_NAME, root.mDescription);
+            values.put(Document.COLUMN_DISPLAY_NAME, root.getRootName(resources));
             values.putNull(Document.COLUMN_SUMMARY);
             values.putNull(Document.COLUMN_LAST_MODIFIED);
             values.putNull(Document.COLUMN_ICON);
@@ -113,7 +110,7 @@
     }
 
     @VisibleForTesting
-    void putDocument(int deviceId, String parentFullPath, MtpObjectInfo info) throws Exception {
+    void putDocument(int deviceId, MtpObjectInfo info) throws Exception {
         database.beginTransaction();
         try {
             final String mimeType = CursorHelper.formatTypeToMimeType(info.getFormat());
@@ -134,9 +131,7 @@
             values.put(COLUMN_DEVICE_ID, deviceId);
             values.put(COLUMN_STORAGE_ID, info.getStorageId());
             values.put(COLUMN_OBJECT_HANDLE, info.getObjectHandle());
-            values.put(COLUMN_FULL_PATH, parentFullPath + "/" + escape(info.getName()));
-            values.put(
-                    Document.COLUMN_MIME_TYPE, CursorHelper.formatTypeToMimeType(info.getFormat()));
+            values.put(Document.COLUMN_MIME_TYPE, mimeType);
             values.put(Document.COLUMN_DISPLAY_NAME, info.getName());
             values.putNull(Document.COLUMN_SUMMARY);
             values.put(
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index 7ce32542..8e1335f 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -1,6 +1,5 @@
 package com.android.mtp;
 
-
 import android.database.Cursor;
 import android.mtp.MtpConstants;
 import android.mtp.MtpObjectInfo;
@@ -15,7 +14,6 @@
         MtpDatabase.COLUMN_DEVICE_ID,
         MtpDatabase.COLUMN_STORAGE_ID,
         MtpDatabase.COLUMN_OBJECT_HANDLE,
-        MtpDatabase.COLUMN_FULL_PATH,
         DocumentsContract.Document.COLUMN_MIME_TYPE,
         DocumentsContract.Document.COLUMN_DISPLAY_NAME,
         DocumentsContract.Document.COLUMN_SUMMARY,
@@ -25,6 +23,8 @@
         DocumentsContract.Document.COLUMN_SIZE
     };
 
+    private final TestResources resources = new TestResources();
+
     @Override
     public void tearDown() {
         MtpDatabase.deleteDatabase(getContext());
@@ -32,35 +32,10 @@
 
     public void testPutRootDocument() throws Exception {
         final MtpDatabase database = new MtpDatabase(getContext());
-        final MtpRoot root = new MtpRoot(
-                0,
-                1,
-                "Device A",
-                "Storage",
-                1000,
-                2000,
-                "");
-        database.putRootDocument(root);
-
-        final MtpRoot duplicatedNameRoot = new MtpRoot(
-                0,
-                2,
-                "Device A",
-                "Storage",
-                1000,
-                2000,
-                "");
-        database.putRootDocument(duplicatedNameRoot);
-
-        final MtpRoot strangeNameRoot = new MtpRoot(
-                0,
-                3,
-                "Device A",
-                "/@#%&<>Storage",
-                1000,
-                2000,
-                "");
-        database.putRootDocument(strangeNameRoot);
+        database.putRootDocument(resources, new MtpRoot(0, 1, "Device", "Storage", 1000, 2000, ""));
+        database.putRootDocument(resources, new MtpRoot(0, 2, "Device", "Storage", 1000, 2000, ""));
+        database.putRootDocument(
+                resources, new MtpRoot(0, 3, "Device", "/@#%&<>Storage", 1000, 2000, ""));
 
         final Cursor cursor = database.queryChildDocuments(COLUMN_NAMES);
         assertEquals(3, cursor.getCount());
@@ -70,22 +45,23 @@
         assertEquals("deviceId", 0, cursor.getInt(1));
         assertEquals("storageId", 1, cursor.getInt(2));
         assertTrue("objectHandle", cursor.isNull(3));
-        assertEquals("fullPath", "/0/Storage", cursor.getString(4));
-        assertEquals("mimeType", DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(5));
-        assertEquals("displayName", "Storage", cursor.getString(6));
-        assertTrue("summary", cursor.isNull(7));
-        assertTrue("lastModified", cursor.isNull(8));
-        assertTrue("icon", cursor.isNull(9));
-        assertEquals("flag", 0, cursor.getInt(10));
-        assertEquals("size", 1000, cursor.getInt(11));
+        assertEquals("mimeType", DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(4));
+        assertEquals("displayName", "Device Storage", cursor.getString(5));
+        assertTrue("summary", cursor.isNull(6));
+        assertTrue("lastModified", cursor.isNull(7));
+        assertTrue("icon", cursor.isNull(8));
+        assertEquals("flag", 0, cursor.getInt(9));
+        assertEquals("size", 1000, cursor.getInt(10));
 
         cursor.moveToNext();
         assertEquals("documentId", 2, cursor.getInt(0));
-        assertEquals("fullPath", "/0/Storage", cursor.getString(4));
+        assertEquals("displayName", "Device Storage", cursor.getString(5));
 
         cursor.moveToNext();
         assertEquals("documentId", 3, cursor.getInt(0));
-        assertEquals("fullPath", "/0/%2F%40%23%25%26%3C%3EStorage", cursor.getString(4));
+        assertEquals("displayName", "Device /@#%&<>Storage", cursor.getString(5));
+
+        cursor.close();
     }
 
     public void testPutDocument() throws Exception {
@@ -96,7 +72,7 @@
         builder.setStorageId(5);
         builder.setFormat(MtpConstants.FORMAT_TEXT);
         builder.setCompressedSize(1000);
-        database.putDocument(0, "/0/Storage", builder.build());
+        database.putDocument(0, builder.build());
 
         final Cursor cursor = database.queryChildDocuments(COLUMN_NAMES);
         assertEquals(1, cursor.getCount());
@@ -105,17 +81,16 @@
         assertEquals("deviceId", 0, cursor.getInt(1));
         assertEquals("storageId", 5, cursor.getInt(2));
         assertEquals("objectHandle", 100, cursor.getInt(3));
-        assertEquals("fullPath", "/0/Storage/test.txt", cursor.getString(4));
-        assertEquals("mimeType", "text/plain", cursor.getString(5));
-        assertEquals("displayName", "test.txt", cursor.getString(6));
-        assertTrue("summary", cursor.isNull(7));
-        assertTrue("lastModified", cursor.isNull(8));
-        assertTrue("icon", cursor.isNull(9));
+        assertEquals("mimeType", "text/plain", cursor.getString(4));
+        assertEquals("displayName", "test.txt", cursor.getString(5));
+        assertTrue("summary", cursor.isNull(6));
+        assertTrue("lastModified", cursor.isNull(7));
+        assertTrue("icon", cursor.isNull(8));
         assertEquals(
                 "flag",
                 DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
                 DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
-                cursor.getInt(10));
-        assertEquals("size", 1000, cursor.getInt(11));
+                cursor.getInt(9));
+        assertEquals("size", 1000, cursor.getInt(10));
     }
 }
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 0c03814c..5765f0a 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -16,9 +16,6 @@
 
 package com.android.mtp;
 
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
 import android.database.Cursor;
 import android.mtp.MtpConstants;
 import android.mtp.MtpObjectInfo;
@@ -26,7 +23,6 @@
 import android.provider.DocumentsContract.Root;
 import android.provider.DocumentsContract;
 import android.test.AndroidTestCase;
-import android.test.mock.MockResources;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import java.io.FileNotFoundException;
@@ -39,16 +35,7 @@
     private TestContentResolver mResolver;
     private MtpDocumentsProvider mProvider;
     private TestMtpManager mMtpManager;
-    private final MockResources mResources = new MockResources() {
-        @Override
-        public String getString(int id) throws NotFoundException {
-            switch (id) {
-                case R.string.root_name:
-                    return "%1$s %2$s";
-            }
-            throw new NotFoundException();
-        }
-    };
+    private final TestResources mResources = new TestResources();
 
     @Override
     public void setUp() throws IOException {
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
new file mode 100644
index 0000000..eb80e3b
--- /dev/null
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mtp;
+
+import android.test.mock.MockResources;
+
+class TestResources extends MockResources {
+    @Override
+    public String getString(int id) throws NotFoundException {
+        switch (id) {
+            case R.string.root_name:
+                return "%1$s %2$s";
+        }
+        throw new NotFoundException();
+    }
+}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 9b1f103..c44a7960 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -225,4 +225,10 @@
 
     <!-- [CHAR LIMIT=NONE] Label of a running process that represents another user -->
     <string name="running_process_item_user_label">User: <xliff:g id="user_name">%1$s</xliff:g></string>
+
+    <!-- Launch defaults preference summary with some set [CHAR LIMIT=40] -->
+    <string name="launch_defaults_some">Some defaults set</string>
+    <!-- Launch defaults preference summary with none set [CHAR LIMIT=40] -->
+    <string name="launch_defaults_none">No defaults set</string>
+
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
new file mode 100644
index 0000000..344bf62
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settingslib.applications;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.hardware.usb.IUsbManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AppUtils {
+    private static final String TAG = "AppUtils";
+
+    public static CharSequence getLaunchByDefaultSummary(ApplicationsState.AppEntry appEntry,
+            IUsbManager usbManager, PackageManager pm, Context context) {
+        String packageName = appEntry.info.packageName;
+        boolean hasPreferred = hasPreferredActivities(pm, packageName)
+                || hasUsbDefaults(usbManager, packageName);
+        int status = pm.getIntentVerificationStatus(packageName, UserHandle.myUserId());
+        // consider a visible current link-handling state to be any explicitly designated behavior
+        boolean hasDomainURLsPreference =
+                status != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+        return context.getString(hasPreferred || hasDomainURLsPreference
+                ? R.string.launch_defaults_some
+                : R.string.launch_defaults_none);
+    }
+
+    public static boolean hasUsbDefaults(IUsbManager usbManager, String packageName) {
+        try {
+            if (usbManager != null) {
+                return usbManager.hasDefaults(packageName, UserHandle.myUserId());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "mUsbManager.hasDefaults", e);
+        }
+        return false;
+    }
+
+    public static boolean hasPreferredActivities(PackageManager pm, String packageName) {
+        // Get list of preferred activities
+        List<ComponentName> prefActList = new ArrayList<>();
+        // Intent list cannot be null. so pass empty list
+        List<IntentFilter> intentList = new ArrayList<>();
+        pm.getPreferredActivities(intentList, prefActList, packageName);
+        Log.d(TAG, "Have " + prefActList.size() + " number of activities in preferred list");
+        return prefActList.size() > 0;
+    }
+
+}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3a5680d..7c86f96 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -999,9 +999,7 @@
     <!-- Screen pinning dialog title. -->
     <string name="screen_pinning_title">Screen is pinned</string>
     <!-- Screen pinning dialog description. -->
-    <string name="screen_pinning_description">This keeps it in view until you unpin. Touch and hold Back and Overview at the same time to unpin.</string>
-    <!-- Screen pinning dialog description when in accessibility mode. -->
-    <string name="screen_pinning_description_accessible">This keeps it in view until you unpin. Touch and hold Overview to unpin.</string>
+    <string name="screen_pinning_description">This keeps it in view until you unpin. Touch and hold Back to unpin.</string>
     <!-- Screen pinning positive response. -->
     <string name="screen_pinning_positive">Got it</string>
     <!-- Screen pinning negative response. -->
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index 1d0bfe7..9a4cd93 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -27,4 +27,9 @@
     void cancelPreloadingRecents();
     void showNextAffiliatedTask();
     void showPrevAffiliatedTask();
+
+    /**
+     * Docks the top-most task and opens recents.
+     */
+    void dockTopTask();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index c216f97..9400108 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -284,6 +284,11 @@
     }
 
     @Override
+    public void dockTopTask() {
+        mImpl.dockTopTask();
+    }
+
+    @Override
     public void showNextAffiliatedTask() {
         mImpl.showNextAffiliatedTask();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index cea0b53..2c8937a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -418,6 +418,16 @@
         showRelativeAffiliatedTask(false);
     }
 
+    public void dockTopTask() {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
+        if (topTask != null && !ssp.isInHomeStack(topTask.id)) {
+            ssp.moveTaskToDockedStack(topTask.id,
+                    ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT);
+            showRecents(false /* triggeredFromAltTab */);
+        }
+    }
+
     /**
      * Returns the preloaded load plan and invalidates it.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
index 31ee8ad..28299d3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
@@ -234,14 +234,6 @@
         dismissAllowingStateLoss();
         mRecentsActivity.dismissRecentsToHomeWithoutTransitionAnimation();
 
-        // In debug mode, we force all task to be resizeable regardless of the
-        // current app configuration.
-        for (int i = additionalTasks; i >= 0; --i) {
-            if (mTasks[i] != null) {
-                ssp.setTaskResizeable(mTasks[i].key.id);
-            }
-        }
-
         // Show tasks as they might not be currently visible - beginning with the oldest so that
         // the focus ends on the selected one.
         for (int i = additionalTasks; i >= 0; --i) {
@@ -277,8 +269,7 @@
         if (mTasks[0].key.stackId != DOCKED_STACK_ID) {
             int taskId = mTasks[0].key.id;
             SystemServicesProxy ssp = Recents.getSystemServices();
-            ssp.setTaskResizeable(taskId);
-            ssp.dockTask(taskId, createMode);
+            ssp.startTaskInDockedMode(taskId, createMode);
             mRecentsView.launchTask(mTasks[0], null, DOCKED_STACK_ID);
         } else {
             Toast.makeText(getContext(), "Already docked", Toast.LENGTH_SHORT);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 231843e..10075bc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -214,11 +214,8 @@
                         .setVisibility(View.INVISIBLE);
             }
 
-            final int description = mAccessibilityService.isEnabled()
-                    ? R.string.screen_pinning_description_accessible
-                    : R.string.screen_pinning_description;
             ((TextView) mLayout.findViewById(R.id.screen_pinning_description))
-                    .setText(description);
+                    .setText(R.string.screen_pinning_description);
             final int backBgVisibility =
                     mAccessibilityService.isEnabled() ? View.INVISIBLE : View.VISIBLE;
             mLayout.findViewById(R.id.screen_pinning_back_bg).setVisibility(backBgVisibility);
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 a51e475..221af15 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -266,17 +266,6 @@
         return null;
     }
 
-    /** Allow a task to resize. */
-    public void setTaskResizeable(int taskId) {
-        if (mIam == null) return;
-
-        try {
-            mIam.setTaskResizeable(taskId, true);
-        } catch (RemoteException e) {
-            e.printStackTrace();
-        }
-    }
-
     /**
      * Resizes the given task to the new bounds.
      */
@@ -290,8 +279,8 @@
         }
     }
 
-    /** Docks a task to the side of the screen. */
-    public void dockTask(int taskId, int createMode) {
+    /** Docks a task to the side of the screen and starts it. */
+    public void startTaskInDockedMode(int taskId, int createMode) {
         if (mIam == null) return;
 
         try {
@@ -301,6 +290,17 @@
         }
     }
 
+    /** Docks an already resumed task to the side of the screen. */
+    public void moveTaskToDockedStack(int taskId, int createMode) {
+        if (mIam == null) return;
+
+        try {
+            mIam.moveTaskToDockedStack(taskId, createMode, true /* onTop */);
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+    }
+
     /** Returns the focused stack id. */
     public int getFocusedStack() {
         if (mIam == null) return -1;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index ad0d758..d5d0713 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -785,8 +785,7 @@
                 // Dock the new task if we are hovering over a valid dock state
                 if (event.dockState != TaskStack.DockState.NONE) {
                     SystemServicesProxy ssp = Recents.getSystemServices();
-                    ssp.setTaskResizeable(event.task.key.id);
-                    ssp.dockTask(event.task.key.id, event.dockState.createMode);
+                    ssp.startTaskInDockedMode(event.task.key.id, event.dockState.createMode);
                     launchTask(event.task, null, INVALID_STACK_ID);
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 8938669..0def599 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -217,7 +217,7 @@
 
     private boolean mDeviceProvisioned = false;
 
-    private RecentsComponent mRecents;
+    protected RecentsComponent mRecents;
 
     protected int mZenMode;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index cfde791..cfdb01e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -88,7 +88,6 @@
 import android.view.ViewStub;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
-import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.Interpolator;
@@ -146,7 +145,6 @@
 import com.android.systemui.statusbar.policy.FullscreenUserSwitcher;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.HotspotControllerImpl;
-import com.android.systemui.statusbar.policy.KeyButtonView;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.LocationControllerImpl;
@@ -1115,13 +1113,22 @@
         }
     };
 
-    private long mLastLockToAppLongPress;
-    private View.OnLongClickListener mLongPressBackRecentsListener =
-            new View.OnLongClickListener() {
+    private View.OnLongClickListener mLongPressBackListener = new View.OnLongClickListener() {
         @Override
         public boolean onLongClick(View v) {
-            handleLongPressBackRecents(v);
-            return true;
+            return handleLongPressBack();
+        }
+    };
+
+    private View.OnLongClickListener mRecentsLongClickListener = new View.OnLongClickListener() {
+
+        @Override
+        public boolean onLongClick(View v) {
+            if (mRecents != null) {
+                mRecents.dockTopTask();
+                return true;
+            }
+            return false;
         }
     };
 
@@ -1170,9 +1177,9 @@
         mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
         mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
         mNavigationBarView.getRecentsButton().setLongClickable(true);
-        mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);
+        mNavigationBarView.getRecentsButton().setOnLongClickListener(mRecentsLongClickListener);
         mNavigationBarView.getBackButton().setLongClickable(true);
-        mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
+        mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackListener);
         mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
         mNavigationBarView.getHomeButton().setOnLongClickListener(mLongPressHomeListener);
         mAssistManager.onConfigurationChanged();
@@ -4048,7 +4055,7 @@
 
     private void vibrateForCameraGesture() {
         // Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep.
-        mVibrator.vibrate(new long[] { 0, 750L }, -1 /* repeat */);
+        mVibrator.vibrate(new long[]{0, 750L}, -1 /* repeat */);
     }
 
     public void onScreenTurnedOn() {
@@ -4057,58 +4064,22 @@
     }
 
     /**
-     * This handles long-press of both back and recents.  They are
-     * handled together to capture them both being long-pressed
-     * at the same time to exit screen pinning (lock task).
-     *
-     * When accessibility mode is on, only a long-press from recents
-     * is required to exit.
-     *
-     * In all other circumstances we try to pass through long-press events
-     * for Back, so that apps can still use it.  Which can be from two things.
-     * 1) Not currently in screen pinning (lock task).
-     * 2) Back is long-pressed without recents.
+     * Handles long press for back button. This exits screen pinning.
      */
-    private void handleLongPressBackRecents(View v) {
+    private boolean handleLongPressBack() {
         try {
-            boolean sendBackLongPress = false;
             IActivityManager activityManager = ActivityManagerNative.getDefault();
-            boolean isAccessiblityEnabled = mAccessibilityManager.isEnabled();
-            if (activityManager.isInLockTaskMode() && !isAccessiblityEnabled) {
-                long time = System.currentTimeMillis();
-                // If we recently long-pressed the other button then they were
-                // long-pressed 'together'
-                if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
-                    activityManager.stopLockTaskModeOnCurrent();
-                    // When exiting refresh disabled flags.
-                    mNavigationBarView.setDisabledFlags(mDisabled1, true);
-                } else if ((v.getId() == R.id.back)
-                        && !mNavigationBarView.getRecentsButton().isPressed()) {
-                    // If we aren't pressing recents right now then they presses
-                    // won't be together, so send the standard long-press action.
-                    sendBackLongPress = true;
-                }
-                mLastLockToAppLongPress = time;
-            } else {
-                // If this is back still need to handle sending the long-press event.
-                if (v.getId() == R.id.back) {
-                    sendBackLongPress = true;
-                } else if (isAccessiblityEnabled && activityManager.isInLockTaskMode()) {
-                    // When in accessibility mode a long press that is recents (not back)
-                    // should stop lock task.
-                    activityManager.stopLockTaskModeOnCurrent();
-                    // When exiting refresh disabled flags.
-                    mNavigationBarView.setDisabledFlags(mDisabled1, true);
-                }
-            }
-            if (sendBackLongPress) {
-                KeyButtonView keyButtonView = (KeyButtonView) v;
-                keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
-                keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
+            if (activityManager.isInLockTaskMode()) {
+                activityManager.stopLockTaskModeOnCurrent();
+
+                // When exiting refresh disabled flags.
+                mNavigationBarView.setDisabledFlags(mDisabled1, true);
+                return true;
             }
         } catch (RemoteException e) {
             Log.d(TAG, "Unable to reach activity manager", e);
         }
+        return false;
     }
 
     // Recents
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 496e4df..927b995 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -1500,7 +1500,7 @@
                 mState = STATE_SENSING;
                 if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING.");
                 EventLogTags.writeDeviceIdle(mState, "step");
-                cancelAlarmLocked();
+                scheduleAlarmLocked(mConstants.SENSING_TIMEOUT, false);
                 cancelLocatingLocked();
                 mAnyMotionDetector.checkForAnyMotion();
                 mNotMoving = false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c728b39..257f034 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -24,6 +24,7 @@
 import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.HOME_STACK_ID;
 import static android.app.ActivityManager.INVALID_STACK_ID;
+import static android.app.ActivityManager.PINNED_STACK_ID;
 import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
@@ -74,6 +75,7 @@
 import android.os.storage.IMountService;
 import android.os.storage.MountServiceInternal;
 import android.os.storage.StorageManager;
+import android.provider.Settings.Global;
 import android.service.voice.IVoiceInteractionSession;
 import android.service.voice.VoiceInteractionSession;
 import android.util.ArrayMap;
@@ -1188,6 +1190,7 @@
     String mOrigDebugApp = null;
     boolean mOrigWaitForDebugger = false;
     boolean mAlwaysFinishActivities = false;
+    boolean mForceResizableActivites;
     IActivityController mController = null;
     String mProfileApp = null;
     ProcessRecord mProfileProc = null;
@@ -8638,9 +8641,10 @@
                 // - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
                 //   that task to freeform
                 // - otherwise the task is not moved
-                // Note it's not allowed to resize a home stack task, or a docked task.
+                // Note it's not allowed to resize a home, docked, or pinned stack task.
                 int stackId = task.stack.mStackId;
-                if (stackId == HOME_STACK_ID || stackId == DOCKED_STACK_ID) {
+                if (stackId == HOME_STACK_ID || stackId == DOCKED_STACK_ID
+                        || stackId == PINNED_STACK_ID) {
                     throw new IllegalArgumentException("trying to resizeTask on a "
                             + "home or docked task");
                 }
@@ -9081,6 +9085,29 @@
         }
     }
 
+    /**
+     * Moves the top activity in the input stackId to the pinned stack.
+     *
+     * @param stackId Id of stack to move the top activity to pinned stack.
+     * @param bounds Bounds to use for pinned stack.
+     *
+     * @return True if the top activity of the input stack was successfully moved to the pinned
+     *          stack.
+     */
+    @Override
+    public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
+        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                "moveTopActivityToPinnedStack()");
+        synchronized (this) {
+            long ident = Binder.clearCallingIdentity();
+            try {
+                return mStackSupervisor.moveTopStackActivityToPinnedStackLocked(stackId, bounds);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
     @Override
     public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode) {
         enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
@@ -11613,14 +11640,17 @@
 
     private void retrieveSettings() {
         final ContentResolver resolver = mContext.getContentResolver();
-        String debugApp = Settings.Global.getString(
-            resolver, Settings.Global.DEBUG_APP);
+        String debugApp = Settings.Global.getString(resolver, Settings.Global.DEBUG_APP);
         boolean waitForDebugger = Settings.Global.getInt(
             resolver, Settings.Global.WAIT_FOR_DEBUGGER, 0) != 0;
         boolean alwaysFinishActivities = Settings.Global.getInt(
             resolver, Settings.Global.ALWAYS_FINISH_ACTIVITIES, 0) != 0;
         boolean forceRtl = Settings.Global.getInt(
                 resolver, Settings.Global.DEVELOPMENT_FORCE_RTL, 0) != 0;
+        int defaultForceResizable = Build.IS_DEBUGGABLE ? 1 : 0;
+        boolean forceResizable = Settings.Global.getInt(
+                resolver, Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
+                defaultForceResizable) != 0;
         // Transfer any global setting for forcing RTL layout, into a System Property
         SystemProperties.set(Settings.Global.DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
 
@@ -11635,6 +11665,7 @@
             mDebugApp = mOrigDebugApp = debugApp;
             mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger;
             mAlwaysFinishActivities = alwaysFinishActivities;
+            mForceResizableActivites = forceResizable;
             // This happens before any activities are started, so we can
             // change mConfiguration in-place.
             updateConfigurationLocked(configuration, null, true);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8bf1d22..99539e4 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -22,6 +22,7 @@
 import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.HOME_STACK_ID;
 import static android.app.ActivityManager.LAST_STATIC_STACK_ID;
+import static android.app.ActivityManager.PINNED_STACK_ID;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
 
 import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -253,6 +254,9 @@
 
     private final LaunchingTaskPositioner mTaskPositioner;
 
+    // If the bounds of task contained in this stack should be persisted across power cycles.
+    final boolean mPersistTaskBounds;
+
     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;
@@ -354,10 +358,6 @@
         return count;
     }
 
-    int numTasks() {
-        return mTaskHistory.size();
-    }
-
     ActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer,
             RecentTasks recentTasks) {
         mActivityContainer = activityContainer;
@@ -370,6 +370,7 @@
         mRecentTasks = recentTasks;
         mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
                 ? new LaunchingTaskPositioner() : null;
+        mPersistTaskBounds = mStackId != DOCKED_STACK_ID && mStackId != PINNED_STACK_ID;
     }
 
     void attachDisplay(ActivityStackSupervisor.ActivityDisplay activityDisplay, boolean onTop) {
@@ -1306,6 +1307,11 @@
             return true;
         }
 
+        if (mStackId == PINNED_STACK_ID) {
+            // Pinned stack is always visible if it exist.
+            return true;
+        }
+
         final int stackIndex = mStacks.indexOf(this);
 
         if (stackIndex == mStacks.size() - 1) {
@@ -1328,8 +1334,9 @@
         }
 
         final int belowFocusedIndex = mStacks.indexOf(focusedStack) - 1;
-        if (focusedStackId == DOCKED_STACK_ID && stackIndex == belowFocusedIndex) {
-            // Stacks directly behind the docked stack are always visible.
+        if ((focusedStackId == DOCKED_STACK_ID || focusedStackId == PINNED_STACK_ID)
+                && stackIndex == belowFocusedIndex) {
+            // Stacks directly behind the docked or pinned stack are always visible.
             return true;
         }
 
@@ -1343,9 +1350,10 @@
             }
             if (belowFocusedIndex >= 0) {
                 final ActivityStack stack = mStacks.get(belowFocusedIndex);
-                if (stack.mStackId == DOCKED_STACK_ID && stackIndex == (belowFocusedIndex - 1)) {
-                    // The stack behind the docked stack is also visible so we can have a complete
-                    // backdrop to the translucent activity when the docked stack is up.
+                if ((stack.mStackId == DOCKED_STACK_ID || stack.mStackId == PINNED_STACK_ID)
+                        && stackIndex == (belowFocusedIndex - 1)) {
+                    // The stack behind the docked or pinned stack is also visible so we can have a
+                    // complete backdrop to the translucent activity when the docked stack is up.
                     return true;
                 }
             }
@@ -2784,9 +2792,10 @@
             final String myReason = reason + " adjustFocus";
             if (next != r) {
                 if (next != null && (mStackId == FREEFORM_WORKSPACE_STACK_ID
-                        || mStackId == DOCKED_STACK_ID)) {
-                    // For freeform and docked stacks we always keep the focus within the stack as
-                    // long as there is a running activity in the stack that we can adjust focus to.
+                        || mStackId == DOCKED_STACK_ID || mStackId == PINNED_STACK_ID)) {
+                    // For freeform, docked, and pinned stacks we always keep the focus within the
+                    // stack as long as there is a running activity in the stack that we can adjust
+                    // focus to.
                     mService.setFocusedActivityLocked(next, myReason);
                     return;
                 } else {
@@ -4633,6 +4642,51 @@
         r.taskConfigOverride = task.mOverrideConfig;
     }
 
+    void setFocusAndResumeStateIfNeeded(
+            ActivityRecord r, boolean setFocus, boolean setResume, String reason) {
+        // If the activity had focus before move focus to this stack.
+        if (setFocus) {
+            // If the activity owns the last resumed activity, transfer that together,
+            // so that we don't resume the same activity again in the new stack.
+            // Apps may depend on onResume()/onPause() being called in pairs.
+            if (setResume) {
+                mResumedActivity = r;
+                // Move the stack in which we are placing the activity to the front. We don't use
+                // ActivityManagerService.setFocusedActivityLocked, because if the activity is
+                // already focused, the call will short-circuit and do nothing.
+                moveToFront(reason);
+            } else {
+                // We need to not only move the stack to the front, but also have the activity
+                // focused. This will achieve both goals.
+                mService.setFocusedActivityLocked(r, reason);
+            }
+        }
+    }
+
+    /**
+     * Moves the input activity from its current stack to this one.
+     * NOTE: The current task of the activity isn't moved to this stack. Instead a new task is
+     * created on this stack which the activity is added to.
+     * */
+    void moveActivityToStack(ActivityRecord r) {
+        final ActivityStack prevStack = r.task.stack;
+        if (prevStack.mStackId == mStackId) {
+            // You are already in the right stack silly...
+            return;
+        }
+
+        final boolean wasFocused = mStackSupervisor.isFrontStack(prevStack)
+                && (mStackSupervisor.topRunningActivityLocked() == r);
+        final boolean wasResumed = wasFocused && (prevStack.mResumedActivity == r);
+
+        final TaskRecord task = createTaskRecord(
+                mStackSupervisor.getNextTaskId(), r.info, r.intent, null, null, true);
+        r.setTask(task, null);
+        task.addActivityToTop(r);
+        setAppTask(r, task);
+        setFocusAndResumeStateIfNeeded(r, wasFocused, wasResumed, "moveActivityToStack");
+    }
+
     private void setAppTask(ActivityRecord r, TaskRecord task) {
         final Rect bounds = task.getLaunchBounds();
         task.updateOverrideConfiguration(bounds);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 99f7ec6..cd2f124 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -295,10 +295,6 @@
     /** Set when we have taken too long waiting to go to sleep. */
     boolean mSleepTimeout = false;
 
-    /** Indicates if we are running on a Leanback-only (TV) device. Only initialized after
-     * setWindowManager is called. **/
-    private boolean mLeanbackOnlyDevice;
-
     /**
      * We don't want to allow the device to go to sleep while in the process
      * of launching an activity.  This is primarily to allow alarm intent
@@ -446,9 +442,6 @@
             mHomeStack = mFocusedStack = mLastFocusedStack = getStack(HOME_STACK_ID);
 
             mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
-
-            // Initialize this here, now that we can get a valid reference to PackageManager.
-            mLeanbackOnlyDevice = isLeanbackOnlyDevice();
         }
     }
 
@@ -1780,70 +1773,68 @@
     ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds) {
         final TaskRecord task = r.task;
 
-        // On leanback only devices we should keep all activities in the same stack.
-        if (!mLeanbackOnlyDevice &&
-                (r.isApplicationActivity() || (task != null && task.isApplicationTask()))) {
+        if (!(r.isApplicationActivity() || (task != null && task.isApplicationTask()))) {
+            return mHomeStack;
+        }
 
-            ActivityStack stack;
+        ActivityStack stack;
 
-            if (task != null && task.stack != null) {
-                stack = task.stack;
-                if (stack.isOnHomeDisplay()) {
-                    if (mFocusedStack != stack) {
-                        if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                                "computeStackFocus: Setting " + "focused stack to r=" + r
-                                + " task=" + task);
-                    } else {
-                        if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                            "computeStackFocus: Focused stack already=" + mFocusedStack);
-                    }
-                }
-                return stack;
-            }
-
-            final ActivityContainer container = r.mInitialActivityContainer;
-            if (container != null) {
-                // The first time put it on the desired stack, after this put on task stack.
-                r.mInitialActivityContainer = null;
-                return container.mStack;
-            }
-
-            // The fullscreen stack can contain any task regardless of if the task is resizeable
-            // or not. So, we let the task go in the fullscreen task if it is the focus stack.
-            // If the freeform stack has focus, and the activity to be launched is resizeable,
-            // we can also put it in the focused stack.
-            final boolean canUseFocusedStack =
-                    mFocusedStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID
-                    || mFocusedStack.mStackId == FREEFORM_WORKSPACE_STACK_ID && r.info.resizeable;
-            if (canUseFocusedStack
-                    && (!newTask || mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
-                if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                        "computeStackFocus: Have a focused stack=" + mFocusedStack);
-                return mFocusedStack;
-            }
-
-            // We first try to put the task in the first dynamic stack.
-            final ArrayList<ActivityStack> homeDisplayStacks = mHomeStack.mStacks;
-            for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-                stack = homeDisplayStacks.get(stackNdx);
-                final boolean isDynamicStack = stack.mStackId >= FIRST_DYNAMIC_STACK_ID;
-                if (isDynamicStack) {
+        if (task != null && task.stack != null) {
+            stack = task.stack;
+            if (stack.isOnHomeDisplay()) {
+                if (mFocusedStack != stack) {
                     if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                            "computeStackFocus: Setting focused stack=" + stack);
-                    return stack;
+                            "computeStackFocus: Setting " + "focused stack to r=" + r
+                            + " task=" + task);
+                } else {
+                    if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+                        "computeStackFocus: Focused stack already=" + mFocusedStack);
                 }
             }
-
-            // If there is no suitable dynamic stack then we figure out which static stack to use.
-            final int stackId = task != null ? task.getLaunchStackId() :
-                        bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
-                                         FULLSCREEN_WORKSPACE_STACK_ID;
-            stack = getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
-            if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
-                    + r + " stackId=" + stack.mStackId);
             return stack;
         }
-        return mHomeStack;
+
+        final ActivityContainer container = r.mInitialActivityContainer;
+        if (container != null) {
+            // The first time put it on the desired stack, after this put on task stack.
+            r.mInitialActivityContainer = null;
+            return container.mStack;
+        }
+
+        // The fullscreen stack can contain any task regardless of if the task is resizeable
+        // or not. So, we let the task go in the fullscreen task if it is the focus stack.
+        // If the freeform stack has focus, and the activity to be launched is resizeable,
+        // we can also put it in the focused stack.
+        final boolean canUseFocusedStack =
+                mFocusedStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID
+                || mFocusedStack.mStackId == FREEFORM_WORKSPACE_STACK_ID && r.info.resizeable;
+        if (canUseFocusedStack
+                && (!newTask || mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
+            if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+                    "computeStackFocus: Have a focused stack=" + mFocusedStack);
+            return mFocusedStack;
+        }
+
+        // We first try to put the task in the first dynamic stack.
+        final ArrayList<ActivityStack> homeDisplayStacks = mHomeStack.mStacks;
+        for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+            stack = homeDisplayStacks.get(stackNdx);
+            final boolean isDynamicStack = stack.mStackId >= FIRST_DYNAMIC_STACK_ID;
+            if (isDynamicStack) {
+                if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+                        "computeStackFocus: Setting focused stack=" + stack);
+                return stack;
+            }
+        }
+
+        // If there is no suitable dynamic stack then we figure out which static stack to use.
+        final int stackId = task != null ? task.getLaunchStackId() :
+                    bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
+                                     FULLSCREEN_WORKSPACE_STACK_ID;
+        stack = getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
+        if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
+                + r + " stackId=" + stack.mStackId);
+        return stack;
     }
 
     boolean setFocusedStack(ActivityRecord r, String reason) {
@@ -2758,8 +2749,7 @@
         boolean didSomething = false;
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
-            final int numStacks = stacks.size();
-            for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = stacks.get(stackNdx);
                 if (stack.finishDisabledPackageActivitiesLocked(
                         packageName, filterByClasses, doit, evenPersistent, userId)) {
@@ -2885,13 +2875,16 @@
         }
 
         if (stackId != task.stack.mStackId) {
-            moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, FORCE_FOCUS, reason);
-        } else {
-            task.stack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
-                task.getTopActivity() == null ? null : task.getTopActivity().appTimeTracker,
-                reason);
+            moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, !FORCE_FOCUS, reason);
+
+            // moveTaskToStackUncheckedLocked() should already placed the task on top,
+            // still need moveTaskToFrontLocked() below for any transition settings.
         }
 
+        final ActivityRecord r = task.getTopActivity();
+        task.stack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
+                r == null ? null : r.appTimeTracker, reason);
+
         if (DEBUG_STACK) Slog.d(TAG_STACK,
                 "findTaskToMoveToFront: moved to front of stack=" + task.stack);
     }
@@ -2998,84 +2991,87 @@
         }
 
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
+        mWindowManager.deferSurfaceLayout();
+        try {
+            ActivityRecord r = stack.topRunningActivityLocked();
 
-        ActivityRecord r = stack.topRunningActivityLocked();
-
-        mTmpBounds.clear();
-        mTmpConfigs.clear();
-        ArrayList<TaskRecord> tasks = stack.getAllTasks();
-        for (int i = tasks.size() - 1; i >= 0; i--) {
-            TaskRecord task = tasks.get(i);
-            if (task.mResizeable) {
-                if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
-                    // For freeform stack we don't adjust the size of the tasks to match that
-                    // of the stack, but we do try to make sure the tasks are still contained
-                    // with the bounds of the stack.
-                    tempRect2.set(task.mBounds);
-                    fitWithinBounds(tempRect2, bounds);
-                    task.updateOverrideConfiguration(tempRect2);
-                } else {
-                    task.updateOverrideConfiguration(bounds);
-                }
-            }
-
-            mTmpConfigs.put(task.taskId, task.mOverrideConfig);
-            mTmpBounds.put(task.taskId, task.mBounds);
-        }
-        stack.mFullscreen = mWindowManager.resizeStack(stackId, bounds, mTmpConfigs, mTmpBounds);
-        if (stack.mStackId == DOCKED_STACK_ID) {
-            // Dock stack funness...Yay!
-            if (stack.mFullscreen) {
-                // The dock stack went fullscreen which is kinda like dismissing it.
-                // In this case we make all other static stacks fullscreen and move all
-                // docked stack tasks to the fullscreen stack.
-                for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
-                    if (i != DOCKED_STACK_ID && getStack(i) != null) {
-                        resizeStackLocked(i, null, preserveWindows, true);
+            mTmpBounds.clear();
+            mTmpConfigs.clear();
+            ArrayList<TaskRecord> tasks = stack.getAllTasks();
+            for (int i = tasks.size() - 1; i >= 0; i--) {
+                TaskRecord task = tasks.get(i);
+                if (task.mResizeable) {
+                    if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+                        // For freeform stack we don't adjust the size of the tasks to match that
+                        // of the stack, but we do try to make sure the tasks are still contained
+                        // with the bounds of the stack.
+                        tempRect2.set(task.mBounds);
+                        fitWithinBounds(tempRect2, bounds);
+                        task.updateOverrideConfiguration(tempRect2);
+                    } else {
+                        task.updateOverrideConfiguration(bounds);
                     }
                 }
 
-                final int count = tasks.size();
-                for (int i = 0; i < count; i++) {
-                    moveTaskToStackLocked(tasks.get(i).taskId,
-                            FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS);
-                }
+                mTmpConfigs.put(task.taskId, task.mOverrideConfig);
+                mTmpBounds.put(task.taskId, task.mBounds);
+            }
+            stack.mFullscreen = mWindowManager.resizeStack(stackId, bounds, mTmpConfigs, mTmpBounds);
+            if (stack.mStackId == DOCKED_STACK_ID) {
+                // Dock stack funness...Yay!
+                if (stack.mFullscreen) {
+                    // The dock stack went fullscreen which is kinda like dismissing it.
+                    // In this case we make all other static stacks fullscreen and move all
+                    // docked stack tasks to the fullscreen stack.
+                    for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
+                        if (i != DOCKED_STACK_ID && getStack(i) != null) {
+                            resizeStackLocked(i, null, preserveWindows, true);
+                        }
+                    }
 
-                // stack shouldn't contain anymore activities, so nothing to resume.
-                r = null;
-            } else {
-                // Docked stacks occupy a dedicated region on screen so the size of all other
-                // static stacks need to be adjusted so they don't overlap with the docked stack.
-                // We get the bounds to use from window manager which has been adjusted for any
-                // screen controls and is also the same for all stacks.
-                mWindowManager.getStackDockedModeBounds(HOME_STACK_ID, tempRect);
+                    final int count = tasks.size();
+                    for (int i = 0; i < count; i++) {
+                        moveTaskToStackLocked(tasks.get(i).taskId,
+                                FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS);
+                    }
 
-                for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
-                    if (i != DOCKED_STACK_ID) {
-                        ActivityStack otherStack = getStack(i);
-                        if (otherStack != null) {
-                            resizeStackLocked(i, tempRect, PRESERVE_WINDOWS, true);
+                    // stack shouldn't contain anymore activities, so nothing to resume.
+                    r = null;
+                } else {
+                    // Docked stacks occupy a dedicated region on screen so the size of all other
+                    // static stacks need to be adjusted so they don't overlap with the docked stack.
+                    // We get the bounds to use from window manager which has been adjusted for any
+                    // screen controls and is also the same for all stacks.
+                    mWindowManager.getStackDockedModeBounds(HOME_STACK_ID, tempRect);
+
+                    for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
+                        if (i != DOCKED_STACK_ID) {
+                            ActivityStack otherStack = getStack(i);
+                            if (otherStack != null) {
+                                resizeStackLocked(i, tempRect, PRESERVE_WINDOWS, true);
+                            }
                         }
                     }
                 }
+                // Since we are resizing the stack, all other operations should strive to preserve
+                // windows.
+                preserveWindows = true;
             }
-            // Since we are resizing the stack, all other operations should strive to preserve
-            // windows.
-            preserveWindows = true;
-        }
-        stack.setBounds(bounds);
+            stack.setBounds(bounds);
 
-        if (r != null) {
-            final boolean updated = stack.ensureActivityConfigurationLocked(r, 0, preserveWindows);
-            // And we need to make sure at this point that all other activities
-            // are made visible with the correct configuration.
-            ensureActivitiesVisibleLocked(r, 0, preserveWindows);
-            if (!updated) {
-                resumeTopActivitiesLocked(stack, null, null);
+            if (r != null) {
+                final boolean updated = stack.ensureActivityConfigurationLocked(r, 0, preserveWindows);
+                // And we need to make sure at this point that all other activities
+                // are made visible with the correct configuration.
+                ensureActivitiesVisibleLocked(r, 0, preserveWindows);
+                if (!updated) {
+                    resumeTopActivitiesLocked(stack, null, null);
+                }
             }
+        } finally {
+            mWindowManager.continueSurfaceLayout();
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
         }
-
-        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
     void resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow) {
@@ -3198,7 +3194,7 @@
      */
     private boolean restoreRecentTaskLocked(TaskRecord task, int stackId) {
         if (stackId == INVALID_STACK_ID) {
-            stackId = mLeanbackOnlyDevice ? mHomeStack.mStackId : task.getLaunchStackId();
+            stackId = task.getLaunchStackId();
         }
         if (task.stack != null) {
             // Task has already been restored once. See if we need to do anything more
@@ -3265,23 +3261,8 @@
 
         // If the task had focus before (or we're requested to move focus),
         // move focus to the new stack.
-        if (forceFocus || wasFocused) {
-            // If the task owns the last resumed activity, transfer that together,
-            // so that we don't resume the same activity again in the new stack.
-            // Apps may depend on onResume()/onPause() being called in pairs.
-            if (wasResumed) {
-                stack.mResumedActivity = r;
-                // Move the stack in which we are placing the task to the front. We don't use
-                // ActivityManagerService.setFocusedActivityLocked, because if the activity is
-                // already focused, the call will short-circuit and do nothing.
-                stack.moveToFront(reason);
-            } else {
-                // We need to not only move the stack to the front, but also have the activity
-                // focused. This will achieve both goals.
-                mService.setFocusedActivityLocked(r, reason);
-            }
-
-        }
+        stack.setFocusAndResumeStateIfNeeded(
+                r, forceFocus || wasFocused, wasResumed, reason);
 
         return stack;
     }
@@ -3293,7 +3274,8 @@
             return;
         }
         final String reason = "moveTaskToStack";
-        if (stackId == DOCKED_STACK_ID || stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+        if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID
+                || stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
             // We are about to relaunch the activity because its configuration changed due to
             // being maximized, i.e. size change. The activity will first remove the old window
             // and then add a new one. This call will tell window manager about this, so it can
@@ -3314,7 +3296,7 @@
                 && task.mBounds == null && task.mLastNonFullscreenBounds != null) {
             resizeTaskLocked(task, task.mLastNonFullscreenBounds,
                     RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
-        } else if (stackId == DOCKED_STACK_ID) {
+        } else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
             resizeTaskLocked(task, stack.mBounds,
                     RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
         }
@@ -3325,6 +3307,46 @@
         resumeTopActivitiesLocked();
     }
 
+    boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect bounds) {
+        final ActivityStack stack = getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP);
+        if (stack == null) {
+            throw new IllegalArgumentException(
+                    "moveTopStackActivityToPinnedStackLocked: Unknown stackId=" + stackId);
+        }
+
+        final ActivityRecord r = stack.topRunningActivityLocked();
+        if (r == null) {
+            Slog.w(TAG, "moveTopStackActivityToPinnedStackLocked: No top running activity"
+                    + " in stack=" + stack);
+            return false;
+        }
+
+        if (!r.info.supportsPip) {
+            Slog.w(TAG,
+                    "moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for "
+                    + " r=" + r);
+            return false;
+        }
+
+        final TaskRecord task = r.task;
+        if (task.mActivities.size() == 1) {
+            // There is only one activity in the task. So, we can just move the task over to the
+            // pinned stack without re-parenting the activity in a different task.
+            moveTaskToStackLocked(task.taskId, PINNED_STACK_ID, ON_TOP, FORCE_FOCUS);
+        } else {
+            final ActivityStack pinnedStack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+            pinnedStack.moveActivityToStack(r);
+        }
+
+        resizeStackLocked(PINNED_STACK_ID, bounds, PRESERVE_WINDOWS, true);
+
+        // The task might have already been running and its visibility needs to be synchronized with
+        // the visibility of the stack / windows.
+        ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+        resumeTopActivitiesLocked();
+        return true;
+    }
+
     void positionTaskInStackLocked(int taskId, int stackId, int position) {
         final TaskRecord task = anyTaskForIdLocked(taskId);
         if (task == null) {
@@ -4924,18 +4946,6 @@
         }
     }
 
-    private boolean isLeanbackOnlyDevice() {
-        boolean onLeanbackOnly = false;
-        try {
-            onLeanbackOnly = AppGlobals.getPackageManager().hasSystemFeature(
-                    PackageManager.FEATURE_LEANBACK_ONLY);
-        } catch (RemoteException e) {
-            // noop
-        }
-
-        return onLeanbackOnly;
-    }
-
     /**
      * Adjust bounds to stay within stack bounds.
      *
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index fe87a93..120b40c 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -20,6 +20,7 @@
 import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.HOME_STACK_ID;
+import static android.app.ActivityManager.PINNED_STACK_ID;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
@@ -317,7 +318,7 @@
         mNextAffiliateTaskId = nextTaskId;
         mCallingUid = callingUid;
         mCallingPackage = callingPackage;
-        mResizeable = resizeable;
+        mResizeable = resizeable || mService.mForceResizableActivites;
         mPrivileged = privileged;
         ActivityInfo info = mActivities.get(0).info;
         mMinimalSize = info != null && info.layout != null ? info.layout.minimalSize : -1;
@@ -419,7 +420,7 @@
         } else {
             autoRemoveRecents = false;
         }
-        mResizeable = info.resizeable;
+        mResizeable = info.resizeable || mService.mForceResizableActivites;
         mLockTaskMode = info.lockTaskLaunchMode;
         mPrivileged = (info.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0;
         setLockTaskAuth();
@@ -625,6 +626,9 @@
         // Only set this based on the first activity
         if (mActivities.isEmpty()) {
             taskType = r.mActivityType;
+            if (taskType == HOME_ACTIVITY_TYPE && mService.mForceResizableActivites) {
+                mResizeable = r.info.resizeable;
+            }
             isPersistable = r.isPersistable();
             mCallingUid = r.launchedFromUid;
             mCallingPackage = r.launchedFromPackage;
@@ -1189,14 +1193,14 @@
 
         mFullscreen = bounds == null;
         if (mFullscreen) {
-            if (mBounds != null && stack.mStackId != DOCKED_STACK_ID) {
+            if (mBounds != null && stack.mPersistTaskBounds) {
                 mLastNonFullscreenBounds = mBounds;
             }
             mBounds = null;
             mOverrideConfig = Configuration.EMPTY;
         } else {
             mBounds = new Rect(bounds);
-            if (stack == null || stack.mStackId != DOCKED_STACK_ID) {
+            if (stack == null || stack.mPersistTaskBounds) {
                 mLastNonFullscreenBounds = mBounds;
             }
 
@@ -1235,11 +1239,12 @@
 
     /** Returns the bounds that should be used to launch this task. */
     Rect getLaunchBounds() {
+        final int stackId = stack.mStackId;
         if (stack == null
-                || stack.mStackId == HOME_STACK_ID
-                || stack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+                || stackId == HOME_STACK_ID
+                || stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
             return (mResizeable && stack != null) ? stack.mBounds : null;
-        } else if (stack.mStackId == DOCKED_STACK_ID) {
+        } else if (!stack.mPersistTaskBounds) {
             return stack.mBounds;
         }
         return mLastNonFullscreenBounds;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b3e7f5f..aed2f0b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2276,7 +2276,7 @@
                         mSettings.enableSystemPackageLPw(packageName);
 
                         try {
-                            scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, UserHandle.SYSTEM);
+                            scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, null);
                         } catch (PackageManagerException e) {
                             Slog.e(TAG, "Failed to parse original system package: "
                                     + e.getMessage());
@@ -5685,7 +5685,7 @@
             }
             try {
                 scanPackageTracedLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
-                        scanFlags, currentTime, UserHandle.SYSTEM);
+                        scanFlags, currentTime, null);
             } catch (PackageManagerException e) {
                 Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
 
@@ -5791,8 +5791,6 @@
      */
     private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
             long currentTime, UserHandle user) throws PackageManagerException {
-        Preconditions.checkNotNull(user);
-
         if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
         parseFlags |= mDefParseFlags;
         PackageParser pp = new PackageParser();
@@ -12141,7 +12139,7 @@
                 int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
                 try {
                     scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime,
-                            UserHandle.SYSTEM);
+                            null);
                 } catch (PackageManagerException e) {
                     Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade: "
                             + e.getMessage());
@@ -13276,8 +13274,7 @@
 
         final PackageParser.Package newPkg;
         try {
-            newPkg = scanPackageTracedLI(disabledPs.codePath, parseFlags, SCAN_NO_PATHS, 0,
-                    UserHandle.SYSTEM);
+            newPkg = scanPackageTracedLI(disabledPs.codePath, parseFlags, SCAN_NO_PATHS, 0, null);
         } catch (PackageManagerException e) {
             Slog.w(TAG, "Failed to restore system package:" + newPs.name + ": " + e.getMessage());
             return false;
@@ -15841,8 +15838,7 @@
                 synchronized (mInstallLock) {
                     PackageParser.Package pkg = null;
                     try {
-                        pkg = scanPackageTracedLI(new File(codePath), parseFlags, 0, 0,
-                                UserHandle.SYSTEM);
+                        pkg = scanPackageTracedLI(new File(codePath), parseFlags, 0, 0, null);
                     } catch (PackageManagerException e) {
                         Slog.w(TAG, "Failed to scan " + codePath + ": " + e.getMessage());
                     }
@@ -16005,8 +16001,7 @@
             synchronized (mInstallLock) {
                 final PackageParser.Package pkg;
                 try {
-                    pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0,
-                            UserHandle.SYSTEM);
+                    pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0, null);
                     loaded.add(pkg.applicationInfo);
                 } catch (PackageManagerException e) {
                     Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage());
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 2365a45..dc34904 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -456,10 +456,12 @@
         return -startPos / denom;
     }
 
-    private Animation createScaleUpAnimationLocked(
-            int transit, boolean enter, int appWidth, int appHeight) {
-        Animation a = null;
+    private Animation createScaleUpAnimationLocked(int transit, boolean enter,
+            Rect containingFrame) {
+        Animation a;
         getDefaultNextAppTransitionStartRect(mTmpStartRect);
+        final int appWidth = containingFrame.width();
+        final int appHeight = containingFrame.height();
         if (enter) {
             // Entering app zooms out from the center of the initial rect.
             float scaleW = mTmpStartRect.width() / (float) appWidth;
@@ -746,10 +748,11 @@
      * activity that is leaving, and the activity that is entering.
      */
     Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
-            int appWidth, int appHeight, int orientation, int transit, Rect containingFrame,
-            Rect contentInsets, @Nullable Rect surfaceInsets, boolean resizedWindow,
-            int taskId) {
+            int orientation, int transit, Rect containingFrame, Rect contentInsets,
+            @Nullable Rect surfaceInsets, boolean freeform, int taskId) {
         Animation a;
+        final int appWidth = containingFrame.width();
+        final int appHeight = containingFrame.height();
         getDefaultNextAppTransitionStartRect(mTmpStartRect);
         final int thumbWidthI = mTmpStartRect.width();
         final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
@@ -762,7 +765,7 @@
 
         switch (thumbTransitState) {
             case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
-                if (resizedWindow) {
+                if (freeform) {
                     a = createAspectScaledThumbnailEnterNonFullscreenAnimationLocked(
                             containingFrame, surfaceInsets, taskId);
                 } else {
@@ -953,8 +956,10 @@
      * This animation is created when we are doing a thumbnail transition, for the activity that is
      * leaving, and the activity that is entering.
      */
-    Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth,
-            int appHeight, int transit, int taskId) {
+    Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, Rect containingFrame,
+            int transit, int taskId) {
+        final int appWidth = containingFrame.width();
+        final int appHeight = containingFrame.height();
         Bitmap thumbnailHeader = getAppTransitionThumbnailHeader(taskId);
         Animation a;
         getDefaultNextAppTransitionStartRect(mTmpStartRect);
@@ -1016,18 +1021,22 @@
         return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
     }
 
-    private Animation createRelaunchAnimation(int appWidth, int appHeight,
-            Rect containingFrame) {
+    private Animation createRelaunchAnimation(Rect containingFrame, Rect contentInsets) {
         getDefaultNextAppTransitionStartRect(mTmpFromClipRect);
         final int left = mTmpFromClipRect.left;
         final int top = mTmpFromClipRect.top;
         mTmpFromClipRect.offset(-left, -top);
-        mTmpToClipRect.set(0, 0, appWidth, appHeight);
+        // TODO: Isn't that strange that we ignore exact position of the containingFrame?
+        mTmpToClipRect.set(0, 0, containingFrame.width(), containingFrame.height());
         AnimationSet set = new AnimationSet(true);
         float fromWidth = mTmpFromClipRect.width();
         float toWidth = mTmpToClipRect.width();
         float fromHeight = mTmpFromClipRect.height();
-        float toHeight = mTmpToClipRect.height();
+        // While the window might span the whole display, the actual content will be cropped to the
+        // system decoration frame, for example when the window is docked. We need to take into
+        // account the visible height when constructing the animation.
+        float toHeight = mTmpToClipRect.height() - contentInsets.top - contentInsets.bottom;
+        int translateAdjustment = 0;
         if (fromWidth <= toWidth && fromHeight <= toHeight) {
             // The final window is larger in both dimensions than current window (e.g. we are
             // maximizing), so we can simply unclip the new window and there will be no disappearing
@@ -1037,12 +1046,17 @@
             // The disappearing window has one larger dimension. We need to apply scaling, so the
             // first frame of the entry animation matches the old window.
             set.addAnimation(new ScaleAnimation(fromWidth / toWidth, 1, fromHeight / toHeight, 1));
+            // We might not be going exactly full screen, but instead be aligned under the status
+            // bar using cropping. We still need to account for the cropped part, which will also
+            // be scaled.
+            translateAdjustment = (int) (contentInsets.top * fromHeight / toHeight);
         }
 
-        // We might not be going exactly full screen, but instead be aligned under the status bar.
-        // We need to take this into account when creating the translate animation.
+        // We animate the translation from the old position of the removed window, to the new
+        // position of the added window. The latter might not be full screen, for example docked for
+        // docked windows.
         TranslateAnimation translate = new TranslateAnimation(left - containingFrame.left,
-                0, top - containingFrame.top, 0);
+                0, top - containingFrame.top - translateAdjustment, 0);
         set.addAnimation(translate);
         set.setDuration(DEFAULT_APP_TRANSITION_DURATION);
         set.setZAdjustment(Animation.ZORDER_TOP);
@@ -1060,10 +1074,30 @@
                 && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CLIP_REVEAL;
     }
 
+    /**
+     *
+     * @param frame These are the bounds of the window when it finishes the animation. This is where
+     *              the animation must usually finish in entrance animation, as the next frame will
+     *              display the window at these coordinates. In case of exit animation, this is
+     *              where the animation must start, as the frame before the animation is displaying
+     *              the window at these bounds.
+     * @param insets Knowing where the window will be positioned is not enough. Some parts of the
+     *               window might be obscured, usually by the system windows (status bar and
+     *               navigation bar) and we use content insets to convey that information. This
+     *               usually affects the animation aspects vertically, as the system decoration is
+     *               at the top and the bottom. For example when we animate from full screen to
+     *               recents, we want to exclude the covered parts, because they won't match the
+     *               thumbnail after the last frame is executed.
+     * @param surfaceInsets In rare situation the surface is larger than the content and we need to
+     *                      know about this to make the animation frames match. We currently use
+     *                      this for freeform windows, which have larger surfaces to display
+     *                      shadows. When we animate them from recents, we want to match the content
+     *                      to the recents thumbnail and hence need to account for the surface being
+     *                      bigger.
+     */
     Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
-            int appWidth, int appHeight, int orientation, Rect containingFrame, Rect contentInsets,
-            @Nullable Rect surfaceInsets, Rect appFrame, boolean isVoiceInteraction,
-            boolean resizedWindow, int taskId) {
+            int orientation, Rect frame, Rect insets, @Nullable Rect surfaceInsets,
+            boolean isVoiceInteraction, boolean freeform, int taskId) {
         Animation a;
         if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
                 || transit == TRANSIT_TASK_OPEN
@@ -1086,7 +1120,7 @@
                     + " anim=" + a + " transit=" + appTransitionToString(transit)
                     + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
         } else if (transit == TRANSIT_ACTIVITY_RELAUNCH) {
-            a = createRelaunchAnimation(appWidth, appHeight, containingFrame);
+            a = createRelaunchAnimation(frame, insets);
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
                     "applyAnimation:"
                     + " anim=" + a + " nextAppTransition=" + mNextAppTransition
@@ -1108,14 +1142,14 @@
                     + " transit=" + appTransitionToString(transit)
                     + " Callers=" + Debug.getCallers(3));
         } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
-            a = createClipRevealAnimationLocked(transit, enter, appFrame);
+            a = createClipRevealAnimationLocked(transit, enter, frame);
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
                     "applyAnimation:"
                             + " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL"
                             + " transit=" + appTransitionToString(transit)
                             + " Callers=" + Debug.getCallers(3));
         } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
-            a = createScaleUpAnimationLocked(transit, enter, appWidth, appHeight);
+            a = createScaleUpAnimationLocked(transit, enter, frame);
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
                     "applyAnimation:"
                     + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP"
@@ -1126,7 +1160,7 @@
             mNextAppTransitionScaleUp =
                     (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
             a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
-                    appWidth, appHeight, transit, taskId);
+                    frame, transit, taskId);
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
                 String animName = mNextAppTransitionScaleUp ?
                         "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
@@ -1140,8 +1174,8 @@
             mNextAppTransitionScaleUp =
                     (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
             a = createAspectScaledThumbnailEnterExitAnimationLocked(
-                    getThumbnailTransitionState(enter), appWidth, appHeight, orientation, transit,
-                    containingFrame, contentInsets, surfaceInsets, resizedWindow, taskId);
+                    getThumbnailTransitionState(enter), orientation, transit, frame,
+                    insets, surfaceInsets, freeform, taskId);
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
                 String animName = mNextAppTransitionScaleUp ?
                         "ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN";
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index bf7063f..a61e83f 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -300,7 +300,8 @@
                 return false;
             }
 
-            if ((mAppToken.allDrawn || animating || mAppToken.startingDisplayed)
+            if ((mAppToken.allDrawn || mAppToken.mAnimatingWithSavedSurface
+                    || animating || mAppToken.startingDisplayed)
                     && animation != null) {
                 if (!animating) {
                     if (DEBUG_ANIM) Slog.v(TAG,
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 5a1ed58..943e3ea 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -52,6 +52,13 @@
 
     final boolean voiceInteraction;
 
+    // Whether the window has a saved surface from last pause, which can be
+    // used to start an entering animation earlier.
+    boolean mHasSavedSurface;
+
+    // Whether we're performing an entering animation with a saved surface.
+    boolean mAnimatingWithSavedSurface;
+
     Task mTask;
     boolean appFullscreen;
     int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -114,11 +121,14 @@
     // This application will have its window replaced due to relaunch. This allows window manager
     // to differentiate between simple removal of a window and replacement. In the latter case it
     // will preserve the old window until the new one is drawn.
-    boolean mReplacingWindow;
+    boolean mWillReplaceWindow;
     // If true, the replaced window was already requested to be removed.
     boolean mReplacingRemoveRequested;
     // Whether the replacement of the window should trigger app transition animation.
     boolean mAnimateReplacingWindow;
+    // If not null, the window that will be used to replace the old one. This is being set when
+    // the window is added and unset when this window reports its first draw.
+    WindowState mReplacingWindow;
 
     AppWindowToken(WindowManagerService _service, IApplicationToken _token,
             boolean _voiceInteraction) {
@@ -257,10 +267,13 @@
         final int N = allAppWindows.size();
         for (int i=0; i<N; i++) {
             WindowState win = allAppWindows.get(i);
+            // If we're animating with a saved surface, we're already visible.
+            // Return true so that the alpha doesn't get cleared.
             if (!win.mAppFreezing
-                    && (win.mViewVisibility == View.VISIBLE ||
-                        (win.mWinAnimator.isAnimating() &&
-                                !service.mAppTransition.isTransitionSet()))
+                    && (win.mViewVisibility == View.VISIBLE
+                    || mAnimatingWithSavedSurface
+                    || (win.mWinAnimator.isAnimating() &&
+                            !service.mAppTransition.isTransitionSet()))
                     && !win.mDestroying && win.isDrawnLw()) {
                 return true;
             }
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index f5e97e5..f35ea66 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -77,7 +77,6 @@
         mSurfaceControl = surface;
         mFlags = flags;
         mLocalWin = localWin;
-        mUid = Binder.getCallingUid();
         mNotifiedWindows = new ArrayList<WindowState>();
     }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7c56180..4d11452 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -17,12 +17,15 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.HOME_STACK_ID;
+import static android.app.ActivityManager.PINNED_STACK_ID;
 import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
 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 static com.android.server.wm.WindowManagerService.H.RESIZE_TASK;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+
 
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -187,7 +190,8 @@
                 bounds = mTmpRect;
                 mFullscreen = true;
             } else {
-                if (mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID || bounds.isEmpty()) {
+                if ((mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
+                        && mStack.mStackId != PINNED_STACK_ID) || bounds.isEmpty()) {
                     // ensure bounds are entirely within the display rect
                     if (!bounds.intersect(mTmpRect)) {
                         // Can't set bounds outside the containing display...Sorry!
@@ -241,8 +245,7 @@
     private boolean useCurrentBounds() {
         final DisplayContent displayContent = mStack.getDisplayContent();
         if (mFullscreen
-                || mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID
-                || mStack.mStackId == DOCKED_STACK_ID
+                || mStack.allowTaskResize()
                 || displayContent == null
                 || displayContent.getDockedStackLocked() != null) {
             return true;
@@ -322,6 +325,10 @@
         return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
     }
 
+    boolean inHomeStack() {
+        return mStack != null && mStack.mStackId == HOME_STACK_ID;
+    }
+
     boolean inFreeformWorkspace() {
         return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
     }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 6734fd6..9b3d478 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -22,6 +22,7 @@
 import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.LAST_STATIC_STACK_ID;
+import static android.app.ActivityManager.PINNED_STACK_ID;
 import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
 import static com.android.server.wm.WindowManagerService.TAG;
@@ -123,7 +124,8 @@
 
     boolean allowTaskResize() {
         return mStackId == FREEFORM_WORKSPACE_STACK_ID
-                || mStackId == DOCKED_STACK_ID;
+                || mStackId == DOCKED_STACK_ID
+                || mStackId == PINNED_STACK_ID;
     }
 
     /**
@@ -202,6 +204,7 @@
     private boolean useCurrentBounds() {
         if (mFullscreen
                 || mStackId == DOCKED_STACK_ID
+                || mStackId == PINNED_STACK_ID
                 || mDisplayContent == null
                 || mDisplayContent.getDockedStackLocked() != null) {
             return true;
@@ -393,7 +396,7 @@
 
         Rect bounds = null;
         final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
-        if (mStackId == DOCKED_STACK_ID || (dockedStack != null
+        if (mStackId == DOCKED_STACK_ID || (dockedStack != null && mStackId != PINNED_STACK_ID
                 && mStackId >= FIRST_STATIC_STACK_ID && mStackId <= LAST_STATIC_STACK_ID)) {
             // The existence of a docked stack affects the size of any static stack created since
             // the docked stack occupies a dedicated region on screen.
@@ -422,6 +425,7 @@
 
     void getStackDockedModeBoundsLocked(Rect outBounds) {
         if (mStackId == DOCKED_STACK_ID
+                || mStackId == PINNED_STACK_ID
                 || mStackId > LAST_STATIC_STACK_ID
                 || mDisplayContent == null) {
             outBounds.set(mBounds);
@@ -533,7 +537,7 @@
         for (int i = 0; i < count; i++) {
             final TaskStack otherStack = mService.mStackIdToStack.valueAt(i);
             final int otherStackId = otherStack.mStackId;
-            if (otherStackId != DOCKED_STACK_ID
+            if (otherStackId != DOCKED_STACK_ID && mStackId != PINNED_STACK_ID
                     && otherStackId >= FIRST_STATIC_STACK_ID
                     && otherStackId <= LAST_STATIC_STACK_ID) {
                 mService.mH.sendMessage(
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 928a117..f4a4140 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -31,6 +31,7 @@
 
 import android.content.Context;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -413,7 +414,7 @@
 
             final AppWindowToken atoken = win.mAppToken;
             if (winAnimator.mDrawState == WindowStateAnimator.READY_TO_SHOW) {
-                if (atoken == null || atoken.allDrawn) {
+                if (atoken == null || atoken.allDrawn || atoken.mAnimatingWithSavedSurface) {
                     if (winAnimator.performShowLocked()) {
                         setPendingLayoutChanges(displayId,
                                 WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
@@ -731,8 +732,15 @@
             mWindowPlacerLocked.requestTraversal();
         }
 
+        if (mAnimating && !wasAnimating && Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+            Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+        }
+
         if (!mAnimating && wasAnimating) {
             mWindowPlacerLocked.requestTraversal();
+            if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+                Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+            }
         }
 
         mService.destroyPreservedSurfaceLocked();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index cd5fbb0..62553cf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1327,8 +1327,14 @@
             addAttachedWindowToListLocked(win, addToToken);
         }
 
-        if (win.mAppToken != null && addToToken) {
-            win.mAppToken.allAppWindows.add(win);
+        final AppWindowToken appToken = win.mAppToken;
+        if (appToken != null) {
+            if (addToToken) {
+                appToken.allAppWindows.add(win);
+            }
+            if (appToken.mWillReplaceWindow) {
+                appToken.mReplacingWindow = win;
+            }
         }
     }
 
@@ -2053,7 +2059,7 @@
     }
 
     private void prepareWindowReplacementTransition(AppWindowToken atoken) {
-        if (atoken == null || !atoken.mReplacingWindow || !atoken.mAnimateReplacingWindow) {
+        if (atoken == null || !atoken.mWillReplaceWindow || !atoken.mAnimateReplacingWindow) {
             return;
         }
         atoken.allDrawn = false;
@@ -2157,6 +2163,8 @@
                 + " isAnimating=" + win.mWinAnimator.isAnimating()
                 + " app-animation="
                 + (win.mAppToken != null ? win.mAppToken.mAppAnimator.animation : null)
+                + " mWillReplaceWindow="
+                + (win.mAppToken != null ? win.mAppToken.mWillReplaceWindow : false)
                 + " inPendingTransaction="
                 + (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false)
                 + " mDisplayFrozen=" + mDisplayFrozen);
@@ -2168,8 +2176,8 @@
         // animation wouldn't be seen.
         if (win.mHasSurface && okToDisplay()) {
             final AppWindowToken appToken = win.mAppToken;
-            if (appToken != null && appToken.mReplacingWindow) {
-                // This window is going to be replaced. We need to kepp it around until the new one
+            if (appToken != null && appToken.mWillReplaceWindow) {
+                // This window is going to be replaced. We need to keep it around until the new one
                 // gets added, then we will get rid of this one.
                 if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Preserving " + win + " until the new one is "
                         + "added");
@@ -2679,9 +2687,16 @@
                 if (winAnimator.mSurfaceControl != null) {
                     if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win
                             + ": mExiting=" + win.mExiting);
+                    // If we are using a saved surface to do enter animation, just let the
+                    // animation run and don't destroy the surface. This could happen when
+                    // the app sets visibility to invisible for the first time after resume,
+                    // or when the user exits immediately after a resume. In both cases, we
+                    // don't want to destroy the saved surface.
+                    final boolean isAnimatingWithSavedSurface =
+                            win.mAppToken != null && win.mAppToken.mAnimatingWithSavedSurface;
                     // If we are not currently running the exit animation, we
                     // need to see about starting one.
-                    if (!win.mExiting) {
+                    if (!win.mExiting && !isAnimatingWithSavedSurface) {
                         surfaceChanged = true;
                         // Try starting an animation; if there isn't one, we
                         // can destroy the surface right away.
@@ -2833,12 +2848,19 @@
         try {
             synchronized (mWindowMap) {
                 WindowState win = windowForClientLocked(session, client, false);
-                if (DEBUG_ADD_REMOVE) Slog.d(TAG, "finishDrawingWindow: " + win);
+                if (DEBUG_ADD_REMOVE) Slog.d(TAG, "finishDrawingWindow: " + win + " mDrawState="
+                        + (win != null ? win.mWinAnimator.drawStateToString() : "null"));
                 if (win != null && win.mWinAnimator.finishDrawingLocked()) {
                     if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
                         getDefaultDisplayContentLocked().pendingLayoutChanges |=
                                 WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                     }
+                    if (win.mAppToken != null) {
+                        // App has drawn something to its windows, we're no longer animating with
+                        // the saved surfaces. If the user exits now, we only want to save again
+                        // if allDrawn is true.
+                        win.mAppToken.mAnimatingWithSavedSurface = false;
+                    }
                     win.setDisplayLayoutNeeded();
                     mWindowPlacerLocked.requestTraversal();
                 }
@@ -2862,46 +2884,44 @@
                     "applyAnimation: atoken=" + atoken);
 
             // Determine the visible rect to calculate the thumbnail clip
-            WindowState win = atoken.findMainWindow();
-            Rect containingFrame = new Rect(0, 0, width, height);
-            Rect contentInsets = new Rect();
-            Rect appFrame = new Rect(0, 0, width, height);
+            final WindowState win = atoken.findMainWindow();
+            final Rect frame = new Rect(0, 0, width, height);
+            final Rect insets = new Rect();
             Rect surfaceInsets = null;
             final boolean fullscreen = win != null && win.isFullscreen(width, height);
             final boolean freeform = win != null && win.inFreeformWorkspace();
+            final boolean docked = win != null && win.inDockedWorkspace();
             if (win != null) {
                 // Containing frame will usually cover the whole screen, including dialog windows.
                 // For freeform workspace windows it will not cover the whole screen and it also
                 // won't exactly match the final freeform window frame (e.g. when overlapping with
                 // the status bar). In that case we need to use the final frame.
                 if (freeform) {
-                    containingFrame.set(win.mFrame);
+                    frame.set(win.mFrame);
                 } else {
-                    containingFrame.set(win.mContainingFrame);
+                    frame.set(win.mContainingFrame);
                 }
                 surfaceInsets = win.getAttrs().surfaceInsets;
-                if (fullscreen) {
+                if (fullscreen || docked) {
                     // For fullscreen windows use the window frames and insets to set the thumbnail
-                    // clip. For none-fullscreen windows we use the app display region so the clip
+                    // clip. For non-fullscreen windows we use the app display region so the clip
                     // isn't affected by the window insets.
-                    contentInsets.set(win.mContentInsets);
-                    appFrame.set(win.mFrame);
-                } else {
-                    appFrame.set(containingFrame);
+                    insets.set(win.mContentInsets);
                 }
             }
 
-            final int containingWidth = containingFrame.width();
-            final int containingHeight = containingFrame.height();
             if (atoken.mLaunchTaskBehind) {
                 // Differentiate the two animations. This one which is briefly on the screen
                 // gets the !enter animation, and the other activity which remains on the
                 // screen gets the enter animation. Both appear in the mOpeningApps set.
                 enter = false;
             }
-            Animation a = mAppTransition.loadAnimation(lp, transit, enter, containingWidth,
-                    containingHeight, mCurConfiguration.orientation, containingFrame, contentInsets,
-                    surfaceInsets, appFrame, isVoiceInteraction, freeform, atoken.mTask.mTaskId);
+            if (DEBUG_APP_TRANSITIONS) Slog.d(TAG, "Loading animation for app transition."
+                    + " transit=" + AppTransition.appTransitionToString(transit) + " enter=" + enter
+                    + " frame=" + frame + " insets=" + insets + " surfaceInsets=" + surfaceInsets);
+            Animation a = mAppTransition.loadAnimation(lp, transit, enter,
+                    mCurConfiguration.orientation, frame, insets, surfaceInsets, isVoiceInteraction,
+                    freeform, atoken.mTask.mTaskId);
             if (a != null) {
                 if (DEBUG_ANIM) {
                     RuntimeException e = null;
@@ -2911,6 +2931,8 @@
                     }
                     Slog.v(TAG, "Loaded animation " + a + " for " + atoken, e);
                 }
+                final int containingWidth = frame.width();
+                final int containingHeight = frame.height();
                 atoken.mAppAnimator.setAnimation(a, containingWidth, containingHeight,
                         mAppTransition.canSkipFirstFrame());
             }
@@ -3909,7 +3931,7 @@
         // transition animation
         // * or this is an opening app and windows are being replaced.
         if (wtoken.hidden == visible || (wtoken.hidden && wtoken.mIsExiting) ||
-                (visible && wtoken.mReplacingWindow)) {
+                (visible && wtoken.mWillReplaceWindow)) {
             boolean changed = false;
             if (DEBUG_APP_TRANSITIONS) Slog.v(
                 TAG, "Changing app " + wtoken + " hidden=" + wtoken.hidden
@@ -4657,7 +4679,7 @@
                 throw new IllegalArgumentException("resizeStack: stackId " + stackId
                         + " not found.");
             }
-            if (stack.setBounds(bounds, configs, taskBounds)) {
+            if (stack.setBounds(bounds, configs, taskBounds) && stack.isVisibleLocked()) {
                 stack.resizeWindows();
                 stack.getDisplayContent().layoutNeeded = true;
                 mWindowPlacerLocked.performSurfacePlacement();
@@ -4710,6 +4732,26 @@
         }
     }
 
+    /**
+     * Starts deferring layout passes. Useful when doing multiple changes but to optimize
+     * performance, only one layout pass should be done. This can be called multiple times, and
+     * layouting will be resumed once the last caller has called {@link #continueSurfaceLayout}
+     */
+    public void deferSurfaceLayout() {
+        synchronized (mWindowMap) {
+            mWindowPlacerLocked.deferLayout();
+        }
+    }
+
+    /**
+     * Resumes layout passes after deferring them. See {@link #deferSurfaceLayout()}
+     */
+    public void continueSurfaceLayout() {
+        synchronized (mWindowMap) {
+            mWindowPlacerLocked.continueLayout();
+        }
+    }
+
     public void getTaskBounds(int taskId, Rect bounds) {
         synchronized (mWindowMap) {
             Task task = mTaskIdToTask.get(taskId);
@@ -6972,7 +7014,7 @@
                     + " asbinder=" + window.asBinder());
         }
 
-        final int callerPid = Binder.getCallingPid();
+        final int callerUid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         IBinder token = null;
 
@@ -6997,6 +7039,7 @@
                         final IBinder winBinder = window.asBinder();
                         token = new Binder();
                         mDragState = new DragState(this, token, surface, flags, winBinder);
+                        mDragState.mUid = callerUid;
                         token = mDragState.mToken = new Binder();
 
                         // 5 second timeout for this window to actually begin the drag
@@ -8346,7 +8389,7 @@
                 final int numTokens = tokens.size();
                 for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
                     final AppWindowToken wtoken = tokens.get(tokenNdx);
-                    if (wtoken.mIsExiting && !wtoken.mReplacingWindow) {
+                    if (wtoken.mIsExiting && !wtoken.mWillReplaceWindow) {
                         continue;
                     }
                     i = reAddAppWindowsLocked(displayContent, i, wtoken);
@@ -8416,7 +8459,8 @@
             } else if (wtoken != null) {
                 winAnimator.mAnimLayer =
                         w.mLayer + wtoken.mAppAnimator.animLayerAdjustment;
-                if (wtoken.mReplacingWindow && wtoken.mAnimateReplacingWindow) {
+                if (wtoken.mWillReplaceWindow && wtoken.mAnimateReplacingWindow &&
+                        wtoken.mReplacingWindow != w) {
                     // We know that we will be animating a relaunching window in the near future,
                     // which will receive a z-order increase. We want the replaced window to
                     // immediately receive the same treatment, e.g. to be above the dock divider.
@@ -9962,7 +10006,9 @@
                 Slog.w(TAG, "Attempted to set replacing window on non-existing app token " + token);
                 return;
             }
-            appWindowToken.mReplacingWindow = true;
+            if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Marking app token " + appWindowToken
+                    + " as replacing window.");
+            appWindowToken.mWillReplaceWindow = true;
             appWindowToken.mAnimateReplacingWindow = animate;
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7ea64f1..e9ea3a8 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -28,6 +28,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static com.android.server.wm.WindowManagerService.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerService.DEBUG_CONFIGURATION;
 import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
 import static com.android.server.wm.WindowManagerService.DEBUG_ORIENTATION;
@@ -35,6 +36,7 @@
 import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
 import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
 
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.graphics.Point;
 import android.os.PowerManager;
@@ -55,7 +57,6 @@
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.graphics.Region;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -446,7 +447,8 @@
                     + WindowManagerService.TYPE_LAYER_OFFSET;
             mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);
             mAttachedWindow = attachedWindow;
-            if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + this + " to " + mAttachedWindow);
+            if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + this + " to "
+                    + mAttachedWindow);
 
             final WindowList childWindows = mAttachedWindow.mChildWindows;
             final int numChildWindows = childWindows.size();
@@ -552,7 +554,7 @@
     @Override
     public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf, Rect sf,
             Rect osf) {
-        if (mAppToken != null && mAppToken.mReplacingWindow
+        if (mAppToken != null && mAppToken.mWillReplaceWindow
                 && (mExiting || !mAppToken.mReplacingRemoveRequested)) {
             // This window is being replaced and either already got information that it's being
             // removed or we are still waiting for some information. Because of this we don't
@@ -1307,10 +1309,12 @@
 
     void maybeRemoveReplacedWindow() {
         AppWindowToken token = mAppToken;
-        if (token != null && token.mReplacingWindow) {
-            token.mReplacingWindow = false;
+        if (token != null && token.mWillReplaceWindow && token.mReplacingWindow == this) {
+            if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replacing window: " + this);
+            token.mWillReplaceWindow = false;
             token.mAnimateReplacingWindow = false;
             token.mReplacingRemoveRequested = false;
+            token.mReplacingWindow = null;
             for (int i = token.allAppWindows.size() - 1; i >= 0; i--) {
                 WindowState win = token.allAppWindows.get(i);
                 if (win.mExiting) {
@@ -1501,6 +1505,31 @@
         return mExiting || (mService.mClosingApps.contains(mAppToken));
     }
 
+    boolean saveSurfaceIfNeeded() {
+        if (ActivityManager.isLowRamDeviceStatic()) {
+            // Don't save surfaces on Svelte devices.
+            return false;
+        }
+
+        Task task = getTask();
+        if (task == null || task.inHomeStack()) {
+            // Don't save surfaces for home stack apps. These usually resume and draw
+            // first frame very fast. Saving surfaces are mostly a waste of memory.
+            return false;
+        }
+
+        // We want to save surface if the app's windows are "allDrawn", or if we're
+        // currently animating with save surfaces. (If the app didn't even finish
+        // drawing when the user exits, but we have a saved surface from last time,
+        // we still want to keep that surface.)
+        if (mAppToken.allDrawn || mAppToken.mAnimatingWithSavedSurface) {
+            mAppToken.mHasSavedSurface = true;
+            return true;
+        }
+
+        return false;
+    }
+
     @Override
     public boolean isDefaultDisplay() {
         final DisplayContent displayContent = getDisplayContent();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 1754123..68c39ba 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -38,7 +38,6 @@
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.graphics.Region;
 import android.os.Debug;
 import android.os.RemoteException;
@@ -536,6 +535,7 @@
             mDrawState = COMMIT_DRAW_PENDING;
             return true;
         }
+
         return false;
     }
 
@@ -555,7 +555,8 @@
         mDrawState = READY_TO_SHOW;
         boolean result = false;
         final AppWindowToken atoken = mWin.mAppToken;
-        if (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
+        if (atoken == null || atoken.allDrawn || atoken.mAnimatingWithSavedSurface ||
+                mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
             result = performShowLocked();
         }
         if (mDestroyPreservedSurfaceUponRedraw && result) {
@@ -996,12 +997,16 @@
     }
 
     void destroySurfaceLocked() {
-        if (mWin.mAppToken != null && mWin == mWin.mAppToken.startingWindow) {
-            mWin.mAppToken.startingDisplayed = false;
+        final AppWindowToken wtoken = mWin.mAppToken;
+        if (wtoken != null) {
+            wtoken.mHasSavedSurface = false;
+            wtoken.mAnimatingWithSavedSurface = false;
+            if (mWin == wtoken.startingWindow) {
+                wtoken.startingDisplayed = false;
+            }
         }
 
         if (mSurfaceControl != null) {
-
             int i = mWin.mChildWindows.size();
             while (i > 0) {
                 i--;
@@ -1428,7 +1433,7 @@
         // living in a different stack. If we suddenly crop it to the new stack bounds, it might
         // get cut off. We don't want it to happen, so we let it ignore the stack bounds until it
         // gets removed. The window that will replace it will abide them.
-        if (appToken != null && appToken.mCropWindowsToStack && !appToken.mReplacingWindow) {
+        if (appToken != null && appToken.mCropWindowsToStack && !appToken.mWillReplaceWindow) {
             TaskStack stack = w.getTask().mStack;
             stack.getBounds(mTmpStackBounds);
             // When we resize we use the big surface approach, which means we can't trust the
@@ -1854,8 +1859,7 @@
                 }
             }
 
-            if (mWin.mAttrs.type != TYPE_APPLICATION_STARTING
-                    && mWin.mAppToken != null) {
+            if (mWin.mAttrs.type != TYPE_APPLICATION_STARTING && mWin.mAppToken != null) {
                 mWin.mAppToken.firstWindowDrawn = true;
 
                 if (mWin.mAppToken.startingData != null) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 0c004b2..b267860 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -17,6 +17,7 @@
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.WindowManagerService.DEBUG;
 import static com.android.server.wm.WindowManagerService.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerService.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerService.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
 import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT_REPEATS;
@@ -111,13 +112,34 @@
     private int mPreferredModeId = 0;
 
     private boolean mTraversalScheduled;
+    private int mDeferDepth = 0;
 
     public WindowSurfacePlacer(WindowManagerService service) {
         mService = service;
         mWallpaperControllerLocked = mService.mWallpaperControllerLocked;
     }
 
+    /**
+     * See {@link WindowManagerService#deferSurfaceLayout()}
+     */
+    void deferLayout() {
+        mDeferDepth++;
+    }
+
+    /**
+     * See {@link WindowManagerService#continueSurfaceLayout()}
+     */
+    void continueLayout() {
+        mDeferDepth--;
+        if (mDeferDepth <= 0) {
+            performSurfacePlacement();
+        }
+    }
+
     final void performSurfacePlacement() {
+        if (mDeferDepth > 0) {
+            return;
+        }
         int loopCount = 6;
         do {
             mTraversalScheduled = false;
@@ -340,6 +362,10 @@
                 // Don't remove this window until rotation has completed.
                 continue;
             }
+            // Discard the saved surface if window size is changed, it can't be reused.
+            if (win.mAppToken != null && win.mAppToken.mHasSavedSurface) {
+                win.mWinAnimator.destroySurfaceLocked();
+            }
             win.reportResized();
             mService.mResizingWindows.remove(i);
         }
@@ -371,7 +397,9 @@
                 if (mWallpaperControllerLocked.isWallpaperTarget(win)) {
                     wallpaperDestroyed = true;
                 }
-                win.mWinAnimator.destroySurfaceLocked();
+                if (!win.saveSurfaceIfNeeded()) {
+                    win.mWinAnimator.destroySurfaceLocked();
+                }
             } while (i > 0);
             mService.mDestroySurface.clear();
         }
@@ -1174,6 +1202,18 @@
                 }
             }
             createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer);
+
+            if (wtoken.mHasSavedSurface) {
+                if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+                        "Early start of animation: " + wtoken + ", allDrawn=" + wtoken.allDrawn);
+
+                for (int j = 0; j < wtoken.windows.size(); j++) {
+                    WindowState ws = wtoken.windows.get(j);
+                    ws.mWinAnimator.mDrawState = WindowStateAnimator.READY_TO_SHOW;
+                }
+                wtoken.mHasSavedSurface = false;
+                wtoken.mAnimatingWithSavedSurface = true;
+            }
         }
 
         AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ?  null :
@@ -1219,6 +1259,10 @@
                         + wtoken.allDrawn + " startingDisplayed="
                         + wtoken.startingDisplayed + " startingMoved="
                         + wtoken.startingMoved);
+
+                if (wtoken.mHasSavedSurface || wtoken.mAnimatingWithSavedSurface) {
+                    continue;
+                }
                 if (!wtoken.allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
                     return false;
                 }