Merge changes If9b31b3b,Iea400614,I9567aaf5,I6696029f,I3d4f434e, ...

* changes:
  Don't show new groups until their children inflate
  Cleanup PreparationCoordinator a bit
  Add onCleanup() method to Pluggables
  Introduce GroupEntryBuilder
  NotificationEntryBuilder can modify existing entries
  Move creationTime into ListEntry (from NotifEntry)
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
index 48ce8ab..8f8fc29e 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
@@ -26,7 +26,6 @@
 import android.provider.Settings;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
 import androidx.test.rule.ActivityTestRule;
 
 import org.junit.After;
@@ -37,7 +36,6 @@
 /**
  * Base class for all autofill tests.
  */
-@LargeTest
 public abstract class AbstractAutofillPerfTestCase {
 
     @ClassRule
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
index fb5ea80..5f52dc7 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
@@ -24,10 +24,14 @@
 import android.view.View;
 import android.widget.EditText;
 
+import androidx.test.filters.LargeTest;
+
 import com.android.perftests.autofill.R;
 
+import org.junit.Ignore;
 import org.junit.Test;
 
+@LargeTest
 public class LoginTest extends AbstractAutofillPerfTestCase {
 
     private EditText mUsername;
@@ -90,6 +94,8 @@
     /**
      * Now the service returns autofill data, for both username and password.
      */
+    // TODO(b/162216576): fix fail test and re-enable it
+    @Ignore
     @Test
     public void testFocus_autofillBothFields() throws Throwable {
         MyAutofillService.newCannedResponse()
@@ -142,6 +148,8 @@
     /**
      * Now the service returns autofill data, but just for username.
      */
+    // TODO(b/162216576): fix fail test and re-enable it
+    @Ignore
     @Test
     public void testFocus_autofillUsernameOnly() throws Throwable {
         // Must set ignored ids so focus on password does not trigger new requests
@@ -258,6 +266,8 @@
         });
     }
 
+    // TODO(b/162216576): fix fail test and re-enable it
+    @Ignore
     @Test
     public void testCallbacks() throws Throwable {
         MyAutofillService.newCannedResponse()
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java
index f02c96d..9b853fe 100644
--- a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java
@@ -33,7 +33,6 @@
 import android.util.Log;
 
 import androidx.annotation.NonNull;
-import androidx.test.filters.LargeTest;
 import androidx.test.rule.ActivityTestRule;
 
 import com.android.compatibility.common.util.ActivitiesWatcher;
@@ -51,7 +50,6 @@
 /**
  * Base class for all content capture tests.
  */
-@LargeTest
 public abstract class AbstractContentCapturePerfTestCase {
 
     private static final String TAG = AbstractContentCapturePerfTestCase.class.getSimpleName();
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java
index 750d3b4..7257509 100644
--- a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java
@@ -21,11 +21,14 @@
 import android.perftests.utils.BenchmarkState;
 import android.view.View;
 
+import androidx.test.filters.LargeTest;
+
 import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
 import com.android.perftests.contentcapture.R;
 
 import org.junit.Test;
 
+@LargeTest
 public class LoginTest extends AbstractContentCapturePerfTestCase {
 
     @Test
diff --git a/api/test-current.txt b/api/test-current.txt
index 866b9383..963ef7c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -553,11 +553,13 @@
     method public int getActivityType();
     method public android.graphics.Rect getAppBounds();
     method public android.graphics.Rect getBounds();
+    method @NonNull public android.graphics.Rect getMaxBounds();
     method public int getRotation();
     method public int getWindowingMode();
     method public void setActivityType(int);
     method public void setAppBounds(android.graphics.Rect);
     method public void setBounds(android.graphics.Rect);
+    method public void setMaxBounds(@Nullable android.graphics.Rect);
     method public void setRotation(int);
     method public void setTo(android.app.WindowConfiguration);
     method public void setWindowingMode(int);
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index ec81ae3..79f05a3 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -310,7 +310,6 @@
      * Sets the maximum bounds to the provided {@link Rect}.
      * @param rect the new bounds value.
      * @see #getMaxBounds()
-     * @hide
      */
     public void setMaxBounds(@Nullable Rect rect) {
         if (rect == null) {
@@ -364,10 +363,8 @@
         return mBounds;
     }
 
-    /**
-     * @see #setMaxBounds(Rect)
-     * @hide
-     */
+    /** @see #setMaxBounds(Rect) */
+    @NonNull
     public Rect getMaxBounds() {
         return mMaxBounds;
     }
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 15625cd..decf053 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -683,6 +683,8 @@
      *<p>BACKWARD_COMPATIBLE devices capable of streaming concurrently with other devices as described by
      * {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds} have the
      * following guaranteed streams (when streaming concurrently with other devices)</p>
+     * <p> Note: The sizes mentioned for these concurrent streams are the maximum sizes guaranteed
+     * to be supported. Sizes smaller than these, obtained by {@link StreamConfigurationMap#getOutputSizes} for a particular format, are supported as well. </p>
      *
      * <table>
      * <tr><th colspan="5">Concurrent stream guaranteed configurations</th></tr>
@@ -696,7 +698,7 @@
      * </table><br>
      * </p>
      *
-     * <p> Devices which are not backwards-compatible, support a mandatory single stream of size sVGA with image format {@code DEPTH16} during concurrent operation.
+     * <p> Devices which are not backwards-compatible, support a mandatory single stream of size sVGA with image format {@code DEPTH16} during concurrent operation. </p>
      *
      * <p> For guaranteed concurrent stream configurations:</p>
      * <p> sVGA refers to the camera device's maximum resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index ffb087f..5d4003a 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -608,7 +608,7 @@
     public static final int TYPE_BLUETOOTH   = 7;
 
     /**
-     * Dummy data connection.  This should not be used on shipping devices.
+     * Fake data connection.  This should not be used on shipping devices.
      * @deprecated This is not used any more.
      */
     @Deprecated
@@ -1084,9 +1084,9 @@
      *                   to remove an existing always-on VPN configuration.
      * @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
      *        {@code false} otherwise.
-     * @param lockdownWhitelist The list of packages that are allowed to access network directly
+     * @param lockdownAllowlist The list of packages that are allowed to access network directly
      *         when VPN is in lockdown mode but is not running. Non-existent packages are ignored so
-     *         this method must be called when a package that should be whitelisted is installed or
+     *         this method must be called when a package that should be allowed is installed or
      *         uninstalled.
      * @return {@code true} if the package is set as always-on VPN controller;
      *         {@code false} otherwise.
@@ -1094,10 +1094,10 @@
      */
     @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
     public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage,
-            boolean lockdownEnabled, @Nullable List<String> lockdownWhitelist) {
+            boolean lockdownEnabled, @Nullable List<String> lockdownAllowlist) {
         try {
             return mService.setAlwaysOnVpnPackage(
-                    userId, vpnPackage, lockdownEnabled, lockdownWhitelist);
+                    userId, vpnPackage, lockdownEnabled, lockdownAllowlist);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index e449615..713b688 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -28,7 +28,7 @@
  * @hide
  */
 public class NetworkState implements Parcelable {
-    private static final boolean SANITY_CHECK_ROAMING = false;
+    private static final boolean VALIDATE_ROAMING_STATE = false;
 
     public static final NetworkState EMPTY = new NetworkState(null, null, null, null, null, null);
 
@@ -52,7 +52,7 @@
 
         // This object is an atomic view of a network, so the various components
         // should always agree on roaming state.
-        if (SANITY_CHECK_ROAMING && networkInfo != null && networkCapabilities != null) {
+        if (VALIDATE_ROAMING_STATE && networkInfo != null && networkCapabilities != null) {
             if (networkInfo.isRoaming() == networkCapabilities
                     .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)) {
                 Slog.wtf("NetworkState", "Roaming state disagreement between " + networkInfo
diff --git a/core/java/android/net/SntpClient.java b/core/java/android/net/SntpClient.java
index 8c6faf6..6c94b7a 100644
--- a/core/java/android/net/SntpClient.java
+++ b/core/java/android/net/SntpClient.java
@@ -140,7 +140,7 @@
             final long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
             final long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET);
 
-            /* do sanity check according to RFC */
+            /* Do validation according to RFC */
             // TODO: validate originateTime == requestTime.
             checkValidServerReply(leap, mode, stratum, transmitTime);
 
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 9c2c5b8..8e90a119 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -119,7 +119,7 @@
  * <p> The Android system starts a VPN in the background by calling
  * {@link android.content.Context#startService startService()}. In Android 8.0
  * (API level 26) and higher, the system places VPN apps on the temporary
- * whitelist for a short period so the app can start in the background. The VPN
+ * allowlist for a short period so the app can start in the background. The VPN
  * app must promote itself to the foreground after it's launched or the system
  * will shut down the app.
  *
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 0cc469a..c4048e5 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -26,6 +26,7 @@
 import android.annotation.TestApi;
 import android.app.KeyguardManager;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -1157,9 +1158,19 @@
      * </p><p>
      * The real size may be smaller than the physical size of the screen when the
      * window manager is emulating a smaller display (using adb shell wm size).
-     * </p>
+     * </p><p>
+     * In general, {@link #getRealSize(Point)} and {@link WindowManager#getMaximumWindowMetrics()}
+     * report the same bounds except that certain areas of the display may not be available to
+     * windows created in the {@link WindowManager}'s {@link Context}.
+     *
+     * For example, imagine a device which has a multi-task mode that limits windows to half of the
+     * screen. In this case, {@link WindowManager#getMaximumWindowMetrics()} reports the
+     * bounds of the screen half where the window is located, while {@link #getRealSize(Point)}
+     * still reports the bounds of the whole display.
      *
      * @param outSize Set to the real size of the display.
+     *
+     * @see WindowManager#getMaximumWindowMetrics()
      */
     public void getRealSize(Point outSize) {
         synchronized (this) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 1d54d92..cf4315f 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -72,6 +72,7 @@
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.graphics.PixelFormat;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.IBinder;
@@ -473,9 +474,18 @@
      *
      * Note that this might still be smaller than the size of the physical display if certain areas
      * of the display are not available to windows created in this {@link Context}.
+     * <p>
+     * For example, given that there's a device which have a multi-task mode to limit activities
+     * to a half screen. In this case, {@link #getMaximumWindowMetrics()} reports the bounds of
+     * the half screen which the activity is located, while {@link Display#getRealSize(Point)} still
+     * reports the bounds of the whole physical display.
      *
-     * @see #getMaximumWindowMetrics()
+     * Despite this, {@link #getMaximumWindowMetrics()} and {@link Display#getRealSize(Point)}
+     * reports the same bounds in general.
+     *
+     * @see #getCurrentWindowMetrics()
      * @see WindowMetrics
+     * @see Display#getRealSize(Point)
      */
     default @NonNull WindowMetrics getMaximumWindowMetrics() {
         throw new UnsupportedOperationException();
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 2fe7c02..b4561c5 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -29,7 +29,6 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.Insets;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Bundle;
@@ -233,17 +232,16 @@
 
     @Override
     public WindowMetrics getMaximumWindowMetrics() {
-        final Rect maxBounds = getMaximumBounds();
+        final Context context = mParentWindow != null ? mParentWindow.getContext() : mContext;
+        final Rect maxBounds = getMaximumBounds(context);
+
         return new WindowMetrics(maxBounds, computeWindowInsets(maxBounds));
     }
 
-    private Rect getMaximumBounds() {
-        // TODO(b/128338354): Current maximum bound is display size, but it should be displayArea
-        //  bound after displayArea feature is finished.
-        final Display display = mContext.getDisplayNoVerify();
-        final Point displaySize = new Point();
-        display.getRealSize(displaySize);
-        return new Rect(0, 0, displaySize.x, displaySize.y);
+    private static Rect getMaximumBounds(Context context) {
+        synchronized (ResourcesManager.getInstance()) {
+            return context.getResources().getConfiguration().windowConfiguration.getMaxBounds();
+        }
     }
 
     // TODO(b/150095967): Set window type to LayoutParams
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c216638..b85cbef 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -204,6 +204,7 @@
     optional WindowContainerProto window_container = 1;
     optional string name = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
     repeated DisplayAreaChildProto children = 3 [deprecated=true];
+    optional bool is_task_display_area = 4;
 }
 
 /* represents a generic child of a DisplayArea */
diff --git a/libs/hwui/AutoBackendTextureRelease.cpp b/libs/hwui/AutoBackendTextureRelease.cpp
index 72747e8..33264d5 100644
--- a/libs/hwui/AutoBackendTextureRelease.cpp
+++ b/libs/hwui/AutoBackendTextureRelease.cpp
@@ -25,7 +25,8 @@
 namespace android {
 namespace uirenderer {
 
-AutoBackendTextureRelease::AutoBackendTextureRelease(GrContext* context, AHardwareBuffer* buffer) {
+AutoBackendTextureRelease::AutoBackendTextureRelease(GrDirectContext* context,
+                                                     AHardwareBuffer* buffer) {
     AHardwareBuffer_Desc desc;
     AHardwareBuffer_describe(buffer, &desc);
     bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
@@ -67,8 +68,9 @@
     textureRelease->unref(false);
 }
 
-void AutoBackendTextureRelease::makeImage(AHardwareBuffer* buffer, android_dataspace dataspace,
-                                          GrContext* context) {
+void AutoBackendTextureRelease::makeImage(AHardwareBuffer* buffer,
+                                          android_dataspace dataspace,
+                                          GrDirectContext* context) {
     AHardwareBuffer_Desc desc;
     AHardwareBuffer_describe(buffer, &desc);
     SkColorType colorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
@@ -81,7 +83,7 @@
     }
 }
 
-void AutoBackendTextureRelease::newBufferContent(GrContext* context) {
+void AutoBackendTextureRelease::newBufferContent(GrDirectContext* context) {
     if (mBackendTexture.isValid()) {
         mUpdateProc(mImageCtx, context);
     }
diff --git a/libs/hwui/AutoBackendTextureRelease.h b/libs/hwui/AutoBackendTextureRelease.h
index acdd63c..06f51fc 100644
--- a/libs/hwui/AutoBackendTextureRelease.h
+++ b/libs/hwui/AutoBackendTextureRelease.h
@@ -31,7 +31,8 @@
  */
 class AutoBackendTextureRelease final {
 public:
-    AutoBackendTextureRelease(GrContext* context, AHardwareBuffer* buffer);
+    AutoBackendTextureRelease(GrDirectContext* context,
+                              AHardwareBuffer* buffer);
 
     const GrBackendTexture& getTexture() const { return mBackendTexture; }
 
@@ -42,9 +43,11 @@
 
     inline sk_sp<SkImage> getImage() const { return mImage; }
 
-    void makeImage(AHardwareBuffer* buffer, android_dataspace dataspace, GrContext* context);
+    void makeImage(AHardwareBuffer* buffer,
+                   android_dataspace dataspace,
+                   GrDirectContext* context);
 
-    void newBufferContent(GrContext* context);
+    void newBufferContent(GrDirectContext* context);
 
 private:
     // The only way to invoke dtor is with unref, when mUsageCount is 0.
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 67d8c07..6589dbd 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -189,7 +189,7 @@
 sk_sp<SkImage> DeferredLayerUpdater::ImageSlot::createIfNeeded(AHardwareBuffer* buffer,
                                                                android_dataspace dataspace,
                                                                bool forceCreate,
-                                                               GrContext* context) {
+                                                               GrDirectContext* context) {
     if (!mTextureRelease || !mTextureRelease->getImage().get() || dataspace != mDataspace ||
         forceCreate || mBuffer != buffer) {
         if (buffer != mBuffer) {
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 05f3774..6731e9c 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -106,7 +106,7 @@
         ~ImageSlot() { clear(); }
 
         sk_sp<SkImage> createIfNeeded(AHardwareBuffer* buffer, android_dataspace dataspace,
-                                      bool forceCreate, GrContext* context);
+                                      bool forceCreate, GrDirectContext* context);
 
     private:
         void clear();
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index c2d2ff8..8724442 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -21,7 +21,7 @@
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 #include <GLES3/gl3.h>
-#include <GrContext.h>
+#include <GrDirectContext.h>
 #include <SkCanvas.h>
 #include <SkImage.h>
 #include <utils/GLUtils.h>
@@ -285,7 +285,7 @@
         return (image.get() != nullptr);
     }
 
-    sk_sp<GrContext> mGrContext;
+    sk_sp<GrDirectContext> mGrContext;
     renderthread::VulkanManager mVulkanManager;
     std::mutex mVkLock;
 };
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 0dea354..b71bb07 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -118,7 +118,7 @@
     }
     int imgWidth = image->width();
     int imgHeight = image->height();
-    sk_sp<GrContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
+    sk_sp<GrDirectContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
 
     if (bitmap->colorType() == kRGBA_F16_SkColorType &&
         !grContext->colorTypeSupportedAsSurface(bitmap->colorType())) {
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index 14a297f..dd0fc69 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -15,7 +15,7 @@
  */
 
 #include "GLFunctorDrawable.h"
-#include <GrContext.h>
+#include <GrDirectContext.h>
 #include <private/hwui/DrawGlInfo.h>
 #include "FunctorDrawable.h"
 #include "GrBackendSurface.h"
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index f839213e..f95f347 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -29,7 +29,7 @@
 void LayerDrawable::onDraw(SkCanvas* canvas) {
     Layer* layer = mLayerUpdater->backingLayer();
     if (layer) {
-        DrawLayer(canvas->getGrContext(), canvas, layer, nullptr, nullptr, true);
+        DrawLayer(canvas->recordingContext(), canvas, layer, nullptr, nullptr, true);
     }
 }
 
@@ -67,8 +67,12 @@
              isIntegerAligned(dstDevRect.y()));
 }
 
-bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
-                              const SkRect* srcRect, const SkRect* dstRect,
+// TODO: Context arg probably doesn't belong here – do debug check at callsite instead.
+bool LayerDrawable::DrawLayer(GrRecordingContext* context,
+                              SkCanvas* canvas,
+                              Layer* layer,
+                              const SkRect* srcRect,
+                              const SkRect* dstRect,
                               bool useLayerTransform) {
     if (context == nullptr) {
         SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface"));
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
index 7cd515a..ffbb480 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.h
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -32,8 +32,12 @@
 public:
     explicit LayerDrawable(DeferredLayerUpdater* layerUpdater) : mLayerUpdater(layerUpdater) {}
 
-    static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer, const SkRect* srcRect,
-                          const SkRect* dstRect, bool useLayerTransform);
+    static bool DrawLayer(GrRecordingContext* context,
+    					  SkCanvas* canvas,
+    					  Layer* layer,
+    					  const SkRect* srcRect,
+    					  const SkRect* dstRect,
+    					  bool useLayerTransform);
 
 protected:
     virtual SkRect onGetBounds() override {
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 206b58f..565fb61 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -190,7 +190,7 @@
     auto glesVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
     auto size = glesVersion ? strlen(glesVersion) : -1;
     cacheManager().configureContext(&options, glesVersion, size);
-    sk_sp<GrContext> grContext(GrContext::MakeGL(std::move(glInterface), options));
+    sk_sp<GrDirectContext> grContext(GrDirectContext::MakeGL(std::move(glInterface), options));
     LOG_ALWAYS_FATAL_IF(!grContext.get());
     setGrContext(grContext);
 }
@@ -204,7 +204,7 @@
     initGrContextOptions(options);
     auto vkDriverVersion = mVkManager->getDriverVersion();
     cacheManager().configureContext(&options, &vkDriverVersion, sizeof(vkDriverVersion));
-    sk_sp<GrContext> grContext = mVkManager->createContext(options);
+    sk_sp<GrDirectContext> grContext = mVkManager->createContext(options);
     LOG_ALWAYS_FATAL_IF(!grContext.get());
     setGrContext(grContext);
 }
@@ -263,7 +263,7 @@
     return *mReadback;
 }
 
-void RenderThread::setGrContext(sk_sp<GrContext> context) {
+void RenderThread::setGrContext(sk_sp<GrDirectContext> context) {
     mCacheManager->reset(context);
     if (mGrContext) {
         mRenderState->onContextDestroyed();
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 2c295bc..b8ce556 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -17,7 +17,7 @@
 #ifndef RENDERTHREAD_H_
 #define RENDERTHREAD_H_
 
-#include <GrContext.h>
+#include <GrDirectContext.h>
 #include <SkBitmap.h>
 #include <apex/choreographer.h>
 #include <cutils/compiler.h>
@@ -106,8 +106,8 @@
     ProfileDataContainer& globalProfileData() { return mGlobalProfileData; }
     Readback& readback();
 
-    GrContext* getGrContext() const { return mGrContext.get(); }
-    void setGrContext(sk_sp<GrContext> cxt);
+    GrDirectContext* getGrContext() const { return mGrContext.get(); }
+    void setGrContext(sk_sp<GrDirectContext> cxt);
 
     CacheManager& cacheManager() { return *mCacheManager; }
     VulkanManager& vulkanManager() { return *mVkManager; }
@@ -186,7 +186,7 @@
     ProfileDataContainer mGlobalProfileData;
     Readback* mReadback = nullptr;
 
-    sk_sp<GrContext> mGrContext;
+    sk_sp<GrDirectContext> mGrContext;
     CacheManager* mCacheManager;
     VulkanManager* mVkManager;
 };
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 3cb1607..249936e 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -20,7 +20,7 @@
 #include <EGL/eglext.h>
 #include <GrBackendSemaphore.h>
 #include <GrBackendSurface.h>
-#include <GrContext.h>
+#include <GrDirectContext.h>
 #include <GrTypes.h>
 #include <android/sync.h>
 #include <ui/FatVector.h>
@@ -369,7 +369,7 @@
     }
 }
 
-sk_sp<GrContext> VulkanManager::createContext(const GrContextOptions& options) {
+sk_sp<GrDirectContext> VulkanManager::createContext(const GrContextOptions& options) {
     auto getProc = [](const char* proc_name, VkInstance instance, VkDevice device) {
         if (device != VK_NULL_HANDLE) {
             return vkGetDeviceProcAddr(device, proc_name);
@@ -388,7 +388,7 @@
     backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2;
     backendContext.fGetProc = std::move(getProc);
 
-    return GrContext::MakeVulkan(backendContext, options);
+    return GrDirectContext::MakeVulkan(backendContext, options);
 }
 
 VkFunctorInitParams VulkanManager::getVkFunctorInitParams() const {
@@ -562,9 +562,11 @@
     delete surface;
 }
 
-VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode,
+VulkanSurface* VulkanManager::createSurface(ANativeWindow* window,
+                                            ColorMode colorMode,
                                             sk_sp<SkColorSpace> surfaceColorSpace,
-                                            SkColorType surfaceColorType, GrContext* grContext,
+                                            SkColorType surfaceColorType,
+                                            GrDirectContext* grContext,
                                             uint32_t extraBuffers) {
     LOG_ALWAYS_FATAL_IF(!hasVkContext(), "Not initialized");
     if (!window) {
@@ -575,7 +577,7 @@
                                  *this, extraBuffers);
 }
 
-status_t VulkanManager::fenceWait(int fence, GrContext* grContext) {
+status_t VulkanManager::fenceWait(int fence, GrDirectContext* grContext) {
     if (!hasVkContext()) {
         ALOGE("VulkanManager::fenceWait: VkDevice not initialized");
         return INVALID_OPERATION;
@@ -623,7 +625,7 @@
     return OK;
 }
 
-status_t VulkanManager::createReleaseFence(int* nativeFence, GrContext* grContext) {
+status_t VulkanManager::createReleaseFence(int* nativeFence, GrDirectContext* grContext) {
     *nativeFence = -1;
     if (!hasVkContext()) {
         ALOGE("VulkanManager::createReleaseFence: VkDevice not initialized");
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 8b19f13..3f2df8d 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -57,9 +57,11 @@
     bool hasVkContext() { return mDevice != VK_NULL_HANDLE; }
 
     // Create and destroy functions for wrapping an ANativeWindow in a VulkanSurface
-    VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode,
+    VulkanSurface* createSurface(ANativeWindow* window,
+                                 ColorMode colorMode,
                                  sk_sp<SkColorSpace> surfaceColorSpace,
-                                 SkColorType surfaceColorType, GrContext* grContext,
+                                 SkColorType surfaceColorType,
+                                 GrDirectContext* grContext,
                                  uint32_t extraBuffers);
     void destroySurface(VulkanSurface* surface);
 
@@ -70,18 +72,18 @@
     void destroy();
 
     // Inserts a wait on fence command into the Vulkan command buffer.
-    status_t fenceWait(int fence, GrContext* grContext);
+    status_t fenceWait(int fence, GrDirectContext* grContext);
 
     // Creates a fence that is signaled when all the pending Vulkan commands are finished on the
     // GPU.
-    status_t createReleaseFence(int* nativeFence, GrContext* grContext);
+    status_t createReleaseFence(int* nativeFence, GrDirectContext* grContext);
 
     // Returned pointers are owned by VulkanManager.
     // An instance of VkFunctorInitParams returned from getVkFunctorInitParams refers to
     // the internal state of VulkanManager: VulkanManager must be alive to use the returned value.
     VkFunctorInitParams getVkFunctorInitParams() const;
 
-    sk_sp<GrContext> createContext(const GrContextOptions& options);
+    sk_sp<GrDirectContext> createContext(const GrContextOptions& options);
 
     uint32_t getDriverVersion() const { return mDriverVersion; }
 
diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
index a4d7b82..edd3e4e 100644
--- a/libs/hwui/tests/unit/CacheManagerTests.cpp
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -26,7 +26,7 @@
 using namespace android::uirenderer;
 using namespace android::uirenderer::renderthread;
 
-static size_t getCacheUsage(GrContext* grContext) {
+static size_t getCacheUsage(GrDirectContext* grContext) {
     size_t cacheUsage;
     grContext->getResourceCacheUsage(nullptr, &cacheUsage);
     return cacheUsage;
@@ -35,7 +35,7 @@
 RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) {
     int32_t width = DeviceInfo::get()->getWidth();
     int32_t height = DeviceInfo::get()->getHeight();
-    GrContext* grContext = renderThread.getGrContext();
+    GrDirectContext* grContext = renderThread.getGrContext();
     ASSERT_TRUE(grContext != nullptr);
 
     // create pairs of offscreen render targets and images until we exceed the
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 1038069..5721fb7 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2951,25 +2951,26 @@
         if ((expecting == null) || (old == expecting)) {
             mProcessNames.remove(name, uid);
         }
-        if (old != null && old.uidRecord != null) {
-            old.uidRecord.numProcs--;
-            old.uidRecord.procRecords.remove(old);
-            if (old.uidRecord.numProcs == 0) {
+        final ProcessRecord record = expecting != null ? expecting : old;
+        if (record != null && record.uidRecord != null) {
+            final UidRecord uidRecord = record.uidRecord;
+            uidRecord.numProcs--;
+            uidRecord.procRecords.remove(record);
+            if (uidRecord.numProcs == 0) {
                 // No more processes using this uid, tell clients it is gone.
                 if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
-                        "No more processes in " + old.uidRecord);
-                mService.enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
+                        "No more processes in " + uidRecord);
+                mService.enqueueUidChangeLocked(uidRecord, -1, UidRecord.CHANGE_GONE);
                 EventLogTags.writeAmUidStopped(uid);
                 mActiveUids.remove(uid);
                 mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT,
                         ActivityManager.PROCESS_CAPABILITY_NONE);
             }
-            old.uidRecord = null;
+            record.uidRecord = null;
         }
         mIsolatedProcesses.remove(uid);
         mGlobalIsolatedUids.freeIsolatedUidLocked(uid);
         // Remove the (expected) ProcessRecord from the app zygote
-        final ProcessRecord record = expecting != null ? expecting : old;
         if (record != null && record.appZygote) {
             removeProcessFromAppZygoteLocked(record);
         }
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index 86ad0b3..e9868fd 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -59,7 +59,7 @@
     private static final String TAG = "NetworkStatsFactory";
 
     private static final boolean USE_NATIVE_PARSING = true;
-    private static final boolean SANITY_CHECK_NATIVE = false;
+    private static final boolean VALIDATE_NATIVE_STATS = false;
 
     /** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
     private final File mStatsXtIfaceAll;
@@ -347,7 +347,7 @@
                             INTERFACES_ALL, TAG_ALL, mUseBpfStats) != 0) {
                         throw new IOException("Failed to parse network stats");
                     }
-                    if (SANITY_CHECK_NATIVE) {
+                    if (VALIDATE_NATIVE_STATS) {
                         final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid,
                                 UID_ALL, INTERFACES_ALL, TAG_ALL);
                         assertEquals(javaStats, stats);
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
index 9eff5d1..ce74169 100644
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
@@ -227,7 +227,7 @@
         for (int i = 0; i < delta.size(); i++) {
             entry = delta.getValues(i, entry);
 
-            // As a last-ditch sanity check, report any negative values and
+            // As a last-ditch check, report any negative values and
             // clamp them so recording below doesn't croak.
             if (entry.isNegative()) {
                 if (mObserver != null) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 4c4680b..a2b46a0 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3208,6 +3208,7 @@
                 // If we're stuck in a really low-level reboot loop, and a
                 // rescue party is trying to prompt the user for a factory data
                 // reset, we must GET TO DA CHOPPA!
+                // No check point from ShutdownCheckPoints will be dumped at this state.
                 PowerManagerService.lowLevelReboot(reason);
             } else {
                 throw new IllegalStateException("Too early to call shutdown() or reboot()");
@@ -5114,6 +5115,7 @@
                 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
             }
 
+            ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
             final long ident = Binder.clearCallingIdentity();
             try {
                 shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait);
@@ -5132,10 +5134,11 @@
         public void rebootSafeMode(boolean confirm, boolean wait) {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
 
+            String reason = PowerManager.REBOOT_SAFE_MODE;
+            ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
             final long ident = Binder.clearCallingIdentity();
             try {
-                shutdownOrRebootInternal(HALT_MODE_REBOOT_SAFE_MODE, confirm,
-                        PowerManager.REBOOT_SAFE_MODE, wait);
+                shutdownOrRebootInternal(HALT_MODE_REBOOT_SAFE_MODE, confirm, reason, wait);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -5151,6 +5154,7 @@
         public void shutdown(boolean confirm, String reason, boolean wait) {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
 
+            ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
             final long ident = Binder.clearCallingIdentity();
             try {
                 shutdownOrRebootInternal(HALT_MODE_SHUTDOWN, confirm, reason, wait);
diff --git a/services/core/java/com/android/server/power/ShutdownCheckPoints.java b/services/core/java/com/android/server/power/ShutdownCheckPoints.java
index e6d0ddd..05ee7df 100644
--- a/services/core/java/com/android/server/power/ShutdownCheckPoints.java
+++ b/services/core/java/com/android/server/power/ShutdownCheckPoints.java
@@ -90,18 +90,19 @@
     }
 
     /** Records the stack trace of this {@link Thread} as a shutdown check point. */
-    public static void recordCheckPoint() {
-        INSTANCE.recordCheckPointInternal();
+    public static void recordCheckPoint(@Nullable String reason) {
+        INSTANCE.recordCheckPointInternal(reason);
     }
 
     /** Records the pid of the caller process as a shutdown check point. */
-    public static void recordCheckPoint(int callerProcessId) {
-        INSTANCE.recordCheckPointInternal(callerProcessId);
+    public static void recordCheckPoint(int callerProcessId, @Nullable String reason) {
+        INSTANCE.recordCheckPointInternal(callerProcessId, reason);
     }
 
     /** Records the {@link android.content.Intent} name and package as a shutdown check point. */
-    public static void recordCheckPoint(String intentName, String packageName) {
-        INSTANCE.recordCheckPointInternal(intentName, packageName);
+    public static void recordCheckPoint(
+            String intentName, String packageName, @Nullable String reason) {
+        INSTANCE.recordCheckPointInternal(intentName, packageName, reason);
     }
 
     /** Serializes the recorded check points and writes them to given {@code printWriter}. */
@@ -119,24 +120,24 @@
     }
 
     @VisibleForTesting
-    void recordCheckPointInternal() {
-        recordCheckPointInternal(new SystemServerCheckPoint(mInjector));
+    void recordCheckPointInternal(@Nullable String reason) {
+        recordCheckPointInternal(new SystemServerCheckPoint(mInjector, reason));
         Slog.v(TAG, "System server shutdown checkpoint recorded");
     }
 
     @VisibleForTesting
-    void recordCheckPointInternal(int callerProcessId) {
+    void recordCheckPointInternal(int callerProcessId, @Nullable String reason) {
         recordCheckPointInternal(callerProcessId == Process.myPid()
-                ? new SystemServerCheckPoint(mInjector)
-                : new BinderCheckPoint(mInjector, callerProcessId));
+                ? new SystemServerCheckPoint(mInjector, reason)
+                : new BinderCheckPoint(mInjector, callerProcessId, reason));
         Slog.v(TAG, "Binder shutdown checkpoint recorded with pid=" + callerProcessId);
     }
 
     @VisibleForTesting
-    void recordCheckPointInternal(String intentName, String packageName) {
+    void recordCheckPointInternal(String intentName, String packageName, @Nullable String reason) {
         recordCheckPointInternal("android".equals(packageName)
-                ? new SystemServerCheckPoint(mInjector)
-                : new IntentCheckPoint(mInjector, intentName, packageName));
+                ? new SystemServerCheckPoint(mInjector, reason)
+                : new IntentCheckPoint(mInjector, intentName, packageName, reason));
         Slog.v(TAG, String.format("Shutdown intent checkpoint recorded intent=%s from package=%s",
                 intentName, packageName));
     }
@@ -182,14 +183,20 @@
     private abstract static class CheckPoint {
 
         private final long mTimestamp;
+        @Nullable private final String mReason;
 
-        CheckPoint(Injector injector) {
+        CheckPoint(Injector injector, @Nullable String reason) {
             mTimestamp = injector.currentTimeMillis();
+            mReason = reason;
         }
 
         final void dump(PrintWriter printWriter) {
             printWriter.print("Shutdown request from ");
             printWriter.print(getOrigin());
+            if (mReason != null) {
+                printWriter.print(" for reason ");
+                printWriter.print(mReason);
+            }
             printWriter.print(" at ");
             printWriter.print(DATE_FORMAT.format(new Date(mTimestamp)));
             printWriter.println(" (epoch=" + mTimestamp + ")");
@@ -206,8 +213,8 @@
 
         private final StackTraceElement[] mStackTraceElements;
 
-        SystemServerCheckPoint(Injector injector) {
-            super(injector);
+        SystemServerCheckPoint(Injector injector, @Nullable String reason) {
+            super(injector, reason);
             mStackTraceElements = Thread.currentThread().getStackTrace();
         }
 
@@ -263,8 +270,8 @@
         private final int mCallerProcessId;
         private final IActivityManager mActivityManager;
 
-        BinderCheckPoint(Injector injector, int callerProcessId) {
-            super(injector);
+        BinderCheckPoint(Injector injector, int callerProcessId, @Nullable String reason) {
+            super(injector, reason);
             mCallerProcessId = callerProcessId;
             mActivityManager = injector.activityManager();
         }
@@ -307,8 +314,9 @@
         private final String mIntentName;
         private final String mPackageName;
 
-        IntentCheckPoint(Injector injector, String intentName, String packageName) {
-            super(injector);
+        IntentCheckPoint(
+                Injector injector, String intentName, String packageName, @Nullable String reason) {
+            super(injector, reason);
             mIntentName = intentName;
             mPackageName = packageName;
         }
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 2621d8b..e94575c 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -65,7 +65,7 @@
     private static final int RADIOS_STATE_POLL_SLEEP_MS = 100;
     // maximum time we wait for the shutdown broadcast before going on.
     private static final int MAX_BROADCAST_TIME = 10 * 1000;
-    private static final int MAX_CHECK_POINTS_DUMP_WAIT_TIME = 20 * 1000;
+    private static final int MAX_CHECK_POINTS_DUMP_WAIT_TIME = 10 * 1000;
     private static final int MAX_RADIO_WAIT_TIME = 12 * 1000;
     private static final int MAX_UNCRYPT_WAIT_TIME = 15 * 60 * 1000;
     // constants for progress bar. the values are roughly estimated based on timeout.
@@ -165,6 +165,10 @@
             }
         }
 
+        // Add checkpoint for this shutdown attempt. The user might still cancel the dialog, but
+        // this point preserves the system trace of the trigger point of the ShutdownThread.
+        ShutdownCheckPoints.recordCheckPoint(/* reason= */ null);
+
         final int longPressBehavior = context.getResources().getInteger(
                         com.android.internal.R.integer.config_longPressOnPowerBehavior);
         final int resourceId = mRebootSafeMode
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 93ea5e3..9bc8afa 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -67,6 +67,7 @@
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.notification.NotificationDelegate;
 import com.android.server.policy.GlobalActionsProvider;
+import com.android.server.power.ShutdownCheckPoints;
 import com.android.server.power.ShutdownThread;
 
 import java.io.FileDescriptor;
@@ -1182,13 +1183,14 @@
     @Override
     public void shutdown() {
         enforceStatusBarService();
+        String reason = PowerManager.SHUTDOWN_USER_REQUESTED;
+        ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
         long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.prepareForPossibleShutdown();
             // ShutdownThread displays UI, so give it a UI context.
             mHandler.post(() ->
-                    ShutdownThread.shutdown(getUiContext(),
-                        PowerManager.SHUTDOWN_USER_REQUESTED, false));
+                    ShutdownThread.shutdown(getUiContext(), reason, false));
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -1200,6 +1202,10 @@
     @Override
     public void reboot(boolean safeMode) {
         enforceStatusBarService();
+        String reason = safeMode
+                ? PowerManager.REBOOT_SAFE_MODE
+                : PowerManager.SHUTDOWN_USER_REQUESTED;
+        ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
         long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.prepareForPossibleShutdown();
@@ -1208,8 +1214,7 @@
                 if (safeMode) {
                     ShutdownThread.rebootSafeMode(getUiContext(), true);
                 } else {
-                    ShutdownThread.reboot(getUiContext(),
-                            PowerManager.SHUTDOWN_USER_REQUESTED, false);
+                    ShutdownThread.reboot(getUiContext(), reason, false);
                 }
             });
         } finally {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e84f040..f7cb014 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -118,6 +118,7 @@
 import com.android.internal.app.IVoiceInteractor;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.pm.InstantAppResolver;
+import com.android.server.power.ShutdownCheckPoints;
 import com.android.server.uri.NeededUriGrants;
 import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
 import com.android.server.wm.ActivityStackSupervisor.PendingActivityLaunch;
@@ -647,6 +648,19 @@
                 mRequest.resolveActivity(mSupervisor);
             }
 
+            // Add checkpoint for this shutdown or reboot attempt, so we can record the original
+            // intent action and package name.
+            if (mRequest.intent != null) {
+                String intentAction = mRequest.intent.getAction();
+                String callingPackage = mRequest.callingPackage;
+                if (intentAction != null && callingPackage != null
+                        && (Intent.ACTION_REQUEST_SHUTDOWN.equals(intentAction)
+                                || Intent.ACTION_SHUTDOWN.equals(intentAction)
+                                || Intent.ACTION_REBOOT.equals(intentAction))) {
+                    ShutdownCheckPoints.recordCheckPoint(intentAction, callingPackage, null);
+                }
+            }
+
             int res;
             synchronized (mService.mGlobalLock) {
                 final boolean globalConfigWillChange = mRequest.globalConfig != null
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 0d8e882..13033a6 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -93,9 +93,17 @@
     private final Rect mTmpRect = new Rect();
 
     static final int BOUNDS_CHANGE_NONE = 0;
-    // Return value from {@link setBounds} indicating the position of the override bounds changed.
+
+    /**
+     * Return value from {@link #setBounds(Rect)} indicating the position of the override bounds
+     * changed.
+     */
     static final int BOUNDS_CHANGE_POSITION = 1;
-    // Return value from {@link setBounds} indicating the size of the override bounds changed.
+
+    /**
+     * Return value from {@link #setBounds(Rect)} indicating the size of the override bounds
+     * changed.
+     */
     static final int BOUNDS_CHANGE_SIZE = 1 << 1;
 
     /**
@@ -226,6 +234,11 @@
         return equivalentBounds(getRequestedOverrideBounds(),  bounds);
     }
 
+    /** Similar to {@link #equivalentRequestedOverrideBounds(Rect)}, but compares max bounds. */
+    public boolean equivalentRequestedOverrideMaxBounds(Rect bounds) {
+        return equivalentBounds(getRequestedOverrideMaxBounds(),  bounds);
+    }
+
     /**
      * Returns whether the two bounds are equal to each other or are a combination of null or empty.
      */
@@ -238,7 +251,6 @@
     /**
      * Returns the effective bounds of this container, inheriting the first non-empty bounds set in
      * its ancestral hierarchy, including itself.
-     * @return
      */
     public Rect getBounds() {
         mReturnBounds.set(getConfiguration().windowConfiguration.getBounds());
@@ -249,6 +261,12 @@
         outBounds.set(getBounds());
     }
 
+    /** Similar to {@link #getBounds()}, but reports the max bounds. */
+    public Rect getMaxBounds() {
+        mReturnBounds.set(getConfiguration().windowConfiguration.getMaxBounds());
+        return mReturnBounds;
+    }
+
     /**
      * Sets {@code out} to the top-left corner of the bounds as returned by {@link #getBounds()}.
      */
@@ -273,6 +291,13 @@
         return mReturnBounds;
     }
 
+    /** Similar to {@link #getRequestedOverrideBounds()}, but returns the max bounds. */
+    public Rect getRequestedOverrideMaxBounds() {
+        mReturnBounds.set(getRequestedOverrideConfiguration().windowConfiguration.getMaxBounds());
+
+        return mReturnBounds;
+    }
+
     /**
      * Returns {@code true} if the {@link WindowConfiguration} in the requested override
      * {@link Configuration} specifies bounds.
@@ -283,7 +308,7 @@
 
     /**
      * Sets the passed in {@link Rect} to the current bounds.
-     * @see {@link #getRequestedOverrideBounds()}.
+     * @see #getRequestedOverrideBounds()
      */
     public void getRequestedOverrideBounds(Rect outBounds) {
         outBounds.set(getRequestedOverrideBounds());
@@ -295,19 +320,25 @@
      * {@link #getRequestedOverrideBounds()}. If
      * an empty {@link Rect} or null is specified, this container will be considered to match its
      * parent bounds {@see #matchParentBounds} and will inherit bounds from its parent.
+     *
      * @param bounds The bounds defining the container size.
+     *
      * @return a bitmask representing the types of changes made to the bounds.
      */
     public int setBounds(Rect bounds) {
         int boundsChange = diffRequestedOverrideBounds(bounds);
+        final boolean overrideMaxBounds = providesMaxBounds()
+                && diffRequestedOverrideMaxBounds(bounds) != BOUNDS_CHANGE_NONE;
 
-        if (boundsChange == BOUNDS_CHANGE_NONE) {
+        if (boundsChange == BOUNDS_CHANGE_NONE && !overrideMaxBounds) {
             return boundsChange;
         }
 
-
         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
         mRequestsTmpConfig.windowConfiguration.setBounds(bounds);
+        if (overrideMaxBounds) {
+            mRequestsTmpConfig.windowConfiguration.setMaxBounds(bounds);
+        }
         onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
 
         return boundsChange;
@@ -318,6 +349,40 @@
         return setBounds(mTmpRect);
     }
 
+    /**
+     * Returns {@code true} if this {@link ConfigurationContainer} provides the maximum bounds to
+     * its child {@link ConfigurationContainer}s. Returns {@code false}, otherwise.
+     * <p>
+     * The maximum bounds is how large a window can be expanded. Currently only
+     * {@link DisplayContent} and {@link DisplayArea} effect this property.
+     * </p>
+     */
+    protected boolean providesMaxBounds() {
+        return false;
+    }
+
+    int diffRequestedOverrideMaxBounds(Rect bounds) {
+        if (equivalentRequestedOverrideMaxBounds(bounds)) {
+            return BOUNDS_CHANGE_NONE;
+        }
+
+        int boundsChange = BOUNDS_CHANGE_NONE;
+
+        final Rect existingBounds = getRequestedOverrideMaxBounds();
+
+        if (bounds == null || existingBounds.left != bounds.left
+                || existingBounds.top != bounds.top) {
+            boundsChange |= BOUNDS_CHANGE_POSITION;
+        }
+
+        if (bounds == null || existingBounds.width() != bounds.width()
+                || existingBounds.height() != bounds.height()) {
+            boundsChange |= BOUNDS_CHANGE_SIZE;
+        }
+
+        return boundsChange;
+    }
+
     int diffRequestedOverrideBounds(Rect bounds) {
         if (equivalentRequestedOverrideBounds(bounds)) {
             return BOUNDS_CHANGE_NONE;
@@ -340,10 +405,6 @@
         return boundsChange;
     }
 
-    boolean hasOverrideConfiguration() {
-        return mHasOverrideConfiguration;
-    }
-
     public WindowConfiguration getWindowConfiguration() {
         return mFullConfiguration.windowConfiguration;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 546c5d4..6ffb482 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -25,6 +25,7 @@
 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOW_TOKENS;
 
 import static com.android.internal.util.Preconditions.checkState;
+import static com.android.server.wm.DisplayAreaProto.IS_TASK_DISPLAY_AREA;
 import static com.android.server.wm.DisplayAreaProto.NAME;
 import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
@@ -194,6 +195,7 @@
         final long token = proto.start(fieldId);
         super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
         proto.write(NAME, mName);
+        proto.write(IS_TASK_DISPLAY_AREA, isTaskDisplayArea());
         proto.end(token);
     }
 
@@ -349,6 +351,15 @@
         return info;
     }
 
+    @Override
+    public boolean providesMaxBounds() {
+        return true;
+    }
+
+    protected boolean isTaskDisplayArea() {
+        return false;
+    }
+
     /**
      * DisplayArea that contains WindowTokens, and orders them according to their type.
      */
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0b1f4d9e..77fc2283 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1930,6 +1930,7 @@
         final DisplayInfo displayInfo = updateDisplayAndOrientation(config.uiMode, config);
         calculateBounds(displayInfo, mTmpBounds);
         config.windowConfiguration.setBounds(mTmpBounds);
+        config.windowConfiguration.setMaxBounds(mTmpBounds);
         config.windowConfiguration.setWindowingMode(getWindowingMode());
         config.windowConfiguration.setDisplayWindowingMode(getWindowingMode());
 
@@ -2544,10 +2545,6 @@
         return mDisplayAreaPolicy.getDefaultTaskDisplayArea();
     }
 
-    void positionDisplayAt(int position, boolean includingParents) {
-        getParent().positionChildAt(position, this, includingParents);
-    }
-
     /**
      * Returns true if the input point is within an app window.
      */
@@ -5393,6 +5390,11 @@
         });
     }
 
+    @Override
+    public boolean providesMaxBounds() {
+        return true;
+    }
+
     /** The entry for proceeding to handle {@link #mFixedRotationLaunchingApp}. */
     class FixedRotationTransitionListener extends WindowManagerInternal.AppTransitionListener {
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 0e24fc8..4f6f75d 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -356,6 +356,8 @@
     private WindowState mFocusedWindow;
     private WindowState mLastFocusedWindow;
 
+    private WindowState mSystemUiControllingWindow;
+
     // The states of decor windows from the last layout. These are used to generate another display
     // layout in different bounds but with the same states.
     private boolean mLastNavVisible;
@@ -3436,6 +3438,7 @@
             }
         }
         final WindowState win = winCandidate;
+        mSystemUiControllingWindow = win;
 
         mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
 
@@ -3740,8 +3743,12 @@
                         & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
         final boolean hideStatusBarSysui =
                 (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
-        final boolean hideNavBarSysui =
-                (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
+        final boolean hideNavBarSysui = (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
+                // We shouldn't rely on the system UI visibilities anymore because the window can
+                // use the new API (e.g., WindowInsetsController.hide) to hide navigation bar.
+                // TODO(b/149813814): clean up the system UI flag usages in this function.
+                || !win.getRequestedInsetsState().getSourceOrDefaultVisibility(
+                        ITYPE_NAVIGATION_BAR);
 
         final boolean transientStatusBarAllowed = getStatusBar() != null
                 && (notificationShadeHasFocus || (!mForceShowSystemBars
@@ -3755,15 +3762,16 @@
                 && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
         final DisplayPolicy defaultDisplayPolicy =
                 mService.getDefaultDisplayContentLocked().getDisplayPolicy();
-        if (pendingPanic && hideNavBarSysui && !isKeyguardShowing()
+        if (pendingPanic && hideNavBarSysui && win != mNotificationShade
+                && getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)
                 // TODO (b/111955725): Show keyguard presentation on all external displays
                 && defaultDisplayPolicy.isKeyguardDrawComplete()) {
             // The user performed the panic gesture recently, we're about to hide the bars,
             // we're no longer on the Keyguard and the screen is ready. We can now request the bars.
             mPendingPanicGestureUptime = 0;
-            mStatusBarController.showTransient();
             if (!isNavBarEmpty(vis)) {
-                mNavigationBarController.showTransient();
+                mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap(
+                        new int[] {ITYPE_NAVIGATION_BAR}));
             }
         }
 
@@ -3908,6 +3916,9 @@
     }
 
     private boolean isImmersiveMode(WindowState win) {
+        if (win == null) {
+            return false;
+        }
         final int behavior = win.mAttrs.insetsFlags.behavior;
         return getNavigationBar() != null
                 && canHideNavigationBar()
@@ -3942,11 +3953,7 @@
                     return;
                 }
                 mPendingPanicGestureUptime = SystemClock.uptimeMillis();
-                if (!isNavBarEmpty(mLastSystemUiFlags)) {
-                    mNavigationBarController.showTransient();
-                    mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap(
-                            new int[] {ITYPE_NAVIGATION_BAR}));
-                }
+                updateSystemUiVisibilityLw();
             }
         }
     };
@@ -3955,7 +3962,7 @@
         // Detect user pressing the power button in panic when an application has
         // taken over the whole screen.
         boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(isScreenOn,
-                SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags),
+                SystemClock.elapsedRealtime(), isImmersiveMode(mSystemUiControllingWindow),
                 isNavBarEmpty(mLastSystemUiFlags));
         if (panic) {
             mHandler.post(mHiddenNavPanic);
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 246753a..a847744 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1294,7 +1294,13 @@
     }
 
     void onSplitScreenModeDismissed() {
-        onSplitScreenModeDismissed(null /* toTop */);
+        // The focused task could be a non-resizeable fullscreen root task that is on top of the
+        // other split-screen tasks, therefore had to dismiss split-screen, make sure the current
+        // focused root task can still be on top after dismissal
+        final Task rootTask = getFocusedStack();
+        final Task toTop =
+                rootTask != null && !rootTask.inSplitScreenWindowingMode() ? rootTask : null;
+        onSplitScreenModeDismissed(toTop);
     }
 
     void onSplitScreenModeDismissed(Task toTop) {
@@ -1838,6 +1844,10 @@
         return lastReparentedStack;
     }
 
+    @Override
+    protected boolean isTaskDisplayArea() {
+        return true;
+    }
 
     @Override
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2dd25c9..0b942ba 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2931,8 +2931,8 @@
         synchronized (mGlobalLock) {
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
             if (displayContent != null && mRoot.getTopChild() != displayContent) {
-                displayContent.positionDisplayAt(WindowContainer.POSITION_TOP,
-                        true /* includingParents */);
+                displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP,
+                        displayContent, true /* includingParents */);
             }
         }
         syncInputTransactions();
@@ -8017,7 +8017,7 @@
 
         final DisplayContent displayContent = touchedWindow.getDisplayContent();
         if (!displayContent.isOnTop()) {
-            displayContent.positionDisplayAt(WindowContainer.POSITION_TOP,
+            displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP, displayContent,
                     true /* includingParents */);
         }
         handleTaskFocusChange(touchedWindow.getTask());
diff --git a/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java b/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java
index a1f1056..2230ddd 100644
--- a/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java
@@ -72,21 +72,32 @@
     @Test
     public void testSystemServerEntry() {
         mTestInjector.setCurrentTime(1000);
-        mInstance.recordCheckPointInternal();
+        mInstance.recordCheckPointInternal("reason1");
 
         assertTrue(dumpToString().startsWith(
-                "Shutdown request from SYSTEM at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+                "Shutdown request from SYSTEM for reason reason1 "
+                        + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
                         + "com.android.server.power.ShutdownCheckPointsTest"
                         + ".testSystemServerEntry\n at "));
     }
 
     @Test
-    public void testSystemServiceBinderEntry() {
+    public void testSystemServerEntryWithoutReason() {
         mTestInjector.setCurrentTime(1000);
-        mInstance.recordCheckPointInternal(Process.myPid());
+        mInstance.recordCheckPointInternal(null);
 
         assertTrue(dumpToString().startsWith(
-                "Shutdown request from SYSTEM at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+                "Shutdown request from SYSTEM at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"));
+    }
+
+    @Test
+    public void testSystemServiceBinderEntry() {
+        mTestInjector.setCurrentTime(1000);
+        mInstance.recordCheckPointInternal(Process.myPid(), "reason1");
+
+        assertTrue(dumpToString().startsWith(
+                "Shutdown request from SYSTEM for reason reason1 "
+                        + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
                         + "com.android.server.power.ShutdownCheckPointsTest"
                         + ".testSystemServiceBinderEntry\n at "));
     }
@@ -99,10 +110,11 @@
         when(mActivityManager.getRunningAppProcesses()).thenReturn(runningAppProcessInfos);
 
         mTestInjector.setCurrentTime(1000);
-        mInstance.recordCheckPointInternal(1);
+        mInstance.recordCheckPointInternal(1, "reason1");
 
         assertEquals(
-                "Shutdown request from BINDER at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+                "Shutdown request from BINDER for reason reason1 "
+                        + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
                         + "com.android.server.power.ShutdownCheckPointsTest"
                         + ".testCallerProcessBinderEntry\n"
                         + "From process process_name (pid=1)\n\n",
@@ -114,10 +126,11 @@
         when(mActivityManager.getRunningAppProcesses()).thenThrow(new RemoteException("Error"));
 
         mTestInjector.setCurrentTime(1000);
-        mInstance.recordCheckPointInternal(1);
+        mInstance.recordCheckPointInternal(1, "reason1");
 
         assertEquals(
-                "Shutdown request from BINDER at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+                "Shutdown request from BINDER for reason reason1 "
+                        + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
                         + "com.android.server.power.ShutdownCheckPointsTest"
                         + ".testRemoteExceptionOnBinderEntry\n"
                         + "From process ? (pid=1)\n\n",
@@ -127,10 +140,11 @@
     @Test
     public void testUnknownProcessBinderEntry() {
         mTestInjector.setCurrentTime(1000);
-        mInstance.recordCheckPointInternal(1);
+        mInstance.recordCheckPointInternal(1, "reason1");
 
         assertEquals(
-                "Shutdown request from BINDER at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+                "Shutdown request from BINDER for reason reason1 "
+                        + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
                         + "com.android.server.power.ShutdownCheckPointsTest"
                         + ".testUnknownProcessBinderEntry\n"
                         + "From process ? (pid=1)\n\n",
@@ -138,12 +152,22 @@
     }
 
     @Test
-    public void testSystemServiceIntentEntry() {
+    public void testBinderEntryWithoutReason() throws RemoteException {
         mTestInjector.setCurrentTime(1000);
-        mInstance.recordCheckPointInternal("some.intent", "android");
+        mInstance.recordCheckPointInternal(1, null);
 
         assertTrue(dumpToString().startsWith(
-                "Shutdown request from SYSTEM at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+                "Shutdown request from BINDER at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"));
+    }
+
+    @Test
+    public void testSystemServiceIntentEntry() {
+        mTestInjector.setCurrentTime(1000);
+        mInstance.recordCheckPointInternal("some.intent", "android", "reason1");
+
+        assertTrue(dumpToString().startsWith(
+                "Shutdown request from SYSTEM for reason reason1 "
+                        + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
                         + "com.android.server.power.ShutdownCheckPointsTest"
                         + ".testSystemServiceIntentEntry\n at "));
     }
@@ -151,39 +175,47 @@
     @Test
     public void testIntentEntry() {
         mTestInjector.setCurrentTime(1000);
-        mInstance.recordCheckPointInternal("some.intent", "some.app");
+        mInstance.recordCheckPointInternal("some.intent", "some.app", "reason1");
 
         assertEquals(
-                "Shutdown request from INTENT at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+                "Shutdown request from INTENT for reason reason1 "
+                        + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
                         + "Intent: some.intent\n"
                         + "Package: some.app\n\n",
                 dumpToString());
     }
 
     @Test
+    public void testIntentEntryWithoutReason() {
+        mTestInjector.setCurrentTime(1000);
+        mInstance.recordCheckPointInternal("some.intent", "some.app", null);
+
+        assertTrue(dumpToString().startsWith(
+                "Shutdown request from INTENT at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"));
+    }
+
+    @Test
     public void testMultipleEntries() {
         mTestInjector.setCurrentTime(1000);
-        mInstance.recordCheckPointInternal(1);
+        mInstance.recordCheckPointInternal(1, "reason1");
         mTestInjector.setCurrentTime(2000);
-        mInstance.recordCheckPointInternal(2);
+        mInstance.recordCheckPointInternal(2, "reason2");
         mTestInjector.setCurrentTime(3000);
-        mInstance.recordCheckPointInternal("intent", "app");
+        mInstance.recordCheckPointInternal("intent", "app", "reason3");
 
         assertEquals(
-                "Shutdown request from BINDER at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+                "Shutdown request from BINDER for reason reason1 "
+                        + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
                         + "com.android.server.power.ShutdownCheckPointsTest.testMultipleEntries\n"
-                        + "From process ? (pid=1)\n"
-                        + "\n"
-                        + "Shutdown request from BINDER at 1970-01-01 00:00:02.000 UTC (epoch=2000)"
-                        + "\n"
+                        + "From process ? (pid=1)\n\n"
+                        + "Shutdown request from BINDER for reason reason2 "
+                        + "at 1970-01-01 00:00:02.000 UTC (epoch=2000)\n"
                         + "com.android.server.power.ShutdownCheckPointsTest.testMultipleEntries\n"
-                        + "From process ? (pid=2)\n"
-                        + "\n"
-                        + "Shutdown request from INTENT at 1970-01-01 00:00:03.000 UTC (epoch=3000)"
-                        + "\n"
+                        + "From process ? (pid=2)\n\n"
+                        + "Shutdown request from INTENT for reason reason3 "
+                        + "at 1970-01-01 00:00:03.000 UTC (epoch=3000)\n"
                         + "Intent: intent\n"
-                        + "Package: app\n"
-                        + "\n",
+                        + "Package: app\n\n",
                 dumpToString());
     }
 
@@ -193,23 +225,22 @@
         ShutdownCheckPoints limitedInstance = new ShutdownCheckPoints(mTestInjector);
 
         mTestInjector.setCurrentTime(1000);
-        limitedInstance.recordCheckPointInternal("intent.1", "app.1");
+        limitedInstance.recordCheckPointInternal("intent.1", "app.1", "reason1");
         mTestInjector.setCurrentTime(2000);
-        limitedInstance.recordCheckPointInternal("intent.2", "app.2");
+        limitedInstance.recordCheckPointInternal("intent.2", "app.2", "reason2");
         mTestInjector.setCurrentTime(3000);
-        limitedInstance.recordCheckPointInternal("intent.3", "app.3");
+        limitedInstance.recordCheckPointInternal("intent.3", "app.3", "reason3");
 
         // Drops first intent.
         assertEquals(
-                "Shutdown request from INTENT at 1970-01-01 00:00:02.000 UTC (epoch=2000)\n"
+                "Shutdown request from INTENT for reason reason2 "
+                        + "at 1970-01-01 00:00:02.000 UTC (epoch=2000)\n"
                         + "Intent: intent.2\n"
-                        + "Package: app.2\n"
-                        + "\n"
-                        + "Shutdown request from INTENT at 1970-01-01 00:00:03.000 UTC (epoch=3000)"
-                        + "\n"
+                        + "Package: app.2\n\n"
+                        + "Shutdown request from INTENT for reason reason3 "
+                        + "at 1970-01-01 00:00:03.000 UTC (epoch=3000)\n"
                         + "Intent: intent.3\n"
-                        + "Package: app.3\n"
-                        + "\n",
+                        + "Package: app.3\n\n",
                 dumpToString(limitedInstance));
     }
 
@@ -219,11 +250,11 @@
         File baseFile = new File(tempDir, "checkpoints");
 
         mTestInjector.setCurrentTime(1000);
-        mInstance.recordCheckPointInternal("first.intent", "first.app");
+        mInstance.recordCheckPointInternal("first.intent", "first.app", "reason1");
         dumpToFile(baseFile);
 
         mTestInjector.setCurrentTime(2000);
-        mInstance.recordCheckPointInternal("second.intent", "second.app");
+        mInstance.recordCheckPointInternal("second.intent", "second.app", "reason2");
         dumpToFile(baseFile);
 
         File[] dumpFiles = tempDir.listFiles();
@@ -231,16 +262,18 @@
 
         assertEquals(2, dumpFiles.length);
         assertEquals(
-                "Shutdown request from INTENT at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+                "Shutdown request from INTENT for reason reason1 "
+                        + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
                         + "Intent: first.intent\n"
                         + "Package: first.app\n\n",
                 readFileAsString(dumpFiles[0].getAbsolutePath()));
         assertEquals(
-                "Shutdown request from INTENT at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+                "Shutdown request from INTENT for reason reason1 "
+                        + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
                         + "Intent: first.intent\n"
                         + "Package: first.app\n\n"
-                        + "Shutdown request from INTENT at 1970-01-01 00:00:02.000 UTC (epoch=2000)"
-                        + "\n"
+                        + "Shutdown request from INTENT for reason reason2 "
+                        + "at 1970-01-01 00:00:02.000 UTC (epoch=2000)\n"
                         + "Intent: second.intent\n"
                         + "Package: second.app\n\n",
                 readFileAsString(dumpFiles[1].getAbsolutePath()));
@@ -254,21 +287,22 @@
         File baseFile = new File(tempDir, "checkpoints");
 
         mTestInjector.setCurrentTime(1000);
-        instance.recordCheckPointInternal("first.intent", "first.app");
+        instance.recordCheckPointInternal("first.intent", "first.app", "reason1");
         dumpToFile(instance, baseFile);
 
         mTestInjector.setCurrentTime(2000);
-        instance.recordCheckPointInternal("second.intent", "second.app");
+        instance.recordCheckPointInternal("second.intent", "second.app", "reason2");
         dumpToFile(instance, baseFile);
 
         File[] dumpFiles = tempDir.listFiles();
         assertEquals(1, dumpFiles.length);
         assertEquals(
-                "Shutdown request from INTENT at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+                "Shutdown request from INTENT for reason reason1 "
+                        + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
                         + "Intent: first.intent\n"
                         + "Package: first.app\n\n"
-                        + "Shutdown request from INTENT at 1970-01-01 00:00:02.000 UTC (epoch=2000)"
-                        + "\n"
+                        + "Shutdown request from INTENT for reason reason2 "
+                        + "at 1970-01-01 00:00:02.000 UTC (epoch=2000)\n"
                         + "Intent: second.intent\n"
                         + "Package: second.app\n\n",
                 readFileAsString(dumpFiles[0].getAbsolutePath()));
diff --git a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
index bcd93715..5828d02 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
@@ -33,6 +33,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
@@ -324,6 +325,47 @@
         assertEquals(100, listener.mOverrideConfiguration.smallestScreenWidthDp);
     }
 
+    @Test
+    public void testSetMaxBoundsByHierarchy() {
+        final TestConfigurationContainer root =
+                new TestConfigurationContainer(true /* providesMaxBounds */);
+        final Rect bounds = new Rect(0, 0, 10, 10);
+        final TestConfigurationContainer child = new TestConfigurationContainer();
+        root.addChild(child);
+
+        root.setBounds(bounds);
+
+        assertEquals(bounds, root.getBounds());
+        assertEquals(bounds, root.getConfiguration().windowConfiguration.getBounds());
+        assertEquals(bounds, child.getBounds());
+        assertEquals(bounds, child.getConfiguration().windowConfiguration.getBounds());
+
+        assertEquals(bounds, root.getMaxBounds());
+        assertEquals(bounds, root.getConfiguration().windowConfiguration.getMaxBounds());
+        assertEquals(bounds, child.getMaxBounds());
+        assertEquals(bounds, child.getConfiguration().windowConfiguration.getMaxBounds());
+    }
+
+    @Test
+    public void testSetBoundsNotOverrideMaxBounds() {
+        final TestConfigurationContainer root = new TestConfigurationContainer();
+        final Rect bounds = new Rect(0, 0, 10, 10);
+        final TestConfigurationContainer child = new TestConfigurationContainer();
+        root.addChild(child);
+
+        root.setBounds(bounds);
+
+        assertEquals(bounds, root.getBounds());
+        assertEquals(bounds, root.getConfiguration().windowConfiguration.getBounds());
+        assertEquals(bounds, child.getBounds());
+        assertEquals(bounds, child.getConfiguration().windowConfiguration.getBounds());
+
+        assertTrue(root.getMaxBounds().isEmpty());
+        assertTrue(root.getConfiguration().windowConfiguration.getMaxBounds().isEmpty());
+        assertTrue(child.getMaxBounds().isEmpty());
+        assertTrue(child.getConfiguration().windowConfiguration.getMaxBounds().isEmpty());
+    }
+
     /**
      * Contains minimal implementation of {@link ConfigurationContainer}'s abstract behavior needed
      * for testing.
@@ -333,6 +375,14 @@
         private List<TestConfigurationContainer> mChildren = new ArrayList<>();
         private TestConfigurationContainer mParent;
 
+        private boolean mProvidesMaxBounds = false;
+
+        TestConfigurationContainer() {}
+
+        TestConfigurationContainer(boolean providesMaxBounds) {
+            mProvidesMaxBounds = providesMaxBounds;
+        }
+
         TestConfigurationContainer addChild(TestConfigurationContainer childContainer) {
             final ConfigurationContainer oldParent = childContainer.getParent();
             childContainer.mParent = this;
@@ -369,6 +419,11 @@
         protected ConfigurationContainer getParent() {
             return mParent;
         }
+
+        @Override
+        public boolean providesMaxBounds() {
+            return mProvidesMaxBounds;
+        }
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index c8ed87d..e17601e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
@@ -40,8 +41,10 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 
+import android.graphics.Rect;
 import android.os.Binder;
 import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
 
 import com.google.android.collect.Lists;
 
@@ -63,7 +66,6 @@
  */
 @Presubmit
 public class DisplayAreaTest {
-
     @Rule
     public SystemServicesTestRule mWmsRule = new SystemServicesTestRule();
 
@@ -379,6 +381,42 @@
         assertThat(result).isEqualTo(tda1);
     }
 
+    @Test
+    public void testSetMaxBounds() {
+        final Rect parentBounds = new Rect(0, 0, 100, 100);
+        final Rect childBounds1 = new Rect(parentBounds.left, parentBounds.top,
+                parentBounds.right / 2, parentBounds.bottom);
+        final Rect childBounds2 = new Rect(parentBounds.right / 2, parentBounds.top,
+                parentBounds.right, parentBounds.bottom);
+        TestDisplayArea parentDa = new TestDisplayArea(mWms, parentBounds);
+        TestDisplayArea childDa1 = new TestDisplayArea(mWms, childBounds1);
+        TestDisplayArea childDa2 = new TestDisplayArea(mWms, childBounds2);
+        parentDa.addChild(childDa1, 0);
+        parentDa.addChild(childDa2, 1);
+
+        assertEquals(parentBounds, parentDa.getMaxBounds());
+        assertEquals(childBounds1, childDa1.getMaxBounds());
+        assertEquals(childBounds2, childDa2.getMaxBounds());
+
+        final WindowToken windowToken = createWindowToken(TYPE_APPLICATION);
+        childDa1.addChild(windowToken, 0);
+
+        assertEquals("DisplayArea's children must have the same max bounds as itself",
+                childBounds1, windowToken.getMaxBounds());
+    }
+
+    private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
+        private TestDisplayArea(WindowManagerService wms, Rect bounds) {
+            super(wms, ANY, "half display area");
+            setBounds(bounds);
+        }
+
+        @Override
+        SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+            return new MockSurfaceControlBuilder();
+        }
+    }
+
     private WindowToken createWindowToken(int type) {
         return new WindowToken(mWmsRule.getWindowManagerService(), new Binder(),
                 type, false /* persist */, null /* displayContent */,
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 5161fba..952997e 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -24,6 +24,7 @@
     test_suites: ["device-tests"],
     libs: ["android.test.runner"],
     static_libs: [
+        "androidx.test.ext.junit",
         "flickertestapplib",
         "flickerlib",
         "truth-prebuilt",
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
new file mode 100644
index 0000000..dcabce8
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2020 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.flicker
+
+import com.android.server.wm.flicker.dsl.LayersAssertion
+import com.android.server.wm.flicker.dsl.WmAssertion
+import com.android.server.wm.flicker.helpers.WindowUtils
+
+@JvmOverloads
+fun WmAssertion.statusBarWindowIsAlwaysVisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("statusBarWindowIsAlwaysVisible", enabled, bugId) {
+        this.showsAboveAppWindow(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
+    }
+}
+
+@JvmOverloads
+fun WmAssertion.navBarWindowIsAlwaysVisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("navBarWindowIsAlwaysVisible", enabled, bugId) {
+        this.showsAboveAppWindow(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertion.noUncoveredRegions(
+    beginRotation: Int,
+    endRotation: Int = beginRotation,
+    allStates: Boolean = true,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
+    val endingBounds = WindowUtils.getDisplayBounds(endRotation)
+    if (allStates) {
+        all("noUncoveredRegions", enabled, bugId) {
+            if (startingBounds == endingBounds) {
+                this.coversRegion(startingBounds)
+            } else {
+                this.coversRegion(startingBounds)
+                        .then()
+                        .coversRegion(endingBounds)
+            }
+        }
+    } else {
+        start("noUncoveredRegions_StartingPos") {
+            this.coversRegion(startingBounds)
+        }
+        end("noUncoveredRegions_EndingPos") {
+            this.coversRegion(endingBounds)
+        }
+    }
+}
+
+@JvmOverloads
+fun LayersAssertion.navBarLayerIsAlwaysVisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("navBarLayerIsAlwaysVisible", enabled, bugId) {
+        this.showsLayer(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertion.statusBarLayerIsAlwaysVisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("statusBarLayerIsAlwaysVisible", enabled, bugId) {
+        this.showsLayer(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertion.navBarLayerRotatesAndScales(
+    beginRotation: Int,
+    endRotation: Int = beginRotation,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    val startingPos = WindowUtils.getNavigationBarPosition(beginRotation)
+    val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
+
+    start("navBarLayerRotatesAndScales_StartingPos", enabled, bugId) {
+        this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+    }
+    end("navBarLayerRotatesAndScales_EndingPost", enabled, bugId) {
+        this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, endingPos)
+    }
+
+    if (startingPos == endingPos) {
+        all("navBarLayerRotatesAndScales", enabled, bugId) {
+            this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+        }
+    }
+}
+
+@JvmOverloads
+fun LayersAssertion.statusBarLayerRotatesScales(
+    beginRotation: Int,
+    endRotation: Int = beginRotation,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    val startingPos = WindowUtils.getStatusBarPosition(beginRotation)
+    val endingPos = WindowUtils.getStatusBarPosition(endRotation)
+
+    start("statusBarLayerRotatesScales_StartingPos", enabled, bugId) {
+        this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, startingPos)
+    }
+    end("statusBarLayerRotatesScales_EndingPos", enabled, bugId) {
+        this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, endingPos)
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.kt
deleted file mode 100644
index b69e6a9..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.kt
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
- * Copyright (C) 2020 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.flicker
-
-import android.app.Instrumentation
-import android.content.Context
-import android.content.Intent
-import android.os.RemoteException
-import android.os.SystemClock
-import android.platform.helpers.IAppHelper
-import android.util.Rational
-import android.view.Surface
-import androidx.test.uiautomator.By
-import androidx.test.uiautomator.UiDevice
-import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.helpers.AutomationUtils
-import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.helpers.PipAppHelper
-
-/**
- * Collection of common transitions which can be used to test different apps or scenarios.
- */
-internal object CommonTransitions {
-    private const val ITERATIONS = 1
-    private const val APP_LAUNCH_TIMEOUT: Long = 10000
-    private fun setRotation(device: UiDevice, rotation: Int) {
-        try {
-            when (rotation) {
-                Surface.ROTATION_270 -> device.setOrientationLeft()
-                Surface.ROTATION_90 -> device.setOrientationRight()
-                Surface.ROTATION_0 -> device.setOrientationNatural()
-                else -> device.setOrientationNatural()
-            }
-            // Wait for animation to complete
-            SystemClock.sleep(1000)
-        } catch (e: RemoteException) {
-            throw RuntimeException(e)
-        }
-    }
-
-    /**
-     * Build a test tag for the test
-     * @param testName Name of the transition(s) being tested
-     * @param app App being launcher
-     * @param rotation Initial screen rotation
-     *
-     * @return test tag with pattern <NAME>__<APP>__<ROTATION>
-    </ROTATION></APP></NAME> */
-    private fun buildTestTag(testName: String, app: IAppHelper, rotation: Int): String {
-        return buildTestTag(
-                testName, app, rotation, rotation, app2 = null, extraInfo = "")
-    }
-
-    /**
-     * Build a test tag for the test
-     * @param testName Name of the transition(s) being tested
-     * @param app App being launcher
-     * @param beginRotation Initial screen rotation
-     * @param endRotation End screen rotation (if any, otherwise use same as initial)
-     *
-     * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
-    </END_ROTATION></BEGIN_ROTATION></APP></NAME> */
-    private fun buildTestTag(
-        testName: String,
-        app: IAppHelper,
-        beginRotation: Int,
-        endRotation: Int
-    ): String {
-        return buildTestTag(
-                testName, app, beginRotation, endRotation, app2 = null, extraInfo = "")
-    }
-
-    /**
-     * Build a test tag for the test
-     * @param testName Name of the transition(s) being tested
-     * @param app App being launcher
-     * @param app2 Second app being launched (if any)
-     * @param beginRotation Initial screen rotation
-     * @param endRotation End screen rotation (if any, otherwise use same as initial)
-     * @param extraInfo Additional information to append to the tag
-     *
-     * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>]
-    </EXTRA></NAME> */
-    private fun buildTestTag(
-        testName: String,
-        app: IAppHelper,
-        beginRotation: Int,
-        endRotation: Int,
-        app2: IAppHelper?,
-        extraInfo: String
-    ): String {
-        val testTag = StringBuilder()
-        testTag.append(testName)
-                .append("__")
-                .append(app.launcherName)
-        if (app2 != null) {
-            testTag.append("-")
-                    .append(app2.launcherName)
-        }
-        testTag.append("__")
-                .append(Surface.rotationToString(beginRotation))
-        if (endRotation != beginRotation) {
-            testTag.append("-")
-                    .append(Surface.rotationToString(endRotation))
-        }
-        if (extraInfo.isNotEmpty()) {
-            testTag.append("__")
-                    .append(extraInfo)
-        }
-        return testTag.toString()
-    }
-
-    fun openAppWarm(
-        testApp: IAppHelper,
-        instrumentation: Instrumentation,
-        device: UiDevice,
-        beginRotation: Int
-    ): TransitionRunner.TransitionBuilder {
-        return TransitionRunner.TransitionBuilder(instrumentation)
-                .withTag(buildTestTag("openAppWarm", testApp, beginRotation))
-                .recordAllRuns()
-                .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
-                .runBeforeAll { setRotation(device, beginRotation) }
-                .runBeforeAll { testApp.open() }
-                .runBefore { device.pressHome() }
-                .runBefore { device.waitForIdle() }
-                .runBefore { setRotation(device, beginRotation) }
-                .run { testApp.open() }
-                .runAfterAll { testApp.exit() }
-                .runAfterAll { AutomationUtils.setDefaultWait() }
-                .repeat(ITERATIONS)
-    }
-
-    fun closeAppWithBackKey(
-        testApp: IAppHelper,
-        instrumentation: Instrumentation,
-        device: UiDevice,
-        beginRotation: Int
-    ): TransitionRunner.TransitionBuilder {
-        return TransitionRunner.TransitionBuilder(instrumentation)
-                .withTag(buildTestTag("closeAppWithBackKey", testApp, beginRotation))
-                .recordAllRuns()
-                .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
-                .runBefore { testApp.open() }
-                .runBefore { device.waitForIdle() }
-                .run { device.pressBack() }
-                .run { device.waitForIdle() }
-                .runAfterAll { testApp.exit() }
-                .runAfterAll { AutomationUtils.setDefaultWait() }
-                .repeat(ITERATIONS)
-    }
-
-    fun closeAppWithHomeKey(
-        testApp: IAppHelper,
-        instrumentation: Instrumentation,
-        device: UiDevice,
-        beginRotation: Int
-    ): TransitionRunner.TransitionBuilder {
-        return TransitionRunner.TransitionBuilder(instrumentation)
-                .withTag(buildTestTag("closeAppWithHomeKey", testApp, beginRotation))
-                .recordAllRuns()
-                .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
-                .runBefore { testApp.open() }
-                .runBefore { device.waitForIdle() }
-                .run { device.pressHome() }
-                .run { device.waitForIdle() }
-                .runAfterAll { testApp.exit() }
-                .runAfterAll { AutomationUtils.setDefaultWait() }
-                .repeat(ITERATIONS)
-    }
-
-    fun openAppCold(
-        testApp: IAppHelper,
-        instrumentation: Instrumentation,
-        device: UiDevice,
-        beginRotation: Int
-    ): TransitionRunner.TransitionBuilder {
-        return TransitionRunner.TransitionBuilder(instrumentation)
-                .withTag(buildTestTag("openAppCold", testApp, beginRotation))
-                .recordAllRuns()
-                .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
-                .runBefore { device.pressHome() }
-                .runBeforeAll { setRotation(device, beginRotation) }
-                .runBefore { testApp.exit() }
-                .runBefore { device.waitForIdle() }
-                .run { testApp.open() }
-                .runAfterAll { testApp.exit() }
-                .runAfterAll { setRotation(device, Surface.ROTATION_0) }
-                .repeat(ITERATIONS)
-    }
-
-    fun changeAppRotation(
-        testApp: IAppHelper,
-        instrumentation: Instrumentation,
-        device: UiDevice,
-        beginRotation: Int,
-        endRotation: Int
-    ): TransitionRunner.TransitionBuilder {
-        return TransitionRunner.TransitionBuilder(instrumentation)
-                .withTag(buildTestTag("changeAppRotation", testApp, beginRotation, endRotation))
-                .recordAllRuns()
-                .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
-                .runBeforeAll { testApp.open() }
-                .runBefore { setRotation(device, beginRotation) }
-                .run { setRotation(device, endRotation) }
-                .runAfterAll { testApp.exit() }
-                .runAfterAll { setRotation(device, Surface.ROTATION_0) }
-                .repeat(ITERATIONS)
-    }
-
-    fun changeAppRotation(
-        intent: Intent,
-        intentId: String,
-        context: Context,
-        instrumentation: Instrumentation,
-        device: UiDevice,
-        beginRotation: Int,
-        endRotation: Int
-    ): TransitionRunner.TransitionBuilder {
-        val testTag = "changeAppRotation_" + intentId + "_" +
-                Surface.rotationToString(beginRotation) + "_" +
-                Surface.rotationToString(endRotation)
-        return TransitionRunner.TransitionBuilder(instrumentation)
-                .withTag(testTag)
-                .recordAllRuns()
-                .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
-                .runBeforeAll {
-                    context.startActivity(intent)
-                    device.wait(Until.hasObject(By.pkg(intent.component?.packageName)
-                            .depth(0)), APP_LAUNCH_TIMEOUT)
-                }
-                .runBefore { setRotation(device, beginRotation) }
-                .run { setRotation(device, endRotation) }
-                .runAfterAll { AutomationUtils.stopPackage(context, intent.component?.packageName) }
-                .runAfterAll { setRotation(device, Surface.ROTATION_0) }
-                .repeat(ITERATIONS)
-    }
-
-    fun appToSplitScreen(
-        testApp: IAppHelper,
-        instrumentation: Instrumentation,
-        device: UiDevice,
-        beginRotation: Int
-    ): TransitionRunner.TransitionBuilder {
-        return TransitionRunner.TransitionBuilder(instrumentation)
-                .withTag(buildTestTag("appToSplitScreen", testApp, beginRotation))
-                .recordAllRuns()
-                .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
-                .runBeforeAll { setRotation(device, beginRotation) }
-                .runBefore { testApp.open() }
-                .runBefore { device.waitForIdle() }
-                .runBefore { SystemClock.sleep(500) }
-                .run { AutomationUtils.launchSplitScreen(device) }
-                .runAfter { AutomationUtils.exitSplitScreen(device) }
-                .runAfterAll { testApp.exit() }
-                .repeat(ITERATIONS)
-    }
-
-    fun splitScreenToLauncher(
-        testApp: IAppHelper,
-        instrumentation: Instrumentation,
-        device: UiDevice,
-        beginRotation: Int
-    ): TransitionRunner.TransitionBuilder {
-        return TransitionRunner.TransitionBuilder(instrumentation)
-                .withTag(buildTestTag("splitScreenToLauncher", testApp, beginRotation))
-                .recordAllRuns()
-                .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
-                .runBefore { testApp.open() }
-                .runBefore { device.waitForIdle() }
-                .runBefore { AutomationUtils.launchSplitScreen(device) }
-                .run { AutomationUtils.exitSplitScreen(device) }
-                .runAfterAll { testApp.exit() }
-                .repeat(ITERATIONS)
-    }
-
-    fun editTextSetFocus(
-        testApp: ImeAppHelper,
-        instrumentation: Instrumentation,
-        device: UiDevice,
-        beginRotation: Int
-    ): TransitionRunner.TransitionBuilder {
-        return TransitionRunner.TransitionBuilder(instrumentation)
-                .withTag(buildTestTag("editTextSetFocus", testApp, beginRotation))
-                .recordAllRuns()
-                .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
-                .runBefore { device.pressHome() }
-                .runBefore { setRotation(device, beginRotation) }
-                .runBefore { testApp.open() }
-                .run { testApp.openIME(device) }
-                .runAfterAll { testApp.exit() }
-                .repeat(ITERATIONS)
-    }
-
-    fun resizeSplitScreen(
-        testAppTop: IAppHelper,
-        testAppBottom: ImeAppHelper,
-        instrumentation: Instrumentation,
-        device: UiDevice,
-        beginRotation: Int,
-        startRatio: Rational,
-        stopRatio: Rational
-    ): TransitionRunner.TransitionBuilder {
-        val description = (startRatio.toString().replace("/", "-") + "_to_" +
-                stopRatio.toString().replace("/", "-"))
-        val testTag = buildTestTag("resizeSplitScreen", testAppTop, beginRotation,
-                beginRotation, testAppBottom, description)
-        return TransitionRunner.TransitionBuilder(instrumentation)
-                .withTag(testTag)
-                .recordAllRuns()
-                .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
-                .runBeforeAll { setRotation(device, beginRotation) }
-                .runBeforeAll { AutomationUtils.clearRecents(instrumentation) }
-                .runBefore { testAppBottom.open() }
-                .runBefore { device.pressHome() }
-                .runBefore { testAppTop.open() }
-                .runBefore { device.waitForIdle() }
-                .runBefore { AutomationUtils.launchSplitScreen(device) }
-                .runBefore {
-                    val snapshot = device.findObject(
-                            By.res(device.launcherPackageName, "snapshot"))
-                    snapshot.click()
-                }
-                .runBefore { testAppBottom.openIME(device) }
-                .runBefore { device.pressBack() }
-                .runBefore { AutomationUtils.resizeSplitScreen(device, startRatio) }
-                .run { AutomationUtils.resizeSplitScreen(device, stopRatio) }
-                .runAfter { AutomationUtils.exitSplitScreen(device) }
-                .runAfter { device.pressHome() }
-                .runAfterAll { testAppTop.exit() }
-                .runAfterAll { testAppBottom.exit() }
-                .repeat(ITERATIONS)
-    }
-
-    fun editTextLoseFocusToHome(
-        testApp: ImeAppHelper,
-        instrumentation: Instrumentation,
-        device: UiDevice,
-        beginRotation: Int
-    ): TransitionRunner.TransitionBuilder {
-        return TransitionRunner.TransitionBuilder(instrumentation)
-                .withTag(buildTestTag("editTextLoseFocusToHome", testApp, beginRotation))
-                .recordAllRuns()
-                .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
-                .runBefore { device.pressHome() }
-                .runBefore { setRotation(device, beginRotation) }
-                .runBefore { testApp.open() }
-                .runBefore { testApp.openIME(device) }
-                .run { device.pressHome() }
-                .run { device.waitForIdle() }
-                .runAfterAll { testApp.exit() }
-                .repeat(ITERATIONS)
-    }
-
-    fun editTextLoseFocusToApp(
-        testApp: ImeAppHelper,
-        instrumentation: Instrumentation,
-        device: UiDevice,
-        beginRotation: Int
-    ): TransitionRunner.TransitionBuilder {
-        return TransitionRunner.TransitionBuilder(instrumentation)
-                .withTag(buildTestTag("editTextLoseFocusToApp", testApp, beginRotation))
-                .recordAllRuns()
-                .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
-                .runBefore { device.pressHome() }
-                .runBefore { setRotation(device, beginRotation) }
-                .runBefore { testApp.open() }
-                .runBefore { testApp.openIME(device) }
-                .run { device.pressBack() }
-                .run { device.waitForIdle() }
-                .runAfterAll { testApp.exit() }
-                .repeat(ITERATIONS)
-    }
-
-    fun enterPipMode(
-        testApp: PipAppHelper,
-        instrumentation: Instrumentation,
-        device: UiDevice,
-        beginRotation: Int
-    ): TransitionRunner.TransitionBuilder {
-        return TransitionRunner.TransitionBuilder(instrumentation)
-                .withTag(buildTestTag("enterPipMode", testApp, beginRotation))
-                .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
-                .runBefore { device.pressHome() }
-                .runBefore { setRotation(device, beginRotation) }
-                .runBefore { testApp.open() }
-                .run { testApp.clickEnterPipButton(device) }
-                .runAfter { testApp.closePipWindow(device) }
-                .runAfterAll { testApp.exit() }
-                .repeat(ITERATIONS)
-    }
-
-    fun exitPipModeToHome(
-        testApp: PipAppHelper,
-        instrumentation: Instrumentation,
-        device: UiDevice,
-        beginRotation: Int
-    ): TransitionRunner.TransitionBuilder {
-        return TransitionRunner.TransitionBuilder(instrumentation)
-                .withTag(buildTestTag("exitPipModeToHome", testApp, beginRotation))
-                .recordAllRuns()
-                .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
-                .runBefore { device.pressHome() }
-                .runBefore { setRotation(device, beginRotation) }
-                .runBefore { testApp.open() }
-                .run { testApp.clickEnterPipButton(device) }
-                .run { testApp.closePipWindow(device) }
-                .run { device.waitForIdle() }
-                .run { testApp.exit() }
-                .repeat(ITERATIONS)
-    }
-
-    fun exitPipModeToApp(
-        testApp: PipAppHelper,
-        instrumentation: Instrumentation,
-        device: UiDevice,
-        beginRotation: Int
-    ): TransitionRunner.TransitionBuilder {
-        return TransitionRunner.TransitionBuilder(instrumentation)
-                .withTag(buildTestTag("exitPipModeToApp", testApp, beginRotation))
-                .recordAllRuns()
-                .runBeforeAll { AutomationUtils.wakeUpAndGoToHomeScreen() }
-                .run { device.pressHome() }
-                .run { setRotation(device, beginRotation) }
-                .run { testApp.open() }
-                .run { testApp.clickEnterPipButton(device) }
-                .run { AutomationUtils.expandPipWindow(device) }
-                .run { device.waitForIdle() }
-                .run { testApp.exit() }
-                .repeat(ITERATIONS)
-    }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.kt
deleted file mode 100644
index 43cfdff..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.kt
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2020 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.flicker
-
-import android.platform.helpers.IAppHelper
-import android.util.Rational
-import android.view.Surface
-import androidx.test.InstrumentationRegistry
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.LargeTest
-import androidx.test.runner.AndroidJUnit4
-import androidx.test.uiautomator.UiDevice
-import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.helpers.PipAppHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-
-/**
- * Tests to help debug individual transitions, capture video recordings and create test cases.
- *
- * Not actual tests
- */
-@LargeTest
-@FlakyTest
-@RunWith(AndroidJUnit4::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class DebugTest {
-    private val instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val testApp: IAppHelper = StandardAppHelper(instrumentation,
-            "com.android.server.wm.flicker.testapp", "SimpleApp")
-    private val uiDevice = UiDevice.getInstance(instrumentation)
-
-    /**
-     * atest FlickerTests:DebugTest#openAppCold
-     */
-    @Test
-    fun openAppCold() {
-        CommonTransitions.openAppCold(testApp, instrumentation, uiDevice, Surface.ROTATION_0)
-                .recordAllRuns().build().run()
-    }
-
-    /**
-     * atest FlickerTests:DebugTest#openAppWarm
-     */
-    @Test
-    fun openAppWarm() {
-        CommonTransitions.openAppWarm(testApp, instrumentation, uiDevice, Surface.ROTATION_0)
-                .recordAllRuns().build().run()
-    }
-
-    /**
-     * atest FlickerTests:DebugTest#changeOrientationFromNaturalToLeft
-     */
-    @Test
-    fun changeOrientationFromNaturalToLeft() {
-        CommonTransitions.changeAppRotation(testApp, instrumentation, uiDevice, Surface.ROTATION_0,
-                Surface.ROTATION_270).recordAllRuns().build().run()
-    }
-
-    /**
-     * atest FlickerTests:DebugTest#closeAppWithBackKey
-     */
-    @Test
-    fun closeAppWithBackKey() {
-        CommonTransitions.closeAppWithBackKey(testApp, instrumentation, uiDevice,
-                Surface.ROTATION_0).recordAllRuns().build().run()
-    }
-
-    /**
-     * atest FlickerTests:DebugTest#closeAppWithHomeKey
-     */
-    @Test
-    fun closeAppWithHomeKey() {
-        CommonTransitions.closeAppWithHomeKey(testApp, instrumentation, uiDevice,
-                Surface.ROTATION_0).recordAllRuns().build().run()
-    }
-
-    /**
-     * atest FlickerTests:DebugTest#openAppToSplitScreen
-     */
-    @Test
-    fun openAppToSplitScreen() {
-        CommonTransitions.appToSplitScreen(testApp, instrumentation, uiDevice,
-                Surface.ROTATION_0).includeJankyRuns().recordAllRuns()
-                .build().run()
-    }
-
-    /**
-     * atest FlickerTests:DebugTest#splitScreenToLauncher
-     */
-    @Test
-    fun splitScreenToLauncher() {
-        CommonTransitions.splitScreenToLauncher(testApp, instrumentation, uiDevice,
-                Surface.ROTATION_0).includeJankyRuns().recordAllRuns().build().run()
-    }
-
-    /**
-     * atest FlickerTests:DebugTest#resizeSplitScreen
-     */
-    @Test
-    fun resizeSplitScreen() {
-        val bottomApp = ImeAppHelper(instrumentation)
-        CommonTransitions.resizeSplitScreen(
-                testApp,
-                bottomApp,
-                instrumentation,
-                uiDevice,
-                Surface.ROTATION_0,
-                Rational(1, 3), Rational(2, 3)
-        ).includeJankyRuns().build().run()
-    }
-    // IME tests
-    /**
-     * atest FlickerTests:DebugTest#editTextSetFocus
-     */
-    @Test
-    fun editTextSetFocus() {
-        val testApp = ImeAppHelper(instrumentation)
-        CommonTransitions.editTextSetFocus(testApp, instrumentation, uiDevice, Surface.ROTATION_0)
-                .includeJankyRuns()
-                .build().run()
-    }
-
-    /**
-     * atest FlickerTests:DebugTest#editTextLoseFocusToHome
-     */
-    @Test
-    fun editTextLoseFocusToHome() {
-        val testApp = ImeAppHelper(instrumentation)
-        CommonTransitions.editTextLoseFocusToHome(testApp, instrumentation, uiDevice,
-                Surface.ROTATION_0).includeJankyRuns().build().run()
-    }
-
-    /**
-     * atest FlickerTests:DebugTest#editTextLoseFocusToApp
-     */
-    @Test
-    fun editTextLoseFocusToApp() {
-        val testApp = ImeAppHelper(instrumentation)
-        CommonTransitions.editTextLoseFocusToHome(testApp, instrumentation, uiDevice,
-                Surface.ROTATION_0).includeJankyRuns().build().run()
-    }
-    // PIP tests
-    /**
-     * atest FlickerTests:DebugTest#enterPipMode
-     */
-    @Test
-    fun enterPipMode() {
-        val testApp = PipAppHelper(instrumentation)
-        CommonTransitions.enterPipMode(testApp, instrumentation, uiDevice, Surface.ROTATION_0)
-                .includeJankyRuns().build().run()
-    }
-
-    /**
-     * atest FlickerTests:DebugTest#exitPipModeToHome
-     */
-    @Test
-    fun exitPipModeToHome() {
-        val testApp = PipAppHelper(instrumentation)
-        CommonTransitions.exitPipModeToHome(testApp, instrumentation, uiDevice, Surface.ROTATION_0)
-                .includeJankyRuns()
-                .build().run()
-    }
-
-    /**
-     * atest FlickerTests:DebugTest#exitPipModeToApp
-     */
-    @Test
-    fun exitPipModeToApp() {
-        val testApp = PipAppHelper(instrumentation)
-        CommonTransitions.exitPipModeToApp(testApp, instrumentation, uiDevice, Surface.ROTATION_0)
-                .includeJankyRuns().build().run()
-    }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt
index d7586d0..eaf4d87 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt
@@ -16,15 +16,12 @@
 
 package com.android.server.wm.flicker
 
+import android.os.RemoteException
+import android.os.SystemClock
 import android.platform.helpers.IAppHelper
-import android.util.Log
-import androidx.test.InstrumentationRegistry
+import android.view.Surface
+import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
-import com.android.server.wm.flicker.helpers.AutomationUtils
-import com.google.common.truth.Truth
-import org.junit.After
-import org.junit.AfterClass
-import org.junit.Before
 
 /**
  * Base class of all Flicker test that performs common functions for all flicker tests:
@@ -37,101 +34,96 @@
  * - Fails tests if results are not available for any test due to jank.
  */
 abstract class FlickerTestBase {
-    lateinit var testApp: IAppHelper
-    open val instrumentation by lazy {
+    val instrumentation by lazy {
         InstrumentationRegistry.getInstrumentation()
     }
     val uiDevice by lazy {
         UiDevice.getInstance(instrumentation)
     }
-    lateinit var tesults: List<TransitionResult>
-    private var lastResult: TransitionResult? = null
 
     /**
-     * Runs a transition, returns a cached result if the transition has run before.
-     */
-    fun run(transition: TransitionRunner) {
-        if (transitionResults.containsKey(transition.testTag)) {
-            tesults = transitionResults[transition.testTag]
-                    ?: throw IllegalStateException("Results do not contain test tag " +
-                            transition.testTag)
-            return
+     * Build a test tag for the test
+     * @param testName Name of the transition(s) being tested
+     * @param app App being launcher
+     * @param rotation Initial screen rotation
+     *
+     * @return test tag with pattern <NAME>__<APP>__<ROTATION>
+    </ROTATION></APP></NAME> */
+    protected fun buildTestTag(testName: String, app: IAppHelper, rotation: Int): String {
+        return buildTestTag(
+                testName, app, rotation, rotation, app2 = null, extraInfo = "")
+    }
+
+    /**
+     * Build a test tag for the test
+     * @param testName Name of the transition(s) being tested
+     * @param app App being launcher
+     * @param beginRotation Initial screen rotation
+     * @param endRotation End screen rotation (if any, otherwise use same as initial)
+     *
+     * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
+    </END_ROTATION></BEGIN_ROTATION></APP></NAME> */
+    protected fun buildTestTag(
+        testName: String,
+        app: IAppHelper,
+        beginRotation: Int,
+        endRotation: Int
+    ): String {
+        return buildTestTag(
+                testName, app, beginRotation, endRotation, app2 = null, extraInfo = "")
+    }
+
+    /**
+     * Build a test tag for the test
+     * @param testName Name of the transition(s) being tested
+     * @param app App being launcher
+     * @param app2 Second app being launched (if any)
+     * @param beginRotation Initial screen rotation
+     * @param endRotation End screen rotation (if any, otherwise use same as initial)
+     * @param extraInfo Additional information to append to the tag
+     *
+     * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>]
+    </EXTRA></NAME> */
+    protected fun buildTestTag(
+        testName: String,
+        app: IAppHelper,
+        beginRotation: Int,
+        endRotation: Int,
+        app2: IAppHelper?,
+        extraInfo: String
+    ): String {
+        var testTag = "${testName}__$${app.launcherName}"
+        if (app2 != null) {
+            testTag += "-${app2.launcherName}"
         }
-        tesults = transition.run().results
-        /* Fail if we don't have any results due to jank */
-        Truth.assertWithMessage("No results to test because all transition runs were invalid " +
-                "because of Jank").that(tesults).isNotEmpty()
-        transitionResults[transition.testTag] = tesults
-    }
-
-    /**
-     * Runs a transition, returns a cached result if the transition has run before.
-     */
-    @Before
-    fun runTransition() {
-        run(transitionToRun)
-    }
-
-    /**
-     * Gets the transition that will be executed
-     */
-    abstract val transitionToRun: TransitionRunner
-
-    /**
-     * Goes through a list of transition results and checks assertions on each result.
-     */
-    fun checkResults(assertion: (TransitionResult) -> Unit) {
-        for (result in tesults) {
-            lastResult = result
-            assertion(result)
+        testTag += "__${Surface.rotationToString(beginRotation)}"
+        if (endRotation != beginRotation) {
+            testTag += "-${Surface.rotationToString(endRotation)}"
         }
-        lastResult = null
+        if (extraInfo.isNotEmpty()) {
+            testTag += "__$extraInfo"
+        }
+        return testTag
     }
 
-    /**
-     * Kludge to mark a file for saving. If `checkResults` fails, the last result is not
-     * cleared. This indicates the assertion failed for the result, so mark it for saving.
-     */
-    @After
-    fun markArtifactsForSaving() {
-        lastResult?.flagForSaving()
+    protected fun Flicker.setRotation(rotation: Int) {
+        try {
+            when (rotation) {
+                Surface.ROTATION_270 -> device.setOrientationLeft()
+                Surface.ROTATION_90 -> device.setOrientationRight()
+                Surface.ROTATION_0 -> device.setOrientationNatural()
+                else -> device.setOrientationNatural()
+            }
+            // Wait for animation to complete
+            SystemClock.sleep(1000)
+        } catch (e: RemoteException) {
+            throw RuntimeException(e)
+        }
     }
 
     companion object {
-        const val TAG = "FLICKER"
         const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"
         const val STATUS_BAR_WINDOW_TITLE = "StatusBar"
         const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
-        private val transitionResults = mutableMapOf<String, List<TransitionResult>>()
-
-        /**
-         * Teardown any system settings and clean up test artifacts from the file system.
-         *
-         * Note: test artifacts for failed tests will remain on the device.
-         */
-        @AfterClass
-        @JvmStatic
-        fun teardown() {
-            AutomationUtils.setDefaultWait()
-            transitionResults.values
-                .flatten()
-                .forEach {
-                    if (it.canDelete()) {
-                        it.delete()
-                    } else {
-                        if (it.layersTraceExists()) {
-                            Log.e(TAG, "Layers trace saved to ${it.layersTracePath}")
-                        }
-                        if (it.windowManagerTraceExists()) {
-                            Log.e(TAG,
-                                    "WindowManager trace saved to ${it.windowManagerTracePath}")
-                        }
-                        if (it.screenCaptureVideoExists()) {
-                            Log.e(TAG,
-                                    "Screen capture video saved to ${it.screenCaptureVideoPath()}")
-                        }
-                    }
-                }
-        }
     }
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt
index 1f8150c..e7d1f8e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt
@@ -17,52 +17,20 @@
 package com.android.server.wm.flicker
 
 import android.view.Surface
-import androidx.test.filters.FlakyTest
-import org.junit.Test
 import org.junit.runners.Parameterized
 
 abstract class NonRotationTestBase(
-    beginRotationName: String,
-    protected val beginRotation: Int
+    protected val rotationName: String,
+    protected val rotation: Int
 ) : FlickerTestBase() {
-    @FlakyTest(bugId = 141361128)
-    @Test
-    fun checkCoveredRegion_noUncoveredRegions() {
-        val displayBounds = WindowUtils.getDisplayBounds(beginRotation)
-        checkResults {
-            LayersTraceSubject.assertThat(it).coversRegion(
-                    displayBounds).forAllEntries()
-        }
-    }
-
-    @FlakyTest(bugId = 141361128)
-    @Test
-    fun checkVisibility_navBarLayerIsAlwaysVisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries()
-        }
-    }
-
-    @FlakyTest(bugId = 141361128)
-    @Test
-    fun checkVisibility_statusBarLayerIsAlwaysVisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries()
-        }
-    }
-
     companion object {
+        const val SCREENSHOT_LAYER = "RotationLayer"
+
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<Array<Any>> {
             val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
-            val params: MutableCollection<Array<Any>> = ArrayList()
-            for (begin in supportedRotations) {
-                params.add(arrayOf(Surface.rotationToString(begin), begin))
-            }
-            return params
+            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt
index dfc3c07..3b67727 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt
@@ -17,8 +17,6 @@
 package com.android.server.wm.flicker
 
 import android.view.Surface
-import androidx.test.filters.FlakyTest
-import org.junit.Test
 import org.junit.runners.Parameterized
 
 abstract class RotationTestBase(
@@ -27,82 +25,7 @@
     protected val beginRotation: Int,
     protected val endRotation: Int
 ) : FlickerTestBase() {
-    @FlakyTest(bugId = 140855415)
-    @Test
-    fun checkVisibility_navBarWindowIsAlwaysVisible() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries()
-        }
-    }
-
-    @FlakyTest(bugId = 140855415)
-    @Test
-    fun checkVisibility_statusBarWindowIsAlwaysVisible() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries()
-        }
-    }
-
-    @Test
-    fun checkPosition_navBarLayerRotatesAndScales() {
-        val startingPos = WindowUtils.getNavigationBarPosition(beginRotation)
-        val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
-        if (startingPos == endingPos) {
-            checkResults {
-                LayersTraceSubject.assertThat(it)
-                        .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
-                        .forAllEntries()
-            }
-        } else {
-            checkResults {
-                LayersTraceSubject.assertThat(it)
-                        .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
-                        .inTheBeginning()
-            }
-            checkResults {
-                LayersTraceSubject.assertThat(it)
-                        .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos)
-                        .atTheEnd()
-            }
-        }
-    }
-
-    @Test
-    fun checkPosition_statusBarLayerRotatesScales() {
-        val startingPos = WindowUtils.getStatusBarPosition(beginRotation)
-        val endingPos = WindowUtils.getStatusBarPosition(endRotation)
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, startingPos)
-                    .inTheBeginning()
-            LayersTraceSubject.assertThat(it)
-                    .hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, endingPos).atTheEnd()
-        }
-    }
-
-    @FlakyTest(bugId = 140855415)
-    @Test
-    fun checkVisibility_navBarLayerIsAlwaysVisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries()
-        }
-    }
-
-    @FlakyTest(bugId = 140855415)
-    @Test
-    fun checkVisibility_statusBarLayerIsAlwaysVisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries()
-        }
-    }
-
     companion object {
-        const val SCREENSHOT_LAYER = "RotationLayer"
-
         @Parameterized.Parameters(name = "{0}-{1}")
         @JvmStatic
         fun getParams(): Collection<Array<Any>> {
@@ -123,4 +46,4 @@
             return params
         }
     }
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.kt
index e579533..7147577 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.kt
@@ -17,14 +17,15 @@
 package com.android.server.wm.flicker.helpers
 
 import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
 import com.android.server.wm.flicker.StandardAppHelper
 
 abstract class FlickerAppHelper(
     instr: Instrumentation,
-    launcherName: String
-) : StandardAppHelper(instr, sFlickerPackage, launcherName) {
+    launcherName: String,
+    launcherStrategy: ILauncherStrategy
+) : StandardAppHelper(instr, sFlickerPackage, launcherName, launcherStrategy) {
     companion object {
-        var sFindTimeout = 10000
         var sFlickerPackage = "com.android.server.wm.flicker.testapp"
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index 979cbea..c1b7657 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -17,6 +17,8 @@
 package com.android.server.wm.flicker.helpers
 
 import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.UiDevice
 import androidx.test.uiautomator.Until
@@ -24,17 +26,26 @@
 
 open class ImeAppHelper(
     instr: Instrumentation,
-    launcherName: String = "ImeApp"
-) : FlickerAppHelper(instr, launcherName) {
+    launcherName: String = "ImeApp",
+    launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
+            .getInstance(instr)
+            .launcherStrategy
+) : FlickerAppHelper(instr, launcherName, launcherStrategy) {
     open fun openIME(device: UiDevice) {
         val editText = device.wait(
                 Until.findObject(By.res(getPackage(), "plain_text_input")),
-                AutomationUtils.FIND_TIMEOUT)
+                FIND_TIMEOUT)
         Assert.assertNotNull("Text field not found, this usually happens when the device " +
                 "was left in an unknown state (e.g. in split screen)", editText)
         editText.click()
-        if (!AutomationUtils.waitForIME(device)) {
+        if (!device.waitForIME()) {
             Assert.fail("IME did not appear")
         }
     }
+
+    open fun closeIME(device: UiDevice) {
+        device.pressBack()
+        // Using only the AccessibilityInfo it is not possible to identify if the IME is active
+        device.waitForIdle(1000)
+    }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index daee810..d10bb1e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -17,20 +17,27 @@
 package com.android.server.wm.flicker.helpers
 
 import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.UiDevice
 import org.junit.Assert
 
-class PipAppHelper(instr: Instrumentation) : FlickerAppHelper(instr, "PipApp") {
+class PipAppHelper(
+    instr: Instrumentation,
+    launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
+            .getInstance(instr)
+            .launcherStrategy
+) : FlickerAppHelper(instr, "PipApp", launcherStrategy) {
     fun clickEnterPipButton(device: UiDevice) {
         val enterPipButton = device.findObject(By.res(getPackage(), "enter_pip"))
         Assert.assertNotNull("Pip button not found, this usually happens when the device " +
                 "was left in an unknown state (e.g. in split screen)", enterPipButton)
         enterPipButton.click()
-        AutomationUtils.hasPipWindow(device)
+        device.hasPipWindow()
     }
 
     fun closePipWindow(device: UiDevice) {
-        AutomationUtils.closePipWindow(device)
+        device.closePipWindow()
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 814cdcf..80d0394 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -16,11 +16,19 @@
 
 package com.android.server.wm.flicker.ime
 
-import androidx.test.filters.FlakyTest
+import android.view.Surface
 import androidx.test.filters.LargeTest
-import com.android.server.wm.flicker.CommonTransitions
-import com.android.server.wm.flicker.TransitionRunner
+import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -35,33 +43,52 @@
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class CloseImeAutoOpenWindowToAppTest(
-    beginRotationName: String,
-    beginRotation: Int
-) : CloseImeWindowToAppTest(beginRotationName, beginRotation) {
-    init {
-        testApp = ImeAppAutoFocusHelper(instrumentation)
-    }
+    rotationName: String,
+    rotation: Int
+) : CloseImeWindowToAppTest(rotationName, rotation) {
+    override val testApp: ImeAppHelper
+        get() = ImeAppAutoFocusHelper(instrumentation)
 
-    override val transitionToRun: TransitionRunner
-        get() = CommonTransitions.editTextLoseFocusToApp(testApp as ImeAppAutoFocusHelper,
-                instrumentation, uiDevice, beginRotation)
-                .includeJankyRuns().build()
-
-    @FlakyTest(bugId = 141458352)
     @Test
-    override fun checkVisibility_imeLayerBecomesInvisible() {
-        super.checkVisibility_imeLayerBecomesInvisible()
-    }
+    override fun test() {
+        flicker(instrumentation) {
+            withTag { buildTestTag("imeToAppAutoOpen", testApp, rotation) }
+            repeat { 1 }
+            setup {
+                eachRun {
+                    device.wakeUpAndGoToHomeScreen()
+                    this.setRotation(rotation)
+                    testApp.open()
+                    testApp.openIME(device)
+                }
+            }
+            teardown {
+                eachRun {
+                    testApp.exit()
+                    this.setRotation(Surface.ROTATION_0)
+                }
+            }
+            transitions {
+                device.pressBack()
+                device.waitForIdle()
+            }
+            assertions {
+                windowManagerTrace {
+                    navBarWindowIsAlwaysVisible()
+                    statusBarWindowIsAlwaysVisible()
+                    imeAppWindowIsAlwaysVisible(testApp, bugId = 141458352)
+                }
 
-    @FlakyTest(bugId = 141458352)
-    @Test
-    override fun checkVisibility_imeAppLayerIsAlwaysVisible() {
-        super.checkVisibility_imeAppLayerIsAlwaysVisible()
-    }
-
-    @FlakyTest(bugId = 141458352)
-    @Test
-    override fun checkVisibility_imeAppWindowIsAlwaysVisible() {
-        super.checkVisibility_imeAppWindowIsAlwaysVisible()
+                layersTrace {
+                    navBarLayerIsAlwaysVisible()
+                    statusBarLayerIsAlwaysVisible()
+                    noUncoveredRegions(rotation)
+                    navBarLayerRotatesAndScales(rotation)
+                    statusBarLayerRotatesScales(rotation)
+                    imeLayerBecomesInvisible(bugId = 141458352)
+                    imeAppLayerIsAlwaysVisible(testApp, bugId = 141458352)
+                }
+            }
+        }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index c2025b6..31d1fd3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -16,11 +16,19 @@
 
 package com.android.server.wm.flicker.ime
 
-import androidx.test.filters.FlakyTest
+import android.view.Surface
 import androidx.test.filters.LargeTest
-import com.android.server.wm.flicker.CommonTransitions
-import com.android.server.wm.flicker.TransitionRunner
+import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -35,33 +43,53 @@
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class CloseImeAutoOpenWindowToHomeTest(
-    beginRotationName: String,
-    beginRotation: Int
-) : CloseImeWindowToHomeTest(beginRotationName, beginRotation) {
-    init {
-        testApp = ImeAppAutoFocusHelper(instrumentation)
-    }
+    rotationName: String,
+    rotation: Int
+) : CloseImeWindowToHomeTest(rotationName, rotation) {
+    override val testApp: ImeAppHelper
+        get() = ImeAppAutoFocusHelper(instrumentation)
 
-    override val transitionToRun: TransitionRunner
-        get() = CommonTransitions.editTextLoseFocusToHome(testApp as ImeAppAutoFocusHelper,
-                instrumentation, uiDevice, beginRotation)
-                .includeJankyRuns().build()
-
-    @FlakyTest(bugId = 141458352)
     @Test
-    override fun checkVisibility_imeWindowBecomesInvisible() {
-        super.checkVisibility_imeWindowBecomesInvisible()
-    }
+    override fun test() {
+        flicker(instrumentation) {
+            withTag { buildTestTag("imeToHomeAutoOpen", testApp, rotation) }
+            repeat { 1 }
+            setup {
+                eachRun {
+                    device.wakeUpAndGoToHomeScreen()
+                    this.setRotation(rotation)
+                    testApp.open()
+                    testApp.openIME(device)
+                }
+            }
+            teardown {
+                eachRun {
+                    testApp.exit()
+                    this.setRotation(Surface.ROTATION_0)
+                }
+            }
+            transitions {
+                device.pressHome()
+                device.waitForIdle()
+            }
+            assertions {
+                windowManagerTrace {
+                    navBarWindowIsAlwaysVisible()
+                    statusBarWindowIsAlwaysVisible()
+                    imeWindowBecomesInvisible(bugId = 141458352)
+                    imeAppWindowBecomesInvisible(testApp, bugId = 157449248)
+                }
 
-    @FlakyTest(bugId = 141458352)
-    @Test
-    override fun checkVisibility_imeLayerBecomesInvisible() {
-        super.checkVisibility_imeLayerBecomesInvisible()
-    }
-
-    @FlakyTest(bugId = 157449248)
-    @Test
-    override fun checkVisibility_imeAppWindowBecomesInvisible() {
-        super.checkVisibility_imeAppWindowBecomesInvisible()
+                layersTrace {
+                    navBarLayerIsAlwaysVisible()
+                    statusBarLayerIsAlwaysVisible()
+                    noUncoveredRegions(rotation)
+                    navBarLayerRotatesAndScales(rotation)
+                    statusBarLayerRotatesScales(rotation)
+                    imeLayerBecomesInvisible(bugId = 141458352)
+                    imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
+                }
+            }
+        }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index b38262e..67c46d3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -16,14 +16,19 @@
 
 package com.android.server.wm.flicker.ime
 
-import androidx.test.filters.FlakyTest
+import android.view.Surface
 import androidx.test.filters.LargeTest
-import com.android.server.wm.flicker.CommonTransitions
-import com.android.server.wm.flicker.LayersTraceSubject
 import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.TransitionRunner
-import com.android.server.wm.flicker.WmTraceSubject
+import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -38,49 +43,51 @@
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 open class CloseImeWindowToAppTest(
-    beginRotationName: String,
-    beginRotation: Int
-) : NonRotationTestBase(beginRotationName, beginRotation) {
-    init {
-        testApp = ImeAppHelper(instrumentation)
-    }
-
-    override val transitionToRun: TransitionRunner
-        get() = CommonTransitions.editTextLoseFocusToApp(testApp as ImeAppHelper,
-                instrumentation, uiDevice, beginRotation)
-                .includeJankyRuns().build()
-
-    @FlakyTest
-    @Test
-    open fun checkVisibility_imeLayerBecomesInvisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .showsLayer(IME_WINDOW_TITLE)
-                    .then()
-                    .hidesLayer(IME_WINDOW_TITLE)
-                    .forAllEntries()
-        }
-    }
+    rotationName: String,
+    rotation: Int
+) : NonRotationTestBase(rotationName, rotation) {
+    open val testApp = ImeAppHelper(instrumentation)
 
     @Test
-    open fun checkVisibility_imeAppLayerIsAlwaysVisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .showsLayer(testApp.getPackage())
-                    .forAllEntries()
+    open fun test() {
+        flicker(instrumentation) {
+            withTag { buildTestTag("imeToApp", testApp, rotation) }
+            repeat { 1 }
+            setup {
+                eachRun {
+                    device.wakeUpAndGoToHomeScreen()
+                    this.setRotation(rotation)
+                    testApp.open()
+                    testApp.openIME(device)
+                }
+            }
+            teardown {
+                eachRun {
+                    testApp.exit()
+                    this.setRotation(Surface.ROTATION_0)
+                }
+            }
+            transitions {
+                device.pressBack()
+                device.waitForIdle()
+            }
+            assertions {
+                windowManagerTrace {
+                    navBarWindowIsAlwaysVisible()
+                    statusBarWindowIsAlwaysVisible()
+                    imeAppWindowIsAlwaysVisible(testApp)
+                }
+
+                layersTrace {
+                    navBarLayerIsAlwaysVisible()
+                    statusBarLayerIsAlwaysVisible()
+                    noUncoveredRegions(rotation)
+                    navBarLayerRotatesAndScales(rotation)
+                    statusBarLayerRotatesScales(rotation)
+                    imeLayerBecomesInvisible(enabled = false)
+                    imeAppLayerIsAlwaysVisible(testApp)
+                }
+            }
         }
     }
-
-    @Test
-    open fun checkVisibility_imeAppWindowIsAlwaysVisible() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsAppWindowOnTop(testApp.getPackage())
-                    .forAllEntries()
-        }
-    }
-
-    companion object {
-        const val IME_WINDOW_TITLE = "InputMethod"
-    }
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index ca04bab..b643ec2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -16,20 +16,26 @@
 
 package com.android.server.wm.flicker.ime
 
-import androidx.test.filters.FlakyTest
+import android.view.Surface
 import androidx.test.filters.LargeTest
-import com.android.server.wm.flicker.CommonTransitions
-import com.android.server.wm.flicker.LayersTraceSubject
 import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.TransitionRunner
-import com.android.server.wm.flicker.WmTraceSubject
+import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.openQuickstep
+import com.android.server.wm.flicker.helpers.reopenAppFromOverview
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
-
 /**
  * Test IME window closing to home transitions.
  * To run this test: `atest FlickerTests:CloseImeWindowToHomeTest`
@@ -38,67 +44,60 @@
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 open class CloseImeWindowToHomeTest(
-    beginRotationName: String,
-    beginRotation: Int
-) : NonRotationTestBase(beginRotationName, beginRotation) {
-    init {
-        testApp = ImeAppHelper(instrumentation)
-    }
-
-    override val transitionToRun: TransitionRunner
-        get() = CommonTransitions.editTextLoseFocusToHome(testApp as ImeAppHelper,
-                instrumentation, uiDevice, beginRotation)
-                .includeJankyRuns().build()
+    rotationName: String,
+    rotation: Int
+) : NonRotationTestBase(rotationName, rotation) {
+    open val testApp = ImeAppHelper(instrumentation)
 
     @Test
-    open fun checkVisibility_imeWindowBecomesInvisible() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsNonAppWindow(IME_WINDOW_TITLE)
-                    .then()
-                    .hidesNonAppWindow(IME_WINDOW_TITLE)
-                    .forAllEntries()
+    open fun test() {
+        flicker(instrumentation) {
+            withTag { buildTestTag("imeToHome", testApp, rotation) }
+            repeat { 1 }
+            setup {
+                test {
+                    device.wakeUpAndGoToHomeScreen()
+                    this.setRotation(rotation)
+                    testApp.open()
+                }
+                eachRun {
+                    device.openQuickstep()
+                    device.reopenAppFromOverview()
+                    this.setRotation(rotation)
+                    testApp.openIME(device)
+                }
+            }
+            transitions {
+                device.pressHome()
+                device.waitForIdle()
+            }
+            teardown {
+                eachRun {
+                    device.pressHome()
+                }
+                test {
+                    testApp.exit()
+                    this.setRotation(Surface.ROTATION_0)
+                }
+            }
+            assertions {
+                windowManagerTrace {
+                    navBarWindowIsAlwaysVisible()
+                    statusBarWindowIsAlwaysVisible()
+                    imeWindowBecomesInvisible()
+                    imeAppWindowBecomesInvisible(testApp)
+                }
+
+                layersTrace {
+                    navBarLayerIsAlwaysVisible()
+                    statusBarLayerIsAlwaysVisible()
+                    noUncoveredRegions(rotation)
+                    navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0)
+                    statusBarLayerRotatesScales(rotation, Surface.ROTATION_0)
+                    imeLayerBecomesInvisible(bugId = 153739621)
+                    imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
+                }
+            }
         }
     }
-
-    @FlakyTest(bugId = 153739621)
-    @Test
-    open fun checkVisibility_imeLayerBecomesInvisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .skipUntilFirstAssertion()
-                    .showsLayer(IME_WINDOW_TITLE)
-                    .then()
-                    .hidesLayer(IME_WINDOW_TITLE)
-                    .forAllEntries()
-        }
-    }
-
-    @FlakyTest(bugId = 153739621)
-    @Test
-    fun checkVisibility_imeAppLayerBecomesInvisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .skipUntilFirstAssertion()
-                    .showsLayer(testApp.getPackage())
-                    .then()
-                    .hidesLayer(testApp.getPackage())
-                    .forAllEntries()
-        }
-    }
-
-    @Test
-    open fun checkVisibility_imeAppWindowBecomesInvisible() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsAppWindowOnTop(testApp.getPackage())
-                    .then()
-                    .appWindowNotOnTop(testApp.getPackage())
-                    .forAllEntries()
-        }
-    }
-
-    companion object {
-        const val IME_WINDOW_TITLE: String = "InputMethod"
-    }
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ImeAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ImeAssertions.kt
new file mode 100644
index 0000000..b2be54f
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ImeAssertions.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 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.flicker.ime
+
+import android.platform.helpers.IAppHelper
+import com.android.server.wm.flicker.dsl.LayersAssertion
+import com.android.server.wm.flicker.dsl.WmAssertion
+
+const val IME_WINDOW_TITLE = "InputMethod"
+
+@JvmOverloads
+fun LayersAssertion.imeLayerBecomesVisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("imeLayerBecomesVisible", enabled, bugId) {
+        this.hidesLayer(IME_WINDOW_TITLE)
+                .then()
+                .showsLayer(IME_WINDOW_TITLE)
+    }
+}
+
+fun LayersAssertion.imeLayerBecomesInvisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("imeLayerBecomesInvisible", enabled, bugId) {
+        this.showsLayer(IME_WINDOW_TITLE)
+                .then()
+                .hidesLayer(IME_WINDOW_TITLE)
+    }
+}
+
+fun LayersAssertion.imeAppLayerIsAlwaysVisible(
+    testApp: IAppHelper,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("imeAppLayerIsAlwaysVisible", enabled, bugId) {
+        this.showsLayer(testApp.getPackage())
+    }
+}
+
+fun WmAssertion.imeAppWindowIsAlwaysVisible(
+    testApp: IAppHelper,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("imeAppWindowIsAlwaysVisible", enabled, bugId) {
+        this.showsAppWindowOnTop(testApp.getPackage())
+    }
+}
+
+fun WmAssertion.imeWindowBecomesInvisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("imeWindowBecomesInvisible", enabled, bugId) {
+        this.showsNonAppWindow(IME_WINDOW_TITLE)
+                .then()
+                .hidesNonAppWindow(IME_WINDOW_TITLE)
+    }
+}
+
+fun WmAssertion.imeAppWindowBecomesInvisible(
+    testApp: IAppHelper,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("imeAppWindowBecomesInvisible", enabled, bugId) {
+        this.showsAppWindowOnTop(testApp.getPackage())
+                .then()
+                .appWindowNotOnTop(testApp.getPackage())
+    }
+}
+
+fun LayersAssertion.imeAppLayerBecomesInvisible(
+    testApp: IAppHelper,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("imeAppLayerBecomesInvisible", enabled, bugId) {
+        this.skipUntilFirstAssertion()
+                .showsLayer(testApp.getPackage())
+                .then()
+                .hidesLayer(testApp.getPackage())
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index c7731f3..5874a07 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -16,13 +16,19 @@
 
 package com.android.server.wm.flicker.ime
 
+import android.view.Surface
 import androidx.test.filters.LargeTest
-import com.android.server.wm.flicker.CommonTransitions
-import com.android.server.wm.flicker.LayersTraceSubject
 import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.TransitionRunner
-import com.android.server.wm.flicker.WmTraceSubject
+import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -37,38 +43,58 @@
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class OpenImeWindowTest(
-    beginRotationName: String,
-    beginRotation: Int
-) : NonRotationTestBase(beginRotationName, beginRotation) {
-    init {
-        testApp = ImeAppHelper(instrumentation)
-    }
-
-    override val transitionToRun: TransitionRunner
-        get() = CommonTransitions.editTextSetFocus(testApp as ImeAppHelper,
-                instrumentation, uiDevice, beginRotation)
-                .includeJankyRuns().build()
-
+    rotationName: String,
+    rotation: Int
+) : NonRotationTestBase(rotationName, rotation) {
     @Test
-    fun checkVisibility_imeWindowBecomesVisible() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .skipUntilFirstAssertion()
-                    .hidesNonAppWindow(IME_WINDOW_TITLE)
-                    .then()
-                    .showsNonAppWindow(IME_WINDOW_TITLE)
-                    .forAllEntries()
-        }
-    }
+    fun test() {
+        val testApp = ImeAppHelper(instrumentation)
 
-    @Test
-    fun checkVisibility_imeLayerBecomesVisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .hidesLayer(IME_WINDOW_TITLE)
-                    .then()
-                    .showsLayer(IME_WINDOW_TITLE)
-                    .forAllEntries()
+        flicker(instrumentation) {
+            withTag { buildTestTag("openIme", testApp, rotation) }
+            repeat { 1 }
+            setup {
+                test {
+                    device.wakeUpAndGoToHomeScreen()
+                    this.setRotation(rotation)
+                    testApp.open()
+                }
+            }
+            transitions {
+                testApp.openIME(device)
+            }
+            teardown {
+                eachRun {
+                    testApp.closeIME(device)
+                }
+                test {
+                    testApp.exit()
+                    this.setRotation(Surface.ROTATION_0)
+                }
+            }
+            assertions {
+                windowManagerTrace {
+                    navBarWindowIsAlwaysVisible()
+                    statusBarWindowIsAlwaysVisible()
+
+                    all("imeWindowBecomesVisible") {
+                        this.skipUntilFirstAssertion()
+                            .hidesNonAppWindow(IME_WINDOW_TITLE)
+                            .then()
+                            .showsNonAppWindow(IME_WINDOW_TITLE)
+                    }
+                }
+
+                layersTrace {
+                    navBarLayerIsAlwaysVisible()
+                    statusBarLayerIsAlwaysVisible()
+                    noUncoveredRegions(rotation)
+                    navBarLayerRotatesAndScales(rotation)
+                    statusBarLayerRotatesScales(rotation)
+
+                    imeLayerBecomesVisible()
+                }
+            }
         }
     }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 88b8854..1240e0d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -16,14 +16,17 @@
 
 package com.android.server.wm.flicker.launch
 
-import androidx.test.filters.FlakyTest
+import android.view.Surface
 import androidx.test.filters.LargeTest
-import com.android.server.wm.flicker.CommonTransitions
-import com.android.server.wm.flicker.LayersTraceSubject
-import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.StandardAppHelper
-import com.android.server.wm.flicker.TransitionRunner
-import com.android.server.wm.flicker.WmTraceSubject
+import com.android.server.wm.flicker.dsl.flicker
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -38,50 +41,49 @@
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class OpenAppColdTest(
-    beginRotationName: String,
-    beginRotation: Int
-) : NonRotationTestBase(beginRotationName, beginRotation) {
-    init {
-        testApp = StandardAppHelper(instrumentation,
-                "com.android.server.wm.flicker.testapp", "SimpleApp")
-    }
-
-    override val transitionToRun: TransitionRunner
-        get() = CommonTransitions.openAppCold(testApp, instrumentation, uiDevice, beginRotation)
-                .includeJankyRuns().build()
-
+    rotationName: String,
+    rotation: Int
+) : OpenAppTestBase(rotationName, rotation) {
     @Test
-    fun checkVisibility_wallpaperWindowBecomesInvisible() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsBelowAppWindow("Wallpaper")
-                    .then()
-                    .hidesBelowAppWindow("Wallpaper")
-                    .forAllEntries()
-        }
-    }
+    fun test() {
+        flicker(instrumentation) {
+            withTag { buildTestTag("openAppCold", testApp, rotation) }
+            repeat { 1 }
+            setup {
+                test {
+                    device.wakeUpAndGoToHomeScreen()
+                }
+                eachRun {
+                    this.setRotation(rotation)
+                }
+            }
+            transitions {
+                testApp.open()
+            }
+            teardown {
+                eachRun {
+                    testApp.exit()
+                    this.setRotation(Surface.ROTATION_0)
+                }
+            }
+            assertions {
+                windowManagerTrace {
+                    navBarWindowIsAlwaysVisible()
+                    statusBarWindowIsAlwaysVisible()
+                    appWindowReplacesLauncherAsTopWindow(bugId = 141361128)
+                    wallpaperWindowBecomesInvisible()
+                }
 
-    @FlakyTest(bugId = 140855415)
-    @Test
-    fun checkZOrder_appWindowReplacesLauncherAsTopWindow() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsAppWindowOnTop(
-                            "com.android.launcher3/.Launcher")
-                    .then()
-                    .showsAppWindowOnTop(testApp.getPackage())
-                    .forAllEntries()
-        }
-    }
-
-    @Test
-    fun checkVisibility_wallpaperLayerBecomesInvisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .showsLayer("Wallpaper")
-                    .then()
-                    .replaceVisibleLayer("Wallpaper", testApp.getPackage())
-                    .forAllEntries()
+                layersTrace {
+                    noUncoveredRegions(rotation, bugId = 141361128)
+                    // During testing the launcher is always in portrait mode
+                    navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation)
+                    statusBarLayerRotatesScales(Surface.ROTATION_0, rotation)
+                    navBarLayerIsAlwaysVisible(bugId = 141361128)
+                    statusBarLayerIsAlwaysVisible(bugId = 141361128)
+                    wallpaperLayerBecomesInvisible(bugId = 141361128)
+                }
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTestBase.kt
new file mode 100644
index 0000000..3cec077
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTestBase.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 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.flicker.launch
+
+import com.android.server.wm.flicker.NonRotationTestBase
+import com.android.server.wm.flicker.StandardAppHelper
+import com.android.server.wm.flicker.dsl.LayersAssertion
+import com.android.server.wm.flicker.dsl.WmAssertion
+
+abstract class OpenAppTestBase(
+    rotationName: String,
+    rotation: Int
+) : NonRotationTestBase(rotationName, rotation) {
+    protected val testApp = StandardAppHelper(instrumentation,
+            "com.android.server.wm.flicker.testapp", "SimpleApp")
+
+    protected fun WmAssertion.wallpaperWindowBecomesInvisible(
+        bugId: Int = 0,
+        enabled: Boolean = bugId == 0
+    ) {
+        all("wallpaperWindowBecomesInvisible", enabled, bugId) {
+            this.showsBelowAppWindow("Wallpaper")
+                    .then()
+                    .hidesBelowAppWindow("Wallpaper")
+        }
+    }
+
+    protected fun WmAssertion.appWindowReplacesLauncherAsTopWindow(
+        bugId: Int = 0,
+        enabled: Boolean = bugId == 0
+    ) {
+        all("appWindowReplacesLauncherAsTopWindow", enabled, bugId) {
+            this.showsAppWindowOnTop(
+                    "Launcher")
+                    .then()
+                    .showsAppWindowOnTop(testApp.getPackage())
+        }
+    }
+
+    protected fun LayersAssertion.wallpaperLayerBecomesInvisible(
+        bugId: Int = 0,
+        enabled: Boolean = bugId == 0
+    ) {
+        all("appWindowReplacesLauncherAsTopWindow", enabled, bugId) {
+            this.showsLayer("Wallpaper")
+                    .then()
+                    .replaceVisibleLayer("Wallpaper", testApp.getPackage())
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index f0bc3f0..98413a1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -16,14 +16,18 @@
 
 package com.android.server.wm.flicker.launch
 
-import androidx.test.filters.FlakyTest
+import android.view.Surface
 import androidx.test.filters.LargeTest
-import com.android.server.wm.flicker.CommonTransitions
-import com.android.server.wm.flicker.LayersTraceSubject
-import com.android.server.wm.flicker.NonRotationTestBase
 import com.android.server.wm.flicker.StandardAppHelper
-import com.android.server.wm.flicker.TransitionRunner
-import com.android.server.wm.flicker.WmTraceSubject
+import com.android.server.wm.flicker.dsl.flicker
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -38,50 +42,56 @@
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class OpenAppWarmTest(
-    beginRotationName: String,
-    beginRotation: Int
-) : NonRotationTestBase(beginRotationName, beginRotation) {
-    init {
-        testApp = StandardAppHelper(instrumentation,
+    rotationName: String,
+    rotation: Int
+) : OpenAppTestBase(rotationName, rotation) {
+    @Test
+    fun test() {
+        val testApp = StandardAppHelper(instrumentation,
                 "com.android.server.wm.flicker.testapp", "SimpleApp")
-    }
 
-    override val transitionToRun: TransitionRunner
-        get() = CommonTransitions.openAppWarm(testApp, instrumentation, uiDevice, beginRotation)
-                .includeJankyRuns().build()
+        flicker(instrumentation) {
+            withTag { buildTestTag("openAppWarm", testApp, rotation) }
+            repeat { 1 }
+            setup {
+                test {
+                    device.wakeUpAndGoToHomeScreen()
+                    testApp.open()
+                }
+                eachRun {
+                    device.pressHome()
+                    this.setRotation(rotation)
+                }
+            }
+            transitions {
+                testApp.open()
+            }
+            teardown {
+                eachRun {
+                    this.setRotation(Surface.ROTATION_0)
+                }
+                test {
+                    testApp.exit()
+                }
+            }
+            assertions {
+                windowManagerTrace {
+                    navBarWindowIsAlwaysVisible()
+                    statusBarWindowIsAlwaysVisible()
+                    appWindowReplacesLauncherAsTopWindow(bugId = 141361128)
+                    wallpaperWindowBecomesInvisible(enabled = false)
+                }
 
-    @Test
-    fun checkVisibility_wallpaperBecomesInvisible() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsBelowAppWindow("Wallpaper")
-                    .then()
-                    .hidesBelowAppWindow("Wallpaper")
-                    .forAllEntries()
+                layersTrace {
+                    noUncoveredRegions(rotation, bugId = 141361128)
+                    // During testing the launcher is always in portrait mode
+                    navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation)
+                    statusBarLayerRotatesScales(Surface.ROTATION_0, rotation)
+                    navBarLayerIsAlwaysVisible(bugId = 141361128)
+                    statusBarLayerIsAlwaysVisible(bugId = 141361128)
+                    wallpaperLayerBecomesInvisible(bugId = 141361128)
+                }
+            }
         }
     }
-
-    @FlakyTest(bugId = 140855415)
-    @Test
-    fun checkZOrder_appWindowReplacesLauncherAsTopWindow() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsAppWindowOnTop(
-                            "com.android.launcher3/.Launcher")
-                    .then()
-                    .showsAppWindowOnTop(testApp.getPackage())
-                    .forAllEntries()
-        }
-    }
-
-    @Test
-    fun checkVisibility_wallpaperLayerBecomesInvisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .showsLayer("Wallpaper")
-                    .then()
-                    .replaceVisibleLayer("Wallpaper", testApp.getPackage())
-                    .forAllEntries()
-        }
-    }
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt
index 79321f9..4afabd4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt
@@ -16,66 +16,36 @@
 
 package com.android.server.wm.flicker.pip
 
-import androidx.test.InstrumentationRegistry
-import androidx.test.filters.LargeTest
-import androidx.test.uiautomator.UiDevice
-import com.android.server.wm.flicker.LayersTraceSubject
+import com.android.server.wm.flicker.dsl.LayersAssertion
 import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.WmTraceSubject
-import com.android.server.wm.flicker.helpers.AutomationUtils
+import com.android.server.wm.flicker.dsl.WmAssertion
 import com.android.server.wm.flicker.helpers.PipAppHelper
-import org.junit.AfterClass
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
 
-@LargeTest
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
 abstract class PipTestBase(
-    beginRotationName: String,
-    beginRotation: Int
-) : NonRotationTestBase(beginRotationName, beginRotation) {
-    init {
-        testApp = PipAppHelper(instrumentation)
-    }
+    rotationName: String,
+    rotation: Int
+) : NonRotationTestBase(rotationName, rotation) {
+    protected val testApp = PipAppHelper(instrumentation)
 
-    @Test
-    fun checkVisibility_pipWindowBecomesVisible() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .skipUntilFirstAssertion()
+    protected fun WmAssertion.pipWindowBecomesVisible() {
+        all("pipWindowBecomesVisible") {
+            this.skipUntilFirstAssertion()
                     .showsAppWindowOnTop(sPipWindowTitle)
                     .then()
                     .hidesAppWindow(sPipWindowTitle)
-                    .forAllEntries()
         }
     }
 
-    @Test
-    fun checkVisibility_pipLayerBecomesVisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .skipUntilFirstAssertion()
+    protected fun LayersAssertion.pipLayerBecomesVisible() {
+        all("pipLayerBecomesVisible") {
+            this.skipUntilFirstAssertion()
                     .showsLayer(sPipWindowTitle)
                     .then()
                     .hidesLayer(sPipWindowTitle)
-                    .forAllEntries()
         }
     }
 
     companion object {
         const val sPipWindowTitle = "PipMenuActivity"
-
-        @AfterClass
-        @JvmStatic
-        fun teardown() {
-            val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
-            if (AutomationUtils.hasPipWindow(device)) {
-                AutomationUtils.closePipWindow(device)
-            }
-        }
     }
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
index 89ffb7a..e5a73f7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
@@ -16,12 +16,21 @@
 
 package com.android.server.wm.flicker.pip
 
+import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.LargeTest
-import com.android.server.wm.flicker.CommonTransitions
-import com.android.server.wm.flicker.TransitionRunner
-import com.android.server.wm.flicker.WmTraceSubject
-import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.server.wm.flicker.dsl.flicker
+import com.android.server.wm.flicker.helpers.closePipWindow
+import com.android.server.wm.flicker.helpers.expandPipWindow
+import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -37,27 +46,55 @@
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 152738416)
 class PipToAppTest(
-    beginRotationName: String,
-    beginRotation: Int
-) : PipTestBase(beginRotationName, beginRotation) {
-    override val transitionToRun: TransitionRunner
-        get() = CommonTransitions.exitPipModeToApp(testApp as PipAppHelper, instrumentation,
-                uiDevice, beginRotation)
-                .includeJankyRuns().build()
-
+    rotationName: String,
+    rotation: Int
+) : PipTestBase(rotationName, rotation) {
     @Test
-    fun checkVisibility_backgroundWindowVisibleBehindPipLayer() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .skipUntilFirstAssertion()
-                    .showsAppWindowOnTop(sPipWindowTitle)
-                    .then()
-                    .showsBelowAppWindow("Wallpaper")
-                    .then()
-                    .showsAppWindowOnTop(testApp.getPackage())
-                    .then()
-                    .appWindowNotOnTop(testApp.getPackage())
-                    .forAllEntries()
+    fun test() {
+        flicker(instrumentation) {
+            withTag { buildTestTag("exitPipModeToApp", testApp, rotation) }
+            repeat { 1 }
+            setup {
+                eachRun {
+                    device.wakeUpAndGoToHomeScreen()
+                }
+            }
+            teardown {
+                eachRun {
+                    testApp.exit()
+                    this.setRotation(Surface.ROTATION_0)
+                }
+                test {
+                    if (device.hasPipWindow()) {
+                        device.closePipWindow()
+                    }
+                }
+            }
+            transitions {
+                device.pressHome()
+                this.setRotation(rotation)
+                testApp.open()
+                testApp.clickEnterPipButton(device)
+                device.expandPipWindow()
+                device.waitForIdle()
+                testApp.exit()
+            }
+            assertions {
+                windowManagerTrace {
+                    navBarWindowIsAlwaysVisible()
+                    statusBarWindowIsAlwaysVisible()
+                    pipWindowBecomesVisible()
+                }
+
+                layersTrace {
+                    navBarLayerIsAlwaysVisible()
+                    statusBarLayerIsAlwaysVisible()
+                    noUncoveredRegions(rotation)
+                    navBarLayerRotatesAndScales(rotation)
+                    statusBarLayerRotatesScales(rotation)
+                    pipLayerBecomesVisible()
+                }
+            }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
index 8591360..f6d9ce2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
@@ -16,12 +16,20 @@
 
 package com.android.server.wm.flicker.pip
 
+import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.LargeTest
-import com.android.server.wm.flicker.CommonTransitions
-import com.android.server.wm.flicker.TransitionRunner
-import com.android.server.wm.flicker.WmTraceSubject
-import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.server.wm.flicker.dsl.flicker
+import com.android.server.wm.flicker.helpers.closePipWindow
+import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -37,24 +45,63 @@
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 152738416)
 class PipToHomeTest(
-    beginRotationName: String,
-    beginRotation: Int
-) : PipTestBase(beginRotationName, beginRotation) {
-    override val transitionToRun: TransitionRunner
-        get() = CommonTransitions.exitPipModeToHome(testApp as PipAppHelper, instrumentation,
-                uiDevice, beginRotation)
-                .includeJankyRuns().build()
-
+    rotationName: String,
+    rotation: Int
+) : PipTestBase(rotationName, rotation) {
     @Test
-    fun checkVisibility_backgroundWindowVisibleBehindPipLayer() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsAppWindowOnTop(sPipWindowTitle)
-                    .then()
-                    .showsBelowAppWindow("Wallpaper")
-                    .then()
-                    .showsAppWindowOnTop("Wallpaper")
-                    .forAllEntries()
+    fun test() {
+        flicker(instrumentation) {
+            withTag { buildTestTag("exitPipModeToApp", testApp, rotation) }
+            repeat { 1 }
+            setup {
+                eachRun {
+                    device.wakeUpAndGoToHomeScreen()
+                    device.pressHome()
+                    this.setRotation(rotation)
+                    testApp.open()
+                }
+            }
+            teardown {
+                eachRun {
+                    testApp.exit()
+                    this.setRotation(Surface.ROTATION_0)
+                }
+                test {
+                    if (device.hasPipWindow()) {
+                        device.closePipWindow()
+                    }
+                }
+            }
+            transitions {
+                testApp.clickEnterPipButton(device)
+                testApp.closePipWindow(device)
+                device.waitForIdle()
+                testApp.exit()
+            }
+            assertions {
+                windowManagerTrace {
+                    navBarWindowIsAlwaysVisible()
+                    statusBarWindowIsAlwaysVisible()
+                    pipWindowBecomesVisible()
+
+                    all {
+                        this.showsAppWindowOnTop(sPipWindowTitle)
+                                .and()
+                                .showsBelowAppWindow("Wallpaper")
+                                .then()
+                                .showsAboveAppWindow("Wallpaper")
+                    }
+                }
+
+                layersTrace {
+                    navBarLayerIsAlwaysVisible()
+                    statusBarLayerIsAlwaysVisible()
+                    noUncoveredRegions(rotation)
+                    navBarLayerRotatesAndScales(rotation)
+                    statusBarLayerRotatesScales(rotation)
+                    pipLayerBecomesVisible()
+                }
+            }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index fb1cb39..239c082 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -16,15 +16,21 @@
 
 package com.android.server.wm.flicker.rotation
 
-import android.util.Log
-import androidx.test.filters.FlakyTest
+import android.view.Surface
 import androidx.test.filters.LargeTest
-import com.android.server.wm.flicker.CommonTransitions
-import com.android.server.wm.flicker.LayersTraceSubject
+import com.android.server.wm.flicker.NonRotationTestBase.Companion.SCREENSHOT_LAYER
 import com.android.server.wm.flicker.RotationTestBase
 import com.android.server.wm.flicker.StandardAppHelper
-import com.android.server.wm.flicker.TransitionRunner
-import com.android.server.wm.flicker.WindowUtils
+import com.android.server.wm.flicker.dsl.flicker
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -44,42 +50,76 @@
     beginRotation: Int,
     endRotation: Int
 ) : RotationTestBase(beginRotationName, endRotationName, beginRotation, endRotation) {
-    init {
-        testApp = StandardAppHelper(instrumentation,
+    @Test
+    fun test() {
+        val testApp = StandardAppHelper(instrumentation,
                 "com.android.server.wm.flicker.testapp", "SimpleApp")
-    }
 
-    override val transitionToRun: TransitionRunner
-        get() = CommonTransitions.changeAppRotation(testApp, instrumentation, uiDevice,
-                beginRotation, endRotation)
-                .includeJankyRuns().build()
+        flicker(instrumentation) {
+            withTag {
+                buildTestTag("changeAppRotation", testApp, beginRotation, endRotation)
+            }
+            repeat { 1 }
+            setup {
+                eachRun {
+                    device.wakeUpAndGoToHomeScreen()
+                    testApp.open()
+                    this.setRotation(beginRotation)
+                }
+            }
+            teardown {
+                eachRun {
+                    testApp.exit()
+                    this.setRotation(Surface.ROTATION_0)
+                }
+            }
+            transitions {
+                this.setRotation(endRotation)
+            }
+            assertions {
+                windowManagerTrace {
+                    navBarWindowIsAlwaysVisible(bugId = 140855415)
+                    statusBarWindowIsAlwaysVisible(bugId = 140855415)
+                }
 
-    @Test
-    fun checkPosition_appLayerRotates() {
-        val startingPos = WindowUtils.getAppPosition(beginRotation)
-        val endingPos = WindowUtils.getAppPosition(endRotation)
-        Log.e(TAG, "startingPos=$startingPos endingPos=$endingPos")
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .hasVisibleRegion(testApp.getPackage(), startingPos).inTheBeginning()
-            LayersTraceSubject.assertThat(it)
-                    .hasVisibleRegion(testApp.getPackage(), endingPos).atTheEnd()
+                layersTrace {
+                    navBarLayerIsAlwaysVisible(bugId = 140855415)
+                    statusBarLayerIsAlwaysVisible(bugId = 140855415)
+                    noUncoveredRegions(beginRotation, endRotation, allStates = false)
+                    navBarLayerRotatesAndScales(beginRotation, endRotation)
+                    statusBarLayerRotatesScales(beginRotation, endRotation)
+                }
+
+                layersTrace {
+                    val startingPos = WindowUtils.getDisplayBounds(beginRotation)
+                    val endingPos = WindowUtils.getDisplayBounds(endRotation)
+
+                    start("appLayerRotates_StartingPos") {
+                        this.hasVisibleRegion(testApp.getPackage(), startingPos)
+                    }
+
+                    end("appLayerRotates_EndingPos") {
+                        this.hasVisibleRegion(testApp.getPackage(), endingPos)
+                    }
+
+                    all("screenshotLayerBecomesInvisible", enabled = false) {
+                        this.showsLayer(testApp.getPackage())
+                                .then()
+                                .replaceVisibleLayer(
+                                        testApp.getPackage(),
+                                        SCREENSHOT_LAYER)
+                                .then()
+                                .showsLayer(testApp.getPackage())
+                                .and()
+                                .showsLayer(SCREENSHOT_LAYER)
+                                .then()
+                                .replaceVisibleLayer(
+                                        SCREENSHOT_LAYER,
+                                        testApp.getPackage()
+                                )
+                    }
+                }
+            }
         }
     }
-
-    @FlakyTest
-    @Test
-    fun checkVisibility_screenshotLayerBecomesInvisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .showsLayer(testApp.getPackage())
-                    .then()
-                    .replaceVisibleLayer(testApp.getPackage(), SCREENSHOT_LAYER)
-                    .then()
-                    .showsLayer(testApp.getPackage()).and().showsLayer(SCREENSHOT_LAYER)
-                    .then()
-                    .replaceVisibleLayer(SCREENSHOT_LAYER, testApp.getPackage())
-                    .forAllEntries()
-        }
-    }
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 1cd1998..4746376 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -17,15 +17,24 @@
 package com.android.server.wm.flicker.rotation
 
 import android.content.Intent
+import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
 import android.view.Surface
-import androidx.test.InstrumentationRegistry
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.LargeTest
-import com.android.server.wm.flicker.CommonTransitions
-import com.android.server.wm.flicker.LayersTraceSubject
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Until
 import com.android.server.wm.flicker.RotationTestBase
-import com.android.server.wm.flicker.TransitionRunner
-import com.android.server.wm.flicker.WindowUtils
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.dsl.flicker
+import com.android.server.wm.flicker.helpers.stopPackage
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.testapp.ActivityOptions
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -42,67 +51,97 @@
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 147659548)
 class SeamlessAppRotationTest(
+    testId: String,
     private val intent: Intent,
     beginRotationName: String,
     endRotationName: String,
     beginRotation: Int,
     endRotation: Int
 ) : RotationTestBase(beginRotationName, endRotationName, beginRotation, endRotation) {
-    override val transitionToRun: TransitionRunner
-        get() {
-            var intentId = ""
-            if (intent.extras?.getBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD) == true) {
-                intentId = "BUSY_UI_THREAD"
-            }
-            return CommonTransitions.changeAppRotation(intent, intentId,
-                    InstrumentationRegistry.getContext(), instrumentation, uiDevice,
-                    beginRotation, endRotation).build()
+    @Test
+    fun test() {
+        var intentId = ""
+        if (intent.extras?.getBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD) == true) {
+            intentId = "BUSY_UI_THREAD"
         }
 
-    @Test
-    fun checkPosition_appLayerRotates() {
-        val startingPos = WindowUtils.getAppPosition(beginRotation)
-        val endingPos = WindowUtils.getAppPosition(endRotation)
-        if (startingPos == endingPos) {
-            checkResults {
-                LayersTraceSubject.assertThat(it)
-                        .hasVisibleRegion(intent.component?.packageName ?: "", startingPos)
-                        .forAllEntries()
+        flicker(instrumentation) {
+            withTag {
+                "changeAppRotation_" + intentId + "_" +
+                        Surface.rotationToString(beginRotation) + "_" +
+                        Surface.rotationToString(endRotation)
             }
-        } else {
-            checkResults {
-                LayersTraceSubject.assertThat(it)
-                        .hasVisibleRegion(intent.component?.packageName ?: "", startingPos)
-                        .then()
-                        .hasVisibleRegion(intent.component?.packageName ?: "", endingPos)
-                        .forAllEntries()
+            repeat { 1 }
+            setup {
+                eachRun {
+                    device.wakeUpAndGoToHomeScreen()
+                    instrumentation.targetContext.startActivity(intent)
+                    device.wait(Until.hasObject(By.pkg(intent.component?.packageName)
+                            .depth(0)), APP_LAUNCH_TIMEOUT)
+                    this.setRotation(beginRotation)
+                }
             }
-        }
-    }
+            teardown {
+                eachRun {
+                    stopPackage(
+                            instrumentation.targetContext,
+                            intent.component?.packageName
+                                    ?: error("Unable to determine package name for intent"))
+                    this.setRotation(Surface.ROTATION_0)
+                }
+            }
+            transitions {
+                this.setRotation(endRotation)
+            }
+            assertions {
+                windowManagerTrace {
+                    navBarWindowIsAlwaysVisible(bugId = 140855415)
+                    statusBarWindowIsAlwaysVisible(bugId = 140855415)
+                }
 
-    @Test
-    fun checkCoveredRegion_noUncoveredRegions() {
-        val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
-        val endingBounds = WindowUtils.getDisplayBounds(endRotation)
-        if (startingBounds == endingBounds) {
-            checkResults {
-                LayersTraceSubject.assertThat(it)
-                        .coversRegion(startingBounds)
-                        .forAllEntries()
-            }
-        } else {
-            checkResults {
-                LayersTraceSubject.assertThat(it)
-                        .coversRegion(startingBounds)
-                        .then()
-                        .coversRegion(endingBounds)
-                        .forAllEntries()
+                layersTrace {
+                    navBarLayerIsAlwaysVisible(bugId = 140855415)
+                    statusBarLayerIsAlwaysVisible(bugId = 140855415)
+                    noUncoveredRegions(beginRotation, endRotation, allStates = true)
+                    navBarLayerRotatesAndScales(beginRotation, endRotation)
+                    statusBarLayerRotatesScales(beginRotation, endRotation, enabled = false)
+                }
+
+                layersTrace {
+                    all("appLayerRotates"/*, bugId = 147659548*/) {
+                        val startingPos = WindowUtils.getDisplayBounds(beginRotation)
+                        val endingPos = WindowUtils.getDisplayBounds(endRotation)
+
+                        if (startingPos == endingPos) {
+                            this.hasVisibleRegion(
+                                    intent.component?.packageName ?: "",
+                                    startingPos)
+                        } else {
+                            this.hasVisibleRegion(intent.component?.packageName ?: "", startingPos)
+                                    .then()
+                                    .hasVisibleRegion(intent.component?.packageName
+                                            ?: "", endingPos)
+                        }
+                    }
+
+                    all("noUncoveredRegions"/*, bugId = 147659548*/) {
+                        val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
+                        val endingBounds = WindowUtils.getDisplayBounds(endRotation)
+                        if (startingBounds == endingBounds) {
+                            this.coversRegion(startingBounds)
+                        } else {
+                            this.coversRegion(startingBounds)
+                                    .then()
+                                    .coversRegion(endingBounds)
+                        }
+                    }
+                }
             }
         }
     }
 
     companion object {
-        // launch test activity that supports seamless rotation
+        private const val APP_LAUNCH_TIMEOUT: Long = 10000
 
         // launch test activity that supports seamless rotation with a busy UI thread to miss frames
         // when the app is asked to redraw
@@ -110,18 +149,20 @@
         @JvmStatic
         fun getParams(): Collection<Array<Any>> {
             val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
-            val params: MutableCollection<Array<Any>> = ArrayList()
-            val testIntents = ArrayList<Intent>()
+            val params = mutableListOf<Array<Any>>()
+            val testIntents = mutableListOf<Intent>()
 
             // launch test activity that supports seamless rotation
             var intent = Intent(Intent.ACTION_MAIN)
             intent.component = ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME
+            intent.flags = FLAG_ACTIVITY_NEW_TASK
             testIntents.add(intent)
 
             // launch test activity that supports seamless rotation with a busy UI thread to miss frames
             // when the app is asked to redraw
             intent = Intent(intent)
             intent.putExtra(ActivityOptions.EXTRA_STARVE_UI_THREAD, true)
+            intent.flags = FLAG_ACTIVITY_NEW_TASK
             testIntents.add(intent)
             for (testIntent in testIntents) {
                 for (begin in supportedRotations) {
@@ -133,7 +174,13 @@
                                             ActivityOptions.EXTRA_STARVE_UI_THREAD) == true) {
                                 testId += "_" + "BUSY_UI_THREAD"
                             }
-                            params.add(arrayOf(testId, testIntent, begin, end))
+                            params.add(arrayOf(
+                                    testId,
+                                    testIntent,
+                                    Surface.rotationToString(begin),
+                                    Surface.rotationToString(end),
+                                    begin,
+                                    end))
                         }
                     }
                 }
@@ -141,4 +188,4 @@
             return params
         }
     }
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
index b5611a4..7c19696 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
@@ -16,13 +16,22 @@
 
 package com.android.server.wm.flicker.splitscreen
 
+import android.os.SystemClock
+import android.view.Surface
 import androidx.test.filters.LargeTest
-import com.android.server.wm.flicker.CommonTransitions
-import com.android.server.wm.flicker.LayersTraceSubject
 import com.android.server.wm.flicker.NonRotationTestBase
 import com.android.server.wm.flicker.StandardAppHelper
-import com.android.server.wm.flicker.TransitionRunner
-import com.android.server.wm.flicker.WmTraceSubject
+import com.android.server.wm.flicker.dsl.flicker
+import com.android.server.wm.flicker.helpers.exitSplitScreen
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -37,42 +46,63 @@
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class OpenAppToSplitScreenTest(
-    beginRotationName: String,
-    beginRotation: Int
-) : NonRotationTestBase(beginRotationName, beginRotation) {
-    init {
-        testApp = StandardAppHelper(instrumentation,
-                "com.android.server.wm.flicker.testapp", "SimpleApp")
-    }
-
-    override val transitionToRun: TransitionRunner
-        get() = CommonTransitions.appToSplitScreen(testApp, instrumentation, uiDevice,
-                beginRotation).includeJankyRuns().build()
-
+    rotationName: String,
+    rotation: Int
+) : NonRotationTestBase(rotationName, rotation) {
     @Test
-    fun checkVisibility_navBarWindowIsAlwaysVisible() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries()
+    fun test() {
+        val testApp = StandardAppHelper(instrumentation,
+        "com.android.server.wm.flicker.testapp", "SimpleApp")
+
+        flicker(instrumentation) {
+            withTag { buildTestTag("appToSplitScreen", testApp, rotation) }
+            repeat { 1 }
+            setup {
+                eachRun {
+                    device.wakeUpAndGoToHomeScreen()
+                    this.setRotation(rotation)
+                    testApp.open()
+                    SystemClock.sleep(500)
+                }
+            }
+            teardown {
+                eachRun {
+                    device.exitSplitScreen()
+                    testApp.exit()
+                }
+            }
+            transitions {
+                device.launchSplitScreen()
+            }
+            assertions {
+                windowManagerTrace {
+                    navBarWindowIsAlwaysVisible()
+                    statusBarWindowIsAlwaysVisible()
+                }
+
+                layersTrace {
+                    navBarLayerIsAlwaysVisible()
+                    statusBarLayerIsAlwaysVisible()
+                    noUncoveredRegions(rotation)
+                    navBarLayerRotatesAndScales(rotation)
+                    statusBarLayerRotatesScales(rotation)
+
+                    all("dividerLayerBecomesVisible") {
+                        this.hidesLayer(DOCKED_STACK_DIVIDER)
+                                .then()
+                                .showsLayer(DOCKED_STACK_DIVIDER)
+                    }
+                }
+            }
         }
     }
 
-    @Test
-    fun checkVisibility_statusBarWindowIsAlwaysVisible() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries()
-        }
-    }
-
-    @Test
-    fun checkVisibility_dividerLayerBecomesVisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .hidesLayer(DOCKED_STACK_DIVIDER)
-                    .then()
-                    .showsLayer(DOCKED_STACK_DIVIDER)
-                    .forAllEntries()
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<Array<Any>> {
+            val supportedRotations = intArrayOf(Surface.ROTATION_0)
+            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
         }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
index 6b597e5..a93330d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
@@ -19,24 +19,27 @@
 import android.graphics.Region
 import android.util.Rational
 import android.view.Surface
-import androidx.test.InstrumentationRegistry
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.LargeTest
-import androidx.test.runner.AndroidJUnit4
-import androidx.test.uiautomator.UiDevice
-import com.android.server.wm.flicker.CommonTransitions
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.uiautomator.By
 import com.android.server.wm.flicker.FlickerTestBase
-import com.android.server.wm.flicker.LayersTrace
-import com.android.server.wm.flicker.LayersTraceSubject
 import com.android.server.wm.flicker.StandardAppHelper
-import com.android.server.wm.flicker.TransitionRunner
-import com.android.server.wm.flicker.TransitionResult
-import com.android.server.wm.flicker.WindowUtils
-import com.android.server.wm.flicker.WmTraceSubject
-import com.android.server.wm.flicker.helpers.AutomationUtils
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.google.common.truth.Truth
-import org.junit.AfterClass
+import com.android.server.wm.flicker.helpers.exitSplitScreen
+import com.android.server.wm.flicker.helpers.isInSplitScreen
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.helpers.resizeSplitScreen
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -53,146 +56,130 @@
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 159096424)
 class ResizeSplitScreenTest : FlickerTestBase() {
-    init {
-        testApp = StandardAppHelper(instrumentation,
+    @Test
+    fun test() {
+        val testAppTop = StandardAppHelper(instrumentation,
                 "com.android.server.wm.flicker.testapp", "SimpleApp")
-    }
+        val testAppBottom = ImeAppHelper(instrumentation)
 
-    override val transitionToRun: TransitionRunner
-        get() {
-            val bottomApp = ImeAppHelper(instrumentation)
-            return CommonTransitions.resizeSplitScreen(testApp, bottomApp, instrumentation,
-                    uiDevice, Surface.ROTATION_0,
-                    Rational(1, 3), Rational(2, 3))
-                    .includeJankyRuns().build()
-        }
+        flicker(instrumentation) {
+            withTag {
+                val description = (startRatio.toString().replace("/", "-") + "_to_" +
+                        stopRatio.toString().replace("/", "-"))
+                buildTestTag("resizeSplitScreen", testAppTop, rotation,
+                        rotation, testAppBottom, description)
+            }
+            repeat { 1 }
+            setup {
+                eachRun {
+                    device.wakeUpAndGoToHomeScreen()
+                    this.setRotation(rotation)
+                    this.launcherStrategy.clearRecentAppsFromOverview()
+                    testAppBottom.open()
+                    device.pressHome()
+                    testAppTop.open()
+                    device.waitForIdle()
+                    device.launchSplitScreen()
+                    val snapshot = device.findObject(By.res(device.launcherPackageName, "snapshot"))
+                    snapshot.click()
+                    testAppBottom.openIME(device)
+                    device.pressBack()
+                    device.resizeSplitScreen(startRatio)
+                }
+            }
+            teardown {
+                eachRun {
+                    device.exitSplitScreen()
+                    device.pressHome()
+                    testAppTop.exit()
+                    testAppBottom.exit()
+                }
+                test {
+                    if (device.isInSplitScreen()) {
+                        device.exitSplitScreen()
+                    }
+                }
+            }
+            transitions {
+                device.resizeSplitScreen(stopRatio)
+            }
+            assertions {
+                windowManagerTrace {
+                    navBarWindowIsAlwaysVisible()
+                    statusBarWindowIsAlwaysVisible()
 
-    @Test
-    fun checkVisibility_topAppLayerIsAlwaysVisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .showsLayer(sSimpleActivity)
-                    .forAllEntries()
-        }
-    }
+                    all("topAppWindowIsAlwaysVisible", bugId = 156223549) {
+                        this.showsAppWindow(sSimpleActivity)
+                    }
 
-    @Test
-    fun checkVisibility_bottomAppLayerIsAlwaysVisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .showsLayer(sImeActivity)
-                    .forAllEntries()
-        }
-    }
+                    all("bottomAppWindowIsAlwaysVisible", bugId = 156223549) {
+                        this.showsAppWindow(sImeActivity)
+                    }
+                }
 
-    @Test
-    fun checkVisibility_dividerLayerIsAlwaysVisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .showsLayer(DOCKED_STACK_DIVIDER)
-                    .forAllEntries()
-        }
-    }
+                layersTrace {
+                    navBarLayerIsAlwaysVisible()
+                    statusBarLayerIsAlwaysVisible()
+                    noUncoveredRegions(rotation)
+                    navBarLayerRotatesAndScales(rotation)
+                    statusBarLayerRotatesScales(rotation)
 
-    @Test
-    @FlakyTest
-    fun checkPosition_appsStartingBounds() {
-        val displayBounds = WindowUtils.getDisplayBounds()
-        checkResults { result: TransitionResult ->
-            val entries = LayersTrace.parseFrom(result.layersTrace,
-                    result.layersTracePath, result.layersTraceChecksum)
-            Truth.assertThat(entries.entries).isNotEmpty()
-            val startingDividerBounds = entries.entries[0].getVisibleBounds(
-                    DOCKED_STACK_DIVIDER).bounds
-            val startingTopAppBounds = Region(0, 0, startingDividerBounds.right,
-                    startingDividerBounds.top + WindowUtils.getDockedStackDividerInset())
-            val startingBottomAppBounds = Region(0,
-                    startingDividerBounds.bottom - WindowUtils.getDockedStackDividerInset(),
-                    displayBounds.right,
-                    displayBounds.bottom - WindowUtils.getNavigationBarHeight())
-            LayersTraceSubject.assertThat(result)
-                    .hasVisibleRegion("SimpleActivity", startingTopAppBounds)
-                    .inTheBeginning()
-            LayersTraceSubject.assertThat(result)
-                    .hasVisibleRegion("ImeActivity", startingBottomAppBounds)
-                    .inTheBeginning()
-        }
-    }
+                    all("topAppLayerIsAlwaysVisible") {
+                        this.showsLayer(sSimpleActivity)
+                    }
 
-    @Test
-    @FlakyTest
-    fun checkPosition_appsEndingBounds() {
-        val displayBounds = WindowUtils.getDisplayBounds()
-        checkResults { result: TransitionResult ->
-            val entries = LayersTrace.parseFrom(result.layersTrace,
-                    result.layersTracePath, result.layersTraceChecksum)
-            Truth.assertThat(entries.entries).isNotEmpty()
-            val endingDividerBounds = entries.entries[entries.entries.size - 1].getVisibleBounds(
-                    DOCKED_STACK_DIVIDER).bounds
-            val startingTopAppBounds = Region(0, 0, endingDividerBounds.right,
-                    endingDividerBounds.top + WindowUtils.getDockedStackDividerInset())
-            val startingBottomAppBounds = Region(0,
-                    endingDividerBounds.bottom - WindowUtils.getDockedStackDividerInset(),
-                    displayBounds.right,
-                    displayBounds.bottom - WindowUtils.getNavigationBarHeight())
-            LayersTraceSubject.assertThat(result)
-                    .hasVisibleRegion(sSimpleActivity, startingTopAppBounds)
-                    .atTheEnd()
-            LayersTraceSubject.assertThat(result)
-                    .hasVisibleRegion(sImeActivity, startingBottomAppBounds)
-                    .atTheEnd()
-        }
-    }
+                    all("bottomAppLayerIsAlwaysVisible") {
+                        this.showsLayer(sImeActivity)
+                    }
 
-    @Test
-    fun checkVisibility_navBarWindowIsAlwaysVisible() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE)
-                    .forAllEntries()
-        }
-    }
+                    all("dividerLayerIsAlwaysVisible") {
+                        this.showsLayer(DOCKED_STACK_DIVIDER)
+                    }
 
-    @Test
-    fun checkVisibility_statusBarWindowIsAlwaysVisible() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE)
-                    .forAllEntries()
-        }
-    }
+                    start("appsStartingBounds", enabled = false) {
+                        val displayBounds = WindowUtils.displayBounds
+                        val entry = this.trace.entries.firstOrNull()
+                                ?: throw IllegalStateException("Trace is empty")
+                        val dividerBounds = entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
 
-    @Test
-    @FlakyTest(bugId = 156223549)
-    fun checkVisibility_topAppWindowIsAlwaysVisible() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsAppWindow(sSimpleActivity)
-                    .forAllEntries()
-        }
-    }
+                        val topAppBounds = Region(0, 0, dividerBounds.right,
+                                dividerBounds.top + WindowUtils.dockedStackDividerInset)
+                        val bottomAppBounds = Region(0,
+                                dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
+                                displayBounds.right,
+                                displayBounds.bottom - WindowUtils.navigationBarHeight)
+                        this.hasVisibleRegion("SimpleActivity", topAppBounds)
+                                .and()
+                                .hasVisibleRegion("ImeActivity", bottomAppBounds)
+                    }
 
-    @Test
-    @FlakyTest(bugId = 156223549)
-    fun checkVisibility_bottomAppWindowIsAlwaysVisible() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsAppWindow(sImeActivity)
-                    .forAllEntries()
+                    end("appsEndingBounds", enabled = false) {
+                        val displayBounds = WindowUtils.displayBounds
+                        val entry = this.trace.entries.lastOrNull()
+                                ?: throw IllegalStateException("Trace is empty")
+                        val dividerBounds = entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
+
+                        val topAppBounds = Region(0, 0, dividerBounds.right,
+                                dividerBounds.top + WindowUtils.dockedStackDividerInset)
+                        val bottomAppBounds = Region(0,
+                                dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
+                                displayBounds.right,
+                                displayBounds.bottom - WindowUtils.navigationBarHeight)
+
+                        this.hasVisibleRegion(sSimpleActivity, topAppBounds)
+                                .and()
+                                .hasVisibleRegion(sImeActivity, bottomAppBounds)
+                    }
+                }
+            }
         }
     }
 
     companion object {
         private const val sSimpleActivity = "SimpleActivity"
         private const val sImeActivity = "ImeActivity"
-
-        @AfterClass
-        @JvmStatic
-        fun teardown() {
-            val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
-            if (AutomationUtils.isInSplitScreen(device)) {
-                AutomationUtils.exitSplitScreen(device)
-            }
-        }
+        private val rotation = Surface.ROTATION_0
+        private val startRatio = Rational(1, 3)
+        private val stopRatio = Rational(2, 3)
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
index fdcafdb..268ba9e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
@@ -17,19 +17,22 @@
 package com.android.server.wm.flicker.splitscreen
 
 import android.view.Surface
-import androidx.test.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
-import androidx.test.runner.AndroidJUnit4
-import androidx.test.uiautomator.UiDevice
-import com.android.server.wm.flicker.CommonTransitions
 import com.android.server.wm.flicker.FlickerTestBase
-import com.android.server.wm.flicker.LayersTraceSubject
 import com.android.server.wm.flicker.StandardAppHelper
-import com.android.server.wm.flicker.TransitionRunner
-import com.android.server.wm.flicker.WindowUtils
-import com.android.server.wm.flicker.WmTraceSubject
-import com.android.server.wm.flicker.helpers.AutomationUtils
-import org.junit.AfterClass
+import com.android.server.wm.flicker.dsl.flicker
+import com.android.server.wm.flicker.helpers.exitSplitScreen
+import com.android.server.wm.flicker.helpers.isInSplitScreen
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -43,68 +46,63 @@
 @RunWith(AndroidJUnit4::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class SplitScreenToLauncherTest : FlickerTestBase() {
-    init {
-        testApp = StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+    private val rotation: Int = Surface.ROTATION_0
+    @Test
+    fun test() {
+        val testApp = StandardAppHelper(instrumentation,
                 "com.android.server.wm.flicker.testapp", "SimpleApp")
-    }
 
-    override val transitionToRun: TransitionRunner
-        get() = CommonTransitions.splitScreenToLauncher(testApp, instrumentation, uiDevice,
-                Surface.ROTATION_0).includeJankyRuns().build()
+        flicker(instrumentation) {
+            withTag { buildTestTag("splitScreenToLauncher", testApp, rotation) }
+            repeat { 1 }
+            setup {
+                eachRun {
+                    device.wakeUpAndGoToHomeScreen()
+                    testApp.open()
+                    this.setRotation(rotation)
+                    device.launchSplitScreen()
+                    device.waitForIdle()
+                }
+            }
+            teardown {
+                eachRun {
+                    testApp.exit()
+                }
+                test {
+                    if (device.isInSplitScreen()) {
+                        device.exitSplitScreen()
+                    }
+                }
+            }
+            transitions {
+                device.exitSplitScreen()
+            }
+            assertions {
+                windowManagerTrace {
+                    navBarWindowIsAlwaysVisible()
+                    statusBarWindowIsAlwaysVisible()
+                }
 
-    @Test
-    fun checkCoveredRegion_noUncoveredRegions() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .coversRegion(WindowUtils.getDisplayBounds()).forAllEntries()
-        }
-    }
+                layersTrace {
+                    navBarLayerIsAlwaysVisible()
+                    statusBarLayerIsAlwaysVisible()
+                    noUncoveredRegions(rotation)
+                    navBarLayerRotatesAndScales(rotation)
+                    statusBarLayerRotatesScales(rotation)
 
-    @Test
-    fun checkVisibility_dividerLayerBecomesInVisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .showsLayer(DOCKED_STACK_DIVIDER)
-                    .then()
-                    .hidesLayer(DOCKED_STACK_DIVIDER)
-                    .forAllEntries()
-        }
-    }
+                    // b/161435597 causes the test not to work on 90 degrees
+                    all("dividerLayerBecomesInvisible") {
+                        this.showsLayer(DOCKED_STACK_DIVIDER)
+                                .then()
+                                .hidesLayer(DOCKED_STACK_DIVIDER)
+                    }
 
-    @Test
-    fun checkVisibility_appLayerBecomesInVisible() {
-        checkResults {
-            LayersTraceSubject.assertThat(it)
-                    .showsLayer(testApp.getPackage())
-                    .then()
-                    .hidesLayer(testApp.getPackage())
-                    .forAllEntries()
-        }
-    }
-
-    @Test
-    fun checkVisibility_navBarWindowIsAlwaysVisible() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries()
-        }
-    }
-
-    @Test
-    fun checkVisibility_statusBarWindowIsAlwaysVisible() {
-        checkResults {
-            WmTraceSubject.assertThat(it)
-                    .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries()
-        }
-    }
-
-    companion object {
-        @AfterClass
-        @JvmStatic
-        fun teardown() {
-            val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
-            if (AutomationUtils.isInSplitScreen(device)) {
-                AutomationUtils.exitSplitScreen(device)
+                    all("appLayerBecomesInvisible") {
+                        this.showsLayer(testApp.getPackage())
+                            .then()
+                            .hidesLayer(testApp.getPackage())
+                    }
+                }
             }
         }
     }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1e7fecf..0f24b0c 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -4226,7 +4226,7 @@
             callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
         }
 
-        // Sanity check before testing started keepalive.
+        // Basic check before testing started keepalive.
         try (SocketKeepalive ka = mCm.createSocketKeepalive(
                 myNet, testSocket, myIPv4, dstIPv4, executor, callback)) {
             ka.start(validKaInterval);