Added animations to dim layers

Added animations to dim layers when dims are set for
WindowState surfaces since they're controlled by the system.

Test: Dims for dialogs now animate.
Test: DimmerTests
Fixes: 71841698
Fixes: 69553362
Fixes: 71614627
Fixes: 72333587

Change-Id: I34f6f3d7885d5c9c0075a941e40d68bc5618a016
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 9fe16ae..b435605 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -17,34 +17,115 @@
 package com.android.server.wm;
 
 import android.util.ArrayMap;
-import android.util.Slog;
 import android.view.SurfaceControl;
 import android.graphics.Rect;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
  * black layers of varying opacity at various Z-levels which create the effect of a Dim.
  */
 class Dimmer {
     private static final String TAG = "WindowManager";
+    private static final int DEFAULT_DIM_ANIM_DURATION = 200;
 
-    private class DimState {
-        SurfaceControl mSurfaceControl;
+    private class DimAnimatable implements SurfaceAnimator.Animatable {
+        private final SurfaceControl mDimLayer;
+
+        private DimAnimatable(SurfaceControl dimLayer) {
+            mDimLayer = dimLayer;
+        }
+
+        @Override
+        public SurfaceControl.Transaction getPendingTransaction() {
+            return mHost.getPendingTransaction();
+        }
+
+        @Override
+        public void commitPendingTransaction() {
+            mHost.commitPendingTransaction();
+        }
+
+        @Override
+        public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
+        }
+
+        @Override
+        public void onAnimationLeashDestroyed(SurfaceControl.Transaction t) {
+        }
+
+        @Override
+        public void destroyAfterPendingTransaction(SurfaceControl surface) {
+            mHost.destroyAfterPendingTransaction(surface);
+        }
+
+        @Override
+        public SurfaceControl.Builder makeAnimationLeash() {
+            return mHost.makeAnimationLeash();
+        }
+
+        @Override
+        public SurfaceControl getAnimationLeashParent() {
+            return mHost.getSurfaceControl();
+        }
+
+        @Override
+        public SurfaceControl getSurfaceControl() {
+            return mDimLayer;
+        }
+
+        @Override
+        public SurfaceControl getParentSurfaceControl() {
+            return mHost.getSurfaceControl();
+        }
+
+        @Override
+        public int getSurfaceWidth() {
+            // This will determine the size of the leash created. This should be the size of the
+            // host and not the dim layer since the dim layer may get bigger during animation. If
+            // that occurs, the leash size cannot change so we need to ensure the leash is big
+            // enough that the dim layer can grow.
+            // This works because the mHost will be a Task which has the display bounds.
+            return mHost.getSurfaceWidth();
+        }
+
+        @Override
+        public int getSurfaceHeight() {
+            // See getSurfaceWidth() above for explanation.
+            return mHost.getSurfaceHeight();
+        }
+    }
+
+    @VisibleForTesting
+    class DimState {
+        /**
+         * The layer where property changes should be invoked on.
+         */
+        SurfaceControl mDimLayer;
         boolean mDimming;
+        boolean isVisible;
+        SurfaceAnimator mSurfaceAnimator;
 
         /**
-         * Used for Dims not assosciated with a WindowContainer. See {@link Dimmer#dimAbove} for
+         * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
          * details on Dim lifecycle.
          */
         boolean mDontReset;
 
-        DimState(SurfaceControl ctl) {
-            mSurfaceControl = ctl;
+        DimState(SurfaceControl dimLayer) {
+            mDimLayer = dimLayer;
             mDimming = true;
+            mSurfaceAnimator = new SurfaceAnimator(new DimAnimatable(dimLayer), () -> {
+                if (!mDimming) {
+                    mDimLayer.destroy();
+                }
+            }, mHost.mService.mAnimator::addAfterPrepareSurfacesRunnable, mHost.mService);
         }
-    };
+    }
 
-    private ArrayMap<WindowContainer, DimState> mDimLayerUsers = new ArrayMap<>();
+    @VisibleForTesting
+    ArrayMap<WindowContainer, DimState> mDimLayerUsers = new ArrayMap<>();
 
     /**
      * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
@@ -56,19 +137,18 @@
         mHost = host;
     }
 
-    SurfaceControl makeDimLayer() {
-        final SurfaceControl control = mHost.makeChildSurface(null)
+    private SurfaceControl makeDimLayer() {
+        return mHost.makeChildSurface(null)
                 .setParent(mHost.getSurfaceControl())
                 .setColorLayer(true)
                 .setName("Dim Layer for - " + mHost.getName())
                 .build();
-        return control;
     }
 
     /**
      * Retreive the DimState for a given child of the host.
      */
-    DimState getDimState(WindowContainer container) {
+    private DimState getDimState(WindowContainer container) {
         DimState state = mDimLayerUsers.get(container);
         if (state == null) {
             final SurfaceControl ctl = makeDimLayer();
@@ -88,14 +168,12 @@
     private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
             float alpha) {
         final DimState d = getDimState(container);
-        t.show(d.mSurfaceControl);
         if (container != null) {
-            t.setRelativeLayer(d.mSurfaceControl,
-                    container.getSurfaceControl(), relativeLayer);
+            t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
         } else {
-            t.setLayer(d.mSurfaceControl, Integer.MAX_VALUE);
+            t.setLayer(d.mDimLayer, Integer.MAX_VALUE);
         }
-        t.setAlpha(d.mSurfaceControl, alpha);
+        t.setAlpha(d.mDimLayer, alpha);
 
         d.mDimming = true;
     }
@@ -107,16 +185,18 @@
      */
     void stopDim(SurfaceControl.Transaction t) {
         DimState d = getDimState(null);
-        t.hide(d.mSurfaceControl);
+        t.hide(d.mDimLayer);
+        d.isVisible = false;
         d.mDontReset = false;
     }
+
     /**
      * Place a Dim above the entire host container. The caller is responsible for calling stopDim to
      * remove this effect. If the Dim can be assosciated with a particular child of the host
      * consider using the other variant of dimAbove which ties the Dim lifetime to the child
      * lifetime more explicitly.
      *
-     * @param t A transaction in which to apply the Dim.
+     * @param t     A transaction in which to apply the Dim.
      * @param alpha The alpha at which to Dim.
      */
     void dimAbove(SurfaceControl.Transaction t, float alpha) {
@@ -128,9 +208,9 @@
      * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
      * and the child should call dimAbove again to request the Dim to continue.
      *
-     * @param t A transaction in which to apply the Dim.
+     * @param t         A transaction in which to apply the Dim.
      * @param container The container which to dim above. Should be a child of our host.
-     * @param alpha The alpha at which to Dim.
+     * @param alpha     The alpha at which to Dim.
      */
     void dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
         dim(t, container, 1, alpha);
@@ -139,9 +219,9 @@
     /**
      * Like {@link #dimAbove} but places the dim below the given container.
      *
-     * @param t A transaction in which to apply the Dim.
+     * @param t         A transaction in which to apply the Dim.
      * @param container The container which to dim below. Should be a child of our host.
-     * @param alpha The alpha at which to Dim.
+     * @param alpha     The alpha at which to Dim.
      */
 
     void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
@@ -159,7 +239,7 @@
     void resetDimStates() {
         for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
             final DimState state = mDimLayerUsers.valueAt(i);
-            if (state.mDontReset == false) {
+            if (!state.mDontReset) {
                 state.mDimming = false;
             }
         }
@@ -169,7 +249,7 @@
      * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
      * described in {@link #resetDimStates}.
      *
-     * @param t A transaction in which to update the dims.
+     * @param t      A transaction in which to update the dims.
      * @param bounds The bounds at which to dim.
      * @return true if any Dims were updated.
      */
@@ -177,19 +257,80 @@
         boolean didSomething = false;
         for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
             DimState state = mDimLayerUsers.valueAt(i);
+            WindowContainer container = mDimLayerUsers.keyAt(i);
+
             // TODO: We want to animate the addition and removal of Dim's instead of immediately
             // acting. When we do this we need to take care to account for the "Replacing Windows"
             // case (and seamless dim transfer).
-            if (state.mDimming == false) {
+            if (!state.mDimming) {
                 mDimLayerUsers.removeAt(i);
-                state.mSurfaceControl.destroy();
+                startDimExit(container, state.mSurfaceAnimator, t);
             } else {
                 didSomething = true;
                 // TODO: Once we use geometry from hierarchy this falls away.
-                t.setSize(state.mSurfaceControl, bounds.width(), bounds.height());
-                t.setPosition(state.mSurfaceControl, bounds.left, bounds.top);
+                t.setSize(state.mDimLayer, bounds.width(), bounds.height());
+                t.setPosition(state.mDimLayer, bounds.left, bounds.top);
+                if (!state.isVisible) {
+                    state.isVisible = true;
+                    t.show(state.mDimLayer);
+                    startDimEnter(container, state.mSurfaceAnimator, t);
+                }
             }
         }
         return didSomething;
     }
+
+    private void startDimEnter(WindowContainer container, SurfaceAnimator animator,
+            SurfaceControl.Transaction t) {
+        startAnim(container, animator, t, 0 /* startAlpha */, 1 /* endAlpha */);
+    }
+
+    private void startDimExit(WindowContainer container, SurfaceAnimator animator,
+            SurfaceControl.Transaction t) {
+        startAnim(container, animator, t, 1 /* startAlpha */, 0 /* endAlpha */);
+    }
+
+    private void startAnim(WindowContainer container, SurfaceAnimator animator,
+            SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
+        animator.startAnimation(t, new LocalAnimationAdapter(
+                new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
+                mHost.mService.mSurfaceAnimationRunner), false /* hidden */);
+    }
+
+    private long getDimDuration(WindowContainer container) {
+        // If there's no container, then there isn't an animation occurring while dimming. Set the
+        // duration to 0 so it immediately dims to the set alpha.
+        if (container == null) {
+            return 0;
+        }
+
+        // Otherwise use the same duration as the animation on the WindowContainer
+        AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
+        return animationAdapter == null ? DEFAULT_DIM_ANIM_DURATION
+                : animationAdapter.getDurationHint();
+    }
+
+    private static class AlphaAnimationSpec implements LocalAnimationAdapter.AnimationSpec {
+        private final long mDuration;
+        private final float mFromAlpha;
+        private final float mToAlpha;
+
+        AlphaAnimationSpec(float fromAlpha, float toAlpha, long duration) {
+            mFromAlpha = fromAlpha;
+            mToAlpha = toAlpha;
+            mDuration = duration;
+        }
+
+        @Override
+        public long getDuration() {
+            return mDuration;
+        }
+
+        @Override
+        public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
+            float alpha = ((float) currentPlayTime / getDuration()) * (mToAlpha - mFromAlpha)
+                    + mFromAlpha;
+            t.setAlpha(sc, alpha);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3f49f0c..7674b5e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3599,6 +3599,8 @@
     }
 
     private final class AboveAppWindowContainers extends NonAppWindowContainers {
+        private final Dimmer mDimmer = new Dimmer(this);
+        private final Rect mTmpDimBoundsRect = new Rect();
         AboveAppWindowContainers(String name, WindowManagerService service) {
             super(name, service);
         }
@@ -3630,6 +3632,22 @@
                 imeContainer.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE);
             }
         }
+
+        @Override
+        Dimmer getDimmer() {
+            return mDimmer;
+        }
+
+        @Override
+        void prepareSurfaces() {
+            mDimmer.resetDimStates();
+            super.prepareSurfaces();
+            getBounds(mTmpDimBoundsRect);
+
+            if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+                scheduleAnimation();
+            }
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 0628436..7d970d9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -462,8 +462,8 @@
                 } else {
                     mStack.getBounds(mTmpRect);
                     mTmpRect.intersect(getBounds());
+                    out.set(mTmpRect);
                 }
-                out.set(mTmpRect);
             } else {
                 out.set(getBounds());
             }
@@ -640,6 +640,7 @@
         mPreserveNonFloatingState = false;
     }
 
+    @Override
     Dimmer getDimmer() {
         return mDimmer;
     }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index bc0f9ad..cf54b67 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1722,6 +1722,7 @@
                 || activityType == ACTIVITY_TYPE_ASSISTANT;
     }
 
+    @Override
     Dimmer getDimmer() {
         return mDimmer;
     }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 1f9255a..d58f02c 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1219,4 +1219,11 @@
             outPos.offset(-parentBounds.left, -parentBounds.top);
         }
     }
+
+    Dimmer getDimmer() {
+        if (mParent == null) {
+            return null;
+        }
+        return mParent.getDimmer();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4fb2390..066e4e6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -907,9 +907,16 @@
     public static WindowManagerService main(final Context context, final InputManagerService im,
             final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
             WindowManagerPolicy policy) {
+        return main(context, im, haveInputMethods, showBootMsgs, onlyCore, policy,
+                new SurfaceAnimationRunner());
+    }
+
+    public static WindowManagerService main(final Context context, final InputManagerService im,
+            final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
+            WindowManagerPolicy policy, SurfaceAnimationRunner surfaceAnimationRunner) {
         DisplayThread.getHandler().runWithScissors(() ->
                 sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
-                        onlyCore, policy), 0);
+                        onlyCore, policy, surfaceAnimationRunner), 0);
         return sInstance;
     }
 
@@ -932,7 +939,7 @@
 
     private WindowManagerService(Context context, InputManagerService inputManager,
             boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
-            WindowManagerPolicy policy) {
+            WindowManagerPolicy policy, SurfaceAnimationRunner surfaceAnimationRunner) {
         installLock(this, INDEX_WINDOW);
         mContext = context;
         mHaveInputMethods = haveInputMethods;
@@ -1059,7 +1066,7 @@
                 PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM);
         mHoldingScreenWakeLock.setReferenceCounted(false);
 
-        mSurfaceAnimationRunner = new SurfaceAnimationRunner();
+        mSurfaceAnimationRunner = surfaceAnimationRunner;
 
         mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 477dd2b..55c982c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2138,18 +2138,6 @@
         mInputWindowHandle.inputChannel = null;
     }
 
-    private Dimmer getDimmer() {
-        Task task = getTask();
-        if (task != null) {
-            return task.getDimmer();
-        }
-        TaskStack taskStack = getStack();
-        if (taskStack != null) {
-            return taskStack.getDimmer();
-        }
-        return null;
-    }
-
     /** Returns true if the replacement window was removed. */
     boolean removeReplacedWindowIfNeeded(WindowState replacement) {
         if (mWillReplaceWindow && mReplacementWindow == replacement && replacement.hasDrawnLw()) {
@@ -4516,11 +4504,11 @@
     private void applyDims(Dimmer dimmer) {
         if (!mAnimatingExit && mAppDied) {
             mIsDimming = true;
-            getDimmer().dimAbove(getPendingTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
+            dimmer.dimAbove(getPendingTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
         } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
-                && !mAnimatingExit && isVisible()) {
+                && !mAnimatingExit && isVisible() && !mWinAnimator.mLastHidden) {
             mIsDimming = true;
-            getDimmer().dimBelow(getPendingTransaction(), this, mAttrs.dimAmount);
+            dimmer.dimBelow(getPendingTransaction(), this, mAttrs.dimAmount);
         }
     }
 
@@ -4531,7 +4519,6 @@
         if (dimmer != null) {
             applyDims(dimmer);
         }
-
         updateSurfacePosition(mPendingTransaction);
 
         mWinAnimator.prepareSurfaceLocked(true);
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
index 70906df..396fef4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
@@ -16,11 +16,14 @@
 
 package com.android.server.wm;
 
-import java.util.HashMap;
-
-import org.junit.Test;
-import org.junit.Before;
-import org.junit.runner.RunWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
@@ -28,22 +31,25 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.stubbing.Answer;
 
 /**
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.DimmerTests;
+ * atest FrameworksServicesTests:com.android.server.wm.DimmerTests;
  */
 @Presubmit
+@Ignore("b/72450130")
 @RunWith(AndroidJUnit4.class)
 public class DimmerTests extends WindowTestsBase {
+
+    public DimmerTests() {
+        super(spy(new SurfaceAnimationRunner()));
+    }
+
     private class TestWindowContainer extends WindowContainer<TestWindowContainer> {
         final SurfaceControl mControl = mock(SurfaceControl.class);
         final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class);
@@ -65,12 +71,14 @@
 
     private class MockSurfaceBuildingContainer extends WindowContainer<TestWindowContainer> {
         final SurfaceSession mSession = new SurfaceSession();
-        SurfaceControl mBuiltSurface = null;
-        final SurfaceControl mHostControl = mock(SurfaceControl.class);
         final SurfaceControl.Transaction mHostTransaction = mock(SurfaceControl.Transaction.class);
 
         MockSurfaceBuildingContainer() {
             super(sWm);
+            mSurfaceControl = sWm.makeSurfaceBuilder(mSession)
+                    .setName("test surface")
+                    .setSize(1, 1)
+                    .build();
         }
 
         class MockSurfaceBuilder extends SurfaceControl.Builder {
@@ -80,12 +88,19 @@
 
             @Override
             public SurfaceControl build() {
-                SurfaceControl sc = mock(SurfaceControl.class);
-                mBuiltSurface = sc;
-                return sc;
+                return spy(sWm.makeSurfaceBuilder(mSession)
+                        .setName("test surface")
+                        .setSize(1, 1)
+                        .build());
             }
         }
 
+        @Override
+        SurfaceControl.Builder makeSurface() {
+            return sWm.makeSurfaceBuilder(mSession)
+                    .setName("test surface")
+                    .setSize(1, 1);
+        }
 
         @Override
         SurfaceControl.Builder makeChildSurface(WindowContainer child) {
@@ -93,11 +108,6 @@
         }
 
         @Override
-        public SurfaceControl getSurfaceControl() {
-            return mHostControl;
-        }
-
-        @Override
         public SurfaceControl.Transaction getPendingTransaction() {
             return mHostTransaction;
         }
@@ -114,29 +124,37 @@
 
         mTransaction = mock(SurfaceControl.Transaction.class);
         mDimmer = new Dimmer(mHost);
+
+        doAnswer((Answer<Void>) invocation -> {
+            Runnable runnable = invocation.getArgument(3);
+            runnable.run();
+            return null;
+        }).when(sWm.mSurfaceAnimationRunner).startAnimation(any(), any(), any(), any());
     }
 
     @Test
     public void testDimAboveNoChildCreatesSurface() throws Exception {
         final float alpha = 0.8f;
         mDimmer.dimAbove(mTransaction, alpha);
-        assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
 
-        verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
-        verify(mTransaction).show(mHost.mBuiltSurface);
-        verify(mTransaction).setLayer(mHost.mBuiltSurface, Integer.MAX_VALUE);
+        SurfaceControl dimLayer = getDimLayer(null);
+
+        assertNotNull("Dimmer should have created a surface", dimLayer);
+
+        verify(mTransaction).setAlpha(dimLayer, alpha);
+        verify(mTransaction).setLayer(dimLayer, Integer.MAX_VALUE);
     }
 
     @Test
     public void testDimAboveNoChildRedundantlyUpdatesAlphaOnExistingSurface() throws Exception {
         float alpha = 0.8f;
         mDimmer.dimAbove(mTransaction, alpha);
-        final SurfaceControl firstSurface = mHost.mBuiltSurface;
+        final SurfaceControl firstSurface = getDimLayer(null);
 
         alpha = 0.9f;
         mDimmer.dimAbove(mTransaction, alpha);
 
-        assertEquals(firstSurface, mHost.mBuiltSurface);
+        assertEquals(firstSurface, getDimLayer(null));
         verify(mTransaction).setAlpha(firstSurface, 0.9f);
     }
 
@@ -148,16 +166,20 @@
         int height = 300;
         Rect bounds = new Rect(0, 0, width, height);
         mDimmer.updateDims(mTransaction, bounds);
-        verify(mTransaction).setSize(mHost.mBuiltSurface, width, height);
+
+        verify(mTransaction).setSize(getDimLayer(null), width, height);
+        verify(mTransaction).show(getDimLayer(null));
     }
 
     @Test
     public void testDimAboveNoChildNotReset() throws Exception {
         mDimmer.dimAbove(mTransaction, 0.8f);
+        SurfaceControl dimLayer = getDimLayer(null);
         mDimmer.resetDimStates();
 
         mDimmer.updateDims(mTransaction, new Rect());
-        verify(mHost.mBuiltSurface, never()).destroy();
+        verify(mTransaction).show(getDimLayer(null));
+        verify(dimLayer, never()).destroy();
     }
 
     @Test
@@ -167,11 +189,12 @@
 
         final float alpha = 0.8f;
         mDimmer.dimAbove(mTransaction, child, alpha);
-        assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+        SurfaceControl mDimLayer = getDimLayer(child);
 
-        verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
-        verify(mTransaction).show(mHost.mBuiltSurface);
-        verify(mTransaction).setRelativeLayer(mHost.mBuiltSurface, child.mControl, 1);
+        assertNotNull("Dimmer should have created a surface", mDimLayer);
+
+        verify(mTransaction).setAlpha(mDimLayer, alpha);
+        verify(mTransaction).setRelativeLayer(mDimLayer, child.mControl, 1);
     }
 
     @Test
@@ -181,11 +204,12 @@
 
         final float alpha = 0.8f;
         mDimmer.dimBelow(mTransaction, child, alpha);
-        assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+        SurfaceControl mDimLayer = getDimLayer(child);
 
-        verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
-        verify(mTransaction).show(mHost.mBuiltSurface);
-        verify(mTransaction).setRelativeLayer(mHost.mBuiltSurface, child.mControl, -1);
+        assertNotNull("Dimmer should have created a surface", mDimLayer);
+
+        verify(mTransaction).setAlpha(mDimLayer, alpha);
+        verify(mTransaction).setRelativeLayer(mDimLayer, child.mControl, -1);
     }
 
     @Test
@@ -195,9 +219,11 @@
 
         final float alpha = 0.8f;
         mDimmer.dimAbove(mTransaction, child, alpha);
+        SurfaceControl dimLayer = getDimLayer(child);
         mDimmer.resetDimStates();
+
         mDimmer.updateDims(mTransaction, new Rect());
-        verify(mHost.mBuiltSurface).destroy();
+        verify(dimLayer).destroy();
     }
 
     @Test
@@ -207,10 +233,16 @@
 
         final float alpha = 0.8f;
         mDimmer.dimAbove(mTransaction, child, alpha);
+        SurfaceControl dimLayer = getDimLayer(child);
         mDimmer.resetDimStates();
         mDimmer.dimAbove(mTransaction, child, alpha);
 
         mDimmer.updateDims(mTransaction, new Rect());
-        verify(mHost.mBuiltSurface, never()).destroy();
+        verify(mTransaction).show(dimLayer);
+        verify(dimLayer, never()).destroy();
+    }
+
+    private SurfaceControl getDimLayer(WindowContainer windowContainer) {
+        return mDimmer.mDimLayerUsers.get(windowContainer).mDimLayer;
     }
 }
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 35ca493..81fd889 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
 
 import android.os.PowerSaveState;
 import android.util.proto.ProtoOutputStream;
@@ -68,6 +69,11 @@
     private Runnable mRunnableWhenAddingSplashScreen;
 
     static synchronized WindowManagerService getWindowManagerService(Context context) {
+        return getWindowManagerService(context, new SurfaceAnimationRunner());
+    }
+
+    static synchronized WindowManagerService getWindowManagerService(Context context,
+            SurfaceAnimationRunner surfaceAnimationRunner) {
         if (sWm == null) {
             // We only want to do this once for the test process as we don't want WM to try to
             // register a bunch of local services again.
@@ -105,7 +111,7 @@
             }
 
             sWm = WindowManagerService.main(context, ims, true, false,
-                    false, new TestWindowManagerPolicy());
+                    false, new TestWindowManagerPolicy(), surfaceAnimationRunner);
         }
         return sWm;
     }
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 69b1378..7918901 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -84,6 +84,16 @@
     WindowState mChildAppWindowBelow;
     HashSet<WindowState> mCommonWindows;
 
+    private final SurfaceAnimationRunner mSurfaceAnimationRunner;
+
+    public WindowTestsBase() {
+        this(new SurfaceAnimationRunner());
+    }
+
+    public WindowTestsBase(SurfaceAnimationRunner surfaceAnimationRunner) {
+        mSurfaceAnimationRunner = surfaceAnimationRunner;
+    }
+
     @Before
     public void setUp() throws Exception {
         if (!sOneTimeSetupDone) {
@@ -98,7 +108,7 @@
         final Context context = InstrumentationRegistry.getTargetContext();
         AttributeCache.init(context);
 
-        sWm = TestWindowManagerPolicy.getWindowManagerService(context);
+        sWm = TestWindowManagerPolicy.getWindowManagerService(context, mSurfaceAnimationRunner);
         beforeCreateDisplay();
 
         context.getDisplay().getDisplayInfo(mDisplayInfo);