Re-implement Z-ordering based off SurfaceControl hierarchy.

First steps in using the SurfaceControl hierarchy pervasively through
the WindowManager. For each level of the WindowContainer hierarchy
we instantiate a buffer-less SurfaceControl which is used to apply
constraints to all of it's children. For now we use full-screen surfaces
at every non-leaf level and so the only constraint we apply is Z-ordering.

Calculation of Z-ordering is done through the WindowContainer#assignLayers
pass and begins with DisplayContent. Changes are accumulated in-to a pending
transaction which is applied as part of prepareSurfaces (which now applies
to each level of the WindowContainer hierarchy rather than just windows).

Bug: 64815723
Test: Existing tests pass. go/wm-smoke. ZOrderTests.
Change-Id: Ib31bc3107c7fa398cf2ed72430fcb7596fad6fd5
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 9162a97..7748ae4 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -8062,19 +8062,6 @@
     }
 
     @Override
-    public boolean canMagnifyWindow(int windowType) {
-        switch (windowType) {
-            case WindowManager.LayoutParams.TYPE_INPUT_METHOD:
-            case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG:
-            case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR:
-            case WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY: {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @Override
     public boolean isTopLevelWindow(int windowType) {
         if (windowType >= WindowManager.LayoutParams.FIRST_SUB_WINDOW
                 && windowType <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index de4fd7cd..88d1e55 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -292,6 +292,8 @@
         public void setMagnificationSpecLocked(MagnificationSpec spec) {
             mMagnifedViewport.updateMagnificationSpecLocked(spec);
             mMagnifedViewport.recomputeBoundsLocked();
+
+            mService.applyMagnificationSpec(spec);
             mService.scheduleAnimationLocked();
         }
 
@@ -421,7 +423,7 @@
         public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
             MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
             if (spec != null && !spec.isNop()) {
-                if (!mService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
+                if (!windowState.shouldMagnify()) {
                     return null;
                 }
             }
@@ -476,6 +478,7 @@
             private final ViewportWindow mWindow;
 
             private boolean mFullRedrawNeeded;
+            private int mTempLayer = 0;
 
             public MagnifiedViewport() {
                 mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
@@ -565,7 +568,7 @@
                     portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
                     windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
 
-                    if (mService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
+                    if (windowState.shouldMagnify()) {
                         mMagnificationRegion.op(windowBounds, Region.Op.UNION);
                         mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
                     } else {
@@ -676,10 +679,12 @@
 
             private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
                 final DisplayContent dc = mService.getDefaultDisplayContentLocked();
+                mTempLayer = 0;
                 dc.forAllWindows((w) -> {
                     if (w.isOnScreen() && w.isVisibleLw()
                             && !w.mWinAnimator.mEnterAnimationPending) {
-                        outWindows.put(w.mLayer, w);
+                        mTempLayer++;
+                        outWindows.put(mTempLayer, w);
                     }
                 }, false /* traverseTopToBottom */ );
             }
@@ -705,7 +710,7 @@
                     SurfaceControl surfaceControl = null;
                     try {
                         mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
-                        surfaceControl = new SurfaceControl.Builder(mService.mFxSession)
+                        surfaceControl = mService.getDefaultDisplayContentLocked().makeOverlay()
                                 .setName(SURFACE_TITLE)
                                 .setSize(mTempPoint.x, mTempPoint.y) // not a typo
                                 .setFormat(PixelFormat.TRANSLUCENT)
@@ -714,8 +719,6 @@
                         /* ignore */
                     }
                     mSurfaceControl = surfaceControl;
-                    mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay()
-                            .getLayerStack());
                     mSurfaceControl.setLayer(mService.mPolicy.getWindowLayerFromTypeLw(
                             TYPE_MAGNIFICATION_OVERLAY)
                             * WindowManagerService.TYPE_LAYER_MULTIPLIER);
@@ -1005,6 +1008,8 @@
 
         private final long mRecurringAccessibilityEventsIntervalMillis;
 
+        private int mTempLayer = 0;
+
         public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
                 WindowsForAccessibilityCallback callback) {
             mContext = windowManagerService.mContext;
@@ -1090,6 +1095,7 @@
                     if (isReportedWindowType(windowState.mAttrs.type)) {
                         // Add the window to the ones to be reported.
                         WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen);
+                        window.layer = addedWindows.size();
                         addedWindows.add(window.token);
                         windows.add(window);
                         if (windowState.isFocused()) {
@@ -1323,9 +1329,10 @@
 
         private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
             final DisplayContent dc = mService.getDefaultDisplayContentLocked();
+            mTempLayer = 0;
             dc.forAllWindows((w) -> {
                 if (w.isVisibleLw()) {
-                    outWindows.put(w.mLayer, w);
+                    outWindows.put(mTempLayer++, w);
                 }
             }, false /* traverseTopToBottom */ );
         }
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 2ef7f25..accfc65 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -253,7 +253,6 @@
 
     private void updateLayers() {
         mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */);
-        updateThumbnailLayer();
     }
 
     private void stepThumbnailAnimation(long currentTime) {
@@ -283,27 +282,12 @@
                 + "][" + tmpFloats[Matrix.MSKEW_X]
                 + "," + tmpFloats[Matrix.MSCALE_Y] + "]");
         thumbnail.setAlpha(thumbnailTransformation.getAlpha());
-        updateThumbnailLayer();
         thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
                 tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
         thumbnail.setWindowCrop(thumbnailTransformation.getClipRect());
     }
 
     /**
-     * Updates the thumbnail layer z order to just above the highest animation layer if changed
-     */
-    void updateThumbnailLayer() {
-        if (thumbnail != null) {
-            final int layer = mAppToken.getHighestAnimLayer();
-            if (DEBUG_LAYERS) Slog.v(TAG,
-                    "Setting thumbnail layer " + mAppToken + ": layer=" + layer);
-            thumbnail.setLayer(layer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
-                    - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
-            mThumbnailLayer = layer;
-        }
-    }
-
-    /**
      * Sometimes we need to synchronize the first frame of animation with some external event, e.g.
      * Recents hiding some of its content. To achieve this, we prolong the start of the animaiton
      * and keep producing the first frame of the animation.
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 9729e50..f19cd0f 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -30,7 +30,6 @@
 import android.util.Slog;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 
 /**
  * Four black surfaces put together to make a black frame.
@@ -42,22 +41,22 @@
         final int layer;
         final SurfaceControl surface;
 
-        BlackSurface(SurfaceSession session, int layer, int l, int t, int r, int b, int layerStack)
-                throws OutOfResourcesException {
+        BlackSurface(int layer,
+                int l, int t, int r, int b, DisplayContent dc) throws OutOfResourcesException {
             left = l;
             top = t;
             this.layer = layer;
             int w = r-l;
             int h = b-t;
 
-            surface = new SurfaceControl.Builder(session)
+            surface = dc.makeOverlay()
                     .setName("BlackSurface")
                     .setSize(w, h)
                     .setColorLayer(true)
+                    .setParent(null) // TODO: Work-around for b/69259549
                     .build();
 
             surface.setAlpha(1);
-            surface.setLayerStack(layerStack);
             surface.setLayer(layer);
             surface.show();
             if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
@@ -114,30 +113,32 @@
         }
     }
 
-    public BlackFrame(SurfaceSession session, Rect outer, Rect inner, int layer, int layerStack,
+    public BlackFrame(Rect outer, Rect inner, int layer, DisplayContent dc,
             boolean forceDefaultOrientation) throws OutOfResourcesException {
         boolean success = false;
 
         mForceDefaultOrientation = forceDefaultOrientation;
 
+        // TODO: Why do we use 4 surfaces instead of just one big one behind the screenshot?
+        // b/68253229
         mOuterRect = new Rect(outer);
         mInnerRect = new Rect(inner);
         try {
             if (outer.top < inner.top) {
-                mBlackSurfaces[0] = new BlackSurface(session, layer,
-                        outer.left, outer.top, inner.right, inner.top, layerStack);
+                mBlackSurfaces[0] = new BlackSurface(layer,
+                        outer.left, outer.top, inner.right, inner.top, dc);
             }
             if (outer.left < inner.left) {
-                mBlackSurfaces[1] = new BlackSurface(session, layer,
-                        outer.left, inner.top, inner.left, outer.bottom, layerStack);
+                mBlackSurfaces[1] = new BlackSurface(layer,
+                        outer.left, inner.top, inner.left, outer.bottom, dc);
             }
             if (outer.bottom > inner.bottom) {
-                mBlackSurfaces[2] = new BlackSurface(session, layer,
-                        inner.left, inner.bottom, outer.right, outer.bottom, layerStack);
+                mBlackSurfaces[2] = new BlackSurface(layer,
+                        inner.left, inner.bottom, outer.right, outer.bottom, dc);
             }
             if (outer.right > inner.right) {
-                mBlackSurfaces[3] = new BlackSurface(session, layer,
-                        inner.right, outer.top, outer.right, inner.bottom, layerStack);
+                mBlackSurfaces[3] = new BlackSurface(layer,
+                        inner.right, outer.top, outer.right, inner.bottom, dc);
             }
             success = true;
         } finally {
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index 2d5d1b2..2a216ab 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -33,7 +33,6 @@
 import android.view.Surface;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 
 class CircularDisplayMask {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "CircularDisplayMask" : TAG_WM;
@@ -54,8 +53,10 @@
     private boolean mDimensionsUnequal = false;
     private int mMaskThickness;
 
-    public CircularDisplayMask(Display display, SurfaceSession session, int zOrder,
+    public CircularDisplayMask(DisplayContent dc, int zOrder,
             int screenOffset, int maskThickness) {
+        final Display display = dc.getDisplay();
+
         mScreenSize = new Point();
         display.getSize(mScreenSize);
         if (mScreenSize.x != mScreenSize.y + screenOffset) {
@@ -66,7 +67,7 @@
 
         SurfaceControl ctrl = null;
         try {
-            ctrl = new SurfaceControl.Builder(session)
+            ctrl = dc.makeOverlay()
                     .setName("CircularDisplayMask")
                     .setSize(mScreenSize.x, mScreenSize.y) // not a typo
                     .setFormat(PixelFormat.TRANSLUCENT)
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index cc94807..b534b8a 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -375,6 +375,10 @@
         return toString();
     }
 
+    boolean isAlwaysOnTop() {
+        return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
+    }
+
     abstract protected int getChildCount();
 
     abstract protected E getChildAt(int index);
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index 8fb2be8..3f360e2 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -67,8 +67,7 @@
 
     private boolean mDestroyed = false;
 
-    private final int mDisplayId;
-
+    private final DisplayContent mDisplayContent;
 
     /** Interface implemented by users of the dim layer */
     interface DimLayerUser {
@@ -93,18 +92,18 @@
 
     private final String mName;
 
-    DimLayer(WindowManagerService service, DimLayerUser user, int displayId, String name) {
+    DimLayer(WindowManagerService service, DimLayerUser user, DisplayContent dc, String name) {
         mUser = user;
-        mDisplayId = displayId;
+        mDisplayContent = dc;
         mService = service;
         mName = name;
-        if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: displayId=" + displayId);
+        if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: dc=" + dc);
     }
 
     private void constructSurface(WindowManagerService service) {
         service.openSurfaceTransaction();
         try {
-            mDimSurface = new SurfaceControl.Builder(service.mFxSession)
+            mDimSurface = mDisplayContent.makeSurface()
                     .setName(mName)
                     .setSize(16, 16)
                     .setColorLayer(true)
@@ -112,7 +111,6 @@
 
             if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
                     "  DIM " + mDimSurface + ": CREATE");
-            mDimSurface.setLayerStack(mDisplayId);
             adjustBounds();
             adjustAlpha(mAlpha);
             adjustLayer(mLayer);
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 6f9e45a..a1a10d8 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -54,7 +54,6 @@
         final boolean previousFullscreen = state.dimLayer != null
                 && state.dimLayer == mSharedFullScreenDimLayer;
         DimLayer newDimLayer;
-        final int displayId = mDisplayContent.getDisplayId();
         if (dimLayerUser.dimFullscreen()) {
             if (previousFullscreen && mSharedFullScreenDimLayer != null) {
                 // Update the bounds for fullscreen in case of rotation.
@@ -69,8 +68,8 @@
                     newDimLayer = state.dimLayer;
                 } else {
                     // Create new full screen dim layer.
-                    newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
-                            getDimLayerTag(dimLayerUser));
+                    newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser,
+                            mDisplayContent, getDimLayerTag(dimLayerUser));
                 }
                 dimLayerUser.getDimBounds(mTmpBounds);
                 newDimLayer.setBounds(mTmpBounds);
@@ -80,7 +79,7 @@
             }
         } else {
             newDimLayer = (state.dimLayer == null || previousFullscreen)
-                    ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
+                    ? new DimLayer(mDisplayContent.mService, dimLayerUser, mDisplayContent,
                             getDimLayerTag(dimLayerUser))
                     : state.dimLayer;
             dimLayerUser.getDimBounds(mTmpBounds);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4d839d0..9494ade 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -142,8 +142,10 @@
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.InputDevice;
+import android.view.MagnificationSpec;
 import android.view.Surface;
 import android.view.SurfaceControl;
+import android.view.SurfaceSession;
 import android.view.WindowManagerPolicy;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -346,10 +348,37 @@
     // {@code false} if this display is in the processing of being created.
     private boolean mDisplayReady = false;
 
-    private final WindowLayersController mLayersController;
     WallpaperController mWallpaperController;
     int mInputMethodAnimLayerAdjustment;
 
+    private final SurfaceSession mSession = new SurfaceSession();
+
+    /**
+     * We organize all top-level Surfaces in to the following layers.
+     * mOverlayLayer contains a few Surfaces which are always on top of others
+     * and omitted from Screen-Magnification ({@link WindowState#isScreenOverlay})
+     * {@link #mWindowingLayer} contains everything else.
+     */
+    private SurfaceControl mOverlayLayer;
+
+    /**
+     * See {@link #mOverlayLayer}
+     */
+    private SurfaceControl mWindowingLayer;
+
+    /**
+     * Specifies the size of the surfaces in {@link #mOverlayLayer} and {@link #mWindowingLayer}.
+     * <p>
+     * For these surfaces currently we use a surface based on the larger of width or height so we
+     * don't have to resize when rotating the display.
+     */
+    private int mSurfaceSize;
+
+    /**
+     * A list of surfaces to be destroyed after {@link #mPendingTransaction} is applied.
+     */
+    private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
+
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
         if (winAnimator.hasSurface()) {
@@ -503,9 +532,6 @@
         return true;
     };
 
-    private final Consumer<WindowState> mPrepareWindowSurfaces =
-            w -> w.mWinAnimator.prepareSurfaceLocked(true);
-
     private final Consumer<WindowState> mPerformLayout = w -> {
         // Don't do layout of a window if it is not visible, or soon won't be visible, to avoid
         // wasting time and funky changes while a window is animating away.
@@ -741,13 +767,11 @@
      * initialize direct children.
      * @param display May not be null.
      * @param service You know.
-     * @param layersController window layer controller used to assign layer to the windows on this
-     *                         display.
      * @param wallpaperController wallpaper windows controller used to adjust the positioning of the
      *                            wallpaper windows in the window list.
      */
     DisplayContent(Display display, WindowManagerService service,
-            WindowLayersController layersController, WallpaperController wallpaperController) {
+            WallpaperController wallpaperController) {
         if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
             throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
                     + " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
@@ -756,7 +780,6 @@
 
         mDisplay = display;
         mDisplayId = display.getDisplayId();
-        mLayersController = layersController;
         mWallpaperController = wallpaperController;
         display.getDisplayInfo(mDisplayInfo);
         display.getMetrics(mDisplayMetrics);
@@ -768,6 +791,22 @@
         mPinnedStackControllerLocked = new PinnedStackController(service, this);
         mDimLayerController = new DimLayerController(this);
 
+        mSurfaceSize = Math.max(mBaseDisplayHeight, mBaseDisplayWidth);
+
+        final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession)
+                .setSize(mSurfaceSize, mSurfaceSize)
+                .setOpaque(true);
+        mWindowingLayer = b.setName("Display Root").build();
+        mOverlayLayer = b.setName("Display Overlays").build();
+
+        mPendingTransaction.setLayer(mWindowingLayer, 0)
+                .setLayerStack(mWindowingLayer, mDisplayId)
+                .show(mWindowingLayer)
+                .setLayer(mOverlayLayer, 1)
+                .setLayerStack(mOverlayLayer, mDisplayId)
+                .show(mOverlayLayer);
+        mPendingTransaction.apply();
+
         // These are the only direct children we should ever have and they are permanent.
         super.addChild(mBelowAppWindowsContainers, null);
         super.addChild(mTaskStackContainers, null);
@@ -1071,8 +1110,7 @@
             //       it doesn't support hardware OpenGL emulation yet.
             if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
                     && screenRotationAnimation.hasScreenshot()) {
-                if (screenRotationAnimation.setRotationInTransaction(
-                        rotation, mService.mFxSession,
+                if (screenRotationAnimation.setRotationInTransaction(rotation,
                         MAX_ANIMATION_DURATION, mService.getTransitionAnimationScaleLocked(),
                         mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight)) {
                     mService.scheduleAnimationLocked();
@@ -1947,6 +1985,9 @@
                     mService.unregisterPointerEventListener(mService.mMousePositionTracker);
                 }
             }
+            // The pending transaction won't be applied so we should
+            // just clean up any surfaces pending destruction.
+            onPendingTransactionApplied();
         } finally {
             mRemovingDisplay = false;
         }
@@ -2342,10 +2383,16 @@
 
     /** Updates the layer assignment of windows on this display. */
     void assignWindowLayers(boolean setLayoutNeeded) {
-        mLayersController.assignWindowLayers(this);
+        assignChildLayers(mPendingTransaction);
         if (setLayoutNeeded) {
             setLayoutNeeded();
         }
+
+        // We accumlate the layer changes in-to "mPendingTransaction" but we defer
+        // the application of this transaction until the animation pass triggers
+        // prepareSurfaces. This allows us to synchronize Z-ordering changes with
+        // the hiding and showing of surfaces.
+        scheduleAnimation();
     }
 
     // TODO: This should probably be called any time a visual change is made to the hierarchy like
@@ -2701,10 +2748,6 @@
         }
     }
 
-    void prepareWindowSurfaces() {
-        forAllWindows(mPrepareWindowSurfaces, false /* traverseTopToBottom */);
-    }
-
     boolean inputMethodClientHasFocus(IInputMethodClient client) {
         final WindowState imFocus = computeImeTarget(false /* updateImeTarget */);
         if (imFocus == null) {
@@ -3062,13 +3105,6 @@
                 // Include this window.
 
                 final WindowStateAnimator winAnim = w.mWinAnimator;
-                int layer = winAnim.mSurfaceController.getLayer();
-                if (mScreenshotApplicationState.maxLayer < layer) {
-                    mScreenshotApplicationState.maxLayer = layer;
-                }
-                if (mScreenshotApplicationState.minLayer > layer) {
-                    mScreenshotApplicationState.minLayer = layer;
-                }
 
                 // Don't include wallpaper in bounds calculation
                 if (!w.mIsWallpaper && !mutableIncludeFullDisplay.value) {
@@ -3112,8 +3148,6 @@
 
             final WindowState appWin = mScreenshotApplicationState.appWin;
             final boolean screenshotReady = mScreenshotApplicationState.screenshotReady;
-            final int maxLayer = mScreenshotApplicationState.maxLayer;
-            final int minLayer = mScreenshotApplicationState.minLayer;
 
             if (appToken != null && appWin == null) {
                 // Can't find a window to snapshot.
@@ -3134,11 +3168,6 @@
             // because we don't want to release the mWindowMap lock until the screenshot is
             // taken.
 
-            if (maxLayer == 0) {
-                if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
-                        + ": returning null maxLayer=" + maxLayer);
-                return null;
-            }
 
             if (!mutableIncludeFullDisplay.value) {
                 // Constrain frame to the screen size.
@@ -3183,8 +3212,6 @@
             convertCropForSurfaceFlinger(crop, rot, dw, dh);
 
             if (DEBUG_SCREENSHOT) {
-                Slog.i(TAG_WM, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "
-                        + maxLayer + " appToken=" + appToken);
                 forAllWindows(w -> {
                     final WindowSurfaceController controller = w.mWinAnimator.mSurfaceController;
                     Slog.i(TAG_WM, w + ": " + w.mLayer
@@ -3206,11 +3233,13 @@
             SurfaceControl.openTransaction();
             SurfaceControl.closeTransactionSync();
 
-            bitmap = screenshoter.screenshot(crop, width, height, minLayer, maxLayer,
+            // TODO(b/68392460): We should screenshot Task controls directly
+            // but it's difficult at the moment as the Task doesn't have the
+            // correct size set.
+            bitmap = screenshoter.screenshot(crop, width, height, 0, 1,
                     inRotation, rot);
             if (bitmap == null) {
-                Slog.w(TAG_WM, "Screenshot failure taking screenshot for (" + dw + "x" + dh
-                        + ") to layer " + maxLayer);
+                Slog.w(TAG_WM, "Failed to take screenshot");
                 return null;
             }
         }
@@ -3366,6 +3395,10 @@
      * I.e Activities.
      */
     private final class TaskStackContainers extends DisplayChildWindowContainer<TaskStack> {
+        /**
+         * A control placed at the appropriate level for transitions to occur.
+         */
+        SurfaceControl mAnimationLayer = null;
 
         // Cached reference to some special stacks we tend to get a lot so we don't need to loop
         // through the list to find them.
@@ -3677,6 +3710,50 @@
             // to prevent freezing/unfreezing the display too early.
             return mLastOrientation;
         }
+
+        @Override
+        void assignChildLayers(SurfaceControl.Transaction t) {
+            final int NORMAL_STACK_STATE = 0;
+            final int BOOSTED_STATE = 1;
+            final int ALWAYS_ON_TOP_STATE = 2;
+
+            // We allow stacks to change visual order from the AM specified order due to
+            // Z-boosting during animations. However we must take care to ensure TaskStacks
+            // which are marked as alwaysOnTop remain that way.
+            int layer = 0;
+            for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) {
+                for (int i = 0; i < mChildren.size(); i++) {
+                    final TaskStack s = mChildren.get(i);
+                    layer++;
+                    if (state == NORMAL_STACK_STATE) {
+                        s.assignLayer(t, layer);
+                    } else if (state == BOOSTED_STATE && s.needsZBoost()) {
+                        s.assignLayer(t, layer);
+                    } else if (state == ALWAYS_ON_TOP_STATE &&
+                            s.isAlwaysOnTop()) {
+                        s.assignLayer(t, layer);
+                    }
+                    s.assignChildLayers(t);
+                }
+                // The appropriate place for App-Transitions to occur is right
+                // above all other animations but still below things in the Picture-and-Picture
+                // windowing mode.
+                if (state == BOOSTED_STATE && mAnimationLayer != null) {
+                    t.setLayer(mAnimationLayer, layer + 1);
+                }
+            }
+        }
+
+        @Override
+        void onParentSet() {
+            super.onParentSet();
+            if (getParent() != null) {
+                mAnimationLayer = makeSurface().build();
+            } else {
+                mAnimationLayer.destroy();
+                mAnimationLayer = null;
+            }
+        }
     }
 
     /**
@@ -3760,4 +3837,114 @@
         E screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
                 boolean useIdentityTransform, int rotation);
     }
+
+    SurfaceControl.Builder makeSurface(SurfaceSession s) {
+        return mService.makeSurfaceBuilder(s)
+                .setParent(mWindowingLayer);
+    }
+
+    @Override
+    SurfaceSession getSession() {
+        return mSession;
+    }
+
+    @Override
+    SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+        final SurfaceControl.Builder b = mService.makeSurfaceBuilder(child.getSession());
+        b.setName(child.getName());
+
+        b.setSize(mSurfaceSize, mSurfaceSize);
+        if (child.isScreenOverlay()) {
+            return b.setParent(mOverlayLayer);
+        } else {
+            return b.setParent(mWindowingLayer);
+        }
+    }
+
+    /**
+     * The makeSurface variants are for use by the window-container
+     * hierarchy. makeOverlay here is a function for various non windowing
+     * overlays like the ScreenRotation screenshot, the Strict Mode Flash
+     * and other potpourii.
+     */
+    SurfaceControl.Builder makeOverlay() {
+        return mService.makeSurfaceBuilder(mSession)
+            .setParent(mOverlayLayer);
+    }
+
+    void applyMagnificationSpec(MagnificationSpec spec) {
+        applyMagnificationSpec(mPendingTransaction, spec);
+        mPendingTransaction.apply();
+    }
+
+    @Override
+    void onParentSet() {
+        // Since we are the top of the SurfaceControl hierarchy here
+        // we create the root surfaces explicitly rather than chaining
+        // up as the default implementation in onParentSet does. So we
+        // explicitly do NOT call super here.
+    }
+
+    @Override
+    void assignChildLayers(SurfaceControl.Transaction t) {
+        t.setLayer(mOverlayLayer, 1)
+                .setLayer(mWindowingLayer, 0);
+
+        // These are layers as children of "mWindowingLayer"
+        mBelowAppWindowsContainers.assignLayer(t, 0);
+        mTaskStackContainers.assignLayer(t, 1);
+        mAboveAppWindowsContainers.assignLayer(t, 2);
+
+        WindowState imeTarget = mService.mInputMethodTarget;
+        if (imeTarget == null || imeTarget.inSplitScreenWindowingMode()) {
+            // In split-screen windowing mode we can't layer the
+            // IME relative to the IME target because it needs to
+            // go over the docked divider, so instead we place it on top
+            // of everything and use relative layering of windows which need
+            // to go above it (see special logic in WindowState#assignLayer)
+            mImeWindowsContainers.assignLayer(t, 3);
+        } else {
+            t.setRelativeLayer(mImeWindowsContainers.getSurfaceControl(),
+                    imeTarget.getSurfaceControl(),
+                    // TODO: We need to use an extra level on the app surface to ensure
+                    // this is always above SurfaceView but always below attached window.
+                    1);
+        }
+
+        // Above we have assigned layers to our children, now we ask them to assign
+        // layers to their children.
+        mBelowAppWindowsContainers.assignChildLayers(t);
+        mTaskStackContainers.assignChildLayers(t);
+        mAboveAppWindowsContainers.assignChildLayers(t);
+        mImeWindowsContainers.assignChildLayers(t);
+    }
+
+    /**
+     * Here we satisfy an unfortunate special case of the IME in split-screen mode. Imagine
+     * that the IME target is one of the docked applications. We'd like the docked divider to be
+     * above both of the applications, and we'd like the IME to be above the docked divider.
+     * However we need child windows of the applications to be above the IME (Text drag handles).
+     * This is a non-strictly hierarcical layering and we need to break out of the Z ordering
+     * somehow. We do this by relatively ordering children of the target to the IME in cooperation
+     * with {@link #WindowState#assignLayer}
+     */
+    void assignRelativeLayerForImeTargetChild(SurfaceControl.Transaction t, WindowContainer child) {
+        t.setRelativeLayer(child.getSurfaceControl(), mImeWindowsContainers.getSurfaceControl(), 1);
+    }
+
+    @Override
+    void destroyAfterPendingTransaction(SurfaceControl surface) {
+        mPendingDestroyingSurfaces.add(surface);
+    }
+
+    /**
+     * Destroys any surfaces that have been put into the pending list with
+     * {@link #destroyAfterTransaction}.
+     */
+    void onPendingTransactionApplied() {
+        for (int i = mPendingDestroyingSurfaces.size() - 1; i >= 0; i--) {
+            mPendingDestroyingSurfaces.get(i).destroy();
+        }
+        mPendingDestroyingSurfaces.clear();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index d79ba89..4b18d59 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -146,7 +146,7 @@
         mService = service;
         mDisplayContent = displayContent;
         final Context context = service.mContext;
-        mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId(),
+        mDimLayer = new DimLayer(displayContent.mService, this, displayContent,
                 "DockedStackDim");
         mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
                 context, android.R.interpolator.fast_out_slow_in);
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
index 8bec8d7..fddf6ca 100644
--- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -49,19 +49,19 @@
     private int mRotation;
     private boolean mVisible;
 
-    public EmulatorDisplayOverlay(Context context, Display display, SurfaceSession session,
+    public EmulatorDisplayOverlay(Context context, DisplayContent dc,
             int zOrder) {
+        final Display display = dc.getDisplay();
         mScreenSize = new Point();
         display.getSize(mScreenSize);
 
         SurfaceControl ctrl = null;
         try {
-            ctrl = new SurfaceControl.Builder(session)
+            ctrl = dc.makeOverlay()
                     .setName("EmulatorDisplayOverlay")
                     .setSize(mScreenSize.x, mScreenSize.y)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
-            ctrl.setLayerStack(display.getLayerStack());
             ctrl.setLayer(zOrder);
             ctrl.setPosition(0, 0);
             ctrl.show();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index f541926..43dfccc 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -138,7 +138,6 @@
     ParcelFileDescriptor mSurfaceTraceFd;
     RemoteEventTrace mRemoteEventTrace;
 
-    private final WindowLayersController mLayersController;
     final WallpaperController mWallpaperController;
 
     private final Handler mHandler;
@@ -163,7 +162,6 @@
     RootWindowContainer(WindowManagerService service) {
         mService = service;
         mHandler = new MyHandler(service.mH.getLooper());
-        mLayersController = new WindowLayersController(mService);
         mWallpaperController = new WallpaperController(mService);
     }
 
@@ -231,7 +229,7 @@
     }
 
     private DisplayContent createDisplayContent(final Display display) {
-        final DisplayContent dc = new DisplayContent(display, mService, mLayersController,
+        final DisplayContent dc = new DisplayContent(display, mService,
                 mWallpaperController);
         final int displayId = display.getDisplayId();
 
@@ -1103,4 +1101,9 @@
     String getName() {
         return "ROOT";
     }
+
+    @Override
+    void scheduleAnimation() {
+        mService.scheduleAnimationLocked();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 3350fea..70bf15c 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -225,7 +225,7 @@
     }
 
     public ScreenRotationAnimation(Context context, DisplayContent displayContent,
-            SurfaceSession session, boolean inTransaction, boolean forceDefaultOrientation,
+            boolean inTransaction, boolean forceDefaultOrientation,
             boolean isSecure, WindowManagerService service) {
         mService = service;
         mContext = context;
@@ -269,7 +269,7 @@
 
         try {
             try {
-                mSurfaceControl = new SurfaceControl.Builder(session)
+                mSurfaceControl = displayContent.makeOverlay()
                         .setName("ScreenshotSurface")
                         .setSize(mWidth, mHeight)
                         .setSecure(isSecure)
@@ -281,7 +281,6 @@
                 // TODO(multidisplay): we should use the proper display
                 SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
                         SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), sur);
-                mSurfaceControl.setLayerStack(display.getLayerStack());
                 mSurfaceControl.setLayer(SCREEN_FREEZE_LAYER_SCREENSHOT);
                 mSurfaceControl.setAlpha(0);
                 mSurfaceControl.show();
@@ -370,11 +369,11 @@
     }
 
     // Must be called while in a transaction.
-    public boolean setRotationInTransaction(int rotation, SurfaceSession session,
+    public boolean setRotationInTransaction(int rotation,
             long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight) {
         setRotationInTransaction(rotation);
         if (TWO_PHASE_ANIMATION) {
-            return startAnimation(session, maxAnimationDuration, animationScale,
+            return startAnimation(maxAnimationDuration, animationScale,
                     finalWidth, finalHeight, false, 0, 0);
         }
 
@@ -385,7 +384,7 @@
     /**
      * Returns true if animating.
      */
-    private boolean startAnimation(SurfaceSession session, long maxAnimationDuration,
+    private boolean startAnimation(long maxAnimationDuration,
             float animationScale, int finalWidth, int finalHeight, boolean dismissing,
             int exitAnim, int enterAnim) {
         if (mSurfaceControl == null) {
@@ -561,8 +560,8 @@
                 Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
                         mOriginalWidth*2, mOriginalHeight*2);
                 Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
-                mCustomBlackFrame = new BlackFrame(session, outer, inner,
-                        SCREEN_FREEZE_LAYER_CUSTOM, layerStack, false);
+                mCustomBlackFrame = new BlackFrame(outer, inner,
+                        SCREEN_FREEZE_LAYER_CUSTOM, mDisplayContent, false);
                 mCustomBlackFrame.setMatrix(mFrameInitialMatrix);
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
@@ -601,8 +600,8 @@
                             mOriginalWidth*2, mOriginalHeight*2);
                     inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
                 }
-                mExitingBlackFrame = new BlackFrame(session, outer, inner,
-                        SCREEN_FREEZE_LAYER_EXIT, layerStack, mForceDefaultOrientation);
+                mExitingBlackFrame = new BlackFrame(outer, inner,
+                        SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation);
                 mExitingBlackFrame.setMatrix(mFrameInitialMatrix);
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
@@ -624,8 +623,8 @@
                 Rect outer = new Rect(-finalWidth*1, -finalHeight*1,
                         finalWidth*2, finalHeight*2);
                 Rect inner = new Rect(0, 0, finalWidth, finalHeight);
-                mEnteringBlackFrame = new BlackFrame(session, outer, inner,
-                        SCREEN_FREEZE_LAYER_ENTER, layerStack, false);
+                mEnteringBlackFrame = new BlackFrame(outer, inner,
+                        SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false);
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
             } finally {
@@ -642,7 +641,7 @@
     /**
      * Returns true if animating.
      */
-    public boolean dismiss(SurfaceSession session, long maxAnimationDuration,
+    public boolean dismiss(long maxAnimationDuration,
             float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
         if (DEBUG_STATE) Slog.v(TAG, "Dismiss!");
         if (mSurfaceControl == null) {
@@ -650,7 +649,7 @@
             return false;
         }
         if (!mStarted) {
-            startAnimation(session, maxAnimationDuration, animationScale, finalWidth, finalHeight,
+            startAnimation(maxAnimationDuration, animationScale, finalWidth, finalHeight,
                     true, exitAnim, enterAnim);
         }
         if (!mStarted) {
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index eb8ee69..f51a6a9 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -41,15 +41,14 @@
     private boolean mDrawNeeded;
     private final int mThickness = 20;
 
-    public StrictModeFlash(Display display, SurfaceSession session) {
+    public StrictModeFlash(DisplayContent dc) {
         SurfaceControl ctrl = null;
         try {
-            ctrl = new SurfaceControl.Builder(session)
+            ctrl = dc.makeOverlay()
                     .setName("StrictModeFlash")
                     .setSize(1, 1)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
-            ctrl.setLayerStack(display.getLayerStack());
             ctrl.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101);  // one more than Watermark? arbitrary.
             ctrl.setPosition(0, 0);
             ctrl.show();
diff --git a/services/core/java/com/android/server/wm/SurfaceBuilderFactory.java b/services/core/java/com/android/server/wm/SurfaceBuilderFactory.java
new file mode 100644
index 0000000..5390e5a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SurfaceBuilderFactory.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.view.SurfaceSession;
+import android.view.SurfaceControl;
+
+interface SurfaceBuilderFactory {
+    SurfaceControl.Builder make(SurfaceSession s);
+};
+
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 13435d7..a7ba9c7 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -237,6 +237,8 @@
 
     @Override
     void onParentSet() {
+        super.onParentSet();
+
         // Update task bounds if needed.
         updateDisplayInfo(getDisplayContent());
 
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 12f6b5a..f2bd898 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -243,7 +243,9 @@
     /**
      * @param display The Display that the window being dragged is on.
      */
-    void register(Display display) {
+    void register(DisplayContent displayContent) {
+        final Display display = displayContent.getDisplay();
+
         if (DEBUG_TASK_POSITIONING) {
             Slog.d(TAG, "Registering task positioner");
         }
@@ -305,7 +307,7 @@
         }
         mService.pauseRotationLocked();
 
-        mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId(), TAG_LOCAL);
+        mDimLayer = new DimLayer(mService, this, displayContent, TAG_LOCAL);
         mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
         mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
         mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 053fb47..112643b 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -700,7 +700,7 @@
         }
 
         mDisplayContent = dc;
-        mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId(),
+        mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent,
                 "animation background stackId=" + mStackId);
         updateBoundsForWindowModeChange();
         super.onDisplayChanged(dc);
@@ -914,6 +914,8 @@
 
     @Override
     void onParentSet() {
+        super.onParentSet();
+
         if (getParent() != null || mDisplayContent == null) {
             return;
         }
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index d97aaac..9216b66 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -53,7 +53,7 @@
     private int mLastDH;
     private boolean mDrawNeeded;
 
-    Watermark(Display display, DisplayMetrics dm, SurfaceSession session, String[] tokens) {
+    Watermark(DisplayContent dc, DisplayMetrics dm, String[] tokens) {
         if (false) {
             Log.i(TAG_WM, "*********************** WATERMARK");
             for (int i=0; i<tokens.length; i++) {
@@ -61,7 +61,7 @@
             }
         }
 
-        mDisplay = display;
+        mDisplay = dc.getDisplay();
         mTokens = tokens;
 
         StringBuilder builder = new StringBuilder(32);
@@ -114,7 +114,7 @@
 
         SurfaceControl ctrl = null;
         try {
-            ctrl = new SurfaceControl.Builder(session)
+            ctrl = dc.makeOverlay()
                     .setName("WatermarkSurface")
                     .setSize(1, 1)
                     .setFormat(PixelFormat.TRANSLUCENT)
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 1912095..4a70a15 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -200,7 +200,7 @@
                     ++mAnimTransactionSequence;
                     dc.updateWindowsForAnimator(this);
                     dc.updateWallpaperForAnimator(this);
-                    dc.prepareWindowSurfaces();
+                    dc.prepareSurfaces();
                 }
 
                 for (int i = 0; i < numDisplays; i++) {
@@ -237,6 +237,13 @@
                 if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
             }
 
+            final int numDisplays = mDisplayContentsAnimators.size();
+            for (int i = 0; i < numDisplays; i++) {
+                final int displayId = mDisplayContentsAnimators.keyAt(i);
+                final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
+                dc.onPendingTransactionApplied();
+            }
+
             boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
             boolean doRequest = false;
             if (mBulkUpdateParams != 0) {
@@ -271,6 +278,7 @@
             mService.destroyPreservedSurfaceLocked();
             mService.mWindowPlacerLocked.destroyPendingSurfaces();
 
+
             if (DEBUG_WINDOW_TRACE) {
                 Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
                         + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8f4b897..a32a7f7 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -21,9 +21,13 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static com.android.server.wm.proto.WindowContainerProto.CONFIGURATION_CONTAINER;
 import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION;
+import static android.view.SurfaceControl.Transaction;
 
 import android.annotation.CallSuper;
 import android.content.res.Configuration;
+import android.view.MagnificationSpec;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
 import android.util.Pools;
 
 import android.util.proto.ProtoOutputStream;
@@ -64,7 +68,14 @@
             new Pools.SynchronizedPool<>(3);
 
     // The owner/creator for this container. No controller if null.
-    private WindowContainerController mController;
+     WindowContainerController mController;
+
+    protected SurfaceControl mSurfaceControl;
+
+    /**
+     * Applied as part of the animation pass in "prepareSurfaces".
+     */
+    protected Transaction mPendingTransaction = new Transaction();
 
     @Override
     final protected WindowContainer getParent() {
@@ -101,7 +112,22 @@
      * Supposed to be overridden and contain actions that should be executed after parent was set.
      */
     void onParentSet() {
-        // Do nothing by default.
+        if (mParent == null) {
+            return;
+        }
+        if (mSurfaceControl == null) {
+            // If we don't yet have a surface, but we now have a parent, we should
+            // build a surface.
+            mSurfaceControl = makeSurface().build();
+            mPendingTransaction.show(mSurfaceControl);
+        } else {
+            // If we have a surface but a new parent, we just need to perform a reparent.
+            mPendingTransaction.reparent(mSurfaceControl, mParent.mSurfaceControl.getHandle());
+        }
+
+        // Either way we need to ask the parent to assign us a Z-order.
+        mParent.assignChildLayers();
+        scheduleAnimation();
     }
 
     // Temp. holders for a chain of containers we are currently processing.
@@ -188,6 +214,11 @@
             mChildren.remove(child);
         }
 
+        if (mSurfaceControl != null) {
+            destroyAfterPendingTransaction(mSurfaceControl);
+            mSurfaceControl = null;
+        }
+
         if (mParent != null) {
             mParent.removeChild(this);
         }
@@ -195,6 +226,7 @@
         if (mController != null) {
             setController(null);
         }
+
     }
 
     /**
@@ -407,7 +439,7 @@
     }
 
     /**
-a     * Returns whether this child is on top of the window hierarchy.
+     * @return Whether this child is on top of the window hierarchy.
      */
     boolean isOnTop() {
         return getParent().getTopChild() == this && getParent().isOnTop();
@@ -673,6 +705,99 @@
         mController = controller;
     }
 
+    SurfaceControl.Builder makeSurface() {
+        final WindowContainer p = getParent();
+        return p.makeChildSurface(this);
+    }
+
+    SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+        final WindowContainer p = getParent();
+        // Give the parent a chance to set properties. In hierarchy v1 we rely
+        // on this to set full-screen dimensions on all our Surface-less Layers.
+        final SurfaceControl.Builder b = p.makeChildSurface(child);
+        if (child.isScreenOverlay()) {
+            // If it's a screen overlay it's been promoted in the hierarchy (wrt to the
+            // WindowContainer hierarchy vs the SurfaceControl hierarchy)
+            // and we shouldn't set ourselves as the parent.
+            return b;
+        } else {
+            return b.setParent(mSurfaceControl);
+        }
+    }
+
+    /**
+     * There are various layers which require promotion from the WindowContainer
+     * hierarchy to the Overlay layer described in {@link DisplayContent}. See {@link WindowState}
+     * for the particular usage.
+     *
+     * TODO: Perhaps this should be eliminated, either through modifying
+     * the window container hierarchy or through modifying the way we express these overlay
+     * Surfaces (for example, the Magnification Overlay could be implemented like the Strict-mode
+     * Flash and not actually use a WindowState).
+     */
+    boolean isScreenOverlay() {
+        return false;
+    }
+
+    /**
+     * @return Whether this WindowContainer should be magnified by the accessibility magnifier.
+     */
+    boolean shouldMagnify() {
+        for (int i = 0; i < mChildren.size(); i++) {
+            if (!mChildren.get(i).shouldMagnify()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    SurfaceSession getSession() {
+        if (getParent() != null) {
+            return getParent().getSession();
+        }
+        return null;
+    }
+
+    void assignLayer(Transaction t, int layer) {
+        if (mSurfaceControl != null) {
+            t.setLayer(mSurfaceControl, layer);
+        }
+    }
+
+    void assignChildLayers(Transaction t) {
+        int layer = 0;
+        boolean boosting = false;
+
+        // We use two passes as a way to promote children which
+        // need Z-boosting to the end of the list.
+        for (int i = 0; i < 2; i++ ) {
+            for (int j = 0; j < mChildren.size(); ++j) {
+                final WindowContainer wc = mChildren.get(j);
+                if (wc.needsZBoost() && !boosting) {
+                    continue;
+                }
+                wc.assignLayer(t, layer);
+                wc.assignChildLayers(t);
+
+                layer++;
+            }
+            boosting = true;
+        }
+    }
+
+    void assignChildLayers() {
+        assignChildLayers(mPendingTransaction);
+    }
+
+    boolean needsZBoost() {
+        for (int i = 0; i < mChildren.size(); i++) {
+            if (mChildren.get(i).needsZBoost()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Write to a protocol buffer output stream. Protocol buffer message definition is at
      * {@link com.android.server.wm.proto.WindowContainerProto}.
@@ -719,4 +844,55 @@
             mConsumerWrapperPool.release(this);
         }
     }
+
+    // TODO(b/68336570): Should this really be on WindowContainer since it
+    // can only be used on the top-level nodes that aren't animated?
+    // (otherwise we would be fighting other callers of setMatrix).
+    void applyMagnificationSpec(Transaction t, MagnificationSpec spec) {
+        if (shouldMagnify()) {
+            t.setMatrix(mSurfaceControl, spec.scale, 0, 0, spec.scale)
+                    .setPosition(mSurfaceControl, spec.offsetX, spec.offsetY);
+        } else {
+            for (int i = 0; i < mChildren.size(); i++) {
+                mChildren.get(i).applyMagnificationSpec(t, spec);
+            }
+        }
+    }
+
+    /**
+     * TODO: Once we totally eliminate global transaction we will pass transaction in here
+     * rather than merging to global.
+     */
+    void prepareSurfaces() {
+        SurfaceControl.mergeToGlobalTransaction(mPendingTransaction);
+        for (int i = 0; i < mChildren.size(); i++) {
+            mChildren.get(i).prepareSurfaces();
+        }
+    }
+
+    /**
+     * Trigger a call to prepareSurfaces from the animation thread, such that
+     * mPendingTransaction will be applied.
+     */
+    void scheduleAnimation() {
+        if (mParent != null) {
+            mParent.scheduleAnimation();
+        }
+    }
+
+    SurfaceControl getSurfaceControl() {
+        return mSurfaceControl;
+    }
+
+    /**
+     * Destroy a given surface after executing mPendingTransaction. This is
+     * largely a workaround for destroy not being part of transactions
+     * rather than an intentional design, so please take care when
+     * expanding use.
+     */
+    void destroyAfterPendingTransaction(SurfaceControl surface) {
+        if (mParent != null) {
+            mParent.destroyAfterPendingTransaction(surface);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
deleted file mode 100644
index 7caf2fe..0000000
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * 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.server.wm;
-
-import android.util.Slog;
-
-import java.util.ArrayDeque;
-import java.util.function.Consumer;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
-import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER;
-
-/**
- * Controller for assigning layers to windows on the display.
- *
- * This class encapsulates general algorithm for assigning layers and special rules that we need to
- * apply on top. The general algorithm goes through windows from bottom to the top and the higher
- * the window is, the higher layer is assigned. The final layer is equal to base layer +
- * adjustment from the order. This means that the window list is assumed to be ordered roughly by
- * the base layer (there are exceptions, e.g. due to keyguard and wallpaper and they need to be
- * handled with care, because they break the algorithm).
- *
- * On top of the general algorithm we add special rules, that govern such amazing things as:
- * <li>IME (which has higher base layer, but will be positioned above application windows)</li>
- * <li>docked/pinned windows (that need to be lifted above other application windows, including
- * animations)
- * <li>dock divider (which needs to live above applications, but below IME)</li>
- * <li>replaced windows, which need to live above their normal level, because they anticipate
- * an animation</li>.
- */
-class WindowLayersController {
-    private final WindowManagerService mService;
-
-    WindowLayersController(WindowManagerService service) {
-        mService = service;
-    }
-
-    private ArrayDeque<WindowState> mPinnedWindows = new ArrayDeque<>();
-    private ArrayDeque<WindowState> mDockedWindows = new ArrayDeque<>();
-    private ArrayDeque<WindowState> mAssistantWindows = new ArrayDeque<>();
-    private ArrayDeque<WindowState> mInputMethodWindows = new ArrayDeque<>();
-    private WindowState mDockDivider = null;
-    private ArrayDeque<WindowState> mReplacingWindows = new ArrayDeque<>();
-    private int mCurBaseLayer;
-    private int mCurLayer;
-    private boolean mAnyLayerChanged;
-    private int mHighestApplicationLayer;
-    private int mHighestDockedAffectedLayer;
-    private int mHighestLayerInImeTargetBaseLayer;
-    private WindowState mImeTarget;
-    private boolean mAboveImeTarget;
-    private ArrayDeque<WindowState> mAboveImeTargetAppWindows = new ArrayDeque();
-
-    private final Consumer<WindowState> mAssignWindowLayersConsumer = w -> {
-        boolean layerChanged = false;
-
-        int oldLayer = w.mLayer;
-        if (w.mBaseLayer == mCurBaseLayer) {
-            mCurLayer += WINDOW_LAYER_MULTIPLIER;
-        } else {
-            mCurBaseLayer = mCurLayer = w.mBaseLayer;
-        }
-        assignAnimLayer(w, mCurLayer);
-
-        // TODO: Preserved old behavior of code here but not sure comparing oldLayer to
-        // mAnimLayer and mLayer makes sense...though the worst case would be unintentional
-        // layer reassignment.
-        if (w.mLayer != oldLayer || w.mWinAnimator.mAnimLayer != oldLayer) {
-            layerChanged = true;
-            mAnyLayerChanged = true;
-        }
-
-        if (w.mAppToken != null) {
-            mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
-                    w.mWinAnimator.mAnimLayer);
-        }
-        if (mImeTarget != null && w.mBaseLayer == mImeTarget.mBaseLayer) {
-            mHighestLayerInImeTargetBaseLayer = Math.max(mHighestLayerInImeTargetBaseLayer,
-                    w.mWinAnimator.mAnimLayer);
-        }
-        if (w.getAppToken() != null && w.inSplitScreenSecondaryWindowingMode()) {
-            mHighestDockedAffectedLayer = Math.max(mHighestDockedAffectedLayer,
-                    w.mWinAnimator.mAnimLayer);
-        }
-
-        collectSpecialWindows(w);
-
-        if (layerChanged) {
-            w.scheduleAnimationIfDimming();
-        }
-    };
-
-    final void assignWindowLayers(DisplayContent dc) {
-        if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based",
-                new RuntimeException("here").fillInStackTrace());
-
-        reset();
-        dc.forAllWindows(mAssignWindowLayersConsumer, false /* traverseTopToBottom */);
-
-        adjustSpecialWindows();
-
-        //TODO (multidisplay): Magnification is supported only for the default display.
-        if (mService.mAccessibilityController != null && mAnyLayerChanged
-                && dc.getDisplayId() == DEFAULT_DISPLAY) {
-            mService.mAccessibilityController.onWindowLayersChangedLocked();
-        }
-
-        if (DEBUG_LAYERS) logDebugLayers(dc);
-    }
-
-    private void logDebugLayers(DisplayContent dc) {
-        dc.forAllWindows((w) -> {
-            final WindowStateAnimator winAnimator = w.mWinAnimator;
-            Slog.v(TAG_WM, "Assign layer " + w + ": " + "mBase=" + w.mBaseLayer
-                    + " mLayer=" + w.mLayer + (w.mAppToken == null
-                    ? "" : " mAppLayer=" + w.mAppToken.getAnimLayerAdjustment())
-                    + " =mAnimLayer=" + winAnimator.mAnimLayer);
-        }, false /* traverseTopToBottom */);
-    }
-
-    private void reset() {
-        mPinnedWindows.clear();
-        mInputMethodWindows.clear();
-        mDockedWindows.clear();
-        mAssistantWindows.clear();
-        mReplacingWindows.clear();
-        mDockDivider = null;
-
-        mCurBaseLayer = 0;
-        mCurLayer = 0;
-        mAnyLayerChanged = false;
-
-        mHighestApplicationLayer = 0;
-        mHighestDockedAffectedLayer = 0;
-        mHighestLayerInImeTargetBaseLayer = (mImeTarget != null) ? mImeTarget.mBaseLayer : 0;
-        mImeTarget = mService.mInputMethodTarget;
-        mAboveImeTarget = false;
-        mAboveImeTargetAppWindows.clear();
-    }
-
-    private void collectSpecialWindows(WindowState w) {
-        if (w.mAttrs.type == TYPE_DOCK_DIVIDER) {
-            mDockDivider = w;
-            return;
-        }
-        if (w.mWillReplaceWindow) {
-            mReplacingWindows.add(w);
-        }
-        if (w.mIsImWindow) {
-            mInputMethodWindows.add(w);
-            return;
-        }
-        if (mImeTarget != null) {
-            if (w.getParentWindow() == mImeTarget && w.mSubLayer > 0) {
-                // Child windows of the ime target with a positive sub-layer should be placed above
-                // the IME.
-                mAboveImeTargetAppWindows.add(w);
-            } else if (mAboveImeTarget && w.mAppToken != null) {
-                // windows of apps above the IME target should be placed above the IME.
-                mAboveImeTargetAppWindows.add(w);
-            }
-            if (w == mImeTarget) {
-                mAboveImeTarget = true;
-            }
-        }
-
-        final int windowingMode = w.getWindowingMode();
-        if (windowingMode == WINDOWING_MODE_PINNED) {
-            mPinnedWindows.add(w);
-        } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            mDockedWindows.add(w);
-        }
-        if (w.isActivityTypeAssistant()) {
-            mAssistantWindows.add(w);
-        }
-    }
-
-    private void adjustSpecialWindows() {
-        // The following adjustments are beyond the highest docked-affected layer
-        int layer = mHighestDockedAffectedLayer +  TYPE_LAYER_OFFSET;
-
-        // Adjust the docked stack windows and dock divider above only the windows that are affected
-        // by the docked stack. When this happens, also boost the assistant window layers, otherwise
-        // the docked stack windows & divider would be promoted above the assistant.
-        if (!mDockedWindows.isEmpty() && mHighestDockedAffectedLayer > 0) {
-            while (!mDockedWindows.isEmpty()) {
-                final WindowState window = mDockedWindows.remove();
-                layer = assignAndIncreaseLayerIfNeeded(window, layer);
-            }
-
-            layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer);
-
-            while (!mAssistantWindows.isEmpty()) {
-                final WindowState window = mAssistantWindows.remove();
-                if (window.mLayer > mHighestDockedAffectedLayer) {
-                    layer = assignAndIncreaseLayerIfNeeded(window, layer);
-                }
-            }
-        }
-
-        // The following adjustments are beyond the highest app layer or boosted layer
-        layer = Math.max(layer, mHighestApplicationLayer + WINDOW_LAYER_MULTIPLIER);
-
-        // 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.
-        while (!mReplacingWindows.isEmpty()) {
-            layer = assignAndIncreaseLayerIfNeeded(mReplacingWindows.remove(), layer);
-        }
-
-        while (!mPinnedWindows.isEmpty()) {
-            layer = assignAndIncreaseLayerIfNeeded(mPinnedWindows.remove(), layer);
-        }
-
-        // Make sure IME is the highest window in the base layer of it's target.
-        if (mImeTarget != null) {
-            if (mImeTarget.mAppToken == null) {
-                // For non-app ime targets adjust the layer we start from to match what we found
-                // when assigning layers. Otherwise, just use the highest app layer we have some far.
-                layer = mHighestLayerInImeTargetBaseLayer + WINDOW_LAYER_MULTIPLIER;
-            }
-
-            while (!mInputMethodWindows.isEmpty()) {
-                layer = assignAndIncreaseLayerIfNeeded(mInputMethodWindows.remove(), layer);
-            }
-
-            // Adjust app windows the should be displayed above the IME since they are above the IME
-            // target.
-            while (!mAboveImeTargetAppWindows.isEmpty()) {
-                layer = assignAndIncreaseLayerIfNeeded(mAboveImeTargetAppWindows.remove(), layer);
-            }
-        }
-
-    }
-
-    private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) {
-        if (win != null) {
-            assignAnimLayer(win, layer);
-            // Make sure we leave space in-between normal windows for dims and such.
-            layer += WINDOW_LAYER_MULTIPLIER;
-        }
-        return layer;
-    }
-
-    private void assignAnimLayer(WindowState w, int layer) {
-        w.mLayer = layer;
-        w.mWinAnimator.mAnimLayer = w.getAnimLayerAdjustment()
-                + w.getSpecialWindowAnimLayerAdjustment();
-        if (w.mAppToken != null) {
-            w.mAppToken.mAppAnimator.updateThumbnailLayer();
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ce34306..7257cc7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -529,7 +529,6 @@
 
     AccessibilityController mAccessibilityController;
 
-    final SurfaceSession mFxSession;
     Watermark mWatermark;
     StrictModeFlash mStrictModeFlash;
     CircularDisplayMask mCircularDisplayMask;
@@ -804,6 +803,13 @@
     static WindowManagerThreadPriorityBooster sThreadPriorityBooster =
             new WindowManagerThreadPriorityBooster();
 
+    class DefaultSurfaceBuilderFactory implements SurfaceBuilderFactory {
+        public SurfaceControl.Builder make(SurfaceSession s) {
+            return new SurfaceControl.Builder(s);
+        }
+    };
+    SurfaceBuilderFactory mSurfaceBuilderFactory = new DefaultSurfaceBuilderFactory();
+
     static void boostPriorityForLockedSection() {
         sThreadPriorityBooster.boost();
     }
@@ -992,7 +998,6 @@
             mPointerEventDispatcher = null;
         }
 
-        mFxSession = new SurfaceSession();
         mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
         mDisplays = mDisplayManager.getDisplays();
         for (Display display : mDisplays) {
@@ -3592,8 +3597,7 @@
                                 com.android.internal.R.dimen.circular_display_mask_thickness);
 
                         mCircularDisplayMask = new CircularDisplayMask(
-                                getDefaultDisplayContentLocked().getDisplay(),
-                                mFxSession,
+                                getDefaultDisplayContentLocked(),
                                 mPolicy.getWindowLayerFromTypeLw(
                                         WindowManager.LayoutParams.TYPE_POINTER)
                                         * TYPE_LAYER_MULTIPLIER + 10, screenOffset, maskThickness);
@@ -3621,8 +3625,7 @@
                 if (mEmulatorDisplayOverlay == null) {
                     mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(
                             mContext,
-                            getDefaultDisplayContentLocked().getDisplay(),
-                            mFxSession,
+                            getDefaultDisplayContentLocked(),
                             mPolicy.getWindowLayerFromTypeLw(
                                     WindowManager.LayoutParams.TYPE_POINTER)
                                     * TYPE_LAYER_MULTIPLIER + 10);
@@ -3670,7 +3673,7 @@
                 // TODO(multi-display): support multiple displays
                 if (mStrictModeFlash == null) {
                     mStrictModeFlash = new StrictModeFlash(
-                            getDefaultDisplayContentLocked().getDisplay(), mFxSession);
+                            getDefaultDisplayContentLocked());
                 }
                 mStrictModeFlash.setVisibility(on);
             } finally {
@@ -4522,7 +4525,7 @@
 
         Display display = displayContent.getDisplay();
         mTaskPositioner = new TaskPositioner(this);
-        mTaskPositioner.register(display);
+        mTaskPositioner.register(displayContent);
         mInputMonitor.updateInputWindowsLw(true /*force*/);
 
         // We need to grab the touch focus so that the touch events during the
@@ -5892,7 +5895,7 @@
 
             displayContent.updateDisplayInfo();
             screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
-                    mFxSession, inTransaction, mPolicy.isDefaultOrientationForced(), isSecure,
+                    inTransaction, mPolicy.isDefaultOrientationForced(), isSecure,
                     this);
             mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
                     screenRotationAnimation);
@@ -5956,7 +5959,7 @@
             if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, isDimming)) {
                 mExitAnimId = mEnterAnimId = 0;
             }
-            if (screenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
+            if (screenRotationAnimation.dismiss(MAX_ANIMATION_DURATION,
                     getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
                         displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
                 scheduleAnimationLocked();
@@ -6040,8 +6043,8 @@
                 if (toks != null && toks.length > 0) {
                     // TODO(multi-display): Show watermarks on secondary displays.
                     final DisplayContent displayContent = getDefaultDisplayContentLocked();
-                    mWatermark = new Watermark(displayContent.getDisplay(),
-                            displayContent.mRealDisplayMetrics, mFxSession, toks);
+                    mWatermark = new Watermark(displayContent, displayContent.mRealDisplayMetrics,
+                            toks);
                 }
             }
         } catch (FileNotFoundException e) {
@@ -7557,4 +7560,13 @@
             w.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
         }, false /* traverseTopToBottom */);
     }
+
+    public void applyMagnificationSpec(MagnificationSpec spec) {
+        getDefaultDisplayContentLocked().applyMagnificationSpec(spec);
+    }
+
+    SurfaceControl.Builder makeSurfaceBuilder(SurfaceSession s) {
+        return mSurfaceBuilderFactory.make(s);
+    }
 }
+
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6b1932d..7f07281 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -23,6 +23,7 @@
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
+import static android.view.SurfaceControl.Transaction;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
@@ -41,6 +42,7 @@
 import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
@@ -54,6 +56,9 @@
 import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
 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_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
@@ -147,6 +152,8 @@
 import android.view.InputChannel;
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
 import android.view.View;
 import android.view.ViewTreeObserver;
 import android.view.WindowInfo;
@@ -4388,4 +4395,64 @@
             return false;
         }
     }
+
+    @Override
+    boolean shouldMagnify() {
+        if (mAttrs.type == TYPE_INPUT_METHOD ||
+                mAttrs.type == TYPE_INPUT_METHOD_DIALOG) {
+            return false;
+        } else if (isScreenOverlay()) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    boolean isScreenOverlay() {
+        // It's tempting to wonder: Have we forgotten the rounded corners overlay?
+        // worry not: it's a fake TYPE_NAVIGATION_BAR.
+        if (mAttrs.type == TYPE_MAGNIFICATION_OVERLAY ||
+                mAttrs.type == TYPE_NAVIGATION_BAR ||
+                mAttrs.type == TYPE_STATUS_BAR) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    SurfaceSession getSession() {
+        if (mSession.mSurfaceSession != null) {
+            return mSession.mSurfaceSession;
+        } else {
+            return getParent().getSession();
+        }
+    }
+
+    @Override
+    boolean needsZBoost() {
+        return getAnimLayerAdjustment() > 0 || mWillReplaceWindow;
+    }
+
+    @Override
+    SurfaceControl.Builder makeSurface() {
+        return mToken.makeChildSurface(this);
+    }
+
+    @Override
+    void prepareSurfaces() {
+        mWinAnimator.prepareSurfaceLocked(true);
+        super.prepareSurfaces();
+    }
+
+    @Override
+    void assignLayer(Transaction t, int layer) {
+        // See comment in assignRelativeLayerForImeTargetChild
+        if (!isChildWindow()
+                || (mService.mInputMethodTarget != getParentWindow())
+                || !inSplitScreenWindowingMode()) {
+            super.assignLayer(t, layer);
+            return;
+        }
+        getDisplayContent().assignRelativeLayerForImeTargetChild(t, this);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 86397ae..ec08988 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -60,7 +60,6 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
-import android.view.MagnificationSpec;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
@@ -559,7 +558,10 @@
         }
         if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "SET FREEZE LAYER", false);
         if (mSurfaceController != null) {
-            mSurfaceController.setLayer(mAnimLayer + 1);
+            // Our SurfaceControl is always at layer 0 within the parent Surface managed by
+            // window-state. We want this old Surface to stay on top of the new one
+            // until we do the swap, so we place it at layer 1.
+            mSurfaceController.mSurfaceControl.setLayer(1);
         }
         mDestroyPreservedSurfaceUponRedraw = true;
         mSurfaceDestroyDeferred = true;
@@ -730,7 +732,6 @@
         try {
             mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top, false);
             mSurfaceController.setLayerStackInTransaction(getLayerStack());
-            mSurfaceController.setLayer(mAnimLayer);
         } finally {
             mService.closeSurfaceTransaction("createSurfaceLocked");
         }
@@ -867,22 +868,6 @@
         mPendingDestroySurface = null;
     }
 
-    void applyMagnificationSpec(MagnificationSpec spec, Matrix transform) {
-        final int surfaceInsetLeft = mWin.mAttrs.surfaceInsets.left;
-        final int surfaceInsetTop = mWin.mAttrs.surfaceInsets.top;
-
-        if (spec != null && !spec.isNop()) {
-            float scale = spec.scale;
-            transform.postScale(scale, scale);
-            transform.postTranslate(spec.offsetX, spec.offsetY);
-
-            // As we are scaling the whole surface, to keep the content
-            // in the same position we will also have to scale the surfaceInsets.
-            transform.postTranslate(-(surfaceInsetLeft*scale - surfaceInsetLeft),
-                    -(surfaceInsetTop*scale - surfaceInsetTop));
-        }
-    }
-
     void computeShownFrameLocked() {
         final boolean selfTransformation = mHasLocalTransformation;
         Transformation attachedTransformation =
@@ -969,11 +954,6 @@
                 tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
             }
 
-            MagnificationSpec spec = getMagnificationSpec();
-            if (spec != null) {
-                applyMagnificationSpec(spec, tmpMatrix);
-            }
-
             // "convert" it into SurfaceFlinger's format
             // (a 2x2 matrix + an offset)
             // Here we must not transform the position of the surface
@@ -1057,49 +1037,16 @@
                 TAG, "computeShownFrameLocked: " + this +
                 " not attached, mAlpha=" + mAlpha);
 
-        MagnificationSpec spec = getMagnificationSpec();
-        if (spec != null) {
-            final Rect frame = mWin.mFrame;
-            final float tmpFloats[] = mService.mTmpFloats;
-            final Matrix tmpMatrix = mWin.mTmpMatrix;
-
-            tmpMatrix.setScale(mWin.mGlobalScale, mWin.mGlobalScale);
-            tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
-
-            applyMagnificationSpec(spec, tmpMatrix);
-
-            tmpMatrix.getValues(tmpFloats);
-
-            mHaveMatrix = true;
-            mDsDx = tmpFloats[Matrix.MSCALE_X];
-            mDtDx = tmpFloats[Matrix.MSKEW_Y];
-            mDtDy = tmpFloats[Matrix.MSKEW_X];
-            mDsDy = tmpFloats[Matrix.MSCALE_Y];
-            float x = tmpFloats[Matrix.MTRANS_X];
-            float y = tmpFloats[Matrix.MTRANS_Y];
-            mWin.mShownPosition.set(Math.round(x), Math.round(y));
-
-            mShownAlpha = mAlpha;
-        } else {
-            mWin.mShownPosition.set(mWin.mFrame.left, mWin.mFrame.top);
-            if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
-                mWin.mShownPosition.offset(mWin.mXOffset, mWin.mYOffset);
-            }
-            mShownAlpha = mAlpha;
-            mHaveMatrix = false;
-            mDsDx = mWin.mGlobalScale;
-            mDtDx = 0;
-            mDtDy = 0;
-            mDsDy = mWin.mGlobalScale;
+        mWin.mShownPosition.set(mWin.mFrame.left, mWin.mFrame.top);
+        if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
+            mWin.mShownPosition.offset(mWin.mXOffset, mWin.mYOffset);
         }
-    }
-
-    private MagnificationSpec getMagnificationSpec() {
-        //TODO (multidisplay): Magnification is supported only for the default display.
-        if (mService.mAccessibilityController != null && mWin.getDisplayId() == DEFAULT_DISPLAY) {
-            return mService.mAccessibilityController.getMagnificationSpecForWindowLocked(mWin);
-        }
-        return null;
+        mShownAlpha = mAlpha;
+        mHaveMatrix = false;
+        mDsDx = mWin.mGlobalScale;
+        mDtDx = 0;
+        mDtDy = 0;
+        mDsDy = mWin.mGlobalScale;
     }
 
     /**
@@ -1140,26 +1087,6 @@
             w.expandForSurfaceInsets(finalClipRect);
         }
 
-        // We may be applying a magnification spec to all windows,
-        // simulating a transformation in screen space, in which case
-        // we need to transform all other screen space values...including
-        // the final crop. This is kind of messed up and we should look
-        // in to actually transforming screen-space via a parent-layer.
-        // b/38322835
-        MagnificationSpec spec = getMagnificationSpec();
-        if (spec != null && !spec.isNop()) {
-            Matrix transform = mWin.mTmpMatrix;
-            RectF finalCrop = mService.mTmpRectF;
-            transform.reset();
-            transform.postScale(spec.scale, spec.scale);
-            transform.postTranslate(-spec.offsetX, -spec.offsetY);
-            transform.mapRect(finalCrop);
-            finalClipRect.top = (int) finalCrop.top;
-            finalClipRect.left = (int) finalCrop.left;
-            finalClipRect.right = (int) finalCrop.right;
-            finalClipRect.bottom = (int) finalCrop.bottom;
-        }
-
         return true;
     }
 
@@ -1615,7 +1542,6 @@
                         mDtDy * w.mHScale * mExtraHScale,
                         mDsDy * w.mVScale * mExtraVScale,
                         recoveringMemory);
-            mSurfaceController.setLayer(mAnimLayer);
 
             if (prepared && mDrawState == HAS_DRAWN) {
                 if (mLastHidden) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index a214523..6746754 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -53,7 +53,7 @@
 
     final WindowStateAnimator mAnimator;
 
-    private SurfaceControlWithBackground mSurfaceControl;
+    SurfaceControlWithBackground mSurfaceControl;
 
     // Should only be set from within setShown().
     private boolean mSurfaceShown = false;
@@ -101,7 +101,8 @@
         mWindowSession = win.mSession;
 
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
-        final SurfaceControl.Builder b = new SurfaceControl.Builder(s)
+        final SurfaceControl.Builder b = win.makeSurface()
+                .setParent(win.getSurfaceControl())
                 .setName(name)
                 .setSize(w, h)
                 .setFormat(format)
@@ -245,25 +246,6 @@
         }
     }
 
-    void setLayer(int layer) {
-        if (mSurfaceControl != null) {
-            mService.openSurfaceTransaction();
-            try {
-                if (mAnimator.mWin.usesRelativeZOrdering()) {
-                    mSurfaceControl.setRelativeLayer(
-                            mAnimator.mWin.getParentWindow()
-                            .mWinAnimator.mSurfaceController.mSurfaceControl,
-                            -1);
-                } else {
-                    mSurfaceLayer = layer;
-                    mSurfaceControl.setLayer(layer);
-                }
-            } finally {
-                mService.closeSurfaceTransaction("setLayer");
-            }
-        }
-    }
-
     void setLayerStackInTransaction(int layerStack) {
         if (mSurfaceControl != null) {
             mSurfaceControl.setLayerStack(layerStack);
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index cd5e475..5081868 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -704,7 +704,7 @@
 
             // Create a new surface for the thumbnail
             WindowState window = appToken.findMainWindow();
-            final SurfaceControl surfaceControl = new SurfaceControl.Builder(mService.mFxSession)
+            final SurfaceControl surfaceControl = appToken.makeSurface()
                     .setName("thumbnail anim")
                     .setSize(dirty.width(), dirty.height())
                     .setFormat(PixelFormat.TRANSLUCENT)
@@ -712,7 +712,6 @@
                             window != null ? window.mOwnerUid : Binder.getCallingUid())
                     .build();
 
-            surfaceControl.setLayerStack(display.getLayerStack());
             if (SHOW_TRANSACTIONS) {
                 Slog.i(TAG, "  THUMBNAIL " + surfaceControl + ": CREATE");
             }
@@ -750,10 +749,13 @@
             anim.restrictDuration(MAX_ANIMATION_DURATION);
             anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
 
-            openingAppAnimator.updateThumbnailLayer();
             openingAppAnimator.thumbnail = surfaceControl;
             openingAppAnimator.thumbnailAnimation = anim;
             mService.mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect);
+
+            // We parent the thumbnail to the app token, and just place it
+            // on top of anything else in the app token.
+            surfaceControl.setLayer(Integer.MAX_VALUE);
         } catch (Surface.OutOfResourcesException e) {
             Slog.e(TAG, "Can't allocate thumbnail/Canvas surface w="
                     + dirty.width() + " h=" + dirty.height(), e);
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index f1e76ab..9a5ebed 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -57,6 +57,7 @@
     <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
 
     <!-- Uses API introduced in O (26) -->
     <uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index 6060881..b55c79b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -129,6 +129,9 @@
             controller.removeStartingWindow();
             waitUntilHandlersIdle();
             assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent));
+
+            controller.getAppWindowToken(mDisplayContent).getParent().getParent().removeImmediately();
+            mDisplayContent.onPendingTransactionApplied();
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index bb88264..d9ab5c8 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -51,71 +51,77 @@
 @RunWith(AndroidJUnit4.class)
 public class AppWindowTokenTests extends WindowTestsBase {
 
+    TaskStack mStack;
+    Task mTask;
+    WindowTestUtils.TestAppWindowToken mToken;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mStack = createTaskStackOnDisplay(mDisplayContent);
+        mTask = createTaskInStack(mStack, 0 /* userId */);
+        mToken = new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+
+        mTask.addChild(mToken, 0);
+    }
+
     @Test
     @Presubmit
     public void testAddWindow_Order() throws Exception {
-        final WindowTestUtils.TestAppWindowToken token =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+        assertEquals(0, mToken.getWindowsCount());
 
-        assertEquals(0, token.getWindowsCount());
-
-        final WindowState win1 = createWindow(null, TYPE_APPLICATION, token, "win1");
-        final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, token,
+        final WindowState win1 = createWindow(null, TYPE_APPLICATION, mToken, "win1");
+        final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, mToken,
                 "startingWin");
-        final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, token, "baseWin");
-        final WindowState win4 = createWindow(null, TYPE_APPLICATION, token, "win4");
+        final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, mToken, "baseWin");
+        final WindowState win4 = createWindow(null, TYPE_APPLICATION, mToken, "win4");
 
         // Should not contain the windows that were added above.
-        assertEquals(4, token.getWindowsCount());
-        assertTrue(token.hasWindow(win1));
-        assertTrue(token.hasWindow(startingWin));
-        assertTrue(token.hasWindow(baseWin));
-        assertTrue(token.hasWindow(win4));
+        assertEquals(4, mToken.getWindowsCount());
+        assertTrue(mToken.hasWindow(win1));
+        assertTrue(mToken.hasWindow(startingWin));
+        assertTrue(mToken.hasWindow(baseWin));
+        assertTrue(mToken.hasWindow(win4));
 
         // The starting window should be on-top of all other windows.
-        assertEquals(startingWin, token.getLastChild());
+        assertEquals(startingWin, mToken.getLastChild());
 
         // The base application window should be below all other windows.
-        assertEquals(baseWin, token.getFirstChild());
-        token.removeImmediately();
+        assertEquals(baseWin, mToken.getFirstChild());
+        mToken.removeImmediately();
     }
 
     @Test
     @Presubmit
     public void testFindMainWindow() throws Exception {
-        final WindowTestUtils.TestAppWindowToken token =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+        assertNull(mToken.findMainWindow());
 
-        assertNull(token.findMainWindow());
-
-        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, token, "window1");
-        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11");
-        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12");
-        assertEquals(window1, token.findMainWindow());
+        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
+        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window11");
+        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window12");
+        assertEquals(window1, mToken.findMainWindow());
         window1.mAnimatingExit = true;
-        assertEquals(window1, token.findMainWindow());
-        final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, token, "window2");
-        assertEquals(window2, token.findMainWindow());
-        token.removeImmediately();
+        assertEquals(window1, mToken.findMainWindow());
+        final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken, "window2");
+        assertEquals(window2, mToken.findMainWindow());
+        mToken.removeImmediately();
     }
 
     @Test
     @Presubmit
     public void testGetTopFullscreenWindow() throws Exception {
-        final WindowTestUtils.TestAppWindowToken token =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+        assertNull(mToken.getTopFullscreenWindow());
 
-        assertNull(token.getTopFullscreenWindow());
-
-        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, token, "window1");
-        final WindowState window11 = createWindow(null, TYPE_APPLICATION, token, "window11");
-        final WindowState window12 = createWindow(null, TYPE_APPLICATION, token, "window12");
-        assertEquals(window12, token.getTopFullscreenWindow());
+        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
+        final WindowState window11 = createWindow(null, TYPE_APPLICATION, mToken, "window11");
+        final WindowState window12 = createWindow(null, TYPE_APPLICATION, mToken, "window12");
+        assertEquals(window12, mToken.getTopFullscreenWindow());
         window12.mAttrs.width = 500;
-        assertEquals(window11, token.getTopFullscreenWindow());
+        assertEquals(window11, mToken.getTopFullscreenWindow());
         window11.mAttrs.width = 500;
-        assertEquals(window1, token.getTopFullscreenWindow());
-        token.removeImmediately();
+        assertEquals(window1, mToken.getTopFullscreenWindow());
+        mToken.removeImmediately();
     }
 
     @Test
@@ -124,27 +130,21 @@
         sWm.mDisplayReady = true;
         sWm.mDisplayEnabled = true;
 
-        // Create an app window with token on a display.
-        final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
-        final WindowTestUtils.TestAppWindowToken appWindowToken =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
-        task.addChild(appWindowToken, 0);
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.setTitle("AppWindow");
-        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken);
-        appWindowToken.addWindow(appWindow);
+        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
+        mToken.addWindow(appWindow);
 
         // Set initial orientation and update.
-        appWindowToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
         sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
                 mDisplayContent.getDisplayId());
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation());
         appWindow.resizeReported = false;
 
         // Update the orientation to perform 180 degree rotation and check that resize was reported.
-        appWindowToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+        mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
         sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
                 mDisplayContent.getDisplayId());
         sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
@@ -159,18 +159,11 @@
         sWm.mDisplayReady = true;
         sWm.mDisplayEnabled = true;
 
-        // Create an app window with token on a display.
-        final DisplayContent defaultDisplayContent = sWm.getDefaultDisplayContentLocked();
-        final TaskStack stack = createTaskStackOnDisplay(defaultDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
-        final WindowTestUtils.TestAppWindowToken appWindowToken =
-                new WindowTestUtils.TestAppWindowToken(defaultDisplayContent);
-        task.addChild(appWindowToken, 0);
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.setTitle("AppWindow");
-        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken);
-        appWindowToken.addWindow(appWindow);
+        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
+        mToken.addWindow(appWindow);
 
         // Set initial orientation and update.
         performRotation(Surface.ROTATION_90);
@@ -193,53 +186,49 @@
     @Test
     @Presubmit
     public void testGetOrientation() throws Exception {
-        final WindowTestUtils.TestAppWindowToken token =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
-        token.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
-        token.setFillsParent(false);
+        mToken.setFillsParent(false);
         // Can specify orientation if app doesn't fill parent.
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
 
-        token.setFillsParent(true);
-        token.hidden = true;
-        token.sendingToBottom = true;
+        mToken.setFillsParent(true);
+        mToken.hidden = true;
+        mToken.sendingToBottom = true;
         // Can not specify orientation if app isn't visible even though it fills parent.
-        assertEquals(SCREEN_ORIENTATION_UNSET, token.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
         // Can specify orientation if the current orientation candidate is orientation behind.
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation(SCREEN_ORIENTATION_BEHIND));
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation(SCREEN_ORIENTATION_BEHIND));
 
-        token.sendingToBottom = false;
-        token.setIsOnTop(true);
-        // Allow for token to provide orientation hidden if on top and not being sent to bottom.
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation());
+        mToken.sendingToBottom = false;
+        mToken.setIsOnTop(true);
+        // Allow for mToken to provide orientation hidden if on top and not being sent to bottom.
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
     }
 
     @Test
     @Presubmit
     public void testKeyguardFlagsDuringRelaunch() throws Exception {
-        final WindowTestUtils.TestAppWindowToken token =
-                new WindowTestUtils.TestAppWindowToken(mDisplayContent);
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.flags |= FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD;
         attrs.setTitle("AppWindow");
-        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, token);
+        final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
 
         // Add window with show when locked flag
-        token.addWindow(appWindow);
-        assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+        mToken.addWindow(appWindow);
+        assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
 
         // Start relaunching
-        token.startRelaunching();
-        assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+        mToken.startRelaunching();
+        assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
 
         // Remove window and make sure that we still report back flag
-        token.removeChild(appWindow);
-        assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+        mToken.removeChild(appWindow);
+        assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
 
         // Finish relaunching and ensure flag is now not reported
-        token.finishRelaunching();
-        assertFalse(token.containsShowWhenLockedWindow() || token.containsDismissKeyguardWindow());
+        mToken.finishRelaunching();
+        assertFalse(mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
index 887def7..873a01b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
@@ -66,7 +66,7 @@
         mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
 
         mPositioner = new TaskPositioner(sWm);
-        mPositioner.register(display);
+        mPositioner.register(mDisplayContent);
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 53d0bfb..a45695f 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -568,11 +568,6 @@
     }
 
     @Override
-    public boolean canMagnifyWindow(int windowType) {
-        return false;
-    }
-
-    @Override
     public boolean isTopLevelWindow(int windowType) {
         return false;
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java
deleted file mode 100644
index 3c3514f..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
-
-/**
- * Tests for the {@link WindowLayersController} class.
- *
- * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.WindowLayersControllerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class WindowLayersControllerTests extends WindowTestsBase {
-
-    @Test
-    public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception {
-        sWm.mInputMethodTarget = null;
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        // The Ime has an higher base layer than app windows and lower base layer than system
-        // windows, so it should be above app windows and below system windows if there isn't an IME
-        // target.
-        assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
-        assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
-        // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
-    }
-
-    @Test
-    public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
-        final WindowState imeAppTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
-        sWm.mInputMethodTarget = imeAppTarget;
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        // Ime should be above all app windows and below system windows if it is targeting an app
-        // window.
-        assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
-        assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
-        assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
-        // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
-    }
-
-    @Test
-    public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
-        final WindowState imeAppTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
-        final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
-                TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
-                "imeAppTargetChildAboveWindow");
-        final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget,
-                TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
-                "imeAppTargetChildBelowWindow");
-
-        sWm.mInputMethodTarget = imeAppTarget;
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        // Ime should be above all app windows except for child windows that are z-ordered above it
-        // and below system windows if it is targeting an app window.
-        assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
-        assertWindowLayerGreaterThan(imeAppTargetChildAboveWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mImeWindow, imeAppTargetChildBelowWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
-        assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
-        // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
-    }
-
-    @Test
-    public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
-        final WindowState appBelowImeTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
-        final WindowState imeAppTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
-        final WindowState appAboveImeTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
-
-        sWm.mInputMethodTarget = imeAppTarget;
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        // Ime should be above all app windows except for non-fullscreen app window above it and
-        // below system windows if it is targeting an app window.
-        assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
-        assertWindowLayerGreaterThan(mImeWindow, appBelowImeTarget);
-        assertWindowLayerGreaterThan(appAboveImeTarget, mImeWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
-        assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
-        // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
-    }
-
-    @Test
-    public void testAssignWindowLayers_ForImeNonAppImeTarget() throws Exception {
-        final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY,
-                mDisplayContent, "imeSystemOverlayTarget",
-                true /* ownerCanAddInternalSystemWindow */);
-
-        sWm.mInputMethodTarget = imeSystemOverlayTarget;
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        // The IME target base layer is higher than all window except for the nav bar window, so the
-        // IME should be above all windows except for the nav bar.
-        assertWindowLayerGreaterThan(mImeWindow, imeSystemOverlayTarget);
-        assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
-        assertWindowLayerGreaterThan(mImeWindow, mStatusBarWindow);
-        assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-
-        // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
-    }
-
-    @Test
-    public void testStackLayers() throws Exception {
-        WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
-                ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, "pinnedStackWindow");
-        WindowState dockedStackWindow = createWindowOnStack(null,
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
-                mDisplayContent, "dockedStackWindow");
-        WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
-                mDisplayContent, "assistantStackWindow");
-
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        assertWindowLayerGreaterThan(dockedStackWindow, mAppWindow);
-        assertWindowLayerGreaterThan(assistantStackWindow, dockedStackWindow);
-        assertWindowLayerGreaterThan(pinnedStackWindow, assistantStackWindow);
-    }
-
-    private void assertWindowLayerGreaterThan(WindowState first, WindowState second)
-            throws Exception {
-        assertGreaterThan(first.mWinAnimator.mAnimLayer, second.mWinAnimator.mAnimLayer);
-    }
-
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 0980f7e..4c5e291 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -73,7 +73,6 @@
     private static boolean sOneTimeSetupDone = false;
     DisplayContent mDisplayContent;
     DisplayInfo mDisplayInfo = new DisplayInfo();
-    WindowLayersController mLayersController;
     WindowState mWallpaperWindow;
     WindowState mImeWindow;
     WindowState mImeDialogWindow;
@@ -98,8 +97,9 @@
 
         final Context context = InstrumentationRegistry.getTargetContext();
         AttributeCache.init(context);
+
         sWm = TestWindowManagerPolicy.getWindowManagerService(context);
-        mLayersController = new WindowLayersController(sWm);
+        beforeCreateDisplay();
 
         context.getDisplay().getDisplayInfo(mDisplayInfo);
         mDisplayContent = createNewDisplay();
@@ -126,6 +126,10 @@
         waitUntilHandlersIdle();
     }
 
+    void beforeCreateDisplay() {
+        // Called before display is created.
+    }
+
     @After
     public void tearDown() throws Exception {
         final LinkedList<WindowState> nonCommonWindows = new LinkedList();
@@ -149,6 +153,14 @@
         waitUntilHandlersIdle();
     }
 
+    /**
+     * @return A SurfaceBuilderFactory to inject in to the WindowManagerService during
+     *         set-up (or null).
+     */
+    SurfaceBuilderFactory getSurfaceBuilderFactory() {
+        return null;
+    }
+
     private WindowState createCommonWindow(WindowState parent, int type, String name) {
         final WindowState win = createWindow(parent, type, name);
         mCommonWindows.add(win);
@@ -162,6 +174,11 @@
         Assert.assertTrue("Excepted " + first + " to be greater than " + second, first > second);
     }
 
+    /** Asserts that the first entry is greater than the second entry. */
+    void assertLessThan(int first, int second) throws Exception {
+        Assert.assertTrue("Excepted " + first + " to be less than " + second, first < second);
+    }
+
     /**
      * Waits until the main handler for WM has processed all messages.
      */
@@ -264,7 +281,7 @@
         final int displayId = sNextDisplayId++;
         final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                 mDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
-        return new DisplayContent(display, sWm, mLayersController, new WallpaperController(sWm));
+        return new DisplayContent(display, sWm, new WallpaperController(sWm));
     }
 
     /** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
index 692e08b..7219104 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
@@ -94,43 +94,6 @@
         assertEquals(null, dc.getWindowToken(token.token));
     }
 
-    @Test
-    public void testAdjustAnimLayer() throws Exception {
-        final WindowTestUtils.TestWindowToken token =
-                new WindowTestUtils.TestWindowToken(0, mDisplayContent);
-        final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1");
-        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11");
-        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12");
-        final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2");
-        final WindowState window3 = createWindow(null, TYPE_APPLICATION, token, "window3");
-
-        window2.mLayer = 100;
-        window3.mLayer = 200;
-
-        // We assign layers once, to get the base values computed by
-        // the controller.
-        mLayersController.assignWindowLayers(mDisplayContent);
-
-        final int window1StartLayer = window1.mWinAnimator.mAnimLayer;
-        final int window11StartLayer = window11.mWinAnimator.mAnimLayer;
-        final int window12StartLayer = window12.mWinAnimator.mAnimLayer;
-        final int window2StartLayer = window2.mWinAnimator.mAnimLayer;
-        final int window3StartLayer = window3.mWinAnimator.mAnimLayer;
-
-        // Then we set an adjustment, and assign them again, they should
-        // be offset.
-        int adj = token.adj = 50;
-        mLayersController.assignWindowLayers(mDisplayContent);
-        final int highestLayer = token.getHighestAnimLayer();
-
-        assertEquals(window1StartLayer + adj, window1.mWinAnimator.mAnimLayer);
-        assertEquals(window11StartLayer + adj, window11.mWinAnimator.mAnimLayer);
-        assertEquals(window12StartLayer + adj, window12.mWinAnimator.mAnimLayer);
-        assertEquals(window2StartLayer + adj, window2.mWinAnimator.mAnimLayer);
-        assertEquals(window3StartLayer + adj, window3.mWinAnimator.mAnimLayer);
-        assertEquals(window3StartLayer + adj, highestLayer);
-    }
-
     /**
      * Test that a window token isn't orphaned by the system when it is requested to be removed.
      * Tokens should only be removed from the system when all their windows are gone.
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
new file mode 100644
index 0000000..f7c4b1f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+import android.util.Log;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+
+/**
+ * Tests for the {@link WindowLayersController} class.
+ *
+ * Build/Install/Run:
+ *  bit FrameworksServicesTests:com.android.server.wm.ZOrderingTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ZOrderingTests extends WindowTestsBase {
+
+    private class LayerRecordingTransaction extends SurfaceControl.Transaction {
+        HashMap<SurfaceControl, Integer> mLayersForControl = new HashMap();
+        HashMap<SurfaceControl, SurfaceControl> mRelativeLayersForControl = new HashMap();
+
+        @Override
+        public SurfaceControl.Transaction setLayer(SurfaceControl sc, int layer) {
+            mRelativeLayersForControl.remove(sc);
+            mLayersForControl.put(sc, layer);
+            return super.setLayer(sc, layer);
+        }
+
+        @Override
+        public SurfaceControl.Transaction setRelativeLayer(SurfaceControl sc,
+                SurfaceControl relativeTo,
+                int layer) {
+            mRelativeLayersForControl.put(sc, relativeTo);
+            mLayersForControl.put(sc, layer);
+            return super.setRelativeLayer(sc, relativeTo, layer);
+        }
+
+        int getLayer(SurfaceControl sc) {
+            return mLayersForControl.get(sc);
+        }
+
+        SurfaceControl getRelativeLayer(SurfaceControl sc) {
+            return mRelativeLayersForControl.get(sc);
+        }
+    };
+
+    // We have WM use our Hierarchy recording subclass of SurfaceControl.Builder
+    // such that we can keep track of the parents of Surfaces as they are constructed.
+    private HashMap<SurfaceControl, SurfaceControl> mParentFor = new HashMap();
+
+    private class HierarchyRecorder extends SurfaceControl.Builder {
+        SurfaceControl mPendingParent;
+
+        HierarchyRecorder(SurfaceSession s) {
+            super(s);
+        }
+
+        public SurfaceControl.Builder setParent(SurfaceControl sc) {
+            mPendingParent = sc;
+            return super.setParent(sc);
+        }
+        public SurfaceControl build() {
+            SurfaceControl sc = super.build();
+            mParentFor.put(sc, mPendingParent);
+            mPendingParent = null;
+            return sc;
+        }
+    };
+
+    class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory {
+        public SurfaceControl.Builder make(SurfaceSession s) {
+            return new HierarchyRecorder(s);
+        }
+    };
+
+    private LayerRecordingTransaction mTransaction;
+
+    @Override
+    void beforeCreateDisplay() {
+        // We can't use @Before here because it may happen after WindowTestsBase @Before
+        // which is after construction of the DisplayContent, meaning the HierarchyRecorder
+        // would miss construction of the top-level layers.
+        mTransaction = new LayerRecordingTransaction();
+        sWm.mSurfaceBuilderFactory = new HierarchyRecordingBuilderFactory();
+    }
+
+    @After
+    public void after() {
+        mTransaction.close();
+        mParentFor.clear();
+    }
+
+    LinkedList<SurfaceControl> getAncestors(LayerRecordingTransaction t, SurfaceControl sc) {
+        LinkedList<SurfaceControl> p = new LinkedList();
+        SurfaceControl current = sc;
+        do {
+            p.addLast(current);
+
+            SurfaceControl rs = t.getRelativeLayer(current);
+            if (rs != null) {
+                current = rs;
+            } else {
+                current = mParentFor.get(current);
+            }
+        } while (current != null);
+        return p;
+    }
+
+    void assertZOrderGreaterThan(LayerRecordingTransaction t,
+            SurfaceControl left, SurfaceControl right) throws Exception {
+        final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left);
+        final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right);
+
+        SurfaceControl commonAncestor = null;
+        SurfaceControl leftTop = leftParentChain.peekLast();
+        SurfaceControl rightTop = rightParentChain.peekLast();
+        while (leftTop != null && rightTop != null && leftTop == rightTop) {
+            commonAncestor = leftParentChain.removeLast();
+            rightParentChain.removeLast();
+            leftTop = leftParentChain.peekLast();
+            rightTop = rightParentChain.peekLast();
+        }
+
+        if (rightTop == null) { // right is the parent of left.
+            assertGreaterThan(t.getLayer(leftTop), 0);
+        } else if (leftTop == null) { // left is the parent of right.
+            assertGreaterThan(0, t.getLayer(rightTop));
+        } else {
+            assertGreaterThan(t.getLayer(leftTop),
+                    t.getLayer(rightTop));
+        }
+    }
+
+    void assertWindowLayerGreaterThan(LayerRecordingTransaction t,
+            WindowState left, WindowState right) throws Exception {
+        assertZOrderGreaterThan(t, left.getSurfaceControl(), right.getSurfaceControl());
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception {
+        sWm.mInputMethodTarget = null;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        // The Ime has an higher base layer than app windows and lower base layer than system
+        // windows, so it should be above app windows and below system windows if there isn't an IME
+        // target.
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
+        final WindowState imeAppTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+        sWm.mInputMethodTarget = imeAppTarget;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        // Ime should be above all app windows and below system windows if it is targeting an app
+        // window.
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
+        final WindowState imeAppTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+        final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
+                TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
+                "imeAppTargetChildAboveWindow");
+        final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget,
+                TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
+                "imeAppTargetChildBelowWindow");
+
+        sWm.mInputMethodTarget = imeAppTarget;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        // Ime should be above all app windows except for child windows that are z-ordered above it
+        // and below system windows if it is targeting an app window.
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+        assertWindowLayerGreaterThan(mTransaction, imeAppTargetChildAboveWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
+        final WindowState appBelowImeTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
+        final WindowState imeAppTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+        final WindowState appAboveImeTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
+
+        sWm.mInputMethodTarget = imeAppTarget;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        // Ime should be above all app windows except for non-fullscreen app window above it and
+        // below system windows if it is targeting an app window.
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, appBelowImeTarget);
+        assertWindowLayerGreaterThan(mTransaction, appAboveImeTarget, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForImeNonAppImeTarget() throws Exception {
+        final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY,
+                mDisplayContent, "imeSystemOverlayTarget",
+                true /* ownerCanAddInternalSystemWindow */);
+
+        sWm.mInputMethodTarget = imeSystemOverlayTarget;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        // The IME target base layer is higher than all window except for the nav bar window, so the
+        // IME should be above all windows except for the nav bar.
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeSystemOverlayTarget);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+
+        // The IME has a higher base layer than the status bar so we may expect it to go
+        // above the status bar once they are both in the Non-App layer, as past versions of this
+        // test enforced. However this seems like the wrong behavior unless the status bar is the
+        // IME target.
+        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testAssignWindowLayers_ForStatusBarImeTarget() throws Exception {
+        sWm.mInputMethodTarget = mStatusBarWindow;
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mStatusBarWindow);
+
+        // And, IME dialogs should always have an higher layer than the IME.
+        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+    }
+
+    @Test
+    public void testStackLayers() throws Exception {
+        final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
+                ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
+                "pinnedStackWindow");
+        final WindowState dockedStackWindow = createWindowOnStack(null,
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+                mDisplayContent, "dockedStackWindow");
+        final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
+                mDisplayContent, "assistantStackWindow");
+
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, mAppWindow);
+        assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow);
+        assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow);
+    }
+}