Merge "Add nicomazz to platform.test.rules oweners" am: 689466a884

Original change: https://android-review.googlesource.com/c/platform/platform_testing/+/2183766

Change-Id: I82c0a88e5514d80f9f17c475f1b3038d97d1afc7
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/build/tasks/tests/instrumentation_test_list.mk b/build/tasks/tests/instrumentation_test_list.mk
index e6e6602..84b6dd6 100644
--- a/build/tasks/tests/instrumentation_test_list.mk
+++ b/build/tasks/tests/instrumentation_test_list.mk
@@ -24,7 +24,6 @@
     FrameworksNetTests \
     FrameworksUiServicesTests \
     BstatsTestApp \
-    ConnTestApp \
     FrameworksServicesTests \
     FrameworksMockingServicesTests \
     WmTests \
@@ -43,7 +42,6 @@
     FrameworksWifiTests \
     FrameworksTelephonyTests \
     ContactsProviderTests \
-    ContactsProviderTests2 \
     SettingsUnitTests \
     TelecomUnitTests \
     TraceurUiTests \
@@ -73,6 +71,7 @@
     SettingsPerfTests \
     ExtServicesUnitTests \
     FrameworksNetSmokeTests \
+    FlickerLibTest \
     FlickerTests \
     FlickerTestApp \
     WMShellFlickerTests \
diff --git a/build/tasks/tests/native_metric_test_list.mk b/build/tasks/tests/native_metric_test_list.mk
index d4796e0..f6eca91 100644
--- a/build/tasks/tests/native_metric_test_list.mk
+++ b/build/tasks/tests/native_metric_test_list.mk
@@ -29,7 +29,8 @@
     mmapPerf \
     netd_benchmark \
     skia_nanobench \
-    VibratorHalIntegrationBenchmark
+    VibratorHalIntegrationBenchmark \
+    librenderengine_bench
 
 ifneq ($(strip $(BOARD_PERFSETUP_SCRIPT)),)
 native_metric_tests += perf-setup
diff --git a/build/tasks/tests/native_test_list.mk b/build/tasks/tests/native_test_list.mk
index 28b73c3..e0dcb92 100644
--- a/build/tasks/tests/native_test_list.mk
+++ b/build/tasks/tests/native_test_list.mk
@@ -38,12 +38,23 @@
     bootstat_tests \
     boringssl_crypto_test \
     boringssl_ssl_test \
+    bsdiff_unittest \
     buffer_hub-test \
     buffer_hub_queue-test \
     buffer_hub_queue_producer-test \
     bugreportz_test \
-    bsdiff_unittest \
+    bytes_test_tests_test_buf \
+    bytes_test_tests_test_buf_mut \
+    bytes_test_tests_test_bytes \
+    bytes_test_tests_test_bytes_odd_alloc \
+    bytes_test_tests_test_bytes_vec_alloc \
+    bytes_test_tests_test_chain \
+    bytes_test_tests_test_debug \
+    bytes_test_tests_test_iter \
+    bytes_test_tests_test_reader \
+    bytes_test_tests_test_take \
     camera_client_test \
+    cesu8_test_src_lib \
     clatd_test \
     confirmationui_invocation_test \
     debuggerd_test \
@@ -68,6 +79,7 @@
     installd_otapreopt_test \
     installd_service_test \
     installd_utils_test \
+    jni_test_src_lib \
     keystore2_crypto_test_rust \
     keystore2_selinux_test \
     keystore2_test \
@@ -75,6 +87,7 @@
     libandroidfw_tests \
     libappfuse_test \
     libbase_test \
+    libbinder_rs-internal_test \
     libbpf_android_test \
     libcutils_test \
     libcutils_test_static \
@@ -90,10 +103,15 @@
     libtextclassifier_tests \
     libsurfaceflinger_unittest \
     libunwindstack_unit_test \
+    libuwb_core_tests \
+    libuwb_uci_jni_rust_tests \
+    libuwb_uci_rust_tests \
+    libuwb_uci_packet_tests \
     libvintf_test \
     linker-unit-tests \
     logcat-unit-tests \
     logd-unit-tests \
+    logger_device_unit_tests \
     kernel-config-unit-tests \
     malloc_debug_unit_tests \
     memory_replay_tests \
@@ -125,6 +143,8 @@
     netd_unit_test \
     netdutils_test \
     nfc_test_utils \
+    num-traits_test_src_lib \
+    num-traits_test_tests_cast \
     perfetto_integrationtests \
     posix_async_io_test \
     prioritydumper_test \
@@ -235,9 +255,12 @@
     NeuralNetworksTest_static \
     NeuralNetworksTest_utils \
     SurfaceFlinger_test \
-    lmkd_unit_test \
-    vrflinger_test
+    lmkd_unit_test
 
 ifeq ($(BOARD_IS_AUTOMOTIVE), true)
 native_tests += libwatchdog_test
 endif
+
+ifneq ($(strip $(BOARD_PERFSETUP_SCRIPT)),)
+native_tests += perf-setup
+endif
diff --git a/build/tasks/tests/platform_test_list.mk b/build/tasks/tests/platform_test_list.mk
index b5fa06c..5c5f70f 100644
--- a/build/tasks/tests/platform_test_list.mk
+++ b/build/tasks/tests/platform_test_list.mk
@@ -9,7 +9,6 @@
     ApiDemos \
     AppCompatibilityTest \
     AppLaunch \
-    AppLaunchWear \
     AppTransitionTests \
     AutoLocTestApp \
     AutoLocVersionedTestApp_v1 \
@@ -54,6 +53,7 @@
     InternalLocTestApp \
     JankMicroBenchmarkTests \
     LauncherIconsApp \
+    long_trace_binder_config.textproto \
     long_trace_config.textproto \
     MemoryUsage \
     MultiDexLegacyTestApp \
diff --git a/libraries/app-helpers/core/src/android/platform/helpers/AbstractStandardAppHelper.java b/libraries/app-helpers/core/src/android/platform/helpers/AbstractStandardAppHelper.java
index fe710b0..59c6a56 100644
--- a/libraries/app-helpers/core/src/android/platform/helpers/AbstractStandardAppHelper.java
+++ b/libraries/app-helpers/core/src/android/platform/helpers/AbstractStandardAppHelper.java
@@ -16,18 +16,25 @@
 
 package android.platform.helpers;
 
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
 import android.accounts.Account;
 import android.accounts.AccountManager;
+import android.app.ActivityManager;
 import android.app.Instrumentation;
+import android.app.UiAutomation;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Build;
 import android.os.Environment;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.platform.helpers.exceptions.AccountException;
 import android.platform.helpers.exceptions.TestHelperException;
 import android.platform.helpers.exceptions.UnknownUiException;
@@ -35,15 +42,17 @@
 import android.support.test.launcherhelper.ILauncherStrategy;
 import android.support.test.launcherhelper.LauncherStrategyFactory;
 import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject2;
 import android.support.test.uiautomator.UiWatcher;
 import android.support.test.uiautomator.Until;
-import androidx.test.InstrumentationRegistry;
 import android.util.Log;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 
+import androidx.test.InstrumentationRegistry;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
@@ -59,6 +68,7 @@
         "Element %s %s is not found in the application %s";
 
     private static final long EXIT_WAIT_TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+    private static final int WAIT_TIME_MS = 10000;
 
     private static File sScreenshotDirectory;
 
@@ -72,6 +82,11 @@
     private final long mAppIdle;
     private final long mLaunchTimeout;
 
+    private final static String NOTIF_PERM = android.Manifest.permission.POST_NOTIFICATIONS;
+    private UiAutomation mAutomation;
+    private int mPreviousFlagValues = 0;
+    private boolean mChangedPermState = false;
+
     public AbstractStandardAppHelper(Instrumentation instr) {
         mInstrumentation = instr;
         mDevice = UiDevice.getInstance(instr);
@@ -101,6 +116,10 @@
      */
     @Override
     public void open() {
+        // Grant notification permission if necessary - otherwise the app may display a permission
+        // prompt that interferes with tests
+        maybeGrantNotificationPermission();
+
         // Turn on the screen if necessary.
         try {
             if (!mDevice.isScreenOn()) {
@@ -123,7 +142,9 @@
             String output = null;
             try {
                 Log.i(LOG_TAG, String.format("Sending command to launch: %s", pkg));
-                mInstrumentation.getContext().startActivity(getOpenAppIntent());
+                mInstrumentation
+                        .getContext()
+                        .startActivity(getOpenAppIntent().addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
             } catch (ActivityNotFoundException e) {
                 removeDialogWatchers();
                 throw new TestHelperException(String.format("Failed to find package: %s", pkg), e);
@@ -201,6 +222,7 @@
                 Until.hasObject(getLauncherStrategy().getWorkspaceSelector()), EXIT_WAIT_TIMEOUT)) {
             throw new IllegalStateException("Failed to exit the app to launcher.");
         }
+        restoreNotificationPermissionState();
     }
 
     /**
@@ -335,8 +357,15 @@
       }
     }
 
+    /** Returns a UI object after waiting for it, and fails if not found. */
+    public static UiObject2 waitForObject(UiDevice device, BySelector selector) {
+        final UiObject2 object = device.wait(Until.findObject(selector), WAIT_TIME_MS);
+        if (object == null) throw new UnknownUiException("Can't find object " + selector);
+        return object;
+    }
+
     protected void waitAndClickById(String packageStr, String id, long timeout) {
-      clickOn(mDevice.wait(Until.findObject(By.res(packageStr, id)), timeout));
+        clickOn(mDevice.wait(Until.findObject(By.res(packageStr, id)), timeout));
     }
 
     protected void waitAndClickByText(String text, long timeout) {
@@ -406,4 +435,89 @@
         }
         return mLauncherStrategy;
     }
+
+    // Check whether we might need to grant this package notification permission.
+    // This would be for packages that meet either of the following criteria:
+    //   - build sdk <= sc-v2 and does not have notification permission
+    //   - sdk > sc-v2, requests notification permission but does not have notification permission
+    private boolean packageNeedsNotificationPermission(PackageManager pm, String pkg) {
+        PackageInfo pInfo;
+        try {
+            pInfo = pm.getPackageInfo(pkg, PackageManager.PackageInfoFlags.of(0));
+        } catch (NameNotFoundException e) {
+            Log.w(LOG_TAG, "package name not found");
+            return false;
+        }
+
+        if (pInfo.applicationInfo == null) {
+            return false;
+        }
+
+        boolean hasNotifPerm = pm.checkPermission(NOTIF_PERM, pkg) == PERMISSION_GRANTED;
+
+        // for apps sc-v2 and below, we set notification permission only if they don't already have
+        // it
+        if (pInfo.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.S_V2) {
+            return !hasNotifPerm;
+        }
+
+        // check for notification permission in the list of package's requested permissions --
+        // apps T and up must request the permission if they're to be granted it
+        if (pInfo.requestedPermissions == null) {
+            return false;
+        }
+        boolean requestedNotifPerm = false;
+        for (String perm : pInfo.requestedPermissions) {
+            if (NOTIF_PERM.equals(perm)) {
+                requestedNotifPerm = true;
+                break;
+            }
+        }
+        return requestedNotifPerm && !hasNotifPerm;
+    }
+
+    private void maybeGrantNotificationPermission() {
+        mAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        mAutomation.adoptShellPermissionIdentity(
+                android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+                android.Manifest.permission.INTERACT_ACROSS_USERS);
+        PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+        if (packageNeedsNotificationPermission(pm, getPackage())) {
+            UserHandle user = UserHandle.of(ActivityManager.getCurrentUser());
+            mChangedPermState = true;
+            mAutomation.grantRuntimePermission(getPackage(),
+                    NOTIF_PERM);
+            mPreviousFlagValues = pm.getPermissionFlags(NOTIF_PERM, getPackage(),
+                    user);
+            pm.updatePermissionFlags(NOTIF_PERM,
+                    getPackage(),
+                    FLAG_PERMISSION_USER_SET
+                            | PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED,
+                    FLAG_PERMISSION_USER_SET,
+                    user);
+        }
+        mAutomation.dropShellPermissionIdentity();
+    }
+
+    private void restoreNotificationPermissionState() {
+        if (mChangedPermState) {
+            mAutomation.adoptShellPermissionIdentity(
+                    android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+                    android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS);
+            PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+            // ensure permission is still granted, in case it was revoked elsewhere
+            if (pm.checkPermission(NOTIF_PERM, getPackage()) == PERMISSION_GRANTED) {
+                mAutomation.revokeRuntimePermission(getPackage(), NOTIF_PERM);
+                UserHandle user = UserHandle.of(ActivityManager.getCurrentUser());
+                pm.updatePermissionFlags(
+                        NOTIF_PERM,
+                        getPackage(),
+                        FLAG_PERMISSION_USER_SET | PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED,
+                        mPreviousFlagValues,
+                        user);
+            }
+            mAutomation.dropShellPermissionIdentity();
+        }
+    }
 }
diff --git a/libraries/app-helpers/interfaces/common/src/android/platform/helpers/IPlayStoreHelper.java b/libraries/app-helpers/interfaces/common/src/android/platform/helpers/IPlayStoreHelper.java
index 958a2be..2bcf1ee 100644
--- a/libraries/app-helpers/interfaces/common/src/android/platform/helpers/IPlayStoreHelper.java
+++ b/libraries/app-helpers/interfaces/common/src/android/platform/helpers/IPlayStoreHelper.java
@@ -54,4 +54,11 @@
      * the app completes uninstallation, though uninstallation cannot be guaranteed.
      */
     public void uninstallApp();
+
+    /**
+     * Setup expectations: YouTube is on the home page.
+     *
+     * <p>This method attempts to verify home page element of YouTube.
+     */
+    void verifyHomePage();
 }
diff --git a/libraries/app-helpers/interfaces/common/src/android/platform/helpers/ISettingsIntelligenceHelper.java b/libraries/app-helpers/interfaces/common/src/android/platform/helpers/ISettingsIntelligenceHelper.java
index e07b800..0c80eb6 100644
--- a/libraries/app-helpers/interfaces/common/src/android/platform/helpers/ISettingsIntelligenceHelper.java
+++ b/libraries/app-helpers/interfaces/common/src/android/platform/helpers/ISettingsIntelligenceHelper.java
@@ -36,6 +36,7 @@
     public static final String PAGE_ACTION_CONNECTED_CAST = "android.settings.CAST_SETTINGS";
     public static final String PAGE_ACTION_DARK_THEME = "android.settings.DARK_THEME_SETTINGS";
     public static final String PAGE_ACTION_DATA_SAVER = "android.settings.DATA_SAVER_SETTINGS";
+    public static final String PAGE_ACTION_DATE = "android.settings.DATE_SETTINGS";
     public static final String PAGE_ACTION_DIGITAL_WELLBEING =
             "com.google.android.apps.wellbeing/.settings.TopLevelSettingsActivity";
     public static final String PAGE_ACTION_DISPLAY = "android.settings.DISPLAY_SETTINGS";
@@ -78,4 +79,4 @@
      * <p>This method opens search page.
      */
     void openSearch();
-}
\ No newline at end of file
+}
diff --git a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IClockHelper.java b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IClockHelper.java
index 98c2c2b..aedb919 100644
--- a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IClockHelper.java
+++ b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IClockHelper.java
@@ -46,4 +46,22 @@
     default public void goToTimer() {
         throw new UnsupportedOperationException("Not yet implemented.");
     }
+
+    /**
+     * Setup expectations: Clock widget is open.
+     *
+     * <p>Create clock widget.
+     */
+    public default void createWidget() {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
+    /**
+     * Setup expectations: Clock is open.
+     *
+     * <p>Clicks Clock widget to go to Clock page.
+     */
+    public default void goToClockFromWidget() {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
 }
diff --git a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/INotificationHelper.java b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/INotificationHelper.java
index 4f65454..ac3f879 100644
--- a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/INotificationHelper.java
+++ b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/INotificationHelper.java
@@ -21,8 +21,11 @@
 import android.support.test.uiautomator.Direction;
 import android.support.test.uiautomator.UiObject2;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import java.util.List;
+
 /** An App Helper interface for the Notification. */
 public interface INotificationHelper extends IAppHelper {
 
@@ -53,6 +56,15 @@
     }
 
     /**
+     * Wait until pop-up notification dismissed
+     *
+     * @return true if notification is dismissed before internal timeout
+     */
+    default boolean waitPopUpNotificationAutoDismissed() {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
+    /**
      * Setup Expectations: None
      *
      * <p>Posts a number of notifications to the device. Successive calls to this should post new
@@ -99,10 +111,44 @@
     }
 
     /**
+     * Setup Expectations: None
+     *
+     * <p>Posts a number of notifications to the device with a groupId. Successive calls to this
+     * should post new notifications in addition to those previously posted. Note that this may fail
+     * if the helper has surpassed the system-defined limit for per-package notifications.
+     *
+     * @param count The number of notifications to post.
+     * @param pkg The application that will be launched by notifications.
+     * @param summary Summary text for this group notification
+     */
+    default void postGroupNotifications(int count, @Nullable String pkg, @NonNull String summary) {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
+    /**
+     * Get the default title text used for notification
+     *
+     * @return the default title text
+     */
+    @NonNull
+    default String getDefaultNotificationTitleText() {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
+    /**
+     * @return the notification shelf, containing overflow notifications that don't fit in the
+     *     screen. If unavailable, returns null.
+     */
+    @Nullable
+    default UiObject2 getNotificationShelf() {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
+    /**
      * Setup Expectations: Shade is open
      *
-     * <p>Posts a bubble notification. This notification is associated with a conversation shortcut,
-     * a BubbleMetadata, and in {@link android.app.Notification.MessagingStyle}.
+     * <p>Posts multiple bubble notification. These notifications are associated with a conversation
+     * shortcut, a BubbleMetadata, and in {@link android.app.Notification.MessagingStyle}.
      *
      * @param senderName Name of notification sender.
      * @param count How many bubble notifications to send.
@@ -112,6 +158,29 @@
     }
 
     /**
+     * Posts a bubble notification. This notification is associated with a conversation shortcut, a
+     * BubbleMetadata, and in {@link android.app.Notification.MessagingStyle}.
+     *
+     * @param senderName Name of notification sender.
+     * @param id An unique notification identifier.
+     * @param text Notification message content.
+     */
+    default void postBubbleNotification(String senderName, int id, String text) {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
+    /**
+     * Updates an existing notification
+     *
+     * @param senderName Name of notification sender.
+     * @param id An identifier of the notification to be updated.
+     * @param text Update message content.
+     */
+    default void updateBubbleNotification(String senderName, int id, String text) {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
+    /**
      * Return notification if found by text.
      *
      * @param text Text that notification contains.
@@ -121,6 +190,15 @@
     }
 
     /**
+     * Return expandableNotificationRows if found
+     *
+     * @return List of expandableNotificationRow
+     */
+    default List<UiObject2> getExpandableNotificationRows() {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
+    /**
      * Setup Expectations: Shade is open
      *
      * <p>Posts a conversation notification. This notification is associated with a conversation
@@ -184,25 +262,39 @@
     /**
      * Setup Expectations: Notification shade opened.
      *
-     * <p>Scrolls to the bottom of the notification shade and taps the "clear all" button if
-     * present.
+     * <p>Determines if the notification shade is showing the "empty shade view"
      */
-    default void clearAllNotifications() {
+    default boolean isShowingEmptyShade() {
         throw new UnsupportedOperationException("Not yet implemented.");
     }
 
     /**
-     * Setup Expectations: Notification snoozing is enabled.
+     * Setup Expectations: Notification shade opened.
      *
-     * <p>Empty the notification shade by first cancelling all of the test app's notifications, then
-     * snoozing all other notifications temporarily. Fails if any notifications are left in the
-     * shade.
-     *
-     * <p>Because unsnoozing can fail from command line, snoozing is implemented with a fixed time,
-     * so tests will take at least as long as the time limit given, plus some buffer built into this
-     * utility, to ensure that this test's snoozing will not interfere with other tests.
+     * <p>Determines if the notification shade is showing the "footer view", which indicates that
+     * there are nonzero notifications and that the shade is scrolled to the bottom.
      */
-    default void runWithEmptyNotificationShade(Runnable task, long taskTimeLimit) {
+    default boolean isShowingFooter() {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
+    /**
+     * Setup Expectations: Notification shade opened.
+     *
+     * <p>Scrolls to the bottom of the notification shade, as determined by the "manage" button in
+     * the footer being visible.
+     */
+    default void scrollToBottom() {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
+    /**
+     * Setup Expectations: Notification shade opened, clearable notifications exist.
+     *
+     * <p>Scrolls to the bottom of the notification shade and taps the "clear all" button, which
+     * must be present.
+     */
+    default void clearAllNotifications() {
         throw new UnsupportedOperationException("Not yet implemented.");
     }
 
@@ -323,6 +415,11 @@
         throw new UnsupportedOperationException("Not yet implemented.");
     }
 
+    /** Closes the notification shade by swiping up. */
+    default void swipeToClose() {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
     /**
      * Scroll feeds on Notifications screen and implement it by "swipe" API to control the distance.
      *
diff --git a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IPhotosHelper.java b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IPhotosHelper.java
index 4ebec54..bddef9f 100644
--- a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IPhotosHelper.java
+++ b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IPhotosHelper.java
@@ -190,4 +190,30 @@
 
     /** Setup expectation: Remove photos app content. */
     public void removeContent();
+
+    /**
+     * This method checks and waits whether any music is active. The music track is active when a
+     * video is playing, even the video is silent or the media volume is muted.
+     *
+     * @return true if any music tracks are active.
+     */
+    public boolean isMusicPlaying();
+
+    /**
+     * Setup expectation: Photos is open.
+     *
+     * <p>Check if device is now in Photos folder page.
+     *
+     * @return Returns true if device is in Photos folder page, false if not.
+     */
+    public boolean isOnDeviceFolderPage();
+
+    /**
+     * Setup expectation: Photos is open.
+     *
+     * <p>Check if device has video player.
+     *
+     * @return Returns true if device has video player, false if not.
+     */
+    public boolean hasVideoPlayer();
 }
diff --git a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IQuickSettingsHelper.java b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IQuickSettingsHelper.java
index fcecffb..f8b9ed8 100644
--- a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IQuickSettingsHelper.java
+++ b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IQuickSettingsHelper.java
@@ -16,33 +16,48 @@
 
 package android.platform.helpers;
 
-/** An App Helper interface for the Quick Settings bar. */
+/**
+ * An App Helper interface for the Quick Settings bar.
+ *
+ * @deprecated use {@link android.system.helpers.QuickSettingsHelper} instead.
+ */
+@Deprecated
 public interface IQuickSettingsHelper extends IAppHelper {
     /**
      * Represents the state of a Quick Setting. Currently this is limited to ON and OFF states;
      * however, other states will be added in the future, for example to differentiate between
      * paired and un-paired, active bluetooth states.
+     *
+     * @deprecated use {@link android.system.helpers.QuickSettingsHelper} instead.
      */
+    @Deprecated
     public enum State {
         ON,
         OFF,
+        UNAVAILABLE,
     }
 
-    /** Represents a Quick Setting switch that can be toggled ON and OFF during a test. */
+    /**
+     * Represents a Quick Setting switch that can be toggled ON and OFF during a test.
+     *
+     * @deprecated use {@link android.system.helpers.QuickSettingsHelper} instead.
+     */
+    @Deprecated
     public enum Setting {
-        AIRPLANE("Airplane", 2000),
-        AUTO_ROTATE("Auto-rotate", 2000),
-        BLUETOOTH("Bluetooth", 15000),
-        DO_NOT_DISTURB("Do Not Disturb", 2000),
-        FLASHLIGHT("Flashlight", 5000),
-        NIGHT_LIGHT("Night Light", 2000),
-        WIFI("Wi-Fi", 10000);
+        AIRPLANE("Airplane", "airplane", 2000),
+        AUTO_ROTATE("Auto-rotate", "rotation", 2000),
+        BLUETOOTH("Bluetooth", "bt", 15000),
+        DO_NOT_DISTURB("Do Not Disturb", "dnd", 2000),
+        FLASHLIGHT("Flashlight", "flashlight", 5000),
+        NIGHT_LIGHT("Night Light", "night", 2000);
 
         private final String mContentDescSubstring;
+        private final String mTileName;
         private final long mExpectedWait;
 
-        Setting(String substring, long wait) {
+        Setting(String substring, String tileName, long wait) {
             mContentDescSubstring = substring;
+            mTileName = tileName;
             mExpectedWait = wait;
         }
 
@@ -51,6 +66,11 @@
             return mContentDescSubstring;
         }
 
+        /** Returns a substring to identify the {@code Setting} by content description. */
+        public String getTileName() {
+            return mTileName;
+        }
+
         /** Returns the longest expected wait time for this option to be toggled ON or OFF. */
         public long getExpectedWait() {
             return mExpectedWait;
@@ -62,6 +82,12 @@
      * already found to be in {@code state}, then no operation is performed. There are no setup
      * requirements to call this method, except that {@code setting} is available from the test and
      * in the Quick Settings menu.
+     *
+     * @param setting The setting defined in enum {@link Setting}
+     * @param tileName The name of tile spec which recognized by quick settings host
+     * @param state The state of specific setting
+     * @deprecated use {@link android.system.helpers.QuickSettingsHelper} instead.
      */
-    public void toggleSetting(Setting setting, State state);
+    @Deprecated
+    public void toggleSetting(Setting setting, String tileName, State state);
 }
diff --git a/libraries/audio-test-harness/client-lib/Android.bp b/libraries/audio-test-harness/client-lib/Android.bp
index 118ce81..5b14d10 100644
--- a/libraries/audio-test-harness/client-lib/Android.bp
+++ b/libraries/audio-test-harness/client-lib/Android.bp
@@ -55,6 +55,7 @@
         "audiotestharness-client-corelib",
         "grpc-java-okhttp-client-lite",
         "audiotestharness-servicegrpclib-lite",
+        "audiotestharness-commonlib-lite",
         "guava",
     ],
     sdk_version: "current",
@@ -72,6 +73,7 @@
     static_libs: [
         "audiotestharness-client-grpclib",
         "grpc-java-core-inprocess",
+        "grpc-java-testing",
         "junit",
         "junit-params",
         "mockito",
diff --git a/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/core/AudioCaptureStream.java b/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/core/AudioCaptureStream.java
index 064970a..3e5bf83 100644
--- a/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/core/AudioCaptureStream.java
+++ b/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/core/AudioCaptureStream.java
@@ -19,6 +19,7 @@
 import com.android.media.audiotestharness.common.Defaults;
 import com.android.media.audiotestharness.proto.AudioFormatOuterClass.AudioFormat;
 
+import java.io.IOException;
 import java.io.InputStream;
 
 /**
@@ -28,6 +29,21 @@
 public abstract class AudioCaptureStream extends InputStream {
 
     /**
+     * Read method that reads a number of audio samples, up to the max into the provided array
+     * starting at offset. These samples are expected to be signed 16-bit values and thus are
+     * provided as an array of short values.
+     *
+     * <p>This method blocks until at least one complete sample is read, and ensures that no
+     * incomplete samples are read.
+     *
+     * @param samples the array to read into.
+     * @param offset the offset to use when writing into samples.
+     * @param len the maximum number of samples to read.
+     * @return the number of samples read into the provided array.
+     */
+    public abstract int read(short[] samples, int offset, int len) throws IOException;
+
+    /**
      * Returns the {@link AudioFormat} corresponding to this {@link AudioCaptureStream}, thus the
      * raw data exposed by this stream will be raw PCM samples matching this format.
      */
diff --git a/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/core/AudioTestHarnessCommunicationException.java b/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/core/AudioTestHarnessCommunicationException.java
new file mode 100644
index 0000000..8922d0b
--- /dev/null
+++ b/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/core/AudioTestHarnessCommunicationException.java
@@ -0,0 +1,27 @@
+/*
+ * 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.media.audiotestharness.client.core;
+
+/**
+ * {@link RuntimeException} used when the Audio Test Harness client libraries run into an issue
+ * while attempting to communicate with the host-side server.
+ */
+public class AudioTestHarnessCommunicationException extends RuntimeException {
+    public AudioTestHarnessCommunicationException(String message, Exception cause) {
+        super(message, cause);
+    }
+}
diff --git a/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/grpc/GrpcAudioCaptureStream.java b/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/grpc/GrpcAudioCaptureStream.java
index 4c7b909..650a8e8 100644
--- a/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/grpc/GrpcAudioCaptureStream.java
+++ b/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/grpc/GrpcAudioCaptureStream.java
@@ -17,69 +17,314 @@
 package com.android.media.audiotestharness.client.grpc;
 
 import com.android.media.audiotestharness.client.core.AudioCaptureStream;
+import com.android.media.audiotestharness.common.Defaults;
 import com.android.media.audiotestharness.proto.AudioTestHarnessGrpc;
+import com.android.media.audiotestharness.proto.AudioTestHarnessService;
+
+import com.google.common.base.Preconditions;
+
+import io.grpc.Context;
+import io.grpc.Status;
+import io.grpc.stub.StreamObserver;
 
 import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
-/** {@link AudioCaptureStream} that utilizes gRPC as its transfer mechanism. */
+/**
+ * {@link AudioCaptureStream} that utilizes gRPC as its transfer mechanism.
+ *
+ * <p>Utilizes Piped I/O streams with the gRPC call writing to the sending side of the pipe, and the
+ * exposed methods from the {@link java.io.InputStream} class being passed to the receiving end of
+ * the pipe.
+ */
 public class GrpcAudioCaptureStream extends AudioCaptureStream {
+    private static final Logger LOGGER = Logger.getLogger(GrpcAudioCaptureStream.class.getName());
 
-    // TODO(b/168817017): Implement this class to utlize gRPC and Piped I/O streams to provide an
-    // InputStream from which the audio samples can be read.
+    /**
+     * Size of the buffer used by the {@link PipedInputStream} to cache internal messages received
+     * over the gRPC connection before they are read by the client.
+     *
+     * <p>This value is currently equal to 35 chunks, 8960 bytes, or just about 100ms of audio
+     * recorded at CD quality.
+     */
+    private static final int BUFFER_SIZE = 35 * Defaults.CAPTURE_CHUNK_TARGET_SIZE_BYTES;
 
-    private final AudioTestHarnessGrpc.AudioTestHarnessStub mAudioTestHarnessStub;
+    private static final int NUM_CHANNELS_MONO = 1;
+    private static final int BITS_PER_SAMPLE_16BIT = 16;
+    private static final int BYTES_PER_SAMPLE_16BIT = BITS_PER_SAMPLE_16BIT / 8;
 
-    private GrpcAudioCaptureStream(AudioTestHarnessGrpc.AudioTestHarnessStub audioTestHarnessStub) {
-        mAudioTestHarnessStub = audioTestHarnessStub;
+    private final Context.CancellableContext mCancellableContext;
+    private final PipedInputStream mInputStream;
+    private final PipedOutputStream mOutputStream;
+
+    /**
+     * {@link Throwable} field used when the underlying gRPC call has an error. This error is
+     * propagated back from the gRPC thread through a callback within the {@link
+     * PipedCaptureChunkStreamObserver}. This field is volatile, as it will only be read by or
+     * written to by single separate threads, but we want to make sure the reading thread is
+     * immediately notified when an error occurs. Furthermore, this is safe since the underlying
+     * Throwable will be immutable.
+     */
+    private volatile Throwable mGrpcError = null;
+
+    private GrpcAudioCaptureStream(
+            Context.CancellableContext cancellableContext,
+            PipedInputStream inputStream,
+            PipedOutputStream outputStream) {
+        mCancellableContext = cancellableContext;
+        mInputStream = inputStream;
+        mOutputStream = outputStream;
     }
 
     static GrpcAudioCaptureStream create(
-            AudioTestHarnessGrpc.AudioTestHarnessStub audioTestHarnessStub) {
-        return new GrpcAudioCaptureStream(audioTestHarnessStub);
+            AudioTestHarnessGrpc.AudioTestHarnessStub audioTestHarnessStub,
+            ScheduledExecutorService scheduledExecutorService)
+            throws IOException {
+        Preconditions.checkNotNull(audioTestHarnessStub, "audioTestHarnessStub cannot be null.");
+        Preconditions.checkNotNull(
+                scheduledExecutorService, "scheduledExecutorService cannot be null.");
+
+        // Create the piped streams that back the stream stream itself.
+        PipedInputStream pipedInputStream = new PipedInputStream(BUFFER_SIZE);
+        PipedOutputStream pipedOutputStream;
+        try {
+            pipedOutputStream = new PipedOutputStream(pipedInputStream);
+        } catch (IOException ioe) {
+            throw new IOException(
+                    "Unable to create Capture Stream due to pipe creation failure", ioe);
+        }
+
+        // Start the gRPC call with a context that can be used for cancellation later.
+        Context.CancellableContext grpcContext =
+                Context.current()
+                        .withCancellation()
+                        .withDeadlineAfter(
+                                Defaults.SYSTEM_TIMEOUT.getSeconds(),
+                                TimeUnit.SECONDS,
+                                scheduledExecutorService);
+
+        GrpcAudioCaptureStream captureStream =
+                new GrpcAudioCaptureStream(grpcContext, pipedInputStream, pipedOutputStream);
+
+        try {
+            grpcContext.call(
+                    () -> {
+                        audioTestHarnessStub.capture(
+                                AudioTestHarnessService.CaptureRequest.getDefaultInstance(),
+                                new PipedCaptureChunkStreamObserver(
+                                        pipedOutputStream,
+                                        (throwable) -> captureStream.mGrpcError = throwable));
+                        return true;
+                    });
+        } catch (Exception e) {
+            throw new IOException("Audio Test Harness gRPC Communication Error", e);
+        }
+
+        return captureStream;
     }
 
     @Override
     public int read(byte[] b) throws IOException {
-        return super.read(b);
+        if (mGrpcError != null) {
+            throw new IOException("Audio Test Harness gRPC Communication Error", mGrpcError);
+        }
+
+        try {
+            return mInputStream.read(b);
+        } catch (IOException ioe) {
+            throw new IOException("Audio Test Harness gRPC Internal Error", ioe);
+        }
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * <p>In general test use, this method is not preferred, and instead the {@link #read(short[],
+     * int, int)} method should be used as that method provides better handling to ensure that
+     * complete samples are read. By contrast, this (and the other read methods) only provide access
+     * to the raw byte data coming from the host.
+     *
+     * <p>The standard read methods can be used with the {@link #getAudioFormat()} to build
+     * extensions on the raw data.
+     */
     @Override
     public int read(byte[] b, int off, int len) throws IOException {
-        return super.read(b, off, len);
+        if (mGrpcError != null) {
+            throw new IOException("Audio Test Harness gRPC Communication Error", mGrpcError);
+        }
+
+        try {
+            return mInputStream.read(b, off, len);
+        } catch (IOException ioe) {
+            throw new IOException("Audio Test Harness gRPC Internal Error", ioe);
+        }
     }
 
     @Override
     public long skip(long n) throws IOException {
-        return super.skip(n);
+        if (mGrpcError != null) {
+            throw new IOException("Audio Test Harness gRPC Communication Error", mGrpcError);
+        }
+
+        return mInputStream.skip(n);
     }
 
     @Override
     public int available() throws IOException {
-        return super.available();
+        if (mGrpcError != null) {
+            throw new IOException("Audio Test Harness gRPC Communication Error", mGrpcError);
+        }
+
+        return mInputStream.available();
     }
 
     @Override
     public void close() throws IOException {
-        super.close();
+        mCancellableContext.cancel(
+                Status.CANCELLED.withDescription("Capture stopped by client").asException());
+
+        mInputStream.close();
+        mOutputStream.close();
     }
 
     @Override
     public synchronized void mark(int readlimit) {
-        super.mark(readlimit);
+        mInputStream.mark(readlimit);
     }
 
     @Override
     public synchronized void reset() throws IOException {
-        super.reset();
+        if (mGrpcError != null) {
+            throw new IOException("Audio Test Harness gRPC Communication Error", mGrpcError);
+        }
+
+        try {
+            mInputStream.reset();
+        } catch (IOException ioe) {
+            throw new IOException("Audio Test Harness gRPC Internal Error", ioe);
+        }
     }
 
     @Override
     public boolean markSupported() {
-        return super.markSupported();
+        return mInputStream.markSupported();
     }
 
     @Override
     public int read() throws IOException {
-        return 0;
+        if (mGrpcError != null) {
+            throw new IOException("Audio Test Harness gRPC Communication Error", mGrpcError);
+        }
+
+        try {
+            return mInputStream.read();
+        } catch (IOException ioe) {
+            throw new IOException("Audio Test Harness gRPC Internal Error", ioe);
+        }
+    }
+
+    @Override
+    public int read(short[] samples, int offset, int len) throws IOException {
+        if (offset < 0 || offset > samples.length) {
+            throw new IllegalArgumentException("Invalid offset");
+        }
+
+        if (len < 0 || len + offset > samples.length) {
+            throw new IllegalArgumentException("Invalid len");
+        }
+
+        if (getAudioFormat().getSampleSizeBits() != BITS_PER_SAMPLE_16BIT
+                || !getAudioFormat().getSigned()) {
+            throw new IllegalArgumentException(
+                    "Bad audio format, this method can only be used to read signed 16-bit integer "
+                            + "audio samples");
+        }
+
+        // Nobody should ask for zero samples, but if they do, simply return since we have no
+        // work to do.
+        if (len == 0) {
+            return 0;
+        }
+
+        // Read from the stream, ensuring that we either read up to the maximum our buffer can
+        // handle, or we are reading complete samples from the stream as determined by the
+        // number of bytes we expect per sample and the number of channels.
+        byte[] buffer = new byte[len * BYTES_PER_SAMPLE_16BIT * getAudioFormat().getChannels()];
+        int read = 0;
+        do {
+            read += read(buffer, read, buffer.length - read);
+        } while (read % (BYTES_PER_SAMPLE_16BIT * getAudioFormat().getChannels()) != 0);
+
+        // Calculate the number of frames we were able to read, and copy that exact
+        // number of samples into the provided array.
+        int samplesRead = read / BYTES_PER_SAMPLE_16BIT;
+        ByteBuffer.wrap(buffer)
+                .order(
+                        getAudioFormat().getBigEndian()
+                                ? ByteOrder.BIG_ENDIAN
+                                : ByteOrder.LITTLE_ENDIAN)
+                .asShortBuffer()
+                .get(samples, offset, samplesRead);
+
+        return samplesRead;
+    }
+
+    /**
+     * {@link StreamObserver} that publishes audio samples received over a gRPC connection to a
+     * piped output stream.
+     */
+    private static final class PipedCaptureChunkStreamObserver
+            implements StreamObserver<AudioTestHarnessService.CaptureChunk> {
+        private static final Logger LOGGER =
+                Logger.getLogger(PipedCaptureChunkStreamObserver.class.getName());
+
+        private final PipedOutputStream mPipedOutputStream;
+        private final Consumer<Throwable> mOnErrorCallback;
+
+        private PipedCaptureChunkStreamObserver(
+                PipedOutputStream pipedOutputStream, Consumer<Throwable> onErrorCallback) {
+            mPipedOutputStream = pipedOutputStream;
+            mOnErrorCallback = onErrorCallback;
+        }
+
+        @Override
+        public void onNext(AudioTestHarnessService.CaptureChunk value) {
+            try {
+                mPipedOutputStream.write(value.getData().toByteArray());
+            } catch (IOException ioe) {
+                LOGGER.log(
+                        Level.WARNING,
+                        "Unable to write segment of audio data, data may have been lost",
+                        ioe);
+            }
+        }
+
+        @Override
+        public void onError(Throwable t) {
+            // Immediately propagate the throwable back to the callback.
+            mOnErrorCallback.accept(t);
+            LOGGER.log(Level.WARNING, "onError called: ", t);
+
+            // On error, close the stream so that any corresponding input streams will also
+            // throw exceptions when attempting to read.
+            try {
+                mPipedOutputStream.close();
+            } catch (IOException ioe) {
+                LOGGER.log(Level.WARNING, "Unable to close the pipedOutputStream", ioe);
+            }
+        }
+
+        @Override
+        public void onCompleted() {
+            LOGGER.log(Level.FINE, "onCompleted called");
+        }
     }
 }
diff --git a/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/grpc/GrpcAudioCaptureStreamFactory.java b/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/grpc/GrpcAudioCaptureStreamFactory.java
index 5d2fd46..a40f1d2 100644
--- a/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/grpc/GrpcAudioCaptureStreamFactory.java
+++ b/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/grpc/GrpcAudioCaptureStreamFactory.java
@@ -20,17 +20,27 @@
 
 import com.google.common.base.Preconditions;
 
+import java.io.IOException;
+import java.util.concurrent.ScheduledExecutorService;
+
 /** Factory for the {@link GrpcAudioCaptureStream}. */
 public class GrpcAudioCaptureStreamFactory {
-    private GrpcAudioCaptureStreamFactory() {}
 
-    public static GrpcAudioCaptureStreamFactory create() {
-        return new GrpcAudioCaptureStreamFactory();
+    private final ScheduledExecutorService mScheduledExecutorService;
+
+    private GrpcAudioCaptureStreamFactory(ScheduledExecutorService scheduledExecutorService) {
+        mScheduledExecutorService = scheduledExecutorService;
     }
 
-    GrpcAudioCaptureStream newStream(
-            AudioTestHarnessGrpc.AudioTestHarnessStub audioTestHarnessStub) {
+    public static GrpcAudioCaptureStreamFactory create(
+            ScheduledExecutorService scheduledExecutorService) {
+        Preconditions.checkNotNull(scheduledExecutorService);
+        return new GrpcAudioCaptureStreamFactory(scheduledExecutorService);
+    }
+
+    GrpcAudioCaptureStream newStream(AudioTestHarnessGrpc.AudioTestHarnessStub audioTestHarnessStub)
+            throws IOException {
         Preconditions.checkNotNull(audioTestHarnessStub, "audioTestHarnessStub cannot be null");
-        return GrpcAudioCaptureStream.create(audioTestHarnessStub);
+        return GrpcAudioCaptureStream.create(audioTestHarnessStub, mScheduledExecutorService);
     }
 }
diff --git a/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/grpc/GrpcAudioTestHarnessClient.java b/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/grpc/GrpcAudioTestHarnessClient.java
index b8fa93b..c93dbdb 100644
--- a/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/grpc/GrpcAudioTestHarnessClient.java
+++ b/libraries/audio-test-harness/client-lib/src/main/java/com/android/media/audiotestharness/client/grpc/GrpcAudioTestHarnessClient.java
@@ -18,6 +18,8 @@
 
 import com.android.media.audiotestharness.client.core.AudioCaptureStream;
 import com.android.media.audiotestharness.client.core.AudioTestHarnessClient;
+import com.android.media.audiotestharness.client.core.AudioTestHarnessCommunicationException;
+import com.android.media.audiotestharness.common.Defaults;
 import com.android.media.audiotestharness.proto.AudioTestHarnessGrpc;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -27,8 +29,9 @@
 import io.grpc.ManagedChannelBuilder;
 
 import java.io.IOException;
-import java.util.concurrent.Executor;
+import java.util.Locale;
 import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -40,6 +43,7 @@
     private static final int MIN_PORT = 0;
     private static final int MAX_PORT = 65535;
     private static final int DEFAULT_NUM_THREADS = 8;
+    private static final String LOCALHOST = "localhost";
 
     private final ManagedChannel mManagedChannel;
     private final GrpcAudioCaptureStreamFactory mGrpcAudioCaptureStreamFactory;
@@ -52,17 +56,23 @@
     }
 
     public static GrpcAudioTestHarnessClient.Builder builder() {
-        return new Builder().setCaptureStreamFactory(GrpcAudioCaptureStreamFactory.create());
+        return new Builder().setAddress(LOCALHOST, Defaults.DEVICE_PORT);
     }
 
     @Override
     public AudioCaptureStream startCapture() {
-        AudioCaptureStream newStream =
-                mGrpcAudioCaptureStreamFactory.newStream(
-                        AudioTestHarnessGrpc.newStub(mManagedChannel));
+        AudioCaptureStream newStream;
+
+        try {
+            newStream =
+                    mGrpcAudioCaptureStreamFactory.newStream(
+                            AudioTestHarnessGrpc.newStub(mManagedChannel));
+        } catch (IOException ioe) {
+            throw new AudioTestHarnessCommunicationException(
+                    "Unable to start a new capture stream.", ioe);
+        }
 
         mAudioCaptureStreams.add(newStream);
-
         return newStream;
     }
 
@@ -88,7 +98,7 @@
 
         private String mHostname;
         private int mPort;
-        private Executor mExecutor;
+        private ScheduledExecutorService mExecutor;
         private GrpcAudioCaptureStreamFactory mGrpcAudioCaptureStreamFactory;
         private ManagedChannel mManagedChannel;
 
@@ -98,7 +108,11 @@
             Preconditions.checkNotNull(hostname, "Hostname cannot be null");
             Preconditions.checkArgument(
                     port >= MIN_PORT && port <= MAX_PORT,
-                    String.format("Port expected in range [%d, %d]", MIN_PORT, MAX_PORT));
+                    String.format(
+                            Locale.getDefault(),
+                            "Port expected in range [%d, %d]",
+                            MIN_PORT,
+                            MAX_PORT));
 
             mHostname = hostname;
             mPort = port;
@@ -106,7 +120,7 @@
             return this;
         }
 
-        public Builder setExecutor(Executor executor) {
+        public Builder setExecutor(ScheduledExecutorService executor) {
             mExecutor = executor;
             return this;
         }
@@ -131,14 +145,20 @@
                 Preconditions.checkState(mHostname != null, "Address must be set.");
 
                 if (mExecutor == null) {
-                    mExecutor = Executors.newFixedThreadPool(DEFAULT_NUM_THREADS);
+                    mExecutor = Executors.newScheduledThreadPool(DEFAULT_NUM_THREADS);
+                }
+
+                if (mGrpcAudioCaptureStreamFactory == null) {
+                    mGrpcAudioCaptureStreamFactory =
+                            GrpcAudioCaptureStreamFactory.create(mExecutor);
                 }
 
                 mManagedChannel =
                         ManagedChannelBuilder.forAddress(mHostname, mPort)
-                                .executor(mExecutor)
                                 .usePlaintext()
+                                .executor(mExecutor)
                                 .build();
+                LOGGER.info(String.format("New Client on for %s:%d", mHostname, mPort));
             }
 
             return new GrpcAudioTestHarnessClient(mGrpcAudioCaptureStreamFactory, mManagedChannel);
diff --git a/libraries/audio-test-harness/server/src/test/java/com/android/media/audiotestharness/server/AudioTestHarnessTestImpl.java b/libraries/audio-test-harness/client-lib/src/test/java/com/android/media/audiotestharness/client/grpc/AudioTestHarnessTestImpl.java
similarity index 84%
rename from libraries/audio-test-harness/server/src/test/java/com/android/media/audiotestharness/server/AudioTestHarnessTestImpl.java
rename to libraries/audio-test-harness/client-lib/src/test/java/com/android/media/audiotestharness/client/grpc/AudioTestHarnessTestImpl.java
index a11d01d..59bdec2 100644
--- a/libraries/audio-test-harness/server/src/test/java/com/android/media/audiotestharness/server/AudioTestHarnessTestImpl.java
+++ b/libraries/audio-test-harness/client-lib/src/test/java/com/android/media/audiotestharness/client/grpc/AudioTestHarnessTestImpl.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.media.audiotestharness.server;
+package com.android.media.audiotestharness.client.grpc;
 
 import com.android.media.audiotestharness.proto.AudioTestHarnessGrpc;
 import com.android.media.audiotestharness.proto.AudioTestHarnessService;
@@ -24,13 +24,12 @@
 import io.grpc.stub.StreamObserver;
 
 /**
- * Test implementation of the {@link
- * com.android.media.audiotestharness.proto.AudioTestHarnessGrpc.AudioTestHarnessImplBase} that
- * contains stubbed methods.
+ * Test implementation of the {@link AudioTestHarnessGrpc.AudioTestHarnessImplBase} that contains
+ * stubbed methods.
  */
 public class AudioTestHarnessTestImpl extends AudioTestHarnessGrpc.AudioTestHarnessImplBase {
 
-    public static final byte[] MESSAGE = {0x0, 0x1, 0x2, 0x3};
+    public static final byte[] MESSAGE = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7};
 
     /**
      * Test implementation of the <code>Capture</code> procedure that simply ignores the request and
diff --git a/libraries/audio-test-harness/client-lib/src/test/java/com/android/media/audiotestharness/client/grpc/GrpcAudioCaptureStreamTests.java b/libraries/audio-test-harness/client-lib/src/test/java/com/android/media/audiotestharness/client/grpc/GrpcAudioCaptureStreamTests.java
index b490169..5c43515 100644
--- a/libraries/audio-test-harness/client-lib/src/test/java/com/android/media/audiotestharness/client/grpc/GrpcAudioCaptureStreamTests.java
+++ b/libraries/audio-test-harness/client-lib/src/test/java/com/android/media/audiotestharness/client/grpc/GrpcAudioCaptureStreamTests.java
@@ -16,25 +16,290 @@
 
 package com.android.media.audiotestharness.client.grpc;
 
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
 
+import com.android.media.audiotestharness.proto.AudioTestHarnessGrpc;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import io.grpc.ManagedChannel;
+import io.grpc.ManagedChannelBuilder;
+import io.grpc.inprocess.InProcessChannelBuilder;
+import io.grpc.inprocess.InProcessServerBuilder;
+import io.grpc.testing.GrpcCleanupRule;
+
+import org.hamcrest.CoreMatchers;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.time.Duration;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 @RunWith(JUnit4.class)
 public class GrpcAudioCaptureStreamTests {
 
+    @Rule public GrpcCleanupRule mGrpcCleanupRule = new GrpcCleanupRule();
+
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Rule public ExpectedException mExceptionRule = ExpectedException.none();
+
+    @Mock ScheduledExecutorService mScheduledExecutorService;
+
+    private ListeningExecutorService mListeningExecutorService;
+
     private GrpcAudioCaptureStreamFactory mGrpcAudioCaptureStreamFactory;
 
+    private AudioTestHarnessGrpc.AudioTestHarnessStub mAudioTestHarnessStub;
+
     @Before
     public void setUp() throws Exception {
-        mGrpcAudioCaptureStreamFactory = GrpcAudioCaptureStreamFactory.create();
+        mListeningExecutorService = MoreExecutors.newDirectExecutorService();
+
+        String serverName = InProcessServerBuilder.generateName();
+
+        mGrpcCleanupRule.register(
+                InProcessServerBuilder.forName(serverName)
+                        .executor(mListeningExecutorService)
+                        .addService(new AudioTestHarnessTestImpl())
+                        .build()
+                        .start());
+
+        ManagedChannel channel =
+                mGrpcCleanupRule.register(
+                        InProcessChannelBuilder.forName(serverName)
+                                .executor(mListeningExecutorService)
+                                .build());
+        mAudioTestHarnessStub = AudioTestHarnessGrpc.newStub(channel);
+
+        mGrpcAudioCaptureStreamFactory =
+                GrpcAudioCaptureStreamFactory.create(mScheduledExecutorService);
+    }
+
+    @Test
+    public void create_returnsNotNullInstance() throws Exception {
+        assertNotNull(
+                GrpcAudioCaptureStream.create(mAudioTestHarnessStub, mScheduledExecutorService));
+    }
+
+    @Test
+    public void create_schedulesCancellationTaskWithProperDeadline() throws Exception {
+        ArgumentCaptor<Long> timeValueCaptor = ArgumentCaptor.forClass(Long.class);
+        ArgumentCaptor<TimeUnit> timeUnitCaptor = ArgumentCaptor.forClass(TimeUnit.class);
+
+        GrpcAudioCaptureStream.create(mAudioTestHarnessStub, mScheduledExecutorService);
+
+        // Grab the timeout from the call to the Scheduled Executor Service as a Duration.
+        verify(mScheduledExecutorService)
+                .schedule((Runnable) any(), timeValueCaptor.capture(), timeUnitCaptor.capture());
+        Duration actualScheduledDuration =
+                Duration.ofNanos(
+                        TimeUnit.NANOSECONDS.convert(
+                                timeValueCaptor.getValue(), timeUnitCaptor.getValue()));
+
+        // Ensure that we are within our expected timeout within a 1s epsilon.
+        Duration difference = actualScheduledDuration.minus(Duration.ofHours(1)).abs();
+        assertTrue(difference.getSeconds() < 1L);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void newStream_throwsNullPointerException_nullStub() throws Exception {
+        assertNotNull(mGrpcAudioCaptureStreamFactory.newStream(/* audioTestHarnessStub= */ null));
     }
 
     @Test(expected = NullPointerException.class)
     public void create_throwsNullPointerException_nullStub() throws Exception {
-        assertNotNull(mGrpcAudioCaptureStreamFactory.newStream(/* audioTestHarnessStub= */ null));
+        GrpcAudioCaptureStream.create(/* audioTestHarnessStub= */ null, mScheduledExecutorService);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void create_throwsNullPointerException_nullScheduledExecutorService() throws Exception {
+        GrpcAudioCaptureStream.create(mAudioTestHarnessStub, /* scheduledExecutorService= */ null);
+    }
+
+    @Test
+    public void available_throwsProperIOException_grpcError() throws Exception {
+        expectGrpcCommunicationErrorException();
+        GrpcAudioCaptureStream grpcAudioCaptureStream = buildCaptureStreamWithPendingGrpcError();
+
+        grpcAudioCaptureStream.available();
+    }
+
+    @Test
+    public void read_returnsProperDataFromGrpc() throws Exception {
+        GrpcAudioCaptureStream grpcAudioCaptureStream =
+                GrpcAudioCaptureStream.create(mAudioTestHarnessStub, mScheduledExecutorService);
+
+        byte[] readBytes = new byte[AudioTestHarnessTestImpl.MESSAGE.length];
+
+        int numBytesRead = grpcAudioCaptureStream.read(readBytes);
+
+        assertEquals(AudioTestHarnessTestImpl.MESSAGE.length, numBytesRead);
+        assertArrayEquals(AudioTestHarnessTestImpl.MESSAGE, readBytes);
+    }
+
+    @Test
+    public void read_singleByte_throwsProperIOException_whenStreamClosed() throws Exception {
+        expectInternalErrorException();
+        GrpcAudioCaptureStream grpcAudioCaptureStream =
+                GrpcAudioCaptureStream.create(mAudioTestHarnessStub, mScheduledExecutorService);
+        grpcAudioCaptureStream.close();
+
+        grpcAudioCaptureStream.read();
+    }
+
+    @Test
+    public void read_multipleBytes_throwsProperIOException_whenStreamClosed() throws Exception {
+        expectInternalErrorException();
+        GrpcAudioCaptureStream grpcAudioCaptureStream =
+                GrpcAudioCaptureStream.create(mAudioTestHarnessStub, mScheduledExecutorService);
+
+        grpcAudioCaptureStream.close();
+
+        grpcAudioCaptureStream.read(new byte[AudioTestHarnessTestImpl.MESSAGE.length]);
+    }
+
+    @Test
+    public void read_multipleBytesWithOffset_throwsProperIOException_whenStreamClosed()
+            throws Exception {
+        expectInternalErrorException();
+        GrpcAudioCaptureStream grpcAudioCaptureStream =
+                GrpcAudioCaptureStream.create(mAudioTestHarnessStub, mScheduledExecutorService);
+        grpcAudioCaptureStream.close();
+
+        grpcAudioCaptureStream.read(
+                new byte[AudioTestHarnessTestImpl.MESSAGE.length], /* off= */ 1, /* len= */ 2);
+    }
+
+    @Test
+    public void read_throwsIOException_grpcError() throws Exception {
+        expectGrpcCommunicationErrorException();
+        GrpcAudioCaptureStream grpcAudioCaptureStream = buildCaptureStreamWithPendingGrpcError();
+
+        grpcAudioCaptureStream.read(new byte[AudioTestHarnessTestImpl.MESSAGE.length]);
+    }
+
+    @Test
+    public void read_shortSamples_readsAsExpected() throws Exception {
+        GrpcAudioCaptureStream grpcAudioCaptureStream =
+                GrpcAudioCaptureStream.create(mAudioTestHarnessStub, mScheduledExecutorService);
+        short[] expected = new short[AudioTestHarnessTestImpl.MESSAGE.length / 2];
+        ByteBuffer.wrap(AudioTestHarnessTestImpl.MESSAGE)
+                .order(ByteOrder.LITTLE_ENDIAN)
+                .asShortBuffer()
+                .get(expected);
+
+        short[] actual = new short[AudioTestHarnessTestImpl.MESSAGE.length / 2];
+        int numRead =
+                grpcAudioCaptureStream.read(actual, /* offset= */ 0, /* max= */ actual.length);
+
+        assertArrayEquals(expected, actual);
+        assertEquals(AudioTestHarnessTestImpl.MESSAGE.length / 2, numRead);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void read_shortSamples_throwsIllegalArgumentException_negativeOffset() throws Exception {
+        GrpcAudioCaptureStream grpcAudioCaptureStream =
+                GrpcAudioCaptureStream.create(mAudioTestHarnessStub, mScheduledExecutorService);
+        grpcAudioCaptureStream.read(/* samples= */ new short[4], /* offset= */ -25, /* max= */ 1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void read_shortSamples_throwsIllegalArgumentException_offsetTooLarge() throws Exception {
+        GrpcAudioCaptureStream grpcAudioCaptureStream =
+                GrpcAudioCaptureStream.create(mAudioTestHarnessStub, mScheduledExecutorService);
+        grpcAudioCaptureStream.read(/* samples= */ new short[4], /* offset= */ 25, /* max= */ 1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void read_shortSamples_throwsIllegalArgumentException_negativeLen() throws Exception {
+        GrpcAudioCaptureStream grpcAudioCaptureStream =
+                GrpcAudioCaptureStream.create(mAudioTestHarnessStub, mScheduledExecutorService);
+        grpcAudioCaptureStream.read(/* samples= */ new short[4], /* offset= */ 0, /* max= */ -24);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void read_shortSamples_throwsIllegalArgumentException_lenTooLarge() throws Exception {
+        GrpcAudioCaptureStream grpcAudioCaptureStream =
+                GrpcAudioCaptureStream.create(mAudioTestHarnessStub, mScheduledExecutorService);
+        grpcAudioCaptureStream.read(/* samples= */ new short[4], /* offset= */ 0, /* max= */ 24);
+    }
+
+    @Test
+    public void reset_throwsProperIOException_whenStreamClosed() throws Exception {
+        expectInternalErrorException();
+        GrpcAudioCaptureStream grpcAudioCaptureStream =
+                GrpcAudioCaptureStream.create(mAudioTestHarnessStub, mScheduledExecutorService);
+        grpcAudioCaptureStream.close();
+
+        grpcAudioCaptureStream.reset();
+    }
+
+    @Test
+    public void reset_throwsProperIOException_grpcError() throws Exception {
+        expectGrpcCommunicationErrorException();
+        GrpcAudioCaptureStream grpcAudioCaptureStream = buildCaptureStreamWithPendingGrpcError();
+
+        grpcAudioCaptureStream.reset();
+    }
+
+    @Test
+    public void skip_throwsProperIOException_grpcError() throws Exception {
+        expectGrpcCommunicationErrorException();
+        GrpcAudioCaptureStream grpcAudioCaptureStream = buildCaptureStreamWithPendingGrpcError();
+
+        grpcAudioCaptureStream.skip(/* n= */ 1);
+    }
+
+    /**
+     * Builds a new {@link GrpcAudioCaptureStream} with a pending gRPC error internally.
+     *
+     * <p>This method is used to verify that gRPC errors take precedence and are propagated properly
+     * to callers.
+     */
+    private GrpcAudioCaptureStream buildCaptureStreamWithPendingGrpcError() throws Exception {
+        AudioTestHarnessGrpc.AudioTestHarnessStub disconnectedStub =
+                AudioTestHarnessGrpc.newStub(
+                        mGrpcCleanupRule.register(
+                                ManagedChannelBuilder.forAddress("localhost", 12345)
+                                        .executor(mListeningExecutorService)
+                                        .build()));
+
+        GrpcAudioCaptureStream grpcAudioCaptureStream =
+                GrpcAudioCaptureStream.create(disconnectedStub, mScheduledExecutorService);
+
+        // Read once from the stream, giving the gRPC exception time to propagate. Even with
+        // the direct executor this is necessary and *should* be deterministic.
+        grpcAudioCaptureStream.read(new byte[1]);
+        return grpcAudioCaptureStream;
+    }
+
+    /** Configures the exception rule to expect the gRPC Communication Error exception. */
+    private void expectGrpcCommunicationErrorException() throws Exception {
+        mExceptionRule.expectMessage("Audio Test Harness gRPC Communication Error");
+        mExceptionRule.expectCause(CoreMatchers.notNullValue(Throwable.class));
+    }
+
+    /** Configures the exception rule to expect the gRPC Internal Error exception. */
+    private void expectInternalErrorException() throws Exception {
+        mExceptionRule.expectMessage("Audio Test Harness gRPC Internal Error");
+        mExceptionRule.expectCause(CoreMatchers.notNullValue(Throwable.class));
     }
 }
diff --git a/libraries/audio-test-harness/client-lib/src/test/java/com/android/media/audiotestharness/client/grpc/GrpcAudioTestHarnessClientTests.java b/libraries/audio-test-harness/client-lib/src/test/java/com/android/media/audiotestharness/client/grpc/GrpcAudioTestHarnessClientTests.java
index 0beb234..d0dec4f 100644
--- a/libraries/audio-test-harness/client-lib/src/test/java/com/android/media/audiotestharness/client/grpc/GrpcAudioTestHarnessClientTests.java
+++ b/libraries/audio-test-harness/client-lib/src/test/java/com/android/media/audiotestharness/client/grpc/GrpcAudioTestHarnessClientTests.java
@@ -63,11 +63,6 @@
         GrpcAudioTestHarnessClient.builder().setAddress("localhost", /* port= */ -123);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void build_throwsIllegalStateException_hostNotSet() throws Exception {
-        GrpcAudioTestHarnessClient.builder().build();
-    }
-
     @Test
     @Parameters(method = "getBuildParameters")
     public void build_returnsNonNullInstance_validParameters(
@@ -81,7 +76,7 @@
             {
                 GrpcAudioTestHarnessClient.builder()
                         .setAddress("service.google.com", 49152)
-                        .setExecutor(Executors.newSingleThreadExecutor())
+                        .setExecutor(Executors.newSingleThreadScheduledExecutor())
             },
             {
                 GrpcAudioTestHarnessClient.builder()
@@ -133,7 +128,7 @@
         verify(mGrpcAudioCaptureStream).close();
     }
 
-    public GrpcAudioTestHarnessClient initMocksAndClient() {
+    public GrpcAudioTestHarnessClient initMocksAndClient() throws Exception {
         when(mGrpcAudioCaptureStreamFactory.newStream(any())).thenReturn(mGrpcAudioCaptureStream);
         return GrpcAudioTestHarnessClient.builder()
                 .setManagedChannel(mManagedChannel)
diff --git a/libraries/audio-test-harness/common/src/main/java/com/android/media/audiotestharness/common/Defaults.java b/libraries/audio-test-harness/common/src/main/java/com/android/media/audiotestharness/common/Defaults.java
index 7ee2a8a..ca4a7bd 100644
--- a/libraries/audio-test-harness/common/src/main/java/com/android/media/audiotestharness/common/Defaults.java
+++ b/libraries/audio-test-harness/common/src/main/java/com/android/media/audiotestharness/common/Defaults.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -19,6 +19,8 @@
 import com.android.media.audiotestharness.proto.AudioDeviceOuterClass.AudioDevice;
 import com.android.media.audiotestharness.proto.AudioFormatOuterClass.AudioFormat;
 
+import java.time.Duration;
+
 /**
  * Contains all the defaults for the Audio Test Harness system shared between the client and server
  * libraries.
@@ -48,7 +50,24 @@
      */
     public static final AudioDevice AUDIO_DEVICE =
             AudioDevice.newBuilder()
-                    .setName("UMM6 [plughw:0,0]")
+                    .setName("UMM-6")
                     .addCapabilities(AudioDevice.Capability.CAPTURE)
                     .build();
+
+    /** Target size for each chunk captured and sent from host to client. */
+    public static final int CAPTURE_CHUNK_TARGET_SIZE_BYTES = 256;
+
+    /**
+     * Timeout for all calls between client and host at which point any outstanding calls will be
+     * cancelled or aborted.
+     */
+    public static final Duration SYSTEM_TIMEOUT = Duration.ofHours(1);
+
+    /**
+     * The port that the device attempts to connect to by default.
+     *
+     * <p>During test set up actions, requests are forwarded from this port on the device to the
+     * server port on the host.
+     */
+    public static final int DEVICE_PORT = 55555;
 }
diff --git a/libraries/audio-test-harness/instrumentation/Android.bp b/libraries/audio-test-harness/instrumentation/Android.bp
new file mode 100644
index 0000000..84da533
--- /dev/null
+++ b/libraries/audio-test-harness/instrumentation/Android.bp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "audiotestharness-instrumentation-app",
+    srcs: ["src/**/*.java"],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    static_libs: [
+        "androidx.test.ext.junit",
+        "androidx.test.rules",
+        "audiotestharness-client-grpclib",
+    ],
+    dex_preopt: {
+        enabled: false,
+    },
+}
diff --git a/libraries/audio-test-harness/instrumentation/AndroidManifest.xml b/libraries/audio-test-harness/instrumentation/AndroidManifest.xml
new file mode 100644
index 0000000..a723a96
--- /dev/null
+++ b/libraries/audio-test-harness/instrumentation/AndroidManifest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.media.audiotestharness.instrumentation">
+
+    <uses-sdk android:minSdkVersion="26"/>
+
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+    <uses-permission android:name="android.permission.CAMERA"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+
+    <application
+        android:usesCleartextTraffic="true">
+        <uses-library android:name="android.test.runner"/>
+        <activity android:name="MainActivity"
+                  android:label="ATHTestApp"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation android:name=".AudioTestHarnessInstrumentationTestRunner"
+                     android:targetPackage="com.android.media.audiotestharness.instrumentation"
+                     android:label="Runs the Audio Test Harness Instrumentation Tests">
+    </instrumentation>
+</manifest>
\ No newline at end of file
diff --git a/libraries/audio-test-harness/instrumentation/audiotestharness-test.xml b/libraries/audio-test-harness/instrumentation/audiotestharness-test.xml
new file mode 100644
index 0000000..8a0f4b5
--- /dev/null
+++ b/libraries/audio-test-harness/instrumentation/audiotestharness-test.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+<configuration description="Runs Audio Test Harness Tests">
+
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="disable-audio" value="false"/>
+        <option name="screen-saver" value="off"/>
+        <option name="set-system-setting" key="volume_music_speaker" value="15" />
+        <option name="set-system-setting" key="volume_music_headset" value="15" />
+        <option name="set-system-setting" key="volume_music_headphone" value="15" />
+        <option name="set-property" key="audio.safemedia.bypass" value="1"/>
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.InstallApkSetup">
+        <option name="apk-path" value="out/target/product/generic/testcases/audiotestharness-instrumentation-app/arm/audiotestharness-instrumentation-app.apk"/>
+    </target_preparer>
+
+    <metrics_collector class="com.android.media.audiotestharness.tradefed.AudioTestHarnessHermeticServerManagingMetricCollector">
+        <option name="capture-device" value="UMM-6"/>
+    </metrics_collector>
+
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys" value="/data/data/com.android.media.audiotestharness.instrumentation/files" />
+        <option name="compress-directories" value="true" />
+        <option name="collect-on-run-ended-only" value="true" />
+        <option name="clean-up" value="false" />
+    </metrics_collector>
+
+    <option name="test-tag" value="AudioTestHarnessTest" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.media.audiotestharness.instrumentation" />
+        <option name="runner" value=".AudioTestHarnessInstrumentationTestRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+
+</configuration>
\ No newline at end of file
diff --git a/libraries/audio-test-harness/instrumentation/res/layout/main_activity.xml b/libraries/audio-test-harness/instrumentation/res/layout/main_activity.xml
new file mode 100644
index 0000000..fb62fb3
--- /dev/null
+++ b/libraries/audio-test-harness/instrumentation/res/layout/main_activity.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/text"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:textSize="18sp"
+          android:autoText="true"
+          android:capitalize="sentences"
+          android:text="@string/main_activity_text" />
\ No newline at end of file
diff --git a/libraries/audio-test-harness/instrumentation/res/values/strings.xml b/libraries/audio-test-harness/instrumentation/res/values/strings.xml
new file mode 100644
index 0000000..136f9bf
--- /dev/null
+++ b/libraries/audio-test-harness/instrumentation/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<resources>
+    <string name="main_activity_text">Audio Test Harness - Tester Application</string>
+</resources>
\ No newline at end of file
diff --git a/libraries/audio-test-harness/instrumentation/src/com/android/media/audiotestharness/instrumentation/AudioTestHarnessCaptureTest.java b/libraries/audio-test-harness/instrumentation/src/com/android/media/audiotestharness/instrumentation/AudioTestHarnessCaptureTest.java
new file mode 100644
index 0000000..bfb650c
--- /dev/null
+++ b/libraries/audio-test-harness/instrumentation/src/com/android/media/audiotestharness/instrumentation/AudioTestHarnessCaptureTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2021 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.media.audiotestharness.instrumentation;
+
+import android.media.AudioAttributes;
+import android.media.MediaPlayer;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+import com.android.media.audiotestharness.client.core.AudioCaptureStream;
+import com.android.media.audiotestharness.client.core.AudioTestHarnessClient;
+import com.android.media.audiotestharness.client.grpc.GrpcAudioTestHarnessClient;
+
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/** Sample test that demonstrates the capture functionality of the Audio Test Harness */
+public class AudioTestHarnessCaptureTest extends ActivityInstrumentationTestCase2<MainActivity> {
+    private static final String TAG = AudioTestHarnessCaptureTest.class.getSimpleName();
+
+    /** On device path to the file that should be played back during the test. */
+    private static final String TEST_FILE = "/system/product/media/audio/ringtones/Lollipop.ogg";
+
+    /** Duration that the file should play. */
+    private static final int TEST_DURATION = 3;
+
+    /** Amplitude value at which we consider a test PASSED. */
+    private static final int TEST_AMPLITUDE_THRESHOLD = 250;
+
+    private static final int NUM_CHANNELS_MONO = 1;
+    private static final int BITS_PER_SAMPLE_16BIT = 16;
+
+    public AudioTestHarnessCaptureTest() {
+        super(MainActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Launch the activity which is required for connections to the Audio Test Harness to go
+        // through. Without this, connections result in ECONNREFUSED.
+        getActivity();
+    }
+
+    /**
+     * Tests that Media Player properly outputs audio through the device speaker by checking that
+     * the microphone in the box picks up audio playback.
+     */
+    @LargeTest
+    public void testPlayAudioFile_outputsAudio() throws Exception {
+
+        // Create a new Audio Test Harness client, and start a capture session with the harness.
+        try (AudioTestHarnessClient audioTestHarnessClient =
+                GrpcAudioTestHarnessClient.builder().build()) {
+            try (AudioCaptureStream audioCaptureStream = audioTestHarnessClient.startCapture()) {
+
+                // Playback the test file (Lollipop.ogg) and capture three seconds of audio
+                // from the harness, ensuring that it is written as a test artifact.
+                MediaPlayer mediaPlayer = startAudioFilePlayback(TEST_FILE);
+
+                // Create a buffer for three seconds of audio captured from the device.
+                int numSamplesRequired =
+                        Math.round(3 * audioCaptureStream.getAudioFormat().getSampleRate());
+                short[] samples = new short[numSamplesRequired];
+
+                // Capture three seconds of audio from the stream.
+                int samplesRead = 0;
+                while (samplesRead < numSamplesRequired) {
+                    samplesRead +=
+                            audioCaptureStream.read(
+                                    samples, samplesRead, samples.length - samplesRead);
+                }
+
+                // Write file to storage and cleanup resources.
+                writeSamplesToAppStorage("testPlayAudioFile_outputsAudio.pcm", samples);
+                mediaPlayer.release();
+
+                // Verify that the amplitude was far above ambient and thus audio playback
+                // was heard by the microphone.
+                int maxAmplitude = Math.abs(samples[0]);
+                for (int i = 1; i < samples.length; i++) {
+                    maxAmplitude = Math.max(maxAmplitude, Math.abs(samples[i]));
+                }
+
+                Log.v(TAG, String.format("Maximum Amplitude of Capture: %d", maxAmplitude));
+                assertTrue(
+                        "Could not detect audio playback", maxAmplitude > TEST_AMPLITUDE_THRESHOLD);
+            }
+        }
+    }
+
+    /**
+     * Plays audio file for given amount of time.
+     *
+     * <p>Instantiates a MediaPlayer and plays the passed in audioFile for audioPlayDuration
+     * milliseconds. If the player fails to instantiate or any exception happened during the play,
+     * the test will fail.
+     *
+     * @return the MediaPlayer instance.
+     */
+    private static MediaPlayer startAudioFilePlayback(String audioFile) {
+        Log.v(TAG, String.format("Playing audio file: %s", audioFile));
+        MediaPlayer mp = new MediaPlayer();
+        try {
+            mp.setAudioAttributes(
+                    new AudioAttributes.Builder()
+                            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
+                            .setUsage(AudioAttributes.USAGE_MEDIA)
+                            .build());
+            mp.setDataSource(audioFile);
+            mp.prepare();
+            int duration = mp.getDuration();
+            if (duration <= 0) {
+                Log.e(TAG, "Failed to grab duration from audio file.");
+                fail("AudioFileWithNegativeDuration");
+            }
+            mp.start();
+        } catch (IOException e) {
+            Log.e(
+                    TAG,
+                    String.format("Exception happened while playing audio file: %s", audioFile),
+                    e);
+            fail("FailedToPlayAudioFile");
+        }
+
+        return mp;
+    }
+
+    /**
+     * Writes a provided short[] to app storage with a given filename. These files are picked up as
+     * test artifacts after the test completes. Any existing files are overwritten.
+     *
+     * @throws IOException if any errors occur while writing the file.
+     */
+    private void writeSamplesToAppStorage(String filename, short[] toWrite) throws IOException {
+        File outputFile = new File(getActivity().getFilesDir(), filename);
+
+        if (outputFile.exists()) {
+            outputFile.delete();
+        }
+        outputFile.createNewFile();
+
+        // Convert our samples into a raw PCM file written little endian.
+        ByteBuffer buf = ByteBuffer.allocate(toWrite.length * 2).order(ByteOrder.LITTLE_ENDIAN);
+        for (short value : toWrite) {
+            buf.putShort(value);
+        }
+
+        Files.write(buf.array(), outputFile);
+
+        Log.v(TAG, String.format("Wrote file (%s) of size (%d)", outputFile, toWrite.length));
+    }
+}
diff --git a/libraries/audio-test-harness/instrumentation/src/com/android/media/audiotestharness/instrumentation/AudioTestHarnessInstrumentationTestRunner.java b/libraries/audio-test-harness/instrumentation/src/com/android/media/audiotestharness/instrumentation/AudioTestHarnessInstrumentationTestRunner.java
new file mode 100644
index 0000000..4234e7a
--- /dev/null
+++ b/libraries/audio-test-harness/instrumentation/src/com/android/media/audiotestharness/instrumentation/AudioTestHarnessInstrumentationTestRunner.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 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.media.audiotestharness.instrumentation;
+
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+
+import junit.framework.TestSuite;
+
+/** {@link InstrumentationTestRunner} for the Audio Test Harness tests. */
+public class AudioTestHarnessInstrumentationTestRunner extends InstrumentationTestRunner {
+    @Override
+    public TestSuite getAllTests() {
+        TestSuite suite = new InstrumentationTestSuite(this);
+        suite.addTestSuite(AudioTestHarnessCaptureTest.class);
+        return suite;
+    }
+
+    @Override
+    public ClassLoader getLoader() {
+        return AudioTestHarnessInstrumentationTestRunner.class.getClassLoader();
+    }
+}
diff --git a/libraries/audio-test-harness/instrumentation/src/com/android/media/audiotestharness/instrumentation/MainActivity.java b/libraries/audio-test-harness/instrumentation/src/com/android/media/audiotestharness/instrumentation/MainActivity.java
new file mode 100644
index 0000000..677d076
--- /dev/null
+++ b/libraries/audio-test-harness/instrumentation/src/com/android/media/audiotestharness/instrumentation/MainActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 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.media.audiotestharness.instrumentation;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * Main Activity for the Audio Test Harness Tester App which includes sample instrumentations for
+ * the system.
+ */
+public class MainActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        View view = getLayoutInflater().inflate(R.layout.main_activity, null);
+        setContentView(view);
+    }
+}
diff --git a/libraries/audio-test-harness/server/Android.bp b/libraries/audio-test-harness/server/Android.bp
index 59f9985..e72c8a6 100644
--- a/libraries/audio-test-harness/server/Android.bp
+++ b/libraries/audio-test-harness/server/Android.bp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -51,13 +51,14 @@
 java_library_host {
     name: "audiotestharness-serverlib",
     srcs: [
-        "src/main/java/com/android/media/audiotestharness/server/AudioTestHarnessGrpcServer.java",
-        "src/main/java/com/android/media/audiotestharness/server/AudioTestHarnessServerModule.java",
+        "src/main/java/com/android/media/audiotestharness/server/*.java",
     ],
     static_libs: [
         "audiotestharness-servicelib",
         "audiotestharness-utilitylib",
         "audiotestharness-javasoundlib",
+        "audiotestharness-corelib",
+        "audiotestharness-configlib",
         "audiotestharness-servicegrpclib-full",
         "grpc-java-netty-shaded",
     ],
@@ -76,6 +77,25 @@
 }
 
 java_library_host {
+    name: "audiotestharness-configlib",
+    srcs: [
+        "src/main/java/com/android/media/audiotestharness/server/config/*.java",
+    ],
+    libs: [
+        "auto_value_annotations",
+    ],
+    static_libs: [
+        "audiotestharness-commonlib-full",
+        "audiotestharness-commonprotolib-full",
+        "guava",
+        "guice",
+    ],
+    plugins: [
+        "auto_value_plugin",
+    ],
+}
+
+java_library_host {
     name: "audiotestharness-servicelib",
     srcs: [
         "src/main/java/com/android/media/audiotestharness/server/service/*.java",
@@ -85,6 +105,7 @@
         "audiotestharness-commonprotolib-full",
         "audiotestharness-servicegrpclib-full",
         "audiotestharness-corelib",
+        "audiotestharness-configlib",
         "guava",
         "guice",
     ],
@@ -115,11 +136,8 @@
 
 java_test_host {
     name: "audiotestharness-serverlib-tests",
-    test_suites: ["general-tests"],
     srcs: [
-        "src/test/java/com/android/media/audiotestharness/server/AudioTestHarnessServerModuleTests.java",
-        "src/test/java/com/android/media/audiotestharness/server/AudioTestHarnessGrpcServerTests.java",
-        "src/test/java/com/android/media/audiotestharness/server/AudioTestHarnessTestImpl.java",
+        "src/test/java/com/android/media/audiotestharness/server/*.java",
     ],
     static_libs: [
         "audiotestharness-serverlib",
@@ -139,7 +157,6 @@
 
 java_test_host {
     name: "audiotestharness-servicelib-tests",
-    test_suites: ["general-tests"],
     srcs: [
         "src/test/java/com/android/media/audiotestharness/server/service/*.java",
     ],
@@ -161,7 +178,6 @@
 
 java_test_host {
     name: "audiotestharness-javasoundlib-tests",
-    test_suites: ["general-tests"],
     srcs: [
         "src/test/java/com/android/media/audiotestharness/server/javasound/*.java",
     ],
@@ -184,7 +200,6 @@
 
 java_test_host {
     name: "audiotestharness-utilitylib-tests",
-    test_suites: ["general-tests"],
     srcs: [
         "src/test/java/com/android/media/audiotestharness/server/utility/*.java",
     ],
diff --git a/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/AudioTestHarnessGrpcServer.java b/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/AudioTestHarnessGrpcServer.java
index 6ac9ee4..19a6711 100644
--- a/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/AudioTestHarnessGrpcServer.java
+++ b/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/AudioTestHarnessGrpcServer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -16,12 +16,10 @@
 package com.android.media.audiotestharness.server;
 
 import com.android.media.audiotestharness.proto.AudioTestHarnessGrpc;
-import com.android.media.audiotestharness.server.utility.PortUtility;
+import com.android.media.audiotestharness.server.config.SharedHostConfiguration;
 
-import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.inject.ConfigurationException;
-import com.google.inject.Guice;
 import com.google.inject.Injector;
 
 import io.grpc.Server;
@@ -30,7 +28,6 @@
 import java.io.IOException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -42,17 +39,15 @@
  * possible to use the service implementation directly and customize the deployment of the service,
  * however, this is meant as an easy alternative for users who just want a working system and don't
  * care how it works under the hood.
+ *
+ * <p>This class should not be subclassed or extended, however is left non-final for testing
+ * purposes.
  */
-public final class AudioTestHarnessGrpcServer implements AutoCloseable {
-
-    private static final int TESTING_PORT = 8080;
+public class AudioTestHarnessGrpcServer implements AutoCloseable {
 
     private static final Logger LOGGER =
             Logger.getLogger(AudioTestHarnessGrpcServer.class.getName());
 
-    /** Number of threads used by the {@link #mExecutor} for task execution. */
-    private static final int THREAD_COUNT = 16;
-
     /** The port on which the gRPC Server should be executed on. */
     private final int mPort;
 
@@ -60,9 +55,6 @@
      * {@link Executor} used for background task execution. These tasks can be the handling of gRPC
      * calls or other background tasks in the system such as the publishing of audio sample data by
      * a {@link com.android.media.audiotestharness.server.core.AudioCapturer}.
-     *
-     * <p>This {@link ExecutorService} utilizes a fixed-thread pool with the number of threads set
-     * by the {@link #THREAD_COUNT} constant.
      */
     private final ExecutorService mExecutor;
 
@@ -72,7 +64,7 @@
     /** Underlying {@link Server} that manages gRPC calls. */
     private Server mServer;
 
-    private AudioTestHarnessGrpcServer(int port, ExecutorService executor, Injector injector) {
+    AudioTestHarnessGrpcServer(int port, ExecutorService executor, Injector injector) {
         LOGGER.finest(String.format("new AudioTestHarnessGrpcServer on Port (%d)", port));
 
         this.mPort = port;
@@ -80,48 +72,10 @@
         this.mInjector = injector;
     }
 
-    /**
-     * Creates a new {@link AudioTestHarnessGrpcServer} with the provided port, {@link
-     * ExecutorService}, and {@link Injector}.
-     *
-     * <p>This method is primarily available for unit testing. In production use, the {@link
-     * #createOnPort(int)} or {@link #createWithDefault()} methods should be used instead.
-     *
-     * @throws NullPointerException if either the executor or injector is null.
-     */
-    @VisibleForTesting
-    static AudioTestHarnessGrpcServer create(
-            int port, ExecutorService executor, Injector injector) {
-        return new AudioTestHarnessGrpcServer(
-                port,
-                Preconditions.checkNotNull(executor, "Executor cannot be null."),
-                Preconditions.checkNotNull(injector, "Injector cannot be null."));
-    }
-
-    /** Creates a new {@link AudioTestHarnessGrpcServer} with the provided port. */
-    public static AudioTestHarnessGrpcServer createOnPort(int port) {
-        LOGGER.finest(
-                String.format(
-                        "Using default Fixed Thread Pool Executor with %d threads and default"
-                                + " Injector using %s",
-                        THREAD_COUNT, AudioTestHarnessServerModule.class.getName()));
-
-        ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);
-        Injector injector = Guice.createInjector(AudioTestHarnessServerModule.create(executor));
-
-        return create(port, executor, injector);
-    }
-
-    /**
-     * Creates a new {@link AudioTestHarnessGrpcServer} on an available port in the dynamic port
-     * range.
-     */
-    public static AudioTestHarnessGrpcServer createWithDefault() {
-        return createOnPort(PortUtility.nextAvailablePort());
-    }
-
     public static void main(String[] args) {
-        try (AudioTestHarnessGrpcServer server = createOnPort(TESTING_PORT)) {
+        try (AudioTestHarnessGrpcServer server =
+                AudioTestHarnessGrpcServerFactory.createFactory()
+                        .createOnTestingPort(SharedHostConfiguration.getDefault())) {
             server.open();
 
             // Ensure that resources are cleanly stopped upon JVM shutdown.
@@ -171,17 +125,15 @@
      */
     @Override
     public void close() {
-
         LOGGER.info("Stopping Audio Test Harness gRPC Server");
         if (mServer != null) {
             mServer.shutdownNow();
+            mServer = null;
         } else {
             LOGGER.warning(
                     "mServer is null indicating that the Audio Test Harness gRPC Server was never"
                             + " started.");
         }
-
-        mExecutor.shutdownNow();
     }
 
     public int getPort() {
diff --git a/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/AudioTestHarnessGrpcServerFactory.java b/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/AudioTestHarnessGrpcServerFactory.java
new file mode 100644
index 0000000..d1cc593
--- /dev/null
+++ b/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/AudioTestHarnessGrpcServerFactory.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2021 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.media.audiotestharness.server;
+
+import com.android.media.audiotestharness.proto.AudioTestHarnessGrpc;
+import com.android.media.audiotestharness.server.config.SharedHostConfiguration;
+import com.android.media.audiotestharness.server.config.SharedHostConfigurationModule;
+import com.android.media.audiotestharness.server.utility.PortUtility;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.logging.Logger;
+
+import javax.annotation.Nullable;
+
+/**
+ * Factory for {@link AudioTestHarnessGrpcServer} instances.
+ *
+ * <p>This class is not meant to be extended, however is left non-final for mocking purposes.
+ */
+public class AudioTestHarnessGrpcServerFactory implements AutoCloseable {
+    private static final Logger LOGGER =
+            Logger.getLogger(AudioTestHarnessGrpcServerFactory.class.getName());
+
+    /** Default port used for testing purposes. */
+    private static final int TESTING_PORT = 8080;
+
+    /**
+     * Default number of threads that should be used for task execution.
+     *
+     * <p>This value is not used when using a provided {@link ExecutorService} and thus can be
+     * overridden in cases where necessary.
+     */
+    private static final int DEFAULT_THREAD_COUNT = 16;
+
+    /**
+     * {@link Executor} used for task execution throughout the system.
+     *
+     * <p>This executor is used both by the gRPC server as well as in underlying libraries such as
+     * the javasoundlib which uses the Executor to handle background capture while another thread
+     * handles gRPC actions.
+     */
+    private final ExecutorService mExecutorService;
+
+    /**
+     * {@link AbstractModule} that should be used as the base module for the system's dependency
+     * injection. This can be overridden for testing or to substitute other implementations.
+     *
+     * <p>At the minimum, this module must be able to provide an instance of the {@link
+     * com.android.media.audiotestharness.proto.AudioTestHarnessGrpc.AudioTestHarnessImplBase} which
+     * is required by the system at runtime.
+     */
+    private final AbstractModule mBaseModule;
+
+    private AudioTestHarnessGrpcServerFactory(
+            ExecutorService executorService, AbstractModule baseModule) {
+        mExecutorService = executorService;
+        mBaseModule = baseModule;
+    }
+
+    /**
+     * Creates a new {@link AudioTestHarnessGrpcServerFactory} with the default ExecutorService,
+     * which is a {@link java.util.concurrent.ThreadPoolExecutor} with {@link #DEFAULT_THREAD_COUNT}
+     * threads.
+     */
+    public static AudioTestHarnessGrpcServerFactory createFactory() {
+        ExecutorService executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_COUNT);
+        return createInternal(
+                Executors.newFixedThreadPool(DEFAULT_THREAD_COUNT),
+                AudioTestHarnessServerModule.create(executorService));
+    }
+
+    /**
+     * Creates a new {@link AudioTestHarnessGrpcServerFactory} with the provided ExecutorService.
+     *
+     * <p>All created AudioTestHarnessGrpcServer instances will make use of this executor for tasks.
+     * Furthermore, this {@link ExecutorService} will be shutdown whenever the {@link #close()}
+     * method is invoked on this factory.
+     */
+    public static AudioTestHarnessGrpcServerFactory createFactoryWithExecutorService(
+            ExecutorService executorService) {
+        return createInternal(
+                executorService, AudioTestHarnessServerModule.create(executorService));
+    }
+
+    @VisibleForTesting
+    static AudioTestHarnessGrpcServerFactory createInternal(
+            ExecutorService executorService, AbstractModule baseModule) {
+        return new AudioTestHarnessGrpcServerFactory(
+                Preconditions.checkNotNull(executorService, "ExecutorService cannot be null."),
+                baseModule);
+    }
+
+    /**
+     * Creates a new {@link AudioTestHarnessGrpcServer} on the specified port.
+     *
+     * <p>This port is not reserved or used until the server's {@link
+     * AudioTestHarnessGrpcServer#open()} method is called.
+     *
+     * @param sharedHostConfiguration the {@link SharedHostConfiguration} that should be used in the
+     *     system. This can be used to override configurable values (such as the device's capture
+     *     device) from their defaults.
+     */
+    public AudioTestHarnessGrpcServer createOnPort(
+            int port, @Nullable SharedHostConfiguration sharedHostConfiguration) {
+        LOGGER.finest(String.format("createOnPort(%d, %s)", port, sharedHostConfiguration));
+        LOGGER.info(
+                String.format(
+                        "Shared Host Configuration is (%s)",
+                        sharedHostConfiguration == null ? "Default" : sharedHostConfiguration));
+
+        // Create an injector for the Audio Test Harness server, if a custom sharedHostConfiguration
+        // was provided, then we add another module which provides this configuration, otherwise,
+        // we create the module with mBaseModule only.
+        Injector injector =
+                sharedHostConfiguration == null
+                        ? Guice.createInjector(mBaseModule)
+                        : Guice.createInjector(
+                                mBaseModule,
+                                SharedHostConfigurationModule.create(sharedHostConfiguration));
+
+        // Verify that the AudioTestHarnessImplBase class is bound, without this binding, the server
+        // will not operate.
+        if (injector.getExistingBinding(
+                        Key.get(AudioTestHarnessGrpc.AudioTestHarnessImplBase.class))
+                == null) {
+            throw new IllegalStateException(
+                    "Cannot create new AudioTestHarnessGrpcServer because there is no binding for"
+                            + " the Audio Test Harness gRPC Service in the module provided at "
+                            + "factory creation.");
+        }
+
+        return new AudioTestHarnessGrpcServer(port, mExecutorService, injector);
+    }
+
+    /**
+     * Creates a new {@link AudioTestHarnessGrpcServer} on the {@link #TESTING_PORT}.
+     *
+     * @param sharedHostConfiguration the {@link SharedHostConfiguration} that should be used in the
+     *     system. This can be used to override configurable values (such as the device's capture
+     *     device) from their defaults.
+     */
+    public AudioTestHarnessGrpcServer createOnTestingPort(
+            @Nullable SharedHostConfiguration sharedHostConfiguration) {
+        LOGGER.finest("createOnTestingPort()");
+        return createOnPort(TESTING_PORT, sharedHostConfiguration);
+    }
+
+    /**
+     * Creates a new {@link AudioTestHarnessGrpcServer} on the next available port within the
+     * dynamic port range.
+     *
+     * @param sharedHostConfiguration the {@link SharedHostConfiguration} that should be used in the
+     *     system. This can be used to override configurable values (such as the device's capture
+     *     device) from their defaults.
+     */
+    public AudioTestHarnessGrpcServer createOnNextAvailablePort(
+            @Nullable SharedHostConfiguration sharedHostConfiguration) {
+        LOGGER.finest("createOnNextAvailablePort()");
+        return createOnPort(PortUtility.nextAvailablePort(), sharedHostConfiguration);
+    }
+
+    /** Shuts down the {@link ExecutorService} used by the factory. */
+    @Override
+    public void close() {
+        LOGGER.fine("Shutting down internal ExecutorService");
+        mExecutorService.shutdownNow();
+    }
+}
diff --git a/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/AudioTestHarnessServerModule.java b/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/AudioTestHarnessServerModule.java
index 1122263..ccf3299 100644
--- a/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/AudioTestHarnessServerModule.java
+++ b/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/AudioTestHarnessServerModule.java
@@ -17,6 +17,7 @@
 package com.android.media.audiotestharness.server;
 
 import com.android.media.audiotestharness.proto.AudioTestHarnessGrpc;
+import com.android.media.audiotestharness.server.config.SharedHostConfiguration;
 import com.android.media.audiotestharness.server.javasound.JavaSoundModule;
 import com.android.media.audiotestharness.server.service.AudioCaptureSessionFactory;
 import com.android.media.audiotestharness.server.service.AudioCaptureSessionFactoryImpl;
@@ -25,6 +26,7 @@
 
 import com.google.common.base.Preconditions;
 import com.google.inject.AbstractModule;
+import com.google.inject.multibindings.OptionalBinder;
 
 import java.util.concurrent.Executor;
 
@@ -67,5 +69,9 @@
         bind(AudioTestHarnessGrpc.AudioTestHarnessImplBase.class).to(AudioTestHarnessImpl.class);
         bind(StreamObserverOutputStreamFactory.class);
         bind(AudioCaptureSessionFactory.class).to(AudioCaptureSessionFactoryImpl.class);
+
+        OptionalBinder.newOptionalBinder(binder(), SharedHostConfiguration.class)
+                .setDefault()
+                .toInstance(SharedHostConfiguration.getDefault());
     }
 }
diff --git a/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/config/SharedHostConfiguration.java b/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/config/SharedHostConfiguration.java
new file mode 100644
index 0000000..70852f7
--- /dev/null
+++ b/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/config/SharedHostConfiguration.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 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.media.audiotestharness.server.config;
+
+import com.android.media.audiotestharness.common.Defaults;
+import com.android.media.audiotestharness.proto.AudioDeviceOuterClass;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Container class that encapsulates configuration or other metadata from the test-host that needs
+ * to be accessible by the Audio Test Harness system.
+ */
+@AutoValue
+public abstract class SharedHostConfiguration {
+
+    public static SharedHostConfiguration create(
+            ImmutableList<AudioDeviceOuterClass.AudioDevice> captureDevices) {
+        return new AutoValue_SharedHostConfiguration(captureDevices);
+    }
+
+    public static SharedHostConfiguration getDefault() {
+        return new AutoValue_SharedHostConfiguration(ImmutableList.of(Defaults.AUDIO_DEVICE));
+    }
+
+    /**
+     * The list of capture devices that should be allocated by the system for capture.
+     *
+     * <p>At the moment, the system only supports a single capture device, so only the first device
+     * is actually used.
+     */
+    public abstract ImmutableList<AudioDeviceOuterClass.AudioDevice> captureDevices();
+}
diff --git a/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/config/SharedHostConfigurationModule.java b/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/config/SharedHostConfigurationModule.java
new file mode 100644
index 0000000..9b32d2c
--- /dev/null
+++ b/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/config/SharedHostConfigurationModule.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 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.media.audiotestharness.server.config;
+
+import com.google.common.base.Preconditions;
+import com.google.inject.AbstractModule;
+import com.google.inject.multibindings.OptionalBinder;
+
+/**
+ * Lightweight Guice module that binds the provided instance of the HostConfiguration item to the
+ * {@link SharedHostConfiguration} class so it can be shared across any users of the Injector this
+ * module is installed in.
+ */
+public class SharedHostConfigurationModule extends AbstractModule {
+
+    private final SharedHostConfiguration mSharedHostConfiguration;
+
+    private SharedHostConfigurationModule(SharedHostConfiguration sharedHostConfiguration) {
+        mSharedHostConfiguration = sharedHostConfiguration;
+    }
+
+    public static SharedHostConfigurationModule create(
+            SharedHostConfiguration sharedHostConfiguration) {
+        Preconditions.checkNotNull(sharedHostConfiguration);
+        return new SharedHostConfigurationModule(sharedHostConfiguration);
+    }
+
+    @Override
+    protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), SharedHostConfiguration.class)
+                .setBinding()
+                .toInstance(mSharedHostConfiguration);
+    }
+}
diff --git a/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/core/AudioSystemService.java b/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/core/AudioSystemService.java
index 7649219..e2f5c51 100644
--- a/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/core/AudioSystemService.java
+++ b/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/core/AudioSystemService.java
@@ -57,4 +57,16 @@
     default AudioCapturer createDefaultCapturer() throws IOException {
         return createCapturerFor(Defaults.AUDIO_DEVICE, Defaults.AUDIO_FORMAT);
     }
+
+    /**
+     * Creates a new {@link AudioCapturer} for the provided {@link AudioDevice} with the default
+     * {@link AudioFormat}.
+     *
+     * @throws IOException if unable to communcate with the underlying audio system, any errors
+     *     occur while attempting to allocate resources for the {@link AudioCapturer}, or the
+     *     provided {@link AudioDevice} cannot be allocated properly.
+     */
+    default AudioCapturer createWithDefaultAudioFormat(AudioDevice device) throws IOException {
+        return createCapturerFor(device, Defaults.AUDIO_FORMAT);
+    }
 }
diff --git a/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/service/AudioTestHarnessImpl.java b/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/service/AudioTestHarnessImpl.java
index 78b6d10..59322ad 100644
--- a/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/service/AudioTestHarnessImpl.java
+++ b/libraries/audio-test-harness/server/src/main/java/com/android/media/audiotestharness/server/service/AudioTestHarnessImpl.java
@@ -15,8 +15,10 @@
  */
 package com.android.media.audiotestharness.server.service;
 
+import com.android.media.audiotestharness.proto.AudioDeviceOuterClass;
 import com.android.media.audiotestharness.proto.AudioTestHarnessGrpc;
 import com.android.media.audiotestharness.proto.AudioTestHarnessService;
+import com.android.media.audiotestharness.server.config.SharedHostConfiguration;
 import com.android.media.audiotestharness.server.core.AudioCapturer;
 import com.android.media.audiotestharness.server.core.AudioSystemService;
 
@@ -50,12 +52,16 @@
     /** Factory for StreamObserverOutputStreams used during the procedure handling process. */
     private final AudioCaptureSessionFactory mAudioCaptureSessionFactory;
 
+    private final SharedHostConfiguration mSharedHostConfiguration;
+
     @Inject
     public AudioTestHarnessImpl(
             AudioSystemService audioSystemService,
-            AudioCaptureSessionFactory audioCaptureSessionFactory) {
+            AudioCaptureSessionFactory audioCaptureSessionFactory,
+            SharedHostConfiguration sharedHostConfiguration) {
         mAudioSystemService = audioSystemService;
         mAudioCaptureSessionFactory = audioCaptureSessionFactory;
+        mSharedHostConfiguration = sharedHostConfiguration;
     }
 
     @Override
@@ -68,16 +74,35 @@
 
         // Allocate the default AudioCapturer from the Audio System Service.
         AudioCapturer capturer;
+        AudioDeviceOuterClass.AudioDevice captureDevice =
+                mSharedHostConfiguration.captureDevices().get(0);
         try {
-            capturer = mAudioSystemService.createDefaultCapturer();
+            // Attempt to allocate with the first requested device, this list should always contain
+            // at least one device.
+            capturer = mAudioSystemService.createWithDefaultAudioFormat(captureDevice);
         } catch (IOException ioe) {
-            LOGGER.log(Level.SEVERE, "Failed to allocate default AudioCapturer", ioe);
+            LOGGER.log(
+                    Level.SEVERE,
+                    String.format("Failed to allocate AudioCapturer %s", captureDevice),
+                    ioe);
             serverCallResponseObserver.onError(
                     Status.UNAVAILABLE
                             .withCause(ioe)
-                            .withDescription("Failed to allocate default AudioCapturer")
+                            .withDescription(
+                                    String.format(
+                                            "Failed to allocate AudioCapturer %s", captureDevice))
                             .asException());
             return;
+        } catch (IndexOutOfBoundsException ioobe) {
+            LOGGER.log(
+                    Level.SEVERE,
+                    "Invalid Shared Host Configuration, no capture device provided. This "
+                            + "indicates there is an issue with the server as this"
+                            + " should never happen.",
+                    ioobe);
+            serverCallResponseObserver.onError(
+                    Status.INTERNAL.withDescription("Internal Configuration Error.").asException());
+            return;
         }
 
         // Start a new capture session
diff --git a/libraries/audio-test-harness/server/src/test/java/com/android/media/audiotestharness/server/AudioTestHarnessGrpcServerTests.java b/libraries/audio-test-harness/server/src/test/java/com/android/media/audiotestharness/server/AudioTestHarnessGrpcServerTests.java
deleted file mode 100644
index 8be66fd..0000000
--- a/libraries/audio-test-harness/server/src/test/java/com/android/media/audiotestharness/server/AudioTestHarnessGrpcServerTests.java
+++ /dev/null
@@ -1,181 +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.media.audiotestharness.server;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-
-import com.android.media.audiotestharness.proto.AudioTestHarnessGrpc;
-import com.android.media.audiotestharness.proto.AudioTestHarnessService;
-import com.android.media.audiotestharness.server.utility.PortUtility;
-
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-
-import io.grpc.ManagedChannel;
-import io.grpc.ManagedChannelBuilder;
-
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.io.IOException;
-import java.net.ServerSocket;
-import java.util.Iterator;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/** Tests for the {@link AudioTestHarnessGrpcServer} class. */
-@RunWith(JUnit4.class)
-public class AudioTestHarnessGrpcServerTests {
-
-    /**
-     * Tests for the {@link AudioTestHarnessGrpcServer#create(int, ExecutorService, Injector)},
-     * {@link AudioTestHarnessGrpcServer#createOnPort(int)}, and {@link
-     * AudioTestHarnessGrpcServer#createWithDefault()} methods.
-     */
-    @Test(expected = NullPointerException.class)
-    public void create_throwsNullPointerException_nullExecutor() throws Exception {
-        AudioTestHarnessGrpcServer.create(
-                /* port= */ 80, /* executor= */ null, Guice.createInjector());
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void create_throwsNullPointerException_nullInjector() throws Exception {
-        AudioTestHarnessGrpcServer.create(
-                /* port= */ 80, Executors.newSingleThreadExecutor(), /* injector= */ null);
-    }
-
-    @Test
-    public void createWithDefault_returnsNonNullResult() throws Exception {
-        assertNotNull(AudioTestHarnessGrpcServer.createWithDefault());
-    }
-
-    @Test
-    public void createWithPort_returnsNonNullResult() throws Exception {
-        assertNotNull(AudioTestHarnessGrpcServer.createOnPort(/* port= */ 80));
-    }
-
-    /** Tests for the {@link AudioTestHarnessGrpcServer#open()} method. */
-    @Test
-    public void open_buildsAndStartsGrpcServerAsExpected() throws Exception {
-        int port = PortUtility.nextAvailablePort();
-        AudioTestHarnessGrpcServer server =
-                setUpTestServer(port, Executors.newSingleThreadExecutor());
-
-        assertPortOpen(port);
-
-        server.open();
-
-        assertServerRunningAsExpected(port);
-    }
-
-    // Per (b/175643926) the following two tests are ignored since they do not run within Forrest.
-    // They do run (and pass) locally.
-
-    @Ignore
-    @Test(expected = IOException.class)
-    public void open_throwsIOExceptionForMissingServiceDependency() throws Exception {
-        AudioTestHarnessGrpcServer.create(
-                        8080, Executors.newSingleThreadExecutor(), Guice.createInjector())
-                .open();
-    }
-
-    @Ignore
-    @Test(expected = IOException.class)
-    public void open_throwsIOException_forPortTaken() throws Exception {
-        int port = PortUtility.nextAvailablePort();
-        AudioTestHarnessGrpcServer server =
-                setUpTestServer(port, Executors.newSingleThreadExecutor());
-
-        new ServerSocket(port);
-        server.open();
-    }
-
-    /** Tests for the {@link AudioTestHarnessGrpcServer#close()} method. */
-    @Test
-    public void close_shutsDownGrpcServerAsExpected() throws Exception {
-        int port = PortUtility.nextAvailablePort();
-        ExecutorService executorService = Executors.newSingleThreadExecutor();
-        AudioTestHarnessGrpcServer server = setUpTestServer(port, executorService);
-
-        server.open();
-        assertServerRunningAsExpected(port);
-
-        server.close();
-        assertPortOpen(port);
-    }
-
-    /**
-     * Builds a {@link AudioTestHarnessGrpcServer} on the specified port with the specified {@link
-     * ExecutorService} for testing purposes using the test implementation of the service.
-     *
-     * <p>While not the standard method of testing gRPC services, these tests aim to ensure that a
-     * working, production-ready server is properly set up and torn down by the {@link
-     * AudioTestHarnessGrpcServer} class. Thus, it uses an actual web server and executor to ensure
-     * that requires can be properly sent and received and that the setup and tear down actions
-     * operate as expected.
-     */
-    private static AudioTestHarnessGrpcServer setUpTestServer(
-            int port, ExecutorService executorService) {
-
-        // Build Guice injector to inject test implementation of the Audio Test Harness service.
-        Injector injector =
-                Guice.createInjector(
-                        binder ->
-                                binder.bind(AudioTestHarnessGrpc.AudioTestHarnessImplBase.class)
-                                        .to(AudioTestHarnessTestImpl.class));
-        return AudioTestHarnessGrpcServer.create(port, executorService, injector);
-    }
-
-    /**
-     * Verifies that the test gRPC Server was set up correctly by attempting to communicate with it
-     * over the specified port.
-     */
-    private static void assertServerRunningAsExpected(int port) {
-        // Attempt to communicate with running host over defined port.
-        ManagedChannel managedChannel =
-                ManagedChannelBuilder.forAddress("0.0.0.0", port).usePlaintext().build();
-        AudioTestHarnessGrpc.AudioTestHarnessBlockingStub stub =
-                AudioTestHarnessGrpc.newBlockingStub(managedChannel);
-        Iterator<AudioTestHarnessService.CaptureChunk> chunks =
-                stub.capture(AudioTestHarnessService.CaptureRequest.getDefaultInstance());
-
-        // Verify that the received message matches the expected message sent by the test
-        // implementation.
-        AudioTestHarnessService.CaptureChunk chunk = chunks.next();
-        assertArrayEquals(AudioTestHarnessTestImpl.MESSAGE, chunk.getData().toByteArray());
-        assertFalse(chunks.hasNext());
-
-        managedChannel.shutdown();
-    }
-
-    /**
-     * Verifies that a given port is open by attempting to create a {@link ServerSocket} on the
-     * port.
-     */
-    private static void assertPortOpen(int port) {
-        try {
-            new ServerSocket(port).close();
-        } catch (IOException ioe) {
-            fail("Expected Port to be open, but was actually not.");
-        }
-    }
-}
diff --git a/libraries/audio-test-harness/server/src/test/java/com/android/media/audiotestharness/server/AudioTestHarnessServerModuleTests.java b/libraries/audio-test-harness/server/src/test/java/com/android/media/audiotestharness/server/AudioTestHarnessServerModuleTests.java
index 68d5ae5..500639a 100644
--- a/libraries/audio-test-harness/server/src/test/java/com/android/media/audiotestharness/server/AudioTestHarnessServerModuleTests.java
+++ b/libraries/audio-test-harness/server/src/test/java/com/android/media/audiotestharness/server/AudioTestHarnessServerModuleTests.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertNotNull;
 
 import com.android.media.audiotestharness.proto.AudioTestHarnessGrpc;
+import com.android.media.audiotestharness.server.config.SharedHostConfiguration;
 import com.android.media.audiotestharness.server.core.AudioSystemService;
 import com.android.media.audiotestharness.server.service.AudioCaptureSessionFactory;
 import com.android.media.audiotestharness.server.service.StreamObserverOutputStreamFactory;
@@ -76,4 +77,9 @@
     public void getInstance_AudioCaptureSessionFactory_returnsInstance() throws Exception {
         assertNotNull(mInjector.getInstance(AudioCaptureSessionFactory.class));
     }
+
+    @Test
+    public void getInstance_SharedHostConfiguration_returnsInstance() throws Exception {
+        assertNotNull(mInjector.getInstance(SharedHostConfiguration.class));
+    }
 }
diff --git a/libraries/audio-test-harness/server/src/test/java/com/android/media/audiotestharness/server/service/AudioTestHarnessImplTests.java b/libraries/audio-test-harness/server/src/test/java/com/android/media/audiotestharness/server/service/AudioTestHarnessImplTests.java
index e64f2f3..ae0e537 100644
--- a/libraries/audio-test-harness/server/src/test/java/com/android/media/audiotestharness/server/service/AudioTestHarnessImplTests.java
+++ b/libraries/audio-test-harness/server/src/test/java/com/android/media/audiotestharness/server/service/AudioTestHarnessImplTests.java
@@ -26,6 +26,7 @@
 
 import com.android.media.audiotestharness.proto.AudioTestHarnessGrpc;
 import com.android.media.audiotestharness.proto.AudioTestHarnessService;
+import com.android.media.audiotestharness.server.config.SharedHostConfiguration;
 import com.android.media.audiotestharness.server.core.AudioCapturer;
 import com.android.media.audiotestharness.server.core.AudioSystemService;
 
@@ -91,7 +92,9 @@
                         .directExecutor()
                         .addService(
                                 new AudioTestHarnessImpl(
-                                        mAudioSystemService, mAudioCaptureSessionFactory))
+                                        mAudioSystemService,
+                                        mAudioCaptureSessionFactory,
+                                        SharedHostConfiguration.getDefault()))
                         .build()
                         .start());
 
@@ -103,7 +106,7 @@
         mStub = AudioTestHarnessGrpc.newStub(channel);
 
         // Ensure the mocks output is valid.
-        when(mAudioSystemService.createDefaultCapturer()).thenReturn(mAudioCapturer);
+        when(mAudioSystemService.createWithDefaultAudioFormat(any())).thenReturn(mAudioCapturer);
         when(mAudioCaptureSessionFactory.createCaptureSession(any(), any()))
                 .then(
                         (inv) -> {
@@ -121,7 +124,9 @@
     @Test
     public void capture_properlyAllocatesDefaultCapturer() throws Exception {
         mBlockingStub.capture(AudioTestHarnessService.CaptureRequest.getDefaultInstance());
-        verify(mAudioSystemService).createDefaultCapturer();
+        verify(mAudioSystemService)
+                .createWithDefaultAudioFormat(
+                        SharedHostConfiguration.getDefault().captureDevices().get(0));
     }
 
     @Test
@@ -187,12 +192,14 @@
 
     @Test
     public void capture_throwsProperStatusException_failureToOpenCapturer() throws Exception {
-        when(mAudioSystemService.createDefaultCapturer())
+        when(mAudioSystemService.createWithDefaultAudioFormat(any()))
                 .thenThrow(new IOException("Some exception occurred."));
 
         mExceptionRule.expect(
                 generateCustomMatcherForExpected(
-                        /* expectedDescription= */ "Failed to allocate default AudioCapturer",
+                        /* expectedDescription= */ String.format(
+                                "Failed to allocate AudioCapturer %s",
+                                SharedHostConfiguration.getDefault().captureDevices().get(0)),
                         Status.UNAVAILABLE));
         mBlockingStub
                 .capture(AudioTestHarnessService.CaptureRequest.getDefaultInstance())
diff --git a/libraries/audio-test-harness/tradefed/Android.bp b/libraries/audio-test-harness/tradefed/Android.bp
index ba343b6..4aa2c02 100644
--- a/libraries/audio-test-harness/tradefed/Android.bp
+++ b/libraries/audio-test-harness/tradefed/Android.bp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -15,13 +15,18 @@
  */
 
 // Contains the Trade Federation extensions for the Audio Test Harness system.
+//
+// Per b/178739059 while the tradefed_java_library_host should be able to
+// automatically copy the JAR to the out/host/<target>/tradefed directory
+// it is currently non-functional. So, we instead use a regular
+// java_library_host and manually copy the JAR with the Android.mk file.
 
 package {
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 java_library_host {
-    name: "audiotestharness-testlib",
+    name: "audiotestharness-tradefed-lib",
 
     srcs: [
         "src/main/java/com/android/media/audiotestharness/tradefed/*.java",
@@ -30,21 +35,29 @@
     libs: [
         "tradefed",
     ],
+
+    static_libs: [
+        "audiotestharness-serverlib",
+        "audiotestharness-configlib",
+        "audiotestharness-commonprotolib-full",
+        "guava",
+    ],
 }
 
 java_test_host {
-    name: "audiotestharness-testlib-tests",
-    test_suites: ["general-tests"],
-
+    name: "audiotestharness-tradefed-lib-tests",
     srcs: [
-        "src/main/java/com/android/media/audiotestharness/tradefed/*.java",
+        "src/test/java/com/android/media/audiotestharness/tradefed/*.java",
     ],
-
     static_libs: [
-        "audiotestharness-testlib",
+        "junit",
+        "audiotestharness-tradefed-lib",
+        "audiotestharness-serverlib",
         "tradefed",
+        "mockito",
+        "objenesis",
+        "guava",
     ],
-
     test_options: {
         unit_test: true,
     },
diff --git a/libraries/audio-test-harness/tradefed/Android.mk b/libraries/audio-test-harness/tradefed/Android.mk
new file mode 100644
index 0000000..436bc49
--- /dev/null
+++ b/libraries/audio-test-harness/tradefed/Android.mk
@@ -0,0 +1,37 @@
+# Copyright (C) 2021 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.
+
+# Custom Android.mk rules based on the TradeFed Contrib Android.mk
+# that ensures that this library is added to the tradefed/ directory
+# and thus the TradeFed classpath when it is built.
+
+# Per b/178739059 while there is a custom rule that should handle this--
+# it was rolled back and no longer functional, thus this custom
+# .mk file is necessary.
+
+LOCAL_PATH := $(call my-dir)
+
+# makefile rules to copy jars to HOST_OUT/tradefed
+# so tradefed.sh can automatically add to classpath
+DEST_JAR := $(HOST_OUT)/tradefed/audiotestharness-tradefed-lib.jar
+BUILT_JAR := $(call intermediates-dir-for,JAVA_LIBRARIES,audiotestharness-tradefed-lib,HOST)/javalib.jar
+$(DEST_JAR): $(BUILT_JAR)
+	$(copy-file-to-new-target)
+
+# this dependency ensure the above rule will be executed if jar is built
+$(HOST_OUT_JAVA_LIBRARIES)/audiotestharness-tradefed-lib.jar : $(DEST_JAR)
+
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/libraries/audio-test-harness/tradefed/src/main/java/com/android/media/audiotestharness/tradefed/AudioTestHarnessHermeticServerManagingMetricCollector.java b/libraries/audio-test-harness/tradefed/src/main/java/com/android/media/audiotestharness/tradefed/AudioTestHarnessHermeticServerManagingMetricCollector.java
index 330a66a..1814b14 100644
--- a/libraries/audio-test-harness/tradefed/src/main/java/com/android/media/audiotestharness/tradefed/AudioTestHarnessHermeticServerManagingMetricCollector.java
+++ b/libraries/audio-test-harness/tradefed/src/main/java/com/android/media/audiotestharness/tradefed/AudioTestHarnessHermeticServerManagingMetricCollector.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -16,10 +16,27 @@
 
 package com.android.media.audiotestharness.tradefed;
 
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
+import com.android.media.audiotestharness.common.Defaults;
+import com.android.media.audiotestharness.proto.AudioDeviceOuterClass;
+import com.android.media.audiotestharness.server.AudioTestHarnessGrpcServer;
+import com.android.media.audiotestharness.server.AudioTestHarnessGrpcServerFactory;
+import com.android.media.audiotestharness.server.config.SharedHostConfiguration;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.device.metric.BaseDeviceMetricCollector;
 import com.android.tradefed.device.metric.DeviceMetricData;
+import com.android.tradefed.log.LogUtil;
 import com.android.tradefed.metrics.proto.MetricMeasurement;
 
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -28,13 +45,157 @@
  */
 public class AudioTestHarnessHermeticServerManagingMetricCollector
         extends BaseDeviceMetricCollector {
+    /**
+     * Command used for executing port reversals with adb.
+     *
+     * <p>In general, a call to adb looks like the following:
+     *
+     * <pre>adb reverse tcp:55555 tcp:51000</pre>
+     *
+     * <p>Which forwards all requests that the device makes to localhost:55555 to the adb host
+     * machine port 51000.
+     */
+    private static final String REVERSE_COMMAND = "reverse";
+
+    /**
+     * Argument specifying the source port to forward requests from on device.
+     *
+     * <p>This value is a constant as it is based on the {@link Defaults#DEVICE_PORT} value.
+     */
+    private static final String SOURCE_PORT_ARGUMENT =
+            String.format("tcp:%d", Defaults.DEVICE_PORT);
+
+    /**
+     * Argument used with a call to
+     *
+     * <pre>adb reverse</pre>
+     *
+     * that undos all currently registered port reversals.
+     */
+    private static final String UNDO_REVERSALS_ARGUMENT = "--remove-all";
+
+    private final AudioTestHarnessGrpcServerFactory mAudioTestHarnessGrpcServerFactory;
+
+    private AudioTestHarnessGrpcServer mAudioTestHarnessGrpcServer;
+
+    @Option(
+            name = "capture-device",
+            description = "The capture device(s) to use for test " + "execution.")
+    private final List<String> mCaptureDevices;
+
+    public AudioTestHarnessHermeticServerManagingMetricCollector() {
+        this(AudioTestHarnessGrpcServerFactory.createFactory(), new ArrayList<>());
+    }
+
+    @VisibleForTesting
+    AudioTestHarnessHermeticServerManagingMetricCollector(
+            AudioTestHarnessGrpcServerFactory audioTestHarnessGrpcServerFactory,
+            List<String> captureDevices) {
+        Preconditions.checkNotNull(
+                audioTestHarnessGrpcServerFactory,
+                "audioTestHarnessGrpcServerFactory cannot be null");
+        mAudioTestHarnessGrpcServerFactory = audioTestHarnessGrpcServerFactory;
+        mCaptureDevices = captureDevices;
+    }
 
     @Override
     public void onTestRunStart(DeviceMetricData runData) {
+        LogUtil.CLog.i("Starting Audio Test Harness...");
+
+        // Use the default configuration if no devices are specified, otherwise, create a
+        // configuration containing the specified devices.
+        SharedHostConfiguration sharedHostConfiguration =
+                mCaptureDevices.isEmpty()
+                        ? null
+                        : SharedHostConfiguration.create(
+                                mCaptureDevices.stream()
+                                        .map(
+                                                (name) ->
+                                                        AudioDeviceOuterClass.AudioDevice
+                                                                .newBuilder()
+                                                                .setName(name)
+                                                                .addCapabilities(
+                                                                        AudioDeviceOuterClass
+                                                                                .AudioDevice
+                                                                                .Capability.CAPTURE)
+                                                                .build())
+                                        .collect(toImmutableList()));
+
+        mAudioTestHarnessGrpcServer =
+                mAudioTestHarnessGrpcServerFactory.createOnNextAvailablePort(
+                        sharedHostConfiguration);
+
+        // Ensure that the server's logs are output through the TradeFed logging system.
+        // This needs to be called after the server is instantiated to ensure
+        // that the static logger on the class has been loaded.
+        AudioTestHarnessServerLogForwardingHandler.configureServerLoggerWithHandler(true);
+
+        try {
+            mAudioTestHarnessGrpcServer.open();
+        } catch (IOException ioe) {
+            throw new RuntimeException(
+                    "Unable to start the Audio Test Harness Server, test cannot continue.", ioe);
+        }
+
+        for (ITestDevice device : getDevices()) {
+            reversePort(device, mAudioTestHarnessGrpcServer.getPort());
+        }
     }
 
     @Override
     public void onTestRunEnd(
             DeviceMetricData runData, Map<String, MetricMeasurement.Metric> currentRunMetrics) {
+        LogUtil.CLog.i("Stopping Audio Test Harness...");
+
+        mAudioTestHarnessGrpcServer.close();
+        mAudioTestHarnessGrpcServerFactory.close();
+
+        for (ITestDevice device : getDevices()) {
+            undoPortReversals(device);
+        }
+    }
+
+    /**
+     * Reverse port-forwards requests from the provided device on the default communication port to
+     * the host at the specified destination port.
+     *
+     * <p>This process allows the device to communicate with the Audio Test Harness.
+     */
+    private void reversePort(ITestDevice testDevice, int destinationPort) {
+        String destinationPortArgument = String.format("tcp:%d", destinationPort);
+        try {
+            LogUtil.CLog.i(
+                    String.format(
+                            "Reversing forwarding connections from device (serial=%s) to host "
+                                    + "(device:%d => host:%d)",
+                            testDevice.getSerialNumber(), Defaults.DEVICE_PORT, destinationPort));
+
+            // Executes the 'adb reverse tcp:<source-port> tcp:<destination-port>' command.
+            testDevice.executeAdbCommand(
+                    REVERSE_COMMAND, SOURCE_PORT_ARGUMENT, destinationPortArgument);
+        } catch (DeviceNotAvailableException dnae) {
+            throw new RuntimeException(
+                    "Unable to forward requests from device to host, test cannot continue since "
+                            + "the device cannot communicate with the Audio Test Harness",
+                    dnae);
+        }
+    }
+
+    /** Undoes all of the reverse port-forwarding on the provided device. */
+    private void undoPortReversals(ITestDevice testDevice) {
+        try {
+            LogUtil.CLog.i(
+                    String.format(
+                            "Undoing port reversals for device (serial=%s)",
+                            testDevice.getSerialNumber()));
+
+            // Executes the 'adb reverse --remove-all' command.
+            testDevice.executeAdbCommand(REVERSE_COMMAND, UNDO_REVERSALS_ARGUMENT);
+        } catch (DeviceNotAvailableException dnae) {
+            LogUtil.CLog.w(
+                    String.format(
+                            "Unable to undo port reversals for device (%s)",
+                            testDevice.getSerialNumber()));
+        }
     }
 }
diff --git a/libraries/audio-test-harness/tradefed/src/main/java/com/android/media/audiotestharness/tradefed/AudioTestHarnessServerLogForwardingHandler.java b/libraries/audio-test-harness/tradefed/src/main/java/com/android/media/audiotestharness/tradefed/AudioTestHarnessServerLogForwardingHandler.java
new file mode 100644
index 0000000..268d3d3
--- /dev/null
+++ b/libraries/audio-test-harness/tradefed/src/main/java/com/android/media/audiotestharness/tradefed/AudioTestHarnessServerLogForwardingHandler.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2021 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.media.audiotestharness.tradefed;
+
+import com.android.media.audiotestharness.server.AudioTestHarnessGrpcServer;
+import com.android.tradefed.log.LogUtil;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+/**
+ * {@link Handler} that outputs all log records to the {@link com.android.tradefed.log.LogUtil.CLog}
+ * CLog shim methods thus forwarding messages from the Java Logging API to the Tradefed Logging API.
+ *
+ * <p>For portability reasons, the Audio Test Harness Server uses the Java Logging API internally
+ * for logging purposes.
+ */
+public class AudioTestHarnessServerLogForwardingHandler extends Handler {
+    private static final String LOG_LINE_ITEM_SEPARATOR = "/";
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Forwards the log onto the CLog shim provided in the {@link LogUtil} class.
+     */
+    @Override
+    public void publish(LogRecord record) {
+        String logMessage = buildLogOutputLine(record);
+        if (record.getLevel().equals(Level.SEVERE)) {
+            LogUtil.CLog.e(logMessage);
+            if (record.getThrown() != null) {
+                LogUtil.CLog.e(record.getThrown());
+            }
+        } else if (record.getLevel().equals(Level.WARNING)) {
+            LogUtil.CLog.w(logMessage);
+            if (record.getThrown() != null) {
+                LogUtil.CLog.w(record.getThrown());
+            }
+        } else if (record.getLevel().equals(Level.INFO)) {
+            LogUtil.CLog.i(logMessage);
+        } else if (record.getLevel().equals(Level.FINE)) {
+            LogUtil.CLog.d(logMessage);
+        } else {
+            LogUtil.CLog.v(logMessage);
+        }
+    }
+
+    /** Does nothing. */
+    @Override
+    public void flush() {}
+
+    /** Does nothing. */
+    @Override
+    public void close() {}
+
+    /**
+     * Constructs a string representation of the provided {@link LogRecord} that consists of the
+     * logger name and message.
+     *
+     * <p>A sample log message from this would look like:
+     *
+     * <pre>com.android.media.audiotestharness.server.AudioTestHarnessGrpcServer - Failed to start
+     * gRPC Server.</pre>
+     */
+    @VisibleForTesting
+    static String buildLogOutputLine(LogRecord logRecord) {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(logRecord.getLoggerName());
+        sb.append(LOG_LINE_ITEM_SEPARATOR);
+        sb.append(logRecord.getMessage());
+
+        return sb.toString();
+    }
+
+    /**
+     * Helper method which configures the root server logger (defined by the main class's package)
+     * with a handler that forwards all log messages.
+     *
+     * @param shouldClearExistingHandlers flag that determines whether or not existing handlers
+     *     should be cleared out from the Server loggers. In general, this flag should most likely
+     *     be true to ensure that nothing is double-logged. However, there may be cases where this
+     *     may not be desired.
+     */
+    public static void configureServerLoggerWithHandler(boolean shouldClearExistingHandlers) {
+        LogManager.getLogManager().reset();
+        Logger serverRootLogger =
+                LogManager.getLogManager()
+                        .getLogger(AudioTestHarnessGrpcServer.class.getName())
+                        .getParent();
+
+        // Ignore programmatically configured log levels since that will be handled by the TradeFed
+        // logging system.
+        serverRootLogger.setUseParentHandlers(false);
+        serverRootLogger.setLevel(Level.ALL);
+
+        // Remove all other handlers from the logger, ensuring that logs are only forwarded
+        // to the TradeFed logging system.
+        if (shouldClearExistingHandlers) {
+            for (Handler handler : serverRootLogger.getHandlers()) {
+                serverRootLogger.removeHandler(handler);
+            }
+        }
+
+        serverRootLogger.addHandler(new AudioTestHarnessServerLogForwardingHandler());
+    }
+}
diff --git a/libraries/audio-test-harness/tradefed/src/test/java/com/android/media/audiotestharness/tradefed/AudioTestHarnessHermeticServerManagingMetricCollectorTests.java b/libraries/audio-test-harness/tradefed/src/test/java/com/android/media/audiotestharness/tradefed/AudioTestHarnessHermeticServerManagingMetricCollectorTests.java
index 59656f4..6232ca8 100644
--- a/libraries/audio-test-harness/tradefed/src/test/java/com/android/media/audiotestharness/tradefed/AudioTestHarnessHermeticServerManagingMetricCollectorTests.java
+++ b/libraries/audio-test-harness/tradefed/src/test/java/com/android/media/audiotestharness/tradefed/AudioTestHarnessHermeticServerManagingMetricCollectorTests.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -16,16 +16,206 @@
 
 package com.android.media.audiotestharness.tradefed;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.android.media.audiotestharness.proto.AudioDeviceOuterClass;
+import com.android.media.audiotestharness.server.AudioTestHarnessGrpcServer;
+import com.android.media.audiotestharness.server.AudioTestHarnessGrpcServerFactory;
+import com.android.media.audiotestharness.server.config.SharedHostConfiguration;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.device.metric.DeviceMetricData;
+import com.android.tradefed.invoker.IInvocationContext;
+import com.android.tradefed.result.ITestInvocationListener;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
 
 @RunWith(JUnit4.class)
 public class AudioTestHarnessHermeticServerManagingMetricCollectorTests {
 
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock AudioTestHarnessGrpcServerFactory mAudioTestHarnessGrpcServerFactory;
+
+    @Mock ITestDevice mTestDeviceOne;
+
+    @Mock ITestDevice mTestDeviceTwo;
+
+    @Mock IInvocationContext mInvocationContext;
+
+    @Mock ITestInvocationListener mTestInvocationListener;
+
+    @Mock AudioTestHarnessGrpcServer mAudioTestHarnessGrpcServer;
+
+    private AudioTestHarnessHermeticServerManagingMetricCollector mMetricCollector;
+
+    private DeviceMetricData mTestDeviceMetricData;
+
+    private List<String> mCaptureDevices;
+
+    @Before
+    public void setUp() throws Exception {
+        when(mAudioTestHarnessGrpcServerFactory.createOnNextAvailablePort(any()))
+                .thenReturn(mAudioTestHarnessGrpcServer);
+
+        mCaptureDevices = new ArrayList<>();
+        mMetricCollector =
+                new AudioTestHarnessHermeticServerManagingMetricCollector(
+                        mAudioTestHarnessGrpcServerFactory, mCaptureDevices);
+
+        when(mInvocationContext.getDevices())
+                .thenReturn(ImmutableList.of(mTestDeviceOne, mTestDeviceTwo));
+        mMetricCollector.init(mInvocationContext, mTestInvocationListener);
+
+        mTestDeviceMetricData = new DeviceMetricData(mInvocationContext);
+    }
+
     @Test
     public void constructor_successfullyCreatesNewMetricCollector() throws Exception {
         new AudioTestHarnessHermeticServerManagingMetricCollector();
-        // If no exceptions are thrown, then test passes.
+    }
+
+    @Test
+    public void onTestRunStart_properlyAttachesLoggingHandlerToServerLogger() throws Exception {
+        mMetricCollector.onTestRunStart(mTestDeviceMetricData);
+
+        Logger serverRootLogger =
+                LogManager.getLogManager()
+                        .getLogger(AudioTestHarnessGrpcServer.class.getName())
+                        .getParent();
+
+        assertEquals(Level.ALL, serverRootLogger.getLevel());
+        assertEquals(1, serverRootLogger.getHandlers().length);
+        assertTrue(
+                serverRootLogger.getHandlers()[0]
+                        instanceof AudioTestHarnessServerLogForwardingHandler);
+    }
+
+    @Test
+    public void onTestRunStart_buildsServerFromFactoryOnAvailablePort() {
+        mMetricCollector.onTestRunStart(mTestDeviceMetricData);
+        verify(mAudioTestHarnessGrpcServerFactory).createOnNextAvailablePort(any());
+    }
+
+    @Test
+    public void onTestRunStart_usesDefaultSharedHostConfigurationWhenNoCaptureDevices() {
+        mMetricCollector.onTestRunStart(mTestDeviceMetricData);
+        verify(mAudioTestHarnessGrpcServerFactory).createOnNextAvailablePort(null);
+    }
+
+    @Test
+    public void onTestRunStart_usesCustomSharedHostConfigurationWhenCaptureDevicesSet() {
+        ArgumentCaptor<SharedHostConfiguration> configurationCaptor =
+                ArgumentCaptor.forClass(SharedHostConfiguration.class);
+        mCaptureDevices.add("TestDevice");
+
+        mMetricCollector.onTestRunStart(mTestDeviceMetricData);
+
+        verify(mAudioTestHarnessGrpcServerFactory)
+                .createOnNextAvailablePort(configurationCaptor.capture());
+
+        SharedHostConfiguration config = configurationCaptor.getValue();
+        assertNotNull(config);
+        assertEquals(1, config.captureDevices().size());
+        assertEquals(
+                AudioDeviceOuterClass.AudioDevice.newBuilder()
+                        .setName("TestDevice")
+                        .addCapabilities(AudioDeviceOuterClass.AudioDevice.Capability.CAPTURE)
+                        .build(),
+                config.captureDevices().get(0));
+    }
+
+    @Test
+    public void onTestRunStart_startsServerProperly() throws Exception {
+        mMetricCollector.onTestRunStart(new DeviceMetricData(mInvocationContext));
+        verify(mAudioTestHarnessGrpcServer).open();
+    }
+
+    @Test
+    public void onTestRunStart_triggersAdbPortReversalToServerPort() throws Exception {
+        when(mAudioTestHarnessGrpcServer.getPort()).thenReturn(8080);
+
+        mMetricCollector.onTestRunStart(mTestDeviceMetricData);
+
+        verify(mTestDeviceOne).executeAdbCommand("reverse", "tcp:55555", "tcp:8080");
+        verify(mTestDeviceTwo).executeAdbCommand("reverse", "tcp:55555", "tcp:8080");
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void onTestRunStart_throwsRuntimeException_serverFailsToStart() throws Exception {
+        doThrow(RuntimeException.class).when(mAudioTestHarnessGrpcServer).open();
+        mMetricCollector.onTestRunStart(mTestDeviceMetricData);
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void onTestRunStart_throwsRuntimeException_portReversalFailure() throws Exception {
+        when(mTestDeviceOne.executeAdbCommand(any())).thenThrow(DeviceNotAvailableException.class);
+
+        mMetricCollector.onTestRunStart(mTestDeviceMetricData);
+    }
+
+    @Test
+    public void onTestRunEnd_closesGrpcServer() throws Exception {
+        mMetricCollector.onTestRunStart(mTestDeviceMetricData);
+
+        mMetricCollector.onTestRunEnd(
+                mTestDeviceMetricData, /* currentRunMetrics= */ ImmutableMap.of());
+
+        verify(mAudioTestHarnessGrpcServer).close();
+    }
+
+    @Test
+    public void onTestRunEnd_closesServerFactory() throws Exception {
+        mMetricCollector.onTestRunStart(mTestDeviceMetricData);
+
+        mMetricCollector.onTestRunEnd(
+                mTestDeviceMetricData, /* currentRunMetrics= */ ImmutableMap.of());
+
+        verify(mAudioTestHarnessGrpcServerFactory).close();
+    }
+
+    @Test
+    public void onTestRunEnd_properlyUndoesPortReversals() throws Exception {
+        mMetricCollector.onTestRunStart(mTestDeviceMetricData);
+
+        mMetricCollector.onTestRunEnd(
+                mTestDeviceMetricData, /* currentRunMetrics= */ ImmutableMap.of());
+
+        verify(mTestDeviceOne).executeAdbCommand("reverse", "--remove-all");
+        verify(mTestDeviceTwo).executeAdbCommand("reverse", "--remove-all");
+    }
+
+    @Test
+    public void onTestRunEnd_executesWithoutException_failureToUndoPortReversal() throws Exception {
+        mMetricCollector.onTestRunStart(mTestDeviceMetricData);
+
+        when(mTestDeviceOne.executeAdbCommand(any())).thenThrow(DeviceNotAvailableException.class);
+
+        // If this call throws no exceptions, then the test passes without issue.
+        mMetricCollector.onTestRunEnd(
+                mTestDeviceMetricData, /* currentRunMetrics= */ ImmutableMap.of());
     }
 }
diff --git a/libraries/audio-test-harness/tradefed/src/test/java/com/android/media/audiotestharness/tradefed/AudioTestHarnessServerLogForwardingHandlerTests.java b/libraries/audio-test-harness/tradefed/src/test/java/com/android/media/audiotestharness/tradefed/AudioTestHarnessServerLogForwardingHandlerTests.java
new file mode 100644
index 0000000..d92a7ad
--- /dev/null
+++ b/libraries/audio-test-harness/tradefed/src/test/java/com/android/media/audiotestharness/tradefed/AudioTestHarnessServerLogForwardingHandlerTests.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 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.media.audiotestharness.tradefed;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.media.audiotestharness.server.AudioTestHarnessGrpcServer;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+@RunWith(JUnitParamsRunner.class)
+public class AudioTestHarnessServerLogForwardingHandlerTests {
+
+    @Test
+    @Parameters(method = "getBuildLogOutputLineParams")
+    public void buildLogOutputLine_properlyBuildsLine(
+            String testCaseName, LogRecord record, String expectedLogOutput) {}
+
+    public static Object[] getBuildLogOutputLineParams() {
+
+        LogRecord[] logsRecords = new LogRecord[3];
+
+        logsRecords[0] = new LogRecord(Level.SEVERE, "Failed to start gRPC Server");
+        logsRecords[0].setLoggerName(
+                "com.android.media.audiotestharness.server.AudioTestHarnessGrpcServer");
+
+        logsRecords[1] = new LogRecord(Level.WARNING, "Resource leak!");
+        logsRecords[1].setLoggerName("com.android.media.audiotestharness.ResourceClass");
+
+        logsRecords[2] = new LogRecord(Level.FINE, "Some debug message");
+        logsRecords[2].setLoggerName("com.android.media.audiotestharness.DebugClass");
+
+        return new Object[][] {
+            {
+                "Test Case One",
+                logsRecords[0],
+                "com.android.media.audiotestharness.server.AudioTestHarnessGrpcServer - "
+                        + "Failed to start gRPC Server"
+            },
+            {
+                "Test Case Two",
+                logsRecords[1],
+                "com.android.media.audiotestharness.server.ResourceClass - Resource leak!"
+            },
+            {
+                "Test Case Three",
+                logsRecords[2],
+                "com.android.media.audiotestharness.server.DebugClass - Some debug message"
+            }
+        };
+    }
+
+    @Test
+    public void configureServerLoggerWithHandler_properlyConfiguresRootLogger() throws Exception {
+        AudioTestHarnessServerLogForwardingHandler.configureServerLoggerWithHandler(/*
+        shouldClearExistingHandlers= */ true);
+
+        Logger serverRootLogger =
+                LogManager.getLogManager()
+                        .getLogger(AudioTestHarnessGrpcServer.class.getName())
+                        .getParent();
+
+        assertEquals(Level.ALL, serverRootLogger.getLevel());
+        assertEquals(1, serverRootLogger.getHandlers().length);
+        assertTrue(
+                serverRootLogger.getHandlers()[0]
+                        instanceof AudioTestHarnessServerLogForwardingHandler);
+    }
+}
diff --git a/libraries/audio-test-harness/utilities/Android.bp b/libraries/audio-test-harness/utilities/Android.bp
new file mode 100644
index 0000000..76bbb74
--- /dev/null
+++ b/libraries/audio-test-harness/utilities/Android.bp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+// Contains the Trade Federation extensions for the Audio Test Harness system.
+// BINARIES ==============================================================
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_binary_host {
+    name: "audiotestharness-audiosystemdiagnostic",
+    srcs: [
+        "src/com/android/media/audiotestharness/utilities/AudioSystemDiagnostic.java",
+    ],
+    manifest: "audiosystemdiagnostic-manifest.inf",
+}
diff --git a/libraries/audio-test-harness/utilities/audiosystemdiagnostic-manifest.inf b/libraries/audio-test-harness/utilities/audiosystemdiagnostic-manifest.inf
new file mode 100644
index 0000000..5344ec7
--- /dev/null
+++ b/libraries/audio-test-harness/utilities/audiosystemdiagnostic-manifest.inf
@@ -0,0 +1 @@
+Main-Class: com.android.media.audiotestharness.utilities.AudioSystemDiagnostic
\ No newline at end of file
diff --git a/libraries/audio-test-harness/utilities/src/com/android/media/audiotestharness/utilities/AudioSystemDiagnostic.java b/libraries/audio-test-harness/utilities/src/com/android/media/audiotestharness/utilities/AudioSystemDiagnostic.java
new file mode 100644
index 0000000..5e4dd37
--- /dev/null
+++ b/libraries/audio-test-harness/utilities/src/com/android/media/audiotestharness/utilities/AudioSystemDiagnostic.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 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.media.audiotestharness.utilities;
+
+import java.util.Arrays;
+import java.util.logging.Logger;
+
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.Mixer;
+
+/**
+ * Helper binary that runs a diagnostic with the AudioSystem class to see if audio devices can be
+ * seen. This class can also be used to determine the name that the Java Audio System has assigned
+ * to the various devices connected to the system.
+ */
+public class AudioSystemDiagnostic {
+    public static final Logger LOGGER = Logger.getLogger(AudioSystemDiagnostic.class.getName());
+
+    public static void main(String args[]) {
+        LOGGER.info("Audio System Diagnostic follows:\n" + buildAudioSystemDiagnosticString());
+    }
+
+    /**
+     * Builds a string containing diagnostic information about the Java Sound API and the devices it
+     * can recognize.
+     */
+    public static String buildAudioSystemDiagnosticString() {
+        StringBuilder diagnosticStringBuilder = new StringBuilder();
+
+        diagnosticStringBuilder.append("=====  Java Audio System Diagnostic  =====\n\n");
+
+        diagnosticStringBuilder.append("-----  Java Version Information  -----\n");
+        diagnosticStringBuilder.append(
+                "Java Version = " + System.getProperty("java.runtime.version") + "\n\n");
+
+        diagnosticStringBuilder.append("-----  Java Audio System Output  -----\n");
+
+        Mixer.Info[] mixers = AudioSystem.getMixerInfo();
+
+        diagnosticStringBuilder.append("Mixers Array = " + Arrays.toString(mixers) + "\n");
+        diagnosticStringBuilder.append("Number of Mixers = " + mixers.length + "\n");
+
+        for (int i = 0; i < mixers.length; i++) {
+            Mixer.Info currentMixer = mixers[i];
+            diagnosticStringBuilder.append("\n");
+            diagnosticStringBuilder.append(
+                    "----- Start Mixer (#" + Integer.toString(i) + ")  -----\n");
+            diagnosticStringBuilder.append("Mixer Name = " + currentMixer.getName() + "\n");
+            diagnosticStringBuilder.append("Mixer Vendor = " + currentMixer.getVendor() + "\n");
+            diagnosticStringBuilder.append("Mixer Version = " + currentMixer.getVersion() + "\n");
+            diagnosticStringBuilder.append(
+                    "Mixer Description = " + currentMixer.getDescription() + "\n");
+            diagnosticStringBuilder.append(
+                    "----- End Mixer (#" + Integer.toString(i) + ")  -----\n");
+        }
+
+        return diagnosticStringBuilder.toString();
+    }
+}
diff --git a/libraries/automotive-helpers/hardkeys-app-helper/Android.bp b/libraries/automotive-helpers/hardkeys-app-helper/Android.bp
index 2e44189..74c734c 100644
--- a/libraries/automotive-helpers/hardkeys-app-helper/Android.bp
+++ b/libraries/automotive-helpers/hardkeys-app-helper/Android.bp
@@ -23,7 +23,7 @@
     libs: [
         "ub-uiautomator",
         "app-helpers-auto-interfaces",
-        "android.car",
+        "android.car-system-stubs",
     ],
     static_libs: [
         "androidx.test.runner",
diff --git a/libraries/automotive-helpers/media-center-app-helper/Android.bp b/libraries/automotive-helpers/media-center-app-helper/Android.bp
index 14375ff..f6776dd 100644
--- a/libraries/automotive-helpers/media-center-app-helper/Android.bp
+++ b/libraries/automotive-helpers/media-center-app-helper/Android.bp
@@ -24,7 +24,7 @@
         "ub-uiautomator",
         "app-helpers-auto-interfaces",
         "automotive-utility-helper",
-        "android.car",
+        "android.car-system-stubs",
     ],
     static_libs: [
         "androidx.test.runner",
diff --git a/libraries/automotive-helpers/settings-app-helper/Android.bp b/libraries/automotive-helpers/settings-app-helper/Android.bp
index 790664e..b34d0af 100644
--- a/libraries/automotive-helpers/settings-app-helper/Android.bp
+++ b/libraries/automotive-helpers/settings-app-helper/Android.bp
@@ -24,7 +24,7 @@
         "ub-uiautomator",
         "app-helpers-auto-interfaces",
         "automotive-utility-helper",
-        "android.car",
+        "android.car-system-stubs",
     ],
     static_libs: [
         "androidx.test.runner",
diff --git a/libraries/automotive-helpers/settings-app-helper/src/android/platform/helpers/SettingUserHelperImpl.java b/libraries/automotive-helpers/settings-app-helper/src/android/platform/helpers/SettingUserHelperImpl.java
index be2a1c5..9de51bf 100644
--- a/libraries/automotive-helpers/settings-app-helper/src/android/platform/helpers/SettingUserHelperImpl.java
+++ b/libraries/automotive-helpers/settings-app-helper/src/android/platform/helpers/SettingUserHelperImpl.java
@@ -89,7 +89,8 @@
     public boolean isUserPresent(String user) {
         UiObject2 addUserButton =
             scrollAndFindUiObject(
-                getResourceFromConfig(APP_NAME, APP_CONFIG, AutoConfigConstants.ADD_PROFILE));
+                getResourceFromConfig(APP_NAME, APP_CONFIG, AutoConfigConstants.ADD_PROFILE),
+                getScrollScreenIndex());
         Log.v(
             TAG,
             String.format(
@@ -97,7 +98,7 @@
                 AutoConfigConstants.ADD_PROFILE, addUserButton));
         if (addUserButton == null) {
             clickbutton(AutoConfigConstants.MANAGE_OTHER_PROFILES);
-            UiObject2 UserObject = scrollAndFindUiObject(By.text(user));
+            UiObject2 UserObject = scrollAndFindUiObject(By.text(user), getScrollScreenIndex());
             return UserObject != null;
         }
         return false;
@@ -134,9 +135,10 @@
     // click an on-screen element if expected text for that element is present
     private void clickbutton(String button_text) {
         UiObject2 buttonObject =
-            scrollAndFindUiObject(getResourceFromConfig(APP_NAME, APP_CONFIG, button_text));
+            scrollAndFindUiObject(getResourceFromConfig(APP_NAME, APP_CONFIG, button_text),
+                getScrollScreenIndex());
         if (buttonObject == null) {
-            buttonObject = scrollAndFindUiObject(By.text(button_text));
+            buttonObject = scrollAndFindUiObject(By.text(button_text), getScrollScreenIndex());
         }
         Log.v(
             TAG,
@@ -154,4 +156,13 @@
     private void goToQuickSettings() {
         clickbutton(AutoConfigConstants.TIME_PATTERN);
     }
+
+    //set scroll index to 1
+    private int getScrollScreenIndex() {
+        int scrollScreenIndex = 0;
+        if (hasSplitScreenSettingsUI()) {
+            scrollScreenIndex = 1;
+        }
+        return scrollScreenIndex;
+    }
 }
\ No newline at end of file
diff --git a/libraries/car-helpers/multiuser-helper/Android.bp b/libraries/car-helpers/multiuser-helper/Android.bp
index a4e40f6..9fd8040 100644
--- a/libraries/car-helpers/multiuser-helper/Android.bp
+++ b/libraries/car-helpers/multiuser-helper/Android.bp
@@ -23,7 +23,7 @@
         "src/**/*.java",
     ],
     static_libs: [
-        "android.car",
+        "android.car-test-stubs",
         "androidx.test.runner",
         "ub-uiautomator",
     ],
diff --git a/libraries/collectors-helper/jank/src/com/android/helpers/JankCollectionHelper.java b/libraries/collectors-helper/jank/src/com/android/helpers/JankCollectionHelper.java
index dbc1a63..dc2d889 100644
--- a/libraries/collectors-helper/jank/src/com/android/helpers/JankCollectionHelper.java
+++ b/libraries/collectors-helper/jank/src/com/android/helpers/JankCollectionHelper.java
@@ -55,109 +55,109 @@
     public enum GfxInfoMetric {
         // Example: "Total frames rendered: 20391"
         TOTAL_FRAMES(
-                Pattern.compile(".*Total frames rendered: (\\d+).*", Pattern.DOTALL),
+                Pattern.compile("Total frames rendered: (\\d+)", Pattern.DOTALL),
                 1,
                 "total_frames"),
         // Example: "Janky frames: 785 (3.85%)"
         JANKY_FRAMES_COUNT(
                 Pattern.compile(
-                        ".*Janky frames: (\\d+) \\(([0-9]+[\\.]?[0-9]+)\\%\\).*", Pattern.DOTALL),
+                        "Janky frames: (\\d+) \\(([0-9]+[\\.]?[0-9]+)\\%\\)", Pattern.DOTALL),
                 1,
                 "janky_frames_count"),
         // Example: "Janky frames: 785 (3.85%)"
         JANKY_FRAMES_PRCNT(
                 Pattern.compile(
-                        ".*Janky frames: (\\d+) \\(([0-9]+[\\.]?[0-9]+)\\%\\).*", Pattern.DOTALL),
+                        "Janky frames: (\\d+) \\(([0-9]+[\\.]?[0-9]+)\\%\\)", Pattern.DOTALL),
                 2,
                 "janky_frames_percent"),
         // Example: "Janky frames (legacy): 785 (3.85%)"
         JANKY_FRAMES_LEGACY_COUNT(
                 Pattern.compile(
-                        ".*Janky frames \\(legacy\\): (\\d+) \\(([0-9]+[\\.]?[0-9]+)\\%\\).*",
+                        "Janky frames \\(legacy\\): (\\d+) \\(([0-9]+[\\.]?[0-9]+)\\%\\)",
                         Pattern.DOTALL),
                 1,
                 "janky_frames_legacy_count"),
         // Example: "Janky frames (legacy): 785 (3.85%)"
         JANKY_FRAMES_LEGACY_PRCNT(
                 Pattern.compile(
-                        ".*Janky frames \\(legacy\\): (\\d+) \\(([0-9]+[\\.]?[0-9]+)\\%\\).*",
+                        "Janky frames \\(legacy\\): (\\d+) \\(([0-9]+[\\.]?[0-9]+)\\%\\)",
                         Pattern.DOTALL),
                 2,
                 "janky_frames_legacy_percent"),
         // Example: "50th percentile: 9ms"
         FRAME_TIME_50TH(
-                Pattern.compile(".*50th percentile: (\\d+)ms.*", Pattern.DOTALL),
+                Pattern.compile("50th percentile: (\\d+)ms", Pattern.DOTALL),
                 1,
                 "frame_render_time_percentile_50"),
         // Example: "90th percentile: 9ms"
         FRAME_TIME_90TH(
-                Pattern.compile(".*90th percentile: (\\d+)ms.*", Pattern.DOTALL),
+                Pattern.compile("90th percentile: (\\d+)ms", Pattern.DOTALL),
                 1,
                 "frame_render_time_percentile_90"),
         // Example: "95th percentile: 9ms"
         FRAME_TIME_95TH(
-                Pattern.compile(".*95th percentile: (\\d+)ms.*", Pattern.DOTALL),
+                Pattern.compile("95th percentile: (\\d+)ms", Pattern.DOTALL),
                 1,
                 "frame_render_time_percentile_95"),
         // Example: "99th percentile: 9ms"
         FRAME_TIME_99TH(
-                Pattern.compile(".*99th percentile: (\\d+)ms.*", Pattern.DOTALL),
+                Pattern.compile("99th percentile: (\\d+)ms", Pattern.DOTALL),
                 1,
                 "frame_render_time_percentile_99"),
         // Example: "Number Missed Vsync: 0"
         NUM_MISSED_VSYNC(
-                Pattern.compile(".*Number Missed Vsync: (\\d+).*", Pattern.DOTALL),
+                Pattern.compile("Number Missed Vsync: (\\d+)", Pattern.DOTALL),
                 1,
                 "missed_vsync"),
         // Example: "Number High input latency: 0"
         NUM_HIGH_INPUT_LATENCY(
-                Pattern.compile(".*Number High input latency: (\\d+).*", Pattern.DOTALL),
+                Pattern.compile("Number High input latency: (\\d+)", Pattern.DOTALL),
                 1,
                 "high_input_latency"),
         // Example: "Number Slow UI thread: 0"
         NUM_SLOW_UI_THREAD(
-                Pattern.compile(".*Number Slow UI thread: (\\d+).*", Pattern.DOTALL),
+                Pattern.compile("Number Slow UI thread: (\\d+)", Pattern.DOTALL),
                 1,
                 "slow_ui_thread"),
         // Example: "Number Slow bitmap uploads: 0"
         NUM_SLOW_BITMAP_UPLOADS(
-                Pattern.compile(".*Number Slow bitmap uploads: (\\d+).*", Pattern.DOTALL),
+                Pattern.compile("Number Slow bitmap uploads: (\\d+)", Pattern.DOTALL),
                 1,
                 "slow_bmp_upload"),
         // Example: "Number Slow issue draw commands: 0"
         NUM_SLOW_DRAW(
-                Pattern.compile(".*Number Slow issue draw commands: (\\d+).*", Pattern.DOTALL),
+                Pattern.compile("Number Slow issue draw commands: (\\d+)", Pattern.DOTALL),
                 1,
                 "slow_issue_draw_cmds"),
         // Example: "Number Frame deadline missed: 0"
         NUM_FRAME_DEADLINE_MISSED(
-                Pattern.compile(".*Number Frame deadline missed: (\\d+).*", Pattern.DOTALL),
+                Pattern.compile("Number Frame deadline missed: (\\d+)", Pattern.DOTALL),
                 1,
                 "deadline_missed"),
         // Number Frame deadline missed (legacy): 0
         NUM_FRAME_DEADLINE_MISSED_LEGACY(
                 Pattern.compile(
-                        ".*Number Frame deadline missed \\(legacy\\): (\\d+).*", Pattern.DOTALL),
+                        "Number Frame deadline missed \\(legacy\\): (\\d+)", Pattern.DOTALL),
                 1,
                 "deadline_missed_legacy"),
         // Example: "50th gpu percentile: 9ms"
         GPU_FRAME_TIME_50TH(
-                Pattern.compile(".*50th gpu percentile: (\\d+)ms.*", Pattern.DOTALL),
+                Pattern.compile("50th gpu percentile: (\\d+)ms", Pattern.DOTALL),
                 1,
                 "gpu_frame_render_time_percentile_50"),
         // Example: "90th gpu percentile: 9ms"
         GPU_FRAME_TIME_90TH(
-                Pattern.compile(".*90th gpu percentile: (\\d+)ms.*", Pattern.DOTALL),
+                Pattern.compile("90th gpu percentile: (\\d+)ms", Pattern.DOTALL),
                 1,
                 "gpu_frame_render_time_percentile_90"),
         // Example: "95th gpu percentile: 9ms"
         GPU_FRAME_TIME_95TH(
-                Pattern.compile(".*95th gpu percentile: (\\d+)ms.*", Pattern.DOTALL),
+                Pattern.compile("95th gpu percentile: (\\d+)ms", Pattern.DOTALL),
                 1,
                 "gpu_frame_render_time_percentile_95"),
         // Example: "99th gpu percentile: 9ms"
         GPU_FRAME_TIME_99TH(
-                Pattern.compile(".*99th gpu percentile: (\\d+)ms.*", Pattern.DOTALL),
+                Pattern.compile("99th gpu percentile: (\\d+)ms", Pattern.DOTALL),
                 1,
                 "gpu_frame_render_time_percentile_99");
 
@@ -173,7 +173,7 @@
 
         public Double parse(String lines) {
             Matcher matcher = mPattern.matcher(lines);
-            if (matcher.matches()) {
+            if (matcher.find()) {
                 return Double.valueOf(matcher.group(mGroupIndex));
             } else {
                 return null;
diff --git a/libraries/collectors-helper/jank/test/src/com/android/helpers/JankCollectionHelperTest.java b/libraries/collectors-helper/jank/test/src/com/android/helpers/JankCollectionHelperTest.java
index a7f4d75..602a772 100644
--- a/libraries/collectors-helper/jank/test/src/com/android/helpers/JankCollectionHelperTest.java
+++ b/libraries/collectors-helper/jank/test/src/com/android/helpers/JankCollectionHelperTest.java
@@ -272,6 +272,91 @@
         mHelper.stopCollecting();
     }
 
+    /**
+     * Test first values for package parsed. Gfx can provide values for every window in separate.
+     * For example gfx provides separate values for: ShellDropTarget, NavigationBar0,
+     * NotificationShade, StatusBar, etc. So we're looking for total value (the first one)
+     */
+    @Test
+    public void testCollect_firstValuesMatch() throws Exception {
+        final String totalApplicationResults =
+                "\n\n** Graphics info for pid 9999 [pkg1] **"
+                        + "\n"
+                        + "\nTotal frames rendered: 900"
+                        + "\nJanky frames: 300 (33.33%)"
+                        + "\nJanky frames (legacy): 200 (22.22%)"
+                        + "\n50th percentile: 150ms"
+                        + "\n90th percentile: 190ms"
+                        + "\n95th percentile: 195ms"
+                        + "\n99th percentile: 199ms"
+                        + "\nNumber Missed Vsync: 1"
+                        + "\nNumber High input latency: 2"
+                        + "\nNumber Slow UI thread: 3"
+                        + "\nNumber Slow bitmap uploads: 4"
+                        + "\nNumber Slow issue draw commands: 5"
+                        + "\nNumber Frame deadline missed (legacy): 3"
+                        + "\nNumber Frame deadline missed: 6";
+
+        final String shellDropTargetResults =
+                "\nWindow: ShellDropTarget"
+                        + "\n"
+                        + "\nTotal frames rendered: 0"
+                        + "\nJanky frames: 0 (00.00%)"
+                        + "\nJanky frames (legacy): 0 (00.00%)"
+                        + "\n50th percentile: 0ms"
+                        + "\n90th percentile: 0ms"
+                        + "\n95th percentile: 0ms"
+                        + "\n99th percentile: 0ms"
+                        + "\nNumber Missed Vsync: 0"
+                        + "\nNumber High input latency: 0"
+                        + "\nNumber Slow UI thread: 0"
+                        + "\nNumber Slow bitmap uploads: 0"
+                        + "\nNumber Slow issue draw commands: 0"
+                        + "\nNumber Frame deadline missed (legacy): 0"
+                        + "\nNumber Frame deadline missed: 0";
+
+        final String totalResults = totalApplicationResults + shellDropTargetResults;
+
+        mockResetCommand("", totalResults);
+        mockGetCommand("", totalResults);
+
+        mHelper.startCollecting();
+        Map<String, Double> metrics = mHelper.getMetrics();
+        assertThat(metrics.get(buildMetricKey("pkg1", TOTAL_FRAMES.getMetricId())))
+                .isEqualTo(900.0);
+        assertThat(metrics.get(buildMetricKey("pkg1", JANKY_FRAMES_COUNT.getMetricId())))
+                .isEqualTo(300.0);
+        assertThat(metrics.get(buildMetricKey("pkg1", JANKY_FRAMES_PRCNT.getMetricId())))
+                .isEqualTo(33.33);
+        assertThat(metrics.get(buildMetricKey("pkg1", JANKY_FRAMES_LEGACY_COUNT.getMetricId())))
+                .isEqualTo(200.0);
+        assertThat(metrics.get(buildMetricKey("pkg1", JANKY_FRAMES_LEGACY_PRCNT.getMetricId())))
+                .isEqualTo(22.22);
+        assertThat(metrics.get(buildMetricKey("pkg1", FRAME_TIME_50TH.getMetricId())))
+                .isEqualTo(150.0);
+        assertThat(metrics.get(buildMetricKey("pkg1", FRAME_TIME_90TH.getMetricId())))
+                .isEqualTo(190.0);
+        assertThat(metrics.get(buildMetricKey("pkg1", FRAME_TIME_95TH.getMetricId())))
+                .isEqualTo(195.0);
+        assertThat(metrics.get(buildMetricKey("pkg1", FRAME_TIME_99TH.getMetricId())))
+                .isEqualTo(199.0);
+        assertThat(metrics.get(buildMetricKey("pkg1", NUM_MISSED_VSYNC.getMetricId())))
+                .isEqualTo(1.0);
+        assertThat(metrics.get(buildMetricKey("pkg1", NUM_HIGH_INPUT_LATENCY.getMetricId())))
+                .isEqualTo(2.0);
+        assertThat(metrics.get(buildMetricKey("pkg1", NUM_SLOW_UI_THREAD.getMetricId())))
+                .isEqualTo(3.0);
+        assertThat(metrics.get(buildMetricKey("pkg1", NUM_SLOW_BITMAP_UPLOADS.getMetricId())))
+                .isEqualTo(4.0);
+        assertThat(metrics.get(buildMetricKey("pkg1", NUM_SLOW_DRAW.getMetricId()))).isEqualTo(5.0);
+        assertThat(metrics.get(buildMetricKey("pkg1", NUM_FRAME_DEADLINE_MISSED.getMetricId())))
+                .isEqualTo(6.0);
+        assertThat(
+                metrics.get(buildMetricKey("pkg1", NUM_FRAME_DEADLINE_MISSED_LEGACY.getMetricId())))
+                .isEqualTo(3.0);
+        mHelper.stopCollecting();
+    }
+
     /** Test that it collects known fields, even if some are unknown. */
     @Test
     public void testCollect_ignoreUnknownField() throws Exception {
diff --git a/libraries/collectors-helper/lyric/src/com/android/helpers/LyricMemProfilerHelper.java b/libraries/collectors-helper/lyric/src/com/android/helpers/LyricMemProfilerHelper.java
index 8647039..c42bdba 100644
--- a/libraries/collectors-helper/lyric/src/com/android/helpers/LyricMemProfilerHelper.java
+++ b/libraries/collectors-helper/lyric/src/com/android/helpers/LyricMemProfilerHelper.java
@@ -22,6 +22,7 @@
 import androidx.test.uiautomator.UiDevice;
 
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Timer;
@@ -45,7 +46,11 @@
 
     private static final int MIN_PROFILE_PERIOD_MS = 100;
 
-    String mPidName = "camera.provider@";
+    private String[] mCameraProcNameList;
+
+    @VisibleForTesting String[] mCameraPidList;
+
+    private String[] mMetricNameList;
 
     // Extract value of "Native Heap:" and  "TOTAL PSS:" from command: "dumpsys meminfo -s [pid]"
     // example of "dumpsys meminfo -s [pid]":
@@ -84,17 +89,15 @@
     // Folling Regexes is for removing "\n" in string
     private static final Pattern REMOVE_CR_PATTERN = Pattern.compile("\n");
 
-    // This Regexes is to match string format as:
-    //   provider@2.7-se:[pid of provider]
+    // List of regexes which are to match string format as:
+    //   [Camera process Name]xxx:[pid of camera process]
     //
-    // Use above pattern to find data section of camera provider
-    // of output string from command "dmabuf_dump"
-    private Pattern mDmabufProcPattern;
+    // Use above pattern to find data section of camera process
+    // in output string from command "dmabuf_dump"
+    private Pattern[] mCameraDmabufPatternList;
 
     private UiDevice mUiDevice;
 
-    private String mCameraProviderPid = "";
-
     private int mProfilePeriodMs = 0;
 
     private Timer mTimer;
@@ -103,17 +106,34 @@
         int mNativeHeap;
         int mTotalPss;
 
+        public MemInfo() {}
+
         public MemInfo(int nativeHeap, int totalPss) {
             mNativeHeap = nativeHeap;
             mTotalPss = totalPss;
         }
     }
 
-    private int mMaxNativeHeap;
+    private MemInfo[] mMaxCameraMemInfoList;
 
-    private int mMaxTotalPss;
+    private int[] mMaxCameraDmabufList;
 
-    private int mMaxDmabuf;
+    private int mMaxTotalCameraDmabuf = 0;
+
+    private int mMaxTotalCameraMemory = 0;
+
+    private synchronized void setMaxResult(
+            MemInfo[] memInfoList, int[] dmabufList, int totalDmabuf, int totalMemory) {
+        for (int i = 0; i < mMaxCameraMemInfoList.length; i++) {
+            mMaxCameraMemInfoList[i].mNativeHeap =
+                    Math.max(mMaxCameraMemInfoList[i].mNativeHeap, memInfoList[i].mNativeHeap);
+            mMaxCameraMemInfoList[i].mTotalPss =
+                    Math.max(mMaxCameraMemInfoList[i].mTotalPss, memInfoList[i].mTotalPss);
+            mMaxCameraDmabufList[i] = Math.max(mMaxCameraDmabufList[i], dmabufList[i]);
+        }
+        mMaxTotalCameraDmabuf = Math.max(mMaxTotalCameraDmabuf, totalDmabuf);
+        mMaxTotalCameraMemory = Math.max(mMaxTotalCameraMemory, totalMemory);
+    }
 
     @VisibleForTesting
     protected UiDevice initUiDevice() {
@@ -125,34 +145,71 @@
         if (null == mUiDevice) {
             mUiDevice = initUiDevice();
         }
-        getCameraProviderPid();
+        if (null != mCameraProcNameList
+                && 0 < mCameraProcNameList.length
+                && null != mMetricNameList
+                && mCameraProcNameList.length == mMetricNameList.length) {
+            mCameraPidList = new String[mCameraProcNameList.length];
+            mCameraDmabufPatternList = new Pattern[mCameraProcNameList.length];
+            for (int i = 0; i < mCameraProcNameList.length; i++) {
+                mCameraPidList[i] = getProcPid(mCameraProcNameList[i]);
+                // This Regexes is to match string format such as:
+                //   [camera process name]xxx:[pid of camera process]
+                //   ex: if "provider@" is camera process name, the pattern is:
+                //   provider@xxx:[pid of camera process]
+                // Use following pattern to find data section of camera prcoess
+                // of output string from command "dmabuf_dump"
+                mCameraDmabufPatternList[i] =
+                        Pattern.compile("\\s*.*:" + mCameraPidList[i] + "\\s*");
+            }
+            // To avoid frequnce of polling memory data too high and interference test case
+            // Set minimum polling period to MIN_PROFILE_PERIOD_MS (100), period profile only
+            // enable when MIN_PROFILE_PERIOD_MS <= configured polling period
+            if (MIN_PROFILE_PERIOD_MS <= mProfilePeriodMs) {
+                if (null == mTimer) {
+                    mMaxCameraMemInfoList = new MemInfo[mCameraProcNameList.length];
+                    for (int i = 0; i < mMaxCameraMemInfoList.length; i++)
+                        mMaxCameraMemInfoList[i] = new MemInfo();
+                    mMaxCameraDmabufList = new int[mCameraProcNameList.length];
+                    mTimer = new Timer();
+                    abstract class MyTimerTask extends TimerTask {
+                        MemInfo[] mTimerMemInfoList;
+                        int[] mTimerDmabufList;
+                        String[] mMemInfoStringList;
 
-        // This Regexes is to match string format as:
-        //   provider@2.7-se:[pid of provider]
-        //
-        // Use following pattern to find data section of camera provider
-        // of output string from command "dmabuf_dump"
-        mDmabufProcPattern = Pattern.compile("\\s*provider@.*:" + mCameraProviderPid + "\\s*");
-
-        // To avoid frequnce of polling memory data too high and interference test case
-        // Set minimum polling period to MIN_PROFILE_PERIOD_MS (100), period profile only
-        // enable when MIN_PROFILE_PERIOD_MS <= configured polling period
-        if (MIN_PROFILE_PERIOD_MS <= mProfilePeriodMs) {
-            if (null == mTimer) {
-                mTimer = new Timer();
-                mTimer.schedule(
-                        new TimerTask() {
-                            @Override
-                            public void run() {
-                                MemInfo memInfo = processMemInfo(getMemInfoString());
-                                int dmabuf = processDmabufDump(getDmabufDumpString());
-                                mMaxNativeHeap = Math.max(mMaxNativeHeap, memInfo.mNativeHeap);
-                                mMaxTotalPss = Math.max(mMaxTotalPss, memInfo.mTotalPss);
-                                mMaxDmabuf = Math.max(mMaxDmabuf, dmabuf);
-                            }
-                        },
-                        MIN_PROFILE_PERIOD_MS,
-                        mProfilePeriodMs);
+                        public MyTimerTask(int procNumber) {
+                            mTimerMemInfoList = new MemInfo[procNumber];
+                            mTimerDmabufList = new int[procNumber];
+                            mMemInfoStringList = new String[procNumber];
+                        }
+                    }
+                    mTimer.schedule(
+                            new MyTimerTask(mCameraProcNameList.length) {
+                                @Override
+                                public void run() {
+                                    int i, totalDmabuf = 0, totalMemory = 0;
+                                    String dmabufString = getDmabufDumpString();
+                                    for (i = 0; i < mTimerMemInfoList.length; i++) {
+                                        mMemInfoStringList[i] = getMemInfoString(mCameraPidList[i]);
+                                    }
+                                    processDmabufDump(dmabufString, mTimerDmabufList);
+                                    for (i = 0; i < mTimerMemInfoList.length; i++) {
+                                        mTimerMemInfoList[i] =
+                                                processMemInfo(mMemInfoStringList[i]);
+                                        totalMemory += mTimerMemInfoList[i].mTotalPss;
+                                        totalDmabuf += mTimerDmabufList[i];
+                                    }
+                                    totalMemory += totalDmabuf;
+                                    setMaxResult(
+                                            mTimerMemInfoList,
+                                            mTimerDmabufList,
+                                            totalDmabuf,
+                                            totalMemory);
+                                }
+                            },
+                            MIN_PROFILE_PERIOD_MS,
+                            mProfilePeriodMs);
+                }
             }
         }
         return true;
@@ -162,19 +219,43 @@
         mProfilePeriodMs = periodMs;
     }
 
-    public void setProfilePidName(String pidName) {
-        mPidName = pidName;
+    public void setProfileCameraProcName(String[] pidNameList) {
+        mCameraProcNameList = pidNameList;
+    }
+
+    public void setProfileMetricName(String[] metricNameList) {
+        mMetricNameList = metricNameList;
     }
 
     @Override
     public Map<String, Integer> getMetrics() {
-        String memInfoString = getMemInfoString();
+        if (null == mCameraPidList && 0 == mCameraPidList.length) {
+            return new HashMap<>();
+        }
+        String[] memInfoStringList = new String[mCameraPidList.length];
         String dmabufDumpString = getDmabufDumpString();
-        Map<String, Integer> metrics = processOutput(memInfoString, dmabufDumpString);
+        int i;
+        for (i = 0; i < memInfoStringList.length; i++) {
+            memInfoStringList[i] = getMemInfoString(mCameraPidList[i]);
+        }
+        Map<String, Integer> metrics = processOutput(memInfoStringList, dmabufDumpString);
+
         if (MIN_PROFILE_PERIOD_MS <= mProfilePeriodMs) {
-            metrics.put("maxNativeHeap", mMaxNativeHeap);
-            metrics.put("maxTotalPss", mMaxTotalPss);
-            metrics.put("maxDmabuf", mMaxDmabuf);
+            for (i = 0; i < mMetricNameList.length; i++) {
+                metrics.put(
+                        "max" + mMetricNameList[i] + "NativeHeap",
+                        mMaxCameraMemInfoList[i].mNativeHeap);
+                metrics.put(
+                        "max" + mMetricNameList[i] + "TotalPss",
+                        mMaxCameraMemInfoList[i].mTotalPss);
+                metrics.put("max" + mMetricNameList[i] + "Dmabuf", mMaxCameraDmabufList[i]);
+            }
+            metrics.put("maxNativeHeap", mMaxCameraMemInfoList[0].mNativeHeap);
+            metrics.put("maxTotalPss", mMaxCameraMemInfoList[0].mTotalPss);
+            metrics.put("maxDmabuf", mMaxCameraDmabufList[0]);
+
+            metrics.put("maxTotalCameraDmabuf", mMaxTotalCameraDmabuf);
+            metrics.put("maxTotalCameraMemory", mMaxTotalCameraMemory);
         }
         return metrics;
     }
@@ -201,54 +282,83 @@
         return new MemInfo(nativeHeap, totalPss);
     }
 
-    private int processDmabufDump(String dmabufDumpString) {
-        int dmabuf = 0;
+    private void processDmabufDump(String dmabufDumpString, int[] dmabufList) {
+        Pattern[] procPatternList =
+                Arrays.copyOf(mCameraDmabufPatternList, mCameraDmabufPatternList.length);
+        int matchCount = 0;
+        Integer matchIndex = null;
         Matcher matcher;
-        boolean procMatched = false;
         for (String line : dmabufDumpString.split("\n")) {
-            if (procMatched) {
-                matcher = METRIC_DMABUF_PSS_PATTERN.matcher(line);
-                if (matcher.find()) {
-                    dmabuf = Integer.parseInt(matcher.group(2));
-                    break;
+            if (null == matchIndex) {
+                for (int i = 0; i < procPatternList.length; i++) {
+                    if (procPatternList[i] != null) {
+                        matcher = procPatternList[i].matcher(line);
+                        if (matcher.find()) {
+                            matchIndex = i;
+                            procPatternList[i] = null;
+                            break;
+                        }
+                    }
                 }
             } else {
-                matcher = mDmabufProcPattern.matcher(line);
+                matcher = METRIC_DMABUF_PSS_PATTERN.matcher(line);
                 if (matcher.find()) {
-                    procMatched = true;
+                    dmabufList[matchIndex] = Integer.parseInt(matcher.group(2));
+                    matchIndex = null;
+                    matchCount++;
+                    if (matchCount == procPatternList.length) break;
                 }
             }
         }
-        return dmabuf;
     }
 
     @VisibleForTesting
-    Map<String, Integer> processOutput(String memInfoString, String dmabufDumpString) {
+    Map<String, Integer> processOutput(String[] memInfoStringList, String dmabufDumpString) {
         Map<String, Integer> metrics = new HashMap<>();
-        MemInfo memInfo = processMemInfo(memInfoString);
-        int dmabuf = processDmabufDump(dmabufDumpString);
-        if (0 < memInfo.mNativeHeap) metrics.put("nativeHeap", memInfo.mNativeHeap);
-        if (0 < memInfo.mTotalPss) metrics.put("totalPss", memInfo.mTotalPss);
-        if (0 < dmabuf) metrics.put("dmabuf", dmabuf);
+        MemInfo[] memInfoList = new MemInfo[mCameraProcNameList.length];
+        int[] dmabufList = new int[mCameraProcNameList.length];
+        int totalDmabuf = 0, totalMemory = 0, i;
+        processDmabufDump(dmabufDumpString, dmabufList);
+        for (i = 0; i < mCameraProcNameList.length; i++) {
+            memInfoList[i] = processMemInfo(memInfoStringList[i]);
+            totalDmabuf += dmabufList[i];
+            totalMemory += memInfoList[i].mTotalPss;
+        }
+        totalMemory += totalDmabuf;
+        if (null != mTimer) {
+            setMaxResult(memInfoList, dmabufList, totalDmabuf, totalMemory);
+        }
+        for (i = 0; i < mMetricNameList.length; i++) {
+            metrics.put(mMetricNameList[i] + "NativeHeap", memInfoList[i].mNativeHeap);
+            metrics.put(mMetricNameList[i] + "TotalPss", memInfoList[i].mTotalPss);
+            metrics.put(mMetricNameList[i] + "Dmabuf", dmabufList[i]);
+        }
+        metrics.put("nativeHeap", memInfoList[0].mNativeHeap);
+        metrics.put("totalPss", memInfoList[0].mTotalPss);
+        metrics.put("dmabuf", dmabufList[0]);
+
+        metrics.put("totalCameraDmabuf", totalDmabuf);
+        metrics.put("totalCameraMemory", totalMemory);
         return metrics;
     }
 
     @VisibleForTesting
-    public String getCameraProviderPid() {
+    String getProcPid(String procName) {
+        String procPid;
         try {
-            mCameraProviderPid = mUiDevice.executeShellCommand(PID_CMD + mPidName).trim();
+            procPid = mUiDevice.executeShellCommand(PID_CMD + procName).trim();
         } catch (IOException e) {
-            Log.e(TAG, "Failed to get camera provider PID");
-            mCameraProviderPid = "";
+            Log.e(TAG, "Failed to get PID of " + procName);
+            procPid = "";
         }
-        return mCameraProviderPid;
+        return procPid;
     }
 
     @VisibleForTesting
-    public String getMemInfoString() {
-        if (!mCameraProviderPid.isEmpty()) {
+    String getMemInfoString(String pidString) {
+        if (!pidString.isEmpty()) {
             try {
-                String cmdString = DUMPSYS_MEMINFO_CMD + mCameraProviderPid;
+                String cmdString = DUMPSYS_MEMINFO_CMD + pidString;
                 return mUiDevice.executeShellCommand(cmdString).trim();
             } catch (IOException e) {
                 Log.e(TAG, "Failed to get Mem info string ");
@@ -258,11 +368,11 @@
     }
 
     @VisibleForTesting
-    public String getDmabufDumpString() {
-        if (!mCameraProviderPid.isEmpty()) {
+    synchronized String getDmabufDumpString() {
+        if (null != mUiDevice) {
             try {
                 final int minDmabufStringLen = 100;
-                final int maxDmabufRetryCount = 3;
+                final int maxDmabufRetryCount = 5;
                 String dmabufString;
                 for (int retryCount = 0; retryCount < maxDmabufRetryCount; retryCount++) {
                     dmabufString = mUiDevice.executeShellCommand(DMABUF_DUMP_CMD).trim();
diff --git a/libraries/collectors-helper/lyric/test/src/com/android/helpers/LyricMemProfilerHelperTest.java b/libraries/collectors-helper/lyric/test/src/com/android/helpers/LyricMemProfilerHelperTest.java
index 41675bf..f81fabf 100644
--- a/libraries/collectors-helper/lyric/test/src/com/android/helpers/LyricMemProfilerHelperTest.java
+++ b/libraries/collectors-helper/lyric/test/src/com/android/helpers/LyricMemProfilerHelperTest.java
@@ -58,16 +58,33 @@
 
     private static final int MOCK_TOTAL_PSS = 200;
 
-    private static final int MOCK_DMABUF = 500;
+    private static final int MOCK_PROVIDER_DMABUF = 500;
+
+    private static final int MOCK_SERVER_DMABUF = 600;
+
+    private static final int MOCK_APP_DMABUF = 700;
+
+    private static final String[] TEST_PID_NAME_LIST = {
+        "provider@", "cameraserver", "id.GoogleCamera"
+    };
+    private static final String[] TEST_METRIC_NAME_LIST = {
+        "CameraProvider", "CameraServer", "CameraApp"
+    };
 
     private String genMemInfoString(int nativeHeap, int totalPss) {
         return ".Native Heap:" + nativeHeap + " TOTAL PSS:" + totalPss + " .";
     }
 
-    private String genDmabufString(int dmabuf, int pid) {
+    private String genDmabufString(
+            int providerDmabuf,
+            int serverDmabuf,
+            int appDmabuf,
+            int providerPid,
+            int serverPid,
+            int appPid) {
         String dmabufDumpString =
                 " provider@2.7-se:"
-                        + pid
+                        + providerPid
                         + "\n"
                         + "                  Name              Rss              Pss        "
                         + " nr_procs            Inode\n"
@@ -84,7 +101,49 @@
                         + "             <unknown>             8 kB             8 kB               "
                         + " 1           205580\n"
                         + "         PROCESS TOTAL          1752 kB          "
-                        + dmabuf
+                        + providerDmabuf
+                        + " kB\n"
+                        + "----------------------\n"
+                        + " cameraserver:"
+                        + serverPid
+                        + "\n"
+                        + "                  Name              Rss              Pss        "
+                        + " nr_procs            Inode\n"
+                        + "             <unknown>           576 kB           576 kB               "
+                        + " 1           153387\n"
+                        + "             <unknown>             8 kB             8 kB               "
+                        + " 1           153388\n"
+                        + "             <unknown>           576 kB           576 kB               "
+                        + " 1           153389\n"
+                        + "             <unknown>             8 kB             8 kB               "
+                        + " 1           153390\n"
+                        + "             <unknown>           576 kB           576 kB               "
+                        + " 1           205579\n"
+                        + "             <unknown>             8 kB             8 kB               "
+                        + " 1           205580\n"
+                        + "         PROCESS TOTAL        315376 kB        "
+                        + serverDmabuf
+                        + " kB\n"
+                        + "----------------------\n"
+                        + " id.GoogleCamera:"
+                        + appPid
+                        + "\n"
+                        + "                  Name              Rss              Pss        "
+                        + " nr_procs            Inode\n"
+                        + "             <unknown>           576 kB           576 kB               "
+                        + " 1           153387\n"
+                        + "             <unknown>             8 kB             8 kB               "
+                        + " 1           153388\n"
+                        + "             <unknown>           576 kB           576 kB               "
+                        + " 1           153389\n"
+                        + "             <unknown>             8 kB             8 kB               "
+                        + " 1           153390\n"
+                        + "             <unknown>           576 kB           576 kB               "
+                        + " 1           205579\n"
+                        + "             <unknown>             8 kB             8 kB               "
+                        + " 1           205580\n"
+                        + "         PROCESS TOTAL        347304 kB        "
+                        + appDmabuf
                         + " kB\n"
                         + "----------------------\n"
                         + "dmabuf total: 1752 kB kernel_rss: 0 kB userspace_rss: 1752 kB"
@@ -99,13 +158,19 @@
         // Return a fake pid for our fake processes and an empty string otherwise.
         doAnswer(
                         (inv) -> {
-                            final int pid = 1234;
+                            final int mockProviderPid = 1234;
+                            final int mockServerPid = 4567;
+                            final int mockAppPid = 8901;
                             String cmd = (String) inv.getArguments()[0];
                             if (cmd.startsWith("pgrep")) {
-                                return Integer.toString(pid);
+                                if (cmd.contains("provider@"))
+                                    return Integer.toString(mockProviderPid);
+                                if (cmd.contains("cameraserver"))
+                                    return Integer.toString(mockServerPid);
+                                return Integer.toString(mockAppPid);
                             } else if (cmd.startsWith("dumpsys meminfo")) {
                                 mMemInfoCmdCounter++;
-                                if (10 == mMemInfoCmdCounter) {
+                                if (10 < mMemInfoCmdCounter && mMemInfoCmdCounter < 15) {
                                     return genMemInfoString(
                                             MOCK_NATIVE_HEAP + 50,
                                             MOCK_TOTAL_PSS + 50); // max value
@@ -115,9 +180,21 @@
                             } else if (cmd.startsWith("dmabuf_dump")) {
                                 mDmabufCmdCounter++;
                                 if (10 == mDmabufCmdCounter) {
-                                    return genDmabufString(MOCK_DMABUF + 50, pid); // max value
+                                    return genDmabufString(
+                                            MOCK_PROVIDER_DMABUF + 50,
+                                            MOCK_SERVER_DMABUF + 50,
+                                            MOCK_APP_DMABUF + 50,
+                                            mockProviderPid,
+                                            mockServerPid,
+                                            mockAppPid); // max value
                                 } else {
-                                    return genDmabufString(MOCK_DMABUF, pid);
+                                    return genDmabufString(
+                                            MOCK_PROVIDER_DMABUF,
+                                            MOCK_SERVER_DMABUF,
+                                            MOCK_APP_DMABUF,
+                                            mockProviderPid,
+                                            mockServerPid,
+                                            mockAppPid);
                                 }
                             }
                             return "";
@@ -127,35 +204,69 @@
     }
 
     @Test
+    @SuppressWarnings("VisibleForTests")
     public void testParsePid() {
         LyricMemProfilerHelper helper = new LyricMemProfilerHelper();
-        String memInfoString = helper.getMemInfoString();
+        helper.setProfileCameraProcName(TEST_PID_NAME_LIST);
+        helper.setProfileMetricName(TEST_METRIC_NAME_LIST);
+        String providerMemInfoString = helper.getMemInfoString("");
         String dmabufDumpString = helper.getDmabufDumpString();
         // memInfo and dmabufDump get empty string due to mCameraProviderPid is empty
-        assertThat(memInfoString).isEmpty();
+        assertThat(providerMemInfoString).isEmpty();
         assertThat(dmabufDumpString).isEmpty();
 
         SystemClock.sleep(1000); // sleep 1 second to wait for camera provider initialize
+        helper.setProfileCameraProcName(TEST_PID_NAME_LIST);
         helper.startCollecting();
-        memInfoString = helper.getMemInfoString();
+        String[] memInfoStringList = new String[TEST_PID_NAME_LIST.length];
         dmabufDumpString = helper.getDmabufDumpString();
-        Map<String, Integer> metrics = helper.processOutput(memInfoString, dmabufDumpString);
+        int i;
+        for (i = 0; i < memInfoStringList.length; i++) {
+            memInfoStringList[i] = helper.getMemInfoString(helper.mCameraPidList[i]);
+        }
+        Map<String, Integer> metrics = helper.processOutput(memInfoStringList, dmabufDumpString);
 
         assertThat(metrics).containsKey("nativeHeap");
         assertThat(metrics).containsKey("totalPss");
         assertThat(metrics).containsKey("dmabuf");
 
+        assertThat(metrics).containsKey("CameraProviderNativeHeap");
+        assertThat(metrics).containsKey("CameraProviderTotalPss");
+        assertThat(metrics).containsKey("CameraProviderDmabuf");
+
+        assertThat(metrics).containsKey("CameraServerNativeHeap");
+        assertThat(metrics).containsKey("CameraServerTotalPss");
+        assertThat(metrics).containsKey("CameraServerDmabuf");
+
+        assertThat(metrics).containsKey("CameraAppNativeHeap");
+        assertThat(metrics).containsKey("CameraAppTotalPss");
+        assertThat(metrics).containsKey("CameraAppDmabuf");
+
         assertThat(metrics.get("nativeHeap")).isGreaterThan(0);
         assertThat(metrics.get("totalPss")).isGreaterThan(0);
-        assertThat(metrics.get("dmabuf")).isGreaterThan(0);
+
+        assertThat(metrics.get("CameraProviderNativeHeap")).isGreaterThan(0);
+        assertThat(metrics.get("CameraProviderTotalPss")).isGreaterThan(0);
+
+        assertThat(metrics.get("CameraServerNativeHeap")).isGreaterThan(0);
+        assertThat(metrics.get("CameraServerTotalPss")).isGreaterThan(0);
+
+        assertThat(metrics.get("CameraAppNativeHeap")).isGreaterThan(0);
+        assertThat(metrics.get("CameraAppTotalPss")).isGreaterThan(0);
     }
 
     @Test
+    @SuppressWarnings("VisibleForTests")
     public void testProcessOutput() {
         LyricMemProfilerHelper helper = new LyricMemProfilerHelper();
+        helper.setProfileCameraProcName(TEST_PID_NAME_LIST);
+        helper.setProfileMetricName(TEST_METRIC_NAME_LIST);
         helper.startCollecting();
-        String pid = helper.getCameraProviderPid();
-        String memInfoString =
+        String providerPid = helper.getProcPid("provider@");
+        String serverPid = helper.getProcPid("cameraserver");
+        String appPid = helper.getProcPid("id.GoogleCamera");
+
+        String providerMemInfoString =
                 "Applications Memory Usage (in Kilobytes):\n"
                         + "Uptime: 2649336 Realtime: 3041976\n"
                         + "** MEMINFO in pid 14612 [android.hardwar] **\n"
@@ -174,9 +285,47 @@
                         + "           TOTAL PSS:   550123            TOTAL RSS:   584800      TOTAL"
                         + " SWAP (KB):        0";
 
+        String serverMemInfoString =
+                "Applications Memory Usage (in Kilobytes):\n"
+                        + "Uptime: 78463994 Realtime: 78463994\n"
+                        + "** MEMINFO in pid 23081 [cameraserver] **\n"
+                        + "App Summary\n"
+                        + "                       Pss(KB)                        Rss(KB)\n"
+                        + "                        ------                         ------\n"
+                        + "           Java Heap:        0                              0\n"
+                        + "         Native Heap:      584                            584\n"
+                        + "                Code:     6860                          22764\n"
+                        + "               Stack:      104                            104\n"
+                        + "            Graphics:        0                              0\n"
+                        + "       Private Other:      636\n"
+                        + "              System:     1446\n"
+                        + "             Unknown:                                    1468\n"
+                        + "\n"
+                        + "           TOTAL PSS:     9630            TOTAL RSS:    24920      TOTAL"
+                        + " SWAP (KB):        0";
+
+        String appMemInfoString =
+                "Applications Memory Usage (in Kilobytes):\n"
+                        + "Uptime: 78887501 Realtime: 78887501\n"
+                        + "** MEMINFO in pid 20264 [com.google.android.GoogleCamera] **\n"
+                        + "App Summary\n"
+                        + "                       Pss(KB)                        Rss(KB)\n"
+                        + "                        ------                         ------\n"
+                        + "           Java Heap:     7724                          35404\n"
+                        + "         Native Heap:    94780                          97908\n"
+                        + "                Code:    14148                         152312\n"
+                        + "               Stack:     1280                           1296\n"
+                        + "            Graphics:    44472                          44472\n"
+                        + "       Private Other:     7524\n"
+                        + "              System:    25432\n"
+                        + "             Unknown:                                   12616\n"
+                        + "\n"
+                        + "           TOTAL PSS:   195360            TOTAL RSS:   344008      TOTAL"
+                        + " SWAP (KB):        0";
+
         String dmabufDumpString =
                 " provider@2.7-se:"
-                        + pid
+                        + providerPid
                         + "\n"
                         + "                  Name              Rss              Pss        "
                         + " nr_procs            Inode\n"
@@ -194,23 +343,98 @@
                         + " 1           205580\n"
                         + "         PROCESS TOTAL          1752 kB          1552 kB\n"
                         + "----------------------\n"
-                        + "dmabuf total: 1752 kB kernel_rss: 0 kB userspace_rss: 1752 kB"
-                        + " userspace_pss: 1752 kB";
+                        + " id.GoogleCamera:"
+                        + appPid
+                        + "\n"
+                        + "                  Name              Rss              Pss        "
+                        + " nr_procs            Inode\n"
+                        + "             <unknown>           576 kB           576 kB               "
+                        + " 1           153387\n"
+                        + "             <unknown>             8 kB             8 kB               "
+                        + " 1           153388\n"
+                        + "             <unknown>           576 kB           576 kB               "
+                        + " 1           153389\n"
+                        + "             <unknown>             8 kB             8 kB               "
+                        + " 1           153390\n"
+                        + "             <unknown>           576 kB           576 kB               "
+                        + " 1           205579\n"
+                        + "             <unknown>             8 kB             8 kB               "
+                        + " 1           205580\n"
+                        + "         PROCESS TOTAL        347304 kB        105137 kB\n"
+                        + "----------------------\n"
+                        + " cameraserver:"
+                        + serverPid
+                        + "\n"
+                        + "                  Name              Rss              Pss        "
+                        + " nr_procs            Inode\n"
+                        + "             <unknown>           576 kB           576 kB               "
+                        + " 1           153387\n"
+                        + "             <unknown>             8 kB             8 kB               "
+                        + " 1           153388\n"
+                        + "             <unknown>           576 kB           576 kB               "
+                        + " 1           153389\n"
+                        + "             <unknown>             8 kB             8 kB               "
+                        + " 1           153390\n"
+                        + "             <unknown>           576 kB           576 kB               "
+                        + " 1           205579\n"
+                        + "             <unknown>             8 kB             8 kB               "
+                        + " 1           205580\n"
+                        + "         PROCESS TOTAL        315376 kB        104349 kB\n"
+                        + "----------------------\n"
+                        + "dmabuf total: 630288 kB kernel_rss: 0 kB userspace_rss: 1410780 kB"
+                        + " userspace_pss: 509763 kB";
 
-        Map<String, Integer> metrics = helper.processOutput(memInfoString, dmabufDumpString);
+        String[] memInfoStringList = {providerMemInfoString, serverMemInfoString, appMemInfoString};
+        Map<String, Integer> metrics = helper.processOutput(memInfoStringList, dmabufDumpString);
+
         assertThat(metrics.get("nativeHeap")).isEqualTo(377584);
         assertThat(metrics.get("totalPss")).isEqualTo(550123);
         assertThat(metrics.get("dmabuf")).isEqualTo(1552);
 
-        metrics = helper.processOutput("", "");
-        assertThat(metrics).doesNotContainKey("nativeHeap");
-        assertThat(metrics).doesNotContainKey("totalPss");
-        assertThat(metrics).doesNotContainKey("dmabuf");
+        assertThat(metrics.get("CameraProviderNativeHeap")).isEqualTo(377584);
+        assertThat(metrics.get("CameraProviderTotalPss")).isEqualTo(550123);
+        assertThat(metrics.get("CameraProviderDmabuf")).isEqualTo(1552);
+
+        assertThat(metrics.get("CameraServerNativeHeap")).isEqualTo(584);
+        assertThat(metrics.get("CameraServerTotalPss")).isEqualTo(9630);
+        assertThat(metrics.get("CameraServerDmabuf")).isEqualTo(104349);
+
+        assertThat(metrics.get("CameraAppNativeHeap")).isEqualTo(94780);
+        assertThat(metrics.get("CameraAppTotalPss")).isEqualTo(195360);
+        assertThat(metrics.get("CameraAppDmabuf")).isEqualTo(105137);
+
+        int totalDmabuf = 1552 + 104349 + 105137;
+        assertThat(metrics.get("totalCameraDmabuf")).isEqualTo(totalDmabuf);
+        int totalMemory = 550123 + 9630 + 195360 + totalDmabuf;
+        assertThat(metrics.get("totalCameraMemory")).isEqualTo(totalMemory);
+        String[] memInfoStringList2 = {"", "", ""};
+        metrics = helper.processOutput(memInfoStringList2, "");
+        assertThat(metrics.get("nativeHeap")).isEqualTo(0);
+        assertThat(metrics.get("totalPss")).isEqualTo(0);
+        assertThat(metrics.get("dmabuf")).isEqualTo(0);
+
+        assertThat(metrics.get("CameraProviderNativeHeap")).isEqualTo(0);
+        assertThat(metrics.get("CameraProviderTotalPss")).isEqualTo(0);
+        assertThat(metrics.get("CameraProviderDmabuf")).isEqualTo(0);
+
+        assertThat(metrics.get("CameraServerNativeHeap")).isEqualTo(0);
+        assertThat(metrics.get("CameraServerTotalPss")).isEqualTo(0);
+        assertThat(metrics.get("CameraServerDmabuf")).isEqualTo(0);
+
+        assertThat(metrics.get("CameraAppNativeHeap")).isEqualTo(0);
+        assertThat(metrics.get("CameraAppTotalPss")).isEqualTo(0);
+        assertThat(metrics.get("CameraAppDmabuf")).isEqualTo(0);
+
+        assertThat(metrics.get("totalCameraDmabuf")).isEqualTo(0);
+        assertThat(metrics.get("totalCameraMemory")).isEqualTo(0);
     }
 
     @Test
+    @SuppressWarnings("VisibleForTests")
     public void testProfilePeriod() {
         LyricMemProfilerHelper helper = new TestableLyricMemProfilerHelper();
+        helper.setProfileCameraProcName(TEST_PID_NAME_LIST);
+        helper.setProfileMetricName(TEST_METRIC_NAME_LIST);
         helper.setProfilePeriodMs(100);
         helper.startCollecting();
         SystemClock.sleep(2000);
@@ -219,23 +443,76 @@
         assertThat(metrics).containsKey("nativeHeap");
         assertThat(metrics).containsKey("totalPss");
         assertThat(metrics).containsKey("dmabuf");
+        assertThat(metrics).containsKey("CameraProviderNativeHeap");
+        assertThat(metrics).containsKey("CameraProviderTotalPss");
+        assertThat(metrics).containsKey("CameraProviderDmabuf");
+        assertThat(metrics).containsKey("CameraServerNativeHeap");
+        assertThat(metrics).containsKey("CameraServerTotalPss");
+        assertThat(metrics).containsKey("CameraServerDmabuf");
+        assertThat(metrics).containsKey("CameraAppNativeHeap");
+        assertThat(metrics).containsKey("CameraAppTotalPss");
+        assertThat(metrics).containsKey("CameraAppDmabuf");
+        assertThat(metrics).containsKey("totalCameraDmabuf");
+        assertThat(metrics).containsKey("totalCameraMemory");
+
         assertThat(metrics).containsKey("maxNativeHeap");
         assertThat(metrics).containsKey("maxTotalPss");
         assertThat(metrics).containsKey("maxDmabuf");
+        assertThat(metrics).containsKey("maxCameraProviderNativeHeap");
+        assertThat(metrics).containsKey("maxCameraProviderTotalPss");
+        assertThat(metrics).containsKey("maxCameraProviderDmabuf");
+        assertThat(metrics).containsKey("maxCameraServerNativeHeap");
+        assertThat(metrics).containsKey("maxCameraServerTotalPss");
+        assertThat(metrics).containsKey("maxCameraServerDmabuf");
+        assertThat(metrics).containsKey("maxCameraAppNativeHeap");
+        assertThat(metrics).containsKey("maxCameraAppTotalPss");
+        assertThat(metrics).containsKey("maxCameraAppDmabuf");
+        assertThat(metrics).containsKey("maxTotalCameraDmabuf");
+        assertThat(metrics).containsKey("maxTotalCameraMemory");
 
         assertThat(metrics.get("nativeHeap")).isLessThan(metrics.get("maxNativeHeap"));
         assertThat(metrics.get("totalPss")).isLessThan(metrics.get("maxTotalPss"));
         assertThat(metrics.get("dmabuf")).isLessThan(metrics.get("maxDmabuf"));
+
+        assertThat(metrics.get("CameraProviderNativeHeap"))
+                .isLessThan(metrics.get("maxCameraProviderNativeHeap"));
+        assertThat(metrics.get("CameraProviderTotalPss"))
+                .isLessThan(metrics.get("maxCameraProviderTotalPss"));
+        assertThat(metrics.get("CameraProviderDmabuf"))
+                .isLessThan(metrics.get("maxCameraProviderDmabuf"));
+
+        assertThat(metrics.get("CameraServerNativeHeap"))
+                .isLessThan(metrics.get("maxCameraServerNativeHeap"));
+        assertThat(metrics.get("CameraServerTotalPss"))
+                .isLessThan(metrics.get("maxCameraServerTotalPss"));
+        assertThat(metrics.get("CameraServerDmabuf"))
+                .isLessThan(metrics.get("maxCameraServerDmabuf"));
+
+        assertThat(metrics.get("CameraAppNativeHeap"))
+                .isLessThan(metrics.get("maxCameraAppNativeHeap"));
+        assertThat(metrics.get("CameraAppTotalPss"))
+                .isLessThan(metrics.get("maxCameraAppTotalPss"));
+        assertThat(metrics.get("CameraAppDmabuf")).isLessThan(metrics.get("maxCameraAppDmabuf"));
+
+        assertThat(metrics.get("totalCameraDmabuf"))
+                .isLessThan(metrics.get("maxTotalCameraDmabuf"));
+        assertThat(metrics.get("totalCameraMemory"))
+                .isLessThan(metrics.get("maxTotalCameraMemory"));
     }
 
     @Test
+    @SuppressWarnings("VisibleForTests")
     public void testProfilePeriodLessThanMin() {
         LyricMemProfilerHelper helper = new TestableLyricMemProfilerHelper();
+        helper.setProfileCameraProcName(TEST_PID_NAME_LIST);
+        helper.setProfileMetricName(TEST_METRIC_NAME_LIST);
         InOrder inOrder = inOrder(mUiDevice);
         helper.setProfilePeriodMs(50);
         helper.startCollecting();
         try {
-            inOrder.verify(mUiDevice).executeShellCommand("pgrep -f -o camera.provider@");
+            inOrder.verify(mUiDevice).executeShellCommand("pgrep -f -o provider@");
+            inOrder.verify(mUiDevice).executeShellCommand("pgrep -f -o cameraserver");
+            inOrder.verify(mUiDevice).executeShellCommand("pgrep -f -o id.GoogleCamera");
         } catch (IOException e) {
             Log.e(TAG, "Failed to execute Shell command");
         }
@@ -247,24 +524,68 @@
         assertThat(metrics).containsKey("nativeHeap");
         assertThat(metrics).containsKey("totalPss");
         assertThat(metrics).containsKey("dmabuf");
+        assertThat(metrics).containsKey("CameraProviderNativeHeap");
+        assertThat(metrics).containsKey("CameraProviderTotalPss");
+        assertThat(metrics).containsKey("CameraProviderDmabuf");
+        assertThat(metrics).containsKey("CameraServerNativeHeap");
+        assertThat(metrics).containsKey("CameraServerTotalPss");
+        assertThat(metrics).containsKey("CameraServerDmabuf");
+        assertThat(metrics).containsKey("CameraAppNativeHeap");
+        assertThat(metrics).containsKey("CameraAppTotalPss");
+        assertThat(metrics).containsKey("CameraAppDmabuf");
+        assertThat(metrics).containsKey("totalCameraDmabuf");
+        assertThat(metrics).containsKey("totalCameraMemory");
+
         assertThat(metrics).doesNotContainKey("maxNativeHeap");
         assertThat(metrics).doesNotContainKey("maxTotalPss");
         assertThat(metrics).doesNotContainKey("maxDmabuf");
+        assertThat(metrics).doesNotContainKey("maxCameraProviderNativeHeap");
+        assertThat(metrics).doesNotContainKey("maxCameraProviderTotalPss");
+        assertThat(metrics).doesNotContainKey("maxCameraProviderDmabuf");
+        assertThat(metrics).doesNotContainKey("maxCameraServerNativeHeap");
+        assertThat(metrics).doesNotContainKey("maxCameraServerTotalPss");
+        assertThat(metrics).doesNotContainKey("maxCameraServerDmabuf");
+        assertThat(metrics).doesNotContainKey("maxCameraAppNativeHeap");
+        assertThat(metrics).doesNotContainKey("maxCameraAppTotalPss");
+        assertThat(metrics).doesNotContainKey("maxCameraAppDmabuf");
+        assertThat(metrics).doesNotContainKey("maxTotalCameraDmabuf");
+        assertThat(metrics).doesNotContainKey("maxTotalCameraMemory");
 
         assertThat(metrics.get("nativeHeap")).isEqualTo(MOCK_NATIVE_HEAP);
         assertThat(metrics.get("totalPss")).isEqualTo(MOCK_TOTAL_PSS);
-        assertThat(metrics.get("dmabuf")).isEqualTo(MOCK_DMABUF);
+        assertThat(metrics.get("dmabuf")).isEqualTo(MOCK_PROVIDER_DMABUF);
+        assertThat(metrics.get("CameraProviderNativeHeap")).isEqualTo(MOCK_NATIVE_HEAP);
+        assertThat(metrics.get("CameraProviderTotalPss")).isEqualTo(MOCK_TOTAL_PSS);
+        assertThat(metrics.get("CameraProviderDmabuf")).isEqualTo(MOCK_PROVIDER_DMABUF);
+        assertThat(metrics.get("CameraServerNativeHeap")).isEqualTo(MOCK_NATIVE_HEAP);
+        assertThat(metrics.get("CameraServerTotalPss")).isEqualTo(MOCK_TOTAL_PSS);
+        assertThat(metrics.get("CameraServerDmabuf")).isEqualTo(MOCK_SERVER_DMABUF);
+        assertThat(metrics.get("CameraAppNativeHeap")).isEqualTo(MOCK_NATIVE_HEAP);
+        assertThat(metrics.get("CameraAppTotalPss")).isEqualTo(MOCK_TOTAL_PSS);
+        assertThat(metrics.get("CameraAppDmabuf")).isEqualTo(MOCK_APP_DMABUF);
+        int totalCameraDmabuf = MOCK_PROVIDER_DMABUF + MOCK_SERVER_DMABUF + MOCK_APP_DMABUF;
+        assertThat(metrics.get("totalCameraDmabuf")).isEqualTo(totalCameraDmabuf);
+        int totalCameraMemory = totalCameraDmabuf + 3 * MOCK_TOTAL_PSS;
+        assertThat(metrics.get("totalCameraMemory")).isEqualTo(totalCameraMemory);
     }
 
     @Test
+    @SuppressWarnings("VisibleForTests")
     public void testSetNewPidName() {
         LyricMemProfilerHelper helper = new TestableLyricMemProfilerHelper();
         InOrder inOrder = inOrder(mUiDevice);
-        final String newPidName = "new.camera.name";
-        helper.setProfilePidName(newPidName);
+        final String newProviderPidName = "new.provider.name";
+        final String newServerPidName = "new.server.name";
+        final String newAppPidName = "new.app.name";
+        final String[] newPidNameList = {newProviderPidName, newServerPidName, newAppPidName};
+        final String[] newMetricNameList = {"CameraProvider", "CameraServer", "CameraApp"};
+        helper.setProfileCameraProcName(newPidNameList);
+        helper.setProfileMetricName(newMetricNameList);
         helper.startCollecting();
         try {
-            inOrder.verify(mUiDevice).executeShellCommand("pgrep -f -o " + newPidName);
+            inOrder.verify(mUiDevice).executeShellCommand("pgrep -f -o " + newProviderPidName);
+            inOrder.verify(mUiDevice).executeShellCommand("pgrep -f -o " + newServerPidName);
+            inOrder.verify(mUiDevice).executeShellCommand("pgrep -f -o " + newAppPidName);
         } catch (IOException e) {
             Log.e(TAG, "Failed to execute Shell command");
         }
diff --git a/libraries/collectors-helper/memory/src/com/android/helpers/HeapDumpHelper.java b/libraries/collectors-helper/memory/src/com/android/helpers/HeapDumpHelper.java
new file mode 100644
index 0000000..4559090
--- /dev/null
+++ b/libraries/collectors-helper/memory/src/com/android/helpers/HeapDumpHelper.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2022 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.helpers;
+
+import android.util.Log;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * HeapDumpHelper is a helper used to collect the heapdump and store the output in a
+ * file created using the given id.
+ */
+public class HeapDumpHelper implements ICollectorHelper<String> {
+    private static final String TAG = HeapDumpHelper.class.getSimpleName();
+    private static final String HEAPDUMP_OUTPUT_FILE_METRIC_NAME = "heapdump_file_";
+    private static final String HEAPDUMP_CMD = "am dumpheap %s %s";
+
+    String mId = null;
+    File mResultsFile = null;
+    private String[] mProcessNames = null;
+    private String mTestOutputDir = null;
+    private UiDevice mUiDevice;
+    HashMap<String, String> mHeapDumpFinalMap;
+
+    @Override
+    public boolean startCollecting() {
+        return true;
+    }
+
+    public void setUp(String testOutputDir, String... processNames) {
+        mProcessNames = processNames;
+        mTestOutputDir = testOutputDir;
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+      }
+
+    @Override
+    public boolean startCollecting(String id) {
+        mId = id;
+        mHeapDumpFinalMap = new HashMap<>();
+        if (!collectHeapDump()) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public Map<String, String> getMetrics() {
+
+        Log.i(TAG, "Metric collector enabled. Dumping the hprof.");
+        int processCount = 0;
+        for (String processName : mProcessNames) {
+            if (mId != null && !mId.isEmpty()) {
+                try {
+                    processCount++;
+                    String finalHeapDumpPath = String.format("%s%s_%s.hprof", mTestOutputDir,
+                            processName.replace("/", "#"), mId);
+                    execHeapDump(processName, finalHeapDumpPath);
+                    mHeapDumpFinalMap.put(HEAPDUMP_OUTPUT_FILE_METRIC_NAME + processCount,
+                            finalHeapDumpPath);
+                } catch (Throwable e) {
+                    Log.e(TAG, "dumpheap command failed", e);
+                }
+            } else {
+                Log.e(TAG, "Metric collector is enabled but the heap dump file id is not valid.");
+            }
+        }
+        return mHeapDumpFinalMap;
+    }
+
+    private String execHeapDump(String processName, String filePath) throws IOException {
+        try {
+            Log.i(TAG, "Running heapdump command :" + String.format(HEAPDUMP_CMD,
+                    processName, filePath));
+            return mUiDevice.executeShellCommand(String.format(HEAPDUMP_CMD,
+                    processName, filePath));
+        } catch (IOException e) {
+            throw new RuntimeException(
+                    String.format("Unable to execute heapdump command for %s ", processName), e);
+        }
+    }
+
+    /**
+     * Returns true if heap dump collection is enabled and heap dump file name is valid.
+     */
+    private boolean collectHeapDump() {
+        if (mId == null || mId.isEmpty()) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public boolean stopCollecting() {
+        mId = null;
+        return true;
+    }
+
+    public Map<String,String> getFinalResultsMap() {
+        return mHeapDumpFinalMap;
+    }
+}
diff --git a/libraries/collectors-helper/memory/src/com/android/helpers/ShowmapSnapshotHelper.java b/libraries/collectors-helper/memory/src/com/android/helpers/ShowmapSnapshotHelper.java
index c8eb6bb..bb6e03a 100644
--- a/libraries/collectors-helper/memory/src/com/android/helpers/ShowmapSnapshotHelper.java
+++ b/libraries/collectors-helper/memory/src/com/android/helpers/ShowmapSnapshotHelper.java
@@ -70,6 +70,7 @@
     private int mDropCacheOption;
     private boolean mCollectForAllProcesses = false;
     private UiDevice mUiDevice;
+    private boolean mRunGcPrecollection;
 
     // Map to maintain per-process memory info
     private Map<String, String> mMemoryMap = new HashMap<>();
@@ -79,10 +80,11 @@
     private Map<String, List<Integer>> mMetricNameIndexMap = new HashMap<>();
 
     public void setUp(String testOutputDir, String... processNames) {
-        mProcessNames = processNames;
-        mTestOutputDir = testOutputDir;
-        mDropCacheOption = 0;
-        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+      mProcessNames = processNames;
+      mTestOutputDir = testOutputDir;
+      mDropCacheOption = 0;
+      mRunGcPrecollection = false;
+      mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
     }
 
     @Override
@@ -146,6 +148,16 @@
                 return mMemoryMap;
             }
 
+            // Force Garbage collect to trim transient objects before taking memory measurements
+            // as memory tests aim to track persistent memory regression instead of transient
+            // memory which also allows for de-noising and reducing likelihood of false alerts.
+            if (mRunGcPrecollection) {
+              Log.i(TAG, "Running GC precollect");
+              android.os.Trace.beginSection("GCPriorToShowmap");
+              Runtime.getRuntime().gc();
+              android.os.Trace.endSection();
+            }
+
             FileWriter writer = new FileWriter(new File(mTestOutputFile), true);
             for (String processName : mProcessNames) {
                 List<Integer> pids = new ArrayList<>();
@@ -198,6 +210,15 @@
     }
 
     /**
+     * Sets option for running GC prior to collection.
+     *
+     * @param shouldGcOnPrecollect whether it should run GC prior to showmap collection
+     */
+    public void setGcOnPrecollectOption(boolean shouldGcOnPrecollect) {
+        mRunGcPrecollection = shouldGcOnPrecollect;
+    }
+
+    /**
      * Set drop cache option.
      *
      * @param dropCacheOption drop pagecache (1), slab (2) or all (3) cache
diff --git a/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/HeapDumpHelperTest.java b/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/HeapDumpHelperTest.java
new file mode 100644
index 0000000..f04f550
--- /dev/null
+++ b/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/HeapDumpHelperTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2022 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.helpers.tests;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.UiDevice;
+import com.android.helpers.HeapDumpHelper;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+/**
+ * Android Unit tests for {@link HeapDumpHelper}.
+ *
+ * To run:
+ * atest CollectorsHelperTest:com.android.helpers.tests.HeapDumpHelperTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class HeapDumpHelperTest {
+    private static final String TAG = HeapDumpHelperTest.class.getSimpleName();
+    private HeapDumpHelper mHeapDumpHelper;
+
+    @Before
+    public void setUp() {
+        mHeapDumpHelper = new HeapDumpHelper();
+    }
+
+    @After
+    public void tearDown() throws IOException {
+        UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        for (Map.Entry<String, String> entry : mHeapDumpHelper.getMetrics().entrySet()) {
+            uiDevice.executeShellCommand(String.format("rm %s", entry.getValue()));
+        }
+    }
+
+    @Test
+    public void testSuccessfulHeapDumpCollection() {
+        mHeapDumpHelper.setUp("/data/local/tmp/", "com.android.systemui");
+        assertTrue(mHeapDumpHelper.startCollecting("sample-heapdump-1"));
+        Map<String, String> metrics = mHeapDumpHelper.getMetrics();
+        assertTrue(metrics.size() == 1);
+    }
+
+    @Test
+    public void testHeapCollectionProcessWithSpecialChars() {
+        mHeapDumpHelper.setUp("/data/local/tmp/", "/system/bin/surfaceflinger");
+        assertTrue(mHeapDumpHelper.startCollecting("sample-heapdump-1"));
+        Map<String, String> metrics = mHeapDumpHelper.getMetrics();
+        assertTrue(metrics.size() == 1);
+        assertTrue(metrics.get("heapdump_file_1").equalsIgnoreCase(
+                "/data/local/tmp/#system#bin#surfaceflinger_sample-heapdump-1.hprof"));
+    }
+
+    @Test
+    public void testSuccessfulHeapDumpCollectionForTwoProcesses() {
+        String[] processNames = new String[] { "com.android.systemui", "system_server" };
+        mHeapDumpHelper.setUp("/data/local/tmp/", processNames);
+        assertTrue(mHeapDumpHelper.startCollecting("sample-heapdump-2"));
+        Map<String, String> metrics = mHeapDumpHelper.getMetrics();
+        assertTrue(metrics.size() == 2);
+    }
+
+    @Test
+    public void testHeapDumpNotCollectedWithEmptyId() {
+        mHeapDumpHelper.setUp("/data/local/tmp/", "com.android.systemui");
+        assertFalse(mHeapDumpHelper.startCollecting(""));
+        Map<String, String> metrics = mHeapDumpHelper.getMetrics();
+        assertTrue(metrics.size() == 0);
+    }
+}
diff --git a/libraries/collectors-helper/simpleperf/src/com/android/helpers/SimpleperfHelper.java b/libraries/collectors-helper/simpleperf/src/com/android/helpers/SimpleperfHelper.java
index 3d900a8..040e152 100644
--- a/libraries/collectors-helper/simpleperf/src/com/android/helpers/SimpleperfHelper.java
+++ b/libraries/collectors-helper/simpleperf/src/com/android/helpers/SimpleperfHelper.java
@@ -50,8 +50,8 @@
 
     private static final int SIMPLEPERF_START_WAIT_COUNT = 3;
     private static final int SIMPLEPERF_START_WAIT_TIME = 1000;
-    private static final int SIMPLEPERF_STOP_WAIT_COUNT = 12;
-    private static final long SIMPLEPERF_STOP_WAIT_TIME = 5000;
+    private static final int SIMPLEPERF_STOP_WAIT_COUNT = 60;
+    private static final long SIMPLEPERF_STOP_WAIT_TIME = 15000;
 
     private UiDevice mUiDevice;
 
diff --git a/libraries/collectors-helper/statsd/src/com/android/helpers/BatteryUsageStatsHelper.java b/libraries/collectors-helper/statsd/src/com/android/helpers/BatteryUsageStatsHelper.java
new file mode 100644
index 0000000..205e071
--- /dev/null
+++ b/libraries/collectors-helper/statsd/src/com/android/helpers/BatteryUsageStatsHelper.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2022 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.helpers;
+
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.os.nano.AtomsProto;
+import com.android.os.nano.AtomsProto.BatteryUsageStatsAtomsProto;
+import com.android.os.nano.AtomsProto.BatteryUsageStatsAtomsProto.BatteryConsumerData;
+import com.android.os.nano.AtomsProto.BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage;
+import com.android.os.nano.AtomsProto.BatteryUsageStatsAtomsProto.UidBatteryConsumer;
+import com.android.os.nano.StatsLog;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Consists of helper methods for collecting the BatteryUsageStatsSinceReset atom from stats and
+ * processing that data into metrics related to power consumption attributed to specific packages
+ * and components of the phone.
+ */
+public class BatteryUsageStatsHelper implements ICollectorHelper<Long> {
+
+    private static final String LOG_TAG = BatteryUsageStatsHelper.class.getSimpleName();
+
+    private StatsdHelper mStatsdHelper = new StatsdHelper();
+
+    @Override
+    public boolean startCollecting() {
+        Log.i(LOG_TAG, "Adding BatteryUsageStats config to statsd.");
+        List<Integer> atomIdList = new ArrayList<>();
+        atomIdList.add(AtomsProto.Atom.BATTERY_USAGE_STATS_SINCE_RESET_FIELD_NUMBER);
+        return mStatsdHelper.addGaugeConfig(atomIdList);
+    }
+
+    private Map<String, Long> batteryUsageStatsFromBucket(StatsLog.GaugeBucketInfo bucket) {
+        PackageManager packageManager =
+                InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
+
+        List<BatteryUsageStatsAtomsProto> atoms =
+                Arrays.stream(bucket.atom)
+                        .filter(a -> a.hasBatteryUsageStatsSinceReset())
+                        .map(a -> a.getBatteryUsageStatsSinceReset().batteryUsageStats)
+                        .collect(Collectors.toList());
+        if (atoms.size() != 1) {
+            throw new IllegalStateException(
+                    String.format(
+                            "Expected exactly 1 BatteryUsageStats atom, but has %d.",
+                            atoms.size()));
+        }
+        BatteryUsageStatsAtomsProto atom = atoms.get(0);
+
+        Map<String, Long> results = new HashMap<>();
+
+        // Collect the top-level consumer data.
+        BatteryConsumerData totalConsumerData = atom.deviceBatteryConsumer;
+        if (totalConsumerData != null) {
+            results.put(
+                    totalConsumptionMetricKey(), totalConsumerData.totalConsumedPowerDeciCoulombs);
+            if (totalConsumerData.powerComponents != null) {
+                for (PowerComponentUsage usage : totalConsumerData.powerComponents) {
+                    results.put(
+                            totalConsumptionByComponentMetricKey(usage.component),
+                            usage.powerDeciCoulombs);
+                    results.put(
+                            totalDurationByComponentMetricKey(usage.component),
+                            usage.durationMillis);
+                }
+            } else {
+                Log.w(LOG_TAG, "Device consumer data doesn't have specific component data.");
+            }
+        } else {
+            Log.w(LOG_TAG, "Atom doesn't have the expected device consumer data.");
+        }
+
+        // Collect the per-UID consumer data.
+        if (atom.uidBatteryConsumers != null) {
+            for (UidBatteryConsumer perUidConsumer : atom.uidBatteryConsumers) {
+                String[] packagesForUid = packageManager.getPackagesForUid(perUidConsumer.uid);
+                String packageNamesForMetrics =
+                        (packagesForUid == null || packagesForUid.length == 0)
+                                ? "unknown"
+                                : String.join("_", packagesForUid);
+                long timeInForeground = perUidConsumer.timeInForegroundMillis;
+                long timeInBackground = perUidConsumer.timeInBackgroundMillis;
+                results.put("time-in-fg-by-" + packageNamesForMetrics + "-ms", timeInForeground);
+                results.put("time-in-bg-by-" + packageNamesForMetrics + "-ms", timeInBackground);
+
+                BatteryConsumerData perUidData = perUidConsumer.batteryConsumerData;
+                if (perUidData != null && perUidData.powerComponents != null) {
+                    for (PowerComponentUsage componentPerUid : perUidData.powerComponents) {
+                        results.put(
+                                attributedConsumptionMetricKey(
+                                        packageNamesForMetrics, componentPerUid.component),
+                                componentPerUid.powerDeciCoulombs);
+                        results.put(
+                                attributedDurationMetricKey(
+                                        packageNamesForMetrics, componentPerUid.component),
+                                componentPerUid.durationMillis);
+                    }
+                } else {
+                    Log.w(
+                            LOG_TAG,
+                            String.format(
+                                    "Per-UID consumer data was missing for: %s",
+                                    packageNamesForMetrics));
+                }
+            }
+        } else {
+            Log.w(LOG_TAG, "Atom doesn't have the expected per-UID consumer data.");
+        }
+
+        results.put("session-start-ms", atom.sessionStartMillis);
+        results.put("session-end-ms", atom.sessionEndMillis);
+        results.put("session-duration-ms", atom.sessionDurationMillis);
+        results.put("session-discharge-pct", (long) atom.sessionDischargePercentage);
+
+        return results;
+    }
+
+    private String totalConsumptionMetricKey() {
+        return "power-consumed-total-dC";
+    }
+
+    private String attributedConsumptionMetricKey(String packages, int component) {
+        return String.format("power-consumed-by-%s-on-%s-dC", packages, componentName(component));
+    }
+
+    private String totalConsumptionByComponentMetricKey(int component) {
+        return String.format("power-consumed-total-on-%s-dC", componentName(component));
+    }
+
+    private String attributedDurationMetricKey(String packages, int component) {
+        return String.format("duration-by-%s-on-%s-ms", packages, componentName(component));
+    }
+
+    private String totalDurationByComponentMetricKey(int component) {
+        return String.format("duration-total-on-%s-ms", componentName(component));
+    }
+
+    private String componentName(int component) {
+        switch (component) {
+            case 0:
+                return "screen";
+            case 1:
+                return "cpu";
+            case 2:
+                return "bluetooth";
+            case 3:
+                return "camera";
+            case 4:
+                return "audio";
+            case 5:
+                return "video";
+            case 6:
+                return "flashlight";
+            case 7:
+                return "system_services";
+            case 8:
+                return "mobile_radio";
+            case 9:
+                return "sensors";
+            case 10:
+                return "gnss";
+            case 11:
+                return "wifi";
+            case 12:
+                return "wakelock";
+            case 13:
+                return "memory";
+            case 14:
+                return "phone";
+            case 15:
+                return "ambient_display";
+            case 16:
+                return "idle";
+            case 17:
+                return "reattributed_to_other_consumers";
+            default:
+                return "unknown_component_" + component;
+        }
+    }
+
+    @Override
+    public Map<String, Long> getMetrics() {
+        List<StatsLog.GaugeMetricData> gaugeMetricList = mStatsdHelper.getGaugeMetrics();
+        if (gaugeMetricList.size() != 1) {
+            throw new IllegalStateException(
+                    String.format(
+                            "Expected exactly 1 gauge metric data, but has %d.",
+                            gaugeMetricList.size()));
+        }
+
+        StatsLog.GaugeMetricData gaugeMetricData = gaugeMetricList.get(0);
+        if (gaugeMetricData.bucketInfo.length != 2) {
+            throw new IllegalStateException(
+                    String.format(
+                            "Expected exactly 2 buckets in data, but has %d.",
+                            gaugeMetricData.bucketInfo.length));
+        }
+        Map<String, Long> beforeData = batteryUsageStatsFromBucket(gaugeMetricData.bucketInfo[0]);
+        Map<String, Long> afterData = batteryUsageStatsFromBucket(gaugeMetricData.bucketInfo[1]);
+
+        printEntries(0, beforeData);
+        printEntries(1, afterData);
+
+        Map<String, Long> results = new HashMap<>();
+        for (String sharedKey : beforeData.keySet()) {
+            if (!afterData.containsKey(sharedKey)) {
+                continue;
+            }
+
+            results.put(sharedKey, afterData.get(sharedKey) - beforeData.get(sharedKey));
+        }
+        return results;
+    }
+
+    @Override
+    public boolean stopCollecting() {
+        return mStatsdHelper.removeStatsConfig();
+    }
+
+    private void printEntries(int bucket, Map<String, Long> data) {
+        for (Map.Entry<String, Long> datum : data.entrySet()) {
+            Log.e(
+                    LOG_TAG,
+                    String.format(
+                            "Bucket: %d\t|\t%s = %s", bucket, datum.getKey(), datum.getValue()));
+        }
+    }
+}
diff --git a/libraries/collectors-helper/statsd/test/src/com/android/helpers/BatteryUsageStatsHelperTest.java b/libraries/collectors-helper/statsd/test/src/com/android/helpers/BatteryUsageStatsHelperTest.java
new file mode 100644
index 0000000..46916c4
--- /dev/null
+++ b/libraries/collectors-helper/statsd/test/src/com/android/helpers/BatteryUsageStatsHelperTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2022 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.helpers;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Android Unit tests for {@link BatteryUsageStatsHelperTest}.
+ *
+ * <p>To run: Disable SELinux: adb shell setenforce 0; if this fails with "permission denied", try
+ * "adb shell su 0 setenforce 0" atest
+ * CollectorsHelperTest:com.android.helpers.BatteryUsageStatsHelperTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class BatteryUsageStatsHelperTest {
+
+    private BatteryUsageStatsHelper mBatteryUsageStatsHelper;
+
+    @Before
+    public void setUp() {
+        mBatteryUsageStatsHelper = new BatteryUsageStatsHelper();
+    }
+
+    /** Test config registration and unregistration works. */
+    @Test
+    public void testBatteryUsageStatsConfig() throws Exception {
+        assertTrue(mBatteryUsageStatsHelper.startCollecting());
+        assertTrue(mBatteryUsageStatsHelper.stopCollecting());
+    }
+
+    /** Test battery usage stats metrics are collected. */
+    @Test
+    public void testBatteryUsageStatsMetrics() throws Exception {
+        assertTrue(mBatteryUsageStatsHelper.startCollecting());
+        Map<String, Long> batteryUsageStats = mBatteryUsageStatsHelper.getMetrics();
+
+        boolean hasTotalConsumed = false;
+        boolean hasTotalConsumedByComponent = false;
+        boolean hasTotalDurationByComponent = false;
+        boolean hasPerUidConsumed = false;
+        boolean hasPerUidDuration = false;
+        boolean hasTimeInFg = false;
+        boolean hasTimeInBg = false;
+        for (Map.Entry<String, Long> entry : batteryUsageStats.entrySet()) {
+            hasTotalConsumed = hasTotalConsumed || entry.getKey().equals("power-consumed-total-dC");
+            hasTotalConsumedByComponent =
+                    hasTotalConsumedByComponent
+                            || Pattern.compile("power-consumed-total-on.*")
+                                    .matcher(entry.getKey())
+                                    .matches();
+            hasTotalDurationByComponent =
+                    hasTotalDurationByComponent
+                            || Pattern.compile("duration-total-on.*")
+                                    .matcher(entry.getKey())
+                                    .matches();
+            hasPerUidConsumed =
+                    hasPerUidConsumed
+                            || Pattern.compile("power-consumed-by.*")
+                                    .matcher(entry.getKey())
+                                    .matches();
+            hasPerUidDuration =
+                    hasPerUidDuration
+                            || Pattern.compile("duration-by.*").matcher(entry.getKey()).matches();
+            hasTimeInFg =
+                    hasTimeInFg
+                            || Pattern.compile("time-in-fg.*").matcher(entry.getKey()).matches();
+            hasTimeInBg =
+                    hasTimeInBg
+                            || Pattern.compile("time-in-bg.*").matcher(entry.getKey()).matches();
+        }
+
+        assertTrue(hasTotalConsumed);
+        assertTrue(hasTotalConsumedByComponent);
+        assertTrue(hasTotalDurationByComponent);
+        assertTrue(hasPerUidConsumed);
+        assertTrue(hasPerUidDuration);
+        assertTrue(hasTimeInFg);
+        assertTrue(hasTimeInBg);
+
+        assertTrue(batteryUsageStats.containsKey("session-start-ms"));
+        assertTrue(batteryUsageStats.containsKey("session-end-ms"));
+        assertTrue(batteryUsageStats.containsKey("session-discharge-pct"));
+
+        assertTrue(mBatteryUsageStatsHelper.stopCollecting());
+    }
+
+    /** Test total consumption is the sum of per-package consumption. */
+    @Test
+    public void testTotalConsumptionIsSumOfPerPackageConsumption() {
+        assertTrue(mBatteryUsageStatsHelper.startCollecting());
+        Map<String, Long> batteryUsageStats = mBatteryUsageStatsHelper.getMetrics();
+
+        long summedConsumption = 0;
+        long reportedConsumption = 0;
+
+        String component = "cpu";
+        for (Map.Entry<String, Long> entry : batteryUsageStats.entrySet()) {
+            Pattern total = Pattern.compile(String.format("power-consumed-total.*%s.*", component));
+            Pattern partial = Pattern.compile(String.format("power.*by.*-on-%s.*", component));
+            if (total.matcher(entry.getKey()).matches()) {
+                reportedConsumption = entry.getValue();
+            } else if (partial.matcher(entry.getKey()).matches()) {
+                summedConsumption += entry.getValue();
+            }
+        }
+
+        assertEquals(
+                "Reported consumption and summed consumption don't match.",
+                reportedConsumption,
+                summedConsumption);
+    }
+
+    /** Test total duration is the sum of per-package durations. */
+    @Test
+    public void testTotalDurationIsSumOfPerPackageDurations() {
+        assertTrue(mBatteryUsageStatsHelper.startCollecting());
+        Map<String, Long> batteryUsageStats = mBatteryUsageStatsHelper.getMetrics();
+
+        long summedDuration = 0;
+        long reportedDuration = 0;
+
+        String component = "cpu";
+        for (Map.Entry<String, Long> entry : batteryUsageStats.entrySet()) {
+            Pattern total = Pattern.compile(String.format("duration-total.*%s.*", component));
+            Pattern partial = Pattern.compile(String.format("duration.*by.*-on-%s.*", component));
+            if (total.matcher(entry.getKey()).matches()) {
+                reportedDuration = entry.getValue();
+            } else if (partial.matcher(entry.getKey()).matches()) {
+                summedDuration += entry.getValue();
+            }
+        }
+
+        assertEquals(
+                "Reported duration and summed duration don't match",
+                reportedDuration,
+                summedDuration);
+    }
+
+    @After
+    public void tearDown() {
+        mBatteryUsageStatsHelper.stopCollecting();
+    }
+}
diff --git a/libraries/collectors-helper/statsd/test/src/com/android/helpers/UiActionLatencyHelperTest.java b/libraries/collectors-helper/statsd/test/src/com/android/helpers/UiActionLatencyHelperTest.java
index 04f43c7..c133457 100644
--- a/libraries/collectors-helper/statsd/test/src/com/android/helpers/UiActionLatencyHelperTest.java
+++ b/libraries/collectors-helper/statsd/test/src/com/android/helpers/UiActionLatencyHelperTest.java
@@ -82,7 +82,7 @@
         assertTrue(mActionLatencyHelper.startCollecting());
         Log.d(LOG_TAG, "testQuickSwitchMetric: started collecting");
 
-        sLauncher.getBackground().quickSwitchToPreviousApp();
+        sLauncher.getLaunchedAppState().quickSwitchToPreviousApp();
 
         // Checking metrics produced by the CUJ.
         final Map<String, StringBuilder> latencyMetrics = mActionLatencyHelper.getMetrics();
@@ -103,7 +103,7 @@
     }
 
     private void startApp(LauncherInstrumentation sLauncher, String appName, String appPackage) {
-        final AllApps allApps = sLauncher.pressHome().switchToAllApps();
+        final AllApps allApps = sLauncher.goHome().switchToAllApps();
         allApps.freeze();
         try {
             allApps.getAppIcon(appName).launch(appPackage);
diff --git a/libraries/collectors-helper/statsd/test/src/com/android/helpers/UiInteractionFrameInfoHelperTest.java b/libraries/collectors-helper/statsd/test/src/com/android/helpers/UiInteractionFrameInfoHelperTest.java
index e6b0f64..47ee840 100644
--- a/libraries/collectors-helper/statsd/test/src/com/android/helpers/UiInteractionFrameInfoHelperTest.java
+++ b/libraries/collectors-helper/statsd/test/src/com/android/helpers/UiInteractionFrameInfoHelperTest.java
@@ -79,22 +79,20 @@
         // The CUJ
         notificationHelper.get().open();
         UiObject2 notification = notificationHelper.get().postBigTextNotification(null /* pkg */);
-        notificationHelper.get().showGuts(notification);
-        notificationHelper.get().hideGuts(notification);
         notificationHelper.get().cancelNotifications();
         notificationHelper.get().exit();
 
         // Checking metrics produced by the CUJ.
         final Map<String, StringBuilder> frameMetrics = mInteractionFrameHelper.getMetrics();
         assertTrue(
-                "No metric missed_frames_cuj_SHADE_SCROLL_FLING missing",
-                frameMetrics.containsKey("cuj_SHADE_SCROLL_FLING_missed_frames"));
+                "No metric cuj_NOTIFICATION_ADD_missed_frames",
+                frameMetrics.containsKey("cuj_NOTIFICATION_ADD_missed_frames"));
         assertTrue(
-                "No metric total_frames_cuj_SHADE_SCROLL_FLING",
-                frameMetrics.containsKey("cuj_SHADE_SCROLL_FLING_total_frames"));
+                "No metric cuj_NOTIFICATION_ADD_total_frames",
+                frameMetrics.containsKey("cuj_NOTIFICATION_ADD_total_frames"));
         assertTrue(
-                "No metric max_frame_time_nanos_cuj_SHADE_SCROLL_FLING",
-                frameMetrics.containsKey("cuj_SHADE_SCROLL_FLING_max_frame_time_ms"));
+                "No metric cuj_NOTIFICATION_ADD_max_frame_time_ms",
+                frameMetrics.containsKey("cuj_NOTIFICATION_ADD_max_frame_time_ms"));
 
         assertTrue(mInteractionFrameHelper.stopCollecting());
         HelperTestUtility.sendKeyCode(KEYCODE_HOME);
diff --git a/libraries/collectors-helper/tests/AndroidManifest.xml b/libraries/collectors-helper/tests/AndroidManifest.xml
index bfcba7b..2ece7f9 100644
--- a/libraries/collectors-helper/tests/AndroidManifest.xml
+++ b/libraries/collectors-helper/tests/AndroidManifest.xml
@@ -15,7 +15,7 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.helpers.tests" >
-    <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="24" />
+    <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="30" />
     <uses-permission android:name="android.permission.DUMP" />
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
diff --git a/libraries/collectors-helper/utilities/src/com/android/helpers/ICollectorHelper.java b/libraries/collectors-helper/utilities/src/com/android/helpers/ICollectorHelper.java
index ea0d8ef..d737cf6 100644
--- a/libraries/collectors-helper/utilities/src/com/android/helpers/ICollectorHelper.java
+++ b/libraries/collectors-helper/utilities/src/com/android/helpers/ICollectorHelper.java
@@ -11,6 +11,14 @@
     boolean startCollecting();
 
     /**
+     * This method will take args which passes an identifier for the helper.
+     * The default implementation is to invoke {@link #startCollecting()} directly.
+     */
+    default boolean startCollecting(String id) {
+        return startCollecting();
+    }
+
+    /**
      * This method will have setup to start collecting the metrics. The default implementation is to
      * invoke {@link #startCollecting()} directly. To apply the filters, must overload this method
      * and use the filters.
diff --git a/libraries/compatibility-common-util/src/com/android/compatibility/common/util/Stat.java b/libraries/compatibility-common-util/src/com/android/compatibility/common/util/Stat.java
index 9748440..0314c32 100644
--- a/libraries/compatibility-common-util/src/com/android/compatibility/common/util/Stat.java
+++ b/libraries/compatibility-common-util/src/com/android/compatibility/common/util/Stat.java
@@ -35,12 +35,21 @@
         public double mMin;
         public double mMax;
         public double mStddev;
+        public double mMedian;
         public int mDataCount;
-        public StatResult(double average, double min, double max, double stddev, int dataCount) {
+
+        public StatResult(
+                double average,
+                double min,
+                double max,
+                double stddev,
+                double median,
+                int dataCount) {
             mAverage = average;
             mMin = min;
             mMax = max;
             mStddev = stddev;
+            mMedian = median;
             mDataCount = dataCount;
         }
     }
@@ -69,7 +78,8 @@
         }
         double variance = sumOfSquares / (data.length - 1);
         double stddev = Math.sqrt(variance);
-        return new StatResult(average, min, max, stddev, data.length);
+        double median = getMedian(data);
+        return new StatResult(average, min, max, stddev, median, data.length);
     }
 
     /**
@@ -78,15 +88,7 @@
      * rejectionThreshold should be bigger than 0.0 and be lowerthan 1.0
      */
     public static StatResult getStatWithOutlierRejection(double[] data, double rejectionThreshold) {
-        double[] dataCopied = Arrays.copyOf(data, data.length);
-        Arrays.sort(dataCopied);
-        int medianIndex = dataCopied.length / 2;
-        double median;
-        if (dataCopied.length % 2 == 1) {
-            median = dataCopied[medianIndex];
-        } else {
-            median = (dataCopied[medianIndex - 1] + dataCopied[medianIndex]) / 2.0;
-        }
+        double median = getMedian(data);
         double thresholdMin = median * (1.0 - rejectionThreshold);
         double thresholdMax = median * (1.0 + rejectionThreshold);
 
@@ -102,6 +104,20 @@
         return getStat(Arrays.copyOf(validData, index));
     }
 
+    /** returns the median value of the passed array */
+    private static double getMedian(double[] data) {
+        double[] dataCopied = Arrays.copyOf(data, data.length);
+        Arrays.sort(dataCopied);
+        int medianIndex = dataCopied.length / 2;
+        double median;
+        if (dataCopied.length % 2 == 1) {
+            median = dataCopied[medianIndex];
+        } else {
+            median = (dataCopied[medianIndex - 1] + dataCopied[medianIndex]) / 2.0;
+        }
+        return median;
+    }
+
     /**
      * return the average value of the passed array
      */
diff --git a/libraries/compatibility-common-util/src/com/android/compatibility/common/util/VersionCodes.java b/libraries/compatibility-common-util/src/com/android/compatibility/common/util/VersionCodes.java
index cdccb79..4942860 100644
--- a/libraries/compatibility-common-util/src/com/android/compatibility/common/util/VersionCodes.java
+++ b/libraries/compatibility-common-util/src/com/android/compatibility/common/util/VersionCodes.java
@@ -50,4 +50,5 @@
     public static final int R = 30;
     public static final int S = 31;
     public static final int S_V2 = 32;
+    public static final int TIRAMISU = 33;
 }
diff --git a/libraries/compatibility-common-util/src/com/android/compatibility/common/util/ZipUtil.java b/libraries/compatibility-common-util/src/com/android/compatibility/common/util/ZipUtil.java
index b44fd46..05b1529 100644
--- a/libraries/compatibility-common-util/src/com/android/compatibility/common/util/ZipUtil.java
+++ b/libraries/compatibility-common-util/src/com/android/compatibility/common/util/ZipUtil.java
@@ -24,7 +24,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.LinkedList;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
@@ -40,11 +41,24 @@
      * @throws IOException if failed to create zip file
      */
     public static void createZip(File dir, File zipFile) throws IOException {
+        createZip(Collections.singletonList(dir), zipFile);
+    }
+
+    /**
+     * Utility method to create a zip file containing the given directory and all its contents.
+     *
+     * @param files list of files or directories to zip
+     * @param zipFile the zip file to create - it should not already exist
+     * @throws IOException if failed to create zip file
+     */
+    public static void createZip(List<File> files, File zipFile) throws IOException {
         ZipOutputStream out = null;
         try {
             FileOutputStream fileStream = new FileOutputStream(zipFile);
             out = new ZipOutputStream(new BufferedOutputStream(fileStream));
-            addToZip(out, dir, new LinkedList<String>());
+            for (File file : files) {
+                addToZip(out, file, new ArrayList<>());
+            }
         } catch (IOException e) {
             zipFile.delete();
             throw e;
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/BaseCollectionListener.java b/libraries/device-collectors/src/main/java/android/device/collectors/BaseCollectionListener.java
index 4d4e1b6..fe6a093 100644
--- a/libraries/device-collectors/src/main/java/android/device/collectors/BaseCollectionListener.java
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/BaseCollectionListener.java
@@ -63,14 +63,20 @@
 
     @Override
     public void onTestRunStart(DataRecord runData, Description description) {
+
+        if (mIsCollectPerRun) {
+            Function<String, Boolean> filter = getFilter(description);
+            testStart(filter, description);
+        }
+    }
+
+    @Override
+    protected void parseArguments() {
+        super.parseArguments();
         Bundle args = getArgsBundle();
         mIsCollectPerRun = "true".equals(args.getString(COLLECT_PER_RUN));
         // By default this flag is set to false to collect the metrics on test failure.
         mSkipTestFailureMetrics = "true".equals(args.getString(SKIP_TEST_FAILURE_METRICS));
-
-        if (mIsCollectPerRun) {
-            mHelper.startCollecting();
-        }
     }
 
     protected Function<String, Boolean> getFilter(Description description) {
@@ -82,11 +88,7 @@
         mIsTestFailed = false;
         if (!mIsCollectPerRun) {
             Function<String, Boolean> filter = getFilter(description);
-            if (filter == null) {
-                mHelper.startCollecting();
-            } else {
-                mHelper.startCollecting(filter);
-            }
+            testStart(filter, description);
         }
     }
 
@@ -96,7 +98,7 @@
     }
 
     @Override
-    public final void onTestEnd(DataRecord testData, Description description) {
+    public void onTestEnd(DataRecord testData, Description description) {
         if (!mIsCollectPerRun) {
             // Skip adding the metrics collected during the test failure
             // if the skip metrics on test failure flag is enabled and the
@@ -119,6 +121,14 @@
         }
     }
 
+    public void testStart(Function<String, Boolean> filter, Description description) {
+        if (filter == null) {
+            mHelper.startCollecting();
+        } else {
+            mHelper.startCollecting(filter);
+        }
+    }
+
     protected void createHelperInstance(ICollectorHelper helper) {
         mHelper = helper;
     }
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/BaseMetricListener.java b/libraries/device-collectors/src/main/java/android/device/collectors/BaseMetricListener.java
index 9d4a132..0e6cae2 100644
--- a/libraries/device-collectors/src/main/java/android/device/collectors/BaseMetricListener.java
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/BaseMetricListener.java
@@ -411,7 +411,7 @@
         return mArgsBundle;
     }
 
-    private void parseArguments() {
+    protected void parseArguments() {
         Bundle args = getArgsBundle();
         // First filter the arguments with the alias
         filterAlias(args);
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/HeapDumpListener.java b/libraries/device-collectors/src/main/java/android/device/collectors/HeapDumpListener.java
new file mode 100644
index 0000000..698bdd0
--- /dev/null
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/HeapDumpListener.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.device.collectors;
+
+import android.device.collectors.annotations.OptionClass;
+import android.os.Bundle;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.helpers.HeapDumpHelper;
+
+import org.junit.runner.Description;
+
+import java.util.Arrays;
+import java.util.function.Function;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * A {@link BaseCollectionListener} that captures and logs the heapdump.
+ */
+@OptionClass(alias = "heapdump-listener")
+public class HeapDumpListener extends BaseCollectionListener<String> {
+
+    private static final String SPACES_PATTERN = "\\s+";
+    private static final String REPLACEMENT_CHAR = "#";
+    private static final String FILE_ID_FORMAT = "%s_%d";
+    private static final String FILE_NAME_PREFIX_FORMAT = "%s_%s";
+    @VisibleForTesting static final String DEFAULT_OUTPUT_DIR = "/data/local/tmp/";
+    @VisibleForTesting static final String OUTPUT_DIR_KEY = "heapdump-test-output-dir";
+    @VisibleForTesting static final String ITERATION_SEPARATOR = ",";
+    @VisibleForTesting static final String ENABLE_ITERATION_IDS = "enable-iteration-ids";
+    @VisibleForTesting static final String ITERATION_ALL_ENABLE = "iteration-all-enable";
+    @VisibleForTesting static final String PROCESS_NAMES_KEY = "heapdump-process-names";
+    @VisibleForTesting static final String PROCESS_SEPARATOR = ",";
+    Map<String, Integer> mTestIterationCount = new HashMap<String, Integer>();
+    Set<Integer> mValidIterationIds;
+    boolean mIsDisabled = false;
+    boolean mIsEnabledForAll = false;
+    private HeapDumpHelper mHeapHelper = new HeapDumpHelper();
+
+    public HeapDumpListener() {
+        createHelperInstance(mHeapHelper);
+    }
+
+    @VisibleForTesting
+    public HeapDumpListener(Bundle args, HeapDumpHelper helper) {
+        super(args, helper);
+        mHeapHelper = helper;
+    }
+
+    /** Process the test arguments */
+    @Override
+    public void setupAdditionalArgs() {
+        Bundle args = getArgsBundle();
+
+        mIsEnabledForAll =
+                Boolean.parseBoolean(args.getString(ITERATION_ALL_ENABLE, String.valueOf(false)));
+
+        String testOutputDir = args.getString(OUTPUT_DIR_KEY, DEFAULT_OUTPUT_DIR);
+
+        // Collect for all processes if process list is empty or null.
+        String procsString = args.getString(PROCESS_NAMES_KEY);
+
+        String[] procs = null;
+        if (procsString != null && !procsString.isEmpty()) {
+          procs = procsString.split(PROCESS_SEPARATOR);
+        }
+
+        mHeapHelper.setUp(testOutputDir, procs);
+
+        if (mIsCollectPerRun) {
+            return;
+        }
+
+        if (!mIsEnabledForAll) {
+            String iterations = args.getString(ENABLE_ITERATION_IDS);
+            if (iterations == null || iterations.isEmpty()) {
+                mIsDisabled = true;
+                return;
+            }
+            String[] iterationArray = iterations.split(ITERATION_SEPARATOR);
+            Set<String> validIterationIdsStr = new HashSet<String>(Arrays.asList(iterationArray));
+            mValidIterationIds = validIterationIdsStr.stream().map(s -> Integer.parseInt(s))
+                    .collect(Collectors.toSet());
+        }
+    }
+
+    @Override
+    public void testStart(Function<String, Boolean> filter, Description description) {
+        updateIterationCount(description);
+        if (mIsEnabledForAll) {
+            mHeapHelper.startCollecting(getHeapDumpFileId(description));
+        } else if (mIsCollectPerRun || mValidIterationIds
+                .contains(mTestIterationCount.get(getTestFileName(description)))) {
+            mHeapHelper.startCollecting(getHeapDumpFileId(description));
+        }
+    }
+
+    @Override
+    public final void onTestEnd(DataRecord testData, Description description) {
+        if (!mIsCollectPerRun) {
+            if (mIsEnabledForAll) {
+                super.onTestEnd(testData, description);
+            } else if (mValidIterationIds
+                    .contains(mTestIterationCount.get(getTestFileName(description)))) {
+                super.onTestEnd(testData, description);
+            }
+        }
+    }
+
+    /**
+     * Update the map that tracks the number of times each test method is called.
+     */
+    private void updateIterationCount(Description description) {
+        String testId = getTestFileName(description);
+        if (mTestIterationCount.containsKey(testId)) {
+            mTestIterationCount.put(testId,
+                    mTestIterationCount.get(getTestFileName(description)) + 1);
+        } else {
+            mTestIterationCount.put(testId, 1);
+        }
+    }
+
+    /**
+     * Returns the unique identifier using the test name and the current iteration number
+     * used to create the heap dump file.
+     */
+    private String getHeapDumpFileId(Description description) {
+        return String.format(FILE_ID_FORMAT, getTestFileName(description),
+                mTestIterationCount.get(getTestFileName(description)));
+    }
+
+    /**
+     * Returns the packagename.classname_methodname which has no spaces and used to create file
+     * names. If class name or method name is null then return the heapdump.
+     */
+    public static String getTestFileName(Description description) {
+        if (description.getClassName() != null && !description.getClassName().isEmpty()
+                && description.getMethodName() != null
+                && !description.getMethodName().isEmpty()) {
+            String[] className = description.getClassName().split("\\$");
+            return String.format(FILE_NAME_PREFIX_FORMAT,
+                    className[0].replaceAll(SPACES_PATTERN, REPLACEMENT_CHAR).trim(),
+                    description.getMethodName().replaceAll(SPACES_PATTERN, REPLACEMENT_CHAR)
+                            .trim());
+        } else {
+            return "heapdump";
+        }
+    }
+}
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/LyricMemProfilerCollector.java b/libraries/device-collectors/src/main/java/android/device/collectors/LyricMemProfilerCollector.java
index 3205fa2..b0cb6d5 100644
--- a/libraries/device-collectors/src/main/java/android/device/collectors/LyricMemProfilerCollector.java
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/LyricMemProfilerCollector.java
@@ -26,11 +26,21 @@
 
     private static final String TAG = LyricMemProfilerCollector.class.getSimpleName();
     private static final String PROFILE_PERIOD_KEY = "profile-period-ms-enable";
-    private static final String PROFILE_PID_NAME = "profile-pid-name";
-    private LyricMemProfilerHelper mHelper = new LyricMemProfilerHelper();
+
+    // separate PID name by ',' character
+    private static final String PROFILE_PID_NAME_LIST = "profile-camera-pid-name-list";
+    private static final String PROFILE_METRIC_NAME_LIST = "profile-camera-metric-name-list";
+
+    private static final String[] DEFAULT_PID_NAME_LIST = {
+        "provider@", "cameraserver", "id.GoogleCamera"
+    };
+    private static final String[] DEFAULT_METRIC_NAME_LIST = {
+        "CameraProvider", "CameraServer", "CameraApp"
+    };
+    private LyricMemProfilerHelper mLyricMemProfilerHelper = new LyricMemProfilerHelper();
 
     public LyricMemProfilerCollector() {
-        createHelperInstance(mHelper);
+        createHelperInstance(mLyricMemProfilerHelper);
     }
 
     /** Adds the options for total pss collector. */
@@ -38,12 +48,20 @@
     public void setupAdditionalArgs() {
         Bundle args = getArgsBundle();
         String argString = args.getString(PROFILE_PERIOD_KEY);
+        String[] pidNameList = DEFAULT_PID_NAME_LIST;
+        String[] metricNameList = DEFAULT_METRIC_NAME_LIST;
         if (argString != null) {
-            mHelper.setProfilePeriodMs(Integer.parseInt(argString));
+            mLyricMemProfilerHelper.setProfilePeriodMs(Integer.parseInt(argString));
         }
-        argString = args.getString(PROFILE_PID_NAME);
+        argString = args.getString(PROFILE_PID_NAME_LIST);
         if (argString != null) {
-            mHelper.setProfilePidName(argString);
+            pidNameList = argString.split(",");
         }
+        argString = args.getString(PROFILE_METRIC_NAME_LIST);
+        if (argString != null) {
+            metricNameList = argString.split(",");
+        }
+        mLyricMemProfilerHelper.setProfileCameraProcName(pidNameList);
+        mLyricMemProfilerHelper.setProfileMetricName(metricNameList);
     }
 }
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/ShowmapSnapshotListener.java b/libraries/device-collectors/src/main/java/android/device/collectors/ShowmapSnapshotListener.java
index 371ef6e..2086328 100644
--- a/libraries/device-collectors/src/main/java/android/device/collectors/ShowmapSnapshotListener.java
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/ShowmapSnapshotListener.java
@@ -33,7 +33,8 @@
  * -e drop-cache [pagecache | slab | all] : drop cache flag
  * -e test-output-dir [path] : path to the output directory
  * -e metric-index [rss:2,pss:3,privatedirty:7] : memory metric name corresponding
- *  to index in the showmap output.
+ * -e gc-precollect [true | false] : whether it needs to run a GC prior to collecting memory
+ * metrics. to index in the showmap output.
  */
 @OptionClass(alias = "showmapsnapshot-collector")
 public class ShowmapSnapshotListener extends BaseCollectionListener<String> {
@@ -45,6 +46,7 @@
   @VisibleForTesting static final String METRIC_NAME_INDEX = "metric-name-index";
   @VisibleForTesting static final String DROP_CACHE_KEY = "drop-cache";
   @VisibleForTesting static final String OUTPUT_DIR_KEY = "test-output-dir";
+  @VisibleForTesting static final String GC_PRECOLLECT_KEY = "gc-precollect";
 
   private ShowmapSnapshotHelper mShowmapSnapshotHelper = new ShowmapSnapshotHelper();
   private final Map<String, Integer> dropCacheValues = new HashMap<String, Integer>() {
@@ -105,7 +107,6 @@
       procs = procsString.split(PROCESS_SEPARATOR);
     }
 
-
     mShowmapSnapshotHelper.setUp(testOutputDir, procs);
 
     String dropCacheValue = args.getString(DROP_CACHE_KEY);
@@ -117,5 +118,8 @@
         return;
       }
     }
+
+    boolean runGcPrecollect = "true".equals(args.getString(GC_PRECOLLECT_KEY, "false"));
+    mShowmapSnapshotHelper.setGcOnPrecollectOption(runGcPrecollect);
   }
 }
diff --git a/libraries/device-collectors/src/main/platform-collectors/src/android/device/collectors/BatteryUsageStatsListener.java b/libraries/device-collectors/src/main/platform-collectors/src/android/device/collectors/BatteryUsageStatsListener.java
new file mode 100644
index 0000000..eb51544
--- /dev/null
+++ b/libraries/device-collectors/src/main/platform-collectors/src/android/device/collectors/BatteryUsageStatsListener.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.device.collectors;
+
+import android.device.collectors.annotations.OptionClass;
+
+import com.android.helpers.BatteryUsageStatsHelper;
+
+/** Captures total and per-UID battery usage stats metrics during test methods. */
+@OptionClass(alias = "battery-usage-stats-collector")
+public class BatteryUsageStatsListener extends BaseCollectionListener<Long> {
+
+    public BatteryUsageStatsListener() {
+        createHelperInstance(new BatteryUsageStatsHelper());
+    }
+}
diff --git a/libraries/device-collectors/src/test/java/android/device/collectors/BaseCollectionListenerTest.java b/libraries/device-collectors/src/test/java/android/device/collectors/BaseCollectionListenerTest.java
index af6d75b..008fa13 100644
--- a/libraries/device-collectors/src/test/java/android/device/collectors/BaseCollectionListenerTest.java
+++ b/libraries/device-collectors/src/test/java/android/device/collectors/BaseCollectionListenerTest.java
@@ -77,7 +77,7 @@
         b.putString(BaseCollectionListener.COLLECT_PER_RUN, "true");
         mListener = initListener(b);
 
-        mListener.onTestRunStart(mListener.createDataRecord(), FAKE_DESCRIPTION);
+        mListener.testRunStarted(FAKE_DESCRIPTION);
         verify(helper, times(1)).startCollecting();
         mListener.onTestStart(mListener.createDataRecord(), FAKE_TEST_DESCRIPTION);
         verify(helper, times(1)).startCollecting();
@@ -97,7 +97,7 @@
         b.putString(BaseCollectionListener.COLLECT_PER_RUN, "false");
         mListener = initListener(b);
 
-        mListener.onTestRunStart(mListener.createDataRecord(), FAKE_DESCRIPTION);
+        mListener.testRunStarted(FAKE_DESCRIPTION);
 
         verify(helper, times(0)).startCollecting();
         mListener.onTestStart(mListener.createDataRecord(), FAKE_TEST_DESCRIPTION);
@@ -123,7 +123,7 @@
         Bundle b = new Bundle();
         mListener = initListener(b);
 
-        mListener.onTestRunStart(mListener.createDataRecord(), FAKE_DESCRIPTION);
+        mListener.testRunStarted(FAKE_DESCRIPTION);
         verify(helper, times(0)).startCollecting();
         mListener.onTestStart(mListener.createDataRecord(), FAKE_TEST_DESCRIPTION);
         verify(helper, times(1)).startCollecting();
@@ -148,7 +148,7 @@
         b.putString(BaseCollectionListener.SKIP_TEST_FAILURE_METRICS, "false");
         mListener = initListener(b);
 
-        mListener.onTestRunStart(mListener.createDataRecord(), FAKE_DESCRIPTION);
+        mListener.testRunStarted(FAKE_DESCRIPTION);
         verify(helper, times(0)).startCollecting();
         mListener.testStarted(FAKE_TEST_DESCRIPTION);
         verify(helper, times(1)).startCollecting();
@@ -169,7 +169,7 @@
         b.putString(BaseCollectionListener.COLLECT_PER_RUN, "false");
         mListener = initListener(b);
 
-        mListener.onTestRunStart(mListener.createDataRecord(), FAKE_DESCRIPTION);
+        mListener.testRunStarted(FAKE_DESCRIPTION);
         verify(helper, times(0)).startCollecting();
         mListener.testStarted(FAKE_TEST_DESCRIPTION);
         verify(helper, times(1)).startCollecting();
@@ -193,7 +193,7 @@
         b.putString(BaseCollectionListener.SKIP_TEST_FAILURE_METRICS, "true");
         mListener = initListener(b);
 
-        mListener.onTestRunStart(mListener.createDataRecord(), FAKE_DESCRIPTION);
+        mListener.testRunStarted(FAKE_DESCRIPTION);
         verify(helper, times(0)).startCollecting();
         mListener.testStarted(FAKE_TEST_DESCRIPTION);
         verify(helper, times(1)).startCollecting();
@@ -217,7 +217,7 @@
         b.putString(BaseCollectionListener.SKIP_TEST_FAILURE_METRICS, "true");
         mListener = initListener(b);
 
-        mListener.onTestRunStart(mListener.createDataRecord(), FAKE_DESCRIPTION);
+        mListener.testRunStarted(FAKE_DESCRIPTION);
         verify(helper, times(0)).startCollecting();
         mListener.testStarted(FAKE_TEST_DESCRIPTION);
         verify(helper, times(1)).startCollecting();
diff --git a/libraries/device-collectors/src/test/java/android/device/collectors/HeapDumpListenerTest.java b/libraries/device-collectors/src/test/java/android/device/collectors/HeapDumpListenerTest.java
new file mode 100644
index 0000000..f4dda56
--- /dev/null
+++ b/libraries/device-collectors/src/test/java/android/device/collectors/HeapDumpListenerTest.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.device.collectors;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.Instrumentation;
+import android.os.Bundle;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.helpers.HeapDumpHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Unit tests for {@link HeapDumpListener} specific behavior. */
+@RunWith(AndroidJUnit4.class)
+public final class HeapDumpListenerTest {
+
+    // A {@code Description} to pass when faking a test run start call.
+    private static final Description RUN_DESCRIPTION = Description.createSuiteDescription("run");
+    private static final Description TEST_DESCRIPTION_1 =
+            Description.createTestDescription("run", "test_one");
+    private static final Description TEST_DESCRIPTION_2 =
+            Description.createTestDescription("run", "test_two");
+
+    @Mock private HeapDumpHelper mHelper;
+    @Mock private Instrumentation mInstrumentation;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    /** Test heapdump collection enabled for all iterations*/
+    @Test
+    public void testHeapCollectionEnableAllIterations() throws Exception {
+        Bundle enableAllBundle = new Bundle();
+        enableAllBundle.putString(HeapDumpListener.ITERATION_ALL_ENABLE, String.valueOf(true));
+        HeapDumpListener collector = new HeapDumpListener(enableAllBundle, mHelper);
+        collector.setInstrumentation(mInstrumentation);
+
+        collector.testRunStarted(RUN_DESCRIPTION);
+        collector.testStarted(TEST_DESCRIPTION_1);
+        verify(mHelper, times(1)).startCollecting("run_test_one_1");
+    }
+
+    /** Test heapdump collection force disable for all iterations.*/
+    @Test
+    public void testHeapCollectionEnableAllFlagDisabled() throws Exception {
+        Bundle disableAllBundle = new Bundle();
+        disableAllBundle.putString(HeapDumpListener.ITERATION_ALL_ENABLE, String.valueOf(false));
+        HeapDumpListener collector = new HeapDumpListener(disableAllBundle, mHelper);
+        collector.setInstrumentation(mInstrumentation);
+
+        collector.testRunStarted(RUN_DESCRIPTION);
+        collector.testStarted(TEST_DESCRIPTION_1);
+        verify(mHelper, times(0)).startCollecting("run_test_one_1");
+    }
+
+    /** Test heapdump collection disabled for all iterations by default.*/
+    @Test
+    public void testHeapCollectionEnableAllDisabledByDefault() throws Exception {
+        Bundle defaultDisabledAllBundle = new Bundle();
+        HeapDumpListener collector = new HeapDumpListener(defaultDisabledAllBundle, mHelper);
+        collector.setInstrumentation(mInstrumentation);
+
+        collector.testRunStarted(RUN_DESCRIPTION);
+        collector.testStarted(TEST_DESCRIPTION_1);
+        verify(mHelper, times(0)).startCollecting("run_test_one_1");
+    }
+
+    /** Test heapdump collection enabled only for the 2nd and 3rd iterations.*/
+    @Test
+    public void testHeapCollectionSpecificIterations() throws Exception {
+        Bundle enableSpecificIterationsBundle = new Bundle();
+        enableSpecificIterationsBundle.putString(
+                HeapDumpListener.ENABLE_ITERATION_IDS, "2,3");
+        HeapDumpListener collector = new HeapDumpListener(enableSpecificIterationsBundle, mHelper);
+        collector.setInstrumentation(mInstrumentation);
+
+        collector.testRunStarted(RUN_DESCRIPTION);
+        collector.testStarted(TEST_DESCRIPTION_1);
+        collector.testStarted(TEST_DESCRIPTION_1);
+        collector.testStarted(TEST_DESCRIPTION_1);
+        verify(mHelper, times(1)).startCollecting("run_test_one_2");
+        verify(mHelper, times(1)).startCollecting("run_test_one_3");
+    }
+
+    /** Test heapdump collection enabled only for the 2nd and 3rd iterations.*/
+    @Test
+    public void testHeapCollectionWithProcessNames() throws Exception {
+        Bundle enableSpecificIterationsBundle = new Bundle();
+        enableSpecificIterationsBundle.putString(
+                HeapDumpListener.ENABLE_ITERATION_IDS, "2,3");
+        enableSpecificIterationsBundle.putString(
+                HeapDumpListener.PROCESS_NAMES_KEY, "system_server");
+        HeapDumpListener collector = new HeapDumpListener(enableSpecificIterationsBundle, mHelper);
+        collector.setInstrumentation(mInstrumentation);
+
+        collector.testRunStarted(RUN_DESCRIPTION);
+        collector.testStarted(TEST_DESCRIPTION_1);
+        collector.testStarted(TEST_DESCRIPTION_1);
+        collector.testStarted(TEST_DESCRIPTION_1);
+        verify(mHelper, times(1)).setUp(HeapDumpListener.DEFAULT_OUTPUT_DIR,  "system_server");
+        verify(mHelper, times(1)).startCollecting("run_test_one_2");
+        verify(mHelper, times(1)).startCollecting("run_test_one_3");
+    }
+
+    /** Test heapdump collection enabled only for the 2nd iterations during multiple tests.*/
+    @Test
+    public void testHeapCollectionSpecificIterationsMultipleTests() throws Exception {
+        Bundle enableSpecificIterationsBundle = new Bundle();
+        enableSpecificIterationsBundle.putString(
+                HeapDumpListener.ENABLE_ITERATION_IDS, "2");
+        HeapDumpListener collector = new HeapDumpListener(enableSpecificIterationsBundle, mHelper);
+        collector.setInstrumentation(mInstrumentation);
+
+        collector.testRunStarted(RUN_DESCRIPTION);
+        collector.testStarted(TEST_DESCRIPTION_1);
+        collector.testStarted(TEST_DESCRIPTION_1);
+        collector.testStarted(TEST_DESCRIPTION_2);
+        collector.testStarted(TEST_DESCRIPTION_2);
+        verify(mHelper, times(1)).startCollecting("run_test_one_2");
+        verify(mHelper, times(1)).startCollecting("run_test_two_2");
+    }
+
+    /** Test heapdump collection enabled for all the iterations and overrides the
+     * individual iteration id flag.*/
+    @Test
+    public void testEnableAllOverrideIndvidualIterationIdFlag() throws Exception {
+        Bundle enableSpecificIterationsBundle = new Bundle();
+        enableSpecificIterationsBundle.putString(
+                HeapDumpListener.ENABLE_ITERATION_IDS, "2");
+        enableSpecificIterationsBundle.putString(
+                HeapDumpListener.ITERATION_ALL_ENABLE, String.valueOf(true));
+        HeapDumpListener collector = new HeapDumpListener(enableSpecificIterationsBundle, mHelper);
+        collector.setInstrumentation(mInstrumentation);
+
+        collector.testRunStarted(RUN_DESCRIPTION);
+        collector.testStarted(TEST_DESCRIPTION_1);
+        collector.testStarted(TEST_DESCRIPTION_1);
+        collector.testStarted(TEST_DESCRIPTION_2);
+        collector.testStarted(TEST_DESCRIPTION_2);
+        verify(mHelper, times(1)).startCollecting("run_test_one_1");
+        verify(mHelper, times(1)).startCollecting("run_test_one_2");
+        verify(mHelper, times(1)).startCollecting("run_test_two_1");
+        verify(mHelper, times(1)).startCollecting("run_test_two_2");
+    }
+
+    /** Test to verify the iteration separator is handled from the test class name.*/
+    @Test
+    public void testHeapCollectionTestWithIterationSeparator() throws Exception {
+        Bundle enableAllBundle = new Bundle();
+        enableAllBundle.putString(HeapDumpListener.ITERATION_ALL_ENABLE, String.valueOf(true));
+        HeapDumpListener collector = new HeapDumpListener(enableAllBundle, mHelper);
+        collector.setInstrumentation(mInstrumentation);
+
+        collector.testRunStarted(RUN_DESCRIPTION);
+        collector.testStarted(Description.createTestDescription("run$1", "test_two"));
+        verify(mHelper, times(1)).startCollecting("run_test_two_1");
+    }
+
+    /** Test to verify per test run heapdump collection. */
+    @Test
+    public void testHeapCollectionOnlyForTestRun() throws Exception {
+        Bundle collectPerRunBundle = new Bundle();
+        collectPerRunBundle.putString(HeapDumpListener.COLLECT_PER_RUN, "true");
+        HeapDumpListener collector = new HeapDumpListener(collectPerRunBundle, mHelper);
+        collector.setInstrumentation(mInstrumentation);
+        collector.testRunStarted(RUN_DESCRIPTION);
+        verify(mHelper, times(1)).startCollecting("heapdump_1");
+    }
+
+    /** Test to verify per test run heap dump collection flag overrides the per test method flag.*/
+    @Test
+    public void testHeapCollectionForTestRunOverridesPerTest() throws Exception {
+        Bundle collectPerRunBundle = new Bundle();
+        collectPerRunBundle.putString(HeapDumpListener.COLLECT_PER_RUN, "true");
+        collectPerRunBundle.putString(HeapDumpListener.ITERATION_ALL_ENABLE, String.valueOf(true));
+        HeapDumpListener collector = new HeapDumpListener(collectPerRunBundle, mHelper);
+        collector.setInstrumentation(mInstrumentation);
+        collector.testRunStarted(RUN_DESCRIPTION);
+        collector.testStarted(Description.createTestDescription("run$1", "test_two"));
+        verify(mHelper, times(1)).startCollecting("heapdump_1");
+        verify(mHelper, times(0)).startCollecting("run_test_two_1");
+    }
+
+    /** Test to verify per test run heap dump collection flag overrides heapdump collection on
+     * specific iteration id's.*/
+    @Test
+    public void testHeapCollectionForTestRunOverridesPerTestIterations() throws Exception {
+        Bundle collectPerRunBundle = new Bundle();
+        collectPerRunBundle.putString(HeapDumpListener.COLLECT_PER_RUN, "true");
+        collectPerRunBundle.putString(HeapDumpListener.ENABLE_ITERATION_IDS, "1");
+        HeapDumpListener collector = new HeapDumpListener(collectPerRunBundle, mHelper);
+        collector.setInstrumentation(mInstrumentation);
+        collector.testRunStarted(RUN_DESCRIPTION);
+        collector.testStarted(Description.createTestDescription("run$1", "test_two"));
+        verify(mHelper, times(1)).startCollecting("heapdump_1");
+        verify(mHelper, times(0)).startCollecting("run_test_two_1");
+    }
+}
diff --git a/libraries/flicker/Android.bp b/libraries/flicker/Android.bp
index f65c6de..8ada1a1 100644
--- a/libraries/flicker/Android.bp
+++ b/libraries/flicker/Android.bp
@@ -62,6 +62,8 @@
         "platform-test-annotations",
         "platform-test-core-rules",
         "health-testing-utils",
+        "collector-device-lib",
+        "kotlinx-coroutines-android",
     ],
 }
 
diff --git a/libraries/flicker/README.md b/libraries/flicker/README.md
index 0b58424..ff8a4c1 100644
--- a/libraries/flicker/README.md
+++ b/libraries/flicker/README.md
@@ -123,8 +123,6 @@
 Captures Layers trace. This monitor is started by default. Build a transition with `skipLayersTrace()` to disable this monitor.
 #### WindowManagerTraceMonitor
 Captures Window Manager trace. This monitor is started by default. Build a transition with `skipWindowManagerTrace()` to disable this monitor.
-#### WindowAnimationFrameStatsMonitor
-Captures WindowAnimationFrameStats for the transition. This monitor is started by default and is used to eliminate *janky* runs. If an iteration has skipped frames, as determined by WindowAnimationFrameStats, the results for the iteration is skipped. If the list of results is empty after all iterations are completed, then the test should fail. Build a transition with `includeJankyRuns()` to disable this monitor.
 #### ScreenRecorder
 Captures screen to a video file. This monitor is disabled by default. Build a transition with `recordEachRun()` to capture each transition or build with `recordAllRuns()` to capture every transition including setup and teardown.
 
@@ -157,4 +155,4 @@
 ```java
     // Check if "Chrome" layer is hidden by parent in the first trace entry.
     assertThat(result).isHiddenByParent("Chrome").inTheBeginning();
-```
\ No newline at end of file
+```
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/Flicker.kt b/libraries/flicker/src/com/android/server/wm/flicker/Flicker.kt
index cc174d9..19f0833 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/Flicker.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/Flicker.kt
@@ -22,7 +22,6 @@
 import androidx.test.uiautomator.UiDevice
 import com.android.server.wm.flicker.assertions.AssertionData
 import com.android.server.wm.flicker.monitor.ITransitionMonitor
-import com.android.server.wm.flicker.monitor.WindowAnimationFrameStatsMonitor
 import com.android.server.wm.traces.common.layers.LayersTrace
 import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
@@ -63,10 +62,6 @@
      */
     @JvmField var repetitions: Int,
     /**
-     * Monitor for janky frames, when filtering out janky runs
-     */
-    @JvmField val frameStatsMonitor: WindowAnimationFrameStatsMonitor?,
-    /**
      * Enabled tracing monitors
      */
     @JvmField val traceMonitors: List<ITransitionMonitor>,
@@ -103,26 +98,39 @@
         private set
 
     /**
-     * Executes the test.
+     * Executes the test transition.
      *
      * @throws IllegalStateException If cannot execute the transition
      */
     fun execute(): Flicker = apply {
         val result = runner.execute(this)
         this.result = result
-        checkIsExecuted()
+        checkHasSuccessfullyExecutedATransitionRun()
     }
 
     /**
-     * Asserts if the transition of this flicker test has ben executed
+     * Asserts if at least a run of the transition of this flicker test has been executed
+     * successfully, indicating that there is something the run the assertions on.
      */
-    private fun checkIsExecuted() {
+    private fun checkHasSuccessfullyExecutedATransitionRun() {
+        val result = result
         if (result == null) {
             execute()
-        }
-        val error = result?.error
-        if (error != null) {
-            throw IllegalStateException("Unable to execute transition", error)
+        } else {
+            if (result.successfulRuns.isEmpty()) {
+                // Only throw the execution exception here if there are no successful transition
+                // runs, otherwise we want to execute the assertions on the successful runs and only
+                // throw the exception after we have collected the transition assertion data, in
+                // which case the execution exception will be thrown in the
+                // result.checkForExecutionErrors() call in this.clear().
+                val executionError = if (result.executionErrors.size == 1) {
+                    result.executionErrors[0]
+                } else {
+                    result.combinedExecutionError
+                }
+
+                throw executionError
+            }
         }
     }
 
@@ -133,7 +141,7 @@
      * @throws AssertionError If the assertions fail or the transition crashed
      */
     fun checkAssertion(assertion: AssertionData) {
-        checkIsExecuted()
+        checkHasSuccessfullyExecutedATransitionRun()
         val result = result
         requireNotNull(result)
 
@@ -144,13 +152,13 @@
     }
 
     /**
-     * Deletes the traces files for successful assertions and clears the cached runner results
-     *
+     * Saves the traces files assertions were run on, clears the cached runner results, and assert
+     * any error that occurred when executing the transitions.
      */
     fun clear() {
         Log.v(FLICKER_TAG, "Cleaning up spec $testName")
         runner.cleanUp()
-        result?.cleanUp()
+        result?.checkForExecutionErrors()
         result = null
     }
 
@@ -173,4 +181,4 @@
     override fun toString(): String {
         return this.testName
     }
-}
\ No newline at end of file
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/FlickerBlockJUnit4ClassRunner.kt b/libraries/flicker/src/com/android/server/wm/flicker/FlickerBlockJUnit4ClassRunner.kt
index d7f7faf..594027d 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/FlickerBlockJUnit4ClassRunner.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/FlickerBlockJUnit4ClassRunner.kt
@@ -111,7 +111,7 @@
      */
     override fun createTest(): Any {
         val test = super.createTest()
-        if (flickerTestParameter?.internalFlicker == null) {
+        if (flickerTestParameter?.isInitialized != true) {
             Log.v(FLICKER_TAG, "Flicker object is not yet initialized")
             injectFlickerOnTestParams(test)
         }
@@ -125,16 +125,14 @@
         val flickerTestParameter = flickerTestParameter
         val flickerMethod = flickerMethod
         if (flickerTestParameter != null && flickerMethod != null) {
-            Log.v(FLICKER_TAG, "Creating flicker object and adding it to test parameter")
+            val testName = test::class.java.simpleName
+            Log.v(FLICKER_TAG, "Creating flicker object for $testName and adding it into " +
+                "test parameter")
             val builder = flickerMethod.invokeExplosively(test) as FlickerBuilder
-            val testName = "${test::class.java.simpleName}_${flickerTestParameter.name}"
-            val flicker = builder.apply {
-                withTestName { testName }
-                repeat { flickerTestParameter.config.repetitions }
-            }.build(TransitionRunnerWithRules(flickerTestParameter.config))
-            flickerTestParameter.internalFlicker = flicker
+            flickerTestParameter.initialize(builder, testName)
         } else {
-            Log.v(FLICKER_TAG, "Missing flicker builder provider method")
+            Log.v(FLICKER_TAG, "Missing flicker builder provider method " +
+                "in ${test::class.java.simpleName}")
         }
     }
 
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/FlickerResult.kt b/libraries/flicker/src/com/android/server/wm/flicker/FlickerResult.kt
index 62da20d..5808c41 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/FlickerResult.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/FlickerResult.kt
@@ -16,9 +16,9 @@
 
 package com.android.server.wm.flicker
 
+import com.android.server.wm.flicker.FlickerRunResult.Companion.RunStatus
 import com.android.server.wm.flicker.assertions.AssertionData
 import com.android.server.wm.flicker.assertions.FlickerAssertionError
-import com.android.server.wm.flicker.assertions.FlickerAssertionErrorBuilder
 import com.google.common.truth.Truth
 
 /**
@@ -28,71 +28,86 @@
     /**
      * Result of each transition run
      */
-    @JvmField val runs: List<FlickerRunResult> = listOf(),
+    @JvmField val runResults: List<FlickerRunResult> = listOf(),
     /**
      * List of test created during the execution
      */
     @JvmField val tags: Set<String> = setOf(),
     /**
-     * Error which happened during the transition
+     * Execution errors which happened during the execution of the Flicker test
      */
-    @JvmField val error: Throwable? = null
+    @JvmField val executionErrors: List<Throwable> = listOf()
 ) {
+    init {
+        for (result in runResults) {
+            require(result.status != RunStatus.UNDEFINED) {
+                "Can't create FlickerResult from RunResult ${result.traceName} " +
+                        "(${result.assertionTag}) with UNDEFINED status."
+            }
+        }
+    }
+
+    /** Successful runs on which we can run assertions */
+    val successfulRuns: List<FlickerRunResult> = runResults.filter { it.isSuccessfulRun }
+    /** Failed runs due to execution errors which we shouldn't run assertions on */
+    private val failedRuns: List<FlickerRunResult> = runResults.filter { it.isFailedRun }
+
+    val combinedExecutionError = CombinedExecutionError(executionErrors)
+
     /**
      * List of failures during assertion
      */
     private val failures: MutableList<FlickerAssertionError> = mutableListOf()
 
     /**
-     * Asserts if the transition of this flicker test has ben executed
-     */
-    private fun checkIsExecuted() {
-        Truth.assertWithMessage(error?.message).that(error).isNull()
-        Truth.assertWithMessage("Transition was not executed").that(runs).isNotEmpty()
-    }
-
-    /**
      * Run the assertion on the trace
      *
      * @throws AssertionError If the assertion fail or the transition crashed
      */
     internal fun checkAssertion(assertion: AssertionData): List<FlickerAssertionError> {
-        checkIsExecuted()
-        val filteredRuns = runs.filter { it.assertionTag == assertion.tag }
-        val currFailures = filteredRuns.mapNotNull { run ->
-            try {
-                assertion.checkAssertion(run)
-                null
-            } catch (error: Throwable) {
-                FlickerAssertionErrorBuilder()
-                    .fromError(error)
-                    .atTag(assertion.tag)
-                    .withTrace(run.traceFiles)
-                    .build()
-            }
-        }
+        Truth.assertWithMessage("Expected to have runResults but none were found")
+                .that(runResults).isNotEmpty()
+        Truth.assertWithMessage("No transitions were not executed successful")
+                .that(successfulRuns).isNotEmpty()
+
+        val filteredRuns = successfulRuns.filter { it.assertionTag == assertion.tag }
+        val currFailures = filteredRuns.mapNotNull { run -> run.checkAssertion(assertion) }
         failures.addAll(currFailures)
         return currFailures
     }
 
     /**
-     * Remove from the device the trace files associated with passed runs.
-     *
-     * If an test fails, or if the transition crashes, retain all traces related to
-     * that run.
+     * Asserts if there have been any execution errors while running the transitions
      */
-    fun cleanUp() {
-        if (error != null) {
-            return
-        }
-        runs.forEach {
-            if (it.canDelete(failures)) {
-                it.cleanUp()
+    internal fun checkForExecutionErrors() {
+        if (executionErrors.isNotEmpty()) {
+            if (executionErrors.size == 1) {
+                throw executionErrors[0]
             }
+            throw combinedExecutionError
         }
     }
 
-    fun isEmpty(): Boolean = error == null && runs.isEmpty()
+    fun isEmpty(): Boolean = executionErrors.isEmpty() && successfulRuns.isEmpty()
 
     fun isNotEmpty(): Boolean = !isEmpty()
-}
\ No newline at end of file
+
+    companion object {
+        class CombinedExecutionError(val errors: List<Throwable>?) : Throwable() {
+            override val message: String? get() {
+                if (errors == null || errors.isEmpty()) {
+                    return null
+                }
+                if (errors.size == 1) {
+                    return errors[0].toString()
+                }
+                return "Combined Errors Of\n\t- " +
+                        errors.joinToString("\n\t\tAND\n\t- ") { it.toString() } +
+                        "\n[NOTE: any details below are only for the first error]"
+            }
+
+            override val cause: Throwable?
+                get() = errors?.get(0)
+        }
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/FlickerRunResult.kt b/libraries/flicker/src/com/android/server/wm/flicker/FlickerRunResult.kt
index 22de5b9..e2782af 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/FlickerRunResult.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/FlickerRunResult.kt
@@ -16,37 +16,39 @@
 
 package com.android.server.wm.flicker
 
-import android.util.Log
 import androidx.annotation.VisibleForTesting
+import com.android.compatibility.common.util.ZipUtil
+import com.android.server.wm.flicker.assertions.AssertionData
 import com.android.server.wm.flicker.assertions.FlickerAssertionError
+import com.android.server.wm.flicker.assertions.FlickerAssertionErrorBuilder
 import com.android.server.wm.flicker.assertions.FlickerSubject
 import com.android.server.wm.flicker.dsl.AssertionTag
+import com.android.server.wm.flicker.traces.FlickerTraceSubject
 import com.android.server.wm.flicker.traces.eventlog.EventLogSubject
 import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace
 import com.android.server.wm.flicker.traces.eventlog.FocusEvent
 import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
 import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.layers.LayersTrace
 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
-import com.android.server.wm.traces.parser.layers.LayersTraceParser
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerTraceParser
-import java.io.IOException
-import java.nio.file.Files
+import java.io.File
 import java.nio.file.Path
+import kotlinx.coroutines.async
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
 
 /**
  * Defines the result of a flicker run
  */
 class FlickerRunResult private constructor(
     /**
-     * Run identifier
+     * The trace files associated with the result (incl. screen recording)
      */
-    @JvmField val iteration: Int,
-    /**
-     * Path to the trace files associated with the result (incl. screen recording)
-     */
-    @JvmField val traceFiles: List<Path>,
+    _traceFile: Path?,
     /**
      * Determines which assertions to run (e.g., start, end, all, or a custom tag)
      */
@@ -56,7 +58,7 @@
      */
     internal val wmSubject: FlickerSubject?,
     /**
-     * Truth subject that corresponds to a [LayersTrace] or [LayerTraceEntry]
+     * Truth subject that corresponds to a [LayersTrace] or [BaseLayerTraceEntry]
      */
     internal val layersSubject: FlickerSubject?,
     /**
@@ -65,51 +67,87 @@
     @VisibleForTesting
     val eventLogSubject: EventLogSubject?
 ) {
+    /**
+     * The object responsible for managing the trace file associated with this result.
+     *
+     * By default the file manager is the RunResult itself but in the case the RunResult is
+     * derived or extracted from another RunResult then that other RunResult should be the trace
+     * file manager.
+     */
+    internal var mTraceFile: TraceFile? =
+            if (_traceFile != null) TraceFile(_traceFile) else null
+
+    internal val traceName = mTraceFile?.traceFile?.fileName ?: "UNNAMED_TRACE"
+
+    var status: RunStatus = RunStatus.UNDEFINED
+        internal set(value) {
+            if (field != value) {
+                require(value != RunStatus.UNDEFINED) {
+                    "Can't set status to UNDEFINED after being defined"
+                }
+                require(!field.isFailure) {
+                    "Status of run already set to a failed status $field " +
+                            "and can't be changed to $value."
+                }
+                field = value
+            }
+
+            mTraceFile?.status = status
+        }
+
+    fun setRunFailed() {
+        status = RunStatus.RUN_FAILED
+    }
+
+    val isSuccessfulRun: Boolean get() = !isFailedRun
+    val isFailedRun: Boolean get() {
+        require(status != RunStatus.UNDEFINED) {
+            "RunStatus cannot be UNDEFINED for $traceName ($assertionTag)"
+        }
+        // Other types of failures can only happen if the run has succeeded
+        return status == RunStatus.RUN_FAILED
+    }
+
     fun getSubjects(): List<FlickerSubject> {
         val result = mutableListOf<FlickerSubject>()
 
-        wmSubject?.run { result.add(this.clone()) }
-        layersSubject?.run { result.add(this.clone()) }
-        eventLogSubject?.run { result.add(this.clone()) }
+        wmSubject?.run { result.add(this) }
+        layersSubject?.run { result.add(this) }
+        eventLogSubject?.run { result.add(this) }
 
         return result
     }
 
-    private fun Path?.tryDelete() {
-        try {
-            this?.let { Files.deleteIfExists(it) }
-        } catch (e: IOException) {
-            Log.e(FLICKER_TAG, "Unable do delete $this", e)
-        }
-    }
-
-    fun canDelete(failures: List<FlickerAssertionError>): Boolean {
-        return failures.flatMap { it.traceFiles }.none { failureTrace ->
-            this.traceFiles.any { it == failureTrace }
+    fun checkAssertion(assertion: AssertionData): FlickerAssertionError? {
+        require(status != RunStatus.UNDEFINED) { "A valid RunStatus has not been provided" }
+        return try {
+            assertion.checkAssertion(this)
+            null
+        } catch (error: Throwable) {
+            status = RunStatus.ASSERTION_FAILED
+            FlickerAssertionErrorBuilder()
+                    .fromError(error)
+                    .atTag(assertion.tag)
+                    .withTrace(this.mTraceFile)
+                    .build()
         }
     }
 
     /**
-     * Delete the trace files collected
+     * Parse a [trace] into a [SubjectType] asynchronously
+     *
+     * The parsed subject is available in [promise]
      */
-    fun cleanUp() {
-        this.traceFiles.forEach { it.tryDelete() }
+    class AsyncSubjectParser<SubjectType : FlickerTraceSubject<*>>(
+        val trace: Path,
+        parser: ((Path) -> SubjectType?)?
+    ) {
+        val promise: Deferred<SubjectType?>? = parser?.run { SCOPE.async { parser(trace) } }
     }
 
-    class Builder @JvmOverloads constructor(private val iteration: Int = 0) {
-        /**
-         * Path to the WindowManager trace file, if collected
-         */
-        var wmTraceFile: Path? = null
-
-        /**
-         * Path to the SurfaceFlinger trace file, if collected
-         */
-        var layersTraceFile: Path? = null
-
-        /**
-         * Path to screen recording of the run, if collected
-         */
+    class Builder {
+        private var wmTraceData: AsyncSubjectParser<WindowManagerTraceSubject>? = null
+        private var layersTraceData: AsyncSubjectParser<LayersTraceSubject>? = null
         var screenRecording: Path? = null
 
         /**
@@ -117,21 +155,43 @@
          */
         var eventLog: List<FocusEvent>? = null
 
-        private fun getTraceFiles() = listOfNotNull(wmTraceFile, layersTraceFile, screenRecording)
+        /**
+         * Parses a [WindowManagerTraceSubject]
+         *
+         * @param traceFile of the trace file to parse
+         * @param parser lambda to parse the trace into a [WindowManagerTraceSubject]
+         */
+        fun setWmTrace(traceFile: Path, parser: (Path) -> WindowManagerTraceSubject?) {
+            wmTraceData = AsyncSubjectParser(traceFile, parser)
+        }
+
+        /**
+         * Parses a [LayersTraceSubject]
+         *
+         * @param traceFile of the trace file to parse
+         * @param parser lambda to parse the trace into a [LayersTraceSubject]
+         */
+        fun setLayersTrace(traceFile: Path, parser: (Path) -> LayersTraceSubject?) {
+            layersTraceData = AsyncSubjectParser(traceFile, parser)
+        }
 
         private fun buildResult(
             assertionTag: String,
             wmSubject: FlickerSubject?,
             layersSubject: FlickerSubject?,
+            status: RunStatus,
+            traceFile: Path? = null,
             eventLogSubject: EventLogSubject? = null
         ): FlickerRunResult {
-            return FlickerRunResult(iteration,
-                getTraceFiles(),
+            val result = FlickerRunResult(
+                traceFile,
                 assertionTag,
                 wmSubject,
                 layersSubject,
                 eventLogSubject
             )
+            result.status = status
+            return result
         }
 
         /**
@@ -144,65 +204,129 @@
         fun buildStateResult(
             assertionTag: String,
             wmTrace: WindowManagerTrace?,
-            layersTrace: LayersTrace?
+            layersTrace: LayersTrace?,
+            wmTraceFile: Path?,
+            layersTraceFile: Path?,
+            testName: String,
+            iteration: Int,
+            status: RunStatus
         ): FlickerRunResult {
             val wmSubject = wmTrace?.let { WindowManagerTraceSubject.assertThat(it).first() }
             val layersSubject = layersTrace?.let { LayersTraceSubject.assertThat(it).first() }
-            return buildResult(assertionTag, wmSubject, layersSubject)
+
+            val traceFiles = mutableListOf<File>()
+            wmTraceFile?.let { traceFiles.add(it.toFile()) }
+            layersTraceFile?.let { traceFiles.add(it.toFile()) }
+            val traceFile = compress(traceFiles, "${assertionTag}_${testName}_$iteration.zip")
+
+            return buildResult(assertionTag, wmSubject, layersSubject, status,
+                    traceFile = traceFile)
         }
 
         @VisibleForTesting
-        fun buildEventLogResult(): FlickerRunResult {
+        fun buildEventLogResult(status: RunStatus): FlickerRunResult {
             val events = eventLog ?: emptyList()
             return buildResult(
                 AssertionTag.ALL,
                 wmSubject = null,
                 layersSubject = null,
-                eventLogSubject = EventLogSubject.assertThat(events)
+                eventLogSubject = EventLogSubject.assertThat(events),
+                status = status
             )
         }
 
         @VisibleForTesting
-        fun buildTraceResults(): List<FlickerRunResult> {
-            var wmTrace: WindowManagerTrace? = null
-            var layersTrace: LayersTrace? = null
+        fun buildTraceResults(
+            testName: String,
+            iteration: Int,
+            status: RunStatus
+        ): List<FlickerRunResult> = runBlocking {
+            val wmSubject = wmTraceData?.promise?.await()
+            val layersSubject = layersTraceData?.promise?.await()
 
-            if (wmTrace == null && wmTraceFile != null) {
-                Log.v(FLICKER_TAG, "Parsing WM trace")
-                wmTrace = wmTraceFile?.let {
-                    val traceData = Files.readAllBytes(it)
-                    WindowManagerTraceParser.parseFromTrace(traceData)
-                }
-            }
-
-            if (layersTrace == null && layersTraceFile != null) {
-                Log.v(FLICKER_TAG, "Parsing Layers trace")
-                layersTrace = layersTraceFile?.let {
-                    val traceData = Files.readAllBytes(it)
-                    LayersTraceParser.parseFromTrace(traceData)
-                }
-            }
-
-            val wmSubject = wmTrace?.let { WindowManagerTraceSubject.assertThat(it) }
-            val layersSubject = layersTrace?.let { LayersTraceSubject.assertThat(it) }
-
+            val traceFile = compress(testName, iteration)
             val traceResult = buildResult(
-                AssertionTag.ALL, wmSubject, layersSubject)
-            val initialStateResult = buildResult(
-                AssertionTag.START, wmSubject?.first(), layersSubject?.first())
-            val finalStateResult = buildResult(
-                AssertionTag.END, wmSubject?.last(), layersSubject?.last())
+                AssertionTag.ALL, wmSubject, layersSubject, traceFile = traceFile, status = status)
 
-            return listOf(initialStateResult, finalStateResult, traceResult)
+            val initialStateResult = buildResult(
+                AssertionTag.START, wmSubject?.first(), layersSubject?.first(), status = status)
+            initialStateResult.mTraceFile = traceResult.mTraceFile
+
+            val finalStateResult = buildResult(
+                AssertionTag.END, wmSubject?.last(), layersSubject?.last(), status = status)
+            finalStateResult.mTraceFile = traceResult.mTraceFile
+
+            listOf(initialStateResult, finalStateResult, traceResult)
         }
 
-        fun buildAll(): List<FlickerRunResult> {
-            val result = buildTraceResults().toMutableList()
-            if (eventLog != null) {
-                result.add(buildEventLogResult())
+        private fun compress(testName: String, iteration: Int): Path? {
+            val traceFiles = mutableListOf<File>()
+            wmTraceData?.trace?.let { traceFiles.add(it.toFile()) }
+            layersTraceData?.trace?.let { traceFiles.add(it.toFile()) }
+            screenRecording?.let { traceFiles.add(it.toFile()) }
+
+            return compress(traceFiles, "${testName}_$iteration.zip")
+        }
+
+        private fun compress(traceFiles: List<File>, archiveName: String): Path? {
+            val files = traceFiles.filter { it.exists() }
+            if (files.isEmpty()) {
+                return null
             }
 
-            return result
+            val firstFile = files.first()
+            val compressedFile = firstFile.resolveSibling(archiveName)
+            ZipUtil.createZip(traceFiles, compressedFile)
+            traceFiles.forEach {
+                it.delete()
+            }
+
+            return compressedFile.toPath()
+        }
+
+        fun buildAll(testName: String, iteration: Int, status: RunStatus): List<FlickerRunResult> {
+            val results = buildTraceResults(testName, iteration, status).toMutableList()
+            if (eventLog != null) {
+                results.add(buildEventLogResult(status = status))
+            }
+
+            return results
+        }
+
+        fun setResultFrom(resultSetter: IResultSetter) {
+            resultSetter.setResult(this)
+        }
+    }
+
+    interface IResultSetter {
+        fun setResult(builder: Builder)
+    }
+
+    companion object {
+        private val SCOPE = CoroutineScope(Dispatchers.IO + SupervisorJob())
+
+        enum class RunStatus(val prefix: String = "", val isFailure: Boolean) {
+            UNDEFINED("???", false),
+
+            RUN_SUCCESS("UNCHECKED", false),
+            ASSERTION_SUCCESS("PASS", false),
+
+            RUN_FAILED("FAILED_RUN", true),
+            PARSING_FAILURE("FAILED_PARSING", true),
+            ASSERTION_FAILED("FAIL", true);
+
+            companion object {
+                fun merge(runStatuses: List<RunStatus>): RunStatus {
+                    val precedence = listOf(ASSERTION_FAILED, RUN_FAILED, ASSERTION_SUCCESS)
+                    for (status in precedence) {
+                        if (runStatuses.any { it == status }) {
+                            return status
+                        }
+                    }
+
+                    return UNDEFINED
+                }
+            }
         }
     }
 }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/FlickerTestParameter.kt b/libraries/flicker/src/com/android/server/wm/flicker/FlickerTestParameter.kt
index 771088f..503b285 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/FlickerTestParameter.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/FlickerTestParameter.kt
@@ -16,107 +16,281 @@
 
 package com.android.server.wm.flicker
 
+import android.app.Instrumentation
+import android.platform.test.rule.NavigationModeRule
+import android.platform.test.rule.PressHomeRule
+import android.platform.test.rule.UnlockScreenRule
 import android.view.Surface
 import android.view.WindowManagerPolicyConstants
+import androidx.annotation.VisibleForTesting
 import com.android.server.wm.flicker.assertions.AssertionData
 import com.android.server.wm.flicker.assertions.FlickerSubject
 import com.android.server.wm.flicker.dsl.AssertionTag
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.SampleAppHelper
+import com.android.server.wm.flicker.rules.ChangeDisplayOrientationRule
+import com.android.server.wm.flicker.rules.LaunchAppRule
+import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
 import com.android.server.wm.flicker.traces.eventlog.EventLogSubject
 import com.android.server.wm.flicker.traces.layers.LayerTraceEntrySubject
 import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import com.android.server.wm.flicker.traces.region.RegionTraceSubject
 import com.android.server.wm.flicker.traces.windowmanager.WindowManagerStateSubject
 import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
+import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.rules.RuleChain
+import org.junit.rules.TestRule
 
 /**
  * Specification of a flicker test for JUnit ParameterizedRunner class
  */
 data class FlickerTestParameter(
     @JvmField val config: MutableMap<String, Any?>,
-    @JvmField val name: String = defaultName(config)
+    private val nameOverride: String? = null
 ) {
-    internal var internalFlicker: Flicker? = null
-    internal val flicker: Flicker get() = internalFlicker ?: error("Flicker not initialized")
-    val isRotated: Boolean
-        get() = config.startRotation == Surface.ROTATION_90 ||
-            config.startRotation == Surface.ROTATION_270
+    private var internalFlicker: Flicker? = null
 
+    private val flicker: Flicker get() = internalFlicker ?: error("Flicker not initialized")
+    private val name: String get() = nameOverride ?: defaultName(this)
+
+    internal val isInitialized: Boolean get() = internalFlicker != null
+    internal val result: FlickerResult? get() = internalFlicker?.result
+
+    /**
+     * If the initial screen rotation is 90 (landscape) or 180 (seascape) degrees
+     */
+    val isLandscapeOrSeascapeAtStart: Boolean
+        get() = startRotation == Surface.ROTATION_90 || startRotation == Surface.ROTATION_270
+
+    /**
+     * Initial screen rotation (see [Surface] for values)
+     *
+     * Defaults to [Surface.ROTATION_0]
+     */
+    val startRotation: Int
+        get() = config.getOrDefault(START_ROTATION, Surface.ROTATION_0) as Int
+
+    /**
+     * Final screen rotation (see [Surface] for values)
+     *
+     * Defaults to [startRotation]
+     */
+    val endRotation: Int
+        get() = config.getOrDefault(END_ROTATION, startRotation) as Int
+
+    /**
+     * Navigation mode, such as 3 button or gestural.
+     *
+     * See [WindowManagerPolicyConstants].NAV_BAR_MODE_* for possible values
+     *
+     * Defaults to [WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY]
+     */
+    val navBarMode: String
+        get() = config.getOrDefault(NAV_BAR_MODE,
+            WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY) as String
+
+    val navBarModeName
+        get() = when (this.navBarMode) {
+            WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY -> "3_BUTTON_NAV"
+            WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY -> "2_BUTTON_NAV"
+            WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY -> "GESTURAL_NAV"
+            else -> "UNKNOWN_NAV_BAR_MODE(${this.navBarMode}"
+        }
+
+    val isGesturalNavigation =
+        navBarMode == WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+
+    /**
+     * Clean the internal flicker reference (cache)
+     */
     fun clear() {
         internalFlicker?.clear()
     }
 
+    /**
+     * Builds a flicker object and assigns it to the test parameters
+     */
+    fun initialize(builder: FlickerBuilder, testName: String) {
+        internalFlicker = builder
+            .withTestName { "${testName}_$name" }
+            .repeat { config.getOrDefault(REPETITIONS, 1) as Int }
+            .build(TransitionRunnerWithRules(getTestSetupRules(builder.instrumentation)))
+    }
+
+    /**
+     * Execute [assertion] on the initial state of a WM trace (before transition)
+     *
+     * @param assertion Assertion predicate
+     */
     fun assertWmStart(assertion: WindowManagerStateSubject.() -> Unit) {
         val assertionData = buildWmStartAssertion(assertion)
         this.flicker.checkAssertion(assertionData)
     }
 
+    /**
+     * Execute [assertion] on the final state of a WM trace (after transition)
+     *
+     * @param assertion Assertion predicate
+     */
     fun assertWmEnd(assertion: WindowManagerStateSubject.() -> Unit) {
         val assertionData = buildWmEndAssertion(assertion)
         this.flicker.checkAssertion(assertionData)
     }
 
+    /**
+     * Execute [assertion] on a WM trace
+     *
+     * @param assertion Assertion predicate
+     */
     fun assertWm(assertion: WindowManagerTraceSubject.() -> Unit) {
         val assertionData = buildWMAssertion(assertion)
         this.flicker.checkAssertion(assertionData)
     }
 
+    /**
+     * Execute [assertion] on a user defined moment ([tag]) of a WM trace
+     *
+     * @param assertion Assertion predicate
+     */
     fun assertWmTag(tag: String, assertion: WindowManagerStateSubject.() -> Unit) {
         val assertionData = buildWMTagAssertion(tag, assertion)
         this.flicker.checkAssertion(assertionData)
     }
 
+    /**
+     * Execute [assertion] on the visible region of a component on the WM trace
+     *
+     * @param component The component for which we want to get the visible region for to run the
+     *                  assertion on
+     * @param assertion Assertion predicate
+     */
+    fun assertWmVisibleRegion(
+        vararg components: FlickerComponentName,
+        assertion: RegionTraceSubject.() -> Unit
+    ) {
+        val assertionData = buildWmVisibleRegionAssertion(components = components, assertion)
+        this.flicker.checkAssertion(assertionData)
+    }
+
+    /**
+     * Execute [assertion] on the initial state of a SF trace (before transition)
+     *
+     * @param assertion Assertion predicate
+     */
     fun assertLayersStart(assertion: LayerTraceEntrySubject.() -> Unit) {
         val assertionData = buildLayersStartAssertion(assertion)
         this.flicker.checkAssertion(assertionData)
     }
 
+    /**
+     * Execute [assertion] on the final state of a SF trace (after transition)
+     *
+     * @param assertion Assertion predicate
+     */
     fun assertLayersEnd(assertion: LayerTraceEntrySubject.() -> Unit) {
         val assertionData = buildLayersEndAssertion(assertion)
         this.flicker.checkAssertion(assertionData)
     }
 
+    /**
+     * Execute [assertion] on a SF trace
+     *
+     * @param assertion Assertion predicate
+     */
     fun assertLayers(assertion: LayersTraceSubject.() -> Unit) {
         val assertionData = buildLayersAssertion(assertion)
         this.flicker.checkAssertion(assertionData)
     }
 
+    /**
+     * Execute [assertion] on a user defined moment ([tag]) of a SF trace
+     *
+     * @param assertion Assertion predicate
+     */
     fun assertLayersTag(tag: String, assertion: LayerTraceEntrySubject.() -> Unit) {
         val assertionData = buildLayersTagAssertion(tag, assertion)
         this.flicker.checkAssertion(assertionData)
     }
 
+    /**
+     * Execute [assertion] on the visible region of a component on the layers trace
+     *
+     * @param components The components for which we want to get the visible region for to run the
+     *   assertion on. The assertion will run on the union of the regions of these components.
+     * @param useCompositionEngineRegionOnly If true, uses only the region calculated from the
+     *   Composition Engine (CE) -- visibleRegion in the proto definition. Otherwise calculates
+     *   the visible region when the information is not available from the CE
+     * @param assertion Assertion predicate
+     */
+    @JvmOverloads
+    fun assertLayersVisibleRegion(
+        vararg components: FlickerComponentName,
+        useCompositionEngineRegionOnly: Boolean = true,
+        assertion: RegionTraceSubject.() -> Unit
+    ) {
+        val assertionData = buildLayersVisibleRegionAssertion(
+                components = components, useCompositionEngineRegionOnly, assertion)
+        this.flicker.checkAssertion(assertionData)
+    }
+
+    /**
+     * Execute [assertion] on a sequence of event logs
+     *
+     * @param assertion Assertion predicate
+     */
     fun assertEventLog(assertion: EventLogSubject.() -> Unit) {
         val assertionData = buildEventLogAssertion(assertion)
         this.flicker.checkAssertion(assertionData)
     }
 
+    /**
+     * Create the default flicker test setup rules. In order:
+     *   - unlock device
+     *   - change orientation
+     *   - change navigation mode
+     *   - launch an app
+     *   - remove all apps
+     *   - go to home screen
+     *
+     * (b/186740751) An app should be launched because, after changing the navigation mode,
+     * the first app launch is handled as a screen size change (similar to a rotation), this
+     * causes different problems during testing (e.g. IME now shown on app launch)
+     */
+    fun getTestSetupRules(instrumentation: Instrumentation): TestRule =
+        RuleChain.outerRule(UnlockScreenRule())
+            .around(NavigationModeRule(navBarMode))
+            .around(LaunchAppRule(SampleAppHelper(instrumentation)))
+            .around(RemoveAllTasksButHomeRule())
+            .around(ChangeDisplayOrientationRule(startRotation))
+            .around(PressHomeRule())
+
     override fun toString(): String = name
 
     companion object {
-        fun defaultName(config: Map<String, Any?>) = buildString {
-            append(config.startRotationName)
-            if (config.endRotation != config.startRotation) {
-                append("_${config.endRotationName}")
-            }
-            if (config.navBarMode.isNotEmpty()) {
-                append("_${config.navBarModeName}")
-            }
-        }
+        internal const val REPETITIONS = "repetitions"
+        internal const val START_ROTATION = "startRotation"
+        internal const val END_ROTATION = "endRotation"
+        internal const val NAV_BAR_MODE = "navBarMode"
 
+        @VisibleForTesting
         @JvmStatic
         fun buildWmStartAssertion(assertion: WindowManagerStateSubject.() -> Unit): AssertionData =
             AssertionData(tag = AssertionTag.START,
                 expectedSubjectClass = WindowManagerStateSubject::class,
                 assertion = assertion as FlickerSubject.() -> Unit)
 
+        @VisibleForTesting
         @JvmStatic
         fun buildWmEndAssertion(assertion: WindowManagerStateSubject.() -> Unit): AssertionData =
             AssertionData(tag = AssertionTag.END,
                 expectedSubjectClass = WindowManagerStateSubject::class,
                 assertion = assertion as FlickerSubject.() -> Unit)
 
+        @VisibleForTesting
         @JvmStatic
         fun buildWMAssertion(assertion: WindowManagerTraceSubject.() -> Unit): AssertionData {
             val closedAssertion: WindowManagerTraceSubject.() -> Unit = {
+                this.clear()
                 assertion()
                 this.forAllEntries()
             }
@@ -125,6 +299,7 @@
                 assertion = closedAssertion as FlickerSubject.() -> Unit)
         }
 
+        @VisibleForTesting
         @JvmStatic
         fun buildWMTagAssertion(
             tag: String,
@@ -133,21 +308,46 @@
             expectedSubjectClass = WindowManagerStateSubject::class,
             assertion = assertion as FlickerSubject.() -> Unit)
 
+        @VisibleForTesting
+        @JvmStatic
+        fun buildWmVisibleRegionAssertion(
+            vararg components: FlickerComponentName,
+            assertion: RegionTraceSubject.() -> Unit
+        ): AssertionData {
+            val closedAssertion: WindowManagerTraceSubject.() -> Unit = {
+                this.clear()
+                // convert WindowManagerTraceSubject to RegionTraceSubject
+                val regionTraceSubject = visibleRegion(*components)
+                // add assertions to the regionTraceSubject's AssertionChecker
+                assertion(regionTraceSubject)
+                // loop through all entries to validate assertions
+                regionTraceSubject.forAllEntries()
+            }
+
+            return AssertionData(tag = AssertionTag.ALL,
+                expectedSubjectClass = WindowManagerTraceSubject::class,
+                assertion = closedAssertion as FlickerSubject.() -> Unit)
+        }
+
+        @VisibleForTesting
         @JvmStatic
         fun buildLayersStartAssertion(assertion: LayerTraceEntrySubject.() -> Unit): AssertionData =
             AssertionData(tag = AssertionTag.START,
                 expectedSubjectClass = LayerTraceEntrySubject::class,
                 assertion = assertion as FlickerSubject.() -> Unit)
 
+        @VisibleForTesting
         @JvmStatic
         fun buildLayersEndAssertion(assertion: LayerTraceEntrySubject.() -> Unit): AssertionData =
             AssertionData(tag = AssertionTag.END,
                 expectedSubjectClass = LayerTraceEntrySubject::class,
                 assertion = assertion as FlickerSubject.() -> Unit)
 
+        @VisibleForTesting
         @JvmStatic
         fun buildLayersAssertion(assertion: LayersTraceSubject.() -> Unit): AssertionData {
             val closedAssertion: LayersTraceSubject.() -> Unit = {
+                this.clear()
                 assertion()
                 this.forAllEntries()
             }
@@ -157,6 +357,7 @@
                 assertion = closedAssertion as FlickerSubject.() -> Unit)
         }
 
+        @VisibleForTesting
         @JvmStatic
         fun buildLayersTagAssertion(
             tag: String,
@@ -165,42 +366,45 @@
             expectedSubjectClass = LayerTraceEntrySubject::class,
             assertion = assertion as FlickerSubject.() -> Unit)
 
+        @VisibleForTesting
+        @JvmOverloads
+        @JvmStatic
+        fun buildLayersVisibleRegionAssertion(
+            vararg components: FlickerComponentName,
+            useCompositionEngineRegionOnly: Boolean = true,
+            assertion: RegionTraceSubject.() -> Unit
+        ): AssertionData {
+            val closedAssertion: LayersTraceSubject.() -> Unit = {
+                this.clear()
+                // convert LayersTraceSubject to RegionTraceSubject
+                val regionTraceSubject =
+                        visibleRegion(components = components, useCompositionEngineRegionOnly)
+
+                // add assertions to the regionTraceSubject's AssertionChecker
+                assertion(regionTraceSubject)
+                // loop through all entries to validate assertions
+                regionTraceSubject.forAllEntries()
+            }
+
+            return AssertionData(tag = AssertionTag.ALL,
+                    expectedSubjectClass = LayersTraceSubject::class,
+                    assertion = closedAssertion as FlickerSubject.() -> Unit)
+        }
+
         @JvmStatic
         fun buildEventLogAssertion(assertion: EventLogSubject.() -> Unit): AssertionData =
             AssertionData(tag = AssertionTag.ALL,
                 expectedSubjectClass = EventLogSubject::class,
                 assertion = assertion as FlickerSubject.() -> Unit)
+
+        fun defaultName(test: FlickerTestParameter) = buildString {
+            append(Surface.rotationToString(test.startRotation))
+            if (test.endRotation != test.startRotation) {
+                append("_${Surface.rotationToString(test.endRotation)}")
+            }
+            if (test.navBarMode.isNotEmpty()) {
+                append("_${test.navBarModeName}")
+            }
+        }
     }
-}
-
-internal const val REPETITIONS = "repetitions"
-internal const val START_ROTATION = "startRotation"
-internal const val END_ROTATION = "endRotation"
-internal const val NAV_BAR_MODE = "navBarMode"
-
-val Map<String, Any?>.repetitions: Int
-    get() = this.getOrDefault(REPETITIONS, 1) as Int
-
-val Map<String, Any?>.startRotation: Int
-    get() = this.getOrDefault(START_ROTATION, Surface.ROTATION_0) as Int
-
-val Map<String, Any?>.endRotation: Int
-    get() = this.getOrDefault(END_ROTATION, startRotation) as Int
-
-val Map<String, Any?>.startRotationName: String
-    get() = Surface.rotationToString(startRotation)
-
-val Map<String, Any?>.endRotationName: String
-    get() = Surface.rotationToString(endRotation)
-
-val Map<String, Any?>.navBarMode: String
-    get() = this.getOrDefault(NAV_BAR_MODE,
-        WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY) as String
-
-val Map<String, Any?>.navBarModeName
-    get() = when (this.navBarMode) {
-        WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY -> "3_BUTTON_NAV"
-        WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY -> "2_BUTTON_NAV"
-        WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY -> "GESTURAL_NAV"
-        else -> "UNKNOWN_NAV_BAR_MODE(${this.navBarMode}"
-    }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/FlickerTestParameterFactory.kt b/libraries/flicker/src/com/android/server/wm/flicker/FlickerTestParameterFactory.kt
index 5883f9b..ca4bb8f 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/FlickerTestParameterFactory.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/FlickerTestParameterFactory.kt
@@ -75,10 +75,10 @@
         endRotation: Int = startRotation
     ) = FlickerTestParameter(
         mutableMapOf(
-            NAV_BAR_MODE to navBarMode,
-            START_ROTATION to startRotation,
-            END_ROTATION to endRotation,
-            REPETITIONS to repetitions
+            FlickerTestParameter.NAV_BAR_MODE to navBarMode,
+            FlickerTestParameter.START_ROTATION to startRotation,
+            FlickerTestParameter.END_ROTATION to endRotation,
+            FlickerTestParameter.REPETITIONS to repetitions
         )
     )
 
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/TraceFile.kt b/libraries/flicker/src/com/android/server/wm/flicker/TraceFile.kt
new file mode 100644
index 0000000..3b21e4a
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/TraceFile.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 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.util.Log
+import com.android.server.wm.flicker.FlickerRunResult.Companion.RunStatus
+import java.io.IOException
+import java.nio.file.Path
+
+class TraceFile(_traceFile: Path) {
+
+    var traceFile = _traceFile
+        private set
+
+    internal val traceName = traceFile.fileName ?: "UNNAMED_TRACE"
+
+    var status: RunStatus = RunStatus.UNDEFINED
+        internal set(value) {
+            if (field != value) {
+                require(value != RunStatus.UNDEFINED) {
+                    "Can't set status to UNDEFINED after being defined"
+                }
+                require(!field.isFailure) {
+                    "Status of run already set to a failed status $field " +
+                            "and can't be changed to $value."
+                }
+                field = value
+                syncFileWithStatus()
+            }
+        }
+
+    private fun syncFileWithStatus() {
+        // Since we don't expect this to run in a multi-threaded context this is fine
+        val localTraceFile = traceFile
+        try {
+            val newFileName = "${status.prefix}_$traceName"
+            val dst = localTraceFile.resolveSibling(newFileName)
+            Utils.renameFile(localTraceFile, dst)
+            traceFile = dst
+        } catch (e: IOException) {
+            Log.e(FLICKER_TAG, "Unable to update file status $this", e)
+        }
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/TransitionRunner.kt b/libraries/flicker/src/com/android/server/wm/flicker/TransitionRunner.kt
index dc10c00..c818bde 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/TransitionRunner.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/TransitionRunner.kt
@@ -17,6 +17,8 @@
 package com.android.server.wm.flicker
 
 import android.util.Log
+import com.android.server.wm.flicker.FlickerRunResult.Companion.RunStatus
+import com.android.server.wm.flicker.monitor.IFileGeneratingMonitor
 import com.android.server.wm.flicker.monitor.ITransitionMonitor
 import com.android.server.wm.traces.common.ConditionList
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory
@@ -83,57 +85,181 @@
      * @param flicker test specification
      */
     internal open fun run(flicker: Flicker): FlickerResult {
-        val uiStableCondition = ConditionList(listOf(
-            WindowManagerConditionsFactory.isWMStateComplete(),
-            WindowManagerConditionsFactory.hasLayersAnimating().negate()
-        ))
         val runs = mutableListOf<FlickerRunResult>()
-        var executionError: Throwable? = null
-        try {
-            try {
-                flicker.testSetup.forEach { it.invoke(flicker) }
-                for (iteration in 0 until flicker.repetitions) {
-                    try {
-                        flicker.runSetup.forEach { it.invoke(flicker) }
-                        flicker.wmHelper.waitFor(uiStableCondition)
-                        flicker.traceMonitors.forEach { it.start() }
-                        flicker.frameStatsMonitor?.run { start() }
-                        flicker.transitions.forEach { it.invoke(flicker) }
-                    } finally {
-                        flicker.wmHelper.waitFor(uiStableCondition)
-                        flicker.traceMonitors.forEach { it.tryStop() }
-                        flicker.frameStatsMonitor?.run { tryStop() }
-                        flicker.runTeardown.forEach { it.invoke(flicker) }
-                    }
-                    if (flicker.frameStatsMonitor?.jankyFramesDetected() == true) {
-                        Log.e(FLICKER_TAG, "Skipping iteration " +
-                            "$iteration/${flicker.repetitions - 1} " +
-                            "for test ${flicker.testName} due to jank. $flicker.frameStatsMonitor")
-                        continue
-                    }
-                }
-            } finally {
-                val runResults = saveResult(flicker, iteration)
-                runs.addAll(runResults)
-                flicker.testTeardown.forEach { it.invoke(flicker) }
+        val executionErrors = mutableListOf<Throwable>()
+        safeExecution(flicker, runs, executionErrors) {
+            runTestSetup(flicker)
+
+            for (x in 0 until flicker.repetitions) {
+                iteration = x
+                runTransitionSetup(flicker)
+                runTransition(flicker)
+                runTransitionTeardown(flicker)
+                processRunTraces(flicker, runs, RunStatus.ASSERTION_SUCCESS)
             }
-        } catch (e: Throwable) {
-            executionError = e
+
+            runTestTeardown(flicker)
         }
 
         runs.addAll(tagsResults)
-        val result = FlickerResult(runs.toList(), tags.toSet(), executionError)
+        val result = FlickerResult(runs.toList(), tags.toSet(), executionErrors)
         cleanUp()
         return result
     }
 
-    private fun saveResult(flicker: Flicker, iteration: Int): List<FlickerRunResult> {
-        val resultBuilder = FlickerRunResult.Builder(iteration)
-        flicker.traceMonitors.forEach {
-            it.save(flicker.testName, iteration, resultBuilder)
+    private fun safeExecution(
+        flicker: Flicker,
+        runs: MutableList<FlickerRunResult>,
+        executionErrors: MutableList<Throwable>,
+        execution: () -> Unit
+    ) {
+        try {
+            execution()
+        } catch (e: TestSetupFailure) {
+            // If we failure on the test setup we can't run any of the transitions
+            executionErrors.add(e)
+        } catch (e: TransitionSetupFailure) {
+            // If we fail on the transition run setup then we don't want to run any further
+            // transitions nor save any results for this run. We simply want to run the test
+            // teardown.
+            executionErrors.add(e)
+            safeExecution(flicker, runs, executionErrors) {
+                runTestTeardown(flicker)
+            }
+        } catch (e: TransitionExecutionFailure) {
+            // If a transition fails to run we don't want to run the following iterations as the
+            // device is likely in an unexpected state which would lead to further errors. We simply
+            // want to run the test teardown
+            executionErrors.add(e)
+            flicker.traceMonitors.forEach { it.tryStop() }
+            safeExecution(flicker, runs, executionErrors) {
+                processRunTraces(flicker, runs, RunStatus.RUN_FAILED)
+                runTestTeardown(flicker)
+            }
+        } catch (e: TransitionTeardownFailure) {
+            // If a transition teardown fails to run we don't want to run the following iterations
+            // as the device is likely in an unexpected state which would lead to further errors.
+            // But, we do want to run the test teardown.
+            executionErrors.add(e)
+            flicker.traceMonitors.forEach { it.tryStop() }
+            safeExecution(flicker, runs, executionErrors) {
+                processRunTraces(flicker, runs, RunStatus.RUN_FAILED)
+                runTestTeardown(flicker)
+            }
+        } catch (e: TraceProcessingFailure) {
+            // If we fail to process the run traces we still want to run the teardowns and report
+            // the execution error.
+            executionErrors.add(e)
+            safeExecution(flicker, runs, executionErrors) {
+                runTransitionTeardown(flicker)
+                runTestTeardown(flicker)
+            }
+        } catch (e: TestTeardownFailure) {
+            // If we fail in the execution of the test teardown there is nothing else to do apart
+            // from reporting the execution error.
+            executionErrors.add(e)
+            for (run in runs) {
+                run.setRunFailed()
+            }
+        }
+    }
+
+    /**
+     * Parses the traces collected by the monitors to generate FlickerRunResults containing the
+     * parsed trace and information about the status of the run.
+     * The run results are added to the runs list which is then used to run Flicker assertions on.
+     */
+    @Throws(TraceProcessingFailure::class)
+    private fun processRunTraces(
+        flicker: Flicker,
+        runs: MutableList<FlickerRunResult>,
+        status: RunStatus
+    ) {
+        try {
+            val runResults = buildRunResults(flicker, iteration, status)
+            runs.addAll(runResults)
+        } catch (e: Throwable) {
+            // We have failed to add the results to the runs, so we can effectively consider these
+            // results as "lost" as they won't be used from now forth. So we can safely rename
+            // to file to indicate the failure and make it easier to find in the archives.
+            flicker.traceMonitors.forEach {
+                // All monitors that generate files we want to keep in the archives should implement
+                // IFileGeneratingMonitor
+                if (it is IFileGeneratingMonitor) {
+                    Utils.addStatusToFileName(it.outputFile, RunStatus.PARSING_FAILURE)
+                }
+            }
+            throw TraceProcessingFailure(e)
         }
 
-        return resultBuilder.buildAll()
+        // Update the status of all the tags created in this iteration and add them to runs
+        for (result in tagsResults) {
+            result.status = status
+            runs.add(result)
+        }
+        tagsResults.clear()
+    }
+
+    @Throws(TestSetupFailure::class)
+    private fun runTestSetup(flicker: Flicker) {
+        try {
+            flicker.testSetup.forEach { it.invoke(flicker) }
+        } catch (e: Throwable) {
+            throw TestSetupFailure(e)
+        }
+    }
+
+    @Throws(TestTeardownFailure::class)
+    private fun runTestTeardown(flicker: Flicker) {
+        try {
+            flicker.testTeardown.forEach { it.invoke(flicker) }
+        } catch (e: Throwable) {
+            throw TestTeardownFailure(e)
+        }
+    }
+
+    @Throws(TransitionSetupFailure::class)
+    private fun runTransitionSetup(flicker: Flicker) {
+        try {
+            flicker.runSetup.forEach { it.invoke(flicker) }
+            flicker.wmHelper.waitFor(UI_STABLE_CONDITIONS)
+        } catch (e: Throwable) {
+            throw TransitionSetupFailure(e)
+        }
+    }
+
+    @Throws(TransitionExecutionFailure::class)
+    private fun runTransition(flicker: Flicker) {
+        try {
+            flicker.traceMonitors.forEach { it.start() }
+            flicker.transitions.forEach { it.invoke(flicker) }
+        } catch (e: Throwable) {
+            throw TransitionExecutionFailure(e)
+        }
+    }
+
+    @Throws(TransitionTeardownFailure::class)
+    private fun runTransitionTeardown(flicker: Flicker) {
+        try {
+            flicker.wmHelper.waitFor(UI_STABLE_CONDITIONS)
+            flicker.traceMonitors.forEach { it.tryStop() }
+            flicker.runTeardown.forEach { it.invoke(flicker) }
+        } catch (e: Throwable) {
+            throw TransitionTeardownFailure(e)
+        }
+    }
+
+    private fun buildRunResults(
+        flicker: Flicker,
+        iteration: Int,
+        status: RunStatus
+    ): List<FlickerRunResult> {
+        val resultBuilder = FlickerRunResult.Builder()
+        flicker.traceMonitors.forEach {
+            resultBuilder.setResultFrom(it)
+        }
+
+        return resultBuilder.buildAll(flicker.testName, iteration, status)
     }
 
     private fun ITransitionMonitor.tryStop() {
@@ -178,18 +304,39 @@
                 getTaggedFilePath(flicker, tag, "layers_trace"))
             Files.write(layersTraceFile, deviceStateBytes.second)
 
-            val builder = FlickerRunResult.Builder(iteration)
-            builder.wmTraceFile = wmTraceFile
-            builder.layersTraceFile = layersTraceFile
-
+            val builder = FlickerRunResult.Builder()
             val result = builder.buildStateResult(
                 tag,
                 deviceState.wmState?.asTrace(),
-                deviceState.layerState?.asTrace()
+                deviceState.layerState?.asTrace(),
+                wmTraceFile,
+                layersTraceFile,
+                flicker.testName,
+                iteration,
+                // Undefined until it is updated in processRunTraces
+                RunStatus.UNDEFINED
             )
             tagsResults.add(result)
         } catch (e: IOException) {
             throw RuntimeException("Unable to create trace file: ${e.message}", e)
         }
     }
-}
\ No newline at end of file
+
+    companion object {
+        /**
+         * Conditions that determine when the UI is in a stable stable and no windows or layers are
+         * animating or changing state.
+         */
+        private val UI_STABLE_CONDITIONS = ConditionList(listOf(
+                WindowManagerConditionsFactory.isWMStateComplete(),
+                WindowManagerConditionsFactory.hasLayersAnimating().negate()
+        ))
+
+        class TestSetupFailure(val e: Throwable) : Throwable(e)
+        class TransitionSetupFailure(val e: Throwable) : Throwable(e)
+        class TransitionExecutionFailure(val e: Throwable) : Throwable(e)
+        class TraceProcessingFailure(val e: Throwable) : Throwable(e)
+        class TransitionTeardownFailure(val e: Throwable) : Throwable(e)
+        class TestTeardownFailure(val e: Throwable) : Throwable(e)
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/TransitionRunnerCached.kt b/libraries/flicker/src/com/android/server/wm/flicker/TransitionRunnerCached.kt
deleted file mode 100644
index 6cab2d1..0000000
--- a/libraries/flicker/src/com/android/server/wm/flicker/TransitionRunnerCached.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2021 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
-
-/**
- * Execute the transitions of a flicker test and caches the results.
- *
- * Return cached results instead of re-executing the transitions if possible.
- *
- * @param runner Actual runner to execute the test
- */
-class TransitionRunnerCached @JvmOverloads constructor(
-    private val runner: TransitionRunner = TransitionRunner()
-) : TransitionRunner() {
-    private var result: FlickerResult? = null
-
-    /**
-     * {@inheritDoc}
-     */
-    override fun run(flicker: Flicker): FlickerResult {
-        if (result?.isEmpty() != false) {
-            result = runner.run(flicker)
-        }
-
-        return result ?: error("Result should not be empty")
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    override fun createTag(flicker: Flicker, tag: String) {
-        runner.createTag(flicker, tag)
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    override fun cleanUp() {
-        super.cleanUp()
-        result?.cleanUp()
-        result = null
-    }
-}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/TransitionRunnerWithRules.kt b/libraries/flicker/src/com/android/server/wm/flicker/TransitionRunnerWithRules.kt
index 9919af5..1d27c18 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/TransitionRunnerWithRules.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/TransitionRunnerWithRules.kt
@@ -16,14 +16,7 @@
 
 package com.android.server.wm.flicker
 
-import android.platform.test.rule.NavigationModeRule
-import android.platform.test.rule.PressHomeRule
-import android.platform.test.rule.UnlockScreenRule
-import com.android.server.wm.flicker.helpers.SampleAppHelper
-import com.android.server.wm.flicker.rules.ChangeDisplayOrientationRule
-import com.android.server.wm.flicker.rules.LaunchAppRule
-import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
-import org.junit.rules.RuleChain
+import org.junit.rules.TestRule
 import org.junit.runner.Description
 import org.junit.runners.model.Statement
 
@@ -32,31 +25,9 @@
  *
  * Allow for easier reuse of test rules
  */
-class TransitionRunnerWithRules(private val testConfig: Map<String, Any?>) : TransitionRunner() {
+class TransitionRunnerWithRules(private val setupRules: TestRule) : TransitionRunner() {
     private var result: FlickerResult? = null
 
-    /**
-     * Create the default flicker test setup rules. In order:
-     *   - unlock device
-     *   - change orientation
-     *   - change navigation mode
-     *   - launch an app
-     *   - remove all apps
-     *   - go to home screen
-     *
-     * (b/186740751) An app should be launched because, after changing the navigation mode,
-     * the first app launch is handled as a screen size change (similar to a rotation), this
-     * causes different problems during testing (e.g. IME now shown on app launch)
-     */
-    private fun buildDefaultSetupRules(flicker: Flicker): RuleChain {
-        return RuleChain.outerRule(UnlockScreenRule())
-            .around(NavigationModeRule(testConfig.navBarMode))
-            .around(LaunchAppRule(SampleAppHelper(flicker.instrumentation)))
-            .around(RemoveAllTasksButHomeRule())
-            .around(ChangeDisplayOrientationRule(testConfig.startRotation))
-            .around(PressHomeRule())
-    }
-
     private fun buildTransitionRule(flicker: Flicker): Statement {
         return object : Statement() {
                 override fun evaluate() {
@@ -70,7 +41,6 @@
     }
 
     private fun buildTransitionChain(flicker: Flicker): Statement {
-        val setupRules = buildDefaultSetupRules(flicker)
         val transitionRule = buildTransitionRule(flicker)
         return setupRules.apply(transitionRule, Description.EMPTY)
     }
@@ -94,4 +64,4 @@
             cleanUp()
         }
     }
-}
\ No newline at end of file
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/Utils.kt b/libraries/flicker/src/com/android/server/wm/flicker/Utils.kt
new file mode 100644
index 0000000..280f454
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/Utils.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 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.compatibility.common.util.SystemUtil
+import com.android.server.wm.flicker.FlickerRunResult.Companion.RunStatus
+import java.nio.file.Path
+
+object Utils {
+    fun renameFile(src: Path, dst: Path) {
+        SystemUtil.runShellCommand("mv $src $dst")
+    }
+
+    fun copyFile(src: Path, dst: Path) {
+        SystemUtil.runShellCommand("cp $src $dst")
+        SystemUtil.runShellCommand("chmod a+r $dst")
+    }
+
+    fun moveFile(src: Path, dst: Path) {
+        // Move the  file to the output directory
+        // Note: Due to b/141386109, certain devices do not allow moving the files between
+        //       directories with different encryption policies, so manually copy and then
+        //       remove the original file
+        //       Moreover, the copied trace file may end up with different permissions, resulting
+        //       in b/162072200, to prevent this, ensure the files are readable after copying
+        copyFile(src, dst)
+        SystemUtil.runShellCommand("rm $src")
+    }
+
+    fun addStatusToFileName(traceFile: Path, status: RunStatus) {
+        val newFileName = "${status.prefix}_${traceFile.fileName}"
+        val dst = traceFile.resolveSibling(newFileName)
+        renameFile(traceFile, dst)
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/assertions/AssertionsChecker.kt b/libraries/flicker/src/com/android/server/wm/flicker/assertions/AssertionsChecker.kt
index a714edc..50654c7 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/assertions/AssertionsChecker.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/assertions/AssertionsChecker.kt
@@ -33,6 +33,16 @@
     private val assertions = mutableListOf<CompoundAssertion<T>>()
     private var skipUntilFirstAssertion = false
 
+    /**
+     * Empty the list of assertions.
+     */
+    internal fun clear() {
+        assertions.clear()
+    }
+
+    /**
+     * Add [assertion] to a new [CompoundAssertion] block.
+     */
     fun add(name: String, isOptional: Boolean = false, assertion: Assertion<T>) {
         assertions.add(CompoundAssertion(assertion, name, isOptional))
     }
@@ -88,7 +98,7 @@
                 lastPassedAssertionIndex = assertionIndex
                 entryIndex++
             } catch (e: Throwable) {
-                // ignore errors are the start of the trace
+                // ignore errors at the start of the trace
                 val ignoreFailure = skipUntilFirstAssertion && lastPassedAssertionIndex == -1
                 if (ignoreFailure) {
                     entryIndex++
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/assertions/FlickerAssertionError.kt b/libraries/flicker/src/com/android/server/wm/flicker/assertions/FlickerAssertionError.kt
index 7947a6f..1dceb87 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/assertions/FlickerAssertionError.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/assertions/FlickerAssertionError.kt
@@ -16,11 +16,11 @@
 
 package com.android.server.wm.flicker.assertions
 
-import java.nio.file.Path
+import com.android.server.wm.flicker.TraceFile
 import kotlin.AssertionError
 
 class FlickerAssertionError(
     message: String,
     cause: Throwable?,
-    val traceFiles: List<Path>
+    val traceFile: TraceFile?
 ) : AssertionError(message, cause)
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/assertions/FlickerAssertionErrorBuilder.kt b/libraries/flicker/src/com/android/server/wm/flicker/assertions/FlickerAssertionErrorBuilder.kt
index 74f32da..2768706 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/assertions/FlickerAssertionErrorBuilder.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/assertions/FlickerAssertionErrorBuilder.kt
@@ -16,24 +16,24 @@
 
 package com.android.server.wm.flicker.assertions
 
+import com.android.server.wm.flicker.TraceFile
 import com.android.server.wm.flicker.dsl.AssertionTag
 import com.android.server.wm.flicker.traces.FlickerSubjectException
 import com.google.common.truth.Fact
 import java.io.ByteArrayOutputStream
 import java.io.PrintStream
-import java.nio.file.Path
 
 class FlickerAssertionErrorBuilder {
     private var error: Throwable? = null
-    private var traceFiles: List<Path> = emptyList()
+    private var traceFile: TraceFile? = null
     private var tag = ""
 
     fun fromError(error: Throwable): FlickerAssertionErrorBuilder = apply {
         this.error = error
     }
 
-    fun withTrace(traceFiles: List<Path>): FlickerAssertionErrorBuilder = apply {
-        this.traceFiles = traceFiles
+    fun withTrace(traceFile: TraceFile?): FlickerAssertionErrorBuilder = apply {
+        this.traceFile = traceFile
     }
 
     fun atTag(tag: String): FlickerAssertionErrorBuilder = apply {
@@ -46,37 +46,36 @@
     }
 
     fun build(): FlickerAssertionError {
-        return FlickerAssertionError(errorMessage, rootCause, traceFiles)
+        return FlickerAssertionError(errorMessage, rootCause, traceFile)
     }
 
     private val errorMessage get() = buildString {
         val error = error
         requireNotNull(error)
         if (error is FlickerSubjectException) {
-            appendln(error.errorType)
-            appendln()
+            appendLine(error.errorType)
+            appendLine()
             append(error.errorDescription)
-            appendln()
+            appendLine()
             append(error.subjectInformation)
-            append("\t").appendln(Fact.fact("Location", tag))
-            appendln()
+            append("\t").appendLine(Fact.fact("Location", tag))
+            appendLine()
         } else {
-            appendln(error.message)
+            appendLine(error.message)
         }
-        appendln("Trace files:")
-        append(traceFileMessage)
-        appendln()
-        appendln("Cause:")
+        append("Trace file:").append(traceFileMessage)
+        appendLine()
+        appendLine("Cause:")
         append(rootCauseStackTrace)
-        appendln()
-        appendln("Full stacktrace:")
-        appendln()
+        appendLine()
+        appendLine("Full stacktrace:")
+        appendLine()
     }
 
     private val traceFileMessage get() = buildString {
-        traceFiles.forEach {
+        traceFile?.traceFile?.let {
             append("\t")
-            appendln(it)
+            append(it)
         }
     }
 
@@ -104,4 +103,4 @@
         }
         return childCause
     }
-}
\ No newline at end of file
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/assertions/FlickerSubject.kt b/libraries/flicker/src/com/android/server/wm/flicker/assertions/FlickerSubject.kt
index c54bc9d..2f3cfea 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/assertions/FlickerSubject.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/assertions/FlickerSubject.kt
@@ -30,7 +30,6 @@
     protected val fm: FailureMetadata,
     data: Any?
 ) : Subject(fm, data) {
-    abstract fun clone(): FlickerSubject
     @VisibleForTesting
     abstract val timestamp: Long
     protected abstract val parent: FlickerSubject?
@@ -114,4 +113,4 @@
         @JvmStatic
         val ASSERTION_TAG = "Assertion"
     }
-}
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/dsl/FlickerBuilder.kt b/libraries/flicker/src/com/android/server/wm/flicker/dsl/FlickerBuilder.kt
index 4330cfe..ae70090 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/dsl/FlickerBuilder.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/dsl/FlickerBuilder.kt
@@ -23,15 +23,14 @@
 import com.android.server.wm.flicker.Flicker
 import com.android.server.wm.flicker.FlickerDslMarker
 import com.android.server.wm.flicker.TransitionRunner
-import com.android.server.wm.flicker.monitor.EventLogMonitor
 import com.android.server.wm.flicker.getDefaultFlickerOutputDir
+import com.android.server.wm.flicker.monitor.EventLogMonitor
 import com.android.server.wm.flicker.monitor.ITransitionMonitor
 import com.android.server.wm.flicker.monitor.LayersTraceMonitor
 import com.android.server.wm.flicker.monitor.ScreenRecorder
-import com.android.server.wm.flicker.monitor.WindowAnimationFrameStatsMonitor
 import com.android.server.wm.flicker.monitor.WindowManagerTraceMonitor
 import com.android.server.wm.traces.common.layers.LayersTrace
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
 import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
@@ -42,9 +41,8 @@
  */
 @FlickerDslMarker
 class FlickerBuilder private constructor(
-    private val instrumentation: Instrumentation,
+    internal val instrumentation: Instrumentation,
     private val launcherStrategy: ILauncherStrategy,
-    private val includeJankyRuns: Boolean,
     private val outputDir: Path,
     private val wmHelper: WindowManagerStateHelper,
     private var testName: String,
@@ -55,16 +53,10 @@
     val device: UiDevice,
     private val traceMonitors: MutableList<ITransitionMonitor>
 ) {
-    private val frameStatsMonitor: WindowAnimationFrameStatsMonitor? = if (includeJankyRuns) {
-        null
-    } else {
-        WindowAnimationFrameStatsMonitor(instrumentation)
-    }
-
-    @JvmOverloads
     /**
      * Default flicker builder constructor
      */
+    @JvmOverloads
     constructor(
         /**
          * Instrumentation to run the tests
@@ -74,11 +66,7 @@
          * Strategy used to interact with the launcher
          */
         launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
-                .getInstance(instrumentation).launcherStrategy,
-        /**
-         * Include or discard janky runs
-         */
-        includeJankyRuns: Boolean = true,
+            .getInstance(instrumentation).launcherStrategy,
         /**
          * Output directory for the test results
          */
@@ -86,11 +74,17 @@
         /**
          * Helper object for WM Synchronization
          */
-        wmHelper: WindowManagerStateHelper = WindowManagerStateHelper(instrumentation)
+        wmHelper: WindowManagerStateHelper = WindowManagerStateHelper(instrumentation),
+        traceMonitors: MutableList<ITransitionMonitor> = mutableListOf<ITransitionMonitor>()
+                .also {
+                    it.add(WindowManagerTraceMonitor(outputDir))
+                    it.add(LayersTraceMonitor(outputDir))
+                    it.add(ScreenRecorder(instrumentation.targetContext, outputDir))
+                    it.add(EventLogMonitor())
+                }
     ) : this(
         instrumentation,
         launcherStrategy,
-        includeJankyRuns,
         outputDir,
         wmHelper,
         testName = "",
@@ -99,22 +93,15 @@
         teardownCommands = TestCommandsBuilder(),
         transitionCommands = mutableListOf(),
         device = UiDevice.getInstance(instrumentation),
-        traceMonitors = mutableListOf<ITransitionMonitor>()
-            .also {
-                it.add(WindowManagerTraceMonitor(outputDir))
-                it.add(LayersTraceMonitor(outputDir))
-                it.add(ScreenRecorder(outputDir, instrumentation.targetContext))
-                it.add(EventLogMonitor())
-            }
+        traceMonitors = traceMonitors
     )
 
     /**
      * Copy constructor
      */
-    constructor(otherBuilder: FlickerBuilder): this(
+    constructor(otherBuilder: FlickerBuilder) : this(
         otherBuilder.instrumentation,
         otherBuilder.launcherStrategy,
-        otherBuilder.includeJankyRuns,
         otherBuilder.outputDir.toAbsolutePath(),
         otherBuilder.wmHelper,
         otherBuilder.testName,
@@ -131,7 +118,7 @@
      *
      * If reused throughout the test, only the last value is stored
      */
-    fun withTestName(testName: () -> String) {
+    fun withTestName(testName: () -> String): FlickerBuilder = apply {
         val name = testName()
         require(!name.contains(" ")) {
             "The test tag can not contain spaces since it is a part of the file name"
@@ -142,7 +129,7 @@
     /**
      * Disable [WindowManagerTraceMonitor].
      */
-    fun withoutWindowManagerTracing() {
+    fun withoutWindowManagerTracing(): FlickerBuilder = apply {
         withWindowManagerTracing { null }
     }
 
@@ -154,7 +141,9 @@
      * If this tracing is disabled, the assertions for [WindowManagerTrace] and
      * [WindowManagerState] will not be executed
      */
-    fun withWindowManagerTracing(traceMonitor: (Path) -> WindowManagerTraceMonitor?) {
+    fun withWindowManagerTracing(
+        traceMonitor: (Path) -> WindowManagerTraceMonitor?
+    ): FlickerBuilder = apply {
         traceMonitors.removeIf { it is WindowManagerTraceMonitor }
         val newMonitor = traceMonitor(outputDir)
 
@@ -166,7 +155,7 @@
     /**
      * Disable [LayersTraceMonitor].
      */
-    fun withoutLayerTracing() {
+    fun withoutLayerTracing(): FlickerBuilder = apply {
         withLayerTracing { null }
     }
 
@@ -175,10 +164,12 @@
      *
      * By default the tracing is always active. To disable tracing return null
      *
-     * If this tracing is disabled, the assertions for [LayersTrace] and [LayerTraceEntry]
+     * If this tracing is disabled, the assertions for [LayersTrace] and [BaseLayerTraceEntry]
      * will not be executed
      */
-    fun withLayerTracing(traceMonitor: (Path) -> LayersTraceMonitor?) {
+    fun withLayerTracing(
+        traceMonitor: (Path) -> LayersTraceMonitor?
+    ): FlickerBuilder = apply {
         traceMonitors.removeIf { it is LayersTraceMonitor }
         val newMonitor = traceMonitor(outputDir)
 
@@ -192,7 +183,9 @@
      *
      * By default the tracing is always active. To disable tracing return null
      */
-    fun withScreenRecorder(screenRecorder: (Path) -> ScreenRecorder?) {
+    fun withScreenRecorder(
+        screenRecorder: (Path) -> ScreenRecorder?
+    ): FlickerBuilder = apply {
         traceMonitors.removeIf { it is ScreenRecorder }
         val newMonitor = screenRecorder(outputDir)
 
@@ -204,7 +197,7 @@
     /**
      * Defines how many times the test run should be repeated
      */
-    fun repeat(predicate: () -> Int) {
+    fun repeat(predicate: () -> Int): FlickerBuilder = apply {
         val repeat = predicate()
         require(repeat >= 1) { "Number of repetitions should be greater or equal to 1" }
         iterations = repeat
@@ -214,7 +207,7 @@
      * Defines the test ([TestCommandsBuilder.testCommands]) and run ([TestCommandsBuilder.runCommands])
      * commands executed before the [transitions] to test
      */
-    fun setup(commands: TestCommandsBuilder.() -> Unit) {
+    fun setup(commands: TestCommandsBuilder.() -> Unit): FlickerBuilder = apply {
         setupCommands.apply { commands() }
     }
 
@@ -222,14 +215,14 @@
      * Defines the test ([TestCommandsBuilder.testCommands]) and run ([TestCommandsBuilder.runCommands])
      * commands executed after the [transitions] to test
      */
-    fun teardown(commands: TestCommandsBuilder.() -> Unit) {
+    fun teardown(commands: TestCommandsBuilder.() -> Unit): FlickerBuilder = apply {
         teardownCommands.apply { commands() }
     }
 
     /**
      * Defines the commands that trigger the behavior to test
      */
-    fun transitions(command: Flicker.() -> Unit) {
+    fun transitions(command: Flicker.() -> Unit): FlickerBuilder = apply {
         transitionCommands.add(command)
     }
 
@@ -244,7 +237,6 @@
         outputDir,
         testName,
         iterations,
-        frameStatsMonitor,
         traceMonitors,
         setupCommands.buildTestCommands(),
         setupCommands.buildRunCommands(),
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/helpers/AutomationUtils.kt b/libraries/flicker/src/com/android/server/wm/flicker/helpers/AutomationUtils.kt
index 9113b2d..bcaa9e7 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/helpers/AutomationUtils.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/helpers/AutomationUtils.kt
@@ -37,7 +37,9 @@
 import com.android.compatibility.common.util.SystemUtil
 import com.android.server.wm.flicker.helpers.WindowUtils.displayBounds
 import com.android.server.wm.flicker.helpers.WindowUtils.estimateNavigationBarPosition
+import com.android.server.wm.traces.common.FlickerComponentName
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory
+import com.android.server.wm.traces.parser.toAndroidRect
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.Assert
 import org.junit.Assert.assertNotNull
@@ -94,7 +96,11 @@
     val y = this.displayHeight / 2
     this.click(x, y)
 
-    wmHelper.waitForAppTransitionIdle()
+    wmHelper.waitFor(
+        WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
+        WindowManagerConditionsFactory.isLayerVisible(FlickerComponentName.SNAPSHOT).negate(),
+        WindowManagerConditionsFactory.isLayerVisible(FlickerComponentName.SPLASH_SCREEN).negate()
+    )
 }
 
 /**
@@ -114,7 +120,7 @@
             navBar.visibleBounds
         } else {
             Log.e(TAG, "Could not find nav bar, infer location")
-            estimateNavigationBarPosition(Surface.ROTATION_0).bounds
+            estimateNavigationBarPosition(Surface.ROTATION_0).bounds.toAndroidRect()
         }
 
         val startX = navBarVisibleBounds.centerX()
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/helpers/ShellTransitionUtils.kt b/libraries/flicker/src/com/android/server/wm/flicker/helpers/ShellTransitionUtils.kt
new file mode 100644
index 0000000..d3cb1f9
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/helpers/ShellTransitionUtils.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+@file:JvmName("ShellTransitionUtils")
+
+package com.android.server.wm.flicker.helpers
+
+import android.os.SystemProperties
+
+val isShellTransitionsEnabled =
+    SystemProperties.getBoolean("persist.wm.debug.shell_transit", false)
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/helpers/StandardAppHelper.kt b/libraries/flicker/src/com/android/server/wm/flicker/helpers/StandardAppHelper.kt
index 20ebfde..7f1d203 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/helpers/StandardAppHelper.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/helpers/StandardAppHelper.kt
@@ -28,7 +28,11 @@
 import androidx.test.uiautomator.BySelector
 import androidx.test.uiautomator.UiDevice
 import androidx.test.uiautomator.Until
+import com.android.server.wm.traces.common.Condition
+import com.android.server.wm.traces.common.DeviceStateDump
 import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.WindowManagerConditionsFactory
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 
@@ -119,10 +123,13 @@
         wmHelper: WindowManagerStateHelper
     ) {
         val activityName = component.toActivityName()
-        wmHelper.waitFor("state of $activityName to be ${WindowManagerState.STATE_DESTROYED}") {
-            !it.wmState.containsActivity(activityName) ||
-                it.wmState.hasActivityState(activityName, WindowManagerState.STATE_DESTROYED)
-        }
+        val waitMsg = "state of $activityName to be ${WindowManagerState.STATE_DESTROYED}"
+        require(
+            wmHelper.waitFor(waitMsg) {
+                !it.wmState.containsActivity(activityName) ||
+                    it.wmState.hasActivityState(activityName, WindowManagerState.STATE_DESTROYED)
+            }
+        ) { "App activity should have been destroyed" }
         wmHelper.waitForAppTransitionIdle()
         // Ensure WindowManagerService wait until all animations have completed
         mInstrumentation.uiAutomation.syncInputTransactions()
@@ -166,18 +173,35 @@
         expectedWindowName: String = "",
         action: String? = null,
         stringExtras: Map<String, String> = mapOf()
+    ) = launchViaIntentAndWaitShown(wmHelper, expectedWindowName, action, stringExtras)
+
+    /**
+     * Launches the app through an intent instead of interacting with the launcher and waits
+     * until the app window is visible
+     */
+    protected fun launchViaIntentAndWaitShown(
+        wmHelper: WindowManagerStateHelper,
+        expectedWindowName: String = "",
+        action: String? = null,
+        stringExtras: Map<String, String> = mapOf(),
+        waitConditions: Array<
+            Condition<DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>>> =
+            emptyArray()
     ) {
         launchAppViaIntent(action, stringExtras)
 
-        val window = if (expectedWindowName.isNotEmpty()) {
-            expectedWindowName
+        val expectedWindow = if (expectedWindowName.isNotEmpty()) {
+            FlickerComponentName("", expectedWindowName)
         } else {
-            component.toWindowName()
+            component
         }
-        wmHelper.waitFor("App is shown") {
-            it.wmState.isComplete() && it.wmState.isWindowVisible(window) &&
-                !it.layerState.isAnimating()
-        }
+        val appShown = wmHelper.waitFor(
+            WindowManagerConditionsFactory.isWMStateComplete(),
+            WindowManagerConditionsFactory.hasLayersAnimating().negate(),
+            WindowManagerConditionsFactory.isWindowVisible(expectedWindow),
+            *waitConditions
+        )
+        require(appShown) { "App didn't launch correctly via intent" }
 
         wmHelper.waitForAppTransitionIdle()
         // During seamless rotation the app window is shown
@@ -187,7 +211,7 @@
         }
 
         // Ensure WindowManagerService wait until all animations have completed
-        mInstrumentation.getUiAutomation().syncInputTransactions()
+        mInstrumentation.uiAutomation.syncInputTransactions()
     }
 
     companion object {
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/helpers/WindowUtils.kt b/libraries/flicker/src/com/android/server/wm/flicker/helpers/WindowUtils.kt
index f40ca50..4c3f490 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/helpers/WindowUtils.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/helpers/WindowUtils.kt
@@ -19,11 +19,11 @@
 import android.content.Context
 import android.graphics.Point
 import android.graphics.Rect
-import android.graphics.Region
 import android.view.Surface
 import android.view.WindowManager
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.traces.common.layers.Display
+import com.android.server.wm.traces.common.region.Region
 
 fun Int.isRotated() = this == Surface.ROTATION_90 || this == Surface.ROTATION_270
 
@@ -69,9 +69,9 @@
         // if the current orientation changes with the requested rotation,
         // flip height and width of display bounds.
         return if (displayIsRotated != requestedDisplayIsRotated) {
-            Region(0, 0, displayBounds.height(), displayBounds.width())
+            Region.from(0, 0, displayBounds.height(), displayBounds.width())
         } else {
-            Region(0, 0, displayBounds.width(), displayBounds.height())
+            Region.from(0, 0, displayBounds.width(), displayBounds.height())
         }
     }
 
@@ -88,7 +88,7 @@
         }
         val resourceId = resources.getIdentifier(resourceName, "dimen", "android")
         val height = resources.getDimensionPixelSize(resourceId)
-        return Region(0, 0, display.layerStackSpace.width, height)
+        return Region.from(0, 0, display.layerStackSpace.width, height)
     }
 
     /**
@@ -98,7 +98,7 @@
      */
     fun getNavigationBarPosition(display: Display): Region {
         val navBarWidth = getDimensionPixelSize("navigation_bar_width")
-        val navBarHeight = navigationBarHeight
+        val navBarHeight = navigationBarFrameHeight
         val displayHeight = display.layerStackSpace.height
         val displayWidth = display.layerStackSpace.width
         val requestedRotation = display.transform.getRotation()
@@ -107,13 +107,13 @@
             // nav bar is at the bottom of the screen
             requestedRotation in listOf(Surface.ROTATION_0, Surface.ROTATION_180) ||
                     isGesturalNavigationEnabled ->
-                Region(0, displayHeight - navBarHeight, displayWidth, displayHeight)
+                Region.from(0, displayHeight - navBarHeight, displayWidth, displayHeight)
             // nav bar is at the right side
             requestedRotation == Surface.ROTATION_90 ->
-                Region(displayWidth - navBarWidth, 0, displayWidth, displayHeight)
+                Region.from(displayWidth - navBarWidth, 0, displayWidth, displayHeight)
             // nav bar is at the left side
             requestedRotation == Surface.ROTATION_270 ->
-                Region(0, 0, navBarWidth, displayHeight)
+                Region.from(0, 0, navBarWidth, displayHeight)
             else -> error("Unknown rotation $requestedRotation")
         }
     }
@@ -136,19 +136,19 @@
             displayHeight = displayBounds.width()
         }
         val navBarWidth = getDimensionPixelSize("navigation_bar_width")
-        val navBarHeight = navigationBarHeight
+        val navBarHeight = navigationBarFrameHeight
 
         return when {
             // nav bar is at the bottom of the screen
             requestedRotation in listOf(Surface.ROTATION_0, Surface.ROTATION_180) ||
                 isGesturalNavigationEnabled ->
-                Region(0, displayHeight - navBarHeight, displayWidth, displayHeight)
+                Region.from(0, displayHeight - navBarHeight, displayWidth, displayHeight)
             // nav bar is at the right side
             requestedRotation == Surface.ROTATION_90 ->
-                Region(displayWidth - navBarWidth, 0, displayWidth, displayHeight)
+                Region.from(displayWidth - navBarWidth, 0, displayWidth, displayHeight)
             // nav bar is at the left side
             requestedRotation == Surface.ROTATION_270 ->
-                Region(0, 0, navBarWidth, displayHeight)
+                Region.from(0, 0, navBarWidth, displayHeight)
             else -> error("Unknown rotation $requestedRotation")
         }
     }
@@ -169,15 +169,11 @@
     }
 
     /**
-     * Gets the navigation bar height
+     * Gets the navigation bar frame height
      */
-    val navigationBarHeight: Int
+    val navigationBarFrameHeight: Int
         get() {
-            var navBarHeight = getDimensionPixelSize("navigation_bar_height")
-            if (isGesturalNavigationEnabled) {
-                navBarHeight += getDimensionPixelSize("navigation_bar_gesture_height")
-            }
-            return navBarHeight
+            return getDimensionPixelSize("navigation_bar_frame_height")
         }
 
     /**
@@ -189,4 +185,4 @@
                     .getIdentifier("docked_stack_divider_insets", "dimen", "android")
             return resources.getDimensionPixelSize(resourceId)
         }
-}
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/EventLogMonitor.kt b/libraries/flicker/src/com/android/server/wm/flicker/monitor/EventLogMonitor.kt
index c1df425..ac639be 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/monitor/EventLogMonitor.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/monitor/EventLogMonitor.kt
@@ -27,7 +27,7 @@
 /**
  * Collects event logs during transitions.
  */
-open class EventLogMonitor : ITransitionMonitor {
+open class EventLogMonitor : ITransitionMonitor, FlickerRunResult.IResultSetter {
     private var _logs = listOf<Event>()
     private lateinit var _logSeparator: String
 
@@ -71,8 +71,12 @@
         _logs = getEventLogs(EVENT_LOG_INPUT_FOCUS_TAG)
     }
 
-    override fun save(testTag: String, flickerRunResultBuilder: FlickerRunResult.Builder) {
-        flickerRunResultBuilder.eventLog = _logs.mapNotNull { event ->
+    override fun setResult(builder: FlickerRunResult.Builder) {
+        builder.eventLog = buildProcessedEventLogs()
+    }
+
+    private fun buildProcessedEventLogs(): List<FocusEvent> {
+        return _logs.mapNotNull { event ->
             val timestamp = event.timeNanos
             val log = (event.data as Array<*>).map { it as String }
             if (log.size != 2) {
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/Extensions.kt b/libraries/flicker/src/com/android/server/wm/flicker/monitor/Extensions.kt
index 88075e1..789352d 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/monitor/Extensions.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/monitor/Extensions.kt
@@ -17,8 +17,8 @@
 @file:JvmName("Extensions")
 package com.android.server.wm.flicker.monitor
 
-import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace
 import com.android.server.wm.flicker.getDefaultFlickerOutputDir
+import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace
 import com.android.server.wm.traces.common.DeviceTraceDump
 import com.android.server.wm.traces.common.layers.LayersTrace
 import com.android.server.wm.traces.parser.DeviceDumpParser
@@ -59,7 +59,7 @@
     predicate: () -> Unit
 ): LayersTrace {
     return LayersTraceParser.parseFromTrace(
-        LayersTraceMonitor(outputDir, traceFlags).withTracing(predicate))
+        LayersTraceMonitor(outputDir, traceFlags = traceFlags).withTracing(predicate))
 }
 
 /**
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/IFileGeneratingMonitor.kt b/libraries/flicker/src/com/android/server/wm/flicker/monitor/IFileGeneratingMonitor.kt
new file mode 100644
index 0000000..f01f820
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/monitor/IFileGeneratingMonitor.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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.monitor
+
+import java.nio.file.Path
+
+interface IFileGeneratingMonitor {
+    val outputFile: Path
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.kt b/libraries/flicker/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.kt
index 45ffcda..984160e 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.kt
@@ -19,37 +19,10 @@
 import com.android.server.wm.flicker.FlickerRunResult
 
 /** Collects test artifacts during a UI transition.  */
-interface ITransitionMonitor {
+interface ITransitionMonitor : FlickerRunResult.IResultSetter {
     /** Starts monitor.  */
     fun start()
 
     /** Stops monitor.  */
     fun stop()
-
-    /**
-     * Saves any monitor artifacts to file adding `testTag` and `iteration` to the file
-     * name.
-     *
-     * @param testTag suffix added to artifact name
-     * @param iteration suffix added to artifact name
-     * @param flickerRunResultBuilder Flicker run results
-     * @return Path to saved artifact
-     */
-    fun save(testTag: String, iteration: Int, flickerRunResultBuilder: FlickerRunResult.Builder) =
-            save("${testTag}_$iteration", flickerRunResultBuilder)
-
-    /**
-     * Saves trace file to the external storage directory suffixing the name with the testtag and
-     * iteration.
-     *
-     *
-     * Moves the trace file from the default location via a shell command since the test app does
-     * not have security privileges to access /data/misc/wmtrace.
-     *
-     * @param testTag suffix added to trace name used to identify trace
-     * @param flickerRunResultBuilder Flicker run results
-     */
-    fun save(testTag: String, flickerRunResultBuilder: FlickerRunResult.Builder) {
-        throw UnsupportedOperationException("Save not implemented for this monitor")
-    }
 }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.kt b/libraries/flicker/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.kt
index b9f16cd..650e39e 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.kt
@@ -17,10 +17,15 @@
 package com.android.server.wm.flicker.monitor
 
 import android.os.RemoteException
+import android.util.Log
 import android.view.WindowManagerGlobal
+import com.android.server.wm.flicker.FLICKER_TAG
 import com.android.server.wm.flicker.FlickerRunResult
+import com.android.server.wm.flicker.getDefaultFlickerOutputDir
 import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
 import com.android.server.wm.traces.common.layers.LayersTrace
+import com.android.server.wm.traces.parser.layers.LayersTraceParser
+import java.nio.file.Files
 import java.nio.file.Path
 
 /**
@@ -28,16 +33,15 @@
  *
  * Use [LayersTraceSubject.assertThat] to make assertions on the trace
  */
-open class LayersTraceMonitor(
-    outputDir: Path,
-    private val traceFlags: Int
-) : TransitionMonitor(outputDir, "layers_trace$WINSCOPE_EXT") {
-
-    constructor(outputDir: Path) : this(outputDir, TRACE_FLAGS)
+open class LayersTraceMonitor @JvmOverloads constructor(
+    outputDir: Path = getDefaultFlickerOutputDir(),
+    sourceFile: Path = TRACE_DIR.resolve("layers_trace$WINSCOPE_EXT"),
+    private val traceFlags: Int = TRACE_FLAGS
+) : TransitionMonitor(outputDir, sourceFile) {
 
     private val windowManager = WindowManagerGlobal.getWindowManagerService()
 
-    override fun start() {
+    override fun startTracing() {
         try {
             windowManager.setLayerTracingFlags(traceFlags)
             windowManager.isLayerTracing = true
@@ -46,7 +50,7 @@
         }
     }
 
-    override fun stop() {
+    override fun stopTracing() {
         try {
             windowManager.isLayerTracing = false
         } catch (e: RemoteException) {
@@ -57,12 +61,15 @@
     override val isEnabled: Boolean
         get() = windowManager.isLayerTracing
 
-    override fun setResult(flickerRunResultBuilder: FlickerRunResult.Builder, traceFile: Path) {
-        flickerRunResultBuilder.layersTraceFile = traceFile
+    override fun setResult(builder: FlickerRunResult.Builder) {
+        builder.setLayersTrace(outputFile) {
+            Log.v(FLICKER_TAG, "Parsing Layers trace")
+            val traceData = Files.readAllBytes(outputFile)
+            val layersTrace = LayersTraceParser.parseFromTrace(traceData)
+            LayersTraceSubject.assertThat(layersTrace)
+        }
     }
 
-    override fun getTracePath(builder: FlickerRunResult.Builder) = builder.layersTraceFile
-
     companion object {
         const val TRACE_FLAGS = 0x47 // TRACE_CRITICAL|TRACE_INPUT|TRACE_COMPOSITION|TRACE_SYNC
     }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/ScreenRecorder.kt b/libraries/flicker/src/com/android/server/wm/flicker/monitor/ScreenRecorder.kt
index 9816aec..12ff42c 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/monitor/ScreenRecorder.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/monitor/ScreenRecorder.kt
@@ -29,19 +29,19 @@
 import android.view.WindowManager
 import com.android.server.wm.flicker.FLICKER_TAG
 import com.android.server.wm.flicker.FlickerRunResult
+import com.android.server.wm.flicker.getDefaultFlickerOutputDir
 import java.nio.file.Files
 import java.nio.file.Path
 
 /** Captures screen contents and saves it as a mp4 video file.  */
 open class ScreenRecorder @JvmOverloads constructor(
-    outputPath: Path,
-    context: Context,
+    private val context: Context,
+    outputDir: Path = getDefaultFlickerOutputDir(),
     private val maxDurationMs: Int = MAX_DURATION_MS,
     private val maxFileSizeBytes: Long = MAX_FILESIZE_BYTES,
     private val width: Int = 720,
-    private val height: Int = 1280,
-    traceFile: String = "transition.mp4"
-) : TraceMonitor(outputPath, outputPath.resolve(traceFile)) {
+    private val height: Int = 1280
+) : TraceMonitor(outputDir, getDefaultFlickerOutputDir().resolve("transition.mp4")) {
     private val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
     private val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
     private var mediaRecorder: MediaRecorder? = null
@@ -49,7 +49,7 @@
     private var virtualDisplay: VirtualDisplay? = null
     private fun prepare() {
         // Set up media recorder
-        val recorder = MediaRecorder()
+        val recorder = MediaRecorder(context)
 
         // Set up audio source
         recorder.setVideoSource(MediaRecorder.VideoSource.SURFACE)
@@ -70,7 +70,7 @@
         recorder.setVideoEncodingBitRate(vidBitRate)
         recorder.setMaxDuration(maxDurationMs)
         recorder.setMaxFileSize(maxFileSizeBytes)
-        recorder.setOutputFile(sourceTraceFilePath.toFile())
+        recorder.setOutputFile(sourceFile.toFile())
         recorder.prepare()
 
         // Create surface
@@ -88,36 +88,36 @@
         this.mediaRecorder = recorder
     }
 
-    override fun start() {
+    override fun startTracing() {
         if (mediaRecorder != null) {
             Log.i(FLICKER_TAG, "Screen recorder already running")
             return
         }
-        Files.deleteIfExists(sourceTraceFilePath)
-        require(!Files.exists(sourceTraceFilePath)) { "Could not delete old trace file" }
-        Files.createDirectories(sourceTraceFilePath.parent)
+        Files.deleteIfExists(sourceFile)
+        require(!Files.exists(sourceFile)) { "Could not delete old trace file" }
+        Files.createDirectories(sourceFile.parent)
 
         prepare()
-        Log.d(FLICKER_TAG, "Starting screen recording to file $sourceTraceFilePath")
+        Log.d(FLICKER_TAG, "Starting screen recording to file $sourceFile")
         mediaRecorder?.start()
 
         var remainingTime = WAIT_TIMEOUT_MS
         do {
             SystemClock.sleep(WAIT_INTERVAL_MS)
             remainingTime -= WAIT_INTERVAL_MS
-        } while (!Files.exists(sourceTraceFilePath) && remainingTime > 0)
+        } while (!Files.exists(sourceFile) && remainingTime > 0)
 
-        require(Files.exists(sourceTraceFilePath)) {
+        require(Files.exists(sourceFile)) {
             "Screen recorder didn't start" }
     }
 
-    override fun stop() {
+    override fun stopTracing() {
         if (mediaRecorder == null) {
             Log.i(FLICKER_TAG, "Screen recorder was not started")
             return
         }
 
-        Log.d(FLICKER_TAG, "Stopping screen recording. Storing result in $sourceTraceFilePath")
+        Log.d(FLICKER_TAG, "Stopping screen recording. Storing result in $sourceFile")
         try {
             mediaRecorder?.stop()
             mediaRecorder?.release()
@@ -133,12 +133,12 @@
     override val isEnabled: Boolean
         get() = mediaRecorder != null
 
-    override fun setResult(flickerRunResultBuilder: FlickerRunResult.Builder, traceFile: Path) {
-        flickerRunResultBuilder.screenRecording = traceFile
+    override fun setResult(builder: FlickerRunResult.Builder) {
+        builder.screenRecording = outputFile
     }
 
     override fun toString(): String {
-        return "ScreenRecorder($sourceTraceFilePath)"
+        return "ScreenRecorder($sourceFile)"
     }
 
     companion object {
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/TraceMonitor.kt b/libraries/flicker/src/com/android/server/wm/flicker/monitor/TraceMonitor.kt
index d941833..fd73030 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/monitor/TraceMonitor.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/monitor/TraceMonitor.kt
@@ -17,48 +17,48 @@
 package com.android.server.wm.flicker.monitor
 
 import androidx.annotation.VisibleForTesting
-import com.android.compatibility.common.util.SystemUtil
 import com.android.server.wm.flicker.FlickerRunResult
+import com.android.server.wm.flicker.Utils
 import java.nio.file.Files
 import java.nio.file.Path
+import java.nio.file.Paths
 
 /**
  * Base class for monitors containing common logic to read the trace as a byte array and save the
  * trace to another location.
  */
 abstract class TraceMonitor internal constructor(
-    @VisibleForTesting var outputPath: Path,
-    protected var sourceTraceFilePath: Path
-) : ITransitionMonitor {
+    outputDir: Path,
+    val sourceFile: Path
+) : ITransitionMonitor, FlickerRunResult.IResultSetter, IFileGeneratingMonitor {
+    @VisibleForTesting
+    override val outputFile: Path = outputDir.resolve(sourceFile.fileName)
     abstract val isEnabled: Boolean
 
-    abstract fun setResult(flickerRunResultBuilder: FlickerRunResult.Builder, traceFile: Path)
-
-    override fun save(testTag: String, flickerRunResultBuilder: FlickerRunResult.Builder) {
-        outputPath.toFile().mkdirs()
-        val savedTrace = outputPath.resolve("${testTag}_${sourceTraceFilePath.fileName}")
-        moveFile(sourceTraceFilePath, savedTrace)
-        require(Files.exists(savedTrace)) { "Unable to save trace file $savedTrace" }
-
-        setResult(flickerRunResultBuilder, savedTrace)
+    final override fun start() {
+        startTracing()
     }
 
-    fun save(testTag: String) {
-        outputPath.toFile().mkdirs()
-        val savedTrace = outputPath.resolve("${testTag}_${sourceTraceFilePath.fileName}")
-        moveFile(sourceTraceFilePath, savedTrace)
-        require(Files.exists(savedTrace)) { "Unable to save trace file $savedTrace" }
+    final override fun stop() {
+        stopTracing()
+        moveTraceFileToOutputDir()
     }
 
-    private fun moveFile(src: Path, dst: Path) {
-        // Move the  file to the output directory
-        // Note: Due to b/141386109, certain devices do not allow moving the files between
-        //       directories with different encryption policies, so manually copy and then
-        //       remove the original file
-        //       Moreover, the copied trace file may end up with different permissions, resulting
-        //       in b/162072200, to prevent this, ensure the files are readable after copying
-        SystemUtil.runShellCommand("cp $src $dst")
-        SystemUtil.runShellCommand("chmod a+r $dst")
-        SystemUtil.runShellCommand("rm $src")
+    abstract fun startTracing()
+    abstract fun stopTracing()
+
+    internal fun moveTraceFileToOutputDir(): Path {
+        Files.createDirectories(outputFile.parent)
+        if (sourceFile != outputFile) {
+            Utils.moveFile(sourceFile, outputFile)
+        }
+        require(Files.exists(outputFile)) { "Unable to save trace file $outputFile" }
+        return outputFile
+    }
+
+    companion object {
+        @JvmStatic
+        protected val TRACE_DIR = Paths.get("/data/misc/wmtrace/")
+        internal const val WINSCOPE_EXT = ".winscope"
     }
 }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/TransitionMonitor.kt b/libraries/flicker/src/com/android/server/wm/flicker/monitor/TransitionMonitor.kt
index 3df8007..bc3589c 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/monitor/TransitionMonitor.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/monitor/TransitionMonitor.kt
@@ -19,20 +19,11 @@
 import com.android.server.wm.flicker.FlickerRunResult
 import java.nio.file.Files
 import java.nio.file.Path
-import java.nio.file.Paths
 
 abstract class TransitionMonitor(
-    outputPath: Path,
-    sourceTraceFilePath: Path
-) : TraceMonitor(outputPath, sourceTraceFilePath) {
-
-    internal constructor(
-        outputDir: Path,
-        traceFileName: String
-    ) : this(outputDir, TRACE_DIR.resolve(traceFileName))
-
-    protected abstract fun getTracePath(builder: FlickerRunResult.Builder): Path?
-
+    outputDir: Path,
+    sourceFile: Path
+) : TraceMonitor(outputDir, sourceFile) {
     /**
      * Acquires the trace generated when executing the commands defined in the [predicate].
      *
@@ -41,7 +32,8 @@
      */
     fun withTracing(predicate: () -> Unit): ByteArray {
         if (this.isEnabled) {
-            throw UnsupportedOperationException("Chained 'withTracing' calls are not supported")
+            throw UnsupportedOperationException("Trace already running. " +
+                    "This is likely due to chained 'withTracing' calls.")
         }
         try {
             this.start()
@@ -51,16 +43,10 @@
         }
 
         val builder = FlickerRunResult.Builder()
-        this.save("withTracing", builder)
-        val path = this.getTracePath(builder)
+        builder.setResultFrom(this)
 
-        return path?.let {
+        return outputFile.let {
             Files.readAllBytes(it).also { _ -> Files.delete(it) }
         } ?: error("Unable to acquire trace")
     }
-
-    companion object {
-        private val TRACE_DIR = Paths.get("/data/misc/wmtrace/")
-        internal const val WINSCOPE_EXT = ".winscope"
-    }
 }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.kt b/libraries/flicker/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.kt
deleted file mode 100644
index 787f6b8..0000000
--- a/libraries/flicker/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.kt
+++ /dev/null
@@ -1,94 +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.monitor
-
-import android.app.Instrumentation
-import android.util.Log
-import android.view.FrameStats
-import com.android.server.wm.flicker.FLICKER_TAG
-import kotlin.math.max
-
-/**
- * Monitors [android.view.WindowAnimationFrameStats] to detect janky frames.
- *
- *
- * Adapted from [androidx.test.jank.internal.WindowAnimationFrameStatsMonitorImpl] using
- * the same threshold to determine jank.
- */
-open class WindowAnimationFrameStatsMonitor(
-    private val instrumentation: Instrumentation
-) : ITransitionMonitor {
-    private var frameStats: FrameStats? = null
-    private var numJankyFrames = 0
-    private var longestFrameNano = 0L
-
-    private fun analyze() {
-        val stats = frameStats ?: throw IllegalStateException("Frame status are only available " +
-                "once the monitor has been stopped")
-
-        val frameCount = stats.frameCount
-        val refreshPeriodNano = stats.refreshPeriodNano
-
-        // Skip first frame
-        for (i in 2 until frameCount) {
-            // Handle frames that have not been presented.
-            if (stats.getFramePresentedTimeNano(i) == FrameStats.UNDEFINED_TIME_NANO) {
-                // The animation must not have completed. Warn and break out of the loop.
-                Log.w(FLICKER_TAG, "Skipping fenced frame.")
-                break
-            }
-
-            val frameDurationNano =
-                    stats.getFramePresentedTimeNano(i) - stats.getFramePresentedTimeNano(i - 1)
-            val normalized = frameDurationNano.toDouble() / refreshPeriodNano
-            if (normalized < PAUSE_THRESHOLD) {
-                if (normalized > 1.0f + MAX_ERROR) {
-                    numJankyFrames++
-                }
-                longestFrameNano = max(longestFrameNano, frameDurationNano)
-            }
-        }
-    }
-
-    override fun start() {
-        // Clear out any previous data
-        frameStats = null
-        numJankyFrames = 0
-        longestFrameNano = 0
-        instrumentation.uiAutomation.clearWindowAnimationFrameStats()
-    }
-
-    override fun stop() {
-        frameStats = instrumentation.uiAutomation.windowAnimationFrameStats
-        analyze()
-    }
-
-    fun jankyFramesDetected(): Boolean = frameStats?.frameCount ?: 0 > 0 && numJankyFrames > 0
-
-    override fun toString(): String = """$frameStats
-        RefreshPeriodNano: ${frameStats?.refreshPeriodNano}
-        NumJankyFrames: $numJankyFrames
-        LongestFrameNano: $longestFrameNano""".trimIndent()
-
-    companion object {
-        // Maximum normalized error in frame duration before the frame is considered janky
-        private const val MAX_ERROR = 0.5
-
-        // Maximum normalized frame duration before the frame is considered a pause
-        private const val PAUSE_THRESHOLD = 15.0
-    }
-}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.kt b/libraries/flicker/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.kt
index 11858cb..fcf3f09 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.kt
@@ -17,10 +17,15 @@
 package com.android.server.wm.flicker.monitor
 
 import android.os.RemoteException
+import android.util.Log
 import android.view.WindowManagerGlobal
+import com.android.server.wm.flicker.FLICKER_TAG
 import com.android.server.wm.flicker.FlickerRunResult
+import com.android.server.wm.flicker.getDefaultFlickerOutputDir
 import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
 import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerTraceParser
+import java.nio.file.Files
 import java.nio.file.Path
 
 /**
@@ -28,11 +33,12 @@
  *
  * Use [WindowManagerTraceSubject.assertThat] to make assertions on the trace
  */
-open class WindowManagerTraceMonitor(
-    outputDir: Path
-) : TransitionMonitor(outputDir, "wm_trace$WINSCOPE_EXT") {
+open class WindowManagerTraceMonitor @JvmOverloads constructor(
+    outputDir: Path = getDefaultFlickerOutputDir(),
+    sourceFile: Path = TRACE_DIR.resolve("wm_trace$WINSCOPE_EXT")
+) : TransitionMonitor(outputDir, sourceFile) {
     private val windowManager = WindowManagerGlobal.getWindowManagerService()
-    override fun start() {
+    override fun startTracing() {
         try {
             windowManager.startWindowTrace()
         } catch (e: RemoteException) {
@@ -40,7 +46,7 @@
         }
     }
 
-    override fun stop() {
+    override fun stopTracing() {
         try {
             windowManager.stopWindowTrace()
         } catch (e: RemoteException) {
@@ -51,9 +57,12 @@
     override val isEnabled: Boolean
         get() = windowManager.isWindowTraceEnabled
 
-    override fun setResult(flickerRunResultBuilder: FlickerRunResult.Builder, traceFile: Path) {
-        flickerRunResultBuilder.wmTraceFile = traceFile
+    override fun setResult(flickerRunResultBuilder: FlickerRunResult.Builder) {
+        flickerRunResultBuilder.setWmTrace(outputFile) {
+            Log.v(FLICKER_TAG, "Parsing WM trace")
+            val traceData = Files.readAllBytes(outputFile)
+            val wmTrace = WindowManagerTraceParser.parseFromTrace(traceData)
+            WindowManagerTraceSubject.assertThat(wmTrace)
+        }
     }
-
-    override fun getTracePath(builder: FlickerRunResult.Builder) = builder.wmTraceFile
 }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/rules/ChangeDisplayOrientationRule.kt b/libraries/flicker/src/com/android/server/wm/flicker/rules/ChangeDisplayOrientationRule.kt
index d7d7b8a..fc1fa3e 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/rules/ChangeDisplayOrientationRule.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/rules/ChangeDisplayOrientationRule.kt
@@ -19,10 +19,12 @@
 import android.app.Instrumentation
 import android.content.Context
 import android.os.RemoteException
+import android.util.Log
 import android.view.Surface
 import android.view.WindowManager
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
+import com.android.server.wm.flicker.FLICKER_TAG
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.rules.TestWatcher
 import org.junit.runner.Description
@@ -34,6 +36,8 @@
     private var initialOrientation = -1
 
     override fun starting(description: Description?) {
+        Log.v(FLICKER_TAG, "Changing display orientation to " +
+            "$targetOrientation ${Surface.rotationToString(targetOrientation)}")
         val wm = instrumentation.context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
         initialOrientation = wm.defaultDisplay.rotation
         setRotation(targetOrientation, instrumentation)
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/rules/FlickerMetricsCollectorRule.kt b/libraries/flicker/src/com/android/server/wm/flicker/rules/FlickerMetricsCollectorRule.kt
new file mode 100644
index 0000000..8263869
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/rules/FlickerMetricsCollectorRule.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 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.rules
+
+import android.app.Instrumentation
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.service.FlickerCollectionListener
+import com.android.server.wm.flicker.service.assertors.AssertionConfigParser
+import com.android.server.wm.flicker.service.assertors.AssertionData
+import com.android.server.wm.traces.common.errors.Error
+import com.google.common.annotations.VisibleForTesting
+import org.junit.rules.TestWatcher
+import org.junit.runner.Description
+
+/**
+ * Call the {@link FlickerCollectionListener} and get the generated {@link ErrorTrace} from
+ * the {@link FlickerService}.
+ */
+open class FlickerMetricsCollectorRule(
+    private val collectionListener: FlickerCollectionListener = FlickerCollectionListener(),
+    instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+) : TestWatcher() {
+
+    init {
+        collectionListener.instrumentation = instrumentation
+    }
+
+    override fun starting(description: Description?) {
+        // The class name we get from the test description object may contain the iteration number
+        // (e.g. #3 for iteration 3) at the end of the class name. We want to remove that as that
+        // isn't actually part of the class name.
+        val className = description?.className?.replace("\\$[0-9]+\$".toRegex(), "")
+        collectionListener.setTransitionClassName(className)
+        collectionListener.testStarted(description)
+    }
+
+    override fun finished(description: Description?) {
+        collectionListener.testFinished(description)
+    }
+
+    private fun filterErrors(category: String): List<Error> {
+        val errorTrace = collectionListener.getErrorTrace()
+        val assertions = AssertionData.readConfiguration().filter {
+            it.category == category
+        }.map { it.assertion.name }
+
+        return errorTrace.entries.flatMap {
+            entry -> entry.errors.asList()
+        }.filter { error -> assertions.contains(error.assertionName) }
+    }
+
+    fun checkPresubmitAssertions() {
+        val errors = filterErrors(AssertionConfigParser.PRESUBMIT_KEY)
+        failIfAnyError(errors)
+    }
+
+    fun checkPostsubmitAssertions() {
+        val errors = filterErrors(AssertionConfigParser.POSTSUBMIT_KEY)
+        failIfAnyError(errors)
+    }
+
+    fun checkFlakyAssertions() {
+        val errors = filterErrors(AssertionConfigParser.FLAKY_KEY)
+        failIfAnyError(errors)
+    }
+
+    @VisibleForTesting
+    fun getMetrics(): Map<String, Int> {
+        return collectionListener.getMetrics()
+    }
+
+    private fun failIfAnyError(errors: List<Error>) {
+        val errorMsg = errors.joinToString("\n") { "${it.assertionName}\n${it.message}" }
+
+        if (errorMsg.isNotEmpty()) {
+            error(errorMsg)
+        }
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/rules/FlickerResultsCollector.kt b/libraries/flicker/src/com/android/server/wm/flicker/rules/FlickerResultsCollector.kt
new file mode 100644
index 0000000..4744638
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/rules/FlickerResultsCollector.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 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.rules
+
+import android.device.collectors.BaseCollectionListener
+import android.util.Log
+import com.android.helpers.ICollectorHelper
+
+class FlickerResultsCollector : BaseCollectionListener<Boolean>() {
+    private val collectionHelper = CollectionHelper()
+    private var criticalUserJourneyName: String = UNDEFINED_CUJ
+
+    init {
+        createHelperInstance(collectionHelper)
+    }
+
+    fun setCriticalUserJourneyName(className: String?) {
+        this.criticalUserJourneyName = className ?: UNDEFINED_CUJ
+    }
+
+    fun postRunResults(results: Map<String, Int>) {
+        collectionHelper.postMetrics(assertionsToMetrics(results))
+    }
+
+    fun getMetrics(): Map<String, Int> {
+        return collectionHelper.metrics
+    }
+
+    /**
+     * Convert the assertions generated by the Flicker Service to specific metric key pairs that
+     * contain enough information to later further and analyze in dashboards.
+     */
+    private fun assertionsToMetrics(assertions: Map<String, Int>): Map<String, Int> {
+        val processedAssertions: MutableMap<String, Int> = mutableMapOf()
+
+        for ((assertionName, result) in assertions) {
+            // Add information about the CUJ we are running the assertions on
+            processedAssertions["$FASS_METRICS_PREFIX::$criticalUserJourneyName::$assertionName"] =
+                    result
+        }
+
+        return processedAssertions
+    }
+
+    class CollectionHelper : ICollectorHelper<Int> {
+        private var metrics: MutableMap<String, Int> = mutableMapOf()
+
+        fun postMetrics(results: Map<String, Int>) {
+            for ((key, res) in results) {
+                require(res == 1 || res == 0)
+                // If a failure is posted for key then we fail
+                metrics[key] = (metrics[key] ?: 1) and res
+            }
+        }
+
+        /** Do nothing. */
+        override fun startCollecting(): Boolean {
+            Log.i(LOG_TAG, "startCollecting")
+            return true
+        }
+
+        /** Do nothing. */
+        override fun stopCollecting(): Boolean {
+            Log.i(LOG_TAG, "stopCollecting")
+            return true
+        }
+
+        /** Collect the assertions metrics for Flicker as a Service.  */
+        override fun getMetrics(): Map<String, Int> {
+            Log.i(LOG_TAG, "getMetrics")
+            return metrics
+        }
+
+        companion object {
+            private val LOG_TAG = this::class.java.simpleName
+        }
+    }
+
+    companion object {
+        // Unique prefix to add to all fass metrics to identify them
+        private const val FASS_METRICS_PREFIX = "FASS"
+        private const val UNDEFINED_CUJ = "UndefinedCUJ"
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/rules/LaunchAppRule.kt b/libraries/flicker/src/com/android/server/wm/flicker/rules/LaunchAppRule.kt
index d425775..af19f76 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/rules/LaunchAppRule.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/rules/LaunchAppRule.kt
@@ -17,7 +17,9 @@
 package com.android.server.wm.flicker.rules
 
 import android.app.Instrumentation
+import android.util.Log
 import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FLICKER_TAG
 import com.android.server.wm.flicker.helpers.StandardAppHelper
 import com.android.server.wm.traces.common.FlickerComponentName
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
@@ -45,6 +47,7 @@
     ): this(StandardAppHelper(instrumentation, appName, component), instrumentation, wmHelper)
 
     override fun starting(description: Description?) {
+        Log.v(FLICKER_TAG, "Launching app $appHelper")
         appHelper.launchViaIntent()
         appHelper.exit(wmHelper)
     }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/rules/RemoveAllTasksButHomeRule.kt b/libraries/flicker/src/com/android/server/wm/flicker/rules/RemoveAllTasksButHomeRule.kt
index e6c66db..1823c7f 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/rules/RemoveAllTasksButHomeRule.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/rules/RemoveAllTasksButHomeRule.kt
@@ -18,6 +18,8 @@
 
 import android.app.ActivityTaskManager
 import android.app.WindowConfiguration
+import android.util.Log
+import com.android.server.wm.flicker.FLICKER_TAG
 import org.junit.rules.TestWatcher
 import org.junit.runner.Description
 
@@ -26,6 +28,7 @@
  */
 class RemoveAllTasksButHomeRule : TestWatcher() {
     override fun starting(description: Description?) {
+        Log.v(FLICKER_TAG, "Removing all tasks (except home)")
         removeAllTasksButHome()
     }
 
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/rules/WMFlickerServiceRule.kt b/libraries/flicker/src/com/android/server/wm/flicker/rules/WMFlickerServiceRule.kt
index 165fa86..bf1c6d7 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/rules/WMFlickerServiceRule.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/rules/WMFlickerServiceRule.kt
@@ -41,8 +41,8 @@
 ) : TestWatcher() {
     private val traceMonitors = mutableListOf<TraceMonitor>()
 
-    protected var wmTrace: WindowManagerTrace = WindowManagerTrace(emptyArray(), source = "")
-    protected var layersTrace: LayersTrace = LayersTrace(emptyArray(), source = "")
+    protected var wmTrace: WindowManagerTrace = WindowManagerTrace(emptyArray())
+    protected var layersTrace: LayersTrace = LayersTrace(emptyArray())
 
     override fun starting(description: Description?) {
         setupMonitors()
@@ -53,36 +53,36 @@
     }
 
     override fun finished(description: Description?) {
-        val testTag = description?.methodName ?: "fass"
         traceMonitors.forEach {
             it.stop()
-            it.save(testTag)
         }
 
         Files.createDirectories(outputDir)
-        wmTrace = getWindowManagerTrace(getFassFilePath(outputDir, testTag, "wm_trace"))
-        layersTrace = getLayersTrace(getFassFilePath(outputDir, testTag, "layers_trace"))
+        wmTrace = getWindowManagerTrace(getFassFilePath(outputDir, "wm_trace"))
+        layersTrace = getLayersTrace(getFassFilePath(outputDir, "layers_trace"))
 
         val flickerService = FlickerService()
-        flickerService.process(wmTrace, layersTrace, outputDir, testTag)
+        flickerService.process(wmTrace, layersTrace, outputDir)
     }
 
     private fun setupMonitors() {
         traceMonitors.add(WindowManagerTraceMonitor(outputDir))
         traceMonitors.add(LayersTraceMonitor(outputDir))
         traceMonitors.add(ScreenRecorder(
-            outputDir,
-            InstrumentationRegistry.getInstrumentation().targetContext)
+            InstrumentationRegistry.getInstrumentation().targetContext, outputDir)
         )
     }
 
     /**
-     * Remove the WM trace and layers trace files collected from previous test runs.
+     * Remove the WM trace and layers trace files collected from previous test runs if the
+     * directory exists.
      */
     private fun cleanupTraceFiles() {
-        Files.list(outputDir).forEach { file ->
-            if (!Files.isDirectory(file)) {
-                Files.delete(file)
+        if (Files.exists(outputDir)) {
+            Files.list(outputDir).forEach { file ->
+                if (!Files.isDirectory(file)) {
+                    Files.delete(file)
+                }
             }
         }
     }
@@ -108,4 +108,4 @@
         val layersTraceByteArray: ByteArray = Files.readAllBytes(traceFilePath)
         return LayersTraceParser.parseFromTrace(layersTraceByteArray)
     }
-}
\ No newline at end of file
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/rules/WMFlickerServiceRuleForTestSpec.kt b/libraries/flicker/src/com/android/server/wm/flicker/rules/WMFlickerServiceRuleForTestSpec.kt
index 5c3606d..9bda5e2 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/rules/WMFlickerServiceRuleForTestSpec.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/rules/WMFlickerServiceRuleForTestSpec.kt
@@ -16,6 +16,7 @@
 
 package com.android.server.wm.flicker.rules
 
+import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.dsl.AssertionTag
 import com.android.server.wm.flicker.service.FlickerService
@@ -25,26 +26,67 @@
 import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
 import com.android.server.wm.traces.common.errors.ErrorTrace
 import org.junit.rules.TestWatcher
+import org.junit.runner.Description
+import java.util.regex.Pattern
 
 /**
  * A test rule reusing flicker data from [FlickerTestParameter], and fetching the traces
- * to call the WM Flicker Service after the test
+ * to call the WM Flicker Service after the test and report metrics from the results.
  */
 @Deprecated("This test rule should be only used with legacy flicker tests. " +
     "For new tests use WMFlickerServiceRule instead")
 class WMFlickerServiceRuleForTestSpec(
     private val testSpec: FlickerTestParameter
 ) : TestWatcher() {
+    private val flickerResultsCollector = FlickerResultsCollector()
+
+    init {
+        flickerResultsCollector.instrumentation = InstrumentationRegistry.getInstrumentation()
+    }
+
+    override fun starting(description: Description?) {
+        val runParameterString = extractRunParameterStringFromMethodName(description?.methodName)
+        val cuj = if (runParameterString.isNullOrBlank()) {
+            description?.className
+        } else {
+            "${description?.className}[$runParameterString]"
+        }
+        flickerResultsCollector.setCriticalUserJourneyName(cuj)
+        flickerResultsCollector.testStarted(description)
+    }
+
+    private fun extractRunParameterStringFromMethodName(methodName: String?): String? {
+        if (methodName.isNullOrBlank()) {
+            return null
+        }
+
+        val pattern = Pattern.compile("\\[(.+)\\]")
+        val matcher = pattern.matcher(methodName)
+        return if (matcher.find()) {
+            matcher.group(1)
+        } else {
+            null
+        }
+    }
+
+    override fun finished(description: Description?) {
+        flickerResultsCollector.testFinished(description)
+    }
+
+    fun getMetrics(): Map<String, Int> {
+        return flickerResultsCollector.getMetrics()
+    }
+
     private fun checkFlicker(category: String): List<ErrorTrace> {
         // run flicker if it was not executed before
-        testSpec.flicker.result ?: testSpec.assertWm { isNotEmpty() }
+        testSpec.result ?: testSpec.assertWm { isNotEmpty() }
 
         val errors = mutableListOf<ErrorTrace>()
-        val result = testSpec.flicker.result ?: error("No flicker results for ${testSpec.flicker}")
+        val result = testSpec.result ?: error("No flicker results for $testSpec")
         val assertions = AssertionData.readConfiguration().filter { it.category == category }
         val flickerService = FlickerService(assertions)
 
-        result.runs
+        result.successfulRuns
             .filter { it.assertionTag == AssertionTag.ALL }
             .filter {
                 val hasWmTrace = it.wmSubject?.let { true } ?: false
@@ -55,35 +97,57 @@
                 val wmSubject = run.wmSubject as WindowManagerTraceSubject
                 val layersSubject = run.layersSubject as LayersTraceSubject
 
-                val outputDir = run.traceFiles
-                    .firstOrNull()
-                    ?.parent
-                    ?: error("Output dir not detected")
+                val outputDir = run.mTraceFile?.traceFile?.parent
+                        ?: error("Output dir not detected")
 
                 val wmTrace = wmSubject.trace
                 val layersTrace = layersSubject.trace
-                errors.add(flickerService.process(wmTrace, layersTrace, outputDir, category))
+                val (errorTrace, assertionsResults) =
+                        flickerService.process(wmTrace, layersTrace, outputDir)
+                errors.add(errorTrace)
+                flickerResultsCollector.postRunResults(assertionsResults)
             }
 
         return errors
     }
 
-    fun checkPresubmitAssertions() {
+    /**
+     * @return true if all assertions pass, false otherwise
+     */
+    @JvmOverloads
+    fun checkPresubmitAssertions(failOnAssertionFailure: Boolean = true): Boolean {
         val errors = checkFlicker(AssertionConfigParser.PRESUBMIT_KEY)
-        failIfAnyError(errors)
+        return handleErrors(errors, failOnAssertionFailure)
     }
 
-    fun checkPostsubmitAssertions() {
+    /**
+     * @return true if all assertions pass, false otherwise
+     */
+    fun checkPostsubmitAssertions(failOnAssertionFailure: Boolean = true): Boolean {
         val errors = checkFlicker(AssertionConfigParser.POSTSUBMIT_KEY)
-        failIfAnyError(errors)
+        return handleErrors(errors, failOnAssertionFailure)
     }
 
-    fun checkFlakyAssertions() {
+    /**
+     * @return true if all assertions pass, false otherwise
+     */
+    fun checkFlakyAssertions(failOnAssertionFailure: Boolean = true): Boolean {
         val errors = checkFlicker(AssertionConfigParser.FLAKY_KEY)
-        failIfAnyError(errors)
+        return handleErrors(errors, failOnAssertionFailure)
     }
 
-    private fun failIfAnyError(errors: List<ErrorTrace>) {
+    private fun handleErrors(errors: List<ErrorTrace>, failOnAssertionFailure: Boolean): Boolean {
+        return if (failOnAssertionFailure) {
+            failIfAnyError(errors)
+        } else {
+            !hasErrors(errors)
+        }
+    }
+
+    /**
+     * @return true if there were no errors
+     */
+    private fun failIfAnyError(errors: List<ErrorTrace>): Boolean {
         val errorMsg = errors.joinToString("\n") { runs ->
             runs.entries.joinToString { state ->
                 state.errors.joinToString { "${it.assertionName}\n${it.message}" }
@@ -92,5 +156,16 @@
         if (errorMsg.isNotEmpty()) {
             error(errorMsg)
         }
+        return true
     }
-}
\ No newline at end of file
+
+    private fun hasErrors(errors: List<ErrorTrace>): Boolean {
+        return errors.any { runs ->
+            runs.entries.any { state ->
+                state.errors.any { error ->
+                    error.assertionName.isNotEmpty()
+                }
+            }
+        }
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/AssertionEngine.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/AssertionEngine.kt
index c18144e..b19f530 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/AssertionEngine.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/AssertionEngine.kt
@@ -40,9 +40,10 @@
         wmTrace: WindowManagerTrace,
         layersTrace: LayersTrace,
         tagTrace: TagTrace
-    ): ErrorTrace {
+    ): Pair<ErrorTrace, Map<String, Int>> {
         val errors = mutableListOf<ErrorState>()
         val allTransitions = getTransitionTags(tagTrace)
+        val assertionsResults = mutableMapOf<String, Int>()
 
         allTransitions
             .filter { knownTypes.contains(it.tag.transition) }
@@ -56,6 +57,9 @@
                 val errorTrace = assertor.analyze(
                     transition.tag, filteredWmTrace, filteredLayersTrace)
                 errors.addAll(errorTrace)
+                assertionsResults.putAll(
+                    getAssertionsResultsForTransition(assertionsOfType, errors)
+                )
             }
 
         /* Ensure all error states with same timestamp are merged */
@@ -65,7 +69,7 @@
                     ErrorState(value.flatten().toTypedArray(), key.toString()) }
                 .values.toTypedArray()
 
-        return ErrorTrace(errorStates, source = "")
+        return ErrorTrace(errorStates) to assertionsResults
     }
 
     /**
@@ -87,12 +91,6 @@
         }
     }
 
-    private fun getEndTagTimestamp(tagTrace: TagTrace, tag: Tag): Long {
-        val finalTag = tag.copy(isStartTag = false)
-        return tagTrace.entries.firstOrNull { state -> state.tags.contains(finalTag) }?.timestamp
-            ?: throw RuntimeException("All open tags should be closed!")
-    }
-
     /**
      * Splits a [WindowManagerTrace] and a [LayersTrace] by a [Transition].
      *
@@ -110,4 +108,31 @@
         val filteredLayersTrace = layersTrace.filter(tag.startTimestamp, tag.endTimestamp)
         return Pair(filteredWmTrace, filteredLayersTrace)
     }
+
+    private fun getEndTagTimestamp(tagTrace: TagTrace, tag: Tag): Long {
+        return tagTrace.entries.firstOrNull { state ->
+            state.tags.any { it.id == tag.id && !it.isStartTag }
+        }?.timestamp ?: throw RuntimeException("All open tags should be closed!")
+    }
+
+    /**
+     * Returns a map that associates an assertion name with 0 if the assertion failed
+     * or 1 if the assertion passed.
+     *
+     * @param assertions the assertions that were run
+     * @param errorStates the generated errors
+     * @return a map containing the assertion name and the result
+     */
+    private fun getAssertionsResultsForTransition(
+        assertions: List<AssertionData>,
+        errorStates: List<ErrorState>
+    ): Map<String, Int> {
+        val errors = errorStates.flatMap { it.errors.map { it.assertionName }.toList() }
+        val results = assertions.associate {
+            val assertionValue = if (errors.contains(it.assertion.name)) 0 else 1
+            "${it.transitionType.name}#${it.assertion.name}" to assertionValue
+        }
+
+        return results
+    }
 }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/FlickerCollectionHelper.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/FlickerCollectionHelper.kt
new file mode 100644
index 0000000..98fd187
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/FlickerCollectionHelper.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2021 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.service
+
+import android.util.Log
+import com.android.helpers.ICollectorHelper
+import com.android.server.wm.flicker.getDefaultFlickerOutputDir
+import com.android.server.wm.flicker.monitor.LayersTraceMonitor
+import com.android.server.wm.flicker.monitor.TraceMonitor
+import com.android.server.wm.flicker.monitor.WindowManagerTraceMonitor
+import com.android.server.wm.flicker.service.FlickerService.Companion.getFassFilePath
+import com.android.server.wm.traces.common.errors.ErrorTrace
+import com.android.server.wm.traces.common.layers.LayersTrace
+import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace
+import com.android.server.wm.traces.parser.layers.LayersTraceParser
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerTraceParser
+import java.nio.file.Files
+import java.nio.file.Path
+
+/**
+ * An {@link ICollectorHelper} for collecting FASS assertions information.
+ *
+ * <p>This parses the output of {@link FlickerService} and returns a collection of
+ * assertions metrics.
+ */
+class FlickerCollectionHelper : ICollectorHelper<Int> {
+    private val LOG_TAG = FlickerCollectionHelper::class.java.simpleName
+    private val outputDir = getDefaultFlickerOutputDir()
+
+    private val UNDEFINED_TRANSITION_CLASS = "UndefinedTransitionClass"
+    private var transitionClassName: String = UNDEFINED_TRANSITION_CLASS
+
+    private val traceMonitors: List<TraceMonitor> = listOf(
+            WindowManagerTraceMonitor(outputDir),
+            LayersTraceMonitor(outputDir)
+    )
+
+    internal var errorTrace: ErrorTrace = ErrorTrace(emptyArray())
+        private set
+
+    private var wmTrace: WindowManagerTrace = WindowManagerTrace(emptyArray())
+    private var layersTrace: LayersTrace = LayersTrace(emptyArray())
+
+    /** Clear existing fass files and start the monitors.  */
+    override fun startCollecting(): Boolean {
+        Log.i(LOG_TAG, "startCollecting")
+        cleanupTraceFiles()
+        traceMonitors.forEach {
+            it.start()
+        }
+        return true
+    }
+
+    /** Collect the assertions metrics for Flicker as a Service.  */
+    override fun getMetrics(): Map<String, Int> {
+        Log.i(LOG_TAG, "getMetrics")
+        traceMonitors.forEach {
+            it.stop()
+        }
+
+        Files.createDirectories(outputDir)
+        wmTrace = getWindowManagerTrace(getFassFilePath(outputDir, "wm_trace"))
+        layersTrace = getLayersTrace(getFassFilePath(outputDir, "layers_trace"))
+
+        val flickerService = FlickerService()
+        val (errors, assertions) = flickerService.process(wmTrace, layersTrace, outputDir)
+        errorTrace = errors
+
+        return assertionsToMetrics(assertions)
+    }
+
+    /** Do nothing, because nothing is needed to disable fass.  */
+    override fun stopCollecting(): Boolean {
+        Log.i(LOG_TAG, "stopCollecting")
+        return true
+    }
+
+    fun setTransitionClassName(className: String?) {
+        this.transitionClassName = className ?: UNDEFINED_TRANSITION_CLASS
+    }
+
+    /**
+     * Convert the assertions generated by the Flicker Service to specific metric key pairs that
+     * contain enough information to later further and analyze in dashboards.
+     */
+    private fun assertionsToMetrics(assertions: Map<String, Int>): Map<String, Int> {
+        val processedAssertions: MutableMap<String, Int> = mutableMapOf()
+
+        for ((assertionName, result) in assertions) {
+            // Add information about the CUJ we are running the assertions on
+            processedAssertions["$transitionClassName::$assertionName"] = result
+        }
+
+        return processedAssertions
+    }
+
+    /**
+     * Remove the WM trace and layers trace files collected from previous test runs if the
+     * directory exists.
+     */
+    private fun cleanupTraceFiles() {
+        if (!Files.exists(outputDir)) {
+            return
+        }
+
+        Files.list(outputDir).filter {
+            file -> !Files.isDirectory(file.toAbsolutePath())
+        }.forEach { file ->
+            Files.delete(file)
+        }
+    }
+
+    /**
+     * Parse the window manager trace file.
+     *
+     * @param traceFilePath
+     * @return parsed window manager trace.
+     */
+    private fun getWindowManagerTrace(traceFilePath: Path): WindowManagerTrace {
+        val wmTraceByteArray: ByteArray = Files.readAllBytes(traceFilePath)
+        return WindowManagerTraceParser.parseFromTrace(wmTraceByteArray)
+    }
+
+    /**
+     * Parse the layers trace file.
+     *
+     * @param traceFilePath
+     * @return parsed layers trace.
+     */
+    private fun getLayersTrace(traceFilePath: Path): LayersTrace {
+        val layersTraceByteArray: ByteArray = Files.readAllBytes(traceFilePath)
+        return LayersTraceParser.parseFromTrace(layersTraceByteArray)
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/FlickerCollectionListener.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/FlickerCollectionListener.kt
new file mode 100644
index 0000000..bf9b1d9
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/FlickerCollectionListener.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 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.service
+
+import android.device.collectors.BaseCollectionListener
+import android.device.collectors.annotations.OptionClass
+import com.android.server.wm.traces.common.errors.ErrorTrace
+import com.google.common.annotations.VisibleForTesting
+
+/**
+ * A {@link FlickerCollectionListener} that captures FASS assertions metrics.
+ *
+ * <p>Do NOT throw exception anywhere in this class. We don't want to halt the test when metrics
+ * collection fails.
+ */
+@OptionClass(alias = "fass-collector")
+class FlickerCollectionListener : BaseCollectionListener<Boolean>() {
+    private val collectionHelper: FlickerCollectionHelper = FlickerCollectionHelper()
+
+    init {
+        createHelperInstance(collectionHelper)
+    }
+
+    fun getErrorTrace(): ErrorTrace {
+        return collectionHelper.errorTrace
+    }
+
+    @VisibleForTesting
+    fun getMetrics(): Map<String, Int> {
+        return collectionHelper.metrics
+    }
+
+    fun setTransitionClassName(className: String?) {
+        this.collectionHelper.setTransitionClassName(className)
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/FlickerService.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/FlickerService.kt
index 827b586..faeef04 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/FlickerService.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/FlickerService.kt
@@ -18,7 +18,7 @@
 
 import android.util.Log
 import com.android.server.wm.flicker.FLICKER_TAG
-import com.android.server.wm.flicker.monitor.TransitionMonitor.Companion.WINSCOPE_EXT
+import com.android.server.wm.flicker.monitor.TraceMonitor.Companion.WINSCOPE_EXT
 import com.android.server.wm.flicker.service.assertors.AssertionData
 import com.android.server.wm.traces.common.errors.ErrorTrace
 import com.android.server.wm.traces.common.layers.LayersTrace
@@ -41,24 +41,24 @@
      *
      * @param wmTrace Window Manager trace
      * @param layersTrace Surface Flinger trace
-     * @return A list containing all failures
+     * @return A pair with an [ErrorTrace] and a map that associates assertion names with
+     * 0 if it fails and 1 if it passes
      */
     fun process(
         wmTrace: WindowManagerTrace,
         layersTrace: LayersTrace,
-        outputDir: Path,
-        testTag: String
-    ): ErrorTrace {
+        outputDir: Path
+    ): Pair<ErrorTrace, Map<String, Int>> {
         val taggingEngine = TaggingEngine(wmTrace, layersTrace) { Log.v("$FLICKER_TAG-PROC", it) }
         val tagTrace = taggingEngine.run()
-        val tagTraceFile = getFassFilePath(outputDir, testTag, "tag_trace")
+        val tagTraceFile = getFassFilePath(outputDir, "tag_trace")
         tagTrace.writeToFile(tagTraceFile)
 
         val assertionEngine = AssertionEngine(assertions) { Log.v("$FLICKER_TAG-ASSERT", it) }
-        val errorTrace = assertionEngine.analyze(wmTrace, layersTrace, tagTrace)
-        val errorTraceFile = getFassFilePath(outputDir, testTag, "error_trace")
+        val (errorTrace, assertions) = assertionEngine.analyze(wmTrace, layersTrace, tagTrace)
+        val errorTraceFile = getFassFilePath(outputDir, "error_trace")
         errorTrace.writeToFile(errorTraceFile)
-        return errorTrace
+        return errorTrace to assertions
     }
 
     companion object {
@@ -66,11 +66,10 @@
          * Returns the computed path for the Fass files.
          *
          * @param outputDir the output directory for the trace file
-         * @param testTag the tag to identify the test
          * @param file the name of the trace file
          * @return the path to the trace file
          */
-        internal fun getFassFilePath(outputDir: Path, testTag: String, file: String): Path =
-                outputDir.resolve("${testTag}_$file$WINSCOPE_EXT")
+        internal fun getFassFilePath(outputDir: Path, file: String): Path =
+                outputDir.resolve("$file$WINSCOPE_EXT")
     }
 }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/AssertionConfigParser.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/AssertionConfigParser.kt
index cc2d839..f5ba9ef 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/AssertionConfigParser.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/AssertionConfigParser.kt
@@ -120,4 +120,4 @@
 
         return assertions
     }
-}
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/TransitionAssertor.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/TransitionAssertor.kt
index 9ec5a29..77ee1aa 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/TransitionAssertor.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/TransitionAssertor.kt
@@ -61,16 +61,14 @@
         categoryKey: String
     ): Map<Long, MutableList<Error>> {
         logger.invoke("Running assertions for $tag $categoryKey")
-        val wmSubject = WindowManagerTraceSubject.assertThat(wmTrace)
-        val layersSubject = LayersTraceSubject.assertThat(layersTrace)
         val assertions = assertions.filter { it.category == categoryKey }
-        return runAssertionsOnSubjects(tag, wmSubject, layersSubject, assertions)
+        return runAssertionsOnSubjects(tag, wmTrace, layersTrace, assertions)
     }
 
     private fun runAssertionsOnSubjects(
         tag: Tag,
-        wmSubject: WindowManagerTraceSubject,
-        layerSubject: LayersTraceSubject,
+        wmTrace: WindowManagerTrace,
+        layersTrace: LayersTrace,
         assertions: List<AssertionData>
     ): Map<Long, MutableList<Error>> {
         val errors = mutableMapOf<Long, MutableList<Error>>()
@@ -79,12 +77,18 @@
             assertions.forEach {
                 val assertion = it.assertion
                 logger.invoke("Running assertion $assertion")
-                val result = assertion.runCatching { evaluate(tag, wmSubject, layerSubject) }
+                val wmSubject = WindowManagerTraceSubject.assertThat(wmTrace)
+                val layersSubject = LayersTraceSubject.assertThat(layersTrace)
+                val result = assertion.runCatching { evaluate(tag, wmSubject, layersSubject) }
                 if (result.isFailure) {
-                    val layer = assertion.getFailureLayer(tag, wmSubject, layerSubject)
-                    val window = assertion.getFailureWindow(tag, wmSubject, layerSubject)
-                    val exception = result.exceptionOrNull() as FlickerSubjectException
-
+                    val layer = assertion.getFailureLayer(tag, wmSubject, layersSubject)
+                    val window = assertion.getFailureWindow(tag, wmSubject, layersSubject)
+                    val exception = result.exceptionOrNull() ?: error("Exception not found")
+                    // if it's not a flicker exception, we don't know what
+                    // happened, raise the exception back to the test
+                    if (exception !is FlickerSubjectException) {
+                        throw exception
+                    }
                     errors.putIfAbsent(exception.timestamp, mutableListOf())
                     val errorEntry = Error(
                         stacktrace = exception.stackTraceToString(),
@@ -111,6 +115,6 @@
             val stateTags = entry.value
             ErrorState(stateTags.toTypedArray(), timestamp.toString())
         }
-        return ErrorTrace(errorStates.toTypedArray(), source = "")
+        return ErrorTrace(errorStates.toTypedArray())
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppLayerBecomesInvisible.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppLayerBecomesInvisible.kt
new file mode 100644
index 0000000..a3c1659
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppLayerBecomesInvisible.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 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.service.assertors.common
+
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
+import com.android.server.wm.traces.common.tags.Tag
+
+/**
+ * Checks if the [getWindowState] layer is visible at the start and then becomes
+ * invisible and remains unpinned and invisible until the end of the transition
+ */
+class AppLayerBecomesInvisible : AppComponentBaseTest() {
+
+    /** {@inheritDoc} */
+    override fun doEvaluate(
+        tag: Tag,
+        wmSubject: WindowManagerTraceSubject,
+        layerSubject: LayersTraceSubject
+    ) {
+        val appComponent = getComponentName(tag, wmSubject)
+        val assertion = LayerBecomesInvisible(appComponent.toWindowName())
+        assertion.evaluate(tag, wmSubject, layerSubject)
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppLayerIsVisibleAlways.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppLayerIsVisibleAlways.kt
new file mode 100644
index 0000000..3b60145
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppLayerIsVisibleAlways.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 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.service.assertors.common
+
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
+import com.android.server.wm.traces.common.tags.Tag
+
+/**
+ * Checks if the [getWindowState] layer is visible throughout the animation
+ */
+class AppLayerIsVisibleAlways : AppComponentBaseTest() {
+    /** {@inheritDoc} */
+    override fun doEvaluate(
+        tag: Tag,
+        wmSubject: WindowManagerTraceSubject,
+        layerSubject: LayersTraceSubject
+    ) {
+        layerSubject.isVisible(getComponentName(tag, wmSubject)).forAllEntries()
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppLayerReduces.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppLayerReduces.kt
new file mode 100644
index 0000000..a2ee893
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppLayerReduces.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 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.service.assertors.common
+
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
+import com.android.server.wm.traces.common.tags.Tag
+
+/**
+ * Checks that the visible region of [getWindowState] always reduces during the animation
+ */
+class AppLayerReduces : AppComponentBaseTest() {
+    /** {@inheritDoc} */
+    override fun doEvaluate(
+        tag: Tag,
+        wmSubject: WindowManagerTraceSubject,
+        layerSubject: LayersTraceSubject
+    ) {
+        val layerName = getComponentName(tag, wmSubject).toLayerName()
+        val layerList = layerSubject.layers { it.name.contains(layerName) && it.isVisible }
+        layerList.zipWithNext { previous, current ->
+            current.visibleRegion.coversAtMost(previous.visibleRegion.region)
+        }
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppLayerRemainInsideDisplayBounds.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppLayerRemainInsideDisplayBounds.kt
new file mode 100644
index 0000000..e7b2762
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppLayerRemainInsideDisplayBounds.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 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.service.assertors.common
+
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
+import com.android.server.wm.traces.common.tags.Tag
+
+/**
+ * Checks if the [getWindowState] layer remains inside the display bounds throughout the whole
+ * animation
+ */
+class AppLayerRemainInsideDisplayBounds : AppComponentBaseTest() {
+    /** {@inheritDoc} */
+    override fun doEvaluate(
+        tag: Tag,
+        wmSubject: WindowManagerTraceSubject,
+        layerSubject: LayersTraceSubject
+    ) {
+        layerSubject.invoke("appLayerRemainInsideDisplayBounds") { entry ->
+            val displays = entry.entry.displays
+            if (displays.isEmpty()) {
+                entry.fail("No displays found")
+            }
+            displays.forEach { display ->
+                entry.visibleRegion(getComponentName(tag, wmSubject))
+                    .coversAtMost(display.layerStackSpace)
+            }
+        }.forAllEntries()
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppLayerReplacesLauncher.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppLayerReplacesLauncher.kt
index 1591f7f..084a9a8 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppLayerReplacesLauncher.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppLayerReplacesLauncher.kt
@@ -38,5 +38,6 @@
         layerSubject.isVisible(Components.LAUNCHER)
             .then()
             .isVisible(getComponentName(tag, wmSubject))
+            .forAllEntries()
     }
 }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppWindowBecomesPinned.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppWindowBecomesPinned.kt
new file mode 100644
index 0000000..d7ab698
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppWindowBecomesPinned.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 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.service.assertors.common
+
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
+import com.android.server.wm.traces.common.tags.Tag
+
+/**
+ * Checks that [getWindowState] window becomes pinned
+ */
+class AppWindowBecomesPinned : AppComponentBaseTest() {
+    /** {@inheritDoc} */
+    override fun doEvaluate(
+        tag: Tag,
+        wmSubject: WindowManagerTraceSubject,
+        layerSubject: LayersTraceSubject
+    ) {
+        val appComponent = getComponentName(tag, wmSubject)
+        wmSubject.invoke("appWindowIsNotPinned") { it.isNotPinned(appComponent) }
+            .then()
+            .invoke("appWindowIsPinned") { it.isPinned(appComponent) }
+            .forAllEntries()
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppWindowIsVisibleAlways.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppWindowIsVisibleAlways.kt
new file mode 100644
index 0000000..21d2116
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppWindowIsVisibleAlways.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 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.service.assertors.common
+
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
+import com.android.server.wm.traces.common.tags.Tag
+
+/**
+ * Checks that [getWindowState] window remains visible throughout the transition
+ */
+class AppWindowIsVisibleAlways : AppComponentBaseTest() {
+    /** {@inheritDoc} */
+    override fun doEvaluate(
+        tag: Tag,
+        wmSubject: WindowManagerTraceSubject,
+        layerSubject: LayersTraceSubject
+    ) {
+        wmSubject.isAppWindowVisible(getComponentName(tag, wmSubject)).forAllEntries()
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppWindowRemainInsideDisplayBounds.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppWindowRemainInsideDisplayBounds.kt
new file mode 100644
index 0000000..5a289dd
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppWindowRemainInsideDisplayBounds.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 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.service.assertors.common
+
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
+import com.android.server.wm.traces.common.tags.Tag
+
+/**
+ * Checks that [getWindowState] window remains inside the display bounds throughout the whole
+ * animation
+ */
+class AppWindowRemainInsideDisplayBounds : AppComponentBaseTest() {
+    /** {@inheritDoc} */
+    override fun doEvaluate(
+        tag: Tag,
+        wmSubject: WindowManagerTraceSubject,
+        layerSubject: LayersTraceSubject
+    ) {
+        wmSubject.invoke("appWindowRemainInsideDisplayBounds") { entry ->
+            val displays = entry.wmState.displays
+            if (displays.isEmpty()) {
+                entry.fail("No displays found")
+            }
+            val display = entry.wmState.displays.sortedBy { it.id }.first()
+            entry.frameRegion(getComponentName(tag, wmSubject))
+                .coversAtMost(display.displayRect)
+        }.forAllEntries()
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppWindowReplacesLauncherAsTopWindow.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppWindowReplacesLauncherAsTopWindow.kt
index 958327d..823bf13 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppWindowReplacesLauncherAsTopWindow.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/AppWindowReplacesLauncherAsTopWindow.kt
@@ -35,5 +35,6 @@
         wmSubject.isAppWindowOnTop(Components.LAUNCHER)
             .then()
             .isAppWindowOnTop(getComponentName(tag, wmSubject))
+            .forAllEntries()
     }
 }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/ComponentBaseTest.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/ComponentBaseTest.kt
index 30288ea..ec34df8 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/ComponentBaseTest.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/ComponentBaseTest.kt
@@ -19,6 +19,9 @@
 import com.android.server.wm.flicker.service.assertors.BaseAssertion
 import com.android.server.wm.traces.common.FlickerComponentName
 
+/**
+ * Base class for tests that require a [component] named window name
+ */
 abstract class ComponentBaseTest(windowName: String) : BaseAssertion() {
     protected val component = FlickerComponentName.unflattenFromString(windowName)
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/EntireScreenCoveredAlways.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/EntireScreenCoveredAlways.kt
index 9820eaa..07c7a48 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/EntireScreenCoveredAlways.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/EntireScreenCoveredAlways.kt
@@ -33,9 +33,13 @@
         layerSubject: LayersTraceSubject
     ) {
         layerSubject.invoke("entireScreenCovered") { entry ->
-            entry.entry.displays.forEach { display ->
+            val displays = entry.entry.displays
+            if (displays.isEmpty()) {
+                entry.fail("No displays found")
+            }
+            displays.forEach { display ->
                 entry.visibleRegion().coversAtLeast(display.layerStackSpace)
             }
-        }
+        }.forAllEntries()
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/EntireScreenCoveredAtEnd.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/EntireScreenCoveredAtEnd.kt
index 62b2539..5696679 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/EntireScreenCoveredAtEnd.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/EntireScreenCoveredAtEnd.kt
@@ -33,7 +33,11 @@
         layerSubject: LayersTraceSubject
     ) {
         val subject = layerSubject.last()
-        subject.entry.displays.forEach { display ->
+        val displays = subject.entry.displays
+        if (displays.isEmpty()) {
+            subject.fail("No displays found")
+        }
+        displays.forEach { display ->
             subject.visibleRegion().coversAtLeast(display.layerStackSpace)
         }
     }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/EntireScreenCoveredAtStart.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/EntireScreenCoveredAtStart.kt
index c51eb51..140da0a 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/EntireScreenCoveredAtStart.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/EntireScreenCoveredAtStart.kt
@@ -33,7 +33,11 @@
         layerSubject: LayersTraceSubject
     ) {
         val subject = layerSubject.first()
-        subject.entry.displays.forEach { display ->
+        val displays = subject.entry.displays
+        if (displays.isEmpty()) {
+            subject.fail("No displays found")
+        }
+        displays.forEach { display ->
             subject.visibleRegion().coversAtLeast(display.layerStackSpace)
         }
     }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LauncherReplacesAppLayer.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LauncherReplacesAppLayer.kt
new file mode 100644
index 0000000..81fcba2
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LauncherReplacesAppLayer.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 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.service.assertors.common
+
+import com.android.server.wm.flicker.service.assertors.Components
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
+import com.android.server.wm.traces.common.tags.Tag
+
+/**
+ * Asserts that:
+ *     [getWindowState] is visible at the start of the trace
+ *     [getWindowState] becomes invisible during the trace and (in the same entry)
+ *     [Components.LAUNCHER] becomes visible
+ */
+class LauncherReplacesAppLayer : AppComponentBaseTest() {
+    /** {@inheritDoc} */
+    override fun doEvaluate(
+        tag: Tag,
+        wmSubject: WindowManagerTraceSubject,
+        layerSubject: LayersTraceSubject
+    ) {
+        layerSubject.isVisible(getComponentName(tag, wmSubject))
+            .then()
+            .isVisible(Components.LAUNCHER)
+            .forAllEntries()
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LauncherWindowReplacesAppAsTopWindow.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LauncherWindowReplacesAppAsTopWindow.kt
index a62de1d..7e23b4f 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LauncherWindowReplacesAppAsTopWindow.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LauncherWindowReplacesAppAsTopWindow.kt
@@ -34,5 +34,6 @@
         wmSubject.isAppWindowOnTop(getComponentName(tag, wmSubject))
             .then()
             .isAppWindowOnTop(Components.LAUNCHER)
+            .forAllEntries()
     }
 }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LayerBecomesInvisible.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LayerBecomesInvisible.kt
new file mode 100644
index 0000000..c66c040
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LayerBecomesInvisible.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 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.service.assertors.common
+
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
+import com.android.server.wm.traces.common.tags.Tag
+
+/**
+ * Checks if the [component] layer is visible at the start of the transition and becomes invisible
+ */
+class LayerBecomesInvisible(windowName: String) : ComponentBaseTest(windowName) {
+    /** {@inheritDoc} */
+    override fun doEvaluate(
+        tag: Tag,
+        wmSubject: WindowManagerTraceSubject,
+        layerSubject: LayersTraceSubject
+    ) {
+        layerSubject.isVisible(component)
+            .then()
+            .isInvisible(component)
+            .forAllEntries()
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LayerBecomesVisible.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LayerBecomesVisible.kt
new file mode 100644
index 0000000..1bb3fd3
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LayerBecomesVisible.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 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.service.assertors.common
+
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
+import com.android.server.wm.traces.common.tags.Tag
+
+/**
+ * Checks if the [component] layer is invisible at the start of the transition and becomes visible
+ */
+class LayerBecomesVisible(windowName: String) : ComponentBaseTest(windowName) {
+    /** {@inheritDoc} */
+    override fun doEvaluate(
+        tag: Tag,
+        wmSubject: WindowManagerTraceSubject,
+        layerSubject: LayersTraceSubject
+    ) {
+        layerSubject.isInvisible(component)
+            .then()
+            .isVisible(component)
+            .forAllEntries()
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LayerIsInvisibleAlways.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LayerIsInvisibleAlways.kt
index 150c2a9..bf46fd5 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LayerIsInvisibleAlways.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LayerIsInvisibleAlways.kt
@@ -30,6 +30,6 @@
         wmSubject: WindowManagerTraceSubject,
         layerSubject: LayersTraceSubject
     ) {
-        layerSubject.isVisible(component)
+        layerSubject.isVisible(component).forAllEntries()
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LayerIsVisibleAlways.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LayerIsVisibleAlways.kt
index a4c1265..a6e6fe7 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LayerIsVisibleAlways.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/LayerIsVisibleAlways.kt
@@ -30,6 +30,6 @@
         wmSubject: WindowManagerTraceSubject,
         layerSubject: LayersTraceSubject
     ) {
-        layerSubject.isVisible(component)
+        layerSubject.isVisible(component).forAllEntries()
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/NonAppWindowBecomesInvisible.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/NonAppWindowBecomesInvisible.kt
new file mode 100644
index 0000000..6a23a26
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/NonAppWindowBecomesInvisible.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 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.service.assertors.common
+
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
+import com.android.server.wm.traces.common.tags.Tag
+
+/**
+ * Checks that non-app window [component] is visible at the start of the transition and
+ * becomes invisible
+ */
+open class NonAppWindowBecomesInvisible(windowName: String) : ComponentBaseTest(windowName) {
+    /** {@inheritDoc} */
+    override fun doEvaluate(
+        tag: Tag,
+        wmSubject: WindowManagerTraceSubject,
+        layerSubject: LayersTraceSubject
+    ) {
+        wmSubject.isNonAppWindowVisible(component)
+            .then()
+            .isNonAppWindowInvisible(component)
+            .forAllEntries()
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/NonAppWindowBecomesVisible.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/NonAppWindowBecomesVisible.kt
index 3ec9cc7..bccc7a9 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/NonAppWindowBecomesVisible.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/NonAppWindowBecomesVisible.kt
@@ -20,7 +20,11 @@
 import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
 import com.android.server.wm.traces.common.tags.Tag
 
-open class NonAppWindowBecomesVisible(windowName: String) : ComponentBaseTest(windowName) {
+/**
+ * Checks that non-app window [component] is invisible at the start of the transition and
+ * becomes visible
+ */
+class NonAppWindowBecomesVisible(windowName: String) : ComponentBaseTest(windowName) {
     /** {@inheritDoc} */
     override fun doEvaluate(
         tag: Tag,
@@ -30,5 +34,6 @@
         wmSubject.isNonAppWindowInvisible(component)
             .then()
             .isAppWindowVisible(component)
+            .forAllEntries()
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/NonAppWindowIsInvisibleAlways.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/NonAppWindowIsInvisibleAlways.kt
index 2cbc239..da299ef 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/NonAppWindowIsInvisibleAlways.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/NonAppWindowIsInvisibleAlways.kt
@@ -30,6 +30,6 @@
         wmSubject: WindowManagerTraceSubject,
         layerSubject: LayersTraceSubject
     ) {
-        wmSubject.isNonAppWindowInvisible(component)
+        wmSubject.isNonAppWindowInvisible(component).forAllEntries()
     }
 }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/NonAppWindowIsVisibleAlways.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/NonAppWindowIsVisibleAlways.kt
index 6492193..4022f68 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/NonAppWindowIsVisibleAlways.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/NonAppWindowIsVisibleAlways.kt
@@ -30,6 +30,6 @@
         wmSubject: WindowManagerTraceSubject,
         layerSubject: LayersTraceSubject
     ) {
-        wmSubject.isNonAppWindowVisible(component)
+        wmSubject.isNonAppWindowVisible(component).forAllEntries()
     }
 }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/PipWindowBecomesInvisible.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/PipWindowBecomesInvisible.kt
new file mode 100644
index 0000000..6ff2c45
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/PipWindowBecomesInvisible.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 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.service.assertors.common
+
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
+import com.android.server.wm.traces.common.tags.Tag
+
+/**
+ * Checks that [getWindowState] window is pinned and visible at the start and then becomes
+ * unpinned and invisible at the same moment, and remains unpinned and invisible
+ * until the end of the transition
+*/
+class PipWindowBecomesInvisible : AppComponentBaseTest() {
+    /** {@inheritDoc} */
+    override fun doEvaluate(
+        tag: Tag,
+        wmSubject: WindowManagerTraceSubject,
+        layerSubject: LayersTraceSubject
+    ) {
+        val appComponent = getComponentName(tag, wmSubject)
+        wmSubject.invoke("hasPipWindow") {
+            it.isPinned(appComponent).isAppWindowVisible(appComponent)
+        }.then().invoke("!hasPipWindow") {
+            it.isNotPinned(appComponent).isAppWindowInvisible(appComponent)
+        }.forAllEntries()
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/RotationLayerAppearsAndVanishes.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/RotationLayerAppearsAndVanishes.kt
index 1f1c1c7..a9ef4ac 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/RotationLayerAppearsAndVanishes.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/RotationLayerAppearsAndVanishes.kt
@@ -33,7 +33,7 @@
         wmSubject: WindowManagerTraceSubject,
         layerSubject: LayersTraceSubject
     ) {
-        val window = wmSubject.trace.entries.first().topVisibleAppWindow
+        val window = wmSubject.first().wmState.topVisibleAppWindow
         val appComponent = FlickerComponentName.unflattenFromString(window)
         layerSubject.isVisible(appComponent)
             .then()
@@ -41,5 +41,6 @@
             .then()
             .isVisible(appComponent)
             .isInvisible(FlickerComponentName.ROTATION)
+            .forAllEntries()
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/StatusBarLayerPositionAtEnd.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/StatusBarLayerPositionAtEnd.kt
index d26d1c6..271b41c 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/StatusBarLayerPositionAtEnd.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/StatusBarLayerPositionAtEnd.kt
@@ -34,10 +34,11 @@
         wmSubject: WindowManagerTraceSubject,
         layerSubject: LayersTraceSubject
     ) {
-        val endDisplay = layerSubject.last().entry.displays.minByOrNull { it.id }
-            ?: throw RuntimeException("Display not found")
+        val targetSubject = layerSubject.last()
+        val endDisplay = targetSubject.entry.displays.firstOrNull { !it.isVirtual }
+            ?: error("Display not found")
 
-        layerSubject.last().visibleRegion(FlickerComponentName.STATUS_BAR)
+        targetSubject.visibleRegion(FlickerComponentName.STATUS_BAR)
             .coversExactly(WindowUtils.getStatusBarPosition(endDisplay))
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/StatusBarLayerPositionAtStart.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/StatusBarLayerPositionAtStart.kt
index ad89b6a..f71445c 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/StatusBarLayerPositionAtStart.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/StatusBarLayerPositionAtStart.kt
@@ -34,10 +34,11 @@
         wmSubject: WindowManagerTraceSubject,
         layerSubject: LayersTraceSubject
     ) {
-        val startDisplay = layerSubject.first().entry.displays.minByOrNull { it.id }
-            ?: throw RuntimeException("Display not found")
+        val targetSubject = layerSubject.first()
+        val startDisplay = targetSubject.entry.displays.firstOrNull { !it.isVirtual }
+            ?: error("Display not found")
 
-        layerSubject.first().visibleRegion(FlickerComponentName.STATUS_BAR)
+        targetSubject.visibleRegion(FlickerComponentName.STATUS_BAR)
             .coversExactly(WindowUtils.getStatusBarPosition(startDisplay))
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/VisibleLayersShownMoreThanOneConsecutiveEntry.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/VisibleLayersShownMoreThanOneConsecutiveEntry.kt
index 57b39d9..4db60a5 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/VisibleLayersShownMoreThanOneConsecutiveEntry.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/VisibleLayersShownMoreThanOneConsecutiveEntry.kt
@@ -32,6 +32,6 @@
         wmSubject: WindowManagerTraceSubject,
         layerSubject: LayersTraceSubject
     ) {
-        layerSubject.visibleLayersShownMoreThanOneConsecutiveEntry()
+        layerSubject.visibleLayersShownMoreThanOneConsecutiveEntry().forAllEntries()
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/VisibleWindowsShownMoreThanOneConsecutiveEntry.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/VisibleWindowsShownMoreThanOneConsecutiveEntry.kt
index c029479..08ddb2e 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/VisibleWindowsShownMoreThanOneConsecutiveEntry.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/VisibleWindowsShownMoreThanOneConsecutiveEntry.kt
@@ -32,6 +32,6 @@
         wmSubject: WindowManagerTraceSubject,
         layerSubject: LayersTraceSubject
     ) {
-        wmSubject.visibleWindowsShownMoreThanOneConsecutiveEntry()
+        wmSubject.visibleWindowsShownMoreThanOneConsecutiveEntry().forAllEntries()
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/WindowMovesOutOfTop.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/WindowMovesOutOfTop.kt
index b717e3f..72c2658 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/WindowMovesOutOfTop.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/WindowMovesOutOfTop.kt
@@ -33,5 +33,6 @@
         wmSubject.isAppWindowOnTop(component)
             .then()
             .isAppWindowNotOnTop(component)
+            .forAllEntries()
     }
 }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/WindowMovesToTop.kt b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/WindowMovesToTop.kt
index baf5ef2..4636192 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/WindowMovesToTop.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/assertors/common/WindowMovesToTop.kt
@@ -33,5 +33,6 @@
         wmSubject.isAppWindowNotOnTop(component)
             .then()
             .isAppWindowOnTop(component)
+            .forAllEntries()
     }
 }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/service/resources/config.json b/libraries/flicker/src/com/android/server/wm/flicker/service/resources/config.json
index 4768e45..b2eb65b 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/service/resources/config.json
+++ b/libraries/flicker/src/com/android/server/wm/flicker/service/resources/config.json
@@ -5,13 +5,7 @@
       "assertions": {
         "presubmit": [
           {
-            "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowIsInvisibleAlways",
-            "args": [
-              "/StatusBar"
-            ]
-          },
-          {
-            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsInvisibleAlways",
+            "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowIsVisibleAlways",
             "args": [
               "/StatusBar"
             ]
@@ -21,9 +15,6 @@
           },
           {
             "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAtEnd"
-          },
-          {
-            "class": "com.android.server.wm.flicker.service.assertors.common.RotationLayerAppearsAndVanishes"
           }
         ],
         "postsubmit": [],
@@ -35,6 +26,12 @@
             ]
           },
           {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAlways",
+            "args": [
+              "/StatusBar"
+            ]
+          },
+          {
             "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAtStart",
             "args": [
               "/NavigationBar0"
@@ -60,6 +57,9 @@
           },
           {
             "class": "com.android.server.wm.flicker.service.assertors.common.VisibleLayersShownMoreThanOneConsecutiveEntry"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.RotationLayerAppearsAndVanishes"
           }
         ]
       }
@@ -126,9 +126,6 @@
             "class": "com.android.server.wm.flicker.service.assertors.common.AppLayerIsVisibleAtEnd"
           },
           {
-            "class": "com.android.server.wm.flicker.service.assertors.common.AppWindowReplacesLauncherAsTopWindow"
-          },
-          {
             "class": "com.android.server.wm.flicker.service.assertors.common.LauncherWindowMovesOutOfTop"
           }
         ],
@@ -154,6 +151,249 @@
           },
           {
             "class": "com.android.server.wm.flicker.service.assertors.common.VisibleLayersShownMoreThanOneConsecutiveEntry"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppWindowReplacesLauncherAsTopWindow"
+          }
+        ]
+      }
+    },
+    {
+      "transition": "APP_CLOSE",
+      "assertions": {
+        "presubmit": [
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAtStart",
+            "args": [
+              "/NavigationBar0"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowIsVisibleAlways",
+            "args": [
+              "/NavigationBar0"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowIsVisibleAlways",
+            "args": [
+              "/StatusBar"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAlways",
+            "args": [
+              "/StatusBar"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAtStart"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAtEnd"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LauncherWindowReplacesAppAsTopWindow"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowBecomesVisible",
+            "args": [
+              "com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LauncherReplacesAppLayer"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAtEnd",
+            "args": [
+              "com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppLayerIsVisibleAtStart"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppLayerIsInvisibleAtEnd"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LauncherWindowMovesToTop"
+          }
+        ],
+        "postsubmit": [],
+        "flaky": [
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAtEnd",
+            "args": [
+              "/NavigationBar0"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NavBarLayerPositionAtStart"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NavBarLayerPositionAtEnd"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.StatusBarLayerPositionAtStart"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.StatusBarLayerPositionAtEnd"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAlways"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsInvisibleAtStart",
+            "args": [
+              "com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.VisibleWindowsShownMoreThanOneConsecutiveEntry"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.VisibleLayersShownMoreThanOneConsecutiveEntry"
+          }
+        ]
+      }
+    },
+    {
+      "transition": "PIP_ENTER",
+      "assertions": {
+        "presubmit": [
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAlways",
+            "args": [
+              "/NavigationBar0"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowIsVisibleAlways",
+            "args": [
+              "/NavigationBar0"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAlways",
+            "args": [
+              "/StatusBar"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowIsVisibleAlways",
+            "args": [
+              "/StatusBar"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAtStart"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAtEnd"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppLayerIsVisibleAlways"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppWindowIsVisibleAlways"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppWindowRemainInsideDisplayBounds"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppLayerRemainInsideDisplayBounds"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppLayerReduces"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppWindowBecomesPinned"
+          }
+        ],
+        "postsubmit": [],
+        "flaky": [
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NavBarLayerPositionAtStart"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NavBarLayerPositionAtEnd"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.StatusBarLayerPositionAtStart"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.StatusBarLayerPositionAtEnd"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAlways"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowBecomesVisible",
+            "args": [
+              "com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity"
+            ]
+          }
+        ]
+      }
+    },
+    {
+      "transition": "PIP_EXIT",
+      "assertions": {
+        "presubmit": [
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAlways",
+            "args": [
+              "/NavigationBar0"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowIsVisibleAlways",
+            "args": [
+              "/NavigationBar0"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAlways",
+            "args": [
+              "/StatusBar"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowIsVisibleAlways",
+            "args": [
+              "/StatusBar"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAtStart"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAtEnd"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppLayerBecomesInvisible"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.PipWindowBecomesInvisible"
+          }
+        ],
+        "postsubmit": [],
+        "flaky": [
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAlways"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NavBarLayerPositionAtStart"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NavBarLayerPositionAtEnd"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.StatusBarLayerPositionAtStart"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.StatusBarLayerPositionAtEnd"
           }
         ]
       }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/traces/FlickerTraceSubject.kt b/libraries/flicker/src/com/android/server/wm/flicker/traces/FlickerTraceSubject.kt
index b5ad419..414a5ed 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/traces/FlickerTraceSubject.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/traces/FlickerTraceSubject.kt
@@ -30,7 +30,7 @@
     fm: FailureMetadata,
     data: Any?
 ) : FlickerSubject(fm, data) {
-    override val timestamp: Long get() = subjects.first().timestamp
+    override val timestamp: Long get() = subjects.firstOrNull()?.timestamp ?: 0L
     override val selfFacts by lazy {
         val firstTimestamp = subjects.firstOrNull()?.timestamp ?: 0L
         val lastTimestamp = subjects.lastOrNull()?.timestamp ?: 0L
@@ -46,6 +46,14 @@
     abstract val subjects: List<EntrySubject>
 
     /**
+     * Empty the subject's list of assertions.
+     */
+    internal fun clear() {
+        assertionsChecker.clear()
+        newAssertionBlock = true
+    }
+
+    /**
      * Adds a new assertion block (if preceded by [then]) or appends an assertion to the
      * latest existing assertion block
      *
@@ -75,12 +83,12 @@
     /**
      * User-defined entry point for the first trace entry
      */
-    fun first(): EntrySubject = subjects.first()
+    fun first(): EntrySubject = subjects.firstOrNull() ?: error("Trace is empty")
 
     /**
      * User-defined entry point for the last trace entry
      */
-    fun last(): EntrySubject = subjects.last()
+    fun last(): EntrySubject = subjects.lastOrNull() ?: error("Trace is empty")
 
     /**
      * Signal that the last assertion set is complete. The next assertion added will start a new
@@ -122,6 +130,14 @@
      * Checks whether all the trace entries on the list are visible for more than one consecutive
      * entry
      *
+     * Ignore the first and last trace subjects. This is necessary because WM and SF traces
+     * log entries only when a change occurs.
+     *
+     * If the trace starts immediately before an animation or if it stops immediately after one,
+     * the first and last entry may contain elements that are visible only for that entry.
+     * Those elements, however, are not flickers, since they existed on the screen before or after
+     * the test.
+     *
      * @param [visibleEntriesProvider] a list of all the entries with their name and index
      */
     protected fun visibleEntriesShownMoreThanOneConsecutiveTime(
@@ -130,9 +146,18 @@
         if (subjects.isEmpty()) {
             return
         }
+        // Duplicate the first and last trace subjects to prevent them from triggering failures
+        // since WM and SF traces log entries only when a change occurs
+        val firstState = subjects.first()
+        val lastState = subjects.last()
+        val subjects = subjects.toMutableList().also {
+            it.add(lastState)
+            it.add(0, firstState)
+        }
         var lastVisible = visibleEntriesProvider(subjects.first())
         val lastNew = lastVisible.toMutableSet()
 
+        // first subject was already taken
         subjects.drop(1).forEachIndexed { index, entrySubject ->
             val currentVisible = visibleEntriesProvider(entrySubject)
             val newVisible = currentVisible.filter { it !in lastVisible }
@@ -154,4 +179,4 @@
 
     override fun toString(): String = "${this::class.simpleName}" +
             "(${subjects.firstOrNull()?.timestamp ?: 0},${subjects.lastOrNull()?.timestamp ?: 0})"
-}
\ No newline at end of file
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/traces/RegionSubject.kt b/libraries/flicker/src/com/android/server/wm/flicker/traces/RegionSubject.kt
deleted file mode 100644
index d10a9f1..0000000
--- a/libraries/flicker/src/com/android/server/wm/flicker/traces/RegionSubject.kt
+++ /dev/null
@@ -1,604 +0,0 @@
-/*
- * Copyright (C) 2021 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.traces
-
-import androidx.annotation.VisibleForTesting
-import com.android.server.wm.flicker.assertions.FlickerSubject
-import com.android.server.wm.traces.common.Rect
-import com.android.server.wm.traces.common.RectF
-import com.android.server.wm.traces.common.Region
-import com.android.server.wm.traces.parser.toAndroidRect
-import com.android.server.wm.traces.parser.toAndroidRegion
-import com.google.common.truth.Fact
-import com.google.common.truth.FailureMetadata
-import com.google.common.truth.StandardSubjectBuilder
-
-/**
- * Truth subject for [Rect] objects, used to make assertions over behaviors that occur on a
- * rectangle.
- */
-class RegionSubject(
-    fm: FailureMetadata,
-    override val parent: FlickerSubject?,
-    val region: android.graphics.Region
-) : FlickerSubject(fm, region) {
-    override val timestamp: Long get() = parent?.timestamp ?: 0
-    private val topPositionSubject
-        get() = check(MSG_ERROR_TOP_POSITION).that(region.bounds.top)
-    private val bottomPositionSubject
-        get() = check(MSG_ERROR_BOTTOM_POSITION).that(region.bounds.bottom)
-    private val leftPositionSubject
-        get() = check(MSG_ERROR_LEFT_POSITION).that(region.bounds.left)
-    private val rightPositionSubject
-        get() = check(MSG_ERROR_RIGHT_POSITION).that(region.bounds.right)
-    private val areaSubject
-        get() = check(MSG_ERROR_AREA).that(region.bounds.area)
-
-    private val android.graphics.Rect.area get() = this.width() * this.height()
-    private val Rect.area get() = this.width * this.height
-
-    override val selfFacts = listOf(Fact.fact("Region - Covered", region.toString()))
-
-    /**
-     * {@inheritDoc}
-     */
-    override fun clone(): FlickerSubject {
-        return RegionSubject(fm, parent, region)
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    override fun fail(reason: List<Fact>): FlickerSubject {
-        val newReason = reason.toMutableList()
-        return super.fail(newReason)
-    }
-
-    private fun assertLeftRightAndAreaEquals(other: android.graphics.Region) {
-        leftPositionSubject.isEqualTo(other.bounds.left)
-        rightPositionSubject.isEqualTo(other.bounds.right)
-        areaSubject.isEqualTo(other.bounds.area)
-    }
-
-    /**
-     * Subtracts [other] from this subject [region]
-     */
-    fun minus(other: Region): RegionSubject = minus(other.toAndroidRegion())
-
-    /**
-     * Subtracts [other] from this subject [region]
-     */
-    fun minus(other: android.graphics.Region): RegionSubject {
-        val remainingRegion = android.graphics.Region(this.region)
-        remainingRegion.op(other, android.graphics.Region.Op.XOR)
-        return assertThat(remainingRegion, this)
-    }
-
-    /**
-     * Adds [other] to this subject [region]
-     */
-    fun plus(other: Region): RegionSubject = plus(other.toAndroidRegion())
-
-    /**
-     * Adds [other] to this subject [region]
-     */
-    fun plus(other: android.graphics.Region): RegionSubject {
-        val remainingRegion = android.graphics.Region(this.region)
-        remainingRegion.op(other, android.graphics.Region.Op.UNION)
-        return assertThat(remainingRegion, this)
-    }
-
-    /**
-     * Asserts that the top and bottom coordinates of [RegionSubject.region] are smaller
-     * or equal to those of [region].
-     *
-     * Also checks that the left and right positions, as well as area, don't change
-     */
-    fun isHigherOrEqual(subject: RegionSubject): RegionSubject = apply {
-        isHigherOrEqual(subject.region)
-    }
-
-    /**
-     * Asserts that the top and bottom coordinates of [other] are smaller or equal to
-     * those of [region].
-     *
-     * Also checks that the left and right positions, as well as area, don't change
-     */
-    fun isHigherOrEqual(other: Rect): RegionSubject = apply {
-        isHigherOrEqual(other.toAndroidRect())
-    }
-
-    /**
-     * Asserts that the top and bottom coordinates of [other] are smaller or equal to
-     * those of [region].
-     *
-     * Also checks that the left and right positions, as well as area, don't change
-     */
-    fun isHigherOrEqual(other: Region): RegionSubject = apply {
-        isHigherOrEqual(other.toAndroidRegion())
-    }
-
-    /**
-     * Asserts that the top and bottom coordinates of [other] are smaller or equal to
-     * those of [region].
-     *
-     * Also checks that the left and right positions, as well as area, don't change
-     */
-    fun isHigherOrEqual(other: android.graphics.Rect): RegionSubject = apply {
-        isHigherOrEqual(android.graphics.Region(other))
-    }
-
-    /**
-     * Asserts that the top and bottom coordinates of [other] are smaller or equal to
-     * those of [region].
-     *
-     * Also checks that the left and right positions, as well as area, don't change
-     */
-    fun isHigherOrEqual(other: android.graphics.Region): RegionSubject = apply {
-        assertLeftRightAndAreaEquals(other)
-        topPositionSubject.isAtMost(other.bounds.top)
-        bottomPositionSubject.isAtMost(other.bounds.bottom)
-    }
-
-    /**
-     * Asserts that the top and bottom coordinates of [RegionSubject.region] are greater
-     * or equal to those of [region].
-     *
-     * Also checks that the left and right positions, as well as area, don't change
-     */
-    fun isLowerOrEqual(subject: RegionSubject): RegionSubject = apply {
-        isLowerOrEqual(subject.region)
-    }
-
-    /**
-     * Asserts that the top and bottom coordinates of [other] are greater or equal to
-     * those of [region].
-     *
-     * Also checks that the left and right positions, as well as area, don't change
-     */
-    fun isLowerOrEqual(other: Rect): RegionSubject = apply {
-        isLowerOrEqual(other.toAndroidRect())
-    }
-
-    /**
-     * Asserts that the top and bottom coordinates of [other] are greater or equal to
-     * those of [region].
-     *
-     * Also checks that the left and right positions, as well as area, don't change
-     */
-    fun isLowerOrEqual(other: android.graphics.Rect): RegionSubject = apply {
-        isLowerOrEqual(android.graphics.Region(other))
-    }
-
-    /**
-     * Asserts that the top and bottom coordinates of [other] are greater or equal to
-     * those of [region].
-     *
-     * Also checks that the left and right positions, as well as area, don't change
-     */
-    fun isLowerOrEqual(other: android.graphics.Region): RegionSubject = apply {
-        assertLeftRightAndAreaEquals(other)
-        topPositionSubject.isAtLeast(other.bounds.top)
-        bottomPositionSubject.isAtLeast(other.bounds.bottom)
-    }
-
-    /**
-     * Asserts that the top and bottom coordinates of [RegionSubject.region] are smaller than
-     * those of [region].
-     *
-     * Also checks that the left and right positions, as well as area, don't change
-     */
-    fun isHigher(subject: RegionSubject): RegionSubject = apply {
-        isHigher(subject.region)
-    }
-
-    /**
-     * Asserts that the top and bottom coordinates of [other] are smaller than those of [region].
-     *
-     * Also checks that the left and right positions, as well as area, don't change
-     */
-    fun isHigher(other: Rect): RegionSubject = apply {
-        isHigher(other.toAndroidRect())
-    }
-
-    /**
-     * Asserts that the top and bottom coordinates of [other] are smaller than those of [region].
-     *
-     * Also checks that the left and right positions, as well as area, don't change
-     */
-    fun isHigher(other: android.graphics.Rect): RegionSubject = apply {
-        isHigher(android.graphics.Region(other))
-    }
-
-    /**
-     * Asserts that the top and bottom coordinates of [other] are smaller than those of [region].
-     *
-     * Also checks that the left and right positions, as well as area, don't change
-     */
-    fun isHigher(other: android.graphics.Region): RegionSubject = apply {
-        assertLeftRightAndAreaEquals(other)
-        topPositionSubject.isLessThan(other.bounds.top)
-        bottomPositionSubject.isLessThan(other.bounds.bottom)
-    }
-
-    /**
-     * Asserts that the top and bottom coordinates of [RegionSubject.region] are greater than
-     * those of [region].
-     *
-     * Also checks that the left and right positions, as well as area, don't change
-     */
-    fun isLower(subject: RegionSubject): RegionSubject = apply {
-        isLower(subject.region)
-    }
-
-    /**
-     * Asserts that the top and bottom coordinates of [other] are greater than those of [region].
-     *
-     * Also checks that the left and right positions, as well as area, don't change
-     */
-    fun isLower(other: Rect): RegionSubject = apply {
-        isLower(other.toAndroidRect())
-    }
-
-    /**
-     * Asserts that the top and bottom coordinates of [other] are greater than those of [region].
-     *
-     * Also checks that the left and right positions, as well as area, don't change
-     */
-    fun isLower(other: android.graphics.Rect): RegionSubject = apply {
-        isLower(android.graphics.Region(other))
-    }
-
-    /**
-     * Asserts that the top and bottom coordinates of [other] are greater than those of [region].
-     *
-     * Also checks that the left and right positions, as well as area, don't change
-     */
-    fun isLower(other: android.graphics.Region): RegionSubject = apply {
-        assertLeftRightAndAreaEquals(other)
-        topPositionSubject.isGreaterThan(other.bounds.top)
-        bottomPositionSubject.isGreaterThan(other.bounds.bottom)
-    }
-
-    /**
-     * Asserts that [region] covers at most [testRegion], that is, its area doesn't cover any
-     * point outside of [testRegion].
-     *
-     * @param testRegion Expected covered area
-     */
-    fun coversAtMost(testRegion: android.graphics.Region): RegionSubject = apply {
-        val testRect = testRegion.bounds
-        val intersection = android.graphics.Region(region)
-        val covers = intersection.op(testRect, android.graphics.Region.Op.INTERSECT) &&
-            !intersection.op(region, android.graphics.Region.Op.XOR)
-
-        if (!covers) {
-            fail(Fact.fact("Region to test", testRegion),
-                Fact.fact("Covered region", region),
-                Fact.fact("Out-of-bounds region", intersection))
-        }
-    }
-
-    /**
-     * Asserts that [region] covers at most [testRegion], that is, its area doesn't cover any
-     * point outside of [testRegion].
-     *
-     * @param testRegion Expected covered area
-     */
-    fun coversAtMost(testRegion: Region): RegionSubject = apply {
-        coversAtMost(testRegion.toAndroidRegion())
-    }
-
-    /**
-     * Asserts that [region] covers at most [testRect], that is, its area doesn't cover any
-     * point outside of [testRect].
-     *
-     * @param testRect Expected covered area
-     */
-    fun coversAtMost(testRect: Rect): RegionSubject = apply {
-        coversAtMost(Region(testRect))
-    }
-
-    /**
-     * Asserts that [region] covers at most [testRect], that is, its area doesn't cover any
-     * point outside of [testRect].
-     *
-     * @param testRect Expected covered area
-     */
-    fun coversAtMost(testRect: android.graphics.Rect): RegionSubject = apply {
-        coversAtMost(android.graphics.Region(testRect))
-    }
-
-    /**
-     * Asserts that [region] covers at least [testRegion], that is, its area covers each point
-     * in the region
-     *
-     * @param testRegion Expected covered area
-     */
-    fun coversAtLeast(testRegion: android.graphics.Region): RegionSubject = apply {
-        val intersection = android.graphics.Region(region)
-        val covers = intersection.op(testRegion, android.graphics.Region.Op.INTERSECT) &&
-            !intersection.op(testRegion, android.graphics.Region.Op.XOR)
-
-        if (!covers) {
-            fail(Fact.fact("Region to test", testRegion),
-                Fact.fact("Covered region", region),
-                Fact.fact("Uncovered region", intersection))
-        }
-    }
-
-    /**
-     * Asserts that [region] covers at least [testRegion], that is, its area covers each point
-     * in the region
-     *
-     * @param testRegion Expected covered area
-     */
-    fun coversAtLeast(testRegion: Region): RegionSubject = apply {
-        coversAtLeast(testRegion.toAndroidRegion())
-    }
-
-    /**
-     * Asserts that [region] covers at least [testRect], that is, its area covers each point
-     * in the region
-     *
-     * @param testRect Expected covered area
-     */
-    fun coversAtLeast(testRect: Rect): RegionSubject = apply {
-        coversAtLeast(Region(testRect))
-    }
-
-    /**
-     * Asserts that [region] covers at least [testRect], that is, its area covers each point
-     * in the region
-     *
-     * @param testRect Expected covered area
-     */
-    fun coversAtLeast(testRect: android.graphics.Rect): RegionSubject = apply {
-        coversAtLeast(android.graphics.Region(testRect))
-    }
-
-    /**
-     * Asserts that [region] covers at exactly [testRegion]
-     *
-     * @param testRegion Expected covered area
-     */
-    fun coversExactly(testRegion: android.graphics.Region): RegionSubject = apply {
-        val intersection = android.graphics.Region(region)
-        val isNotEmpty = intersection.op(testRegion, android.graphics.Region.Op.XOR)
-
-        if (isNotEmpty) {
-            fail(Fact.fact("Region to test", testRegion),
-                Fact.fact("Covered region", region),
-                Fact.fact("Uncovered region", intersection))
-        }
-    }
-
-    /**
-     * Asserts that [region] covers at exactly [testRegion]
-     *
-     * @param testRegion Expected covered area
-     */
-    fun coversExactly(testRegion: Region): RegionSubject = apply {
-        coversExactly(testRegion.toAndroidRegion())
-    }
-
-    /**
-     * Asserts that [region] covers at exactly [testRect]
-     *
-     * @param testRect Expected covered area
-     */
-    fun coversExactly(testRect: Rect): RegionSubject = apply {
-        coversExactly(testRect.toAndroidRect())
-    }
-
-    /**
-     * Asserts that [region] covers at exactly [testRect]
-     *
-     * @param testRect Expected covered area
-     */
-    fun coversExactly(testRect: android.graphics.Rect): RegionSubject = apply {
-        coversExactly(android.graphics.Region(testRect))
-    }
-
-    /**
-     * Asserts that [region] and [testRegion] overlap
-     *
-     * @param testRegion Other area
-     */
-    fun overlaps(testRegion: android.graphics.Region): RegionSubject = apply {
-        val intersection = android.graphics.Region(region)
-        val isEmpty = !intersection.op(testRegion, android.graphics.Region.Op.INTERSECT)
-
-        if (isEmpty) {
-            fail(Fact.fact("Region to test", testRegion),
-                Fact.fact("Covered region", region),
-                Fact.fact("Overlap region", intersection))
-        }
-    }
-
-    /**
-     * Asserts that [region] and [testRegion] overlap
-     *
-     * @param testRegion Other area
-     */
-    fun overlaps(testRegion: Region): RegionSubject = apply {
-        overlaps(testRegion.toAndroidRegion())
-    }
-
-    /**
-     * Asserts that [region] and [testRect] overlap
-     *
-     * @param testRect Other area
-     */
-    fun overlaps(testRect: android.graphics.Rect): RegionSubject = apply {
-        overlaps(android.graphics.Region(testRect))
-    }
-
-    /**
-     * Asserts that [region] and [testRect] overlap
-     *
-     * @param testRect Other area
-     */
-    fun overlaps(testRect: Rect): RegionSubject = apply {
-        overlaps(testRect.toAndroidRect())
-    }
-
-    /**
-     * Asserts that [region] and [testRegion] don't overlap
-     *
-     * @param testRegion Other area
-     */
-    fun notOverlaps(testRegion: android.graphics.Region): RegionSubject = apply {
-        val intersection = android.graphics.Region(region)
-        val isEmpty = !intersection.op(testRegion, android.graphics.Region.Op.INTERSECT)
-
-        if (!isEmpty) {
-            fail(Fact.fact("Region to test", testRegion),
-                Fact.fact("Covered region", region),
-                Fact.fact("Overlap region", intersection))
-        }
-    }
-
-    /**
-     * Asserts that [region] and [testRegion] don't overlap
-     *
-     * @param testRegion Other area
-     */
-    fun notOverlaps(testRegion: Region): RegionSubject = apply {
-        notOverlaps(testRegion.toAndroidRegion())
-    }
-
-    /**
-     * Asserts that [region] and [testRect] don't overlap
-     *
-     * @param testRect Other area
-     */
-    fun notOverlaps(testRect: android.graphics.Rect): RegionSubject = apply {
-        notOverlaps(android.graphics.Region(testRect))
-    }
-
-    /**
-     * Asserts that [region] and [testRect] don't overlap
-     *
-     * @param testRect Other area
-     */
-    fun notOverlaps(testRect: Rect): RegionSubject = apply {
-        notOverlaps(testRect.toAndroidRect())
-    }
-
-    companion object {
-        @VisibleForTesting
-        const val MSG_ERROR_TOP_POSITION = "Incorrect top position"
-
-        @VisibleForTesting
-        const val MSG_ERROR_BOTTOM_POSITION = "Incorrect top position"
-
-        @VisibleForTesting
-        const val MSG_ERROR_LEFT_POSITION = "Incorrect left position"
-
-        @VisibleForTesting
-        const val MSG_ERROR_RIGHT_POSITION = "Incorrect right position"
-
-        @VisibleForTesting
-        const val MSG_ERROR_AREA = "Incorrect rect area"
-
-        private fun mergeRegions(regions: Array<Region>): android.graphics.Region {
-            val result = android.graphics.Region()
-            regions.forEach { region ->
-                region.rects.forEach { rect ->
-                    result.op(rect.toAndroidRect(), android.graphics.Region.Op.UNION)
-                }
-            }
-            return result
-        }
-
-        /**
-         * Boiler-plate Subject.Factory for RectSubject
-         */
-        @JvmStatic
-        fun getFactory(
-            parent: FlickerSubject?
-        ) = Factory { fm: FailureMetadata, region: android.graphics.Region? ->
-            val subjectRegion = region ?: android.graphics.Region()
-            RegionSubject(fm, parent, subjectRegion)
-        }
-
-        /**
-         * User-defined entry point for existing android regions
-         */
-        @JvmStatic
-        fun assertThat(
-            region: android.graphics.Region?,
-            parent: FlickerSubject? = null
-        ): RegionSubject {
-            val strategy = FlickerFailureStrategy()
-            val subject = StandardSubjectBuilder.forCustomFailureStrategy(strategy)
-                .about(getFactory(parent))
-                .that(region ?: android.graphics.Region()) as RegionSubject
-            strategy.init(subject)
-            return subject
-        }
-
-        /**
-         * User-defined entry point for existing rects
-         */
-        @JvmStatic
-        @JvmOverloads
-        fun assertThat(rect: Array<Rect>, parent: FlickerSubject? = null): RegionSubject =
-                assertThat(Region(rect), parent)
-
-        /**
-         * User-defined entry point for existing rects
-         */
-        @JvmStatic
-        @JvmOverloads
-        fun assertThat(rect: Rect?, parent: FlickerSubject? = null): RegionSubject =
-                assertThat(Region(rect), parent)
-
-        /**
-         * User-defined entry point for existing rects
-         */
-        @JvmStatic
-        @JvmOverloads
-        fun assertThat(rect: RectF?, parent: FlickerSubject? = null): RegionSubject =
-                assertThat(rect?.toRect(), parent)
-
-        /**
-         * User-defined entry point for existing rects
-         */
-        @JvmStatic
-        @JvmOverloads
-        fun assertThat(rect: Array<RectF>, parent: FlickerSubject? = null): RegionSubject =
-            assertThat(mergeRegions(rect.map { Region(it.toRect()) }.toTypedArray()), parent)
-
-        /**
-         * User-defined entry point for existing regions
-         */
-        @JvmStatic
-        @JvmOverloads
-        fun assertThat(regions: Array<Region>, parent: FlickerSubject? = null): RegionSubject =
-                assertThat(mergeRegions(regions), parent)
-
-        /**
-         * User-defined entry point for existing regions
-         */
-        @JvmStatic
-        @JvmOverloads
-        fun assertThat(region: Region?, parent: FlickerSubject? = null): RegionSubject =
-                assertThat(region?.toAndroidRegion(), parent)
-    }
-}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/traces/eventlog/EventLogSubject.kt b/libraries/flicker/src/com/android/server/wm/flicker/traces/eventlog/EventLogSubject.kt
index 8036730..4024163 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/traces/eventlog/EventLogSubject.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/traces/eventlog/EventLogSubject.kt
@@ -34,19 +34,14 @@
     override val timestamp: Long get() = 0
     override val parent: FlickerSubject? get() = null
     override val selfFacts by lazy {
-        val firstTimestamp = subjects.first().timestamp
-        val lastTimestamp = subjects.last().timestamp
+        val firstTimestamp = subjects.firstOrNull()?.timestamp ?: 0L
+        val lastTimestamp = subjects.lastOrNull()?.timestamp ?: 0L
         val first = "${prettyTimestamp(firstTimestamp)} (timestamp=$firstTimestamp)"
         val last = "${prettyTimestamp(lastTimestamp)} (timestamp=$lastTimestamp)"
         listOf(Fact.fact("Trace start", first),
                 Fact.fact("Trace end", last))
     }
 
-    /** {@inheritDoc} */
-    override fun clone(): FlickerSubject {
-        return EventLogSubject(fm, trace)
-    }
-
     private val subjects by lazy {
         trace.map { FocusEventSubject.assertThat(it, this) }
     }
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/traces/eventlog/FocusEventSubject.kt b/libraries/flicker/src/com/android/server/wm/flicker/traces/eventlog/FocusEventSubject.kt
index 1f691eb..7aa6217 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/traces/eventlog/FocusEventSubject.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/traces/eventlog/FocusEventSubject.kt
@@ -30,11 +30,6 @@
     override val timestamp: Long get() = 0
     override val selfFacts by lazy { listOf(Fact.simpleFact(event.toString())) }
 
-    /** {@inheritDoc} */
-    override fun clone(): FlickerSubject {
-        return FocusEventSubject(fm, event, parent)
-    }
-
     fun hasFocus() {
         check("Does not have focus")
             .that(event.hasFocus())
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/traces/layers/LayerSubject.kt b/libraries/flicker/src/com/android/server/wm/flicker/traces/layers/LayerSubject.kt
index 614cd9a..8bdc0bc 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/traces/layers/LayerSubject.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/traces/layers/LayerSubject.kt
@@ -17,9 +17,9 @@
 
 import android.graphics.Point
 import com.android.server.wm.flicker.assertions.Assertion
-import com.android.server.wm.flicker.traces.FlickerFailureStrategy
 import com.android.server.wm.flicker.assertions.FlickerSubject
-import com.android.server.wm.flicker.traces.RegionSubject
+import com.android.server.wm.flicker.traces.FlickerFailureStrategy
+import com.android.server.wm.flicker.traces.region.RegionSubject
 import com.android.server.wm.traces.common.Size
 import com.android.server.wm.traces.common.layers.Layer
 import com.google.common.truth.Fact
@@ -64,13 +64,14 @@
      * Visible region calculated by the Composition Engine
      */
     val visibleRegion: RegionSubject get() =
-        RegionSubject.assertThat(layer?.visibleRegion, this)
+        RegionSubject.assertThat(layer?.visibleRegion, this, timestamp)
+
     /**
      * Visible region calculated by the Composition Engine (when available) or calculated
      * based on the layer bounds and transform
      */
     val screenBounds: RegionSubject get() =
-        RegionSubject.assertThat(layer?.screenBounds, this)
+        RegionSubject.assertThat(layer?.screenBounds, this, timestamp)
 
     override val selfFacts = if (layer != null) {
         listOf(Fact.fact("Frame", layer.currFrame), Fact.fact("Layer", layer.name))
@@ -86,11 +87,6 @@
         assertion(this.layer)
     }
 
-    /** {@inheritDoc} */
-    override fun clone(): FlickerSubject {
-        return LayerSubject(fm, parent, timestamp, layer, layerName)
-    }
-
     /**
      * Asserts that current subject doesn't exist in the layer hierarchy
      */
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/traces/layers/LayerTraceEntrySubject.kt b/libraries/flicker/src/com/android/server/wm/flicker/traces/layers/LayerTraceEntrySubject.kt
index 8f35477..8b8112e 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/traces/layers/LayerTraceEntrySubject.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/traces/layers/LayerTraceEntrySubject.kt
@@ -20,10 +20,10 @@
 import com.android.server.wm.flicker.assertions.FlickerSubject
 import com.android.server.wm.flicker.traces.FlickerFailureStrategy
 import com.android.server.wm.flicker.traces.FlickerSubjectException
-import com.android.server.wm.flicker.traces.RegionSubject
+import com.android.server.wm.flicker.traces.region.RegionSubject
 import com.android.server.wm.traces.common.FlickerComponentName
 import com.android.server.wm.traces.common.layers.Layer
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.layers.LayersTrace
 import com.google.common.truth.ExpectFailure
 import com.google.common.truth.Fact
@@ -33,7 +33,7 @@
 import com.google.common.truth.Subject
 
 /**
- * Truth subject for [LayerTraceEntry] objects, used to make assertions over behaviors that
+ * Truth subject for [BaseLayerTraceEntry] objects, used to make assertions over behaviors that
  * occur on a single SurfaceFlinger state.
  *
  * To make assertions over a specific state from a trace it is recommended to create a subject
@@ -56,7 +56,7 @@
  */
 class LayerTraceEntrySubject private constructor(
     fm: FailureMetadata,
-    val entry: LayerTraceEntry,
+    val entry: BaseLayerTraceEntry,
     val trace: LayersTrace?,
     override val parent: FlickerSubject?
 ) : FlickerSubject(fm, entry) {
@@ -70,14 +70,10 @@
     /**
      * Executes a custom [assertion] on the current subject
      */
-    operator fun invoke(assertion: Assertion<LayerTraceEntry>): LayerTraceEntrySubject = apply {
-        assertion(this.entry)
-    }
-
-    /** {@inheritDoc} */
-    override fun clone(): FlickerSubject {
-        return LayerTraceEntrySubject(fm, entry, trace, parent)
-    }
+    operator fun invoke(assertion: Assertion<BaseLayerTraceEntry>): LayerTraceEntrySubject =
+        apply {
+            assertion(this.entry)
+        }
 
     /**
      * Asserts that the current SurfaceFlinger state doesn't contain layers
@@ -98,36 +94,46 @@
     }
 
     /**
-     * Obtains the region occupied by all layers with name containing [component]
+     * Obtains the region occupied by all layers with name containing [components]
      *
-     * @param component Component to search
+     * @param components Components to search for
      * @param useCompositionEngineRegionOnly If true, uses only the region calculated from the
      *   Composition Engine (CE) -- visibleRegion in the proto definition. Otherwise calculates
      *   the visible region when the information is not available from the CE
      */
+    @JvmOverloads
     fun visibleRegion(
-        component: FlickerComponentName? = null,
+        vararg components: FlickerComponentName,
         useCompositionEngineRegionOnly: Boolean = true
     ): RegionSubject {
-        val layerName = component?.toLayerName() ?: ""
-        val selectedLayers = subjects
-            .filter { it.name.contains(layerName) }
+        val layerNames = components.map { it.toLayerName() }
+        val selectedLayers = if (components.isEmpty()) {
+            // No filters so use all subjects
+            subjects
+        } else {
+            subjects.filter {
+                subject -> layerNames.any {
+                    layerName -> subject.name.contains(layerName)
+                }
+            }
+        }
 
         if (selectedLayers.isEmpty()) {
+            val str = if (layerNames.isNotEmpty()) layerNames.joinToString() else "<any>"
             fail(listOf(
-                Fact.fact(ASSERTION_TAG, "visibleRegion(${component?.toLayerName() ?: "<any>"})"),
+                Fact.fact(ASSERTION_TAG, "visibleRegion($str)"),
                 Fact.fact("Use composition engine region", useCompositionEngineRegionOnly),
-                Fact.fact("Could not find", layerName))
+                Fact.fact("Could not find layers", str))
             )
         }
 
         val visibleLayers = selectedLayers.filter { it.isVisible }
         return if (useCompositionEngineRegionOnly) {
             val visibleAreas = visibleLayers.mapNotNull { it.layer?.visibleRegion }.toTypedArray()
-            RegionSubject.assertThat(visibleAreas, this)
+            RegionSubject.assertThat(visibleAreas, this, timestamp)
         } else {
             val visibleAreas = visibleLayers.mapNotNull { it.layer?.screenBounds }.toTypedArray()
-            RegionSubject.assertThat(visibleAreas, this)
+            RegionSubject.assertThat(visibleAreas, this, timestamp)
         }
     }
 
@@ -165,28 +171,32 @@
     fun isVisible(component: FlickerComponentName): LayerTraceEntrySubject = apply {
         contains(component)
         var target: FlickerSubject? = null
-        var reason: Fact? = null
+        var reason = listOf<Fact>()
         val layerName = component.toLayerName()
         val filteredLayers = subjects
             .filter { it.name.contains(layerName) }
         for (layer in filteredLayers) {
             if (layer.layer?.isHiddenByParent == true) {
-                reason = Fact.fact("Hidden by parent", layer.layer.parent?.name)
+                reason = listOf(Fact.fact("Hidden by parent", layer.layer.parent?.name))
                 target = layer
                 continue
             }
             if (layer.isInvisible) {
-                reason = Fact.fact("Is Invisible", layer.layer?.visibilityReason)
+                reason = layer.layer?.visibilityReason
+                    ?.map { Fact.fact("Is Invisible", it) }
+                    ?: emptyList()
                 target = layer
                 continue
             }
-            reason = null
+            reason = emptyList()
             target = null
             break
         }
 
-        reason?.run {
-            target?.fail(Fact.fact(ASSERTION_TAG, "isVisible(${component.toLayerName()})"), reason)
+        if (reason.isNotEmpty()) {
+            target?.fail(
+                Fact.fact(ASSERTION_TAG, "isVisible(${component.toLayerName()})"),
+                *reason.toTypedArray())
         }
     }
 
@@ -213,6 +223,52 @@
     }
 
     /**
+     * Asserts that the entry contains a visible splash screen [Layer] for a [layer] with
+     * [Layer.name] containing any of [component].
+     *
+     * @param component Name of the layer to search
+     */
+    fun isSplashScreenVisibleFor(component: FlickerComponentName): LayerTraceEntrySubject = apply {
+        var target: FlickerSubject? = null
+        var reason: Fact? = null
+        val layerActivityRecordFilter = component.toActivityRecordFilter()
+        val filteredLayers = subjects
+                .filter { layerActivityRecordFilter.containsMatchIn(it.name) }
+
+        if (filteredLayers.isEmpty()) {
+            fail(Fact.fact(ASSERTION_TAG, "isSplashScreenVisibleFor(${component.toLayerName()})"),
+                Fact.fact("Could not find Activity Record layer", component.toShortWindowName()))
+            return this
+        }
+
+        // Check the matched activity record layers for containing splash screens
+        for (layer in filteredLayers) {
+            val splashScreenContainers =
+                layer.layer?.children?.filter { it.name.contains("Splash Screen") }
+            val splashScreenLayers = splashScreenContainers?.flatMap {
+                it.children.filter { childLayer ->
+                    childLayer.name.contains("Splash Screen")
+                }
+            }
+
+            if (splashScreenLayers?.all { it.isHiddenByParent || !it.isVisible } ?: true) {
+                reason = Fact.fact("No splash screen visible for", layer.name)
+                target = layer
+                continue
+            }
+            reason = null
+            target = null
+            break
+        }
+
+        reason?.run {
+            target?.fail(
+                Fact.fact(ASSERTION_TAG, "isSplashScreenVisibleFor(${component.toLayerName()})"),
+                reason)
+        }
+    }
+
+    /**
      * Obtains a [LayerSubject] for the first occurrence of a [Layer] with [Layer.name]
      * containing [component].
      * Always returns a subject, event when the layer doesn't exist. To verify if layer
@@ -272,7 +328,7 @@
         private fun getFactory(
             trace: LayersTrace?,
             parent: FlickerSubject?
-        ): Factory<Subject, LayerTraceEntry> =
+        ): Factory<Subject, BaseLayerTraceEntry> =
             Factory { fm, subject -> LayerTraceEntrySubject(fm, subject, trace, parent) }
 
         /**
@@ -285,7 +341,7 @@
         @JvmStatic
         @JvmOverloads
         fun assertThat(
-            entry: LayerTraceEntry,
+            entry: BaseLayerTraceEntry,
             trace: LayersTrace? = null,
             parent: FlickerSubject? = null
         ): LayerTraceEntrySubject {
@@ -305,6 +361,6 @@
         fun entries(
             trace: LayersTrace? = null,
             parent: FlickerSubject? = null
-        ): Factory<Subject, LayerTraceEntry> = getFactory(trace, parent)
+        ): Factory<Subject, BaseLayerTraceEntry> = getFactory(trace, parent)
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/traces/layers/LayersTraceSubject.kt b/libraries/flicker/src/com/android/server/wm/flicker/traces/layers/LayersTraceSubject.kt
index 39c8c62..95e27e5 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/traces/layers/LayersTraceSubject.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/traces/layers/LayersTraceSubject.kt
@@ -16,18 +16,14 @@
 
 package com.android.server.wm.flicker.traces.layers
 
-import android.graphics.Rect
-import android.graphics.Region
 import com.android.server.wm.flicker.assertions.Assertion
-import com.android.server.wm.flicker.assertions.FlickerSubject
 import com.android.server.wm.flicker.traces.FlickerFailureStrategy
 import com.android.server.wm.flicker.traces.FlickerTraceSubject
+import com.android.server.wm.flicker.traces.region.RegionTraceSubject
 import com.android.server.wm.traces.common.FlickerComponentName
 import com.android.server.wm.traces.common.layers.Layer
 import com.android.server.wm.traces.common.layers.LayersTrace
-import com.android.server.wm.traces.parser.toAndroidRect
-import com.android.server.wm.traces.parser.toAndroidRegion
-import com.google.common.truth.Fact
+import com.android.server.wm.traces.common.region.RegionTrace
 import com.google.common.truth.FailureMetadata
 import com.google.common.truth.FailureStrategy
 import com.google.common.truth.StandardSubjectBuilder
@@ -64,11 +60,6 @@
 ) : FlickerTraceSubject<LayerTraceEntrySubject>(fm, trace) {
     override val selfFacts
         get() = super.selfFacts.toMutableList()
-            .also {
-                if (trace.hasSource()) {
-                    it.add(Fact.fact("Trace file", trace.source))
-                }
-            }
     override val subjects by lazy {
         trace.entries.map { LayerTraceEntrySubject.assertThat(it, trace, this) }
     }
@@ -81,11 +72,6 @@
     }
 
     /** {@inheritDoc} */
-    override fun clone(): FlickerSubject {
-        return LayersTraceSubject(fm, trace, parent)
-    }
-
-    /** {@inheritDoc} */
     override fun then(): LayersTraceSubject = apply { super.then() }
 
     fun isEmpty(): LayersTraceSubject = apply {
@@ -104,7 +90,8 @@
         return subjects
             .map { it.layer(name, frameNumber) }
             .firstOrNull { it.isNotEmpty }
-            ?: LayerSubject.assertThat(null, this, timestamp = subjects.first().entry.timestamp)
+            ?: LayerSubject.assertThat(null, this,
+                timestamp = subjects.firstOrNull()?.entry?.timestamp ?: 0L)
     }
 
     /**
@@ -126,126 +113,6 @@
     }
 
     /**
-     * Asserts that the visible area covered by any [Layer] with [Layer.name] containing any of
-     * [component] covers at least [testRegion], that is, if its area of the layer's visible
-     * region covers each point in the region.
-     *
-     * @param testRegion Expected covered area
-     * @param component Name of the layer to search
-     */
-    @JvmOverloads
-    fun coversAtLeast(
-        testRegion: Rect,
-        component: FlickerComponentName? = null
-    ): LayersTraceSubject = this.coversAtLeast(Region(testRegion), component)
-
-    /**
-     * Asserts that the visible area covered by any [Layer] with [Layer.name] containing any of
-     * [component] covers at least [testRegion], that is, if its area of the layer's visible
-     * region covers each point in the region.
-     *
-     * @param testRegion Expected covered area
-     * @param component Name of the layer to search
-     */
-    @JvmOverloads
-    fun coversAtLeast(
-        testRegion: com.android.server.wm.traces.common.Rect,
-        component: FlickerComponentName? = null
-    ): LayersTraceSubject = this.coversAtLeast(testRegion.toAndroidRect(), component)
-
-    /**
-     * Asserts that the visible area covered by any [Layer] with [Layer.name] containing any of
-     * [component] covers at most [testRegion], that is, if the area of any layer doesn't
-     * cover any point outside of [testRegion].
-     *
-     * @param testRegion Expected covered area
-     * @param component Name of the layer to search
-     */
-    @JvmOverloads
-    fun coversAtMost(
-        testRegion: Rect,
-        component: FlickerComponentName? = null
-    ): LayersTraceSubject = this.coversAtMost(Region(testRegion), component)
-
-    /**
-     * Asserts that the visible area covered by any [Layer] with [Layer.name] containing any of
-     * [component] covers at most [testRegion], that is, if the area of any layer doesn't
-     * cover any point outside of [testRegion].
-     *
-     * @param testRegion Expected covered area
-     * @param component Name of the layer to search
-     */
-    @JvmOverloads
-    fun coversAtMost(
-        testRegion: com.android.server.wm.traces.common.Rect,
-        component: FlickerComponentName? = null
-    ): LayersTraceSubject = this.coversAtMost(testRegion.toAndroidRect(), component)
-
-    /**
-     * Asserts that the visible area covered by any [Layer] with [Layer.name] containing any of
-     * [component] covers at least [testRegion], that is, if its area of the layer's visible
-     * region covers each point in the region.
-     *
-     * @param testRegion Expected covered area
-     * @param component Name of the layer to search
-     */
-    @JvmOverloads
-    fun coversAtLeast(
-        testRegion: Region,
-        component: FlickerComponentName? = null
-    ): LayersTraceSubject = apply {
-        addAssertion("coversAtLeast($testRegion, ${component?.toLayerName()})") {
-            it.visibleRegion(component).coversAtLeast(testRegion)
-        }
-    }
-
-    /**
-     * Asserts that the visible area covered by any [Layer] with [Layer.name] containing any of
-     * [component] covers at least [testRegion], that is, if its area of the layer's visible
-     * region covers each point in the region.
-     *
-     * @param testRegion Expected covered area
-     * @param component Name of the layer to search
-     */
-    @JvmOverloads
-    fun coversAtLeast(
-        testRegion: com.android.server.wm.traces.common.Region,
-        component: FlickerComponentName? = null
-    ): LayersTraceSubject = this.coversAtLeast(testRegion.toAndroidRegion(), component)
-
-    /**
-     * Asserts that the visible area covered by any [Layer] with [Layer.name] containing any of
-     * [component] covers at most [testRegion], that is, if the area of any layer doesn't
-     * cover any point outside of [testRegion].
-     *
-     * @param testRegion Expected covered area
-     * @param component Name of the layer to search
-     */
-    @JvmOverloads
-    fun coversAtMost(
-        testRegion: Region,
-        component: FlickerComponentName? = null
-    ): LayersTraceSubject = apply {
-        addAssertion("coversAtMost($testRegion, ${component?.toLayerName()}") {
-            it.visibleRegion(component).coversAtMost(testRegion)
-        }
-    }
-
-    /**
-     * Asserts that the visible area covered by any [Layer] with [Layer.name] containing any of
-     * [component] covers at most [testRegion], that is, if the area of any layer doesn't
-     * cover any point outside of [testRegion].
-     *
-     * @param testRegion Expected covered area
-     * @param component Name of the layer to search
-     */
-    @JvmOverloads
-    fun coversAtMost(
-        testRegion: com.android.server.wm.traces.common.Region,
-        component: FlickerComponentName? = null
-    ): LayersTraceSubject = this.coversAtMost(testRegion.toAndroidRegion(), component)
-
-    /**
      * Checks that all visible layers are shown for more than one consecutive entry
      */
     @JvmOverloads
@@ -262,23 +129,6 @@
     }
 
     /**
-     * Asserts that a [Layer] with [Layer.name] containing any of [component] has a visible region
-     * of exactly [expectedVisibleRegion] in trace entries.
-     *
-     * @param component Name of the layer to search
-     * @param expectedVisibleRegion Expected visible region of the layer
-     */
-    @JvmOverloads
-    fun coversExactly(
-        expectedVisibleRegion: Region,
-        component: FlickerComponentName? = null
-    ): LayersTraceSubject = apply {
-        addAssertion("coversExactly($component$expectedVisibleRegion)") {
-            it.visibleRegion(component).coversExactly(expectedVisibleRegion)
-        }
-    }
-
-    /**
      * Asserts that each entry in the trace doesn't contain a [Layer] with [Layer.name]
      * containing [component].
      *
@@ -345,6 +195,42 @@
         }
 
     /**
+     * Asserts that each entry in the trace contains a visible splash screen [Layer] for a [layer]
+     * with [Layer.name] containing any of [component].
+     *
+     * @param component Name of the layer to search
+     */
+    @JvmOverloads
+    fun isSplashScreenVisibleFor(
+        component: FlickerComponentName,
+        isOptional: Boolean = false
+    ): LayersTraceSubject = apply {
+        addAssertion("isSplashScreenVisibleFor(${component.toLayerName()})", isOptional) {
+            it.isSplashScreenVisibleFor(component)
+        }
+    }
+
+    /**
+     * Obtains the trace of regions occupied by all layers with name containing [components]
+     *
+     * @param components Components to search for
+     * @param useCompositionEngineRegionOnly If true, uses only the region calculated from the
+     *   Composition Engine (CE) -- visibleRegion in the proto definition. Otherwise calculates
+     *   the visible region when the information is not available from the CE
+     */
+    @JvmOverloads
+    fun visibleRegion(
+        vararg components: FlickerComponentName,
+        useCompositionEngineRegionOnly: Boolean = true
+    ): RegionTraceSubject {
+        val regionTrace = RegionTrace(components, subjects.map {
+            it.visibleRegion(components = components, useCompositionEngineRegionOnly)
+                            .regionEntry
+        }.toTypedArray())
+        return RegionTraceSubject.assertThat(regionTrace, this)
+    }
+
+    /**
      * Executes a custom [assertion] on the current subject
      */
     operator fun invoke(
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/traces/region/RegionSubject.kt b/libraries/flicker/src/com/android/server/wm/flicker/traces/region/RegionSubject.kt
new file mode 100644
index 0000000..097402c
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/traces/region/RegionSubject.kt
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2021 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.traces.region
+
+import androidx.annotation.VisibleForTesting
+import com.android.server.wm.flicker.assertions.FlickerSubject
+import com.android.server.wm.flicker.traces.FlickerFailureStrategy
+import com.android.server.wm.traces.common.Rect
+import com.android.server.wm.traces.common.RectF
+import com.android.server.wm.traces.common.region.Region
+import com.android.server.wm.traces.common.region.RegionEntry
+import com.google.common.truth.Fact
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.StandardSubjectBuilder
+import com.google.common.truth.Subject.Factory
+import kotlin.math.abs
+
+/**
+ * Truth subject for [Rect] objects, used to make assertions over behaviors that occur on a
+ * rectangle.
+ */
+class RegionSubject(
+    fm: FailureMetadata,
+    override val parent: FlickerSubject?,
+    val regionEntry: RegionEntry,
+    override val timestamp: Long
+) : FlickerSubject(fm, regionEntry) {
+
+    val region = regionEntry.region
+
+    private val topPositionSubject
+        get() = check(MSG_ERROR_TOP_POSITION).that(region.bounds.top)
+    private val bottomPositionSubject
+        get() = check(MSG_ERROR_BOTTOM_POSITION).that(region.bounds.bottom)
+    private val leftPositionSubject
+        get() = check(MSG_ERROR_LEFT_POSITION).that(region.bounds.left)
+    private val rightPositionSubject
+        get() = check(MSG_ERROR_RIGHT_POSITION).that(region.bounds.right)
+    private val areaSubject
+        get() = check(MSG_ERROR_AREA).that(region.bounds.area)
+
+    private val android.graphics.Rect.area get() = this.width() * this.height()
+    private val Rect.area get() = this.width * this.height
+
+    override val selfFacts = listOf(Fact.fact("Region - Covered", region.toString()))
+
+    /**
+     * {@inheritDoc}
+     */
+    override fun fail(reason: List<Fact>): FlickerSubject {
+        val newReason = reason.toMutableList()
+        return super.fail(newReason)
+    }
+
+    private fun assertLeftRightAndAreaEquals(other: Region) {
+        leftPositionSubject.isEqualTo(other.bounds.left)
+        rightPositionSubject.isEqualTo(other.bounds.right)
+        areaSubject.isEqualTo(other.bounds.area)
+    }
+
+    /**
+     * Subtracts [other] from this subject [region]
+     */
+    fun minus(other: Region): RegionSubject {
+        val remainingRegion = Region.from(this.region)
+        remainingRegion.op(other, Region.Op.XOR)
+        return assertThat(remainingRegion, this, timestamp)
+    }
+
+    /**
+     * Adds [other] to this subject [region]
+     */
+    fun plus(other: Region): RegionSubject {
+        val remainingRegion = Region.from(this.region)
+        remainingRegion.op(other, Region.Op.UNION)
+        return assertThat(remainingRegion, this, timestamp)
+    }
+
+    /**
+     * Asserts that the top and bottom coordinates of [RegionSubject.region] are smaller
+     * or equal to those of [region].
+     *
+     * Also checks that the left and right positions, as well as area, don't change
+     */
+    fun isHigherOrEqual(subject: RegionSubject): RegionSubject = apply {
+        isHigherOrEqual(subject.region)
+    }
+
+    /**
+     * Asserts that the top and bottom coordinates of [other] are smaller or equal to
+     * those of [region].
+     *
+     * Also checks that the left and right positions, as well as area, don't change
+     */
+    fun isHigherOrEqual(other: Rect): RegionSubject = apply {
+        isHigherOrEqual(Region.from(other))
+    }
+
+    /**
+     * Asserts that the top and bottom coordinates of [other] are smaller or equal to
+     * those of [region].
+     *
+     * Also checks that the left and right positions, as well as area, don't change
+     */
+    fun isHigherOrEqual(other: Region): RegionSubject = apply {
+        assertLeftRightAndAreaEquals(other)
+        topPositionSubject.isAtMost(other.bounds.top)
+        bottomPositionSubject.isAtMost(other.bounds.bottom)
+    }
+
+    /**
+     * Asserts that the top and bottom coordinates of [RegionSubject.region] are greater
+     * or equal to those of [region].
+     *
+     * Also checks that the left and right positions, as well as area, don't change
+     */
+    fun isLowerOrEqual(subject: RegionSubject): RegionSubject = apply {
+        isLowerOrEqual(subject.region)
+    }
+
+    /**
+     * Asserts that the top and bottom coordinates of [other] are greater or equal to
+     * those of [region].
+     *
+     * Also checks that the left and right positions, as well as area, don't change
+     */
+    fun isLowerOrEqual(other: Rect): RegionSubject = apply {
+        isLowerOrEqual(Region.from(other))
+    }
+
+    /**
+     * Asserts that the top and bottom coordinates of [other] are greater or equal to
+     * those of [region].
+     *
+     * Also checks that the left and right positions, as well as area, don't change
+     */
+    fun isLowerOrEqual(other: Region): RegionSubject = apply {
+        assertLeftRightAndAreaEquals(other)
+        topPositionSubject.isAtLeast(other.bounds.top)
+        bottomPositionSubject.isAtLeast(other.bounds.bottom)
+    }
+
+    /**
+     * Asserts that the top and bottom coordinates of [RegionSubject.region] are smaller than
+     * those of [region].
+     *
+     * Also checks that the left and right positions, as well as area, don't change
+     */
+    fun isHigher(subject: RegionSubject): RegionSubject = apply {
+        isHigher(subject.region)
+    }
+
+    /**
+     * Asserts that the top and bottom coordinates of [other] are smaller than those of [region].
+     *
+     * Also checks that the left and right positions, as well as area, don't change
+     */
+    fun isHigher(other: Rect): RegionSubject = apply {
+        isHigher(Region.from(other))
+    }
+
+    /**
+     * Asserts that the top and bottom coordinates of [other] are smaller than those of [region].
+     *
+     * Also checks that the left and right positions, as well as area, don't change
+     */
+    fun isHigher(other: Region): RegionSubject = apply {
+        assertLeftRightAndAreaEquals(other)
+        topPositionSubject.isLessThan(other.bounds.top)
+        bottomPositionSubject.isLessThan(other.bounds.bottom)
+    }
+
+    /**
+     * Asserts that the top and bottom coordinates of [RegionSubject.region] are greater than
+     * those of [region].
+     *
+     * Also checks that the left and right positions, as well as area, don't change
+     */
+    fun isLower(subject: RegionSubject): RegionSubject = apply {
+        isLower(subject.region)
+    }
+
+    /**
+     * Asserts that the top and bottom coordinates of [other] are greater than those of [region].
+     *
+     * Also checks that the left and right positions, as well as area, don't change
+     */
+    fun isLower(other: Rect): RegionSubject = apply {
+        isLower(Region.from(other))
+    }
+
+    /**
+     * Asserts that the top and bottom coordinates of [other] are greater than those of [region].
+     *
+     * Also checks that the left and right positions, as well as area, don't change
+     */
+    fun isLower(other: Region): RegionSubject = apply {
+        assertLeftRightAndAreaEquals(other)
+        topPositionSubject.isGreaterThan(other.bounds.top)
+        bottomPositionSubject.isGreaterThan(other.bounds.bottom)
+    }
+
+    /**
+     * Asserts that [region] covers at most [testRegion], that is, its area doesn't cover any
+     * point outside of [testRegion].
+     *
+     * @param testRegion Expected covered area
+     */
+    fun coversAtMost(testRegion: Region): RegionSubject = apply {
+        val testRect = testRegion.bounds
+        val intersection = Region.from(region)
+        val covers = intersection.op(testRect, Region.Op.INTERSECT) &&
+            !intersection.op(region, Region.Op.XOR)
+
+        if (!covers) {
+            fail(Fact.fact("Region to test", testRegion),
+                Fact.fact("Covered region", region),
+                Fact.fact("Out-of-bounds region", intersection))
+        }
+    }
+
+    /**
+     * Asserts that [region] covers at most [testRect], that is, its area doesn't cover any
+     * point outside of [testRect].
+     *
+     * @param testRect Expected covered area
+     */
+    fun coversAtMost(testRect: Rect): RegionSubject = apply {
+        coversAtMost(Region.from(testRect))
+    }
+
+    /**
+     * Asserts that [region] covers at least [testRegion], that is, its area covers each point
+     * in the region
+     *
+     * @param testRegion Expected covered area
+     */
+    fun coversAtLeast(testRegion: Region): RegionSubject = apply {
+        val intersection = Region.from(region)
+        val covers = intersection.op(testRegion, Region.Op.INTERSECT) &&
+            !intersection.op(testRegion, Region.Op.XOR)
+
+        if (!covers) {
+            fail(Fact.fact("Region to test", testRegion),
+                Fact.fact("Covered region", region),
+                Fact.fact("Uncovered region", intersection))
+        }
+    }
+
+    /**
+     * Asserts that [region] covers at least [testRect], that is, its area covers each point
+     * in the region
+     *
+     * @param testRect Expected covered area
+     */
+    fun coversAtLeast(testRect: Rect): RegionSubject = apply {
+        coversAtLeast(Region.from(testRect))
+    }
+
+    /**
+     * Asserts that [region] covers at exactly [testRegion]
+     *
+     * @param testRegion Expected covered area
+     */
+    fun coversExactly(testRegion: Region): RegionSubject = apply {
+        val intersection = Region.from(region)
+        val isNotEmpty = intersection.op(testRegion, Region.Op.XOR)
+
+        if (isNotEmpty) {
+            fail(Fact.fact("Region to test", testRegion),
+                Fact.fact("Covered region", region),
+                Fact.fact("Uncovered region", intersection))
+        }
+    }
+
+    /**
+     * Asserts that [region] covers at exactly [testRect]
+     *
+     * @param testRect Expected covered area
+     */
+    fun coversExactly(testRect: Rect): RegionSubject = apply {
+        coversExactly(Region.from(testRect))
+    }
+
+    /**
+     * Asserts that [region] and [testRegion] overlap
+     *
+     * @param testRegion Other area
+     */
+    fun overlaps(testRegion: Region): RegionSubject = apply {
+        val intersection = Region.from(region)
+        val isEmpty = !intersection.op(testRegion, Region.Op.INTERSECT)
+
+        if (isEmpty) {
+            fail(Fact.fact("Region to test", testRegion),
+                Fact.fact("Covered region", region),
+                Fact.fact("Overlap region", intersection))
+        }
+    }
+
+    /**
+     * Asserts that [region] and [testRect] overlap
+     *
+     * @param testRect Other area
+     */
+    fun overlaps(testRect: Rect): RegionSubject = apply {
+        overlaps(Region.from(testRect))
+    }
+
+    /**
+     * Asserts that [region] and [testRegion] don't overlap
+     *
+     * @param testRegion Other area
+     */
+    fun notOverlaps(testRegion: Region): RegionSubject = apply {
+        val intersection = Region.from(region)
+        val isEmpty = !intersection.op(testRegion, Region.Op.INTERSECT)
+
+        if (!isEmpty) {
+            fail(Fact.fact("Region to test", testRegion),
+                Fact.fact("Covered region", region),
+                Fact.fact("Overlap region", intersection))
+        }
+    }
+
+    /**
+     * Asserts that [region] and [testRect] don't overlap
+     *
+     * @param testRect Other area
+     */
+    fun notOverlaps(testRect: Rect): RegionSubject = apply {
+        notOverlaps(Region.from(testRect))
+    }
+
+    /**
+     * Asserts that [region] and [previous] have same aspect ratio, margin of error up to 0.1.
+     *
+     * @param other Other region
+     */
+    fun isSameAspectRatio(other: RegionSubject): RegionSubject = apply {
+        val aspectRatio = this.region.width.toFloat() /
+                this.region.height
+        val otherAspectRatio = other.region.width.toFloat() /
+                other.region.height
+        check("Should have same aspect ratio, old is $aspectRatio and new is $otherAspectRatio")
+                .that(abs(aspectRatio - otherAspectRatio) > 0.1).isFalse()
+    }
+
+    companion object {
+        @VisibleForTesting
+        const val MSG_ERROR_TOP_POSITION = "Incorrect top position"
+
+        @VisibleForTesting
+        const val MSG_ERROR_BOTTOM_POSITION = "Incorrect top position"
+
+        @VisibleForTesting
+        const val MSG_ERROR_LEFT_POSITION = "Incorrect left position"
+
+        @VisibleForTesting
+        const val MSG_ERROR_RIGHT_POSITION = "Incorrect right position"
+
+        @VisibleForTesting
+        const val MSG_ERROR_AREA = "Incorrect rect area"
+
+        private fun mergeRegions(regions: Array<Region>): Region {
+            val result = Region.EMPTY
+            regions.forEach { region ->
+                region.rects.forEach { rect ->
+                    result.op(rect, Region.Op.UNION)
+                }
+            }
+            return result
+        }
+
+        /**
+         * Boiler-plate Subject.Factory for RectSubject
+         */
+        @JvmStatic
+        fun getFactory(
+            parent: FlickerSubject?,
+            timestamp: Long
+        ) = Factory { fm: FailureMetadata, region: Region? ->
+            val regionEntry = RegionEntry(region ?: Region.EMPTY, timestamp.toString())
+            RegionSubject(fm, parent, regionEntry, timestamp)
+        }
+
+        /**
+         * User-defined entry point for existing android regions
+         */
+        @JvmStatic
+        fun assertThat(
+            region: Region?,
+            parent: FlickerSubject? = null,
+            timestamp: Long
+        ): RegionSubject {
+            val strategy = FlickerFailureStrategy()
+            val subject = StandardSubjectBuilder.forCustomFailureStrategy(strategy)
+                .about(getFactory(parent, timestamp))
+                .that(region ?: Region.EMPTY) as RegionSubject
+            strategy.init(subject)
+            return subject
+        }
+
+        /**
+         * User-defined entry point for existing rects
+         */
+        @JvmStatic
+        @JvmOverloads
+        fun assertThat(rect: Array<Rect>, parent: FlickerSubject? = null, timestamp: Long):
+            RegionSubject = assertThat(Region(rect), parent, timestamp)
+
+        /**
+         * User-defined entry point for existing rects
+         */
+        @JvmStatic
+        @JvmOverloads
+        fun assertThat(rect: Rect?, parent: FlickerSubject? = null, timestamp: Long):
+            RegionSubject = assertThat(Region.from(rect), parent, timestamp)
+
+        /**
+         * User-defined entry point for existing rects
+         */
+        @JvmStatic
+        @JvmOverloads
+        fun assertThat(rect: RectF?, parent: FlickerSubject? = null, timestamp: Long):
+            RegionSubject = assertThat(rect?.toRect(), parent, timestamp)
+
+        /**
+         * User-defined entry point for existing rects
+         */
+        @JvmStatic
+        @JvmOverloads
+        fun assertThat(rect: Array<RectF>, parent: FlickerSubject? = null, timestamp: Long):
+            RegionSubject = assertThat(mergeRegions(
+                rect.map { Region.from(it.toRect()) }.toTypedArray()), parent, timestamp)
+
+        /**
+         * User-defined entry point for existing regions
+         */
+        @JvmStatic
+        @JvmOverloads
+        fun assertThat(regions: Array<Region>, parent: FlickerSubject? = null, timestamp: Long):
+            RegionSubject = assertThat(mergeRegions(regions), parent, timestamp)
+
+        /**
+         * User-defined entry point
+         *
+         * @param regionEntry to assert
+         * @param parent containing the entry
+         */
+        @JvmStatic
+        @JvmOverloads
+        fun assertThat(regionEntry: RegionEntry?, parent: FlickerSubject? = null, timestamp: Long):
+            RegionSubject = assertThat(regionEntry?.region, parent, timestamp)
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/traces/region/RegionTraceSubject.kt b/libraries/flicker/src/com/android/server/wm/flicker/traces/region/RegionTraceSubject.kt
new file mode 100644
index 0000000..e956ab6
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/traces/region/RegionTraceSubject.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2021 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.traces.region
+
+import com.android.server.wm.flicker.assertions.FlickerSubject
+import com.android.server.wm.flicker.traces.FlickerFailureStrategy
+import com.android.server.wm.flicker.traces.FlickerTraceSubject
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.Rect
+import com.android.server.wm.traces.common.region.Region
+import com.android.server.wm.traces.common.region.RegionTrace
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.StandardSubjectBuilder
+import com.google.common.truth.Subject.Factory
+
+class RegionTraceSubject(
+    fm: FailureMetadata,
+    val trace: RegionTrace,
+    override val parent: FlickerSubject?
+) : FlickerTraceSubject<RegionSubject>(fm, trace) {
+
+    private val components: Array<out FlickerComponentName> = trace.components
+
+    override val subjects by lazy {
+        trace.entries.map { RegionSubject.assertThat(it, this, it.timestamp) }
+    }
+
+    private val componentsAsString get() =
+        if (components.isEmpty()) {
+            "<any>"
+        } else {
+            "[" + components.joinToString() + "]"
+        }
+
+    /**
+     * Asserts that the visible area covered by any element in the state covers at most
+     * [testRegion], that is, if the area of no elements cover any point outside of [testRegion].
+     *
+     * @param testRegion Expected covered area
+     */
+    fun coversAtMost(
+        testRegion: Region
+    ): RegionTraceSubject = apply {
+        addAssertion("coversAtMost($testRegion, $componentsAsString") {
+            it.coversAtMost(testRegion)
+        }
+    }
+
+    /**
+     * Asserts that the visible area covered by any element in the state covers at most
+     * [testRegion], that is, if the area of no elements cover any point outside of [testRegion].
+     *
+     * @param testRegion Expected covered area
+     */
+    fun coversAtMost(
+        testRegion: Rect
+    ): RegionTraceSubject = this.coversAtMost(testRegion)
+
+    /**
+     * Asserts that the visible area covered by any element in the state covers at least
+     * [testRegion], that is, if the area of its elements visible region covers each point in
+     * the region.
+     *
+     * @param testRegion Expected covered area
+     */
+    fun coversAtLeast(
+        testRegion: Region
+    ): RegionTraceSubject = apply {
+        addAssertion("coversAtLeast($testRegion, $componentsAsString)") {
+            it.coversAtLeast(testRegion)
+        }
+    }
+
+    /**
+     * Asserts that the visible area covered by any element in the state covers at least
+     * [testRegion], that is, if the area of its elements visible region covers each point in
+     * the region.
+     *
+     * @param testRegion Expected covered area
+     */
+    fun coversAtLeast(
+        testRegion: Rect
+    ): RegionTraceSubject = this.coversAtLeast(Region.from(testRegion))
+
+    /**
+     * Asserts that the visible region of the trace entries is exactly [expectedVisibleRegion].
+     *
+     * @param expectedVisibleRegion Expected visible region of the layer
+     */
+    fun coversExactly(
+        expectedVisibleRegion: Region
+    ): RegionTraceSubject = apply {
+        addAssertion("coversExactly($expectedVisibleRegion, $componentsAsString)") {
+            it.coversExactly(expectedVisibleRegion)
+        }
+    }
+
+    companion object {
+        /**
+         * Boiler-plate Subject.Factory for RegionTraceSubject
+         */
+        private fun getFactory(
+            parent: FlickerSubject?
+        ): Factory<RegionTraceSubject, RegionTrace> =
+                Factory { fm, subject -> RegionTraceSubject(fm, subject, parent) }
+
+        /**
+         * Creates a [RegionTraceSubject] representing a trace of the visible region of a
+         * window or layer which can be used to make assertions.
+         *
+         * @param trace The region trace to assert on
+         */
+        @JvmStatic
+        @JvmOverloads
+        fun assertThat(
+            trace: RegionTrace,
+            parent: FlickerSubject? = null
+        ): RegionTraceSubject {
+            val strategy = FlickerFailureStrategy()
+            val subject = StandardSubjectBuilder.forCustomFailureStrategy(strategy)
+                    .about(getFactory(parent))
+                    .that(trace) as RegionTraceSubject
+            strategy.init(subject)
+            return subject
+        }
+
+        /**
+         * Static method for getting the subject factory (for use with assertAbout())
+         */
+        @JvmStatic
+        fun entries(
+            parent: FlickerSubject?
+        ): Factory<RegionTraceSubject, RegionTrace> = getFactory(parent)
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/traces/windowmanager/WindowManagerStateSubject.kt b/libraries/flicker/src/com/android/server/wm/flicker/traces/windowmanager/WindowManagerStateSubject.kt
index b7a55dd..e3a74cb 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/traces/windowmanager/WindowManagerStateSubject.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/traces/windowmanager/WindowManagerStateSubject.kt
@@ -21,13 +21,12 @@
 import com.android.server.wm.flicker.assertions.Assertion
 import com.android.server.wm.flicker.assertions.FlickerSubject
 import com.android.server.wm.flicker.traces.FlickerFailureStrategy
-import com.android.server.wm.flicker.traces.RegionSubject
+import com.android.server.wm.flicker.traces.region.RegionSubject
 import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.common.Region
+import com.android.server.wm.traces.common.region.Region
 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
 import com.android.server.wm.traces.common.windowmanager.windows.Activity
 import com.android.server.wm.traces.common.windowmanager.windows.WindowState
-import com.android.server.wm.traces.parser.toAndroidRegion
 import com.google.common.truth.Fact
 import com.google.common.truth.FailureMetadata
 import com.google.common.truth.FailureStrategy
@@ -85,17 +84,15 @@
     val visibleWindows: List<WindowStateSubject>
         get() = subjects.filter { wmState.visibleWindows.contains(it.windowState) }
 
+    val visibleAppWindows: List<WindowStateSubject>
+        get() = subjects.filter { wmState.visibleAppWindows.contains(it.windowState) }
+
     /**
      * Executes a custom [assertion] on the current subject
      */
     operator fun invoke(assertion: Assertion<WindowManagerState>): WindowManagerStateSubject =
         apply { assertion(this.wmState) }
 
-    /** {@inheritDoc} */
-    override fun clone(): FlickerSubject {
-        return WindowManagerStateSubject(fm, wmState, trace, parent)
-    }
-
     /**
      * Asserts the current WindowManager state doesn't contain [WindowState]s
      */
@@ -111,22 +108,45 @@
     }
 
     /**
-     * Obtains the region occupied by all windows with name containing any of [component]
+     * Obtains the region occupied by all windows with name containing any of [components]
+     * alias of [visibleRegion]
      *
-     * @param component Component to search
+     * @param components Components to search
+     * @deprecated using [visibleRegion] is preferred
      */
-    fun frameRegion(component: FlickerComponentName?): RegionSubject {
-        val windowName = component?.toWindowName() ?: ""
-        val selectedWindows = subjects.filter { it.name.contains(windowName) }
+    fun frameRegion(vararg components: FlickerComponentName): RegionSubject {
+        return visibleRegion(*components)
+    }
+
+    /**
+     * Obtains the region occupied by all windows with name containing any of [components]
+     *
+     * @param components Components to search
+     */
+    fun visibleRegion(vararg components: FlickerComponentName): RegionSubject {
+        val windowNames = components.map { it.toWindowName() }
+
+        val selectedWindows = if (components.isEmpty()) {
+            // No filters so use all subjects
+            subjects
+        } else {
+            subjects.filter {
+                subject -> windowNames.any {
+                    layerName -> subject.name.contains(layerName)
+                }
+            }
+        }
 
         if (selectedWindows.isEmpty()) {
-            fail(Fact.fact(ASSERTION_TAG, "frameRegion(${component?.toWindowName() ?: "<any>"})"),
-                    Fact.fact("Could not find", windowName))
+            val str = if (windowNames.isNotEmpty()) windowNames.joinToString() else "<any>"
+            fail(Fact.fact(ASSERTION_TAG, "visibleRegion($str)"),
+                    Fact.fact("Could not find windows", str))
         }
 
         val visibleWindows = selectedWindows.filter { it.isVisible }
-        val frameRegions = visibleWindows.mapNotNull { it.windowState?.frameRegion }.toTypedArray()
-        return RegionSubject.assertThat(frameRegions, this)
+        val visibleRegions = visibleWindows
+                .mapNotNull { it.windowState?.frameRegion }.toTypedArray()
+        return RegionSubject.assertThat(visibleRegions, this, timestamp)
     }
 
     /**
@@ -196,7 +216,14 @@
      */
     fun isAppWindowOnTop(component: FlickerComponentName): WindowManagerStateSubject = apply {
         val windowName = component.toWindowName()
+        if (wmState.visibleAppWindows.isEmpty()) {
+            fail(
+                Fact.fact(ASSERTION_TAG, "isAppWindowOnTop(${component.toWindowName()})"),
+                Fact.fact("Not found", "No visible app windows found")
+            )
+        }
         if (!wmState.topVisibleAppWindow.contains(windowName)) {
+            isNotEmpty()
             val topWindow = subjects.first { it.name == wmState.topVisibleAppWindow }
             topWindow.fail(
                 Fact.fact(ASSERTION_TAG, "isAppWindowOnTop(${component.toWindowName()})"),
@@ -245,8 +272,8 @@
             val (ourTitle, ourRegion) = regions[i]
             for (j in i + 1 until regions.size) {
                 val (otherTitle, otherRegion) = regions[j]
-                if (ourRegion.toAndroidRegion().op(otherRegion.toAndroidRegion(),
-                        android.graphics.Region.Op.INTERSECT)) {
+                if (ourRegion.op(otherRegion,
+                        Region.Op.INTERSECT)) {
                     val window = foundWindows[ourTitle] ?: error("Window $ourTitle not found")
                     val windowSubject = subjects.first { it.windowState == window }
                     windowSubject.fail(Fact.fact(ASSERTION_TAG,
@@ -412,6 +439,19 @@
     }
 
     /**
+     * Asserts the state contains no visible app windows.
+     */
+    fun hasNoVisibleAppWindow() {
+        if (visibleAppWindows.isNotEmpty()) {
+            val visibleAppWindows = visibleAppWindows.joinToString { it.name }
+            fail(
+                Fact.fact(ASSERTION_TAG, "hasNoVisibleAppWindow()"),
+                Fact.fact("Found visible windows", visibleAppWindows)
+            )
+        }
+    }
+
+    /**
      * Asserts the state contains an invisible window with [WindowState.title] matching [component].
      *
      * Also, if [component] has a package name (i.e., is not a system component), also checks that
@@ -517,6 +557,31 @@
     }
 
     /**
+     * Checks if the activity with title containing [component] is visible
+     *
+     * In the case that an app is stopped in the background (e.g. OS stopped it to release memory)
+     * the app window will not be immediately visible when switching back to the app. Checking if a
+     * snapshotStartingWindow is present for that app instead can decrease flakiness levels of the
+     * assertion.
+     *
+     * @param component Component to search
+     * @param isOptional If this assertion is optional or must pass
+     */
+    @JvmOverloads
+    fun isAppSnapshotStartingWindowVisibleFor(
+        component: FlickerComponentName
+    ) {
+        val windowName = component.toWindowName()
+        val activity = wmState.getActivitiesForWindow(windowName).firstOrNull()
+
+        // Check existence and visibility of SnapshotStartingWindow
+        val snapshotStartingWindow = activity?.children
+            ?.firstOrNull { it.name.startsWith("SnapshotStartingWindow for taskId=") }
+        check("SnapshotStartingWindow for Activity=${activity?.name} must be visible.")
+            .that(snapshotStartingWindow?.isVisible ?: false).isTrue()
+    }
+
+    /**
      * Obtains the first subject with [WindowState.title] containing [name].
      *
      * Always returns a subject, event when the layer doesn't exist. To verify if layer
@@ -582,4 +647,4 @@
             return subject
         }
     }
-}
\ No newline at end of file
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/traces/windowmanager/WindowManagerTraceSubject.kt b/libraries/flicker/src/com/android/server/wm/flicker/traces/windowmanager/WindowManagerTraceSubject.kt
index ed77b20..804c115 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/traces/windowmanager/WindowManagerTraceSubject.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/traces/windowmanager/WindowManagerTraceSubject.kt
@@ -17,19 +17,18 @@
 package com.android.server.wm.flicker.traces.windowmanager
 
 import com.android.server.wm.flicker.assertions.Assertion
-import com.android.server.wm.flicker.assertions.FlickerSubject
 import com.android.server.wm.flicker.traces.FlickerFailureStrategy
 import com.android.server.wm.flicker.traces.FlickerTraceSubject
+import com.android.server.wm.flicker.traces.region.RegionTraceSubject
 import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.common.Rect
-import com.android.server.wm.traces.common.Region
+import com.android.server.wm.traces.common.region.RegionTrace
 import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace
 import com.android.server.wm.traces.common.windowmanager.windows.WindowState
-import com.google.common.truth.Fact
 import com.google.common.truth.FailureMetadata
 import com.google.common.truth.FailureStrategy
 import com.google.common.truth.StandardSubjectBuilder
 import com.google.common.truth.Subject
+import com.google.common.truth.Subject.Factory
 
 /**
  * Truth subject for [WindowManagerTrace] objects, used to make assertions over behaviors that
@@ -61,22 +60,12 @@
 ) : FlickerTraceSubject<WindowManagerStateSubject>(fm, trace) {
     override val selfFacts
         get() = super.selfFacts.toMutableList()
-            .also {
-                if (trace.hasSource()) {
-                    it.add(Fact.fact("Trace file", trace.source))
-                }
-            }
 
     override val subjects by lazy {
         trace.entries.map { WindowManagerStateSubject.assertThat(it, this, this) }
     }
 
     /** {@inheritDoc} */
-    override fun clone(): FlickerSubject {
-        return WindowManagerTraceSubject(fm, trace, parent)
-    }
-
-    /** {@inheritDoc} */
     override fun then(): WindowManagerTraceSubject = apply { super.then() }
 
     /** {@inheritDoc} */
@@ -276,6 +265,40 @@
     }
 
     /**
+     * Checks if there are no visible app windows.
+     *
+     * @param isOptional If this assertion is optional or must pass
+     */
+    @JvmOverloads
+    fun hasNoVisibleAppWindow(isOptional: Boolean = false): WindowManagerTraceSubject = apply {
+        addAssertion("hasNoVisibleAppWindow()", isOptional) {
+            it.hasNoVisibleAppWindow()
+        }
+    }
+
+    /**
+     * Checks if the activity with title containing [component] is visible
+     *
+     * In the case that an app is stopped in the background (e.g. OS stopped it to release memory)
+     * the app window will not be immediately visible when switching back to the app. Checking if a
+     * snapshotStartingWindow is present for that app instead can decrease flakiness levels of the
+     * assertion.
+     *
+     * @param component Component to search
+     * @param isOptional If this assertion is optional or must pass
+     */
+    @JvmOverloads
+    fun isAppSnapshotStartingWindowVisibleFor(
+        component: FlickerComponentName,
+        isOptional: Boolean = false
+    ): WindowManagerTraceSubject = apply {
+        addAssertion(
+            "isAppSnapshotStartingWindowVisibleFor(${component.toWindowName()})", isOptional) {
+            it.isAppSnapshotStartingWindowVisibleFor(component)
+        }
+    }
+
+    /**
      * Checks if app window with title containing the [component] is invisible
      *
      * Note: This assertion have issues with the launcher window, because it contains 2 windows
@@ -332,183 +355,16 @@
     }
 
     /**
-     * Asserts the visible area covered by the [WindowState]s matching [component] covers at least
-     * [testRegion], that is, if its area of the window's bounds cover each point in the region.
+     * Obtains the trace of regions occupied by all windows with name containing any of [components]
      *
-     * @param component Component to search
-     * @param testRegion Expected visible area of the window
+     * @param components Components to search
      */
-    fun coversAtLeast(
-        testRegion: Region,
-        component: FlickerComponentName?
-    ): WindowManagerTraceSubject = apply {
-        addAssertion("coversAtLeastRegion(${component?.toWindowName()}, $testRegion)") {
-            it.frameRegion(component).coversAtLeast(testRegion)
-        }
-    }
+    fun visibleRegion(vararg components: FlickerComponentName): RegionTraceSubject {
+        val regionTrace = RegionTrace(components, subjects.map {
+            it.visibleRegion(*components).regionEntry
+        }.toTypedArray())
 
-    /**
-     * Asserts the visible area covered by the [WindowState]s matching [component] covers at least
-     * [testRegion], that is, if its area of the window's bounds cover each point in the region.
-     *
-     * @param component Component to search
-     * @param testRegion Expected visible area of the window
-     */
-    fun coversAtLeast(
-        testRegion: android.graphics.Region,
-        component: FlickerComponentName?
-    ): WindowManagerTraceSubject = apply {
-        addAssertion("coversAtLeastRegion(${component?.toWindowName()}, $testRegion)") {
-            it.frameRegion(component).coversAtLeast(testRegion)
-        }
-    }
-
-    /**
-     * Asserts the visible area covered by the [WindowState]s matching [component] covers at least
-     * [testRect], that is, if its area of the window's bounds cover each point in the region.
-     *
-     * @param component Component to search
-     * @param testRect Expected visible area of the window
-     */
-    fun coversAtLeast(
-        testRect: android.graphics.Rect,
-        component: FlickerComponentName?
-    ): WindowManagerTraceSubject = apply {
-        addAssertion("coversAtLeastRegion(${component?.toWindowName()}, $testRect)") {
-            it.frameRegion(component).coversAtLeast(testRect)
-        }
-    }
-
-    /**
-     * Asserts the visible area covered by the [WindowState]s matching [component] covers at least
-     * [testRect], that is, if its area of the window's bounds cover each point in the region.
-     *
-     * @param component Component to search
-     * @param testRect Expected visible area of the window
-     */
-    fun coversAtLeast(
-        testRect: Rect,
-        component: FlickerComponentName?
-    ): WindowManagerTraceSubject = apply {
-        addAssertion("coversAtLeastRegion(${component?.toWindowName()}, $testRect)") {
-            it.frameRegion(component).coversAtLeast(testRect)
-        }
-    }
-
-    /**
-     * Asserts the visible area covered by the [WindowState]s matching [component] covers at most
-     * [testRegion], that is, if the area of the window state bounds don't cover any point outside
-     * of [testRegion].
-     *
-     * @param component Component to search
-     * @param testRegion Expected visible area of the window
-     */
-    fun coversAtMost(
-        testRegion: Region,
-        component: FlickerComponentName?
-    ): WindowManagerTraceSubject = apply {
-        addAssertion("coversAtMostRegion(${component?.toWindowName()}, $testRegion)") {
-            it.frameRegion(component).coversAtMost(testRegion)
-        }
-    }
-
-    /**
-     * Asserts the visible area covered by the [WindowState]s matching [component] covers at most
-     * [testRegion], that is, if the area of the window state bounds don't cover any point outside
-     * of [testRegion].
-     *
-     * @param component Component to search
-     * @param testRegion Expected visible area of the window
-     */
-    fun coversAtMost(
-        testRegion: android.graphics.Region,
-        component: FlickerComponentName?
-    ): WindowManagerTraceSubject = apply {
-        addAssertion("coversAtMostRegion(${component?.toWindowName()}, $testRegion)") {
-            it.frameRegion(component).coversAtMost(testRegion)
-        }
-    }
-
-    /**
-     * Asserts the visible area covered by the [WindowState]s matching [component] covers at most
-     * [testRect], that is, if the area of the window state bounds don't cover any point outside
-     * of [testRect].
-     *
-     * @param component Component to search
-     * @param testRect Expected visible area of the window
-     */
-    fun coversAtMost(
-        testRect: Rect,
-        component: FlickerComponentName?
-    ): WindowManagerTraceSubject = apply {
-        addAssertion("coversAtMostRegion(${component?.toWindowName()}, $testRect)") {
-            it.frameRegion(component).coversAtMost(testRect)
-        }
-    }
-
-    /**
-     * Asserts the visible area covered by the [WindowState]s matching [component] covers at most
-     * [testRect], that is, if the area of the window state bounds don't cover any point outside
-     * of [testRect].
-     *
-     * @param component Component to search
-     * @param testRect Expected visible area of the window
-     */
-    fun coversAtMost(
-        testRect: android.graphics.Rect,
-        component: FlickerComponentName?
-    ): WindowManagerTraceSubject = apply {
-        addAssertion("coversAtMostRegion(${component?.toWindowName()}, $testRect)") {
-            it.frameRegion(component).coversAtMost(testRect)
-        }
-    }
-
-    /**
-     * Asserts the visible area covered by the [WindowState]s matching [component] covers exactly
-     * [testRegion].
-     *
-     * @param component Component to search
-     * @param testRegion Expected visible area of the window
-     */
-    fun coversExactly(
-        testRegion: android.graphics.Region,
-        component: FlickerComponentName?
-    ): WindowManagerTraceSubject = apply {
-        addAssertion("coversExactly(${component?.toWindowName()}, $testRegion)") {
-            it.frameRegion(component).coversExactly(testRegion)
-        }
-    }
-
-    /**
-     * Asserts the visible area covered by the [WindowState]s matching [component] covers exactly
-     * [testRegion].
-     *
-     * @param component Component to search
-     * @param testRect Expected visible area of the window
-     */
-    fun coversExactly(
-        testRect: Rect,
-        component: FlickerComponentName?
-    ): WindowManagerTraceSubject = apply {
-        addAssertion("coversExactly(${component?.toWindowName()}, $testRect)") {
-            it.frameRegion(component).coversExactly(testRect)
-        }
-    }
-
-    /**
-     * Asserts the visible area covered by the [WindowState]s matching [component] covers exactly
-     * [testRect].
-     *
-     * @param component Component to search
-     * @param testRect Expected visible area of the window
-     */
-    fun coversExactly(
-        testRect: android.graphics.Rect,
-        component: FlickerComponentName?
-    ): WindowManagerTraceSubject = apply {
-        addAssertion("coversExactly(${component?.toWindowName()}, $testRect)") {
-            it.frameRegion(component).coversExactly(testRect)
-        }
+        return RegionTraceSubject.assertThat(regionTrace, this)
     }
 
     /**
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/traces/windowmanager/WindowStateSubject.kt b/libraries/flicker/src/com/android/server/wm/flicker/traces/windowmanager/WindowStateSubject.kt
index 41a3119..10758d8 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/traces/windowmanager/WindowStateSubject.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/traces/windowmanager/WindowStateSubject.kt
@@ -19,12 +19,13 @@
 import com.android.server.wm.flicker.assertions.Assertion
 import com.android.server.wm.flicker.assertions.FlickerSubject
 import com.android.server.wm.flicker.traces.FlickerFailureStrategy
-import com.android.server.wm.flicker.traces.RegionSubject
+import com.android.server.wm.flicker.traces.region.RegionSubject
 import com.android.server.wm.traces.common.windowmanager.windows.WindowState
 import com.google.common.truth.Fact
 import com.google.common.truth.FailureMetadata
 import com.google.common.truth.FailureStrategy
 import com.google.common.truth.StandardSubjectBuilder
+import com.google.common.truth.Subject.Factory
 
 /**
  * Truth subject for [WindowState] objects, used to make assertions over behaviors that occur on a
@@ -56,7 +57,7 @@
     val isVisible: Boolean get() = windowState?.isVisible == true
     val isInvisible: Boolean get() = windowState?.isVisible == false
     val name: String get() = windowState?.name ?: windowTitle ?: ""
-    val frame: RegionSubject get() = RegionSubject.assertThat(windowState?.frame, this)
+    val frame: RegionSubject get() = RegionSubject.assertThat(windowState?.frame, this, timestamp)
 
     override val selfFacts = listOf(
         Fact.fact("Window title", "${windowState?.title ?: windowTitle}"))
@@ -69,11 +70,6 @@
         assertion(this.windowState)
     }
 
-    /** {@inheritDoc} */
-    override fun clone(): FlickerSubject {
-        return WindowStateSubject(fm, parent, timestamp, windowState, windowTitle)
-    }
-
     /**
      * Asserts that current subject doesn't exist in the window hierarchy
      */
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/Buffer.kt b/libraries/flicker/src/com/android/server/wm/traces/common/ActiveBuffer.kt
similarity index 70%
rename from libraries/flicker/src/com/android/server/wm/traces/common/Buffer.kt
rename to libraries/flicker/src/com/android/server/wm/traces/common/ActiveBuffer.kt
index 46a6227..5d0ca54 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/Buffer.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/ActiveBuffer.kt
@@ -16,11 +16,22 @@
 
 package com.android.server.wm.traces.common
 
-class Buffer(width: Int, height: Int, val stride: Int, val format: Int) : Size(width, height) {
-    override fun prettyPrint(): String = prettyPrint(this)
+/**
+ * Wrapper for ActiveBufferProto (frameworks/native/services/surfaceflinger/layerproto/layers.proto)
+ *
+ * This class is used by flicker and Winscope
+ */
+class ActiveBuffer(
+    width: Int,
+    height: Int,
+    val stride: Int,
+    val format: Int
+) : Size(width, height) {
+    override fun prettyPrint(): String =
+        "w:$width, h:$height, stride:$stride, format:$format"
 
     override fun equals(other: Any?): Boolean =
-        other is Buffer &&
+        other is ActiveBuffer &&
         other.height == height &&
         other.width == width &&
         other.stride == stride &&
@@ -37,9 +48,6 @@
     override fun toString(): String = prettyPrint()
 
     companion object {
-        val EMPTY: Buffer = Buffer(0, 0, 0, 0)
-
-        fun prettyPrint(buffer: Buffer): String = "w:${buffer.width}, h:${buffer.height}, " +
-            "stride:${buffer.stride}, format:${buffer.format}"
+        val EMPTY: ActiveBuffer = ActiveBuffer(0, 0, 0, 0)
     }
 }
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/Color.kt b/libraries/flicker/src/com/android/server/wm/traces/common/Color.kt
index 785977e..4650fd7 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/Color.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/Color.kt
@@ -16,46 +16,40 @@
 
 package com.android.server.wm.traces.common
 
-data class Color(val r: Float, val g: Float, val b: Float, val a: Float) {
-    val isEmpty: Boolean
+/**
+ * Wrapper for ColorProto (frameworks/native/services/surfaceflinger/layerproto/common.proto)
+ *
+ * This class is used by flicker and Winscope
+ */
+class Color(r: Float, g: Float, b: Float, val a: Float) : Color3(r, g, b) {
+    override val isEmpty: Boolean
         get() = a == 0f || r < 0 || g < 0 || b < 0
 
-    val isNotEmpty: Boolean
+    override val isNotEmpty: Boolean
         get() = !isEmpty
 
-    fun prettyPrint(): String = prettyPrint(this)
-
-    override fun toString(): String = if (isEmpty) "[empty]" else prettyPrint()
+    override fun prettyPrint(): String {
+        val parentPrint = super.prettyPrint()
+        return "$parentPrint a:$a"
+    }
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is Color) return false
+        if (!super.equals(other)) return false
 
-        if (r != other.r) return false
-        if (g != other.g) return false
-        if (b != other.b) return false
         if (a != other.a) return false
 
         return true
     }
 
     override fun hashCode(): Int {
-        var result = r.hashCode()
-        result = 31 * result + g.hashCode()
-        result = 31 * result + b.hashCode()
+        var result = super.hashCode()
         result = 31 * result + a.hashCode()
         return result
     }
 
     companion object {
-        val EMPTY = Color(r = -1f, g = -1f, b = -1f, a = 0f)
-
-        fun prettyPrint(color: Color): String {
-            val r = FloatFormatter.format(color.r)
-            val g = FloatFormatter.format(color.g)
-            val b = FloatFormatter.format(color.b)
-            val a = FloatFormatter.format(color.a)
-            return "r:$r g:$g b:$b a:$a"
-        }
+        val EMPTY: Color = Color(r = -1f, g = -1f, b = -1f, a = 0f)
     }
-}
\ No newline at end of file
+}
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/Color3.kt b/libraries/flicker/src/com/android/server/wm/traces/common/Color3.kt
new file mode 100644
index 0000000..7eed706
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/Color3.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 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.traces.common
+
+/**
+ * Wrapper for Color3 (frameworks/native/services/surfaceflinger/layerproto/transactions.proto)
+ *
+ * This class is used by flicker and Winscope
+ */
+open class Color3(val r: Float, val g: Float, val b: Float) {
+    open val isEmpty: Boolean
+        get() = r < 0 || g < 0 || b < 0
+
+    open val isNotEmpty: Boolean
+        get() = !isEmpty
+
+    open fun prettyPrint(): String {
+        val r = FloatFormatter.format(r)
+        val g = FloatFormatter.format(g)
+        val b = FloatFormatter.format(b)
+        return "r:$r g:$g b:$b"
+    }
+
+    override fun toString(): String = if (isEmpty) "[empty]" else prettyPrint()
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is Color) return false
+
+        if (r != other.r) return false
+        if (g != other.g) return false
+        if (b != other.b) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = r.hashCode()
+        result = 31 * result + g.hashCode()
+        result = 31 * result + b.hashCode()
+        return result
+    }
+
+    companion object {
+        val EMPTY: Color3 = Color3(r = -1f, g = -1f, b = -1f)
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/ConditionList.kt b/libraries/flicker/src/com/android/server/wm/traces/common/ConditionList.kt
index acc6a5f..a7ecd17 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/ConditionList.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/ConditionList.kt
@@ -28,6 +28,8 @@
 class ConditionList<T>(
     val conditions: List<Condition<T>>
 ) : Condition<T>("", { false }) {
+    constructor(vararg conditions: Condition<T>): this(listOf(*conditions))
+
     override val message: String
         get() = conditions.joinToString(" and ") { it.toString() }
 
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/DeviceStateDump.kt b/libraries/flicker/src/com/android/server/wm/traces/common/DeviceStateDump.kt
index c96cd6c..0363986 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/DeviceStateDump.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/DeviceStateDump.kt
@@ -16,14 +16,14 @@
 
 package com.android.server.wm.traces.common
 
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
 
 /**
  * Represents a state dump containing the [WindowManagerState] and the [LayerTraceEntry] both
  * parsed and in raw (byte) data.
  */
-class DeviceStateDump<WMType : WindowManagerState?, LayerType : LayerTraceEntry?>(
+class DeviceStateDump<WMType : WindowManagerState?, LayerType : BaseLayerTraceEntry?>(
     /**
      * Parsed [WindowManagerState]
      */
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/FlickerComponentName.kt b/libraries/flicker/src/com/android/server/wm/traces/common/FlickerComponentName.kt
index c39a3bf..0d5eae6 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/FlickerComponentName.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/FlickerComponentName.kt
@@ -16,6 +16,8 @@
 
 package com.android.server.wm.traces.common
 
+import kotlin.text.Regex.Companion.escape
+
 /**
  * Create a new component identifier.
  *
@@ -67,6 +69,16 @@
         }
     }
 
+    fun toShortWindowName(): String {
+        return when {
+            packageName.isNotEmpty() && className.isNotEmpty() ->
+                "$packageName/${className.removePrefix(packageName)}"
+            packageName.isNotEmpty() -> packageName
+            className.isNotEmpty() -> className
+            else -> error("Component name should have an activity of class name")
+        }
+    }
+
     /**
      * Obtains the layer name from the component name.
      *
@@ -81,6 +93,10 @@
         return result
     }
 
+    fun toActivityRecordFilter(): Regex {
+        return Regex("ActivityRecord\\{.*${escape(this.toShortWindowName())}")
+    }
+
     private fun appendShortString(sb: StringBuilder, packageName: String, className: String) {
         sb.append(packageName).append('/')
         appendShortClassName(sb, packageName, className)
@@ -104,10 +120,13 @@
         val ROTATION = FlickerComponentName("", "RotationLayer")
         val BACK_SURFACE = FlickerComponentName("", "BackColorSurface")
         val IME = FlickerComponentName("", "InputMethod")
+        val IME_SNAPSHOT = FlickerComponentName("", "IME-snapshot-surface")
         val SPLASH_SCREEN = FlickerComponentName("", "Splash Screen")
         val SNAPSHOT = FlickerComponentName("", "SnapshotStartingWindow")
+        val LETTERBOX = FlickerComponentName("", "Letterbox")
         val WALLPAPER_BBQ_WRAPPER =
                 FlickerComponentName("", "Wallpaper BBQ wrapper")
+        val PIP_CONTENT_OVERLAY = FlickerComponentName("", "PipContentOverlay")
 
         fun unflattenFromString(str: String): FlickerComponentName {
             val sep = str.indexOf('/')
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/ITrace.kt b/libraries/flicker/src/com/android/server/wm/traces/common/ITrace.kt
index 75af785..8d03528 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/ITrace.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/ITrace.kt
@@ -18,12 +18,9 @@
 
 interface ITrace<Entry : ITraceEntry> {
     val entries: Array<Entry>
-    val source: String
 
     fun getEntry(timestamp: Long): Entry {
         return entries.firstOrNull { it.timestamp == timestamp }
                 ?: throw RuntimeException("Entry does not exist for timestamp $timestamp")
     }
-
-    fun hasSource(): Boolean = source.isNotEmpty()
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/Matrix22.kt b/libraries/flicker/src/com/android/server/wm/traces/common/Matrix22.kt
new file mode 100644
index 0000000..95ef83f
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/Matrix22.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 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.traces.common
+
+/**
+ * Representation of a matrix 3x3 used for layer transforms
+ *
+ *          |dsdx dsdy  tx|
+ * matrix = |dtdx dtdy  ty|
+ *          |0    0     1 |
+ */
+open class Matrix22(
+    val dsdx: Float,
+    val dtdx: Float,
+    val dsdy: Float,
+    val dtdy: Float
+) {
+    open fun prettyPrint(): String {
+        val dsdx = FloatFormatter.format(dsdx)
+        val dtdx = FloatFormatter.format(dtdx)
+        val dsdy = FloatFormatter.format(dsdy)
+        val dtdy = FloatFormatter.format(dtdy)
+        return "dsdx:$dsdx   dtdx:$dtdx   dsdy:$dsdy   dtdy:$dtdy"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is Matrix22) return false
+
+        if (dsdx != other.dsdx) return false
+        if (dtdx != other.dtdx) return false
+        if (dsdy != other.dsdy) return false
+        if (dtdy != other.dtdy) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = dsdx.hashCode()
+        result = 31 * result + dtdx.hashCode()
+        result = 31 * result + dsdy.hashCode()
+        result = 31 * result + dtdy.hashCode()
+        return result
+    }
+
+    override fun toString(): String = prettyPrint()
+
+    companion object {
+        val EMPTY: Matrix22 = Matrix22(0f, 0f, 0f, 0f)
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/Matrix33.kt b/libraries/flicker/src/com/android/server/wm/traces/common/Matrix33.kt
new file mode 100644
index 0000000..a1cd005
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/Matrix33.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 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.traces.common
+
+/**
+ * Representation of a matrix 3x3 used for layer transforms
+ *
+ *          |dsdx dsdy  tx|
+ * matrix = |dtdx dtdy  ty|
+ *          |0    0     1 |
+ */
+class Matrix33(
+    dsdx: Float,
+    dtdx: Float,
+    val tx: Float,
+
+    dsdy: Float,
+    dtdy: Float,
+    val ty: Float
+) : Matrix22(dsdx, dtdx, dsdy, dtdy) {
+    override fun prettyPrint(): String {
+        val parentPrint = super.prettyPrint()
+        val tx = FloatFormatter.format(dsdx)
+        val ty = FloatFormatter.format(dtdx)
+        return "$parentPrint   tx:$tx   ty:$ty"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is Matrix33) return false
+        if (!super.equals(other)) return false
+
+        if (tx != other.tx) return false
+        if (ty != other.ty) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = super.hashCode()
+        result = 31 * result + tx.hashCode()
+        result = 31 * result + ty.hashCode()
+        return result
+    }
+
+    companion object {
+        val EMPTY: Matrix33 = Matrix33(0f, 0f, 0f, 0f, 0f, 0f)
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/Rect.kt b/libraries/flicker/src/com/android/server/wm/traces/common/Rect.kt
index 07caa72..31b1cc0 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/Rect.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/Rect.kt
@@ -16,6 +16,13 @@
 
 package com.android.server.wm.traces.common
 
+/**
+ * Wrapper for RectProto
+ *     - frameworks/native/services/surfaceflinger/layerproto/common.proto and
+ *     - frameworks/base/core/proto/android/graphics/rect.proto
+ *
+ * This class is used by flicker and Winscope
+ */
 open class Rect(
     val left: Int = 0,
     val top: Int = 0,
@@ -24,12 +31,12 @@
 ) {
     val height: Int get() = bottom - top
     val width: Int get() = right - left
-    fun centerX(): Int = left + right / 2
-    fun centerY(): Int = top + bottom / 2
+    fun centerX(): Int = (left + right) / 2
+    fun centerY(): Int = (top + bottom) / 2
     /**
      * Returns true if the rectangle is empty (left >= right or top >= bottom)
      */
-    val isEmpty: Boolean = width == 0 || height == 0
+    val isEmpty: Boolean = width <= 0 || height <= 0
 
     val isNotEmpty: Boolean = !isEmpty
 
@@ -40,7 +47,8 @@
         return RectF(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())
     }
 
-    open fun prettyPrint(): String = prettyPrint(this)
+    open fun prettyPrint(): String =
+        if (isEmpty) "[empty]" else "($left, $top) - ($right, $bottom)"
 
     override fun equals(other: Any?): Boolean = other?.toString() == this.toString()
 
@@ -59,6 +67,33 @@
     }
 
     /**
+     * Returns a [Rect] where the dimensions don't exceed those of [crop]
+     *
+     * @param crop The crop that should be applied to this layer
+     */
+    fun crop(crop: Rect): Rect {
+        val newLeft = maxOf(left, crop.left)
+        val newTop = maxOf(top, crop.top)
+        val newRight = minOf(right, crop.right)
+        val newBottom = minOf(bottom, crop.bottom)
+        return Rect(newLeft, newTop, newRight, newBottom)
+    }
+
+    /** Returns true if: fLeft <= x < fRight && fTop <= y < fBottom.
+    Returns false if SkIRect is empty.
+
+    Considers input to describe constructed SkIRect: (x, y, x + 1, y + 1) and
+    returns true if constructed area is completely enclosed by SkIRect area.
+
+    @param x  test SkIPoint x-coordinate
+    @param y  test SkIPoint y-coordinate
+    @return   true if (x, y) is inside SkIRect
+     */
+    fun contains(x: Int, y: Int): Boolean {
+        return x in left until right && y in top until bottom
+    }
+
+    /**
      * If the specified rectangle intersects this rectangle, return true and set
      * this rectangle to that intersection, otherwise return false and do not
      * change this rectangle. No check is performed to see if either rectangle
@@ -81,12 +116,13 @@
         return result
     }
 
-    override fun toString(): String = if (isEmpty) "[empty]" else prettyPrint()
+    override fun toString(): String = prettyPrint()
+
+    fun clone(): Rect {
+        return if (this == EMPTY) EMPTY else Rect(left, top, right, bottom)
+    }
 
     companion object {
-        val EMPTY = Rect()
-
-        fun prettyPrint(rect: Rect): String = "(${rect.left}, ${rect.top}) - " +
-            "(${rect.right}, ${rect.bottom})"
+        val EMPTY: Rect = Rect()
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/RectF.kt b/libraries/flicker/src/com/android/server/wm/traces/common/RectF.kt
index 5830ad0..b6fe7a9 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/RectF.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/RectF.kt
@@ -16,6 +16,11 @@
 
 package com.android.server.wm.traces.common
 
+/**
+ * Wrapper for FloatRectProto (frameworks/native/services/surfaceflinger/layerproto/layers.proto)
+ *
+ * This class is used by flicker and Winscope
+ */
 data class RectF(
     val left: Float = 0f,
     val top: Float = 0f,
@@ -29,7 +34,7 @@
      * Returns true if the rectangle is empty (left >= right or top >= bottom)
      */
     val isEmpty: Boolean
-        get() = width == 0f || height == 0f
+        get() = width <= 0f || height <= 0f
     val isNotEmpty: Boolean
         get() = !isEmpty
 
@@ -57,6 +62,19 @@
     }
 
     /**
+     * Returns a [RectF] where the dimensions don't exceed those of [crop]
+     *
+     * @param crop The crop that should be applied to this layer
+     */
+    fun crop(crop: RectF): RectF {
+        val newLeft = maxOf(left, crop.left)
+        val newTop = maxOf(top, crop.top)
+        val newRight = minOf(right, crop.right)
+        val newBottom = minOf(bottom, crop.bottom)
+        return RectF(newLeft, newTop, newRight, newBottom)
+    }
+
+    /**
      * If the rectangle specified by left,top,right,bottom intersects this
      * rectangle, return true and set this rectangle to that intersection,
      * otherwise return false and do not change this rectangle. No check is
@@ -107,9 +125,18 @@
      */
     fun intersection(r: RectF): RectF = intersection(r.left, r.top, r.right, r.bottom)
 
-    fun prettyPrint(): String = prettyPrint(this)
+    fun prettyPrint(): String =
+        if (isEmpty) {
+            "[empty]"
+        } else {
+            val left = FloatFormatter.format(left)
+            val top = FloatFormatter.format(top)
+            val right = FloatFormatter.format(right)
+            val bottom = FloatFormatter.format(bottom)
+            "($left, $top) - ($right, $bottom)"
+        }
 
-    override fun toString(): String = if (isEmpty) "[empty]" else prettyPrint()
+    override fun toString(): String = prettyPrint()
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
@@ -132,14 +159,6 @@
     }
 
     companion object {
-        val EMPTY = RectF()
-
-        fun prettyPrint(rect: RectF): String {
-            val left = FloatFormatter.format(rect.left)
-            val top = FloatFormatter.format(rect.top)
-            val right = FloatFormatter.format(rect.right)
-            val bottom = FloatFormatter.format(rect.bottom)
-            return "($left, $top) - ($right, $bottom)"
-        }
+        val EMPTY: RectF = RectF()
     }
 }
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/Region.kt b/libraries/flicker/src/com/android/server/wm/traces/common/Region.kt
deleted file mode 100644
index b6c1734..0000000
--- a/libraries/flicker/src/com/android/server/wm/traces/common/Region.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2021 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.traces.common
-
-class Region(val rects: Array<Rect>) : Rect(
-    rects.map { it.left }.minOrNull() ?: 0,
-    rects.map { it.top }.minOrNull() ?: 0,
-    rects.map { it.right }.maxOrNull() ?: 0,
-    rects.map { it.bottom }.maxOrNull() ?: 0
-) {
-    constructor(
-        left: Int,
-        top: Int,
-        right: Int,
-        bottom: Int
-    ) : this(Rect(left, top, right, bottom))
-
-    constructor(rect: Rect?): this(rect?.let { arrayOf(rect) } ?: emptyArray())
-
-    constructor(rect: RectF?): this(rect?.toRect())
-
-    constructor() : this(Rect.EMPTY)
-
-    override fun toString(): String = prettyPrint()
-
-    override fun prettyPrint(): String = rects.joinToString(", ") { it.prettyPrint() }
-
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (other !is Region) return false
-        if (!super.equals(other)) return false
-
-        if (!rects.contentEquals(other.rects)) return false
-
-        return true
-    }
-
-    override fun hashCode(): Int {
-        var result = super.hashCode()
-        result = 31 * result + rects.contentHashCode()
-        return result
-    }
-
-    companion object {
-        val EMPTY = Region()
-    }
-}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/Size.kt b/libraries/flicker/src/com/android/server/wm/traces/common/Size.kt
index d5bd20a..10e04d7 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/Size.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/Size.kt
@@ -16,6 +16,11 @@
 
 package com.android.server.wm.traces.common
 
+/**
+ * Wrapper for SizeProto (frameworks/native/services/surfaceflinger/layerproto/common.proto)
+ *
+ * This class is used by flicker and Winscope
+ */
 open class Size(val width: Int, val height: Int) {
     open val isEmpty: Boolean
         get() = height == 0 || width == 0
@@ -23,7 +28,7 @@
     val isNotEmpty: Boolean
         get() = !isEmpty
 
-    open fun prettyPrint(): String = prettyPrint(this)
+    open fun prettyPrint(): String = "$width x $height"
 
     override fun toString(): String = if (isEmpty) "[empty]" else prettyPrint()
 
@@ -40,7 +45,5 @@
 
     companion object {
         val EMPTY: Size = Size(0, 0)
-
-        fun prettyPrint(bounds: Size): String = "${bounds.width} x ${bounds.height}"
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/WaitCondition.kt b/libraries/flicker/src/com/android/server/wm/traces/common/WaitCondition.kt
index cd8e316..8a275db 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/WaitCondition.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/WaitCondition.kt
@@ -44,7 +44,7 @@
     private val supplier: () -> T,
     private val condition: Condition<T>,
     private val retryLimit: Int,
-    private val onLog: ((String) -> Unit)?,
+    private val onLog: ((String, Boolean) -> Unit)?,
     private val onFailure: ((T) -> Any)?,
     private val onRetry: ((T) -> Any)?,
     private val onSuccess: ((T) -> Any)?
@@ -53,17 +53,18 @@
      * @return `false` if the condition does not satisfy within the time limit.
      */
     fun waitFor(): Boolean {
-        onLog?.invoke("***Waiting for $condition")
+        onLog?.invoke("***Waiting for $condition", /* isError */ false)
         var currState: T? = null
         for (i in 0..retryLimit) {
             currState = supplier.invoke()
             if (condition.isSatisfied(currState)) {
-                onLog?.invoke("***Waiting for $condition ... Success!")
+                onLog?.invoke("***Waiting for $condition ... Success!", /* isError */ false)
                 onSuccess?.invoke(currState)
                 return true
             } else {
                 val detailedMessage = condition.getMessage(currState)
-                onLog?.invoke("***Waiting for $detailedMessage... retry=${i + 1}")
+                onLog?.invoke("***Waiting for $detailedMessage... retry=${i + 1}",
+                    /* isError */ true)
                 if (i < retryLimit) {
                     onRetry?.invoke(currState)
                 }
@@ -75,7 +76,7 @@
         } else {
             condition.toString()
         }
-        onLog?.invoke("***Waiting for $detailedMessage ... Failed!")
+        onLog?.invoke("***Waiting for $detailedMessage ... Failed!", /* isError */ true)
         if (onFailure != null) {
             require(currState != null) { "Missing last result for failure notification" }
             onFailure.invoke(currState)
@@ -91,7 +92,7 @@
         private var onFailure: ((T) -> Any)? = null
         private var onRetry: ((T) -> Any)? = null
         private var onSuccess: ((T) -> Any)? = null
-        private var onLog: ((String) -> Unit)? = null
+        private var onLog: ((String, Boolean) -> Unit)? = null
 
         fun withCondition(condition: Condition<T>) =
             apply { conditions.add(condition) }
@@ -115,7 +116,7 @@
         fun onFailure(onFailure: (T) -> Any): Builder<T> =
             apply { this.onFailure = onFailure }
 
-        fun onLog(onLog: (String) -> Unit): Builder<T> =
+        fun onLog(onLog: (String, Boolean) -> Unit): Builder<T> =
             apply { this.onLog = onLog }
 
         fun onRetry(onRetry: ((T) -> Any)? = null): Builder<T> =
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/WindowManagerConditionsFactory.kt b/libraries/flicker/src/com/android/server/wm/traces/common/WindowManagerConditionsFactory.kt
index 030ad18..03a7e27 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/WindowManagerConditionsFactory.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/WindowManagerConditionsFactory.kt
@@ -17,13 +17,15 @@
 package com.android.server.wm.traces.common
 
 import com.android.server.wm.traces.common.layers.Layer
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.layers.Transform
 import com.android.server.wm.traces.common.layers.Transform.Companion.isFlagSet
 import com.android.server.wm.traces.common.service.PlatformConsts
 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
 import com.android.server.wm.traces.common.windowmanager.windows.WindowState
 
+typealias DUMP = DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
+
 object WindowManagerConditionsFactory {
     private val navBarWindowName = FlickerComponentName.NAV_BAR.toWindowName()
     private val navBarLayerName = FlickerComponentName.NAV_BAR.toLayerName()
@@ -33,14 +35,14 @@
     /**
      * Condition to check if the nav bar window is visible
      */
-    fun isNavBarVisible(): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    fun isNavBarVisible(): Condition<DUMP> =
         ConditionList(listOf(
             isNavBarWindowVisible(), isNavBarLayerVisible(), isNavBarLayerOpaque()))
 
     /**
      * Condition to check if the nav bar window is visible
      */
-    fun isNavBarWindowVisible(): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    fun isNavBarWindowVisible(): Condition<DUMP> =
         Condition("isNavBarWindowVisible") {
             it.wmState.isWindowVisible(navBarWindowName)
         }
@@ -48,22 +50,22 @@
     /**
      * Condition to check if the nav bar layer is visible
      */
-    fun isNavBarLayerVisible(): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    fun isNavBarLayerVisible(): Condition<DUMP> =
         isLayerVisible(navBarLayerName)
 
     /**
      * Condition to check if the nav bar layer is opaque
      */
-    fun isNavBarLayerOpaque(): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    fun isNavBarLayerOpaque(): Condition<DUMP> =
         Condition("isNavBarLayerOpaque") {
-            it.layerState.getLayerWithBuffer(navBarLayerName)
-                ?.color?.a ?: 0f == 1f
+            (it.layerState.getLayerWithBuffer(navBarLayerName)
+                ?.color?.a ?: 0f) == 1f
         }
 
     /**
      * Condition to check if the status bar window is visible
      */
-    fun isStatusBarVisible(): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    fun isStatusBarVisible(): Condition<DUMP> =
         ConditionList(listOf(
             isStatusBarWindowVisible(), isStatusBarLayerVisible(), isStatusBarLayerOpaque()))
 
@@ -71,7 +73,7 @@
      * Condition to check if the nav bar window is visible
      */
     fun isStatusBarWindowVisible():
-        Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+        Condition<DUMP> =
         Condition("isStatusBarWindowVisible") {
             it.wmState.isWindowVisible(statusBarWindowName)
         }
@@ -79,26 +81,26 @@
     /**
      * Condition to check if the nav bar layer is visible
      */
-    fun isStatusBarLayerVisible(): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    fun isStatusBarLayerVisible(): Condition<DUMP> =
         isLayerVisible(statusBarLayerName)
 
     /**
      * Condition to check if the nav bar layer is opaque
      */
-    fun isStatusBarLayerOpaque(): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    fun isStatusBarLayerOpaque(): Condition<DUMP> =
         Condition("isStatusBarLayerOpaque") {
-            it.layerState.getLayerWithBuffer(statusBarLayerName)
-                ?.color?.a ?: 0f == 1f
+            (it.layerState.getLayerWithBuffer(statusBarLayerName)
+                ?.color?.a ?: 0f) == 1f
         }
 
-    fun isHomeActivityVisible(): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    fun isHomeActivityVisible(): Condition<DUMP> =
         Condition("isHomeActivityVisible") {
             it.wmState.homeActivity?.isVisible == true
         }
 
     fun isAppTransitionIdle(
         displayId: Int
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    ): Condition<DUMP> =
         Condition("isAppTransitionIdle[$displayId]") {
             it.wmState.getDisplay(displayId)
                 ?.appTransitionState == WindowManagerState.APP_STATE_IDLE
@@ -106,38 +108,38 @@
 
     fun containsActivity(
         component: FlickerComponentName
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    ): Condition<DUMP> =
         Condition("containsActivity[${component.toActivityName()}]") {
             it.wmState.containsActivity(component.toActivityName())
         }
 
     fun containsWindow(
         component: FlickerComponentName
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    ): Condition<DUMP> =
         Condition("containsWindow[${component.toWindowName()}]") {
             it.wmState.containsWindow(component.toWindowName())
         }
 
     fun isWindowSurfaceShown(
         windowName: String
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    ): Condition<DUMP> =
         Condition("isWindowSurfaceShown[$windowName]") {
             it.wmState.isWindowSurfaceShown(windowName)
         }
 
     fun isWindowSurfaceShown(
         component: FlickerComponentName
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    ): Condition<DUMP> =
         isWindowSurfaceShown(component.toWindowName())
 
     fun isActivityVisible(
         component: FlickerComponentName
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
-        Condition("isActivityVisible") {
+    ): Condition<DUMP> =
+        Condition("isActivityVisible[${component.toActivityName()}]") {
             it.wmState.isActivityVisible(component.toActivityName())
         }
 
-    fun isWMStateComplete(): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    fun isWMStateComplete(): Condition<DUMP> =
         Condition("isWMStateComplete") {
             it.wmState.isComplete()
         }
@@ -145,8 +147,8 @@
     fun hasRotation(
         expectedRotation: Int,
         displayId: Int
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> {
-        val hasRotationCondition = Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>>(
+    ): Condition<DUMP> {
+        val hasRotationCondition = Condition<DUMP>(
             "hasRotation[$expectedRotation, display=$displayId]") {
             val currRotation = it.wmState.getRotation(displayId)
             currRotation == expectedRotation
@@ -159,23 +161,34 @@
         ))
     }
 
+    fun isWindowVisible(
+        component: FlickerComponentName,
+        displayId: Int = 0
+    ): Condition<DUMP> =
+        ConditionList(
+            containsActivity(component),
+            containsWindow(component),
+            isActivityVisible(component),
+            isWindowSurfaceShown(component),
+            isAppTransitionIdle(displayId))
+
     fun isLayerVisible(
         layerName: String
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    ): Condition<DUMP> =
         Condition("isLayerVisible[$layerName]") {
             it.layerState.isVisible(layerName)
         }
 
     fun isLayerVisible(
         layerId: Int
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
-        Condition("isLayerVisible[$layerId]") {
+    ): Condition<DUMP> =
+        Condition("isLayerVisible[layerId=$layerId]") {
             it.layerState.getLayerById(layerId)?.isVisible ?: false
         }
 
     fun isLayerColorAlphaOne(
         component: FlickerComponentName
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    ): Condition<DUMP> =
         Condition("isLayerColorAlphaOne[${component.toLayerName()}]") {
             val layers = it.layerState.getVisibleLayersByName(component)
             layers.any { layer -> layer.color.a == 1.0f }
@@ -183,7 +196,7 @@
 
     fun isLayerColorAlphaOne(
         layerId: Int
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    ): Condition<DUMP> =
         Condition("isLayerColorAlphaOne[$layerId]") {
             val layer = it.layerState.getLayerById(layerId)
             layer?.color?.a == 1.0f
@@ -192,7 +205,7 @@
     fun isLayerTransformFlagSet(
         component: FlickerComponentName,
         transform: Int
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    ): Condition<DUMP> =
         Condition("isLayerTransformFlagSet[" +
             "${component.toLayerName()},transform=$transform]") {
             val layers = it.layerState.getVisibleLayersByName(component)
@@ -202,7 +215,7 @@
     fun isLayerTransformFlagSet(
         layerId: Int,
         transform: Int
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    ): Condition<DUMP> =
         Condition("isLayerTransformFlagSet[$layerId, $transform]") {
             val layer = it.layerState.getLayerById(layerId)
             layer?.transform?.type?.isFlagSet(transform) ?: false
@@ -210,7 +223,7 @@
 
     fun isLayerTransformIdentity(
         layerId: Int
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    ): Condition<DUMP> =
         ConditionList(listOf(
             isLayerTransformFlagSet(layerId, Transform.SCALE_VAL).negate(),
             isLayerTransformFlagSet(layerId, Transform.TRANSLATE_VAL).negate(),
@@ -220,25 +233,26 @@
     private fun isTransformFlagSet(layer: Layer, transform: Int): Boolean =
         layer.transform.type?.isFlagSet(transform) ?: false
 
-    fun LayerTraceEntry.getVisibleLayersByName(
+    fun BaseLayerTraceEntry.getVisibleLayersByName(
         component: FlickerComponentName
     ): List<Layer> = visibleLayers.filter { it.name.contains(component.toLayerName()) }
 
     fun isLayerVisible(
         component: FlickerComponentName
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    ): Condition<DUMP> =
         isLayerVisible(component.toLayerName())
 
-    fun hasLayersAnimating(): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    fun hasLayersAnimating(): Condition<DUMP> =
         Condition("hasLayersAnimating") {
             it.layerState.isAnimating()
         }
 
     fun isPipWindowLayerSizeMatch(
         layerId: Int
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
-        Condition("isPipWindowLayerSizeMatch") {
-            val pipWindow = it.wmState.pinnedWindows.firstOrNull { it.layerId == layerId }
+    ): Condition<DUMP> =
+        Condition("isPipWindowLayerSizeMatch[layerId=$layerId]") {
+            val pipWindow = it.wmState.pinnedWindows
+                .firstOrNull { pinnedWindow -> pinnedWindow.layerId == layerId }
                 ?: error("Unable to find window with layerId $layerId")
             val windowHeight = pipWindow.frame.height.toFloat()
             val windowWidth = pipWindow.frame.width.toFloat()
@@ -251,14 +265,14 @@
             windowHeight == layerHeight && windowWidth == layerWidth
         }
 
-    fun hasPipWindow(): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    fun hasPipWindow(): Condition<DUMP> =
         Condition("hasPipWindow") {
             it.wmState.hasPipWindow()
         }
 
     fun isImeShown(
         displayId: Int
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    ): Condition<DUMP> =
         ConditionList(listOf(
             isImeOnDisplay(displayId),
             isLayerVisible(FlickerComponentName.IME),
@@ -268,20 +282,20 @@
 
     private fun isImeOnDisplay(
         displayId: Int
-    ): Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+    ): Condition<DUMP> =
         Condition("isImeOnDisplay[$displayId]") {
             it.wmState.inputMethodWindowState?.displayId == displayId
         }
 
     private fun isImeSurfaceShown():
-        Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+        Condition<DUMP> =
         Condition("isImeSurfaceShown") {
             it.wmState.inputMethodWindowState?.isSurfaceShown == true
         }
 
     fun isAppLaunchEnded(taskId: Int):
-        Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
-        Condition("containsVisibleAppLaunchWindow[$taskId]") { dump ->
+        Condition<DUMP> =
+        Condition("containsVisibleAppLaunchWindow[taskId=$taskId]") { dump ->
             val windowStates = dump.wmState.getRootTask(taskId)?.activities?.flatMap {
                 it.children.filterIsInstance<WindowState>()
             }
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/errors/ErrorState.kt b/libraries/flicker/src/com/android/server/wm/traces/common/errors/ErrorState.kt
index 0d285e0..8fc4e81 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/errors/ErrorState.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/errors/ErrorState.kt
@@ -29,5 +29,6 @@
     }
 
     override fun toString(): String = "FlickerErrorState(" +
-            "timestamp=${prettyTimestamp(timestamp)}, numberOfErrors=${errors.size})"
+            "timestamp=${prettyTimestamp(timestamp)}, numberOfErrors=${errors.size} " +
+            "${errors.joinToString("\n") { it.assertionName }})"
 }
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/errors/ErrorTrace.kt b/libraries/flicker/src/com/android/server/wm/traces/common/errors/ErrorTrace.kt
index e851e1d..c2627e8 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/errors/ErrorTrace.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/errors/ErrorTrace.kt
@@ -21,22 +21,20 @@
 /**
  * Represents all the states with errors in an entire trace.
  * @param entries States with errors contained in this trace
- * @param source Source of the trace
  */
 data class ErrorTrace(
-    override val entries: Array<ErrorState>,
-    override val source: String
+    override val entries: Array<ErrorState>
 ) : ITrace<ErrorState>,
     List<ErrorState> by entries.toList() {
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is ErrorTrace) return false
-        if (entries != other.entries) return false
+        if (!entries.contentEquals(other.entries)) return false
         return true
     }
 
     override fun hashCode(): Int = entries.contentDeepHashCode()
 
-    override fun toString(): String = "FlickerErrorTrace(First: ${entries.first()}," +
-            "End: ${entries.last()})"
+    override fun toString(): String = "FlickerErrorTrace(First: ${entries.firstOrNull()}," +
+            "End: ${entries.lastOrNull()})"
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/layers/BaseLayerTraceEntry.kt b/libraries/flicker/src/com/android/server/wm/traces/common/layers/BaseLayerTraceEntry.kt
new file mode 100644
index 0000000..d3ccecf
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/layers/BaseLayerTraceEntry.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 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.traces.common.layers
+
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.ITraceEntry
+import com.android.server.wm.traces.common.prettyTimestamp
+
+abstract class BaseLayerTraceEntry : ITraceEntry {
+    abstract val hwcBlob: String
+    abstract val where: String
+    abstract val displays: Array<Display>
+    val stableId: String get() = this::class.simpleName ?: error("Unable to determine class")
+    val name: String get() = prettyTimestamp(timestamp)
+
+    abstract val flattenedLayers: Array<Layer>
+    val visibleLayers: Array<Layer>
+        get() = flattenedLayers.filter { it.isVisible }.toTypedArray()
+
+    // for winscope
+    val isVisible: Boolean = true
+    val children: Array<Layer>
+        get() = flattenedLayers.filter { it.isRootLayer }.toTypedArray()
+
+    fun getLayerWithBuffer(name: String): Layer? {
+        return flattenedLayers.firstOrNull {
+            it.name.contains(name) && it.activeBuffer.isNotEmpty
+        }
+    }
+
+    fun getLayerById(layerId: Int): Layer? = this.flattenedLayers.firstOrNull { it.id == layerId }
+
+    /**
+     * Checks if any layer in the screen is animating.
+     *
+     * The screen is animating when a layer is not simple rotation, of when the pip overlay
+     * layer is visible
+     */
+    fun isAnimating(windowName: String = ""): Boolean {
+        val layers = visibleLayers.filter { it.name.contains(windowName) }
+        val layersAnimating = layers.any { layer -> !layer.transform.isSimpleRotation }
+        val pipAnimating = isVisible(FlickerComponentName.PIP_CONTENT_OVERLAY.toWindowName())
+        return layersAnimating || pipAnimating
+    }
+
+    /**
+     * Check if at least one window which matches provided window name is visible.
+     */
+    fun isVisible(windowName: String): Boolean =
+        visibleLayers.any { it.name.contains(windowName) }
+
+    fun asTrace(): LayersTrace = LayersTrace(arrayOf(this))
+
+    override fun toString(): String {
+        return "${prettyTimestamp(timestamp)} (timestamp=$timestamp)"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        return other is BaseLayerTraceEntry && other.timestamp == this.timestamp
+    }
+
+    override fun hashCode(): Int {
+        var result = timestamp.hashCode()
+        result = 31 * result + hwcBlob.hashCode()
+        result = 31 * result + where.hashCode()
+        result = 31 * result + displays.contentHashCode()
+        result = 31 * result + isVisible.hashCode()
+        result = 31 * result + flattenedLayers.contentHashCode()
+        return result
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/layers/Display.kt b/libraries/flicker/src/com/android/server/wm/traces/common/layers/Display.kt
index 1dfa94e..3ca6513 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/layers/Display.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/layers/Display.kt
@@ -20,7 +20,7 @@
 import com.android.server.wm.traces.common.Size
 
 /**
- * Representation of a Display in the SF trace
+ * Wrapper for DisplayProto (frameworks/native/services/surfaceflinger/layerproto/display.proto)
  */
 data class Display(
     val id: ULong,
@@ -28,8 +28,11 @@
     val layerStackId: Int,
     val size: Size,
     val layerStackSpace: Rect,
-    val transform: Transform
+    val transform: Transform,
+    val isVirtual: Boolean
 ) {
+    val isOff = layerStackId == BLANK_LAYER_STACK
+
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is Display) return false
@@ -40,6 +43,7 @@
         if (size != other.size) return false
         if (layerStackSpace != other.layerStackSpace) return false
         if (transform != other.transform) return false
+        if (isVirtual != other.isVirtual) return false
 
         return true
     }
@@ -51,17 +55,21 @@
         result = 31 * result + size.hashCode()
         result = 31 * result + layerStackSpace.hashCode()
         result = 31 * result + transform.hashCode()
+        result = 31 * result + isVirtual.hashCode()
         return result
     }
 
     companion object {
+        const val BLANK_LAYER_STACK = - 1
+
         val EMPTY = Display(
             id = 0.toULong(),
             name = "EMPTY",
-            layerStackId = -1,
+            layerStackId = BLANK_LAYER_STACK,
             size = Size.EMPTY,
             layerStackSpace = Rect.EMPTY,
-            transform = Transform.EMPTY
+            transform = Transform.EMPTY,
+            isVirtual = false
         )
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/layers/Layer.kt b/libraries/flicker/src/com/android/server/wm/traces/common/layers/Layer.kt
index ddff3eb..e549bbd 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/layers/Layer.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/layers/Layer.kt
@@ -16,11 +16,11 @@
 
 package com.android.server.wm.traces.common.layers
 
-import com.android.server.wm.traces.common.Buffer
+import com.android.server.wm.traces.common.ActiveBuffer
 import com.android.server.wm.traces.common.Color
 import com.android.server.wm.traces.common.Rect
 import com.android.server.wm.traces.common.RectF
-import com.android.server.wm.traces.common.Region
+import com.android.server.wm.traces.common.region.Region
 import com.android.server.wm.traces.common.layers.Transform.Companion.isFlagSet
 
 /**
@@ -36,7 +36,7 @@
     val parentId: Int,
     val z: Int,
     val visibleRegion: Region?,
-    val activeBuffer: Buffer,
+    val activeBuffer: ActiveBuffer,
     val flags: Int,
     val bounds: RectF,
     val color: Color,
@@ -56,7 +56,8 @@
     val backgroundBlurRadius: Int,
     val crop: Rect?,
     val isRelativeOf: Boolean,
-    val zOrderRelativeOfId: Int
+    val zOrderRelativeOfId: Int,
+    val stackId: Int
 ) {
     val stableId: String = "$type $id $name"
     var parent: Layer? = null
@@ -231,29 +232,35 @@
      *
      * @return
      */
-    val visibilityReason: String
+    val visibilityReason: Array<String>
         get() {
-            return when {
-                isVisible -> ""
-                isContainerLayer -> "ContainerLayer"
-                isHiddenByPolicy -> "Flag is hidden"
-                isHiddenByParent -> "Hidden by parent ${parent?.name}"
-                isBufferLayer && isActiveBufferEmpty -> "Buffer is empty"
-                color.isEmpty -> "Alpha is 0"
-                crop?.isEmpty ?: false -> "Crop is 0x0"
-                bounds.isEmpty -> "Bounds is 0x0"
-                !transform.isValid -> "Transform is invalid"
-                isRelativeOf && zOrderRelativeOf == null -> "RelativeOf layer has been removed"
-                isEffectLayer && !fillsColor && !drawsShadows && !hasBlur ->
-                    "Effect layer does not have color fill, shadow or blur"
-                _occludedBy.isNotEmpty() -> {
-                    val occludedByIds = _occludedBy.joinToString(", ") { it.id.toString() }
-                    "Layer is occluded by: $occludedByIds"
-                }
-                visibleRegion?.isEmpty ?: false ->
-                    "Visible region calculated by Composition Engine is empty"
-                else -> "Unknown"
+            if (isVisible) {
+                return emptyArray()
             }
+            val reasons = mutableListOf<String>()
+            if (isContainerLayer) reasons.add("ContainerLayer")
+            if (isHiddenByPolicy) reasons.add("Flag is hidden")
+            if (isHiddenByParent) reasons.add("Hidden by parent ${parent?.name}")
+            if (isBufferLayer && isActiveBufferEmpty) reasons.add("Buffer is empty")
+            if (color.isEmpty) reasons.add("Alpha is 0")
+            if (crop?.isEmpty == true) reasons.add("Crop is 0x0")
+            if (bounds.isEmpty) reasons.add("Bounds is 0x0")
+            if (!transform.isValid) reasons.add("Transform is invalid")
+            if (isRelativeOf && zOrderRelativeOf == null) {
+                reasons.add("RelativeOf layer has been removed")
+            }
+            if (isEffectLayer && !fillsColor && !drawsShadows && !hasBlur) {
+                reasons.add("Effect layer does not have color fill, shadow or blur")
+            }
+            if (_occludedBy.isNotEmpty()) {
+                val occludedByIds = _occludedBy.joinToString(", ") { it.id.toString() }
+                reasons.add("Layer is occluded by: $occludedByIds")
+            }
+            if (visibleRegion?.isEmpty == true) {
+                reasons.add("Visible region calculated by Composition Engine is empty")
+            }
+            if (reasons.isEmpty()) reasons.add("Unknown")
+            return reasons.toTypedArray()
         }
 
     val screenBounds: RectF = when {
@@ -274,11 +281,24 @@
             }
         }
 
-    fun contains(innerLayer: Layer): Boolean {
+    /**
+     * Returns true iff the [innerLayer] screen bounds are inside or equal to this layer's
+     * [screenBounds] and neither layers are rotating.
+     */
+    fun contains(innerLayer: Layer, crop: RectF = RectF.EMPTY): Boolean {
         return if (!this.transform.isSimpleRotation || !innerLayer.transform.isSimpleRotation) {
             false
         } else {
-            this.screenBounds.contains(innerLayer.screenBounds)
+            val thisBounds: RectF
+            val innerLayerBounds: RectF
+            if (crop.isNotEmpty) {
+                thisBounds = this.screenBounds.crop(crop)
+                innerLayerBounds = innerLayer.screenBounds.crop(crop)
+            } else {
+                thisBounds = this.screenBounds
+                innerLayerBounds = innerLayer.screenBounds
+            }
+            thisBounds.contains(innerLayerBounds)
         }
     }
 
@@ -298,8 +318,18 @@
         _coveredBy.addAll(layers)
     }
 
-    fun overlaps(other: Layer): Boolean =
-        !this.screenBounds.intersection(other.screenBounds).isEmpty
+    fun overlaps(other: Layer, crop: RectF = RectF.EMPTY): Boolean {
+        val thisBounds: RectF
+        val otherBounds: RectF
+        if (crop.isNotEmpty) {
+            thisBounds = this.screenBounds.crop(crop)
+            otherBounds = other.screenBounds.crop(crop)
+        } else {
+            thisBounds = this.screenBounds
+            otherBounds = other.screenBounds
+        }
+        return !thisBounds.intersection(otherBounds).isEmpty
+    }
 
     override fun toString(): String {
         return buildString {
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/layers/LayerTraceEntry.kt b/libraries/flicker/src/com/android/server/wm/traces/common/layers/LayerTraceEntry.kt
index 47ec4bf..b493000 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/layers/LayerTraceEntry.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/layers/LayerTraceEntry.kt
@@ -16,8 +16,8 @@
 
 package com.android.server.wm.traces.common.layers
 
-import com.android.server.wm.traces.common.ITraceEntry
-import com.android.server.wm.traces.common.prettyTimestamp
+import com.android.server.wm.traces.common.Rect
+import com.android.server.wm.traces.common.RectF
 
 /**
  * Represents a single Layer trace entry.
@@ -28,24 +28,21 @@
  **/
 open class LayerTraceEntry constructor(
     override val timestamp: Long,
-    val hwcBlob: String,
-    val where: String,
-    val displays: Array<Display>,
+    override val hwcBlob: String,
+    override val where: String,
+    override val displays: Array<Display>,
     _rootLayers: Array<Layer>
-) : ITraceEntry {
-    val isVisible = true
-    val stableId: String get() = this::class.simpleName ?: error("Unable to determine class")
-    val name: String get() = prettyTimestamp(timestamp)
-
-    val flattenedLayers: Array<Layer> = fillFlattenedLayers(_rootLayers)
-    val children: Array<Layer> get() = flattenedLayers.filter { it.isRootLayer }.toTypedArray()
+) : BaseLayerTraceEntry() {
+    override val flattenedLayers: Array<Layer> = fillFlattenedLayers(_rootLayers)
 
     private fun fillFlattenedLayers(rootLayers: Array<Layer>): Array<Layer> {
         val opaqueLayers = mutableListOf<Layer>()
         val transparentLayers = mutableListOf<Layer>()
         val layers = mutableListOf<Layer>()
+        // some of the flickerlib traces are old and don't have the display data on the trace
+        val mainDisplaySize = displays.firstOrNull { !it.isVirtual }?.layerStackSpace ?: Rect.EMPTY
         val roots = rootLayers.fillOcclusionState(
-            opaqueLayers, transparentLayers).toMutableList()
+            opaqueLayers, transparentLayers, mainDisplaySize.toRectF()).toMutableList()
         while (roots.isNotEmpty()) {
             val layer = roots.removeAt(0)
             layers.add(layer)
@@ -60,9 +57,6 @@
                 .flatMap { it.topDownTraversal() }
     }
 
-    val visibleLayers: Array<Layer>
-        get() = flattenedLayers.filter { it.isVisible }.toTypedArray()
-
     private fun Layer.topDownTraversal(): List<Layer> {
         val traverseList = mutableListOf(this)
 
@@ -76,7 +70,8 @@
 
     private fun Array<Layer>.fillOcclusionState(
         opaqueLayers: MutableList<Layer>,
-        transparentLayers: MutableList<Layer>
+        transparentLayers: MutableList<Layer>,
+        displaySize: RectF
     ): Array<Layer> {
         val traversalList = topDownTraversal().reversed()
 
@@ -85,13 +80,16 @@
 
             if (visible) {
                 val occludedBy = opaqueLayers
-                        .filter { it.contains(layer) && !it.hasRoundedCorners }.toTypedArray()
+                    .filter { it.contains(layer, displaySize) && !it.hasRoundedCorners }
+                    .toTypedArray()
                 layer.addOccludedBy(occludedBy)
                 val partiallyOccludedBy = opaqueLayers
-                        .filter { it.overlaps(layer) && it !in layer.occludedBy }
-                        .toTypedArray()
+                    .filter { it.overlaps(layer, displaySize) && it !in layer.occludedBy }
+                    .toTypedArray()
                 layer.addPartiallyOccludedBy(partiallyOccludedBy)
-                val coveredBy = transparentLayers.filter { it.overlaps(layer) }.toTypedArray()
+                val coveredBy = transparentLayers
+                    .filter { it.overlaps(layer, displaySize) }
+                    .toTypedArray()
                 layer.addCoveredBy(coveredBy)
 
                 if (layer.isOpaque) {
@@ -104,46 +102,4 @@
 
         return this
     }
-
-    fun getLayerWithBuffer(name: String): Layer? {
-        return flattenedLayers.firstOrNull {
-            it.name.contains(name) && it.activeBuffer.isNotEmpty
-        }
-    }
-
-    fun getLayerById(layerId: Int): Layer? = this.flattenedLayers.firstOrNull { it.id == layerId }
-
-    /**
-     * Checks the transform of any layer is not a simple rotation
-     */
-    fun isAnimating(windowName: String = ""): Boolean {
-        val layers = visibleLayers.filter { it.name.contains(windowName) }
-        return layers.any { layer -> !layer.transform.isSimpleRotation }
-    }
-
-    /**
-     * Check if at least one window which matches provided window name is visible.
-     */
-    fun isVisible(windowName: String): Boolean =
-        visibleLayers.any { it.name.contains(windowName) }
-
-    fun asTrace(): LayersTrace = LayersTrace(arrayOf(this), source = "")
-
-    override fun toString(): String {
-        return "${prettyTimestamp(timestamp)} (timestamp=$timestamp)"
-    }
-
-    override fun equals(other: Any?): Boolean {
-        return other is LayerTraceEntry && other.timestamp == this.timestamp
-    }
-
-    override fun hashCode(): Int {
-        var result = timestamp.hashCode()
-        result = 31 * result + hwcBlob.hashCode()
-        result = 31 * result + where.hashCode()
-        result = 31 * result + displays.contentHashCode()
-        result = 31 * result + isVisible.hashCode()
-        result = 31 * result + flattenedLayers.contentHashCode()
-        return result
-    }
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/layers/LayerTraceEntryBuilder.kt b/libraries/flicker/src/com/android/server/wm/traces/common/layers/LayerTraceEntryBuilder.kt
index 3bcd774..dc6f568 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/layers/LayerTraceEntryBuilder.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/layers/LayerTraceEntryBuilder.kt
@@ -31,6 +31,8 @@
     private var orphanLayerCallback: ((Layer) -> Boolean)? = null
     private val orphans = mutableListOf<Layer>()
     private val layers = setLayers(layers)
+    private var ignoreVirtualDisplay = false
+    private var ignoreLayersStackMatchNoDisplay = false
 
     private fun setLayers(layers: Array<Layer>): Map<Int, Layer> {
         val result = mutableMapOf<Int, Layer>()
@@ -105,31 +107,90 @@
         updateParents()
         updateRelZParents()
 
+        // Find all root layers (any sibling of the root layer is considered a root layer in the trace)
+        val rootLayers = mutableListOf<Layer>()
+
         // Getting the first orphan works because when dumping the layers, the root layer comes
         // first, and given that orphans are added in the same order as the layers are provided
         // in the first orphan layer should be the root layer.
-        val firstRoot = orphans.firstOrNull() ?: throw IllegalStateException(
-            "Display root layer not found.")
-        orphans.remove(firstRoot)
+        if (orphans.isNotEmpty()) {
+            val firstRoot = orphans.first()
+            orphans.remove(firstRoot)
+            rootLayers.add(firstRoot)
 
-        // Find all root layers (any sibling of the root layer is considered a root layer in the trace)
-        val rootLayers = mutableListOf(firstRoot)
-        val remainingRoots = orphans.filter { it.parentId == firstRoot.parentId }
-        rootLayers.addAll(remainingRoots)
+            val remainingRoots = orphans.filter { it.parentId == firstRoot.parentId }
+            rootLayers.addAll(remainingRoots)
 
-        // Remove RootLayers from orphans
-        orphans.removeAll(rootLayers)
+            // Remove RootLayers from orphans
+            orphans.removeAll(rootLayers)
+        }
 
         return rootLayers
     }
 
+    private fun filterOutLayersInVirtualDisplays(roots: List<Layer>): List<Layer> {
+        val physicalDisplays = displays
+            .filterNot { it.isVirtual }
+            .map { it.layerStackId }
+
+        return roots.filter { physicalDisplays.contains(it.stackId) }
+    }
+
+    private fun filterOutVirtualDisplays(displays: List<Display>): List<Display> {
+        return displays.filterNot { it.isVirtual }
+    }
+
+    private fun filterOutOffDisplays(displays: List<Display>): List<Display> {
+        return displays.filterNot { it.isOff }
+    }
+
+    private fun filterOutLayersStackMatchNoDisplay(roots: List<Layer>): List<Layer> {
+        val displayStacks = displays.map { it.layerStackId }
+        return roots.filter { displayStacks.contains(it.stackId) }
+    }
+
+    /**
+     * Defines if virtual displays and the layers belonging to virtual displays (e.g., Screen Recording) should be
+     * ignored while parsing the entry
+     *
+     * @param ignore If the layers from virtual displays should be ignored or not
+     */
+    fun ignoreVirtualDisplay(ignore: Boolean): LayerTraceEntryBuilder = apply {
+        this.ignoreVirtualDisplay = ignore
+    }
+
+    /**
+     * Ignore layers whose stack ID doesn't match any display. This is the case, for example,
+     * when the device screen is off, or for layers that have not yet been removed after a
+     * display change (e.g., virtual screen recording display removed)
+     *
+     * @param ignore If the layers not matching any stack id should be removed or not
+     */
+    fun ignoreLayersStackMatchNoDisplay(ignore: Boolean): LayerTraceEntryBuilder = apply {
+        this.ignoreLayersStackMatchNoDisplay = ignore
+    }
+
     /** Constructs the layer hierarchy from a flattened list of layers.  */
     fun build(): LayerTraceEntry {
-        val rootLayers = computeRootLayers()
+        val allRoots = computeRootLayers()
+        var filteredRoots = allRoots
+        var filteredDisplays = displays.toList()
+
+        if (ignoreLayersStackMatchNoDisplay) {
+            filteredRoots = filterOutLayersStackMatchNoDisplay(filteredRoots)
+        }
+
+        if (ignoreVirtualDisplay) {
+            filteredRoots = filterOutLayersInVirtualDisplays(filteredRoots)
+            filteredDisplays = filterOutVirtualDisplays(filteredDisplays)
+        }
+
+        filteredDisplays = filterOutOffDisplays(filteredDisplays)
 
         // Fail if we find orphan layers.
         notifyOrphansLayers()
 
-        return LayerTraceEntry(timestamp, hwcBlob, where, displays, rootLayers.toTypedArray())
+        return LayerTraceEntry(timestamp, hwcBlob, where, filteredDisplays.toTypedArray(),
+                filteredRoots.toTypedArray())
     }
-}
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/layers/LayersTrace.kt b/libraries/flicker/src/com/android/server/wm/traces/common/layers/LayersTrace.kt
index 988f357..5cfb219 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/layers/LayersTrace.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/layers/LayersTrace.kt
@@ -28,14 +28,13 @@
  *
  */
 data class LayersTrace(
-    override val entries: Array<LayerTraceEntry>,
-    override val source: String = ""
-) : ITrace<LayerTraceEntry>, List<LayerTraceEntry> by entries.toList() {
-    constructor(entry: LayerTraceEntry): this(arrayOf(entry))
+    override val entries: Array<BaseLayerTraceEntry>
+) : ITrace<BaseLayerTraceEntry>, List<BaseLayerTraceEntry> by entries.toList() {
+    constructor(entry: BaseLayerTraceEntry): this(arrayOf(entry))
 
     override fun toString(): String {
-        return "LayersTrace(Start: ${entries.first()}, " +
-            "End: ${entries.last()})"
+        return "LayersTrace(Start: ${entries.firstOrNull()}, " +
+            "End: ${entries.lastOrNull()})"
     }
 
     override fun equals(other: Any?): Boolean {
@@ -43,14 +42,12 @@
         if (other !is LayersTrace) return false
 
         if (!entries.contentEquals(other.entries)) return false
-        if (source != other.source) return false
 
         return true
     }
 
     override fun hashCode(): Int {
         var result = entries.contentHashCode()
-        result = 31 * result + source.hashCode()
         return result
     }
 
@@ -66,7 +63,7 @@
             this.entries
                 .dropWhile { it.timestamp < from }
                 .dropLastWhile { it.timestamp > to }
-                .toTypedArray(),
-            source = "")
+                .toTypedArray()
+        )
     }
-}
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/layers/Transform.kt b/libraries/flicker/src/com/android/server/wm/traces/common/layers/Transform.kt
index 2c05628..48d29da 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/layers/Transform.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/layers/Transform.kt
@@ -16,11 +16,16 @@
 
 package com.android.server.wm.traces.common.layers
 
-import com.android.server.wm.traces.common.FloatFormatter
+import com.android.server.wm.traces.common.Matrix33
 import com.android.server.wm.traces.common.RectF
 import com.android.server.wm.traces.common.service.PlatformConsts
 
-open class Transform(val type: Int?, val matrix: Matrix) {
+/**
+ * Wrapper for TransformProto (frameworks/native/services/surfaceflinger/layerproto/common.proto)
+ *
+ * This class is used by flicker and Winscope
+ */
+open class Transform(val type: Int?, val matrix: Matrix33) {
 
     /**
      * Returns true if the applying the transform on an an axis aligned rectangle
@@ -122,34 +127,9 @@
         return multiplyRect(matrix, bounds ?: RectF.EMPTY)
     }
 
-    //          |dsdx dsdy  tx|
-    // matrix = |dtdx dtdy  ty|
-    //          |0    0     1 |
-    data class Matrix(
-        val dsdx: Float,
-        val dtdx: Float,
-        val tx: Float,
-
-        val dsdy: Float,
-        val dtdy: Float,
-        val ty: Float
-    ) {
-        fun prettyPrint(): String {
-            val dsdx = FloatFormatter.format(dsdx)
-            val dtdx = FloatFormatter.format(dtdx)
-            val dsdy = FloatFormatter.format(dsdy)
-            val dtdy = FloatFormatter.format(dtdy)
-            return "dsdx:$dsdx   dtdx:$dtdx   dsdy:$dsdy   dtdy:$dtdy"
-        }
-
-        companion object {
-            val EMPTY: Matrix = Matrix(0f, 0f, 0f, 0f, 0f, 0f)
-        }
-    }
-
     private data class Vec2(val x: Float, val y: Float)
 
-    private fun multiplyRect(matrix: Matrix, rect: RectF): RectF {
+    private fun multiplyRect(matrix: Matrix33, rect: RectF): RectF {
         //          |dsdx dsdy  tx|         | left, top         |
         // matrix = |dtdx dtdy  ty|  rect = |                   |
         //          |0    0     1 |         |     right, bottom |
@@ -167,7 +147,7 @@
         )
     }
 
-    private fun multiplyVec2(matrix: Matrix, x: Float, y: Float): Vec2 {
+    private fun multiplyVec2(matrix: Matrix33, x: Float, y: Float): Vec2 {
         // |dsdx dsdy  tx|     | x |
         // |dtdx dtdy  ty|  x  | y |
         // |0    0     1 |     | 1 |
@@ -178,7 +158,7 @@
     }
 
     companion object {
-        val EMPTY: Transform = Transform(type = null, matrix = Matrix.EMPTY)
+        val EMPTY: Transform = Transform(type = null, matrix = Matrix33.EMPTY)
 
         /* transform type flags */
         const val TRANSLATE_VAL = 0x0001
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/region/Region.kt b/libraries/flicker/src/com/android/server/wm/traces/common/region/Region.kt
new file mode 100644
index 0000000..83a98f3
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/region/Region.kt
@@ -0,0 +1,1120 @@
+/*
+ * Copyright (C) 2021 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.traces.common.region
+
+import com.android.server.wm.traces.common.Rect
+import com.android.server.wm.traces.common.RectF
+import kotlin.math.min
+
+/**
+ * Wrapper for RegionProto (frameworks/native/services/surfaceflinger/layerproto/common.proto)
+ *
+ * Implementation based android.graphics.Region's native implementation found in SkRegion.cpp
+ *
+ * This class is used by flicker and Winscope
+ *
+ * It has a single constructor and different [from] functions on its companion because JS
+ * doesn't support constructor overload
+ */
+class Region(rects: Array<Rect> = arrayOf()) {
+    private var fBounds = Rect.EMPTY
+    private var fRunHead: RunHead? = RunHead(isEmptyHead = true)
+
+    init {
+        if (rects.isEmpty()) {
+            setEmpty()
+        } else {
+            for (rect in rects) {
+                union(rect)
+            }
+        }
+    }
+
+    val rects get() = getRectsFromString(toString())
+
+    val width: Int get() = bounds.width
+    val height: Int get() = bounds.height
+    // if null we are a rect not empty
+    val isEmpty: Boolean get() = fRunHead?.isEmptyHead ?: false
+    val isNotEmpty: Boolean get() = !isEmpty
+    val bounds get() = fBounds
+
+    /**
+     * Set the region to the empty region
+     */
+    fun setEmpty(): Boolean {
+        fBounds = Rect.EMPTY
+        fRunHead = RunHead(isEmptyHead = true)
+
+        return false
+    }
+
+    /**
+     * Set the region to the specified region.
+     */
+    fun set(region: Region): Boolean {
+        fBounds = region.fBounds.clone()
+        fRunHead = region.fRunHead?.clone()
+        return !(fRunHead?.isEmptyHead ?: false)
+    }
+
+    /**
+     * Set the region to the specified rectangle
+     */
+    fun set(r: Rect): Boolean {
+        return if (r.isEmpty ||
+            SkRegion_kRunTypeSentinel == r.right ||
+            SkRegion_kRunTypeSentinel == r.bottom) {
+            this.setEmpty()
+        } else {
+            fBounds = r
+            fRunHead = null
+            true
+        }
+    }
+
+    /**
+     * Set the region to the specified rectangle
+     */
+    operator fun set(left: Int, top: Int, right: Int, bottom: Int): Boolean {
+        return set(Rect(left, top, right, bottom))
+    }
+
+    fun isRect(): Boolean {
+        return fRunHead == null
+    }
+
+    fun isComplex(): Boolean {
+        return !this.isEmpty && !this.isRect()
+    }
+
+    fun contains(x: Int, y: Int): Boolean {
+        if (!fBounds.contains(x, y)) {
+            return false
+        }
+        if (this.isRect()) {
+            return true
+        }
+        require(this.isComplex())
+
+        val runs = fRunHead!!.findScanline(y)
+
+        // Skip the Bottom and IntervalCount
+        var runsIndex = 2
+
+        // Just walk this scanline, checking each interval. The X-sentinel will
+        // appear as a left-interval (runs[0]) and should abort the search.
+        //
+        // We could do a bsearch, using interval-count (runs[1]), but need to time
+        // when that would be worthwhile.
+        //
+        while (true) {
+            if (x < runs[runsIndex]) {
+                break
+            }
+            if (x < runs[runsIndex + 1]) {
+                return true
+            }
+            runsIndex += 2
+        }
+        return false
+    }
+
+    override fun toString(): String = prettyPrint()
+
+    class Iterator(private val rgn: Region) {
+        private var done: Boolean
+        private var rect: Rect
+        private var fRuns: List<Int>? = null
+        private var fRunsIndex = 0
+
+        init {
+            fRunsIndex = 0
+            if (rgn.isEmpty) {
+                rect = Rect.EMPTY
+                done = true
+            } else {
+                done = false
+                if (rgn.isRect()) {
+                    rect = rgn.fBounds
+                    fRuns = null
+                } else {
+                    fRuns = rgn.fRunHead!!.readonlyRuns
+                    rect = Rect(fRuns!![3], fRuns!![0],
+                        fRuns!![4], fRuns!![1])
+                    fRunsIndex = 5
+                }
+            }
+        }
+
+        fun next() {
+            if (done) {
+                return
+            }
+
+            if (fRuns == null) { // rect case
+                done = true
+                return
+            }
+
+            val runs = fRuns!!
+            var runsIndex = fRunsIndex
+
+            if (runs[runsIndex] < SkRegion_kRunTypeSentinel) { // valid X value
+                rect = Rect(runs[runsIndex], rect.top, runs[runsIndex + 1], rect.bottom)
+                runsIndex += 2
+            } else { // we're at the end of a line
+                runsIndex += 1
+                if (runs[runsIndex] < SkRegion_kRunTypeSentinel) { // valid Y value
+                    val intervals = runs[runsIndex + 1]
+                    if (0 == intervals) { // empty line
+                        rect = Rect(rect.left, runs[runsIndex], rect.right, rect.bottom)
+                        runsIndex += 3
+                    } else {
+                        rect = Rect(rect.left, rect.bottom, rect.right, rect.bottom)
+                    }
+
+                    assert_sentinel(runs[runsIndex + 2], false)
+                    assert_sentinel(runs[runsIndex + 3], false)
+                    rect =
+                        Rect(runs[runsIndex + 2], rect.top, runs[runsIndex + 3], runs[runsIndex])
+                    runsIndex += 4
+                } else { // end of rgn
+                    done = true
+                }
+            }
+            fRunsIndex = runsIndex
+        }
+
+        fun done(): Boolean {
+            return done
+        }
+
+        fun rect(): Rect {
+            return rect
+        }
+    }
+
+    fun prettyPrint(): String {
+        val iter = Iterator(this)
+        val result = StringBuilder("SkRegion(")
+        while (!iter.done()) {
+            val r = iter.rect()
+            result.append("(${r.left},${r.top},${r.right},${r.bottom})")
+            iter.next()
+        }
+        result.append(")")
+        return result.toString()
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is Region) return false
+        if (!super.equals(other)) return false
+        if (!rects.contentEquals(other.rects)) return false
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = super.hashCode()
+        result = 31 * result + rects.contentHashCode()
+        return result
+    }
+
+    // the native values for these must match up with the enum in SkRegion.h
+    enum class Op(val nativeInt: Int) {
+        DIFFERENCE(0), INTERSECT(1), UNION(2), XOR(3),
+        REVERSE_DIFFERENCE(4), REPLACE(5);
+    }
+
+    fun union(r: Rect): Boolean {
+        return op(r, Op.UNION)
+    }
+
+    fun toRectF(): RectF {
+        return bounds.toRectF()
+    }
+
+    private fun oper(rgnA: Region, rgnB: Region, op: Op): Boolean {
+        // simple cases
+        when (op) {
+            Op.REPLACE -> {
+                this.set(rgnB)
+                return !rgnB.isEmpty
+            }
+            Op.REVERSE_DIFFERENCE -> {
+                // collapse difference and reverse-difference into just difference
+                return this.oper(rgnB, rgnA, Op.DIFFERENCE)
+            }
+            Op.DIFFERENCE -> {
+                if (rgnA.isEmpty) {
+                    this.setEmpty()
+                    return false
+                }
+                if (rgnB.isEmpty || rgnA.bounds.intersection(rgnB.bounds).isEmpty) {
+                    this.set(rgnA)
+                    return !rgnA.isEmpty
+                }
+                if (rgnB.isRect() && rgnB.bounds.contains(rgnA.bounds)) {
+                    this.setEmpty()
+                    return false
+                }
+            }
+            Op.INTERSECT -> {
+                when {
+                    rgnA.isEmpty || rgnB.isEmpty
+                        || rgnA.bounds.intersection(rgnB.bounds).isEmpty -> {
+                        this.setEmpty()
+                        return false
+                    }
+                    rgnA.isRect() && rgnB.isRect() -> {
+                        val rectIntersection = rgnA.bounds.intersection(rgnB.bounds)
+                        this.set(rgnA.bounds.intersection(rgnB.bounds))
+                        return !rectIntersection.isEmpty
+                    }
+                    rgnA.isRect() && rgnA.bounds.contains(rgnB.bounds) -> {
+                        this.set(rgnB)
+                        return !rgnB.isEmpty
+                    }
+                    rgnB.isRect() && rgnB.bounds.contains(rgnA.bounds) -> {
+                        this.set(rgnA)
+                        return !rgnA.isEmpty
+                    }
+                }
+            }
+            Op.UNION -> {
+                when {
+                    rgnA.isEmpty -> {
+                        this.set(rgnB)
+                        return !rgnB.isEmpty
+                    }
+                    rgnB.isEmpty -> {
+                        this.set(rgnA)
+                        return !rgnA.isEmpty
+                    }
+                    rgnA.isRect() && rgnA.bounds.contains(rgnB.bounds) -> {
+                        this.set(rgnA)
+                        return !rgnA.isEmpty
+                    }
+                    rgnB.isRect() && rgnB.bounds.contains(rgnA.bounds) -> {
+                        this.set(rgnB)
+                        return !rgnB.isEmpty
+                    }
+                }
+            }
+            Op.XOR -> {
+                when {
+                    rgnA.isEmpty -> {
+                        this.set(rgnB)
+                        return !rgnB.isEmpty
+                    }
+                    rgnB.isEmpty -> {
+                        this.set(rgnA)
+                        return !rgnA.isEmpty
+                    }
+                }
+            }
+        }
+
+        val array = RunArray()
+        val count = operate(rgnA.getRuns(), rgnB.getRuns(), array, op)
+        require(count <= array.count)
+        return this.setRuns(array, count)
+    }
+
+    class RunArray {
+        private val kRunArrayStackCount = 256
+        var runs: MutableList<Int> = MutableList(kRunArrayStackCount) { 0 }
+        private var fCount: Int = kRunArrayStackCount
+
+        val count: Int get() = fCount
+
+        operator fun get(i: Int): Int {
+            return runs[i]
+        }
+
+        fun resizeToAtLeast(_count: Int) {
+            var count = _count
+            if (count > fCount) {
+                // leave at least 50% extra space for future growth.
+                count += count shr 1
+                val newRuns = MutableList(count) { 0 }
+                runs.forEachIndexed { index, value ->
+                    newRuns[index] = value
+                }
+                runs = newRuns
+                fCount = count
+            }
+        }
+
+        operator fun set(i: Int, value: Int) {
+            runs[i] = value
+        }
+
+        fun subList(startIndex: Int, stopIndex: Int): RunArray {
+            val subRuns = RunArray()
+            subRuns.resizeToAtLeast(this.fCount)
+            for (i in startIndex until stopIndex) {
+                subRuns.runs[i - startIndex] = this.runs[i]
+            }
+            return subRuns
+        }
+
+        fun clone(): RunArray {
+            val clone = RunArray()
+            clone.runs = runs.toMutableList()
+            clone.fCount = fCount
+            return clone
+        }
+    }
+
+    /**
+     * Set this region to the result of performing the Op on the specified
+     * regions. Return true if the result is not empty.
+     */
+    fun op(rgnA: Region, rgnB: Region, op: Op): Boolean {
+        return this.oper(rgnA, rgnB, op)
+    }
+
+    private fun getRuns(): List<Int> {
+        val runs: List<Int>
+        if (this.isEmpty) {
+            runs = MutableList(kRectRegionRuns) { 0 }
+            runs[0] = SkRegion_kRunTypeSentinel
+        } else if (this.isRect()) {
+            runs = buildRectRuns(fBounds)
+        } else {
+            runs = fRunHead!!.readonlyRuns
+        }
+
+        return runs
+    }
+
+    private fun buildRectRuns(bounds: Rect): List<Int> {
+        val runs = MutableList(kRectRegionRuns) { 0 }
+        runs[0] = bounds.top
+        runs[1] = bounds.bottom
+        runs[2] = 1 // 1 interval for this scanline
+        runs[3] = bounds.left
+        runs[4] = bounds.right
+        runs[5] = SkRegion_kRunTypeSentinel
+        runs[6] = SkRegion_kRunTypeSentinel
+        return runs
+    }
+
+    class RunHead(val isEmptyHead: Boolean = false) {
+        fun setRuns(runs: RunArray, count: Int) {
+            this.runs = runs
+            this.fRunCount = count
+        }
+
+        fun computeRunBounds(): Rect {
+            var runsIndex = 0
+            val top = runs[runsIndex]
+            runsIndex++
+
+            var bot: Int
+            var ySpanCount = 0
+            var intervalCount = 0
+            var left = Int.MAX_VALUE
+            var right = Int.MIN_VALUE
+
+            do {
+                bot = runs[runsIndex]
+                runsIndex++
+                require(bot < SkRegion_kRunTypeSentinel)
+                ySpanCount += 1
+
+                val intervals = runs[runsIndex]
+                runsIndex++
+                require(intervals >= 0)
+                require(intervals < SkRegion_kRunTypeSentinel)
+
+                if (intervals > 0) {
+                    val L = runs[runsIndex]
+                    require(L < SkRegion_kRunTypeSentinel)
+                    if (left > L) {
+                        left = L
+                    }
+
+                    runsIndex += intervals * 2
+                    val R = runs[runsIndex - 1]
+                    require(R < SkRegion_kRunTypeSentinel)
+                    if (right < R) {
+                        right = R
+                    }
+
+                    intervalCount += intervals
+                }
+                require(SkRegion_kRunTypeSentinel == runs[runsIndex])
+                runsIndex += 1 // skip x-sentinel
+
+                // test Y-sentinel
+            } while (SkRegion_kRunTypeSentinel > runs[runsIndex])
+
+            fYSpanCount = ySpanCount
+            fIntervalCount = intervalCount
+
+            return Rect(left, top, right, bot)
+        }
+
+        fun clone(): RunHead {
+            val clone = RunHead(isEmptyHead)
+            clone.fIntervalCount = fIntervalCount
+            clone.fYSpanCount = fYSpanCount
+            clone.runs = runs.clone()
+            clone.fRunCount = fRunCount
+            return clone
+        }
+
+        /**
+         *  Return the scanline that contains the Y value. This requires that the Y
+         *  value is already known to be contained within the bounds of the region,
+         *  and so this routine never returns nullptr.
+         *
+         *  It returns the beginning of the scanline, starting with its Bottom value.
+         */
+        fun findScanline(y: Int): List<Int> {
+            val runs = readonlyRuns
+
+            // if the top-check fails, we didn't do a quick check on the bounds
+            require(y >= runs[0])
+
+            var runsIndex = 1 // skip top-Y
+            while (true) {
+                val bottom = runs[runsIndex]
+                // If we hit this, we've walked off the region, and our bounds check
+                // failed.
+                require(bottom < SkRegion_kRunTypeSentinel)
+                if (y < bottom) {
+                    break
+                }
+                runsIndex = SkipEntireScanline(runsIndex)
+            }
+            return runs.subList(runsIndex, runs.size - runsIndex)
+        }
+
+        /**
+         *  Given a scanline (including its Bottom value at runs[0]), return the next
+         *  scanline. Asserts that there is one (i.e. runs[0] < Sentinel)
+         */
+        fun SkipEntireScanline(_runsIndex: Int): Int {
+            var runsIndex = _runsIndex
+            // we are not the Y Sentinel
+            require(runs[runsIndex] < SkRegion_kRunTypeSentinel)
+
+            val intervals = runs[runsIndex + 1]
+            require(runs[runsIndex + 2 + intervals * 2] == SkRegion_kRunTypeSentinel)
+
+            // skip the entire line [B N [L R] S]
+            runsIndex += 1 + 1 + intervals * 2 + 1
+            return runsIndex
+        }
+
+        private var fIntervalCount: Int = 0
+        private var fYSpanCount: Int = 0
+        var runs = RunArray()
+        var fRunCount: Int = 0
+
+        val readonlyRuns: List<Int> get() = runs.runs
+    }
+
+    private fun setRuns(runs: RunArray, _count: Int): Boolean {
+        require(_count > 0)
+
+        var count = _count
+
+        if (isRunCountEmpty(count)) {
+            assert_sentinel(runs[count - 1], true)
+            return this.setEmpty()
+        }
+
+        // trim off any empty spans from the top and bottom
+        // weird I should need this, perhaps op() could be smarter...
+        var trimmedRuns = runs
+        if (count > kRectRegionRuns) {
+            var stopIndex = count
+            assert_sentinel(runs[0], false) // top
+            assert_sentinel(runs[1], false) // bottom
+            // runs[2] is uncomputed intervalCount
+
+            var trimLeft = false
+            if (runs[3] == SkRegion_kRunTypeSentinel) { // should be first left...
+                trimLeft = true
+                assert_sentinel(runs[1], false) // bot: a sentinal would mean two in a row
+                assert_sentinel(runs[2], false) // intervalcount
+                assert_sentinel(runs[3], false) // left
+                assert_sentinel(runs[4], false) // right
+            }
+
+            assert_sentinel(runs[stopIndex - 1], true)
+            assert_sentinel(runs[stopIndex - 2], true)
+
+            var trimRight = false
+            // now check for a trailing empty span
+            if (runs[stopIndex - 5] == SkRegion_kRunTypeSentinel) {
+                // eek, stop[-4] was a bottom with no x-runs
+                trimRight = true
+            }
+
+            var startIndex = 0
+            if (trimLeft) {
+                startIndex += 3
+                trimmedRuns = trimmedRuns.subList(startIndex, count) // skip empty initial span
+                trimmedRuns[0] = runs[1] // set new top to prev bottom
+            }
+            if (trimRight) {
+                // kill empty last span
+                trimmedRuns[stopIndex - 4] = SkRegion_kRunTypeSentinel
+                stopIndex -= 3
+                assert_sentinel(runs[stopIndex - 1], true)    // last y-sentinel
+                assert_sentinel(runs[stopIndex - 2], true)    // last x-sentinel
+                assert_sentinel(runs[stopIndex - 3], false)   // last right
+                assert_sentinel(runs[stopIndex - 4], false)   // last left
+                assert_sentinel(runs[stopIndex - 5], false)   // last interval-count
+                assert_sentinel(runs[stopIndex - 6], false)   // last bottom
+                trimmedRuns = trimmedRuns.subList(startIndex, stopIndex)
+            }
+
+            count = stopIndex - startIndex
+        }
+
+        require(count >= kRectRegionRuns)
+
+        if (runsAreARect(trimmedRuns, count)) {
+            fBounds = Rect(trimmedRuns[3], trimmedRuns[0], trimmedRuns[4], trimmedRuns[1])
+            return this.setRect(fBounds)
+        }
+
+        //  if we get here, we need to become a complex region
+        if (!this.isComplex() || fRunHead!!.fRunCount != count) {
+            fRunHead = RunHead()
+            fRunHead!!.fRunCount = count
+            require(this.isComplex())
+        }
+
+        // must call this before we can write directly into runs()
+        // in case we are sharing the buffer with another region (copy on write)
+        // fRunHead = fRunHead->ensureWritable();
+        // memcpy(fRunHead, runs, count * sizeof(RunType))
+        fRunHead!!.setRuns(trimmedRuns, count)
+        fBounds = fRunHead!!.computeRunBounds()
+
+        // Our computed bounds might be too large, so we have to check here.
+        if (fBounds.isEmpty) {
+            return this.setEmpty()
+        }
+
+        return true
+    }
+
+    private fun setRect(r: Rect): Boolean {
+        if (r.isEmpty || SkRegion_kRunTypeSentinel == r.right ||
+            SkRegion_kRunTypeSentinel == r.bottom) {
+            return this.setEmpty()
+        }
+        fBounds = r
+        fRunHead = null
+        return true
+    }
+
+    private fun isRunCountEmpty(count: Int): Boolean {
+        return count <= 2
+    }
+
+    private fun runsAreARect(runs: RunArray, count: Int): Boolean {
+        require(count >= kRectRegionRuns)
+
+        if (count == kRectRegionRuns) {
+            assert_sentinel(runs[1], false) // bottom
+            require(1 == runs[2])
+            assert_sentinel(runs[3], false)    // left
+            assert_sentinel(runs[4], false)    // right
+            assert_sentinel(runs[5], true)
+            assert_sentinel(runs[6], true)
+
+            require(runs[0] < runs[1])    // valid height
+            require(runs[3] < runs[4])    // valid width
+
+            return true
+        }
+        return false
+    }
+
+    class RgnOper(var top: Int, private val runArray: RunArray, op: Op) {
+        private val fMin = gOpMinMax[op]!!.min
+        private val fMax = gOpMinMax[op]!!.max
+
+        private var fStartDst = 0
+        private var fPrevDst = 1
+        private var fPrevLen = 0
+
+        fun addSpan(
+            bottom: Int,
+            aRuns: List<Int>,
+            bRuns: List<Int>,
+            aRunsIndex: Int,
+            bRunsIndex: Int
+        ) {
+            // skip X values and slots for the next Y+intervalCount
+            val start = fPrevDst + fPrevLen + 2
+            // start points to beginning of dst interval
+            val stop =
+                operateOnSpan(aRuns, bRuns, aRunsIndex, bRunsIndex, runArray, start, fMin, fMax)
+            val len = stop - start
+            require(len >= 1 && (len and 1) == 1)
+            require(SkRegion_kRunTypeSentinel == runArray[stop - 1])
+
+            // Assert memcmp won't exceed fArray->count().
+            require(runArray.count >= start + len - 1)
+            if (fPrevLen == len &&
+                (1 == len || runArray.subList(fPrevDst, fPrevDst + len).runs
+                    == runArray.subList(start, start + len).runs)) {
+                // update Y value
+                runArray[fPrevDst - 2] = bottom
+            } else { // accept the new span
+                if (len == 1 && fPrevLen == 0) {
+                    top = bottom // just update our bottom
+                } else {
+                    runArray[start - 2] = bottom
+                    runArray[start - 1] = len / 2 // len shr 1
+                    fPrevDst = start
+                    fPrevLen = len
+                }
+            }
+        }
+
+        fun flush(): Int {
+            runArray[fStartDst] = top
+            // Previously reserved enough for TWO sentinals.
+            // SkASSERT(fArray->count() > SkToInt(fPrevDst + fPrevLen));
+            runArray[fPrevDst + fPrevLen] = SkRegion_kRunTypeSentinel
+            return fPrevDst - fStartDst + fPrevLen + 1
+        }
+
+        class SpanRect(
+            private val aRuns: List<Int>,
+            private val bRuns: List<Int>,
+            aIndex: Int,
+            bIndex: Int
+        ) {
+            var fLeft: Int = 0
+            var fRight: Int = 0
+            var fInside: Int = 0
+
+            var fALeft: Int
+            var fARight: Int
+            var fBLeft: Int
+            var fBRight: Int
+            var fARuns: Int
+            var fBRuns: Int
+
+            init {
+                fALeft = aRuns[aIndex]
+                fARight = aRuns[aIndex + 1]
+                fBLeft = bRuns[bIndex]
+                fBRight = bRuns[bIndex + 1]
+                fARuns = aIndex + 2
+                fBRuns = bIndex + 2
+            }
+
+            fun done(): Boolean {
+                require(fALeft <= SkRegion_kRunTypeSentinel)
+                require(fBLeft <= SkRegion_kRunTypeSentinel)
+                return fALeft == SkRegion_kRunTypeSentinel &&
+                    fBLeft == SkRegion_kRunTypeSentinel
+            }
+
+            fun next() {
+                var inside = 0
+                var left = 0
+                var right = 0
+                var aFlush = false
+                var bFlush = false
+
+                var aLeft = fALeft
+                var aRight = fARight
+                var bLeft = fBLeft
+                var bRight = fBRight
+
+                if (aLeft < bLeft) {
+                    inside = 1
+                    left = aLeft
+                    if (aRight <= bLeft) { // [...] <...>
+                        right = aRight
+                        aFlush = true
+                    } else { // [...<..]...> or [...<...>...]
+                        aLeft = bLeft
+                        right = bLeft
+                    }
+                } else if (bLeft < aLeft) {
+                    inside = 2
+                    left = bLeft
+                    if (bRight <= aLeft) { // [...] <...>
+                        right = bRight
+                        bFlush = true
+                    } else { // [...<..]...> or [...<...>...]
+                        bLeft = aLeft
+                        right = aLeft
+                    }
+                } else { // a_left == b_left
+                    inside = 3
+                    left = aLeft // or b_left
+                    if (aRight <= bRight) {
+                        bLeft = aRight
+                        right = aRight
+                        aFlush = true
+                    }
+                    if (bRight <= aRight) {
+                        aLeft = bRight
+                        right = bRight
+                        bFlush = true
+                    }
+                }
+
+                if (aFlush) {
+                    aLeft = aRuns[fARuns]
+                    fARuns++
+                    aRight = aRuns[fARuns]
+                    fARuns++
+                }
+                if (bFlush) {
+                    bLeft = bRuns[fBRuns]
+                    fBRuns++
+                    bRight = bRuns[fBRuns]
+                    fBRuns++
+                }
+
+                require(left <= right)
+
+                // now update our state
+                fALeft = aLeft
+                fARight = aRight
+                fBLeft = bLeft
+                fBRight = bRight
+
+                fLeft = left
+                fRight = right
+                fInside = inside
+            }
+        }
+
+        private fun operateOnSpan(
+            a_runs: List<Int>,
+            b_runs: List<Int>,
+            a_run_index: Int,
+            b_run_index: Int,
+            array: RunArray,
+            dstOffset: Int,
+            min: Int,
+            max: Int
+        ): Int {
+            // This is a worst-case for this span plus two for TWO terminating sentinels.
+            array.resizeToAtLeast(
+                dstOffset + distance_to_sentinel(a_runs, a_run_index) +
+                    distance_to_sentinel(b_runs, b_run_index) + 2)
+            var dstIndex = dstOffset
+
+            val rec = SpanRect(a_runs, b_runs, a_run_index, b_run_index)
+            var firstInterval = true
+
+            while (!rec.done()) {
+                rec.next()
+
+                val left = rec.fLeft
+                val right = rec.fRight
+
+                // add left,right to our dst buffer (checking for coincidence
+                if ((rec.fInside - min).toUInt() <= (max - min).toUInt() &&
+                    left < right) { // skip if equal
+                    if (firstInterval || array[dstIndex - 1] < left) {
+                        array[dstIndex] = left
+                        dstIndex++
+                        array[dstIndex] = right
+                        dstIndex++
+                        firstInterval = false
+                    } else {
+                        // update the right edge
+                        array[dstIndex - 1] = right
+                    }
+                }
+            }
+
+            array[dstIndex] = SkRegion_kRunTypeSentinel
+            dstIndex++
+            return dstIndex // dst - &(*array)[0]
+        }
+
+        private fun distance_to_sentinel(runs: List<Int>, startIndex: Int): Int {
+            var index = startIndex
+            if (runs.size <= index) {
+                println("We fucked up...")
+            }
+            while (runs[index] != SkRegion_kRunTypeSentinel) {
+                if (runs.size <= index + 2) {
+                    println("We fucked up...")
+                    return 256
+                }
+                index += 2
+            }
+            return index - startIndex
+        }
+    }
+
+    private fun operate(
+        aRuns: List<Int>,
+        bRuns: List<Int>,
+        dst: RunArray,
+        op: Op,
+        _aRunsIndex: Int = 0,
+        _bRunsIndex: Int = 0
+    ): Int {
+        var aRunsIndex = _aRunsIndex
+        var bRunsIndex = _bRunsIndex
+
+        var aTop = aRuns[aRunsIndex]
+        aRunsIndex++
+        var aBot = aRuns[aRunsIndex]
+        aRunsIndex++
+        var bTop = bRuns[bRunsIndex]
+        bRunsIndex++
+        var bBot = bRuns[bRunsIndex]
+        bRunsIndex++
+
+        aRunsIndex++ // skip the intervalCount
+        bRunsIndex++ // skip the intervalCount
+
+        val gEmptyScanline: List<Int> = listOf(
+            0, // fake bottom value
+            0, // zero intervals
+            SkRegion_kRunTypeSentinel,
+            // just need a 2nd value, since spanRec.init() reads 2 values, even
+            // though if the first value is the sentinel, it ignores the 2nd value.
+            // w/o the 2nd value here, we might read uninitialized memory.
+            // This happens when we are using gSentinel, which is pointing at
+            // our sentinel value.
+            0
+        )
+        val gSentinel = 2
+
+        // Now aRuns and bRuns to their intervals (or sentinel)
+
+        assert_sentinel(aTop, false)
+        assert_sentinel(aBot, false)
+        assert_sentinel(bTop, false)
+        assert_sentinel(bBot, false)
+
+        val oper = RgnOper(min(aTop, bTop), dst, op)
+
+        var prevBot = SkRegion_kRunTypeSentinel // so we fail the first test
+
+        while (aBot < SkRegion_kRunTypeSentinel || bBot < SkRegion_kRunTypeSentinel) {
+            var top: Int
+            var bot = 0
+
+            var run0 = gEmptyScanline
+            var run0Index = gSentinel
+            var run1 = gEmptyScanline
+            var run1Index = gSentinel
+            var aFlush = false
+            var bFlush = false
+
+            if (aTop < bTop) {
+                top = aTop
+                run0 = aRuns
+                run0Index = aRunsIndex
+                if (aBot <= bTop) { // [...] <...>
+                    bot = aBot
+                    aFlush = true
+                } else { // [...<..]...> or [...<...>...]
+                    aTop = bTop
+                    bot = bTop
+                }
+            } else if (bTop < aTop) {
+                top = bTop
+                run1 = bRuns
+                run1Index = bRunsIndex
+                if (bBot <= aTop) { // [...] <...>
+                    bot = bBot
+                    bFlush = true
+                } else { // [...<..]...> or [...<...>...]
+                    bTop = aTop
+                    bot = aTop
+                }
+            } else { // aTop == bTop
+                top = aTop // or bTop
+                run0 = aRuns
+                run0Index = aRunsIndex
+                run1 = bRuns
+                run1Index = bRunsIndex
+                if (aBot <= bBot) {
+                    bTop = aBot
+                    bot = aBot
+                    aFlush = true
+                }
+                if (bBot <= aBot) {
+                    aTop = bBot
+                    bot = bBot
+                    bFlush = true
+                }
+            }
+
+            if (top > prevBot) {
+                oper.addSpan(top, gEmptyScanline, gEmptyScanline, gSentinel, gSentinel)
+            }
+            oper.addSpan(bot, run0, run1, run0Index, run1Index)
+
+            if (aFlush) {
+                aRunsIndex = skipIntervals(aRuns, aRunsIndex)
+                aTop = aBot
+                aBot = aRuns[aRunsIndex]
+                aRunsIndex++ // skip to next index
+                aRunsIndex++ // skip uninitialized intervalCount
+                if (aBot == SkRegion_kRunTypeSentinel) {
+                    aTop = aBot
+                }
+            }
+            if (bFlush) {
+                bRunsIndex = skipIntervals(bRuns, bRunsIndex)
+                bTop = bBot
+                bBot = bRuns[bRunsIndex]
+                bRunsIndex++ // skip to next index
+                bRunsIndex++ // skip uninitialized intervalCount
+                if (bBot == SkRegion_kRunTypeSentinel) {
+                    bTop = bBot
+                }
+            }
+
+            prevBot = bot
+        }
+
+        return oper.flush()
+    }
+
+    private fun skipIntervals(runs: List<Int>, index: Int): Int {
+        val intervals = runs[index - 1]
+        return index + intervals * 2 + 1
+    }
+
+    /**
+     * Perform the specified Op on this region and the specified region. Return
+     * true if the result of the op is not empty.
+     */
+    fun op(region: Region, op: Op): Boolean {
+        return op(this, region, op)
+    }
+
+    /**
+     * Perform the specified Op on this region and the specified rect. Return
+     * true if the result of the op is not empty.
+     */
+    fun op(left: Int, top: Int, right: Int, bottom: Int, op: Op): Boolean {
+        return op(Rect(left, top, right, bottom), op)
+    }
+
+    /**
+     * Perform the specified Op on this region and the specified rect. Return
+     * true if the result of the op is not empty.
+     */
+    fun op(r: Rect, op: Op): Boolean {
+        return op(from(r), op)
+    }
+
+    /**
+     * Set this region to the result of performing the Op on the specified rect
+     * and region. Return true if the result is not empty.
+     */
+    fun op(rect: Rect, region: Region, op: Op): Boolean {
+        return op(from(rect), region, op)
+    }
+
+    fun minus(other: Region): Region {
+        val thisRegion = from(this)
+        thisRegion.op(other, Region.Op.XOR)
+        return thisRegion
+    }
+
+    companion object {
+        val EMPTY get() = Region()
+
+        const val SkRegion_kRunTypeSentinel = 0x7FFFFFFF
+
+        const val kRectRegionRuns = 7
+
+        class MinMax(val min: Int, val max: Int)
+
+        val gOpMinMax = mapOf(
+            Op.DIFFERENCE to MinMax(1, 1),
+            Op.INTERSECT to MinMax(3, 3),
+            Op.UNION to MinMax(1, 3),
+            Op.XOR to MinMax(1, 2)
+        )
+
+        fun from(
+            left: Int,
+            top: Int,
+            right: Int,
+            bottom: Int
+        ): Region = from(Rect(left, top, right, bottom))
+
+        fun from(region: Region): Region = Region().also { it.set(region) }
+
+        fun from(rect: Rect? = null): Region = Region().also {
+            it.fRunHead = null
+            it.setRect(rect ?: Rect.EMPTY)
+        }
+
+        fun from(rect: RectF?): Region = from(rect?.toRect())
+
+        fun from(): Region = from(Rect.EMPTY)
+
+        private fun SkRegionValueIsSentinel(value: Int): Boolean {
+            return value == SkRegion_kRunTypeSentinel
+        }
+
+        private fun assert_sentinel(value: Int, isSentinel: Boolean) {
+            require(SkRegionValueIsSentinel(value) == isSentinel)
+        }
+
+        private fun getRectsFromString(regionString: String): Array<Rect> {
+            val rects: ArrayList<Rect> = ArrayList()
+
+            if (regionString == "SkRegion()") {
+                return rects.toTypedArray()
+            }
+
+            var nativeRegionString = regionString.replace("SkRegion", "")
+            nativeRegionString = nativeRegionString.substring(2, nativeRegionString.length - 2)
+            nativeRegionString = nativeRegionString.replace(")(", ",")
+
+            var rect = Rect.EMPTY
+            for ((i, coord) in nativeRegionString.split(",").withIndex()) {
+                when (i % 4) {
+                    0 -> rect = Rect(coord.toInt(), 0, 0, 0)
+                    1 -> rect = Rect(rect.left, coord.toInt(), 0, 0)
+                    2 -> rect = Rect(rect.left, rect.top, coord.toInt(), 0)
+                    3 -> {
+                        rect = Rect(rect.left, rect.top, rect.right, coord.toInt())
+                        rects.add(rect)
+                    }
+                }
+            }
+
+            return rects.toTypedArray()
+        }
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/region/RegionEntry.kt b/libraries/flicker/src/com/android/server/wm/traces/common/region/RegionEntry.kt
new file mode 100644
index 0000000..4618135
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/region/RegionEntry.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 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.traces.common.region
+
+import com.android.server.wm.traces.common.ITraceEntry
+
+/**
+ * Represents a single Region trace entry.
+ *
+ * This is a generic object that is reused by both Flicker and Winscope and cannot
+ * access internal Java/Android functionality
+ *
+ * The timestamp constructor must be a string due to lack of Kotlin/KotlinJS Long compatibility
+ *
+ **/
+class RegionEntry(val region: Region, _timestamp: String = "0") : ITraceEntry {
+    override val timestamp: Long = _timestamp.toLong()
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/region/RegionTrace.kt b/libraries/flicker/src/com/android/server/wm/traces/common/region/RegionTrace.kt
new file mode 100644
index 0000000..855c778
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/region/RegionTrace.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 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.traces.common.region
+
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.ITrace
+
+/**
+ * Contains a collection of parsed Region trace entries.
+ *
+ * Each entry is parsed into a list of [RegionEntry] objects.
+ *
+ * This is a generic object that is reused by both Flicker and Winscope and cannot
+ * access internal Java/Android functionality
+ *
+ */
+data class RegionTrace(
+    val components: Array<out FlickerComponentName>,
+    override val entries: Array<RegionEntry>
+) : ITrace<RegionEntry>,
+        List<RegionEntry> by entries.toList() {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is RegionTrace) return false
+
+        if (!components.contentEquals(other.components)) return false
+        if (!entries.contentEquals(other.entries)) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = components.contentHashCode()
+        result = 31 * result + entries.contentHashCode()
+        return result
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/service/TaggingEngine.kt b/libraries/flicker/src/com/android/server/wm/traces/common/service/TaggingEngine.kt
index ef46d8e..77cb837 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/service/TaggingEngine.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/service/TaggingEngine.kt
@@ -76,6 +76,6 @@
             .mapValues { (key, value) -> TagState(key.toString(), value.flatten().toTypedArray()) }
             .values.toTypedArray()
 
-        return TagTrace(tagStates, source = "")
+        return TagTrace(tagStates)
     }
 }
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/AppCloseProcessor.kt b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/AppCloseProcessor.kt
index 7b3ffff..727f34e 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/AppCloseProcessor.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/AppCloseProcessor.kt
@@ -20,7 +20,7 @@
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory.isLayerTransformFlagSet
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory.isLayerTransformIdentity
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.layers.Transform
 import com.android.server.wm.traces.common.service.PlatformConsts.TYPE_APPLICATION_STARTING
 import com.android.server.wm.traces.common.service.PlatformConsts.TYPE_BASE_APPLICATION
@@ -40,7 +40,7 @@
         .isAppTransitionIdle(/* default display */ 0)
     private val wmStateComplete = WindowManagerConditionsFactory.isWMStateComplete()
     private val translatingWindows =
-        HashMap<String, DeviceStateDump<WindowManagerState, LayerTraceEntry>>()
+        HashMap<String, DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>>()
 
     override fun getInitialState(tags: MutableMap<Long, MutableList<Tag>>) =
         RetrieveClosingAppLayerId(tags)
@@ -53,9 +53,9 @@
         tags: MutableMap<Long, MutableList<Tag>>
     ) : BaseState(tags) {
         override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             val isStableState = wmStateIdle.isSatisfied(current) ||
                 wmStateComplete.isSatisfied(current) ||
@@ -98,9 +98,9 @@
         private val isTranslating = isLayerTransformFlagSet(layerId, Transform.TRANSLATE_VAL)
 
         override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             if (previous == null) return this
             val startedTransforming = isTranslating.isSatisfied(current) &&
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/AppLaunchProcessor.kt b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/AppLaunchProcessor.kt
index cb75954..8de18ca 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/AppLaunchProcessor.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/AppLaunchProcessor.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.traces.common.service.processors
 
 import com.android.server.wm.traces.common.DeviceStateDump
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.tags.Tag
 import com.android.server.wm.traces.common.tags.Transition
 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
@@ -29,7 +29,7 @@
 class AppLaunchProcessor(logger: (String) -> Unit) : TransitionProcessor(logger) {
     override val transition = Transition.APP_LAUNCH
     private val windowsBecomeVisible =
-        HashMap<Int, DeviceStateDump<WindowManagerState, LayerTraceEntry>>()
+        HashMap<Int, DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>>()
 
     override fun getInitialState(tags: MutableMap<Long, MutableList<Tag>>) =
         WaitUntilWindowIsInVisibleActivity(tags)
@@ -42,9 +42,9 @@
         tags: MutableMap<Long, MutableList<Tag>>
     ) : BaseState(tags) {
         override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             if (previous == null) return this
             val prevVisibleWindows = previous.wmState.visibleWindows
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/BaseFsmState.kt b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/BaseFsmState.kt
index 2b7bb3b..82d197d 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/BaseFsmState.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/BaseFsmState.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.traces.common.service.processors
 
 import com.android.server.wm.traces.common.DeviceStateDump
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.tags.Tag
 import com.android.server.wm.traces.common.tags.Transition
 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
@@ -33,15 +33,15 @@
     internal val transition: Transition
 ) : FSMState(tags) {
     protected abstract fun doProcessState(
-        previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-        current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-        next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+        previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+        current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+        next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
     ): FSMState
 
     override fun process(
-        previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-        current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-        next: DeviceStateDump<WindowManagerState, LayerTraceEntry>?
+        previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+        current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+        next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?
     ): FSMState? {
         return if (next == null) {
             // last state
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/FSMState.kt b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/FSMState.kt
index 6c661ae..13bb4f4 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/FSMState.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/FSMState.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.traces.common.service.processors
 
 import com.android.server.wm.traces.common.DeviceStateDump
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.tags.Tag
 import com.android.server.wm.traces.common.tags.Transition
 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
@@ -31,13 +31,13 @@
  */
 abstract class FSMState(protected val tags: MutableMap<Long, MutableList<Tag>>) {
     abstract fun process(
-        previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-        current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-        next: DeviceStateDump<WindowManagerState, LayerTraceEntry>?
+        previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+        current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+        next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?
     ): FSMState?
 
     protected fun addStartTransitionTag(
-        state: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
+        state: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
         transition: Transition,
         layerId: Int = 0,
         windowToken: String = "",
@@ -54,7 +54,7 @@
     }
 
     protected fun addEndTransitionTag(
-        state: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
+        state: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
         transition: Transition,
         layerId: Int = 0,
         windowToken: String = "",
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/ImeAppearProcessor.kt b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/ImeAppearProcessor.kt
index 44b5c3c..0079b6a 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/ImeAppearProcessor.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/ImeAppearProcessor.kt
@@ -23,7 +23,7 @@
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory.isLayerColorAlphaOne
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory.isLayerTransformFlagSet
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory.isLayerVisible
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.layers.Transform
 import com.android.server.wm.traces.common.service.PlatformConsts
 import com.android.server.wm.traces.common.tags.Tag
@@ -49,9 +49,9 @@
         private val prevImeInvisible = newImeVisible.negate()
 
         override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             if (previous == null) return this
 
@@ -64,7 +64,7 @@
         }
 
         private fun processInputMethodVisible(
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             logger.invoke("(${current.layerState.timestamp}) IME appear started.")
             // add factory method as well
@@ -85,9 +85,9 @@
         private val layerId: Int
     ) : BaseState(tags) {
         override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             val isImeAppearFinished = isImeAppearFinished.isSatisfied(current)
 
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/ImeDisappearProcessor.kt b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/ImeDisappearProcessor.kt
index 3aff56f..8c4dabd 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/ImeDisappearProcessor.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/ImeDisappearProcessor.kt
@@ -22,7 +22,7 @@
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory.isImeShown
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory.isLayerColorAlphaOne
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory.isLayerTransformFlagSet
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.layers.Transform
 import com.android.server.wm.traces.common.service.PlatformConsts
 import com.android.server.wm.traces.common.tags.Tag
@@ -66,9 +66,9 @@
             ))
 
         override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             if (previous == null) return this
 
@@ -83,7 +83,7 @@
         }
 
         private fun processImeDisappearing(
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             logger.invoke("(${current.layerState.timestamp}) IME disappear started.")
             val inputMethodLayer = current.layerState.visibleLayers.first {
@@ -104,9 +104,9 @@
     ) : BaseState(tags) {
         private val imeNotShown = isImeShown(PlatformConsts.DEFAULT_DISPLAY).negate()
         override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             return if (imeNotShown.isSatisfied(current)) {
                 // tag on the last complete state at the start
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/PipEnterProcessor.kt b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/PipEnterProcessor.kt
index 4ec4099..1245ad8 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/PipEnterProcessor.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/PipEnterProcessor.kt
@@ -23,7 +23,7 @@
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory.isAppTransitionIdle
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory.isPipWindowLayerSizeMatch
 import com.android.server.wm.traces.common.layers.Layer
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.layers.Transform
 import com.android.server.wm.traces.common.service.PlatformConsts
 import com.android.server.wm.traces.common.tags.Tag
@@ -54,7 +54,7 @@
     ) : BaseState(tags) {
 
         private fun getScalingLayers(
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): List<Layer> = current.layerState.flattenedLayers.filter {
             WindowManagerConditionsFactory.isLayerTransformFlagSet(
                 it.id,
@@ -63,9 +63,9 @@
         }
 
         override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             if (previous == null) return this
 
@@ -92,7 +92,7 @@
         }
 
         private fun processPipEnterStart(
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             val pipWindow = current.wmState.pinnedWindows.first()
             val startTimestamp = allScalingLayers[pipWindow.layerId]
@@ -128,9 +128,9 @@
         private val layerId: Int
     ) : BaseState(tags) {
         override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             return if (isPipEnterFinished.isSatisfied(current)) {
                 // tag on the last complete state at the start
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/PipExitProcessor.kt b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/PipExitProcessor.kt
index 3ed713e..3660b4c 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/PipExitProcessor.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/PipExitProcessor.kt
@@ -23,7 +23,7 @@
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory.isLayerTransformFlagSet
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory.isLayerVisible
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory.isWMStateComplete
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.layers.Transform
 import com.android.server.wm.traces.common.tags.Tag
 import com.android.server.wm.traces.common.tags.Transition
@@ -37,7 +37,7 @@
 class PipExitProcessor(logger: (String) -> Unit) : TransitionProcessor(logger) {
     override val transition = Transition.PIP_EXIT
     private val scalingWindows =
-        HashMap<String, DeviceStateDump<WindowManagerState, LayerTraceEntry>>()
+        HashMap<String, DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>>()
     private val areLayersAnimating = hasLayersAnimating()
     private val wmStateIdle = isAppTransitionIdle(/* default display */ 0)
     private val wmStateComplete = isWMStateComplete()
@@ -52,9 +52,9 @@
         tags: MutableMap<Long, MutableList<Tag>>
     ) : BaseState(tags) {
         override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             if (previous == null) return this
             // There can only ever be one pinned window at a time
@@ -113,9 +113,9 @@
         private val layerId: Int
     ) : BaseState(tags) {
         override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             val layerInvisible = isLayerVisible(layerId).negate().isSatisfied(current)
             val layerColorAlphaOne = isLayerColorAlphaOne(layerId).isSatisfied(current)
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/PipExpandProcessor.kt b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/PipExpandProcessor.kt
index c15b731..1a9b816 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/PipExpandProcessor.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/PipExpandProcessor.kt
@@ -19,7 +19,7 @@
 import com.android.server.wm.traces.common.DeviceStateDump
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory.isLayerTransformFlagSet
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory.isLayerTransformIdentity
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.layers.Transform
 import com.android.server.wm.traces.common.tags.Tag
 import com.android.server.wm.traces.common.tags.Transition
@@ -32,7 +32,7 @@
 class PipExpandProcessor(logger: (String) -> Unit) : TransitionProcessor(logger) {
     override val transition = Transition.PIP_EXPAND
     private val scalingLayers =
-        HashMap<Int, DeviceStateDump<WindowManagerState, LayerTraceEntry>>()
+        HashMap<Int, DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>>()
 
     override fun getInitialState(tags: MutableMap<Long, MutableList<Tag>>) =
         WaitUntilAppIsNoLongerPinned(tags)
@@ -44,9 +44,9 @@
         tags: MutableMap<Long, MutableList<Tag>>
     ) : BaseState(tags) {
         override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             // Pinned window is no longer pinned
             val prevPinnedWindows = previous?.wmState?.pinnedWindows?.toList() ?: emptyList()
@@ -71,9 +71,9 @@
         private val isScaling = isLayerTransformFlagSet(layerId, Transform.SCALE_VAL)
         private val isIdentity = isLayerTransformIdentity(layerId)
         override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             if (previous == null) return this
             if (previous.wmState.pinnedWindows.isNotEmpty()) {
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/PipResizeProcessor.kt b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/PipResizeProcessor.kt
index 7bb89d2..aece66c 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/PipResizeProcessor.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/PipResizeProcessor.kt
@@ -18,7 +18,7 @@
 
 import com.android.server.wm.traces.common.DeviceStateDump
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory.isLayerTransformFlagSet
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.layers.Transform
 import com.android.server.wm.traces.common.tags.Tag
 import com.android.server.wm.traces.common.tags.Transition
@@ -31,7 +31,7 @@
 class PipResizeProcessor(logger: (String) -> Unit) : TransitionProcessor(logger) {
     override val transition = Transition.PIP_RESIZE
     private val scalingWindows =
-        HashMap<String, DeviceStateDump<WindowManagerState, LayerTraceEntry>>()
+        HashMap<String, DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>>()
 
     override fun getInitialState(tags: MutableMap<Long, MutableList<Tag>>) =
         WaitUntilAppStopsAnimatingYetStillPinned(tags)
@@ -40,9 +40,9 @@
         tags: MutableMap<Long, MutableList<Tag>>
     ) : BaseState(tags) {
         override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             val currPinnedWindow = current.wmState.pinnedWindows.firstOrNull() ?: return this
             previous?.wmState?.pinnedWindows?.firstOrNull() ?: return this
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/RotationProcessor.kt b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/RotationProcessor.kt
index be29ced..d6bf29d 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/RotationProcessor.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/RotationProcessor.kt
@@ -20,7 +20,7 @@
 import com.android.server.wm.traces.common.FlickerComponentName
 import com.android.server.wm.traces.common.RectF
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.tags.Tag
 import com.android.server.wm.traces.common.tags.Transition
 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
@@ -43,9 +43,9 @@
         tags: MutableMap<Long, MutableList<Tag>>
     ) : BaseState(tags) {
         override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             val currDisplayRect = current.wmState.displaySize()
             logger.invoke("(${current.wmState.timestamp}) Initial state. " +
@@ -62,9 +62,9 @@
         private val currDisplayRect: RectF
     ) : BaseState(tags) {
         override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             val newWmDisplayRect = current.wmState.displaySize()
             val newLayersDisplayRect = current.layerState.screenBounds()
@@ -89,7 +89,7 @@
         }
 
         private fun processDisplaySizeChange(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
             newDisplayRect: RectF
         ): FSMState {
             logger.invoke("(${previous.wmState.timestamp}) Display size changed " +
@@ -115,9 +115,9 @@
         private val wmStateComplete = WindowManagerConditionsFactory.isWMStateComplete()
 
         override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState {
             val anyLayerAnimating = areLayersAnimating.isSatisfied(current)
             val rotationLayerExists = rotationLayerExists.isSatisfied(current)
@@ -153,7 +153,7 @@
     }
 
     companion object {
-        private fun LayerTraceEntry.screenBounds() = this.displays.minByOrNull { it.id }
+        private fun BaseLayerTraceEntry.screenBounds() = this.displays.minByOrNull { it.id }
             ?.layerStackSpace?.toRectF() ?: this.children
             .sortedBy { it.id }
             .firstOrNull { it.isRootLayer }
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/TransitionProcessor.kt b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/TransitionProcessor.kt
index 31b30ba..47f6cf2 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/TransitionProcessor.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/service/processors/TransitionProcessor.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.traces.common.service.processors
 
 import com.android.server.wm.traces.common.DeviceStateDump
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.layers.LayersTrace
 import com.android.server.wm.traces.common.service.ITagProcessor
 import com.android.server.wm.traces.common.tags.Tag
@@ -40,9 +40,9 @@
         tags: MutableMap<Long, MutableList<Tag>>
     ) : BaseFsmState(tags, logger, transition) {
         abstract override fun doProcessState(
-            previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
-            current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-            next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+            previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?,
+            current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         ): FSMState
     }
 
@@ -64,10 +64,11 @@
         val dumpList = createDumpList(wmTrace, layersTrace)
         val dumpIterator = dumpList.iterator()
 
-        // keep always a reference to previous, current and next states
-        var previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?
-        var current: DeviceStateDump<WindowManagerState, LayerTraceEntry>? = null
-        var next: DeviceStateDump<WindowManagerState, LayerTraceEntry>? = dumpIterator.next()
+        // always keep a reference to previous, current and next states
+        var previous: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>?
+        var current: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>? = null
+        var next: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>? =
+            dumpIterator.next()
         while (currPosition != null) {
             previous = current
             current = next
@@ -86,14 +87,14 @@
             val stateTags = entry.value
             TagState(timestamp.toString(), stateTags.toTypedArray())
         }
-        return TagTrace(tagStates.toTypedArray(), source = "")
+        return TagTrace(tagStates.toTypedArray())
     }
 
     companion object {
         internal fun createDumpList(
             wmTrace: WindowManagerTrace,
             layersTrace: LayersTrace
-        ): List<DeviceStateDump<WindowManagerState, LayerTraceEntry>> {
+        ): List<DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>> {
             val wmTimestamps = wmTrace.map { it.timestamp }.toTypedArray()
             val layersTimestamps = layersTrace.map { it.timestamp }.toTypedArray()
             val fullTimestamps = setOf(*wmTimestamps, *layersTimestamps).sorted()
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/tags/TagTrace.kt b/libraries/flicker/src/com/android/server/wm/traces/common/tags/TagTrace.kt
index 9aa578f..c0c2140 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/tags/TagTrace.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/tags/TagTrace.kt
@@ -21,11 +21,9 @@
 /**
  * Holds the entire list of [TagState]s representing an entire trace that has been tagged.
  * @param entries Array of tagged states within the trace
- * @param source Source of the trace file
  */
 data class TagTrace(
-    override val entries: Array<TagState>,
-    override val source: String
+    override val entries: Array<TagState>
 ) : ITrace<TagState>,
     List<TagState> by entries.toList() {
     override fun toString(): String = "FlickerTagTrace(${entries.firstOrNull()?.timestamp ?: 0}, " +
@@ -35,13 +33,11 @@
         if (this === other) return true
         if (other !is TagTrace) return false
         if (!entries.contentEquals(other.entries)) return false
-        if (source != other.source) return false
         return true
     }
 
     override fun hashCode(): Int {
         var result = entries.contentDeepHashCode()
-        result = 31 * result + source.hashCode()
         return result
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/windowmanager/WindowManagerState.kt b/libraries/flicker/src/com/android/server/wm/traces/common/windowmanager/WindowManagerState.kt
index ccb31f1..4842d73 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/windowmanager/WindowManagerState.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/windowmanager/WindowManagerState.kt
@@ -23,6 +23,7 @@
 import com.android.server.wm.traces.common.windowmanager.windows.KeyguardControllerState
 import com.android.server.wm.traces.common.windowmanager.windows.RootWindowContainer
 import com.android.server.wm.traces.common.windowmanager.windows.Task
+import com.android.server.wm.traces.common.windowmanager.windows.TaskFragment
 import com.android.server.wm.traces.common.windowmanager.windows.WindowContainer
 import com.android.server.wm.traces.common.windowmanager.windows.WindowManagerPolicy
 import com.android.server.wm.traces.common.windowmanager.windows.WindowState
@@ -65,10 +66,14 @@
     val displays: Array<DisplayContent>
         get() = windowContainers.filterIsInstance<DisplayContent>().toTypedArray()
 
-    // Stacks in z-order with the top most at the front of the list, starting with primary display.
+    // Root tasks in z-order with the top most at the front of the list, starting with primary display.
     val rootTasks: Array<Task>
         get() = displays.flatMap { it.rootTasks.toList() }.toTypedArray()
 
+    // TaskFragments  in z-order with the top most at the front of the list.
+    val taskFragments: Array<TaskFragment>
+        get() = windowContainers.filterIsInstance<TaskFragment>().toTypedArray()
+
     // Windows in z-order with the top most at the front of the list.
     val windowStates: Array<WindowState>
         get() = windowContainers.filterIsInstance<WindowState>().toTypedArray()
@@ -95,9 +100,10 @@
                 activity?.isVisible ?: true
             }
             .toTypedArray()
+    val visibleAppWindows: Array<WindowState>
+        get() = visibleWindows.filter { it.isAppWindow }.toTypedArray()
     val topVisibleAppWindow: String
-        get() = visibleWindows
-            .filter { it.isAppWindow }
+        get() = visibleAppWindows
             .map { it.title }
             .firstOrNull() ?: ""
     val pinnedWindows: Array<WindowState>
@@ -296,12 +302,13 @@
     fun isComplete(): Boolean = !isIncomplete()
     fun isIncomplete(): Boolean {
         return rootTasks.isEmpty() || focusedStackId == -1 || windowStates.isEmpty() ||
-            (focusedApp.isEmpty() && homeActivity == null) || focusedWindow.isEmpty() ||
+            // overview screen has no focused window
+            ((focusedApp.isEmpty() || focusedWindow.isEmpty()) && homeActivity == null) ||
             (focusedActivity.isEmpty() || resumedActivities.isEmpty()) &&
             !keyguardControllerState.isKeyguardShowing
     }
 
-    fun asTrace(): WindowManagerTrace = WindowManagerTrace(arrayOf(this), source = "")
+    fun asTrace(): WindowManagerTrace = WindowManagerTrace(arrayOf(this))
 
     override fun toString(): String {
         return "${prettyTimestamp(timestamp)} (timestamp=$timestamp)"
@@ -344,4 +351,4 @@
     override fun equals(other: Any?): Boolean {
         return other is WindowManagerState && other.timestamp == this.timestamp
     }
-}
\ No newline at end of file
+}
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/windowmanager/WindowManagerTrace.kt b/libraries/flicker/src/com/android/server/wm/traces/common/windowmanager/WindowManagerTrace.kt
index 3517ba9..33695a2 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/windowmanager/WindowManagerTrace.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/windowmanager/WindowManagerTrace.kt
@@ -29,13 +29,12 @@
  *
  */
 data class WindowManagerTrace(
-    override val entries: Array<WindowManagerState>,
-    override val source: String
+    override val entries: Array<WindowManagerState>
 ) : ITrace<WindowManagerState>,
     List<WindowManagerState> by entries.toList() {
     override fun toString(): String {
-        return "WindowManagerTrace(Start: ${entries.first()}, " +
-            "End: ${entries.last()})"
+        return "WindowManagerTrace(Start: ${entries.firstOrNull()}, " +
+            "End: ${entries.lastOrNull()})"
     }
 
     override fun equals(other: Any?): Boolean {
@@ -43,15 +42,12 @@
         if (other !is WindowManagerTrace) return false
 
         if (!entries.contentEquals(other.entries)) return false
-        if (source != other.source) return false
 
         return true
     }
 
     override fun hashCode(): Int {
-        var result = entries.contentHashCode()
-        result = 31 * result + source.hashCode()
-        return result
+        return entries.contentHashCode()
     }
 
     /**
@@ -66,7 +62,6 @@
             this.entries
                 .dropWhile { it.timestamp < from }
                 .dropLastWhile { it.timestamp > to }
-                .toTypedArray(),
-            source = "")
+                .toTypedArray())
     }
-}
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/common/windowmanager/windows/WindowState.kt b/libraries/flicker/src/com/android/server/wm/traces/common/windowmanager/windows/WindowState.kt
index f11bfc8..253be05 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/common/windowmanager/windows/WindowState.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/common/windowmanager/windows/WindowState.kt
@@ -18,7 +18,7 @@
 
 import com.android.server.wm.traces.common.Size
 import com.android.server.wm.traces.common.Rect
-import com.android.server.wm.traces.common.Region
+import com.android.server.wm.traces.common.region.Region
 
 /**
  * Represents a window in the window manager hierarchy
@@ -55,7 +55,7 @@
     val isDebuggerWindow: Boolean = windowType == WINDOW_TYPE_DEBUGGER
     val isValidNavBarType: Boolean = attributes.isValidNavBarType
 
-    val frameRegion: Region = Region(frame)
+    val frameRegion: Region = Region.from(frame)
 
     private fun getWindowTypeSuffix(windowType: Int): String =
         when (windowType) {
diff --git a/libraries/flicker/src/com/android/server/wm/traces/parser/DeviceDumpParser.kt b/libraries/flicker/src/com/android/server/wm/traces/parser/DeviceDumpParser.kt
index d47bdbc..c7bc5cc 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/parser/DeviceDumpParser.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/parser/DeviceDumpParser.kt
@@ -19,7 +19,7 @@
 import com.android.server.wm.traces.common.DeviceStateDump
 import com.android.server.wm.traces.common.DeviceTraceDump
 import com.android.server.wm.traces.common.layers.LayersTrace
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace
 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
 import com.android.server.wm.traces.parser.layers.LayersTraceParser
@@ -34,7 +34,7 @@
         /**
          * Creates a device state dump containing the [WindowManagerTrace] and [LayersTrace]
          * obtained from a `dumpsys` command. The parsed traces will contain a single
-         * [WindowManagerState] or [LayerTraceEntry].
+         * [WindowManagerState] or [BaseLayerTraceEntry].
          *
          * @param wmTraceData [WindowManagerTrace] content
          * @param layersTraceData [LayersTrace] content
@@ -43,7 +43,7 @@
         fun fromDump(
             wmTraceData: ByteArray,
             layersTraceData: ByteArray
-        ): DeviceStateDump<WindowManagerState?, LayerTraceEntry?> {
+        ): DeviceStateDump<WindowManagerState?, BaseLayerTraceEntry?> {
             return DeviceStateDump(
                 wmState = if (wmTraceData.isNotEmpty()) {
                     WindowManagerTraceParser.parseFromDump(wmTraceData).first()
diff --git a/libraries/flicker/src/com/android/server/wm/traces/parser/Extensions.kt b/libraries/flicker/src/com/android/server/wm/traces/parser/Extensions.kt
index 32f2225..16dda67 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/parser/Extensions.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/parser/Extensions.kt
@@ -25,46 +25,17 @@
 import com.android.server.wm.traces.common.DeviceStateDump
 import com.android.server.wm.traces.common.FlickerComponentName
 import com.android.server.wm.traces.common.Rect
-import com.android.server.wm.traces.common.Region
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
 
 internal const val LOG_TAG = "AMWM_FLICKER"
 
-fun Region.toAndroidRegion(): android.graphics.Region {
-    return android.graphics.Region(left, top, right, bottom)
-}
-
 fun Rect.toAndroidRect(): android.graphics.Rect {
     return android.graphics.Rect(left, top, right, bottom)
 }
 
-/**
- * Subtracts [other] region from this [this] region
- */
-fun Region.minus(other: Region): android.graphics.Region = minus(other.toAndroidRegion())
-
-/**
- * Subtracts [other] region from this [this] region
- */
-fun Region.minus(other: android.graphics.Region): android.graphics.Region {
-    val thisRegion = this.toAndroidRegion()
-    thisRegion.op(other, android.graphics.Region.Op.XOR)
-    return thisRegion
-}
-
-/**
- * Adds [other] region to this [this] region
- */
-fun Region.plus(other: Region): android.graphics.Region = plus(other.toAndroidRegion())
-
-/**
- * Adds [other] region to this [this] region
- */
-fun Region.plus(other: android.graphics.Region): android.graphics.Region {
-    val thisRegion = this.toAndroidRegion()
-    thisRegion.op(other, android.graphics.Region.Op.XOR)
-    return thisRegion
+fun android.graphics.Rect.toFlickerRect(): Rect {
+    return Rect(left, top, right, bottom)
 }
 
 private fun executeCommand(uiAutomation: UiAutomation, cmd: String): ByteArray {
@@ -108,7 +79,7 @@
 fun getCurrentStateDump(
     uiAutomation: UiAutomation,
     @WmStateDumpFlags dumpFlags: Int = FLAG_STATE_DUMP_FLAG_WM.or(FLAG_STATE_DUMP_FLAG_LAYERS)
-): DeviceStateDump<WindowManagerState?, LayerTraceEntry?> {
+): DeviceStateDump<WindowManagerState?, BaseLayerTraceEntry?> {
     val currentStateDump = getCurrentState(uiAutomation, dumpFlags)
     val wmTraceData = currentStateDump.first
     val layersTraceData = currentStateDump.second
diff --git a/libraries/flicker/src/com/android/server/wm/traces/parser/errors/ErrorTraceParserUtil.kt b/libraries/flicker/src/com/android/server/wm/traces/parser/errors/ErrorTraceParserUtil.kt
index e91b956..37e1329 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/parser/errors/ErrorTraceParserUtil.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/parser/errors/ErrorTraceParserUtil.kt
@@ -22,7 +22,6 @@
 import com.android.server.wm.traces.common.errors.ErrorState
 import com.android.server.wm.traces.common.errors.ErrorTrace
 import com.android.server.wm.traces.parser.LOG_TAG
-import java.nio.file.Path
 import kotlin.system.measureTimeMillis
 
 /**
@@ -35,13 +34,11 @@
          * of trace entries, storing the flattened layers into its hierarchical structure.
          *
          * @param data binary proto data
-         * @param source Path to source of data for additional debug information
          */
         @JvmOverloads
         @JvmStatic
         fun parseFromTrace(
-            data: ByteArray,
-            source: Path? = null
+            data: ByteArray
         ): ErrorTrace {
             var fileProto: FlickerErrorTraceProto? = null
             try {
@@ -54,7 +51,7 @@
                 throw RuntimeException(e)
             }
             return fileProto?.let {
-                parseFromTrace(it, source)
+                parseFromTrace(it)
             } ?: error("Unable to read flicker errors trace file")
         }
 
@@ -63,13 +60,11 @@
          * of trace entries, storing the flattened layers into its hierarchical structure.
          *
          * @param proto Parsed proto data
-         * @param source Path to source of data for additional debug information
          */
         @JvmOverloads
         @JvmStatic
         fun parseFromTrace(
-            proto: FlickerErrorTraceProto,
-            source: Path? = null
+            proto: FlickerErrorTraceProto
         ): ErrorTrace {
             val states = mutableListOf<ErrorState>()
             var traceParseTime = 0L
@@ -95,8 +90,7 @@
                 traceParseTime += errorParseTime
             }
             return ErrorTrace(
-                    entries = states.toTypedArray(),
-                    source = source?.toString() ?: ""
+                    entries = states.toTypedArray()
             )
         }
     }
diff --git a/libraries/flicker/src/com/android/server/wm/traces/parser/layers/LayerTraceEntryLazy.kt b/libraries/flicker/src/com/android/server/wm/traces/parser/layers/LayerTraceEntryLazy.kt
new file mode 100644
index 0000000..035bbbf
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/traces/parser/layers/LayerTraceEntryLazy.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 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.traces.parser.layers
+
+import android.graphics.Rect
+import android.surfaceflinger.nano.Common
+import android.surfaceflinger.nano.Common.RegionProto
+import android.surfaceflinger.nano.Display
+import android.surfaceflinger.nano.Layers
+import com.android.server.wm.traces.common.ActiveBuffer
+import com.android.server.wm.traces.common.Color
+import com.android.server.wm.traces.common.RectF
+import com.android.server.wm.traces.common.Size
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
+import com.android.server.wm.traces.common.layers.Layer
+import com.android.server.wm.traces.common.layers.LayerTraceEntryBuilder
+import com.android.server.wm.traces.common.region.Region
+
+class LayerTraceEntryLazy(
+    override val timestamp: Long,
+    override val hwcBlob: String = "",
+    override val where: String = "",
+    private val ignoreLayersStackMatchNoDisplay: Boolean = true,
+    private val ignoreLayersInVirtualDisplay: Boolean = true,
+    private var displayProtos: Array<Display.DisplayProto> = emptyArray(),
+    private var layerProtos: Array<Layers.LayerProto> = emptyArray(),
+    private val orphanLayerCallback: ((Layer) -> Boolean)? = null
+) : BaseLayerTraceEntry() {
+
+    private val parsedEntry by lazy {
+        val layers = layerProtos.map { newLayer(it) }.toTypedArray()
+        val displays = displayProtos.map { newDisplay(it) }.toTypedArray()
+        val builder = LayerTraceEntryBuilder(timestamp, layers, displays, hwcBlob, where)
+            .setOrphanLayerCallback(orphanLayerCallback)
+            .ignoreLayersStackMatchNoDisplay(ignoreLayersStackMatchNoDisplay)
+            .ignoreVirtualDisplay(ignoreLayersInVirtualDisplay)
+        displayProtos = emptyArray()
+        layerProtos = emptyArray()
+        builder.build()
+    }
+
+    override val displays: Array<com.android.server.wm.traces.common.layers.Display>
+        get() = parsedEntry.displays
+
+    override val flattenedLayers: Array<Layer>
+        get() = parsedEntry.flattenedLayers
+
+    companion object {
+
+        @JvmStatic
+        fun newLayer(proto: Layers.LayerProto): Layer {
+            // Differentiate between the cases when there's no HWC data on
+            // the trace, and when the visible region is actually empty
+            val activeBuffer = proto.activeBuffer.toBuffer()
+            var visibleRegion = proto.visibleRegion.toRegion()
+            if (visibleRegion == null && activeBuffer.isEmpty) {
+                visibleRegion = Region.EMPTY
+            }
+            val crop = getCrop(proto.crop)
+            return Layer(
+                proto.name ?: "",
+                proto.id,
+                proto.parent,
+                proto.z,
+                visibleRegion,
+                activeBuffer,
+                proto.flags,
+                proto.bounds?.toRectF() ?: RectF.EMPTY,
+                proto.color.toColor(),
+                proto.isOpaque,
+                proto.shadowRadius,
+                proto.cornerRadius,
+                proto.type ?: "",
+                proto.screenBounds?.toRectF(),
+                Transform(proto.transform, proto.position),
+                proto.sourceBounds?.toRectF() ?: RectF.EMPTY,
+                proto.currFrame,
+                proto.effectiveScalingMode,
+                Transform(proto.bufferTransform, position = null),
+                proto.hwcCompositionType,
+                proto.hwcCrop.toRectF() ?: RectF.EMPTY,
+                proto.hwcFrame.toRect(),
+                proto.backgroundBlurRadius,
+                crop,
+                proto.isRelativeOf,
+                proto.zOrderRelativeOf,
+                proto.layerStack
+            )
+        }
+
+        fun newDisplay(
+            proto: Display.DisplayProto
+        ): com.android.server.wm.traces.common.layers.Display {
+            return com.android.server.wm.traces.common.layers.Display(
+                proto.id.toULong(),
+                proto.name,
+                proto.layerStack,
+                proto.size.toSize(),
+                proto.layerStackSpaceRect.toRect(),
+                Transform(proto.transform, position = null),
+                proto.isVirtual
+            )
+        }
+
+        @JvmStatic
+        fun Layers.FloatRectProto?.toRectF(): RectF? {
+            return this?.let {
+                RectF(left = left, top = top, right = right, bottom = bottom)
+            }
+        }
+
+        @JvmStatic
+        fun Common.SizeProto?.toSize(): Size {
+            return this?.let {
+                Size(this.w, this.h)
+            } ?: Size.EMPTY
+        }
+
+        @JvmStatic
+        fun Common.ColorProto?.toColor(): Color {
+            if (this == null) {
+                return Color.EMPTY
+            }
+            return Color(r, g, b, a)
+        }
+
+        @JvmStatic
+        fun Layers.ActiveBufferProto?.toBuffer(): ActiveBuffer {
+            if (this == null) {
+                return ActiveBuffer.EMPTY
+            }
+            return ActiveBuffer(width, height, stride, format)
+        }
+
+        @JvmStatic
+        fun getCrop(crop: Common.RectProto?): com.android.server.wm.traces.common.Rect? {
+            return when {
+                crop == null -> com.android.server.wm.traces.common.Rect.EMPTY
+                // crop (0,0) (-1,-1) means no crop
+                crop.right == -1 && crop.left == 0 && crop.bottom == -1 && crop.top == 0 ->
+                    null
+                (crop.right - crop.left) <= 0 || (crop.bottom - crop.top) <= 0 ->
+                    com.android.server.wm.traces.common.Rect.EMPTY
+                else ->
+                    com.android.server.wm.traces.common.Rect(
+                        crop.left, crop.top, crop.right, crop.bottom
+                    )
+            }
+        }
+
+        /**
+         * Extracts [Rect] from [RegionProto] by returning a rect that encompasses all
+         * the rectangles making up the region.
+         */
+        @JvmStatic
+        fun RegionProto?.toRegion(): Region? {
+            return if (this == null) {
+                null
+            } else {
+                val rects = this.rect.map { it.toRect() }.toTypedArray()
+                return Region(rects)
+            }
+        }
+
+        @JvmStatic
+        fun Common.RectProto?.toRect(): com.android.server.wm.traces.common.Rect {
+            return if ((this == null) ||
+                ((this.right - this.left) <= 0 || (this.bottom - this.top) <= 0)
+            ) {
+                com.android.server.wm.traces.common.Rect.EMPTY
+            } else {
+                com.android.server.wm.traces.common.Rect(
+                    this.left, this.top, this.right, this.bottom
+                )
+            }
+        }
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/traces/parser/layers/LayersTraceParser.kt b/libraries/flicker/src/com/android/server/wm/traces/parser/layers/LayersTraceParser.kt
index bd473e5..fa6c99d 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/parser/layers/LayersTraceParser.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/parser/layers/LayersTraceParser.kt
@@ -16,27 +16,14 @@
 
 package com.android.server.wm.traces.parser.layers
 
-import android.graphics.Rect
-import android.surfaceflinger.nano.Common.RectProto
-import android.surfaceflinger.nano.Common.SizeProto
-import android.surfaceflinger.nano.Display.DisplayProto
 import android.surfaceflinger.nano.Layers
-import android.surfaceflinger.nano.Layers.RegionProto
 import android.surfaceflinger.nano.Layerstrace
 import android.util.Log
-import com.android.server.wm.traces.common.Buffer
-import com.android.server.wm.traces.common.Color
-import com.android.server.wm.traces.common.RectF
-import com.android.server.wm.traces.common.Region
-import com.android.server.wm.traces.common.Size
-import com.android.server.wm.traces.common.layers.Display
 import com.android.server.wm.traces.common.layers.Layer
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
-import com.android.server.wm.traces.common.layers.LayerTraceEntryBuilder
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.layers.LayersTrace
 import com.android.server.wm.traces.parser.LOG_TAG
 import com.google.protobuf.nano.InvalidProtocolBufferNanoException
-import java.nio.file.Path
 import kotlin.math.max
 import kotlin.system.measureTimeMillis
 
@@ -50,14 +37,14 @@
          * of trace entries, storing the flattened layers into its hierarchical structure.
          *
          * @param data binary proto data
-         * @param source Path to source of data for additional debug information
          * @param orphanLayerCallback a callback to handle any unexpected orphan layers
          */
         @JvmOverloads
         @JvmStatic
         fun parseFromTrace(
             data: ByteArray,
-            source: Path? = null,
+            ignoreLayersStackMatchNoDisplay: Boolean = true,
+            ignoreLayersInVirtualDisplay: Boolean = true,
             orphanLayerCallback: ((Layer) -> Boolean)? = null
         ): LayersTrace {
             var fileProto: Layerstrace.LayersTraceFileProto? = null
@@ -71,7 +58,12 @@
                 throw RuntimeException(e)
             }
             return fileProto?.let {
-                parseFromTrace(it, source, orphanLayerCallback)
+                parseFromTrace(
+                    it,
+                    ignoreLayersStackMatchNoDisplay,
+                    ignoreLayersInVirtualDisplay,
+                    orphanLayerCallback
+                )
             } ?: error("Unable to read trace file")
         }
 
@@ -80,31 +72,39 @@
          * of trace entries, storing the flattened layers into its hierarchical structure.
          *
          * @param proto Parsed proto data
-         * @param source Path to source of data for additional debug information
          * @param orphanLayerCallback a callback to handle any unexpected orphan layers
          */
         @JvmOverloads
         @JvmStatic
         fun parseFromTrace(
             proto: Layerstrace.LayersTraceFileProto,
-            source: Path? = null,
+            ignoreLayersStackMatchNoDisplay: Boolean = true,
+            ignoreLayersInVirtualDisplay: Boolean = true,
             orphanLayerCallback: ((Layer) -> Boolean)? = null
         ): LayersTrace {
-            val entries: MutableList<LayerTraceEntry> = ArrayList()
+            val entries: MutableList<BaseLayerTraceEntry> = ArrayList()
             var traceParseTime = 0L
             for (traceProto: Layerstrace.LayersTraceProto in proto.entry) {
                 val entryParseTime = measureTimeMillis {
-                    val entry = newEntry(
-                        traceProto.elapsedRealtimeNanos, traceProto.displays,
-                        traceProto.layers.layers, traceProto.hwcBlob, traceProto.where,
-                        orphanLayerCallback)
+                    val entry = LayerTraceEntryLazy(
+                        traceProto.elapsedRealtimeNanos,
+                        traceProto.hwcBlob,
+                        traceProto.where,
+                        ignoreLayersStackMatchNoDisplay,
+                        ignoreLayersInVirtualDisplay,
+                        traceProto.displays,
+                        traceProto.layers.layers,
+                        orphanLayerCallback
+                    )
                     entries.add(entry)
                 }
                 traceParseTime += entryParseTime
             }
-            Log.v(LOG_TAG, "Parsing duration (Layers Trace): ${traceParseTime}ms " +
-                "(avg ${traceParseTime / max(entries.size, 1)}ms per entry)")
-            return LayersTrace(entries.toTypedArray(), source?.toString() ?: "")
+            Log.v(
+                LOG_TAG, "Parsing duration (Layers Trace): ${traceParseTime}ms " +
+                    "(avg ${traceParseTime / max(entries.size, 1)}ms per entry)"
+            )
+            return LayersTrace(entries.toTypedArray())
         }
 
         /**
@@ -114,11 +114,18 @@
          * @param proto Parsed proto data
          */
         @JvmStatic
-        @Deprecated("This functions parsers old SF dumps. Now SF dumps create a " +
-            "single entry trace, for new dump use [parseFromTrace]")
+        @Deprecated(
+            "This functions parsers old SF dumps. Now SF dumps create a " +
+                "single entry trace, for new dump use [parseFromTrace]"
+        )
         fun parseFromLegacyDump(proto: Layers.LayersProto): LayersTrace {
-            val entry = newEntry(timestamp = 0, displayProtos = emptyArray(),
-                protos = proto.layers)
+            val entry = LayerTraceEntryLazy(
+                timestamp = 0,
+                displayProtos = emptyArray(),
+                layerProtos = proto.layers,
+                ignoreLayersStackMatchNoDisplay = false,
+                ignoreLayersInVirtualDisplay = false
+            )
             return LayersTrace(entry)
         }
 
@@ -129,8 +136,10 @@
          * @param data binary proto data
          */
         @JvmStatic
-        @Deprecated("This functions parsers old SF dumps. Now SF dumps create a " +
-            "single entry trace, for new dump use [parseFromTrace]")
+        @Deprecated(
+            "This functions parsers old SF dumps. Now SF dumps create a " +
+                "single entry trace, for new dump use [parseFromTrace]"
+        )
         fun parseFromLegacyDump(data: ByteArray?): LayersTrace {
             val traceProto = try {
                 Layers.LayersProto.parseFrom(data)
@@ -139,142 +148,5 @@
             }
             return parseFromLegacyDump(traceProto)
         }
-
-        @JvmStatic
-        private fun newEntry(
-            timestamp: Long,
-            displayProtos: Array<DisplayProto>,
-            protos: Array<Layers.LayerProto>,
-            hwcBlob: String = "",
-            where: String = "",
-            orphanLayerCallback: ((Layer) -> Boolean)? = null
-        ): LayerTraceEntry {
-            val layers = protos.map { newLayer(it) }.toTypedArray()
-            val displays = displayProtos.map { newDisplay(it) }.toTypedArray()
-            val builder = LayerTraceEntryBuilder(timestamp, layers, displays, hwcBlob, where)
-            builder.setOrphanLayerCallback(orphanLayerCallback)
-            return builder.build()
-        }
-
-        @JvmStatic
-        private fun newLayer(proto: Layers.LayerProto): Layer {
-            // Differentiate between the cases when there's no HWC data on
-            // the trace, and when the visible region is actually empty
-            val activeBuffer = proto.activeBuffer.toBuffer()
-            var visibleRegion = proto.visibleRegion.toRegion()
-            if (visibleRegion == null && activeBuffer.isEmpty) {
-                visibleRegion = Region.EMPTY
-            }
-            val crop = getCrop(proto.crop)
-            return Layer(
-                    proto.name ?: "",
-                    proto.id,
-                    proto.parent,
-                    proto.z,
-                    visibleRegion,
-                    activeBuffer,
-                    proto.flags,
-                    proto.bounds?.toRectF() ?: RectF.EMPTY,
-                    proto.color.toColor(),
-                    proto.isOpaque,
-                    proto.shadowRadius,
-                    proto.cornerRadius,
-                    proto.type ?: "",
-                    proto.screenBounds?.toRectF(),
-                    Transform(proto.transform, proto.position),
-                    proto.sourceBounds?.toRectF() ?: RectF.EMPTY,
-                    proto.currFrame,
-                    proto.effectiveScalingMode,
-                    Transform(proto.bufferTransform, position = null),
-                    proto.hwcCompositionType,
-                    proto.hwcCrop.toRectF() ?: RectF.EMPTY,
-                    proto.hwcFrame.toRect(),
-                    proto.backgroundBlurRadius,
-                    crop,
-                    proto.isRelativeOf,
-                    proto.zOrderRelativeOf
-            )
-        }
-
-        private fun newDisplay(proto: DisplayProto): Display {
-            return Display(
-                proto.id.toULong(),
-                proto.name,
-                proto.layerStack,
-                proto.size.toSize(),
-                proto.layerStackSpaceRect.toRect(),
-                Transform(proto.transform, position = null)
-            )
-        }
-
-        @JvmStatic
-        private fun Layers.FloatRectProto?.toRectF(): RectF? {
-            return this?.let {
-                RectF(left = left, top = top, right = right, bottom = bottom)
-            }
-        }
-
-        @JvmStatic
-        private fun SizeProto?.toSize(): Size {
-            return this?.let {
-                Size(this.w, this.h)
-            } ?: Size.EMPTY
-        }
-
-        @JvmStatic
-        private fun Layers.ColorProto?.toColor(): Color {
-            if (this == null) {
-                return Color.EMPTY
-            }
-            return Color(r, g, b, a)
-        }
-
-        @JvmStatic
-        private fun Layers.ActiveBufferProto?.toBuffer(): Buffer {
-            if (this == null) {
-                return Buffer.EMPTY
-            }
-            return Buffer(width, height, stride, format)
-        }
-
-        @JvmStatic
-        private fun getCrop(crop: RectProto?): com.android.server.wm.traces.common.Rect? {
-            return when {
-                crop == null -> com.android.server.wm.traces.common.Rect.EMPTY
-                // crop (0,0) (-1,-1) means no crop
-                crop.right == -1 && crop.left == 0 && crop.bottom == -1 && crop.top == 0 ->
-                    null
-                (crop.right - crop.left) <= 0 || (crop.bottom - crop.top) <= 0 ->
-                    com.android.server.wm.traces.common.Rect.EMPTY
-                else ->
-                    com.android.server.wm.traces.common.Rect(
-                        crop.left, crop.top, crop.right, crop.bottom)
-            }
-        }
-
-        /**
-         * Extracts [Rect] from [RegionProto] by returning a rect that encompasses all
-         * the rectangles making up the region.
-         */
-        @JvmStatic
-        private fun RegionProto?.toRegion(): Region? {
-            return if (this == null) {
-                null
-            } else {
-                val rects = this.rect.map { it.toRect() }.toTypedArray()
-                return Region(rects)
-            }
-        }
-
-        @JvmStatic
-        private fun RectProto?.toRect(): com.android.server.wm.traces.common.Rect {
-            return if ((this == null) ||
-                ((this.right - this.left) <= 0 || (this.bottom - this.top) <= 0)) {
-                com.android.server.wm.traces.common.Rect.EMPTY
-            } else {
-                com.android.server.wm.traces.common.Rect(
-                    this.left, this.top, this.right, this.bottom)
-            }
-        }
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/parser/layers/Transform.kt b/libraries/flicker/src/com/android/server/wm/traces/parser/layers/Transform.kt
index 856ebb2..d733721 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/parser/layers/Transform.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/parser/layers/Transform.kt
@@ -18,6 +18,7 @@
 
 import android.surfaceflinger.nano.Layers
 import android.surfaceflinger.nano.Common.TransformProto
+import com.android.server.wm.traces.common.Matrix33
 import com.android.server.wm.traces.common.layers.Transform
 import com.android.server.wm.traces.common.layers.Transform.Companion.FLIP_H_VAL
 import com.android.server.wm.traces.common.layers.Transform.Companion.FLIP_V_VAL
@@ -34,7 +35,7 @@
         )
 
 private fun getMatrix(transform: TransformProto?, position: Layers.PositionProto?):
-        Transform.Matrix {
+        Matrix33 {
     val x = position?.x ?: 0f
     val y = position?.y ?: 0f
 
@@ -42,28 +43,28 @@
         transform == null || Transform.isSimpleTransform(transform.type) ->
             transform?.type.getDefaultTransform(x, y)
         else ->
-            Transform.Matrix(transform.dsdx, transform.dtdx, x, transform.dsdy, transform.dtdy, y)
+            Matrix33(transform.dsdx, transform.dtdx, x, transform.dsdy, transform.dtdy, y)
     }
 }
 
-private fun Int?.getDefaultTransform(x: Float, y: Float): Transform.Matrix {
+private fun Int?.getDefaultTransform(x: Float, y: Float): Matrix33 {
     return when {
         // IDENTITY
         this == null ->
-            Transform.Matrix(1f, 0f, x, 0f, 1f, y)
+            Matrix33(1f, 0f, x, 0f, 1f, y)
         // // ROT_270 = ROT_90|FLIP_H|FLIP_V
         isFlagSet(ROT_90_VAL or FLIP_V_VAL or FLIP_H_VAL) ->
-            Transform.Matrix(0f, -1f, x, 1f, 0f, y)
+            Matrix33(0f, -1f, x, 1f, 0f, y)
         // ROT_180 = FLIP_H|FLIP_V
         isFlagSet(FLIP_V_VAL or FLIP_H_VAL) ->
-            Transform.Matrix(-1f, 0f, x, 0f, -1f, y)
+            Matrix33(-1f, 0f, x, 0f, -1f, y)
         // ROT_90
         isFlagSet(ROT_90_VAL) ->
-            Transform.Matrix(0f, 1f, x, -1f, 0f, y)
+            Matrix33(0f, 1f, x, -1f, 0f, y)
         // IDENTITY
         isFlagClear(SCALE_VAL or ROTATE_VAL) ->
-            Transform.Matrix(1f, 0f, x, 0f, 1f, y)
+            Matrix33(1f, 0f, x, 0f, 1f, y)
         else ->
             throw IllegalStateException("Unknown transform type $this")
     }
-}
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/traces/parser/tags/TagTraceParserUtil.kt b/libraries/flicker/src/com/android/server/wm/traces/parser/tags/TagTraceParserUtil.kt
index 1dd346c..5820f74 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/parser/tags/TagTraceParserUtil.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/parser/tags/TagTraceParserUtil.kt
@@ -23,7 +23,6 @@
 import com.android.server.wm.traces.common.tags.TagTrace
 import com.android.server.wm.traces.common.tags.Transition
 import com.android.server.wm.traces.parser.LOG_TAG
-import java.nio.file.Path
 import kotlin.system.measureTimeMillis
 
 /**
@@ -36,13 +35,11 @@
          * of trace entries, storing the flattened layers into its hierarchical structure.
          *
          * @param data binary proto data
-         * @param source Path to source of data for additional debug information
          */
         @JvmOverloads
         @JvmStatic
         fun parseFromTrace(
-            data: ByteArray,
-            source: Path? = null
+            data: ByteArray
         ): TagTrace {
             var fileProto: FlickerTagTraceProto? = null
             try {
@@ -64,13 +61,11 @@
          * of trace entries, storing the flattened layers into its hierarchical structure.
          *
          * @param proto Parsed proto data
-         * @param source Path to source of data for additional debug informationy
          */
         @JvmOverloads
         @JvmStatic
         fun parseFromTrace(
-            proto: FlickerTagTraceProto,
-            source: Path? = null
+            proto: FlickerTagTraceProto
         ): TagTrace {
             val states = mutableListOf<TagState>()
             var traceParseTime = 0L
@@ -96,8 +91,7 @@
                 traceParseTime += tagParseTime
             }
             return TagTrace(
-                    entries = states.toTypedArray(),
-                    source = source?.toString() ?: ""
+                    entries = states.toTypedArray()
             )
         }
     }
diff --git a/libraries/flicker/src/com/android/server/wm/traces/parser/windowmanager/WindowManagerStateHelper.kt b/libraries/flicker/src/com/android/server/wm/traces/parser/windowmanager/WindowManagerStateHelper.kt
index 522915b..3eb8a26 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/parser/windowmanager/WindowManagerStateHelper.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/parser/windowmanager/WindowManagerStateHelper.kt
@@ -20,7 +20,6 @@
 import android.app.Instrumentation
 import android.app.WindowConfiguration
 import android.graphics.Rect
-import android.graphics.Region
 import android.os.SystemClock
 import android.util.Log
 import android.view.Display
@@ -29,16 +28,18 @@
 import com.android.server.wm.traces.common.windowmanager.windows.WindowContainer
 import com.android.server.wm.traces.common.windowmanager.windows.WindowState
 import com.android.server.wm.traces.common.Condition
+import com.android.server.wm.traces.common.ConditionList
 import com.android.server.wm.traces.common.DeviceStateDump
 import com.android.server.wm.traces.common.FlickerComponentName
 import com.android.server.wm.traces.common.FlickerComponentName.Companion.IME
+import com.android.server.wm.traces.common.FlickerComponentName.Companion.SNAPSHOT
 import com.android.server.wm.traces.parser.LOG_TAG
 import com.android.server.wm.traces.common.WaitCondition
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory
+import com.android.server.wm.traces.common.region.Region
 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
 import com.android.server.wm.traces.parser.getCurrentStateDump
-import com.android.server.wm.traces.parser.toAndroidRegion
 
 open class WindowManagerStateHelper @JvmOverloads constructor(
     /**
@@ -48,13 +49,15 @@
     /**
      * Predicate to supply a new UI information
      */
-    private val deviceDumpSupplier: () -> DeviceStateDump<WindowManagerState, LayerTraceEntry> = {
-        val currState = getCurrentStateDump(instrumentation.uiAutomation)
-        DeviceStateDump(
-            currState.wmState ?: error("Unable to parse WM trace"),
-            currState.layerState ?: error("Unable to parse Layers trace")
-        )
-    },
+    private val deviceDumpSupplier:
+        () -> DeviceStateDump<WindowManagerState, BaseLayerTraceEntry> =
+            {
+            val currState = getCurrentStateDump(instrumentation.uiAutomation)
+            DeviceStateDump(
+                currState.wmState ?: error("Unable to parse WM trace"),
+                currState.layerState ?: error("Unable to parse Layers trace")
+            )
+        },
     /**
      * Number of attempts to satisfy a wait condition
      */
@@ -64,12 +67,12 @@
      */
     private val retryIntervalMs: Long = WaitCondition.DEFAULT_RETRY_INTERVAL_MS
 ) {
-    private var internalState: DeviceStateDump<WindowManagerState, LayerTraceEntry>? = null
+    private var internalState: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>? = null
 
     /**
      * Queries the supplier for a new device state
      */
-    val currentState: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+    val currentState: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
         get() {
             if (internalState == null) {
                 internalState = deviceDumpSupplier.invoke()
@@ -77,169 +80,113 @@
                 waitForValidState()
             }
             return internalState ?: error("Unable to fetch an internal state")
-    }
+        }
 
     protected open fun updateCurrState(
-        value: DeviceStateDump<WindowManagerState, LayerTraceEntry>
+        value: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
     ) {
         internalState = value
     }
 
     private fun createConditionBuilder():
-        WaitCondition.Builder<DeviceStateDump<WindowManagerState, LayerTraceEntry>> =
+        WaitCondition.Builder<DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>> =
         WaitCondition.Builder(deviceDumpSupplier, numRetries)
             .onSuccess { updateCurrState(it) }
             .onFailure { updateCurrState(it) }
-            .onLog { Log.d(LOG_TAG, it) }
+            .onLog { msg, isError -> if (isError) Log.e(LOG_TAG, msg) else Log.d(LOG_TAG, msg) }
             .onRetry { SystemClock.sleep(retryIntervalMs) }
 
-    private fun ConfigurationContainer.isWindowingModeCompatible(
-        requestedWindowingMode: Int
-    ): Boolean {
-        return when (requestedWindowingMode) {
-            WindowConfiguration.WINDOWING_MODE_UNDEFINED -> true
-            WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY ->
-                (windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN ||
-                    windowingMode == WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)
-            else -> windowingMode == requestedWindowingMode
-        }
-    }
-
     /**
      * Wait for the activities to appear in proper stacks and for valid state in AM and WM.
-     * @param waitForActivitiesVisible array of activity states to wait for.
+     * @param waitForActivityState array of activity states to wait for.
      */
-    fun waitForValidState(vararg waitForActivitiesVisible: WaitForValidActivityState): Boolean {
-        val builder = createConditionBuilder()
-            .withCondition(WindowManagerConditionsFactory.isWMStateComplete())
+    private fun waitForValidState(vararg waitForActivityState: WaitForValidActivityState) =
+        waitFor(waitForValidStateCondition(*waitForActivityState))
 
-        if (waitForActivitiesVisible.isNotEmpty()) {
-            builder.withCondition("!shouldWaitForActivities") {
-                !shouldWaitForActivities(it, *waitForActivitiesVisible)
-            }
-        }
-        val success = builder.build().waitFor()
-        if (!success) {
-            Log.e(LOG_TAG, "***Waiting for states failed: " +
-                waitForActivitiesVisible.contentToString())
-        }
-        return success
+    fun waitForFullScreenApp(component: FlickerComponentName) =
+        require(
+        waitFor(isAppFullScreen(component), snapshotGoneCondition)) {
+        "Expected ${component.toWindowName()} to be in full screen"
     }
 
-    fun waitForFullScreenApp(component: FlickerComponentName): Boolean =
-            waitForValidState(
-                    WaitForValidActivityState
-                            .Builder(component)
-                            .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN)
-                            .setActivityType(WindowConfiguration.ACTIVITY_TYPE_STANDARD)
-                            .build())
+    fun waitForHomeActivityVisible() = require(waitFor(isHomeActivityVisible)) {
+        "Expected home activity to be visible"
+    }
 
-    fun waitForHomeActivityVisible(): Boolean =
-        createConditionBuilder()
-            .withCondition(WindowManagerConditionsFactory.isHomeActivityVisible())
-            .withCondition(
-                WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))
-            .withCondition(WindowManagerConditionsFactory.isNavBarVisible())
-            .withCondition(WindowManagerConditionsFactory.isStatusBarVisible())
-            .build()
-            .waitFor()
-
-    fun waitForRecentsActivityVisible(): Boolean =
-        createConditionBuilder()
-            .withCondition("isRecentsActivityVisible") {
-                it.wmState.isRecentsActivityVisible
-            }
-            .build()
-            .waitFor()
+    fun waitForRecentsActivityVisible() = require(
+        waitFor("isRecentsActivityVisible") { it.wmState.isRecentsActivityVisible }) {
+        "Expected recents activity to be visible"
+    }
 
     /**
      * Wait for specific rotation for the default display. Values are Surface#Rotation
      */
     @JvmOverloads
-    fun waitForRotation(rotation: Int, displayId: Int = Display.DEFAULT_DISPLAY): Boolean {
+    fun waitForRotation(rotation: Int, displayId: Int = Display.DEFAULT_DISPLAY) {
         val hasRotationCondition = WindowManagerConditionsFactory.hasRotation(rotation, displayId)
-        return createConditionBuilder()
-            .withCondition("waitForRotation[$rotation]") {
+        val result = waitFor(
+            WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
+            Condition("waitForRotation[$rotation]") {
                 if (!it.wmState.canRotate) {
                     Log.v(LOG_TAG, "Rotation is not allowed in the state")
                     true
                 } else {
                     hasRotationCondition.isSatisfied(it)
                 }
-            }
-            .withCondition(
-                WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))
-            .build()
-            .waitFor()
+            })
+        require(result) { "Could not change rotation" }
     }
 
     fun waitForActivityState(activity: FlickerComponentName, activityState: String): Boolean {
         val activityName = activity.toActivityName()
-        return createConditionBuilder()
-            .withCondition("state of $activityName to be $activityState") {
-                it.wmState.hasActivityState(activityName, activityState)
-            }
-            .build()
-            .waitFor()
+        return waitFor("state of $activityName to be $activityState") {
+            it.wmState.hasActivityState(activityName, activityState)
+        }
     }
 
     /**
      * Waits until the navigation and status bars are visible (windows and layers)
      */
     fun waitForNavBarStatusBarVisible(): Boolean =
-        createConditionBuilder()
-            .withCondition(WindowManagerConditionsFactory.isNavBarVisible())
-            .withCondition(WindowManagerConditionsFactory.isStatusBarVisible())
-            .build()
-            .waitFor()
+        waitFor(
+            WindowManagerConditionsFactory.isNavBarVisible(),
+            WindowManagerConditionsFactory.isStatusBarVisible())
 
-    fun waitForVisibleWindow(component: FlickerComponentName): Boolean =
-        createConditionBuilder()
-            .withCondition(WindowManagerConditionsFactory.containsActivity(component))
-            .withCondition(WindowManagerConditionsFactory.containsWindow(component))
-            .withCondition(WindowManagerConditionsFactory.isActivityVisible(component))
-            .withCondition(WindowManagerConditionsFactory.isWindowSurfaceShown(component))
-            .withCondition(
-                WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))
-            .build()
-            .waitFor()
+    fun waitForVisibleWindow(component: FlickerComponentName) = require(
+        waitFor(
+            WindowManagerConditionsFactory.isWindowVisible(component),
+            WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))) {
+        "Expected window ${component.toWindowName()} to be visible"
+    }
 
-    fun waitForActivityRemoved(component: FlickerComponentName): Boolean =
-        createConditionBuilder()
-            .withCondition(WindowManagerConditionsFactory.containsActivity(component).negate())
-            .withCondition(WindowManagerConditionsFactory.containsWindow(component).negate())
-            .withCondition(
-                WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))
-            .build()
-            .waitFor()
+    fun waitForActivityRemoved(component: FlickerComponentName) = require(
+        waitFor(
+            WindowManagerConditionsFactory.containsActivity(component).negate(),
+            WindowManagerConditionsFactory.containsWindow(component).negate(),
+            WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))) {
+        "Expected activity ${component.toWindowName()} to have been removed"
+    }
 
     @JvmOverloads
     fun waitForAppTransitionIdle(displayId: Int = Display.DEFAULT_DISPLAY): Boolean =
-        createConditionBuilder()
-            .withCondition(WindowManagerConditionsFactory.isAppTransitionIdle(displayId))
-            .build()
-            .waitFor()
+        waitFor(WindowManagerConditionsFactory.isAppTransitionIdle(displayId))
 
-    fun waitForWindowSurfaceDisappeared(component: FlickerComponentName): Boolean {
-        val condition = WindowManagerConditionsFactory.isWindowSurfaceShown(component).negate()
-        return createConditionBuilder()
-            .withCondition(condition)
-            .withCondition(
-                WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))
-            .build()
-            .waitFor()
+    fun waitForWindowSurfaceDisappeared(component: FlickerComponentName) = require(
+        waitFor(
+            WindowManagerConditionsFactory.isWindowSurfaceShown(component).negate(),
+            WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))) {
+        "Expected surface ${component.toLayerName()} to disappear"
     }
 
-    fun waitForSurfaceAppeared(surfaceName: String): Boolean =
-        createConditionBuilder()
-            .withCondition(WindowManagerConditionsFactory.isWindowSurfaceShown(surfaceName))
-            .withCondition(
-                WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))
-            .build()
-            .waitFor()
+    fun waitForSurfaceAppeared(component: FlickerComponentName) = require(
+        waitFor(
+            WindowManagerConditionsFactory.isWindowSurfaceShown(component),
+            WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))) {
+        "Expected surface ${component.toLayerName()} to appear"
+    }
 
     fun waitFor(
-        vararg conditions: Condition<DeviceStateDump<WindowManagerState, LayerTraceEntry>>
+        vararg conditions: Condition<DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>>
     ): Boolean {
         val builder = createConditionBuilder()
         conditions.forEach { builder.withCondition(it) }
@@ -249,90 +196,38 @@
     @JvmOverloads
     fun waitFor(
         message: String = "",
-        waitCondition: (DeviceStateDump<WindowManagerState, LayerTraceEntry>) -> Boolean
-    ): Boolean = createConditionBuilder()
-            .withCondition(message, waitCondition)
-            .build()
-            .waitFor()
-
-    /**
-     * @return true if should wait for some activities to become visible.
-     */
-    private fun shouldWaitForActivities(
-        state: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
-        vararg waitForActivitiesVisible: WaitForValidActivityState
-    ): Boolean {
-        if (waitForActivitiesVisible.isEmpty()) {
-            return false
-        }
-        // If the caller is interested in waiting for some particular activity windows to be
-        // visible before compute the state. Check for the visibility of those activity windows
-        // and for placing them in correct stacks (if requested).
-        var allActivityWindowsVisible = true
-        var tasksInCorrectStacks = true
-        for (activityState in waitForActivitiesVisible) {
-            val matchingWindowStates = state.wmState.getMatchingVisibleWindowState(
-                activityState.windowName ?: "")
-            val activityWindowVisible = matchingWindowStates.isNotEmpty()
-
-            if (!activityWindowVisible) {
-                Log.i(LOG_TAG, "Activity window not visible: ${activityState.windowName}")
-                allActivityWindowsVisible = false
-            } else if (activityState.activityName != null &&
-                !state.wmState.isActivityVisible(activityState.activityName.toActivityName())) {
-                Log.i(LOG_TAG, "Activity not visible: ${activityState.activityName}")
-                allActivityWindowsVisible = false
-            } else {
-                // Check if window is already the correct state requested by test.
-                var windowInCorrectState = false
-                for (ws in matchingWindowStates) {
-                    if (activityState.stackId != ActivityTaskManager.INVALID_STACK_ID &&
-                        ws.stackId != activityState.stackId) {
-                        continue
-                    }
-                    if (!ws.isWindowingModeCompatible(activityState.windowingMode)) {
-                        continue
-                    }
-                    if (activityState.activityType != WindowConfiguration.ACTIVITY_TYPE_UNDEFINED &&
-                        ws.activityType != activityState.activityType) {
-                        continue
-                    }
-                    windowInCorrectState = true
-                    break
-                }
-                if (!windowInCorrectState) {
-                    Log.i(LOG_TAG, "Window in incorrect stack: $activityState")
-                    tasksInCorrectStacks = false
-                }
-            }
-        }
-        return !allActivityWindowsVisible || !tasksInCorrectStacks
-    }
+        waitCondition: (DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>) -> Boolean
+    ): Boolean = waitFor(Condition(message, waitCondition))
 
     /**
      * Waits until the IME window and layer are visible
      */
-    @JvmOverloads
-    fun waitImeShown(displayId: Int = Display.DEFAULT_DISPLAY): Boolean =
-        createConditionBuilder()
-            .withCondition(WindowManagerConditionsFactory.isImeShown(displayId))
-            .withCondition(
-                WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))
-            .build()
-            .waitFor()
+    fun waitImeShown() = require(waitFor(imeShownCondition)) { "Expected IME to be visible" }
 
     /**
      * Waits until the IME layer is no longer visible. Cannot wait for the window as
      * its visibility information is updated at a later state and is not reliable in
      * the trace
      */
-    fun waitImeGone(): Boolean =
-        createConditionBuilder()
-            .withCondition(WindowManagerConditionsFactory.isLayerVisible(IME).negate())
-            .withCondition(
-                WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))
-            .build()
-            .waitFor()
+    fun waitImeGone() = require(waitFor(imeGoneCondition)) { "Expected IME not to be visible" }
+
+    /**
+     * Waits until a window is in PIP mode. That is:
+     *
+     * - wait until a window is pinned ([WindowManagerState.pinnedWindows])
+     * - no layers animating
+     * - and [FlickerComponentName.PIP_CONTENT_OVERLAY] is no longer visible
+     */
+    fun waitPipShown() = require(waitFor(pipShownCondition)) { "Expected PIP window to be visible" }
+
+    /**
+     * Waits until a window is no longer in PIP mode. That is:
+     *
+     * - wait until there are no pinned ([WindowManagerState.pinnedWindows])
+     * - no layers animating
+     * - and [FlickerComponentName.PIP_CONTENT_OVERLAY] is no longer visible
+     */
+    fun waitPipGone() = require(waitFor(pipGoneCondition)) { "Expected PIP window to be gone" }
 
     /**
      * Obtains a [WindowContainer] from the current device state, or null if the WindowContainer
@@ -349,6 +244,132 @@
      */
     fun getWindowRegion(activity: FlickerComponentName): Region {
         val window = getWindow(activity)
-        return window?.frameRegion?.toAndroidRegion() ?: Region()
+        return window?.frameRegion ?: Region.EMPTY
     }
-}
\ No newline at end of file
+
+    companion object {
+        @JvmStatic
+        val isHomeActivityVisible = ConditionList(
+            WindowManagerConditionsFactory.isHomeActivityVisible(),
+            WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
+            WindowManagerConditionsFactory.isNavBarVisible(),
+            WindowManagerConditionsFactory.isStatusBarVisible())
+
+        @JvmStatic
+        val imeGoneCondition = ConditionList(
+            WindowManagerConditionsFactory.isLayerVisible(IME).negate(),
+            WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))
+
+        @JvmStatic
+        val imeShownCondition = ConditionList(
+            WindowManagerConditionsFactory.isImeShown(Display.DEFAULT_DISPLAY),
+            WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY)
+        )
+
+        @JvmStatic
+        val snapshotGoneCondition = ConditionList(
+                WindowManagerConditionsFactory.isLayerVisible(SNAPSHOT).negate(),
+                WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))
+
+        @JvmStatic
+        val pipShownCondition = ConditionList(
+            WindowManagerConditionsFactory.hasLayersAnimating().negate(),
+            WindowManagerConditionsFactory.hasPipWindow(),
+            WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))
+
+        @JvmStatic
+        val pipGoneCondition = ConditionList(
+            WindowManagerConditionsFactory.hasLayersAnimating().negate(),
+            WindowManagerConditionsFactory.hasPipWindow().negate(),
+            WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))
+
+        fun waitForValidStateCondition(
+            vararg waitForActivitiesVisible: WaitForValidActivityState
+        ): Condition<DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>> {
+            val conditions = mutableListOf(WindowManagerConditionsFactory.isWMStateComplete())
+
+            if (waitForActivitiesVisible.isNotEmpty()) {
+                conditions.add(Condition("!shouldWaitForActivities") {
+                    !shouldWaitForActivities(it, *waitForActivitiesVisible)
+                })
+            }
+
+            return ConditionList(*conditions.toTypedArray())
+        }
+
+        fun isAppFullScreen(component: FlickerComponentName) =
+            waitForValidStateCondition(WaitForValidActivityState
+                .Builder(component)
+                .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN)
+                .setActivityType(WindowConfiguration.ACTIVITY_TYPE_STANDARD)
+                .build()
+            )
+
+        /**
+         * @return true if should wait for some activities to become visible.
+         */
+        private fun shouldWaitForActivities(
+            state: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
+            vararg waitForActivitiesVisible: WaitForValidActivityState
+        ): Boolean {
+            if (waitForActivitiesVisible.isEmpty()) {
+                return false
+            }
+            // If the caller is interested in waiting for some particular activity windows to be
+            // visible before compute the state. Check for the visibility of those activity windows
+            // and for placing them in correct stacks (if requested).
+            var allActivityWindowsVisible = true
+            var tasksInCorrectStacks = true
+            for (activityState in waitForActivitiesVisible) {
+                val matchingWindowStates = state.wmState.getMatchingVisibleWindowState(
+                    activityState.windowName ?: "")
+                val activityWindowVisible = matchingWindowStates.isNotEmpty()
+
+                if (!activityWindowVisible) {
+                    Log.i(LOG_TAG, "Activity window not visible: ${activityState.windowName}")
+                    allActivityWindowsVisible = false
+                } else if (activityState.activityName != null &&
+                    !state.wmState.isActivityVisible(activityState.activityName.toActivityName())) {
+                    Log.i(LOG_TAG, "Activity not visible: ${activityState.activityName}")
+                    allActivityWindowsVisible = false
+                } else {
+                    // Check if window is already the correct state requested by test.
+                    var windowInCorrectState = false
+                    for (ws in matchingWindowStates) {
+                        if (activityState.stackId != ActivityTaskManager.INVALID_STACK_ID &&
+                            ws.stackId != activityState.stackId) {
+                            continue
+                        }
+                        if (!ws.isWindowingModeCompatible(activityState.windowingMode)) {
+                            continue
+                        }
+                        if (activityState.activityType !=
+                                WindowConfiguration.ACTIVITY_TYPE_UNDEFINED &&
+                            ws.activityType != activityState.activityType) {
+                            continue
+                        }
+                        windowInCorrectState = true
+                        break
+                    }
+                    if (!windowInCorrectState) {
+                        Log.i(LOG_TAG, "Window in incorrect stack: $activityState")
+                        tasksInCorrectStacks = false
+                    }
+                }
+            }
+            return !allActivityWindowsVisible || !tasksInCorrectStacks
+        }
+
+        private fun ConfigurationContainer.isWindowingModeCompatible(
+            requestedWindowingMode: Int
+        ): Boolean {
+            return when (requestedWindowingMode) {
+                WindowConfiguration.WINDOWING_MODE_UNDEFINED -> true
+                WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY ->
+                    (windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN ||
+                        windowingMode == WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)
+                else -> windowingMode == requestedWindowingMode
+            }
+        }
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/traces/parser/windowmanager/WindowManagerTraceParser.kt b/libraries/flicker/src/com/android/server/wm/traces/parser/windowmanager/WindowManagerTraceParser.kt
index 010f421..3f85fd5 100644
--- a/libraries/flicker/src/com/android/server/wm/traces/parser/windowmanager/WindowManagerTraceParser.kt
+++ b/libraries/flicker/src/com/android/server/wm/traces/parser/windowmanager/WindowManagerTraceParser.kt
@@ -38,8 +38,8 @@
 import com.android.server.wm.nano.WindowManagerTraceFileProto
 import com.android.server.wm.nano.WindowStateProto
 import com.android.server.wm.nano.WindowTokenProto
-import com.android.server.wm.traces.common.Size
 import com.android.server.wm.traces.common.Rect
+import com.android.server.wm.traces.common.Size
 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
 import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace
 import com.android.server.wm.traces.common.windowmanager.windows.Activity
@@ -59,7 +59,6 @@
 import com.android.server.wm.traces.common.windowmanager.windows.WindowToken
 import com.android.server.wm.traces.parser.LOG_TAG
 import com.google.protobuf.nano.InvalidProtocolBufferNanoException
-import java.nio.file.Path
 import kotlin.math.max
 import kotlin.system.measureTimeMillis
 
@@ -85,13 +84,11 @@
      * a list of trace entries.
      *
      * @param data binary proto data
-     * @param source Path to source of data for additional debug information
      */
     @JvmOverloads
     @JvmStatic
     fun parseFromTrace(
-        data: ByteArray?,
-        source: Path? = null
+        data: ByteArray?
     ): WindowManagerTrace {
         var fileProto: WindowManagerTraceFileProto? = null
         try {
@@ -104,7 +101,7 @@
             throw RuntimeException(e)
         }
 
-        return fileProto?.let { parseFromTrace(it, source) }
+        return fileProto?.let { parseFromTrace(it) }
                 ?: error("Unable to read trace file")
     }
 
@@ -112,13 +109,11 @@
      * Uses the proto to generates a list of trace entries.
      *
      * @param proto Parsed proto data
-     * @param source Path to source of data for additional debug information
      */
     @JvmOverloads
     @JvmStatic
     fun parseFromTrace(
-        proto: WindowManagerTraceFileProto,
-        source: Path? = null
+        proto: WindowManagerTraceFileProto
     ): WindowManagerTrace {
         val entries = mutableListOf<WindowManagerState>()
         var traceParseTime = 0L
@@ -133,7 +128,7 @@
 
         Log.v(LOG_TAG, "Parsing duration (WM Trace): ${traceParseTime}ms " +
             "(avg ${traceParseTime / max(entries.size, 1)}ms per entry)")
-        return WindowManagerTrace(entries.toTypedArray(), "${source?.toAbsolutePath()}")
+        return WindowManagerTrace(entries.toTypedArray())
     }
 
     /**
@@ -145,8 +140,7 @@
     @JvmStatic
     fun parseFromDump(proto: WindowManagerServiceDumpProto): WindowManagerTrace {
         return WindowManagerTrace(
-                arrayOf(newTraceEntry(proto, timestamp = 0, where = "")),
-            source = "")
+                arrayOf(newTraceEntry(proto, timestamp = 0, where = "")))
     }
 
     /**
diff --git a/libraries/flicker/test/Android.bp b/libraries/flicker/test/Android.bp
index 314f4aa..61df2c2 100644
--- a/libraries/flicker/test/Android.bp
+++ b/libraries/flicker/test/Android.bp
@@ -34,6 +34,7 @@
     },
     static_libs: [
         "flickerlib",
-        "launcher-aosp-tapl"
+        "launcher-aosp-tapl",
+        "mockito-target-extended-minus-junit4"
     ],
 }
diff --git a/libraries/flicker/test/assets/testdata/assertors/PipExitTagTrace.winscope b/libraries/flicker/test/assets/testdata/assertors/PipExitTagTrace.winscope
new file mode 100644
index 0000000..4514fe2
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/assertors/PipExitTagTrace.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/assertors/appClose/SurfaceFlingerInvalidTrace.winscope b/libraries/flicker/test/assets/testdata/assertors/appClose/SurfaceFlingerInvalidTrace.winscope
new file mode 100644
index 0000000..73af7ea
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/assertors/appClose/SurfaceFlingerInvalidTrace.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/assertors/appClose/SurfaceFlingerTrace.winscope b/libraries/flicker/test/assets/testdata/assertors/appClose/SurfaceFlingerTrace.winscope
new file mode 100644
index 0000000..ae70bac
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/assertors/appClose/SurfaceFlingerTrace.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/assertors/appClose/WindowManagerInvalidTrace.winscope b/libraries/flicker/test/assets/testdata/assertors/appClose/WindowManagerInvalidTrace.winscope
new file mode 100644
index 0000000..d482d4e
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/assertors/appClose/WindowManagerInvalidTrace.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/assertors/appClose/WindowManagerTrace.winscope b/libraries/flicker/test/assets/testdata/assertors/appClose/WindowManagerTrace.winscope
new file mode 100644
index 0000000..895d817
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/assertors/appClose/WindowManagerTrace.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/assertors/config.json b/libraries/flicker/test/assets/testdata/assertors/config.json
index 174da8e..7085063 100644
--- a/libraries/flicker/test/assets/testdata/assertors/config.json
+++ b/libraries/flicker/test/assets/testdata/assertors/config.json
@@ -5,32 +5,8 @@
       "assertions": {
         "presubmit": [
           {
-            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAtStart",
-            "args": [
-              "/NavigationBar0"
-            ]
-          },
-          {
-            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAtEnd",
-            "args": [
-              "/NavigationBar0"
-            ]
-          },
-          {
             "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowIsVisibleAlways",
             "args": [
-              "/NavigationBar0"
-            ]
-          },
-          {
-            "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowIsInvisibleAlways",
-            "args": [
-              "/StatusBar"
-            ]
-          },
-          {
-            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsInvisibleAlways",
-            "args": [
               "/StatusBar"
             ]
           },
@@ -38,13 +14,13 @@
             "class": "com.android.server.wm.flicker.service.assertors.common.VisibleWindowsShownMoreThanOneConsecutiveEntry"
           },
           {
+            "class": "com.android.server.wm.flicker.service.assertors.common.VisibleLayersShownMoreThanOneConsecutiveEntry"
+          },
+          {
             "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAtStart"
           },
           {
             "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAtEnd"
-          },
-          {
-            "class": "com.android.server.wm.flicker.service.assertors.common.VisibleLayersShownMoreThanOneConsecutiveEntry"
           }
         ],
         "postsubmit": [],
@@ -113,10 +89,181 @@
             "class": "com.android.server.wm.flicker.service.assertors.common.AppLayerIsVisibleAtEnd"
           },
           {
-            "class": "com.android.server.wm.flicker.service.assertors.common.AppWindowReplacesLauncherAsTopWindow"
+            "class": "com.android.server.wm.flicker.service.assertors.common.LauncherWindowMovesOutOfTop"
+          }
+        ],
+        "postsubmit": [],
+        "flaky": []
+      }
+    },
+    {
+      "transition": "APP_CLOSE",
+      "assertions": {
+        "presubmit": [
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAtStart",
+            "args": [
+              "/NavigationBar0"
+            ]
           },
           {
-            "class": "com.android.server.wm.flicker.service.assertors.common.LauncherWindowMovesOutOfTop"
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAtEnd",
+            "args": [
+              "/NavigationBar0"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowIsVisibleAlways",
+            "args": [
+              "/NavigationBar0"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowIsVisibleAlways",
+            "args": [
+              "/StatusBar"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAlways",
+            "args": [
+              "/StatusBar"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAtStart"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAtEnd"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LauncherWindowReplacesAppAsTopWindow"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LauncherReplacesAppLayer"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsInvisibleAtStart",
+            "args": [
+              "com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAtEnd",
+            "args": [
+              "com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppLayerIsVisibleAtStart"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppLayerIsInvisibleAtEnd"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LauncherWindowMovesToTop"
+          }
+        ],
+        "postsubmit": [],
+        "flaky": []
+      }
+    },
+    {
+      "transition": "PIP_ENTER",
+      "assertions": {
+        "presubmit": [
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAlways",
+            "args": [
+              "/NavigationBar0"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowIsVisibleAlways",
+            "args": [
+              "/NavigationBar0"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAlways",
+            "args": [
+              "/StatusBar"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowIsVisibleAlways",
+            "args": [
+              "/StatusBar"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAtStart"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAtEnd"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppLayerIsVisibleAlways"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppWindowIsVisibleAlways"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppWindowRemainInsideDisplayBounds"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppLayerRemainInsideDisplayBounds"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppLayerReduces"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppWindowBecomesPinned"
+          }
+        ],
+        "postsubmit": [],
+        "flaky": []
+      }
+    },
+    {
+      "transition": "PIP_EXIT",
+      "assertions": {
+        "presubmit": [
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAlways",
+            "args": [
+              "/NavigationBar0"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowIsVisibleAlways",
+            "args": [
+              "/NavigationBar0"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.LayerIsVisibleAlways",
+            "args": [
+              "/StatusBar"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.NonAppWindowIsVisibleAlways",
+            "args": [
+              "/StatusBar"
+            ]
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAtStart"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.EntireScreenCoveredAtEnd"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.AppLayerBecomesInvisible"
+          },
+          {
+            "class": "com.android.server.wm.flicker.service.assertors.common.PipWindowBecomesInvisible"
           }
         ],
         "postsubmit": [],
diff --git a/libraries/flicker/test/assets/testdata/assertors/pip/enter/SurfaceFlingerInvalidTrace.winscope b/libraries/flicker/test/assets/testdata/assertors/pip/enter/SurfaceFlingerInvalidTrace.winscope
new file mode 100644
index 0000000..1caf0ce
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/assertors/pip/enter/SurfaceFlingerInvalidTrace.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/assertors/pip/enter/SurfaceFlingerTrace.winscope b/libraries/flicker/test/assets/testdata/assertors/pip/enter/SurfaceFlingerTrace.winscope
new file mode 100644
index 0000000..6636c6f
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/assertors/pip/enter/SurfaceFlingerTrace.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/assertors/pip/enter/WindowManagerInvalidTrace.winscope b/libraries/flicker/test/assets/testdata/assertors/pip/enter/WindowManagerInvalidTrace.winscope
new file mode 100644
index 0000000..6dc6195
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/assertors/pip/enter/WindowManagerInvalidTrace.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/assertors/pip/enter/WindowManagerTrace.winscope b/libraries/flicker/test/assets/testdata/assertors/pip/enter/WindowManagerTrace.winscope
new file mode 100644
index 0000000..ce4ceca
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/assertors/pip/enter/WindowManagerTrace.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/assertors/pip/exit/SurfaceFlingerInvalidTrace.winscope b/libraries/flicker/test/assets/testdata/assertors/pip/exit/SurfaceFlingerInvalidTrace.winscope
new file mode 100644
index 0000000..1caf0ce
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/assertors/pip/exit/SurfaceFlingerInvalidTrace.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/assertors/pip/exit/SurfaceFlingerTrace.winscope b/libraries/flicker/test/assets/testdata/assertors/pip/exit/SurfaceFlingerTrace.winscope
new file mode 100644
index 0000000..65e7357
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/assertors/pip/exit/SurfaceFlingerTrace.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/assertors/pip/exit/WindowManagerInvalidTrace.winscope b/libraries/flicker/test/assets/testdata/assertors/pip/exit/WindowManagerInvalidTrace.winscope
new file mode 100644
index 0000000..6dc6195
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/assertors/pip/exit/WindowManagerInvalidTrace.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/assertors/pip/exit/WindowManagerTrace.winscope b/libraries/flicker/test/assets/testdata/assertors/pip/exit/WindowManagerTrace.winscope
new file mode 100644
index 0000000..ea31874
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/assertors/pip/exit/WindowManagerTrace.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/assertors/rotation/SurfaceFlingerInvalidTrace.winscope b/libraries/flicker/test/assets/testdata/assertors/rotation/SurfaceFlingerInvalidTrace.winscope
deleted file mode 100644
index 645f8be..0000000
--- a/libraries/flicker/test/assets/testdata/assertors/rotation/SurfaceFlingerInvalidTrace.winscope
+++ /dev/null
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/assertors/rotation/SurfaceFlingerTrace.winscope b/libraries/flicker/test/assets/testdata/assertors/rotation/SurfaceFlingerTrace.winscope
index 4bdb322..92c112b 100644
--- a/libraries/flicker/test/assets/testdata/assertors/rotation/SurfaceFlingerTrace.winscope
+++ b/libraries/flicker/test/assets/testdata/assertors/rotation/SurfaceFlingerTrace.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/assertors/rotation/WindowManagerTrace.winscope b/libraries/flicker/test/assets/testdata/assertors/rotation/WindowManagerTrace.winscope
index f146dc2..d4d684c 100644
--- a/libraries/flicker/test/assets/testdata/assertors/rotation/WindowManagerTrace.winscope
+++ b/libraries/flicker/test/assets/testdata/assertors/rotation/WindowManagerTrace.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/layers_trace_display_size_crop.winscope b/libraries/flicker/test/assets/testdata/layers_trace_display_size_crop.winscope
new file mode 100755
index 0000000..10b4f27
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/layers_trace_display_size_crop.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/layers_trace_empty_state.winscope b/libraries/flicker/test/assets/testdata/layers_trace_empty_state.winscope
new file mode 100644
index 0000000..c26d627
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/layers_trace_empty_state.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/layers_trace_splashscreen.pb b/libraries/flicker/test/assets/testdata/layers_trace_splashscreen.pb
new file mode 100644
index 0000000..d4b7f44
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/layers_trace_splashscreen.pb
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/quick_switch_to_app_killed_in_background_trace.pb b/libraries/flicker/test/assets/testdata/quick_switch_to_app_killed_in_background_trace.pb
new file mode 100644
index 0000000..bb57532
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/quick_switch_to_app_killed_in_background_trace.pb
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/wm_trace_taskfragment.winscope b/libraries/flicker/test/assets/testdata/wm_trace_taskfragment.winscope
new file mode 100644
index 0000000..c3c2bbd
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/wm_trace_taskfragment.winscope
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/wm_trace_unlock.pb b/libraries/flicker/test/assets/testdata/wm_trace_unlock.pb
new file mode 100644
index 0000000..a215825
--- /dev/null
+++ b/libraries/flicker/test/assets/testdata/wm_trace_unlock.pb
Binary files differ
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.kt
index b398ae5..d86ee36 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.kt
@@ -166,9 +166,6 @@
         override val timestamp: Long get() = 0
         override val parent: FlickerSubject? get() = null
         override val selfFacts = listOf(Fact.fact("SimpleEntry", entry.mData.toString()))
-        override fun clone(): FlickerSubject {
-            return SimpleEntrySubject(fm, entry)
-        }
 
         fun isData42() = apply {
             check("is42").that(entry.mData).isEqualTo(42)
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/EventLogSubjectTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/EventLogSubjectTest.kt
index e8891f4..b4d12f8 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/EventLogSubjectTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/EventLogSubjectTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.server.wm.flicker
 
+import com.android.server.wm.flicker.FlickerRunResult.Companion.RunStatus.RUN_SUCCESS
 import com.android.server.wm.flicker.traces.eventlog.EventLogSubject
 import com.android.server.wm.flicker.traces.eventlog.FocusEvent
 import org.junit.Test
@@ -33,7 +34,7 @@
                         FocusEvent(0, "test WinA window", FocusEvent.Focus.LOST, "test"),
                         FocusEvent(0, "WinB", FocusEvent.Focus.LOST, "test"),
                         FocusEvent(0, "test WinC", FocusEvent.Focus.GAINED, "test"))
-        val result = builder.buildEventLogResult().eventLogSubject
+        val result = builder.buildEventLogResult(RUN_SUCCESS).eventLogSubject
         requireNotNull(result) { "Event log subject was not built" }
         result.focusChanges("WinA", "WinB", "WinC")
                 .forAllEntries()
@@ -46,8 +47,9 @@
 
     @Test
     fun canDetectFocusDoesNotChange() {
-        val result = FlickerRunResult.Builder().buildEventLogResult().eventLogSubject
+        val builder = FlickerRunResult.Builder()
+        val result = builder.buildEventLogResult(RUN_SUCCESS).eventLogSubject
         require(result != null) { "Event log subject was not built" }
         result.focusDoesNotChange().forAllEntries()
     }
-}
\ No newline at end of file
+}
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/FlickerDSLTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/FlickerDSLTest.kt
index ba4bb62..4859718 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/FlickerDSLTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/FlickerDSLTest.kt
@@ -18,7 +18,17 @@
 
 import android.app.Instrumentation
 import androidx.test.platform.app.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil
+import com.android.server.wm.flicker.FlickerResult.Companion.CombinedExecutionError
+import com.android.server.wm.flicker.FlickerRunResult.Companion.RunStatus.ASSERTION_FAILED
+import com.android.server.wm.flicker.FlickerRunResult.Companion.RunStatus.ASSERTION_SUCCESS
+import com.android.server.wm.flicker.TransitionRunner.Companion.TestSetupFailure
+import com.android.server.wm.flicker.TransitionRunner.Companion.TestTeardownFailure
+import com.android.server.wm.flicker.TransitionRunner.Companion.TransitionExecutionFailure
+import com.android.server.wm.flicker.TransitionRunner.Companion.TransitionSetupFailure
+import com.android.server.wm.flicker.TransitionRunner.Companion.TransitionTeardownFailure
 import com.android.server.wm.flicker.assertions.AssertionData
+import com.android.server.wm.flicker.assertions.FlickerAssertionError
 import com.android.server.wm.flicker.assertions.FlickerSubject
 import com.android.server.wm.flicker.dsl.AssertionTag
 import com.android.server.wm.flicker.dsl.FlickerBuilder
@@ -29,9 +39,11 @@
 import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
 import com.google.common.truth.Truth
 import org.junit.Assert
+import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runners.MethodSorters
+import java.lang.RuntimeException
 import kotlin.reflect.KClass
 
 /**
@@ -42,99 +54,75 @@
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class FlickerDSLTest {
     private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val TAG = "tag"
     private var executed = false
 
-    private fun runFlicker(assertion: AssertionData) {
-        executed = false
-        val builder = FlickerBuilder(instrumentation)
-        builder.transitions {
-            withTag(TAG) {
-                device.pressHome()
-            }
-        }
-        val flicker = builder.build()
-        flicker.execute()
-        flicker.checkAssertion(assertion)
-        Truth.assertWithMessage("Assertion was not executed")
-            .that(executed)
-            .isTrue()
-    }
-
-    private fun validateAssertion(
-        assertion: AssertionData,
-        expectedSubjectClass: KClass<out FlickerSubject>,
-        expectedTag: String
-    ) {
-        Truth.assertWithMessage("Unexpected subject type")
-            .that(assertion.expectedSubjectClass)
-            .isEqualTo(expectedSubjectClass)
-        Truth.assertWithMessage("Unexpected tag")
-            .that(assertion.tag)
-            .isEqualTo(expectedTag)
+    @Before
+    fun before() {
+        // Clear the trace output directory
+        SystemUtil.runShellCommand("rm -rf $OUT_DIR && mkdir $OUT_DIR")
     }
 
     @Test
     fun checkBuiltWMStartAssertion() {
         val assertion = FlickerTestParameter.buildWmStartAssertion { executed = true }
         validateAssertion(assertion, WindowManagerStateSubject::class, AssertionTag.START)
-        runFlicker(assertion)
+        runAndAssertExecuted(assertion)
     }
 
     @Test
     fun checkBuiltWMEndAssertion() {
         val assertion = FlickerTestParameter.buildWmEndAssertion { executed = true }
         validateAssertion(assertion, WindowManagerStateSubject::class, AssertionTag.END)
-        runFlicker(assertion)
+        runAndAssertExecuted(assertion)
     }
 
     @Test
     fun checkBuiltWMAssertion() {
         val assertion = FlickerTestParameter.buildWMAssertion { executed = true }
         validateAssertion(assertion, WindowManagerTraceSubject::class, AssertionTag.ALL)
-        runFlicker(assertion)
+        runAndAssertExecuted(assertion)
     }
 
     @Test
     fun checkBuiltWMTagAssertion() {
         val assertion = FlickerTestParameter.buildWMTagAssertion(TAG) { executed = true }
         validateAssertion(assertion, WindowManagerStateSubject::class, TAG)
-        runFlicker(assertion)
+        runAndAssertExecuted(assertion)
     }
 
     @Test
     fun checkBuiltLayersStartAssertion() {
         val assertion = FlickerTestParameter.buildLayersStartAssertion { executed = true }
         validateAssertion(assertion, LayerTraceEntrySubject::class, AssertionTag.START)
-        runFlicker(assertion)
+        runAndAssertExecuted(assertion)
     }
 
     @Test
     fun checkBuiltLayersEndAssertion() {
         val assertion = FlickerTestParameter.buildLayersEndAssertion { executed = true }
         validateAssertion(assertion, LayerTraceEntrySubject::class, AssertionTag.END)
-        runFlicker(assertion)
+        runAndAssertExecuted(assertion)
     }
 
     @Test
     fun checkBuiltLayersAssertion() {
         val assertion = FlickerTestParameter.buildLayersAssertion { executed = true }
         validateAssertion(assertion, LayersTraceSubject::class, AssertionTag.ALL)
-        runFlicker(assertion)
+        runAndAssertExecuted(assertion)
     }
 
     @Test
     fun checkBuiltLayersTagAssertion() {
         val assertion = FlickerTestParameter.buildLayersTagAssertion(TAG) { executed = true }
         validateAssertion(assertion, LayerTraceEntrySubject::class, TAG)
-        runFlicker(assertion)
+        runAndAssertExecuted(assertion)
     }
 
     @Test
     fun checkBuiltEventLogAssertion() {
         val assertion = FlickerTestParameter.buildEventLogAssertion { executed = true }
         validateAssertion(assertion, EventLogSubject::class, AssertionTag.ALL)
-        runFlicker(assertion)
+        runAndAssertExecuted(assertion)
     }
 
     @Test
@@ -171,7 +159,7 @@
             }
             builder.build().execute()
             Assert.fail("Should not have allowed invalid tag name")
-        } catch (e: Exception) {
+        } catch (e: Throwable) {
             Truth.assertWithMessage("Did not validate tag name")
                 .that(e.cause?.message)
                 .contains("The test tag inv lid can not contain spaces")
@@ -203,7 +191,7 @@
         try {
             FlickerBuilder(instrumentation).build().execute()
             Assert.fail("Should not have allowed empty transition")
-        } catch (e: Exception) {
+        } catch (e: Throwable) {
             Truth.assertWithMessage("Flicker did not warn of empty transitions")
                 .that(e.message)
                 .contains("A flicker test must include transitions to run")
@@ -220,20 +208,15 @@
             flicker.execute()
             Assert.fail("Should have raised an exception with message $exceptionMessage")
         } catch (e: Throwable) {
-            Truth.assertWithMessage("Incorrect exception message")
+            Truth.assertWithMessage("Incorrect exception type")
+                    .that(e)
+                    .isInstanceOf(TransitionExecutionFailure::class.java)
+            Truth.assertWithMessage("Exception does not contain the original crash message")
                 .that(e.message)
-                .contains("Unable to execute transition")
-            Truth.assertWithMessage("Test exception does not contain original crash message")
-                .that(e.cause?.message)
                 .contains(exceptionMessage)
         }
     }
 
-    private val failedAssertion = AssertionData(tag = AssertionTag.END,
-            expectedSubjectClass = LayerTraceEntrySubject::class) {
-        this.fail("Expected exception")
-    }
-
     @Test
     fun exceptionContainsDebugInfo() {
         val builder = FlickerBuilder(instrumentation)
@@ -242,17 +225,307 @@
         flicker.execute()
 
         val error = assertThrows(AssertionError::class.java) {
-            flicker.checkAssertion(failedAssertion)
+            flicker.checkAssertion(FAIL_ASSERTION)
         }
         // Exception message
         Truth.assertThat(error).hasMessageThat().contains("Expected exception")
         // Subject facts
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace files")
-        Truth.assertThat(error).hasMessageThat().contains("Entry")
+        assertThatErrorContainsDebugInfo(error)
+        Truth.assertThat(error).hasMessageThat().contains("Trace file")
         Truth.assertThat(error).hasMessageThat().contains("Location")
         // Correct stack trace point
-        Truth.assertThat(error).hasMessageThat().contains("failedAssertion")
+        Truth.assertThat(error).hasMessageThat().contains("FAIL_ASSERTION")
+    }
+
+    @Test
+    fun canDetectTestSetupExecutionError() {
+        val builder = FlickerBuilder(instrumentation)
+        builder.transitions(SIMPLE_TRANSITION)
+        builder.setup {
+            test {
+                throw RuntimeException("Failed to execute test setup")
+            }
+        }
+        val flicker = builder.build()
+        runAndAssertFlickerFailsWithException(flicker, TestSetupFailure::class.java)
+    }
+
+    @Test
+    fun canDetectTransitionSetupExecutionError() {
+        val builder = FlickerBuilder(instrumentation)
+        builder.transitions(SIMPLE_TRANSITION)
+        builder.setup {
+            eachRun {
+                throw RuntimeException("Failed to execute transition setup")
+            }
+        }
+        val flicker = builder.build()
+        runAndAssertFlickerFailsWithException(flicker, TransitionSetupFailure::class.java)
+    }
+
+    @Test
+    fun canDetectTransitionExecutionError() {
+        val builder = FlickerBuilder(instrumentation)
+        builder.transitions {
+            throw RuntimeException("Failed to execute transition")
+        }
+        val flicker = builder.build()
+        runAndAssertFlickerFailsWithException(flicker, TransitionExecutionFailure::class.java)
+    }
+
+    @Test
+    fun canDetectTransitionTeardownExecutionError() {
+        val builder = FlickerBuilder(instrumentation)
+        builder.transitions(SIMPLE_TRANSITION)
+        builder.teardown {
+            eachRun {
+                throw RuntimeException("Failed to execute transition teardown")
+            }
+        }
+        val flicker = builder.build()
+        runAndAssertFlickerFailsWithException(flicker, TransitionTeardownFailure::class.java)
+    }
+
+    @Test
+    fun canDetectTestTeardownExecutionError() {
+        val builder = FlickerBuilder(instrumentation)
+        builder.transitions(SIMPLE_TRANSITION)
+        builder.teardown {
+            test {
+                throw RuntimeException("Failed to execute test teardown")
+            }
+        }
+        val flicker = builder.build()
+        runAndAssertFlickerFailsWithException(flicker, TestTeardownFailure::class.java)
+    }
+
+    @Test
+    fun runsAssertionsOnSuccessfulTransitionsEvenIfSomeFailToExecute() {
+        val repetitions = 3
+        val builder = FlickerBuilder(instrumentation)
+        failOnLastTransitionRun(builder, repetitions)
+        var assertionExecutionCounter = 0
+        val assertions = listOf(
+                FlickerTestParameter.buildWmStartAssertion { assertionExecutionCounter++ },
+                FlickerTestParameter.buildWmEndAssertion { assertionExecutionCounter++ },
+                FlickerTestParameter.buildWMAssertion { assertionExecutionCounter++ },
+                FlickerTestParameter.buildWMTagAssertion(TAG) { assertionExecutionCounter++ },
+                FlickerTestParameter.buildLayersStartAssertion { assertionExecutionCounter++ },
+                FlickerTestParameter.buildLayersEndAssertion { assertionExecutionCounter++ },
+                FlickerTestParameter.buildLayersAssertion { assertionExecutionCounter++ },
+                FlickerTestParameter.buildLayersTagAssertion(TAG) { assertionExecutionCounter++ },
+                FlickerTestParameter.buildEventLogAssertion { assertionExecutionCounter++ }
+        )
+        val flicker = builder.build()
+        runAndAssertFlickerFailsWithException(flicker, TransitionExecutionFailure::class.java,
+                assertions = assertions)
+        Truth.assertWithMessage("All assertions ran on all iterations except the last one")
+                .that(assertionExecutionCounter)
+                .isEqualTo(assertions.size * (repetitions - 1))
+    }
+
+    @Test
+    fun canHandleAndTrackMultipleExecutionErrors() {
+        val repetitions = 2
+        val builder = FlickerBuilder(instrumentation)
+        failOnLastTransitionRun(builder, repetitions)
+        builder.teardown {
+            test {
+                throw RuntimeException("Failed to execute test teardown")
+            }
+        }
+        val flicker = builder.build()
+        try {
+            runFlicker(flicker, PASS_ASSERTION)
+            Assert.fail("Should have raised an execution exception")
+        } catch (e: Throwable) {
+            Truth.assertWithMessage("Incorrect exception type")
+                    .that(e)
+                    .isInstanceOf(CombinedExecutionError::class.java)
+
+            val errors = (e as CombinedExecutionError).errors!!
+
+            // All exception are shown in the error message
+            for (error in errors) {
+                Truth.assertThat(e.message).contains("Failed to execute last transition")
+                Truth.assertThat(e.message).contains("Failed to execute test teardown")
+                Truth.assertThat(e.message).contains("TransitionExecutionFailure")
+                Truth.assertThat(e.message).contains("TestTeardownFailure")
+            }
+
+            // First exception is shown as cause
+            Truth.assertWithMessage("Incorrect exception type")
+                    .that(e.cause)
+                    .isInstanceOf(TransitionExecutionFailure::class.java)
+        }
+    }
+
+    @Test
+    fun savesTracesOfFailedTransitionExecution() {
+        val builder = FlickerBuilder(instrumentation)
+        builder.transitions {
+            throw RuntimeException("Failed to execute transition")
+        }
+        val flicker = builder.build()
+        val OUT_DIR = getDefaultFlickerOutputDir()
+
+        try {
+            runFlicker(flicker, PASS_ASSERTION)
+        } catch (e: TransitionExecutionFailure) {
+            // A TransitionExecutionFailure is expected
+        }
+        assertArchiveContainsAllTraces(runStatus = FlickerRunResult.Companion.RunStatus.RUN_FAILED)
+    }
+
+    @Test
+    fun savesTracesForAllIterations() {
+        val runner = TransitionRunner()
+        val repetitions = 5
+        val flicker = FlickerBuilder(instrumentation)
+                .apply {
+                    transitions {}
+                }
+                .repeat { repetitions }
+                .build(runner)
+        runner.execute(flicker)
+
+        for (iteration in 0 until repetitions) {
+            assertArchiveContainsAllTraces(
+                runStatus = ASSERTION_SUCCESS,
+                iteration = iteration
+            )
+        }
+    }
+
+    @Test
+    fun savesTracesAsFailureOnLayersStartAssertionFailure() {
+        val assertion = FlickerTestParameter.buildLayersStartAssertion {
+            throw Throwable("Failed layers start assertion")
+        }
+        checkTracesAreSavedWithAssertionFailure(assertion)
+    }
+
+    @Test
+    fun savesTracesAsFailureOnLayersEndAssertionFailure() {
+        val assertion = FlickerTestParameter.buildLayersEndAssertion {
+            throw Throwable("Failed layers end assertion")
+        }
+        checkTracesAreSavedWithAssertionFailure(assertion)
+    }
+
+    @Test
+    fun savesTracesAsFailureOnWindowsStartAssertionFailure() {
+        val assertion = FlickerTestParameter.buildWmStartAssertion {
+            throw Throwable("Failed wm start assertion")
+        }
+        checkTracesAreSavedWithAssertionFailure(assertion)
+    }
+
+    @Test
+    fun savesTracesAsFailureOnWindowsEndAssertionFailure() {
+        val assertion = FlickerTestParameter.buildWmEndAssertion {
+            throw Throwable("Failed wm end assertion")
+        }
+        checkTracesAreSavedWithAssertionFailure(assertion)
+    }
+
+    private fun checkTracesAreSavedWithAssertionFailure(assertion: AssertionData) {
+        val runner = TransitionRunner()
+        val flicker = FlickerBuilder(instrumentation)
+                .apply {
+                    transitions {}
+                }
+                .build(runner)
+        runAndAssertFlickerFailsWithException(flicker, FlickerAssertionError::class.java,
+                listOf(assertion))
+
+        assertArchiveContainsAllTraces(runStatus = ASSERTION_FAILED)
+    }
+
+    private fun runAndAssertExecuted(assertion: AssertionData) {
+        executed = false
+        val builder = FlickerBuilder(instrumentation)
+        builder.transitions(SIMPLE_TRANSITION)
+        val flicker = builder.build()
+        runFlicker(flicker, assertion)
+        assertAssertionExecuted()
+    }
+
+    private fun assertAssertionExecuted() {
+        Truth.assertWithMessage("Assertion was not executed")
+                .that(executed)
+                .isTrue()
+    }
+
+    private fun runFlicker(flicker: Flicker, assertion: AssertionData) {
+        runFlicker(flicker, listOf(assertion))
+    }
+
+    private fun runFlicker(flicker: Flicker, assertions: List<AssertionData>) {
+        // TODO: We should probably test that these methods actually get called like this and in
+        //       this order from the ParameterizedRunner/FlickerBlockJUnit4ClassRunner.
+        flicker.execute()
+        for (assertion in assertions) {
+            flicker.checkAssertion(assertion)
+        }
+        flicker.clear()
+    }
+
+    private fun validateAssertion(
+        assertion: AssertionData,
+        expectedSubjectClass: KClass<out FlickerSubject>,
+        expectedTag: String
+    ) {
+        Truth.assertWithMessage("Unexpected subject type")
+                .that(assertion.expectedSubjectClass)
+                .isEqualTo(expectedSubjectClass)
+        Truth.assertWithMessage("Unexpected tag")
+                .that(assertion.tag)
+                .isEqualTo(expectedTag)
+    }
+
+    private fun runAndAssertFlickerFailsWithException(
+        flicker: Flicker,
+        clazz: Class<*>,
+        assertions: List<AssertionData> = listOf(PASS_ASSERTION)
+    ) {
+        try {
+            runFlicker(flicker, assertions)
+            Assert.fail("Should have raised an execution exception")
+        } catch (e: Throwable) {
+            Truth.assertWithMessage("Incorrect exception type")
+                    .that(e)
+                    .isInstanceOf(clazz)
+        }
+    }
+
+    private fun failOnLastTransitionRun(builder: FlickerBuilder, repetitions: Int) {
+        var repetitionsCounter = 0
+        builder.transitions {
+            repetitionsCounter++
+            if (repetitionsCounter == repetitions) {
+                throw RuntimeException("Failed to execute last transition")
+            }
+            withTag(TAG) {
+                device.pressHome()
+            }
+        }
+        builder.repeat { repetitions }
+    }
+
+    companion object {
+        private val TAG = "tag"
+        private val SIMPLE_TRANSITION: Flicker.() -> Unit = {
+            withTag(TAG) {
+                device.pressHome()
+            }
+        }
+        private val PASS_ASSERTION = AssertionData(tag = AssertionTag.END,
+                expectedSubjectClass = LayerTraceEntrySubject::class) {}
+        private val FAIL_ASSERTION = AssertionData(tag = AssertionTag.END,
+                expectedSubjectClass = LayerTraceEntrySubject::class) {
+            this.fail("Expected exception")
+        }
+        private val OUT_DIR = getDefaultFlickerOutputDir()
     }
 }
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/FlickerTestParameterFactoryTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/FlickerTestParameterFactoryTest.kt
index 4ce608e..7be5aed 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/FlickerTestParameterFactoryTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/FlickerTestParameterFactoryTest.kt
@@ -16,10 +16,7 @@
 
 package com.android.server.wm.flicker
 
-import android.app.Instrumentation
 import android.view.Surface
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -32,36 +29,8 @@
  */
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class FlickerTestParameterFactoryTest {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val defaultRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90)
     private val testFactory = FlickerTestParameterFactory.getInstance()
 
-    private fun FlickerBuilder.setDefaultTestCfg(cfg: Map<String, Any?>) = apply {
-        withTestName { "${cfg.startRotationName}_${cfg.endRotationName}_" }
-    }
-
-    private fun validateRotationTest(
-        actual: Map<String, Any?>,
-        rotations: List<Int> = defaultRotations
-    ) {
-        assertWithMessage("Rotation tests should not have the same start and end rotation")
-            .that(actual.startRotation).isNotEqualTo(actual.endRotation)
-        assertWithMessage("Invalid start rotation value ${actual.startRotation}")
-            .that(actual.startRotation).isIn(rotations)
-        assertWithMessage("Invalid end rotation value ${actual.endRotation}")
-            .that(actual.endRotation).isIn(rotations)
-    }
-
-    private fun validateTest(
-        actual: Map<String, Any?>,
-        rotations: List<Int> = defaultRotations
-    ) {
-        assertWithMessage("Tests should have the same start and end rotation")
-            .that(actual.startRotation).isEqualTo(actual.endRotation)
-        assertWithMessage("Invalid rotation value ${actual.startRotation}")
-            .that(actual.startRotation).isIn(rotations)
-    }
-
     @Test
     fun checkBuildTest() {
         val actual = testFactory.getConfigNonRotationTests()
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/TransitionRunnerTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/TransitionRunnerTest.kt
index e6b5883..b819591 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/TransitionRunnerTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/TransitionRunnerTest.kt
@@ -16,22 +16,45 @@
 
 package com.android.server.wm.flicker
 
+import android.view.WindowManagerGlobal
 import androidx.test.platform.app.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil
+import com.android.server.wm.flicker.FlickerRunResult.Companion.RunStatus
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.google.common.truth.Truth
+import org.junit.After
+import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
+import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
+import org.mockito.junit.MockitoJUnitRunner
+import java.lang.RuntimeException
 
 /**
- * Contains [TransitionRunnerTest] and [TransitionRunnerCached] tests.
+ * Contains [TransitionRunner] tests.
  *
  * To run this test: `atest FlickerLibTest:TransitionRunnerTest`
  */
+@RunWith(MockitoJUnitRunner::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class TransitionRunnerTest {
     private val instrumentation = InstrumentationRegistry.getInstrumentation()
 
+    @After
+    fun assertTracingStopped() {
+        val windowManager = WindowManagerGlobal.getWindowManagerService()
+        Truth.assertWithMessage("Layers Trace not stopped")
+                .that(windowManager.isLayerTracing).isFalse()
+        Truth.assertWithMessage("WM Trace not stopped")
+                .that(windowManager.isWindowTraceEnabled).isFalse()
+    }
+
+    @Before
+    fun clearOutputDir() {
+        SystemUtil.runShellCommand("rm -rf ${getDefaultFlickerOutputDir()}")
+    }
+
     @Test
     fun canRunTransition() {
         val runner = TransitionRunner()
@@ -44,25 +67,127 @@
             }.build(runner)
         Truth.assertThat(executed).isFalse()
         val result = runner.execute(flicker)
+        runner.cleanUp()
         Truth.assertThat(executed).isTrue()
-        Truth.assertThat(result.error).isNull()
-        Truth.assertThat(result.runs).hasSize(4)
+        Truth.assertThat(result.executionErrors).isEmpty()
+        Truth.assertThat(result.successfulRuns).hasSize(4)
     }
 
     @Test
-    fun canRunTransitionCached() {
-        val runner = TransitionRunnerCached()
-        var executed = false
+    fun storesTransitionExecutionErrors() {
+        val runner = TransitionRunner()
         val flicker = FlickerBuilder(instrumentation)
             .apply {
                 transitions {
-                    executed = true
+                    throw RuntimeException("Failed to execute transition")
                 }
             }.build(runner)
         val result = runner.execute(flicker)
-        executed = false
-        val cachedResult = runner.execute(flicker)
-        Truth.assertThat(executed).isFalse()
-        Truth.assertThat(cachedResult).isEqualTo(result)
+        runner.cleanUp()
+        Truth.assertThat(result.executionErrors).isNotEmpty()
     }
-}
\ No newline at end of file
+
+    @Test
+    fun keepsSuccessfulTransitionExecutions() {
+        val repetitions = 3
+        var transitionRunCounter = 0
+
+        val runner = TransitionRunner()
+        val flicker = FlickerBuilder(instrumentation)
+                .apply {
+                    transitions {
+                        transitionRunCounter++
+                        if (transitionRunCounter == repetitions) {
+                            // fail on last transition repetition
+                            throw RuntimeException("Failed to execute transition")
+                        }
+                    }
+                }.repeat { repetitions }.build(runner)
+        val result = runner.execute(flicker)
+        runner.cleanUp()
+        Truth.assertThat(result.executionErrors).isNotEmpty()
+        // One for each monitor for each repetition expect the last one
+        // for which the transition failed to execute
+        val expectedResultCount = flicker.traceMonitors.size * (repetitions - 1)
+        Truth.assertThat(result.successfulRuns.size).isEqualTo(expectedResultCount)
+    }
+
+    @Test
+    fun storesSuccessExecutionStatusInRunResult() {
+        val runner = TransitionRunner()
+        val flicker = FlickerBuilder(instrumentation)
+                .apply {
+                    transitions {}
+                }.repeat { 3 }.build(runner)
+        val results = runner.execute(flicker).runResults
+        for (result in results) {
+            Truth.assertThat(result.status).isEqualTo(RunStatus.ASSERTION_SUCCESS)
+        }
+    }
+
+    @Test
+    fun storesFailedExecutionStatusInRunResult() {
+        val runner = TransitionRunner()
+        val flicker = FlickerBuilder(instrumentation)
+                .apply {
+                    transitions {
+                        throw RuntimeException("Failed to execute transition")
+                    }
+                }.repeat { 3 }.build(runner)
+        val results = runner.execute(flicker).runResults
+        for (result in results) {
+            Truth.assertThat(result.status).isEqualTo(RunStatus.RUN_FAILED)
+        }
+    }
+
+    @Test
+    fun savesTraceOnTransitionExecutionErrors() {
+        val runner = TransitionRunner()
+        val flicker = FlickerBuilder(instrumentation)
+                .apply {
+                    transitions {
+                        throw Throwable()
+                    }
+                }
+                .build(runner)
+        runner.execute(flicker)
+
+        assertArchiveContainsAllTraces(runStatus = RunStatus.RUN_FAILED)
+    }
+
+    @Test
+    fun savesTraceOnRunCleanupErrors() {
+        val runner = TransitionRunner()
+        val flicker = FlickerBuilder(instrumentation)
+                .apply {
+                    transitions {}
+                    teardown {
+                        eachRun {
+                            throw RuntimeException("Fail on run teardown")
+                        }
+                    }
+                }
+                .build(runner)
+        runner.execute(flicker)
+
+        assertArchiveContainsAllTraces(runStatus = RunStatus.RUN_FAILED)
+    }
+
+    @Test
+    fun savesTraceOnTestCleanupErrors() {
+        val runner = TransitionRunner()
+        val flicker = FlickerBuilder(instrumentation)
+                .apply {
+                    transitions {}
+                    teardown {
+                        test {
+                            throw RuntimeException("Fail on test teardown")
+                        }
+                    }
+                }
+                .build(runner)
+        runner.execute(flicker)
+
+        assertArchiveContainsAllTraces(runStatus = RunStatus.RUN_FAILED)
+    }
+}
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/UiDeviceExtensionsTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/UiDeviceExtensionsTest.kt
index 9161a5d..51c7dbb 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/UiDeviceExtensionsTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/UiDeviceExtensionsTest.kt
@@ -18,7 +18,7 @@
 
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.traces.common.DeviceStateDump
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
 import com.android.server.wm.traces.parser.FLAG_STATE_DUMP_FLAG_LAYERS
 import com.android.server.wm.traces.parser.FLAG_STATE_DUMP_FLAG_WM
@@ -46,7 +46,7 @@
 
     private fun getCurrStateDump(
         @WmStateDumpFlags dumpFlags: Int = FLAG_STATE_DUMP_FLAG_WM.or(FLAG_STATE_DUMP_FLAG_LAYERS)
-    ): DeviceStateDump<WindowManagerState?, LayerTraceEntry?> {
+    ): DeviceStateDump<WindowManagerState?, BaseLayerTraceEntry?> {
         val instrumentation = InstrumentationRegistry.getInstrumentation()
         return getCurrentStateDump(instrumentation.uiAutomation, dumpFlags)
     }
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/Utils.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/Utils.kt
index 4d2869f..6614447 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/Utils.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/Utils.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerRunResult.Companion.RunStatus.ASSERTION_SUCCESS
 import com.android.server.wm.flicker.traces.FlickerSubjectException
 import com.android.server.wm.traces.common.layers.LayersTrace
 import com.android.server.wm.traces.common.tags.TagTrace
@@ -27,13 +28,15 @@
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerTraceParser
 import com.google.common.io.ByteStreams
 import com.google.common.truth.ExpectFailure
+import com.google.common.truth.Truth
 import com.google.common.truth.TruthFailureSubject
-import java.nio.file.Paths
+import java.io.FileInputStream
+import java.nio.file.Files
+import java.util.zip.ZipInputStream
 
 internal fun readWmTraceFromFile(relativePath: String): WindowManagerTrace {
     return try {
-        WindowManagerTraceParser.parseFromTrace(readTestFile(relativePath),
-            source = Paths.get(relativePath))
+        WindowManagerTraceParser.parseFromTrace(readTestFile(relativePath))
     } catch (e: Exception) {
         throw RuntimeException(e)
     }
@@ -52,8 +55,11 @@
     ignoreOrphanLayers: Boolean = true
 ): LayersTrace {
     return try {
-        LayersTraceParser.parseFromTrace(readTestFile(relativePath),
-            source = Paths.get(relativePath)) { ignoreOrphanLayers }
+        LayersTraceParser.parseFromTrace(
+            readTestFile(relativePath),
+            ignoreLayersStackMatchNoDisplay = false,
+            ignoreLayersInVirtualDisplay = false
+        ) { ignoreOrphanLayers }
     } catch (e: Exception) {
         throw RuntimeException(e)
     }
@@ -61,8 +67,7 @@
 
 internal fun readTagTraceFromFile(relativePath: String): TagTrace {
     return try {
-        TagTraceParserUtil.parseFromTrace(readTestFile(relativePath),
-            source = Paths.get(relativePath))
+        TagTraceParserUtil.parseFromTrace(readTestFile(relativePath))
     } catch (e: Exception) {
         throw RuntimeException(e)
     }
@@ -107,3 +112,35 @@
     require(target is AssertionError) { "Unknown failure $target" }
     return ExpectFailure.assertThat(target)
 }
+
+fun assertThatErrorContainsDebugInfo(error: Throwable, withBlameEntry: Boolean = true) {
+    Truth.assertThat(error).hasMessageThat().contains("What?")
+    Truth.assertThat(error).hasMessageThat().contains("Where?")
+    Truth.assertThat(error).hasMessageThat().contains("Facts")
+    Truth.assertThat(error).hasMessageThat().contains("Trace start")
+    Truth.assertThat(error).hasMessageThat().contains("Trace end")
+
+    if (withBlameEntry) {
+        Truth.assertThat(error).hasMessageThat().contains("Entry")
+    }
+}
+
+fun assertArchiveContainsAllTraces(
+    runStatus: FlickerRunResult.Companion.RunStatus = ASSERTION_SUCCESS,
+    testName: String = "",
+    iteration: Int = 0
+) {
+    val archiveFileName = "${runStatus.prefix}_${testName}_$iteration.zip"
+    val archivePath = getDefaultFlickerOutputDir().resolve(archiveFileName)
+    Truth.assertWithMessage("Expected trace archive `$archivePath` to exist")
+            .that(Files.exists(archivePath)).isTrue()
+
+    val archiveStream = ZipInputStream(FileInputStream(archivePath.toFile()))
+
+    val expectedFiles = listOf("wm_trace.winscope", "layers_trace.winscope", "transition.mp4")
+    val actualFiles = generateSequence { archiveStream.nextEntry }.map { it.name }.toList()
+
+    Truth.assertThat(actualFiles).hasSize(expectedFiles.size)
+    Truth.assertWithMessage("Trace archive doesn't contain all expected traces")
+            .that(actualFiles.containsAll(expectedFiles)).isTrue()
+}
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayerSubjectTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayerSubjectTest.kt
index 5fff302..460b037 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayerSubjectTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayerSubjectTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.server.wm.flicker.layers
 
+import com.android.server.wm.flicker.assertThatErrorContainsDebugInfo
 import com.android.server.wm.flicker.assertThrows
 import com.android.server.wm.flicker.readLayerTraceFromFile
 import com.android.server.wm.flicker.traces.layers.LayerSubject
@@ -41,13 +42,8 @@
                 .layer("ImaginaryLayer", 0)
                 .exists()
         }
+        assertThatErrorContainsDebugInfo(error)
         Truth.assertThat(error).hasMessageThat().contains("ImaginaryLayer")
-        Truth.assertThat(error).hasMessageThat().contains("What?")
-        Truth.assertThat(error).hasMessageThat().contains("Where?")
-        Truth.assertThat(error).hasMessageThat().contains("Entry")
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace file")
         Truth.assertThat(error).hasMessageThat().contains("Layer name")
     }
 
@@ -61,13 +57,7 @@
                     .first()
                     .doesNotExist()
         }
-        Truth.assertThat(error).hasMessageThat().contains("What?")
-        Truth.assertThat(error).hasMessageThat().contains("Where?")
-        Truth.assertThat(error).hasMessageThat().contains("Entry")
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace file")
-        Truth.assertThat(error).hasMessageThat().contains("Entry")
+        assertThatErrorContainsDebugInfo(error)
     }
 
     @Test
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayerTraceEntrySubjectTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayerTraceEntrySubjectTest.kt
index b8fe45a..48ce0b0 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayerTraceEntrySubjectTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayerTraceEntrySubjectTest.kt
@@ -16,18 +16,19 @@
 
 package com.android.server.wm.flicker.layers
 
-import android.graphics.Region
 import com.android.server.wm.flicker.DOCKER_STACK_DIVIDER_COMPONENT
 import com.android.server.wm.flicker.IMAGINARY_COMPONENT
 import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.SIMPLE_APP_COMPONENT
 import com.android.server.wm.flicker.assertFailure
+import com.android.server.wm.flicker.assertThatErrorContainsDebugInfo
 import com.android.server.wm.flicker.assertThrows
 import com.android.server.wm.flicker.assertions.FlickerSubject
 import com.android.server.wm.flicker.readLayerTraceFromFile
 import com.android.server.wm.flicker.traces.layers.LayerTraceEntrySubject
 import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
 import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.region.Region
 import com.google.common.truth.Truth
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -47,11 +48,8 @@
                 .first()
                 .visibleRegion(IMAGINARY_COMPONENT)
         }
+        assertThatErrorContainsDebugInfo(error)
         Truth.assertThat(error).hasMessageThat().contains(IMAGINARY_COMPONENT.className)
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace file")
-        Truth.assertThat(error).hasMessageThat().contains("Entry")
         Truth.assertThat(error).hasMessageThat().contains(FlickerSubject.ASSERTION_TAG)
     }
 
@@ -76,7 +74,7 @@
     @Test
     fun canDetectUncoveredRegion() {
         val trace = readLayerTraceFromFile("layers_trace_emptyregion.pb")
-        val expectedRegion = Region(0, 0, 1440, 2960)
+        val expectedRegion = Region.from(0, 0, 1440, 2960)
         val error = assertThrows(AssertionError::class.java) {
             LayersTraceSubject.assertThat(trace).entry(935346112030)
                 .visibleRegion()
@@ -95,21 +93,21 @@
     @Test
     fun canTestLayerVisibleRegion_layerDoesNotExist() {
         val trace = readLayerTraceFromFile("layers_trace_emptyregion.pb")
-        val expectedVisibleRegion = Region(0, 0, 1, 1)
+        val expectedVisibleRegion = Region.from(0, 0, 1, 1)
         val error = assertThrows(AssertionError::class.java) {
             LayersTraceSubject.assertThat(trace).entry(937229257165)
                 .visibleRegion(IMAGINARY_COMPONENT)
                 .coversExactly(expectedVisibleRegion)
         }
         assertFailure(error)
-            .factValue("Could not find")
+            .factValue("Could not find layers")
                 .contains(IMAGINARY_COMPONENT.toWindowName())
     }
 
     @Test
     fun canTestLayerVisibleRegion_layerDoesNotHaveExpectedVisibleRegion() {
         val trace = readLayerTraceFromFile("layers_trace_emptyregion.pb")
-        val expectedVisibleRegion = Region(0, 0, 1, 1)
+        val expectedVisibleRegion = Region.from(0, 0, 1, 1)
         val error = assertThrows(AssertionError::class.java) {
             LayersTraceSubject.assertThat(trace).entry(937126074082)
                 .visibleRegion(DOCKER_STACK_DIVIDER_COMPONENT)
@@ -123,7 +121,7 @@
     @Test
     fun canTestLayerVisibleRegion_layerIsHiddenByParent() {
         val trace = readLayerTraceFromFile("layers_trace_emptyregion.pb")
-        val expectedVisibleRegion = Region(0, 0, 1, 1)
+        val expectedVisibleRegion = Region.from(0, 0, 1, 1)
         val error = assertThrows(AssertionError::class.java) {
             LayersTraceSubject.assertThat(trace).entry(935346112030)
                 .visibleRegion(SIMPLE_APP_COMPONENT)
@@ -137,7 +135,7 @@
     @Test
     fun canTestLayerVisibleRegion_incorrectRegionSize() {
         val trace = readLayerTraceFromFile("layers_trace_emptyregion.pb")
-        val expectedVisibleRegion = Region(0, 0, 1440, 99)
+        val expectedVisibleRegion = Region.from(0, 0, 1440, 99)
         val error = assertThrows(AssertionError::class.java) {
             LayersTraceSubject.assertThat(trace).entry(937126074082)
                 .visibleRegion(FlickerComponentName.STATUS_BAR)
@@ -151,7 +149,7 @@
     @Test
     fun canTestLayerVisibleRegion() {
         val trace = readLayerTraceFromFile("layers_trace_launch_split_screen.pb")
-        val expectedVisibleRegion = Region(0, 0, 1080, 145)
+        val expectedVisibleRegion = Region.from(0, 0, 1080, 145)
         LayersTraceSubject.assertThat(trace).entry(90480846872160)
             .visibleRegion(FlickerComponentName.STATUS_BAR)
             .coversExactly(expectedVisibleRegion)
@@ -165,7 +163,7 @@
                 .isVisible(SIMPLE_APP_COMPONENT)
         }
         assertFailure(error)
-            .factValue("Is Invisible")
+            .factValue("Is Invisible", 0)
             .contains("Bounds is 0x0")
     }
 
@@ -176,10 +174,10 @@
             .entry(238517209878020)
 
         entry.visibleRegion(useCompositionEngineRegionOnly = false)
-            .coversExactly(Region(0, 0, 1440, 2960))
+            .coversExactly(Region.from(0, 0, 1440, 2960))
 
         entry.visibleRegion(FlickerComponentName.IME,
             useCompositionEngineRegionOnly = false)
-            .coversExactly(Region(0, 171, 1440, 2960))
+            .coversExactly(Region.from(0, 171, 1440, 2960))
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayersTraceEntryTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayersTraceEntryTest.kt
index 670bba4..133a9a1 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayersTraceEntryTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayersTraceEntryTest.kt
@@ -17,6 +17,7 @@
 package com.android.server.wm.flicker.layers
 
 import com.android.server.wm.flicker.IMAGINARY_COMPONENT
+import com.android.server.wm.flicker.assertThatErrorContainsDebugInfo
 import com.android.server.wm.flicker.assertThrows
 import com.android.server.wm.flicker.readLayerTraceFromFile
 import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
@@ -40,10 +41,7 @@
                 .first()
                 .contains(IMAGINARY_COMPONENT)
         }
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace end")
-        Truth.assertThat(error).hasMessageThat().contains("Entry")
-        Truth.assertThat(error).hasMessageThat().contains("Trace file")
+        assertThatErrorContainsDebugInfo(error)
     }
 
     @Test
@@ -120,6 +118,8 @@
     fun canDetectOrphanLayers() {
         try {
             readLayerTraceFromFile("layers_trace_orphanlayers.pb", ignoreOrphanLayers = false)
+                .first()
+                .flattenedLayers
             error("Failed to detect orphaned layers.")
         } catch (exception: RuntimeException) {
             Truth.assertThat(exception.message)
@@ -139,6 +139,7 @@
         val layer = entry.flattenedLayers.first { it.name == layerName }
         Truth.assertWithMessage("$layerName should be invisible because of HWC region")
             .that(layer.visibilityReason)
+            .asList()
             .contains("Visible region calculated by Composition Engine is empty")
     }
 
@@ -160,4 +161,18 @@
             .that(entry.flattenedLayers.map { it.name })
             .doesNotContain(messagesApp)
     }
+
+    @Test
+    fun canParseTraceEmptyState() {
+        val layersTrace = readLayerTraceFromFile("layers_trace_empty_state.winscope")
+        val emptyStates = layersTrace.filter { it.flattenedLayers.isEmpty() }
+
+        Truth.assertWithMessage("Some states in the trace should be empty")
+            .that(emptyStates)
+            .isNotEmpty()
+
+        Truth.assertWithMessage("Expected state 4d4h41m14s193ms to be empty")
+            .that(emptyStates.first().timestamp)
+            .isEqualTo(362474193519965)
+    }
 }
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayersTraceSubjectTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayersTraceSubjectTest.kt
index 9ef6a03..ff0e761 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayersTraceSubjectTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayersTraceSubjectTest.kt
@@ -23,12 +23,12 @@
 import com.android.server.wm.flicker.assertFailure
 import com.android.server.wm.flicker.assertThrows
 import com.android.server.wm.flicker.readLayerTraceFromFile
+import com.android.server.wm.flicker.traces.FlickerSubjectException
 import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
 import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
 import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.common.Region
+import com.android.server.wm.traces.common.region.Region
 import com.android.server.wm.traces.common.layers.LayersTrace
-import com.android.server.wm.traces.parser.minus
 import com.google.common.truth.Truth
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -49,7 +49,6 @@
         }
         Truth.assertThat(error).hasMessageThat().contains("Trace start")
         Truth.assertThat(error).hasMessageThat().contains("Trace end")
-        Truth.assertThat(error).hasMessageThat().contains("Trace file")
     }
 
     @Test
@@ -57,6 +56,7 @@
         val layersTraceEntries = readLayerTraceFromFile("layers_trace_emptyregion.pb")
         try {
             assertThat(layersTraceEntries)
+                .visibleRegion()
                 .coversAtLeast(DISPLAY_REGION)
                 .forAllEntries()
             error("Assertion should not have passed")
@@ -272,9 +272,71 @@
                 .coversExactly(DISPLAY_REGION_ROTATED)
     }
 
+    @Test
+    fun checkCanDetectSplashScreen() {
+        val trace = readLayerTraceFromFile("layers_trace_splashscreen.pb")
+        val newLayer = FlickerComponentName("com.android.server.wm.flicker.testapp",
+            "com.android.server.wm.flicker.testapp.SimpleActivity")
+        assertThat(trace)
+            .isVisible(LAUNCHER_COMPONENT)
+            .then()
+            .isSplashScreenVisibleFor(newLayer, isOptional = false)
+            .then()
+            .isVisible(newLayer)
+            .forAllEntries()
+
+        val failure = assertThrows(FlickerSubjectException::class.java) {
+            assertThat(trace)
+                .isVisible(LAUNCHER_COMPONENT)
+                .then()
+                .isVisible(newLayer)
+                .forAllEntries()
+        }
+        assertFailure(failure).hasMessageThat().contains("Is Invisible")
+    }
+
+    @Test
+    fun checkCanDetectMissingSplashScreen() {
+        val trace = readLayerTraceFromFile("layers_trace_splashscreen.pb")
+        val newLayer = FlickerComponentName("com.android.server.wm.flicker.testapp",
+            "com.android.server.wm.flicker.testapp.SimpleActivity")
+
+        // No splashscreen because no matching activity record
+        var failure = assertThrows(FlickerSubjectException::class.java) {
+            assertThat(trace).first().isSplashScreenVisibleFor(newLayer)
+        }
+        assertFailure(failure).hasMessageThat().contains("Could not find Activity Record layer")
+
+        // No splashscreen for target activity record
+        failure = assertThrows(FlickerSubjectException::class.java) {
+            assertThat(trace).first().isSplashScreenVisibleFor(LAUNCHER_COMPONENT)
+        }
+        assertFailure(failure).hasMessageThat().contains("No splash screen visible")
+    }
+
+    @Test
+    fun checkCanDetectOccludedLayerWithDisplaySizeCrop() {
+        val trace = readLayerTraceFromFile("layers_trace_display_size_crop.winscope")
+        val subject = assertThat(trace)
+        subject.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+        val entrySubject = subject.entry(262022343289032)
+            .layer("SimpleActivity#30168", frameNumber = 1)
+        Truth.assertWithMessage("Layer visibility")
+            .that(entrySubject.isVisible)
+            .isFalse()
+        Truth.assertWithMessage("Layer occluded")
+            .that(entrySubject.layer?.occludedBy)
+            .asList()
+            .isNotEmpty()
+        Truth.assertWithMessage("Layer occluded")
+            .that(entrySubject.layer?.occludedBy?.first()?.name)
+            .contains("RotationLayer")
+    }
+
     companion object {
-        private val DISPLAY_REGION = android.graphics.Region(0, 0, 1440, 2880)
-        private val DISPLAY_REGION_ROTATED = Region(0, 0, 2160, 1080)
+        private val DISPLAY_REGION = Region.from(0, 0, 1440, 2880)
+        private val DISPLAY_REGION_ROTATED = Region.from(0, 0, 2160, 1080)
         private const val SHELL_APP_PACKAGE = "com.android.wm.shell.flicker.testapp"
         private val FIXED_APP = FlickerComponentName(SHELL_APP_PACKAGE,
                 "$SHELL_APP_PACKAGE.FixedActivity")
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayersTraceTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayersTraceTest.kt
index bac0ac4..3c81897 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayersTraceTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/layers/LayersTraceTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.server.wm.flicker.layers
 
+import com.android.server.wm.flicker.assertThatErrorContainsDebugInfo
 import com.android.server.wm.flicker.assertThrows
 import com.android.server.wm.flicker.readLayerTraceFromFile
 import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
@@ -87,7 +88,7 @@
                 .that(layer?.visibleRegion?.isEmpty).isFalse()
         Truth.assertWithMessage("App should visible region")
                 .that(layer?.visibleRegion?.toString())
-                .contains("(346, 1583) - (1094, 2839)")
+                .contains("SkRegion((346,1583,1094,2839))")
 
         val splashScreenLayer = entry.getLayerWithBuffer(
                 "Splash Screen com.android.server.wm.flicker.testapp.SimpleActivity#0")
@@ -95,7 +96,7 @@
                 .that(layer?.visibleRegion?.isEmpty).isFalse()
         Truth.assertWithMessage("Splash screen visible region")
                 .that(layer?.visibleRegion?.toString())
-                .contains("(346, 1583) - (1094, 2839)")
+                .contains("SkRegion((346,1583,1094,2839))")
     }
 
     @Test
@@ -113,7 +114,8 @@
         Truth.assertWithMessage("Layer $layerName should be partially occluded")
                 .that(partiallyOccludedBy.joinToString())
                 .contains("Splash Screen com.android.server.wm.flicker.testapp#0 buffer:w:1440, " +
-                        "h:3040, stride:1472, format:1 frame#1 visible:(346, 1583) - (1094, 2839)")
+                        "h:3040, stride:1472, format:1 frame#1 visible:" +
+                        "SkRegion((346,1583,1094,2839))")
     }
 
     @Test
@@ -123,9 +125,7 @@
             LayersTraceSubject.assertThat(layersTraceEntries)
                     .isEmpty()
         }
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace file")
+        assertThatErrorContainsDebugInfo(error, withBlameEntry = false)
     }
 
     @Test
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/EventLogMonitorTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/EventLogMonitorTest.kt
index c6debc5..65f64b0 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/EventLogMonitorTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/EventLogMonitorTest.kt
@@ -42,21 +42,21 @@
                 "com.android.phone.settings.fdn.FdnSetting (server)",
             "reason=test")
 
-        val result = FlickerRunResult.Builder()
-        monitor.save("test", result)
+        val resultBuilder = FlickerRunResult.Builder()
+        resultBuilder.setResultFrom(monitor)
 
-        assertEquals(2, result.eventLog?.size)
+        assertEquals(2, resultBuilder.eventLog?.size)
         assertEquals(
             "4749f88 com.android.phone/com.android.phone.settings.fdn.FdnSetting (server)",
-            result.eventLog?.get(0)?.window)
-        assertEquals(FocusEvent.Focus.LOST, result.eventLog?.get(0)?.focus)
+            resultBuilder.eventLog?.get(0)?.window)
+        assertEquals(FocusEvent.Focus.LOST, resultBuilder.eventLog?.get(0)?.focus)
         assertEquals(
             "7c01447 com.android.phone/com.android.phone.settings.fdn.FdnSetting (server)",
-            result.eventLog?.get(1)?.window)
-        assertEquals(FocusEvent.Focus.GAINED, result.eventLog?.get(1)?.focus)
-        assertTrue(result.eventLog?.get(0)?.timestamp ?: 0
-            <= result.eventLog?.get(1)?.timestamp ?: 0)
-        assertEquals(result.eventLog?.get(0)?.reason, "test")
+            resultBuilder.eventLog?.get(1)?.window)
+        assertEquals(FocusEvent.Focus.GAINED, resultBuilder.eventLog?.get(1)?.focus)
+        assertTrue(resultBuilder.eventLog?.get(0)?.timestamp ?: 0
+            <= resultBuilder.eventLog?.get(1)?.timestamp ?: 0)
+        assertEquals(resultBuilder.eventLog?.get(0)?.reason, "test")
     }
 
     @Test
@@ -84,21 +84,21 @@
             "reason=test")
         monitor.stop()
 
-        val result = FlickerRunResult.Builder()
-        monitor.save("test", result)
+        val resultBuilder = FlickerRunResult.Builder()
+        resultBuilder.setResultFrom(monitor)
 
-        assertEquals(2, result.eventLog?.size)
+        assertEquals(2, resultBuilder.eventLog?.size)
         assertEquals("479f88 " +
             "com.android.phone/" +
             "com.android.phone.settings.fdn.FdnSetting (server)",
-            result.eventLog?.get(0)?.window)
-        assertEquals(FocusEvent.Focus.LOST, result.eventLog?.get(0)?.focus)
+            resultBuilder.eventLog?.get(0)?.window)
+        assertEquals(FocusEvent.Focus.LOST, resultBuilder.eventLog?.get(0)?.focus)
         assertEquals("7c01447 com.android.phone/" +
             "com.android.phone.settings.fdn.FdnSetting (server)",
-            result.eventLog?.get(1)?.window)
-        assertEquals(FocusEvent.Focus.GAINED, result.eventLog?.get(1)?.focus)
-        assertTrue(result.eventLog?.get(0)?.timestamp ?: 0
-            <= result.eventLog?.get(1)?.timestamp ?: 0)
+            resultBuilder.eventLog?.get(1)?.window)
+        assertEquals(FocusEvent.Focus.GAINED, resultBuilder.eventLog?.get(1)?.focus)
+        assertTrue(resultBuilder.eventLog?.get(0)?.timestamp ?: 0
+            <= resultBuilder.eventLog?.get(1)?.timestamp ?: 0)
     }
 
     @Test
@@ -119,24 +119,24 @@
                 "reason=test")
         monitor.stop()
 
-        val result = FlickerRunResult.Builder()
-        monitor.save("test", result)
+        val resultBuilder = FlickerRunResult.Builder()
+        resultBuilder.setResultFrom(monitor)
 
-        assertEquals(2, result.eventLog?.size)
+        assertEquals(2, resultBuilder.eventLog?.size)
         assertEquals(
                 "4749f88 com.android.phone/com.android.phone.settings.fdn.FdnSetting (server)",
-                result.eventLog?.get(0)?.window)
-        assertEquals(FocusEvent.Focus.LOST, result.eventLog?.get(0)?.focus)
+                resultBuilder.eventLog?.get(0)?.window)
+        assertEquals(FocusEvent.Focus.LOST, resultBuilder.eventLog?.get(0)?.focus)
         assertEquals(
                 "7c01447 com.android.phone/com.android.phone.settings.fdn.FdnSetting (server)",
-                result.eventLog?.get(1)?.window)
-        assertEquals(FocusEvent.Focus.GAINED, result.eventLog?.get(1)?.focus)
-        assertTrue(result.eventLog?.get(0)?.timestamp ?: 0
-            <= result.eventLog?.get(1)?.timestamp ?: 0)
-        assertEquals(result.eventLog?.get(0)?.reason, "test")
+                resultBuilder.eventLog?.get(1)?.window)
+        assertEquals(FocusEvent.Focus.GAINED, resultBuilder.eventLog?.get(1)?.focus)
+        assertTrue(resultBuilder.eventLog?.get(0)?.timestamp ?: 0
+            <= resultBuilder.eventLog?.get(1)?.timestamp ?: 0)
+        assertEquals(resultBuilder.eventLog?.get(0)?.reason, "test")
     }
 
     private companion object {
         const val INPUT_FOCUS_TAG = 62001
     }
-}
\ No newline at end of file
+}
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.kt
index 8060511..2a8af8b 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.kt
@@ -18,7 +18,6 @@
 
 import android.surfaceflinger.nano.Layerstrace
 import androidx.test.uiautomator.UiDevice
-import com.android.server.wm.flicker.FlickerRunResult
 import com.google.common.truth.Truth
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -41,10 +40,6 @@
                         or Layerstrace.LayersTraceFileProto.MAGIC_NUMBER_L.toLong())
     }
 
-    override fun getTraceFile(result: FlickerRunResult): Path? {
-        return result.traceFiles.firstOrNull { it.toString().contains("layers_trace") }
-    }
-
     @Test
     fun withSFTracing() {
         val trace = withSFTracing {
@@ -57,4 +52,4 @@
             .that(trace.entries)
             .isNotEmpty()
     }
-}
\ No newline at end of file
+}
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.kt
index 37001be..6a4feff 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.kt
@@ -19,7 +19,7 @@
 import android.app.Instrumentation
 import android.os.SystemClock
 import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerRunResult
+import com.android.compatibility.common.util.SystemUtil
 import com.android.server.wm.flicker.getDefaultFlickerOutputDir
 import com.google.common.truth.Truth
 import org.junit.After
@@ -40,13 +40,18 @@
     @Before
     fun setup() {
         val outputDir = getDefaultFlickerOutputDir()
-        mScreenRecorder = ScreenRecorder(outputDir, instrumentation.targetContext)
+        mScreenRecorder = ScreenRecorder(instrumentation.targetContext, outputDir)
+    }
+
+    @Before
+    fun clearOutputDir() {
+        SystemUtil.runShellCommand("rm -rf ${getDefaultFlickerOutputDir()}")
     }
 
     @After
     fun teardown() {
         mScreenRecorder.stop()
-        mScreenRecorder.outputPath.toFile().delete()
+        Files.deleteIfExists(mScreenRecorder.outputFile)
     }
 
     @Test
@@ -54,9 +59,9 @@
         mScreenRecorder.start()
         SystemClock.sleep(100)
         mScreenRecorder.stop()
-        val file = mScreenRecorder.outputPath.toFile()
+        val file = mScreenRecorder.outputFile
         Truth.assertWithMessage("Screen recording file not found")
-            .that(file.exists())
+            .that(Files.exists(file))
             .isTrue()
     }
 
@@ -65,18 +70,7 @@
         mScreenRecorder.start()
         SystemClock.sleep(3000)
         mScreenRecorder.stop()
-        val builder = FlickerRunResult.Builder()
-        mScreenRecorder.save("test", builder)
-        val traces = builder.buildAll().mapNotNull { result ->
-            result.traceFiles.firstOrNull {
-                it.toString().contains("transition")
-            }
-        }
-        traces.forEach {
-            Truth.assertWithMessage("Trace file $it not found").that(Files.exists(it)).isTrue()
-        }
-        traces.forEach {
-            Files.deleteIfExists(it)
-        }
+        val trace = mScreenRecorder.outputFile
+        Truth.assertWithMessage("Trace file $trace not found").that(Files.exists(trace)).isTrue()
     }
 }
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/TraceMonitorTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/TraceMonitorTest.kt
index fa7aee4..5cc53de 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/TraceMonitorTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/TraceMonitorTest.kt
@@ -18,13 +18,13 @@
 
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
-import com.android.server.wm.flicker.FlickerRunResult
 import com.android.server.wm.flicker.getDefaultFlickerOutputDir
 import com.android.server.wm.traces.common.DeviceTraceDump
 import com.android.server.wm.traces.parser.DeviceDumpParser
 import com.google.common.io.Files
 import com.google.common.truth.Truth
 import org.junit.After
+import org.junit.Before
 import org.junit.Test
 import java.nio.file.Path
 
@@ -33,7 +33,6 @@
     lateinit var savedTrace: Path
     abstract fun getMonitor(outputDir: Path): T
     abstract fun assertTrace(traceData: ByteArray)
-    abstract fun getTraceFile(result: FlickerRunResult): Path?
 
     protected val instrumentation = InstrumentationRegistry.getInstrumentation()
     protected val device = UiDevice.getInstance(instrumentation)
@@ -42,6 +41,12 @@
         getMonitor(outputDir)
     }
 
+    @Before
+    fun before() {
+        Truth.assertWithMessage("Trace already enabled before starting test")
+                .that(traceMonitor.isEnabled).isFalse()
+    }
+
     @After
     fun teardown() {
         device.pressHome()
@@ -51,18 +56,20 @@
         if (::savedTrace.isInitialized) {
             savedTrace.toFile().delete()
         }
+        Truth.assertWithMessage("Failed to disable trace at end of test")
+                .that(traceMonitor.isEnabled).isFalse()
     }
 
     @Test
     @Throws(Exception::class)
-    fun canStartLayersTrace() {
+    fun canStartTrace() {
         traceMonitor.start()
         Truth.assertThat(traceMonitor.isEnabled).isTrue()
     }
 
     @Test
     @Throws(Exception::class)
-    fun canStopLayersTrace() {
+    fun canStopTrace() {
         traceMonitor.start()
         Truth.assertThat(traceMonitor.isEnabled).isTrue()
         traceMonitor.stop()
@@ -71,17 +78,12 @@
 
     @Test
     @Throws(Exception::class)
-    fun captureLayersTrace() {
+    fun captureTrace() {
         traceMonitor.start()
         traceMonitor.stop()
-        val builder = FlickerRunResult.Builder()
-        traceMonitor.save("capturedTrace", builder)
-        val results = builder.buildAll()
-        Truth.assertWithMessage("Expected 3 results for the trace").that(results).hasSize(3)
-        val result = results.first()
-        savedTrace = getTraceFile(result) ?: error("Could not find saved trace file")
+        val savedTrace = traceMonitor.outputFile
         val testFile = savedTrace.toFile()
-        Truth.assertThat(testFile.exists()).isTrue()
+        Truth.assertWithMessage("File $testFile exists").that(testFile.exists()).isTrue()
         val trace = Files.toByteArray(testFile)
         Truth.assertThat(trace.size).isGreaterThan(0)
         assertTrace(trace)
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.kt
deleted file mode 100644
index c45cb12..0000000
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.kt
+++ /dev/null
@@ -1,48 +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.monitor
-
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.uiautomator.UiDevice
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import org.junit.Before
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runners.MethodSorters
-
-/**
- * Contains [WindowAnimationFrameStatsMonitor] tests. To run this test: `atest
- * FlickerLibTest:WindowAnimationFrameStatsMonitorTest`
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class WindowAnimationFrameStatsMonitorTest {
-    private val instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val frameStatsMonitor = WindowAnimationFrameStatsMonitor(instrumentation)
-    private val uiDevice = UiDevice.getInstance(instrumentation)
-
-    @Before
-    fun setup() {
-        uiDevice.wakeUpAndGoToHomeScreen()
-    }
-
-    @Test
-    fun captureWindowAnimationFrameStats() {
-        frameStatsMonitor.start()
-        uiDevice.pressRecentApps()
-        frameStatsMonitor.stop()
-    }
-}
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.kt
index 1cdf679..4d3617b 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.test.uiautomator.UiDevice
 import com.android.server.wm.nano.WindowManagerTraceFileProto
-import com.android.server.wm.flicker.FlickerRunResult
 import com.google.common.truth.Truth
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -37,10 +36,6 @@
                         or WindowManagerTraceFileProto.MAGIC_NUMBER_L.toLong())
     }
 
-    override fun getTraceFile(result: FlickerRunResult): Path? {
-        return result.traceFiles.firstOrNull { it.toString().contains("wm_trace") }
-    }
-
     @Test
     fun withWMTracing() {
         val trace = withWMTracing {
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/RegionSubjectTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/region/RegionSubjectTest.kt
similarity index 63%
rename from libraries/flicker/test/src/com/android/server/wm/flicker/RegionSubjectTest.kt
rename to libraries/flicker/test/src/com/android/server/wm/flicker/region/RegionSubjectTest.kt
index b5816d2..8ab34f5 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/RegionSubjectTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/region/RegionSubjectTest.kt
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.server.wm.flicker
+package com.android.server.wm.flicker.region
 
-import com.android.server.wm.flicker.traces.RegionSubject
+import com.android.server.wm.flicker.assertThrows
+import com.android.server.wm.flicker.traces.region.RegionSubject
 import com.android.server.wm.traces.common.Rect
 import com.google.common.truth.Truth
 import org.junit.FixMethodOrder
@@ -25,7 +26,7 @@
 
 /**
  * Contains [RegionSubject] tests. To run this test:
- * `atest FlickerLibTest:RectSubjectTest`
+ * `atest FlickerLibTest:RegionSubjectTest`
  */
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class RegionSubjectTest {
@@ -38,16 +39,16 @@
 
     private fun expectAllFailPositionChange(expectedMessage: String, rectA: Rect, rectB: Rect) {
         assertFail(expectedMessage) {
-            RegionSubject.assertThat(rectA).isHigher(rectB)
+            RegionSubject.assertThat(rectA, timestamp = 0).isHigher(rectB)
         }
         assertFail(expectedMessage) {
-            RegionSubject.assertThat(rectA).isHigherOrEqual(rectB)
+            RegionSubject.assertThat(rectA, timestamp = 0).isHigherOrEqual(rectB)
         }
         assertFail(expectedMessage) {
-            RegionSubject.assertThat(rectA).isLower(rectB)
+            RegionSubject.assertThat(rectA, timestamp = 0).isLower(rectB)
         }
         assertFail(expectedMessage) {
-            RegionSubject.assertThat(rectA).isLowerOrEqual(rectB)
+            RegionSubject.assertThat(rectA, timestamp = 0).isLowerOrEqual(rectB)
         }
     }
 
@@ -55,13 +56,13 @@
     fun detectPositionChangeHigher() {
         val rectA = Rect(left = 0, top = 0, right = 1, bottom = 1)
         val rectB = Rect(left = 0, top = 1, right = 1, bottom = 2)
-        RegionSubject.assertThat(rectA).isHigher(rectB)
-        RegionSubject.assertThat(rectA).isHigherOrEqual(rectB)
+        RegionSubject.assertThat(rectA, timestamp = 0).isHigher(rectB)
+        RegionSubject.assertThat(rectA, timestamp = 0).isHigherOrEqual(rectB)
         assertFail(RegionSubject.MSG_ERROR_TOP_POSITION) {
-            RegionSubject.assertThat(rectA).isLower(rectB)
+            RegionSubject.assertThat(rectA, timestamp = 0).isLower(rectB)
         }
         assertFail(RegionSubject.MSG_ERROR_TOP_POSITION) {
-            RegionSubject.assertThat(rectA).isLowerOrEqual(rectB)
+            RegionSubject.assertThat(rectA, timestamp = 0).isLowerOrEqual(rectB)
         }
     }
 
@@ -69,13 +70,13 @@
     fun detectPositionChangeLower() {
         val rectA = Rect(left = 0, top = 2, right = 1, bottom = 3)
         val rectB = Rect(left = 0, top = 0, right = 1, bottom = 1)
-        RegionSubject.assertThat(rectA).isLower(rectB)
-        RegionSubject.assertThat(rectA).isLowerOrEqual(rectB)
+        RegionSubject.assertThat(rectA, timestamp = 0).isLower(rectB)
+        RegionSubject.assertThat(rectA, timestamp = 0).isLowerOrEqual(rectB)
         assertFail(RegionSubject.MSG_ERROR_TOP_POSITION) {
-            RegionSubject.assertThat(rectA).isHigher(rectB)
+            RegionSubject.assertThat(rectA, timestamp = 0).isHigher(rectB)
         }
         assertFail(RegionSubject.MSG_ERROR_TOP_POSITION) {
-            RegionSubject.assertThat(rectA).isHigherOrEqual(rectB)
+            RegionSubject.assertThat(rectA, timestamp = 0).isHigherOrEqual(rectB)
         }
     }
 
@@ -83,13 +84,13 @@
     fun detectPositionChangeEqualHigherLower() {
         val rectA = Rect(left = 0, top = 1, right = 1, bottom = 0)
         val rectB = Rect(left = 1, top = 1, right = 2, bottom = 0)
-        RegionSubject.assertThat(rectA).isHigherOrEqual(rectB)
-        RegionSubject.assertThat(rectA).isLowerOrEqual(rectB)
+        RegionSubject.assertThat(rectA, timestamp = 0).isHigherOrEqual(rectB)
+        RegionSubject.assertThat(rectA, timestamp = 0).isLowerOrEqual(rectB)
         assertFail(RegionSubject.MSG_ERROR_TOP_POSITION) {
-            RegionSubject.assertThat(rectA).isHigher(rectB)
+            RegionSubject.assertThat(rectA, timestamp = 0).isHigher(rectB)
         }
         assertFail(RegionSubject.MSG_ERROR_TOP_POSITION) {
-            RegionSubject.assertThat(rectA).isLower(rectB)
+            RegionSubject.assertThat(rectA, timestamp = 0).isLower(rectB)
         }
     }
 
@@ -106,10 +107,10 @@
     fun detectCoversAtLeast() {
         val rectA = Rect(left = 1, top = 1, right = 2, bottom = 2)
         val rectB = Rect(left = 0, top = 0, right = 2, bottom = 2)
-        RegionSubject.assertThat(rectA).coversAtLeast(rectA)
-        RegionSubject.assertThat(rectB).coversAtLeast(rectA)
+        RegionSubject.assertThat(rectA, timestamp = 0).coversAtLeast(rectA)
+        RegionSubject.assertThat(rectB, timestamp = 0).coversAtLeast(rectA)
         assertFail("SkRegion((0,0,2,1)(0,1,1,2))") {
-            RegionSubject.assertThat(rectA).coversAtLeast(rectB)
+            RegionSubject.assertThat(rectA, timestamp = 0).coversAtLeast(rectB)
         }
     }
 
@@ -117,10 +118,10 @@
     fun detectCoversAtMost() {
         val rectA = Rect(left = 1, top = 1, right = 2, bottom = 2)
         val rectB = Rect(left = 0, top = 0, right = 2, bottom = 2)
-        RegionSubject.assertThat(rectA).coversAtMost(rectA)
-        RegionSubject.assertThat(rectA).coversAtMost(rectB)
+        RegionSubject.assertThat(rectA, timestamp = 0).coversAtMost(rectA)
+        RegionSubject.assertThat(rectA, timestamp = 0).coversAtMost(rectB)
         assertFail("SkRegion((0,0,2,1)(0,1,1,2))") {
-            RegionSubject.assertThat(rectB).coversAtMost(rectA)
+            RegionSubject.assertThat(rectB, timestamp = 0).coversAtMost(rectA)
         }
     }
 
@@ -128,9 +129,9 @@
     fun detectCoversExactly() {
         val rectA = Rect(left = 1, top = 1, right = 2, bottom = 2)
         val rectB = Rect(left = 0, top = 0, right = 2, bottom = 2)
-        RegionSubject.assertThat(rectA).coversExactly(rectA)
+        RegionSubject.assertThat(rectA, timestamp = 0).coversExactly(rectA)
         assertFail("SkRegion((0,0,2,1)(0,1,1,2))") {
-            RegionSubject.assertThat(rectA).coversExactly(rectB)
+            RegionSubject.assertThat(rectA, timestamp = 0).coversExactly(rectB)
         }
     }
 
@@ -139,10 +140,10 @@
         val rectA = Rect(left = 1, top = 1, right = 2, bottom = 2)
         val rectB = Rect(left = 0, top = 0, right = 2, bottom = 2)
         val rectC = Rect(left = 2, top = 2, right = 3, bottom = 3)
-        RegionSubject.assertThat(rectA).overlaps(rectB)
-        RegionSubject.assertThat(rectB).overlaps(rectA)
+        RegionSubject.assertThat(rectA, timestamp = 0).overlaps(rectB)
+        RegionSubject.assertThat(rectB, timestamp = 0).overlaps(rectA)
         assertFail("Overlap region: SkRegion()") {
-            RegionSubject.assertThat(rectA).overlaps(rectC)
+            RegionSubject.assertThat(rectA, timestamp = 0).overlaps(rectC)
         }
     }
 
@@ -151,10 +152,10 @@
         val rectA = Rect(left = 1, top = 1, right = 2, bottom = 2)
         val rectB = Rect(left = 2, top = 2, right = 3, bottom = 3)
         val rectC = Rect(left = 0, top = 0, right = 2, bottom = 2)
-        RegionSubject.assertThat(rectA).notOverlaps(rectB)
-        RegionSubject.assertThat(rectB).notOverlaps(rectA)
+        RegionSubject.assertThat(rectA, timestamp = 0).notOverlaps(rectB)
+        RegionSubject.assertThat(rectB, timestamp = 0).notOverlaps(rectA)
         assertFail("SkRegion((1,1,2,2))") {
-            RegionSubject.assertThat(rectA).notOverlaps(rectC)
+            RegionSubject.assertThat(rectA, timestamp = 0).notOverlaps(rectC)
         }
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/region/RegionTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/region/RegionTest.kt
new file mode 100644
index 0000000..57b57b6
--- /dev/null
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/region/RegionTest.kt
@@ -0,0 +1,1614 @@
+/*
+ * Copyright (C) 2021 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.region
+
+import com.android.server.wm.traces.common.Rect
+import com.android.server.wm.traces.common.region.Region
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotSame
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runners.MethodSorters
+import kotlin.random.Random
+
+/**
+ * Contains [Region] tests. To run this test:
+ * `atest FlickerLibTest:RegionTest`
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class RegionTest {
+    // DIFFERENCE
+    private val DIFFERENCE_WITH1 = arrayOf(
+        intArrayOf(0, 0), intArrayOf(4, 4), intArrayOf(10, 10), intArrayOf(19, 19),
+        intArrayOf(19, 0), intArrayOf(10, 4), intArrayOf(4, 10), intArrayOf(0, 19))
+    private val DIFFERENCE_WITHOUT1 = arrayOf(intArrayOf(5, 5), intArrayOf(9, 9), intArrayOf(9, 5),
+        intArrayOf(5, 9))
+
+    private val DIFFERENCE_WITH2 = arrayOf(intArrayOf(0, 0), intArrayOf(19, 0), intArrayOf(9, 9),
+        intArrayOf(19, 9), intArrayOf(0, 19), intArrayOf(9, 19))
+    private val DIFFERENCE_WITHOUT2 = arrayOf(intArrayOf(10, 10), intArrayOf(19, 10),
+        intArrayOf(10, 19), intArrayOf(19, 19), intArrayOf(29, 10), intArrayOf(29, 29),
+        intArrayOf(10, 29))
+
+    private val DIFFERENCE_WITH3 = arrayOf(intArrayOf(0, 0), intArrayOf(19, 0), intArrayOf(0, 19),
+        intArrayOf(19, 19))
+    private val DIFFERENCE_WITHOUT3 = arrayOf(intArrayOf(40, 40), intArrayOf(40, 59),
+        intArrayOf(59, 40), intArrayOf(59, 59))
+
+    // INTERSECT
+    private val INTERSECT_WITH1 = arrayOf(intArrayOf(5, 5), intArrayOf(9, 9), intArrayOf(9, 5),
+        intArrayOf(5, 9))
+    private val INTERSECT_WITHOUT1 = arrayOf(intArrayOf(0, 0), intArrayOf(2, 2), intArrayOf(4, 4),
+        intArrayOf(10, 10), intArrayOf(19, 19), intArrayOf(19, 0), intArrayOf(10, 4),
+        intArrayOf(4, 10), intArrayOf(0, 19))
+
+    private val INTERSECT_WITH2 = arrayOf(intArrayOf(10, 10), intArrayOf(19, 10),
+        intArrayOf(10, 19), intArrayOf(19, 19))
+    private val INTERSECT_WITHOUT2 = arrayOf(intArrayOf(0, 0), intArrayOf(19, 0), intArrayOf(9, 9),
+        intArrayOf(19, 9), intArrayOf(0, 19), intArrayOf(9, 19), intArrayOf(29, 10),
+        intArrayOf(29, 29), intArrayOf(10, 29))
+
+    // UNION
+    private val UNION_WITH1 = arrayOf(intArrayOf(0, 0), intArrayOf(2, 2), intArrayOf(4, 4),
+        intArrayOf(6, 6), intArrayOf(10, 10), intArrayOf(19, 19), intArrayOf(19, 0),
+        intArrayOf(10, 4), intArrayOf(4, 10), intArrayOf(0, 19), intArrayOf(5, 5), intArrayOf(9, 9),
+        intArrayOf(9, 5), intArrayOf(5, 9))
+    private val UNION_WITHOUT1 = arrayOf(intArrayOf(0, 20), intArrayOf(20, 20), intArrayOf(20, 0))
+
+    private val UNION_WITH2 = arrayOf(intArrayOf(0, 0), intArrayOf(2, 2), intArrayOf(19, 0),
+        intArrayOf(9, 9), intArrayOf(19, 9), intArrayOf(0, 19), intArrayOf(9, 19),
+        intArrayOf(21, 21), intArrayOf(10, 10), intArrayOf(19, 10), intArrayOf(10, 19),
+        intArrayOf(19, 19), intArrayOf(29, 10), intArrayOf(29, 29), intArrayOf(10, 29))
+    private val UNION_WITHOUT2 = arrayOf(intArrayOf(0, 29), intArrayOf(0, 20), intArrayOf(9, 29),
+        intArrayOf(9, 20), intArrayOf(29, 0), intArrayOf(20, 0), intArrayOf(29, 9),
+        intArrayOf(20, 9))
+
+    private val UNION_WITH3 = arrayOf(intArrayOf(0, 0), intArrayOf(2, 2), intArrayOf(19, 0),
+        intArrayOf(0, 19), intArrayOf(19, 19), intArrayOf(40, 40), intArrayOf(41, 41),
+        intArrayOf(40, 59), intArrayOf(59, 40), intArrayOf(59, 59))
+    private val UNION_WITHOUT3 = arrayOf(intArrayOf(20, 20), intArrayOf(39, 39))
+
+    // XOR
+    private val XOR_WITH1 = arrayOf(intArrayOf(0, 0), intArrayOf(2, 2), intArrayOf(4, 4),
+        intArrayOf(10, 10), intArrayOf(19, 19), intArrayOf(19, 0), intArrayOf(10, 4),
+        intArrayOf(4, 10), intArrayOf(0, 19))
+    private val XOR_WITHOUT1 = arrayOf(intArrayOf(5, 5), intArrayOf(6, 6), intArrayOf(9, 9),
+        intArrayOf(9, 5), intArrayOf(5, 9))
+
+    private val XOR_WITH2 = arrayOf(intArrayOf(0, 0), intArrayOf(2, 2), intArrayOf(19, 0),
+        intArrayOf(9, 9), intArrayOf(19, 9), intArrayOf(0, 19), intArrayOf(9, 19),
+        intArrayOf(21, 21), intArrayOf(29, 10), intArrayOf(10, 29), intArrayOf(20, 10),
+        intArrayOf(10, 20), intArrayOf(20, 20), intArrayOf(29, 29))
+    private val XOR_WITHOUT2 = arrayOf(intArrayOf(10, 10), intArrayOf(11, 11), intArrayOf(19, 10),
+        intArrayOf(10, 19), intArrayOf(19, 19))
+
+    private val XOR_WITH3 = arrayOf(intArrayOf(0, 0), intArrayOf(2, 2), intArrayOf(19, 0),
+        intArrayOf(0, 19), intArrayOf(19, 19), intArrayOf(40, 40), intArrayOf(41, 41),
+        intArrayOf(40, 59), intArrayOf(59, 40), intArrayOf(59, 59))
+    private val XOR_WITHOUT3 = arrayOf(intArrayOf(20, 20), intArrayOf(39, 39))
+
+    // REVERSE_DIFFERENCE
+    private val REVERSE_DIFFERENCE_WITH2 = arrayOf(intArrayOf(29, 10), intArrayOf(10, 29),
+        intArrayOf(20, 10), intArrayOf(10, 20), intArrayOf(20, 20), intArrayOf(29, 29),
+        intArrayOf(21, 21))
+    private val REVERSE_DIFFERENCE_WITHOUT2 = arrayOf(intArrayOf(0, 0), intArrayOf(19, 0),
+        intArrayOf(0, 19), intArrayOf(19, 19), intArrayOf(2, 2), intArrayOf(11, 11))
+
+    private val REVERSE_DIFFERENCE_WITH3 = arrayOf(intArrayOf(40, 40), intArrayOf(40, 59),
+        intArrayOf(59, 40), intArrayOf(59, 59), intArrayOf(41, 41))
+    private val REVERSE_DIFFERENCE_WITHOUT3 = arrayOf(intArrayOf(0, 0), intArrayOf(19, 0),
+        intArrayOf(0, 19), intArrayOf(19, 19), intArrayOf(20, 20), intArrayOf(39, 39),
+        intArrayOf(2, 2))
+
+    private var mRegion: Region = Region()
+
+    private fun verifyPointsInsideRegion(area: Array<IntArray>) {
+        for (i in area.indices) {
+            assertTrue(mRegion.contains(area[i][0], area[i][1]))
+        }
+    }
+
+    private fun verifyPointsOutsideRegion(area: Array<IntArray>) {
+        for (i in area.indices) {
+            assertFalse(mRegion.contains(area[i][0], area[i][1]))
+        }
+    }
+
+    @Before
+    fun setup() {
+        mRegion = Region()
+    }
+
+    @Test
+    fun testConstructor() {
+        // Test Region()
+        Region()
+
+        // Test Region(Region)
+        val oriRegion = Region()
+        Region.from(oriRegion)
+
+        // Test Region(Rect)
+        val rect = Rect()
+        Region.from(rect)
+
+        // Test Region(int, int, int, int)
+        Region.from(0, 0, 100, 100)
+    }
+
+    @Test
+    fun testSet1() {
+        val rect = Rect(1, 2, 3, 4)
+        val oriRegion = Region.from(rect)
+        assertTrue(mRegion.set(oriRegion))
+        assertEquals(1, mRegion.bounds.left)
+        assertEquals(2, mRegion.bounds.top)
+        assertEquals(3, mRegion.bounds.right)
+        assertEquals(4, mRegion.bounds.bottom)
+    }
+
+    @Test
+    fun testSet2() {
+        val rect = Rect(1, 2, 3, 4)
+        assertTrue(mRegion.set(rect))
+        assertEquals(1, mRegion.bounds.left)
+        assertEquals(2, mRegion.bounds.top)
+        assertEquals(3, mRegion.bounds.right)
+        assertEquals(4, mRegion.bounds.bottom)
+    }
+
+    @Test
+    fun testSet3() {
+        assertTrue(mRegion.set(1, 2, 3, 4))
+        assertEquals(1, mRegion.bounds.left)
+        assertEquals(2, mRegion.bounds.top)
+        assertEquals(3, mRegion.bounds.right)
+        assertEquals(4, mRegion.bounds.bottom)
+    }
+
+    @Test
+    fun testIsRect() {
+        assertFalse(mRegion.isRect())
+        mRegion = Region.from(1, 2, 3, 4)
+        assertTrue(mRegion.isRect())
+    }
+
+    @Test
+    fun testIsComplex() {
+        // Region is empty
+        assertFalse(mRegion.isComplex())
+
+        // Only one rectangle
+        mRegion = Region()
+        mRegion.set(1, 2, 3, 4)
+        assertFalse(mRegion.isComplex())
+
+        // More than one rectangle
+        mRegion = Region()
+        mRegion.set(1, 1, 2, 2)
+        mRegion.union(Rect(3, 3, 5, 5))
+        assertTrue(mRegion.isComplex())
+    }
+
+    @Test
+    fun testUnion() {
+        val rect1 = Rect()
+        val rect2 = Rect(0, 0, 20, 20)
+        val rect3 = Rect(5, 5, 10, 10)
+        val rect4 = Rect(10, 10, 30, 30)
+        val rect5 = Rect(40, 40, 60, 60)
+
+        // union (inclusive-or) the two regions
+        mRegion.set(rect2)
+        // union null rectangle
+        assertTrue(mRegion.contains(6, 6))
+        assertTrue(mRegion.union(rect1))
+        assertTrue(mRegion.contains(6, 6))
+
+        // 1. union rectangle inside this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.contains(6, 6))
+        assertTrue(mRegion.union(rect3))
+        verifyPointsInsideRegion(UNION_WITH1)
+        verifyPointsOutsideRegion(UNION_WITHOUT1)
+
+        // 2. union rectangle overlap this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertFalse(mRegion.contains(21, 21))
+        assertTrue(mRegion.union(rect4))
+        verifyPointsInsideRegion(UNION_WITH2)
+        verifyPointsOutsideRegion(UNION_WITHOUT2)
+
+        // 3. union rectangle out of this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertFalse(mRegion.contains(41, 41))
+        assertTrue(mRegion.union(rect5))
+        verifyPointsInsideRegion(UNION_WITH3)
+        verifyPointsOutsideRegion(UNION_WITHOUT3)
+    }
+
+    @Test
+    fun testContains() {
+        mRegion.set(2, 2, 5, 5)
+        // Not contain (1, 1).
+        assertFalse(mRegion.contains(1, 1))
+
+        // Test point inside this region.
+        assertTrue(mRegion.contains(3, 3))
+
+        // Test left-top corner.
+        assertTrue(mRegion.contains(2, 2))
+
+        // Test left-bottom corner.
+        assertTrue(mRegion.contains(2, 4))
+
+        // Test right-top corner.
+        assertTrue(mRegion.contains(4, 2))
+
+        // Test right-bottom corner.
+        assertTrue(mRegion.contains(4, 4))
+
+        // Though you set 5, but 5 is not contained by this region.
+        assertFalse(mRegion.contains(5, 5))
+        assertFalse(mRegion.contains(2, 5))
+        assertFalse(mRegion.contains(5, 2))
+
+        // Set a new rectangle.
+        mRegion.set(6, 6, 8, 8)
+        assertFalse(mRegion.contains(3, 3))
+        assertTrue(mRegion.contains(7, 7))
+    }
+
+    @Test
+    fun testEmpty() {
+        assertTrue(mRegion.isEmpty)
+        mRegion = Region.from(1, 2, 3, 4)
+        assertFalse(mRegion.isEmpty)
+        mRegion.setEmpty()
+        assertTrue(mRegion.isEmpty)
+    }
+
+    @Test
+    fun testOp1() {
+        val rect1 = Rect()
+        val rect2 = Rect(0, 0, 20, 20)
+        val rect3 = Rect(5, 5, 10, 10)
+        val rect4 = Rect(10, 10, 30, 30)
+        val rect5 = Rect(40, 40, 60, 60)
+        verifyNullRegionOp1(rect1)
+        verifyDifferenceOp1(rect1, rect2, rect3, rect4, rect5)
+        verifyIntersectOp1(rect1, rect2, rect3, rect4, rect5)
+        verifyUnionOp1(rect1, rect2, rect3, rect4, rect5)
+        verifyXorOp1(rect1, rect2, rect3, rect4, rect5)
+        verifyReverseDifferenceOp1(rect1, rect2, rect3, rect4, rect5)
+        verifyReplaceOp1(rect1, rect2, rect3, rect4, rect5)
+    }
+
+    private fun verifyNullRegionOp1(rect1: Rect) {
+        // Region without rectangle
+        mRegion = Region()
+        assertFalse(mRegion.op(rect1, Region.Op.DIFFERENCE))
+        assertFalse(mRegion.op(rect1, Region.Op.INTERSECT))
+        assertFalse(mRegion.op(rect1, Region.Op.UNION))
+        assertFalse(mRegion.op(rect1, Region.Op.XOR))
+        assertFalse(mRegion.op(rect1, Region.Op.REVERSE_DIFFERENCE))
+        assertFalse(mRegion.op(rect1, Region.Op.REPLACE))
+    }
+
+    private fun verifyDifferenceOp1(
+        rect1: Rect,
+        rect2: Rect,
+        rect3: Rect,
+        rect4: Rect,
+        rect5: Rect
+    ) {
+        // DIFFERENCE, Region with rectangle
+        // subtract the op region from the first region
+        mRegion = Region()
+        // subtract null rectangle
+        mRegion.set(rect2)
+        assertTrue(mRegion.op(rect1, Region.Op.DIFFERENCE))
+
+        // 1. subtract rectangle inside this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(6, 6))
+        assertTrue(mRegion.op(rect3, Region.Op.DIFFERENCE))
+        verifyPointsInsideRegion(DIFFERENCE_WITH1)
+        verifyPointsOutsideRegion(DIFFERENCE_WITHOUT1)
+
+        // 2. subtract rectangle overlap this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(11, 11))
+        assertTrue(mRegion.op(rect4, Region.Op.DIFFERENCE))
+        verifyPointsInsideRegion(DIFFERENCE_WITH2)
+        verifyPointsOutsideRegion(DIFFERENCE_WITHOUT2)
+
+        // 3. subtract rectangle out of this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.op(rect5, Region.Op.DIFFERENCE))
+        verifyPointsInsideRegion(DIFFERENCE_WITH3)
+        verifyPointsOutsideRegion(DIFFERENCE_WITHOUT3)
+    }
+
+    private fun verifyIntersectOp1(
+        rect1: Rect,
+        rect2: Rect,
+        rect3: Rect,
+        rect4: Rect,
+        rect5: Rect
+    ) {
+        // INTERSECT, Region with rectangle
+        // intersect the two regions
+        mRegion = Region()
+        // intersect null rectangle
+        mRegion.set(rect2)
+        assertFalse(mRegion.op(rect1, Region.Op.INTERSECT))
+
+        // 1. intersect rectangle inside this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.op(rect3, Region.Op.INTERSECT))
+        verifyPointsInsideRegion(INTERSECT_WITH1)
+        verifyPointsOutsideRegion(INTERSECT_WITHOUT1)
+
+        // 2. intersect rectangle overlap this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(9, 9))
+        assertTrue(mRegion.op(rect4, Region.Op.INTERSECT))
+        verifyPointsInsideRegion(INTERSECT_WITH2)
+        verifyPointsOutsideRegion(INTERSECT_WITHOUT2)
+
+        // 3. intersect rectangle out of this region
+        mRegion.set(rect2)
+        assertFalse(mRegion.op(rect5, Region.Op.INTERSECT))
+    }
+
+    private fun verifyUnionOp1(rect1: Rect, rect2: Rect, rect3: Rect, rect4: Rect, rect5: Rect) {
+        // UNION, Region with rectangle
+        // union (inclusive-or) the two regions
+        mRegion = Region()
+        mRegion.set(rect2)
+        // union null rectangle
+        assertTrue(mRegion.contains(6, 6))
+        assertTrue(mRegion.op(rect1, Region.Op.UNION))
+        assertTrue(mRegion.contains(6, 6))
+
+        // 1. union rectangle inside this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.contains(6, 6))
+        assertTrue(mRegion.op(rect3, Region.Op.UNION))
+        verifyPointsInsideRegion(UNION_WITH1)
+        verifyPointsOutsideRegion(UNION_WITHOUT1)
+
+        // 2. union rectangle overlap this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertFalse(mRegion.contains(21, 21))
+        assertTrue(mRegion.op(rect4, Region.Op.UNION))
+        verifyPointsInsideRegion(UNION_WITH2)
+        verifyPointsOutsideRegion(UNION_WITHOUT2)
+
+        // 3. union rectangle out of this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertFalse(mRegion.contains(41, 41))
+        assertTrue(mRegion.op(rect5, Region.Op.UNION))
+        verifyPointsInsideRegion(UNION_WITH3)
+        verifyPointsOutsideRegion(UNION_WITHOUT3)
+    }
+
+    private fun verifyXorOp1(rect1: Rect, rect2: Rect, rect3: Rect, rect4: Rect, rect5: Rect) {
+        // XOR, Region with rectangle
+        // exclusive-or the two regions
+        mRegion = Region()
+        // xor null rectangle
+        mRegion.set(rect2)
+        assertTrue(mRegion.op(rect1, Region.Op.XOR))
+
+        // 1. xor rectangle inside this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.contains(6, 6))
+        assertTrue(mRegion.op(rect3, Region.Op.XOR))
+        verifyPointsInsideRegion(XOR_WITH1)
+        verifyPointsOutsideRegion(XOR_WITHOUT1)
+
+        // 2. xor rectangle overlap this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.contains(11, 11))
+        assertFalse(mRegion.contains(21, 21))
+        assertTrue(mRegion.op(rect4, Region.Op.XOR))
+        verifyPointsInsideRegion(XOR_WITH2)
+        verifyPointsOutsideRegion(XOR_WITHOUT2)
+
+        // 3. xor rectangle out of this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertFalse(mRegion.contains(41, 41))
+        assertTrue(mRegion.op(rect5, Region.Op.XOR))
+        verifyPointsInsideRegion(XOR_WITH3)
+        verifyPointsOutsideRegion(XOR_WITHOUT3)
+    }
+
+    private fun verifyReverseDifferenceOp1(
+        rect1: Rect,
+        rect2: Rect,
+        rect3: Rect,
+        rect4: Rect,
+        rect5: Rect
+    ) {
+        // REVERSE_DIFFERENCE, Region with rectangle
+        // reverse difference the first region from the op region
+        mRegion = Region()
+        mRegion.set(rect2)
+        // reverse difference null rectangle
+        assertFalse(mRegion.op(rect1, Region.Op.REVERSE_DIFFERENCE))
+
+        // 1. reverse difference rectangle inside this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.contains(6, 6))
+        assertFalse(mRegion.op(rect3, Region.Op.REVERSE_DIFFERENCE))
+
+        // 2. reverse difference rectangle overlap this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.contains(11, 11))
+        assertFalse(mRegion.contains(21, 21))
+        assertTrue(mRegion.op(rect4, Region.Op.REVERSE_DIFFERENCE))
+        verifyPointsInsideRegion(REVERSE_DIFFERENCE_WITH2)
+        verifyPointsOutsideRegion(REVERSE_DIFFERENCE_WITHOUT2)
+
+        // 3. reverse difference rectangle out of this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertFalse(mRegion.contains(41, 41))
+        assertTrue(mRegion.op(rect5, Region.Op.REVERSE_DIFFERENCE))
+        verifyPointsInsideRegion(REVERSE_DIFFERENCE_WITH3)
+        verifyPointsOutsideRegion(REVERSE_DIFFERENCE_WITHOUT3)
+    }
+
+    private fun verifyReplaceOp1(rect1: Rect, rect2: Rect, rect3: Rect, rect4: Rect, rect5: Rect) {
+        // REPLACE, Region with rectangle
+        // replace the dst region with the op region
+        mRegion = Region()
+        mRegion.set(rect2)
+        // subtract null rectangle
+        assertFalse(mRegion.op(rect1, Region.Op.REPLACE))
+        // subtract rectangle inside this region
+        mRegion.set(rect2)
+        assertEquals(rect2, mRegion.bounds)
+        assertTrue(mRegion.op(rect3, Region.Op.REPLACE))
+        assertNotSame(rect2, mRegion.bounds)
+        assertEquals(rect3, mRegion.bounds)
+        // subtract rectangle overlap this region
+        mRegion.set(rect2)
+        assertEquals(rect2, mRegion.bounds)
+        assertTrue(mRegion.op(rect4, Region.Op.REPLACE))
+        assertNotSame(rect2, mRegion.bounds)
+        assertEquals(rect4, mRegion.bounds)
+        // subtract rectangle out of this region
+        mRegion.set(rect2)
+        assertEquals(rect2, mRegion.bounds)
+        assertTrue(mRegion.op(rect5, Region.Op.REPLACE))
+        assertNotSame(rect2, mRegion.bounds)
+        assertEquals(rect5, mRegion.bounds)
+    }
+
+    @Test
+    fun testOp2() {
+        val rect2 = Rect(0, 0, 20, 20)
+        val rect3 = Rect(5, 5, 10, 10)
+        val rect4 = Rect(10, 10, 30, 30)
+        val rect5 = Rect(40, 40, 60, 60)
+        verifyNullRegionOp2()
+        verifyDifferenceOp2(rect2)
+        verifyIntersectOp2(rect2)
+        verifyUnionOp2(rect2)
+        verifyXorOp2(rect2)
+        verifyReverseDifferenceOp2(rect2)
+        verifyReplaceOp2(rect2, rect3, rect4, rect5)
+    }
+
+    private fun verifyNullRegionOp2() {
+        // Region without rectangle
+        mRegion = Region()
+        assertFalse(mRegion.op(0, 0, 0, 0, Region.Op.DIFFERENCE))
+        assertFalse(mRegion.op(0, 0, 0, 0, Region.Op.INTERSECT))
+        assertFalse(mRegion.op(0, 0, 0, 0, Region.Op.UNION))
+        assertFalse(mRegion.op(0, 0, 0, 0, Region.Op.XOR))
+        assertFalse(mRegion.op(0, 0, 0, 0, Region.Op.REVERSE_DIFFERENCE))
+        assertFalse(mRegion.op(0, 0, 0, 0, Region.Op.REPLACE))
+    }
+
+    private fun verifyDifferenceOp2(rect2: Rect) {
+        // DIFFERENCE, Region with rectangle
+        // subtract the op region from the first region
+        mRegion = Region()
+        // subtract null rectangle
+        mRegion.set(rect2)
+        assertTrue(mRegion.op(0, 0, 0, 0, Region.Op.DIFFERENCE))
+
+        // 1. subtract rectangle inside this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(6, 6))
+        assertTrue(mRegion.op(5, 5, 10, 10, Region.Op.DIFFERENCE))
+        verifyPointsInsideRegion(DIFFERENCE_WITH1)
+        verifyPointsOutsideRegion(DIFFERENCE_WITHOUT1)
+
+        // 2. subtract rectangle overlap this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(11, 11))
+        assertTrue(mRegion.op(10, 10, 30, 30, Region.Op.DIFFERENCE))
+        verifyPointsInsideRegion(DIFFERENCE_WITH2)
+        verifyPointsOutsideRegion(DIFFERENCE_WITHOUT2)
+
+        // 3. subtract rectangle out of this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.op(40, 40, 60, 60, Region.Op.DIFFERENCE))
+        verifyPointsInsideRegion(DIFFERENCE_WITH3)
+        verifyPointsOutsideRegion(DIFFERENCE_WITHOUT3)
+    }
+
+    private fun verifyIntersectOp2(rect2: Rect) {
+        // INTERSECT, Region with rectangle
+        // intersect the two regions
+        mRegion = Region()
+        // intersect null rectangle
+        mRegion.set(rect2)
+        assertFalse(mRegion.op(0, 0, 0, 0, Region.Op.INTERSECT))
+
+        // 1. intersect rectangle inside this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.op(5, 5, 10, 10, Region.Op.INTERSECT))
+        verifyPointsInsideRegion(INTERSECT_WITH1)
+        verifyPointsOutsideRegion(INTERSECT_WITHOUT1)
+
+        // 2. intersect rectangle overlap this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(9, 9))
+        assertTrue(mRegion.op(10, 10, 30, 30, Region.Op.INTERSECT))
+        verifyPointsInsideRegion(INTERSECT_WITH2)
+        verifyPointsOutsideRegion(INTERSECT_WITHOUT2)
+
+        // 3. intersect rectangle out of this region
+        mRegion.set(rect2)
+        assertFalse(mRegion.op(40, 40, 60, 60, Region.Op.INTERSECT))
+    }
+
+    private fun verifyUnionOp2(rect2: Rect) {
+        // UNION, Region with rectangle
+        // union (inclusive-or) the two regions
+        mRegion = Region()
+        mRegion.set(rect2)
+        // union null rectangle
+        assertTrue(mRegion.contains(6, 6))
+        assertTrue(mRegion.op(0, 0, 0, 0, Region.Op.UNION))
+        assertTrue(mRegion.contains(6, 6))
+
+        // 1. union rectangle inside this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.contains(6, 6))
+        assertTrue(mRegion.op(5, 5, 10, 10, Region.Op.UNION))
+        verifyPointsInsideRegion(UNION_WITH1)
+        verifyPointsOutsideRegion(UNION_WITHOUT1)
+
+        // 2. union rectangle overlap this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertFalse(mRegion.contains(21, 21))
+        assertTrue(mRegion.op(10, 10, 30, 30, Region.Op.UNION))
+        verifyPointsInsideRegion(UNION_WITH2)
+        verifyPointsOutsideRegion(UNION_WITHOUT2)
+
+        // 3. union rectangle out of this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertFalse(mRegion.contains(41, 41))
+        assertTrue(mRegion.op(40, 40, 60, 60, Region.Op.UNION))
+        verifyPointsInsideRegion(UNION_WITH3)
+        verifyPointsOutsideRegion(UNION_WITHOUT3)
+    }
+
+    private fun verifyXorOp2(rect2: Rect) {
+        // XOR, Region with rectangle
+        // exclusive-or the two regions
+        mRegion = Region()
+        mRegion.set(rect2)
+        // xor null rectangle
+        assertTrue(mRegion.op(0, 0, 0, 0, Region.Op.XOR))
+
+        // 1. xor rectangle inside this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.contains(6, 6))
+        assertTrue(mRegion.op(5, 5, 10, 10, Region.Op.XOR))
+        verifyPointsInsideRegion(XOR_WITH1)
+        verifyPointsOutsideRegion(XOR_WITHOUT1)
+
+        // 2. xor rectangle overlap this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.contains(11, 11))
+        assertFalse(mRegion.contains(21, 21))
+        assertTrue(mRegion.op(10, 10, 30, 30, Region.Op.XOR))
+        verifyPointsInsideRegion(XOR_WITH2)
+        verifyPointsOutsideRegion(XOR_WITHOUT2)
+
+        // 3. xor rectangle out of this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertFalse(mRegion.contains(41, 41))
+        assertTrue(mRegion.op(40, 40, 60, 60, Region.Op.XOR))
+        verifyPointsInsideRegion(XOR_WITH3)
+        verifyPointsOutsideRegion(XOR_WITHOUT3)
+    }
+
+    private fun verifyReverseDifferenceOp2(rect2: Rect) {
+        // REVERSE_DIFFERENCE, Region with rectangle
+        // reverse difference the first region from the op region
+        mRegion = Region()
+        mRegion.set(rect2)
+        // reverse difference null rectangle
+        assertFalse(mRegion.op(0, 0, 0, 0, Region.Op.REVERSE_DIFFERENCE))
+        // reverse difference rectangle inside this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.contains(6, 6))
+        assertFalse(mRegion.op(5, 5, 10, 10, Region.Op.REVERSE_DIFFERENCE))
+        // reverse difference rectangle overlap this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.contains(11, 11))
+        assertFalse(mRegion.contains(21, 21))
+        assertTrue(mRegion.op(10, 10, 30, 30, Region.Op.REVERSE_DIFFERENCE))
+        verifyPointsInsideRegion(REVERSE_DIFFERENCE_WITH2)
+        verifyPointsOutsideRegion(REVERSE_DIFFERENCE_WITHOUT2)
+        // reverse difference rectangle out of this region
+        mRegion.set(rect2)
+        assertTrue(mRegion.contains(2, 2))
+        assertFalse(mRegion.contains(41, 41))
+        assertTrue(mRegion.op(40, 40, 60, 60, Region.Op.REVERSE_DIFFERENCE))
+        verifyPointsInsideRegion(REVERSE_DIFFERENCE_WITH3)
+        verifyPointsOutsideRegion(REVERSE_DIFFERENCE_WITHOUT3)
+    }
+
+    private fun verifyReplaceOp2(rect2: Rect, rect3: Rect, rect4: Rect, rect5: Rect) {
+        // REPLACE, Region w1ith rectangle
+        // replace the dst region with the op region
+        mRegion = Region()
+        mRegion.set(rect2)
+        // subtract null rectangle
+        assertFalse(mRegion.op(0, 0, 0, 0, Region.Op.REPLACE))
+        // subtract rectangle inside this region
+        mRegion.set(rect2)
+        assertEquals(rect2, mRegion.bounds)
+        assertTrue(mRegion.op(5, 5, 10, 10, Region.Op.REPLACE))
+        assertNotSame(rect2, mRegion.bounds)
+        assertEquals(rect3, mRegion.bounds)
+        // subtract rectangle overlap this region
+        mRegion.set(rect2)
+        assertEquals(rect2, mRegion.bounds)
+        assertTrue(mRegion.op(10, 10, 30, 30, Region.Op.REPLACE))
+        assertNotSame(rect2, mRegion.bounds)
+        assertEquals(rect4, mRegion.bounds)
+        // subtract rectangle out of this region
+        mRegion.set(rect2)
+        assertEquals(rect2, mRegion.bounds)
+        assertTrue(mRegion.op(40, 40, 60, 60, Region.Op.REPLACE))
+        assertNotSame(rect2, mRegion.bounds)
+        assertEquals(rect5, mRegion.bounds)
+    }
+
+    @Test
+    fun testOp3() {
+        val region1 = Region()
+        val region2 = Region.from(0, 0, 20, 20)
+        val region3 = Region.from(5, 5, 10, 10)
+        val region4 = Region.from(10, 10, 30, 30)
+        val region5 = Region.from(40, 40, 60, 60)
+        verifyNullRegionOp3(region1)
+        verifyDifferenceOp3(region1, region2, region3, region4, region5)
+        verifyIntersectOp3(region1, region2, region3, region4, region5)
+        verifyUnionOp3(region1, region2, region3, region4, region5)
+        verifyXorOp3(region1, region2, region3, region4, region5)
+        verifyReverseDifferenceOp3(region1, region2, region3, region4, region5)
+        verifyReplaceOp3(region1, region2, region3, region4, region5)
+    }
+
+    private fun verifyNullRegionOp3(region1: Region) {
+        // Region without rectangle
+        mRegion = Region()
+        assertFalse(mRegion.op(region1, Region.Op.DIFFERENCE))
+        assertFalse(mRegion.op(region1, Region.Op.INTERSECT))
+        assertFalse(mRegion.op(region1, Region.Op.UNION))
+        assertFalse(mRegion.op(region1, Region.Op.XOR))
+        assertFalse(mRegion.op(region1, Region.Op.REVERSE_DIFFERENCE))
+        assertFalse(mRegion.op(region1, Region.Op.REPLACE))
+    }
+
+    private fun verifyDifferenceOp3(
+        region1: Region,
+        region2: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // DIFFERENCE, Region with rectangle
+        // subtract the op region from the first region
+        mRegion = Region()
+        // subtract null rectangle
+        mRegion.set(region2)
+        assertTrue(mRegion.op(region1, Region.Op.DIFFERENCE))
+
+        // 1. subtract rectangle inside this region
+        mRegion.set(region2)
+        assertTrue(mRegion.contains(6, 6))
+        assertTrue(mRegion.op(region3, Region.Op.DIFFERENCE))
+        verifyPointsInsideRegion(DIFFERENCE_WITH1)
+        verifyPointsOutsideRegion(DIFFERENCE_WITHOUT1)
+
+        // 2. subtract rectangle overlap this region
+        mRegion.set(region2)
+        assertTrue(mRegion.contains(11, 11))
+        assertTrue(mRegion.op(region4, Region.Op.DIFFERENCE))
+        verifyPointsInsideRegion(DIFFERENCE_WITH2)
+        verifyPointsOutsideRegion(DIFFERENCE_WITHOUT2)
+
+        // 3. subtract rectangle out of this region
+        mRegion.set(region2)
+        assertTrue(mRegion.op(region5, Region.Op.DIFFERENCE))
+        verifyPointsInsideRegion(DIFFERENCE_WITH3)
+        verifyPointsOutsideRegion(DIFFERENCE_WITHOUT3)
+    }
+
+    private fun verifyIntersectOp3(
+        region1: Region,
+        region2: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // INTERSECT, Region with rectangle
+        // intersect the two regions
+        mRegion = Region()
+        mRegion.set(region2)
+        // intersect null rectangle
+        assertFalse(mRegion.op(region1, Region.Op.INTERSECT))
+
+        // 1. intersect rectangle inside this region
+        mRegion.set(region2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.op(region3, Region.Op.INTERSECT))
+        verifyPointsInsideRegion(INTERSECT_WITH1)
+        verifyPointsOutsideRegion(INTERSECT_WITHOUT1)
+
+        // 2. intersect rectangle overlap this region
+        mRegion.set(region2)
+        assertTrue(mRegion.contains(9, 9))
+        assertTrue(mRegion.op(region4, Region.Op.INTERSECT))
+        verifyPointsInsideRegion(INTERSECT_WITH2)
+        verifyPointsOutsideRegion(INTERSECT_WITHOUT2)
+
+        // 3. intersect rectangle out of this region
+        mRegion.set(region2)
+        assertFalse(mRegion.op(region5, Region.Op.INTERSECT))
+    }
+
+    private fun verifyUnionOp3(
+        region1: Region,
+        region2: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // UNION, Region with rectangle
+        // union (inclusive-or) the two regions
+        mRegion = Region()
+        // union null rectangle
+        mRegion.set(region2)
+        assertTrue(mRegion.contains(6, 6))
+        assertTrue(mRegion.op(region1, Region.Op.UNION))
+        assertTrue(mRegion.contains(6, 6))
+
+        // 1. union rectangle inside this region
+        mRegion.set(region2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.contains(6, 6))
+        assertTrue(mRegion.op(region3, Region.Op.UNION))
+        verifyPointsInsideRegion(UNION_WITH1)
+        verifyPointsOutsideRegion(UNION_WITHOUT1)
+
+        // 2. union rectangle overlap this region
+        mRegion.set(region2)
+        assertTrue(mRegion.contains(2, 2))
+        assertFalse(mRegion.contains(21, 21))
+        assertTrue(mRegion.op(region4, Region.Op.UNION))
+        verifyPointsInsideRegion(UNION_WITH2)
+        verifyPointsOutsideRegion(UNION_WITHOUT2)
+
+        // 3. union rectangle out of this region
+        mRegion.set(region2)
+        assertTrue(mRegion.contains(2, 2))
+        assertFalse(mRegion.contains(41, 41))
+        assertTrue(mRegion.op(region5, Region.Op.UNION))
+        verifyPointsInsideRegion(UNION_WITH3)
+        verifyPointsOutsideRegion(UNION_WITHOUT3)
+    }
+
+    private fun verifyXorOp3(
+        region1: Region,
+        region2: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // XOR, Region with rectangle
+        // exclusive-or the two regions
+        mRegion = Region()
+        // xor null rectangle
+        mRegion.set(region2)
+        assertTrue(mRegion.op(region1, Region.Op.XOR))
+
+        // 1. xor rectangle inside this region
+        mRegion.set(region2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.contains(6, 6))
+        assertTrue(mRegion.op(region3, Region.Op.XOR))
+        verifyPointsInsideRegion(XOR_WITH1)
+        verifyPointsOutsideRegion(XOR_WITHOUT1)
+
+        // 2. xor rectangle overlap this region
+        mRegion.set(region2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.contains(11, 11))
+        assertFalse(mRegion.contains(21, 21))
+        assertTrue(mRegion.op(region4, Region.Op.XOR))
+        verifyPointsInsideRegion(XOR_WITH2)
+        verifyPointsOutsideRegion(XOR_WITHOUT2)
+
+        // 3. xor rectangle out of this region
+        mRegion.set(region2)
+        assertTrue(mRegion.contains(2, 2))
+        assertFalse(mRegion.contains(41, 41))
+        assertTrue(mRegion.op(region5, Region.Op.XOR))
+        verifyPointsInsideRegion(XOR_WITH3)
+        verifyPointsOutsideRegion(XOR_WITHOUT3)
+    }
+
+    private fun verifyReverseDifferenceOp3(
+        region1: Region,
+        region2: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // REVERSE_DIFFERENCE, Region with rectangle
+        // reverse difference the first region from the op region
+        mRegion = Region()
+        // reverse difference null rectangle
+        mRegion.set(region2)
+        assertFalse(mRegion.op(region1, Region.Op.REVERSE_DIFFERENCE))
+
+        // 1. reverse difference rectangle inside this region
+        mRegion.set(region2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.contains(6, 6))
+        assertFalse(mRegion.op(region3, Region.Op.REVERSE_DIFFERENCE))
+
+        // 2. reverse difference rectangle overlap this region
+        mRegion.set(region2)
+        assertTrue(mRegion.contains(2, 2))
+        assertTrue(mRegion.contains(11, 11))
+        assertFalse(mRegion.contains(21, 21))
+        assertTrue(mRegion.op(region4, Region.Op.REVERSE_DIFFERENCE))
+        verifyPointsInsideRegion(REVERSE_DIFFERENCE_WITH2)
+        verifyPointsOutsideRegion(REVERSE_DIFFERENCE_WITHOUT2)
+
+        // 3. reverse difference rectangle out of this region
+        mRegion.set(region2)
+        assertTrue(mRegion.contains(2, 2))
+        assertFalse(mRegion.contains(41, 41))
+        assertTrue(mRegion.op(region5, Region.Op.REVERSE_DIFFERENCE))
+        verifyPointsInsideRegion(REVERSE_DIFFERENCE_WITH3)
+        verifyPointsOutsideRegion(REVERSE_DIFFERENCE_WITHOUT3)
+    }
+
+    private fun verifyReplaceOp3(
+        region1: Region,
+        region2: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // REPLACE, Region with rectangle
+        // replace the dst region with the op region
+        mRegion = Region()
+        mRegion.set(region2)
+        // subtract null rectangle
+        assertFalse(mRegion.op(region1, Region.Op.REPLACE))
+        // subtract rectangle inside this region
+        mRegion.set(region2)
+        assertEquals(region2.bounds, mRegion.bounds)
+        assertTrue(mRegion.op(region3, Region.Op.REPLACE))
+        assertNotSame(region2.bounds, mRegion.bounds)
+        assertEquals(region3.bounds, mRegion.bounds)
+        // subtract rectangle overlap this region
+        mRegion.set(region2)
+        assertEquals(region2.bounds, mRegion.bounds)
+        assertTrue(mRegion.op(region4, Region.Op.REPLACE))
+        assertNotSame(region2.bounds, mRegion.bounds)
+        assertEquals(region4.bounds, mRegion.bounds)
+        // subtract rectangle out of this region
+        mRegion.set(region2)
+        assertEquals(region2.bounds, mRegion.bounds)
+        assertTrue(mRegion.op(region5, Region.Op.REPLACE))
+        assertNotSame(region2.bounds, mRegion.bounds)
+        assertEquals(region5.bounds, mRegion.bounds)
+    }
+
+    @Test
+    fun testOp4() {
+        val rect1 = Rect()
+        val rect2 = Rect(0, 0, 20, 20)
+        val region1 = Region()
+        val region2 = Region.from(0, 0, 20, 20)
+        val region3 = Region.from(5, 5, 10, 10)
+        val region4 = Region.from(10, 10, 30, 30)
+        val region5 = Region.from(40, 40, 60, 60)
+        verifyNullRegionOp4(rect1, region1)
+        verifyDifferenceOp4(rect1, rect2, region1, region3, region4, region5)
+        verifyIntersectOp4(rect1, rect2, region1, region3, region4, region5)
+        verifyUnionOp4(rect1, rect2, region1, region3, region4, region5)
+        verifyXorOp4(rect1, rect2, region1, region3, region4, region5)
+        verifyReverseDifferenceOp4(rect1, rect2, region1, region3, region4,
+            region5)
+        verifyReplaceOp4(rect1, rect2, region1, region2, region3, region4,
+            region5)
+    }
+
+    private fun verifyNullRegionOp4(rect1: Rect, region1: Region) {
+        // Region without rectangle
+        mRegion = Region()
+        assertFalse(mRegion.op(rect1, region1, Region.Op.DIFFERENCE))
+        assertFalse(mRegion.op(rect1, region1, Region.Op.INTERSECT))
+        assertFalse(mRegion.op(rect1, region1, Region.Op.UNION))
+        assertFalse(mRegion.op(rect1, region1, Region.Op.XOR))
+        assertFalse(mRegion.op(rect1, region1, Region.Op.REVERSE_DIFFERENCE))
+        assertFalse(mRegion.op(rect1, region1, Region.Op.REPLACE))
+    }
+
+    private fun verifyDifferenceOp4(
+        rect1: Rect,
+        rect2: Rect,
+        region1: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // DIFFERENCE, Region with rectangle
+        // subtract the op region from the first region
+        mRegion = Region()
+        // subtract null rectangle
+        assertTrue(mRegion.op(rect2, region1, Region.Op.DIFFERENCE))
+
+        // 1. subtract rectangle inside this region
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region3, Region.Op.DIFFERENCE))
+        verifyPointsInsideRegion(DIFFERENCE_WITH1)
+        verifyPointsOutsideRegion(DIFFERENCE_WITHOUT1)
+
+        // 2. subtract rectangle overlap this region
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region4, Region.Op.DIFFERENCE))
+        verifyPointsInsideRegion(DIFFERENCE_WITH2)
+        verifyPointsOutsideRegion(DIFFERENCE_WITHOUT2)
+
+        // 3. subtract rectangle out of this region
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region5, Region.Op.DIFFERENCE))
+        verifyPointsInsideRegion(DIFFERENCE_WITH3)
+        verifyPointsOutsideRegion(DIFFERENCE_WITHOUT3)
+    }
+
+    private fun verifyIntersectOp4(
+        rect1: Rect,
+        rect2: Rect,
+        region1: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // INTERSECT, Region with rectangle
+        // intersect the two regions
+        mRegion = Region()
+        // intersect null rectangle
+        mRegion.set(rect1)
+        assertFalse(mRegion.op(rect2, region1, Region.Op.INTERSECT))
+
+        // 1. intersect rectangle inside this region
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region3, Region.Op.INTERSECT))
+        verifyPointsInsideRegion(INTERSECT_WITH1)
+        verifyPointsOutsideRegion(INTERSECT_WITHOUT1)
+
+        // 2. intersect rectangle overlap this region
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region4, Region.Op.INTERSECT))
+        verifyPointsInsideRegion(INTERSECT_WITH2)
+        verifyPointsOutsideRegion(INTERSECT_WITHOUT2)
+
+        // 3. intersect rectangle out of this region
+        mRegion.set(rect1)
+        assertFalse(mRegion.op(rect2, region5, Region.Op.INTERSECT))
+    }
+
+    private fun verifyUnionOp4(
+        rect1: Rect,
+        rect2: Rect,
+        region1: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // UNION, Region with rectangle
+        // union (inclusive-or) the two regions
+        mRegion = Region()
+        // union null rectangle
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region1, Region.Op.UNION))
+        assertTrue(mRegion.contains(6, 6))
+
+        // 1. union rectangle inside this region
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region3, Region.Op.UNION))
+        verifyPointsInsideRegion(UNION_WITH1)
+        verifyPointsOutsideRegion(UNION_WITHOUT1)
+
+        // 2. union rectangle overlap this region
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region4, Region.Op.UNION))
+        verifyPointsInsideRegion(UNION_WITH2)
+        verifyPointsOutsideRegion(UNION_WITHOUT2)
+
+        // 3. union rectangle out of this region
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region5, Region.Op.UNION))
+        verifyPointsInsideRegion(UNION_WITH3)
+        verifyPointsOutsideRegion(UNION_WITHOUT3)
+    }
+
+    private fun verifyXorOp4(
+        rect1: Rect,
+        rect2: Rect,
+        region1: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // XOR, Region with rectangle
+        // exclusive-or the two regions
+        mRegion = Region()
+        // xor null rectangle
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region1, Region.Op.XOR))
+
+        // 1. xor rectangle inside this region
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region3, Region.Op.XOR))
+        verifyPointsInsideRegion(XOR_WITH1)
+        verifyPointsOutsideRegion(XOR_WITHOUT1)
+
+        // 2. xor rectangle overlap this region
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region4, Region.Op.XOR))
+        verifyPointsInsideRegion(XOR_WITH2)
+        verifyPointsOutsideRegion(XOR_WITHOUT2)
+
+        // 3. xor rectangle out of this region
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region5, Region.Op.XOR))
+        verifyPointsInsideRegion(XOR_WITH3)
+        verifyPointsOutsideRegion(XOR_WITHOUT3)
+    }
+
+    private fun verifyReverseDifferenceOp4(
+        rect1: Rect,
+        rect2: Rect,
+        region1: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // REVERSE_DIFFERENCE, Region with rectangle
+        // reverse difference the first region from the op region
+        mRegion = Region()
+        // reverse difference null rectangle
+        mRegion.set(rect1)
+        assertFalse(mRegion.op(rect2, region1, Region.Op.REVERSE_DIFFERENCE))
+
+        // 1. reverse difference rectangle inside this region
+        mRegion.set(rect1)
+        assertFalse(mRegion.op(rect2, region3, Region.Op.REVERSE_DIFFERENCE))
+
+        // 2. reverse difference rectangle overlap this region
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region4, Region.Op.REVERSE_DIFFERENCE))
+        verifyPointsInsideRegion(REVERSE_DIFFERENCE_WITH2)
+        verifyPointsOutsideRegion(REVERSE_DIFFERENCE_WITHOUT2)
+
+        // 3. reverse difference rectangle out of this region
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region5, Region.Op.REVERSE_DIFFERENCE))
+        verifyPointsInsideRegion(REVERSE_DIFFERENCE_WITH3)
+        verifyPointsOutsideRegion(REVERSE_DIFFERENCE_WITHOUT3)
+    }
+
+    private fun verifyReplaceOp4(
+        rect1: Rect,
+        rect2: Rect,
+        region1: Region,
+        region2: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // REPLACE, Region with rectangle
+        // replace the dst region with the op region
+        mRegion = Region()
+        // subtract null rectangle
+        mRegion.set(rect1)
+        assertFalse(mRegion.op(rect2, region1, Region.Op.REPLACE))
+        // subtract rectangle inside this region
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region3, Region.Op.REPLACE))
+        assertNotSame(region2.bounds, mRegion.bounds)
+        assertEquals(region3.bounds, mRegion.bounds)
+        // subtract rectangle overlap this region
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region4, Region.Op.REPLACE))
+        assertNotSame(region2.bounds, mRegion.bounds)
+        assertEquals(region4.bounds, mRegion.bounds)
+        // subtract rectangle out of this region
+        mRegion.set(rect1)
+        assertTrue(mRegion.op(rect2, region5, Region.Op.REPLACE))
+        assertNotSame(region2.bounds, mRegion.bounds)
+        assertEquals(region5.bounds, mRegion.bounds)
+    }
+
+    @Test
+    fun testOp5() {
+        val region1 = Region()
+        val region2 = Region.from(0, 0, 20, 20)
+        val region3 = Region.from(5, 5, 10, 10)
+        val region4 = Region.from(10, 10, 30, 30)
+        val region5 = Region.from(40, 40, 60, 60)
+        verifyNullRegionOp5(region1)
+        verifyDifferenceOp5(region1, region2, region3, region4, region5)
+        verifyIntersectOp5(region1, region2, region3, region4, region5)
+        verifyUnionOp5(region1, region2, region3, region4, region5)
+        verifyXorOp5(region1, region2, region3, region4, region5)
+        verifyReverseDifferenceOp5(region1, region2, region3, region4, region5)
+        verifyReplaceOp5(region1, region2, region3, region4, region5)
+    }
+
+    private fun verifyNullRegionOp5(region1: Region) {
+        // Region without rectangle
+        mRegion = Region()
+        assertFalse(mRegion.op(mRegion, region1, Region.Op.DIFFERENCE))
+        assertFalse(mRegion.op(mRegion, region1, Region.Op.INTERSECT))
+        assertFalse(mRegion.op(mRegion, region1, Region.Op.UNION))
+        assertFalse(mRegion.op(mRegion, region1, Region.Op.XOR))
+        assertFalse(mRegion.op(mRegion, region1, Region.Op.REVERSE_DIFFERENCE))
+        assertFalse(mRegion.op(mRegion, region1, Region.Op.REPLACE))
+    }
+
+    private fun verifyDifferenceOp5(
+        region1: Region,
+        region2: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // DIFFERENCE, Region with rectangle
+        // subtract the op region from the first region
+        mRegion = Region()
+        // subtract null rectangle
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region1, Region.Op.DIFFERENCE))
+
+        // 1. subtract rectangle inside this region
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region3, Region.Op.DIFFERENCE))
+        verifyPointsInsideRegion(DIFFERENCE_WITH1)
+        verifyPointsOutsideRegion(DIFFERENCE_WITHOUT1)
+
+        // 2. subtract rectangle overlap this region
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region4, Region.Op.DIFFERENCE))
+        verifyPointsInsideRegion(DIFFERENCE_WITH2)
+        verifyPointsOutsideRegion(DIFFERENCE_WITHOUT2)
+
+        // 3. subtract rectangle out of this region
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region5, Region.Op.DIFFERENCE))
+        verifyPointsInsideRegion(DIFFERENCE_WITH3)
+        verifyPointsOutsideRegion(DIFFERENCE_WITHOUT3)
+    }
+
+    private fun verifyIntersectOp5(
+        region1: Region,
+        region2: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // INTERSECT, Region with rectangle
+        // intersect the two regions
+        mRegion = Region()
+        // intersect null rectangle
+        mRegion.set(region1)
+        assertFalse(mRegion.op(region2, region1, Region.Op.INTERSECT))
+
+        // 1. intersect rectangle inside this region
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region3, Region.Op.INTERSECT))
+        verifyPointsInsideRegion(INTERSECT_WITH1)
+        verifyPointsOutsideRegion(INTERSECT_WITHOUT1)
+
+        // 2. intersect rectangle overlap this region
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region4, Region.Op.INTERSECT))
+        verifyPointsInsideRegion(INTERSECT_WITH2)
+        verifyPointsOutsideRegion(INTERSECT_WITHOUT2)
+
+        // 3. intersect rectangle out of this region
+        mRegion.set(region1)
+        assertFalse(mRegion.op(region2, region5, Region.Op.INTERSECT))
+    }
+
+    private fun verifyUnionOp5(
+        region1: Region,
+        region2: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // UNION, Region with rectangle
+        // union (inclusive-or) the two regions
+        mRegion = Region()
+        // union null rectangle
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region1, Region.Op.UNION))
+        assertTrue(mRegion.contains(6, 6))
+
+        // 1. union rectangle inside this region
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region3, Region.Op.UNION))
+        verifyPointsInsideRegion(UNION_WITH1)
+        verifyPointsOutsideRegion(UNION_WITHOUT1)
+
+        // 2. union rectangle overlap this region
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region4, Region.Op.UNION))
+        verifyPointsInsideRegion(UNION_WITH2)
+        verifyPointsOutsideRegion(UNION_WITHOUT2)
+
+        // 3. union rectangle out of this region
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region5, Region.Op.UNION))
+        verifyPointsInsideRegion(UNION_WITH3)
+        verifyPointsOutsideRegion(UNION_WITHOUT3)
+    }
+
+    private fun verifyXorOp5(
+        region1: Region,
+        region2: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // XOR, Region with rectangle
+        // exclusive-or the two regions
+        mRegion = Region()
+        // xor null rectangle
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region1, Region.Op.XOR))
+
+        // 1. xor rectangle inside this region
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region3, Region.Op.XOR))
+        verifyPointsInsideRegion(XOR_WITH1)
+        verifyPointsOutsideRegion(XOR_WITHOUT1)
+
+        // 2. xor rectangle overlap this region
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region4, Region.Op.XOR))
+        verifyPointsInsideRegion(XOR_WITH2)
+        verifyPointsOutsideRegion(XOR_WITHOUT2)
+
+        // 3. xor rectangle out of this region
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region5, Region.Op.XOR))
+        verifyPointsInsideRegion(XOR_WITH3)
+        verifyPointsOutsideRegion(XOR_WITHOUT3)
+    }
+
+    private fun verifyReverseDifferenceOp5(
+        region1: Region,
+        region2: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // REVERSE_DIFFERENCE, Region with rectangle
+        // reverse difference the first region from the op region
+        mRegion = Region()
+        // reverse difference null rectangle
+        mRegion.set(region1)
+        assertFalse(mRegion.op(region2, region1, Region.Op.REVERSE_DIFFERENCE))
+
+        // 1. reverse difference rectangle inside this region
+        mRegion.set(region1)
+        assertFalse(mRegion.op(region2, region3, Region.Op.REVERSE_DIFFERENCE))
+
+        // 2. reverse difference rectangle overlap this region
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region4, Region.Op.REVERSE_DIFFERENCE))
+        verifyPointsInsideRegion(REVERSE_DIFFERENCE_WITH2)
+        verifyPointsOutsideRegion(REVERSE_DIFFERENCE_WITHOUT2)
+
+        // 3. reverse difference rectangle out of this region
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region5, Region.Op.REVERSE_DIFFERENCE))
+        verifyPointsInsideRegion(REVERSE_DIFFERENCE_WITH3)
+        verifyPointsOutsideRegion(REVERSE_DIFFERENCE_WITHOUT3)
+    }
+
+    private fun verifyReplaceOp5(
+        region1: Region,
+        region2: Region,
+        region3: Region,
+        region4: Region,
+        region5: Region
+    ) {
+        // REPLACE, Region with rectangle
+        // replace the dst region with the op region
+        mRegion = Region()
+        // subtract null rectangle
+        mRegion.set(region1)
+        assertFalse(mRegion.op(region2, region1, Region.Op.REPLACE))
+        // subtract rectangle inside this region
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region3, Region.Op.REPLACE))
+        assertNotSame(region2.bounds, mRegion.bounds)
+        assertEquals(region3.bounds, mRegion.bounds)
+        // subtract rectangle overlap this region
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region4, Region.Op.REPLACE))
+        assertNotSame(region2.bounds, mRegion.bounds)
+        assertEquals(region4.bounds, mRegion.bounds)
+        // subtract rectangle out of this region
+        mRegion.set(region1)
+        assertTrue(mRegion.op(region2, region5, Region.Op.REPLACE))
+        assertNotSame(region2.bounds, mRegion.bounds)
+        assertEquals(region5.bounds, mRegion.bounds)
+    }
+
+    val flickerRegionOperations = listOf(
+        Region.Op.DIFFERENCE,
+        Region.Op.INTERSECT,
+        Region.Op.UNION,
+        Region.Op.XOR,
+        Region.Op.REVERSE_DIFFERENCE,
+        Region.Op.REPLACE
+    )
+    val nativeRegionOperations = listOf(
+        android.graphics.Region.Op.DIFFERENCE,
+        android.graphics.Region.Op.INTERSECT,
+        android.graphics.Region.Op.UNION,
+        android.graphics.Region.Op.XOR,
+        android.graphics.Region.Op.REVERSE_DIFFERENCE,
+        android.graphics.Region.Op.REPLACE
+    )
+
+    @Test
+    fun testFlickerRegionAndNativeRegionProvideSameOutputForSingleOperation() {
+        testFlickerRegionAndNativeRegionProvideSameOutput(1)
+    }
+
+    @Test
+    fun testFlickerRegionAndNativeRegionProvideSameOutputForSingleOperationFromEmpty() {
+        testFlickerRegionAndNativeRegionProvideSameOutput(1, startEmpty = true)
+    }
+
+    @Test
+    fun testFlickerRegionAndNativeRegionProvideSameOutputForTwoOperations() {
+        testFlickerRegionAndNativeRegionProvideSameOutput(2)
+    }
+
+    @Test
+    fun testFlickerRegionAndNativeRegionProvideSameOutputForTwoOperationsFromEmpty() {
+        testFlickerRegionAndNativeRegionProvideSameOutput(2, startEmpty = true)
+    }
+
+    @Test
+    fun testFlickerRegionAndNativeRegionProvideSameOutputForThreeOperations() {
+        testFlickerRegionAndNativeRegionProvideSameOutput(3)
+    }
+
+    @Test
+    fun testFlickerRegionAndNativeRegionProvideSameOutputForThreeOperationsFromEmpty() {
+        testFlickerRegionAndNativeRegionProvideSameOutput(3, startEmpty = true)
+    }
+
+    @Test
+    fun testFlickerRegionAndNativeRegionProvideSameOutputForFourOperations() {
+        testFlickerRegionAndNativeRegionProvideSameOutput(4)
+    }
+
+    @Test
+    fun testFlickerRegionAndNativeRegionProvideSameOutputForFourOperationsFromEmpty() {
+        testFlickerRegionAndNativeRegionProvideSameOutput(4, startEmpty = true)
+    }
+
+    @Test
+    fun testFlickerRegionAndNativeRegionProvideSameOutputForFiveOperations() {
+        testFlickerRegionAndNativeRegionProvideSameOutput(5)
+    }
+
+    @Test
+    fun testFlickerRegionAndNativeRegionProvideSameOutputForFiveOperationsFromEmpty() {
+        testFlickerRegionAndNativeRegionProvideSameOutput(5, startEmpty = true)
+    }
+
+    @Test
+    fun testFlickerRegionAndNativeRegionProvideSameOutputFor100Operations() {
+        testFlickerRegionAndNativeRegionProvideSameOutput(100, iterations = 10)
+    }
+
+    @Test
+    fun testFlickerRegionAndNativeRegionProvideSameOutputFor100OperationsFromEmpty() {
+        testFlickerRegionAndNativeRegionProvideSameOutput(
+            100, startEmpty = true, iterations = 10)
+    }
+
+    @Test
+    fun testFlickerRegionAndNativeRegionProvideSameOutputFor1000Operations() {
+        testFlickerRegionAndNativeRegionProvideSameOutput(100, iterations = 5)
+    }
+
+    @Test
+    fun testFlickerRegionAndNativeRegionProvideSameBoundsForSingleOperation() {
+        testFlickerRegionAndNativeRegionProvideSameBounds(1)
+    }
+
+    @Test
+    fun testFlickerRegionAndNativeRegionProvideSameBoundsForSingleOperationFromEmpty() {
+        testFlickerRegionAndNativeRegionProvideSameBounds(1, startEmpty = true)
+    }
+
+    @Test
+    fun testFlickerRegionAndNativeRegionProvideSameBoundsForFiveOperations() {
+        testFlickerRegionAndNativeRegionProvideSameBounds(5)
+    }
+
+    @Test
+    fun testFlickerRegionAndNativeRegionProvideSameBoundsForFiveOperationsFromEmpty() {
+        testFlickerRegionAndNativeRegionProvideSameBounds(5, startEmpty = true)
+    }
+
+    private fun testFlickerRegionAgainstNativeRegion(
+        totalOperations: Int,
+        startEmpty: Boolean = false,
+        iterations: Int = 100,
+        assertion: (
+            seed: Long,
+            history: String,
+            flickerRegion: Region,
+            nativeRegion: android.graphics.Region
+        ) -> Any,
+        seed: Long = System.currentTimeMillis()
+    ) {
+        val random = Random(seed)
+
+        for (i in 1..iterations) {
+            val flickerRegion: Region
+            val nativeRegion: android.graphics.Region
+            if (startEmpty) {
+                flickerRegion = Region.EMPTY
+                nativeRegion = android.graphics.Region()
+            } else {
+                val left = random.nextInt(0, 5000)
+                val top = random.nextInt(0, 5000)
+                val right = random.nextInt(left, 5000)
+                val bottom = random.nextInt(top, 5000)
+                flickerRegion = Region.from(left, top, right, bottom)
+                nativeRegion = android.graphics.Region(left, top, right, bottom)
+            }
+            var history = "$flickerRegion\n"
+            for (j in 1..totalOperations) {
+                val left = random.nextInt(0, 5000)
+                val top = random.nextInt(0, 5000)
+                val right = random.nextInt(left, 5000)
+                val bottom = random.nextInt(top, 5000)
+                val otherFlickerRegion = Region.from(left, top, right, bottom)
+                val otherNativeRegion = android.graphics.Region(left, top, right, bottom)
+
+                val operationIndex = random.nextInt(0, flickerRegionOperations.size)
+                val flickerOperation = flickerRegionOperations[operationIndex]
+                val nativeOperation = nativeRegionOperations[operationIndex]
+
+                history += "\t$flickerOperation $otherFlickerRegion\n"
+
+                flickerRegion.op(otherFlickerRegion, flickerOperation)
+                nativeRegion.op(otherNativeRegion, nativeOperation)
+
+                assertion(seed, history, flickerRegion, nativeRegion)
+            }
+        }
+    }
+
+    private fun testFlickerRegionAndNativeRegionProvideSameOutput(
+        totalOperations: Int,
+        startEmpty: Boolean = false,
+        iterations: Int = 100
+    ) {
+        testFlickerRegionAgainstNativeRegion(totalOperations, startEmpty, iterations, {
+            seed: Long,
+            history: String,
+            flickerRegion: Region,
+            nativeRegion: android.graphics.Region -> {
+                assertEquals("Ran with seed $seed\n" +
+                        "$history should equal \n" +
+                        "$nativeRegion\n but was\n$flickerRegion\n\n",
+                        nativeRegion.toString(), flickerRegion.toString())
+            }
+        })
+    }
+
+    private fun testFlickerRegionAndNativeRegionProvideSameBounds(
+        totalOperations: Int,
+        startEmpty: Boolean = false,
+        iterations: Int = 100
+    ) {
+        testFlickerRegionAgainstNativeRegion(totalOperations, startEmpty, iterations, {
+            seed: Long,
+            history: String,
+            flickerRegion: Region,
+            nativeRegion: android.graphics.Region -> {
+                val bounds = flickerRegion.bounds
+                val nBounds = nativeRegion.bounds
+                assertEquals("Ran with seed $seed\n" +
+                        "$history.bounds() should equal \n" +
+                        "${nBounds}\n but was\n${bounds}\n\n",
+                        "${nBounds.left},${nBounds.top},${nBounds.right},${nBounds.bottom}",
+                        "${bounds.left},${bounds.top},${bounds.right},${bounds.bottom}")
+            }
+        })
+    }
+}
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/service/AssertionEngineTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/service/AssertionEngineTest.kt
index 99c08ce..3440cca 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/service/AssertionEngineTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/service/AssertionEngineTest.kt
@@ -41,6 +41,9 @@
     private val tagTrace by lazy {
         readTagTraceFromFile("assertors/AppLaunchAndRotationsTagTrace.winscope")
     }
+    private val tagTraceEndTag by lazy {
+        readTagTraceFromFile("assertors/PipExitTagTrace.winscope")
+    }
     private val transitionTags by lazy { assertionEngine.getTransitionTags(tagTrace) }
 
     @Test
@@ -50,6 +53,15 @@
     }
 
     @Test
+    fun canExtractTransitionTags_noEndInfo() {
+        Truth.assertThat(tagTraceEndTag.entries.size).isEqualTo(2)
+
+        val transitionTags = assertionEngine.getTransitionTags(tagTraceEndTag)
+        Truth.assertThat(transitionTags).isNotEmpty()
+        Truth.assertThat(transitionTags.size).isEqualTo(1)
+    }
+
+    @Test
     fun canSplitTraces_singleTag() {
         val blocks = transitionTags
             .filter { it.tag.transition == Transition.APP_LAUNCH }
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/service/FassMockTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/service/FassMockTest.kt
index a69af64..0e481d6 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/service/FassMockTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/service/FassMockTest.kt
@@ -20,7 +20,6 @@
 import androidx.test.uiautomator.UiDevice
 import com.android.server.wm.flicker.helpers.SampleAppHelper
 import com.android.server.wm.flicker.rules.WMFlickerServiceRule
-import com.android.server.wm.traces.common.FlickerComponentName
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.FixMethodOrder
 import org.junit.Rule
@@ -49,9 +48,4 @@
         wmHelper.waitForHomeActivityVisible()
         dummyAppHelper.launchViaIntent(wmHelper)
     }
-
-    companion object {
-        private val DUMMY_APP = FlickerComponentName("com.google.android.apps.messaging",
-            "com.google.android.apps.messaging.ui.ConversationListActivity")
-    }
 }
\ No newline at end of file
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/service/FlickerMetricsCollectorRuleTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/service/FlickerMetricsCollectorRuleTest.kt
new file mode 100644
index 0000000..408c9f6
--- /dev/null
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/service/FlickerMetricsCollectorRuleTest.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2021 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.service
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.server.wm.flicker.helpers.SampleAppHelper
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.rules.FlickerMetricsCollectorRule
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runners.MethodSorters
+
+/**
+ * A test for [FlickerMetricsCollectorRule] checking that the traces are collected.
+ * To run this test: `atest FlickerLibTest:FlickerMetricsCollectorRuleTest`
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class FlickerMetricsCollectorRuleTest {
+    @get:Rule
+    val flickerRule = FlickerMetricsCollectorRule()
+
+    private val instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val device = UiDevice.getInstance(instrumentation)
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val testApp: SampleAppHelper = SampleAppHelper(instrumentation)
+
+    @Before
+    fun setup() {
+        device.wakeUpAndGoToHomeScreen()
+        wmHelper.waitForHomeActivityVisible()
+    }
+
+    @Test
+    fun runAssertion() {
+        device.pressHome()
+        wmHelper.waitForHomeActivityVisible()
+        flickerRule.checkPresubmitAssertions()
+    }
+
+    @Test
+    fun hasMetricsToReport() {
+        openAndCloseTestApp()
+        val metrics = flickerRule.getMetrics()
+        assertTrue(metrics.isNotEmpty())
+    }
+
+    @Test
+    fun metricsKeyContainsAllRequiredInformation() {
+        openTestApp()
+        val metrics = flickerRule.getMetrics()
+        val cujSource = this::class.java.simpleName
+        metrics.forEach { (key, _) ->
+            run {
+                assertTrue("Contains CUJ information ($cujSource)",
+                        key.contains(cujSource))
+                assertTrue("Contains Transition information (APP_LAUNCH)",
+                        key.contains("APP_LAUNCH"))
+            }
+        }
+    }
+
+    @Test
+    fun metricsValuesAreValid() {
+        openAndCloseTestApp()
+        val metrics = flickerRule.getMetrics()
+        metrics.forEach { (_, value) ->
+            assertTrue("Value is valid", value == 0 || value == 1)
+        }
+    }
+
+    private fun openTestApp() {
+        testApp.launchViaIntent(wmHelper)
+        wmHelper.waitForFullScreenApp(testApp.component)
+    }
+
+    private fun goHome() {
+        device.pressHome()
+        wmHelper.waitForHomeActivityVisible()
+    }
+
+    private fun openAndCloseTestApp() {
+        openTestApp()
+        goHome()
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/service/RotationMockTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/service/RotationMockTest.kt
index fdc59bf..1e94093 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/service/RotationMockTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/service/RotationMockTest.kt
@@ -21,7 +21,6 @@
 import com.android.server.wm.flicker.helpers.SampleAppHelper
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.rules.WMFlickerServiceRule
-import com.android.server.wm.traces.common.FlickerComponentName
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.FixMethodOrder
 import org.junit.Rule
@@ -54,9 +53,4 @@
         device.setOrientationNatural()
         instrumentation.uiAutomation.syncInputTransactions()
     }
-
-    companion object {
-        private val DUMMY_APP = FlickerComponentName("com.google.android.apps.messaging",
-                "com.google.android.apps.messaging.ui.ConversationListActivity")
-    }
 }
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/service/TraceIsTaggableTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/service/TraceIsTaggableTest.kt
index efddbbd..e1f5857 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/service/TraceIsTaggableTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/service/TraceIsTaggableTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.server.wm.flicker.service
 
+import android.view.Display
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
 import com.android.server.wm.flicker.helpers.SampleAppHelper
@@ -27,25 +28,36 @@
 import com.android.server.wm.traces.common.tags.Transition
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import com.google.common.truth.Truth
+import org.junit.Before
 import org.junit.Test
 
+/**
+ * Contains a test that records a trace and then tag it.
+ *
+ * To run this test: `atest FlickerLibTest:TraceIsTaggableTest`
+ */
 class TraceIsTaggableTest {
     private val instrumentation = InstrumentationRegistry.getInstrumentation()
     private val device = UiDevice.getInstance(instrumentation)
     private val wmHelper = WindowManagerStateHelper(instrumentation)
 
+    @Before
+    fun setup() {
+        device.pressHome()
+        wmHelper.waitForHomeActivityVisible()
+    }
+
     @Test
     fun canCreateTagsFromDeviceTrace() {
 
         // Generates trace of opening the messaging application from home screen
         val trace = withTracing {
-            device.pressHome()
             SampleAppHelper(instrumentation).launchViaIntent(wmHelper)
 
             // Wait until transition is fully completed
-            WindowManagerStateHelper().waitFor(
+            wmHelper.waitFor(
                 hasLayersAnimating().negate(),
-                isAppTransitionIdle(/* default display */ 0),
+                isAppTransitionIdle(Display.DEFAULT_DISPLAY),
                 isWMStateComplete()
             )
         }
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/service/WMFlickerServiceRuleForTestSpecTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/service/WMFlickerServiceRuleForTestSpecTest.kt
index 3de48c9..e2ece9b 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/service/WMFlickerServiceRuleForTestSpecTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/service/WMFlickerServiceRuleForTestSpecTest.kt
@@ -17,14 +17,19 @@
 package com.android.server.wm.flicker.service
 
 import android.app.Instrumentation
+import android.view.Surface
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.SampleAppHelper
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
+import org.hamcrest.core.StringContains
+import org.hamcrest.core.StringStartsWith
+import org.junit.Assert
 import org.junit.FixMethodOrder
 import org.junit.Rule
 import org.junit.Test
@@ -32,6 +37,10 @@
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
 
+/**
+ * A test for [WMFlickerServiceRuleForTestSpec] checking that metrics are reported
+ * To run this test: `atest FlickerLibTest:WMFlickerServiceRuleForTestSpecTest`
+ */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -40,20 +49,87 @@
     val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec)
 
     private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val testApp: SampleAppHelper = SampleAppHelper(instrumentation)
 
     @FlickerBuilderProvider
-    fun emptyFlicker(): FlickerBuilder {
+    fun openAndCloseTestApp(): FlickerBuilder {
         return FlickerBuilder(instrumentation).apply {
             transitions {
                 device.wakeUpAndGoToHomeScreen()
                 wmHelper.waitForAppTransitionIdle()
+
+                testApp.launchViaIntent(wmHelper)
+                wmHelper.waitForFullScreenApp(testApp.component)
+
+                device.pressHome()
+                wmHelper.waitForHomeActivityVisible()
             }
         }
     }
 
     @Test
     fun runAssertion() {
-        flickerRule.checkPresubmitAssertions()
+        flickerRule.checkPresubmitAssertions(failOnAssertionFailure = false)
+    }
+
+    @Test
+    fun hasMetricsToReport() {
+        flickerRule.checkPresubmitAssertions(failOnAssertionFailure = false)
+        val metrics = flickerRule.getMetrics()
+        Assert.assertTrue(metrics.isNotEmpty())
+    }
+
+    @Test
+    fun metricsKeyContainsCuj() {
+        flickerRule.checkPresubmitAssertions(failOnAssertionFailure = false)
+        val metrics = flickerRule.getMetrics()
+        val cujSource = this::class.java.simpleName
+        assertMetricsContain("CUJ", metrics, cujSource)
+    }
+
+    @Test
+    fun metricsKeyContainsTestParams() {
+        flickerRule.checkPresubmitAssertions(failOnAssertionFailure = false)
+        val metrics = flickerRule.getMetrics()
+        var paramsString = Surface.rotationToString(testSpec.startRotation)
+        if (testSpec.endRotation != testSpec.startRotation) {
+            paramsString += "_${Surface.rotationToString(testSpec.endRotation)}"
+        }
+        if (testSpec.navBarMode.isNotEmpty()) {
+            paramsString += "_${testSpec.navBarModeName}"
+        }
+        assertMetricsContain("Test Params", metrics, paramsString)
+    }
+
+    @Test
+    fun metricsKeyContainsFassPrefix() {
+        flickerRule.checkPresubmitAssertions(failOnAssertionFailure = false)
+        val metrics = flickerRule.getMetrics()
+        assertMetricsContain("FASS Prefix", metrics, "FASS")
+        metrics.forEach { (key, _) ->
+            run {
+                Assert.assertThat("Contains FASS Prefix",
+                        key, StringStartsWith(false, "FASS"))
+            }
+        }
+    }
+
+    @Test
+    fun metricsValuesAreValid() {
+        flickerRule.checkPresubmitAssertions(failOnAssertionFailure = false)
+        val metrics = flickerRule.getMetrics()
+        metrics.forEach { (_, value) ->
+            Assert.assertTrue("Value is valid", value == 0 || value == 1)
+        }
+    }
+
+    private fun assertMetricsContain(message: String, metrics: Map<String, Int>, string: String) {
+        metrics.forEach { (key, _) ->
+            run {
+                Assert.assertThat("Contains $message ($string)",
+                        key, StringContains.containsString(string))
+            }
+        }
     }
 
     companion object {
@@ -61,8 +137,7 @@
         @JvmStatic
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance()
-                .getConfigNonRotationTests(repetitions = 1)
-                .take(1)
+                .getConfigNonRotationTests(repetitions = 2)
         }
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/service/assertors/AppCloseAssertionsTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/service/assertors/AppCloseAssertionsTest.kt
new file mode 100644
index 0000000..af177f3
--- /dev/null
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/service/assertors/AppCloseAssertionsTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 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.service.assertors
+
+import com.android.server.wm.flicker.readLayerTraceFromFile
+import com.android.server.wm.flicker.readTestFile
+import com.android.server.wm.flicker.readWmTraceFromFile
+import com.android.server.wm.traces.common.tags.Tag
+import com.android.server.wm.traces.common.tags.Transition
+import com.google.common.truth.Truth
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runners.MethodSorters
+
+/**
+ * Contains tests for App Close assertions. To run this test:
+ * `atest FlickerLibTest:AppCloseAssertionsTest`
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class AppCloseAssertionsTest {
+    private val jsonByteArray = readTestFile("assertors/config.json")
+    private val assertions =
+        AssertionConfigParser.parseConfigFile(String(jsonByteArray))
+            .filter { it.transitionType == Transition.APP_CLOSE }
+
+    private val appCloseAssertor = TransitionAssertor(assertions) { }
+
+    @Test
+    fun testValidAppCloseTraces() {
+        val wmTrace = readWmTraceFromFile(
+            "assertors/appClose/WindowManagerTrace.winscope")
+        val layersTrace = readLayerTraceFromFile(
+            "assertors/appClose/SurfaceFlingerTrace.winscope")
+        val errorTrace = appCloseAssertor.analyze(VALID_APP_CLOSE_TAG, wmTrace, layersTrace)
+
+        Truth.assertThat(errorTrace).isEmpty()
+    }
+
+    @Test
+    fun testInvalidAppCloseTraces() {
+        val wmTrace = readWmTraceFromFile(
+            "assertors/appClose/WindowManagerInvalidTrace.winscope")
+        val layersTrace = readLayerTraceFromFile(
+            "assertors/appClose/SurfaceFlingerInvalidTrace.winscope")
+        val errorTrace = appCloseAssertor.analyze(INVALID_APP_CLOSE_TAG, wmTrace, layersTrace)
+
+        Truth.assertWithMessage("Number of entries with failures")
+            .that(errorTrace.entries)
+            .asList()
+            .hasSize(5)
+        val numErrors = errorTrace.entries.sumOf { it.errors.size }
+        Truth.assertWithMessage("Numer of errors")
+            .that(numErrors)
+            .isEqualTo(5)
+    }
+
+    companion object {
+        private val VALID_APP_CLOSE_TAG = Tag(1, Transition.APP_CLOSE, true,
+            windowToken = "b0e56e5")
+        private val INVALID_APP_CLOSE_TAG = Tag(2, Transition.APP_CLOSE, true,
+            windowToken = "31291d8")
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/service/assertors/AppLaunchAssertionsTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/service/assertors/AppLaunchAssertionsTest.kt
index ea6350d..b3fe178 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/service/assertors/AppLaunchAssertionsTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/service/assertors/AppLaunchAssertionsTest.kt
@@ -59,7 +59,7 @@
         val errorTrace = appLaunchAssertor.analyze(INVALID_APP_LAUNCH_TAG, wmTrace, layersTrace)
 
         Truth.assertThat(errorTrace).isNotEmpty()
-        Truth.assertThat(errorTrace.entries.size).isEqualTo(1)
+        Truth.assertThat(errorTrace.entries.size).isEqualTo(4)
     }
 
     companion object {
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/service/assertors/PipEnterAssertionsTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/service/assertors/PipEnterAssertionsTest.kt
new file mode 100644
index 0000000..293ba0a
--- /dev/null
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/service/assertors/PipEnterAssertionsTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 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.service.assertors
+
+import com.android.server.wm.flicker.readLayerTraceFromFile
+import com.android.server.wm.flicker.readTestFile
+import com.android.server.wm.flicker.readWmTraceFromFile
+import com.android.server.wm.traces.common.tags.Tag
+import com.android.server.wm.traces.common.tags.Transition
+import com.google.common.truth.Truth
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runners.MethodSorters
+
+/**
+ * Contains tests for Pip Enter assertions. To run this test:
+ * `atest FlickerLibTest:PipEnterAssertionsTest`
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class PipEnterAssertionsTest {
+    private val jsonByteArray = readTestFile("assertors/config.json")
+    private val assertions =
+        AssertionConfigParser.parseConfigFile(String(jsonByteArray))
+            .filter { it.transitionType == Transition.PIP_ENTER }
+
+    private val pipEnterAssertor = TransitionAssertor(assertions) { }
+
+    @Test
+    fun testValidPipEnterTraces() {
+        val wmTrace = readWmTraceFromFile(
+            "assertors/pip/enter/WindowManagerTrace.winscope")
+        val layersTrace = readLayerTraceFromFile(
+            "assertors/pip/enter/SurfaceFlingerTrace.winscope")
+        val errorTrace = pipEnterAssertor.analyze(VALID_PIP_ENTER_TAG, wmTrace, layersTrace)
+
+        Truth.assertThat(errorTrace).isEmpty()
+    }
+
+    @Test
+    fun testInvalidPipEnterTraces() {
+        val wmTrace = readWmTraceFromFile(
+            "assertors/pip/enter/WindowManagerInvalidTrace.winscope")
+        val layersTrace = readLayerTraceFromFile(
+            "assertors/pip/enter/SurfaceFlingerInvalidTrace.winscope")
+        val errorTrace = pipEnterAssertor.analyze(INVALID_PIP_ENTER_TAG, wmTrace, layersTrace)
+
+        Truth.assertThat(errorTrace).isNotEmpty()
+        Truth.assertThat(errorTrace.entries).asList().hasSize(3)
+    }
+
+    companion object {
+        private val VALID_PIP_ENTER_TAG = Tag(1, Transition.PIP_ENTER, true,
+            layerId = 620)
+        private val INVALID_PIP_ENTER_TAG = Tag(2, Transition.PIP_ENTER, true,
+            layerId = 188)
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/service/assertors/PipExitAssertionsTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/service/assertors/PipExitAssertionsTest.kt
new file mode 100644
index 0000000..f8391e9
--- /dev/null
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/service/assertors/PipExitAssertionsTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 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.service.assertors
+
+import com.android.server.wm.flicker.readLayerTraceFromFile
+import com.android.server.wm.flicker.readTestFile
+import com.android.server.wm.flicker.readWmTraceFromFile
+import com.android.server.wm.traces.common.tags.Tag
+import com.android.server.wm.traces.common.tags.Transition
+import com.google.common.truth.Truth
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runners.MethodSorters
+
+/**
+ * Contains tests for Pip Exit assertions. To run this test:
+ * `atest FlickerLibTest:PipExitAssertionsTest`
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class PipExitAssertionsTest {
+    private val jsonByteArray = readTestFile("assertors/config.json")
+    private val assertions =
+        AssertionConfigParser.parseConfigFile(String(jsonByteArray))
+            .filter { it.transitionType == Transition.PIP_EXIT }
+
+    private val pipExitAssertor = TransitionAssertor(assertions) { }
+
+    @Test
+    fun testValidPipExitTraces() {
+        val wmTrace = readWmTraceFromFile(
+            "assertors/pip/exit/WindowManagerTrace.winscope")
+        val layersTrace = readLayerTraceFromFile(
+            "assertors/pip/exit/SurfaceFlingerTrace.winscope")
+        val errorTrace = pipExitAssertor.analyze(VALID_PIP_EXIT_TAG, wmTrace, layersTrace)
+
+        Truth.assertThat(errorTrace).isEmpty()
+    }
+
+    @Test
+    fun testInvalidPipExitTraces() {
+        val wmTrace = readWmTraceFromFile(
+            "assertors/pip/exit/WindowManagerInvalidTrace.winscope")
+        val layersTrace = readLayerTraceFromFile(
+            "assertors/pip/exit/SurfaceFlingerInvalidTrace.winscope")
+        val errorTrace = pipExitAssertor.analyze(INVALID_PIP_EXIT_TAG, wmTrace, layersTrace)
+
+        Truth.assertThat(errorTrace).isNotEmpty()
+        Truth.assertThat(errorTrace.entries).asList().hasSize(2)
+        val allErrors = errorTrace.entries.flatMap { it.errors.toList() }
+        Truth.assertThat(allErrors).hasSize(2)
+    }
+
+    companion object {
+        private val VALID_PIP_EXIT_TAG = Tag(1, Transition.PIP_ENTER, true,
+            layerId = 180)
+        private val INVALID_PIP_EXIT_TAG = Tag(2, Transition.PIP_ENTER, true,
+            layerId = 188)
+    }
+}
\ No newline at end of file
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/service/assertors/RotationAssertionsTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/service/assertors/RotationAssertionsTest.kt
index d0f7ea0..0f8a30e 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/service/assertors/RotationAssertionsTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/service/assertors/RotationAssertionsTest.kt
@@ -21,7 +21,6 @@
 import com.android.server.wm.flicker.readWmTraceFromFile
 import com.android.server.wm.traces.common.tags.Tag
 import com.android.server.wm.traces.common.tags.Transition
-import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace
 import com.google.common.truth.Truth
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -41,7 +40,7 @@
     private val rotationAssertor = TransitionAssertor(assertions) { }
 
     @Test
-    fun testValidRotationWmTrace() {
+    fun testValidRotationTrace() {
         val wmTrace = readWmTraceFromFile("assertors/rotation/WindowManagerTrace.winscope")
         val layersTrace = readLayerTraceFromFile("assertors/rotation/SurfaceFlingerTrace.winscope")
         val errorTrace = rotationAssertor.analyze(ROTATION_TAG, wmTrace, layersTrace)
@@ -49,26 +48,7 @@
         Truth.assertThat(errorTrace).isEmpty()
     }
 
-    @Test
-    fun testValidRotationLayersTrace() {
-        val trace = readLayerTraceFromFile("assertors/rotation/SurfaceFlingerTrace.winscope")
-        val errorTrace = rotationAssertor.analyze(ROTATION_TAG, EMPTY_WM_TRACE, trace)
-
-        Truth.assertThat(errorTrace).isEmpty()
-    }
-
-    @Test
-    fun testInvalidRotationLayersTrace() {
-        val trace = readLayerTraceFromFile(
-            "assertors/rotation/SurfaceFlingerInvalidTrace.winscope")
-        val errorTrace = rotationAssertor.analyze(ROTATION_TAG, EMPTY_WM_TRACE, trace)
-
-        Truth.assertThat(errorTrace).isNotEmpty()
-        Truth.assertThat(errorTrace.entries.size).isEqualTo(1)
-    }
-
     companion object {
-        private val EMPTY_WM_TRACE = WindowManagerTrace(emptyArray(), source = "")
         private val ROTATION_TAG = Tag(1, Transition.ROTATION, true)
     }
 }
\ No newline at end of file
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/windowmanager/WindowManagerStateHelperTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/windowmanager/WindowManagerStateHelperTest.kt
index dd6f5c2..84d467b 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/windowmanager/WindowManagerStateHelperTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/windowmanager/WindowManagerStateHelperTest.kt
@@ -16,22 +16,22 @@
 
 package com.android.server.wm.flicker.windowmanager
 
-import android.view.Display
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.readWmTraceFromDumpFile
 import com.android.server.wm.flicker.readWmTraceFromFile
 import com.android.server.wm.flicker.traces.windowmanager.WindowManagerStateSubject
-import com.android.server.wm.traces.common.Buffer
+import com.android.server.wm.traces.common.ActiveBuffer
 import com.android.server.wm.traces.common.Color
 import com.android.server.wm.traces.common.DeviceStateDump
 import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.Matrix33
 import com.android.server.wm.traces.common.Rect
 import com.android.server.wm.traces.common.RectF
-import com.android.server.wm.traces.common.Region
+import com.android.server.wm.traces.common.region.Region
 import com.android.server.wm.traces.common.layers.Layer
-import com.android.server.wm.traces.common.layers.LayerTraceEntry
+import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
 import com.android.server.wm.traces.common.layers.LayerTraceEntryBuilder
 import com.android.server.wm.traces.common.layers.Transform
 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
@@ -53,7 +53,7 @@
         /**
          * Predicate to supply a new UI information
          */
-        deviceDumpSupplier: () -> DeviceStateDump<WindowManagerState, LayerTraceEntry>,
+        deviceDumpSupplier: () -> DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>,
         numRetries: Int = 5,
         retryIntervalMs: Long = 500L
     ) : WindowManagerStateHelper(InstrumentationRegistry.getInstrumentation(),
@@ -61,7 +61,9 @@
         var wmState: WindowManagerState = _wmState
             private set
 
-        override fun updateCurrState(value: DeviceStateDump<WindowManagerState, LayerTraceEntry>) {
+        override fun updateCurrState(
+            value: DeviceStateDump<WindowManagerState, BaseLayerTraceEntry>
+        ) {
             wmState = value.wmState
         }
     }
@@ -73,7 +75,7 @@
         "com.android.server.wm.flicker.testapp/.SimpleActivity")
 
     private fun createImaginaryLayer(name: String, index: Int, id: Int, parentId: Int): Layer {
-        val transform = Transform(0, Transform.Matrix(0f, 0f, 0f, 0f, 0f, 0f))
+        val transform = Transform(0, Matrix33.EMPTY)
         val rect = RectF(
             left = index.toFloat(),
             top = index.toFloat(),
@@ -85,8 +87,8 @@
             id,
             parentId,
             z = 0,
-            visibleRegion = Region(rect.toRect()),
-            activeBuffer = Buffer(1, 1, 1, 1),
+            visibleRegion = Region.from(rect.toRect()),
+            activeBuffer = ActiveBuffer(1, 1, 1, 1),
             flags = 0,
             bounds = rect,
             color = Color(0f, 0f, 0f, 1f),
@@ -106,7 +108,8 @@
             crop = rect.toRect(),
             backgroundBlurRadius = 0,
             isRelativeOf = false,
-            zOrderRelativeOfId = -1
+            zOrderRelativeOfId = -1,
+            stackId = 0
         )
     }
 
@@ -124,7 +127,7 @@
 
     private fun WindowManagerTrace.asSupplier(
         startingTimestamp: Long = 0
-    ): () -> DeviceStateDump<WindowManagerState, LayerTraceEntry> {
+    ): () -> DeviceStateDump<WindowManagerState, BaseLayerTraceEntry> {
         val iterator = this.dropWhile { it.timestamp < startingTimestamp }.iterator()
         return {
             if (iterator.hasNext()) {
@@ -135,10 +138,10 @@
                 if (wmState.inputMethodWindowState?.isSurfaceShown == true) {
                     layerList.add(FlickerComponentName.IME)
                 }
-                val layerTraceEntry = LayerTraceEntryBuilder(timestamp = 0,
+                val ILayerTraceEntry = LayerTraceEntryBuilder(timestamp = 0,
                     displays = emptyArray(),
                     layers = createImaginaryVisibleLayers(layerList)).build()
-                DeviceStateDump(wmState, layerTraceEntry)
+                DeviceStateDump(wmState, ILayerTraceEntry)
             } else {
                 error("Reached the end of the trace")
             }
@@ -157,7 +160,7 @@
                 .isNonAppWindowVisible(FlickerComponentName.IME)
             error("IME state should not be available")
         } catch (e: AssertionError) {
-            helper.waitImeShown(Display.DEFAULT_DISPLAY)
+            helper.waitImeShown()
             WindowManagerStateSubject
                 .assertThat(helper.wmState)
                 .isNonAppWindowVisible(FlickerComponentName.IME)
@@ -214,7 +217,12 @@
                 .containsAppWindow(simpleAppComponentName)
             error("SimpleActivity window should not exist in the start of the trace")
         } catch (e: AssertionError) {
+            // nothing to do
+        }
+
+        try {
             helper.waitForVisibleWindow(simpleAppComponentName)
+        } catch (e: IllegalArgumentException) {
             WindowManagerStateSubject
                 .assertThat(helper.wmState)
                 .notContains(simpleAppComponentName)
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/windowmanager/WindowManagerStateSubjectTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/windowmanager/WindowManagerStateSubjectTest.kt
index 0fe73db..43bea61 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/windowmanager/WindowManagerStateSubjectTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/windowmanager/WindowManagerStateSubjectTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.windowmanager
 
-import android.graphics.Region
 import com.android.server.wm.flicker.CHROME_SPLASH_SCREEN_COMPONENT
 import com.android.server.wm.flicker.IMAGINARY_COMPONENT
 import com.android.server.wm.flicker.LAUNCHER_COMPONENT
@@ -26,19 +25,26 @@
 import com.android.server.wm.flicker.SHELL_SPLIT_SCREEN_SECONDARY_COMPONENT
 import com.android.server.wm.flicker.WALLPAPER_COMPONENT
 import com.android.server.wm.flicker.assertFailure
+import com.android.server.wm.flicker.assertThatErrorContainsDebugInfo
 import com.android.server.wm.flicker.assertThrows
 import com.android.server.wm.flicker.assertions.FlickerSubject
+import com.android.server.wm.flicker.readWmTraceFromDumpFile
 import com.android.server.wm.flicker.readWmTraceFromFile
 import com.android.server.wm.flicker.traces.FlickerSubjectException
 import com.android.server.wm.flicker.traces.windowmanager.WindowManagerStateSubject
 import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject.Companion.assertThat
 import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.region.Region
+import com.android.server.wm.traces.common.windowmanager.WindowManagerState
 import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace
+import com.android.server.wm.traces.common.windowmanager.windows.ConfigurationContainer
+import com.android.server.wm.traces.common.windowmanager.windows.KeyguardControllerState
+import com.android.server.wm.traces.common.windowmanager.windows.RootWindowContainer
+import com.android.server.wm.traces.common.windowmanager.windows.WindowContainer
 import com.google.common.truth.Truth
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runners.MethodSorters
-import java.lang.AssertionError
 
 /**
  * Contains [WindowManagerStateSubject] tests.
@@ -52,20 +58,17 @@
     // The first frame where the chrome splash screen is shown
     private val traceFirstChromeFlashScreenTimestamp = 9215551505798
     // The bounds of the display used to generate the trace [trace]
-    private val displayBounds = Region(0, 0, 1440, 2960)
+    private val displayBounds = Region.from(0, 0, 1440, 2960)
     // The region covered by the status bar in the trace
-    private val statusBarRegion = Region(0, 0, 1440, 171)
+    private val statusBarRegion = Region.from(0, 0, 1440, 171)
 
     @Test
     fun exceptionContainsDebugInfo() {
         val error = assertThrows(AssertionError::class.java) {
-            assertThat(trace).first().frameRegion(IMAGINARY_COMPONENT)
+            assertThat(trace).first().visibleRegion(IMAGINARY_COMPONENT)
         }
+        assertThatErrorContainsDebugInfo(error)
         Truth.assertThat(error).hasMessageThat().contains(IMAGINARY_COMPONENT.className)
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace file")
-        Truth.assertThat(error).hasMessageThat().contains("Entry")
         Truth.assertThat(error).hasMessageThat().contains(FlickerSubject.ASSERTION_TAG)
     }
 
@@ -99,9 +102,9 @@
         val entry = assertThat(trace)
             .entry(traceFirstFrameTimestamp)
 
-        entry.frameRegion(FlickerComponentName.STATUS_BAR)
+        entry.visibleRegion(FlickerComponentName.STATUS_BAR)
                 .coversAtLeast(statusBarRegion)
-        entry.frameRegion(LAUNCHER_COMPONENT)
+        entry.visibleRegion(LAUNCHER_COMPONENT)
             .coversAtLeast(displayBounds)
     }
 
@@ -109,24 +112,24 @@
     fun canDetectWindowCoversAtLeastRegion_smallerRegion() {
         val entry = assertThat(trace)
             .entry(traceFirstFrameTimestamp)
-        entry.frameRegion(FlickerComponentName.STATUS_BAR)
-                .coversAtLeast(Region(0, 0, 100, 100))
-        entry.frameRegion(LAUNCHER_COMPONENT)
-            .coversAtLeast(Region(0, 0, 100, 100))
+        entry.visibleRegion(FlickerComponentName.STATUS_BAR)
+                .coversAtLeast(Region.from(0, 0, 100, 100))
+        entry.visibleRegion(LAUNCHER_COMPONENT)
+            .coversAtLeast(Region.from(0, 0, 100, 100))
     }
 
     @Test
     fun canDetectWindowCoversAtLeastRegion_largerRegion() {
         val subject = assertThat(trace).entry(traceFirstFrameTimestamp)
         var failure = assertThrows(FlickerSubjectException::class.java) {
-            subject.frameRegion(FlickerComponentName.STATUS_BAR)
-                    .coversAtLeast(Region(0, 0, 1441, 171))
+            subject.visibleRegion(FlickerComponentName.STATUS_BAR)
+                    .coversAtLeast(Region.from(0, 0, 1441, 171))
         }
         assertFailure(failure).factValue("Uncovered region").contains("SkRegion((1440,0,1441,171))")
 
         failure = assertThrows(FlickerSubjectException::class.java) {
-            subject.frameRegion(LAUNCHER_COMPONENT)
-                .coversAtLeast(Region(0, 0, 1440, 2961))
+            subject.visibleRegion(LAUNCHER_COMPONENT)
+                .coversAtLeast(Region.from(0, 0, 1440, 2961))
         }
         assertFailure(failure).factValue("Uncovered region")
             .contains("SkRegion((0,2960,1440,2961))")
@@ -137,9 +140,9 @@
         val entry = assertThat(trace)
                 .entry(traceFirstFrameTimestamp)
 
-        entry.frameRegion(FlickerComponentName.STATUS_BAR)
+        entry.visibleRegion(FlickerComponentName.STATUS_BAR)
                 .coversExactly(statusBarRegion)
-        entry.frameRegion(LAUNCHER_COMPONENT)
+        entry.visibleRegion(LAUNCHER_COMPONENT)
                 .coversExactly(displayBounds)
     }
 
@@ -147,15 +150,15 @@
     fun canDetectWindowCoversExactlyRegion_smallerRegion() {
         val subject = assertThat(trace).entry(traceFirstFrameTimestamp)
         var failure = assertThrows(FlickerSubjectException::class.java) {
-            subject.frameRegion(FlickerComponentName.STATUS_BAR)
-                    .coversAtMost(Region(0, 0, 100, 100))
+            subject.visibleRegion(FlickerComponentName.STATUS_BAR)
+                    .coversAtMost(Region.from(0, 0, 100, 100))
         }
         assertFailure(failure).factValue("Out-of-bounds region")
                 .contains("SkRegion((100,0,1440,100)(0,100,1440,171))")
 
         failure = assertThrows(FlickerSubjectException::class.java) {
-            subject.frameRegion(LAUNCHER_COMPONENT)
-                    .coversAtMost(Region(0, 0, 100, 100))
+            subject.visibleRegion(LAUNCHER_COMPONENT)
+                    .coversAtMost(Region.from(0, 0, 100, 100))
         }
         assertFailure(failure).factValue("Out-of-bounds region")
                 .contains("SkRegion((100,0,1440,100)(0,100,1440,2960))")
@@ -165,14 +168,14 @@
     fun canDetectWindowCoversExactlyRegion_largerRegion() {
         val subject = assertThat(trace).entry(traceFirstFrameTimestamp)
         var failure = assertThrows(FlickerSubjectException::class.java) {
-            subject.frameRegion(FlickerComponentName.STATUS_BAR)
-                    .coversAtLeast(Region(0, 0, 1441, 171))
+            subject.visibleRegion(FlickerComponentName.STATUS_BAR)
+                    .coversAtLeast(Region.from(0, 0, 1441, 171))
         }
         assertFailure(failure).factValue("Uncovered region").contains("SkRegion((1440,0,1441,171))")
 
         failure = assertThrows(FlickerSubjectException::class.java) {
-            subject.frameRegion(LAUNCHER_COMPONENT)
-                    .coversAtLeast(Region(0, 0, 1440, 2961))
+            subject.visibleRegion(LAUNCHER_COMPONENT)
+                    .coversAtLeast(Region.from(0, 0, 1440, 2961))
         }
         assertFailure(failure).factValue("Uncovered region")
                 .contains("SkRegion((0,2960,1440,2961))")
@@ -182,9 +185,9 @@
     fun canDetectWindowCoversAtMostRegion_extactSize() {
         val entry = assertThat(trace)
             .entry(traceFirstFrameTimestamp)
-        entry.frameRegion(FlickerComponentName.STATUS_BAR)
+        entry.visibleRegion(FlickerComponentName.STATUS_BAR)
                 .coversAtMost(statusBarRegion)
-        entry.frameRegion(LAUNCHER_COMPONENT)
+        entry.visibleRegion(LAUNCHER_COMPONENT)
             .coversAtMost(displayBounds)
     }
 
@@ -192,15 +195,15 @@
     fun canDetectWindowCoversAtMostRegion_smallerRegion() {
         val subject = assertThat(trace).entry(traceFirstFrameTimestamp)
         var failure = assertThrows(FlickerSubjectException::class.java) {
-            subject.frameRegion(FlickerComponentName.STATUS_BAR)
-                    .coversAtMost(Region(0, 0, 100, 100))
+            subject.visibleRegion(FlickerComponentName.STATUS_BAR)
+                    .coversAtMost(Region.from(0, 0, 100, 100))
         }
         assertFailure(failure).factValue("Out-of-bounds region")
             .contains("SkRegion((100,0,1440,100)(0,100,1440,171))")
 
         failure = assertThrows(FlickerSubjectException::class.java) {
-            subject.frameRegion(LAUNCHER_COMPONENT)
-                .coversAtMost(Region(0, 0, 100, 100))
+            subject.visibleRegion(LAUNCHER_COMPONENT)
+                .coversAtMost(Region.from(0, 0, 100, 100))
         }
         assertFailure(failure).factValue("Out-of-bounds region")
             .contains("SkRegion((100,0,1440,100)(0,100,1440,2960))")
@@ -211,10 +214,10 @@
         val entry = assertThat(trace)
             .entry(traceFirstFrameTimestamp)
 
-        entry.frameRegion(FlickerComponentName.STATUS_BAR)
-                .coversAtMost(Region(0, 0, 1441, 171))
-        entry.frameRegion(LAUNCHER_COMPONENT)
-            .coversAtMost(Region(0, 0, 1440, 2961))
+        entry.visibleRegion(FlickerComponentName.STATUS_BAR)
+                .coversAtMost(Region.from(0, 0, 1441, 171))
+        entry.visibleRegion(LAUNCHER_COMPONENT)
+            .coversAtMost(Region.from(0, 0, 1440, 2961))
     }
 
     @Test
@@ -320,4 +323,71 @@
         lastEntry.isAppWindowVisible(SHELL_SPLIT_SCREEN_PRIMARY_COMPONENT)
         lastEntry.isAppWindowVisible(SHELL_SPLIT_SCREEN_SECONDARY_COMPONENT)
     }
-}
\ No newline at end of file
+
+    @Test
+    fun canHandleNoSubjects() {
+        val emptyRootContainer = RootWindowContainer(
+            WindowContainer(
+                title = "root",
+                token = "",
+                orientation = 0,
+                layerId = 0,
+                _isVisible = true,
+                children = emptyArray(),
+                configurationContainer = ConfigurationContainer(null, null, null)
+            )
+        )
+        val noWindowsState = WindowManagerState(
+            where = "",
+            policy = null,
+            focusedApp = "",
+            focusedDisplayId = 0,
+            focusedWindow = "",
+            inputMethodWindowAppToken = "",
+            isHomeRecentsComponent = false,
+            isDisplayFrozen = false,
+            pendingActivities = emptyArray(),
+            root = emptyRootContainer,
+            keyguardControllerState = KeyguardControllerState(
+                isAodShowing = false,
+                isKeyguardShowing = false,
+                keyguardOccludedStates = mapOf()
+            )
+        )
+
+        val mockComponent = FlickerComponentName("", "Mock")
+
+        val failure = assertThrows(FlickerSubjectException::class.java) {
+            WindowManagerStateSubject
+                .assertThat(noWindowsState).isAppWindowOnTop(mockComponent)
+        }
+        assertFailure(failure).hasMessageThat().contains("No visible app windows found")
+    }
+
+    @Test
+    fun canDetectNoVisibleAppWindows() {
+        val trace = readWmTraceFromFile("wm_trace_unlock.pb")
+        val firstEntry = assertThat(trace).first()
+        firstEntry.hasNoVisibleAppWindow()
+    }
+
+    @Test
+    fun canDetectHasVisibleAppWindows() {
+        val trace = readWmTraceFromFile("wm_trace_unlock.pb")
+        val lastEntry = assertThat(trace).last()
+        val failure = assertThrows(FlickerSubjectException::class.java) {
+            lastEntry.hasNoVisibleAppWindow()
+        }
+        assertFailure(failure).hasMessageThat().contains("Found visible windows")
+    }
+
+    @Test
+    fun canDetectTaskFragment() {
+        // Verify if parser can read a dump file with 2 TaskFragments showed side-by-side.
+        val trace = readWmTraceFromDumpFile("wm_trace_taskfragment.winscope")
+        // There's only one entry in dump file.
+        val entry = assertThat(trace).first()
+        // Verify there's exact 2 TaskFragments in window hierarchy.
+        Truth.assertThat(entry.wmState.taskFragments.size).isEqualTo(2)
+    }
+}
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/windowmanager/WindowManagerTraceSubjectTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/windowmanager/WindowManagerTraceSubjectTest.kt
index 60632fa..3c640b0 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/windowmanager/WindowManagerTraceSubjectTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/windowmanager/WindowManagerTraceSubjectTest.kt
@@ -24,6 +24,7 @@
 import com.android.server.wm.flicker.SCREEN_DECOR_COMPONENT
 import com.android.server.wm.flicker.WALLPAPER_COMPONENT
 import com.android.server.wm.flicker.assertFailure
+import com.android.server.wm.flicker.assertThatErrorContainsDebugInfo
 import com.android.server.wm.flicker.assertThrows
 import com.android.server.wm.flicker.readWmTraceFromFile
 import com.android.server.wm.flicker.traces.FlickerSubjectException
@@ -199,8 +200,36 @@
         val error = assertThrows(AssertionError::class.java) {
             assertThat(chromeTrace).isEmpty()
         }
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace file")
+        assertThatErrorContainsDebugInfo(error, withBlameEntry = false)
+    }
+
+    @Test
+    fun testCanDetectSnapshotStartingWindow() {
+        val trace = readWmTraceFromFile("quick_switch_to_app_killed_in_background_trace.pb")
+        val app1 = FlickerComponentName("com.android.server.wm.flicker.testapp",
+            "com.android.server.wm.flicker.testapp.ImeActivity")
+        val app2 = FlickerComponentName("com.android.server.wm.flicker.testapp",
+            "com.android.server.wm.flicker.testapp.SimpleActivity")
+        assertThat(trace).isAppWindowVisible(app1)
+            .then()
+            .isAppSnapshotStartingWindowVisibleFor(app2, isOptional = true)
+            .then()
+            .isAppWindowVisible(app2)
+            .then()
+            .isAppSnapshotStartingWindowVisibleFor(app1, isOptional = true)
+            .then()
+            .isAppWindowVisible(app1)
+            .forAllEntries()
+
+        val failure = assertThrows(FlickerSubjectException::class.java) {
+            assertThat(trace)
+                .isAppWindowVisible(app1)
+                .then()
+                .isAppWindowVisible(app2)
+                .then()
+                .isAppWindowVisible(app1)
+                .forAllEntries()
+        }
+        assertFailure(failure).hasMessageThat().contains("Is Invisible")
     }
 }
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/windowmanager/WindowStateSubjectTest.kt b/libraries/flicker/test/src/com/android/server/wm/flicker/windowmanager/WindowStateSubjectTest.kt
index b7ab535..10bc7aa 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/windowmanager/WindowStateSubjectTest.kt
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/windowmanager/WindowStateSubjectTest.kt
@@ -17,6 +17,7 @@
 package com.android.server.wm.flicker.windowmanager
 
 import com.android.server.wm.flicker.IMAGINARY_COMPONENT
+import com.android.server.wm.flicker.assertThatErrorContainsDebugInfo
 import com.android.server.wm.flicker.assertThrows
 import com.android.server.wm.flicker.readWmTraceFromFile
 import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject
@@ -35,13 +36,8 @@
                     .windowState(IMAGINARY_COMPONENT.className)
                     .exists()
         }
+        assertThatErrorContainsDebugInfo(error)
         Truth.assertThat(error).hasMessageThat().contains(IMAGINARY_COMPONENT.className)
-        Truth.assertThat(error).hasMessageThat().contains("What?")
-        Truth.assertThat(error).hasMessageThat().contains("Where?")
-        Truth.assertThat(error).hasMessageThat().contains("Entry")
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace file")
         Truth.assertThat(error).hasMessageThat().contains("Window title")
     }
 
@@ -54,12 +50,6 @@
                     .first()
                     .doesNotExist()
         }
-        Truth.assertThat(error).hasMessageThat().contains("What?")
-        Truth.assertThat(error).hasMessageThat().contains("Where?")
-        Truth.assertThat(error).hasMessageThat().contains("Entry")
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace start")
-        Truth.assertThat(error).hasMessageThat().contains("Trace file")
-        Truth.assertThat(error).hasMessageThat().contains("Entry")
+        assertThatErrorContainsDebugInfo(error)
     }
 }
\ No newline at end of file
diff --git a/libraries/health/rules/Android.bp b/libraries/health/rules/Android.bp
index 8f889a8..a472fed 100644
--- a/libraries/health/rules/Android.bp
+++ b/libraries/health/rules/Android.bp
@@ -18,7 +18,6 @@
 
 java_library {
     name: "platform-test-rules",
-    sdk_version: "test_current",
     static_libs: [
         "androidx.test.runner",
         "androidx.test.uiautomator",
@@ -31,8 +30,12 @@
         "launcher-aosp-tapl",
         "flickerlib",
         "statsd-helper",
+        "health-testing-utils",
     ],
-    srcs: ["src/**/*.java"],
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
 }
 
 java_library {
@@ -49,6 +52,7 @@
         "package-helper",
         "statsd-helper",
         "launcher-aosp-tapl",
+        "health-testing-utils",
     ],
     srcs: ["src/**/*.java"],
     exclude_srcs: [
diff --git a/libraries/health/rules/TEST_MAPPING b/libraries/health/rules/TEST_MAPPING
new file mode 100644
index 0000000..be870e3
--- /dev/null
+++ b/libraries/health/rules/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "vendor/google_testing/integration/tests/scenarios/src/android/platform/test/scenario/sysui"
+    }
+  ]
+}
diff --git a/libraries/health/rules/src/android/platform/test/rule/ArtifactSaver.java b/libraries/health/rules/src/android/platform/test/rule/ArtifactSaver.java
new file mode 100644
index 0000000..3b456fc
--- /dev/null
+++ b/libraries/health/rules/src/android/platform/test/rule/ArtifactSaver.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.platform.test.rule;
+
+import android.os.ParcelFileDescriptor;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
+
+import org.junit.runner.Description;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/** Utilities for producing test artifacts. */
+public class ArtifactSaver {
+    private static final String TAG = ArtifactSaver.class.getSimpleName();
+
+    // Presubmit tests have a time limit. We are not taking expensive bugreports from presubmits.
+    private static boolean sShouldTakeBugreport = !PresubmitRule.runningInPresubmit();
+
+    public static File artifactFile(String fileName) {
+        return new File(
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getFilesDir(),
+                fileName);
+    }
+
+    static File artifactFile(Description description, String prefix, String ext) {
+        return artifactFile(
+                prefix
+                        + "-"
+                        + description.getTestClass().getSimpleName()
+                        + "."
+                        + description.getMethodName()
+                        + "."
+                        + ext);
+    }
+
+    public static void onError(Description description, Throwable e) {
+        final UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        final File screenshot = artifactFile(description, "TestScreenshot", "png");
+        final File hierarchy = artifactFile(description, "Hierarchy", "zip");
+
+        device.takeScreenshot(screenshot);
+
+        // Dump accessibility hierarchy
+        try {
+            device.dumpWindowHierarchy(artifactFile(description, "AccessibilityHierarchy", "uix"));
+        } catch (Exception ex) {
+            android.util.Log.e(TAG, "Failed to save accessibility hierarchy", ex);
+        }
+
+        // Dump window hierarchy
+        try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(hierarchy))) {
+            out.putNextEntry(new ZipEntry("bugreport.txt"));
+            dumpCommandAndOutput("dumpsys window windows", out);
+            dumpCommandAndOutput("dumpsys package", out);
+            out.closeEntry();
+
+            out.putNextEntry(new ZipEntry("visible_windows.zip"));
+            dumpCommandOutput("cmd window dump-visible-window-views", out);
+            out.closeEntry();
+        } catch (IOException ex) {
+        }
+
+        android.util.Log.e(
+                TAG,
+                "Failed test "
+                        + description.getMethodName()
+                        + ",\nscreenshot will be saved to "
+                        + screenshot
+                        + ",\nUI dump at: "
+                        + hierarchy
+                        + " (use go/web-hv to open the dump file)",
+                e);
+
+        // Dump bugreport
+        if (sShouldTakeBugreport && FailureWatcher.getSystemAnomalyMessage(device) != null) {
+            // Taking bugreport is expensive, we should do this only once.
+            sShouldTakeBugreport = false;
+            dumpCommandOutput("bugreportz -s", artifactFile(description, "Bugreport", "zip"));
+        }
+    }
+
+    private static void dumpCommandAndOutput(String cmd, OutputStream out) throws IOException {
+        out.write(("\n\n" + cmd + "\n").getBytes());
+        dumpCommandOutput(cmd, out);
+    }
+
+    public static void dumpCommandOutput(String cmd, File out) {
+        try (BufferedOutputStream buffered = new BufferedOutputStream(new FileOutputStream(out))) {
+            dumpCommandOutput(cmd, buffered);
+        } catch (IOException ex) {
+        }
+    }
+
+    private static void dumpCommandOutput(String cmd, OutputStream out) throws IOException {
+        try (ParcelFileDescriptor.AutoCloseInputStream in =
+                new ParcelFileDescriptor.AutoCloseInputStream(
+                        InstrumentationRegistry.getInstrumentation()
+                                .getUiAutomation()
+                                .executeShellCommand(cmd))) {
+            android.os.FileUtils.copy(in, out);
+        }
+    }
+}
diff --git a/libraries/health/rules/src/android/platform/test/rule/BaseOrientationRule.kt b/libraries/health/rules/src/android/platform/test/rule/BaseOrientationRule.kt
new file mode 100644
index 0000000..a64773c
--- /dev/null
+++ b/libraries/health/rules/src/android/platform/test/rule/BaseOrientationRule.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.rule
+
+import android.graphics.Rect
+import android.os.RemoteException
+import android.platform.test.rule.Orientation.LANDSCAPE
+import android.platform.test.rule.Orientation.PORTRAIT
+import android.platform.test.util.HealthTestingUtils
+import androidx.test.uiautomator.By
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.runner.Description
+
+/** Locks the orientation in Landscape before starting the test, and goes back to natural after. */
+class LandscapeOrientationRule : BaseOrientationRule(LANDSCAPE)
+
+/** Locks the orientation in Portrait before starting the test, and goes back to natural after. */
+class PortraitOrientationRule : BaseOrientationRule(PORTRAIT)
+
+private enum class Orientation {
+    LANDSCAPE,
+    PORTRAIT,
+}
+
+private val Rect.orientation: Orientation
+    get() = if (width() > height()) { LANDSCAPE } else { PORTRAIT }
+
+/** Uses launcher rect to decide which rotation to apply to match [expectedOrientation]. */
+sealed class BaseOrientationRule private constructor(private val expectedOrientation: Orientation) :
+    TestWatcher() {
+
+    private val mLauncher = LauncherInstrumentation()
+
+    override fun starting(description: Description) {
+        try {
+            uiDevice.pressHome()
+            mLauncher.setEnableRotation(true)
+            if (launcherVisibleBounds?.orientation == expectedOrientation) {
+                // In the case of tablets, the default orientation might be landscape already.
+                return
+            }
+            uiDevice.setOrientationLeft()
+            HealthTestingUtils.waitForNullDiag {
+                when (launcherVisibleBounds?.orientation) {
+                    expectedOrientation -> null // No error == success.
+                    null -> "Launcher is not found"
+                    else -> "Visible orientation is not ${expectedOrientation.name}"
+                }
+            }
+        } catch (e: RemoteException) {
+            throw RuntimeException(
+                "RemoteException when forcing ${expectedOrientation.name} rotation on the device",
+                e)
+        }
+    }
+
+    override fun finished(description: Description) {
+        try {
+            uiDevice.setOrientationNatural()
+            mLauncher.setEnableRotation(false)
+            uiDevice.unfreezeRotation()
+        } catch (e: RemoteException) {
+            val message = "RemoteException when restoring natural rotation of the device"
+            throw RuntimeException(message, e)
+        }
+    }
+
+    private val launcherVisibleBounds: Rect?
+        get() {
+            val launcher =
+                uiDevice.findObject(By.res("android", "content").pkg(uiDevice.launcherPackageName))
+            return launcher?.visibleBounds
+        }
+}
diff --git a/libraries/health/rules/src/android/platform/test/rule/CrashDetector.java b/libraries/health/rules/src/android/platform/test/rule/CrashDetector.java
new file mode 100644
index 0000000..f64867f
--- /dev/null
+++ b/libraries/health/rules/src/android/platform/test/rule/CrashDetector.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.rule;
+
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.IOException;
+
+/** A rule that fails if the specified package crashed during the test. */
+public class CrashDetector implements TestRule {
+    private static final String TAG = CrashDetector.class.getSimpleName();
+    private String mExpectedPid;
+    private final UiDevice mDevice;
+    private final String mPackageName;
+
+    public CrashDetector(String packageName) {
+        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mPackageName = packageName;
+    }
+
+    private String getPackagePid() throws IOException {
+        return mDevice.executeShellCommand("pidof " + mPackageName).replaceAll("\\s", "");
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                mExpectedPid = getPackagePid();
+                Log.d(TAG, "Enter, PID=" + mExpectedPid);
+                try {
+                    base.evaluate();
+                } catch (Throwable t) {
+                    detectCrash(t);
+                    throw t;
+                }
+                detectCrash(null);
+            }
+
+            private void detectCrash(Throwable cause) throws IOException {
+                final String newPid = getPackagePid();
+                if (!mExpectedPid.equals(newPid)) {
+                    throw new AssertionError(
+                            mPackageName
+                                    + " crashed, old pid= "
+                                    + mExpectedPid
+                                    + " , new pid="
+                                    + newPid,
+                            cause);
+                }
+            }
+        };
+    }
+
+    public void onLegitimatePackageRestart() {
+        try {
+            mExpectedPid = getPackagePid();
+            Log.d(TAG, "onLegitimatePackageRestart, PID=" + mExpectedPid);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/libraries/health/rules/src/android/platform/test/rule/DeviceTypeRule.kt b/libraries/health/rules/src/android/platform/test/rule/DeviceTypeRule.kt
new file mode 100644
index 0000000..306fbff
--- /dev/null
+++ b/libraries/health/rules/src/android/platform/test/rule/DeviceTypeRule.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.rule
+
+import android.app.Instrumentation
+import android.os.Build
+import android.support.test.uiautomator.UiDevice
+import androidx.test.InstrumentationRegistry
+import com.android.internal.R
+import kotlin.annotation.AnnotationRetention.RUNTIME
+import kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS
+import kotlin.annotation.AnnotationTarget.CLASS
+import org.junit.AssumptionViolatedException
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * Rule that allow some tests to be executed only on [FoldableOnly] or [LargeScreenOnly] devices.
+ */
+class DeviceTypeRule : TestRule {
+
+    private val isFoldable = isFoldable()
+    private val isLargeScreen = isLargeScreen()
+
+    override fun apply(base: Statement, description: Description): Statement {
+        if (description.getAnnotation(LargeScreenOnly::class.java) != null && !isLargeScreen) {
+            return createAssumptionViolatedStatement(
+                "Skipping test on ${Build.PRODUCT} as it doesn't have a large screen.")
+        }
+
+        if (description.getAnnotation(FoldableOnly::class.java) != null && !isFoldable) {
+            return createAssumptionViolatedStatement(
+                "Skipping test on ${Build.PRODUCT} as it is not a foldable.")
+        }
+
+        return base
+    }
+}
+
+private fun isFoldable(): Boolean {
+    return getInstrumentation()
+        .targetContext
+        .resources
+        .getIntArray(R.array.config_foldedDeviceStates)
+        .isNotEmpty()
+}
+
+private fun isLargeScreen(): Boolean {
+    val sizeDp = getUiDevice().displaySizeDp
+    return sizeDp.x >= LARGE_SCREEN_DP_THRESHOLD && sizeDp.y >= LARGE_SCREEN_DP_THRESHOLD
+}
+
+private fun createAssumptionViolatedStatement(message: String) =
+    object : Statement() {
+        override fun evaluate() {
+            throw AssumptionViolatedException(message)
+        }
+    }
+
+private fun getInstrumentation(): Instrumentation = InstrumentationRegistry.getInstrumentation()
+
+private fun getUiDevice(): UiDevice = UiDevice.getInstance(getInstrumentation())
+
+private const val LARGE_SCREEN_DP_THRESHOLD = 600
+
+@Retention(RUNTIME) @Target(ANNOTATION_CLASS, CLASS) annotation class LargeScreenOnly
+
+@Retention(RUNTIME) @Target(ANNOTATION_CLASS, CLASS) annotation class FoldableOnly
diff --git a/libraries/health/rules/src/android/platform/test/rule/FailureWatcher.java b/libraries/health/rules/src/android/platform/test/rule/FailureWatcher.java
index 3b46815..bc3ab63 100644
--- a/libraries/health/rules/src/android/platform/test/rule/FailureWatcher.java
+++ b/libraries/health/rules/src/android/platform/test/rule/FailureWatcher.java
@@ -15,25 +15,18 @@
  */
 package android.platform.test.rule;
 
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
-import android.os.FileUtils;
-import android.os.ParcelFileDescriptor.AutoCloseInputStream;
-import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
 import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
 
 import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
 
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
-
-/** A rule that generates debug artifact files for failed tests. */
+/** A rule that performs post-processing of test failures. */
 public class FailureWatcher extends TestWatcher {
     private static final String TAG = "FailureWatcher";
     private final UiDevice mDevice;
@@ -43,70 +36,52 @@
     }
 
     @Override
-    protected void failed(Throwable e, Description description) {
-        onError(mDevice, description, e);
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                try {
+                    FailureWatcher.super.apply(base, description).evaluate();
+                } catch (Throwable t) {
+                    final String systemAnomalyMessage = getSystemAnomalyMessage(mDevice);
+                    if (systemAnomalyMessage != null) {
+                        throw new AssertionError(systemAnomalyMessage, t);
+                    } else {
+                        throw t;
+                    }
+                }
+            }
+        };
     }
 
-    public static File diagFile(Description description, String prefix, String ext) {
-        return new File(
-                getInstrumentation().getTargetContext().getFilesDir(),
-                prefix
-                        + "-"
-                        + description.getTestClass().getSimpleName()
-                        + "."
-                        + description.getMethodName()
-                        + "."
-                        + ext);
+    private static BySelector getAnyObjectSelector() {
+        return By.textStartsWith("");
     }
 
-    public static void onError(UiDevice device, Description description, Throwable e) {
-        if (device == null) return;
-        final File sceenshot = diagFile(description, "TestScreenshot", "png");
-        final File hierarchy = diagFile(description, "Hierarchy", "zip");
-
-        // Dump window hierarchy
-        try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(hierarchy))) {
-            out.putNextEntry(new ZipEntry("bugreport.txt"));
-            dumpStringCommand("dumpsys window windows", out);
-            dumpStringCommand("dumpsys package", out);
-            out.closeEntry();
-
-            out.putNextEntry(new ZipEntry("visible_windows.zip"));
-            dumpCommand("cmd window dump-visible-window-views", out);
-            out.closeEntry();
-        } catch (IOException ex) {
+    static String getSystemAnomalyMessage(UiDevice device) {
+        if (!device.wait(Until.hasObject(getAnyObjectSelector()), 10000)) {
+            return "Screen is empty";
         }
 
-        Log.e(
-                TAG,
-                "Failed test "
-                        + description.getMethodName()
-                        + ",\nscreenshot will be saved to "
-                        + sceenshot
-                        + ",\nUI dump at: "
-                        + hierarchy
-                        + " (use go/web-hv to open the dump file)",
-                e);
-        device.takeScreenshot(sceenshot);
+        final StringBuilder sb = new StringBuilder();
 
-        // Dump accessibility hierarchy
-        try {
-            device.dumpWindowHierarchy(diagFile(description, "AccessibilityHierarchy", "uix"));
-        } catch (IOException ex) {
-            Log.e(TAG, "Failed to save accessibility hierarchy", ex);
+        UiObject2 object = device.findObject(By.res("android", "alertTitle").pkg("android"));
+        if (object != null) {
+            sb.append("TITLE: ").append(object.getText());
         }
-    }
 
-    private static void dumpStringCommand(String cmd, OutputStream out) throws IOException {
-        out.write(("\n\n" + cmd + "\n").getBytes());
-        dumpCommand(cmd, out);
-    }
-
-    private static void dumpCommand(String cmd, OutputStream out) throws IOException {
-        try (AutoCloseInputStream in =
-                new AutoCloseInputStream(
-                        getInstrumentation().getUiAutomation().executeShellCommand(cmd))) {
-            FileUtils.copy(in, out);
+        object = device.findObject(By.res("android", "message").pkg("android"));
+        if (object != null) {
+            sb.append(" PACKAGE: ")
+                    .append(object.getApplicationPackage())
+                    .append(" MESSAGE: ")
+                    .append(object.getText());
         }
+
+        if (sb.length() != 0) {
+            return "System alert popup is visible: " + sb;
+        }
+
+        return null;
     }
 }
diff --git a/libraries/health/rules/src/android/platform/test/rule/LandscapeOrientationRule.java b/libraries/health/rules/src/android/platform/test/rule/LandscapeOrientationRule.java
deleted file mode 100644
index 11fbca4..0000000
--- a/libraries/health/rules/src/android/platform/test/rule/LandscapeOrientationRule.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.platform.test.rule;
-
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-
-import static org.junit.Assert.assertEquals;
-
-import android.os.RemoteException;
-import android.os.SystemClock;
-
-import org.junit.runner.Description;
-
-/**
- * Locks landscape orientation before running a test and goes back to natural orientation
- * afterwards.
- */
-public class LandscapeOrientationRule extends TestWatcher {
-
-    @Override
-    protected void starting(Description description) {
-        try {
-            getUiDevice().setOrientationNatural();
-            int currentOrientation = getContext().getResources().getConfiguration().orientation;
-            if (currentOrientation != ORIENTATION_LANDSCAPE) { // ORIENTATION_PORTRAIT
-                getUiDevice().setOrientationLeft();
-                for (int i = 0; i != 100; ++i) {
-                    int rotatedOrientation =
-                            getContext().getResources().getConfiguration().orientation;
-                    if (rotatedOrientation == ORIENTATION_LANDSCAPE) break;
-                    if (i == 99) {
-                        assertEquals(
-                                "Orientation should be landscape",
-                                ORIENTATION_LANDSCAPE,
-                                rotatedOrientation);
-                    }
-                    SystemClock.sleep(100);
-                }
-            }
-        } catch (RemoteException e) {
-            String message = "RemoteException when forcing landscape rotation on the device";
-            throw new RuntimeException(message, e);
-        }
-    }
-
-    @Override
-    protected void finished(Description description) {
-        try {
-            if (!getUiDevice().isNaturalOrientation()) {
-                getUiDevice().setOrientationNatural();
-            }
-            getUiDevice().unfreezeRotation();
-        } catch (RemoteException e) {
-            String message = "RemoteException when restoring natural rotation of the device";
-            throw new RuntimeException(message, e);
-        }
-    }
-}
diff --git a/libraries/health/rules/src/android/platform/test/rule/NavigationModeRule.java b/libraries/health/rules/src/android/platform/test/rule/NavigationModeRule.java
index f747906..6187cd7 100644
--- a/libraries/health/rules/src/android/platform/test/rule/NavigationModeRule.java
+++ b/libraries/health/rules/src/android/platform/test/rule/NavigationModeRule.java
@@ -18,7 +18,6 @@
 
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 
-import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_2BUTTON_OVERLAY;
 import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_3BUTTON_OVERLAY;
 import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_GESTURAL_OVERLAY;
 
@@ -84,9 +83,7 @@
     private static String getCurrentOverlayPackage(int currentInteractionMode) {
         return QuickStepContract.isGesturalMode(currentInteractionMode)
                 ? NAV_BAR_MODE_GESTURAL_OVERLAY
-                : QuickStepContract.isSwipeUpMode(currentInteractionMode)
-                        ? NAV_BAR_MODE_2BUTTON_OVERLAY
-                        : NAV_BAR_MODE_3BUTTON_OVERLAY;
+                : NAV_BAR_MODE_3BUTTON_OVERLAY;
     }
 
     @Override
diff --git a/libraries/health/rules/src/android/platform/test/rule/OrientationRule.kt b/libraries/health/rules/src/android/platform/test/rule/OrientationRule.kt
new file mode 100644
index 0000000..c1e5ac1
--- /dev/null
+++ b/libraries/health/rules/src/android/platform/test/rule/OrientationRule.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.rule
+
+import android.platform.test.rule.OrientationRule.Landscape
+import android.platform.test.rule.OrientationRule.Portrait
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * This rule will lock orientation before running a test class and unlock after. The orientation is
+ * natural by default, and landscape or portrait if the test or one of its superclasses is marked
+ * with the [Landscape] or [Portrait] annotation, .
+ */
+class OrientationRule : TestRule {
+
+    override fun apply(base: Statement, description: Description): Statement {
+        val testClass = description.testClass
+
+        val hasLandscapeAnnotation = testClass.hasAnnotation(Landscape::class.java)
+        val hasPortraitAnnotation = testClass.hasAnnotation(Portrait::class.java)
+        if (hasLandscapeAnnotation && hasPortraitAnnotation) {
+            throw IllegalStateException(
+                "Both @Portrait and @Landscape annotations at the same time are not yet supported.")
+        }
+
+        val orientationRule =
+            if (hasLandscapeAnnotation) {
+                LandscapeOrientationRule()
+            } else if (hasPortraitAnnotation) {
+                PortraitOrientationRule()
+            } else NaturalOrientationRule()
+
+        return orientationRule.apply(base, description)
+    }
+
+    private fun <T> Class<T>?.hasAnnotation(annotation: Class<out Annotation>): Boolean =
+        if (this == null) {
+            false
+        } else if (isAnnotationPresent(annotation)) {
+            true
+        } else {
+            superclass.hasAnnotation(annotation)
+        }
+
+    @Retention(AnnotationRetention.RUNTIME)
+    @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS)
+    annotation class Landscape
+
+    @Retention(AnnotationRetention.RUNTIME)
+    @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS)
+    annotation class Portrait
+}
diff --git a/libraries/health/rules/src/android/platform/test/rule/PlatinumRule.java b/libraries/health/rules/src/android/platform/test/rule/PlatinumRule.java
new file mode 100644
index 0000000..225f150
--- /dev/null
+++ b/libraries/health/rules/src/android/platform/test/rule/PlatinumRule.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.platform.test.rule;
+
+import android.os.Build;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.AssumptionViolatedException;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Arrays;
+
+/**
+ * Rule that runs tests marked with @Platinum on Platinum test runs, only on devices listed in the
+ * comma-separated string passed as an argument to the @Platinum annotation. The test will be
+ * skipped, for Platinum runs, on devices not in the list.
+ */
+public class PlatinumRule implements TestRule {
+    @Override
+    public Statement apply(Statement base, Description description) {
+        // If the test is not annotated with @Platinum, this rule is not applicable.
+        final Platinum annotation = description.getTestClass().getAnnotation(Platinum.class);
+        if (annotation == null) return base;
+
+        // If the test suite isn't running with
+        // "exclude-annotation": "androidx.test.filters.FlakyTest", then this is not a platinum
+        // test, and the rule is not applicable.
+        final String nonAnnotationArgument =
+                InstrumentationRegistry.getArguments().getString("notAnnotation", "");
+        if (!Arrays.stream(nonAnnotationArgument.split(","))
+                .anyMatch("androidx.test.filters.FlakyTest"::equals)) {
+            return base;
+        }
+
+        // If the target IS listed in the annotation's parameter, this rule is not applicable.
+        final boolean match = Arrays.asList(annotation.value().split(",")).contains(Build.PRODUCT);
+        if (match) return base;
+
+        // The test will be skipped upon start.
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                throw new AssumptionViolatedException(
+                        "Skipping the test on target "
+                                + Build.PRODUCT
+                                + " which in not in "
+                                + annotation.value());
+            }
+        };
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.TYPE})
+    public @interface Platinum {
+        String value();
+    }
+}
diff --git a/libraries/health/rules/src/android/platform/test/rule/PresubmitRule.java b/libraries/health/rules/src/android/platform/test/rule/PresubmitRule.java
new file mode 100644
index 0000000..6a69e88
--- /dev/null
+++ b/libraries/health/rules/src/android/platform/test/rule/PresubmitRule.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.platform.test.rule;
+
+import android.os.Build;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.AssumptionViolatedException;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Arrays;
+
+/**
+ * Rule that runs tests marked with @Presubmit on presubmit test runs, only on devices listed in the
+ * comma-separated string passed as an argument to the @Presubmit annotation. The test will be
+ * skipped, for presubmit runs, on devices not in the list.
+ */
+public class PresubmitRule implements TestRule {
+    public static boolean runningInPresubmit() {
+        // We run in presubmit when there is a parameter to exclude postsubmits.
+        final String nonAnnotationArgument =
+                InstrumentationRegistry.getArguments().getString("notAnnotation", "");
+        return Arrays.stream(nonAnnotationArgument.split(","))
+                .anyMatch("android.platform.test.annotations.Postsubmit"::equals);
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        // If the test is not annotated with @Presubmit, this rule is not applicable.
+        final Presubmit annotation = description.getTestClass().getAnnotation(Presubmit.class);
+        if (annotation == null) return base;
+
+        // If the test suite isn't running with
+        // "exclude-annotation": "android.platform.test.annotations.Postsubmit", then this is not
+        // a presubmit test, and the rule is not applicable.
+        if (!runningInPresubmit()) {
+            return base;
+        }
+
+        // If the target IS listed in the annotation's parameter, this rule is not applicable.
+        final boolean match = Arrays.asList(annotation.value().split(",")).contains(Build.PRODUCT);
+        if (match) return base;
+
+        // The test will be skipped upon start.
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                throw new AssumptionViolatedException(
+                        "Skipping the test on target "
+                                + Build.PRODUCT
+                                + " which in not in "
+                                + annotation.value());
+            }
+        };
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.TYPE})
+    public @interface Presubmit {
+        String value();
+    }
+}
diff --git a/libraries/health/rules/src/android/platform/test/rule/SamplerRule.java b/libraries/health/rules/src/android/platform/test/rule/SamplerRule.java
new file mode 100644
index 0000000..14034da
--- /dev/null
+++ b/libraries/health/rules/src/android/platform/test/rule/SamplerRule.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.rule;
+
+import android.os.SystemClock;
+import android.util.Log;
+
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * A rule that generates a file that helps diagnosing cases when the test process was terminated
+ * because the test execution took too long, and tests that ran for too long even without being
+ * terminated. If the process was terminated or the test was long, the test leaves an artifact with
+ * stack traces of all threads, every second. This will help understanding where we stuck.
+ */
+public class SamplerRule extends TestWatcher {
+    private static final String TAG = SamplerRule.class.getSimpleName();
+    private static final int TOO_LONG_TEST_MS = 60000;
+    private static boolean sEnabled;
+
+    public static void enable(boolean enabled) {
+        // The rule need to be explicitly enabled to avoid slowing down performance tests.
+        sEnabled = enabled;
+    }
+
+    public static Thread startThread(Description description) {
+        Thread thread =
+                new Thread() {
+                    @Override
+                    public void run() {
+                        // Write all-threads stack stace every second while the test is running.
+                        // After the test finishes, delete that file. If the test process is
+                        // terminated due to timeout, the trace file won't be deleted.
+                        final File file = getFile();
+
+                        final long startTime = SystemClock.elapsedRealtime();
+                        try (final OutputStreamWriter outputStreamWriter =
+                                new OutputStreamWriter(
+                                        new BufferedOutputStream(new FileOutputStream(file)))) {
+                            writeSampleEverySecond(outputStreamWriter);
+                        } catch (IOException | InterruptedException e) {
+                            // Simply suppressing the exceptions, nothing to do here.
+                        } finally {
+                            // If the process is not killed, then there was no test timeout, and
+                            // we are not interested in the trace file, unless the test ran too
+                            // long.
+                            if (SystemClock.elapsedRealtime() - startTime < TOO_LONG_TEST_MS) {
+                                file.delete();
+                            } else {
+                                Log.d(
+                                        TAG,
+                                        "Test execution is too long, generating sample file "
+                                                + file);
+                            }
+                        }
+                    }
+
+                    private File getFile() {
+                        final String strDate = new SimpleDateFormat("HH:mm:ss").format(new Date());
+
+                        final String descStr = description.getTestClass().getSimpleName();
+                        return ArtifactSaver.artifactFile(
+                                "ThreadStackSamples-" + strDate + "-" + descStr + ".txt");
+                    }
+
+                    private void writeSampleEverySecond(OutputStreamWriter writer)
+                            throws IOException, InterruptedException {
+                        int count = 0;
+                        while (true) {
+                            writer.write(
+                                    "#"
+                                            + (count++)
+                                            + " =============================================\r\n");
+                            for (StackTraceElement[] stack : getAllStackTraces().values()) {
+                                writer.write("---------------------\r\n");
+                                for (StackTraceElement frame : stack) {
+                                    writer.write(frame.toString() + "\r\n");
+                                }
+                            }
+                            writer.flush();
+
+                            sleep(1000);
+                        }
+                    }
+                };
+
+        thread.start();
+        return thread;
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        if (!sEnabled) return base;
+
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                final Thread traceThread = startThread(description);
+                try {
+                    SamplerRule.super.apply(base, description).evaluate();
+                } finally {
+                    traceThread.interrupt();
+                    traceThread.join();
+                }
+            }
+        };
+    }
+}
diff --git a/libraries/health/rules/src/android/platform/test/rule/ScreenRecordRule.java b/libraries/health/rules/src/android/platform/test/rule/ScreenRecordRule.java
index fb9d052..b082184 100644
--- a/libraries/health/rules/src/android/platform/test/rule/ScreenRecordRule.java
+++ b/libraries/health/rules/src/android/platform/test/rule/ScreenRecordRule.java
@@ -43,6 +43,26 @@
 
     private static final String TAG = "ScreenRecordRule";
 
+    public static void runWithRecording(ThrowingRunnable runnable, Description description)
+            throws Throwable {
+        Instrumentation inst = getInstrumentation();
+        UiAutomation automation = inst.getUiAutomation();
+        UiDevice device = UiDevice.getInstance(inst);
+
+        File outputFile = ArtifactSaver.artifactFile(description, "ScreenRecord", "mp4");
+        device.executeShellCommand("killall screenrecord");
+        ParcelFileDescriptor output = automation.executeShellCommand("screenrecord " + outputFile);
+        String screenRecordPid = device.executeShellCommand("pidof screenrecord");
+        try {
+            runnable.run();
+        } finally {
+            device.executeShellCommand("kill -INT " + screenRecordPid);
+            Log.e(TAG, "Screenrecord captured at: " + outputFile);
+            output.close();
+        }
+        automation.executeShellCommand("rm " + outputFile);
+    }
+
     @Override
     public Statement apply(Statement base, Description description) {
         if (description.getAnnotation(ScreenRecord.class) == null) {
@@ -52,24 +72,7 @@
         return new Statement() {
             @Override
             public void evaluate() throws Throwable {
-                Instrumentation inst = getInstrumentation();
-                UiAutomation automation = inst.getUiAutomation();
-                UiDevice device = UiDevice.getInstance(inst);
-
-                File outputFile = FailureWatcher.diagFile(description, "ScreenRecord", "mp4");
-                device.executeShellCommand("killall screenrecord");
-                ParcelFileDescriptor output =
-                        automation.executeShellCommand("screenrecord " + outputFile);
-                String screenRecordPid = device.executeShellCommand("pidof screenrecord");
-                try {
-                    base.evaluate();
-                } finally {
-                    device.executeShellCommand("kill -INT " + screenRecordPid);
-                    Log.e(TAG, "Screenrecord captured at: " + outputFile);
-                    output.close();
-                }
-                // Delete the file if the test was successful.
-                automation.executeShellCommand("rm " + outputFile);
+                runWithRecording(base::evaluate, description);
             }
         };
     }
@@ -78,4 +81,8 @@
     @Retention(RetentionPolicy.RUNTIME)
     @Target(ElementType.METHOD)
     public @interface ScreenRecord {}
+
+    public interface ThrowingRunnable {
+        void run() throws Throwable;
+    }
 }
diff --git a/libraries/health/rules/src/android/platform/test/rule/SettingOverrideRule.java b/libraries/health/rules/src/android/platform/test/rule/SettingOverrideRule.java
new file mode 100644
index 0000000..7b749df
--- /dev/null
+++ b/libraries/health/rules/src/android/platform/test/rule/SettingOverrideRule.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.platform.test.rule;
+
+
+import android.content.ContentResolver;
+import android.provider.Settings;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.runner.Description;
+
+/** This rule will override secure setting and revert it after the test. */
+public class SettingOverrideRule extends TestWatcher {
+
+    private final ContentResolver contentResolver = InstrumentationRegistry.getInstrumentation()
+            .getContext().getContentResolver();
+
+    private final String mSettingName;
+    private final int mOverrideValue;
+
+    private int mOriginalValue;
+
+    public SettingOverrideRule(String name, int value) {
+        mSettingName = name;
+        mOverrideValue = value;
+    }
+
+    @Override
+    protected void starting(Description description) {
+        try {
+            mOriginalValue = Settings.Secure.getInt(contentResolver, mSettingName);
+        } catch (Settings.SettingNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+
+        Settings.Secure.putInt(contentResolver, mSettingName, mOverrideValue);
+    }
+
+    @Override
+    protected void finished(Description description) {
+        Settings.Secure.putInt(contentResolver, mSettingName, mOriginalValue);
+    }
+}
diff --git a/libraries/health/rules/src/android/platform/test/rule/SystemHealthRule.java b/libraries/health/rules/src/android/platform/test/rule/SystemHealthRule.java
new file mode 100644
index 0000000..4b8c5c7
--- /dev/null
+++ b/libraries/health/rules/src/android/platform/test/rule/SystemHealthRule.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.rule;
+
+import android.content.Context;
+import android.os.DropBoxManager;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Assert;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.Date;
+
+/**
+ * Test rule that diagnoses system health issues that happened during the test run. It pulls from
+ * Dropbox service crashes that happened during the test run and attaches them to the "test failed"
+ * exception.
+ */
+public class SystemHealthRule implements TestRule {
+    long mTestStartTime;
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                mTestStartTime = System.currentTimeMillis();
+                try {
+                    base.evaluate();
+                } catch (Throwable t) {
+                    tryRethrowingWithSystemHealth(t);
+                    throw t;
+                }
+            }
+
+            // If the test failed, and there was a system health issue while the test was
+            // running, add this information to the diags.
+            private void tryRethrowingWithSystemHealth(Throwable cause) {
+                final String systemHealthMessage =
+                        getSystemHealthMessage(
+                                InstrumentationRegistry.getInstrumentation().getContext(),
+                                mTestStartTime);
+                if (systemHealthMessage != null) {
+                    throw new AssertionError(
+                            "There was a system health problem while test was running:\n"
+                                    + systemHealthMessage,
+                            cause);
+                }
+            }
+        };
+    }
+
+    private static String truncateCrash(String text, int maxLines) {
+        String[] lines = text.split("\\r?\\n");
+        StringBuilder ret = new StringBuilder();
+        for (int i = 0; i < maxLines && i < lines.length; i++) {
+            ret.append(lines[i]);
+            ret.append('\n');
+        }
+        if (lines.length > maxLines) {
+            ret.append("... ");
+            ret.append(lines.length - maxLines);
+            ret.append(" more lines truncated ...\n");
+        }
+        return ret.toString();
+    }
+
+    private static String checkCrash(Context context, String label, long startTime) {
+        DropBoxManager dropbox = context.getSystemService(DropBoxManager.class);
+        Assert.assertNotNull("Unable access the DropBoxManager service", dropbox);
+
+        long timestamp = startTime;
+        DropBoxManager.Entry entry;
+        StringBuilder errorDetails = new StringBuilder();
+        while (null != (entry = dropbox.getNextEntry(label, timestamp))) {
+            errorDetails.append("------------------------------\n");
+            timestamp = entry.getTimeMillis();
+            errorDetails.append(new Date(timestamp));
+            errorDetails.append(": ");
+            errorDetails.append(entry.getTag());
+            errorDetails.append(": ");
+            final String dropboxSnippet = entry.getText(4096);
+            if (dropboxSnippet != null) errorDetails.append(truncateCrash(dropboxSnippet, 40));
+            errorDetails.append("    ...\n");
+            entry.close();
+        }
+        return errorDetails.length() != 0 ? errorDetails.toString() : null;
+    }
+
+    public static String getSystemHealthMessage(Context context, long startTime) {
+        try {
+            StringBuilder errors = new StringBuilder();
+
+            final String[] labels = {
+                "system_app_anr",
+                "system_app_crash",
+                "system_app_native_crash",
+                "system_server_anr",
+                "system_server_crash",
+                "system_server_native_crash",
+                "system_server_watchdog",
+            };
+
+            for (String label : labels) {
+                final String crash = checkCrash(context, label, startTime);
+                if (crash != null) errors.append(crash);
+            }
+
+            return errors.length() != 0
+                    ? "Current time: " + new Date(System.currentTimeMillis()) + "\n" + errors
+                    : null;
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public void resetStartTime() {
+        mTestStartTime = System.currentTimeMillis();
+    }
+}
diff --git a/libraries/health/rules/src/android/platform/test/rule/flicker/FlickerRuleBase.java b/libraries/health/rules/src/android/platform/test/rule/flicker/FlickerRuleBase.java
deleted file mode 100644
index aeaceb5..0000000
--- a/libraries/health/rules/src/android/platform/test/rule/flicker/FlickerRuleBase.java
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.platform.test.rule.flicker;
-
-import android.os.Environment;
-import android.platform.test.rule.TestWatcher;
-import android.util.Log;
-
-import com.android.server.wm.flicker.monitor.LayersTraceMonitor;
-import com.android.server.wm.flicker.monitor.WindowManagerTraceMonitor;
-import com.android.server.wm.traces.common.layers.LayersTrace;
-import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace;
-import com.android.server.wm.traces.parser.layers.LayersTraceParser;
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerTraceParser;
-
-import com.google.common.io.Files;
-import java.util.HashMap;
-import java.util.Map;
-import java.nio.file.Paths;
-import java.io.File;
-import org.junit.runner.Description;
-
-/**
- * Base class that encapsulates the logic for collecting and parsing
- * the window manager trace and surface flinger trace.
- */
-public abstract class FlickerRuleBase extends TestWatcher {
-
-    private static final String TAG = FlickerRuleBase.class.getSimpleName();
-
-    // Suffix is added by the trace monitor at the time of saving the file.
-    private static final String WM_TRACE_FILE_SUFFIX = "wm_trace.pb";
-
-    // Suffix is added by the trace monitor at the time of saving the file.
-    private static final String LAYERS_TRACE_FILE_SUFFIX = "layers_trace.pb";
-
-    // Option to customize the root directory to save the trace files.
-    private static final String TRACE_DIRECTORY_ROOT = "trace-directory-root";
-
-    // To keep track of the method name and the current iteration count.
-    private static Map<String, Integer> mMethodNameCount = new HashMap<>();
-    private static boolean updateMethodCount = true;
-    private boolean mIsWmTraceEnabled = false;
-    private boolean mIsLayersTraceEnabled = false;
-    private WindowManagerTraceMonitor mWmTraceMonitor;
-    private LayersTraceMonitor mLayersTraceMonitor;
-    private String mTraceDirectoryRoot = null;
-    private boolean mIsRuleDisabled= false;
-    private WindowManagerTrace mWindowManagerTrace;
-    private LayersTrace mLayerTrace;
-
-    public FlickerRuleBase() {
-    }
-
-    @Override
-    protected void starting(Description description) {
-        if (!mIsWmTraceEnabled && !mIsLayersTraceEnabled) {
-            Log.v(TAG, "Both surface flinger and layer tracing are disabled.");
-            return;
-        }
-
-        setupRootDirectory();
-
-        // Verify if WM tracing is already started by another window manager based
-        // rule otherwise proceed with starting the WM trace.
-        if (mIsWmTraceEnabled) {
-            if (!mWmTraceMonitor.isEnabled()) {
-                mWmTraceMonitor.start();
-                Log.v(TAG, "WM trace started successfully.");
-            } else {
-                Log.v(TAG, "WM trace already enabled.");
-            }
-        }
-
-        // Verify if layers tracing is already started by another layers based
-        // rule otherwise proceed with starting the layer trace.
-        if (mIsLayersTraceEnabled) {
-            if (!mLayersTraceMonitor.isEnabled()) {
-                mLayersTraceMonitor.start();
-                Log.v(TAG, "Layers trace started successfully.");
-            } else {
-                Log.v(TAG, "Layers trace already enabled.");
-            }
-        }
-
-        // If traces are already started by other window manager or layer based rule
-        // then exit without incrementing the method iteration count.
-        if (!updateMethodCount) {
-            return;
-        }
-
-        // Update the method name with current iteration count.
-        if (mMethodNameCount.containsKey(description.toString())) {
-            mMethodNameCount.put(description.toString(),
-                    mMethodNameCount.get(description.toString()) + 1);
-        } else {
-            mMethodNameCount.put(description.toString(), 1);
-        }
-
-        // Cleanup the trace file from previous test runs.
-        cleanupTraceFiles(description);
-
-        updateMethodCount = false;
-    }
-
-    @Override
-    protected void finished(Description description) {
-
-        updateMethodCount = true;
-
-        // Verify if WM tracing is already stopped by another window manager based
-        // rule. Otherwise proceed with stopping the trace.
-        if (mIsWmTraceEnabled) {
-            if (mWmTraceMonitor.isEnabled()) {
-                mWmTraceMonitor.stop();
-                Log.v(TAG, "WM trace stopped successfully.");
-            } else {
-                Log.v(TAG, "WM trace already stopped.");
-            }
-        }
-
-        if (mIsWmTraceEnabled) {
-            // Verify if WM trace file already exist for the current test. It could have been
-            // created by another Window manager based rule.
-            if (!new File(getFinalTraceFilePath(description, WM_TRACE_FILE_SUFFIX)).exists()) {
-                // Appends the trace file suffix "_wm_trace.pb" and store it under the root
-                // directory.
-                mWmTraceMonitor.save(getFileNamePrefix(description));
-                Log.v(TAG, "WM trace successfully saved in the destination folder.");
-            } else {
-                Log.v(TAG, "WM trace already saved in the destination folder.");
-            }
-            mWindowManagerTrace = getWindowManagerTrace(
-                    getFinalTraceFilePath(description, WM_TRACE_FILE_SUFFIX));
-        }
-
-        // Verify if layers tracing is already stopped by another layers based
-        // rule. Otherwise proceed with stopping the trace.
-        if (mIsLayersTraceEnabled) {
-            if (mLayersTraceMonitor.isEnabled()) {
-                mLayersTraceMonitor.stop();
-                Log.v(TAG, "Layers trace stopped successfully.");
-            } else {
-                Log.v(TAG, "Layers trace already stopped.");
-            }
-        }
-
-        if (mIsLayersTraceEnabled) {
-            // Verify if layer trace file already exist for the current test. It could have been
-            // created by another layers based rule.
-            if (!new File(getFinalTraceFilePath(description, LAYERS_TRACE_FILE_SUFFIX)).exists()) {
-                // Appends the trace file suffix "_layers_trace.pb" and store it under the root
-                // directory.
-                mLayersTraceMonitor.save(getFileNamePrefix(description));
-                Log.v(TAG, "Layers trace successfully saved in the destination folder.");
-            } else {
-                Log.v(TAG, "Layers trace already saved in the destination folder.");
-            }
-            mLayerTrace = getLayersTrace(
-                    getFinalTraceFilePath(description, LAYERS_TRACE_FILE_SUFFIX));
-        }
-
-        validateFlickerConditions();
-    }
-
-    /**
-    * Setup the root directory to save the WM traces and layers trace collected during the test.
-    */
-    private void setupRootDirectory() {
-        mTraceDirectoryRoot = getArguments().getString(TRACE_DIRECTORY_ROOT,
-                Environment.getExternalStorageDirectory().getPath() + "/flicker_trace/");
-        if (!mTraceDirectoryRoot.endsWith("/")) {
-            mTraceDirectoryRoot = new StringBuilder(mTraceDirectoryRoot).append("/").toString();
-        }
-
-        // Create root directory if it does not already exist.
-        File rootDirectory = new File(mTraceDirectoryRoot);
-        if (!rootDirectory.exists()) {
-            if (rootDirectory.mkdirs()) {
-                Log.v(TAG, "Trace root directory created successfully.");
-            } else {
-                throw new RuntimeException(
-                        "Unable to create the trace root directory." + mTraceDirectoryRoot);
-            }
-        } else {
-            Log.v(TAG, "Trace root directory already exists.");
-        }
-
-        if (mIsWmTraceEnabled) {
-            mWmTraceMonitor = new WindowManagerTraceMonitor(Paths.get(mTraceDirectoryRoot));
-        }
-
-        if (mIsLayersTraceEnabled) {
-            mLayersTraceMonitor = new LayersTraceMonitor(Paths.get(mTraceDirectoryRoot));
-        }
-    }
-
-    /**
-     * Remove the WM trace and layers trace files collected from previous test runs.
-     */
-    private void cleanupTraceFiles(Description description) {
-        if (new File(getFinalTraceFilePath(description, WM_TRACE_FILE_SUFFIX)).exists()) {
-            new File(getFinalTraceFilePath(description, WM_TRACE_FILE_SUFFIX)).delete();
-            Log.v(TAG, "Removed the already existing wm trace file.");
-        }
-
-        if (new File(getFinalTraceFilePath(description, LAYERS_TRACE_FILE_SUFFIX)).exists()) {
-            new File(getFinalTraceFilePath(description, LAYERS_TRACE_FILE_SUFFIX)).delete();
-            Log.v(TAG, "Removed the already existing layers trace file.");
-        }
-    }
-
-    /**
-     * Retrieve the path of the trace file in the device.
-     *
-     * @param description
-     * @param suffix
-     * @return path to the trace file.
-     */
-    private String getFinalTraceFilePath(Description description, String suffix) {
-        return String.format(
-                "%s%s_%s", mTraceDirectoryRoot, getFileNamePrefix(description),
-                suffix);
-    }
-
-    /**
-     * Construct file name using the class name, method name and the current iteration count
-     * of the method.
-     *
-     * @param description
-     * @return the file name used to save the WM trace proto file.
-     */
-    private String getFileNamePrefix(Description description) {
-        return description.getClassName() + "_" + description.getMethodName() + "_"
-                + mMethodNameCount.get(description.toString());
-    }
-
-    /**
-     * Parse the window manager trace file.
-     *
-     * @param finalTraceFilePath
-     * @return parsed window manager trace.
-     */
-    private WindowManagerTrace getWindowManagerTrace(String finalTraceFilePath) {
-        Log.v(TAG, "Processing window manager trace file.");
-        try {
-            byte[] wmTraceByteArray = Files.toByteArray(new File(finalTraceFilePath));
-            if (wmTraceByteArray != null) {
-                WindowManagerTrace wmTrace = WindowManagerTraceParser
-                        .parseFromTrace(wmTraceByteArray);
-                return wmTrace;
-            } else {
-                throw new RuntimeException("Window manager trace contents are empty.");
-            }
-        } catch (Exception e) {
-            throw new RuntimeException("Unable to read the proto file." + finalTraceFilePath);
-        }
-    }
-
-    /**
-     * Parse the layers trace file.
-     *
-     * @param finalTraceFilePath
-     * @return parsed layers trace.
-     */
-    private LayersTrace getLayersTrace(String finalTraceFilePath) {
-        Log.v(TAG, "Processing layers trace file.");
-        try {
-            byte[] layersTraceByteArray = Files.toByteArray(new File(finalTraceFilePath));
-            if (layersTraceByteArray != null) {
-                LayersTrace layersTrace = LayersTraceParser
-                        .parseFromTrace(layersTraceByteArray);
-                return layersTrace;
-            } else {
-                throw new RuntimeException("layers trace contents are empty.");
-            }
-        } catch (Exception e) {
-            throw new RuntimeException("Unable to read the proto file." + finalTraceFilePath);
-        }
-    }
-
-    /**
-     * Child class should implement this method to test for flicker conditions.
-     *
-     */
-    protected abstract void validateFlickerConditions();
-
-    protected void enableWmTrace() {
-        mIsWmTraceEnabled = true;
-    }
-
-    protected void enableLayerTrace() {
-        mIsLayersTraceEnabled = true;
-    }
-
-    protected WindowManagerTrace getWindowManagerTrace() {
-        return mWindowManagerTrace;
-    }
-
-    protected LayersTrace getLayersTrace() {
-        return mLayerTrace;
-    }
-}
diff --git a/libraries/health/rules/src/android/platform/test/rule/flicker/LayersFlickerRuleBase.java b/libraries/health/rules/src/android/platform/test/rule/flicker/LayersFlickerRuleBase.java
deleted file mode 100644
index e52c5de..0000000
--- a/libraries/health/rules/src/android/platform/test/rule/flicker/LayersFlickerRuleBase.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.platform.test.rule.flicker;
-
-import android.os.Environment;
-import android.util.Log;
-
-import com.android.server.wm.traces.common.layers.LayersTrace;
-
-/**
- * Base class that encapsulates the logic for enabling the layers trace, parsing the
- * surface flinger trace. Extend this class to add validation for layers trace based
- * flicker conditions.
- */
-public abstract class LayersFlickerRuleBase extends FlickerRuleBase {
-
-    private static final String TAG = LayersFlickerRuleBase.class.getSimpleName();
-
-    LayersFlickerRuleBase() {
-        enableLayerTrace();
-        Log.v(TAG, "Enabled the surface flinger trace.");
-    }
-
-    protected void validateFlickerConditions() {
-        validateLayersFlickerConditions(getLayersTrace());
-    }
-
-    /**
-     * Override this method to provide layers trace based flicker validations.
-     *
-     * @param layersTrace
-     */
-    protected abstract void validateLayersFlickerConditions(LayersTrace layersTrace);
-}
diff --git a/libraries/health/rules/src/android/platform/test/rule/flicker/LayersFlickerRuleCommon.java b/libraries/health/rules/src/android/platform/test/rule/flicker/LayersFlickerRuleCommon.java
deleted file mode 100644
index 86b2801..0000000
--- a/libraries/health/rules/src/android/platform/test/rule/flicker/LayersFlickerRuleCommon.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.platform.test.rule.flicker;
-
-import android.util.Log;
-
-import com.android.server.wm.traces.common.layers.LayersTrace;
-
-/**
- * Rule used for validating the common layers trace based flicker assertions applicable
- * for all the CUJ's
- */
-public class LayersFlickerRuleCommon extends LayersFlickerRuleBase {
-
-    private static final String TAG = LayersFlickerRuleCommon.class.getSimpleName();
-
-    protected void validateLayersFlickerConditions(LayersTrace layersTrace) {
-
-        // TODO: b/183516705 Implementation for layer trace based common validations.
-        Log.v(TAG, "Successfully verified the common layers flicker conditions.");
-    }
-}
diff --git a/libraries/health/rules/src/android/platform/test/rule/flicker/WindowManagerFlickerRuleBase.java b/libraries/health/rules/src/android/platform/test/rule/flicker/WindowManagerFlickerRuleBase.java
deleted file mode 100644
index 387cfc1..0000000
--- a/libraries/health/rules/src/android/platform/test/rule/flicker/WindowManagerFlickerRuleBase.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.platform.test.rule.flicker;
-
-import android.os.Environment;
-import android.util.Log;
-
-import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace;
-
-/**
- * Base class that encapsulates the logic for enabling the window manager trace, parsing the
- * window manager trace. Extend this class to add validation for window manager trace based
- * flicker conditions.
- */
-public abstract class WindowManagerFlickerRuleBase extends FlickerRuleBase {
-
-    private static final String TAG = WindowManagerFlickerRuleBase.class.getSimpleName();
-
-    WindowManagerFlickerRuleBase() {
-        enableWmTrace();
-        Log.v(TAG, "Enabled the window manager trace.");
-    }
-
-    protected void validateFlickerConditions() {
-        validateWMFlickerConditions(getWindowManagerTrace());
-    }
-
-    /**
-     * Override this method to provide window manager trace based flicker validations.
-     *
-     * @param windowManagerTrace
-     */
-    protected abstract void validateWMFlickerConditions(WindowManagerTrace windowManagerTrace);
-}
diff --git a/libraries/health/rules/src/android/platform/test/rule/flicker/WindowManagerFlickerRuleCommon.java b/libraries/health/rules/src/android/platform/test/rule/flicker/WindowManagerFlickerRuleCommon.java
deleted file mode 100644
index 42a4ffb..0000000
--- a/libraries/health/rules/src/android/platform/test/rule/flicker/WindowManagerFlickerRuleCommon.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.platform.test.rule.flicker;
-
-import android.util.Log;
-
-import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject;
-import com.android.server.wm.traces.common.FlickerComponentName;
-import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace;
-
-/**
- * Rule used for validating the common window manager trace based flicker assertions applicable
- * for all the CUJ's
- */
-public class WindowManagerFlickerRuleCommon extends WindowManagerFlickerRuleBase {
-
-    private static final String TAG = WindowManagerFlickerRuleCommon.class.getSimpleName();
-    private static final FlickerComponentName NAV_BAR_COMPONENT =
-            new FlickerComponentName("", "NavigationBar0");
-    private static final FlickerComponentName STATUS_BAR_COMPONENT =
-            new FlickerComponentName("", "StatusBar");
-
-    protected void validateWMFlickerConditions(WindowManagerTrace wmTrace) {
-        // Verify that there’s an non-app window with names NavigationBar, StatusBar above
-        // the app window and is visible in all winscope log entries.
-        WindowManagerTraceSubject.assertThat(wmTrace)
-                .isAboveAppWindowVisible(NAV_BAR_COMPONENT)
-                .isAboveAppWindowVisible(STATUS_BAR_COMPONENT)
-                .forAllEntries();
-
-        // Verify that all visible windows are visible for more than one consecutive entry
-        // in the log entries.
-        WindowManagerTraceSubject.assertThat(wmTrace)
-                .visibleWindowsShownMoreThanOneConsecutiveEntry()
-                .forAllEntries();
-        Log.v(TAG, "Successfully verified the window manager flicker conditions.");
-    }
-}
diff --git a/libraries/health/rules/tests/AndroidTest.xml b/libraries/health/rules/tests/AndroidTest.xml
new file mode 100644
index 0000000..a370a32
--- /dev/null
+++ b/libraries/health/rules/tests/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+<configuration description="Configuration for platform health rule tests.">
+  <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    <option name="force-root" value="true" />
+  </target_preparer>
+  <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+    <option name="cleanup-apks" value="true" />
+    <option name="test-file-name" value="PlatformRuleTests.apk" />
+  </target_preparer>
+  <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+    <option name="package" value="android.platform.test.rule.tests" />
+  </test>
+</configuration>
diff --git a/libraries/health/rules/tests/TEST_MAPPING b/libraries/health/rules/tests/TEST_MAPPING
new file mode 100644
index 0000000..7ca4174
--- /dev/null
+++ b/libraries/health/rules/tests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "PlatformRuleTests"
+    }
+  ]
+}
diff --git a/libraries/health/runners/longevity/platform/Android.bp b/libraries/health/runners/longevity/platform/Android.bp
index 91fde8d..0ad4e4a 100644
--- a/libraries/health/runners/longevity/platform/Android.bp
+++ b/libraries/health/runners/longevity/platform/Android.bp
@@ -40,6 +40,7 @@
         "common-platform-scenarios",
         "guava",
         "platform-test-composers",
+        "microbenchmark-device-lib"
     ],
     min_sdk_version: "26",
 }
diff --git a/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/LongevityClassRunner.java b/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/LongevityClassRunner.java
index f1cc67d..9e3db39 100644
--- a/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/LongevityClassRunner.java
+++ b/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/LongevityClassRunner.java
@@ -17,15 +17,11 @@
 package android.platform.test.longevity;
 
 import android.os.Bundle;
+import android.platform.test.microbenchmark.Microbenchmark;
+
 import androidx.annotation.VisibleForTesting;
 import androidx.test.InstrumentationRegistry;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
@@ -33,12 +29,18 @@
 import org.junit.internal.runners.statements.RunAfters;
 import org.junit.internal.runners.statements.RunBefores;
 import org.junit.runner.Description;
+import org.junit.runner.notification.StoppedByUserException;
 import org.junit.runners.BlockJUnit4ClassRunner;
 import org.junit.runners.model.FrameworkMethod;
 import org.junit.runners.model.InitializationError;
 import org.junit.runners.model.MultipleFailureException;
 import org.junit.runners.model.Statement;
-import org.junit.runner.notification.StoppedByUserException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * A {@link BlockJUnit4ClassRunner} that runs the test class's {@link BeforeClass} methods as {@link
@@ -119,6 +121,11 @@
     protected Statement withBefores(FrameworkMethod method, Object target, Statement statement) {
         List<FrameworkMethod> allBeforeMethods = new ArrayList<>();
         allBeforeMethods.addAll(getTestClass().getAnnotatedMethods(BeforeClass.class));
+        // Workaround to support @NoMetricBefore/@NoMetricAfter methods used in microbenchmark
+        // runner.
+        // TODO(b/205019000) TODO(b/148104702): these annotations seen as a temporary solutions
+        // and supposed to be eventually removed
+        allBeforeMethods.addAll(getTestClass().getAnnotatedMethods(Microbenchmark.NoMetricBefore.class));
         allBeforeMethods.addAll(getTestClass().getAnnotatedMethods(Before.class));
         return allBeforeMethods.isEmpty()
                 ? statement
@@ -131,9 +138,17 @@
      */
     @Override
     protected Statement withAfters(FrameworkMethod method, Object target, Statement statement) {
+        final List<FrameworkMethod> afterMethods = new ArrayList<>();
+        afterMethods.addAll(getTestClass().getAnnotatedMethods(After.class));
+        // Workaround to support @NoMetricBefore/@NoMetricAfter methods used in microbenchmark
+        // runner.
+        // TODO(b/205019000) TODO(b/148104702): these annotations seen as a temporary solutions
+        // and supposed to be eventually removed
+        afterMethods.addAll(getTestClass().getAnnotatedMethods(Microbenchmark.NoMetricAfter.class));
+
         return addRunAfters(
                 statement,
-                getTestClass().getAnnotatedMethods(After.class),
+                afterMethods,
                 getTestClass().getAnnotatedMethods(AfterClass.class),
                 target);
     }
diff --git a/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/Profile.java b/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/Profile.java
index 6b35160..b16f231 100644
--- a/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/Profile.java
+++ b/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/Profile.java
@@ -108,27 +108,36 @@
         if (mConfiguration == null) {
             return;
         }
-        mOrderedScenariosList = new ArrayList<>(mConfiguration.getScenariosList());
-        if (mOrderedScenariosList.isEmpty()) {
+        final List<Scenario> orderedScenarios = new ArrayList<>(mConfiguration.getScenariosList());
+        if (orderedScenarios.isEmpty()) {
             throw new IllegalArgumentException("Profile must have at least one scenario.");
         }
         if (mConfiguration.getSchedule().equals(Schedule.TIMESTAMPED)) {
-            Collections.sort(mOrderedScenariosList, new ScenarioTimestampComparator());
+            if (mConfiguration.getRepetitions() != 1) {
+                throw new IllegalArgumentException("Repetitions param not supported for TIMESTAMPED scheduler");
+            }
+
+            Collections.sort(orderedScenarios, new ScenarioTimestampComparator());
             try {
                 mFirstScenarioTimestampMs =
-                        TIMESTAMP_FORMATTER.parse(mOrderedScenariosList.get(0).getAt()).getTime();
+                        TIMESTAMP_FORMATTER.parse(orderedScenarios.get(0).getAt()).getTime();
             } catch (ParseException e) {
                 throw new IllegalArgumentException(
                         "Cannot parse the timestamp of the first scenario.", e);
             }
         } else if (mConfiguration.getSchedule().equals(Schedule.INDEXED)) {
-            Collections.sort(mOrderedScenariosList, new ScenarioIndexedComparator());
+            Collections.sort(orderedScenarios, new ScenarioIndexedComparator());
         } else if (mConfiguration.getSchedule().equals(Schedule.SEQUENTIAL)) {
             // Do nothing. Rely on the natural ordering specified in the profile.
         } else {
             throw new UnsupportedOperationException(
                     "Only scheduled profiles are currently supported.");
         }
+
+        mOrderedScenariosList = new ArrayList<>();
+        for (int i = 0; i < mConfiguration.getRepetitions(); i++) {
+            mOrderedScenariosList.addAll(orderedScenarios);
+        }
     }
 
     public List<Runner> getRunnerSequence(List<Runner> input) {
diff --git a/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/profile.proto b/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/profile.proto
index 1949adb..b2e799c 100644
--- a/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/profile.proto
+++ b/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/profile.proto
@@ -50,4 +50,9 @@
         optional AfterTest after_test = 5 [default = EXIT];
     }
     repeated Scenario scenarios = 2;
+
+    // Amount of times the whole scenarios will be executed.
+    // For example, scenarios A->B->C with repetitions = 2 will be executed twice in a row:
+    // A->B->C->A->B->C.
+    optional int32 repetitions = 3 [default = 1];
 }
diff --git a/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ProfileTest.java b/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ProfileTest.java
index aacee7c..94df8b8 100644
--- a/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ProfileTest.java
+++ b/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ProfileTest.java
@@ -69,6 +69,7 @@
                                     .setJourney(
                                             "android.platform.test.scenario.calendar.FlingWeekPage"))
                     .build();
+
     private static final String CONFIG_WITH_INVALID_JOURNEY_KEY = "config_with_invalid_journey";
     protected static final Configuration CONFIG_WITH_INVALID_JOURNEY =
             Configuration.newBuilder()
@@ -80,11 +81,38 @@
                                             "android.platform.test.scenario.calendar.FlingWeekPage"))
                     .addScenarios(Scenario.newBuilder().setAt("00:02:00").setJourney("invalid"))
                     .build();
+
+    private static final String CONFIG_WITH_REPEATED_TIMESTAMPED_JOURNEY_KEY =
+            "config_with_repeated_timestamped_journey";
+    protected static final Configuration CONFIG_WITH_REPEATED_TIMESTAMPED_JOURNEY =
+            Configuration.newBuilder()
+                    .setRepetitions(2)
+                    .setSchedule(Schedule.TIMESTAMPED)
+                    .addScenarios(
+                            Scenario.newBuilder().setJourney(
+                                    "android.platform.test.scenario.calendar.FlingWeekPage"))
+                    .build();
+
+    private static final String CONFIG_WITH_REPEATED_JOURNEY_KEY = "config_with_repeated_journey";
+    protected static final Configuration CONFIG_WITH_REPEATED_JOURNEY =
+            Configuration.newBuilder()
+                    .setRepetitions(2)
+                    .setSchedule(Schedule.SEQUENTIAL)
+                    .addScenarios(
+                            Scenario.newBuilder().setJourney(
+                                    "android.platform.test.scenario.calendar.FlingWeekPage"))
+                    .addScenarios(
+                            Scenario.newBuilder().setJourney(
+                                    "android.platform.test.scenario.calendar.FlingDayPage"))
+                    .build();
+
     private static final String CONFIG_WITH_MISSING_TIMESTAMPS_KEY =
             "config_with_missing_timestamps";
     protected static final ImmutableMap<String, Configuration> TEST_CONFIGS= ImmutableMap.of(
             VALID_CONFIG_KEY, VALID_CONFIG,
-            CONFIG_WITH_INVALID_JOURNEY_KEY, CONFIG_WITH_INVALID_JOURNEY);
+            CONFIG_WITH_INVALID_JOURNEY_KEY, CONFIG_WITH_INVALID_JOURNEY,
+            CONFIG_WITH_REPEATED_TIMESTAMPED_JOURNEY_KEY, CONFIG_WITH_REPEATED_TIMESTAMPED_JOURNEY,
+            CONFIG_WITH_REPEATED_JOURNEY_KEY, CONFIG_WITH_REPEATED_JOURNEY);
     private static final ImmutableList<String> AVAILABLE_JOURNEYS = ImmutableList.of(
             "android.platform.test.scenario.calendar.FlingWeekPage",
             "android.platform.test.scenario.calendar.FlingDayPage",
@@ -129,6 +157,37 @@
     }
 
     /**
+     * Tests that the returned runners are ordered according to their scheduled timestamps.
+     */
+    @Test
+    public void testProfileRepeatRespected() {
+        ImmutableList<String> expectedJourneyOrder = ImmutableList.of(
+            "android.platform.test.scenario.calendar.FlingWeekPage",
+            "android.platform.test.scenario.calendar.FlingDayPage",
+            "android.platform.test.scenario.calendar.FlingWeekPage",
+            "android.platform.test.scenario.calendar.FlingDayPage");
+
+        List<Runner> output = getProfile(getArguments(CONFIG_WITH_REPEATED_JOURNEY_KEY))
+                .getRunnerSequence(mMockInput);
+        List<String> outputDescriptions = output.stream().map(r ->
+                r.getDescription().getDisplayName()).collect(Collectors.toList());
+        boolean respected = outputDescriptions.equals(expectedJourneyOrder);
+        assertThat(respected).isTrue();
+    }
+
+    /**
+     * Tests that the returned runners are ordered according to their scheduled timestamps.
+     */
+    @Test
+    public void testTimestampedProfileWithRepeatThrows() {
+        exceptionThrown.expect(IllegalArgumentException.class);
+        exceptionThrown.expectMessage("Repetitions param not supported for TIMESTAMPED scheduler");
+
+        List<Runner> output = getProfile(getArguments(CONFIG_WITH_REPEATED_TIMESTAMPED_JOURNEY_KEY))
+                .getRunnerSequence(mMockInput);
+    }
+
+    /**
      * Tests that an exception is thrown for profiles with invalid scenario names.
      */
     @Test
diff --git a/libraries/health/runners/microbenchmark/src/android/platform/test/microbenchmark/Functional.java b/libraries/health/runners/microbenchmark/src/android/platform/test/microbenchmark/Functional.java
index 4043b37..de73e30 100644
--- a/libraries/health/runners/microbenchmark/src/android/platform/test/microbenchmark/Functional.java
+++ b/libraries/health/runners/microbenchmark/src/android/platform/test/microbenchmark/Functional.java
@@ -15,6 +15,9 @@
  */
 package android.platform.test.microbenchmark;
 
+import android.platform.test.rule.ArtifactSaver;
+import android.platform.test.rule.SamplerRule;
+
 import org.junit.internal.AssumptionViolatedException;
 import org.junit.internal.runners.statements.RunAfters;
 import org.junit.internal.runners.statements.RunBefores;
@@ -27,7 +30,10 @@
 import org.junit.runners.model.Statement;
 import org.junit.runners.model.TestClass;
 
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
 
 /**
  * Runner for functional tests that's compatible with annotations used in microbenchmark
@@ -36,36 +42,95 @@
  * one.
  */
 public class Functional extends BlockJUnit4ClassRunner {
+
+    private final Set<FrameworkMethod> mMethodsWithSavedArtifacts = new HashSet<>();
+
     public Functional(Class<?> klass) throws InitializationError {
         super(new TestClass(klass));
     }
 
+    private Statement artifactSaver(Statement statement, Stream<FrameworkMethod> methods) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                try {
+                    statement.evaluate();
+                } catch (Throwable e) {
+                    methods.forEach(
+                            method -> {
+                                if (mMethodsWithSavedArtifacts.contains(method)) return;
+                                mMethodsWithSavedArtifacts.add(method);
+                                ArtifactSaver.onError(describeChild(method), e);
+                            });
+                    throw e;
+                }
+            }
+        };
+    }
+
     @Override
-    protected Statement withBefores(FrameworkMethod method, Object target, Statement statement) {
-        statement = super.withBefores(method, target, statement);
+    protected Statement withBefores(FrameworkMethod method, Object target, Statement s) {
+        s = super.withBefores(method, target, s);
 
         // Add @NoMetricBefore's
         List<FrameworkMethod> befores =
                 getTestClass().getAnnotatedMethods(Microbenchmark.NoMetricBefore.class);
-        return befores.isEmpty() ? statement : new RunBefores(statement, befores, target);
+        final Statement statement = befores.isEmpty() ? s : new RunBefores(s, befores, target);
+        // Error artifact saver for exceptions thrown in test-befores and the test method, before
+        // test-afters and the exit part of test rules are executed.
+        return artifactSaver(statement, Stream.of(method));
     }
 
     @Override
-    protected Statement withAfters(FrameworkMethod method, Object target, Statement statement) {
+    protected Statement withAfters(FrameworkMethod method, Object target, Statement s) {
         // Add @NoMetricAfter's
         List<FrameworkMethod> afters =
                 getTestClass().getAnnotatedMethods(Microbenchmark.NoMetricAfter.class);
-        statement = afters.isEmpty() ? statement : new RunAfters(statement, afters, target);
+        s = afters.isEmpty() ? s : new RunAfters(s, afters, target);
 
-        return super.withAfters(method, target, statement);
+        final Statement statement = super.withAfters(method, target, s);
+        // Error artifact saver for exceptions thrown in "method-afters", i.e. outside the method
+        // and method-befores, but before the finalizing the rules.
+        return artifactSaver(statement, Stream.of(method));
+    }
+
+    @Override
+    protected Statement methodBlock(FrameworkMethod method) {
+        // Error artifact saver for exceptions thrown outside "method-afters", i.e. in method rules.
+        return artifactSaver(super.methodBlock(method), Stream.of(method));
+    }
+
+    @Override
+    protected Statement withBeforeClasses(Statement s) {
+        // Error artifact saver for exceptions thrown in class-befores, before class-afters and
+        // the exit part of class rules are executed.
+        return artifactSaver(super.withBeforeClasses(s), getChildren().stream());
+    }
+
+    @Override
+    protected Statement withAfterClasses(Statement s) {
+        // Error artifact saver for exceptions thrown outside "class-befores", but inside class
+        // rules, i.e. in class afters.
+        return artifactSaver(super.withAfterClasses(s), getChildren().stream());
     }
 
     @Override
     protected Statement classBlock(RunNotifier notifier) {
-        final Statement statement = super.classBlock(notifier);
+        // Error artifact saver for exceptions thrown outside class befores and afters, i.e. in
+        // class rules.
+        final Statement parentStatement;
+        try {
+            SamplerRule.enable(true);
+            parentStatement = super.classBlock(notifier);
+        } finally {
+            SamplerRule.enable(false);
+        }
+        final Statement statement = artifactSaver(parentStatement, getChildren().stream());
         return new Statement() {
             @Override
             public void evaluate() throws Throwable {
+                mMethodsWithSavedArtifacts.clear();
+
                 try {
                     statement.evaluate();
                 } catch (Throwable e) {
diff --git a/libraries/health/utils/src/android/platform/test/util/HealthTestingUtils.java b/libraries/health/utils/src/android/platform/test/util/HealthTestingUtils.java
new file mode 100644
index 0000000..3dd94b0
--- /dev/null
+++ b/libraries/health/utils/src/android/platform/test/util/HealthTestingUtils.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.platform.test.util;
+
+import android.os.SystemClock;
+
+import org.junit.Assert;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+/** Helper class for writing health tests. */
+public class HealthTestingUtils {
+
+    private static final String TAG = "HealthTestingUtils";
+    private static final int SLEEP_MS = 100;
+    private static final int WAIT_TIME_MS = 10000;
+    private static final int DEFAULT_SETTLE_TIME_MS = 3000;
+
+    private HealthTestingUtils() {}
+
+    /** Supplier of a boolean that can throw an exception. */
+    public interface Condition {
+        boolean isTrue() throws Throwable;
+    }
+
+    /**
+     * Waits for a diagnostics to become null within 10 sec.
+     *
+     * @param diagnostics Supplier of the error message. It should return null in case of success.
+     *     The method repeatedly calls this provider while it's returning non-null. If it keeps
+     *     returning non-null after 10 sec, the method throws an exception using the last string
+     *     returned by the provider. Otherwise, it succeeds.
+     */
+    public static void waitForNullDiag(Supplier<String> diagnostics) {
+        final String[] lastDiag = new String[1];
+        waitForCondition(() -> lastDiag[0], () -> (lastDiag[0] = diagnostics.get()) == null);
+    }
+
+    /**
+     * Waits for a value producer to produce a result that's isPresent() and fails if it doesn't
+     * happen within 10 sec.
+     *
+     * @param message Supplier of the error message.
+     * @param resultProducer Result producer.
+     * @return The present value returned by the producer.
+     */
+    public static <T> T waitForValuePresent(
+            Supplier<String> message, Supplier<Optional<T>> resultProducer) {
+        class ResultHolder {
+            public T value;
+        }
+        final ResultHolder result = new ResultHolder();
+
+        waitForCondition(
+                message,
+                () -> {
+                    final Optional<T> optionalResult = resultProducer.get();
+                    if (optionalResult.isPresent()) {
+                        result.value = optionalResult.get();
+                        return true;
+                    } else {
+                        return false;
+                    }
+                });
+
+        return result.value;
+    }
+
+    /**
+     * Waits for a condition and fails if it doesn't become true within 10 sec.
+     *
+     * @param message Supplier of the error message.
+     * @param condition Condition.
+     */
+    public static void waitForCondition(Supplier<String> message, Condition condition) {
+        waitForCondition(message, condition, WAIT_TIME_MS);
+    }
+
+    /**
+     * Waits for a condition and fails if it doesn't become true within specified time period.
+     *
+     * @param message Supplier of the error message.
+     * @param condition Condition.
+     * @param timeoutMs Timeout.
+     */
+    public static void waitForCondition(
+            Supplier<String> message, Condition condition, long timeoutMs) {
+        final long startTime = SystemClock.uptimeMillis();
+        while (SystemClock.uptimeMillis() < startTime + timeoutMs) {
+            try {
+                if (condition.isTrue()) {
+                    return;
+                }
+            } catch (Throwable t) {
+                throw new RuntimeException(t);
+            }
+            SystemClock.sleep(SLEEP_MS);
+        }
+
+        // Check once more before failing.
+        try {
+            if (condition.isTrue()) {
+                return;
+            }
+        } catch (Throwable t) {
+            throw new RuntimeException(t);
+        }
+
+        Assert.fail(message.get());
+    }
+
+    /** @see HealthTestingUtils#waitForValueToSettle */
+    public static <T> T waitForValueToSettle(Supplier<String> errorMessage, Supplier<T> supplier) {
+        return waitForValueToSettle(
+                errorMessage,
+                supplier,
+                /* minimumSettleTime= */ DEFAULT_SETTLE_TIME_MS,
+                /* timeoutMs= */ WAIT_TIME_MS);
+    }
+
+    /**
+     * Waits for the supplier to return the same value for a specified time period. If the value
+     * changes, the timer gets restarted. Fails when reaching the timeout. The minimum running time
+     * of this method is the settle time.
+     *
+     * @return the settled value. Fails if it doesn't settle.
+     */
+    public static <T> T waitForValueToSettle(
+            Supplier<String> errorMessage,
+            Supplier<T> supplier,
+            long minimumSettleTime,
+            long timeoutMs) {
+        final long startTime = SystemClock.uptimeMillis();
+        long settledSince = startTime;
+        T previousValue = null;
+
+        while (SystemClock.uptimeMillis() < startTime + timeoutMs) {
+            T newValue = supplier.get();
+            final long currentTime = SystemClock.uptimeMillis();
+
+            if (!Objects.equals(previousValue, newValue)) {
+                settledSince = currentTime;
+                previousValue = newValue;
+            } else if (currentTime >= settledSince + minimumSettleTime) {
+                return previousValue;
+            }
+
+            SystemClock.sleep(SLEEP_MS);
+        }
+
+        Assert.fail(errorMessage.get());
+
+        return null;
+    }
+}
diff --git a/libraries/launcher-helper/src/android/support/test/launcherhelper/CommonLauncherHelper.java b/libraries/launcher-helper/src/android/support/test/launcherhelper/CommonLauncherHelper.java
index ed1d8ae..a9902c9 100644
--- a/libraries/launcher-helper/src/android/support/test/launcherhelper/CommonLauncherHelper.java
+++ b/libraries/launcher-helper/src/android/support/test/launcherhelper/CommonLauncherHelper.java
@@ -201,7 +201,7 @@
         }
 
         // Go to the home page
-        final Workspace workspace = launcher.pressHome();
+        final Workspace workspace = launcher.goHome();
         AppIcon icon = workspace.tryGetWorkspaceAppIcon(appName);
         if (icon == null) {
             icon = workspace.switchToAllApps().getAppIcon(appName);
diff --git a/libraries/launcher-helper/src/android/support/test/launcherhelper/NexusLauncherStrategy.java b/libraries/launcher-helper/src/android/support/test/launcherhelper/NexusLauncherStrategy.java
index 9ea5a5f..42e1162 100644
--- a/libraries/launcher-helper/src/android/support/test/launcherhelper/NexusLauncherStrategy.java
+++ b/libraries/launcher-helper/src/android/support/test/launcherhelper/NexusLauncherStrategy.java
@@ -143,7 +143,7 @@
     /** {@inheritDoc} */
     @Override
     public void openOverview() {
-        mLauncher.pressHome().switchToOverview();
+        mLauncher.goHome().switchToOverview();
     }
 
     /** {@inheritDoc} */
diff --git a/libraries/sts-common-util/device-side/src/com/android/sts/common/util/StsExtraBusinessLogicTestCase.java b/libraries/sts-common-util/device-side/src/com/android/sts/common/util/StsExtraBusinessLogicTestCase.java
index 5f53362..f9aa294 100644
--- a/libraries/sts-common-util/device-side/src/com/android/sts/common/util/StsExtraBusinessLogicTestCase.java
+++ b/libraries/sts-common-util/device-side/src/com/android/sts/common/util/StsExtraBusinessLogicTestCase.java
@@ -17,24 +17,24 @@
 package com.android.sts.common.util;
 
 import android.os.Build;
+import android.system.Os;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
 
 import com.android.compatibility.common.util.ExtraBusinessLogicTestCase;
-import com.android.compatibility.common.util.PropertyUtil;
 
 import org.junit.Rule;
 import org.junit.runner.Description;
 
 import java.time.LocalDate;
 import java.util.List;
+import java.util.Optional;
 
 /** The device-side implementation of StsLogic. */
 public class StsExtraBusinessLogicTestCase extends ExtraBusinessLogicTestCase implements StsLogic {
 
     private LocalDate deviceSpl = null;
-    private LocalDate kernelSpl = null;
     @Rule public DescriptionProvider descriptionProvider = new DescriptionProvider();
 
     protected StsExtraBusinessLogicTestCase() {
@@ -63,18 +63,8 @@
     }
 
     @Override
-    public LocalDate getKernelSpl() {
-        if (kernelSpl == null) {
-            // set in:
-            // test/sts/tools/sts-tradefed/src/com/android/tradefed/targetprep/multi/KernelSPL.java
-            String kernelSplString =
-                    PropertyUtil.getProperty("persist.sts.build_version_kernel_security_patch");
-            if (kernelSplString == null) {
-                return null;
-            }
-            kernelSpl = SplUtils.localDateFromSplString(kernelSplString);
-        }
-        return kernelSpl;
+    public Optional<LocalDate> getKernelBuildDate() {
+        return UnameVersion.parseBuildTimestamp(Os.uname().version);
     }
 
     @Override
diff --git a/libraries/sts-common-util/device-side/tests/Android.bp b/libraries/sts-common-util/device-side/tests/Android.bp
new file mode 100644
index 0000000..0bb445f
--- /dev/null
+++ b/libraries/sts-common-util/device-side/tests/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2022 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "StsCommonUtilDeviceTests",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "androidx.test.runner",
+        "sts-device-util",
+        "junit",
+    ],
+}
diff --git a/libraries/sts-common-util/device-side/tests/AndroidManifest.xml b/libraries/sts-common-util/device-side/tests/AndroidManifest.xml
new file mode 100644
index 0000000..297d913
--- /dev/null
+++ b/libraries/sts-common-util/device-side/tests/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.sts.common.util.tests" >
+    <application>
+        <uses-library android:name="android.test.runner"/>
+    </application>
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.sts.common.util.tests"
+        android:label="Tests for STS common device-side utils" />
+</manifest>
+
diff --git a/libraries/sts-common-util/device-side/tests/AndroidTest.xml b/libraries/sts-common-util/device-side/tests/AndroidTest.xml
new file mode 100644
index 0000000..616f027
--- /dev/null
+++ b/libraries/sts-common-util/device-side/tests/AndroidTest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (c) 2022 Google Inc.
+
+    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.
+-->
+<configuration description="Config for STS common device-side utils tests">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="StsCommonUtilDeviceTests.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.sts.common.util.tests" />
+    </test>
+</configuration>
diff --git a/libraries/sts-common-util/device-side/tests/src/com/android/sts/common/util/UnameVersionDeviceTest.java b/libraries/sts-common-util/device-side/tests/src/com/android/sts/common/util/UnameVersionDeviceTest.java
new file mode 100644
index 0000000..d4f4ba3
--- /dev/null
+++ b/libraries/sts-common-util/device-side/tests/src/com/android/sts/common/util/UnameVersionDeviceTest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 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.sts.common.util;
+
+import static org.junit.Assert.assertTrue;
+
+import android.system.Os;
+
+import org.junit.Test;
+
+public class UnameVersionDeviceTest {
+
+    @Test
+    public final void testOsUnameVersion() throws Exception {
+        String version = Os.uname().version;
+        assertTrue(
+                "Could not parse device version: " + version,
+                UnameVersion.parseBuildTimestamp(version).isPresent());
+    }
+}
diff --git a/libraries/sts-common-util/host-side/Android.bp b/libraries/sts-common-util/host-side/Android.bp
index f151cd3..7a07ef4 100644
--- a/libraries/sts-common-util/host-side/Android.bp
+++ b/libraries/sts-common-util/host-side/Android.bp
@@ -24,6 +24,7 @@
 
     static_libs: [
         "sts-common-util-lib",
+        "xz-java",
     ],
 
     libs: [
diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/FridaUtils.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/FridaUtils.java
new file mode 100644
index 0000000..f9b28f4
--- /dev/null
+++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/FridaUtils.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2022 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.sts.common;
+
+import static com.android.sts.common.CommandUtil.runAndCheck;
+import static java.util.stream.Collectors.toList;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+import org.tukaani.xz.XZInputStream;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.sts.common.util.FridaUtilsBusinessLogicHandler;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.FileUtil;
+import com.android.tradefed.util.RunUtil;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonParser;
+
+// import org.json.JSONException;
+
+public class FridaUtils implements AutoCloseable {
+    private static final String PRODUCT_CPU_ABI_KEY = "ro.product.cpu.abi";
+    private static final String PRODUCT_CPU_ABILIST_KEY = "ro.product.cpu.abilist";
+    private static final String FRIDA_PACKAGE = "frida-inject";
+    private static final String FRIDA_OS = "android";
+    private static final String TMP_PATH = "/data/local/tmp/";
+
+    // https://docs.github.com/en/rest/releases/releases
+    private static final String FRIDA_LATEST_GITHUB_API_URL =
+            "https://api.github.com/repos/frida/frida/releases/latest";
+    private static final String FRIDA_ASSETS_GITHUB_API_URL =
+            "https://api.github.com/repos/frida/frida/releases";
+
+    private final String fridaAbi;
+    private final String fridaVersion;
+    private final ITestDevice device;
+    private final CompatibilityBuildHelper buildHelper;
+    private final String remoteFridaExeName;
+    private List<Integer> runningPids = new ArrayList<>();
+    private List<String> fridaFiles = new ArrayList<>();
+
+    private FridaUtils(ITestDevice device, IBuildInfo buildInfo)
+            throws DeviceNotAvailableException, UnsupportedOperationException, IOException {
+        this.device = device;
+        this.buildHelper = new CompatibilityBuildHelper(buildInfo);
+
+        // Figure out which version we should be using
+        Optional<String> versionOpt = FridaUtilsBusinessLogicHandler.getFridaVersion();
+        String version = versionOpt.isPresent() ? versionOpt.get() : getLatestFridaVersion();
+
+        // Figure out which Frida arch we should be using for our device
+        fridaAbi = getFridaAbiFor(device);
+        fridaVersion = version;
+        String fridaExeName =
+                String.format("%s-%s-%s-%s", FRIDA_PACKAGE, fridaVersion, FRIDA_OS, fridaAbi);
+
+        // Download Frida if needed
+        File localFridaExe;
+        try {
+            localFridaExe = buildHelper.getTestFile(fridaExeName);
+            CLog.d("%s found at %s", fridaExeName, localFridaExe.getAbsolutePath());
+        } catch (FileNotFoundException e) {
+            CLog.d("%s not found.", fridaExeName);
+            String fridaUrl = getFridaDownloadUrl(fridaVersion);
+            CLog.d("Downloading Frida from %s", fridaUrl);
+            try {
+                URL url = new URL(fridaUrl);
+                URLConnection conn = url.openConnection();
+                XZInputStream in = new XZInputStream(conn.getInputStream());
+                File tmpOutput = FileUtil.createTempFile("STS", fridaExeName);
+                FileUtil.writeToFile(in, tmpOutput);
+                localFridaExe = new File(buildHelper.getTestsDir(), fridaExeName);
+                FileUtil.copyFile(tmpOutput, localFridaExe);
+                tmpOutput.delete();
+            } catch (Exception e2) {
+                CLog.e(
+                        "Could not download Frida. Please manually download '%s' and extract to "
+                                + "'%s', renaming the file to '%s' as necessary.",
+                        fridaUrl, buildHelper.getTestsDir(), fridaExeName);
+                throw e2;
+            }
+        }
+
+        // Upload Frida binary to device
+        device.enableAdbRoot();
+        remoteFridaExeName = new File(TMP_PATH, localFridaExe.getName()).getAbsolutePath();
+        device.pushFile(localFridaExe, remoteFridaExeName);
+        runAndCheck(device, String.format("chmod a+x '%s'", remoteFridaExeName));
+        fridaFiles.add(remoteFridaExeName);
+        device.disableAdbRoot();
+    }
+
+    /**
+     * Find out which Frida binary we need and download it if needed.
+     *
+     * @param device device to use Frida on
+     * @param buildInfo test device build info (from test.getBuild())
+     * @return an AutoCloseable FridaUtils object that can be used to run Frida scripts with
+     */
+    public static FridaUtils withFrida(ITestDevice device, IBuildInfo buildInfo)
+            throws DeviceNotAvailableException, UnsupportedOperationException, IOException {
+        return new FridaUtils(device, buildInfo);
+    }
+
+    /**
+     * Upload and run frida script on given process.
+     *
+     * @param fridaJsScriptContent Content of the Frida JS script. Note: this is not a file name
+     * @param pid PID of the process to attach Frida to
+     * @return ByteArrayOutputStream containing stdout and stderr of frida command
+     */
+    public ByteArrayOutputStream withFridaScript(final String fridaJsScriptContent, int pid)
+            throws DeviceNotAvailableException, FileNotFoundException, IOException,
+                    TimeoutException, InterruptedException {
+        // Upload Frida script to device
+        device.enableAdbRoot();
+        String uuid = UUID.randomUUID().toString();
+        String remoteFridaJsScriptName =
+                new File(TMP_PATH, "frida_" + uuid + ".js").getAbsolutePath();
+        device.pushString(fridaJsScriptContent, remoteFridaJsScriptName);
+        fridaFiles.add(remoteFridaJsScriptName);
+
+        // Execute Frida, binding to given PID, in the background
+        List<String> cmd =
+                List.of(
+                        "adb",
+                        "-s",
+                        device.getSerialNumber(),
+                        "shell",
+                        remoteFridaExeName,
+                        "-p",
+                        String.valueOf(pid),
+                        "-s",
+                        remoteFridaJsScriptName,
+                        "--runtime=v8");
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        RunUtil.getDefault().runCmdInBackground(cmd, output);
+
+        // frida can fail to attach after a short pause so wait for that
+        TimeUnit.SECONDS.sleep(5);
+        try {
+            Map<Integer, String> pids =
+                    ProcessUtil.waitProcessRunning(device, "^" + remoteFridaExeName);
+            assertEquals("Unexpected Frida processes with the same name", 1, pids.size());
+            runningPids.add(pids.keySet().iterator().next());
+        } catch (Exception e) {
+            CLog.e(e);
+            CLog.e("Frida attach output: %s", output.toString(StandardCharsets.UTF_8));
+            throw e;
+        }
+        device.disableAdbRoot();
+        return output;
+    }
+
+    @Override
+    /** Kill all running Frida processes and delete all files uploaded. */
+    public void close() throws DeviceNotAvailableException, TimeoutException {
+        device.enableAdbRoot();
+        for (Integer pid : runningPids) {
+            ProcessUtil.killPid(device, pid.intValue(), 10_000L);
+        }
+        for (String file : fridaFiles) {
+            device.deleteFile(file);
+        }
+        device.disableAdbRoot();
+    }
+
+    /**
+     * Return the best ABI of Frida that we should download for given device.
+     *
+     * <p>Throw UnsupportedOperationException if Frida does not support device's ABI.
+     */
+    private String getFridaAbiFor(ITestDevice device)
+            throws DeviceNotAvailableException, UnsupportedOperationException {
+        for (String abi : getSupportedAbis(device)) {
+            if (abi.startsWith("arm64")) {
+                return "arm64";
+            } else if (abi.startsWith("armeabi")) {
+                return "arm";
+            } else if (abi.startsWith("x86_64")) {
+                return "x86_64";
+            } else if (abi.startsWith("x86")) {
+                return "x86";
+            }
+        }
+        throw new UnsupportedOperationException(
+                String.format("Device %s is not supported by Frida", device.getSerialNumber()));
+    }
+
+    /** Return a list of supported ABIs by the device in order of preference. */
+    private List<String> getSupportedAbis(ITestDevice device) throws DeviceNotAvailableException {
+        String primaryAbi = device.getProperty(PRODUCT_CPU_ABI_KEY);
+        String[] supportedAbis = device.getProperty(PRODUCT_CPU_ABILIST_KEY).split(",");
+        return Stream.concat(Stream.of(primaryAbi), Arrays.stream(supportedAbis))
+                .distinct()
+                .collect(toList());
+    }
+
+    private static JsonElement getJson(String url) throws IOException, JsonParseException {
+        URLConnection conn = new URL(url).openConnection();
+        InputStreamReader reader = new InputStreamReader(conn.getInputStream());
+        return new JsonParser().parse(reader);
+    }
+
+    private static String getLatestFridaVersion() throws IOException, JsonParseException {
+        return getJson(FRIDA_LATEST_GITHUB_API_URL).getAsJsonObject().get("tag_name").getAsString();
+    }
+
+    private String getFridaDownloadUrl(String version) throws IOException, JsonParseException {
+        assertNotNull(
+                "Did not get frida filename template from BusinessLogic",
+                FridaUtilsBusinessLogicHandler.getFridaFilenameTemplate());
+        String name =
+                MessageFormat.format(
+                        FridaUtilsBusinessLogicHandler.getFridaFilenameTemplate(),
+                        FRIDA_PACKAGE,
+                        fridaVersion,
+                        FRIDA_OS,
+                        fridaAbi);
+
+        JsonArray releases = getJson(FRIDA_ASSETS_GITHUB_API_URL).getAsJsonArray();
+
+        for (JsonElement release : releases) {
+            if (release.getAsJsonObject().get("tag_name").getAsString().equals(version)) {
+                for (JsonElement asset : release.getAsJsonObject().getAsJsonArray("assets")) {
+                    if (asset.getAsJsonObject().get("name").getAsString().equals(name)) {
+                        return asset.getAsJsonObject().get("browser_download_url").getAsString();
+                    }
+                }
+            }
+        }
+        fail("Could not find frida asset '" + name + "' in '" + FRIDA_ASSETS_GITHUB_API_URL + "'");
+        return null;
+    }
+}
diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/RootcanalUtils.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/RootcanalUtils.java
index 3849b95..ea22885 100644
--- a/libraries/sts-common-util/host-side/src/com/android/sts/common/RootcanalUtils.java
+++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/RootcanalUtils.java
@@ -16,14 +16,24 @@
 
 package com.android.sts.common;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertFalse;
 import static com.android.sts.common.CommandUtil.runAndCheck;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileWriter;
+import java.io.InputStream;
 import java.io.IOException;
 import java.io.StringReader;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.TimeUnit;
 import java.util.List;
@@ -74,34 +84,31 @@
         assertNotNull("Device not set", device);
         try {
             device.enableAdbRoot();
+            ProcessUtil.killAll(
+                    device, "android\\.hardware\\.bluetooth@1\\.1-service\\.sim", 10_000, false);
             runAndCheck(device, String.format("rm -rf '%s'", LOCK_FILENAME));
             device.disableAdbRoot();
             // OverlayFsUtils' finished() will restart the device.
             overlayFsUtils.finished(d);
+            runAndCheck(device, "svc bluetooth enable");
         } catch (DeviceNotAvailableException e) {
             throw new AssertionError("Device unavailable when cleaning up", e);
+        } catch (TimeoutException e) {
+            CLog.w("Could not kill rootcanal HAL during cleanup");
         }
     }
 
-    /** Replace existing HAL with RootCanal HAL on current device. */
-    public void enableRootcanal()
-            throws DeviceNotAvailableException, IOException, InterruptedException,
-                    TimeoutException {
-        enableRootcanal(6111);
-    }
-
     /**
      * Replace existing HAL with RootCanal HAL on current device.
      *
-     * @param port host TCP port to adb-forward to rootcanal control port.
+     * @return an instance of RootcanalController
      */
-    public void enableRootcanal(int port)
+    public RootcanalController enableRootcanal()
             throws DeviceNotAvailableException, IOException, InterruptedException,
                     TimeoutException {
         ITestDevice device = test.getDevice();
-        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(test.getBuild());
         assertNotNull("Device not set", device);
-        assertNotNull("Build not set", buildHelper);
+        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(test.getBuild());
 
         // Check and made sure we're not calling this more than once for a device
         assertFalse("rootcanal set up called more than once", device.doesFileExist(LOCK_FILENAME));
@@ -202,7 +209,14 @@
             }
         }
 
-        device.executeAdbCommand("forward", "tcp:6111", String.format("tcp:%d", port));
+        // Forward root canal control ports on the device to the host
+        int testPort = findOpenPort();
+        device.executeAdbCommand("forward", String.format("tcp:%d", testPort), "tcp:6111");
+
+        int hciPort = findOpenPort();
+        device.executeAdbCommand("forward", String.format("tcp:%d", hciPort), "tcp:6211");
+
+        return new RootcanalController(testPort, hciPort);
     }
 
     private void tryUpdateVintfManifest(ITestDevice device)
@@ -269,4 +283,183 @@
             TimeUnit.MILLISECONDS.sleep(250);
         }
     }
+
+    /** Find an open TCP port on the host */
+    private static int findOpenPort() throws IOException {
+        try (ServerSocket socket = new ServerSocket(0)) {
+            socket.setReuseAddress(true);
+            return socket.getLocalPort();
+        }
+    }
+
+    /** Class that encapsulates a virtual HCI device that can be controlled by HCI commands. */
+    public static class HciDevice implements AutoCloseable {
+        private static final String READ_FAIL_MSG = "Failed to read HCI packet";
+        private final Socket hciSocket;
+
+        private HciDevice(Socket hciSocket) {
+            this.hciSocket = hciSocket;
+        }
+
+        @Override
+        public void close() throws IOException {
+            hciSocket.close();
+        }
+
+        /**
+         * Convenient wrapper around sendHciPacket to send a HCI command packet to device.
+         *
+         * @param ogf Opcode group field
+         * @param ocf Opcode command field
+         * @param params the rest of the command parameters
+         */
+        public void sendHciCmd(int ogf, int ocf, byte[] params) throws IOException {
+            assertTrue("params length must be less than 256 bytes", params.length < 256);
+            ByteBuffer cmd = ByteBuffer.allocate(4 + params.length).order(ByteOrder.LITTLE_ENDIAN);
+            int opcode = (ogf << 10) | ocf;
+            cmd.put((byte) 0x01).putShort((short) opcode).put((byte) params.length).put(params);
+            sendHciPacket(cmd.array());
+        }
+
+        /**
+         * Send raw HCI packet to device.
+         *
+         * @param packet raw packet data to send to device
+         */
+        public void sendHciPacket(byte[] packet) throws IOException {
+            CLog.d("sending HCI: %s", Arrays.toString(packet));
+            hciSocket.getOutputStream().write(packet);
+        }
+
+        /** Read one HCI packet from device, blocking until data is available. */
+        public byte[] readHciPacket() throws IOException {
+            ByteArrayOutputStream ret = new ByteArrayOutputStream();
+            InputStream in = hciSocket.getInputStream();
+
+            // Read the packet type
+            byte[] typeBuf = new byte[1];
+            assertEquals(READ_FAIL_MSG, 1, in.read(typeBuf, 0, 1));
+            ret.write(typeBuf);
+
+            // Read the header and figure out how much data to read
+            // according to BT core spec 5.2 vol 4 part A section 2 & part E section 5.4
+            byte[] hdrBuf = new byte[4];
+            int dataLength;
+
+            switch (typeBuf[0]) {
+                case 0x01: // Command packet
+                case 0x03: // Synch data packet
+                    assertEquals(READ_FAIL_MSG, 3, in.read(hdrBuf, 0, 3));
+                    ret.write(hdrBuf, 0, 3);
+                    dataLength = hdrBuf[2];
+                    break;
+
+                case 0x02: // Async data packet
+                    assertEquals(READ_FAIL_MSG, 4, in.read(hdrBuf, 0, 4));
+                    ret.write(hdrBuf, 0, 4);
+                    dataLength = (((int) hdrBuf[2]) & 0xFF) | ((((int) hdrBuf[3]) & 0xFF) << 8);
+                    break;
+
+                case 0x04: // Event
+                    assertEquals(READ_FAIL_MSG, 2, in.read(hdrBuf, 0, 2));
+                    ret.write(hdrBuf, 0, 2);
+                    dataLength = hdrBuf[1];
+                    break;
+
+                case 0x05: // ISO synch data packet
+                    assertEquals(READ_FAIL_MSG, 4, in.read(hdrBuf, 0, 4));
+                    ret.write(hdrBuf, 0, 4);
+                    dataLength = (((int) hdrBuf[2]) & 0xFF) | ((((int) hdrBuf[3]) & 0xFC) << 6);
+                    break;
+
+                default:
+                    throw new IOException("Unexpected packet type: " + String.valueOf(typeBuf[0]));
+            }
+
+            // Read the data payload
+            byte[] data = new byte[dataLength];
+            assertEquals(READ_FAIL_MSG, dataLength, in.read(data, 0, dataLength));
+            ret.write(data, 0, dataLength);
+
+            return ret.toByteArray();
+        }
+    }
+
+    public static class RootcanalController implements AutoCloseable {
+        private final int testPort;
+        private final int hciPort;
+        private Socket rootcanalTestChannel = null;
+        private List<HciDevice> hciDevices = new ArrayList<>();
+
+        private RootcanalController(int testPort, int hciPort)
+                throws IOException, InterruptedException {
+            this.testPort = testPort;
+            this.hciPort = hciPort;
+            CLog.d("Rootcanal controller initialized; testPort=%d, hciPort=%d", testPort, hciPort);
+        }
+
+        @Override
+        public void close() throws IOException {
+            rootcanalTestChannel.close();
+            for (HciDevice dev : hciDevices) {
+                dev.close();
+            }
+        }
+
+        /**
+         * Create a new HCI device by connecting to rootcanal's HCI socket.
+         *
+         * @return HciDevice object that allows sending/receiving from the HCI port
+         */
+        public HciDevice createHciDevice()
+                throws DeviceNotAvailableException, IOException, InterruptedException {
+            HciDevice dev = new HciDevice(new Socket("localhost", hciPort));
+            hciDevices.add(dev);
+            return dev;
+        }
+
+        /**
+         * Send one command to rootcanal test channel.
+         *
+         * <p>Send `help` command for list of accepted commands from Rootcanal.
+         *
+         * @param cmd command to send
+         * @param args arguments for the command
+         * @return Response string from rootcanal
+         */
+        public String sendTestChannelCommand(String cmd, String... args)
+                throws IOException, InterruptedException {
+            if (rootcanalTestChannel == null) {
+                rootcanalTestChannel = new Socket("localhost", testPort);
+                CLog.d(
+                        "RootCanal test channel init: "
+                                + readTestChannel(rootcanalTestChannel.getInputStream()));
+            }
+
+            // Translated from system/bt/vendor_libs/test_vendor_lib/scripts/test_channel.py
+            ByteArrayOutputStream msg = new ByteArrayOutputStream();
+            msg.write(cmd.length());
+            msg.write(cmd.getBytes("ASCII"));
+            msg.write(args.length);
+            for (String arg : args) {
+                msg.write(arg.length());
+                msg.write(arg.getBytes("ASCII"));
+            }
+
+            rootcanalTestChannel.getOutputStream().write(msg.toByteArray());
+            return readTestChannel(rootcanalTestChannel.getInputStream());
+        }
+
+        /** Read one message from rootcanal test channel. */
+        private String readTestChannel(InputStream in) throws IOException {
+            // Translated from system/bt/vendor_libs/test_vendor_lib/scripts/test_channel.py
+            ByteBuffer sizeBuf = ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN);
+            in.read(sizeBuf.array(), 0, Integer.BYTES);
+            int size = sizeBuf.getInt();
+
+            byte[] buf = new byte[size];
+            in.read(buf, 0, size);
+            return new String(buf);
+        }
+    }
 }
diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/StsExtraBusinessLogicHostTestBase.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/StsExtraBusinessLogicHostTestBase.java
index c31f80c..a0b92d0 100644
--- a/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/StsExtraBusinessLogicHostTestBase.java
+++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/StsExtraBusinessLogicHostTestBase.java
@@ -21,6 +21,7 @@
 import com.android.sts.common.util.DescriptionProvider;
 import com.android.sts.common.util.SplUtils;
 import com.android.sts.common.util.StsLogic;
+import com.android.sts.common.util.UnameVersionHost;
 import com.android.tradefed.device.DeviceNotAvailableException;
 
 import org.junit.Rule;
@@ -28,13 +29,13 @@
 
 import java.time.LocalDate;
 import java.util.List;
+import java.util.Optional;
 
 /** The host-side implementation of StsLogic. */
 public class StsExtraBusinessLogicHostTestBase extends ExtraBusinessLogicHostTestBase
         implements StsLogic {
 
     private LocalDate deviceSpl = null;
-    private LocalDate kernelSpl = null;
     @Rule public DescriptionProvider descriptionProvider = new DescriptionProvider();
 
     public StsExtraBusinessLogicHostTestBase() {
@@ -68,18 +69,12 @@
     }
 
     @Override
-    public LocalDate getKernelSpl() {
-        if (kernelSpl == null) {
-            // set in:
-            // test/sts/tools/sts-tradefed/src/com/android/tradefed/targetprep/multi/KernelSPL.java
-            String kernelSplString =
-                    getBuild().getBuildAttributes().get("cts:build_version_kernel_security_patch");
-            if (kernelSplString == null) {
-                return null;
-            }
-            kernelSpl = SplUtils.localDateFromSplString(kernelSplString);
+    public Optional<LocalDate> getKernelBuildDate() {
+        try {
+            return new UnameVersionHost(getDevice()).parseBuildTimestamp();
+        } catch (DeviceNotAvailableException e) {
+            throw new RuntimeException("Couldn't get the kernel build timestamp", e);
         }
-        return kernelSpl;
     }
 
     @Override
diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/util/UnameVersionHost.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/util/UnameVersionHost.java
new file mode 100644
index 0000000..f4f0462
--- /dev/null
+++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/util/UnameVersionHost.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 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.sts.common.util;
+
+import com.android.sts.common.CommandUtil;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.util.CommandResult;
+
+import java.time.LocalDate;
+import java.util.Optional;
+
+public final class UnameVersionHost {
+
+    private final ITestDevice device;
+    private String unameVersion = null;
+
+    public UnameVersionHost(ITestDevice device) {
+        this.device = device;
+    }
+
+    public final String getUnameVersion() throws DeviceNotAvailableException {
+        if (this.unameVersion != null) {
+            return this.unameVersion;
+        }
+
+        // https://android.googlesource.com/platform/system/core/+/master/shell_and_utilities/README.md
+        // uname is part of Android since 6.0 Marshmallow
+        CommandResult res = CommandUtil.runAndCheck(device, "uname -v");
+        this.unameVersion = res.getStdout().trim();
+        return this.unameVersion;
+    }
+
+    public final Optional<LocalDate> parseBuildTimestamp() throws DeviceNotAvailableException {
+        return UnameVersion.parseBuildTimestamp(getUnameVersion());
+    }
+}
diff --git a/libraries/sts-common-util/host-side/tests/Android.bp b/libraries/sts-common-util/host-side/tests/Android.bp
new file mode 100644
index 0000000..6975caf
--- /dev/null
+++ b/libraries/sts-common-util/host-side/tests/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2022 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_test_host {
+    name: "StsCommonUtilHostTests",
+
+    defaults: ["tradefed_errorprone_defaults"],
+    srcs: ["src/**/*.java"],
+
+    libs: ["tradefed"],
+
+    static_libs: [
+        "sts-host-util",
+    ],
+
+    // tag this module as a test artifact
+    test_suites: ["general-tests"],
+}
diff --git a/libraries/sts-common-util/host-side/tests/src/com/android/sts/common/util/UnameVersionHostTest.java b/libraries/sts-common-util/host-side/tests/src/com/android/sts/common/util/UnameVersionHostTest.java
new file mode 100644
index 0000000..9bda60e
--- /dev/null
+++ b/libraries/sts-common-util/host-side/tests/src/com/android/sts/common/util/UnameVersionHostTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 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.sts.common.util;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class UnameVersionHostTest extends BaseHostJUnit4Test {
+
+    @Test
+    public final void testGetUnameVersion() throws Exception {
+        String version = new UnameVersionHost(getDevice()).getUnameVersion();
+        assertTrue(
+                "Could not parse device version: " + version,
+                UnameVersion.parseBuildTimestamp(version).isPresent());
+    }
+}
diff --git a/libraries/sts-common-util/util/Android.bp b/libraries/sts-common-util/util/Android.bp
index 403dcc2..b82d4dc 100644
--- a/libraries/sts-common-util/util/Android.bp
+++ b/libraries/sts-common-util/util/Android.bp
@@ -21,6 +21,7 @@
     name: "sts-common-util-devicesidelib",
     visibility: [
         "//platform_testing/libraries/sts-common-util/device-side",
+        "//platform_testing/libraries/sts-common-util/util/tests",
     ],
     sdk_version: "current",
     srcs: ["src/**/*.java"],
@@ -35,6 +36,7 @@
     name: "sts-common-util-lib",
     visibility: [
         "//platform_testing/libraries/sts-common-util/host-side",
+        "//platform_testing/libraries/sts-common-util/util/tests",
     ],
     host_supported: true,
     srcs: ["src/**/*.java"],
diff --git a/libraries/sts-common-util/util/src/com/android/sts/common/util/FridaUtilsBusinessLogicHandler.java b/libraries/sts-common-util/util/src/com/android/sts/common/util/FridaUtilsBusinessLogicHandler.java
new file mode 100644
index 0000000..fee29ef
--- /dev/null
+++ b/libraries/sts-common-util/util/src/com/android/sts/common/util/FridaUtilsBusinessLogicHandler.java
@@ -0,0 +1,30 @@
+package com.android.sts.common.util;
+
+import java.util.Optional;
+
+/** GCL-accessible Business Logic utility for FridaUtils */
+public class FridaUtilsBusinessLogicHandler {
+    // {0} = FRIDA_PACKAGE (e.g. 'frida-inject')
+    // {1} = fridaVersion (e.g. '15.1.17')
+    // {2} = FRIDA_OS (e.g. 'android')
+    // {3} = fridaAbi (e.g. 'arm64')
+    // Obtained from BusinessLogic, e.g. "{0}-{1}-{2}-{3}.xz"
+    private static String fridaFilenameTemplate;
+    private static String fridaVersion;
+
+    public static String getFridaFilenameTemplate() {
+        return fridaFilenameTemplate;
+    }
+
+    public void setFridaFilenameTemplate(String template) {
+        fridaFilenameTemplate = template;
+    }
+
+    public static Optional<String> getFridaVersion() {
+        return Optional.ofNullable(fridaVersion);
+    }
+
+    public void setFridaVersion(String version) {
+        fridaVersion = version;
+    }
+}
diff --git a/libraries/sts-common-util/util/src/com/android/sts/common/util/StsLogic.java b/libraries/sts-common-util/util/src/com/android/sts/common/util/StsLogic.java
index 8dd749c..99cea1f 100644
--- a/libraries/sts-common-util/util/src/com/android/sts/common/util/StsLogic.java
+++ b/libraries/sts-common-util/util/src/com/android/sts/common/util/StsLogic.java
@@ -29,6 +29,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
 /** Common STS extra business logic for host-side and device-side to implement. */
@@ -38,30 +39,39 @@
 
     // keep in sync with google3:
     // //wireless/android/partner/apbs/*/config/xtsbgusinesslogic/sts_business_logic.gcl
-    List<String> STS_EXTRA_BUSINESS_LOGIC_FULL = Arrays.asList(new String[] {
-            "uploadSpl",
-            "uploadModificationTime",
-            "uploadKernelBugs",
-            "declaredSpl",
-    });
-    List<String> STS_EXTRA_BUSINESS_LOGIC_INCREMENTAL = Arrays.asList(new String[] {
-            "uploadSpl",
-            "uploadModificationTime",
-            "uploadKernelBugs",
-            "declaredSpl",
-            "incremental",
-    });
+    List<String> STS_EXTRA_BUSINESS_LOGIC_FULL =
+            Arrays.asList(
+                    new String[] {
+                        "uploadSpl",
+                        "uploadModificationTime",
+                        "uploadKernelBugs",
+                        "declaredSpl",
+                        "fridaAssetTemplate",
+                    });
+    List<String> STS_EXTRA_BUSINESS_LOGIC_INCREMENTAL =
+            Arrays.asList(
+                    new String[] {
+                        "uploadSpl",
+                        "uploadModificationTime",
+                        "uploadKernelBugs",
+                        "declaredSpl",
+                        "incremental",
+                        "fridaAssetTemplate",
+                    });
 
     // intentionally empty because declaredSpl and incremental skipping is not desired when
     // developing STS tests.
-    List<String> STS_EXTRA_BUSINESS_LOGIC_DEVELOP = Arrays.asList(new String[] {
-    });
+    List<String> STS_EXTRA_BUSINESS_LOGIC_DEVELOP =
+            Arrays.asList(
+                    new String[] {
+                        "fridaAssetTemplate",
+                    });
 
     Description getTestDescription();
 
     LocalDate getPlatformSpl();
 
-    LocalDate getKernelSpl();
+    Optional<LocalDate> getKernelBuildDate();
 
     boolean shouldUseKernelSpl();
 
@@ -100,6 +110,7 @@
     }
 
     default LocalDate getDeviceSpl() {
+        LocalDate platformSpl = getPlatformSpl();
         if (shouldUseKernelSpl()) {
             Set<String> bugIds = BusinessLogicSetStore.getSet("kernel_bugs");
             boolean isKernel = false;
@@ -107,16 +118,24 @@
                 isKernel |= bugIds.contains(Long.toString(bugId));
             }
             if (isKernel) {
-                LocalDate kernelSpl = getKernelSpl();
-                if (kernelSpl != null) {
-                    return kernelSpl;
+                Optional<LocalDate> kernelBuildDate = getKernelBuildDate();
+                if (!kernelBuildDate.isPresent()) {
+                    logWarn(LOG_TAG, "could not read kernel SPL, falling back to platform SPL");
+                    return platformSpl;
                 }
-                // could not get the kernel SPL even though we should use it
-                // falling back to platform SPL
-                logWarn(LOG_TAG, "could not read kernel SPL, falling back to platform SPL");
+                if (kernelBuildDate.get().plusMonths(2).isAfter(platformSpl)) {
+                    // if the kernel was built within the past 2 months, it's likely still in
+                    // support and the platform SPL is still canonical.
+                    // 2 months because the future ASB is at most (partner + public) months ahead
+                    logInfo(LOG_TAG, "kernel SPL is too recent, using platform SPL");
+                    return platformSpl;
+                }
+                // the kernel build date can be used to approximate the SPL of the device/build
+                // when it stopped getting kernel updates
+                return kernelBuildDate.get();
             }
         }
-        return getPlatformSpl();
+        return platformSpl;
     }
 
     default LocalDate getMinTestSpl() {
diff --git a/libraries/sts-common-util/util/src/com/android/sts/common/util/UnameVersion.java b/libraries/sts-common-util/util/src/com/android/sts/common/util/UnameVersion.java
new file mode 100644
index 0000000..3da5ff7
--- /dev/null
+++ b/libraries/sts-common-util/util/src/com/android/sts/common/util/UnameVersion.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2022 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.sts.common.util;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Tools for parsing uname version strings */
+public final class UnameVersion {
+
+    // assuming KBUILD_BUILD_TIMESTAMP is $(shell date)
+    // "date" default format from the source code:
+    //     format = "%a %b %e %H:%M:%S %Z %Y";
+    // weekday, month, monthday, hms, tz, year
+    private static final Map<Pattern, DateTimeFormatter[]> PATTERN_TO_FORMATTER = new HashMap<>();
+
+    static {
+        // matches:
+        // #1 SMP PREEMPT Fri May 13 12:22:46 UTC 2022
+        // #1 SMP PREEMPT Tue Jun 11 08:31:19 -03 2019
+        // #1 SMP PREEMPT Thu Feb 7 12:05:45 GMT+2 2019
+        // #1 SMP PREEMPT Mon Feb 19 14:33:34 2018
+        PATTERN_TO_FORMATTER.put(
+                Pattern.compile(
+                        // weekday
+                        "(Sun|Mon|Tue|Wed|Thu|Fri|Sat) "
+                                // month
+                                + "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) "
+                                // day of month
+                                + "\\d{1,2} "
+                                // HH:MM:SS
+                                + "\\d{2}:\\d{2}:\\d{2} "
+                                // (optional) timezone
+                                + "(\\S+ )?"
+                                // year
+                                + "\\d{4,}"),
+                new DateTimeFormatter[] {
+                    DateTimeFormatter.ofPattern("E MMM d H:m:s z u"),
+                    DateTimeFormatter.ofPattern("E MMM d H:m:s X u"),
+                    DateTimeFormatter.ofPattern("E MMM d H:m:s O u"),
+                    DateTimeFormatter.ofPattern("E MMM d H:m:s u"),
+                });
+
+        // matches:
+        // #1 SMP PREEMPT 2021-08-28 09:42:03
+        PATTERN_TO_FORMATTER.put(
+                Pattern.compile("\\d{4,}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}") // YYYY-MM-DD HH:MM:SS
+                ,
+                new DateTimeFormatter[] {
+                    DateTimeFormatter.ofPattern("u-M-d H:m:s"),
+                });
+    }
+
+    /**
+     * @param unameVersion The output of `uname -v` or android.system.Os.uname().version. See {@link
+     *     com.android.sts.common.util.UnameVersionHost UnameVersionHost} for a host-side helper.
+     * @return An Optional with the LocalDate representation of the timestamp in the uname version
+     *     string.
+     */
+    public static final Optional<LocalDate> parseBuildTimestamp(String unameVersion) {
+        for (Map.Entry<Pattern, DateTimeFormatter[]> entry : PATTERN_TO_FORMATTER.entrySet()) {
+            Matcher m = entry.getKey().matcher(unameVersion);
+            if (m.find()) {
+                DateTimeFormatter[] formatters = entry.getValue();
+                for (DateTimeFormatter formatter : formatters) {
+                    try {
+                        return Optional.of(LocalDate.parse(m.group(), formatter));
+                    } catch (DateTimeParseException e) {
+                        // do nothing; try next formatter
+                    }
+                }
+            }
+        }
+        // must be a non-standard kernel build version
+        return Optional.empty();
+    }
+}
diff --git a/libraries/sts-common-util/util/tests/Android.bp b/libraries/sts-common-util/util/tests/Android.bp
new file mode 100644
index 0000000..12cfd66
--- /dev/null
+++ b/libraries/sts-common-util/util/tests/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2022 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_test_host {
+    name: "StsCommonUtilTests",
+
+    defaults: ["tradefed_errorprone_defaults"],
+    srcs: ["src/**/*.java"],
+
+    libs: ["tradefed"],
+
+    static_libs: [
+        "sts-common-util-lib",
+    ],
+
+    // tag this module as a test artifact
+    test_suites: ["general-tests"],
+
+    java_resource_dirs: ["res/"],
+}
diff --git a/libraries/sts-common-util/util/tests/res/edi_uname_versions.txt b/libraries/sts-common-util/util/tests/res/edi_uname_versions.txt
new file mode 100644
index 0000000..8cc1bf4
--- /dev/null
+++ b/libraries/sts-common-util/util/tests/res/edi_uname_versions.txt
@@ -0,0 +1,9999 @@
+#1 SMP PREEMPT Tue May 28 15:40:05 KST 2019
+#1 SMP PREEMPT Thu Mar 3 04:58:58 CST 2022
+#1 SMP PREEMPT Sun Aug 1 05:58:57 CDT 2021
+#1 SMP PREEMPT Tue May 25 13:51:26 KST 2021
+#1 SMP PREEMPT Tue Dec 1 15:04:12 CST 2020
+#2 SMP PREEMPT Wed Jun 17 17:06:06 CST 2020
+#1 SMP PREEMPT Sat May 30 10:27:15 CST 2020
+#2 SMP PREEMPT Fri Apr 15 11:13:02 CST 2022
+#1 SMP PREEMPT Mon Jul 12 10:34:00 +07 2021
+#1 SMP PREEMPT Mon Oct 15 19:23:05 KST 2018
+#1 SMP PREEMPT Wed Mar 31 03:47:20 CST 2021
+#1 SMP PREEMPT Wed Oct 10 16:52:24 CST 2018
+#1 SMP PREEMPT Fri Oct 8 23:07:10 PDT 2021
+#1 SMP PREEMPT Wed Mar 18 19:42:17 JST 2020
+#1 SMP PREEMPT Thu Jun 18 12:17:07 CST 2020
+#1 SMP PREEMPT Tue Jul 30 03:22:00 CDT 2019
+#2 SMP PREEMPT Tue Jul 13 11:20:50 CST 2021
+#1 SMP PREEMPT Mon May 23 15:28:46 CST 2022
+#1 SMP PREEMPT Thu Dec 19 02:19:55 CST 2019
+#1 SMP PREEMPT Wed Jun 23 20:10:16 KST 2021
+#1 SMP PREEMPT Thu Apr 7 17:41:01 CST 2022
+#1 SMP PREEMPT Thu Feb 13 04:23:09 JST 2020
+#1 SMP PREEMPT Thu Apr 9 00:25:07 PDT 2020
+#1 SMP PREEMPT Thu Dec 13 10:35:58 CST 2018
+#1 SMP PREEMPT Wed Apr 11 12:09:58 KST 2018
+#1 SMP PREEMPT Mon Jul 22 03:05:48 JST 2019
+#1 SMP PREEMPT Thu Jul 9 00:09:25 KST 2020
+#1 SMP PREEMPT Mon Feb 14 01:39:36 JST 2022
+#1 SMP PREEMPT Fri Mar 11 06:21:28 KST 2022
+#1 SMP PREEMPT Tue May 11 17:50:19 CST 2021
+#1 SMP PREEMPT Tue Aug 21 18:15:05 CST 2018
+#1 SMP PREEMPT Mon Jul 15 12:17:40 CST 2019
+#3 SMP PREEMPT Tue Feb 13 17:53:31 KST 2018
+#1 SMP PREEMPT Fri Jul 16 17:57:30 CST 2021
+#1 SMP PREEMPT Thu Mar 14 13:11:39 CST 2019
+#1 SMP PREEMPT Fri Mar 25 10:34:59 CST 2022
+#1 SMP PREEMPT Fri Sep 3 13:05:27 CST 2021
+#2 SMP PREEMPT Sun Nov 10 01:16:13 CST 2019
+#1 SMP PREEMPT Fri Dec 27 13:54:26 CST 2019
+#1 SMP PREEMPT Sat Nov 17 21:17:21 CST 2018
+#1 SMP PREEMPT Thu Oct 22 00:33:32 CST 2020
+#1 SMP PREEMPT Sat Jan 8 00:32:37 JST 2022
+#2 SMP PREEMPT Thu Feb 14 09:41:11 UTC 2019
+#1 SMP PREEMPT Tue Dec 11 13:22:52 CST 2018
+#1 SMP PREEMPT Fri May 4 20:47:58 KST 2018
+#2 SMP PREEMPT Wed Dec 23 21:11:48 CST 2020
+#2 SMP PREEMPT Thu Mar 28 09:31:49 CST 2019
+#1 SMP PREEMPT Thu Dec 23 15:26:22 CST 2021
+#1 SMP PREEMPT Wed Oct 28 10:58:39 CST 2020
+#1 SMP PREEMPT Fri Aug 21 08:42:22 KST 2020
+#1 SMP PREEMPT Thu Mar 12 20:09:56 2020
+#1 SMP PREEMPT Sat Oct 12 13:00:10 CST 2019
+#1 SMP PREEMPT Thu Jul 1 05:23:09 CST 2021
+#1 SMP PREEMPT Sat Oct 10 03:34:16 CDT 2020
+#1 SMP PREEMPT Tue May 10 15:22:36 CST 2022
+#1 SMP PREEMPT Tue Feb 18 21:38:57 CST 2020
+#1 SMP PREEMPT Thu Jul 4 20:13:56 IST 2019
+#1 SMP PREEMPT Sat Feb 27 18:33:03 CST 2021
+#1 SMP PREEMPT Mon Oct 14 13:06:17 CST 2019
+#2 SMP PREEMPT Tue Nov 24 19:13:11 KST 2020
+#1 SMP PREEMPT Tue Nov 3 16:07:55 KST 2020
+#1 SMP PREEMPT Wed Mar 21 13:43:10 CST 2018
+#1 SMP PREEMPT Fri Dec 13 08:52:24 CST 2019
+#1 SMP PREEMPT Wed Jan 19 19:31:18 UTC 2022
+#1 SMP PREEMPT Thu Dec 7 13:18:27 KST 2017
+#1 SMP Fri Jul 10 00:01:12 UTC 2020
+#1 SMP PREEMPT Thu Sep 20 10:36:44 CST 2018
+#1 SMP PREEMPT Tue Oct 27 17:49:49 KST 2020
+#1 SMP PREEMPT Tue Mar 29 19:22:13 PDT 2022
+#1 SMP PREEMPT Mon Jan 7 16:39:43 CST 2019
+#2 SMP PREEMPT Thu Oct 10 14:08:23 KST 2019
+#1 SMP PREEMPT Mon Jul 29 19:02:41 KST 2019
+#1 SMP PREEMPT Tue Jan 9 09:54:26 CST 2018
+#1 SMP PREEMPT Fri Apr 5 16:43:20 HKT 2019
+#1 SMP PREEMPT Wed Oct 13 03:00:36 CST 2021
+#1 SMP PREEMPT Thu Nov 28 20:52:01 CST 2019
+#1 SMP PREEMPT Mon Apr 23 22:24:31 PDT 2018
+#1 SMP PREEMPT Mon Jan 22 21:44:40 BRST 2018
+#1 SMP PREEMPT Mon May 17 20:54:21 IST 2021
+#1 SMP PREEMPT Tue Aug 18 00:23:14 KST 2020
+#1 SMP PREEMPT Mon Jan 21 12:56:56 CST 2019
+#1 SMP PREEMPT Wed Mar 4 20:53:56 KST 2020
+#1 SMP PREEMPT Thu Jun 21 18:26:00 CST 2018
+#1 SMP PREEMPT Wed Mar 20 10:53:25 CST 2019
+#1 SMP PREEMPT Sun Nov 1 16:48:19 CST 2020
+#1 SMP PREEMPT Thu Aug 12 23:07:33 CST 2021
+#1 SMP PREEMPT Fri Aug 7 21:04:11 CST 2020
+#1 SMP PREEMPT Tue Mar 24 03:57:52 KST 2020
+#1 SMP PREEMPT Fri Feb 21 23:05:21 KST 2020
+#1 SMP PREEMPT Wed Oct 13 00:23:50 CST 2021
+#1 SMP PREEMPT Sun May 27 16:39:42 KST 2018
+#1 SMP PREEMPT Mon Nov 23 17:52:10 HKT 2020
+#1 SMP PREEMPT Wed Mar 17 01:53:57 IST 2021
+#1 SMP PREEMPT Wed Jan 15 10:23:25 CST 2020
+#1 SMP PREEMPT Mon May 3 14:31:12 KST 2021
+#1 SMP PREEMPT Wed May 20 18:32:03 CST 2020
+#1 SMP PREEMPT Thu Mar 19 16:10:29 CST 2020
+#1 SMP PREEMPT Mon Mar 7 03:14:35 CST 2022
+#1 SMP PREEMPT Wed Feb 23 12:47:48 CST 2022
+#1 SMP PREEMPT Tue Jun 30 10:50:43 CST 2020
+#1 SMP PREEMPT Thu Jun 24 00:21:07 CST 2021
+#1 SMP PREEMPT Mon Feb 15 21:36:15 PST 2021
+#1 SMP PREEMPT Thu May 31 20:51:14 KST 2018
+#1 SMP PREEMPT Wed Nov 29 06:17:53 CST 2017
+#1 SMP PREEMPT Tue Aug 21 23:40:44 CST 2018
+#1 SMP PREEMPT Sat Jan 4 14:42:08 CST 2020
+#1 SMP PREEMPT Tue Aug 7 19:44:20 KST 2018
+#1 SMP PREEMPT Wed Jul 4 07:37:58 KST 2018
+#1 SMP PREEMPT Mon Apr 15 21:16:11 CST 2019
+#1 SMP PREEMPT Fri Oct 15 17:12:58 CST 2021
+#1 SMP PREEMPT Tue Jul 21 12:55:15 CST 2020
+#1 SMP PREEMPT Tue May 17 16:06:41 CST 2022
+#1 SMP PREEMPT Sat Apr 20 18:02:43 CST 2019
+#1 SMP PREEMPT Thu Dec 10 19:55:04 PST 2020
+#1 SMP PREEMPT Thu Mar 15 22:27:45 UTC 2018
+#1 SMP PREEMPT Sun Sep 26 16:56:46 CST 2021
+#1 SMP PREEMPT Thu May 7 00:30:40 CST 2020
+#1 SMP PREEMPT Wed Feb 13 12:41:20 CST 2019
+#2 SMP PREEMPT Thu Jul 11 01:06:40 CST 2019
+#1 SMP PREEMPT Thu Mar 19 19:27:32 CST 2020
+#1 SMP PREEMPT Sat Jan 29 03:37:19 CST 2022
+#2 SMP PREEMPT Mon Nov 23 20:36:16 KST 2020
+#67 SMP PREEMPT Thu Sep 19 11:55:48 CST 2019
+#1 SMP PREEMPT Mon Oct 8 22:09:02 WIB 2018
+#1 SMP PREEMPT Wed Dec 30 20:56:00 CST 2020
+#1 SMP PREEMPT Fri Nov 5 06:02:44 JST 2021
+#1 SMP PREEMPT Wed Sep 8 13:55:01 CST 2021
+#1 SMP PREEMPT Tue Mar 3 21:59:30 CST 2020
+#2 SMP PREEMPT Tue Aug 27 16:38:03 KST 2019
+#1 SMP PREEMPT Fri Aug 30 11:46:04 CST 2019
+#1 SMP PREEMPT Tue Jan 8 11:45:52 KST 2019
+#1 SMP PREEMPT Thu Jan 31 14:35:13 CST 2019
+#1 SMP PREEMPT Thu Feb 13 03:08:20 JST 2020
+#1 SMP PREEMPT Wed Sep 18 19:24:23 CDT 2019
+#1 SMP PREEMPT Fri Mar 16 17:53:14 CST 2018
+#1 SMP PREEMPT Sat May 14 02:14:19 CST 2022
+#1 SMP PREEMPT Tue Mar 1 02:49:02 UTC 2022
+#6 SMP PREEMPT Mon Jun 1 14:11:06 CST 2020
+#1 SMP PREEMPT Fri Nov 12 04:05:58 JST 2021
+#17 SMP PREEMPT Thu Aug 13 20:31:46 CST 2020
+#1 SMP PREEMPT Tue Oct 8 17:53:01 KST 2019
+#1 SMP PREEMPT Mon Apr 18 12:30:54 CST 2022
+#1 SMP PREEMPT Sun Feb 27 00:17:49 CST 2022
+#2 SMP PREEMPT Tue Oct 29 16:02:49 CST 2019
+#1 SMP PREEMPT Wed Oct 30 11:14:55 CST 2019
+#1 SMP PREEMPT Thu Dec 5 06:30:07 KST 2019
+#1 SMP PREEMPT Wed Feb 5 01:59:36 CST 2020
+#1 SMP PREEMPT Mon Jul 15 13:41:08 IST 2019
+#1 SMP PREEMPT Sun May 15 11:11:41 CST 2022
+#2 SMP PREEMPT Mon Mar 16 21:20:07 KST 2020
+#1 SMP PREEMPT Wed Feb 26 20:48:43 CST 2020
+#1 SMP PREEMPT Mon Mar 23 15:26:02 CST 2020
+#2 SMP PREEMPT Tue Mar 20 22:50:02 CST 2018
+#1 SMP PREEMPT Wed Feb 16 12:17:57 CST 2022
+#1 SMP PREEMPT Thu Dec 10 19:05:46 CST 2020
+#2 SMP PREEMPT Thu Nov 7 13:07:38 CST 2019
+#1 SMP PREEMPT Wed Jun 26 04:38:26 CST 2019
+#1 SMP PREEMPT Wed Nov 7 15:56:43 CST 2018
+#1 SMP PREEMPT Tue Feb 13 17:00:59 KST 2018
+#2 SMP PREEMPT Sat Nov 23 00:43:32 CST 2019
+#1 SMP PREEMPT Sun Dec 17 01:15:54 CST 2017
+#1 SMP PREEMPT Wed May 12 21:37:45 PDT 2021
+#1 SMP PREEMPT Wed Nov 28 13:59:25 KST 2018
+#2 SMP PREEMPT Fri Nov 12 15:20:31 CST 2021
+#1 SMP PREEMPT Fri Jan 4 22:05:26 CST 2019
+#12 SMP PREEMPT Mon Aug 10 12:27:00 CST 2020
+#1 SMP PREEMPT Tue Oct 22 01:00:13 PDT 2019
+#1 SMP PREEMPT Sat Oct 12 12:28:05 CST 2019
+#1 SMP PREEMPT Thu Apr 7 13:24:58 CST 2022
+#1 SMP PREEMPT Wed Oct 17 15:13:38 CST 2018
+#11 SMP PREEMPT Mon Apr 18 16:23:48 CST 2022
+#1 SMP PREEMPT Fri Jun 5 22:30:16 IST 2020
+#1 SMP PREEMPT Tue Jun 22 02:44:59 JST 2021
+#2 SMP PREEMPT Fri May 1 15:34:45 CST 2020
+#1 SMP PREEMPT Fri Sep 13 01:01:18 PDT 2019
+#1 SMP PREEMPT Sun Apr 5 01:21:38 CST 2020
+#1 SMP PREEMPT Thu Jan 16 12:43:32 CST 2020
+#2 SMP PREEMPT Mon Sep 7 18:48:52 CST 2020
+#1 SMP PREEMPT Fri Nov 12 11:22:36 JST 2021
+#1 SMP PREEMPT Tue Nov 30 11:54:42 PST 2021
+#1 SMP PREEMPT Tue Oct 16 02:26:24 JST 2018
+#1 SMP PREEMPT Wed Feb 17 03:04:11 JST 2021
+#1 SMP PREEMPT Fri Feb 7 17:58:39 KST 2020
+#1 SMP PREEMPT Mon Jan 10 21:44:05 CST 2022
+#1 SMP PREEMPT Wed May 8 10:35:43 BRT 2019
+#1 SMP PREEMPT Mon Jan 17 23:35:07 CST 2022
+#1 SMP PREEMPT Tue Jun 15 11:19:45 CST 2021
+#1 SMP PREEMPT Tue Sep 18 01:55:06 CST 2018
+#2 SMP PREEMPT Tue May 22 14:14:37 KST 2018
+#84 SMP PREEMPT Wed Dec 18 09:24:03 CST 2019
+#1 SMP PREEMPT Wed Dec 18 00:27:46 PST 2019
+#1 SMP PREEMPT Sat Nov 10 17:22:16 CST 2018
+#1 SMP PREEMPT Wed Dec 12 01:43:58 CST 2018
+#1 SMP PREEMPT Mon Jul 2 15:29:26 KST 2018
+#1 SMP PREEMPT Tue May 18 11:00:15 CST 2021
+#1 repo:r-tv-dev SMP PREEMPT Mon Dec 6 22:19:04 CST 2021
+#1 SMP PREEMPT Fri Aug 3 16:24:47 CST 2018
+#1 SMP PREEMPT Tue Jun 5 15:39:55 KST 2018
+#1 SMP PREEMPT Wed Mar 21 21:07:36 KST 2018
+#1 SMP PREEMPT Sat Oct 26 23:56:53 PDT 2019
+#1 SMP PREEMPT Thu May 3 22:03:34 CST 2018
+#1 SMP PREEMPT Wed Nov 29 18:38:39 EST 2017
+#1 SMP PREEMPT Tue Dec 17 21:32:40 CST 2019
+#1 SMP PREEMPT Tue Feb 22 19:51:20 CST 2022
+#1 SMP PREEMPT Thu Jan 6 23:27:39 CST 2022
+#1 SMP PREEMPT Mon Apr 8 00:51:38 PDT 2019
+#2 SMP PREEMPT Thu Jan 3 17:10:57 CST 2019
+#1 SMP PREEMPT Fri May 29 17:59:08 KST 2020
+#1 SMP PREEMPT Sat May 11 17:26:04 CST 2019
+#1 SMP PREEMPT Thu Oct 31 10:43:21 CST 2019
+#1 SMP PREEMPT Fri Apr 29 22:04:49 CST 2022
+#1 SMP PREEMPT Wed Aug 4 22:02:55 CST 2021
+#1 SMP PREEMPT Fri Jul 26 17:35:27 CST 2019
+#1 SMP PREEMPT Thu Feb 21 04:14:44 CST 2019
+#1 SMP PREEMPT Tue Jan 7 12:11:41 IST 2020
+#1 SMP PREEMPT Fri Feb 25 20:06:09 CST 2022
+#1 SMP PREEMPT Sat Jan 4 10:39:00 CST 2020
+#1 SMP PREEMPT Thu Mar 3 15:40:39 CST 2022
+#1 SMP PREEMPT Tue Apr 27 10:57:41 KST 2021
+#1 SMP PREEMPT Thu Nov 25 04:15:55 PST 2021
+#1 SMP PREEMPT Wed Aug 22 23:46:57 PDT 2018
+#2 SMP PREEMPT Thu Nov 11 11:10:52 CET 2021
+#1 SMP PREEMPT Wed Jul 4 18:04:21 CST 2018
+#2 SMP PREEMPT Fri Mar 8 16:05:28 KST 2019
+#1 SMP PREEMPT Tue Mar 8 12:49:47 CST 2022
+#1 SMP PREEMPT Mon May 23 15:15:12 CST 2022
+#1 SMP PREEMPT Sat Mar 26 16:28:46 CST 2022
+#1 SMP PREEMPT Mon Mar 8 21:15:55 CST 2021
+#1 SMP PREEMPT Fri Dec 18 06:27:32 CST 2020
+#2 SMP PREEMPT Thu Jun 18 16:04:10 CST 2020
+#1 SMP PREEMPT Mon Feb 14 22:21:36 PST 2022
+#1 SMP PREEMPT Wed Jan 30 16:33:16 CST 2019
+#1 SMP PREEMPT Thu Apr 22 03:16:50 JST 2021
+#1 SMP PREEMPT Mon Jun 8 16:49:56 JST 2020
+#1 SMP PREEMPT Wed Aug 7 10:22:23 CST 2019
+#1 SMP PREEMPT Wed Feb 9 08:07:31 UTC 2022
+#1 SMP PREEMPT Tue Aug 11 20:48:23 CST 2020
+#1 SMP PREEMPT Mon Dec 27 02:10:38 CST 2021
+#1 SMP PREEMPT Wed Jan 27 02:59:07 KST 2021
+#1 SMP PREEMPT Thu May 9 16:28:20 GMT 2019
+#1 SMP PREEMPT Thu Feb 1 03:57:27 CST 2018
+#1 SMP PREEMPT Mon Feb 26 13:12:13 CST 2018
+#1 SMP PREEMPT Tue Feb 18 12:38:38 KST 2020
+#1 SMP PREEMPT Fri Jan 29 15:04:17 KST 2021
+#1 SMP PREEMPT Mon Apr 27 12:13:51 CST 2020
+#1 SMP PREEMPT Tue Aug 4 17:09:41 CST 2020
+#2 SMP PREEMPT Fri Nov 27 16:32:04 CST 2020
+#1 SMP PREEMPT Tue Dec 15 23:15:22 CST 2020
+#1 SMP PREEMPT Fri May 24 17:55:16 CST 2019
+#1 SMP PREEMPT Fri Apr 2 06:18:54 CST 2021
+#1 SMP PREEMPT Fri Apr 17 05:57:29 CST 2020
+#1 SMP PREEMPT Wed Mar 17 01:14:30 CST 2021
+#1 SMP PREEMPT Fri Apr 30 00:59:10 CST 2021
+#1 SMP PREEMPT Fri Sep 28 03:59:07 KST 2018
+#2 SMP PREEMPT Tue Nov 24 15:45:57 CST 2020
+#1 SMP PREEMPT Fri Sep 7 00:52:56 PDT 2018
+#1 SMP PREEMPT Fri Jul 2 19:34:09 CST 2021
+#1 SMP PREEMPT Sun Aug 15 00:08:19 CST 2021
+#1 SMP PREEMPT Fri Jan 19 20:39:15 EST 2018
+#1 SMP PREEMPT Fri Aug 7 10:43:25 KST 2020
+#1 SMP PREEMPT Sat Feb 23 18:00:25 CST 2019
+#1 SMP PREEMPT Wed Oct 17 19:23:09 CST 2018
+#1 SMP PREEMPT Mon May 18 11:49:49 CST 2020
+#2 SMP PREEMPT Sun Nov 17 03:37:37 CST 2019
+#1 SMP PREEMPT Wed Jan 8 00:14:03 CST 2020
+#1 SMP PREEMPT Thu Jan 7 00:48:48 CST 2021
+#1 SMP PREEMPT Tue Jan 21 07:39:06 CST 2020
+#1 SMP PREEMPT Mon Jul 2 10:39:07 KST 2018
+#1 SMP PREEMPT Sat Aug 29 13:34:55 JST 2020
+#1 SMP PREEMPT Mon Aug 3 16:38:02 IST 2020
+#1 SMP PREEMPT Thu Jan 20 23:37:46 CST 2022
+#1 SMP PREEMPT Sun May 22 15:32:30 PDT 2022
+#1 SMP PREEMPT Wed May 26 11:12:32 HKT 2021
+#1 SMP PREEMPT Fri Aug 16 01:01:52 KST 2019
+#1 SMP PREEMPT Tue Nov 27 12:44:48 CST 2018
+#1 SMP PREEMPT Fri Aug 17 22:31:03 PDT 2018
+#2 SMP PREEMPT Thu Dec 5 02:32:30 CST 2019
+#6 SMP PREEMPT Fri May 13 13:45:06 UTC 2022
+#0 SMP PREEMPT Fri Apr 10 23:00:22 UTC 2020
+#1 SMP PREEMPT Fri Mar 11 03:11:16 UTC 2022
+#1 SMP PREEMPT Fri Jan 25 18:10:43 CST 2019
+#1 SMP PREEMPT Wed Jun 19 12:46:10 -03 2019
+#1 SMP PREEMPT Thu May 13 18:14:16 CST 2021
+#1 SMP PREEMPT Thu Nov 25 19:59:09 CST 2021
+#1 SMP PREEMPT Fri Mar 22 10:48:51 EDT 2019
+#1 SMP PREEMPT Thu Dec 17 15:00:50 CET 2020
+#1 SMP PREEMPT Mon Jul 6 11:39:57 CST 2020
+#1 SMP PREEMPT Fri Jun 4 13:46:27 CDT 2021
+#1 SMP PREEMPT Mon Oct 19 13:47:07 KST 2020
+#1 SMP PREEMPT Thu Mar 10 11:43:42 PST 2022
+#1 SMP PREEMPT Thu Aug 12 17:31:20 CST 2021
+#1 SMP PREEMPT Fri Mar 16 17:46:20 2018
+#1 SMP PREEMPT Tue Nov 3 20:57:20 CST 2020
+#1 SMP PREEMPT Thu Apr 2 18:23:30 PDT 2020
+#1 SMP PREEMPT Thu Oct 15 16:43:06 CST 2020
+#1 SMP PREEMPT Tue Nov 26 18:24:19 KST 2019
+#1 SMP PREEMPT Fri Mar 13 16:31:23 JST 2020
+#2 SMP PREEMPT Sat Jul 28 17:03:31 CST 2018
+#1 SMP PREEMPT Wed Apr 11 23:07:39 KST 2018
+#2 SMP PREEMPT Thu Sep 3 16:29:48 KST 2020
+#1 SMP PREEMPT Mon Aug 26 21:16:22 CDT 2019
+#1 SMP PREEMPT Wed May 2 19:12:37 CST 2018
+#1 SMP PREEMPT Tue Apr 13 21:24:45 KST 2021
+#2 SMP PREEMPT Thu Nov 15 14:58:08 CST 2018
+#1 SMP PREEMPT Fri Mar 29 22:29:19 CST 2019
+#1 SMP PREEMPT Tue Apr 14 14:14:46 CST 2020
+#2 SMP PREEMPT Tue Feb 22 20:08:19 KST 2022
+#2 SMP PREEMPT Thu Apr 23 18:25:38 KST 2020
+#1 SMP PREEMPT Wed Jan 19 19:31:18 UTC 2022
+#1 SMP PREEMPT Wed Sep 9 03:47:59 CST 2020
+#1 SMP PREEMPT Thu Jul 16 04:14:35 CDT 2020
+#1 SMP PREEMPT Sat Oct 23 03:36:32 CST 2021
+#1 SMP PREEMPT Wed Sep 1 01:54:26 CDT 2021
+#2 SMP PREEMPT Tue Oct 1 08:13:26 KST 2019
+#1 SMP PREEMPT Mon Oct 29 18:49:42 WIB 2018
+#1 SMP PREEMPT Fri Mar 13 21:11:49 CST 2020
+#1 SMP PREEMPT Sun Jan 24 00:59:49 CST 2021
+#1 SMP PREEMPT Fri Jan 25 00:40:38 CST 2019
+#1 SMP PREEMPT Fri Jul 26 04:05:50 CST 2019
+#1 SMP PREEMPT Tue Nov 6 14:54:15 KST 2018
+#1 SMP PREEMPT Thu Jul 2 12:20:23 CST 2020
+#1 SMP PREEMPT Tue Nov 20 01:33:28 CST 2018
+#1 SMP PREEMPT Tue Aug 18 21:03:16 CST 2020
+#2 SMP PREEMPT Mon Mar 15 21:13:02 KST 2021
+#1 SMP PREEMPT Tue Feb 11 16:07:56 CST 2020
+#1 SMP PREEMPT Sun Jan 5 23:49:45 PST 2020
+#1 SMP PREEMPT Tue Mar 27 16:48:51 KST 2018
+#1 SMP PREEMPT Thu Apr 25 21:33:37 CST 2019
+#1 SMP PREEMPT Thu Jun 10 14:33:00 CST 2021
+#1 SMP PREEMPT Tue Nov 28 17:09:01 KST 2017
+#1 SMP PREEMPT Thu Nov 30 06:26:30 CST 2017
+#1 SMP PREEMPT Mon Oct 21 14:19:57 KST 2019
+#1 SMP PREEMPT Wed Feb 3 22:55:41 CST 2021
+#1 SMP PREEMPT Mon Apr 19 18:26:15 KST 2021
+#1 SMP PREEMPT Tue Oct 13 17:13:41 CST 2020
+#1 SMP PREEMPT Mon Jul 22 23:06:13 CST 2019
+#1 SMP PREEMPT Fri Jun 29 15:34:34 CST 2018
+#1 SMP PREEMPT Fri Apr 9 11:55:20 KST 2021
+#1 SMP PREEMPT Mon Feb 21 19:08:20 CST 2022
+#1 SMP PREEMPT Fri May 24 00:50:54 CST 2019
+#1 SMP PREEMPT Sun Dec 6 23:35:14 CST 2020
+#1 SMP PREEMPT Mon Apr 1 14:54:24 KST 2019
+#1 SMP PREEMPT Fri Apr 23 01:50:20 CST 2021
+#1 SMP PREEMPT Fri Aug 7 05:41:08 CST 2020
+#1 SMP PREEMPT Mon Apr 1 12:15:43 CST 2019
+#2 SMP PREEMPT Fri Jan 18 00:18:52 CST 2019
+#1 SMP PREEMPT Fri Jan 11 06:48:41 KST 2019
+#1 SMP PREEMPT Wed Sep 15 00:11:29 CST 2021
+#1 SMP PREEMPT Fri May 10 21:48:44 CST 2019
+#2 SMP PREEMPT Wed Jun 23 12:31:27 +07 2021
+#3 SMP PREEMPT Wed Jul 22 18:41:01 KST 2020
+#1 SMP PREEMPT Fri Jul 3 01:17:30 CST 2020
+#1 SMP PREEMPT Fri Mar 12 12:57:59 KST 2021
+#1 SMP PREEMPT Tue Jul 9 23:20:49 CST 2019
+#1 SMP PREEMPT Fri Oct 18 22:00:10 CST 2019
+#1 SMP PREEMPT Mon Feb 21 16:51:19 CST 2022
+#1 SMP PREEMPT Tue Oct 12 05:22:30 CST 2021
+#1 SMP PREEMPT Fri Feb 18 19:30:25 CST 2022
+#1 SMP PREEMPT Thu Jan 25 04:56:47 EST 2018
+#1 SMP PREEMPT Thu Oct 31 18:40:53 CST 2019
+#1 SMP PREEMPT Wed Jul 25 23:42:15 PDT 2018
+#1 SMP PREEMPT Thu Nov 1 12:44:38 KST 2018
+#1 SMP PREEMPT Fri Mar 2 17:58:07 CST 2018
+#1 SMP PREEMPT Mon Jul 29 18:37:49 WIB 2019
+#1 SMP PREEMPT Thu Apr 25 14:08:57 CST 2019
+#1 SMP PREEMPT Fri Apr 20 15:15:19 CST 2018
+#1 SMP PREEMPT Thu Feb 14 12:49:55 CST 2019
+#1 SMP PREEMPT Wed Aug 11 13:18:05 KST 2021
+#1 SMP PREEMPT Thu Dec 31 00:52:08 CST 2020
+#1 SMP PREEMPT Fri Jul 23 22:40:56 KST 2021
+#1 SMP PREEMPT Tue Nov 9 23:53:27 CST 2021
+#1 SMP PREEMPT Sat Nov 9 00:32:09 CST 2019
+#1 SMP PREEMPT Thu May 30 13:14:14 CST 2019
+#8 SMP PREEMPT Sat Dec 26 12:16:52 CST 2020
+#1 SMP PREEMPT Sun Oct 31 04:55:57 PDT 2021
+#2 SMP PREEMPT Sat Dec 15 19:00:26 CST 2018
+#1 SMP PREEMPT Wed Oct 20 23:54:05 CST 2021
+#1 SMP PREEMPT Tue Jul 7 14:14:37 CST 2020
+#1 SMP PREEMPT Mon Mar 9 17:36:27 CST 2020
+#1 SMP PREEMPT Wed Dec 11 12:30:37 KST 2019
+#1 SMP PREEMPT Thu Dec 3 05:09:43 CST 2020
+#1 SMP PREEMPT Thu Mar 24 22:19:56 CST 2022
+#1 SMP PREEMPT Thu Jun 14 23:02:03 CST 2018
+#1 SMP PREEMPT Tue Nov 20 15:40:34 EST 2018
+#1 SMP PREEMPT Thu Sep 23 21:25:43 CST 2021
+#1 SMP PREEMPT Thu Aug 16 19:58:10 CST 2018
+#1 SMP PREEMPT Thu Nov 1 20:09:18 CST 2018
+#1 SMP PREEMPT Fri Mar 29 16:15:11 -03 2019
+#1 SMP PREEMPT Tue Jan 12 15:46:58 CST 2021
+#1 SMP PREEMPT Wed Jun 12 12:22:44 KST 2019
+#1 SMP PREEMPT Wed Jul 17 11:23:48 CST 2019
+#1 SMP PREEMPT Mon Mar 23 10:28:12 CDT 2020
+#1 SMP PREEMPT Fri Jul 26 03:31:03 CST 2019
+#1 SMP PREEMPT Fri May 29 12:01:05 CST 2020
+#1 SMP PREEMPT Mon Dec 14 16:15:35 CST 2020
+#1 SMP PREEMPT Mon Feb 21 17:10:59 CST 2022
+#1 SMP PREEMPT Fri Nov 24 23:33:59 KST 2017
+#2 SMP PREEMPT Sat Jan 6 13:29:18 CST 2018
+#1 SMP PREEMPT Sat Mar 16 16:57:56 CST 2019
+#1 SMP PREEMPT Fri May 7 11:38:42 KST 2021
+#1 SMP PREEMPT Fri Jun 21 00:46:14 PDT 2019
+#6 SMP PREEMPT Thu Dec 2 11:18:33 UTC 2021
+#1 SMP PREEMPT Sun Jun 14 00:34:24 CST 2020
+#1 SMP PREEMPT Fri Mar 22 11:01:34 CST 2019
+#1 SMP PREEMPT Tue Sep 17 22:57:35 UTC 2019
+#1 SMP PREEMPT Wed Dec 19 20:43:08 CST 2018
+#1 SMP PREEMPT Wed Dec 12 00:00:57 PST 2018
+#1 SMP PREEMPT Wed Jul 3 07:40:47 CST 2019
+#1 SMP PREEMPT Thu Dec 12 11:28:11 KST 2019
+#1 SMP PREEMPT Mon Nov 11 16:01:49 CST 2019
+#2 SMP PREEMPT Fri Oct 18 10:49:15 CST 2019
+#1 SMP PREEMPT Fri Mar 5 05:23:55 PST 2021
+#2 SMP PREEMPT Wed Sep 9 11:21:53 CST 2020
+#1 SMP PREEMPT Thu Mar 26 12:20:53 2020
+#1 SMP PREEMPT Tue Apr 28 17:36:26 JST 2020
+#1 SMP PREEMPT Tue Sep 28 11:23:47 UTC 2021
+#1 SMP PREEMPT Sat Mar 24 23:49:07 EDT 2018
+#1 SMP PREEMPT Wed Apr 6 22:51:55 CST 2022
+#1 SMP PREEMPT Mon May 17 07:43:55 CST 2021
+#22 SMP PREEMPT Tue Nov 10 11:18:50 CST 2020
+#1 SMP PREEMPT Fri May 22 01:33:25 CST 2020
+#1 SMP PREEMPT Sun Jul 14 00:07:18 PDT 2019
+#1 SMP PREEMPT Mon May 10 23:53:48 CST 2021
+#1 SMP PREEMPT Wed Mar 31 10:50:05 KST 2021
+#2 SMP PREEMPT Mon Mar 11 14:54:31 CST 2019
+#1 SMP PREEMPT Tue May 7 01:54:40 KST 2019
+#1 SMP PREEMPT Thu Aug 6 12:33:11 CST 2020
+#1 SMP PREEMPT Fri May 3 02:30:25 CST 2019
+#1 SMP PREEMPT Wed May 25 11:14:00 CST 2022
+#1 SMP PREEMPT Sat Jan 11 17:21:25 CST 2020
+#1 SMP PREEMPT Wed Nov 10 14:21:38 CST 2021
+#1 SMP PREEMPT Sun May 24 07:58:41 JST 2020
+#1 SMP PREEMPT Thu Aug 6 21:21:58 WIB 2020
+#1 SMP PREEMPT Fri May 14 11:28:13 KST 2021
+#1 SMP PREEMPT Fri Oct 9 18:43:30 CST 2020
+#1 SMP PREEMPT Sat May 11 00:40:17 CST 2019
+#1 SMP PREEMPT Mon Apr 1 22:56:46 CST 2019
+#1 SMP PREEMPT Tue May 28 19:10:04 -03 2019
+#1 SMP PREEMPT Fri Jul 3 01:17:30 CST 2020
+#1 SMP PREEMPT Fri May 6 18:09:43 CST 2022
+#1 SMP PREEMPT Tue Nov 13 18:36:32 IST 2018
+#2 SMP PREEMPT Sat Jun 22 00:49:49 KST 2019
+#1 SMP PREEMPT Thu Dec 17 10:22:03 KST 2020
+#2 SMP PREEMPT Wed May 8 19:03:57 KST 2019
+#1 SMP PREEMPT Thu Aug 27 22:51:49 CST 2020
+#1 SMP PREEMPT Fri Jan 11 16:23:50 CST 2019
+#1 SMP PREEMPT Thu Jan 20 14:04:42 UTC 2022
+#1 SMP PREEMPT Tue Aug 3 00:32:50 CST 2021
+#2 SMP PREEMPT Sat Sep 7 20:14:56 CST 2019
+#1 SMP PREEMPT Tue May 14 17:15:56 KST 2019
+#1 SMP PREEMPT Sun Sep 8 20:54:36 WIB 2019
+#1 SMP PREEMPT Tue Oct 22 09:37:49 -02 2019
+#2 SMP PREEMPT Thu Oct 31 12:02:20 KST 2019
+#1 SMP PREEMPT Tue Aug 18 02:31:30 JST 2020
+#1 SMP PREEMPT Mon Sep 10 12:32:43 CST 2018
+#1 SMP PREEMPT Mon Nov 1 16:15:42 CST 2021
+#2 SMP PREEMPT Sat Aug 11 18:16:17 CST 2018
+#1 SMP PREEMPT Sat Aug 1 18:35:26 CST 2020
+#2 SMP PREEMPT Mon May 6 04:52:48 CST 2019
+#1 SMP PREEMPT Thu Mar 25 12:31:40 CST 2021
+#2 SMP PREEMPT Mon Jun 8 14:02:06 CEST 2020
+#1 SMP PREEMPT Tue Jun 30 01:28:45 CST 2020
+#2 SMP PREEMPT Thu Sep 6 15:54:51 CST 2018
+#1 SMP PREEMPT Mon Sep 28 23:46:32 KST 2020
+#1 SMP PREEMPT Sat Dec 4 16:42:08 CST 2021
+#9 SMP PREEMPT Mon Dec 16 10:26:39 CST 2019
+#1 SMP PREEMPT Fri Mar 23 08:10:06 CDT 2018
+#1 SMP PREEMPT Tue Aug 14 06:21:32 CST 2018
+#1 SMP PREEMPT Mon May 4 13:09:07 IST 2020
+#2 SMP PREEMPT Wed Jul 14 21:00:57 CST 2021
+#1 SMP PREEMPT Tue Feb 8 22:29:54 CST 2022
+#1 SMP PREEMPT Fri Apr 15 11:08:02 JST 2022
+#1 SMP PREEMPT Mon Nov 4 00:09:54 PST 2019
+#1 SMP PREEMPT Wed Dec 22 18:10:48 CST 2021
+#1 SMP PREEMPT Thu Dec 7 18:35:47 KST 2017
+#1 SMP PREEMPT Fri Mar 16 01:55:41 CST 2018
+#1 SMP PREEMPT Tue Jan 2 15:37:29 KST 2018
+#1 SMP PREEMPT Tue Feb 27 18:55:16 CST 2018
+#1 SMP PREEMPT Sun Jan 7 18:58:25 CST 2018
+#2 SMP PREEMPT Wed Mar 6 14:45:08 CST 2019
+#1 SMP PREEMPT Wed Feb 27 20:39:51 JST 2019
+#1 SMP PREEMPT Tue Sep 22 21:24:35 KST 2020
+#1 SMP PREEMPT Sat Oct 20 05:43:17 CST 2018
+#27 SMP PREEMPT Tue Jul 30 11:20:38 CST 2019
+#1 SMP PREEMPT Mon Jun 18 15:46:53 KST 2018
+#1 SMP PREEMPT Thu May 3 19:10:38 CST 2018
+#1 SMP PREEMPT Mon Jul 13 14:56:52 KST 2020
+#1 SMP PREEMPT Sat Jul 18 02:47:07 CST 2020
+#1 SMP PREEMPT Mon Nov 4 12:39:35 CST 2019
+#1 SMP PREEMPT Thu Aug 5 16:20:03 CDT 2021
+#1 SMP PREEMPT Thu Jun 25 00:12:57 CST 2020
+#1 SMP PREEMPT Fri Feb 11 17:28:02 KST 2022
+#1 SMP PREEMPT Wed Jan 31 10:43:16 CST 2018
+#1 SMP PREEMPT Thu Nov 5 22:39:17 KST 2020
+#1 SMP PREEMPT Tue May 19 23:55:22 KST 2020
+#1 SMP PREEMPT Thu May 12 22:14:57 CST 2022
+#1 SMP PREEMPT Wed Aug 29 22:05:09 CST 2018
+#1 SMP PREEMPT Sat Jul 17 03:55:28 KST 2021
+#1 SMP PREEMPT Wed Dec 8 15:20:20 CST 2021
+#1 SMP PREEMPT Thu Aug 20 16:22:25 2020
+#1 SMP PREEMPT Wed May 29 10:14:53 EDT 2019
+#1 SMP PREEMPT Tue Sep 10 03:30:14 CST 2019
+#1 SMP PREEMPT Sat Feb 16 18:58:13 CST 2019
+#1 SMP PREEMPT Wed Aug 8 17:00:50 CST 2018
+#1 SMP PREEMPT Thu Nov 30 16:03:16 CST 2017
+#1 SMP PREEMPT Tue Nov 12 12:44:36 IST 2019
+#1 SMP PREEMPT Mon Jun 4 22:17:40 PDT 2018
+#1 SMP PREEMPT Tue Aug 18 12:07:11 KST 2020
+#1 SMP PREEMPT Thu Aug 5 15:28:53 PDT 2021
+#1 SMP PREEMPT Mon May 14 09:42:17 CST 2018
+#1 SMP PREEMPT Sun Apr 17 09:17:22 CDT 2022
+#1 SMP PREEMPT Sun Aug 26 14:07:51 CST 2018
+#1 SMP PREEMPT Fri May 21 14:01:57 JST 2021
+#1 SMP PREEMPT Wed Jul 4 05:23:29 CST 2018
+#1 SMP PREEMPT Wed Mar 9 10:55:04 CST 2022
+#1 SMP PREEMPT Tue Apr 30 10:44:01 CST 2019
+#1 SMP PREEMPT Wed Sep 25 18:33:56 KST 2019
+#1 SMP PREEMPT Wed Aug 15 14:14:13 CST 2018
+#1 SMP PREEMPT Sat Dec 18 01:06:45 CST 2021
+#1 SMP PREEMPT Thu Apr 5 22:10:53 CST 2018
+#1 SMP PREEMPT Wed May 11 18:20:32 CST 2022
+#2 SMP PREEMPT Tue Nov 12 20:38:11 CST 2019
+#1 SMP PREEMPT Tue Jun 9 22:12:57 CST 2020
+#1 SMP PREEMPT Tue Mar 5 18:58:07 CST 2019
+#1 SMP PREEMPT Sun Oct 11 00:32:10 CST 2020
+#1 SMP PREEMPT Tue Apr 16 22:52:33 CST 2019
+#2 SMP PREEMPT Tue Dec 18 12:08:29 CST 2018
+#3 SMP PREEMPT Wed Feb 16 11:14:37 CST 2022
+#1 SMP PREEMPT Fri Mar 25 14:39:13 CST 2022
+#1 SMP PREEMPT Wed Apr 20 15:32:23 CST 2022
+#1 SMP PREEMPT Wed Nov 8 06:19:15 UTC 2017
+#1 SMP PREEMPT Tue Jan 19 02:10:36 CST 2021
+#1 SMP PREEMPT Wed Apr 29 11:20:41 CST 2020
+#2 SMP PREEMPT Fri Jul 3 21:36:59 KST 2020
+#1 SMP PREEMPT Mon Jul 6 21:00:29 WIB 2020
+#1 SMP PREEMPT Wed Jun 27 17:43:03 HKT 2018
+#1 SMP PREEMPT Fri Jan 4 09:34:57 CST 2019
+#2 SMP PREEMPT Mon Apr 2 19:28:03 CST 2018
+#1 SMP PREEMPT Wed Oct 13 15:00:50 JST 2021
+#1 SMP PREEMPT Sun Apr 24 01:34:30 CST 2022
+#1 SMP PREEMPT Thu Mar 3 13:26:19 CST 2022
+#2 SMP PREEMPT Mon Mar 29 04:25:19 JST 2021
+#1 SMP PREEMPT Mon May 2 23:50:27 CST 2022
+#1 SMP PREEMPT Thu Jul 15 23:23:33 CST 2021
+#1 SMP PREEMPT Tue Mar 6 03:50:43 CST 2018
+#4 SMP PREEMPT Mon Dec 11 16:40:38 CST 2017
+#1 SMP PREEMPT Thu Jul 1 16:50:48 CST 2021
+#1 SMP PREEMPT Wed Mar 14 20:58:58 UTC 2018
+#1 SMP PREEMPT Fri Nov 29 16:01:59 CST 2019
+#1 SMP PREEMPT Wed Apr 22 11:28:10 CDT 2020
+#1 SMP PREEMPT Thu Apr 26 10:21:38 CST 2018
+#1 SMP PREEMPT Tue Dec 12 18:56:36 CST 2017
+#1 SMP PREEMPT Fri Oct 13 04:41:16 UTC 2017
+#1 SMP PREEMPT Mon May 10 20:14:37 KST 2021
+#38 SMP PREEMPT Mon Mar 15 11:29:28 CST 2021
+#1 SMP PREEMPT Tue Jan 14 11:09:43 CST 2020
+#1 SMP PREEMPT Fri May 10 05:43:07 KST 2019
+#1 SMP PREEMPT Mon Feb 26 18:46:01 CST 2018
+#1 SMP PREEMPT Thu Oct 21 04:31:24 IST 2021
+#1 SMP PREEMPT Mon Sep 14 22:59:42 CST 2020
+#1 SMP PREEMPT Fri Jul 16 15:45:42 PDT 2021
+#1 SMP PREEMPT Fri May 6 02:55:13 PDT 2022
+#1 SMP PREEMPT Fri Apr 2 12:04:05 +07 2021
+#1 SMP PREEMPT Mon Nov 27 11:55:36 BRST 2017
+#1 SMP PREEMPT Mon Apr 6 10:36:41 CST 2020
+#1 SMP PREEMPT Tue Aug 6 11:02:30 CST 2019
+#1 SMP PREEMPT Mon Jul 23 22:12:13 PDT 2018
+#2 SMP PREEMPT Thu Sep 5 01:32:02 CST 2019
+#1 SMP PREEMPT Mon Apr 2 13:03:10 CST 2018
+#1 SMP PREEMPT Wed May 8 20:41:09 CST 2019
+#1 SMP PREEMPT Wed Aug 21 11:44:11 +07 2019
+#2 SMP PREEMPT Sat Mar 19 03:23:31 JST 2022
+#3 SMP PREEMPT Wed Oct 13 11:16:15 CST 2021
+#1 SMP PREEMPT Thu Apr 25 19:48:25 KST 2019
+#1 SMP PREEMPT Fri Apr 9 10:41:09 CST 2021
+#1 SMP PREEMPT Tue Nov 28 12:29:02 CST 2017
+#1 SMP PREEMPT Mon Dec 2 12:57:17 CST 2019
+#2 SMP PREEMPT Mon Sep 23 21:34:27 KST 2019
+#152 SMP PREEMPT Thu Nov 22 19:52:28 CST 2018
+#1 SMP PREEMPT Tue Dec 15 10:26:42 CST 2020
+#1 SMP PREEMPT Thu Jan 13 03:08:32 JST 2022
+#1 SMP PREEMPT Fri Mar 9 19:58:46 CST 2018
+#1 SMP PREEMPT Tue Nov 6 23:05:45 CST 2018
+#2 SMP PREEMPT Mon Jul 20 12:29:03 CST 2020
+#2 SMP PREEMPT Sun Jul 26 00:26:29 CST 2020
+#1 SMP PREEMPT Tue Apr 9 21:49:40 PDT 2019
+#2 SMP PREEMPT Thu Sep 26 15:54:46 CST 2019
+#1 SMP PREEMPT Thu May 31 15:07:25 CST 2018
+#1 SMP PREEMPT Sun Dec 2 20:33:57 2018
+#1 SMP PREEMPT Fri Aug 9 09:54:35 CDT 2019
+#1 SMP PREEMPT Mon Jan 14 11:54:01 CST 2019
+#1 SMP PREEMPT Thu Jan 10 19:33:33 CST 2019
+#2 SMP PREEMPT Fri Nov 2 11:19:08 CST 2018
+#1 SMP PREEMPT Wed Apr 8 02:34:03 CST 2020
+#1 SMP PREEMPT Wed Jul 17 20:39:49 IST 2019
+#1 SMP PREEMPT Tue Apr 2 16:35:12 KST 2019
+#1 SMP PREEMPT Wed Jul 18 04:14:47 PDT 2018
+#1 SMP PREEMPT Fri Dec 13 00:48:18 CST 2019
+#1 SMP PREEMPT Wed Apr 11 16:03:50 CST 2018
+#2 SMP PREEMPT Wed May 27 11:12:10 CST 2020
+#1 SMP PREEMPT Fri Nov 26 07:30:36 UTC 2021
+#2 SMP PREEMPT Fri Sep 27 11:40:25 KST 2019
+#1 SMP PREEMPT Mon Sep 3 18:45:24 KST 2018
+#17 SMP PREEMPT Wed Oct 27 16:20:48 CST 2021
+#2 SMP PREEMPT Fri Jul 24 15:22:20 KST 2020
+#1 SMP PREEMPT Wed Apr 6 11:34:34 IST 2022
+#1 SMP PREEMPT Fri Jan 1 00:00:00 UTC 2021
+#1 SMP PREEMPT Mon Oct 12 21:25:51 JST 2020
+#1 SMP PREEMPT Wed Jun 23 12:52:28 CST 2021
+#1 SMP PREEMPT Thu Nov 30 22:11:35 CST 2017
+#1 SMP PREEMPT Mon Jan 4 10:57:50 CST 2021
+#2 SMP PREEMPT Tue Aug 27 05:32:14 KST 2019
+#1 SMP PREEMPT Tue Sep 7 13:29:15 CST 2021
+#2 SMP PREEMPT Tue Sep 11 22:27:49 CST 2018
+#1 SMP PREEMPT Thu Jan 14 15:36:24 CST 2021
+#1 SMP PREEMPT Fri May 15 00:15:47 CST 2020
+#1 SMP PREEMPT Mon Jun 4 20:32:33 KST 2018
+#2 SMP PREEMPT Fri Jul 19 16:38:33 KST 2019
+#1 SMP PREEMPT Wed Dec 2 14:52:08 CST 2020
+#1 SMP PREEMPT Fri Apr 8 16:45:35 CST 2022
+#1 SMP PREEMPT Wed May 11 05:17:33 UTC 2022
+#1 SMP PREEMPT Fri Feb 2 17:45:28 KST 2018
+#1 SMP PREEMPT Mon Jul 27 15:39:24 KST 2020
+#1 SMP PREEMPT Wed May 27 00:39:59 PDT 2020
+#1 SMP PREEMPT Sat Jan 8 17:24:46 CST 2022
+#1 SMP PREEMPT Sat Nov 7 12:00:13 CST 2020
+#1 SMP PREEMPT Thu Dec 10 19:40:51 CST 2020
+#1 SMP PREEMPT Thu Mar 5 22:08:57 CST 2020
+#1 SMP PREEMPT Thu Jan 13 21:44:44 CST 2022
+#2 SMP PREEMPT Wed Jul 8 19:35:59 KST 2020
+#1 SMP PREEMPT Wed Dec 5 22:05:38 CST 2018
+#1 SMP PREEMPT Wed May 11 20:42:51 CST 2022
+#1 SMP PREEMPT Mon Dec 28 12:24:38 CST 2020
+#1 SMP PREEMPT Mon Apr 8 01:49:35 -03 2019
+#1 SMP PREEMPT Thu Jul 1 19:18:42 CST 2021
+#2 SMP PREEMPT Thu Mar 17 13:43:54 KST 2022
+#1 SMP PREEMPT Wed Nov 7 09:36:23 KST 2018
+#1 SMP PREEMPT Mon Oct 29 13:14:06 KST 2018
+#1 SMP PREEMPT Thu Aug 1 21:02:17 CST 2019
+#1 SMP PREEMPT Wed Oct 28 11:42:10 CDT 2020
+#1 SMP PREEMPT Fri Aug 16 23:33:51 CST 2019
+#1 SMP PREEMPT Fri May 13 18:54:54 CST 2022
+#1 SMP PREEMPT Tue May 15 22:21:43 PDT 2018
+#1 SMP PREEMPT Fri Jul 13 19:36:58 CST 2018
+#1 SMP PREEMPT Wed Mar 28 21:54:07 CST 2018
+#1 SMP PREEMPT Mon Mar 30 18:25:20 CST 2020
+#1 SMP PREEMPT Tue Oct 22 14:47:11 CST 2019
+#1 SMP PREEMPT Wed Oct 28 22:38:10 WIB 2020
+#49 SMP PREEMPT Mon Sep 21 10:46:23 CST 2020
+#1 SMP PREEMPT Wed Nov 14 13:40:45 CST 2018
+#1 SMP PREEMPT Thu Jun 18 19:59:20 CST 2020
+#1 SMP PREEMPT Fri Mar 16 13:02:59 2018
+#2 SMP PREEMPT Wed May 16 04:04:04 CST 2018
+#1 SMP PREEMPT Mon Apr 12 23:55:33 CST 2021
+#1 SMP PREEMPT Mon Nov 4 00:38:09 CST 2019
+#1 SMP PREEMPT Mon Sep 30 22:28:24 KST 2019
+#1 SMP PREEMPT Thu Jul 16 13:07:21 KST 2020
+#1 SMP PREEMPT Thu May 23 18:54:31 CST 2019
+#1 SMP PREEMPT Wed Jan 15 18:17:00 UTC 2020
+#1 SMP PREEMPT Wed Jan 12 18:42:58 CST 2022
+#1 SMP PREEMPT Tue Apr 14 18:43:12 CST 2020
+#1 SMP PREEMPT Thu Jul 15 07:12:08 CST 2021
+#1 SMP PREEMPT Fri Mar 15 17:43:53 CST 2019
+#4 SMP PREEMPT Fri Jan 7 09:30:12 HKT 2022
+#1 SMP PREEMPT Tue Jan 11 11:59:36 CST 2022
+#2 SMP PREEMPT Thu Mar 21 11:19:26 -03 2019
+#1 SMP PREEMPT Sat Nov 9 22:23:35 CST 2019
+#1 SMP PREEMPT Thu Aug 1 19:45:07 KST 2019
+#1 SMP PREEMPT Sun Mar 31 03:13:48 CST 2019
+#1 SMP PREEMPT Thu Dec 7 18:54:29 KST 2017
+#1 SMP PREEMPT Tue Jul 14 18:33:24 KST 2020
+#1 SMP PREEMPT Wed Jul 18 00:12:57 CST 2018
+#1 SMP PREEMPT Tue Oct 20 09:33:29 PDT 2020
+#1 SMP PREEMPT Wed Apr 28 16:17:27 +07 2021
+#1 SMP PREEMPT Fri Jul 31 12:24:19 CST 2020
+#1 SMP PREEMPT Mon Apr 23 12:02:52 CST 2018
+#2 SMP PREEMPT Thu Jan 2 17:58:12 CST 2020
+#1 SMP PREEMPT Tue Aug 13 15:23:08 CDT 2019
+#1 SMP PREEMPT Tue Oct 9 20:44:52 PDT 2018
+#1 SMP PREEMPT Fri Jan 3 19:14:57 CST 2020
+#1 SMP PREEMPT Thu Jan 27 07:08:17 JST 2022
+#2 SMP PREEMPT Mon Aug 24 21:41:47 CST 2020
+#1 SMP PREEMPT Wed Apr 7 17:08:31 +07 2021
+#2 SMP PREEMPT Wed Dec 22 17:39:29 CST 2021
+#1 SMP PREEMPT Thu Oct 8 20:42:26 CST 2020
+#1 SMP PREEMPT Thu Dec 27 14:10:35 CST 2018
+#1 SMP PREEMPT Fri Feb 18 15:53:12 CST 2022
+#1 SMP PREEMPT Wed Feb 27 14:53:29 EST 2019
+#2 SMP PREEMPT Fri Jan 21 15:30:26 CST 2022
+#1 SMP PREEMPT Mon Feb 22 07:02:20 UTC 2021
+#1 SMP PREEMPT Wed Aug 15 14:53:34 CST 2018
+#1 SMP PREEMPT Thu Mar 17 16:37:31 CST 2022
+#1 SMP PREEMPT Thu Jan 10 16:18:00 CST 2019
+#1 SMP PREEMPT Mon Apr 19 20:27:34 PDT 2021
+#1 SMP PREEMPT Mon Aug 9 16:10:28 CST 2021
+#2 SMP PREEMPT Wed Sep 11 15:29:34 CST 2019
+#1 SMP PREEMPT Wed May 19 22:26:48 CST 2021
+#2 SMP PREEMPT Mon Apr 1 12:42:17 KST 2019
+#1 SMP PREEMPT Sat Jun 13 09:21:35 JST 2020
+#1 SMP PREEMPT Tue Apr 10 16:57:02 EDT 2018
+#1 SMP PREEMPT Fri Jul 20 22:18:16 CST 2018
+#1 SMP PREEMPT Mon Nov 26 18:52:44 CST 2018
+#1 SMP PREEMPT Thu Dec 7 15:35:19 CST 2017
+#1 SMP PREEMPT Mon Aug 19 20:14:08 CST 2019
+#1 SMP PREEMPT Fri Jan 7 14:51:36 UTC 2022
+#6 SMP PREEMPT Thu Dec 9 20:50:43 UTC 2021
+#1 SMP PREEMPT Fri May 11 22:12:28 CST 2018
+#1 SMP PREEMPT Wed Jan 13 16:44:12 CST 2021
+#1 SMP PREEMPT Tue Feb 19 22:11:23 CST 2019
+#1 SMP PREEMPT Tue Apr 20 21:53:50 PDT 2021
+#1 SMP PREEMPT Tue Oct 31 14:47:39 2017
+#1 SMP PREEMPT Tue Aug 14 10:27:44 KST 2018
+#1 SMP PREEMPT Thu Jan 11 19:03:33 CST 2018
+#2 SMP PREEMPT Fri Jun 25 16:07:59 CST 2021
+#1 SMP PREEMPT Tue Oct 6 01:49:05 JST 2020
+#1 SMP PREEMPT Tue Sep 24 18:59:02 CST 2019
+#1 SMP PREEMPT Thu May 31 19:01:58 CST 2018
+#1 SMP PREEMPT Fri Apr 10 19:16:18 CST 2020
+#1 SMP PREEMPT Fri Apr 5 17:15:13 KST 2019
+#1 SMP PREEMPT Tue Oct 9 04:04:42 CST 2018
+#1 SMP PREEMPT Thu Apr 1 10:09:56 PDT 2021
+#1 SMP PREEMPT Thu Aug 6 19:58:47 CST 2020
+#1 SMP PREEMPT Thu Aug 19 23:19:25 WIB 2021
+#1 SMP PREEMPT Fri Jan 14 14:51:53 CST 2022
+#1 SMP PREEMPT Wed Mar 7 02:17:54 JST 2018
+#1 SMP PREEMPT Tue Jan 2 11:00:40 KST 2018
+#2 SMP PREEMPT Fri Apr 15 22:16:03 CST 2022
+#1 SMP PREEMPT Tue Nov 3 16:02:38 KST 2020
+#1 SMP PREEMPT Sat Mar 13 10:51:05 CST 2021
+#1 SMP PREEMPT Mon Dec 9 19:07:26 CST 2019
+#2 SMP PREEMPT Tue Jun 11 15:42:06 CST 2019
+#1 SMP PREEMPT Wed Jan 9 10:56:44 CST 2019
+#1 SMP PREEMPT Tue Apr 27 11:07:54 HKT 2021
+#1 SMP PREEMPT Mon Feb 4 23:37:03 PST 2019
+#1 SMP PREEMPT Sat Jan 13 06:57:47 KST 2018
+#1 SMP PREEMPT Mon Dec 9 23:09:43 PST 2019
+#1 SMP PREEMPT Wed Apr 24 09:44:15 CST 2019
+#2 SMP PREEMPT Sat Jul 4 15:39:57 KST 2020
+#2 SMP PREEMPT Mon Jul 8 20:03:56 KST 2019
+#1 SMP PREEMPT Wed Feb 26 15:06:18 KST 2020
+#8 SMP PREEMPT Mon May 11 20:24:45 CST 2020
+#1 SMP PREEMPT Wed Jan 22 12:28:31 IST 2020
+#1 SMP PREEMPT Mon Oct 29 23:24:37 CST 2018
+#1 SMP PREEMPT Thu Feb 27 17:46:20 PST 2020
+#1 SMP PREEMPT Mon Mar 21 21:44:53 CST 2022
+#1 SMP PREEMPT Wed Jan 20 08:01:16 PST 2021
+#1 SMP PREEMPT Tue Dec 22 14:08:47 CST 2020
+#1 SMP PREEMPT Thu Feb 27 14:43:11 CST 2020
+#1 SMP PREEMPT Tue Jun 30 01:57:06 CST 2020
+#1 SMP PREEMPT Fri Oct 8 18:28:38 CST 2021
+#1 SMP PREEMPT Tue Dec 5 11:25:57 CST 2017
+#2 SMP PREEMPT Wed Jun 13 22:53:06 CST 2018
+#1 SMP PREEMPT Tue Jun 23 17:02:37 CST 2020
+#1 SMP PREEMPT Tue Nov 27 18:45:12 IST 2018
+#1 SMP PREEMPT Thu Dec 10 21:41:45 CST 2020
+#1 SMP PREEMPT Thu Jan 4 13:47:50 KST 2018
+#1 SMP PREEMPT Wed Dec 2 12:39:01 KST 2020
+#1 SMP PREEMPT Tue Aug 7 21:14:51 JST 2018
+#1 SMP PREEMPT Tue May 21 19:07:26 KST 2019
+#2 SMP PREEMPT Wed Nov 20 11:24:32 CST 2019
+#2 SMP PREEMPT Thu Jan 18 12:18:34 CST 2018
+#1 SMP PREEMPT Mon Jan 20 17:01:20 CST 2020
+#2 SMP PREEMPT Wed Apr 6 18:42:17 CST 2022
+#1 SMP PREEMPT Tue Nov 27 09:54:06 CST 2018
+#1 SMP PREEMPT Mon Aug 9 09:48:54 CST 2021
+#1 SMP PREEMPT Fri Sep 14 21:02:43 CST 2018
+#1 SMP PREEMPT Tue Jul 14 21:49:59 CST 2020
+#2 SMP PREEMPT Mon Jul 29 14:01:00 CST 2019
+#1 SMP PREEMPT Wed Mar 14 16:48:04 CST 2018
+#1 SMP PREEMPT Mon Oct 22 16:01:48 CST 2018
+#2 SMP PREEMPT Sun Sep 29 22:01:45 CST 2019
+#2 SMP PREEMPT Mon May 13 16:51:06 CST 2019
+#2 SMP PREEMPT Sat Dec 22 17:27:44 CST 2018
+#2 SMP PREEMPT Thu Apr 9 21:45:20 CST 2020
+#1 SMP PREEMPT Thu Jan 25 12:14:43 CST 2018
+#1 SMP PREEMPT Fri Jan 3 12:39:47 CST 2020
+#1 SMP PREEMPT Fri Dec 10 00:35:21 CST 2021
+#1 SMP PREEMPT Wed Aug 7 07:01:24 CST 2019
+#1 SMP PREEMPT Sun Feb 3 13:23:15 CST 2019
+#1 SMP PREEMPT Mon Oct 28 17:05:01 CST 2019
+#1 SMP PREEMPT Sat Jul 17 09:26:10 KST 2021
+#1 SMP PREEMPT Sun Apr 11 14:04:59 CST 2021
+#1 SMP PREEMPT Sat Mar 21 19:55:47 CST 2020
+#1 SMP PREEMPT Mon Dec 13 12:04:46 CST 2021
+#1 SMP PREEMPT Thu Jan 16 11:47:26 CST 2020
+#1 SMP PREEMPT Thu Jan 10 11:34:11 CST 2019
+#1 SMP PREEMPT Tue Oct 9 16:19:26 CST 2018
+#2 SMP PREEMPT Thu Jan 20 19:38:05 +07 2022
+#1 SMP PREEMPT Thu Oct 28 17:22:37 CST 2021
+#1 SMP PREEMPT Wed Mar 21 09:51:49 CST 2018
+#1 SMP PREEMPT Fri Aug 23 20:42:25 CST 2019
+#2 SMP PREEMPT Sat Feb 22 17:25:25 CST 2020
+#1 SMP PREEMPT Thu Dec 13 19:09:56 KST 2018
+#1 SMP PREEMPT Thu Apr 7 17:51:07 CST 2022
+#1 SMP PREEMPT Wed Apr 7 18:29:44 CST 2021
+#1 SMP PREEMPT Mon May 6 19:54:29 CST 2019
+#1 SMP PREEMPT Thu Nov 11 19:43:57 CST 2021
+#1 SMP PREEMPT Tue Mar 29 16:25:46 CST 2022
+#1 SMP PREEMPT Mon Aug 16 22:04:03 CST 2021
+#1 SMP PREEMPT Tue Sep 11 09:59:39 CDT 2018
+#1 SMP PREEMPT Wed Apr 27 18:23:53 CST 2022
+#2 SMP PREEMPT Thu May 14 00:05:02 CST 2020
+#2 SMP PREEMPT Thu May 13 20:47:42 CST 2021
+#2 SMP PREEMPT Fri May 21 19:39:45 KST 2021
+#1 SMP PREEMPT Fri Aug 9 18:35:45 CST 2019
+#1 SMP PREEMPT Wed Jul 18 04:25:39 PDT 2018
+#1 SMP PREEMPT Wed Jan 17 06:52:09 CST 2018
+#2 SMP PREEMPT Fri Jul 24 17:10:16 CST 2020
+#1 SMP PREEMPT Sun Jul 26 22:10:19 IST 2020
+#1 SMP PREEMPT Wed Jun 24 17:01:16 CST 2020
+#4 SMP PREEMPT Tue Nov 20 11:49:22 CST 2018
+#1 SMP PREEMPT Mon Mar 11 22:55:24 CST 2019
+#1 SMP PREEMPT Thu Dec 23 15:58:48 UTC 2021
+#4 SMP PREEMPT Fri Sep 10 00:04:21 CST 2021
+#1 SMP PREEMPT Tue Oct 22 18:06:20 CST 2019
+#1 SMP PREEMPT Tue Jun 22 21:36:56 HKT 2021
+#1 SMP PREEMPT Thu Nov 14 11:44:16 CST 2019
+#1 SMP PREEMPT Mon Apr 12 20:31:29 KST 2021
+#1 SMP PREEMPT Mon Sep 23 14:56:30 CST 2019
+#1 SMP PREEMPT Thu Jan 13 22:15:43 UTC 2022
+#1 SMP PREEMPT Mon Dec 17 21:17:48 CST 2018
+#1 SMP PREEMPT Fri Feb 7 15:38:31 KST 2020
+#1 SMP PREEMPT Tue Mar 8 11:07:43 CST 2022
+#1 SMP PREEMPT Fri Dec 18 17:37:33 CST 2020
+#2 SMP PREEMPT Thu Sep 3 14:23:27 CST 2020
+#1 SMP Tue Nov 23 17:29:59 KST 2021
+#1 SMP PREEMPT Sun Jun 30 10:29:18 CST 2019
+#1 SMP PREEMPT Mon Aug 12 17:32:14 CST 2019
+#1 SMP PREEMPT Thu Nov 25 01:20:50 CST 2021
+#1 SMP PREEMPT Wed Nov 20 09:45:25 GMT 2019
+#1 SMP PREEMPT Mon Feb 26 18:38:13 2018
+#4 SMP PREEMPT Thu Jun 21 16:21:05 HKT 2018
+#1 SMP PREEMPT Thu Jan 2 14:06:33 IST 2020
+#1 SMP PREEMPT Tue Nov 26 17:46:43 JST 2019
+#1 SMP PREEMPT Wed May 1 12:04:58 CEST 2019
+#1 SMP PREEMPT Wed Jul 21 12:40:50 CST 2021
+#1 SMP PREEMPT Thu Oct 21 18:12:17 CST 2021
+#1 SMP PREEMPT Wed Sep 30 09:41:35 CST 2020
+#1 SMP PREEMPT Thu Mar 14 17:41:12 CST 2019
+#1 SMP PREEMPT Mon Nov 12 14:10:23 KST 2018
+#2 SMP PREEMPT Mon Jan 21 23:08:23 CST 2019
+#1 SMP PREEMPT Tue Apr 16 23:37:45 CST 2019
+#1 SMP PREEMPT Wed Mar 11 12:42:22 KST 2020
+#1 SMP PREEMPT Mon Feb 18 13:30:34 UTC 2019
+#1 SMP PREEMPT Thu Dec 9 06:58:37 PST 2021
+#2 SMP PREEMPT Tue Jul 3 10:46:34 CST 2018
+#2 SMP PREEMPT Thu Aug 29 23:02:36 CST 2019
+#1 SMP PREEMPT Sat Feb 26 23:34:07 PST 2022
+#1 SMP PREEMPT Sun Dec 20 18:53:26 CST 2020
+#1 SMP PREEMPT Wed Dec 1 07:27:33 CST 2021
+#1 SMP PREEMPT Thu Sep 6 12:55:08 CST 2018
+#1 SMP PREEMPT Wed Oct 30 04:20:26 CST 2019
+#1 SMP PREEMPT Sat Aug 11 02:45:13 IST 2018
+#1 SMP PREEMPT Tue Mar 13 23:46:20 EDT 2018
+#1 SMP PREEMPT Tue Sep 18 17:13:20 CST 2018
+#1 SMP PREEMPT Mon Dec 14 19:48:16 KST 2020
+#1 SMP PREEMPT Tue Jun 2 20:25:29 KST 2020
+#1 SMP PREEMPT Tue Apr 13 21:21:35 CST 2021
+#1 SMP PREEMPT Thu Nov 26 15:23:28 CST 2020
+#1 SMP PREEMPT Wed Apr 24 16:15:06 CST 2019
+#1 SMP PREEMPT Sat Aug 15 14:04:27 CST 2020
+#1 SMP PREEMPT Thu Jul 12 10:58:36 2018
+#1 SMP PREEMPT Fri May 7 01:33:11 CST 2021
+#1 SMP PREEMPT Tue Dec 4 17:42:57 CST 2018
+#1 SMP PREEMPT Wed Feb 27 21:44:16 CST 2019
+#1 SMP PREEMPT Thu Oct 25 19:24:45 KST 2018
+#1 SMP PREEMPT Wed Dec 16 15:39:36 +07 2020
+#1 SMP PREEMPT Mon Dec 13 15:20:44 CST 2021
+#1 SMP PREEMPT Fri Sep 18 14:38:53 KST 2020
+#1 SMP PREEMPT Wed Aug 28 14:34:17 CST 2019
+#1 SMP PREEMPT Tue Mar 12 14:50:12 CST 2019
+#2 SMP PREEMPT Wed Feb 3 03:21:51 JST 2021
+#1 SMP PREEMPT Sun Dec 15 22:07:03 CST 2019
+#1 SMP PREEMPT Wed Jan 19 19:31:18 UTC 2022
+#3 SMP PREEMPT Tue Aug 3 11:05:01 CST 2021
+#11 SMP PREEMPT Tue Jul 24 10:09:25 CST 2018
+#1 SMP PREEMPT Sat Oct 26 15:18:22 CST 2019
+#1 SMP PREEMPT Mon Dec 6 08:14:47 UTC 2021
+#1 SMP PREEMPT Thu Oct 18 14:41:37 CST 2018
+#1 SMP PREEMPT Thu Jun 25 12:02:29 CST 2020
+#1 SMP PREEMPT Thu Mar 1 17:54:48 CST 2018
+#1 SMP PREEMPT Tue Apr 12 00:47:30 CST 2022
+#1 SMP PREEMPT Mon Mar 16 11:09:23 CST 2020
+#1 SMP PREEMPT Fri Nov 27 16:08:24 KST 2020
+#1 SMP PREEMPT Wed Oct 17 12:52:55 CST 2018
+#1 SMP PREEMPT Mon Jul 5 11:15:31 +07 2021
+#1 SMP PREEMPT Tue Dec 12 23:50:55 KST 2017
+#2 SMP PREEMPT Mon Nov 16 10:03:01 CST 2020
+#1 SMP PREEMPT Tue Aug 31 10:50:45 IST 2021
+#1 SMP PREEMPT Thu Jul 12 16:55:10 KST 2018
+#1 SMP PREEMPT Mon Sep 17 06:38:45 CST 2018
+#1 SMP PREEMPT Fri Jun 21 00:45:31 PDT 2019
+#1 SMP PREEMPT Sat Dec 30 11:15:26 CST 2017
+#1 SMP PREEMPT Fri Feb 1 20:36:54 JST 2019
+#2 SMP PREEMPT Thu Sep 26 12:21:17 CST 2019
+#1 SMP PREEMPT Thu May 27 14:58:10 CST 2021
+#1 SMP PREEMPT Fri Dec 28 14:16:47 CST 2018
+#1 SMP PREEMPT Tue Aug 3 12:30:57 CST 2021
+#1 SMP PREEMPT Tue Mar 27 15:45:03 KST 2018
+#1 SMP PREEMPT Thu Feb 7 12:05:45 GMT+2 2019
+#1 SMP PREEMPT Fri Sep 18 10:45:55 JST 2020
+#1 SMP PREEMPT Wed Mar 23 23:39:54 CST 2022
+#6 SMP PREEMPT Thu Dec 2 11:18:33 UTC 2021
+#1 SMP PREEMPT Thu Nov 12 13:34:48 IST 2020
+#1 SMP PREEMPT Tue Apr 9 13:17:41 CST 2019
+#1 SMP PREEMPT Wed Aug 18 22:14:31 CST 2021
+#1 SMP PREEMPT Tue Sep 7 12:05:39 UTC 2021
+#1 SMP PREEMPT Mon May 13 14:55:36 CST 2019
+#1 SMP PREEMPT Tue Jun 30 16:52:30 CST 2020
+#1 SMP PREEMPT Tue Jan 11 13:58:34 UTC 2022
+#1 SMP PREEMPT Fri Jul 6 12:32:39 CST 2018
+#1 SMP PREEMPT Tue Nov 10 06:31:30 KST 2020
+#2 SMP PREEMPT Sun Jul 26 00:50:15 CST 2020
+#1 SMP PREEMPT Tue Sep 26 16:58:26 CST 2017
+#2 SMP PREEMPT Fri Nov 1 04:55:37 CST 2019
+#1 SMP PREEMPT Fri Jul 26 03:31:03 CST 2019
+#1 SMP PREEMPT Wed Nov 27 17:50:10 CST 2019
+#1 SMP PREEMPT Tue May 18 16:07:34 CST 2021
+#1 SMP PREEMPT Mon Mar 12 18:47:19 KST 2018
+#1 SMP PREEMPT Wed Jan 6 03:42:08 CST 2021
+#1 SMP PREEMPT Thu Jun 18 02:36:01 CDT 2020
+#1 SMP PREEMPT Sat Apr 2 05:28:51 JST 2022
+#1 SMP PREEMPT Tue Aug 21 02:07:24 CST 2018
+#1 SMP PREEMPT Tue Feb 19 17:42:41 CST 2019
+#1 SMP PREEMPT Sun Apr 5 01:19:45 CST 2020
+#1 SMP PREEMPT Thu Mar 7 04:58:45 PST 2019
+#1 SMP PREEMPT Mon Jan 17 20:16:41 CST 2022
+#1 SMP PREEMPT Wed Jul 1 23:45:41 CDT 2020
+#1 SMP PREEMPT Tue Dec 28 15:54:59 CST 2021
+#1 SMP PREEMPT Tue Jan 14 12:30:25 KST 2020
+#1 SMP PREEMPT Wed Jun 19 16:00:44 KST 2019
+#1 SMP PREEMPT Tue Aug 25 10:23:10 2020
+#1 SMP PREEMPT Wed Nov 13 13:54:38 KST 2019
+#1 SMP PREEMPT Thu Oct 18 18:32:34 CST 2018
+#1 SMP PREEMPT Fri Sep 27 17:04:24 CST 2019
+#1 SMP PREEMPT Tue Apr 19 11:12:43 CST 2022
+#1 SMP PREEMPT Wed Sep 26 12:18:52 CST 2018
+#1 SMP PREEMPT Mon Jan 14 20:16:50 CST 2019
+#1 SMP PREEMPT Mon Sep 27 15:28:52 CST 2021
+#2 SMP PREEMPT Fri Oct 16 16:00:25 KST 2020
+#2 SMP PREEMPT Thu Jun 28 18:34:40 CST 2018
+#1 SMP PREEMPT Thu Jan 3 10:18:27 IST 2019
+#1 SMP PREEMPT Wed Sep 19 11:22:29 CST 2018
+#2 SMP PREEMPT Thu May 14 00:05:02 CST 2020
+#1 SMP PREEMPT Fri Aug 6 13:12:08 PDT 2021
+#1 SMP PREEMPT Mon Oct 26 15:33:19 CST 2020
+#2 SMP PREEMPT Wed Oct 17 18:53:53 CST 2018
+#1 SMP PREEMPT Wed Dec 29 12:17:27 CST 2021
+#1 SMP PREEMPT Tue Aug 21 20:27:47 CST 2018
+#1 SMP PREEMPT Tue Mar 12 01:39:43 CST 2019
+#1 SMP PREEMPT Sat Sep 12 11:12:38 CST 2020
+#1 SMP PREEMPT Thu Mar 17 18:53:26 CST 2022
+#1 SMP PREEMPT Tue Jan 7 00:13:01 KST 2020
+#1 SMP PREEMPT Mon Mar 18 23:40:05 PDT 2019
+#1 SMP PREEMPT Mon Dec 11 15:19:50 CST 2017
+#1 SMP PREEMPT Fri Nov 23 10:53:51 CST 2018
+#1 SMP PREEMPT Wed Dec 29 10:43:50 CST 2021
+#1 SMP PREEMPT Wed Nov 14 11:57:26 CST 2018
+#1 SMP PREEMPT Thu Jul 30 11:16:07 CST 2020
+#1 SMP PREEMPT Fri May 4 15:09:53 KST 2018
+#1 SMP PREEMPT Mon Aug 16 14:52:11 CST 2021
+#1 SMP PREEMPT Thu Mar 3 07:12:30 UTC 2022
+#1 SMP PREEMPT Wed Mar 27 20:24:06 CST 2019
+#1 SMP PREEMPT Fri Dec 25 01:51:21 JST 2020
+#1 SMP PREEMPT Wed Apr 7 09:40:55 +07 2021
+#1 SMP PREEMPT Fri Jan 26 18:09:44 KST 2018
+#2 SMP PREEMPT Sun Sep 8 21:52:38 JST 2019
+#1 SMP PREEMPT Thu Oct 17 11:39:19 KST 2019
+#1 SMP PREEMPT Wed Dec 1 21:27:17 CST 2021
+#1 SMP PREEMPT Mon May 10 23:53:48 CST 2021
+#1 SMP PREEMPT Tue Mar 17 00:05:50 PDT 2020
+#2 SMP PREEMPT Fri Dec 15 19:03:26 CST 2017
+#1 SMP PREEMPT Fri Mar 20 16:07:45 CST 2020
+#1 SMP PREEMPT Fri Apr 2 18:46:47 CST 2021
+#2 SMP PREEMPT Mon Oct 14 20:35:23 CST 2019
+#1 SMP PREEMPT Tue Nov 26 16:29:47 CST 2019
+#1 SMP PREEMPT Fri May 3 17:03:25 IST 2019
+#2 SMP PREEMPT Mon Sep 9 14:59:39 KST 2019
+#1 SMP PREEMPT Mon Apr 25 03:23:58 CST 2022
+#1 SMP PREEMPT Wed May 12 00:02:30 CST 2021
+#1 SMP PREEMPT Fri Oct 25 01:17:05 CST 2019
+#1 SMP PREEMPT Wed Oct 9 03:58:52 CST 2019
+#1 SMP PREEMPT Tue Apr 10 15:50:52 KST 2018
+#1 SMP PREEMPT Fri Dec 27 10:44:07 JST 2019
+#1 SMP PREEMPT Fri Aug 2 18:08:45 BRT 2019
+#1 SMP PREEMPT Tue May 3 11:01:14 CDT 2022
+#1 SMP PREEMPT Thu May 7 22:12:28 CST 2020
+#1 SMP PREEMPT Fri Aug 3 12:35:26 CST 2018
+#1 SMP PREEMPT Mon Oct 29 15:20:39 CST 2018
+#1 SMP PREEMPT Mon Nov 1 04:56:45 PDT 2021
+#1 SMP PREEMPT Fri Apr 30 09:54:03 CST 2021
+#1 SMP PREEMPT Fri Aug 28 15:36:35 KST 2020
+#1 SMP PREEMPT Wed Jul 4 22:47:10 CST 2018
+#1 SMP PREEMPT Mon Aug 3 14:22:01 CST 2020
+#3 SMP PREEMPT Thu Sep 17 12:33:10 CST 2020
+#1 SMP PREEMPT Tue Jan 11 19:28:01 UTC 2022
+#1 SMP PREEMPT Thu Jun 10 14:43:04 CST 2021
+#1 SMP PREEMPT Mon Aug 31 11:35:28 PDT 2020
+#1 SMP PREEMPT Sat Dec 22 09:12:55 CST 2018
+#1 SMP PREEMPT Thu Mar 17 16:33:23 CST 2022
+#1 SMP PREEMPT Mon Dec 10 15:03:20 CST 2018
+#1 SMP PREEMPT Fri Aug 2 05:34:27 CDT 2019
+#1 SMP PREEMPT Thu Apr 18 14:24:20 2019
+#1 SMP PREEMPT Mon Sep 28 17:51:21 CST 2020
+#32 SMP PREEMPT Sat Apr 2 15:48:12 HKT 2022
+#1 SMP PREEMPT Wed Aug 5 13:34:07 CST 2020
+#1 SMP PREEMPT Fri Nov 19 19:24:05 CST 2021
+#1 SMP PREEMPT Sun Sep 8 14:11:35 PDT 2019
+#1 SMP PREEMPT Fri Apr 20 12:08:08 IST 2018
+#1 SMP PREEMPT Wed Aug 15 14:56:40 CST 2018
+#1 SMP PREEMPT Wed Feb 13 13:12:16 CST 2019
+#1 SMP PREEMPT Wed Apr 15 02:49:31 CST 2020
+#1 SMP PREEMPT Sat Sep 7 13:07:06 CST 2019
+#2 SMP Sat Aug 15 04:31:15 UTC 2020
+#2 SMP PREEMPT Mon Oct 12 11:20:35 CST 2020
+#2 SMP PREEMPT Fri Dec 6 11:44:48 CST 2019
+#2 SMP PREEMPT Mon Feb 5 18:50:49 CST 2018
+#2 SMP PREEMPT Thu Mar 18 15:05:08 CST 2021
+#1 SMP PREEMPT Tue May 5 00:53:16 PDT 2020
+#2 SMP PREEMPT Thu Jul 30 20:15:31 KST 2020
+#1 SMP PREEMPT Thu Oct 15 14:26:31 CST 2020
+#1 SMP PREEMPT Fri Oct 30 04:09:45 CST 2020
+#2 SMP PREEMPT Fri Aug 24 20:23:00 CST 2018
+#1 SMP PREEMPT Fri Jun 28 23:47:47 CST 2019
+#1 SMP PREEMPT Thu Mar 21 21:25:29 CST 2019
+#1 SMP PREEMPT Thu Apr 21 10:59:35 KST 2022
+#1 SMP PREEMPT Tue Nov 13 23:58:31 CST 2018
+#1 SMP PREEMPT Fri Jan 10 18:16:47 CST 2020
+#1 SMP PREEMPT Thu Apr 4 11:39:08 CST 2019
+#1 SMP PREEMPT Mon Feb 10 21:49:08 CST 2020
+#2 SMP PREEMPT Fri Oct 25 01:44:29 CST 2019
+#1 SMP PREEMPT Tue Jul 28 14:08:07 KST 2020
+#1 SMP PREEMPT Mon May 11 02:35:55 PDT 2020
+#2 SMP PREEMPT Tue Jun 22 16:50:25 KST 2021
+#1 SMP PREEMPT Tue Apr 16 22:52:33 CST 2019
+#1 SMP PREEMPT Sun Feb 13 18:08:59 CST 2022
+#1 SMP PREEMPT Tue Aug 18 00:09:12 CST 2020
+#1 SMP PREEMPT Tue Jan 23 11:37:28 CST 2018
+#1 SMP PREEMPT Mon Apr 27 18:28:54 CST 2020
+#1 SMP PREEMPT Sat Jun 6 22:16:01 CST 2020
+#1 SMP PREEMPT Wed Apr 10 15:08:15 KST 2019
+#1 SMP PREEMPT Sat Oct 9 12:28:27 CST 2021
+#1 SMP PREEMPT Wed Mar 2 02:22:28 CST 2022
+#1 SMP PREEMPT Wed May 19 09:02:06 IST 2021
+#1 SMP PREEMPT Fri Jul 10 15:42:39 CST 2020
+#3 SMP PREEMPT Mon Dec 7 14:39:09 CST 2020
+#1 SMP PREEMPT Tue Jul 14 17:06:44 KST 2020
+#1 SMP PREEMPT Thu Oct 3 04:28:27 KST 2019
+#1 SMP PREEMPT Thu Jan 20 22:50:19 CST 2022
+#1 SMP PREEMPT Mon Apr 16 16:14:05 CST 2018
+#1 SMP PREEMPT Fri Jan 5 23:41:53 JST 2018
+#1 SMP PREEMPT Fri Nov 22 12:46:04 KST 2019
+#2 SMP PREEMPT Tue Nov 30 17:18:09 CST 2021
+#2 SMP PREEMPT Fri Mar 20 20:27:53 KST 2020
+#1 SMP PREEMPT Sun Jul 18 20:34:46 PDT 2021
+#1 SMP PREEMPT Wed Dec 19 11:16:47 CST 2018
+#1 SMP PREEMPT Tue Jan 5 21:38:22 CST 2021
+#1 SMP PREEMPT Fri May 28 16:13:09 CST 2021
+#1 SMP PREEMPT Wed May 26 14:54:55 CST 2021
+#1 SMP PREEMPT Wed Mar 27 18:17:12 CDT 2019
+#1 SMP PREEMPT Fri Dec 31 12:41:31 CST 2021
+#1 SMP PREEMPT Sun Mar 7 16:56:33 CST 2021
+#1 SMP PREEMPT Thu Jan 2 15:17:12 CST 2020
+#1 SMP PREEMPT Thu Jan 10 21:59:56 KST 2019
+#1 SMP PREEMPT Tue Apr 3 12:49:24 CST 2018
+#1 SMP PREEMPT Thu Mar 25 13:38:26 KST 2021
+#1 SMP PREEMPT Thu Mar 7 15:34:15 UTC 2019
+#2 SMP PREEMPT Fri Sep 28 22:02:55 CST 2018
+#1 SMP PREEMPT Sun Sep 26 14:22:59 CST 2021
+#1 SMP PREEMPT Thu Mar 8 20:14:28 CST 2018
+#1 SMP PREEMPT Mon Sep 2 12:55:28 CST 2019
+#6 SMP PREEMPT Thu Dec 2 11:18:33 UTC 2021
+#1 SMP PREEMPT Wed Jul 11 17:01:58 IST 2018
+#1 SMP PREEMPT Fri Nov 20 00:35:34 CST 2020
+#1 SMP PREEMPT Wed Jun 24 10:17:14 UTC 2020
+#1 SMP PREEMPT Thu Jun 13 10:55:52 2019
+#1 SMP PREEMPT Thu Nov 2 10:09:20 CDT 2017
+#1 SMP PREEMPT Thu May 30 11:28:29 CST 2019
+#1 SMP PREEMPT Wed Dec 26 13:30:13 KST 2018
+#1 SMP PREEMPT Fri May 14 13:34:18 CST 2021
+#2 SMP PREEMPT Wed Nov 27 13:37:39 CST 2019
+#1 SMP PREEMPT Thu Dec 13 17:10:02 KST 2018
+#2 SMP PREEMPT Tue Dec 8 19:49:06 KST 2020
+#1 SMP PREEMPT Tue Oct 16 23:34:04 PDT 2018
+#1 SMP PREEMPT Wed Apr 6 08:46:39 UTC 2022
+#1 SMP PREEMPT Thu Feb 22 20:31:32 CET 2018
+#1 SMP PREEMPT Tue Apr 7 17:17:53 CST 2020
+#1 SMP PREEMPT Tue Jan 15 18:55:03 WIB 2019
+#2 SMP PREEMPT Tue Dec 11 18:04:34 CST 2018
+#1 SMP PREEMPT Wed Apr 27 04:36:39 KST 2022
+#1 SMP PREEMPT Tue Oct 23 03:59:14 CST 2018
+#1 SMP PREEMPT Thu Jul 9 01:09:21 CST 2020
+#1 SMP PREEMPT Wed Oct 2 17:00:15 2019
+#1 SMP PREEMPT Fri Sep 18 23:15:37 HKT 2020
+#1 SMP PREEMPT Fri Oct 8 19:21:12 UTC 2021
+#1 SMP PREEMPT Wed Mar 2 10:32:41 CST 2022
+#1 SMP PREEMPT Wed Oct 28 22:38:39 CST 2020
+#1 SMP PREEMPT Tue Mar 26 04:16:57 PDT 2019
+#1 SMP PREEMPT Fri Feb 7 10:38:03 KST 2020
+#2 SMP PREEMPT Sun Oct 13 16:51:23 CST 2019
+#2 SMP PREEMPT Wed Apr 28 16:55:47 KST 2021
+#1 SMP PREEMPT Thu Feb 14 12:09:43 CST 2019
+#1 SMP PREEMPT Thu Mar 28 21:42:13 CST 2019
+#1 SMP PREEMPT Wed Feb 7 01:19:50 WIB 2018
+#1 SMP PREEMPT Wed Jan 27 06:52:28 KST 2021
+#1 SMP PREEMPT Thu Aug 12 17:52:53 KST 2021
+#1 SMP PREEMPT Fri Mar 11 03:11:16 UTC 2022
+#1 SMP PREEMPT Wed Dec 11 16:39:35 CST 2019
+#1 SMP PREEMPT Mon Aug 12 13:12:54 CST 2019
+#272 SMP PREEMPT Wed Dec 12 15:36:04 CST 2018
+#1 SMP PREEMPT Wed Dec 9 21:48:33 CST 2020
+#1 SMP PREEMPT Wed Oct 17 04:06:11 CST 2018
+#2 SMP PREEMPT Wed Mar 14 20:32:18 CST 2018
+#1 SMP PREEMPT Thu Jun 20 23:17:41 KST 2019
+#1 SMP PREEMPT Tue Jun 12 14:36:43 CST 2018
+#1 SMP PREEMPT Tue Jul 28 00:28:21 CST 2020
+#1 SMP PREEMPT Fri Apr 3 09:26:39 CDT 2020
+#1 SMP PREEMPT Thu Nov 28 20:03:18 IST 2019
+#1 SMP PREEMPT Tue Aug 31 16:40:28 CST 2021
+#1 SMP PREEMPT Sat Jun 9 10:10:17 KST 2018
+#2 SMP PREEMPT Sat Jul 31 03:18:48 JST 2021
+#1 SMP PREEMPT Thu Jul 18 21:02:43 CST 2019
+#1 SMP PREEMPT Wed Jun 27 21:48:17 CST 2018
+#1 SMP PREEMPT Tue Dec 19 12:42:48 CST 2017
+#1 SMP PREEMPT Wed Sep 8 02:07:28 CST 2021
+#1 SMP PREEMPT Fri Oct 23 11:04:47 KST 2020
+#1 SMP PREEMPT Wed Aug 23 11:41:44 CST 2017
+#1 SMP PREEMPT Sat Mar 13 09:23:15 CST 2021
+#1 SMP PREEMPT Thu Dec 2 14:05:22 CST 2021
+#2 SMP PREEMPT Tue Oct 1 11:53:34 KST 2019
+#1 SMP PREEMPT Thu Apr 16 08:58:56 CST 2020
+#1 SMP PREEMPT Tue Sep 14 18:45:42 CST 2021
+#1 SMP PREEMPT Thu Mar 3 02:02:11 UTC 2022
+#1 SMP PREEMPT Wed Apr 13 00:00:23 CST 2022
+#1 SMP PREEMPT Thu Dec 16 01:54:29 CST 2021
+#1 SMP PREEMPT Sun Apr 12 15:43:21 CST 2020
+#1 SMP PREEMPT Tue Jun 30 02:29:37 CST 2020
+#2 SMP PREEMPT Tue Nov 19 15:27:16 CST 2019
+#1 SMP PREEMPT Wed Mar 9 21:02:02 CST 2022
+#2 SMP PREEMPT Mon Mar 30 19:01:07 CST 2020
+#2 SMP PREEMPT Thu Sep 12 11:51:52 CST 2019
+#1 SMP PREEMPT Wed Jun 3 09:00:15 KST 2020
+#1 SMP PREEMPT Fri Dec 18 16:57:53 CST 2020
+#2 SMP PREEMPT Wed Apr 3 17:20:05 KST 2019
+#1 SMP PREEMPT Fri Jul 13 16:39:12 CST 2018
+#1 SMP PREEMPT Wed Feb 3 19:05:35 CST 2021
+#1 SMP PREEMPT Mon Mar 21 13:47:42 KST 2022
+#2 SMP PREEMPT Thu Jan 2 12:10:28 CST 2020
+#1 SMP PREEMPT Fri Aug 27 11:42:45 CST 2021
+#1 SMP PREEMPT Tue Sep 17 12:51:21 CST 2019
+#2 SMP PREEMPT Sat Dec 29 02:46:22 CST 2018
+#2 SMP PREEMPT Tue Oct 22 14:52:04 KST 2019
+#1 SMP PREEMPT Fri Mar 27 00:43:45 KST 2020
+#1 SMP PREEMPT Sat Dec 7 13:59:31 UTC 2019
+#1 SMP PREEMPT Fri May 11 14:08:06 KST 2018
+#1 SMP PREEMPT Wed May 1 14:39:38 CST 2019
+#1 SMP PREEMPT Thu Dec 12 12:01:44 CST 2019
+#4 SMP PREEMPT Fri Nov 22 02:49:48 CST 2019
+#6 SMP PREEMPT Mon Jan 28 18:44:39 CST 2019
+#1 SMP PREEMPT Tue Dec 11 14:36:51 CST 2018
+#2 SMP PREEMPT Thu Dec 5 16:25:09 CST 2019
+#1 SMP PREEMPT Fri Jul 26 04:03:32 CST 2019
+#1 SMP PREEMPT Fri Jul 10 15:21:20 KST 2020
+#1 SMP PREEMPT Fri Feb 25 14:08:58 CST 2022
+#1 SMP PREEMPT Tue Dec 11 15:36:02 CST 2018
+#3 SMP PREEMPT Wed Aug 12 20:45:43 KST 2020
+#1 SMP PREEMPT Mon Mar 28 13:34:18 CST 2022
+#1 SMP PREEMPT Tue Sep 10 10:31:11 2019
+#1 SMP PREEMPT Mon Jul 5 10:58:00 KST 2021
+#1 SMP PREEMPT Thu Aug 2 06:15:45 CST 2018
+#1 SMP PREEMPT Sat Feb 19 00:27:51 CST 2022
+#1 SMP PREEMPT Wed Mar 20 23:41:08 PDT 2019
+#1 SMP PREEMPT Fri Mar 19 11:45:47 CST 2021
+#1 SMP PREEMPT Thu Feb 14 17:47:20 CST 2019
+#1 SMP PREEMPT Wed May 4 14:27:40 CDT 2022
+#2 SMP PREEMPT Tue Mar 23 17:53:16 CST 2021
+#1 SMP PREEMPT Thu May 31 19:18:31 CST 2018
+#1 SMP PREEMPT Mon Mar 30 23:08:26 PDT 2020
+#1 SMP PREEMPT Tue Apr 13 13:13:56 PDT 2021
+#6 SMP PREEMPT Tue Jul 30 15:42:01 CST 2019
+#1 SMP PREEMPT Wed Jun 30 09:40:41 CST 2021
+#1 SMP PREEMPT Tue Apr 26 21:21:52 CST 2022
+#1 SMP PREEMPT Mon Jan 28 14:49:18 EST 2019
+#1 SMP PREEMPT Fri Mar 11 14:50:38 CST 2022
+#1 SMP PREEMPT Tue Jul 20 17:11:28 CST 2021
+#1 SMP PREEMPT Sat Aug 25 06:04:34 CST 2018
+#1 SMP PREEMPT Wed Apr 17 06:26:13 CDT 2019
+#1 SMP PREEMPT Sat Jul 4 05:19:36 CST 2020
+#1 SMP PREEMPT Mon May 28 20:37:40 KST 2018
+#1 SMP PREEMPT Thu Dec 5 14:48:14 KST 2019
+#1 SMP PREEMPT Sun Oct 11 01:39:01 CST 2020
+#1 SMP PREEMPT Mon Nov 16 19:50:18 CST 2020
+#4 SMP PREEMPT Mon Jul 23 21:49:30 CST 2018
+#1 SMP PREEMPT Thu Nov 12 10:32:39 CST 2020
+#1 SMP PREEMPT Tue May 26 11:23:57 EDT 2020
+#6 SMP PREEMPT Thu Dec 2 11:18:33 UTC 2021
+#1 SMP PREEMPT Thu May 23 04:33:06 KST 2019
+#1 SMP PREEMPT Wed Oct 9 09:19:10 CST 2019
+#1 SMP PREEMPT Wed May 26 20:53:59 CST 2021
+#1 SMP PREEMPT Wed Jan 12 02:12:28 UTC 2022
+#1 SMP PREEMPT Mon Nov 23 23:21:06 CST 2020
+#1 SMP PREEMPT Wed Jun 24 18:54:02 CST 2020
+#1 SMP PREEMPT Sat Mar 2 16:17:35 CST 2019
+#1 SMP PREEMPT Fri Jul 31 19:05:53 CST 2020
+#1 SMP PREEMPT Tue Apr 19 10:16:26 CST 2022
+#1 SMP PREEMPT Thu Jul 23 17:12:15 KST 2020
+#1 SMP PREEMPT Tue Apr 13 22:08:20 JST 2021
+#1 SMP PREEMPT Tue Feb 15 20:47:04 CST 2022
+#1 SMP PREEMPT Mon Sep 23 12:34:08 CST 2019
+#2 SMP PREEMPT Mon Aug 27 19:11:06 CST 2018
+#2 SMP PREEMPT Tue Dec 31 14:49:59 CST 2019
+#1 SMP PREEMPT Mon Apr 25 01:02:45 CST 2022
+#2 SMP PREEMPT Thu May 30 12:18:01 CST 2019
+#1 SMP PREEMPT Thu May 16 13:23:04 BRT 2019
+#1 SMP PREEMPT Tue Apr 9 13:01:15 EDT 2019
+#1 SMP PREEMPT Tue Jul 9 23:20:49 CST 2019
+#1 SMP PREEMPT Wed Jan 19 19:31:18 UTC 2022
+#1 SMP PREEMPT Fri Dec 3 05:23:54 CST 2021
+#1 SMP PREEMPT Thu Feb 15 02:17:12 JST 2018
+#1 SMP PREEMPT Wed Apr 29 10:11:21 CST 2020
+#2 SMP PREEMPT Tue May 14 07:15:00 CST 2019
+#3 SMP PREEMPT Thu Oct 31 16:37:30 CST 2019
+#1 SMP PREEMPT Wed Sep 26 00:41:36 PDT 2018
+#1 SMP PREEMPT Fri Jun 5 06:32:03 KST 2020
+#2 SMP PREEMPT Thu Dec 6 09:05:19 CST 2018
+#1 SMP PREEMPT Mon Jan 13 20:06:46 KST 2020
+#1 SMP PREEMPT Fri Jul 10 18:35:50 CST 2020
+#1 SMP PREEMPT Sun Apr 24 01:10:11 CST 2022
+#1 SMP PREEMPT Mon Dec 10 20:44:56 EST 2018
+#1 SMP PREEMPT Thu Apr 1 16:16:15 CST 2021
+#1 SMP PREEMPT Tue Mar 5 18:16:00 CST 2019
+#1 SMP PREEMPT Wed Nov 4 02:09:33 JST 2020
+#2 SMP PREEMPT Mon Mar 19 19:03:46 CST 2018
+#2 SMP PREEMPT Wed Oct 28 13:15:51 CST 2020
+#1 SMP PREEMPT Thu Apr 23 09:05:57 CST 2020
+#1 SMP PREEMPT Fri Jan 1 00:46:26 CST 2021
+#1 SMP PREEMPT Mon Mar 1 23:36:08 CST 2021
+#1 SMP PREEMPT Sat Sep 1 05:29:16 CST 2018
+#1 SMP PREEMPT Fri Jan 17 01:51:06 2020
+#1 SMP PREEMPT Thu May 21 18:24:59 CST 2020
+#1 SMP PREEMPT Fri Feb 8 16:40:59 KST 2019
+#2 SMP PREEMPT Thu May 13 11:34:14 CST 2021
+#1 SMP PREEMPT Wed Jul 29 17:00:56 CST 2020
+#1 SMP PREEMPT Wed Oct 21 11:32:40 KST 2020
+#2 SMP PREEMPT Tue Apr 9 18:22:26 CST 2019
+#1 SMP PREEMPT Tue Mar 26 01:24:24 CST 2019
+#1 SMP PREEMPT Wed Apr 20 21:38:06 CST 2022
+#1 SMP PREEMPT Thu Feb 20 03:55:56 CST 2020
+#1 SMP PREEMPT Fri Dec 21 12:26:45 CST 2018
+#1 SMP PREEMPT Tue Jun 4 00:37:39 EDT 2019
+#2 SMP PREEMPT Sun Feb 18 22:15:47 CST 2018
+#1 SMP PREEMPT Thu Feb 11 22:49:22 PST 2021
+#1 SMP PREEMPT Wed Feb 7 23:04:19 KST 2018
+#1 SMP PREEMPT Wed Jan 3 23:40:36 CST 2018
+#1 SMP PREEMPT Tue Apr 17 19:34:29 CST 2018
+#1 SMP PREEMPT Thu Jul 2 01:59:05 JST 2020
+#1 SMP PREEMPT Thu Apr 5 20:12:29 KST 2018
+#1 SMP PREEMPT Tue Feb 15 17:26:20 CST 2022
+#1 SMP PREEMPT Wed May 16 19:21:34 CST 2018
+#1 SMP PREEMPT Wed Sep 23 23:29:43 CST 2020
+#1 SMP PREEMPT Wed Mar 27 01:56:41 CST 2019
+#2 SMP PREEMPT Tue Jun 4 07:15:14 CST 2019
+#1 SMP PREEMPT Wed May 6 21:08:00 CST 2020
+#2 SMP PREEMPT Sat May 21 11:25:07 CST 2022
+#1 SMP PREEMPT Thu Dec 19 14:23:08 UTC 2019
+#1 SMP PREEMPT Thu Oct 21 22:02:30 CST 2021
+#1 SMP PREEMPT Wed Dec 22 13:53:32 UTC 2021
+#1 SMP PREEMPT Tue Oct 1 00:37:25 PDT 2019
+#1 SMP PREEMPT Mon Sep 27 16:07:35 CST 2021
+#1 SMP PREEMPT Fri Mar 6 11:22:22 CST 2020
+#1 SMP PREEMPT Wed Jun 3 19:58:06 CST 2020
+#2 SMP PREEMPT Wed Mar 31 18:59:57 KST 2021
+#1 SMP PREEMPT Thu Nov 18 10:58:16 CST 2021
+#1 SMP PREEMPT Fri Oct 25 15:20:19 KST 2019
+#1 SMP PREEMPT Wed May 15 19:59:43 IST 2019
+#1 SMP PREEMPT Thu Feb 3 23:07:28 UTC 2022
+#1 SMP PREEMPT Sat Jan 29 03:53:04 CST 2022
+#1 SMP PREEMPT Thu Apr 30 14:16:59 CDT 2020
+#1 SMP PREEMPT Fri Jul 30 05:43:43 CST 2021
+#1 SMP PREEMPT Fri Jan 17 03:25:13 CST 2020
+#1 SMP PREEMPT Mon May 11 02:25:23 PDT 2020
+#1 SMP PREEMPT Thu Mar 17 16:28:22 CST 2022
+#1 SMP PREEMPT Mon Jan 7 11:43:36 CST 2019
+#1 SMP PREEMPT Tue Dec 17 16:06:58 CST 2019
+#1 SMP PREEMPT Sat Oct 17 04:04:57 CST 2020
+#1 SMP PREEMPT Thu Apr 30 15:18:13 CST 2020
+#1 SMP PREEMPT Thu Aug 8 20:43:04 CST 2019
+#1 SMP PREEMPT Fri Jul 31 13:13:29 CST 2020
+#1 SMP PREEMPT Thu May 9 18:23:11 CST 2019
+#1 SMP PREEMPT Wed Jun 23 02:37:41 CST 2021
+#6 SMP PREEMPT Fri Aug 23 12:16:39 CST 2019
+#1 SMP PREEMPT Thu Nov 12 14:59:54 CST 2020
+#1 SMP PREEMPT Wed Feb 19 18:00:37 CST 2020
+#1 SMP PREEMPT Fri Jul 10 15:45:09 CST 2020
+#1 SMP PREEMPT Fri Dec 15 11:50:37 CST 2017
+#2 SMP PREEMPT Fri Sep 27 18:49:54 KST 2019
+#1 SMP PREEMPT Mon Nov 11 17:57:00 CST 2019
+#1 SMP PREEMPT Thu Jul 19 17:41:16 CST 2018
+#1 SMP PREEMPT Mon Mar 11 13:30:24 BRT 2019
+#1 SMP PREEMPT Thu Sep 26 10:18:10 CST 2019
+#1 SMP PREEMPT Fri Mar 29 21:21:33 CST 2019
+#2 SMP PREEMPT Sat Nov 10 01:35:23 CST 2018
+#1 SMP PREEMPT Sun May 1 14:16:47 CST 2022
+#1 SMP PREEMPT Thu Dec 17 16:26:37 CST 2020
+#1 SMP PREEMPT Tue Jun 16 19:11:21 CST 2020
+#1 SMP PREEMPT Thu May 28 13:51:27 CST 2020
+#1 SMP PREEMPT Sun Nov 24 01:29:25 CST 2019
+#1 SMP PREEMPT Thu Jan 25 17:31:26 KST 2018
+#1 SMP PREEMPT Mon Apr 26 13:52:06 CST 2021
+#1 SMP PREEMPT Mon Jan 8 15:51:03 KST 2018
+#1 SMP PREEMPT Sun Apr 10 05:13:07 PDT 2022
+#1 SMP PREEMPT Thu Apr 7 00:27:25 CST 2022
+#1 SMP PREEMPT Tue Nov 2 13:49:26 JST 2021
+#1 SMP PREEMPT Mon Jul 30 23:37:18 PDT 2018
+#1 SMP PREEMPT Sun May 8 18:26:51 CST 2022
+#1 SMP PREEMPT Tue Jan 7 22:57:13 CST 2020
+#1 SMP PREEMPT Thu Sep 24 11:21:03 CST 2020
+#1 SMP PREEMPT Mon Dec 27 13:51:22 KST 2021
+#1 SMP PREEMPT Mon Feb 28 09:41:50 HKT 2022
+#2 SMP PREEMPT Tue Apr 28 19:38:27 CST 2020
+#1 SMP PREEMPT Thu Mar 19 23:13:49 IST 2020
+#2 SMP PREEMPT Thu Apr 22 11:55:29 CST 2021
+#1 SMP PREEMPT Wed Dec 19 11:16:47 CST 2018
+#1 SMP PREEMPT Wed Nov 7 12:15:49 CST 2018
+#1 SMP PREEMPT Fri Apr 13 13:10:12 CST 2018
+#1 SMP PREEMPT Sun Apr 25 13:24:34 CST 2021
+#1 SMP PREEMPT Tue Mar 26 09:53:52 CST 2019
+#2 SMP PREEMPT Mon Apr 6 05:31:46 CST 2020
+#2 SMP PREEMPT Fri Jun 22 16:22:13 CST 2018
+#1 SMP PREEMPT Tue Sep 22 20:11:51 PDT 2020
+#1 SMP PREEMPT Wed Feb 19 04:32:26 CST 2020
+#8 SMP PREEMPT Sat Mar 5 12:37:48 CST 2022
+#1 SMP PREEMPT Thu Jan 11 03:51:44 CST 2018
+#1 SMP PREEMPT Tue Jun 23 05:07:54 KST 2020
+#1 SMP PREEMPT Wed May 16 16:05:00 CST 2018
+#1 SMP PREEMPT Fri Nov 20 10:53:16 PST 2020
+#1 SMP PREEMPT Thu Dec 23 21:48:32 CST 2021
+#1 SMP PREEMPT Mon Aug 20 10:47:54 CST 2018
+#1 SMP PREEMPT Fri Sep 27 01:23:46 CST 2019
+#1 SMP PREEMPT Wed Mar 10 00:56:16 IST 2021
+#1 SMP PREEMPT Mon Oct 7 12:32:26 CST 2019
+#2 SMP PREEMPT Tue Sep 25 18:58:45 CST 2018
+#3 SMP PREEMPT Thu May 12 11:56:07 UTC 2022
+#1 SMP PREEMPT Tue Dec 12 22:19:08 CST 2017
+#1 SMP PREEMPT Fri May 18 09:38:40 2018
+#1 SMP PREEMPT Fri Jan 15 00:18:43 KST 2021
+#1 SMP PREEMPT Wed Jul 18 18:59:24 CST 2018
+#1 SMP PREEMPT Fri May 13 00:39:54 CST 2022
+#1 SMP PREEMPT Mon Nov 9 11:52:25 CST 2020
+#1 SMP PREEMPT Wed Nov 13 17:14:32 CST 2019
+#109 SMP PREEMPT Thu Apr 22 15:27:58 CST 2021
+#1 SMP PREEMPT Thu May 17 13:30:43 CDT 2018
+#1 SMP PREEMPT Sat Nov 2 00:22:03 CST 2019
+#1 SMP PREEMPT Sun Apr 15 14:58:59 CST 2018
+#1 SMP PREEMPT Wed Sep 16 05:20:36 EDT 2020
+#1 SMP PREEMPT Thu Jun 13 13:00:45 EDT 2019
+#1 SMP PREEMPT Fri Apr 23 21:17:50 CDT 2021
+#1 SMP PREEMPT Thu Oct 28 20:31:33 UTC 2021
+#1 SMP PREEMPT Wed Feb 27 00:59:16 CST 2019
+#1 SMP PREEMPT Thu Jul 9 08:59:13 CST 2020
+#1 SMP PREEMPT Mon Jun 8 03:28:07 PDT 2020
+#1 SMP PREEMPT Tue Jun 1 19:59:47 KST 2021
+#3 SMP PREEMPT Mon Jul 30 06:10:21 CST 2018
+#1 SMP PREEMPT Wed Jul 3 16:38:26 2019
+#1 SMP PREEMPT Fri Apr 1 22:02:09 CST 2022
+#1 SMP PREEMPT Wed Mar 14 11:21:59 CST 2018
+#1 SMP PREEMPT Thu Jul 25 02:27:23 KST 2019
+#1 SMP PREEMPT Mon Mar 18 20:49:34 CST 2019
+#2 SMP PREEMPT Fri Mar 29 19:00:24 KST 2019
+#2 SMP PREEMPT Fri Aug 6 21:07:55 CST 2021
+#32 SMP PREEMPT Fri Nov 23 19:53:14 CST 2018
+#2 SMP PREEMPT Fri May 31 11:24:08 CST 2019
+#2 SMP PREEMPT Mon Jan 15 16:26:27 CST 2018
+#1 SMP PREEMPT Mon May 18 17:58:47 WIB 2020
+#1 SMP PREEMPT Mon Mar 7 17:00:39 KST 2022
+#1 SMP PREEMPT Tue Jul 10 10:11:25 CST 2018
+#1 SMP PREEMPT Fri Oct 30 16:10:22 KST 2020
+#1 SMP PREEMPT Mon Mar 14 19:23:26 CST 2022
+#1 SMP PREEMPT Wed Jul 31 19:49:59 CST 2019
+#1 SMP PREEMPT Fri Sep 11 17:36:00 CST 2020
+#245 SMP PREEMPT Fri Mar 11 11:09:55 CST 2022
+#1 SMP PREEMPT Mon May 3 20:00:23 KST 2021
+#1 SMP PREEMPT Fri Sep 27 12:25:44 CST 2019
+#1 SMP PREEMPT Thu Mar 25 10:25:58 CST 2021
+#1 SMP PREEMPT Wed Apr 20 11:26:16 CST 2022
+#1 SMP PREEMPT Tue Apr 26 02:16:02 CST 2022
+#1 SMP PREEMPT Sun Nov 21 17:44:08 CST 2021
+#1 SMP PREEMPT Fri Mar 12 11:00:43 KST 2021
+#1 SMP PREEMPT Sat Oct 19 17:14:42 CST 2019
+#1 SMP PREEMPT Mon Oct 25 20:29:15 CST 2021
+#6 SMP PREEMPT Fri Oct 22 14:02:46 UTC 2021
+#1 SMP PREEMPT Wed Feb 28 03:20:07 CST 2018
+#1 SMP PREEMPT Mon Jan 14 17:40:06 CST 2019
+#1 SMP PREEMPT Thu Dec 3 11:56:20 KST 2020
+#1 SMP PREEMPT Wed Dec 13 13:06:50 CST 2017
+#1 SMP PREEMPT Wed Jan 12 01:33:22 UTC 2022
+#1 SMP PREEMPT Wed Nov 10 21:23:13 CST 2021
+#1 SMP PREEMPT Mon Nov 2 10:32:10 KST 2020
+#6 SMP PREEMPT Mon Dec 30 11:48:30 CST 2019
+#1 SMP PREEMPT Tue Jan 18 21:49:30 JST 2022
+#1 SMP PREEMPT Tue Aug 13 20:47:44 CST 2019
+#1 SMP PREEMPT Fri Oct 9 16:46:29 CST 2020
+#2 SMP PREEMPT Mon Dec 20 20:04:26 CST 2021
+#1 SMP PREEMPT Sat May 21 12:54:41 CST 2022
+#1 SMP PREEMPT Mon Apr 11 22:21:16 CST 2022
+#1 SMP PREEMPT Wed Jan 15 06:35:37 CST 2020
+#1 SMP PREEMPT Fri Jun 11 13:18:39 CST 2021
+#1 SMP PREEMPT Fri Jun 8 22:05:29 WIB 2018
+#1 SMP PREEMPT Fri Apr 22 18:54:32 CST 2022
+#1 SMP PREEMPT Wed Sep 4 18:34:55 CST 2019
+#4 SMP PREEMPT Mon Oct 21 21:16:45 CST 2019
+#1 SMP PREEMPT Thu Sep 13 00:37:14 CST 2018
+#1 SMP PREEMPT Wed Sep 1 10:17:21 UTC 2021
+#32 SMP PREEMPT Fri Oct 8 11:26:11 CST 2021
+#1 SMP PREEMPT Fri Apr 6 02:43:32 CST 2018
+#1 SMP PREEMPT Fri Jun 26 16:08:30 KST 2020
+#6 SMP PREEMPT Fri Apr 19 16:01:30 CST 2019
+#1 SMP PREEMPT Wed Jul 10 21:53:42 CST 2019
+#1 SMP PREEMPT Sat Apr 27 10:34:42 KST 2019
+#1 SMP PREEMPT Wed Apr 20 18:48:26 KST 2022
+#1 SMP PREEMPT Wed Oct 17 16:01:10 CST 2018
+#1 SMP PREEMPT Thu Oct 14 10:13:36 CST 2021
+#2 SMP PREEMPT Tue Aug 13 02:05:55 CST 2019
+#2 SMP PREEMPT Wed Jan 12 11:09:11 CST 2022
+#1 SMP PREEMPT Sat Jan 9 01:43:19 IST 2021
+#1 SMP PREEMPT Thu Dec 30 19:31:56 CST 2021
+#1 SMP PREEMPT Thu Oct 11 20:11:40 KST 2018
+#1 SMP PREEMPT Mon Jun 3 13:32:11 UTC 2019
+#2 SMP PREEMPT Wed Dec 30 19:34:30 CST 2020
+#1 SMP PREEMPT Thu Mar 3 18:21:52 CST 2022
+#1 SMP PREEMPT Wed Mar 27 17:06:47 CST 2019
+#1 SMP PREEMPT Thu Feb 10 21:31:27 CST 2022
+#1 SMP PREEMPT Tue Jan 7 16:40:41 CST 2020
+#1 SMP PREEMPT Sat Apr 4 23:10:04 CST 2020
+#37 SMP PREEMPT Fri Aug 30 10:22:55 CST 2019
+#1 SMP PREEMPT Thu Sep 13 22:04:30 CST 2018
+#1 SMP PREEMPT Fri Oct 16 16:11:09 KST 2020
+#1 SMP PREEMPT Wed May 18 21:46:03 CST 2022
+#1 SMP PREEMPT Sun Sep 19 00:32:13 CST 2021
+#1 SMP PREEMPT Mon Nov 26 15:12:32 IST 2018
+#197 SMP PREEMPT Wed Feb 16 20:53:45 CST 2022
+#1 SMP PREEMPT Tue Oct 8 20:54:33 +07 2019
+#1 SMP PREEMPT Fri Dec 18 02:23:48 CST 2020
+#1 SMP PREEMPT Thu Apr 7 23:52:31 CST 2022
+#1 SMP PREEMPT Fri Nov 5 04:05:18 CST 2021
+#2 SMP PREEMPT Tue Mar 2 11:32:39 CST 2021
+#1 SMP PREEMPT Wed Oct 27 15:43:04 CST 2021
+#1 SMP PREEMPT Tue Mar 2 21:31:53 CST 2021
+#1 SMP PREEMPT Thu Jan 27 18:39:18 CST 2022
+#1 SMP PREEMPT Mon Jan 3 23:09:22 KST 2022
+#1 SMP PREEMPT Wed Jan 12 15:36:39 CST 2022
+#2 SMP PREEMPT Thu Jan 9 11:32:12 HKT 2020
+#2 SMP PREEMPT Mon Dec 21 13:56:03 CST 2020
+#1 SMP PREEMPT Thu Jan 28 03:14:30 JST 2021
+#1 SMP PREEMPT Thu Jan 20 21:44:23 CST 2022
+#1 SMP PREEMPT Wed Jan 23 00:57:18 CST 2019
+#1 SMP PREEMPT Thu Nov 5 22:43:44 IST 2020
+#1 SMP PREEMPT Wed Sep 26 01:13:49 PDT 2018
+#1 SMP PREEMPT Thu Jan 9 00:12:30 CST 2020
+#1 SMP PREEMPT Thu Sep 24 04:55:42 CST 2020
+#2 SMP PREEMPT Wed Apr 17 19:32:59 CST 2019
+#1 SMP PREEMPT Sat Feb 6 19:14:36 IST 2021
+#1 SMP PREEMPT Thu Apr 7 18:02:38 CST 2022
+#1 SMP PREEMPT Fri Sep 7 18:35:24 CST 2018
+#1 SMP PREEMPT Tue Nov 3 02:12:22 JST 2020
+#1 SMP PREEMPT Fri Apr 15 18:59:09 CST 2022
+#2 SMP PREEMPT Tue Jul 13 19:58:54 KST 2021
+#1 SMP PREEMPT Thu Oct 31 14:49:50 2019
+#1 SMP PREEMPT Mon Nov 18 17:16:33 CST 2019
+#2 SMP PREEMPT Tue Aug 11 14:57:13 CST 2020
+#1 SMP PREEMPT Thu Sep 26 00:08:03 PDT 2019
+#1 SMP PREEMPT Thu Jan 20 02:48:23 CST 2022
+#1 SMP PREEMPT Thu Nov 8 15:33:19 KST 2018
+#1 SMP PREEMPT Thu Jan 14 20:14:30 PST 2021
+#1 SMP PREEMPT Mon Nov 30 21:50:53 KST 2020
+#1 SMP PREEMPT Fri Jul 27 03:37:44 CST 2018
+#1 SMP PREEMPT Fri Sep 7 05:56:29 CST 2018
+#1 SMP PREEMPT Thu Apr 4 19:50:25 CST 2019
+#1 SMP PREEMPT Tue Aug 6 05:56:21 PDT 2019
+#1 SMP PREEMPT Fri Aug 28 00:56:13 CST 2020
+#2 SMP PREEMPT Fri Mar 27 18:30:40 KST 2020
+#2 SMP PREEMPT Thu Jun 13 06:27:50 CST 2019
+#2 SMP PREEMPT Thu Apr 25 06:09:21 CST 2019
+#53 SMP PREEMPT Tue Jun 12 19:58:09 CST 2018
+#1 SMP PREEMPT Fri Dec 31 09:59:57 IST 2021
+#1 SMP PREEMPT Sat Jun 27 17:21:58 IST 2020
+#1 SMP PREEMPT Wed Oct 28 13:42:57 CST 2020
+#1 SMP PREEMPT Fri Dec 24 11:35:27 CST 2021
+#1 SMP PREEMPT Mon Nov 12 16:00:34 CET 2018
+#1 SMP PREEMPT Tue May 10 02:05:11 CST 2022
+#1 SMP PREEMPT Tue Feb 19 20:46:43 -03 2019
+#1 SMP PREEMPT Fri Sep 11 01:36:45 IST 2020
+#2 SMP PREEMPT Thu Nov 12 09:49:32 CST 2020
+#1 SMP PREEMPT Thu Feb 15 13:30:59 JST 2018
+#1 SMP PREEMPT Mon Mar 7 11:50:07 CST 2022
+#2 SMP PREEMPT Fri Oct 12 15:47:04 CST 2018
+#1 SMP PREEMPT Tue May 10 14:47:17 CST 2022
+#1 SMP PREEMPT Fri Dec 11 11:22:28 CST 2020
+#1 SMP PREEMPT Mon Dec 28 10:19:16 CST 2020
+#1 SMP PREEMPT Mon May 16 20:07:26 CST 2022
+#1 SMP PREEMPT Tue Aug 21 02:07:24 CST 2018
+#2 SMP PREEMPT Wed Mar 4 01:39:13 CST 2020
+#1 SMP PREEMPT Fri Aug 13 18:36:02 CST 2021
+#1 SMP PREEMPT Wed Aug 19 21:36:05 CST 2020
+#1 SMP PREEMPT Tue Jul 31 01:27:07 CST 2018
+#1 SMP PREEMPT Fri Jul 17 08:09:23 CST 2020
+#1 SMP PREEMPT Sat Mar 20 01:22:15 CST 2021
+#1 SMP PREEMPT Wed Dec 30 03:41:37 CST 2020
+#1 SMP PREEMPT Mon Jul 5 11:28:12 CDT 2021
+#1 SMP PREEMPT Mon Dec 28 21:11:39 CST 2020
+#1 SMP PREEMPT Fri Jan 7 13:01:32 CST 2022
+#1 SMP PREEMPT Wed Jul 17 10:17:29 CST 2019
+#1 SMP PREEMPT Mon May 6 22:18:01 CST 2019
+#5 SMP PREEMPT Thu Oct 15 20:35:35 CST 2020
+#1 SMP PREEMPT Fri Aug 6 14:30:20 CST 2021
+#1 SMP PREEMPT Mon Jun 21 22:21:08 CST 2021
+#1 SMP PREEMPT Wed Jun 13 10:59:55 HKT 2018
+#1 SMP PREEMPT Mon Oct 21 17:11:55 CST 2019
+#1 SMP PREEMPT Tue Apr 13 23:07:44 CST 2021
+#1 SMP PREEMPT Thu Nov 28 20:52:01 CST 2019
+#1 SMP PREEMPT Wed Aug 12 12:58:11 KST 2020
+#1 SMP PREEMPT Wed Apr 29 18:26:07 CST 2020
+#1 SMP PREEMPT Fri Feb 21 12:27:07 CST 2020
+#2 SMP PREEMPT Mon Sep 7 22:36:27 CST 2020
+#1 SMP PREEMPT Thu Jul 22 23:23:12 CST 2021
+#2 SMP PREEMPT Mon Oct 15 19:00:47 HKT 2018
+#1 SMP PREEMPT Tue Feb 22 23:28:22 CST 2022
+#2 SMP PREEMPT Thu Nov 15 23:02:30 CST 2018
+#1 SMP PREEMPT Thu Nov 15 18:40:11 KST 2018
+#1 SMP PREEMPT Tue May 17 03:01:54 CST 2022
+#1 SMP PREEMPT Tue Jul 30 08:31:10 CDT 2019
+#1 SMP PREEMPT Mon May 13 12:20:33 CST 2019
+#1 SMP PREEMPT Wed Dec 22 11:54:15 KST 2021
+#1 SMP PREEMPT Sat Nov 7 05:28:47 KST 2020
+#2 SMP PREEMPT Wed Sep 25 21:04:36 CST 2019
+#1 SMP PREEMPT Fri Apr 16 14:11:03 PDT 2021
+#1 SMP PREEMPT Mon Sep 28 20:54:41 CST 2020
+#1 SMP PREEMPT Tue Feb 19 19:47:49 CST 2019
+#1 SMP PREEMPT Fri Jul 3 11:37:54 CST 2020
+#1 SMP PREEMPT Wed Sep 5 05:56:01 CST 2018
+#1 SMP PREEMPT Thu Jul 1 22:27:41 CST 2021
+#2 SMP PREEMPT Thu Feb 17 23:19:19 CST 2022
+#1 SMP PREEMPT Sun Jun 28 17:50:13 CST 2020
+#1 SMP PREEMPT Mon Nov 5 13:06:42 CST 2018
+#1 SMP PREEMPT Fri Dec 21 01:12:42 KST 2018
+#1 SMP PREEMPT Wed Jun 13 04:00:06 CST 2018
+#2 SMP PREEMPT Tue Feb 2 03:54:52 CST 2021
+#1 SMP PREEMPT Mon Jan 13 11:24:11 CST 2020
+#1 SMP PREEMPT Mon May 6 00:42:30 CST 2019
+#1 SMP PREEMPT Wed Jul 4 12:47:15 CST 2018
+#2 SMP PREEMPT Wed Nov 20 21:38:19 CST 2019
+#1 SMP PREEMPT Wed Mar 4 17:30:17 CST 2020
+#1 SMP PREEMPT Sat Mar 27 03:40:36 CST 2021
+#1 SMP PREEMPT Thu Sep 26 18:52:57 CST 2019
+#1 SMP PREEMPT Mon Jul 19 18:33:45 CST 2021
+#2 SMP PREEMPT Wed Mar 7 16:57:32 CST 2018
+#2 SMP PREEMPT Wed Jun 26 04:08:41 KST 2019
+#1 SMP PREEMPT Fri Oct 11 16:05:57 KST 2019
+#1 SMP PREEMPT Fri Mar 8 10:19:51 KST 2019
+#1 SMP PREEMPT Tue May 17 08:38:16 JST 2022
+#2 SMP PREEMPT Sat Nov 23 14:08:21 CST 2019
+#1 SMP PREEMPT Sat May 30 04:11:26 CST 2020
+#2 SMP PREEMPT Fri Jul 23 11:19:54 CST 2021
+#2 SMP PREEMPT Wed Apr 22 16:30:52 CST 2020
+#1 SMP PREEMPT Mon Aug 24 14:29:30 CST 2020
+#1 SMP PREEMPT Fri Apr 29 20:12:43 CST 2022
+#1 SMP PREEMPT Fri Dec 4 01:57:45 JST 2020
+#1 SMP PREEMPT Thu Jan 6 10:12:37 UTC 2022
+#1 SMP PREEMPT Wed Nov 20 20:14:30 CST 2019
+#1 SMP PREEMPT Wed Jan 26 21:14:07 CST 2022
+#1 SMP PREEMPT Sat Feb 3 14:23:45 CST 2018
+#2 SMP PREEMPT Mon Jan 10 15:19:47 CST 2022
+#1 SMP PREEMPT Tue Feb 4 12:35:17 KST 2020
+#1 SMP PREEMPT Mon Nov 30 19:10:31 WIB 2020
+#1 SMP PREEMPT Thu Sep 12 10:31:30 CST 2019
+#2 SMP PREEMPT Tue Dec 3 05:13:45 KST 2019
+#1 SMP PREEMPT Wed Nov 18 06:26:17 IST 2020
+#1 SMP PREEMPT Tue Jul 7 23:29:53 CST 2020
+#1 SMP PREEMPT Mon Feb 21 18:08:36 CST 2022
+#1 SMP PREEMPT Mon May 3 16:34:48 KST 2021
+#1 SMP PREEMPT Wed Oct 14 08:56:10 KST 2020
+#1 SMP PREEMPT Tue Jan 22 20:22:19 PST 2019
+#1 SMP PREEMPT Mon Apr 25 13:56:28 CST 2022
+#1 SMP PREEMPT Fri Dec 18 16:45:50 CST 2020
+#1 SMP PREEMPT Fri Sep 11 15:09:04 JST 2020
+#1 SMP PREEMPT Wed Apr 14 17:13:05 IST 2021
+#1 SMP PREEMPT Mon Dec 13 11:00:11 CST 2021
+#1 SMP PREEMPT Fri Jan 4 18:27:27 IST 2019
+#1 SMP PREEMPT Tue Oct 13 14:15:35 IST 2020
+#2 SMP PREEMPT Wed Mar 4 16:42:42 CST 2020
+#2 SMP PREEMPT Wed May 29 18:30:13 CST 2019
+#1 SMP PREEMPT Mon May 9 03:45:11 UTC 2022
+#2 SMP PREEMPT Sat Mar 2 10:27:25 CST 2019
+#2 SMP PREEMPT Thu Apr 16 14:46:31 CST 2020
+#1 SMP PREEMPT Wed Dec 19 08:27:16 CST 2018
+#1 SMP PREEMPT Wed Aug 18 14:31:58 KST 2021
+#1 SMP PREEMPT Wed Nov 24 23:15:39 CST 2021
+#1 SMP PREEMPT Wed Dec 22 16:09:28 CST 2021
+#2 SMP PREEMPT Mon Nov 15 20:29:38 HKT 2021
+#1 SMP PREEMPT Wed May 13 05:57:02 CST 2020
+#1 SMP PREEMPT Fri May 14 12:10:42 +07 2021
+#1 SMP PREEMPT Wed Oct 23 23:05:01 CST 2019
+#2 SMP PREEMPT Thu Sep 5 10:55:21 CST 2019
+#2 SMP PREEMPT Thu Aug 8 20:45:09 KST 2019
+#1 SMP PREEMPT Tue Apr 17 10:07:12 CST 2018
+#36 SMP PREEMPT Sat Aug 29 00:55:39 EDT 2020
+#2 SMP PREEMPT Wed Sep 25 23:07:01 CST 2019
+#3 SMP PREEMPT Fri Mar 6 22:25:29 KST 2020
+#1 SMP PREEMPT Tue Jul 16 15:07:54 KST 2019
+#1 SMP PREEMPT Tue Apr 3 22:07:45 CST 2018
+#1 SMP PREEMPT Tue Sep 14 21:44:54 CST 2021
+#104 SMP PREEMPT Tue Jan 5 17:49:12 CST 2021
+#1 SMP PREEMPT Thu Nov 11 09:53:32 CST 2021
+#1 SMP PREEMPT Sat Dec 4 23:32:49 CST 2021
+#1 SMP PREEMPT Mon Aug 12 20:32:01 CST 2019
+#1 SMP PREEMPT Sun Oct 20 11:57:44 CST 2019
+#1 SMP PREEMPT Fri Jun 1 16:53:38 CST 2018
+#2 SMP PREEMPT Mon Feb 4 20:14:10 -02 2019
+#1 SMP PREEMPT Tue Oct 13 17:11:01 JST 2020
+#1 SMP PREEMPT Mon Oct 19 15:11:49 KST 2020
+#1 SMP PREEMPT Wed Nov 4 12:26:44 CST 2020
+#1 SMP PREEMPT Fri Sep 24 03:08:03 CST 2021 f2fs-hash:3571f543c4
+#1 SMP PREEMPT Fri May 21 12:10:59 CST 2021
+#1 SMP PREEMPT Sat Jul 25 12:06:45 CST 2020
+#1 SMP PREEMPT Mon May 11 02:06:41 PDT 2020
+#1 SMP PREEMPT Fri Jan 7 16:24:13 CST 2022
+#2 SMP PREEMPT Wed Jun 17 23:44:09 CST 2020
+#2 SMP PREEMPT Wed Nov 6 11:19:33 CST 2019
+#1 SMP PREEMPT Sun Apr 28 16:28:01 CST 2019
+#1 SMP PREEMPT Tue Aug 18 21:26:13 CST 2020
+#1 SMP PREEMPT Fri May 17 23:30:10 KST 2019
+#1 SMP PREEMPT Tue Nov 27 00:05:13 CST 2018
+#1 SMP PREEMPT Thu Mar 8 20:14:28 CST 2018
+#1 SMP PREEMPT Wed Sep 30 05:43:31 CST 2020
+#1 SMP PREEMPT Wed Aug 29 00:17:12 CDT 2018
+#2 SMP PREEMPT Thu Jul 2 21:56:30 CST 2020
+#1 SMP PREEMPT Wed Jul 29 00:23:58 CST 2020
+#1 SMP PREEMPT Tue Oct 9 08:27:06 KST 2018
+#1 SMP PREEMPT Thu Aug 1 14:47:06 CST 2019
+#1 SMP PREEMPT Mon Mar 7 16:19:20 CST 2022
+#1 SMP PREEMPT Mon May 16 13:36:34 KST 2022
+#1 SMP PREEMPT Sat Dec 23 20:31:04 CST 2017
+#2 SMP PREEMPT Tue Mar 13 18:41:27 KST 2018
+#1 SMP PREEMPT Wed Jan 8 14:44:43 CST 2020
+#1 SMP PREEMPT Mon May 20 19:46:16 KST 2019
+#1 SMP PREEMPT Thu Sep 17 02:52:34 CST 2020
+#1 SMP PREEMPT Sat Apr 24 04:06:33 KST 2021
+#1 SMP PREEMPT Sun Aug 9 18:22:21 CST 2020
+#2 SMP PREEMPT Tue Feb 23 22:51:14 CST 2021
+#2 SMP PREEMPT Wed Dec 19 18:25:16 UTC 2018
+#1 SMP PREEMPT Thu Jan 16 18:33:41 CST 2020
+#1 SMP PREEMPT Tue Jul 30 08:15:29 PDT 2019
+#0 SMP PREEMPT Mon Jan 18 18:11:28 UTC 2021
+#1 SMP PREEMPT Fri May 17 11:27:01 CST 2019
+#1 SMP PREEMPT Mon Feb 21 14:22:07 CST 2022
+#1 SMP PREEMPT Tue Jan 4 23:02:06 CST 2022
+#1 SMP PREEMPT Tue Jan 16 14:57:04 CST 2018
+#1 SMP PREEMPT Sun Feb 14 20:38:21 PST 2021
+#1 SMP PREEMPT Wed Jul 15 21:34:43 KST 2020
+#1 SMP PREEMPT Fri Feb 28 17:54:15 CST 2020
+#1 SMP PREEMPT Fri May 7 18:34:35 KST 2021
+#1 SMP PREEMPT Wed Dec 22 15:40:25 CST 2021
+#1 SMP PREEMPT Wed Mar 23 17:27:36 CST 2022
+#2 SMP PREEMPT Fri Nov 29 19:18:25 KST 2019
+#1 SMP PREEMPT Mon Sep 2 19:16:14 KST 2019
+#1 SMP PREEMPT Tue Nov 16 16:18:52 PST 2021
+#1 SMP PREEMPT Fri Mar 9 14:42:17 CST 2018
+#1 SMP PREEMPT Wed Oct 28 23:58:57 IST 2020
+#10 SMP PREEMPT Tue Jan 15 20:17:37 CST 2019
+#1 SMP PREEMPT Mon May 6 17:02:52 CST 2019
+#39 SMP PREEMPT Thu Dec 3 10:16:12 CST 2020
+#1 SMP PREEMPT Sat Apr 25 12:50:31 CST 2020
+#6 SMP PREEMPT Thu Dec 2 11:18:33 UTC 2021
+#2 SMP PREEMPT Thu Feb 13 17:32:51 CST 2020
+#1 SMP PREEMPT Tue Sep 29 11:58:55 CST 2020
+#1 SMP PREEMPT Wed Sep 19 21:51:58 IST 2018
+#1 SMP PREEMPT Fri Mar 22 02:36:24 CST 2019
+#1 SMP PREEMPT Mon Jan 14 00:30:32 CST 2019
+#1 SMP PREEMPT Mon Mar 25 01:47:39 CST 2019
+#1 SMP PREEMPT Fri Jun 21 16:43:23 KST 2019
+#1 SMP PREEMPT Sat Aug 3 00:08:41 CST 2019
+#1 SMP PREEMPT Mon May 6 12:32:35 CST 2019
+#1 SMP PREEMPT Tue Mar 22 12:45:38 CST 2022
+#1 SMP PREEMPT Tue Oct 22 23:35:08 EDT 2019
+#5 SMP PREEMPT Sat Dec 11 12:27:32 CST 2021
+#1 SMP PREEMPT Wed Nov 4 16:19:40 CST 2020
+#1 SMP PREEMPT Mon Sep 6 16:14:45 CST 2021
+#1 SMP PREEMPT Mon Jan 6 18:34:25 CST 2020
+#1 SMP PREEMPT Thu Mar 31 22:36:44 CST 2022
+#1 SMP PREEMPT Fri Dec 10 00:35:21 CST 2021
+#1 SMP PREEMPT Wed Dec 2 02:16:14 KST 2020
+#1 SMP PREEMPT Fri Nov 5 18:05:17 CST 2021
+#1 SMP PREEMPT Fri Aug 27 00:53:33 IST 2021
+#2 SMP PREEMPT Sat May 9 23:07:54 CST 2020
+#1 SMP PREEMPT Thu Dec 12 15:48:15 KST 2019
+#1 SMP PREEMPT Sat Mar 16 14:31:19 CST 2019
+#1 SMP PREEMPT Tue May 4 13:49:03 CST 2021
+#1 SMP PREEMPT Tue Apr 21 08:14:11 CDT 2020
+#1 SMP PREEMPT Fri Mar 25 21:15:07 CST 2022
+#1 SMP PREEMPT Fri May 3 06:53:34 CST 2019
+#1 SMP PREEMPT Wed Mar 27 14:14:46 CST 2019
+#1 SMP PREEMPT Tue Jul 3 22:35:38 CST 2018
+#2 SMP PREEMPT Mon Aug 31 18:05:32 KST 2020
+#1 SMP PREEMPT Sat Jan 5 12:40:52 CST 2019
+#1 SMP PREEMPT Wed Nov 28 00:44:03 CST 2018
+#1 SMP PREEMPT Tue Jul 13 20:26:34 KST 2021
+#1 SMP PREEMPT Thu Sep 9 02:51:31 CDT 2021
+#1 SMP PREEMPT Thu Jul 23 19:04:26 CST 2020
+#1 SMP PREEMPT Fri Mar 1 20:18:44 CST 2019
+#1 SMP PREEMPT Tue Oct 27 17:43:52 HKT 2020
+#2 SMP PREEMPT Tue Sep 29 16:28:12 KST 2020
+#1 SMP PREEMPT Wed Apr 18 13:19:41 KST 2018
+#1 SMP PREEMPT Wed Mar 14 18:47:17 JST 2018
+#2 SMP PREEMPT Tue Oct 6 11:50:25 KST 2020
+#1 SMP PREEMPT Fri Mar 25 13:15:19 UTC 2022
+#1 SMP PREEMPT Sat Nov 21 13:33:51 CST 2020
+#1 SMP PREEMPT Tue Apr 2 22:18:29 CST 2019
+#1 SMP PREEMPT Thu Feb 27 00:29:07 CST 2020
+#211 SMP PREEMPT Tue Mar 1 10:37:32 CST 2022
+#1 SMP PREEMPT Wed May 26 19:16:57 CST 2021
+#2 SMP PREEMPT Tue Oct 23 15:05:02 CST 2018
+#1 SMP PREEMPT Wed Jul 17 12:34:44 CST 2019
+#1 SMP PREEMPT Thu Apr 1 13:31:18 CST 2021
+#1 SMP PREEMPT Tue May 21 07:03:30 CST 2019
+#1 SMP PREEMPT Tue Aug 20 18:49:25 KST 2019
+#1 SMP PREEMPT Mon Dec 13 14:44:53 CST 2021
+#1 SMP PREEMPT Sat Jul 13 11:19:58 CST 2019
+#1 SMP PREEMPT Tue Jun 12 12:06:07 CST 2018
+#1 SMP PREEMPT Mon Dec 24 14:38:16 CST 2018
+#1 SMP PREEMPT Tue Jan 11 16:38:35 UTC 2022
+#1 SMP PREEMPT Fri Dec 27 19:22:17 CST 2019
+#1 SMP PREEMPT Wed Jun 26 21:34:21 CST 2019
+#1 SMP PREEMPT Mon Sep 13 12:48:19 CST 2021
+#1 SMP PREEMPT Fri Nov 26 23:31:28 CST 2021
+#1 SMP PREEMPT Fri Dec 6 06:21:08 CST 2019
+#1 SMP PREEMPT Thu Nov 15 22:32:23 WIB 2018
+#1 SMP PREEMPT Tue May 5 23:30:09 KST 2020
+#1 SMP PREEMPT Fri Feb 25 20:17:01 CST 2022
+#2 SMP PREEMPT Mon Dec 17 23:40:27 CST 2018
+#1 SMP PREEMPT Wed Jan 10 11:14:04 KST 2018
+#52 SMP PREEMPT Tue Sep 22 14:39:53 CST 2020
+#1 SMP PREEMPT Fri Dec 28 10:17:18 CST 2018
+#1 SMP PREEMPT Thu Mar 18 01:04:54 CST 2021
+#1 SMP PREEMPT Thu Feb 25 01:57:57 JST 2021
+#1 SMP PREEMPT Thu Sep 19 19:35:41 KST 2019
+#1 SMP PREEMPT Wed Aug 5 19:53:19 CST 2020
+#1 SMP PREEMPT Thu Jun 24 23:08:25 CST 2021
+#1 SMP PREEMPT Fri Jul 6 16:01:51 KST 2018
+#2 SMP PREEMPT Wed Dec 23 20:55:23 CST 2020
+#1 SMP PREEMPT Tue Nov 27 22:58:39 CST 2018
+#1 SMP PREEMPT Wed Sep 25 23:41:02 PDT 2019
+#1 SMP PREEMPT Wed Dec 30 21:48:40 CST 2020
+#1 SMP PREEMPT Wed Jan 15 11:31:15 UTC 2020
+#2 SMP PREEMPT Thu Oct 17 00:52:43 CST 2019
+#1 SMP PREEMPT Thu Feb 21 05:46:00 PST 2019
+#1 SMP PREEMPT Sat Oct 26 23:52:56 PDT 2019
+#1 SMP PREEMPT Mon Apr 26 08:52:36 CST 2021
+#1 SMP PREEMPT Wed Dec 23 20:10:53 KST 2020
+#1 SMP PREEMPT Tue Sep 29 15:43:23 CST 2020
+#1 SMP PREEMPT Wed Feb 5 19:12:34 CST 2020
+#2 SMP PREEMPT Wed Dec 19 15:29:22 CST 2018
+#1 SMP PREEMPT Thu Mar 25 20:49:12 PDT 2021
+#1 SMP PREEMPT Thu Nov 8 14:14:36 CST 2018
+#1 SMP PREEMPT Wed Jul 24 18:15:30 CST 2019
+#1 SMP PREEMPT Fri May 31 21:58:32 CST 2019
+#1 SMP PREEMPT Wed Dec 13 15:49:03 KST 2017
+#1 SMP PREEMPT Wed Jan 24 02:55:46 KST 2018
+#1 SMP PREEMPT Wed Apr 8 19:17:27 CST 2020
+#2 SMP PREEMPT Fri Dec 31 15:11:48 CST 2021
+#1 SMP PREEMPT Mon Aug 17 11:51:04 CST 2020
+#1 SMP PREEMPT Wed Feb 16 21:34:52 CST 2022
+#1 SMP PREEMPT Thu Oct 24 17:10:00 CST 2019
+#1 SMP PREEMPT Mon May 11 02:08:21 PDT 2020
+#1 SMP PREEMPT Tue Nov 26 02:38:35 KST 2019
+#1 SMP PREEMPT Tue Jan 30 03:47:18 CST 2018
+#1 SMP PREEMPT Thu Jul 2 15:05:18 CST 2020
+#1 SMP PREEMPT Wed Aug 22 15:34:23 CST 2018
+#1 SMP PREEMPT Sat Mar 13 22:11:22 CST 2021
+#2 SMP PREEMPT Thu Jan 16 11:46:50 CST 2020
+#1 SMP PREEMPT Wed Dec 9 17:37:58 CST 2020
+#1 SMP PREEMPT Mon Nov 19 16:13:50 CST 2018
+#1 SMP PREEMPT Sun Jan 6 23:57:25 PST 2019
+#17 SMP PREEMPT Thu Aug 13 15:43:10 CST 2020
+#104 SMP PREEMPT Tue Mar 8 15:01:49 CST 2022
+#1 SMP PREEMPT Mon Nov 12 14:01:27 CST 2018
+#1 SMP PREEMPT Wed May 16 18:11:52 2018
+#1 SMP PREEMPT Thu Aug 12 11:59:58 CST 2021 f2fs-hash:cf69bad37b
+#1 SMP PREEMPT Mon Dec 10 23:45:34 PST 2018
+#1 SMP PREEMPT Tue Sep 10 18:01:37 KST 2019
+#1 SMP PREEMPT Wed May 4 01:17:26 IST 2022
+#1 SMP PREEMPT Wed Mar 18 19:45:27 JST 2020
+#1 SMP PREEMPT Tue Jun 22 16:25:51 KST 2021
+#1 SMP PREEMPT Mon Aug 3 22:38:03 CDT 2020
+#2 SMP PREEMPT Tue Oct 30 12:04:52 CST 2018
+#1 SMP PREEMPT Sun Jul 14 21:38:01 PDT 2019
+#1 SMP PREEMPT Wed Dec 1 22:19:56 CST 2021
+#1 SMP PREEMPT Tue May 7 19:11:21 IST 2019
+#2 SMP PREEMPT Mon Aug 27 17:51:45 CST 2018
+#1 SMP PREEMPT Tue Oct 30 02:04:48 PDT 2018
+#1 SMP PREEMPT Fri Mar 30 21:31:45 EDT 2018
+#6 SMP Sun Dec 2 18:19:50 CET 2018
+#1 SMP PREEMPT Thu Oct 18 17:40:56 CST 2018
+#1 SMP PREEMPT Mon May 6 14:40:37 CST 2019
+#1 SMP PREEMPT Thu Sep 10 19:58:25 KST 2020
+#2 SMP Tue Mar 3 21:27:43 UTC 2020
+#1 SMP PREEMPT Fri Apr 30 09:59:43 IST 2021
+#1 SMP PREEMPT Mon Aug 5 12:26:07 CST 2019
+#2 SMP PREEMPT Wed Dec 6 16:57:49 HST 2017
+#1 SMP PREEMPT Tue Oct 23 15:47:22 KST 2018
+#1 SMP PREEMPT Fri Dec 8 13:14:09 CST 2017
+#1 SMP PREEMPT Mon Apr 29 03:34:54 CST 2019
+#1 SMP PREEMPT Mon Jan 8 10:53:18 KST 2018
+#1 SMP PREEMPT Wed Jul 18 04:23:09 PDT 2018
+#0 SMP PREEMPT Mon Feb 1 23:21:01 UTC 2021
+#2 SMP PREEMPT Thu Mar 21 09:16:04 KST 2019
+#1 SMP PREEMPT Thu Mar 5 15:02:45 CST 2020
+#1 SMP PREEMPT Wed Mar 25 20:48:51 KST 2020
+#1 SMP PREEMPT Wed Mar 20 09:15:53 UTC 2019
+#1 SMP PREEMPT Sat Jan 29 02:31:34 CST 2022
+#2 SMP PREEMPT Thu Oct 10 03:22:02 CST 2019
+#1 SMP PREEMPT Wed Aug 26 06:28:35 KST 2020
+#1 SMP PREEMPT Sun Mar 22 01:10:31 CST 2020
+#1 SMP PREEMPT Thu Nov 30 03:21:54 CST 2017
+#1 SMP PREEMPT Thu Feb 27 16:38:39 CST 2020
+#2 SMP PREEMPT Fri Oct 23 12:26:59 CST 2020
+#2 SMP PREEMPT Mon Jan 3 17:03:33 CST 2022
+#1 SMP PREEMPT Fri May 11 00:35:53 KST 2018
+#1 SMP PREEMPT Tue May 22 20:03:39 CST 2018
+#1 SMP PREEMPT Tue Apr 3 16:30:15 HKT 2018
+#1 SMP PREEMPT Fri Sep 28 13:17:21 CST 2018
+#1 SMP PREEMPT Sat Sep 25 12:31:50 PDT 2021
+#1 SMP PREEMPT Fri Feb 15 21:19:27 CST 2019
+#2 SMP PREEMPT Fri Sep 14 19:24:30 CST 2018
+#1 SMP PREEMPT Fri Oct 22 19:56:57 CST 2021
+#1 SMP PREEMPT Thu Jul 29 22:41:38 KST 2021
+#1 SMP PREEMPT Thu Dec 17 20:54:51 CST 2020
+#1 SMP PREEMPT Thu Aug 8 05:16:58 CST 2019
+#1 SMP PREEMPT Sat Mar 2 14:23:53 CST 2019
+#1 SMP PREEMPT Fri Sep 14 12:59:17 KST 2018
+#1 SMP PREEMPT Thu Feb 17 08:18:31 CST 2022
+#1 SMP PREEMPT Fri Aug 31 02:51:24 CST 2018
+#1 SMP PREEMPT Mon Nov 2 12:37:54 KST 2020
+#2 SMP PREEMPT Thu Mar 11 21:40:16 CST 2021
+#2 SMP PREEMPT Fri Sep 13 16:00:56 CST 2019
+#1 SMP PREEMPT Thu Jan 24 00:35:27 PST 2019
+#1 SMP PREEMPT Wed Oct 13 00:57:04 CST 2021
+#1 SMP PREEMPT Sun Apr 29 00:45:10 CST 2018
+#1 SMP PREEMPT Thu Aug 23 12:23:34 -03 2018
+#2 SMP PREEMPT Thu Oct 10 21:32:17 KST 2019
+#1 SMP PREEMPT Mon Apr 11 21:22:22 CST 2022
+#1 SMP PREEMPT Fri May 15 05:43:21 CDT 2020
+#1 SMP PREEMPT Mon Nov 15 19:57:52 IST 2021
+#1 SMP PREEMPT Tue Jan 22 19:35:19 PST 2019
+#1 SMP PREEMPT Mon Sep 10 19:54:57 CST 2018
+#1 SMP PREEMPT Fri Mar 19 23:57:47 CST 2021
+#1 SMP PREEMPT Fri Mar 16 17:09:37 2018
+#1 SMP PREEMPT Tue Aug 21 19:43:37 KST 2018
+#1 SMP PREEMPT Tue Sep 4 23:06:29 IST 2018
+#2 SMP PREEMPT Tue Sep 29 14:31:43 KST 2020
+#1 SMP PREEMPT Fri Dec 31 12:24:22 CST 2021
+#1 SMP PREEMPT Sat Jan 13 17:34:50 CST 2018
+#1 SMP PREEMPT Wed Apr 8 14:35:05 CST 2020
+#1 SMP PREEMPT Thu Sep 24 13:06:42 KST 2020
+#1 SMP PREEMPT Thu Jul 26 15:38:27 EDT 2018
+#1 SMP PREEMPT Mon Jul 19 15:36:29 CST 2021
+#23 SMP PREEMPT Fri Oct 15 12:43:01 PDT 2021
+#1 SMP PREEMPT Tue Sep 22 06:12:52 JST 2020
+#1 SMP PREEMPT Fri Apr 29 15:35:24 CST 2022
+#1 SMP PREEMPT Mon Mar 25 19:31:05 CST 2019
+#1 SMP PREEMPT Thu Jun 28 18:40:47 CST 2018
+#1 SMP PREEMPT Mon Sep 9 15:02:15 CST 2019
+#1 SMP PREEMPT Mon Dec 6 09:17:27 JST 2021
+#2 SMP Tue Jun 2 01:11:20 UTC 2020
+#2 SMP PREEMPT Mon Feb 15 03:53:10 JST 2021
+#1 SMP PREEMPT Fri Jan 25 04:45:44 CST 2019
+#1 SMP PREEMPT Mon May 13 14:14:39 CST 2019
+#2 SMP PREEMPT Tue Aug 24 12:19:33 CST 2021
+#1 SMP PREEMPT Mon Feb 7 11:04:41 CST 2022
+#1 SMP PREEMPT Wed Jul 21 15:20:47 KST 2021
+#1 SMP PREEMPT Fri Nov 13 15:17:08 KST 2020
+#1 SMP PREEMPT Fri May 27 04:52:54 UTC 2022
+#1 SMP PREEMPT Tue Jun 5 23:09:54 UTC 2018
+#2 SMP PREEMPT Wed Feb 20 06:53:37 CST 2019
+#1 SMP PREEMPT Thu Dec 13 13:30:02 CST 2018
+#2 SMP PREEMPT Mon Dec 16 17:17:53 CST 2019
+#1 SMP PREEMPT Tue Mar 10 15:33:10 IST 2020
+#1 SMP PREEMPT Thu May 17 11:11:53 CST 2018
+#2 SMP PREEMPT Mon Jun 17 19:50:42 CST 2019
+#1 SMP PREEMPT Tue May 29 12:59:30 CST 2018
+#1 SMP PREEMPT Fri Mar 20 11:05:52 CST 2020
+#1 SMP PREEMPT Fri Nov 12 17:08:17 CST 2021
+#1 SMP PREEMPT Mon Dec 25 18:29:30 CST 2017
+#1 SMP PREEMPT Sun Feb 28 20:49:00 PST 2021
+#1 SMP PREEMPT Thu Jun 27 11:33:03 HKT 2019
+#1 SMP PREEMPT Tue Jul 2 11:21:02 JST 2019
+#1 SMP PREEMPT Mon May 9 19:41:47 KST 2022
+#1 SMP PREEMPT Tue Apr 27 18:55:50 KST 2021
+#2 SMP PREEMPT Thu Dec 26 23:55:30 CST 2019
+#1 SMP PREEMPT Wed Jan 20 03:08:44 CST 2021
+#1 SMP PREEMPT Wed Mar 25 13:45:41 KST 2020
+#2 SMP PREEMPT Tue Oct 22 21:21:20 CST 2019
+#1 SMP PREEMPT Wed May 29 16:23:49 CST 2019
+#1 SMP PREEMPT Fri Jul 10 04:16:49 CDT 2020
+#2 SMP PREEMPT Thu Jan 16 08:02:23 CST 2020
+#1 SMP PREEMPT Thu Mar 17 19:38:12 KST 2022
+#1 SMP PREEMPT Mon Mar 30 23:27:35 CST 2020
+#1 SMP PREEMPT Thu Apr 18 13:44:22 CST 2019
+#1 SMP PREEMPT Mon Mar 9 19:04:19 CST 2020
+#2 SMP PREEMPT Tue Feb 23 15:12:55 CET 2021
+#2 SMP PREEMPT Fri Jul 23 16:37:21 KST 2021
+#1 SMP PREEMPT Sat Nov 24 12:42:24 CST 2018
+#1 SMP PREEMPT Fri Apr 16 20:05:48 CST 2021
+#1 SMP PREEMPT Thu Apr 30 11:57:51 CST 2020
+#1 SMP PREEMPT Mon Jun 25 10:35:11 KST 2018
+#1 SMP PREEMPT Thu Oct 22 17:35:11 KST 2020
+#1 SMP PREEMPT Mon Oct 25 00:28:34 CDT 2021
+#1 SMP PREEMPT Thu May 5 19:21:12 CST 2022
+#1 SMP PREEMPT Tue Jun 22 09:00:40 +07 2021
+#1 SMP PREEMPT Mon Dec 18 16:10:20 2017
+#1 SMP PREEMPT Fri Oct 22 13:30:56 CST 2021
+#1 SMP PREEMPT Fri Apr 26 06:13:49 CST 2019
+#2 SMP PREEMPT Sun Feb 24 15:16:09 KST 2019
+#1 SMP PREEMPT Mon Feb 12 19:24:12 KST 2018
+#1 SMP PREEMPT Sat Dec 8 21:01:19 KST 2018
+#1 SMP PREEMPT Wed May 5 22:10:11 CST 2021
+#1 SMP PREEMPT Wed Jul 15 16:05:21 CST 2020
+#1 SMP PREEMPT Wed Jan 30 10:46:49 CST 2019
+#1 SMP PREEMPT Fri Mar 11 16:37:28 CST 2022
+#1 SMP PREEMPT Mon Mar 23 20:28:25 CST 2020
+#1 SMP PREEMPT Tue Dec 15 03:06:47 CST 2020
+#1 SMP PREEMPT Thu Mar 10 19:46:29 CST 2022
+#1 SMP PREEMPT Wed Apr 17 00:33:08 CST 2019
+#1 SMP PREEMPT Mon Apr 18 10:14:08 UTC 2022
+#1 SMP PREEMPT Tue Aug 11 01:40:03 CST 2020
+#1 SMP PREEMPT Mon Jul 23 02:39:13 JST 2018
+#1 SMP PREEMPT Thu Apr 11 06:11:15 KST 2019
+#1 SMP PREEMPT Fri Jun 12 14:30:45 JST 2020
+#1 SMP PREEMPT Tue Oct 23 05:51:17 KST 2018
+#1 SMP PREEMPT Tue May 11 05:16:10 CST 2021
+#1 SMP PREEMPT Wed Sep 9 23:03:35 KST 2020
+#1 SMP PREEMPT Thu Apr 15 13:03:39 CST 2021
+#2 SMP PREEMPT Wed Oct 17 14:54:08 CST 2018
+#1 SMP PREEMPT Tue Oct 15 21:12:41 CST 2019
+#1 SMP PREEMPT Thu Nov 23 00:19:51 CST 2017
+#1 SMP PREEMPT Mon Jul 27 08:45:10 KST 2020
+#1 SMP PREEMPT Tue Mar 5 23:49:05 PST 2019
+#1 SMP PREEMPT Wed Mar 24 00:48:05 KST 2021
+#1 SMP PREEMPT Wed Feb 16 20:44:51 CST 2022
+#1 SMP PREEMPT Fri May 21 13:53:01 CST 2021
+#1 SMP PREEMPT Wed Apr 29 07:14:46 CST 2020
+#1 SMP PREEMPT Mon Jan 29 16:11:35 CST 2018
+#2 SMP PREEMPT Tue Feb 23 22:51:14 CST 2021
+#1 SMP PREEMPT Fri Apr 24 04:57:15 CST 2020
+#1 SMP PREEMPT Wed Sep 12 10:51:06 CDT 2018
+#1 SMP PREEMPT Tue May 18 03:10:27 CST 2021
+#1 SMP PREEMPT Sat Sep 5 23:38:17 CST 2020
+#1 SMP PREEMPT Thu Apr 18 00:00:46 CST 2019
+#1 SMP PREEMPT Fri Apr 15 16:49:17 CST 2022
+#1 SMP PREEMPT Mon Jan 28 22:36:06 KST 2019
+#1 SMP PREEMPT Fri Apr 13 03:55:53 CST 2018
+#1 SMP PREEMPT Wed Aug 21 19:53:26 CST 2019
+#1 SMP PREEMPT Wed Aug 29 10:09:46 KST 2018
+#1 SMP PREEMPT Mon Oct 5 18:55:40 KST 2020
+#1 SMP PREEMPT Tue Mar 20 11:36:02 CST 2018
+#1 SMP PREEMPT Fri Aug 16 21:27:22 CST 2019
+#1 SMP PREEMPT Mon Mar 23 12:33:35 CST 2020
+#1 SMP PREEMPT Fri Dec 10 08:49:08 CST 2021
+#1 SMP PREEMPT Sun Apr 8 21:23:29 CST 2018
+#1 SMP PREEMPT Thu Jul 2 10:11:33 CST 2020
+#1 SMP PREEMPT Fri Aug 9 20:48:16 CST 2019
+#1 SMP PREEMPT Thu Mar 11 12:32:06 PST 2021
+#1 SMP PREEMPT Mon Oct 18 11:41:50 UTC 2021
+#1 SMP PREEMPT Tue Sep 28 11:23:45 CST 2021
+#1 SMP PREEMPT Wed Aug 7 04:11:46 CST 2019
+#1 SMP PREEMPT Tue Jan 22 19:43:00 PST 2019
+#1 SMP PREEMPT Mon Apr 8 14:36:17 KST 2019
+#1 SMP PREEMPT Fri Apr 19 15:57:01 KST 2019
+#2 SMP PREEMPT Sat Oct 9 12:02:05 CST 2021
+#1 SMP PREEMPT Thu Feb 17 03:50:53 CST 2022
+#1 SMP PREEMPT Mon Feb 4 23:45:04 PST 2019
+#1 SMP PREEMPT Sat Sep 21 18:53:57 CST 2019
+#1 SMP PREEMPT Thu Feb 10 17:18:01 CST 2022 f2fs-hash:a54920e5c9
+#2 SMP PREEMPT Wed Nov 7 21:05:15 CST 2018
+#1 SMP PREEMPT Sat Oct 19 17:10:44 CST 2019
+#1 SMP PREEMPT Tue Mar 19 00:15:45 PDT 2019
+#1 SMP PREEMPT Tue Dec 15 00:26:45 CST 2020
+#1 SMP PREEMPT Thu Mar 29 09:37:57 IST 2018
+#1 SMP PREEMPT Mon May 3 15:33:02 KST 2021
+#1 SMP PREEMPT Thu Mar 12 14:04:01 KST 2020
+#1 SMP PREEMPT Thu Jan 24 16:30:51 CST 2019
+#1 SMP PREEMPT Fri Jun 11 03:51:47 CST 2021
+#1 SMP PREEMPT Thu May 24 19:03:54 CST 2018
+#1 SMP PREEMPT Wed Nov 4 13:34:41 CST 2020
+#4 SMP PREEMPT Sun Aug 8 06:11:39 CDT 2021
+#1 SMP PREEMPT Thu Apr 8 23:37:02 HKT 2021
+#1 SMP PREEMPT Tue Mar 31 11:07:20 CST 2020
+#1 SMP PREEMPT Tue Jun 16 17:49:19 KST 2020
+#2 SMP PREEMPT Fri Apr 24 21:03:56 KST 2020
+#1 SMP PREEMPT Thu Apr 11 22:11:16 CST 2019
+#1 SMP PREEMPT Wed Dec 1 23:29:10 CST 2021
+#1 SMP PREEMPT Thu May 6 14:06:20 KST 2021
+#1 SMP PREEMPT Fri Jul 31 18:39:22 CST 2020
+#2 SMP PREEMPT Tue Feb 6 09:39:28 CST 2018
+#1 SMP PREEMPT Sun Feb 13 20:40:19 CST 2022
+#1 SMP PREEMPT Fri Mar 2 16:36:20 CST 2018
+#2 SMP PREEMPT Fri Jul 17 17:21:42 KST 2020
+#1 SMP PREEMPT Fri Nov 2 16:00:23 CST 2018
+#1 SMP PREEMPT Wed Sep 30 00:29:36 KST 2020
+#1 SMP PREEMPT Thu Sep 20 17:03:40 CST 2018
+#1 SMP PREEMPT Wed May 8 18:53:50 CST 2019
+#1 SMP PREEMPT Sun Dec 9 22:47:32 CST 2018
+#1 SMP PREEMPT Tue Feb 22 16:21:15 CST 2022
+#1 SMP PREEMPT Sat Mar 7 22:04:58 CST 2020
+#1 SMP PREEMPT Fri Jun 19 17:43:09 CST 2020
+#1 SMP PREEMPT Mon Oct 5 14:30:47 IST 2020
+#1 SMP PREEMPT Tue Mar 19 15:53:00 CST 2019
+#1 SMP PREEMPT Fri Dec 3 14:33:24 CST 2021
+#1 SMP PREEMPT Tue Apr 28 01:50:31 PDT 2020
+#1 SMP PREEMPT Fri Jan 17 15:41:16 KST 2020
+#1 SMP PREEMPT Sat Nov 11 09:53:15 CST 2017
+#1 SMP PREEMPT Thu Jun 20 00:45:16 PDT 2019
+#2 SMP PREEMPT Tue Aug 28 18:48:33 CST 2018
+#1 SMP PREEMPT Wed Mar 28 10:48:45 CST 2018
+#1 SMP PREEMPT Wed Oct 20 16:55:30 CST 2021
+#2 SMP PREEMPT Sun Feb 24 15:24:37 KST 2019
+#1 SMP PREEMPT Tue Jan 11 17:25:19 CST 2022
+#1 SMP PREEMPT Fri Apr 5 16:47:21 KST 2019
+#1 SMP PREEMPT Thu Jul 19 17:13:53 BRT 2018
+#1 SMP PREEMPT Fri Nov 1 11:52:11 KST 2019
+#1 SMP PREEMPT Wed Jan 10 13:46:22 EST 2018
+#1 SMP PREEMPT Thu Aug 6 21:12:15 CST 2020
+#1 SMP PREEMPT Sat Nov 10 05:51:20 KST 2018
+#1 SMP PREEMPT Tue Jan 11 19:28:01 UTC 2022
+#1 SMP PREEMPT Thu May 6 17:17:47 CST 2021
+#2 SMP PREEMPT Wed Nov 20 14:42:57 CST 2019
+#1 SMP PREEMPT Fri Mar 5 22:31:36 PST 2021
+#1 SMP PREEMPT Wed Mar 30 19:28:43 CST 2022
+#1 SMP PREEMPT Wed Dec 2 13:47:53 KST 2020
+#1 SMP PREEMPT Wed Oct 13 04:27:14 CST 2021
+#1 SMP PREEMPT Tue Jan 8 13:06:47 KST 2019
+#1 SMP PREEMPT Fri May 18 03:54:01 KST 2018
+#1 SMP PREEMPT Thu May 20 12:41:52 JST 2021
+#2 SMP PREEMPT Thu Mar 7 16:00:59 CST 2019
+#3 SMP PREEMPT Mon Apr 22 12:56:50 CST 2019
+#1 SMP PREEMPT Wed May 11 10:08:52 CST 2022
+#1 SMP PREEMPT Wed Feb 13 03:28:19 PST 2019
+#1 SMP PREEMPT Sun Dec 30 03:51:11 CST 2018
+#1 SMP PREEMPT Fri Jun 12 12:59:16 CST 2020
+#1 SMP PREEMPT Tue Apr 17 15:43:54 2018
+#2 SMP PREEMPT Fri Nov 9 19:14:40 CST 2018
+#1 SMP PREEMPT Wed Feb 7 10:20:25 CST 2018
+#1 SMP PREEMPT Thu Nov 29 02:38:08 IST 2018
+#1 SMP PREEMPT Tue Sep 11 18:11:16 CST 2018
+#1 SMP PREEMPT Tue Nov 12 12:26:59 KST 2019
+#1 SMP PREEMPT Thu Jul 5 20:28:37 CST 2018
+#1 SMP PREEMPT Fri Jan 4 22:03:32 CST 2019
+#2 SMP PREEMPT Wed Jun 19 22:12:17 KST 2019
+#1 SMP PREEMPT Fri Jun 26 23:46:36 CDT 2020
+#1 SMP PREEMPT Tue Jul 2 04:24:26 CDT 2019
+#1 SMP PREEMPT Fri Mar 16 08:28:48 CST 2018
+#2 SMP PREEMPT Tue Aug 13 14:02:59 KST 2019
+#1 SMP PREEMPT Sat Mar 16 12:33:08 KST 2019
+#1 SMP PREEMPT Tue Jan 29 04:42:23 CST 2019
+#1 SMP PREEMPT Wed Oct 21 11:57:57 CST 2020
+#1 SMP PREEMPT Wed Mar 9 01:25:18 CST 2022
+#1 SMP PREEMPT Mon Jul 12 20:59:41 CST 2021
+#1 SMP PREEMPT Wed Jun 13 17:53:13 KST 2018
+#1 SMP PREEMPT Thu Jan 23 14:16:00 KST 2020
+#1 SMP PREEMPT Thu Apr 5 20:46:19 CST 2018
+#2 SMP PREEMPT Fri Nov 2 17:59:50 CST 2018
+#1 SMP PREEMPT Tue Jul 2 14:43:00 KST 2019
+#1 SMP PREEMPT Wed Mar 28 15:53:46 CST 2018
+#2 SMP PREEMPT Tue Apr 23 00:51:11 KST 2019
+#1 SMP PREEMPT Wed Aug 22 00:49:59 CST 2018
+#1 SMP PREEMPT Wed May 30 16:28:02 KST 2018
+#1 SMP PREEMPT Wed Feb 27 18:56:40 CST 2019
+#1 SMP PREEMPT Wed Jan 30 18:05:18 CST 2019
+#1 SMP PREEMPT Thu Jul 2 01:26:02 CST 2020
+#1 SMP PREEMPT Fri Jun 8 01:28:46 JST 2018
+#1 SMP PREEMPT Wed Apr 25 10:34:28 JST 2018
+#1 SMP PREEMPT Fri Aug 3 19:51:24 IST 2018
+#1 SMP PREEMPT Wed Jan 23 20:09:18 CST 2019
+#1 SMP PREEMPT Sun Feb 28 20:54:11 PST 2021
+#1 SMP PREEMPT Sat Oct 23 15:11:30 UTC 2021
+#1 SMP PREEMPT Tue Apr 16 07:42:45 CDT 2019
+#1 SMP PREEMPT Fri Mar 30 16:28:20 KST 2018
+#1 SMP PREEMPT Wed Jan 16 17:35:28 HKT 2019
+#1 SMP PREEMPT Wed Feb 13 13:41:26 CST 2019
+#1 SMP PREEMPT Thu Mar 31 16:12:46 UTC 2022
+#1 SMP PREEMPT Tue Jan 8 14:54:42 JST 2019
+#1 SMP PREEMPT Tue Jul 6 14:13:24 +07 2021
+#2 SMP PREEMPT Fri Jul 26 20:48:52 KST 2019
+#1 SMP PREEMPT Fri Dec 31 12:56:11 CST 2021
+#1 SMP PREEMPT Sat Jan 19 01:23:07 JST 2019
+#1 SMP PREEMPT Sat May 9 11:31:06 CST 2020
+#1 SMP PREEMPT Fri Apr 22 22:05:22 CST 2022
+#1 SMP PREEMPT Thu Nov 22 07:06:34 UTC 2018
+#2 SMP PREEMPT Wed May 16 18:39:11 CST 2018
+#1 SMP PREEMPT Mon Apr 26 19:48:03 KST 2021
+#1 SMP PREEMPT Wed Dec 8 05:32:12 UTC 2021
+#1 SMP PREEMPT Wed Dec 6 11:26:04 KST 2017
+#1 SMP PREEMPT Mon Nov 4 13:43:15 CST 2019
+#1 SMP PREEMPT Fri Mar 1 15:48:46 CST 2019
+#1 SMP PREEMPT Wed Jun 17 17:02:01 CST 2020
+#1 SMP PREEMPT Wed Jul 22 18:30:56 CST 2020
+#1 SMP PREEMPT Fri Jun 5 19:00:08 CST 2020
+#1 SMP PREEMPT Mon Dec 25 11:24:15 CST 2017
+#1 SMP PREEMPT Tue Mar 3 06:23:49 KST 2020
+#1 SMP PREEMPT Fri Mar 11 19:38:38 CST 2022
+#1 SMP PREEMPT Sun Apr 29 05:10:18 CST 2018
+#1 SMP PREEMPT Fri Aug 20 19:19:48 CST 2021
+#1 SMP PREEMPT Wed May 9 23:11:51 KST 2018
+#1 SMP PREEMPT Wed May 6 23:18:25 CST 2020
+#1 SMP PREEMPT Tue Jul 23 04:44:00 KST 2019
+#1 SMP PREEMPT Tue May 21 10:27:42 CST 2019
+#1 SMP PREEMPT Tue Jun 30 15:54:53 CST 2020
+#1 SMP PREEMPT Mon Feb 14 13:48:08 CST 2022 f2fs-hash:eca162dae7
+#1 SMP PREEMPT Thu May 13 15:46:40 IST 2021
+#1 SMP PREEMPT Tue Mar 17 07:43:51 CST 2020
+#2 SMP PREEMPT Fri May 25 23:10:30 CST 2018
+#1 SMP PREEMPT Thu Jan 4 12:42:00 KST 2018
+#1 SMP PREEMPT Wed Apr 4 03:17:47 CST 2018
+#2 SMP PREEMPT Tue Nov 3 21:54:30 CST 2020
+#1 SMP PREEMPT Thu Oct 24 17:49:04 CST 2019
+#1 SMP PREEMPT Mon Jan 29 13:07:41 CST 2018
+#1 SMP PREEMPT Thu Sep 9 20:30:20 PDT 2021
+#1 SMP PREEMPT Fri Aug 13 19:13:58 +07 2021
+#1 SMP PREEMPT Thu Sep 2 15:41:04 CST 2021
+#2 SMP PREEMPT Thu Mar 8 15:03:28 CST 2018
+#1 SMP PREEMPT Tue Jun 5 17:50:39 PDT 2018
+#1 SMP PREEMPT Fri Mar 18 20:12:06 CST 2022
+#1 SMP PREEMPT Mon Jul 26 16:47:49 KST 2021
+#1 SMP PREEMPT Wed Sep 26 13:14:43 KST 2018
+#1 SMP PREEMPT Sun Dec 2 20:33:57 2018
+#1 SMP PREEMPT Wed Feb 23 12:47:48 KST 2022
+#48 SMP PREEMPT Thu Mar 24 09:12:39 CST 2022
+#1 SMP PREEMPT Mon Jul 26 22:28:11 CST 2021
+#2 SMP PREEMPT Thu Sep 24 18:05:28 CST 2020
+#1 SMP PREEMPT Mon Dec 28 21:11:39 CST 2020
+#1 SMP PREEMPT Tue Apr 19 10:58:14 CST 2022
+#1 SMP PREEMPT Mon Nov 26 20:16:57 CST 2018
+#1 SMP PREEMPT Sat Feb 29 09:12:00 KST 2020
+#1 SMP PREEMPT Mon Mar 7 22:31:33 CST 2022
+#2 SMP PREEMPT Wed Jun 17 01:57:01 CST 2020
+#1 SMP PREEMPT Wed Apr 13 21:41:46 CST 2022
+#1 SMP PREEMPT Fri Dec 7 19:38:31 CST 2018
+#1 SMP PREEMPT Mon May 11 00:12:22 CST 2020
+#1 SMP PREEMPT Tue Oct 20 19:17:54 CST 2020
+#1 SMP PREEMPT Fri Feb 14 23:25:49 CST 2020
+#2 SMP PREEMPT Sun Mar 22 14:25:59 CST 2020
+#1 SMP PREEMPT Thu Nov 5 23:14:29 CST 2020
+#1 SMP PREEMPT Thu Nov 8 22:03:27 CST 2018
+#1 SMP PREEMPT Sat Jul 18 13:27:51 CST 2020
+#1 SMP PREEMPT Sat Apr 2 01:33:09 CST 2022
+#1 SMP PREEMPT Wed Jul 22 18:37:46 CST 2020
+#1 SMP PREEMPT Thu Nov 12 22:07:09 JST 2020
+#1 SMP PREEMPT Fri Jan 22 13:25:14 CST 2021
+#1 SMP PREEMPT Sat Aug 3 04:07:21 CST 2019
+#1 SMP PREEMPT Wed Apr 24 20:48:37 CST 2019
+#1 SMP PREEMPT Tue Aug 13 01:24:16 CST 2019
+#2 SMP PREEMPT Wed Mar 20 02:38:13 CST 2019
+#1 SMP PREEMPT Thu Oct 11 23:32:21 CST 2018
+#1 SMP PREEMPT Fri Sep 18 18:47:45 CST 2020
+#1 SMP PREEMPT Fri Jan 15 11:36:23 CST 2021
+#1 SMP PREEMPT Thu Dec 12 22:45:44 PST 2019
+#1 SMP PREEMPT Wed Jun 26 01:46:22 KST 2019
+#1 SMP PREEMPT Tue May 19 18:20:38 CST 2020
+#1 SMP PREEMPT Thu Aug 8 20:21:13 CST 2019
+#1 SMP PREEMPT Fri Dec 17 11:57:24 CST 2021
+#1 SMP PREEMPT Tue Oct 23 18:22:34 CST 2018
+#1 SMP PREEMPT Fri Mar 9 17:47:21 CST 2018
+#1 SMP PREEMPT Wed Apr 20 15:43:53 CST 2022
+#1 SMP PREEMPT Fri Oct 12 15:54:48 KST 2018
+#1 SMP PREEMPT Wed Sep 15 22:48:45 CST 2021
+#5 SMP PREEMPT Fri Apr 15 16:18:45 CST 2022
+#1 SMP PREEMPT Fri Feb 11 05:33:07 CST 2022
+#1 SMP PREEMPT Thu Feb 10 20:53:12 CST 2022
+#1 SMP PREEMPT Sat Dec 29 17:25:06 CST 2018
+#1 SMP PREEMPT Thu Oct 22 05:20:22 CST 2020
+#1 SMP PREEMPT Tue Mar 9 20:25:12 PST 2021
+#1 SMP PREEMPT Fri Mar 11 03:13:00 UTC 2022
+#1 SMP PREEMPT Mon Jan 24 22:48:02 CST 2022
+#1 SMP PREEMPT Wed Feb 21 15:40:48 KST 2018
+#1 SMP PREEMPT Fri Oct 26 18:27:45 KST 2018
+#1 SMP PREEMPT Fri Nov 27 23:05:58 KST 2020
+#1 SMP PREEMPT Mon Mar 14 21:36:33 JST 2022
+#1 SMP PREEMPT Fri May 1 17:31:30 KST 2020
+#3 SMP PREEMPT Mon Dec 30 11:06:37 CST 2019
+#1 SMP PREEMPT Tue Feb 22 22:32:19 CST 2022
+#1 SMP PREEMPT Wed Aug 4 17:13:21 +07 2021
+#1 SMP PREEMPT Tue Apr 27 14:01:32 CST 2021
+#2 SMP PREEMPT Fri Jan 12 15:04:51 CST 2018
+#1 SMP PREEMPT Wed May 11 14:16:28 IST 2022
+#1 SMP PREEMPT Fri May 3 06:04:13 CST 2019
+#1 SMP PREEMPT Tue Oct 27 16:26:46 KST 2020
+#2 SMP PREEMPT Sat Oct 26 05:15:00 CST 2019
+#1 SMP PREEMPT Fri Feb 15 04:12:32 KST 2019
+#1 SMP PREEMPT Thu Aug 30 10:17:23 CDT 2018
+#1 SMP PREEMPT Mon Dec 11 19:04:30 HKT 2017
+#1 SMP PREEMPT Thu Apr 30 22:05:55 IST 2020
+#1 SMP PREEMPT Thu Oct 21 16:24:56 CST 2021
+#1 SMP PREEMPT Fri Jun 4 17:26:52 +07 2021
+#1 SMP PREEMPT Fri Feb 11 14:23:10 CST 2022
+#1 SMP PREEMPT Wed Nov 6 17:49:01 2019
+#2 SMP PREEMPT Wed Aug 14 21:25:32 KST 2019
+#1 SMP PREEMPT Thu Jul 15 18:05:27 CST 2021
+#1 SMP PREEMPT Fri Mar 19 00:28:13 CST 2021
+#1 SMP PREEMPT Tue Apr 9 21:53:42 PDT 2019
+#17 SMP PREEMPT Mon Sep 13 16:55:33 CST 2021
+#1 SMP PREEMPT Thu Dec 12 23:34:38 JST 2019
+#1 SMP PREEMPT Mon Jun 4 12:43:12 CST 2018
+#1 SMP PREEMPT Wed Mar 10 19:52:50 IST 2021
+#1 SMP PREEMPT Mon May 6 12:37:56 UTC 2019
+#2 SMP PREEMPT Thu Jan 6 17:10:25 CST 2022
+#1 SMP PREEMPT Mon Jan 14 18:17:42 KST 2019
+#1 SMP PREEMPT Thu Apr 25 17:00:24 KST 2019
+#1 SMP PREEMPT Thu May 20 11:02:08 CST 2021
+#1 SMP PREEMPT Tue Sep 18 12:23:54 PDT 2018
+#1 SMP PREEMPT Fri Jul 17 23:09:35 CST 2020
+#3 SMP PREEMPT Wed Sep 1 22:17:30 CST 2021
+#1 SMP PREEMPT Tue Feb 15 03:09:33 CST 2022
+#1 SMP PREEMPT Tue Aug 10 22:25:53 EDT 2021
+#1 SMP PREEMPT Mon Oct 1 19:06:35 KST 2018
+#1 SMP PREEMPT Mon Nov 4 11:45:39 CST 2019
+#1 SMP PREEMPT Mon Mar 30 23:07:40 PDT 2020
+#2 SMP PREEMPT Mon Jul 20 13:24:18 CST 2020
+#1 SMP PREEMPT Wed Nov 18 00:04:42 CST 2020
+#1 SMP PREEMPT Mon Mar 18 23:52:57 PDT 2019
+#1 SMP PREEMPT Mon Dec 3 13:58:06 KST 2018
+#1 SMP PREEMPT Thu Jun 28 11:45:23 -03 2018
+#1 SMP PREEMPT Mon Jan 8 18:13:07 CST 2018
+#1 SMP PREEMPT Fri Dec 20 17:26:34 HKT 2019
+#1 SMP PREEMPT Fri Dec 25 13:11:35 PST 2020
+#1 SMP PREEMPT Tue Jun 23 13:04:52 CST 2020
+#0 SMP PREEMPT Thu May 21 01:56:58 UTC 2020
+#1 SMP PREEMPT Sun Mar 7 11:05:16 KST 2021
+#1 SMP PREEMPT Wed Feb 5 21:40:26 KST 2020
+#1 SMP PREEMPT Mon Apr 29 19:25:21 CST 2019
+#1 SMP PREEMPT Wed Jul 29 15:52:42 UTC 2020
+#1 SMP PREEMPT Fri May 20 12:15:29 CST 2022
+#1 SMP PREEMPT Tue Dec 7 17:27:32 CST 2021
+#1 SMP PREEMPT Wed Aug 7 06:58:24 CDT 2019
+#1 SMP PREEMPT Thu Sep 12 12:54:18 KST 2019
+#1 SMP PREEMPT Mon Feb 25 22:00:25 KST 2019
+#1 SMP PREEMPT Thu Nov 1 19:55:51 CST 2018
+#1 SMP PREEMPT Sat Dec 25 19:57:06 PST 2021
+#1 SMP PREEMPT Fri Oct 13 04:41:53 UTC 2017
+#2 SMP PREEMPT Mon Sep 23 14:21:48 KST 2019
+#1 SMP PREEMPT Wed Dec 13 13:06:07 CST 2017
+#1 SMP PREEMPT Thu Aug 13 08:54:45 CST 2020
+#2 SMP PREEMPT Thu Apr 23 11:07:25 CST 2020
+#1 SMP PREEMPT Tue Oct 23 19:19:25 CST 2018
+#1 SMP PREEMPT Sun Aug 1 14:50:29 CST 2021
+#3 SMP PREEMPT Fri Sep 20 17:08:52 KST 2019
+#2 SMP PREEMPT Mon Jun 22 17:34:39 CST 2020
+#1 SMP PREEMPT Fri Dec 18 17:27:55 CST 2020
+#4 SMP PREEMPT Mon Jun 28 07:09:08 CDT 2021
+#1 SMP PREEMPT Thu Jan 13 18:53:05 CST 2022
+#1 SMP PREEMPT Thu Jun 11 21:30:52 CST 2020
+#1 SMP PREEMPT Mon Nov 1 14:12:44 CST 2021
+#1 SMP PREEMPT Thu Jan 6 04:36:20 CST 2022
+#2 SMP PREEMPT Mon Jul 13 16:13:07 KST 2020
+#1 SMP PREEMPT Fri Jun 12 16:51:16 KST 2020
+#2 SMP PREEMPT Mon Apr 12 19:10:48 CST 2021
+#1 SMP PREEMPT Sat Sep 18 10:11:31 CST 2021
+#1 SMP PREEMPT Sat Nov 9 15:06:53 CST 2019
+#1 SMP PREEMPT Sat Oct 30 00:13:35 CST 2021
+#1 SMP PREEMPT Fri Aug 6 02:23:41 CST 2021
+#1 SMP PREEMPT Wed Dec 20 11:41:25 KST 2017
+#1 SMP PREEMPT Mon Jan 21 23:57:04 JST 2019
+#1 SMP PREEMPT Fri Feb 5 04:09:43 CST 2021
+#1 SMP PREEMPT Thu Sep 6 19:05:48 CST 2018
+#1 SMP PREEMPT Mon May 18 20:36:35 CST 2020
+#1 SMP PREEMPT Fri Mar 29 10:06:42 JST 2019
+#1 SMP PREEMPT Wed Mar 16 23:49:44 CST 2022
+#1 SMP PREEMPT Thu Aug 29 00:44:19 CST 2019
+#1 SMP PREEMPT Fri Sep 18 02:59:06 CST 2020
+#1 SMP PREEMPT Mon Mar 1 19:59:31 KST 2021
+#1 SMP PREEMPT Wed Jun 13 16:19:17 CST 2018
+#1 SMP PREEMPT Sat May 1 03:27:35 CST 2021
+#1 SMP PREEMPT Mon Apr 18 20:42:03 CST 2022
+#1 SMP PREEMPT Fri Sep 7 00:17:12 PDT 2018
+#2 SMP PREEMPT Thu Jun 18 10:31:03 CST 2020
+#1 SMP PREEMPT Thu Sep 12 00:48:29 JST 2019
+#2 SMP PREEMPT Sat Mar 7 15:08:11 CST 2020
+#1 SMP PREEMPT Thu Aug 1 06:10:51 CST 2019
+#1 SMP PREEMPT Thu Aug 8 11:23:14 CST 2019
+#1 SMP PREEMPT Mon May 9 17:30:42 CST 2022
+#1 SMP PREEMPT Thu Apr 18 18:56:34 CST 2019
+#1 SMP PREEMPT Wed Sep 12 22:14:51 CST 2018
+#1 SMP PREEMPT Fri Aug 9 12:44:32 KST 2019
+#1 SMP PREEMPT Mon Dec 14 16:49:40 CST 2020
+#1 SMP PREEMPT Thu Aug 5 10:32:23 KST 2021
+#1 SMP PREEMPT Wed Nov 6 11:40:41 CST 2019
+#1 SMP PREEMPT Wed Dec 2 05:04:14 CST 2020
+#1 SMP PREEMPT Fri Jul 5 17:03:41 CST 2019
+#1 SMP PREEMPT Wed Jul 28 17:16:52 KST 2021
+#1 SMP PREEMPT Sun Jun 14 02:57:02 CST 2020
+#1 SMP PREEMPT Thu Mar 14 11:33:20 CST 2019
+#1 SMP PREEMPT Tue Jan 9 11:02:42 CST 2018
+#1 SMP PREEMPT Sat Sep 18 14:20:17 CST 2021
+#2 SMP PREEMPT Mon Jan 20 15:15:02 KST 2020
+#1 SMP PREEMPT Fri Nov 29 20:00:13 IST 2019
+#1 SMP PREEMPT Wed Sep 25 23:59:04 KST 2019
+#1 SMP PREEMPT Sat Apr 13 12:18:06 CST 2019
+#1 SMP PREEMPT Thu Jun 3 03:13:41 KST 2021
+#1 SMP PREEMPT Thu Aug 5 00:41:22 CST 2021
+#1 SMP PREEMPT Tue Mar 5 16:56:56 CST 2019
+#2 SMP PREEMPT Wed Jun 19 09:54:19 CST 2019
+#1 SMP PREEMPT Thu Apr 8 13:33:48 KST 2021
+#1 SMP PREEMPT Wed Nov 14 16:20:36 CST 2018
+#1 SMP PREEMPT Tue Jun 8 21:39:36 CST 2021
+#1 SMP PREEMPT Wed Apr 24 20:23:23 KST 2019
+#1 SMP PREEMPT Sat Jun 16 19:47:46 CST 2018
+#1 SMP PREEMPT Wed Mar 4 12:23:54 CST 2020
+#1 SMP PREEMPT Thu Jan 31 15:04:06 CST 2019
+#2 SMP PREEMPT Sat Jun 20 22:22:28 CST 2020
+#1 SMP PREEMPT Fri Nov 20 13:42:04 KST 2020
+#4 SMP PREEMPT Fri Aug 24 13:55:12 CST 2018
+#1 SMP PREEMPT Mon Aug 31 09:13:16 CDT 2020
+#1 SMP PREEMPT Wed Jul 31 20:03:53 CST 2019
+#1 SMP PREEMPT Tue Jan 18 14:32:55 KST 2022
+#1 SMP PREEMPT Sat Dec 30 02:12:05 CST 2017
+#1 SMP PREEMPT Tue Mar 17 20:25:12 PDT 2020
+#1 SMP PREEMPT Thu May 9 18:23:11 CST 2019
+#1 SMP PREEMPT Thu Dec 28 21:57:55 KST 2017
+#1 SMP PREEMPT Thu Dec 23 20:12:04 CST 2021
+#1 SMP PREEMPT Thu May 24 10:19:13 CST 2018
+#1 SMP PREEMPT Fri Nov 13 12:13:08 CST 2020
+#2 SMP PREEMPT Fri Jan 25 11:32:37 KST 2019
+#1 SMP PREEMPT Sat Aug 1 16:24:02 CST 2020
+#1 SMP PREEMPT Wed Jun 10 16:47:07 CST 2020
+#1 SMP PREEMPT Wed Dec 19 10:59:51 KST 2018
+#2 SMP PREEMPT Fri Sep 28 17:53:54 CST 2018
+#10 SMP PREEMPT Wed Dec 5 15:26:06 CST 2018
+#1 SMP PREEMPT Sat Sep 18 12:26:40 CST 2021
+#1 SMP PREEMPT Thu Jan 11 12:30:29 CST 2018
+#1 SMP PREEMPT Mon Nov 5 18:13:41 CST 2018
+#1 SMP PREEMPT Sun Feb 7 16:46:23 CST 2021
+#1 SMP PREEMPT Thu Apr 16 03:45:45 CST 2020
+#1 SMP PREEMPT Wed Jan 6 04:10:05 CST 2021
+#1 SMP PREEMPT Thu Oct 11 01:13:52 PDT 2018
+#1 SMP PREEMPT Tue May 22 15:48:33 CST 2018
+#1 SMP PREEMPT Tue Mar 3 11:43:43 KST 2020
+#1 SMP PREEMPT Thu Nov 14 21:56:59 CST 2019
+#1 SMP PREEMPT Fri Oct 16 22:59:21 CST 2020
+#1 SMP PREEMPT Wed Jun 17 18:01:35 CST 2020
+#1 SMP PREEMPT Thu Apr 19 16:40:46 2018
+#1 SMP PREEMPT Fri Dec 4 09:47:42 CST 2020
+#1 SMP PREEMPT Mon Feb 7 21:02:01 KST 2022
+#1 SMP PREEMPT Thu Sep 23 18:57:27 CST 2021
+#1 SMP PREEMPT Fri Sep 7 11:42:10 KST 2018
+#1 SMP PREEMPT Tue Jul 9 15:47:03 CST 2019
+#1 SMP PREEMPT Tue Apr 2 09:31:21 CST 2019
+#1 SMP PREEMPT Fri Dec 6 12:57:44 CST 2019
+#2 SMP PREEMPT Tue Jul 9 15:51:56 KST 2019
+#1 SMP PREEMPT Fri May 28 13:07:13 KST 2021
+#1 SMP PREEMPT Thu Jan 20 21:49:17 CST 2022
+#1 SMP PREEMPT Wed Apr 22 19:08:02 CST 2020
+#2 SMP PREEMPT Tue Mar 27 12:47:50 CST 2018
+#2 SMP PREEMPT Sat Dec 4 13:02:02 CST 2021
+#1 SMP PREEMPT Wed Feb 3 11:39:49 CST 2021
+#1 SMP PREEMPT Tue Feb 18 15:27:05 CST 2020
+#1 SMP PREEMPT Tue Jan 8 15:06:42 JST 2019
+#1 SMP PREEMPT Wed Dec 11 22:46:42 CST 2019
+#1 SMP PREEMPT Wed Dec 11 17:47:01 CST 2019
+#1 SMP PREEMPT Wed Dec 2 14:01:13 KST 2020
+#1 SMP PREEMPT Mon Mar 2 11:28:55 CST 2020
+#1 SMP PREEMPT Mon Apr 23 15:19:00 CST 2018
+#1 SMP PREEMPT Wed Jul 28 15:30:44 JST 2021
+#1 SMP PREEMPT Thu Apr 2 12:12:24 CST 2020
+#2 SMP PREEMPT Thu Jan 14 23:36:39 CST 2021
+#1 SMP PREEMPT Thu Feb 27 18:48:30 CST 2020
+#1 SMP PREEMPT Fri Jun 1 16:05:59 CST 2018
+#1 SMP PREEMPT Mon Jan 24 04:39:48 CST 2022
+#1 SMP PREEMPT Thu May 26 19:59:40 CST 2022
+#1 SMP PREEMPT Tue Sep 15 16:29:40 CST 2020
+#1 SMP PREEMPT Wed Jan 17 16:52:03 KST 2018
+#1 SMP PREEMPT Tue Jul 13 12:34:20 UTC 2021
+#25 SMP PREEMPT Thu Aug 1 11:15:17 CST 2019
+#1 SMP PREEMPT Tue Nov 30 12:50:47 CST 2021
+#1 SMP PREEMPT Tue Apr 5 22:31:47 CST 2022
+#1 SMP PREEMPT Thu Jul 25 18:37:15 KST 2019
+#1 SMP PREEMPT Thu May 24 05:19:24 KST 2018
+#1 SMP PREEMPT Fri Feb 25 18:33:46 JST 2022
+#1 SMP PREEMPT Mon Apr 16 11:57:54 KST 2018
+#2 SMP PREEMPT Thu Jun 13 19:00:19 CST 2019
+#1 SMP PREEMPT Tue Mar 5 04:07:21 CST 2019
+#1 SMP PREEMPT Thu Nov 5 16:07:24 CST 2020
+#1 SMP PREEMPT Wed Mar 18 12:13:45 CST 2020
+#1 SMP PREEMPT Wed Feb 23 23:06:44 CST 2022
+#1 SMP PREEMPT Tue Jun 12 23:27:18 UTC 2018
+#1 SMP PREEMPT Mon Aug 2 09:01:34 CDT 2021
+#1 SMP PREEMPT Wed Feb 24 12:30:27 PST 2021
+#1 SMP PREEMPT Fri Oct 25 11:24:20 CST 2019
+#2 SMP Tue Oct 20 17:16:44 CEST 2020
+#1 SMP PREEMPT Sat Apr 23 22:18:21 PDT 2022
+#1 SMP PREEMPT Fri Dec 18 18:51:24 CST 2020
+#1 SMP PREEMPT Sat Jan 9 01:43:19 IST 2021
+#1 SMP PREEMPT Mon May 28 09:27:46 CST 2018
+#1 SMP PREEMPT Tue Aug 20 20:58:53 KST 2019
+#1 SMP PREEMPT Wed Dec 23 00:03:42 CST 2020
+#1 SMP PREEMPT Fri Aug 6 12:55:42 CST 2021
+#1 SMP PREEMPT Tue Jun 16 11:29:09 CDT 2020
+#1 SMP PREEMPT Wed Mar 17 23:09:42 CST 2021
+#1 SMP PREEMPT Sat May 11 14:47:21 CST 2019
+#1 SMP PREEMPT Thu Dec 27 10:36:24 CST 2018
+#3 SMP PREEMPT Tue Feb 9 21:19:32 KST 2021
+#1 SMP PREEMPT Tue Jul 3 18:08:26 KST 2018
+#1 SMP PREEMPT Thu May 9 19:55:37 CST 2019
+#1 SMP PREEMPT Fri Mar 15 18:20:39 CST 2019
+#1 SMP PREEMPT Thu Apr 19 17:56:17 CST 2018
+#2 SMP Fri Nov 16 15:49:31 KST 2018
+#1 SMP PREEMPT Thu May 13 10:41:28 CST 2021
+#1 SMP PREEMPT Tue Jan 19 18:49:47 CST 2021
+#1 SMP PREEMPT Mon Jul 26 17:45:36 CST 2021
+#1 SMP PREEMPT Wed Aug 22 09:46:18 CST 2018
+#2 SMP PREEMPT Mon Apr 5 15:05:00 HKT 2021
+#1 SMP PREEMPT Thu Feb 28 12:30:20 CST 2019
+#1 SMP PREEMPT Wed Dec 20 00:19:29 CST 2017
+#1 SMP PREEMPT Wed Mar 21 21:04:49 KST 2018
+#1 SMP PREEMPT Tue Apr 13 19:40:10 CST 2021
+#1 SMP PREEMPT Mon Nov 26 15:47:54 CST 2018
+#1 SMP PREEMPT Tue May 5 20:35:51 KST 2020
+#1 SMP PREEMPT Fri Jul 6 12:01:14 JST 2018
+#2 SMP PREEMPT Mon Jun 17 15:18:57 KST 2019
+#1 SMP PREEMPT Tue Dec 7 15:01:43 PST 2021
+#1 SMP PREEMPT Sat Feb 6 11:58:28 IST 2021
+#1 SMP PREEMPT Tue Apr 9 09:17:06 CDT 2019
+#1 SMP PREEMPT Wed Jan 30 23:01:52 PST 2019
+#1 SMP PREEMPT Mon Mar 5 18:26:50 CST 2018
+#1 SMP PREEMPT Tue Jan 30 03:47:18 CST 2018
+#1 SMP PREEMPT Mon Nov 9 03:27:20 CST 2020
+#1 SMP PREEMPT Tue Jul 31 23:06:42 PDT 2018
+#1 SMP PREEMPT Fri Feb 11 19:55:46 CST 2022
+#1 SMP PREEMPT Wed Jul 31 04:47:37 CST 2019
+#1 SMP PREEMPT Wed Aug 8 13:43:42 CST 2018
+#1 SMP PREEMPT Wed Jun 6 22:30:13 UTC 2018
+#1 SMP PREEMPT Tue Apr 13 01:41:46 JST 2021
+#1 SMP PREEMPT Wed Apr 7 12:56:28 CST 2021
+#1 SMP PREEMPT Wed Jan 15 23:29:29 2020
+#1 SMP PREEMPT Tue Nov 27 15:53:41 CST 2018
+#1 SMP PREEMPT Fri Feb 4 05:35:51 CST 2022
+#1 SMP PREEMPT Tue Mar 30 02:27:08 KST 2021
+#1 SMP PREEMPT Sat Jun 8 02:21:21 CDT 2019
+#34 SMP PREEMPT Tue Jul 28 16:46:59 CST 2020
+#1 SMP PREEMPT Wed Jan 19 10:30:20 CST 2022
+#2 SMP PREEMPT Thu Oct 14 20:05:27 CST 2021
+#1 SMP PREEMPT Tue Nov 16 21:06:47 CST 2021
+#42 SMP PREEMPT Thu Oct 29 17:27:17 CST 2020
+#2 SMP PREEMPT Sun Sep 29 05:03:21 CST 2019
+#1 SMP PREEMPT Sat Jan 26 19:37:48 CST 2019
+#1 SMP PREEMPT Wed Dec 6 05:27:00 CST 2017
+#1 SMP PREEMPT Wed Jul 22 20:19:45 CST 2020
+#1 SMP PREEMPT Wed May 25 10:26:36 CST 2022
+#1 SMP PREEMPT Thu Dec 24 10:51:03 KST 2020
+#2 SMP PREEMPT Mon Jan 10 19:11:47 CST 2022
+#1 SMP PREEMPT Thu Jan 6 19:41:19 IST 2022
+#1 SMP PREEMPT Tue May 19 23:21:22 CST 2020
+#1 SMP PREEMPT Fri Jan 3 22:30:17 CST 2020
+#2 SMP PREEMPT Fri Aug 6 18:13:34 CST 2021
+#1 SMP PREEMPT Thu Dec 14 21:20:40 KST 2017
+#1 SMP PREEMPT Fri May 7 17:51:11 CST 2021
+#1 SMP PREEMPT Sun Feb 9 23:58:15 PST 2020
+#1 SMP PREEMPT Thu Dec 3 20:08:30 +07 2020
+#1 SMP PREEMPT Mon Dec 2 11:45:27 IST 2019
+#1 SMP PREEMPT Wed Mar 24 01:18:17 JST 2021
+#1 SMP PREEMPT Wed May 12 15:53:32 +07 2021
+#1 SMP PREEMPT Wed Dec 5 23:43:14 PST 2018
+#2 SMP PREEMPT Wed Sep 16 20:47:03 CST 2020
+#1 SMP PREEMPT Tue Jan 4 15:17:02 CST 2022
+#1 SMP PREEMPT Fri Mar 27 00:12:17 CST 2020
+#1 SMP PREEMPT Tue Apr 14 14:10:19 CST 2020
+#1 SMP PREEMPT Fri Jun 28 04:51:03 CST 2019
+#2 SMP PREEMPT Tue Jan 26 17:04:24 CST 2021
+#1 SMP PREEMPT Fri Mar 27 10:52:41 CST 2020
+#1 SMP PREEMPT Tue May 14 18:45:06 CST 2019
+#1 SMP PREEMPT Fri Dec 13 05:30:57 UTC 2019
+#1 SMP PREEMPT Tue Nov 13 21:17:29 CST 2018
+#1 SMP PREEMPT Thu Mar 15 12:57:15 CST 2018
+#1 SMP PREEMPT Thu Apr 11 19:23:58 CST 2019
+#1 SMP PREEMPT Fri Jul 12 03:28:37 CST 2019
+#2 SMP PREEMPT Fri Jul 5 13:56:29 CST 2019
+#1 SMP PREEMPT Wed Apr 10 18:01:14 CST 2019
+#1 SMP PREEMPT Sun Nov 17 22:34:58 JST 2019
+#1 SMP PREEMPT Mon Dec 14 21:30:08 CST 2020
+#1 SMP PREEMPT Tue Jun 19 20:42:03 CST 2018
+#1 SMP PREEMPT Mon Nov 25 16:44:12 CST 2019
+#1 SMP PREEMPT Mon Jan 28 11:54:44 CST 2019
+#1 SMP PREEMPT Wed Oct 30 14:54:51 CST 2019
+#1 SMP PREEMPT Mon Oct 26 19:01:18 WIB 2020
+#1 SMP PREEMPT Tue Oct 30 14:05:26 IST 2018
+#1 SMP PREEMPT Tue May 19 11:15:14 CST 2020
+#1 SMP PREEMPT Thu Nov 15 01:12:00 CST 2018
+#1 SMP PREEMPT Thu Jan 10 09:36:13 CST 2019
+#1 SMP PREEMPT Mon Aug 10 20:00:53 CST 2020
+#1 SMP PREEMPT Fri Feb 21 11:55:55 UTC 2020
+#1 SMP PREEMPT Tue Sep 10 12:36:54 KST 2019
+#1 SMP PREEMPT Mon May 16 23:24:58 CST 2022
+#1 SMP PREEMPT Wed Jan 15 12:50:05 CST 2020
+#1 SMP PREEMPT Wed Aug 22 19:49:33 CST 2018
+#1 SMP PREEMPT Fri Mar 6 18:50:03 CST 2020
+#1 SMP PREEMPT Wed Dec 12 16:56:48 CST 2018
+#1 SMP PREEMPT Fri Jan 11 19:26:17 IST 2019
+#1 SMP PREEMPT Mon Jan 27 01:08:24 PST 2020
+#1 SMP PREEMPT Thu Jul 25 22:11:55 CST 2019
+#1 SMP PREEMPT Wed Oct 23 23:05:01 CST 2019
+#1 SMP PREEMPT Thu Jun 3 17:55:43 KST 2021
+#1 SMP PREEMPT Thu May 19 19:17:05 PDT 2022
+#1 SMP PREEMPT Tue Apr 27 18:49:50 CST 2021
+#1 SMP PREEMPT Fri Aug 13 05:12:20 CST 2021
+#2 SMP PREEMPT Wed Jul 29 14:45:12 CST 2020
+#1 SMP PREEMPT Thu Oct 31 23:52:50 PDT 2019
+#1 SMP PREEMPT Fri Jan 7 05:02:48 CST 2022
+#1 SMP PREEMPT Fri Dec 10 08:49:08 CST 2021
+#1 SMP PREEMPT Tue Jan 2 18:35:14 UTC 2018
+#1 SMP PREEMPT Thu May 24 20:53:27 CST 2018
+#1 SMP PREEMPT Wed Jun 13 10:56:57 2018
+#1 SMP PREEMPT Fri Jan 4 06:19:53 CST 2019
+#1 SMP PREEMPT Thu May 24 21:35:56 CST 2018
+#1 SMP PREEMPT Wed Aug 26 00:16:02 CST 2020
+#1 SMP PREEMPT Sat Dec 22 02:47:33 KST 2018
+#1 SMP PREEMPT Fri Mar 6 05:50:34 CST 2020
+#1 SMP PREEMPT Thu Apr 9 01:09:50 CST 2020
+#1 SMP PREEMPT Wed Jan 10 01:47:48 CST 2018
+#1 SMP PREEMPT Wed Mar 27 14:11:18 CST 2019
+#1 SMP PREEMPT Wed Sep 1 17:26:37 CST 2021
+#1 SMP PREEMPT Wed Nov 13 13:05:06 CST 2019
+#1 SMP PREEMPT Mon Nov 9 21:45:39 KST 2020
+#1 SMP PREEMPT Fri Dec 24 14:51:57 UTC 2021
+#1 SMP PREEMPT Tue Apr 7 14:47:08 CDT 2020
+#1 SMP PREEMPT Wed Oct 20 15:37:45 CST 2021
+#1 SMP PREEMPT Fri Nov 13 18:53:37 KST 2020
+#1 SMP PREEMPT Tue Jan 22 11:45:25 CST 2019
+#1 SMP PREEMPT Mon Jun 25 21:33:32 CST 2018
+#1 SMP PREEMPT Mon Jun 8 16:53:03 CST 2020
+#2 SMP PREEMPT Thu May 9 17:08:22 CST 2019
+#1 SMP PREEMPT Sat Mar 21 01:14:57 JST 2020
+#2 SMP PREEMPT Sun Feb 7 11:10:36 CST 2021
+#1 SMP PREEMPT Thu Feb 13 04:30:01 JST 2020
+#1 SMP PREEMPT Mon Apr 11 18:56:12 CST 2022
+#1 SMP PREEMPT Tue Apr 12 22:27:20 CST 2022
+#1 SMP PREEMPT Tue May 8 10:38:24 CST 2018
+#1 SMP PREEMPT Mon Nov 11 10:16:18 CST 2019
+#1 SMP PREEMPT Fri Sep 10 18:52:12 CST 2021
+#1 SMP PREEMPT Mon Dec 6 21:19:37 CST 2021
+#2 SMP PREEMPT Thu May 28 10:50:02 KST 2020
+#1 SMP PREEMPT Fri Jul 5 17:38:38 CST 2019
+#1 SMP PREEMPT Thu Aug 8 18:46:10 KST 2019
+#1 SMP PREEMPT Sat Dec 1 15:52:39 CST 2018
+#1 SMP PREEMPT Wed Aug 4 22:54:45 KST 2021
+#1 SMP PREEMPT Wed Sep 16 16:09:49 KST 2020
+#1 SMP PREEMPT Thu Apr 1 17:52:07 CST 2021
+#1 SMP PREEMPT Fri Jan 31 20:25:19 KST 2020
+#2 SMP PREEMPT Mon Jul 27 20:14:24 CST 2020
+#1 SMP PREEMPT Thu Jun 6 09:56:22 CDT 2019
+#1 SMP PREEMPT Mon Sep 17 22:14:35 CST 2018
+#2 SMP PREEMPT Tue Oct 12 11:59:56 CST 2021
+#2 SMP PREEMPT Tue Aug 27 13:58:39 CST 2019
+#1 SMP PREEMPT Wed Aug 12 00:02:30 CST 2020
+#1 SMP PREEMPT Fri Mar 15 22:05:40 CST 2019
+#2 SMP PREEMPT Wed Nov 3 12:32:45 CST 2021
+#1 SMP PREEMPT Fri Mar 18 16:55:22 CST 2022
+#1 SMP PREEMPT Fri Aug 10 13:58:47 CST 2018
+#1 SMP PREEMPT Mon Jul 23 21:41:07 CST 2018
+#1 SMP PREEMPT Fri Jun 22 11:04:04 CST 2018
+#1 SMP PREEMPT Mon Feb 17 11:42:03 KST 2020
+#1 SMP PREEMPT Mon Aug 31 01:52:01 CST 2020
+#1 SMP PREEMPT Fri Jun 14 10:14:56 CST 2019
+#2 SMP PREEMPT Thu Mar 14 11:16:24 CST 2019
+#1 SMP PREEMPT Wed Apr 21 07:53:12 CST 2021
+#2 SMP PREEMPT Tue Sep 15 16:27:48 KST 2020
+#1 SMP PREEMPT Mon Jun 3 23:54:22 KST 2019
+#1 SMP PREEMPT Wed May 18 12:00:41 CST 2022
+#1 SMP PREEMPT Sat Apr 20 20:48:41 CST 2019
+#1 SMP PREEMPT Tue Feb 2 20:47:28 PST 2021
+#1 SMP PREEMPT Thu Sep 13 22:04:30 CST 2018
+#1 SMP PREEMPT Tue Jan 12 00:45:51 CST 2021
+#1 SMP PREEMPT Thu Jan 27 17:23:48 CST 2022
+#1 SMP PREEMPT Tue Aug 21 00:19:34 CST 2018
+#1 SMP PREEMPT Sun Oct 21 20:09:26 CST 2018
+#29 SMP PREEMPT Thu Nov 19 10:22:49 CST 2020
+#1 SMP PREEMPT Thu Feb 25 01:00:09 CST 2021
+#1 SMP PREEMPT Wed Aug 7 05:27:48 KST 2019
+#14 SMP PREEMPT Tue Jan 22 17:04:39 CST 2019
+#1 SMP PREEMPT Wed Apr 11 03:56:29 CST 2018
+#1 SMP PREEMPT Fri Mar 26 11:40:02 CST 2021
+#1 SMP PREEMPT Tue Mar 16 17:06:42 CST 2021
+#1 SMP PREEMPT Wed May 13 07:54:22 CST 2020
+#1 SMP PREEMPT Fri Mar 29 14:05:07 CST 2019
+#1 SMP PREEMPT Tue Apr 10 14:20:59 KST 2018
+#1 SMP PREEMPT Mon Mar 23 00:19:54 CST 2020
+#2 SMP PREEMPT Mon Apr 20 22:14:24 CST 2020
+#1 SMP PREEMPT Thu May 9 03:11:56 JST 2019
+#1 SMP PREEMPT Fri Apr 5 16:47:37 CST 2019
+#1 SMP PREEMPT Sun Mar 1 18:51:26 CST 2020
+#1 SMP PREEMPT Tue Apr 7 10:18:18 HKT 2020
+#2 SMP PREEMPT Wed Jul 24 23:25:44 KST 2019
+#1 SMP PREEMPT Sat Nov 14 11:06:28 CST 2020
+#1 SMP PREEMPT Thu Mar 14 14:49:26 BRT 2019
+#1 SMP PREEMPT Tue Apr 20 17:32:28 KST 2021
+#1 SMP PREEMPT Mon Jun 22 21:33:26 KST 2020
+#1 SMP PREEMPT Fri Mar 6 11:22:22 CST 2020
+#1 SMP PREEMPT Fri Feb 28 20:54:38 WIB 2020
+#2 SMP PREEMPT Wed Jan 31 17:06:31 CST 2018
+#1 SMP PREEMPT Fri Dec 27 17:02:58 KST 2019
+#1 SMP PREEMPT Thu Dec 20 01:58:12 JST 2018
+#1 SMP PREEMPT Mon Mar 16 14:43:17 CST 2020
+#1 SMP PREEMPT Thu Mar 18 10:11:38 HKT 2021
+#1 SMP PREEMPT Wed Jun 13 22:20:45 PDT 2018
+#1 SMP PREEMPT Tue Dec 26 18:38:02 KST 2017
+#1 SMP PREEMPT Mon Feb 22 20:12:42 CST 2021
+#1 SMP PREEMPT Wed Mar 6 21:47:54 CST 2019
+#1 SMP PREEMPT Fri Aug 17 17:39:46 CST 2018
+#1 SMP PREEMPT Thu Jul 1 02:48:37 JST 2021
+#1 SMP PREEMPT Sat Jun 13 06:49:51 CST 2020
+#1 SMP PREEMPT Mon Dec 20 20:45:26 CST 2021
+#1 SMP PREEMPT Tue Aug 18 00:17:11 KST 2020
+#1 SMP PREEMPT Fri Dec 31 15:38:27 CST 2021
+#1 SMP PREEMPT Fri Apr 27 22:46:36 IST 2018
+#1 SMP PREEMPT Thu Feb 14 20:16:25 CST 2019
+#1 SMP PREEMPT Mon Dec 27 13:57:17 CST 2021
+#1 SMP PREEMPT Sat May 9 12:51:19 CST 2020
+#1 SMP PREEMPT Wed Jul 4 22:45:14 CST 2018
+#1 SMP PREEMPT Fri Oct 9 13:20:11 CDT 2020
+#1 SMP PREEMPT Wed Nov 7 18:53:56 KST 2018
+#1 SMP PREEMPT Fri Oct 29 01:03:46 CST 2021
+#1 SMP PREEMPT Fri Jun 4 18:48:06 CDT 2021
+#1 SMP PREEMPT Tue Dec 25 01:30:00 CST 2018
+#1 SMP PREEMPT Tue Oct 6 01:40:52 CST 2020
+#1 SMP PREEMPT Wed Mar 6 00:02:16 PST 2019
+#1 SMP PREEMPT Thu Nov 26 14:15:09 CST 2020
+#1 SMP PREEMPT Tue Dec 18 17:21:21 CST 2018
+#1 SMP PREEMPT Mon Jan 18 16:04:56 CST 2021
+#1 SMP PREEMPT Wed Oct 24 11:48:34 EDT 2018
+#1 SMP PREEMPT Wed Dec 15 18:10:32 CST 2021
+#1 SMP PREEMPT Wed Jun 20 21:55:31 PDT 2018
+#1 SMP PREEMPT Tue May 28 14:20:28 CST 2019
+#2 SMP PREEMPT Mon Mar 9 11:48:20 CST 2020
+#1 SMP PREEMPT Wed Jun 17 03:29:26 CST 2020
+#2 SMP PREEMPT Tue Jul 9 07:09:14 CST 2019
+#1 SMP PREEMPT Tue Jun 11 08:31:19 -03 2019
+#1 SMP PREEMPT Thu Aug 20 23:28:40 CST 2020
+#1 SMP PREEMPT Wed May 18 12:49:40 CST 2022
+#1 SMP PREEMPT Wed Mar 7 01:54:27 CST 2018
+#1 SMP PREEMPT Tue Jan 26 22:41:23 PST 2021
+#1 SMP PREEMPT Mon Mar 14 20:27:06 PDT 2022
+#1 SMP PREEMPT Thu Dec 6 21:25:27 CST 2018
+#1 SMP PREEMPT Wed Mar 24 17:50:49 KST 2021
+#1 SMP PREEMPT Fri Feb 2 07:19:17 BRST 2018
+#1 SMP PREEMPT Mon Oct 26 11:27:10 CST 2020
+#1 SMP PREEMPT Fri Oct 12 21:53:02 CST 2018
+#1 SMP PREEMPT Fri Apr 20 12:02:55 CST 2018
+#1 SMP PREEMPT Sat Dec 7 02:20:34 CST 2019
+#1 SMP PREEMPT Sat Feb 3 17:08:22 CST 2018
+#6 SMP Fri Jul 5 16:37:54 CEST 2019
+#1 SMP PREEMPT Thu Sep 12 22:35:49 CST 2019
+#2 SMP PREEMPT Sun Mar 31 13:49:40 CST 2019
+#1 SMP PREEMPT Fri Aug 30 19:31:56 -03 2019
+#1 SMP PREEMPT Fri Sep 25 16:50:20 CST 2020
+#1 SMP PREEMPT Wed Jan 16 11:30:17 KST 2019
+#1 SMP PREEMPT Wed May 9 04:22:25 CST 2018
+#1 SMP PREEMPT Mon Oct 22 17:43:44 KST 2018
+#1 SMP PREEMPT Sat Oct 10 03:34:16 CDT 2020
+#1 SMP PREEMPT Mon Dec 2 20:42:07 KST 2019
+#1 SMP PREEMPT Mon Apr 6 14:58:10 JST 2020
+#1 SMP PREEMPT Fri Jul 5 12:27:43 IST 2019
+#1 SMP PREEMPT Wed Sep 5 03:59:40 CST 2018
+#1 SMP PREEMPT Thu Mar 3 12:37:54 CST 2022
+#1 SMP PREEMPT Thu Oct 15 15:50:29 HKT 2020
+#1 SMP PREEMPT Thu May 27 15:56:01 KST 2021
+#1 SMP PREEMPT Tue Feb 15 04:05:02 CST 2022
+#1 SMP PREEMPT Fri Nov 5 16:22:12 CST 2021
+#1 SMP PREEMPT Tue May 12 12:33:42 CST 2020
+#1 SMP PREEMPT Thu Jun 7 23:09:09 CST 2018
+#1 SMP PREEMPT Mon Sep 21 12:33:20 CST 2020
+#1 SMP PREEMPT Thu Aug 2 19:58:52 2018
+#1 SMP PREEMPT Thu Dec 9 17:02:35 CST 2021
+#2 SMP PREEMPT Thu Dec 5 20:44:16 CST 2019
+#1 SMP PREEMPT Sat Nov 24 02:38:32 CST 2018
+#1 SMP PREEMPT Tue Jul 7 02:16:36 PDT 2020
+#1 SMP PREEMPT Tue Jul 21 12:46:25 CST 2020
+#1 SMP PREEMPT Wed Mar 9 19:15:39 PST 2022
+#2 SMP Tue Oct 23 12:26:57 KST 2018
+#1 SMP PREEMPT Fri Jul 10 03:51:06 IST 2020
+#1 SMP PREEMPT Mon Apr 20 15:35:20 CST 2020
+#1 SMP PREEMPT Sat Nov 14 08:59:58 CST 2020
+#1 SMP PREEMPT Fri Mar 5 05:47:44 PST 2021
+#1 SMP PREEMPT Tue Dec 10 20:46:53 CST 2019
+#1 SMP PREEMPT Fri May 25 07:52:12 IST 2018
+#1 SMP PREEMPT Thu Nov 21 15:57:40 KST 2019
+#1 SMP PREEMPT Mon Mar 1 17:24:06 CST 2021
+#1 SMP PREEMPT Tue Jul 6 09:12:31 CDT 2021
+#1 SMP PREEMPT Thu Jan 24 15:16:30 2019
+#1 SMP PREEMPT Wed Apr 17 18:29:19 CST 2019
+#2 SMP PREEMPT Thu Jul 4 16:52:54 CST 2019
+#1 SMP PREEMPT Mon Apr 13 22:15:44 EDT 2020
+#1 SMP PREEMPT Mon Jan 13 20:14:07 CST 2020
+#1 SMP PREEMPT Thu Mar 18 17:40:47 CST 2021
+#2 SMP PREEMPT Sun Sep 29 21:00:30 CST 2019
+#2 SMP PREEMPT Sun Nov 8 16:44:19 KST 2020
+#1 SMP PREEMPT Fri Apr 8 05:08:54 UTC 2022
+#1 SMP PREEMPT Tue Dec 10 21:43:59 CST 2019
+#1 SMP PREEMPT Tue Apr 13 16:17:58 JST 2021
+#1 SMP PREEMPT Wed Mar 24 16:08:47 CST 2021
+#1 SMP PREEMPT Mon Oct 15 05:17:00 CST 2018
+#1 SMP PREEMPT Mon Dec 14 14:43:29 CST 2020
+#2 SMP PREEMPT Tue Dec 22 14:20:16 KST 2020
+#1 SMP PREEMPT Wed Mar 28 15:45:28 CST 2018
+#1 SMP PREEMPT Tue Aug 7 16:50:51 CST 2018
+#1 SMP PREEMPT Thu Jul 15 00:32:25 CST 2021
+#1 SMP PREEMPT Tue Apr 27 21:41:31 CST 2021
+#1 SMP PREEMPT Thu Aug 1 07:00:36 CST 2019
+#1 SMP PREEMPT Thu Aug 9 04:57:32 KST 2018
+#1 SMP PREEMPT Mon Apr 19 20:15:03 PDT 2021
+#2 SMP PREEMPT Sun Jul 21 09:14:35 CST 2019
+#1 SMP PREEMPT Tue Aug 4 23:38:15 KST 2020
+#1 SMP PREEMPT Sun Oct 25 19:17:59 CST 2020
+#2 SMP PREEMPT Wed Oct 23 17:30:26 CST 2019
+#2 SMP PREEMPT Thu Nov 8 14:32:21 CST 2018
+#1 SMP PREEMPT Mon Aug 20 16:49:35 CST 2018
+#1 SMP PREEMPT Wed Oct 21 19:18:09 CST 2020
+#1 SMP PREEMPT Tue Jul 13 11:06:43 HKT 2021
+#2 SMP PREEMPT Thu Feb 17 18:11:59 CST 2022
+#15 SMP PREEMPT Sat Oct 10 20:15:10 CST 2020
+#1 SMP PREEMPT Fri Nov 26 23:32:10 CST 2021
+#1 SMP PREEMPT Wed Jan 5 17:06:18 UTC 2022
+#6 SMP PREEMPT Thu Mar 28 23:14:32 CST 2019
+#1 SMP PREEMPT Thu Dec 30 21:37:12 CST 2021
+#2 SMP PREEMPT Thu Jul 23 22:30:06 CST 2020
+#1 SMP PREEMPT Wed Aug 1 10:45:26 CST 2018
+#117 SMP PREEMPT Mon Mar 4 14:57:35 CST 2019
+#1 SMP PREEMPT Fri Jan 17 01:22:05 2020
+#2 SMP PREEMPT Thu Aug 27 19:00:09 CST 2020
+#1 SMP PREEMPT Tue Aug 6 05:50:49 PDT 2019
+#2 SMP Sat Jul 4 14:02:57 UTC 2020
+#2 SMP PREEMPT Wed Mar 14 14:19:14 CST 2018
+#1 SMP PREEMPT Wed Jan 12 02:12:14 CST 2022
+#4 SMP PREEMPT Thu Oct 7 19:36:07 UTC 2021
+#1 SMP PREEMPT Wed Jul 1 00:40:09 CST 2020
+#1 SMP PREEMPT Fri Nov 16 17:14:14 CST 2018
+#1 SMP PREEMPT Sat Apr 23 19:42:11 PDT 2022
+#1 SMP PREEMPT Tue Oct 31 14:47:39 2017
+#1 SMP PREEMPT Tue Mar 15 22:36:41 CST 2022
+#1 SMP PREEMPT Mon Aug 16 19:38:05 CST 2021
+#2 SMP PREEMPT Wed Jan 15 15:44:34 CST 2020
+#1 SMP PREEMPT Fri Feb 15 19:11:44 2019
+#1 SMP PREEMPT Sun Nov 15 04:02:45 JST 2020
+#1 SMP PREEMPT Fri Nov 20 16:04:46 CST 2020
+#1 SMP PREEMPT Tue Jul 23 01:31:38 CST 2019
+#1 SMP PREEMPT Mon Dec 17 02:12:23 CST 2018
+#1 SMP PREEMPT Thu May 5 18:50:39 CST 2022
+#1 SMP PREEMPT Tue Aug 20 20:51:42 WIB 2019
+#1 SMP PREEMPT Tue Aug 20 09:56:42 CST 2019
+#1 SMP PREEMPT Tue Jun 26 12:38:57 CST 2018
+#1 SMP PREEMPT Fri May 7 22:05:24 CST 2021
+#1 SMP PREEMPT Mon Mar 14 11:09:48 WIB 2022
+#2 SMP PREEMPT Thu Jan 13 19:42:22 HKT 2022
+#2 SMP PREEMPT Sun Oct 13 23:21:15 CST 2019
+#1 SMP PREEMPT Mon Jun 8 20:50:35 CST 2020
+#1 SMP PREEMPT Sun Feb 13 21:14:32 CST 2022
+#1 SMP PREEMPT Sat May 22 01:06:32 CST 2021
+#1 SMP PREEMPT Sat Oct 10 22:26:09 CST 2020
+#1 SMP PREEMPT Mon Mar 22 23:52:23 CST 2021
+#1 SMP PREEMPT Thu Aug 2 10:01:33 CEST 2018
+#1 SMP PREEMPT Fri Mar 29 02:19:30 CDT 2019
+#2 SMP PREEMPT Wed Jun 9 20:54:17 KST 2021
+#1 SMP PREEMPT Thu Mar 31 13:10:34 CST 2022
+#1 SMP PREEMPT Mon Jan 20 20:12:51 KST 2020
+#1 SMP PREEMPT Sat Oct 23 05:34:54 JST 2021
+#1 SMP PREEMPT Thu Dec 21 21:29:13 CST 2017
+#1 SMP PREEMPT Wed Jan 2 16:46:55 CST 2019
+#1 SMP PREEMPT Mon Aug 24 19:29:29 KST 2020
+#1 SMP PREEMPT Sat May 15 13:29:51 PDT 2021
+#1 SMP PREEMPT Mon Jun 7 20:59:01 KST 2021
+#1 SMP PREEMPT Tue May 21 11:43:31 CST 2019
+#1 SMP PREEMPT Wed Oct 10 17:50:53 2018
+#2 SMP PREEMPT Sat Mar 7 00:21:20 CST 2020
+#1 SMP PREEMPT Thu Mar 4 14:10:10 CST 2021
+#1 SMP PREEMPT Wed Oct 3 04:06:29 CDT 2018
+#1 SMP PREEMPT Tue Apr 12 20:38:45 CST 2022
+#6 SMP PREEMPT Tue Jun 15 10:17:02 CST 2021
+#1 SMP PREEMPT Thu Mar 11 15:19:41 KST 2021
+#1 SMP PREEMPT Wed May 30 04:01:22 CST 2018
+#1 SMP PREEMPT Tue Aug 21 15:20:42 KST 2018
+#1 SMP PREEMPT Tue Jul 2 17:56:59 CST 2019
+#1 SMP PREEMPT Wed Jun 10 20:44:17 KST 2020
+#1 SMP PREEMPT Sun Apr 29 22:22:48 PDT 2018
+#1 SMP PREEMPT Tue Apr 30 10:20:38 CST 2019
+#1 SMP PREEMPT Sat Apr 9 01:25:21 CST 2022
+#2 SMP PREEMPT Fri Mar 9 11:32:23 CST 2018
+#1 SMP PREEMPT Mon Jul 27 18:48:23 KST 2020
+#1 SMP PREEMPT Fri Feb 11 19:58:02 CST 2022
+#1 SMP PREEMPT Mon Jan 4 18:25:53 CST 2021
+#1 SMP PREEMPT Tue Nov 16 14:06:23 CST 2021
+#1 SMP PREEMPT Tue Apr 14 16:50:31 CST 2020
+#1 SMP PREEMPT Tue Apr 14 20:01:41 2020
+#29 SMP PREEMPT Tue Nov 9 12:17:45 CST 2021
+#1 SMP PREEMPT Fri Feb 25 19:15:11 JST 2022
+#1 SMP PREEMPT Mon Mar 8 11:04:31 CST 2021
+#1 SMP PREEMPT Tue May 21 14:43:07 CST 2019
+#3 SMP PREEMPT Tue Feb 22 21:09:42 CST 2022
+#2 SMP PREEMPT Sat Jul 6 07:15:51 CST 2019
+#1 SMP PREEMPT Tue Mar 19 01:32:31 CST 2019
+#1 SMP PREEMPT Mon Feb 12 12:59:22 CST 2018
+#1 SMP PREEMPT Thu Apr 22 11:19:20 KST 2021
+#1 SMP PREEMPT Sun Feb 21 20:23:03 PST 2021
+#8 SMP PREEMPT Tue May 19 18:34:12 CST 2020
+#1 SMP PREEMPT Thu Aug 13 17:50:25 KST 2020
+#1 SMP PREEMPT Fri Jul 30 17:36:00 +07 2021
+#4 SMP PREEMPT Wed Jan 19 14:07:05 UTC 2022
+#1 SMP PREEMPT Tue Oct 9 03:40:59 CST 2018
+#1 SMP PREEMPT Wed Mar 11 19:24:17 CST 2020
+#1 SMP PREEMPT Tue Mar 29 21:37:39 CST 2022
+#1 SMP PREEMPT Wed Mar 16 23:12:53 CST 2022
+#1 SMP PREEMPT Wed Mar 20 11:08:52 CST 2019
+#1 SMP PREEMPT Fri Dec 24 21:23:41 CST 2021
+#1 SMP PREEMPT Mon Mar 8 16:37:21 KST 2021
+#4 SMP PREEMPT Fri Jan 8 20:52:08 CST 2021
+#1 SMP PREEMPT Fri May 15 00:20:56 CST 2020
+#1 SMP PREEMPT Tue Aug 20 12:03:21 KST 2019
+#1 SMP PREEMPT Fri Feb 12 04:07:16 UTC 2021
+#1 SMP PREEMPT Tue Apr 19 19:36:31 PDT 2022
+#1 SMP PREEMPT Sun May 12 21:39:47 PDT 2019
+#1 SMP PREEMPT Sun Jan 23 14:08:38 CST 2022
+#1 SMP PREEMPT Wed Dec 2 06:07:43 JST 2020
+#1 SMP PREEMPT Thu Jun 3 02:04:13 CST 2021
+#1 SMP PREEMPT Tue Nov 17 16:55:53 CST 2020
+#1 SMP PREEMPT Thu Apr 4 03:27:44 JST 2019
+#1 SMP PREEMPT Tue Sep 14 23:36:40 CST 2021
+#1 SMP PREEMPT Tue Jul 13 13:22:23 CST 2021
+#1 SMP PREEMPT Thu Apr 25 19:25:51 KST 2019
+#1 SMP PREEMPT Sat May 16 03:12:44 CST 2020
+#2 SMP Mon Jan 6 16:59:04 IST 2020
+#1 SMP PREEMPT Tue Jul 7 01:45:31 PDT 2020
+#1 SMP PREEMPT Tue Apr 7 18:14:11 CST 2020
+#1 SMP PREEMPT Mon Nov 9 17:25:56 CST 2020
+#1 SMP PREEMPT Tue May 22 20:20:50 CST 2018
+#1 SMP PREEMPT Fri Mar 9 23:16:13 KST 2018
+#2 SMP PREEMPT Fri Sep 17 23:51:06 CST 2021
+#1 SMP PREEMPT Mon Oct 7 20:55:19 KST 2019
+#1 SMP PREEMPT Sun Aug 11 21:57:28 CST 2019
+#1 SMP PREEMPT Tue Apr 24 16:45:45 2018
+#2 SMP PREEMPT Tue Jun 25 21:18:29 KST 2019
+#1 SMP PREEMPT Fri Mar 5 21:28:42 CST 2021
+#1 SMP PREEMPT Mon Dec 23 12:54:01 CST 2019
+#1 SMP PREEMPT Thu Mar 11 05:03:53 CST 2021
+#4 SMP PREEMPT Fri Feb 18 17:39:53 UTC 2022
+#1 SMP PREEMPT Mon Dec 3 13:53:28 CST 2018
+#2 SMP PREEMPT Thu Mar 8 11:03:00 CST 2018
+#1 SMP PREEMPT Mon Oct 22 20:17:51 CST 2018
+#1 SMP PREEMPT Fri Mar 15 16:50:42 CST 2019
+#1 SMP PREEMPT Mon Jul 2 13:20:37 KST 2018
+#1 SMP PREEMPT Tue Jul 10 11:52:56 CST 2018
+#1 SMP PREEMPT Tue Apr 28 12:05:50 CST 2020
+#1 SMP PREEMPT Fri May 8 20:00:53 KST 2020
+#1 SMP PREEMPT Tue Nov 26 01:54:49 CST 2019
+#2 SMP PREEMPT Tue Mar 31 19:38:29 CST 2020
+#2 SMP PREEMPT Thu Jan 28 19:55:26 CST 2021
+#1 SMP PREEMPT Tue Aug 6 00:16:12 CST 2019
+#1 SMP PREEMPT Thu Apr 8 14:01:35 CST 2021
+#1 SMP PREEMPT Fri Mar 19 15:45:05 CST 2021
+#2 SMP PREEMPT Thu Dec 19 13:51:15 CST 2019
+#1 SMP PREEMPT Fri Feb 28 22:33:36 CST 2020
+#1 SMP PREEMPT Sun Aug 19 20:16:15 CST 2018
+#2 SMP PREEMPT Tue Dec 24 18:22:13 CST 2019
+#1 SMP PREEMPT Fri Dec 4 12:31:50 +07 2020
+#1 SMP PREEMPT Wed Aug 18 16:33:05 KST 2021
+#2 SMP PREEMPT Tue Feb 25 15:34:50 KST 2020
+#1 SMP PREEMPT Mon Jan 18 14:15:21 JST 2021
+#2 SMP PREEMPT Fri Nov 1 12:40:10 KST 2019
+#1 SMP PREEMPT Mon Jan 10 05:55:26 CST 2022
+#1 SMP PREEMPT Wed Aug 14 10:12:25 KST 2019
+#1 SMP PREEMPT Fri Feb 25 19:14:39 JST 2022
+#1 SMP PREEMPT Mon Sep 6 22:10:58 CST 2021
+#1 SMP PREEMPT Thu Aug 15 13:03:52 CST 2019
+#1 SMP PREEMPT Fri Apr 23 15:10:36 KST 2021
+#1 SMP PREEMPT Sat Jul 3 15:37:27 CST 2021
+#2 SMP PREEMPT Thu Jun 13 21:11:49 CST 2019
+#2 SMP PREEMPT Tue Apr 14 23:46:58 CST 2020
+#1 SMP PREEMPT Wed Jul 17 23:39:36 KST 2019
+#1 SMP PREEMPT Sat Feb 29 07:14:03 KST 2020
+#1 SMP PREEMPT Mon Nov 5 01:20:04 CST 2018
+#1 SMP PREEMPT Wed Dec 8 13:09:36 CST 2021
+#2 SMP PREEMPT Mon May 27 18:53:00 CST 2019
+#1 SMP PREEMPT Wed Feb 3 13:25:24 IST 2021
+#30 SMP PREEMPT Tue Sep 17 22:14:46 CST 2019
+#1 SMP PREEMPT Sun Jan 13 03:51:55 CST 2019
+#1 SMP PREEMPT Tue Dec 1 02:15:54 IST 2020
+#1 SMP PREEMPT Sat Apr 4 09:28:11 CDT 2020
+#1 SMP PREEMPT Fri Dec 4 19:43:31 KST 2020
+#1 SMP PREEMPT Tue May 1 12:54:38 KST 2018
+#1 SMP PREEMPT Fri Apr 8 14:39:27 CST 2022
+#1 SMP PREEMPT Fri May 31 14:26:02 CST 2019
+#1 SMP PREEMPT Wed Apr 22 21:43:18 KST 2020
+#1 SMP PREEMPT Fri Nov 10 18:10:11 UTC 2017
+#1 SMP PREEMPT Tue Sep 4 09:23:39 CST 2018
+#1 SMP PREEMPT Fri Feb 5 13:57:47 CST 2021
+#1 SMP PREEMPT Thu Sep 13 11:33:25 CST 2018
+#1 SMP PREEMPT Wed Jun 12 23:38:50 PDT 2019
+#1 SMP PREEMPT Mon Jul 19 18:55:12 CST 2021
+#1 SMP PREEMPT Fri Apr 8 03:31:23 JST 2022
+#1 SMP PREEMPT Sun Jul 5 17:54:58 CDT 2020
+#1 SMP PREEMPT Tue Jun 5 16:49:32 KST 2018
+#1 SMP PREEMPT Mon Nov 9 20:17:49 CST 2020
+#1 SMP PREEMPT Fri Jul 17 08:09:10 UTC 2020
+#1 SMP PREEMPT Thu Jul 11 14:43:45 IST 2019
+#2 SMP PREEMPT Mon Jun 22 14:03:47 CST 2020
+#1 SMP PREEMPT Tue Dec 3 22:36:37 CST 2019
+#1 SMP PREEMPT Wed Jul 15 11:02:11 CST 2020
+#1 SMP PREEMPT Mon Jan 11 22:51:07 CST 2021
+#1 SMP PREEMPT Tue Dec 5 10:37:36 JST 2017
+#1 SMP PREEMPT Wed Jun 20 17:01:04 CST 2018
+#1 SMP PREEMPT Tue Oct 9 16:23:23 CST 2018
+#1 SMP PREEMPT Mon Jul 27 16:49:24 KST 2020
+#1 SMP PREEMPT Thu Dec 16 10:44:22 PST 2021
+#1 SMP PREEMPT Fri Nov 1 11:51:21 KST 2019
+#1 SMP PREEMPT Tue Feb 13 16:29:27 CST 2018
+#1 SMP PREEMPT Tue Apr 27 00:52:06 CST 2021
+#1 SMP PREEMPT Tue Aug 13 20:47:44 CST 2019
+#1 SMP PREEMPT Mon May 10 01:15:56 UTC 2021
+#2 SMP PREEMPT Wed Mar 9 15:00:16 CST 2022
+#1 SMP PREEMPT Thu Oct 15 11:10:14 KST 2020
+#1 SMP PREEMPT Thu Jan 31 11:00:32 CST 2019
+#1 SMP PREEMPT Thu Oct 29 07:46:31 UTC 2020
+#2 SMP PREEMPT Sat Jan 12 12:01:15 KST 2019
+#1 SMP PREEMPT Thu May 9 21:08:01 CST 2019
+#1 SMP PREEMPT Mon Jun 8 02:42:21 PDT 2020
+#1 SMP PREEMPT Mon Apr 25 21:58:03 PDT 2022
+#2 SMP PREEMPT Thu Oct 18 17:13:48 -03 2018
+#2 SMP PREEMPT Thu Nov 18 17:23:08 CST 2021
+#1 SMP PREEMPT Tue Mar 6 20:33:03 CST 2018
+#2 SMP PREEMPT Sat Mar 16 10:36:14 CST 2019
+#1 SMP PREEMPT Thu Nov 18 08:44:26 CST 2021
+#1 SMP PREEMPT Tue May 3 00:00:55 KST 2022
+#1 SMP PREEMPT Mon Mar 26 09:16:07 CDT 2018
+#1 SMP PREEMPT Tue Dec 3 16:43:35 CST 2019
+#1 SMP PREEMPT Mon Nov 11 17:21:41 CST 2019
+#2 SMP PREEMPT Wed Jul 29 17:43:47 KST 2020
+#1 SMP PREEMPT Mon Oct 26 11:27:02 CST 2020
+#1 SMP PREEMPT Thu May 5 09:32:24 CST 2022
+#1 SMP PREEMPT Sun Mar 13 06:41:39 CST 2022
+#1 SMP PREEMPT Wed Jul 25 23:35:04 PDT 2018
+#1 SMP PREEMPT Tue Jun 22 08:50:23 KST 2021
+#1 SMP PREEMPT Sat Sep 25 21:25:20 CST 2021
+#1 SMP PREEMPT Mon Jul 26 17:18:29 KST 2021
+#1 SMP PREEMPT Thu Dec 19 11:13:28 HKT 2019
+#1 SMP PREEMPT Sat Mar 12 20:10:49 CST 2022
+#1 SMP PREEMPT Thu Nov 7 15:38:37 CST 2019
+#2 SMP Mon Jul 6 11:37:22 IST 2020
+#1 SMP PREEMPT Thu Mar 7 17:58:42 CST 2019
+#1 SMP PREEMPT Fri Jan 8 16:07:54 CST 2021
+#1 SMP PREEMPT Sun Apr 14 21:52:45 PDT 2019
+#1 SMP PREEMPT Fri Sep 21 17:27:34 CST 2018
+#1 SMP PREEMPT Thu Dec 3 03:56:21 CST 2020
+#1 SMP PREEMPT Thu Nov 5 22:37:21 KST 2020
+#2 SMP PREEMPT Thu Mar 21 08:41:48 +03 2019
+#1 SMP PREEMPT Tue Mar 29 19:55:57 CST 2022
+#25 SMP PREEMPT Tue Nov 19 09:54:11 CST 2019
+#1 SMP PREEMPT Fri Feb 18 05:52:00 CST 2022
+#1 SMP PREEMPT Sat Sep 1 17:34:05 CST 2018
+#2 SMP PREEMPT Fri Mar 11 01:23:39 CST 2022
+#1 SMP PREEMPT Thu Feb 17 08:18:31 CST 2022
+#1 SMP PREEMPT Thu Jun 11 20:44:15 CST 2020
+#1 SMP PREEMPT Wed Jul 31 04:47:41 CST 2019
+#1 SMP PREEMPT Wed Sep 1 15:39:25 CST 2021
+#1 SMP PREEMPT Wed Sep 1 09:31:21 UTC 2021
+#1 SMP PREEMPT Wed Mar 9 12:06:37 CST 2022
+#1 SMP PREEMPT Mon Aug 6 10:51:22 CST 2018
+#1 SMP PREEMPT Thu Dec 7 14:01:22 CST 2017
+#1 SMP PREEMPT Tue Nov 16 16:42:20 CST 2021
+#2 SMP PREEMPT Mon Jul 27 11:48:05 CST 2020
+#4 SMP PREEMPT Fri Jan 10 11:49:23 CST 2020
+#1 SMP PREEMPT Thu Apr 23 08:59:48 GMT 2020
+#1 SMP PREEMPT Thu Oct 17 13:43:31 KST 2019
+#1 SMP PREEMPT Fri Aug 14 18:37:08 CDT 2020
+#2 SMP PREEMPT Thu Aug 5 21:34:26 KST 2021
+#1 SMP PREEMPT Sat Jul 27 13:13:31 CST 2019
+#1 SMP PREEMPT Tue Apr 26 01:26:07 CST 2022
+#1 SMP PREEMPT Wed Apr 14 23:39:41 CST 2021
+#1 SMP PREEMPT Thu May 26 18:57:48 CST 2022
+#1 SMP PREEMPT Thu May 9 01:16:28 KST 2019
+#1 SMP PREEMPT Wed Apr 6 19:26:00 CST 2022
+#36 SMP PREEMPT Fri May 3 19:58:41 KST 2019
+#1 SMP PREEMPT Thu Mar 10 18:46:19 UTC 2022
+#2 SMP PREEMPT Mon Feb 21 15:11:10 CST 2022
+#1 SMP PREEMPT Sun Feb 28 16:51:13 CST 2021
+#1 SMP PREEMPT Tue Mar 12 16:13:07 CST 2019
+#1 SMP PREEMPT Fri May 20 15:19:57 IST 2022
+#1 SMP PREEMPT Wed Jun 5 00:34:18 PDT 2019
+#88 SMP PREEMPT Fri Oct 11 12:43:45 CST 2019
+#1 SMP PREEMPT Fri Oct 15 10:40:03 CST 2021
+#1 SMP PREEMPT Sat Mar 24 15:58:30 CST 2018
+#1 SMP PREEMPT Wed May 11 17:52:45 CST 2022
+#1 SMP PREEMPT Tue Apr 3 20:09:44 KST 2018
+#1 SMP PREEMPT Fri Apr 8 22:05:44 KST 2022
+#1 SMP PREEMPT Mon Dec 28 12:49:50 KST 2020
+#1 SMP PREEMPT Wed Apr 17 12:21:46 CST 2019
+#1 SMP PREEMPT Thu Apr 29 12:05:07 KST 2021
+#1 SMP PREEMPT Fri Dec 29 14:21:02 CST 2017
+#2 SMP PREEMPT Tue Jul 16 11:23:59 CST 2019
+#1 SMP PREEMPT Tue Apr 30 12:22:27 CST 2019
+#2 SMP PREEMPT Tue Oct 6 12:24:25 KST 2020
+#1 SMP PREEMPT Wed Apr 11 10:13:09 CST 2018
+#1 SMP PREEMPT Wed Jan 12 04:40:33 CST 2022
+#1 SMP PREEMPT Tue Dec 15 17:33:51 CST 2020
+#1 SMP PREEMPT Tue Apr 16 22:52:33 CST 2019
+#2 SMP PREEMPT Wed Dec 18 11:42:09 CST 2019
+#1 SMP PREEMPT Thu May 19 12:27:41 PDT 2022
+#1 SMP PREEMPT Thu Aug 16 19:32:29 CST 2018
+#1 SMP PREEMPT Thu Jul 5 17:53:34 CST 2018
+#1 SMP PREEMPT Wed Apr 14 12:45:04 CST 2021
+#1 SMP PREEMPT Fri Mar 19 20:03:04 CST 2021
+#1 SMP PREEMPT Tue Dec 12 10:38:18 JST 2017
+#1 SMP PREEMPT Mon Mar 22 18:34:58 CST 2021
+#2 SMP PREEMPT Wed Dec 18 15:55:15 KST 2019
+#2 SMP PREEMPT Sat Sep 5 14:57:26 KST 2020
+#1 SMP PREEMPT Wed Sep 25 15:20:47 CST 2019
+#1 SMP PREEMPT Mon Jul 6 20:03:24 CST 2020
+#1 SMP PREEMPT Tue Jul 6 19:04:30 CST 2021
+#1 SMP PREEMPT Thu Jan 16 10:58:06 CST 2020
+#2 SMP PREEMPT Thu Mar 12 14:50:31 CST 2020
+#2 SMP PREEMPT Wed Feb 12 16:34:59 CST 2020
+#1 SMP PREEMPT Mon Jun 25 01:41:32 JST 2018
+#2 SMP PREEMPT Mon Jun 1 03:31:20 CST 2020
+#1 SMP PREEMPT Fri Sep 10 02:20:38 JST 2021
+#1 SMP PREEMPT Mon Apr 27 18:28:54 CST 2020
+#1 SMP PREEMPT Fri Mar 19 18:54:33 CST 2021
+#1 SMP PREEMPT Tue Jan 25 16:16:13 CST 2022
+#1 SMP PREEMPT Thu Mar 12 15:52:54 -03 2020
+#1 SMP PREEMPT Wed Mar 20 10:37:03 CST 2019
+#1 SMP PREEMPT Fri Aug 23 15:22:00 KST 2019
+#1 SMP PREEMPT Sun Sep 29 23:33:17 PDT 2019
+#1 SMP PREEMPT Wed Jun 12 17:26:03 CST 2019
+#1 SMP PREEMPT Fri Mar 18 10:58:08 CST 2022
+#1 SMP PREEMPT Mon Nov 26 16:15:41 CST 2018
+#1 SMP PREEMPT Thu Apr 12 13:14:26 CST 2018
+#1 SMP PREEMPT Sun May 19 18:47:20 CST 2019
+#1 SMP PREEMPT Mon Apr 16 21:28:07 CST 2018
+#1 SMP PREEMPT Wed Mar 9 13:58:25 CST 2022
+#1 SMP PREEMPT Mon Dec 4 06:10:21 CST 2017
+#1 SMP PREEMPT Thu Apr 30 21:00:44 CDT 2020
+#1 SMP PREEMPT Mon Mar 12 19:03:35 CST 2018
+#1 SMP PREEMPT Sun Feb 6 22:12:35 CST 2022
+#1 SMP PREEMPT Tue Jan 25 21:40:07 CST 2022
+#1 SMP PREEMPT Wed Jan 24 14:14:51 KST 2018
+#1 SMP PREEMPT Fri Mar 29 23:14:59 CST 2019
+#1 SMP PREEMPT Mon Dec 2 11:46:02 KST 2019
+#1 SMP PREEMPT Sat Oct 19 17:14:21 CST 2019
+#12 SMP PREEMPT Tue Aug 28 16:09:35 CST 2018
+#1 SMP PREEMPT Tue Sep 24 18:59:02 CST 2019
+#1 SMP PREEMPT Wed Jun 5 05:49:08 KST 2019
+#1 SMP PREEMPT Sat Jan 9 01:43:19 IST 2021
+#1 SMP PREEMPT Thu Mar 29 13:17:47 CST 2018
+#1 SMP PREEMPT Tue Jun 23 01:30:10 CST 2020
+#1 SMP PREEMPT Tue May 12 17:16:10 KST 2020
+#1 SMP PREEMPT Thu Apr 23 11:08:05 GMT 2020
+#1 SMP PREEMPT Tue Oct 9 00:30:44 CST 2018
+#1 SMP PREEMPT Fri Nov 6 01:14:10 IST 2020
+#1 SMP PREEMPT Sun Sep 19 19:40:04 CST 2021
+#1 SMP PREEMPT Mon May 21 01:34:01 PDT 2018
+#1 SMP PREEMPT Mon May 31 10:49:10 CST 2021
+#1 SMP PREEMPT Fri Jul 5 17:51:10 KST 2019
+#1 SMP PREEMPT Sun Dec 20 11:26:56 CST 2020
+#1 SMP PREEMPT Fri Mar 11 00:10:54 CST 2022
+#1 SMP PREEMPT Mon Jun 22 21:37:05 KST 2020
+#1 SMP PREEMPT Wed Oct 14 14:21:53 CST 2020
+#1 SMP PREEMPT Fri Mar 8 16:05:03 KST 2019
+#1 SMP PREEMPT Tue Sep 28 19:22:49 CST 2021
+#1 SMP PREEMPT Fri Jun 29 19:39:24 CST 2018
+#1 SMP PREEMPT Mon Dec 10 21:50:49 CST 2018
+#1 SMP PREEMPT Wed Mar 20 00:19:35 PDT 2019
+#13 SMP PREEMPT Mon Oct 28 09:08:03 CST 2019
+#1 SMP PREEMPT Tue Jul 3 16:11:51 KST 2018
+#1 SMP PREEMPT Thu Jun 6 19:53:03 CST 2019
+#1 SMP PREEMPT Fri Sep 28 14:01:12 KST 2018
+#1 SMP PREEMPT Wed Dec 18 00:03:03 PST 2019
+#1 SMP PREEMPT Mon Sep 17 10:14:47 CST 2018
+#2 SMP PREEMPT Tue Oct 8 18:30:03 KST 2019
+#1 SMP PREEMPT Tue Feb 6 18:45:27 CST 2018
+#1 SMP PREEMPT Thu Mar 15 09:57:39 CST 2018
+#6 SMP PREEMPT Tue Oct 5 20:03:09 UTC 2021
+#1 SMP PREEMPT Thu Dec 10 14:24:53 CST 2020
+#1 SMP PREEMPT Sun Dec 29 23:23:44 CST 2019
+#1 SMP PREEMPT Thu Sep 2 14:47:14 CST 2021
+#2 SMP PREEMPT Fri Apr 3 21:13:19 CST 2020
+#1 SMP PREEMPT Fri Aug 2 19:05:56 CST 2019
+#1 SMP PREEMPT Tue Apr 5 11:22:01 +07 2022
+#1 SMP PREEMPT Wed Mar 2 22:08:25 CST 2022
+#1 SMP PREEMPT Fri Oct 19 20:45:55 2018
+#1 SMP PREEMPT Thu Oct 31 17:04:21 CST 2019
+#1 SMP PREEMPT Fri Mar 8 14:24:52 -03 2019
+#1 SMP PREEMPT Tue Apr 10 14:56:10 BRT 2018
+#1 SMP PREEMPT Wed May 23 05:20:35 CST 2018
+#1 SMP PREEMPT Wed May 13 05:12:34 CST 2020
+#1 SMP PREEMPT Thu Nov 19 00:32:17 KST 2020
+#1 SMP PREEMPT Fri Mar 20 14:21:57 KST 2020
+#1 SMP PREEMPT Tue Aug 6 00:25:47 PDT 2019
+#1 SMP PREEMPT Mon Apr 29 11:55:00 KST 2019
+#1 SMP PREEMPT Fri Jul 16 16:16:49 KST 2021
+#1 SMP PREEMPT Wed Feb 19 21:13:56 KST 2020
+#1 SMP PREEMPT Sat Apr 2 18:36:50 CST 2022
+#1 SMP PREEMPT Thu Oct 24 18:17:23 WIB 2019
+#1 SMP PREEMPT Sat Apr 2 14:54:08 UTC 2022
+#1 SMP PREEMPT Thu Jan 27 04:45:01 JST 2022
+#1 SMP PREEMPT Thu Jan 25 00:32:07 KST 2018
+#1 SMP PREEMPT Tue Jan 9 12:42:57 CST 2018
+#1 SMP PREEMPT Mon Aug 13 23:27:08 CST 2018
+#2 SMP PREEMPT Fri Jul 10 17:28:41 CST 2020
+#1 SMP PREEMPT Mon May 4 13:09:07 IST 2020
+#2 SMP PREEMPT Tue Jan 11 09:34:32 CST 2022
+#1 SMP PREEMPT Mon Feb 14 03:18:26 JST 2022
+#1 SMP PREEMPT Wed May 25 23:51:37 CST 2022
+#3 SMP PREEMPT Mon Aug 16 19:41:05 CST 2021
+#1 SMP PREEMPT Tue May 26 09:30:08 CST 2020
+#1 SMP PREEMPT Sun Dec 15 06:08:11 EST 2019
+#1 SMP PREEMPT Thu May 9 03:18:06 CST 2019
+#1 SMP PREEMPT Fri Mar 6 16:03:50 CST 2020
+#1 SMP PREEMPT Thu Nov 29 16:14:59 CST 2018
+#1 SMP PREEMPT Wed Aug 26 17:07:49 CST 2020
+#2 SMP PREEMPT Fri Jul 16 08:07:44 UTC 2021
+#1 SMP PREEMPT Tue Mar 6 03:02:16 JST 2018
+#1 SMP PREEMPT Tue Dec 14 20:29:24 CST 2021
+#1 SMP PREEMPT Mon Apr 23 22:19:18 PDT 2018
+#1 SMP PREEMPT Wed Jan 15 00:06:04 PST 2020
+#2 SMP PREEMPT Tue Nov 27 16:14:48 CST 2018
+#1 SMP PREEMPT Tue Oct 30 18:26:24 CST 2018
+#2 SMP PREEMPT Wed Feb 20 00:09:30 CST 2019
+#1 SMP PREEMPT Wed Jul 4 15:12:57 2018
+#1 SMP PREEMPT Sat Dec 19 13:30:35 +07 2020
+#2 SMP PREEMPT Tue Mar 13 14:28:38 CST 2018
+#1 SMP PREEMPT Wed Sep 25 06:16:42 CST 2019
+#1 SMP PREEMPT Thu Feb 13 23:25:33 CST 2020
+#1 SMP PREEMPT Thu May 3 23:55:54 CDT 2018
+#1 SMP PREEMPT Wed Mar 21 13:18:24 CDT 2018
+#1 SMP PREEMPT Fri Jun 8 18:48:30 CST 2018
+#1 SMP PREEMPT Thu Feb 27 22:58:30 CST 2020
+#1 SMP PREEMPT Tue May 26 03:15:01 JST 2020
+#0 SMP PREEMPT Thu Aug 5 07:04:42 UTC 2021
+#1 SMP PREEMPT Fri May 22 10:27:29 CDT 2020
+#2 SMP PREEMPT Tue Oct 20 17:49:47 KST 2020
+#2 SMP PREEMPT Wed Jul 21 12:37:43 KST 2021
+#1 SMP PREEMPT Tue May 18 13:26:13 KST 2021
+#1 SMP PREEMPT Sat Apr 21 14:07:10 CST 2018
+#1 SMP PREEMPT Thu Jun 7 06:02:19 CDT 2018
+#1 SMP PREEMPT Fri Oct 26 16:27:16 CST 2018
+#1 SMP PREEMPT Tue May 17 03:27:33 CST 2022
+#1 SMP PREEMPT Sat Jun 15 20:37:00 CST 2019
+#2 SMP PREEMPT Sat Sep 15 14:14:13 CST 2018
+#1 SMP PREEMPT Sat Apr 2 17:41:00 CST 2022
+#2 SMP PREEMPT Tue Apr 7 14:03:13 CST 2020
+#2 SMP PREEMPT Tue May 28 12:42:21 KST 2019
+#1 SMP PREEMPT Sun Aug 11 22:43:53 UTC 2019
+#1 SMP PREEMPT Wed Apr 1 17:41:17 KST 2020
+#1 SMP PREEMPT Wed Jan 8 02:13:18 CST 2020
+#1 SMP PREEMPT Fri Feb 11 23:16:03 CST 2022
+#1 SMP PREEMPT Sat Jan 19 18:36:48 CST 2019
+#1 SMP PREEMPT Sat Sep 18 18:33:34 CST 2021
+#2 SMP PREEMPT Mon May 14 04:04:16 CST 2018
+#1 SMP PREEMPT Tue Apr 26 22:46:05 CST 2022
+#1 SMP PREEMPT Thu Oct 31 19:40:36 KST 2019
+#1 SMP PREEMPT Tue Jan 7 18:23:32 CST 2020
+#1 SMP PREEMPT Mon Jul 6 19:08:44 CST 2020
+#2 SMP PREEMPT Thu Aug 1 12:31:03 KST 2019
+#2 SMP PREEMPT Sun Jun 21 18:46:03 CST 2020
+#18 SMP PREEMPT Mon Dec 24 15:33:46 CST 2018
+#1 SMP PREEMPT Mon Oct 8 20:02:01 KST 2018
+#1 SMP PREEMPT Tue Oct 15 21:12:41 CST 2019
+#1 SMP PREEMPT Thu Jan 14 19:57:16 PST 2021
+#1 SMP PREEMPT Sat Dec 12 08:24:37 CST 2020
+#1 SMP PREEMPT Wed Mar 17 12:12:00 KST 2021
+#1 SMP PREEMPT Thu Sep 24 17:15:59 CST 2020
+#1 SMP PREEMPT Tue Jan 25 17:03:07 CST 2022
+#1 SMP PREEMPT Fri Apr 10 21:47:24 UTC 2020
+#1 SMP PREEMPT Fri Feb 4 23:39:26 PST 2022
+#1 SMP PREEMPT Sat Jun 19 14:19:22 CST 2021
+#2 SMP PREEMPT Fri Dec 18 19:06:06 CST 2020
+#1 SMP PREEMPT Wed Feb 9 21:59:47 CST 2022
+#1 SMP PREEMPT Wed Oct 23 16:03:34 CST 2019
+#1 SMP PREEMPT Sat Aug 8 19:54:55 CST 2020
+#2 SMP PREEMPT Wed Dec 20 16:06:55 CST 2017
+#1 SMP PREEMPT Sun Mar 4 03:10:06 CST 2018
+#1 SMP PREEMPT Thu Dec 31 16:24:13 CST 2020
+#1 SMP PREEMPT Tue Mar 5 03:41:53 CST 2019
+#1 SMP PREEMPT Fri Sep 10 15:50:18 CST 2021
+#1 SMP PREEMPT Wed Aug 12 17:20:39 CST 2020
+#1 SMP PREEMPT Sun Mar 27 01:04:39 CST 2022
+#1 SMP PREEMPT Thu Jul 16 23:28:09 CST 2020
+#1 SMP PREEMPT Mon Feb 22 18:29:34 CST 2021
+#1 SMP PREEMPT Thu Dec 17 15:15:03 CST 2020
+#1 SMP PREEMPT Tue Apr 7 17:58:57 KST 2020
+#1 SMP PREEMPT Fri Dec 13 15:18:46 KST 2019
+#1 SMP PREEMPT Mon Jan 24 14:13:40 IST 2022
+#2 SMP PREEMPT Fri Jan 8 01:52:57 CST 2021
+#2 SMP PREEMPT Fri Apr 24 02:10:32 CST 2020
+#1 SMP PREEMPT Wed Jul 8 15:24:15 CST 2020
+#1 SMP PREEMPT Fri Jan 29 10:51:26 CST 2021
+#1 SMP PREEMPT Mon Jan 10 20:14:19 CST 2022
+#1 SMP PREEMPT Tue May 22 22:24:24 PDT 2018
+#2 SMP PREEMPT Sat May 30 02:03:39 KST 2020
+#1 SMP PREEMPT Sat Apr 10 21:28:33 CST 2021
+#1 SMP PREEMPT Fri May 31 13:21:52 CDT 2019
+#1 SMP PREEMPT Fri Jun 19 23:29:03 UTC 2020
+#1 SMP PREEMPT Fri Nov 22 16:40:20 CST 2019
+#1 SMP PREEMPT Wed Jul 4 05:54:00 KST 2018
+#1 SMP PREEMPT Wed Apr 17 15:28:19 CST 2019
+#2 SMP PREEMPT Wed Apr 8 18:51:32 CST 2020
+#1 SMP PREEMPT Sat Oct 24 23:42:03 CST 2020
+#1 SMP PREEMPT Mon Sep 27 13:27:59 CST 2021
+#1 SMP PREEMPT Thu Mar 17 13:32:07 CST 2022
+#1 SMP PREEMPT Wed Feb 9 11:27:13 CST 2022
+#1 SMP PREEMPT Thu Jan 20 19:22:01 EST 2022
+#1 SMP PREEMPT Tue Nov 23 22:58:44 CST 2021
+#1 SMP PREEMPT Tue Jan 8 12:30:36 CST 2019
+#1 SMP PREEMPT Fri Mar 29 15:59:57 CST 2019
+#1 SMP PREEMPT Mon Nov 9 18:30:52 CST 2020
+#1 SMP PREEMPT Sun Sep 26 17:29:50 CST 2021
+#1 SMP PREEMPT Fri Oct 12 15:28:23 CST 2018
+#2 SMP PREEMPT Mon Sep 17 21:43:39 CST 2018
+#1 SMP PREEMPT Fri Apr 9 19:35:38 CST 2021
+#1 SMP PREEMPT Tue Oct 13 00:16:22 CST 2020
+#1 SMP PREEMPT Tue Nov 5 13:01:59 CST 2019
+#1 SMP PREEMPT Thu Jan 7 10:24:55 CST 2021
+#1 SMP PREEMPT Mon Jul 5 11:28:12 CDT 2021
+#1 SMP PREEMPT Fri Jun 12 00:59:52 JST 2020
+#1 SMP PREEMPT Fri May 28 15:52:29 CST 2021
+#1 SMP PREEMPT Fri Dec 18 17:24:30 UTC 2020
+#1 SMP PREEMPT Tue Jul 9 23:20:49 CST 2019
+#2 SMP PREEMPT Wed Jul 17 15:12:38 KST 2019
+#1 SMP PREEMPT Mon Oct 28 03:24:50 CST 2019
+#1 SMP PREEMPT Tue Dec 19 12:25:16 CST 2017
+#1 SMP PREEMPT Sun Apr 3 12:04:42 CST 2022
+#1 SMP PREEMPT Wed Oct 10 10:45:29 CST 2018
+#1 SMP PREEMPT Mon Nov 23 16:05:26 CST 2020
+#1 SMP PREEMPT Tue Oct 8 13:42:12 IST 2019
+#2 SMP PREEMPT Thu Jul 2 12:16:00 KST 2020
+#1 SMP PREEMPT Tue May 28 13:53:06 CST 2019
+#1 SMP PREEMPT Mon Oct 5 19:54:29 PDT 2020
+#1 SMP PREEMPT Tue Nov 27 00:05:24 CST 2018
+#1 SMP PREEMPT Fri Sep 21 16:31:19 CST 2018
+#1 SMP PREEMPT Tue Aug 4 12:34:37 CST 2020
+#1 SMP PREEMPT Thu Apr 8 19:36:09 HKT 2021
+#1 SMP PREEMPT Mon Mar 29 15:19:36 CST 2021
+#1 SMP PREEMPT Wed Jul 31 04:47:41 CST 2019
+#1 SMP PREEMPT Tue Mar 13 12:07:58 CST 2018
+#1 SMP PREEMPT Mon Aug 31 17:40:38 CST 2020
+#1 SMP PREEMPT Sat Oct 23 09:36:55 UTC 2021
+#2 SMP PREEMPT Mon Jan 6 16:52:17 CST 2020
+#1 SMP PREEMPT Tue Aug 31 15:06:43 CST 2021
+#1 SMP PREEMPT Thu Nov 1 18:29:02 CST 2018
+#3 SMP PREEMPT Mon Nov 2 14:24:59 CST 2020
+#1 SMP PREEMPT Fri Apr 15 21:33:01 CST 2022
+#1 SMP PREEMPT Tue Aug 25 14:02:38 KST 2020
+#1 SMP PREEMPT Sat Jan 19 00:49:43 IST 2019
+#1 SMP PREEMPT Fri Oct 19 01:42:46 JST 2018
+#1 SMP PREEMPT Fri May 22 08:12:48 CDT 2020
+#2 SMP PREEMPT Wed Jul 22 04:21:10 JST 2020
+#1 SMP PREEMPT Wed Mar 2 23:44:17 CST 2022
+#1 SMP PREEMPT Sun Mar 21 07:01:43 CST 2021
+#1 SMP PREEMPT Mon Jul 13 16:35:04 CST 2020
+#1 SMP PREEMPT Mon Jun 18 15:06:31 KST 2018
+#115 SMP PREEMPT Sat Feb 20 15:58:54 CST 2021
+#2 SMP Fri Apr 30 18:51:33 CEST 2021
+#1 SMP PREEMPT Mon Sep 30 20:07:28 CDT 2019
+#1 SMP PREEMPT Wed May 30 12:03:43 CST 2018
+#6 SMP PREEMPT Wed Dec 22 20:30:02 UTC 2021
+#2 SMP PREEMPT Mon Oct 22 06:06:08 CST 2018
+#1 SMP PREEMPT Tue Aug 24 23:07:16 KST 2021
+#1 SMP PREEMPT Tue Nov 13 14:16:59 CST 2018
+#1 SMP PREEMPT Tue Jul 31 20:00:04 KST 2018
+#1 SMP PREEMPT Tue Feb 26 23:46:19 PST 2019
+#1 SMP PREEMPT Tue May 1 03:44:45 CST 2018
+#1 SMP PREEMPT Sat Apr 28 12:46:56 CST 2018
+#1 SMP PREEMPT Sat Feb 27 18:03:09 IST 2021
+#1 SMP PREEMPT Mon Jul 5 01:25:07 CST 2021
+#2 SMP PREEMPT Fri Feb 18 13:12:45 CST 2022
+#1 SMP PREEMPT Tue Apr 28 17:44:56 KST 2020
+#1 SMP PREEMPT Mon Nov 2 12:05:05 KST 2020
+#6 SMP PREEMPT Tue Sep 1 19:47:43 CST 2020
+#2 SMP PREEMPT Sat Aug 1 21:13:37 CST 2020
+#1 SMP PREEMPT Fri Apr 13 11:08:18 CST 2018
+#1 SMP PREEMPT Thu May 19 15:59:48 CST 2022
+#1 SMP PREEMPT Mon Jan 22 16:35:52 CST 2018
+#1 SMP PREEMPT Fri Jul 26 00:28:03 CST 2019
+#1 SMP PREEMPT Thu Apr 29 12:24:43 CST 2021
+#1 SMP PREEMPT Wed May 20 05:50:17 CST 2020
+#1 SMP PREEMPT Thu May 28 23:48:30 CST 2020
+#1 SMP PREEMPT Mon Oct 28 01:09:34 PDT 2019
+#3 SMP PREEMPT Thu Jun 27 20:32:35 KST 2019
+#1 SMP PREEMPT Tue Mar 8 22:00:17 CST 2022
+#1 SMP PREEMPT Wed Mar 16 18:37:51 CST 2022
+#1 SMP PREEMPT Wed May 16 07:47:31 KST 2018
+#1 SMP PREEMPT Thu Dec 20 18:32:03 KST 2018
+#1 SMP PREEMPT Tue Apr 14 14:47:27 CST 2020
+#1 SMP PREEMPT Mon Dec 13 02:38:32 CST 2021
+#1 SMP PREEMPT Wed Apr 13 10:41:18 CST 2022
+#1 SMP PREEMPT Tue Jan 11 16:25:26 CST 2022
+#1 SMP PREEMPT Wed May 29 01:35:12 PDT 2019
+#1 SMP PREEMPT Thu Jun 4 21:30:43 KST 2020
+#1 SMP PREEMPT Fri Apr 8 18:17:00 CST 2022
+#1 SMP PREEMPT Fri Apr 22 21:54:17 CST 2022
+#1 SMP PREEMPT Wed Aug 8 11:05:16 CST 2018
+#1 SMP PREEMPT Sat Sep 1 05:29:16 CST 2018
+#1 SMP PREEMPT Mon Feb 17 15:26:57 KST 2020
+#1 SMP PREEMPT Fri Jul 3 12:56:01 CST 2020
+#1 SMP PREEMPT Thu Apr 1 13:38:53 CST 2021
+#1 SMP PREEMPT Sat Jul 4 11:36:50 CST 2020
+#1 SMP PREEMPT Sat Sep 21 01:56:18 JST 2019
+#1 SMP PREEMPT Thu Jun 14 16:34:58 CST 2018
+#1 SMP PREEMPT Sat Feb 19 22:15:44 CST 2022
+#1 SMP PREEMPT Wed Jun 30 10:27:58 CST 2021
+#1 SMP PREEMPT Mon May 25 22:17:51 CST 2020
+#1 SMP PREEMPT Mon Mar 4 12:36:41 IST 2019
+#1 SMP PREEMPT Fri Jan 7 22:43:53 CST 2022
+#1 SMP PREEMPT Wed Dec 2 15:49:08 KST 2020
+#2 SMP PREEMPT Sat Oct 12 11:52:44 CST 2019
+#1 SMP PREEMPT Sat May 5 15:22:50 CST 2018
+#1 SMP PREEMPT Wed Dec 4 14:46:50 CST 2019
+#1 SMP PREEMPT Wed Oct 3 22:21:27 CST 2018
+#1 SMP PREEMPT Wed May 6 22:26:06 KST 2020
+#1 SMP PREEMPT Thu Mar 26 01:37:10 JST 2020
+#1 SMP PREEMPT Mon Dec 10 15:22:13 CET 2018
+#1 SMP PREEMPT Wed Jan 22 02:10:43 CST 2020
+#1 SMP PREEMPT Thu Nov 11 21:32:50 CST 2021
+#3 SMP PREEMPT Mon May 16 10:08:01 CST 2022
+#1 SMP PREEMPT Fri Sep 10 03:28:48 CST 2021
+#1 SMP PREEMPT Tue Aug 24 01:56:30 JST 2021
+#1 SMP PREEMPT Wed Mar 4 23:25:42 PST 2020
+#1 SMP PREEMPT Thu Dec 27 04:02:54 CST 2018
+#1 SMP PREEMPT Thu Sep 16 03:49:49 CST 2021
+#1 SMP PREEMPT Fri Jun 28 13:04:38 CST 2019
+#1 SMP PREEMPT Mon Jul 27 12:31:59 CST 2020
+#1 SMP PREEMPT Sat Oct 9 13:38:50 CST 2021
+#1 SMP PREEMPT Mon Jul 5 11:28:12 CDT 2021
+#1 SMP PREEMPT Thu Dec 27 16:28:47 CST 2018
+#1 SMP PREEMPT Fri Apr 3 23:31:36 CST 2020
+#1 SMP PREEMPT Sun Aug 1 13:52:10 CST 2021
+#1 SMP PREEMPT Tue Nov 9 22:57:53 CST 2021
+#1 SMP PREEMPT Tue Nov 9 13:47:59 CST 2021
+#1 SMP PREEMPT Thu Feb 10 17:11:11 CST 2022
+#1 SMP PREEMPT Thu Dec 3 16:34:32 CST 2020
+#1 SMP PREEMPT Fri Nov 26 23:31:28 CST 2021
+#1 SMP PREEMPT Mon Oct 26 04:18:27 CST 2020
+#1 SMP PREEMPT Tue May 7 21:28:47 CST 2019
+#1 SMP PREEMPT Thu Jun 24 16:39:49 KST 2021
+#2 SMP PREEMPT Fri Nov 22 11:04:52 CST 2019
+#1 SMP PREEMPT Wed Jun 30 12:49:56 KST 2021
+#1 SMP PREEMPT Thu Mar 3 03:10:42 PST 2022
+#1 SMP PREEMPT Fri Jan 18 18:19:49 CST 2019
+#1 SMP PREEMPT Fri Dec 28 19:37:43 CST 2018
+#1 SMP PREEMPT Mon May 4 21:25:24 KST 2020
+#1 SMP PREEMPT Wed Feb 13 14:26:30 GMT+2 2019
+#1 SMP PREEMPT Fri Mar 6 06:04:50 CST 2020
+#2 SMP PREEMPT Wed Apr 1 19:21:54 KST 2020
+#1 SMP PREEMPT Wed Dec 12 21:12:15 CST 2018
+#1 SMP PREEMPT Thu May 13 20:09:45 WIB 2021
+#1 SMP PREEMPT Mon Mar 2 16:31:14 CST 2020
+#1 SMP PREEMPT Sat May 22 00:20:28 CST 2021
+#1 SMP PREEMPT Fri Dec 29 19:34:32 CST 2017
+#1 SMP PREEMPT Sun Jul 18 16:36:05 CST 2021
+#2 SMP PREEMPT Wed Oct 31 20:23:22 UTC 2018
+#1 SMP PREEMPT Tue Jan 25 18:46:46 KST 2022
+#1 SMP PREEMPT Fri May 28 15:09:08 CDT 2021
+#6 SMP PREEMPT Thu Dec 2 11:18:33 UTC 2021
+#1 SMP PREEMPT Wed Oct 14 03:36:44 CDT 2020
+#2 SMP PREEMPT Mon Jan 14 20:46:49 CST 2019
+#1 SMP PREEMPT Wed Dec 23 20:48:15 HKT 2020
+#1 SMP PREEMPT Wed Apr 18 19:58:55 CST 2018
+#1 SMP PREEMPT Tue Nov 26 15:53:17 CST 2019
+#1 SMP PREEMPT Tue Nov 13 23:32:43 KST 2018
+#1 SMP PREEMPT Fri Feb 22 17:43:49 CST 2019
+#1 SMP PREEMPT Sat Oct 31 06:25:46 KST 2020
+#26 SMP PREEMPT Tue Aug 6 10:03:50 CST 2019
+#2 SMP Fri Jul 20 11:28:21 KST 2018
+#1 SMP PREEMPT Wed Dec 2 12:13:11 KST 2020
+#1 SMP PREEMPT Thu Mar 5 16:41:43 CST 2020
+#1 SMP PREEMPT Mon May 10 09:31:24 KST 2021
+#1 SMP PREEMPT Wed Mar 7 10:34:30 CST 2018
+#1 SMP PREEMPT Fri Jan 22 00:15:37 CST 2021
+#2 SMP PREEMPT Mon Jul 30 03:15:34 CST 2018
+#1 SMP PREEMPT Mon Oct 22 07:49:35 EDT 2018
+#1 SMP PREEMPT Thu Nov 19 00:28:23 KST 2020
+#2 SMP PREEMPT Fri Feb 22 15:31:55 KST 2019
+#1 SMP PREEMPT Wed Sep 9 00:14:22 PDT 2020
+#1 SMP PREEMPT Thu Jan 31 16:42:51 KST 2019
+#103 SMP PREEMPT Thu May 23 17:44:08 CST 2019
+#1 SMP PREEMPT Tue Dec 12 20:18:20 CST 2017
+#1 SMP PREEMPT Fri Oct 5 15:48:33 KST 2018
+#1 SMP PREEMPT Thu Jul 8 17:00:36 CST 2021
+#1 SMP PREEMPT Mon Jul 19 16:23:08 KST 2021
+#1 SMP PREEMPT Thu Sep 9 00:44:43 CST 2021
+#1 SMP PREEMPT Mon May 11 20:07:58 2020
+#2 SMP PREEMPT Tue Mar 17 15:50:36 CST 2020
+#1 SMP PREEMPT Tue Feb 25 21:20:45 CST 2020
+#1 SMP PREEMPT Wed Nov 14 19:37:37 CST 2018
+#1 SMP PREEMPT Thu Feb 21 11:35:22 WIB 2019
+#1 SMP PREEMPT Fri Mar 11 17:56:36 CST 2022
+#1 SMP PREEMPT Mon Oct 22 03:37:22 CST 2018
+#1 SMP PREEMPT Tue Sep 18 00:43:33 PDT 2018
+#1 SMP PREEMPT Fri Apr 16 19:03:56 CST 2021
+#1 SMP PREEMPT Tue Nov 12 16:35:48 CST 2019
+#1 SMP PREEMPT Tue Aug 6 05:54:54 PDT 2019
+#1 SMP PREEMPT Wed Feb 27 10:56:07 CST 2019
+#2 SMP PREEMPT Fri Dec 14 11:35:41 UTC 2018
+#1 SMP PREEMPT Tue Jan 22 18:23:54 KST 2019
+#1 SMP PREEMPT Tue Jul 20 10:37:33 CST 2021
+#1 SMP PREEMPT Mon Jan 10 06:58:42 UTC 2022
+#1 SMP PREEMPT Fri May 24 13:08:27 CST 2019
+#1 SMP PREEMPT Fri Dec 3 21:55:26 CST 2021
+#1 SMP PREEMPT Wed Jul 24 17:09:38 CST 2019
+#1 SMP PREEMPT Tue Jan 25 16:53:00 CST 2022
+#1 SMP PREEMPT Mon Mar 19 12:52:40 KST 2018
+#1 SMP PREEMPT Fri Feb 5 10:43:33 CST 2021
+#1 SMP PREEMPT Wed Sep 30 09:41:35 CST 2020
+#2 SMP PREEMPT Thu Mar 21 18:02:35 -03 2019
+#1 SMP PREEMPT Mon May 7 06:22:30 CST 2018
+#1 SMP PREEMPT Fri Aug 3 11:03:28 CST 2018
+#1 SMP PREEMPT Fri Mar 23 16:50:59 CST 2018
+#1 SMP PREEMPT Fri Aug 9 12:14:05 CST 2019
+#1 SMP PREEMPT Sat Mar 21 15:53:11 CST 2020
+#1 SMP PREEMPT Tue Jul 28 04:32:34 CST 2020
+#1 SMP PREEMPT Fri Dec 7 12:39:23 CST 2018
+#1 SMP PREEMPT Sun Oct 27 11:22:36 CST 2019
+#2 SMP PREEMPT Thu Aug 20 16:43:34 CST 2020
+#1 SMP PREEMPT Tue Apr 9 21:59:41 CST 2019
+#1 SMP PREEMPT Mon Nov 23 22:03:06 CST 2020
+#2 SMP PREEMPT Fri Nov 15 11:39:07 WIB 2019
+#1 SMP PREEMPT Tue Dec 14 15:38:49 CST 2021
+#1 SMP PREEMPT Wed Oct 23 20:01:34 CST 2019
+#1 SMP PREEMPT Mon Mar 18 21:01:14 JST 2019
+#1 SMP PREEMPT Sat Nov 9 17:08:04 KST 2019
+#1 SMP PREEMPT Wed Jan 8 23:29:19 CST 2020
+#1 SMP PREEMPT Wed Aug 11 13:56:15 PDT 2021
+#1 SMP PREEMPT Thu Feb 1 17:50:06 CST 2018
+#1 SMP PREEMPT Mon Jan 10 06:58:42 UTC 2022
+#2 SMP PREEMPT Sat Sep 11 18:00:46 CST 2021
+#1 SMP PREEMPT Mon Apr 19 22:12:48 CST 2021
+#1 SMP PREEMPT Thu Oct 18 16:28:59 KST 2018
+#1 SMP PREEMPT Fri Oct 23 18:04:02 CST 2020
+#1 SMP PREEMPT Fri Aug 17 16:43:37 CST 2018
+#1 SMP PREEMPT Wed May 11 21:21:04 CST 2022
+#3 SMP PREEMPT Fri Oct 19 13:26:12 CST 2018
+#1 SMP PREEMPT Thu Sep 10 19:19:23 KST 2020
+#1 SMP PREEMPT Mon Jul 19 18:33:45 CST 2021
+#1 SMP PREEMPT Sun Sep 13 21:13:05 CST 2020
+#1 SMP PREEMPT Fri Dec 3 21:49:23 CST 2021
+#1 SMP PREEMPT Thu Sep 16 23:09:24 CST 2021
+#1 SMP PREEMPT Tue Jun 25 11:17:54 CST 2019
+#2 SMP PREEMPT Tue Jan 30 05:30:02 CST 2018
+#4 SMP PREEMPT Mon Jun 7 14:05:31 CST 2021
+#1 SMP PREEMPT Wed Apr 17 11:01:53 2019
+#1 SMP PREEMPT Tue Nov 3 04:39:23 CST 2020
+#1 SMP PREEMPT Sat Aug 29 09:50:26 KST 2020
+#1 SMP PREEMPT Wed Sep 5 05:27:36 CST 2018
+#1 SMP PREEMPT Sun Sep 6 14:03:02 CST 2020
+#1 SMP PREEMPT Wed Jan 9 03:19:37 CST 2019
+#1 SMP PREEMPT Mon Dec 2 15:12:53 CST 2019
+#1 SMP PREEMPT Tue May 19 17:16:45 UTC 2020
+#1 SMP PREEMPT Tue Sep 7 18:48:50 CST 2021
+#2 SMP PREEMPT Thu Aug 23 18:25:00 CST 2018
+#1 SMP PREEMPT Thu Oct 4 15:49:26 KST 2018
+#1 SMP PREEMPT Wed May 25 07:15:53 PDT 2022
+#14 SMP PREEMPT Tue Jul 14 14:04:12 CST 2020
+#1 SMP PREEMPT Mon Dec 3 10:10:12 KST 2018
+#1 SMP PREEMPT Tue Aug 27 17:45:59 CST 2019
+#1 SMP PREEMPT Tue Sep 8 11:19:20 KST 2020
+#3 SMP PREEMPT Mon Jun 7 10:30:52 CST 2021
+#9 SMP PREEMPT Mon Dec 28 14:20:55 CST 2020
+#1 SMP PREEMPT Mon Jan 25 22:48:59 WIB 2021
+#1 SMP PREEMPT Mon Nov 2 11:35:50 KST 2020
+#2 SMP PREEMPT Tue Sep 29 14:31:43 KST 2020
+#1 SMP PREEMPT Mon Oct 5 19:18:33 KST 2020
+#1 SMP PREEMPT Sat Apr 4 16:01:59 CST 2020
+#1 SMP PREEMPT Thu Jul 12 20:22:26 CST 2018
+#1 SMP PREEMPT Fri Mar 19 11:20:11 KST 2021
+#1 SMP PREEMPT Sun Jan 13 11:38:12 PST 2019
+#1 SMP PREEMPT Wed Sep 25 21:51:56 CST 2019
+#1 SMP PREEMPT Fri Jan 11 02:50:32 CST 2019
+#6 SMP PREEMPT Thu Jun 3 14:55:59 CST 2021
+#1 SMP PREEMPT Sat Jan 22 10:47:13 CST 2022
+#1 SMP PREEMPT Sat Jul 18 14:33:08 CST 2020
+#1 SMP PREEMPT Fri Nov 8 21:20:35 CST 2019
+#1 SMP PREEMPT Wed Dec 5 23:45:40 KST 2018
+#1 SMP PREEMPT Thu Apr 4 08:52:11 KST 2019
+#1 SMP PREEMPT Wed Dec 8 11:45:51 CST 2021
+#1 SMP PREEMPT Mon Feb 14 10:39:43 CST 2022
+#1 SMP PREEMPT Tue Oct 19 16:07:33 CST 2021
+#1 SMP PREEMPT Thu Apr 15 13:39:31 PDT 2021
+#1 SMP PREEMPT Wed Jul 24 18:16:39 CST 2019
+#1 SMP PREEMPT Wed Jan 8 15:36:37 CST 2020
+#2 SMP PREEMPT Thu Oct 31 12:11:48 KST 2019
+#1 SMP PREEMPT Sun Aug 8 20:08:38 PDT 2021
+#1 SMP PREEMPT Thu May 28 12:19:07 CST 2020
+#1 SMP PREEMPT Fri Jun 8 00:19:23 CST 2018
+#1 SMP PREEMPT Thu Oct 11 13:37:07 KST 2018
+#2 SMP PREEMPT Thu Aug 16 18:45:21 CST 2018
+#1 SMP PREEMPT Thu Jun 13 16:49:50 CST 2019
+#3 SMP PREEMPT Mon Aug 30 11:57:56 CST 2021
+#1 SMP PREEMPT Tue Jan 2 14:05:49 KST 2018
+#2 SMP PREEMPT Tue Oct 8 18:08:43 KST 2019
+#1 SMP PREEMPT Wed Jan 6 01:38:41 CST 2021
+#2 SMP PREEMPT Thu Jul 19 10:26:40 CST 2018
+#1 SMP PREEMPT Wed May 11 01:15:32 WIB 2022 f2fs-hash:9f5701dafc
+#1 SMP PREEMPT Sat Jun 6 10:48:29 CST 2020
+#1 SMP PREEMPT Wed Feb 3 01:31:39 PST 2021
+#1 SMP PREEMPT Mon Dec 23 00:11:54 CST 2019
+#1 SMP PREEMPT Tue Dec 22 05:47:20 CST 2020
+#1 SMP PREEMPT Sun Oct 4 12:30:57 CST 2020
+#1 SMP PREEMPT Thu Mar 18 19:09:19 PDT 2021
+#1 SMP PREEMPT Tue Jul 21 13:45:34 CST 2020
+#1 SMP PREEMPT Tue Feb 8 15:49:36 CST 2022
+#1 SMP PREEMPT Tue May 5 01:48:55 PDT 2020
+#1 SMP PREEMPT Wed Mar 30 21:59:39 CST 2022
+#1 SMP PREEMPT Sat Jun 12 08:10:45 KST 2021
+#1 SMP PREEMPT Mon May 24 19:55:49 HKT 2021
+#1 SMP PREEMPT Sun Jan 13 11:38:05 PST 2019
+#5 SMP PREEMPT Fri Jun 25 05:48:10 CDT 2021
+#1 SMP PREEMPT Mon Dec 3 22:30:04 PST 2018
+#1 SMP PREEMPT Fri Jun 19 16:53:47 KST 2020
+#1 SMP PREEMPT Tue Dec 8 12:47:30 CST 2020
+#1 SMP PREEMPT Wed Feb 16 06:52:31 KST 2022
+#1 SMP PREEMPT Sun Apr 26 21:51:29 CST 2020
+#1 SMP PREEMPT Fri Jan 29 06:38:39 CST 2021
+#2 SMP PREEMPT Fri Jul 26 12:46:37 CST 2019
+#1 SMP PREEMPT Wed Jan 15 16:01:49 CST 2020
+#1 SMP PREEMPT Thu Mar 15 11:42:25 KST 2018
+#1 SMP PREEMPT Tue Jul 31 20:27:42 CST 2018
+#1 SMP PREEMPT Tue Jun 29 20:55:47 CST 2021
+#1 repo:r-amlogic SMP PREEMPT Tue Apr 5 20:27:38 CST 2022
+#1 SMP PREEMPT Tue Oct 16 01:25:02 PDT 2018
+#1 SMP PREEMPT Fri Nov 12 11:36:25 UTC 2021
+#1 SMP PREEMPT Thu Jun 28 14:47:42 CST 2018
+#1 SMP PREEMPT Mon May 9 10:47:07 CST 2022
+#1 SMP PREEMPT Sat Mar 7 02:38:44 CST 2020
+#1 SMP PREEMPT Tue Apr 16 15:30:32 KST 2019
+#1 SMP PREEMPT Sat Aug 29 19:08:17 CST 2020
+#2 SMP PREEMPT Mon Dec 23 13:32:15 CST 2019
+#2 SMP PREEMPT Fri Jan 12 16:20:08 KST 2018
+#1 SMP PREEMPT Sat Sep 4 18:00:40 CST 2021
+#2 SMP PREEMPT Thu May 7 06:19:27 CST 2020
+#2 SMP PREEMPT Sun Feb 23 20:02:01 CST 2020
+#1 SMP PREEMPT Tue Sep 29 01:02:57 CST 2020
+#1 SMP PREEMPT Tue Jun 26 12:33:16 PDT 2018
+#1 SMP PREEMPT Thu Mar 28 06:11:01 CDT 2019
+#1 SMP PREEMPT Fri Jan 3 21:45:43 KST 2020
+#1 SMP PREEMPT Thu Jul 19 13:19:51 CST 2018
+#1 SMP PREEMPT Tue Sep 15 18:08:33 CST 2020
+#1 SMP PREEMPT Fri May 22 08:12:48 CDT 2020
+#1 SMP PREEMPT Tue May 22 02:37:49 CST 2018
+#1 SMP PREEMPT Wed Mar 20 00:20:51 PDT 2019
+#1 SMP PREEMPT Mon Sep 21 16:40:04 CST 2020
+#1 SMP PREEMPT Wed May 27 22:32:32 +07 2020
+#1 SMP PREEMPT Tue Oct 1 01:01:23 PDT 2019
+#2 SMP PREEMPT Wed Jan 22 20:07:05 KST 2020
+#1 SMP PREEMPT Tue Sep 24 18:59:02 CST 2019
+#1 SMP PREEMPT Thu Jul 12 15:49:39 BRT 2018
+#1 SMP PREEMPT Thu Apr 21 03:30:58 UTC 2022
+#1 SMP PREEMPT Tue Apr 19 20:40:00 KST 2022
+#1 SMP PREEMPT Fri Mar 4 01:37:46 CST 2022
+#2 SMP PREEMPT Fri Sep 21 18:59:23 CST 2018
+#1 SMP PREEMPT Thu Mar 8 03:47:56 CST 2018
+#1 SMP PREEMPT Thu Jul 18 11:31:11 IST 2019
+#2 SMP PREEMPT Fri Dec 22 18:51:35 CST 2017
+#1 SMP PREEMPT Tue Dec 4 14:26:19 JST 2018
+#1 SMP PREEMPT Thu Jan 21 11:42:40 CST 2021
+#1 SMP PREEMPT Mon Apr 20 20:48:12 CST 2020
+#1 SMP PREEMPT Sat Dec 5 13:36:13 CST 2020
+#1 SMP PREEMPT Thu Sep 13 18:11:36 CST 2018
+#1 SMP PREEMPT Mon Nov 20 06:58:12 EST 2017
+#0 SMP PREEMPT Wed Nov 24 10:04:17 UTC 2021
+#1 SMP PREEMPT Wed Aug 4 17:13:52 CST 2021
+#1 SMP PREEMPT Sat Aug 7 20:37:23 PDT 2021
+#1 SMP PREEMPT Mon Aug 5 13:10:35 CST 2019
+#2 SMP PREEMPT Wed Sep 22 17:10:27 IST 2021
+#1 SMP PREEMPT Sat Mar 19 01:57:14 KST 2022
+#2 SMP PREEMPT Thu Nov 11 18:47:56 CST 2021
+#1 SMP PREEMPT Tue Dec 12 22:19:08 CST 2017
+#2 SMP PREEMPT Tue Sep 29 07:05:12 UTC 2020
+#1 SMP PREEMPT Mon Jun 11 16:22:08 CST 2018
+#1 SMP PREEMPT Tue Mar 12 11:33:33 CST 2019
+#1 SMP PREEMPT Wed May 11 11:27:34 CST 2022
+#1 SMP PREEMPT Mon Jun 10 15:43:57 WIB 2019
+#1 SMP PREEMPT Wed Oct 10 16:43:14 CST 2018
+#1 SMP PREEMPT Thu Apr 7 00:12:26 CST 2022
+#1 SMP PREEMPT Fri Nov 8 12:08:09 CST 2019
+#1 SMP PREEMPT Tue Jul 3 18:01:29 CST 2018
+#1 SMP PREEMPT Mon Dec 10 11:47:12 CST 2018
+#1 SMP PREEMPT Tue Nov 12 03:19:17 KST 2019
+#1 SMP PREEMPT Thu Aug 13 16:05:06 CST 2020
+#1 SMP PREEMPT Thu Nov 28 23:40:20 KST 2019
+#1 SMP PREEMPT Mon Jul 9 16:58:58 KST 2018
+#1 SMP PREEMPT Mon Feb 14 22:23:26 CST 2022
+#1 SMP PREEMPT Sat Feb 20 15:51:38 CST 2021
+#1 SMP PREEMPT Wed Mar 31 20:55:21 CST 2021
+#1 SMP PREEMPT Fri Feb 15 10:36:32 CST 2019
+#2 SMP PREEMPT Thu Mar 29 17:02:22 CST 2018
+#1 SMP PREEMPT Fri Mar 2 20:17:39 CST 2018
+#1 SMP PREEMPT Wed Jun 10 02:21:43 +07 2020
+#1 SMP PREEMPT Thu Nov 29 06:22:20 CST 2018
+#2 SMP PREEMPT Mon Jan 20 18:13:41 KST 2020
+#1 SMP PREEMPT Tue Sep 22 14:24:01 CST 2020
+#2 SMP PREEMPT Mon Oct 19 11:18:16 KST 2020
+#1 SMP PREEMPT Sun Dec 1 16:32:05 CST 2019
+#1 SMP PREEMPT Fri Oct 29 16:05:12 CST 2021
+#1 SMP PREEMPT Sun Dec 3 01:47:17 CST 2017
+#1 SMP PREEMPT Fri Aug 21 04:04:19 GMT 2020
+#1 SMP PREEMPT Fri Dec 14 19:06:04 KST 2018
+#1 SMP PREEMPT Tue Jul 7 19:44:12 CST 2020
+#1 SMP PREEMPT Thu May 16 23:05:41 CST 2019
+#1 SMP PREEMPT Fri Apr 17 22:45:14 KST 2020
+#1 SMP PREEMPT Thu Jan 25 14:05:29 CST 2018
+#1 SMP PREEMPT Wed Oct 16 05:23:38 JST 2019
+#1 SMP PREEMPT Wed Mar 4 00:50:01 KST 2020
+#1 SMP PREEMPT Fri Jul 10 21:58:37 CST 2020
+#4 SMP PREEMPT Wed Sep 25 22:34:20 PDT 2019
+#1 SMP PREEMPT Tue Dec 29 16:22:18 CST 2020
+#1 SMP PREEMPT Wed Nov 20 22:08:08 CST 2019
+#1 SMP PREEMPT Thu May 19 21:56:06 CST 2022
+#2 SMP PREEMPT Tue Sep 25 20:48:45 CST 2018
+#2 SMP PREEMPT Tue Oct 29 17:06:46 KST 2019
+#1 SMP PREEMPT Sat Nov 7 19:57:17 JST 2020
+#2 SMP PREEMPT Fri Dec 20 10:22:54 KST 2019
+#1 SMP PREEMPT Thu Nov 15 03:14:27 CST 2018
+#1 SMP PREEMPT Tue Feb 9 20:48:28 PST 2021
+#1 SMP PREEMPT Thu Jan 14 20:21:49 PST 2021
+#1 SMP PREEMPT Wed Dec 18 00:31:39 PST 2019
+#1 SMP PREEMPT Wed Jul 14 09:48:34 CDT 2021
+#1 SMP PREEMPT Sun Nov 14 20:27:15 PST 2021
+#1 SMP PREEMPT Mon Apr 12 17:42:55 CST 2021
+#1 SMP PREEMPT Tue Sep 15 21:53:57 CST 2020
+#1 SMP PREEMPT Thu Jul 2 01:26:02 CST 2020
+#2 SMP PREEMPT Mon Feb 17 11:34:16 KST 2020
+#5 SMP PREEMPT Thu Jul 22 16:35:19 CST 2021
+#1 SMP PREEMPT Tue Jul 30 20:17:03 CST 2019
+#1 SMP PREEMPT Tue Sep 29 00:28:54 KST 2020
+#1 SMP PREEMPT Fri Jul 5 19:59:59 CST 2019
+#2 SMP PREEMPT Thu Aug 29 10:22:18 KST 2019
+#2 SMP PREEMPT Tue Feb 23 19:41:15 CST 2021
+#2 SMP PREEMPT Wed Oct 10 17:06:30 CST 2018
+#1 SMP PREEMPT Tue Dec 12 16:46:04 CST 2017
+#1 SMP PREEMPT Thu Jan 9 14:41:27 KST 2020
+#1 SMP PREEMPT Sat Feb 13 12:36:38 CST 2021
+#1 SMP PREEMPT Fri May 7 09:49:50 KST 2021
+#1 SMP PREEMPT Sat Mar 21 14:58:59 KST 2020
+#1 SMP PREEMPT Tue Mar 29 09:52:44 KST 2022
+#2 SMP PREEMPT Fri Aug 28 15:19:23 KST 2020
+#1 SMP PREEMPT Sat Sep 21 19:08:49 CST 2019
+#1 SMP PREEMPT Fri Nov 6 02:37:27 IST 2020
+#1 SMP PREEMPT Tue Jan 26 23:07:19 PST 2021
+#1 SMP PREEMPT Fri Oct 29 09:05:44 UTC 2021
+#1 SMP PREEMPT Wed Jun 23 01:13:18 CST 2021
+#2 SMP PREEMPT Thu Mar 7 17:02:54 CST 2019
+#1 SMP PREEMPT Wed Jan 20 23:49:36 EST 2021
+#1 SMP PREEMPT Tue Aug 13 03:48:04 CST 2019
+#1 SMP PREEMPT Tue Dec 8 11:54:01 CST 2020
+#1 SMP PREEMPT Tue Feb 2 19:50:07 CST 2021
+#2 SMP PREEMPT Fri Jul 2 00:39:41 CST 2021
+#1 SMP PREEMPT Tue Jan 23 17:49:58 CST 2018
+#1 SMP PREEMPT Mon Aug 19 19:38:07 CST 2019
+#1 SMP PREEMPT Thu Apr 14 13:50:22 CST 2022
+#1 SMP PREEMPT Fri Apr 17 10:03:47 CST 2020
+#6 SMP PREEMPT Wed Dec 22 20:30:02 UTC 2021
+#1 SMP PREEMPT Mon Sep 14 16:23:37 KST 2020
+#81 SMP PREEMPT Mon Oct 15 10:16:34 CST 2018
+#2 SMP PREEMPT Wed Jul 8 20:10:17 CST 2020
+#1 SMP PREEMPT Thu Nov 1 16:55:31 CST 2018
+#1 SMP PREEMPT Wed Jun 26 04:38:31 CST 2019
+#1 SMP PREEMPT Sat Dec 7 09:05:55 KST 2019
+#1 SMP PREEMPT Tue Mar 15 11:27:08 CST 2022
+#1 SMP PREEMPT Thu Nov 12 21:14:21 CST 2020
+#1 SMP PREEMPT Wed Feb 26 18:22:30 CST 2020
+#1 SMP PREEMPT Fri May 8 18:37:00 KST 2020
+#1 SMP PREEMPT Sat Apr 10 11:37:53 CST 2021
+#1 SMP PREEMPT Sat Jan 26 02:31:10 CST 2019
+#1 SMP PREEMPT Tue Dec 28 00:30:36 CST 2021
+#1 SMP PREEMPT Thu Mar 3 07:22:10 -03 2022
+#1 SMP PREEMPT Thu Mar 18 11:33:09 KST 2021
+#1 SMP PREEMPT Wed Jan 20 20:00:48 PST 2021
+#2 SMP PREEMPT Tue Dec 18 11:58:51 CST 2018
+#4 SMP PREEMPT Thu Jan 6 15:12:11 CST 2022
+#1 SMP PREEMPT Thu Mar 31 18:36:01 CST 2022
+#1 SMP PREEMPT Mon Aug 3 16:42:41 KST 2020
+#1 SMP PREEMPT Sun Aug 12 02:45:13 CST 2018
+#1 SMP PREEMPT Tue Oct 26 10:34:42 UTC 2021
+#2 SMP PREEMPT Fri Oct 30 19:56:44 KST 2020
+#1 SMP PREEMPT Fri Aug 30 16:24:26 EDT 2019
+#1 SMP PREEMPT Mon Feb 21 20:34:16 CST 2022
+#1 SMP PREEMPT Wed Feb 28 13:03:14 KST 2018
+#1 SMP PREEMPT Thu Mar 24 22:48:09 CST 2022
+#36 SMP PREEMPT Fri Jul 24 16:19:25 CST 2020
+#1 SMP PREEMPT Fri Mar 6 03:15:54 JST 2020
+#1 SMP PREEMPT Thu May 13 21:55:27 PDT 2021
+#1 SMP PREEMPT Tue Aug 24 16:32:01 CST 2021
+#2 SMP PREEMPT Thu Aug 29 15:27:04 KST 2019
+#1 SMP PREEMPT Sat May 22 17:26:37 IST 2021
+#1 SMP PREEMPT Fri Nov 22 20:17:07 KST 2019
+#1 SMP PREEMPT Mon Jun 3 17:32:00 CST 2019
+#1 SMP PREEMPT Sat Apr 10 19:59:32 PDT 2021
+#1 SMP PREEMPT Wed Jan 5 21:27:04 CST 2022
+#1 SMP PREEMPT Tue Oct 20 14:23:35 CST 2020
+#1 SMP PREEMPT Thu Apr 7 21:17:23 CST 2022
+#1 SMP PREEMPT Sun Mar 14 21:47:20 CST 2021
+#1 SMP PREEMPT Mon May 10 21:26:09 CST 2021
+#1 SMP PREEMPT Fri Sep 21 18:39:29 CST 2018
+#1 SMP PREEMPT Tue Oct 30 18:50:35 KST 2018
+#1 SMP PREEMPT Fri Dec 13 19:46:40 CST 2019
+#6 SMP PREEMPT Wed Dec 22 20:30:02 UTC 2021
+#1 SMP PREEMPT Wed Oct 20 09:38:22 CEST 2021
+#1 SMP PREEMPT Thu Jan 24 20:47:39 CST 2019
+#1 SMP PREEMPT Tue Nov 28 22:36:37 CST 2017
+#3 SMP PREEMPT Tue Aug 18 18:17:12 KST 2020
+#1 SMP PREEMPT Fri Jan 3 12:50:28 CST 2020
+#11 SMP PREEMPT Wed Nov 28 11:13:43 CST 2018
+#6 SMP PREEMPT Fri Jan 21 14:04:04 UTC 2022
+#1 SMP PREEMPT Sun Oct 25 18:23:06 CST 2020
+#1 SMP PREEMPT Tue May 12 12:33:42 CST 2020
+#1 SMP PREEMPT Thu May 2 10:47:29 KST 2019
+#1 SMP PREEMPT Mon Feb 8 17:51:24 CST 2021
+#1 SMP PREEMPT Tue Aug 14 19:01:32 KST 2018
+#1 SMP PREEMPT Thu May 2 23:40:47 IST 2019
+#1 SMP PREEMPT Fri Nov 9 02:01:58 CST 2018
+#1 SMP PREEMPT Wed Jun 30 10:38:23 CST 2021
+#1 SMP PREEMPT Wed Sep 1 01:54:26 CDT 2021
+#1 SMP PREEMPT Tue Mar 1 20:38:45 CST 2022
+#1 SMP PREEMPT Tue Nov 24 10:45:00 CST 2020
+#1 SMP PREEMPT Wed Jul 17 19:33:01 CST 2019
+#1 SMP PREEMPT Fri Apr 20 00:12:42 CST 2018
+#2 SMP PREEMPT Thu Apr 26 12:29:51 CST 2018
+#1 SMP PREEMPT Sat Sep 21 06:38:41 CST 2019
+#2 SMP PREEMPT Tue Oct 23 00:00:50 CST 2018
+#1 SMP PREEMPT Mon Jul 1 22:33:27 CST 2019
+#1 SMP PREEMPT Mon Oct 25 20:58:06 CST 2021
+#2 SMP PREEMPT Thu Sep 10 20:24:26 KST 2020
+#1 SMP PREEMPT Mon Sep 13 23:08:17 JST 2021
+#2 SMP PREEMPT Fri Dec 28 13:05:16 CST 2018
+#1 SMP PREEMPT Tue Dec 17 04:22:39 CST 2019
+#1 SMP PREEMPT Thu Aug 27 17:23:11 KST 2020
+#1 SMP PREEMPT Fri May 31 14:40:26 CST 2019
+#3 SMP PREEMPT Mon Sep 7 16:19:40 KST 2020
+#1 SMP PREEMPT Tue Oct 29 16:47:17 JST 2019
+#2 SMP PREEMPT Tue Mar 23 06:12:17 CST 2021
+#1 SMP PREEMPT Thu Nov 5 03:43:26 KST 2020
+#1 SMP PREEMPT Thu Sep 23 04:23:13 CST 2021
+#1 SMP PREEMPT Thu Sep 5 21:23:00 KST 2019
+#1 SMP PREEMPT Fri Dec 11 13:54:32 IST 2020
+#1 SMP PREEMPT Mon Jul 27 23:04:55 CST 2020
+#1 SMP PREEMPT Thu Nov 5 04:15:56 KST 2020
+#1 SMP PREEMPT Wed Aug 18 11:46:42 CST 2021
+#6 SMP PREEMPT Fri Oct 22 14:02:46 UTC 2021
+#1 SMP PREEMPT Mon Sep 6 06:32:16 CDT 2021
+#1 SMP PREEMPT Thu Oct 7 16:10:05 IST 2021
+#1 SMP PREEMPT Fri May 4 15:25:00 CST 2018
+#1 SMP PREEMPT Mon Jul 26 16:18:41 CST 2021
+#1 SMP PREEMPT Sat Dec 29 06:16:05 CST 2018
+#1 SMP PREEMPT Fri Jan 12 20:44:28 IST 2018
+#1 SMP PREEMPT Sat Oct 27 17:59:42 KST 2018
+#1 SMP PREEMPT Tue Dec 19 04:29:38 KST 2017
+#2 SMP PREEMPT Fri Apr 24 19:57:23 CST 2020
+#1 SMP PREEMPT Wed Nov 18 04:41:56 IST 2020
+#1 SMP PREEMPT Fri Oct 5 03:19:50 KST 2018
+#1 SMP PREEMPT Tue Apr 30 19:35:36 CST 2019
+#1 SMP PREEMPT Tue Jun 30 22:36:47 KST 2020
+#1 SMP PREEMPT Sat Nov 21 02:41:06 CST 2020
+#1 SMP PREEMPT Sat Dec 19 06:40:54 CST 2020
+#1 SMP PREEMPT Mon May 25 23:23:29 CST 2020
+#1 SMP PREEMPT Tue Dec 25 18:24:04 CST 2018
+#1 SMP PREEMPT Fri Apr 8 14:30:47 CST 2022
+#1 SMP PREEMPT Mon Mar 11 22:50:52 CST 2019
+#1 SMP PREEMPT Tue Apr 7 07:44:51 UTC 2020
+#2 SMP PREEMPT Mon Jul 22 20:25:17 KST 2019
+#1 SMP PREEMPT Wed Jan 16 03:31:27 CST 2019
+#1 SMP PREEMPT Mon Jan 14 18:22:11 CST 2019
+#1 SMP PREEMPT Wed Jan 24 11:12:47 KST 2018
+#1 SMP PREEMPT Thu May 19 19:09:49 CST 2022
+#1 SMP PREEMPT Wed Jul 24 16:14:59 JST 2019
+#1 SMP PREEMPT Mon Oct 1 14:48:26 KST 2018
+#1 SMP PREEMPT Thu Jul 15 16:51:55 KST 2021
+#1 SMP PREEMPT Wed May 8 02:49:54 JST 2019
+#1 SMP PREEMPT Fri Nov 19 19:40:34 CST 2021
+#1 SMP PREEMPT Tue Dec 18 18:44:05 CST 2018
+#1 SMP PREEMPT Fri Jun 12 14:33:56 JST 2020
+#1 SMP PREEMPT Sat Jan 30 02:32:04 IST 2021
+#1 SMP PREEMPT Mon Feb 10 22:13:58 KST 2020
+#1 SMP PREEMPT Tue Mar 8 14:26:00 CST 2022
+#1 SMP PREEMPT Wed Aug 12 03:08:40 CST 2020
+#1 SMP PREEMPT Tue Dec 17 16:56:59 KST 2019
+#1 SMP PREEMPT Tue Jun 8 18:07:30 CST 2021
+#1 SMP PREEMPT Thu Apr 1 16:55:39 KST 2021
+#1 SMP PREEMPT Sat Mar 5 01:12:17 CST 2022
+#1 SMP PREEMPT Mon Nov 29 20:52:02 CST 2021
+#1 SMP PREEMPT Thu Jun 18 11:05:26 CST 2020
+#1 SMP PREEMPT Wed Feb 12 15:26:19 CST 2020
+#1 SMP PREEMPT Tue Apr 16 22:34:20 CST 2019
+#1 SMP PREEMPT Sat Jan 26 11:50:10 CST 2019
+#1 SMP PREEMPT Tue Jan 7 10:23:09 CST 2020
+#1 SMP PREEMPT Thu May 5 16:49:38 CST 2022
+#1 SMP PREEMPT Mon May 13 17:06:40 CST 2019
+#1 SMP PREEMPT Sat Jun 5 02:02:19 CST 2021
+#1 SMP PREEMPT Mon May 17 11:46:50 KST 2021
+#1 SMP PREEMPT Wed Dec 5 06:06:43 CST 2018
+#1 SMP PREEMPT Thu Jul 18 20:48:19 CST 2019
+#1 SMP PREEMPT Thu Sep 12 22:25:36 CST 2019
+#1 SMP PREEMPT Fri Jun 4 14:14:06 KST 2021
+#1 SMP PREEMPT Wed Jun 2 00:29:57 CST 2021
+#1 SMP PREEMPT Tue Mar 31 22:04:23 KST 2020
+#2 SMP PREEMPT Wed Jun 26 15:16:30 KST 2019
+#1 SMP PREEMPT Fri Aug 3 20:12:55 IST 2018
+#1 SMP PREEMPT Sat Sep 5 06:48:25 CDT 2020
+#2 SMP PREEMPT Tue Jun 25 19:10:18 CST 2019
+#1 SMP PREEMPT Wed Sep 12 14:41:31 BRT 2018
+#1 SMP PREEMPT Thu Nov 30 01:54:42 KST 2017
+#1 SMP PREEMPT Fri Apr 1 15:53:18 CST 2022
+#1 SMP PREEMPT Wed Jul 24 19:57:59 CST 2019
+#1 SMP PREEMPT Mon Sep 10 22:03:14 CST 2018
+#1 SMP PREEMPT Mon Jan 4 15:11:42 CST 2021
+#1 SMP PREEMPT Mon Mar 11 20:41:46 CST 2019
+#1 SMP PREEMPT Mon Aug 9 21:14:24 CST 2021
+#1 SMP PREEMPT Fri Jan 25 17:31:17 CST 2019
+#2 SMP PREEMPT Mon Jul 1 18:43:56 CST 2019
+#1 SMP PREEMPT Tue Feb 11 15:22:06 KST 2020
+#1 SMP PREEMPT Wed Mar 20 10:53:25 CST 2019
+#1 SMP PREEMPT Fri Sep 25 04:11:01 CST 2020
+#1 SMP PREEMPT Tue Aug 27 22:10:37 CST 2019
+#2 SMP PREEMPT Wed Apr 20 12:06:14 CST 2022
+#1 SMP PREEMPT Thu Nov 7 16:40:39 CST 2019
+#1 SMP PREEMPT Fri Dec 13 01:40:55 JST 2019
+#15 SMP PREEMPT Sun Dec 23 10:52:20 CST 2018
+#1 SMP PREEMPT Tue Jul 31 13:13:43 CST 2018
+#1 SMP PREEMPT Tue Apr 28 02:03:48 PDT 2020
+#1 SMP PREEMPT Wed Sep 18 00:02:11 CST 2019
+#1 SMP PREEMPT Fri Jan 21 01:13:10 KST 2022
+#1 SMP PREEMPT Fri Apr 20 00:12:42 CST 2018
+#1 SMP PREEMPT Tue Mar 22 22:26:33 CST 2022
+#1 SMP PREEMPT Wed Jul 11 02:48:03 CST 2018
+#1 SMP PREEMPT Wed Jul 14 17:18:52 CST 2021
+#1 SMP PREEMPT Tue Dec 21 04:30:07 PST 2021
+#1 SMP PREEMPT Sat Feb 16 18:36:22 CST 2019
+#1 SMP PREEMPT Thu Dec 26 10:38:57 CST 2019
+#2 SMP PREEMPT Thu Sep 30 15:20:18 CST 2021
+#2 SMP PREEMPT Mon Sep 14 19:46:06 CST 2020
+#1 SMP PREEMPT Thu Aug 2 07:47:40 CDT 2018
+#4 SMP PREEMPT Sun Aug 8 06:11:39 CDT 2021
+#1 SMP PREEMPT Mon Jan 25 10:56:36 UTC 2021
+#1 SMP PREEMPT Mon Feb 25 19:49:35 WIB 2019
+#1 SMP PREEMPT Tue Feb 18 16:10:13 CST 2020
+#1 SMP PREEMPT Thu Oct 4 21:22:45 CST 2018
+#1 SMP PREEMPT Mon Jun 18 18:46:17 KST 2018
+#1 SMP PREEMPT Fri Jul 24 05:27:09 GMT 2020
+#1 SMP PREEMPT Wed Apr 28 20:29:36 -03 2021
+#1 SMP PREEMPT Fri Nov 24 15:32:52 KST 2017
+#1 SMP PREEMPT Wed Feb 5 11:51:30 CST 2020
+#1 SMP PREEMPT Thu Aug 19 23:12:22 CST 2021
+#1 SMP PREEMPT Tue Jun 12 20:45:41 KST 2018
+#2 SMP PREEMPT Wed Jul 3 20:55:45 KST 2019
+#1 SMP PREEMPT Thu Jul 11 17:12:00 JST 2019
+#1 SMP PREEMPT Fri May 25 22:51:08 KST 2018
+#1 SMP PREEMPT Thu Mar 25 15:07:25 CDT 2021
+#2 SMP PREEMPT Mon Sep 21 17:24:04 KST 2020
+#1 SMP PREEMPT Fri Mar 15 19:36:26 2019
+#6 SMP PREEMPT Fri Jan 21 14:04:04 UTC 2022
+#1 SMP PREEMPT Tue Jun 30 18:14:02 CST 2020
+#2 SMP PREEMPT Tue Sep 17 11:32:43 KST 2019
+#1 SMP PREEMPT Thu Dec 23 10:09:01 CST 2021
+#1 SMP PREEMPT Tue Jan 18 17:38:04 UTC 2022
+#2 SMP PREEMPT Mon Mar 18 15:19:43 KST 2019
+#1 SMP PREEMPT Tue Oct 9 00:41:11 CST 2018
+#1 SMP PREEMPT Wed Nov 3 08:28:42 CDT 2021
+#1 SMP PREEMPT Thu Aug 9 13:04:59 CST 2018
+#1 SMP PREEMPT Mon Aug 30 23:55:17 PDT 2021
+#1 SMP PREEMPT Thu Apr 28 23:34:08 CST 2022
+#1 SMP PREEMPT Fri Dec 7 09:00:25 KST 2018
+#1 SMP PREEMPT Wed Nov 28 14:09:13 CST 2018
+#1 SMP PREEMPT Thu Mar 4 11:55:03 PST 2021
+#1 SMP PREEMPT Thu Sep 6 12:00:14 CST 2018
+#2 SMP Thu Sep 27 12:33:44 UTC 2018
+#1 SMP PREEMPT Wed Oct 3 12:51:45 KST 2018
+#1 SMP PREEMPT Thu Sep 6 01:49:40 CST 2018
+#1 SMP PREEMPT Wed Aug 26 02:56:05 CST 2020
+#1 SMP PREEMPT Fri Oct 16 19:23:06 CST 2020
+#1 SMP PREEMPT Thu Dec 31 02:31:11 CST 2020
+#1 SMP PREEMPT Tue Jan 15 13:57:02 CST 2019
+#1 SMP PREEMPT Thu Jun 21 03:46:21 CST 2018
+#1 SMP PREEMPT Wed Apr 25 18:56:51 CST 2018
+#1 SMP PREEMPT Mon Jun 4 20:55:43 CST 2018
+#2 SMP PREEMPT Wed Sep 2 12:25:38 CST 2020
+#2 SMP PREEMPT Thu Aug 8 07:02:11 CST 2019
+#1 SMP PREEMPT Fri Apr 29 11:40:00 CST 2022
+#2 SMP PREEMPT Thu Jan 2 10:39:25 KST 2020
+#1 SMP PREEMPT Fri Nov 15 19:37:40 IST 2019
+#2 SMP Sun Dec 8 15:45:24 UTC 2019
+#1 SMP PREEMPT Fri Apr 3 10:53:24 EDT 2020
+#1 SMP PREEMPT Thu Apr 22 13:29:49 JST 2021
+#1 SMP PREEMPT Tue Mar 12 05:03:44 CST 2019
+#1 SMP PREEMPT Wed Nov 25 13:14:17 CST 2020
+#1 SMP PREEMPT Wed Oct 28 16:05:37 CDT 2020
+#1 SMP PREEMPT Thu Sep 6 13:03:13 CST 2018
+#1 SMP PREEMPT Fri Dec 11 11:09:55 CST 2020
+#2 SMP PREEMPT Thu Dec 24 17:58:35 KST 2020
+#1 SMP PREEMPT Thu Jul 29 11:15:27 KST 2021
+#1 SMP PREEMPT Sun Aug 29 14:16:17 PDT 2021
+#1 SMP PREEMPT Tue May 22 20:42:34 CST 2018
+#1 SMP PREEMPT Tue May 22 21:54:38 CST 2018
+#1 SMP PREEMPT Tue Apr 12 20:38:45 CST 2022
+#2 SMP PREEMPT Wed Aug 11 16:22:45 CST 2021
+#1 SMP PREEMPT Tue Dec 26 05:53:20 EST 2017
+#1 SMP PREEMPT Wed Mar 2 11:51:52 CST 2022
+#1 SMP PREEMPT Thu Dec 12 22:47:05 PST 2019
+#1 SMP PREEMPT Mon Jul 30 20:03:31 KST 2018
+#1 SMP PREEMPT Wed May 6 21:08:00 CST 2020
+#1 SMP PREEMPT Thu Aug 29 16:19:28 KST 2019
+#2 SMP PREEMPT Tue Nov 19 20:12:10 CST 2019
+#2 SMP PREEMPT Tue Feb 25 06:24:15 KST 2020
+#6 SMP PREEMPT Wed Dec 22 20:30:02 UTC 2021
+#1 SMP PREEMPT Wed Jul 28 19:53:18 CST 2021
+#2 SMP PREEMPT Mon May 4 21:26:02 CST 2020
+#1 SMP PREEMPT Mon Aug 6 23:26:39 KST 2018
+#1 SMP PREEMPT Mon Jul 8 15:58:33 CST 2019
+#1 SMP PREEMPT Tue Nov 13 12:53:47 CST 2018
+#1 SMP PREEMPT Sun Apr 14 19:43:37 PDT 2019
+#1 SMP PREEMPT Thu Mar 25 09:37:46 CST 2021
+#1 SMP PREEMPT Wed Dec 11 11:25:10 CST 2019
+#1 SMP PREEMPT Thu Feb 4 11:24:18 CST 2021
+#1 SMP PREEMPT Mon Oct 8 17:55:21 CST 2018
+#1 SMP PREEMPT Wed May 29 14:04:17 PDT 2019
+#1 SMP PREEMPT Fri May 11 12:22:35 CST 2018
+#1 SMP PREEMPT Wed Jul 24 13:41:40 CST 2019
+#1 SMP PREEMPT Tue Feb 22 17:29:56 CST 2022
+#1 SMP PREEMPT Fri Dec 18 21:22:19 WIB 2020
+#1 SMP PREEMPT Fri Oct 26 03:33:58 CST 2018
+#1 SMP PREEMPT Tue Dec 21 00:43:27 UTC 2021
+#1 SMP PREEMPT Wed Dec 9 06:47:56 EST 2020
+#1 SMP PREEMPT Tue Oct 30 18:14:50 KST 2018
+#1 SMP PREEMPT Wed Jul 18 04:29:53 PDT 2018
+#1 SMP PREEMPT Sat Feb 6 02:22:45 CST 2021
+#1 SMP PREEMPT Thu Jul 1 03:40:39 CST 2021
+#1 SMP PREEMPT Mon Nov 11 17:29:03 CST 2019
+#1 SMP PREEMPT Sun Nov 11 17:43:13 CST 2018
+#1 SMP PREEMPT Sat Aug 14 20:44:59 CST 2021
+#1 SMP PREEMPT Mon Nov 22 19:22:09 CST 2021 f2fs-hash:c001757c47
+#1 SMP PREEMPT Mon Sep 14 21:44:13 CST 2020
+#1 SMP PREEMPT Mon Mar 12 13:36:24 CST 2018
+#1 SMP PREEMPT Wed Jul 7 18:28:35 CST 2021
+#2 SMP PREEMPT Thu Oct 22 15:22:43 KST 2020
+#1 SMP PREEMPT Thu May 20 21:08:31 CST 2021
+#1 SMP PREEMPT Wed Mar 10 05:11:45 UTC 2021
+#1 SMP PREEMPT Fri May 8 19:51:46 KST 2020
+#1 SMP PREEMPT Sat May 9 22:16:16 CST 2020
+#1 SMP PREEMPT Tue Oct 1 01:03:21 KST 2019
+#1 SMP PREEMPT Wed Mar 3 16:04:17 CST 2021
+#1 SMP PREEMPT Thu Sep 30 06:09:25 PDT 2021
+#1 SMP PREEMPT Thu Jun 4 17:05:13 KST 2020
+#1 SMP PREEMPT Tue Dec 26 13:05:02 KST 2017
+#1 SMP PREEMPT Thu Feb 10 22:49:10 CST 2022
+#1 SMP PREEMPT Thu Jan 28 15:50:09 IST 2021
+#1 SMP PREEMPT Fri Jan 10 14:39:46 CST 2020
+#1 SMP PREEMPT Fri Jul 31 14:42:28 UTC 2020
+#1 SMP PREEMPT Sat May 28 01:02:39 CST 2022
+#1 SMP PREEMPT Fri Nov 23 17:06:55 KST 2018
+#1 SMP PREEMPT Sat Jul 14 21:58:29 CST 2018
+#1 SMP PREEMPT Tue Mar 16 13:28:48 +07 2021
+#1 SMP PREEMPT Sun Sep 29 16:05:08 CST 2019
+#1 SMP PREEMPT Mon Aug 12 23:10:45 KST 2019
+#1 SMP PREEMPT Tue Oct 22 04:16:43 EDT 2019
+#1 SMP PREEMPT Sat Aug 18 16:01:13 CST 2018
+#2 SMP PREEMPT Thu Jul 30 11:19:34 CST 2020
+#2 SMP PREEMPT Thu Apr 12 11:08:17 CST 2018
+#1 SMP PREEMPT Sun Sep 27 02:05:54 CST 2020
+#1 SMP PREEMPT Sat Feb 20 20:37:36 CST 2021
+#2 SMP PREEMPT Wed Feb 7 17:35:26 KST 2018
+#1 SMP PREEMPT Wed Oct 13 01:07:19 CST 2021
+#1 SMP PREEMPT Thu Dec 13 22:42:15 CST 2018
+#1 SMP PREEMPT Thu Jun 3 11:18:00 CST 2021
+#1 SMP PREEMPT Mon Nov 12 19:26:19 CST 2018
+#1 SMP PREEMPT Sat Jul 11 02:58:52 CST 2020
+#2 SMP PREEMPT Mon Dec 10 22:23:04 CST 2018
+#1 SMP PREEMPT Wed May 15 16:29:31 KST 2019
+#1 SMP PREEMPT Thu Jan 13 03:23:50 CST 2022
+#1 SMP PREEMPT Fri Jul 5 22:44:08 KST 2019
+#1 SMP PREEMPT Wed Mar 9 16:37:24 CST 2022
+#1 SMP PREEMPT Mon Feb 18 21:32:14 KST 2019
+#1 SMP PREEMPT Sat May 21 02:56:06 CST 2022
+#8 SMP PREEMPT Tue Nov 9 15:51:45 CST 2021
+#2 SMP PREEMPT Tue Apr 23 20:28:19 KST 2019
+#1 SMP PREEMPT Tue Oct 19 16:35:29 CST 2021
+#1 SMP PREEMPT Mon Jan 6 16:35:56 CST 2020
+#1 SMP PREEMPT Thu May 13 23:38:59 CST 2021
+#1 SMP PREEMPT Sat Apr 4 06:47:28 CDT 2020
+#1 SMP PREEMPT Thu Mar 3 10:22:08 PST 2022
+#1 SMP PREEMPT Mon Mar 29 20:21:10 PDT 2021
+#1 SMP PREEMPT Thu Mar 28 18:45:41 KST 2019
+#1 SMP PREEMPT Fri Jan 17 01:22:05 2020
+#1 SMP PREEMPT Sat May 22 17:26:37 IST 2021
+#1 SMP PREEMPT Wed Jan 15 22:53:49 CST 2020
+#1 SMP PREEMPT Mon Jan 22 01:43:03 JST 2018
+#2 SMP PREEMPT Tue Sep 7 15:12:23 CST 2021
+#1 SMP PREEMPT Wed Sep 29 02:37:49 CST 2021 f2fs-hash:c001757c47
+#1 SMP PREEMPT Wed Oct 30 16:52:17 KST 2019
+#1 SMP PREEMPT Thu Jul 9 21:29:48 CST 2020
+#1 SMP PREEMPT Tue Apr 19 13:04:57 CST 2022
+#1 SMP PREEMPT Fri Aug 6 11:44:58 CST 2021
+#2 SMP PREEMPT Thu Jan 2 12:21:30 CST 2020
+#1 SMP PREEMPT Wed Jul 24 19:11:46 IST 2019
+#1 SMP PREEMPT Tue Feb 22 06:16:46 CST 2022
+#1 SMP PREEMPT Wed Jul 18 18:35:48 CST 2018
+#1 SMP PREEMPT Wed Apr 3 18:28:44 CST 2019
+#1 SMP PREEMPT Thu Mar 15 18:09:56 CST 2018
+#4 SMP PREEMPT Fri Oct 12 10:56:04 CST 2018
+#1 SMP PREEMPT Fri Mar 1 22:18:39 CST 2019
+#1 SMP PREEMPT Thu Aug 2 10:01:33 CEST 2018
+#1 SMP PREEMPT Wed Dec 19 19:39:43 KST 2018
+#1 SMP PREEMPT Sat Jan 9 17:23:38 +07 2021
+#1 SMP PREEMPT Mon Oct 5 19:42:43 PDT 2020
+#1 SMP PREEMPT Thu Feb 11 18:52:32 -04 2021
+#2 SMP PREEMPT Thu Sep 19 11:24:23 CST 2019
+#1 SMP PREEMPT Sat Apr 28 15:47:38 WIB 2018
+#1 SMP PREEMPT Tue Jun 12 12:47:27 KST 2018
+#1 SMP PREEMPT Wed Oct 23 18:32:00 CST 2019
+#1 SMP PREEMPT Thu Aug 30 22:12:44 CST 2018
+#1 SMP PREEMPT Fri Oct 12 17:24:55 CST 2018
+#1 SMP PREEMPT Thu Jul 5 23:23:03 CST 2018
+#1 SMP PREEMPT Thu Jan 25 15:58:04 CST 2018
+#1 SMP PREEMPT Mon Jun 21 17:32:21 CST 2021
+#1 SMP PREEMPT Thu Dec 23 23:49:55 KST 2021
+#1 SMP PREEMPT Tue Jul 27 12:56:37 CST 2021
+#1 SMP PREEMPT Thu Mar 31 17:50:05 CST 2022
+#1 SMP PREEMPT Wed Jul 4 17:14:02 CST 2018
+#1 SMP PREEMPT Tue Jul 2 18:27:36 CST 2019
+#1 SMP PREEMPT Wed Jun 5 18:24:31 CST 2019
+#1 SMP PREEMPT Tue Apr 14 20:01:41 2020
+#1 SMP PREEMPT Tue Jun 2 14:01:41 CST 2020
+#1 SMP PREEMPT Tue Oct 2 01:21:13 JST 2018
+#2 SMP PREEMPT Thu Sep 19 21:07:16 KST 2019
+#1 SMP PREEMPT Thu Sep 26 11:02:53 CST 2019
+#2 SMP PREEMPT Fri Aug 6 21:41:08 CST 2021
+#1 SMP PREEMPT Fri Feb 14 10:20:09 KST 2020
+#1 SMP PREEMPT Mon Apr 9 05:19:48 JST 2018
+#1 SMP PREEMPT Mon May 11 18:03:43 CST 2020
+#1 SMP PREEMPT Mon Oct 18 21:28:20 CST 2021
+#1 SMP PREEMPT Sat Jul 11 07:27:21 CDT 2020
+#1 SMP PREEMPT Tue Mar 16 14:09:41 KST 2021
+#1 SMP PREEMPT Thu Oct 22 17:58:34 CST 2020
+#1 SMP PREEMPT Wed May 8 10:47:12 CST 2019
+#2 SMP PREEMPT Wed Jan 8 01:49:56 CST 2020
+#1 SMP PREEMPT Wed Sep 2 11:06:46 CST 2020
+#8 SMP PREEMPT Thu Oct 24 21:02:08 CST 2019
+#1 SMP PREEMPT Thu Mar 22 18:11:33 CST 2018
+#1 SMP PREEMPT Tue Sep 18 12:05:30 CST 2018
+#1 SMP PREEMPT Sun Apr 24 12:26:18 CST 2022
+#1 SMP PREEMPT Sat Jan 8 17:24:46 CST 2022
+#1 SMP PREEMPT Fri Dec 31 11:18:16 CST 2021
+#1 SMP PREEMPT Fri Jul 12 19:35:52 2019
+#1 SMP PREEMPT Sat May 23 10:46:44 KST 2020
+#1 SMP PREEMPT Wed Nov 27 04:00:33 CST 2019
+#1 SMP PREEMPT Fri Feb 5 14:25:28 CST 2021
+#6 SMP PREEMPT Fri Jan 21 14:04:04 UTC 2022
+#1 SMP PREEMPT Wed Nov 27 22:10:12 CST 2019
+#2 SMP PREEMPT Sat Dec 7 00:11:06 CST 2019
+#1 SMP PREEMPT Tue Dec 1 02:15:54 IST 2020
+#1 SMP PREEMPT Mon Jan 22 17:09:55 EST 2018
+#1 SMP PREEMPT Fri Aug 20 18:29:36 CST 2021
+#1 SMP PREEMPT Mon Feb 4 18:44:37 KST 2019
+#2 SMP PREEMPT Wed May 15 16:48:08 KST 2019
+#1 SMP PREEMPT Sun Apr 26 12:08:10 CST 2020
+#1 SMP PREEMPT Sun Apr 28 20:39:41 CST 2019
+#1 SMP PREEMPT Wed May 29 14:04:09 PDT 2019
+#1 SMP PREEMPT Wed May 20 03:50:38 CST 2020
+#2 SMP PREEMPT Mon Mar 2 19:56:06 KST 2020
+#1 SMP PREEMPT Sat Oct 20 18:41:41 CST 2018
+#0 SMP PREEMPT Mon Jul 5 01:55:12 UTC 2021
+#1 SMP PREEMPT Mon Jan 10 16:05:49 UTC 2022
+#1 SMP PREEMPT Tue Mar 19 18:34:27 CST 2019
+#1 SMP PREEMPT Tue Nov 23 04:58:14 CST 2021
+#1 SMP PREEMPT Fri Apr 26 10:38:13 KST 2019
+#1 SMP PREEMPT Sat Sep 18 09:42:52 CST 2021
+#1 SMP PREEMPT Tue Jan 14 18:02:09 CST 2020
+#1 SMP PREEMPT Mon Oct 19 06:26:20 UTC 2020
+#1 SMP PREEMPT Sun Jun 21 23:16:42 CST 2020
+#1 SMP PREEMPT Tue Aug 18 01:03:36 CDT 2020
+#2 SMP PREEMPT Thu Dec 24 16:32:01 CST 2020
+#1 SMP PREEMPT Fri May 28 02:12:51 CST 2021
+#1 SMP PREEMPT Fri Apr 23 09:18:35 KST 2021
+#1 SMP PREEMPT Wed Oct 21 22:04:12 CST 2020
+#2 SMP PREEMPT Thu Oct 15 01:37:37 CST 2020
+#1 SMP PREEMPT Tue Dec 29 14:12:00 IST 2020
+#2 SMP PREEMPT Fri Nov 23 20:57:43 CST 2018
+#1 SMP PREEMPT Fri Aug 2 16:23:26 KST 2019
+#1 SMP PREEMPT Tue Jul 28 13:14:38 CDT 2020
+#1 SMP PREEMPT Fri May 15 17:00:18 CST 2020
+#1 SMP PREEMPT Tue Nov 3 18:17:08 KST 2020
+#1 SMP PREEMPT Tue Jul 7 01:50:32 PDT 2020
+#1 SMP PREEMPT Thu Dec 20 20:10:52 CST 2018
+#1 SMP PREEMPT Wed Oct 27 19:49:23 CST 2021
+#1 SMP PREEMPT Wed Mar 30 17:06:16 CST 2022
+#1 SMP PREEMPT Thu Dec 2 00:04:38 CST 2021
+#1 SMP PREEMPT Tue Sep 25 15:17:14 CST 2018
+#8 SMP PREEMPT Fri Feb 1 02:56:45 CST 2019
+#1 SMP PREEMPT Tue Jul 31 13:13:43 CST 2018
+#1 SMP PREEMPT Fri Dec 18 00:36:26 CST 2020
+#2 SMP PREEMPT Wed Feb 12 18:56:20 KST 2020
+#1 SMP PREEMPT Fri Mar 15 17:13:23 CST 2019
+#2 SMP PREEMPT Tue Nov 26 11:34:01 KST 2019
+#1 SMP PREEMPT Thu Oct 28 20:31:33 UTC 2021
+#2 SMP PREEMPT Mon Jul 13 14:45:18 CST 2020
+#2 SMP PREEMPT Tue May 29 17:52:51 CST 2018
+#3 SMP PREEMPT Mon Feb 7 04:08:10 JST 2022
+#1 SMP PREEMPT Fri Mar 1 00:10:02 EST 2019
+#1 SMP PREEMPT Fri Apr 27 05:34:14 JST 2018
+#1 SMP PREEMPT Tue Feb 18 02:45:04 KST 2020
+#1 SMP PREEMPT Tue Oct 29 01:26:23 CST 2019
+#1 SMP PREEMPT Sat Jul 4 19:16:42 CST 2020
+#1 SMP PREEMPT Fri Sep 10 18:49:44 UTC 2021
+#1 SMP PREEMPT Sat Feb 6 11:58:28 IST 2021
+#1 SMP PREEMPT Tue Nov 27 23:47:30 KST 2018
+#2 SMP PREEMPT Thu Jan 17 15:10:07 CST 2019
+#1 SMP PREEMPT Thu Dec 9 23:32:42 CST 2021
+#1 SMP PREEMPT Thu Aug 2 07:47:40 CDT 2018
+#1 SMP PREEMPT Fri Dec 3 06:28:02 CST 2021
+#1 SMP PREEMPT Tue Jul 13 02:32:30 JST 2021
+#1 SMP PREEMPT Wed Jan 8 12:39:51 PST 2020
+#1 SMP PREEMPT Fri Jul 10 11:13:16 KST 2020
+#1 SMP PREEMPT Wed Dec 22 15:47:09 CST 2021
+#1 SMP PREEMPT Mon Mar 14 05:08:37 CST 2022
+#1 SMP PREEMPT Wed Aug 21 23:55:36 WIB 2019
+#1 SMP PREEMPT Thu Jul 18 11:31:11 IST 2019
+#1 SMP PREEMPT Mon Oct 5 21:46:12 KST 2020
+#2 SMP PREEMPT Tue May 12 16:34:18 CST 2020
+#2 SMP PREEMPT Tue Jul 31 00:02:50 CST 2018
+#2 SMP PREEMPT Thu Oct 3 11:57:02 KST 2019
+#1 SMP PREEMPT Thu Mar 8 15:34:50 KST 2018
+#24 SMP PREEMPT Thu Dec 12 10:09:55 CST 2019
+#1 SMP PREEMPT Tue Apr 7 16:39:13 KST 2020
+#1 SMP PREEMPT Mon Apr 22 14:31:13 KST 2019
+#1 SMP PREEMPT Wed Mar 27 17:38:38 KST 2019
+#1 SMP PREEMPT Fri Mar 4 16:51:44 CST 2022
+#1 SMP PREEMPT Thu Apr 2 09:22:17 CST 2020
+#1 SMP PREEMPT Tue May 4 21:43:30 PDT 2021
+#1 SMP PREEMPT Fri May 25 10:34:23 WIB 2018
+#1 SMP PREEMPT Wed Jan 30 15:55:50 CST 2019
+#1 SMP PREEMPT Tue Dec 31 16:56:14 CST 2019
+#7 SMP PREEMPT Fri Dec 28 14:26:56 CST 2018
+#1 SMP PREEMPT Thu Dec 23 16:48:29 KST 2021
+#1 SMP PREEMPT Fri Feb 15 18:08:44 CST 2019
+#1 SMP PREEMPT Thu Mar 10 00:19:08 IST 2022
+#1 SMP PREEMPT Tue Jul 7 03:46:55 CST 2020
+#2 SMP PREEMPT Wed Nov 13 01:37:36 CST 2019
+#1 SMP PREEMPT Mon Oct 18 17:40:09 UTC 2021
+#1 SMP PREEMPT Mon Nov 12 19:45:22 CST 2018
+#1 SMP PREEMPT Thu Mar 18 22:20:52 -04 2021
+#1 SMP PREEMPT Fri Feb 25 16:46:24 CST 2022
+#1 SMP PREEMPT Mon Jul 19 12:01:41 CST 2021
+#1 SMP PREEMPT Thu Dec 20 01:13:07 KST 2018
+#1 SMP PREEMPT Wed Nov 4 16:33:43 JST 2020
+#1 SMP PREEMPT Fri Dec 6 20:35:56 CST 2019
+#1 SMP PREEMPT Wed Sep 19 16:08:35 KST 2018
+#1 SMP PREEMPT Tue May 12 06:17:23 CST 2020
+#2 SMP PREEMPT Fri Dec 27 09:08:18 CET 2019
+#1 SMP PREEMPT Tue Apr 13 21:07:53 CST 2021
+#1 SMP PREEMPT Thu May 17 16:45:47 KST 2018
+#1 SMP PREEMPT Wed Jul 3 16:38:26 2019
+#1 SMP PREEMPT Thu Jul 8 23:36:27 CST 2021
+#1 SMP PREEMPT Mon Jun 28 12:43:01 KST 2021
+#2 SMP PREEMPT Wed Jun 12 16:05:50 KST 2019
+#1 SMP PREEMPT Sat Apr 21 06:26:33 CST 2018
+#2 SMP PREEMPT Wed Dec 23 13:11:57 CST 2020
+#1 SMP PREEMPT Fri Aug 7 07:27:51 GMT 2020
+#1 SMP PREEMPT Sat Jan 1 22:24:09 PST 2022
+#2 SMP PREEMPT Thu Oct 29 06:12:49 CST 2020
+#8 SMP PREEMPT Tue Dec 4 00:37:43 CST 2018
+#1 SMP PREEMPT Sat Aug 25 01:04:49 HKT 2018
+#1 SMP PREEMPT Thu Sep 3 10:43:54 CST 2020
+#1 SMP PREEMPT Sun Mar 20 15:36:50 CST 2022
+#1 SMP PREEMPT Thu Dec 5 23:12:42 CST 2019
+#1 SMP PREEMPT Thu Oct 29 18:25:52 CST 2020
+#1 SMP PREEMPT Tue Sep 28 21:37:46 CST 2021
+#1 SMP PREEMPT Fri Jan 3 18:54:38 CST 2020
+#1 SMP PREEMPT Tue Nov 23 17:33:13 CST 2021
+#5 SMP Tue Jul 7 17:14:55 CEST 2020
+#1 SMP PREEMPT Mon Nov 27 17:10:49 KST 2017
+#1 SMP PREEMPT Tue Aug 13 17:41:14 CST 2019
+#1 SMP PREEMPT Fri Nov 8 17:36:23 CST 2019
+#1 SMP PREEMPT Wed Oct 7 16:20:04 KST 2020
+#1 SMP PREEMPT Mon Mar 12 16:06:38 CST 2018
+#1 SMP PREEMPT Thu Nov 7 21:10:35 CST 2019
+#1 SMP PREEMPT Mon Mar 14 16:55:06 CST 2022
+#1 SMP PREEMPT Tue Aug 6 17:48:57 CST 2019
+#2 SMP PREEMPT Wed Mar 6 10:12:00 CST 2019
+#1 SMP PREEMPT Mon Apr 25 23:46:17 CST 2022
+#14 SMP PREEMPT Fri Mar 30 15:38:17 CST 2018
+#1 SMP PREEMPT Mon Aug 26 22:38:45 CST 2019
+#1 SMP PREEMPT Tue Jun 8 11:05:30 KST 2021
+#1 SMP PREEMPT Sat Dec 29 15:26:06 CST 2018
+#2 SMP PREEMPT Sat Apr 27 05:10:55 CST 2019
+#1 SMP PREEMPT Wed Apr 20 11:29:58 UTC 2022
+#1 SMP PREEMPT Fri Jun 18 00:57:35 CST 2021
+#1 SMP PREEMPT Fri Jun 12 21:33:27 CST 2020
+#1 SMP PREEMPT Sat Mar 5 01:29:19 CST 2022 f2fs-hash:52df6f035d4
+#1 SMP PREEMPT Thu Mar 1 03:12:54 CST 2018
+#1 SMP PREEMPT Wed Sep 25 15:16:37 KST 2019
+#1 SMP PREEMPT Thu Feb 28 22:00:12 CST 2019
+#1 SMP PREEMPT Thu Dec 3 21:12:37 CST 2020
+#1 SMP PREEMPT Mon Jan 24 21:53:51 CST 2022
+#1 SMP PREEMPT Thu May 14 20:12:51 KST 2020
+#1 SMP PREEMPT Fri Nov 12 19:46:20 CST 2021
+#1 SMP PREEMPT Tue Feb 18 23:52:10 PST 2020
+#2 SMP PREEMPT Mon Feb 22 13:32:48 CST 2021
+#1 SMP PREEMPT Tue Dec 28 11:25:15 PST 2021
+#1 SMP PREEMPT Thu Oct 11 19:39:39 UTC 2018
+#2 SMP PREEMPT Tue Feb 9 11:44:14 KST 2021
+#1 SMP PREEMPT Fri Feb 28 20:15:37 CST 2020
+#1 SMP PREEMPT Tue Jan 12 16:37:20 CST 2021
+#1 SMP PREEMPT Mon Jan 14 16:34:21 CST 2019
+#1 SMP PREEMPT Thu Jan 21 16:08:45 CST 2021
+#1 SMP PREEMPT Fri Jun 21 15:25:40 CST 2019
+#1 SMP PREEMPT Fri Aug 28 17:07:26 CST 2020
+#1 SMP PREEMPT Fri Dec 6 21:30:58 KST 2019
+#1 SMP PREEMPT Thu Jan 9 18:27:50 CST 2020
+#1 SMP PREEMPT Sun Oct 7 20:58:17 CST 2018
+#2 SMP PREEMPT Thu Jan 24 21:27:21 CST 2019
+#1 SMP PREEMPT Mon Apr 6 22:48:36 JST 2020
+#1 SMP PREEMPT Mon Feb 8 10:13:56 CET 2021
+#2 SMP PREEMPT Fri Apr 12 14:15:05 KST 2019
+#1 SMP PREEMPT Mon May 17 03:55:20 CST 2021
+#1 SMP PREEMPT Sat Apr 28 10:53:20 CST 2018
+#2 SMP PREEMPT Tue Mar 15 19:57:34 CST 2022
+#2 SMP PREEMPT Wed Oct 21 20:23:42 CST 2020
+#1 SMP PREEMPT Fri Mar 16 15:12:33 KST 2018
+#1 SMP PREEMPT Wed Mar 4 23:17:16 KST 2020
+#2 SMP PREEMPT Sat May 15 14:13:49 CST 2021
+#1 SMP PREEMPT Fri Jan 19 00:01:07 CST 2018
+#1 SMP PREEMPT Fri Jun 14 19:30:18 CST 2019
+#1 SMP PREEMPT Tue Oct 1 10:42:12 KST 2019
+#1 SMP PREEMPT Thu Aug 15 02:38:53 PDT 2019
+#1 SMP PREEMPT Wed Nov 17 19:25:08 CST 2021
+#2 SMP PREEMPT Wed Oct 30 15:22:45 CST 2019
+#1 SMP PREEMPT Thu Jun 24 15:53:19 CST 2021
+#1 SMP PREEMPT Mon Nov 11 13:17:09 CST 2019
+#1 SMP PREEMPT Wed Apr 15 13:20:36 PDT 2020
+#1 SMP PREEMPT Tue Jan 18 22:53:08 CST 2022
+#2 SMP PREEMPT Mon Mar 25 11:50:11 CST 2019
+#1 SMP PREEMPT Sat Aug 28 01:08:43 EDT 2021
+#1 SMP PREEMPT Thu Jul 18 01:49:22 CST 2019
+#1 SMP PREEMPT Tue Jun 4 10:32:16 CST 2019
+#1 SMP PREEMPT Wed Mar 6 18:07:06 CST 2019
+#1 SMP PREEMPT Sat May 7 10:39:05 UTC 2022
+#1 SMP PREEMPT Tue Jul 14 16:56:23 KST 2020
+#1 SMP PREEMPT Sat Jan 29 00:10:15 UTC 2022
+#1 SMP PREEMPT Tue Dec 29 10:03:48 CST 2020
+#1 SMP PREEMPT Fri Jul 17 19:20:13 CST 2020
+#1 SMP PREEMPT Tue Sep 17 10:47:59 2019
+#1 SMP PREEMPT Thu Jan 27 00:54:59 CST 2022
+#1 SMP PREEMPT Fri Aug 10 09:28:55 PDT 2018
+#1 SMP PREEMPT Tue Dec 18 15:47:42 CST 2018
+#1 SMP PREEMPT Wed Nov 4 13:09:37 -03 2020
+#1 SMP PREEMPT Thu Jan 14 17:44:31 -02 2021
+#1 SMP PREEMPT Sat Apr 2 16:52:52 CST 2022
+#1 SMP PREEMPT Thu Nov 7 00:28:25 WIB 2019
+#1 SMP PREEMPT Thu Nov 7 18:57:48 CST 2019
+#1 SMP PREEMPT Mon Apr 25 11:57:33 CST 2022
+#1 SMP PREEMPT Thu Aug 2 17:27:56 KST 2018
+#29 SMP PREEMPT Mon Aug 5 15:07:22 CST 2019
+#1 SMP PREEMPT Tue Jul 3 23:39:22 KST 2018
+#1 SMP PREEMPT Fri May 24 18:39:15 CST 2019
+#1 SMP PREEMPT Sat Dec 23 10:00:06 CST 2017
+#1 SMP PREEMPT Sun Apr 24 12:56:59 PDT 2022
+#1 SMP PREEMPT Tue Aug 14 14:56:53 KST 2018
+#1 SMP PREEMPT Tue Apr 19 18:32:43 CST 2022
+#1 SMP PREEMPT Mon Sep 17 22:10:21 CST 2018
+#1 SMP PREEMPT Fri Dec 20 12:08:54 CST 2019
+#41 SMP PREEMPT Fri Mar 25 15:36:58 CST 2022
+#1 SMP PREEMPT Mon Jun 14 21:37:50 +07 2021
+#2 SMP PREEMPT Mon Aug 3 20:21:48 CST 2020
+#1 SMP PREEMPT Mon Dec 7 02:39:44 EST 2020
+#1 SMP PREEMPT Mon Apr 29 17:48:02 CST 2019
+#1 SMP PREEMPT Thu Mar 7 21:40:17 CST 2019
+#1 SMP PREEMPT Thu Mar 17 17:13:41 CST 2022
+#1 SMP PREEMPT Tue Aug 21 10:46:48 CST 2018
+#2 SMP PREEMPT Thu Oct 22 16:46:18 KST 2020
+#1 SMP PREEMPT Wed Sep 23 23:16:05 KST 2020
+#1 SMP PREEMPT Fri Feb 26 11:54:44 CST 2021
+#1 SMP PREEMPT Mon Jun 1 11:49:23 CST 2020
+#1 SMP PREEMPT Fri Jul 26 03:31:03 CST 2019
+#1 SMP PREEMPT Thu Sep 9 16:11:33 CST 2021
+#1 SMP PREEMPT Fri Dec 4 13:11:33 CST 2020
+#1 SMP PREEMPT Sat Jan 20 04:22:11 CST 2018
+#1 SMP PREEMPT Thu Oct 22 16:21:35 JST 2020
+#1 SMP PREEMPT Sat Mar 19 04:35:41 CST 2022
+#1 SMP PREEMPT Tue Jan 18 02:08:35 CST 2022
+#1 SMP PREEMPT Tue May 5 00:49:22 PDT 2020
+#1 SMP PREEMPT Thu Feb 17 17:09:33 CST 2022
+#1 SMP PREEMPT Thu Jan 11 10:44:50 KST 2018
+#1 SMP PREEMPT Wed Jun 17 17:08:33 CST 2020
+#1 SMP PREEMPT Wed Jul 14 09:48:34 CDT 2021
+#1 SMP PREEMPT Fri Jun 14 12:56:17 KST 2019
+#2 SMP PREEMPT Mon Jun 7 15:45:04 CST 2021
+#1 SMP PREEMPT Wed Jul 29 05:04:08 CST 2020
+#2 SMP PREEMPT Wed Apr 29 19:19:43 KST 2020
+#1 SMP PREEMPT Thu Aug 2 19:51:48 CST 2018
+#1 SMP PREEMPT Thu Jan 25 18:20:44 CST 2018
+#1 SMP PREEMPT Thu Nov 28 12:21:51 CST 2019
+#1 SMP PREEMPT Mon Apr 27 00:42:16 CST 2020
+#2 SMP PREEMPT Wed Aug 29 16:20:50 CST 2018
+#1 SMP PREEMPT Mon Sep 27 19:04:34 CST 2021
+#1 SMP PREEMPT Mon Sep 30 22:14:38 KST 2019
+#1 SMP PREEMPT Wed Mar 6 10:15:42 CST 2019
+#1 SMP PREEMPT Tue Aug 17 17:22:04 +07 2021
+#1 SMP PREEMPT Fri Oct 12 03:52:18 CST 2018
+#1 SMP PREEMPT Thu Dec 16 17:45:28 CST 2021
+#1 SMP PREEMPT Thu Jun 27 16:08:16 CST 2019
+#1 SMP PREEMPT Fri Jan 5 15:43:16 CST 2018
+#1 SMP PREEMPT Tue Aug 17 23:58:50 CST 2021
+#1 SMP PREEMPT Tue Apr 9 21:51:03 PDT 2019
+#1 SMP PREEMPT Thu May 12 16:12:52 CST 2022
+#1 SMP PREEMPT Thu Aug 27 23:01:53 CST 2020
+#1 SMP PREEMPT Sat Apr 17 14:07:51 CST 2021
+#1 SMP PREEMPT Mon Feb 26 11:58:45 WIB 2018
+#1 SMP PREEMPT Wed Jun 30 09:21:12 KST 2021
+#1 SMP PREEMPT Thu Jul 4 20:22:00 CST 2019
+#2 SMP PREEMPT Thu May 31 12:10:55 CST 2018
+#1 SMP PREEMPT Tue Jul 10 18:58:51 CDT 2018
+#1 SMP PREEMPT Fri Nov 27 12:13:03 CST 2020
+#1 SMP PREEMPT Tue Sep 22 11:08:45 KST 2020
+#1 SMP PREEMPT Thu Nov 5 23:04:22 CST 2020
+#1 SMP PREEMPT Thu Jun 18 21:15:43 HKT 2020
+#1 SMP PREEMPT Wed May 25 07:18:25 PDT 2022
+#1 SMP PREEMPT Sat May 8 12:14:03 IST 2021
+#1 SMP PREEMPT Tue Apr 30 22:40:50 CST 2019
+#1 SMP PREEMPT Thu Jan 6 01:41:13 CST 2022
+#1 SMP PREEMPT Fri Jul 30 02:50:38 CDT 2021
+#1 SMP PREEMPT Mon Apr 22 21:39:10 CST 2019
+#1 SMP PREEMPT Thu Oct 29 00:22:26 CST 2020
+#1 SMP PREEMPT Wed Oct 28 16:05:37 CDT 2020
+#1 SMP PREEMPT Wed May 5 15:51:08 +07 2021
+#1 SMP PREEMPT Fri Aug 24 03:56:04 CST 2018
+#1 SMP PREEMPT Fri Jun 8 15:09:51 CST 2018
+#1 SMP PREEMPT Sun Mar 10 15:59:09 CST 2019
+#1 SMP PREEMPT Fri Dec 17 18:14:56 CST 2021
+#1 SMP PREEMPT Thu Jul 30 06:33:34 CDT 2020
+#1 SMP PREEMPT Thu Nov 21 16:07:33 CET 2019
+#1 SMP PREEMPT Fri Jul 31 11:28:43 CST 2020
+#1 SMP PREEMPT Fri Feb 11 22:10:12 UTC 2022
+#1 SMP PREEMPT Thu Sep 17 14:47:27 KST 2020
+#1 SMP PREEMPT Mon Nov 29 20:06:50 CST 2021
+#1 SMP PREEMPT Wed May 22 22:17:16 CST 2019
+#1 SMP PREEMPT Mon Feb 1 20:41:58 CST 2021
+#1 SMP PREEMPT Sat Feb 12 11:12:03 CST 2022
+#1 SMP PREEMPT Tue Jul 14 03:06:33 CST 2020
+#1 SMP PREEMPT Mon Apr 4 03:15:01 CST 2022
+#1 SMP Wed Nov 25 10:04:20 CET 2020
+#1 SMP PREEMPT Tue Feb 26 23:50:49 PST 2019
+#1 SMP PREEMPT Thu Aug 6 06:34:51 CDT 2020
+#1 SMP PREEMPT Fri Jan 14 00:08:48 CST 2022
+#1 SMP PREEMPT Thu Mar 3 16:53:38 CST 2022
+#1 SMP PREEMPT Sat Oct 19 18:20:20 CST 2019
+#1 SMP PREEMPT Mon Dec 13 15:32:28 CST 2021
+#1 SMP PREEMPT Fri Aug 2 17:56:48 JST 2019
+#1 SMP PREEMPT Wed Jul 11 17:47:28 CST 2018
+#1 SMP PREEMPT Wed Jul 24 06:26:03 CST 2019
+#1 SMP PREEMPT Sun Mar 13 18:11:29 CST 2022
+#1 SMP PREEMPT Fri Nov 5 11:41:15 CST 2021
+#1 SMP PREEMPT Thu Mar 22 21:40:24 KST 2018
+#1 SMP PREEMPT Wed Jan 16 00:14:12 PST 2019
+#1 SMP PREEMPT Fri Nov 22 20:30:14 KST 2019
+#4 SMP PREEMPT Wed Aug 7 21:04:59 CST 2019
+#1 SMP PREEMPT Thu May 24 17:25:49 CST 2018
+#1 SMP PREEMPT Fri Jun 18 15:12:10 CST 2021
+#1 SMP PREEMPT Thu Sep 3 21:38:08 CST 2020
+#2 SMP PREEMPT Thu Feb 28 01:09:01 CST 2019
+#1 SMP PREEMPT Thu Dec 16 00:03:50 KST 2021
+#1 SMP PREEMPT Fri Mar 6 15:48:51 CST 2020
+#1 SMP PREEMPT Fri Dec 17 18:07:28 CST 2021
+#1 SMP PREEMPT Fri May 7 13:35:10 KST 2021
+#1 SMP PREEMPT Thu Jul 25 16:27:19 KST 2019
+#1 SMP PREEMPT Thu Nov 15 19:37:06 KST 2018
+#2 SMP PREEMPT Wed May 25 20:27:11 CST 2022
+#1 SMP PREEMPT Mon Mar 23 13:15:19 CST 2020
+#1 SMP PREEMPT Wed Aug 28 13:07:21 KST 2019
+#1 SMP PREEMPT Mon May 24 22:13:11 WIB 2021
+#1 SMP PREEMPT Thu Aug 22 16:31:52 KST 2019
+#1 SMP PREEMPT Thu Jan 11 16:00:33 JST 2018
+#39 SMP PREEMPT Mon Jul 23 14:50:04 CST 2018
+#1 SMP PREEMPT Thu Sep 13 20:38:13 BRT 2018
+#1 SMP PREEMPT Thu Jul 9 22:55:29 CST 2020
+#1 SMP PREEMPT Wed Oct 10 05:01:16 CST 2018
+#2 SMP PREEMPT Thu Jun 28 01:37:03 CST 2018
+#1 SMP PREEMPT Wed Nov 28 19:28:04 CST 2018
+#1 SMP PREEMPT Mon Jul 5 22:18:59 CST 2021
+#1 SMP PREEMPT Fri Sep 24 17:01:28 CST 2021
+#1 SMP PREEMPT Thu Feb 6 17:41:14 CST 2020
+#1 SMP PREEMPT Mon May 28 19:29:49 KST 2018
+#1 SMP PREEMPT Mon May 16 08:48:31 JST 2022
+#1 SMP PREEMPT Tue Dec 25 17:56:16 CST 2018
+#1 SMP PREEMPT Fri Mar 12 15:59:21 CST 2021
+#1 SMP PREEMPT Wed Mar 16 15:23:52 CST 2022
+#1 SMP PREEMPT Wed Jan 19 13:36:52 CST 2022
+#2 SMP PREEMPT Thu Jul 12 16:14:44 KST 2018
+#1 SMP PREEMPT Thu Oct 28 04:18:40 JST 2021
+#1 SMP PREEMPT Fri Sep 20 19:20:58 CST 2019
+#1 SMP PREEMPT Thu Nov 15 04:15:02 PST 2018
+#1 SMP PREEMPT Thu May 6 20:21:39 CST 2021
+#1 SMP PREEMPT Tue Apr 14 17:24:41 KST 2020
+#1 SMP PREEMPT Tue Aug 4 12:10:07 CST 2020
+#1 SMP PREEMPT Mon Jan 24 11:50:14 CST 2022
+#1 SMP PREEMPT Thu May 21 20:14:18 KST 2020
+#1 SMP PREEMPT Fri Oct 25 19:07:14 CST 2019
+#1 SMP PREEMPT Tue Jun 12 17:03:20 CST 2018
+#2 SMP PREEMPT Tue Nov 17 16:42:37 KST 2020
+#1 SMP PREEMPT Wed Mar 18 13:02:17 KST 2020
+#1 SMP PREEMPT Fri Aug 7 09:41:23 CST 2020
+#2 SMP PREEMPT Fri Jul 3 15:12:14 JST 2020
+#2 SMP PREEMPT Mon Jan 25 22:02:09 CST 2021
+#0 SMP PREEMPT Tue Sep 24 20:13:32 UTC 2019
+#1 SMP PREEMPT Wed Jun 2 08:12:04 CST 2021
+#1 SMP PREEMPT Fri Jul 26 04:03:32 CST 2019
+#1 SMP PREEMPT Fri Dec 4 14:16:28 KST 2020
+#1 SMP PREEMPT Fri May 31 15:57:19 CST 2019
+#1 SMP PREEMPT Tue Jul 20 19:19:43 IST 2021
+#2 SMP PREEMPT Fri Dec 24 11:55:31 CST 2021
+#1 SMP PREEMPT Fri Mar 8 15:54:52 CST 2019
+#1 SMP PREEMPT Mon Jan 21 11:18:11 KST 2019
+#1 SMP PREEMPT Fri Aug 14 17:29:01 CST 2020
+#1 SMP PREEMPT Mon Jul 1 19:37:49 KST 2019
+#1 SMP PREEMPT Mon Jul 22 20:19:06 KST 2019
+#1 SMP PREEMPT Thu Jun 14 15:04:20 KST 2018
+#1 SMP PREEMPT Sat Jan 19 12:59:35 CST 2019
+#1 SMP PREEMPT Thu Jul 26 06:13:26 CST 2018
+#1 SMP PREEMPT Fri Jul 10 04:50:41 CST 2020
+#1 SMP PREEMPT Mon Aug 5 19:55:33 KST 2019
+#1 SMP PREEMPT Tue Apr 16 22:52:33 CST 2019
+#1 SMP PREEMPT Tue Nov 26 15:04:04 CST 2019
+#1 SMP PREEMPT Wed Aug 18 16:25:33 CST 2021
+#1 SMP PREEMPT Thu May 13 21:31:27 CST 2021
+#2 SMP PREEMPT Thu Oct 10 16:07:59 CST 2019
+#1 SMP PREEMPT Thu Sep 24 18:11:12 CST 2020
+#1 SMP PREEMPT Mon Sep 14 15:51:28 CST 2020
+#1 SMP PREEMPT Sat Mar 20 20:53:47 PDT 2021
+#1 SMP PREEMPT Sat Dec 25 20:06:15 CST 2021
+#1 SMP PREEMPT Fri Mar 11 01:01:17 JST 2022
+#1 SMP PREEMPT Thu Jan 10 15:31:09 CST 2019
+#1 SMP PREEMPT Fri Jul 27 19:23:11 CST 2018
+#1 SMP PREEMPT Fri Apr 26 14:05:39 CDT 2019
+#15 SMP PREEMPT Tue Jul 10 16:38:48 CST 2018
+#2 SMP PREEMPT Mon Jul 30 03:15:34 CST 2018
+#1 SMP PREEMPT Thu Oct 29 12:38:58 CST 2020
+#1 SMP PREEMPT Tue Jun 30 19:50:46 IST 2020
+#1 SMP PREEMPT Sat Dec 22 18:51:05 CST 2018
+#1 SMP PREEMPT Thu May 23 22:51:57 KST 2019
+#1 SMP PREEMPT Fri Jul 10 19:14:02 CST 2020
+#2 SMP PREEMPT Tue Nov 24 15:06:43 CST 2020
+#1 SMP PREEMPT Tue Jul 16 16:29:56 JST 2019
+#2 SMP PREEMPT Wed Feb 9 19:15:53 CST 2022
+#1 SMP PREEMPT Mon Mar 15 21:14:07 PDT 2021
+#1 SMP PREEMPT Thu Dec 6 18:54:56 KST 2018
+#1 SMP PREEMPT Tue Oct 20 14:58:27 CST 2020
+#1 SMP PREEMPT Sun Aug 1 08:59:12 CST 2021
+#1 SMP PREEMPT Tue Dec 21 18:53:52 KST 2021
+#1 SMP PREEMPT Wed Feb 9 03:25:00 CST 2022
+#1 SMP PREEMPT Fri Jun 8 11:25:57 CST 2018
+#1 SMP PREEMPT Fri Aug 31 20:59:54 CST 2018
+#1 SMP PREEMPT Fri Aug 7 06:08:26 CST 2020
+#1 SMP PREEMPT Fri Sep 3 00:17:50 UTC 2021
+#2 SMP PREEMPT Mon Dec 3 17:02:43 CST 2018
+#1 SMP PREEMPT Wed Apr 18 04:47:57 CST 2018
+#6 SMP PREEMPT Tue Mar 20 11:27:30 CST 2018
+#1 SMP PREEMPT Wed May 18 21:50:13 CST 2022
+#1 SMP PREEMPT Tue Jun 22 12:42:22 CST 2021
+#2 SMP PREEMPT Thu Mar 21 07:01:07 CST 2019
+#1 SMP PREEMPT Thu Apr 23 13:15:03 CST 2020
+#1 SMP PREEMPT Mon Mar 30 22:50:39 PDT 2020
+#1 SMP PREEMPT Tue Apr 30 22:53:28 CST 2019
+#1 SMP PREEMPT Thu May 3 13:11:39 CST 2018
+#1 SMP PREEMPT Fri Jan 7 02:28:34 CST 2022
+#2 SMP PREEMPT Thu Aug 13 01:17:39 CST 2020
+#2 SMP PREEMPT Mon Sep 10 16:10:59 CST 2018
+#1 SMP PREEMPT Thu Sep 20 12:02:02 KST 2018
+#1 SMP PREEMPT Thu May 31 21:34:13 IST 2018
+#1 SMP PREEMPT Tue Mar 6 18:16:34 CST 2018
+#1 SMP PREEMPT Wed Aug 26 04:07:01 CDT 2020
+#1 SMP PREEMPT Thu Sep 13 12:30:16 HKT 2018
+#1 SMP PREEMPT Sat Jan 9 01:43:19 IST 2021
+#1 SMP PREEMPT Sat Aug 3 01:00:13 KST 2019
+#1 SMP PREEMPT Sat Jun 27 03:48:42 CDT 2020
+#2 SMP PREEMPT Mon Sep 21 18:37:33 CST 2020
+#323 SMP PREEMPT Thu Mar 10 09:21:21 CST 2022
+#1 SMP PREEMPT Mon Oct 19 15:11:49 KST 2020
+#1 SMP PREEMPT Wed Jan 12 18:47:01 CST 2022
+#1 SMP PREEMPT Tue Jul 6 13:53:01 KST 2021
+#1 SMP PREEMPT Wed Apr 13 00:10:48 CST 2022
+#1 SMP PREEMPT Sat Oct 30 12:34:13 CST 2021
+#1 SMP PREEMPT Fri Jun 19 17:08:39 KST 2020
+#1 SMP PREEMPT Tue Nov 6 10:05:29 CST 2018
+#1 SMP PREEMPT Fri Sep 18 23:34:26 CST 2020
+#1 SMP PREEMPT Wed Feb 20 09:23:55 KST 2019
+#1 SMP PREEMPT Wed Jan 3 12:45:19 CST 2018
+#1 SMP PREEMPT Thu Feb 28 03:36:07 PST 2019
+#1 SMP PREEMPT Tue Dec 10 00:11:12 PST 2019
+#2 SMP PREEMPT Mon Feb 7 03:52:22 JST 2022
+#1 SMP PREEMPT Tue Apr 26 11:40:17 PDT 2022
+#1 SMP PREEMPT Fri Nov 19 21:19:23 CST 2021
+#1 SMP PREEMPT Wed Dec 18 20:29:43 KST 2019
+#4 SMP PREEMPT Sat Mar 17 12:44:06 CST 2018
+#1 SMP PREEMPT Wed Mar 4 19:12:33 CST 2020
+#1 SMP PREEMPT Mon Jun 11 14:17:26 CST 2018
+#1 SMP PREEMPT Sat Aug 10 08:02:42 KST 2019
+#1 SMP PREEMPT Thu May 13 06:27:37 CST 2021
+#1 SMP PREEMPT Mon Mar 4 14:31:59 CST 2019
+#1 SMP PREEMPT Wed Mar 24 13:08:34 CST 2021
+#1 SMP PREEMPT Tue Mar 6 20:07:16 CST 2018
+#1 SMP PREEMPT Wed Oct 23 23:05:01 CST 2019
+#1 SMP PREEMPT Sun Mar 13 05:03:55 PDT 2022
+#1 SMP PREEMPT Thu Apr 1 13:01:12 CST 2021
+#1 SMP PREEMPT Mon Mar 1 19:17:41 IST 2021
+#1 SMP PREEMPT Sun Jan 6 23:50:00 PST 2019
+#1 SMP PREEMPT Tue Nov 28 02:18:11 CST 2017
+#1 SMP PREEMPT Thu Sep 26 06:04:21 GMT 2019
+#2 SMP PREEMPT Wed Jun 5 06:17:41 CST 2019
+#1 SMP PREEMPT Tue Aug 14 16:24:22 CST 2018
+#1 SMP PREEMPT Wed Nov 20 09:45:25 GMT 2019
+#1 SMP PREEMPT Wed Jul 21 16:28:46 CST 2021
+#1 SMP PREEMPT Wed May 11 15:12:13 CST 2022
+#1 SMP PREEMPT Thu Mar 21 03:09:34 CST 2019
+#1 SMP PREEMPT Tue Apr 9 12:39:06 CST 2019
+#1 SMP PREEMPT Sat Oct 3 14:25:50 CDT 2020
+#1 SMP PREEMPT Wed Jul 28 15:59:10 KST 2021
+#1 SMP PREEMPT Wed Aug 22 18:30:11 CST 2018
+#1 SMP PREEMPT Tue Dec 29 14:52:24 CST 2020
+#1 SMP PREEMPT Wed Jun 19 21:01:42 CST 2019
+#1 SMP PREEMPT Mon Apr 29 15:41:55 CST 2019
+#1 SMP PREEMPT Wed Feb 23 15:43:06 CST 2022
+#1 SMP PREEMPT Wed Apr 21 07:53:12 CST 2021
+#1 SMP PREEMPT Mon Feb 8 22:14:57 CST 2021
+#2 SMP PREEMPT Mon Jul 22 20:33:56 CST 2019
+#1 SMP PREEMPT Wed May 23 21:08:35 KST 2018
+#1 SMP PREEMPT Tue Oct 26 12:45:27 PDT 2021
+#1 SMP PREEMPT Fri Jan 21 00:09:47 CST 2022
+#3 SMP PREEMPT Tue May 18 09:19:00 EDT 2021
+#1 SMP PREEMPT Fri Jul 20 19:29:58 CST 2018
+#1 SMP PREEMPT Wed Feb 6 01:31:11 CST 2019
+#1 SMP PREEMPT Wed Dec 22 15:55:18 CST 2021
+#1 SMP PREEMPT Tue Mar 26 00:17:29 CST 2019
+#1 SMP PREEMPT Wed May 26 10:25:23 KST 2021
+#1 SMP PREEMPT Sun Apr 10 23:48:27 CST 2022
+#1 SMP PREEMPT Sat Jul 6 00:22:54 CST 2019
+#2 SMP PREEMPT Fri Mar 8 15:53:21 KST 2019
+#1 SMP PREEMPT Thu Sep 23 23:18:54 CST 2021
+#1 SMP PREEMPT Wed Nov 13 21:59:27 CST 2019
+#1 SMP PREEMPT Sun Apr 24 23:45:00 CST 2022
+#1 SMP PREEMPT Thu Nov 2 10:12:23 CDT 2017
+#1 SMP PREEMPT Wed Dec 27 14:09:32 CST 2017
+#1 SMP PREEMPT Thu Sep 16 16:28:14 CST 2021
+#2 SMP PREEMPT Wed Oct 10 02:00:01 CST 2018
+#1 SMP PREEMPT Wed Oct 31 11:52:54 KST 2018
+#1 SMP PREEMPT Sat Apr 23 01:34:50 CST 2022
+#1 SMP PREEMPT Mon Jun 1 12:54:33 PDT 2020
+#1 SMP PREEMPT Fri Jun 8 15:31:01 KST 2018
+#2 SMP PREEMPT Tue Jul 28 19:08:04 CST 2020
+#1 SMP PREEMPT Sat Sep 28 17:39:06 KST 2019
+#1 SMP PREEMPT Fri Dec 20 17:44:55 CST 2019
+#1 SMP PREEMPT Wed Apr 17 15:38:47 GMT 2019
+#1 SMP PREEMPT Thu Sep 20 17:35:23 CST 2018
+#1 SMP PREEMPT Tue Jan 22 22:24:56 CST 2019
+#1 SMP PREEMPT Wed Mar 9 21:02:02 CST 2022
+#1 SMP PREEMPT Wed Apr 29 03:15:57 CST 2020
+#1 SMP PREEMPT Mon Jan 24 09:42:33 CST 2022
+#1 SMP PREEMPT Mon Feb 1 20:26:24 PST 2021
+#1 SMP PREEMPT Wed Dec 6 14:02:42 JST 2017
+#1 SMP PREEMPT Thu Feb 28 14:10:24 CST 2019
+#2 SMP PREEMPT Tue Sep 10 04:45:46 CST 2019
+#1 SMP PREEMPT Wed Jul 3 16:38:26 2019
+#1 SMP PREEMPT Thu Mar 26 00:26:42 PDT 2020
+#1 SMP PREEMPT Thu Sep 12 12:44:15 CST 2019
+#1 SMP PREEMPT Sat Mar 28 11:56:26 CST 2020
+#2 SMP PREEMPT Thu Oct 18 11:10:44 CST 2018
+#1 SMP PREEMPT Fri Sep 14 23:35:18 CST 2018
+#1 SMP PREEMPT Wed Mar 31 15:19:06 KST 2021
+#1 SMP PREEMPT Sat Jan 18 11:25:19 CST 2020
+#1 SMP PREEMPT Tue Jan 25 14:52:06 KST 2022
+#1 SMP PREEMPT Tue Jan 11 19:28:01 UTC 2022
+#2 SMP PREEMPT Tue Dec 24 17:02:44 CST 2019
+#3 SMP PREEMPT Fri Jan 29 13:32:00 CST 2021
+#1 SMP PREEMPT Fri Oct 16 10:55:10 CST 2020
+#2 SMP PREEMPT Sun Sep 8 23:22:52 PDT 2019
+#1 SMP PREEMPT Tue Oct 27 11:54:14 CST 2020
+#1 SMP PREEMPT Wed Dec 11 16:17:01 CST 2019
+#1 SMP PREEMPT Tue Aug 6 13:14:24 KST 2019
+#1 SMP PREEMPT Wed Aug 1 05:45:02 CDT 2018
+#1 SMP PREEMPT Wed Oct 9 20:41:01 KST 2019
+#2 SMP PREEMPT Mon Jun 21 10:11:57 KST 2021
+#1 SMP PREEMPT Tue Dec 1 13:07:46 CST 2020
+#1 SMP PREEMPT Tue Feb 23 16:06:35 CST 2021
+#1 SMP PREEMPT Fri Nov 8 01:04:04 CST 2019
+#1 SMP PREEMPT Thu Dec 5 16:33:33 CST 2019
+#1 SMP PREEMPT Fri Mar 6 16:55:05 CST 2020
+#1 SMP PREEMPT Mon Aug 2 23:49:36 CST 2021
+#1 SMP PREEMPT Thu Mar 10 18:51:27 UTC 2022
+#2 SMP PREEMPT Fri Nov 15 03:02:13 CST 2019
+#1 SMP PREEMPT Mon Apr 1 15:04:53 CDT 2019
+#1 SMP PREEMPT Mon Jun 8 15:37:27 KST 2020
+#1 SMP PREEMPT Tue Nov 10 17:57:28 CST 2020
+#1 SMP PREEMPT Fri May 28 11:54:43 UTC 2021
+#1 SMP PREEMPT Fri Oct 9 11:02:11 KST 2020
+#1 SMP PREEMPT Wed Oct 14 13:04:53 CST 2020
+#10 SMP PREEMPT Wed Mar 13 17:54:56 CST 2019
+#1 SMP PREEMPT Wed Jan 8 01:44:01 CST 2020
+#1 SMP PREEMPT Tue Sep 11 23:19:48 PDT 2018
+#1 SMP PREEMPT Tue Nov 26 14:35:58 KST 2019
+#1 SMP PREEMPT Tue May 28 11:20:06 CDT 2019
+#1 SMP PREEMPT Thu Jan 4 05:22:04 EST 2018
+#1 SMP PREEMPT Mon May 13 18:41:33 CST 2019
+#1 SMP PREEMPT Fri Dec 18 05:58:27 CST 2020
+#1 SMP PREEMPT Wed Jul 17 03:41:04 CST 2019
+#2 SMP PREEMPT Tue Jan 7 11:47:18 CST 2020
+#1 SMP PREEMPT Wed Sep 30 23:49:49 CDT 2020
+#1 SMP PREEMPT Mon Jan 24 20:52:51 CST 2022
+#1 SMP PREEMPT Mon Feb 14 23:02:56 CST 2022
+#2 SMP PREEMPT Mon Sep 2 20:39:39 CST 2019
+#1 SMP PREEMPT Thu Oct 8 21:14:51 KST 2020
+#1 SMP PREEMPT Tue May 1 13:49:53 EDT 2018
+#1 SMP PREEMPT Wed Nov 25 15:38:12 CST 2020
+#1 SMP PREEMPT Sun Oct 4 17:27:14 CST 2020
+#1 SMP PREEMPT Mon Mar 7 17:28:00 CST 2022
+#1 SMP PREEMPT Wed Mar 30 01:25:01 CST 2022
+#1 SMP PREEMPT Thu Jul 18 02:07:35 CDT 2019
+#1 SMP PREEMPT Thu Nov 1 15:17:13 KST 2018
+#1 SMP PREEMPT Fri Feb 11 04:43:46 CST 2022
+#1 SMP PREEMPT Tue Jul 14 15:26:40 CST 2020
+#1 SMP PREEMPT Fri Nov 20 15:28:09 CST 2020
+#1 SMP PREEMPT Tue May 18 23:31:22 JST 2021
+#1 SMP PREEMPT Tue Jul 30 08:11:26 PDT 2019
+#1 SMP PREEMPT Tue Apr 7 20:43:46 PDT 2020
+#2 SMP PREEMPT Sat Sep 28 17:55:13 CST 2019
+#1 SMP PREEMPT Wed Jul 21 11:14:10 UTC 2021
+#1 SMP PREEMPT Thu May 23 16:32:40 KST 2019
+#1 SMP PREEMPT Mon Feb 19 03:17:34 CST 2018
+#1 SMP PREEMPT Mon Jul 13 23:44:17 CST 2020
+#1 SMP PREEMPT Sat Dec 19 16:58:37 CST 2020
+#1 SMP PREEMPT Fri Oct 27 12:20:59 KST 2017
+#1 SMP PREEMPT Tue Oct 16 15:38:41 KST 2018
+#1 SMP PREEMPT Wed Aug 7 22:46:42 CST 2019
+#1 SMP PREEMPT Thu Feb 21 19:13:15 WIB 2019
+#2 SMP PREEMPT Tue Apr 30 06:06:41 CST 2019
+#1 SMP PREEMPT Sat Feb 6 14:48:20 IST 2021
+#1 SMP PREEMPT Mon May 9 16:49:44 CST 2022
+#1 SMP PREEMPT Tue May 18 02:38:35 EDT 2021
+#1 SMP PREEMPT Tue Jun 29 15:51:00 CST 2021
+#1 SMP PREEMPT Wed Jul 14 12:43:41 CST 2021
+#1 SMP PREEMPT Tue Mar 1 03:21:43 JST 2022
+#1 SMP PREEMPT Wed Feb 13 23:27:53 CST 2019
+#1 SMP PREEMPT Thu May 20 17:21:03 CST 2021
+#2 SMP PREEMPT Fri Jul 17 17:32:50 KST 2020
+#1 SMP PREEMPT Wed Aug 29 14:57:34 KST 2018
+#1 SMP PREEMPT Mon Jul 15 18:25:37 WIB 2019
+#1 SMP PREEMPT Wed Feb 13 03:29:02 PST 2019
+#1 SMP PREEMPT Sun Nov 15 22:04:50 WIB 2020
+#1 SMP PREEMPT Wed Feb 24 09:47:01 JST 2021
+#1 SMP PREEMPT Tue May 10 14:45:33 CST 2022
+#1 SMP PREEMPT Wed Jul 3 16:11:05 CST 2019
+#1 SMP PREEMPT Wed Oct 17 16:04:46 KST 2018
+#1 SMP PREEMPT Mon Mar 29 21:51:33 KST 2021
+#1 SMP PREEMPT Thu May 9 15:47:15 CST 2019
+#1 SMP PREEMPT Tue Jul 23 17:13:05 CST 2019
+#1 SMP PREEMPT Mon May 7 06:22:30 CST 2018
+#1 SMP PREEMPT Wed Feb 26 18:40:21 CST 2020
+#1 SMP PREEMPT Wed May 2 17:47:11 CST 2018
+#1 SMP PREEMPT Wed Jan 22 04:43:00 CST 2020
+#1 SMP PREEMPT Tue Jun 16 00:25:17 CST 2020
+#2 SMP PREEMPT Fri Apr 23 11:09:38 CST 2021
+#1 SMP PREEMPT Sat Mar 27 01:52:14 CST 2021
+#2 SMP PREEMPT Tue Apr 2 21:15:53 KST 2019
+#1 SMP PREEMPT Sat Jul 17 17:42:13 CST 2021
+#1 SMP PREEMPT Fri Jan 4 20:47:16 KST 2019
+#1 SMP PREEMPT Thu May 19 15:11:44 CST 2022
+#1 SMP PREEMPT Thu Feb 6 14:23:30 KST 2020
+#1 SMP PREEMPT Tue Jun 4 18:07:07 -03 2019
+#1 SMP PREEMPT Sat Aug 21 16:03:00 CST 2021
+#1 SMP PREEMPT Wed Oct 20 18:27:37 CST 2021
+#1 SMP PREEMPT Wed Sep 29 23:42:22 CST 2021
+#1 SMP PREEMPT Fri Jan 7 12:33:36 UTC 2022
+#1 SMP PREEMPT Thu May 14 09:29:34 CST 2020
+#1 SMP PREEMPT Sun Jun 28 22:26:43 CST 2020
+#1 SMP PREEMPT Thu Nov 1 06:23:10 CDT 2018
+#1 SMP PREEMPT Tue Jul 2 21:38:32 CST 2019
+#1 SMP PREEMPT Wed Nov 14 17:21:08 CST 2018
+#1 SMP PREEMPT Sat Jan 23 02:15:39 IST 2021
+#1 SMP PREEMPT Thu Feb 21 05:32:39 PST 2019
+#2 SMP PREEMPT Tue Apr 16 21:26:46 CST 2019
+#15 SMP PREEMPT Tue May 22 16:11:41 CST 2018
+#2 SMP PREEMPT Wed Sep 12 09:23:30 CST 2018
+#1 SMP PREEMPT Wed May 20 13:39:29 KST 2020
+#2 SMP PREEMPT Thu Feb 4 06:30:25 CET 2021
+#1 SMP PREEMPT Wed Nov 3 17:17:32 UTC 2021
+#1 SMP PREEMPT Sat May 14 12:14:26 CST 2022
+#1 SMP PREEMPT Mon Jul 5 11:28:12 CDT 2021
+#1 SMP PREEMPT Wed Nov 21 19:01:05 CST 2018
+#1 SMP PREEMPT Mon Jan 14 16:27:26 IST 2019
+#1 SMP PREEMPT Tue Aug 21 18:27:19 CST 2018
+#1 SMP PREEMPT Mon Sep 6 20:51:02 PDT 2021
+#1 SMP PREEMPT Sat Oct 31 10:20:57 CST 2020
+#1 SMP PREEMPT Thu Jul 26 12:31:55 KST 2018
+#1 SMP PREEMPT Mon May 17 20:57:50 IST 2021
+#1 SMP PREEMPT Sat Feb 29 09:58:25 CST 2020
+#1 SMP PREEMPT Fri Oct 19 15:28:27 CST 2018
+#1 SMP PREEMPT Mon Jan 15 17:11:13 KST 2018
+#1 SMP PREEMPT Fri Mar 4 15:06:11 CST 2022
+#2 SMP PREEMPT Mon Dec 9 17:29:39 KST 2019
+#1 SMP PREEMPT Thu Jan 20 19:13:48 KST 2022
+#1 SMP PREEMPT Mon Mar 28 22:09:05 CST 2022
+#1 SMP PREEMPT Fri Mar 19 14:37:08 KST 2021
+#1 SMP PREEMPT Fri Dec 27 17:11:04 CST 2019
+#1 SMP PREEMPT Mon Aug 24 15:51:59 CEST 2020
+#1 SMP PREEMPT Fri Sep 17 06:32:15 JST 2021
+#1 SMP PREEMPT Sun Nov 1 01:50:28 CST 2020
+#1 SMP PREEMPT Fri Nov 9 22:18:53 CST 2018
+#1 SMP PREEMPT Mon Jun 28 08:58:54 KST 2021
+#1 SMP PREEMPT Thu Apr 18 09:20:04 JST 2019
+#1 SMP PREEMPT Thu Jun 27 03:08:20 CST 2019
+#1 SMP PREEMPT Sun Oct 25 00:10:33 CST 2020
+#1 SMP PREEMPT Fri Jul 6 18:30:47 CST 2018
+#1 SMP PREEMPT Tue Aug 3 10:05:56 CST 2021
+#1 SMP PREEMPT Thu Apr 12 14:16:42 CST 2018
+#1 SMP PREEMPT Thu Jun 24 23:26:04 HKT 2021
+#1 SMP PREEMPT Thu Mar 15 12:53:43 UTC 2018
+#1 SMP PREEMPT Tue Jul 30 16:29:52 CST 2019
+#1 SMP PREEMPT Mon Dec 9 10:09:37 CST 2019
+#2 SMP PREEMPT Mon Jan 10 09:25:56 CST 2022
+#2 SMP PREEMPT Thu Oct 4 12:52:23 -03 2018
+#1 SMP PREEMPT Mon Apr 22 14:11:52 CST 2019
+#1 SMP PREEMPT Fri Mar 2 15:11:21 CST 2018
+#1 SMP PREEMPT Wed Oct 20 06:41:25 UTC 2021
+#1 SMP PREEMPT Sun Jan 10 19:44:24 CST 2021
+#1 SMP PREEMPT Fri Dec 22 12:30:06 BRST 2017
+#1 SMP PREEMPT Wed Jul 1 19:10:20 CST 2020
+#2 SMP PREEMPT Tue Oct 1 12:29:34 KST 2019
+#1 SMP PREEMPT Mon Apr 5 17:57:52 KST 2021
+#1 SMP PREEMPT Wed Sep 9 05:09:51 KST 2020
+#1 SMP PREEMPT Sat Mar 26 00:30:45 JST 2022
+#1 SMP PREEMPT Fri Mar 11 21:23:00 IST 2022
+#1 SMP PREEMPT Fri Jan 11 15:34:33 CST 2019
+#1 SMP PREEMPT Wed May 11 18:06:01 CST 2022
+#1 SMP PREEMPT Sat Oct 10 09:58:08 WIB 2020
+#2 SMP PREEMPT Fri May 17 14:25:54 CST 2019
+#1 SMP PREEMPT Wed Apr 8 05:23:17 KST 2020
+#2 SMP PREEMPT Tue Jun 30 14:10:43 CST 2020
+#1 SMP PREEMPT Fri Mar 19 23:57:47 CST 2021
+#1 SMP PREEMPT Sat May 5 11:49:16 KST 2018
+#1 SMP PREEMPT Fri Jan 19 14:37:57 CST 2018
+#1 SMP PREEMPT Sat Oct 26 22:51:45 CST 2019
+#2 SMP PREEMPT Wed Oct 20 02:20:00 JST 2021
+#1 SMP PREEMPT Thu Dec 23 11:00:52 CST 2021
+#1 SMP PREEMPT Thu Feb 24 16:05:58 CST 2022
+#1 SMP PREEMPT Wed Mar 7 04:02:56 CST 2018
+#1 SMP PREEMPT Sat Jun 29 04:29:05 CST 2019
+#1 SMP PREEMPT Fri Aug 24 10:26:43 CST 2018
+#1 SMP PREEMPT Sun Jan 13 11:48:44 PST 2019
+#1 SMP PREEMPT Thu Aug 1 06:10:51 CST 2019
+#1 SMP PREEMPT Mon Apr 19 17:59:16 KST 2021
+#1 SMP PREEMPT Sun Nov 21 23:49:55 CST 2021
+#1 SMP PREEMPT Tue May 7 16:12:45 CST 2019
+#1 SMP PREEMPT Fri Sep 18 19:33:21 CST 2020
+#1 SMP PREEMPT Sun Jul 14 21:18:23 PDT 2019
+#1 SMP PREEMPT Sun Jun 14 18:21:53 CST 2020
+#1 SMP PREEMPT Thu Dec 13 18:27:49 CST 2018
+#1 SMP PREEMPT Fri Aug 7 17:00:09 KST 2020
+#1 SMP PREEMPT Sat Oct 23 13:47:42 CST 2021
+#2 SMP PREEMPT Mon Apr 27 08:21:11 CST 2020
+#1 SMP PREEMPT Wed Jun 20 06:59:29 KST 2018
+#2 SMP PREEMPT Wed Dec 27 14:42:29 CST 2017
+#10 SMP PREEMPT Fri Feb 18 16:35:21 HKT 2022
+#6 SMP PREEMPT Thu Apr 8 14:32:49 CST 2021
+#1 SMP PREEMPT Thu Nov 30 19:20:04 KST 2017
+#1 SMP PREEMPT Mon Dec 10 17:49:23 CST 2018
+#1 SMP PREEMPT Sat Apr 30 07:47:39 UTC 2022
+#1 SMP PREEMPT Mon Apr 18 17:34:07 CST 2022
+#1 SMP PREEMPT Sun Mar 20 21:09:28 CST 2022
+#1 SMP PREEMPT Wed Jul 18 04:18:22 PDT 2018
+#1 SMP PREEMPT Tue Jun 9 18:04:39 CST 2020
+#1 SMP PREEMPT Thu Mar 5 18:31:47 CST 2020
+#1 SMP PREEMPT Sun Feb 24 16:33:12 CST 2019
+#1 SMP PREEMPT Sat Aug 3 04:08:49 CST 2019
+#1 SMP PREEMPT Thu Sep 12 09:56:33 CST 2019
+#2 SMP PREEMPT Wed Jun 12 15:04:15 CST 2019
+#1 SMP PREEMPT Thu Nov 1 14:28:38 CST 2018
+#1 SMP PREEMPT Mon Apr 13 16:31:10 KST 2020
+#1 SMP PREEMPT Sat Jul 7 04:30:05 CST 2018
+#1 SMP PREEMPT Wed Jul 14 00:47:15 CST 2021
+#1 SMP PREEMPT Wed Apr 17 16:28:42 KST 2019
+#1 SMP PREEMPT Mon Nov 27 11:28:53 KST 2017
+#1 SMP PREEMPT Fri Nov 8 18:31:14 CST 2019
+#1 SMP PREEMPT Fri Feb 18 00:53:01 WIB 2022
+#1 SMP PREEMPT Fri Jul 12 19:35:52 2019
+#1 SMP PREEMPT Tue May 15 12:19:51 PDT 2018
+#2 SMP PREEMPT Fri Aug 10 17:51:30 CST 2018
+#1 SMP PREEMPT Sat Nov 21 03:38:28 CST 2020
+#1 SMP PREEMPT Thu Apr 26 23:13:34 KST 2018
+#1 SMP PREEMPT Thu May 9 02:02:37 CST 2019
+#1 SMP PREEMPT Mon Apr 16 12:06:00 CEST 2018
+#1 SMP PREEMPT Sun Apr 4 03:15:17 PDT 2021
+#1 SMP PREEMPT Thu Apr 18 20:29:55 CST 2019
+#2 SMP PREEMPT Wed Jun 17 21:13:08 CST 2020
+#1 SMP PREEMPT Thu Oct 28 10:32:37 IST 2021
+#1 SMP PREEMPT Tue Apr 28 23:00:18 PDT 2020
+#1 SMP PREEMPT Tue Sep 11 00:36:41 CST 2018
+#1 SMP PREEMPT Tue Jul 2 09:21:13 CDT 2019
+#1 SMP PREEMPT Tue Feb 6 03:00:04 WIB 2018
+#1 SMP PREEMPT Mon Mar 19 12:27:42 KST 2018
+#1 SMP PREEMPT Thu Sep 12 16:31:23 CST 2019
+#1 SMP PREEMPT Sat Feb 13 01:01:21 JST 2021
+#1 SMP PREEMPT Thu May 9 17:40:38 CST 2019
+#1 SMP PREEMPT Mon Jul 22 15:20:26 KST 2019
+#1 SMP PREEMPT Fri Dec 11 13:54:32 IST 2020
+#2 SMP PREEMPT Mon Jul 1 12:10:06 CST 2019
+#1 SMP PREEMPT Thu Jun 6 00:49:11 CST 2019
+#1 SMP PREEMPT Tue Oct 8 23:53:10 PDT 2019
+#1 SMP PREEMPT Wed Jul 3 16:38:26 2019
+#1 SMP PREEMPT Mon May 6 17:32:18 CST 2019
+#1 SMP PREEMPT Tue Jun 30 12:22:53 CST 2020
+#1 SMP PREEMPT Wed Dec 2 13:42:21 WIB 2020
+#1 SMP PREEMPT Fri Sep 6 15:11:30 KST 2019
+#1 SMP PREEMPT Mon Feb 15 00:56:44 CST 2021
+#1 SMP PREEMPT Thu Dec 12 13:46:28 CST 2019
+#2 SMP PREEMPT Fri May 8 03:50:37 CST 2020
+#1 SMP PREEMPT Tue Aug 25 11:38:07 WIB 2020
+#1 SMP PREEMPT Wed Jul 31 18:43:12 CST 2019
+#1 SMP PREEMPT Mon Apr 9 16:40:14 CST 2018
+#1 SMP PREEMPT Tue Sep 17 12:35:26 CST 2019
+#1 SMP PREEMPT Fri Jan 4 22:05:26 CST 2019
+#1 SMP PREEMPT Thu Nov 19 23:09:47 CST 2020
+#1 SMP PREEMPT Mon Dec 23 16:50:32 KST 2019
+#1 SMP PREEMPT Fri Jul 5 13:56:47 CST 2019
+#1 SMP PREEMPT Fri Apr 10 05:20:58 CDT 2020
+#1 SMP PREEMPT Tue Nov 13 20:09:31 CST 2018
+#1 SMP PREEMPT Wed Oct 24 02:06:13 PDT 2018
+#1 SMP PREEMPT Fri Apr 17 03:28:37 CST 2020
+#1 SMP PREEMPT Thu Nov 7 10:32:45 CST 2019
+#2 SMP PREEMPT Fri Jul 19 18:41:40 CST 2019
+#1 SMP PREEMPT Tue May 15 04:07:29 CST 2018
+#1 SMP PREEMPT Tue Jul 13 03:50:52 CST 2021
+#1 SMP PREEMPT Thu Jul 9 08:59:13 CST 2020
+#1 SMP PREEMPT Sun Sep 19 15:20:31 PDT 2021
+#1 SMP PREEMPT Tue Apr 13 12:39:56 CST 2021
+#1 SMP PREEMPT Thu Aug 19 22:33:20 CST 2021
+#1 SMP PREEMPT Tue Jan 18 06:44:02 CST 2022
+#2 SMP PREEMPT Wed Dec 23 20:25:03 CST 2020
+#1 SMP PREEMPT Sun May 6 23:47:35 CST 2018
+#2 SMP PREEMPT Thu Jul 23 18:44:24 KST 2020
+#1 SMP PREEMPT Fri Jun 12 16:03:11 CST 2020
+#1 SMP PREEMPT Tue Jan 14 12:26:47 CST 2020
+#1 SMP PREEMPT Tue Apr 27 22:40:37 JST 2021
+#1 SMP PREEMPT Thu Jun 14 17:13:38 KST 2018
+#3 SMP PREEMPT Thu Jun 17 08:58:59 CST 2021
+#1 SMP PREEMPT Mon Dec 18 16:37:35 KST 2017
+#1 SMP PREEMPT Thu Dec 21 18:24:39 KST 2017
+#1 SMP PREEMPT Thu Mar 3 15:08:08 KST 2022
+#1 SMP PREEMPT Mon Jan 28 22:33:46 KST 2019
+#1 SMP PREEMPT Thu Dec 17 22:19:15 KST 2020
+#2 SMP PREEMPT Fri Mar 16 21:09:53 CST 2018
+#1 SMP PREEMPT Wed Mar 2 16:12:55 KST 2022
+#1 SMP PREEMPT Thu Dec 26 11:29:49 CST 2019
+#1 SMP PREEMPT Tue Mar 6 13:00:06 CST 2018
+#1 SMP PREEMPT Tue Aug 4 02:29:09 CST 2020
+#1 SMP PREEMPT Fri Aug 28 16:36:30 IST 2020
+#1 SMP PREEMPT Fri Aug 6 13:26:44 PDT 2021
+#2 SMP PREEMPT Wed Apr 29 13:46:58 KST 2020
+#1 SMP PREEMPT Thu Mar 24 12:38:53 CST 2022
+#1 SMP PREEMPT Tue Oct 23 10:52:14 CST 2018
+#1 SMP PREEMPT Thu Sep 12 00:29:15 CST 2019
+#1 SMP PREEMPT Thu Aug 22 16:41:17 KST 2019
+#1 SMP PREEMPT Wed Jan 8 14:13:19 CST 2020
+#1 SMP PREEMPT Tue Apr 19 22:40:12 CST 2022
+#1 SMP PREEMPT Mon Oct 12 13:11:02 KST 2020
+#2 SMP PREEMPT Wed Dec 22 17:31:29 CST 2021
+#1 SMP PREEMPT Fri May 21 14:01:53 JST 2021
+#1 SMP PREEMPT Tue May 28 20:22:26 CST 2019
+#2 SMP PREEMPT Wed Jun 13 20:54:18 CST 2018
+#9 SMP PREEMPT Sat Aug 24 23:19:51 CST 2019
+#1 SMP PREEMPT Wed Oct 27 11:29:36 CST 2021
+#1 SMP PREEMPT Fri Jan 25 15:02:15 CST 2019
+#1 SMP PREEMPT Tue May 15 19:01:40 KST 2018
+#1 SMP PREEMPT Tue Dec 12 06:50:06 CST 2017
+#1 SMP PREEMPT Thu Dec 10 16:42:25 CST 2020
+#1 SMP PREEMPT Tue May 10 18:45:25 CST 2022
+#1 SMP PREEMPT Sat Sep 28 05:27:52 JST 2019
+#1 SMP PREEMPT Fri Dec 14 08:47:46 CST 2018
+#1 SMP PREEMPT Sun Nov 15 03:52:41 JST 2020
+#1 SMP PREEMPT Wed Nov 10 12:05:56 CST 2021
+#2 SMP PREEMPT Wed Dec 16 18:26:34 CST 2020
+#2 SMP PREEMPT Thu Oct 11 16:04:46 CST 2018
+#1 SMP PREEMPT Sun Feb 27 14:36:53 CST 2022
+#1 SMP PREEMPT Thu Jan 25 16:58:00 KST 2018
+#1 SMP PREEMPT Thu Aug 15 21:40:14 CST 2019
+#1 SMP PREEMPT Fri Apr 26 15:19:38 CST 2019
+#2 SMP PREEMPT Wed Mar 4 09:10:51 CST 2020
+#1 SMP PREEMPT Wed Oct 17 11:13:45 2018
+#1 SMP PREEMPT Thu Sep 10 14:43:43 CST 2020
+#1 SMP PREEMPT Mon Dec 2 12:38:06 CST 2019
+#10 SMP PREEMPT Fri Oct 25 15:59:41 CST 2019
+#1 SMP PREEMPT Thu Dec 5 20:38:50 KST 2019
+#2 SMP PREEMPT Wed Oct 23 14:38:34 CST 2019
+#1 SMP PREEMPT Tue Jun 2 15:04:59 CST 2020
+#2 SMP PREEMPT Sun Jan 6 00:22:10 CST 2019
+#1 SMP PREEMPT Fri Jan 25 18:03:02 CST 2019
+#4 SMP PREEMPT Thu Jun 10 05:12:46 CDT 2021
+#1 SMP PREEMPT Fri Aug 9 10:06:41 KST 2019
+#1 SMP PREEMPT Tue May 8 13:20:24 KST 2018
+#1 SMP PREEMPT Wed Sep 26 20:12:33 IST 2018
+#1 SMP PREEMPT Sat Mar 5 10:26:26 UTC 2022
+#1 SMP PREEMPT Wed Dec 23 14:47:27 HKT 2020
+#1 SMP PREEMPT Mon Apr 22 23:10:41 CST 2019
+#1 SMP PREEMPT Thu May 26 19:42:18 CST 2022
+#1 SMP PREEMPT Fri Aug 21 19:04:14 CST 2020
+#1 SMP PREEMPT Wed May 18 15:20:28 CST 2022
+#1 SMP PREEMPT Tue Oct 22 14:56:02 CST 2019
+#1 SMP PREEMPT Fri Apr 26 19:00:40 CST 2019
+#1 SMP PREEMPT Tue Apr 16 23:07:15 CST 2019
+#2 SMP PREEMPT Mon Oct 28 10:15:12 CST 2019
+#1 SMP PREEMPT Thu Mar 18 19:20:54 PDT 2021
+#1 SMP PREEMPT Tue Mar 1 15:50:13 CST 2022
+#1 SMP PREEMPT Tue Apr 7 20:42:39 PDT 2020
+#1 SMP PREEMPT Tue Apr 13 19:29:20 +07 2021
+#1 SMP PREEMPT Thu Oct 29 09:39:54 UTC 2020
+#2 SMP PREEMPT Fri May 17 17:16:30 KST 2019
+#1 SMP PREEMPT Wed Nov 10 20:50:27 PST 2021
+#1 SMP PREEMPT Wed Oct 27 00:03:09 CST 2021
+#1 SMP PREEMPT Tue Mar 1 01:10:57 CST 2022
+#2 SMP PREEMPT Tue Oct 2 19:47:17 CST 2018
+#1 SMP PREEMPT Sun Mar 22 18:23:11 CST 2020
+#1 SMP PREEMPT Fri Feb 11 22:10:12 UTC 2022
+#1 SMP PREEMPT Thu Jan 17 19:09:26 CST 2019
+#1 SMP PREEMPT Mon Apr 23 16:02:20 CST 2018
+#1 SMP PREEMPT Sat Jul 6 14:47:31 CST 2019
+#1 SMP PREEMPT Sat Feb 29 12:06:33 IST 2020
+#1 SMP PREEMPT Thu Jul 29 15:57:25 CST 2021
+#1 SMP PREEMPT Wed Jul 8 21:43:28 CST 2020
+#1 SMP PREEMPT Wed Dec 8 02:20:41 CST 2021 f2fs-hash:2b68ac9e148
+#1 SMP PREEMPT Wed Apr 29 16:55:18 CST 2020
+#1 SMP PREEMPT Sat Jul 31 02:19:20 CST 2021
+#1 SMP PREEMPT Mon Aug 17 18:21:41 CST 2020
+#1 SMP PREEMPT Thu Sep 6 23:43:31 PDT 2018
+#1 SMP PREEMPT Wed Nov 3 11:16:11 CST 2021
+#1 SMP PREEMPT Sun Jan 13 11:36:50 PST 2019
+#1 SMP PREEMPT Tue Apr 2 10:46:10 KST 2019
+#6 SMP PREEMPT Thu Dec 2 11:18:33 UTC 2021
+#1 SMP PREEMPT Mon Apr 9 16:11:25 KST 2018
+#1 SMP PREEMPT Sat Feb 29 23:26:08 CST 2020
+#1 SMP PREEMPT Sat Jun 1 14:59:28 KST 2019
+#1 SMP PREEMPT Tue Jul 16 16:13:43 CST 2019
+#1 SMP PREEMPT Wed Nov 24 02:17:44 CST 2021
+#1 SMP PREEMPT Sun Apr 29 22:14:23 PDT 2018
+#1 SMP PREEMPT Fri Apr 20 14:01:25 CST 2018
+#1 SMP PREEMPT Mon Oct 22 20:58:02 KST 2018
+#1 SMP PREEMPT Mon Oct 15 19:18:31 CST 2018
+#1 SMP PREEMPT Wed Jun 17 12:04:35 CST 2020
+#1 SMP PREEMPT Tue Feb 2 01:51:22 KST 2021
+#1 SMP PREEMPT Mon Mar 23 05:34:58 JST 2020
+#1 SMP PREEMPT Mon Aug 9 18:01:43 IST 2021
+#1 SMP PREEMPT Fri Mar 2 18:49:07 CST 2018
+#1 SMP PREEMPT Wed Mar 21 04:09:35 CST 2018
+#1 SMP PREEMPT Thu Aug 19 16:29:56 KST 2021
+#3 SMP PREEMPT Tue Oct 8 19:40:49 CST 2019
+#2 SMP PREEMPT Fri Jul 5 19:21:10 CST 2019
+#1 SMP PREEMPT Fri Jun 12 17:44:03 JST 2020
+#1 SMP PREEMPT Tue Apr 2 02:41:52 PDT 2019
+#1 SMP PREEMPT Thu Nov 26 15:29:33 CST 2020
+#1 SMP Thu Jun 10 13:41:17 KST 2021
+#1 SMP PREEMPT Wed Apr 15 01:33:46 PDT 2020
+#1 SMP PREEMPT Wed Dec 11 10:10:02 CST 2019
+#2 SMP PREEMPT Tue Apr 16 21:30:15 KST 2019
+#1 SMP PREEMPT Thu Mar 10 21:54:13 CST 2022
+#1 SMP PREEMPT Tue Jan 18 17:38:04 UTC 2022
+#1 SMP PREEMPT Tue Aug 7 14:10:04 CST 2018
+#1 SMP PREEMPT Mon Apr 15 17:22:26 CST 2019
+#1 SMP PREEMPT Thu Jan 14 04:10:43 EST 2021
+#1 SMP PREEMPT Fri Jul 2 07:22:39 KST 2021
+#1 SMP PREEMPT Wed Jul 3 03:40:52 CST 2019
+#1 SMP PREEMPT Thu Aug 6 01:46:09 CST 2020
+#1 SMP PREEMPT Fri Apr 10 19:04:31 CST 2020
+#1 SMP PREEMPT Thu Jul 2 18:13:00 CST 2020
+#1 SMP PREEMPT Wed Jan 27 16:07:04 CST 2021
+#1 SMP PREEMPT Wed Mar 13 12:48:17 CST 2019
+#2 SMP PREEMPT Thu May 9 03:54:53 CST 2019
+#1 SMP PREEMPT Wed May 27 12:45:56 KST 2020
+#2 SMP PREEMPT Thu Apr 19 14:47:25 CST 2018
+#1 SMP PREEMPT Fri Feb 18 16:40:37 CST 2022
+#1 SMP PREEMPT Tue Nov 20 18:20:31 KST 2018
+#1 SMP PREEMPT Fri May 11 15:36:10 CST 2018
+#1 SMP PREEMPT Fri Mar 4 21:14:34 IST 2022
+#1 SMP PREEMPT Wed Apr 29 12:50:36 CST 2020
+#2 SMP PREEMPT Tue Mar 15 22:09:31 +07 2022
+#1 SMP PREEMPT Mon May 9 10:27:17 CST 2022
+#1 SMP PREEMPT Tue Jul 3 06:53:27 CST 2018
+#1 SMP PREEMPT Tue May 10 06:12:17 KST 2022
+#1 SMP PREEMPT Mon Sep 28 14:54:53 CDT 2020
+#1 SMP PREEMPT Tue Aug 13 10:09:43 CST 2019
+#1 SMP PREEMPT Mon Mar 14 19:38:26 CST 2022
+#1 SMP PREEMPT Mon Sep 14 13:57:58 CST 2020
+#1 SMP PREEMPT Wed Nov 4 18:39:53 CST 2020
+#1 SMP PREEMPT Tue Nov 2 19:18:22 CST 2021
+#1 SMP PREEMPT Thu Aug 5 23:11:17 CST 2021
+#1 SMP PREEMPT Mon Apr 27 19:03:19 CST 2020
+#1 SMP PREEMPT Thu Nov 29 16:28:19 CST 2018
+#1 SMP PREEMPT Wed Sep 19 15:39:31 KST 2018
+#1 SMP PREEMPT Wed Sep 15 16:24:11 CST 2021
+#1 SMP PREEMPT Fri Mar 25 23:08:47 CST 2022
+#1 SMP PREEMPT Tue Oct 9 14:37:24 CST 2018
+#1 SMP PREEMPT Tue Aug 3 04:39:44 CST 2021
+#1 SMP PREEMPT Wed May 13 05:48:37 CST 2020
+#1 SMP PREEMPT Tue Oct 29 11:02:11 CST 2019
+#1 SMP PREEMPT Mon Jan 25 17:56:18 WIB 2021
+#1 SMP PREEMPT Thu Apr 25 16:44:03 KST 2019
+#1 SMP PREEMPT Fri Nov 13 17:27:39 CST 2020
+#3 SMP PREEMPT Mon Apr 15 20:28:51 CST 2019
+#2 SMP PREEMPT Thu Jul 30 20:12:09 KST 2020
+#1 SMP PREEMPT Tue Oct 8 14:12:13 CST 2019
+#1 SMP PREEMPT Wed Apr 8 20:32:49 KST 2020
+#1 SMP PREEMPT Fri Apr 22 18:54:32 CST 2022
+#1 SMP PREEMPT Mon Dec 16 15:52:09 CST 2019
+#1 SMP PREEMPT Wed Nov 22 14:51:26 JST 2017
+#1 SMP PREEMPT Thu Aug 20 04:08:58 CST 2020
+#1 SMP PREEMPT Tue Jun 30 14:44:19 CST 2020
+#1 SMP PREEMPT Fri Jul 31 14:38:56 CST 2020
+#1 SMP PREEMPT Tue Mar 1 20:21:31 CST 2022
+#1 SMP PREEMPT Thu Nov 15 03:27:27 PST 2018
+#1 SMP PREEMPT Wed Nov 20 09:45:23 GMT 2019
+#1 SMP PREEMPT Sun Apr 26 13:11:48 CST 2020
+#20 SMP PREEMPT Thu Nov 29 16:14:32 CST 2018
+#1 SMP PREEMPT Tue Oct 15 10:36:39 CST 2019
+#1 SMP PREEMPT Thu Jul 4 17:29:39 CST 2019
+#1 SMP PREEMPT Sat Oct 9 20:10:11 CST 2021
+#1 SMP PREEMPT Tue Jun 26 04:17:57 KST 2018
+#1 SMP PREEMPT Mon Feb 21 18:22:41 CST 2022
+#1 SMP PREEMPT Wed May 22 15:38:27 KST 2019
+#1 SMP PREEMPT Tue Sep 11 11:42:46 KST 2018
+#1 SMP PREEMPT Sun Sep 26 14:22:59 CST 2021
+#12 SMP PREEMPT Tue Oct 23 22:11:47 CST 2018
+#3 SMP PREEMPT Tue Sep 17 14:02:51 CST 2019
+#1 SMP PREEMPT Tue Jan 9 18:53:43 CST 2018
+#1 SMP PREEMPT Fri May 4 18:44:52 KST 2018
+#1 SMP PREEMPT Sat Jan 22 00:23:22 UTC 2022
+#1 SMP PREEMPT Mon Jan 22 21:54:53 PST 2018
+#1 SMP PREEMPT Fri Apr 9 20:01:05 CST 2021
+#1 SMP PREEMPT Sun Mar 27 05:10:53 PDT 2022
+#2 SMP PREEMPT Tue Oct 13 18:43:58 CST 2020
+#1 SMP PREEMPT Tue Jun 9 02:18:01 UTC 2020
+#1 SMP PREEMPT Tue Mar 19 10:24:26 CST 2019
+#1 SMP PREEMPT Wed Mar 11 01:34:05 PDT 2020
+#1 SMP PREEMPT Fri Apr 30 16:09:25 CST 2021
+#2 SMP PREEMPT Tue May 8 18:10:27 CST 2018
+#1 SMP PREEMPT Wed Jun 5 08:48:41 CDT 2019
+#48 SMP PREEMPT Wed Dec 23 18:51:11 CST 2020
+#1 SMP PREEMPT Wed May 20 09:39:51 KST 2020
+#1 SMP PREEMPT Thu Apr 4 20:07:16 CST 2019
+#1 SMP PREEMPT Mon Feb 15 21:45:33 PST 2021
+#1 SMP PREEMPT Wed Jun 16 16:34:52 CST 2021
+#1 SMP PREEMPT Tue Oct 23 14:11:27 KST 2018
+#1 SMP PREEMPT Wed May 27 18:01:02 CST 2020
+#1 SMP PREEMPT Mon Jun 21 18:35:17 KST 2021
+#2 SMP PREEMPT Mon Apr 26 19:45:22 CST 2021
+#1 SMP PREEMPT Fri Mar 6 10:56:39 CST 2020
+#1 SMP PREEMPT Tue Aug 28 03:49:24 KST 2018
+#7 SMP PREEMPT Thu Oct 29 17:24:16 CST 2020
+#1 SMP PREEMPT Fri May 4 15:25:00 CST 2018
+#2 SMP PREEMPT Thu Mar 14 15:51:05 -03 2019
+#1 SMP PREEMPT Fri Aug 7 12:27:01 CST 2020
+#1 SMP PREEMPT Sun Sep 29 16:43:27 CST 2019
+#1 SMP PREEMPT Fri Mar 9 15:48:57 EST 2018
+#2 SMP PREEMPT Tue Feb 15 05:17:03 UTC 2022
+#1 SMP PREEMPT Fri Mar 11 18:21:16 CST 2022
+#1 SMP PREEMPT Thu Nov 8 13:28:40 CST 2018
+#1 SMP PREEMPT Mon May 25 14:15:22 KST 2020
+#1 SMP PREEMPT Tue May 26 16:22:10 CST 2020
+#1 SMP PREEMPT Fri Aug 9 15:51:07 CST 2019
+#1 SMP PREEMPT Thu Aug 6 13:51:16 IDT 2020
+#1 SMP PREEMPT Wed Jan 16 12:27:22 CST 2019
+#1 SMP PREEMPT Tue Apr 24 03:58:52 CST 2018
+#1 SMP PREEMPT Thu Feb 11 10:12:57 PST 2021
+#1 SMP PREEMPT Sat Oct 24 23:42:03 CST 2020
+#1 SMP PREEMPT Sun Jan 9 23:33:19 CST 2022
+#1 SMP PREEMPT Thu Mar 14 22:29:06 CST 2019
+#1 SMP PREEMPT Fri May 13 02:47:26 JST 2022
+#1 SMP PREEMPT Wed Aug 1 16:25:15 CST 2018
+#1 SMP PREEMPT Mon Oct 28 17:39:28 CST 2019
+#1 SMP PREEMPT Tue Nov 13 12:53:47 CST 2018
+#2 SMP PREEMPT Wed Sep 29 10:17:26 CST 2021
+#1 SMP PREEMPT Tue Nov 20 13:26:40 EST 2018
+#1 SMP PREEMPT Mon Jan 17 17:59:58 CST 2022
+#1 SMP PREEMPT Fri Nov 19 08:45:40 WIB 2021
+#1 SMP PREEMPT Tue Dec 14 02:16:20 CST 2021
+#1 SMP PREEMPT Thu Jan 28 17:57:41 KST 2021
+#1 SMP PREEMPT Tue Mar 26 10:36:57 CST 2019
+#1 SMP PREEMPT Mon Apr 8 18:13:53 KST 2019
+#1 SMP PREEMPT Wed Jan 29 11:43:05 KST 2020
+#36 SMP PREEMPT Thu Jun 14 18:47:12 CST 2018
+#1 SMP PREEMPT Tue Dec 15 20:41:31 KST 2020
+#2 SMP PREEMPT Wed Dec 25 00:48:00 CST 2019
+#2 SMP PREEMPT Tue Feb 25 18:01:36 HKT 2020
+#1 SMP PREEMPT Fri Mar 26 21:31:46 CST 2021
+#1 SMP PREEMPT Mon Jun 11 23:49:14 PDT 2018
+#1 SMP PREEMPT Thu Jun 11 23:32:12 KST 2020
+#1 SMP PREEMPT Thu Jul 11 15:59:43 CST 2019
+#1 SMP PREEMPT Wed Nov 11 06:43:08 EST 2020
+#1 SMP PREEMPT Thu Dec 2 18:31:18 CST 2021
+#1 SMP PREEMPT Fri May 22 22:40:43 KST 2020
+#1 SMP PREEMPT Wed May 16 20:32:59 KST 2018
+#1 SMP PREEMPT Tue Mar 16 15:15:26 CST 2021
+#1 SMP PREEMPT Wed Sep 5 01:24:36 CST 2018
+#1 SMP PREEMPT Fri Jan 18 17:36:17 KST 2019
+#1 SMP PREEMPT Sat Aug 17 03:51:03 CST 2019
+#1 SMP PREEMPT Fri Jun 12 17:44:03 JST 2020
+#1 SMP PREEMPT Thu Feb 21 15:42:55 CST 2019
+#1 SMP PREEMPT Thu Jul 11 12:17:49 CST 2019
+#1 SMP PREEMPT Thu Dec 28 10:21:47 CST 2017
+#1 SMP PREEMPT Tue Aug 21 13:21:07 CST 2018
+#1 SMP PREEMPT Tue Aug 13 13:44:38 CST 2019
+#1 SMP PREEMPT Wed Dec 13 06:05:47 2017
+#1 SMP PREEMPT Thu May 24 12:10:43 CST 2018
+#1 SMP PREEMPT Wed Feb 7 15:27:40 KST 2018
+#1 SMP PREEMPT Fri Dec 31 12:39:41 CST 2021
+#1 SMP PREEMPT Mon Jun 28 05:47:08 CDT 2021
+#1 SMP PREEMPT Fri Jan 12 12:42:59 KST 2018
+#1 SMP PREEMPT Mon Dec 11 21:45:26 CST 2017
+#1 SMP PREEMPT Thu Sep 6 03:33:34 CST 2018
+#2 SMP PREEMPT Tue Jul 20 20:59:30 KST 2021
+#1 SMP PREEMPT Sun Mar 13 16:29:50 CST 2022
+#2 SMP PREEMPT Thu Jan 31 19:45:41 CST 2019
+#1 SMP PREEMPT Thu Feb 24 16:30:33 CST 2022
+#0 SMP PREEMPT Thu Oct 8 04:02:27 UTC 2020
+#1 SMP PREEMPT Mon Nov 22 18:20:56 CST 2021
+#1 SMP PREEMPT Fri Jul 2 01:44:48 JST 2021
+#1 SMP PREEMPT Sun Jan 9 06:36:38 PST 2022
+#1 SMP PREEMPT Wed Nov 6 22:19:32 WIB 2019
+#2 SMP PREEMPT Thu Nov 7 15:52:11 CST 2019
+#2 SMP PREEMPT Fri Aug 28 21:50:01 CST 2020
+#1 SMP PREEMPT Wed Jan 5 05:59:38 CST 2022
+#1 SMP PREEMPT Fri Jun 4 17:21:35 KST 2021
+#2 SMP PREEMPT Tue Mar 9 12:11:26 KST 2021
+#1 SMP PREEMPT Sun Nov 18 12:45:42 CST 2018
+#1 SMP PREEMPT Tue Mar 22 11:40:12 CST 2022
+#1 SMP PREEMPT Sun Jan 16 20:00:39 CST 2022
+#1 SMP PREEMPT Wed Jan 17 10:34:04 KST 2018
+#1 SMP PREEMPT Mon Jan 31 15:40:23 UTC 2022
+#2 SMP PREEMPT Wed Jan 17 23:15:02 CST 2018
+#1 SMP PREEMPT Thu Jul 11 15:13:46 CST 2019
+#1 SMP PREEMPT Sun Mar 20 22:53:54 CST 2022
+#1 SMP PREEMPT Thu Sep 16 03:54:23 CST 2021
+#1 SMP PREEMPT Wed Jun 23 19:26:03 UTC 2021
+#4 SMP PREEMPT Wed Feb 9 16:44:57 CST 2022
+#1 SMP PREEMPT Mon Feb 22 21:04:00 CST 2021
+#1 SMP PREEMPT Tue Aug 27 16:22:33 CST 2019
+#1 SMP PREEMPT Thu Jun 7 12:39:09 CST 2018
+#1 SMP PREEMPT Mon Jun 21 07:39:50 EDT 2021
+#1 SMP PREEMPT Fri Oct 18 18:18:44 CST 2019
+#1 SMP PREEMPT Tue Apr 7 19:23:09 CST 2020
+#1 SMP PREEMPT Fri Mar 9 15:46:19 CST 2018
+#1 SMP PREEMPT Fri Sep 11 04:48:00 CST 2020
+#192 SMP PREEMPT Mon Nov 18 16:42:07 CST 2019
+#1 SMP PREEMPT Thu Dec 26 12:18:19 CST 2019
+#1 SMP PREEMPT Wed Jan 13 15:50:15 CST 2021
+#1 SMP PREEMPT Fri Jan 10 12:32:23 CST 2020
+#1 SMP PREEMPT Thu Dec 14 15:11:05 KST 2017
+#1 SMP PREEMPT Sat Jun 29 19:04:31 IST 2019
+#1 SMP PREEMPT Thu Jul 22 20:43:40 CST 2021
+#1 SMP PREEMPT Mon Feb 12 11:15:27 UTC 2018
+#1 SMP PREEMPT Mon Apr 4 19:33:16 PDT 2022
+#1 SMP PREEMPT Tue Mar 6 18:58:00 CST 2018
+#1 SMP PREEMPT Mon May 28 15:21:04 KST 2018
+#2 SMP PREEMPT Tue Nov 24 21:25:40 CST 2020
+#1 SMP PREEMPT Thu Jul 11 23:54:20 CST 2019
+#1 SMP PREEMPT Thu Nov 21 19:24:43 KST 2019
+#1 SMP PREEMPT Mon Jun 29 21:00:03 CST 2020
+#2 SMP PREEMPT Mon Apr 27 08:46:14 UTC 2020
+#1 SMP PREEMPT Fri Jun 26 01:18:48 CDT 2020
+#1 SMP PREEMPT Mon Dec 18 19:12:54 KST 2017
+#1 SMP PREEMPT Tue Sep 14 17:05:52 CST 2021
+#1 SMP PREEMPT Thu Jul 25 03:34:17 CST 2019
+#1 SMP PREEMPT Mon Feb 10 12:31:10 CST 2020
+#1 SMP PREEMPT Tue Nov 27 09:54:06 CST 2018
+#1 SMP PREEMPT Wed May 29 01:46:49 PDT 2019
+#1 SMP PREEMPT Mon May 6 22:18:01 CST 2019
+#1 SMP PREEMPT Fri Oct 4 01:14:10 CDT 2019
+#1 SMP PREEMPT Thu Dec 19 11:28:00 CST 2019
+#1 SMP PREEMPT Thu Feb 8 17:43:02 CST 2018
+#2 SMP PREEMPT Mon Mar 1 20:10:55 CST 2021
+#1 SMP PREEMPT Wed Oct 28 00:06:53 KST 2020
+#6 SMP PREEMPT Sat Apr 9 16:36:08 CST 2022
+#2 SMP PREEMPT Tue Jul 16 04:31:58 CST 2019
+#1 SMP PREEMPT Mon Feb 10 08:47:41 CST 2020
+#1 SMP PREEMPT Mon Feb 5 05:47:59 CST 2018
+#1 SMP PREEMPT Wed Jun 10 15:21:33 KST 2020
+#1 SMP PREEMPT Mon Mar 26 12:54:13 CST 2018
+#1 SMP PREEMPT Wed Feb 16 21:07:43 CST 2022
+#1 SMP PREEMPT Mon Sep 13 15:40:51 CST 2021
+#1 SMP PREEMPT Mon Jun 8 10:23:47 CST 2020
+#1 SMP PREEMPT Thu Nov 8 07:42:12 KST 2018
+#1 SMP PREEMPT Thu Aug 22 11:43:57 KST 2019
+#1 SMP PREEMPT Mon Dec 30 22:10:07 CST 2019
+#1 SMP PREEMPT Thu Apr 14 17:33:48 CST 2022
+#1 SMP PREEMPT Fri Apr 16 01:34:15 CST 2021
+#1 SMP PREEMPT Fri Apr 29 15:10:48 CST 2022
+#2 SMP PREEMPT Tue May 19 13:41:21 CST 2020
+#1 SMP PREEMPT Wed Apr 21 17:15:53 CST 2021
+#1 SMP PREEMPT Wed Jun 16 01:32:53 UTC 2021
+#1 SMP PREEMPT Tue Oct 23 01:15:44 KST 2018
+#1 SMP PREEMPT 2021-08-28 09:42:03
+#2 SMP PREEMPT Mon Sep 6 22:52:05 CST 2021
+#1 SMP PREEMPT Fri Sep 6 07:50:09 CDT 2019
+#1 SMP PREEMPT Wed Aug 15 20:39:18 CST 2018
+#1 SMP PREEMPT Tue Aug 13 20:47:44 CST 2019
+#2 SMP PREEMPT Tue Oct 8 13:02:35 KST 2019
+#1 SMP PREEMPT Mon Jun 1 12:49:29 PDT 2020
+#1 SMP PREEMPT Thu Aug 6 14:57:21 CST 2020
+#1 SMP PREEMPT Tue Jun 8 13:21:37 CST 2021
+#1 SMP PREEMPT Tue Mar 20 13:01:41 CST 2018
+#1 SMP PREEMPT Tue Oct 15 13:14:12 2019
+#1 SMP PREEMPT Wed Dec 16 22:38:14 JST 2020
+#1 SMP PREEMPT Mon Aug 17 02:08:10 CST 2020
+#1 SMP PREEMPT Fri Nov 16 19:10:21 CST 2018
+#1 SMP PREEMPT Fri Feb 26 17:29:22 CST 2021
+#1 SMP PREEMPT Thu Jun 6 09:46:31 CST 2019
+#1 SMP PREEMPT Wed Oct 24 01:35:22 CST 2018
+#3 SMP PREEMPT Mon Mar 14 14:42:43 CST 2022
+#1 SMP PREEMPT Fri Apr 9 12:51:57 CST 2021
+#1 SMP PREEMPT Tue Oct 23 02:49:01 CST 2018
+#1 SMP PREEMPT Wed Jun 9 14:05:16 PDT 2021
+#1 SMP PREEMPT Thu Jul 22 23:23:12 CST 2021
+#1 SMP PREEMPT Sat Apr 30 09:11:45 PDT 2022
+#1 SMP PREEMPT Wed Nov 7 18:52:19 KST 2018
+#2 SMP PREEMPT Fri May 8 01:14:23 CST 2020
+#1 SMP PREEMPT Thu Feb 20 17:30:48 CST 2020
+#1 SMP PREEMPT Thu Jan 24 02:00:44 EST 2019
+#3 SMP PREEMPT Tue Dec 8 22:29:59 CST 2020
+#1 SMP PREEMPT Mon Apr 9 10:54:04 CST 2018
+#2 SMP PREEMPT Tue Jan 15 19:34:24 KST 2019
+#2 SMP PREEMPT Thu Dec 23 19:02:19 KST 2021
+#1 SMP PREEMPT Fri Jun 25 14:39:44 CST 2021
+#1 SMP PREEMPT Thu Jan 24 20:50:20 KST 2019
+#1 SMP PREEMPT Wed Dec 18 17:27:49 CST 2019
+#1 SMP PREEMPT Tue Mar 30 22:02:16 JST 2021
+#3 SMP PREEMPT Mon Sep 7 16:42:10 KST 2020
+#1 SMP PREEMPT Sat Jul 6 21:01:15 CST 2019
+#2 SMP PREEMPT Fri Feb 21 15:35:19 CST 2020
+#1 SMP PREEMPT Wed Jun 24 17:18:17 CST 2020
+#1 SMP PREEMPT Wed Dec 13 17:30:57 2017
+#1 SMP PREEMPT Wed Jun 13 10:56:57 2018
+#1 SMP PREEMPT Mon Jan 8 14:41:49 KST 2018
+#1 SMP PREEMPT Thu Sep 23 18:55:17 CST 2021
+#2 SMP PREEMPT Thu May 13 18:57:40 UTC 2021
+#1 SMP PREEMPT Sun Jul 25 20:01:37 PDT 2021
+#1 SMP PREEMPT Mon Nov 11 20:51:24 KST 2019
+#1 SMP PREEMPT Sat Mar 26 17:18:19 CST 2022
+#1 SMP PREEMPT Tue Apr 3 15:44:55 KST 2018
+#1 SMP PREEMPT Fri Nov 30 02:05:40 JST 2018
+#5 SMP PREEMPT Thu Sep 30 12:16:28 CST 2021
+#1 SMP PREEMPT Mon Apr 27 23:12:16 KST 2020
+#1 SMP PREEMPT Mon Nov 8 22:04:14 CST 2021
+#1 SMP PREEMPT Fri Sep 24 21:11:08 CST 2021
+#2 SMP PREEMPT Mon Oct 26 15:32:45 CST 2020
+#1 SMP PREEMPT Thu Feb 17 12:11:54 CST 2022
+#1 SMP PREEMPT Wed Mar 11 00:20:11 CST 2020
+#1 SMP PREEMPT Tue Oct 15 18:17:42 CST 2019
+#1 SMP PREEMPT Wed Mar 20 13:59:50 EDT 2019
+#1 SMP PREEMPT Thu Jun 10 15:48:52 KST 2021
+#1 SMP PREEMPT Sat Oct 31 21:35:06 CST 2020
+#1 SMP PREEMPT Mon Jan 11 19:16:06 HKT 2021
+#1 SMP PREEMPT Fri Jan 17 12:16:03 CST 2020
+#1 SMP PREEMPT Wed Oct 24 18:01:59 KST 2018
+#1 SMP PREEMPT Tue Mar 1 15:07:41 CST 2022
+#1 SMP PREEMPT Fri Apr 29 14:04:05 CST 2022
+#1 SMP PREEMPT Tue Feb 27 10:10:45 2018
+#1 SMP PREEMPT Thu Mar 19 00:28:24 KST 2020
+#1 SMP PREEMPT Sun Feb 7 18:29:20 CST 2021
+#1 SMP PREEMPT Fri Jun 14 16:00:17 CST 2019
+#1 SMP PREEMPT Thu Feb 25 18:21:16 IST 2021
+#2 SMP PREEMPT Wed Jul 22 10:18:33 KST 2020
+#1 SMP PREEMPT Wed Mar 4 00:33:49 PST 2020
+#1 SMP PREEMPT Thu Dec 31 11:15:07 CST 2020
+#2 SMP PREEMPT Wed Mar 25 19:18:53 CST 2020
+#1 SMP PREEMPT Wed Nov 13 02:09:42 KST 2019
+#2 SMP PREEMPT Sun Oct 28 12:05:02 CET 2018
+#3 SMP PREEMPT Mon Apr 23 18:59:28 CST 2018
+#1 SMP PREEMPT Tue Aug 21 18:27:19 CST 2018
+#1 SMP PREEMPT Wed Mar 16 14:56:10 JST 2022
+#1 SMP PREEMPT Wed Aug 18 14:33:59 KST 2021
+#1 SMP PREEMPT Tue Aug 21 21:21:40 CST 2018
+#1 SMP PREEMPT Sat Jul 10 12:57:05 CST 2021
+#2 SMP PREEMPT Tue Aug 11 21:23:27 KST 2020
+#1 SMP PREEMPT Thu Dec 28 19:04:29 CST 2017
+#1 SMP PREEMPT Tue Apr 20 08:39:01 UTC 2021
+#1 SMP PREEMPT Wed Feb 23 02:43:13 WIB 2022
+#1 SMP PREEMPT Fri Jan 18 15:40:16 CST 2019
+#1 SMP PREEMPT Sat Jun 29 09:27:29 CST 2019
+#1 SMP PREEMPT Tue Feb 6 18:29:03 KST 2018
+#1 SMP PREEMPT Wed Dec 1 23:34:41 CST 2021
+#12 SMP PREEMPT Mon May 27 14:50:25 CST 2019
+#1 SMP PREEMPT Thu Sep 23 00:41:25 CST 2021
+#1 SMP PREEMPT Wed May 12 00:29:56 KST 2021
+#2 SMP PREEMPT Fri Mar 23 14:41:56 CST 2018
+#1 SMP PREEMPT Fri Nov 20 02:26:03 CST 2020
+#1 SMP PREEMPT Thu Aug 20 11:56:11 KST 2020
+#1 SMP PREEMPT Fri Apr 15 22:08:37 CST 2022
+#1 SMP PREEMPT Wed Jan 23 23:49:36 PST 2019
+#2 SMP PREEMPT Mon Apr 12 15:59:28 KST 2021
+#2 SMP PREEMPT Thu Mar 7 18:33:15 CST 2019
+#1 SMP PREEMPT Fri Nov 16 11:09:48 2018
+#1 SMP PREEMPT Sat Dec 14 11:23:57 CST 2019
+#2 SMP PREEMPT Fri Apr 24 11:54:13 KST 2020
+#1 SMP PREEMPT Fri Mar 19 16:06:00 CST 2021
+#1 SMP PREEMPT Tue Nov 20 17:10:39 KST 2018
+#1 SMP PREEMPT Mon Apr 9 14:17:58 KST 2018
+#1 SMP PREEMPT Thu Apr 7 16:10:04 KST 2022
+#1 SMP PREEMPT Tue Apr 30 05:33:31 CST 2019
+#1 SMP PREEMPT Mon Mar 25 21:39:13 WIB 2019
+#1 SMP PREEMPT Tue Dec 24 14:51:31 CST 2019
+#1 SMP PREEMPT Tue Jan 23 12:22:33 KST 2018
+#2 SMP PREEMPT Mon Apr 16 14:38:48 CST 2018
+#1 SMP PREEMPT Mon Aug 13 21:35:30 CST 2018
+#1 SMP PREEMPT Sun Dec 16 23:58:06 PST 2018
+#2 SMP PREEMPT Wed May 20 20:45:27 CST 2020
+#1 SMP PREEMPT Sun Dec 2 20:33:57 2018
+#2 SMP PREEMPT Tue Jan 21 17:02:25 KST 2020
+#1 SMP PREEMPT Wed Jun 10 15:56:39 +07 2020
+#1 SMP PREEMPT Tue Jun 25 10:42:24 -03 2019
+#1 SMP PREEMPT Fri Feb 28 11:25:19 CST 2020
+#2 SMP PREEMPT Thu Dec 24 09:26:04 CST 2020
+#1 SMP PREEMPT Mon Sep 10 17:22:14 CST 2018
+#2 SMP PREEMPT Tue Sep 21 14:52:54 CST 2021
+#1 SMP PREEMPT Wed Dec 5 06:04:02 CST 2018
+#1 SMP PREEMPT Tue Feb 13 20:24:05 JST 2018
+#2 SMP PREEMPT Tue Mar 5 07:34:15 CST 2019
+#1 SMP PREEMPT Mon Mar 11 20:05:10 IST 2019
+#1 SMP PREEMPT Tue Sep 28 00:19:53 CST 2021
+#1 SMP PREEMPT Fri Sep 3 16:14:33 JST 2021
+#1 SMP PREEMPT Fri Mar 11 18:15:17 CST 2022
+#1 SMP PREEMPT Wed Dec 20 19:21:17 CST 2017
+#2 SMP PREEMPT Sun Aug 18 02:28:23 CST 2019
+#1 SMP PREEMPT Mon Mar 22 01:08:36 KST 2021
+#1 SMP PREEMPT Tue Oct 16 01:42:09 PDT 2018
+#1 SMP PREEMPT Wed Dec 22 23:50:03 CST 2021 f2fs-hash:c001757c47
+#2 SMP PREEMPT Mon Dec 25 06:03:31 CST 2017
+#1 SMP PREEMPT Sat Oct 19 04:10:37 CST 2019
+#1 SMP PREEMPT Wed Oct 20 23:00:59 CST 2021
+#1 SMP PREEMPT Sat Jun 12 17:12:54 CST 2021
+#1 SMP PREEMPT Mon Jul 1 19:22:47 CST 2019
+#2 SMP PREEMPT Tue Mar 3 17:15:35 KST 2020
+#2 SMP PREEMPT Fri Jul 24 22:36:09 CST 2020
+#1 SMP PREEMPT Mon May 17 16:33:31 IST 2021
+#1 SMP PREEMPT Tue Sep 29 03:18:46 CST 2020
+#2 SMP PREEMPT Tue May 14 20:14:29 KST 2019
+#2 SMP PREEMPT Fri Apr 3 19:33:07 CST 2020
+#1 SMP PREEMPT Mon Jul 2 20:44:19 CST 2018
+#1 SMP PREEMPT Thu Jan 2 20:26:09 CST 2020
+#1 SMP PREEMPT Mon Jul 23 22:21:53 PDT 2018
+#1 SMP PREEMPT Thu Jun 3 08:42:56 CST 2021
+#1 SMP PREEMPT Tue May 17 14:06:47 UTC 2022
+#1 SMP PREEMPT Fri Jul 13 20:57:20 CST 2018
+#2 SMP PREEMPT Sun Jul 8 18:44:43 CST 2018
+#1 SMP PREEMPT Tue Oct 19 17:41:57 CST 2021
+#2 SMP PREEMPT Wed Nov 6 12:00:54 KST 2019
+#1 SMP PREEMPT Mon Dec 7 18:20:24 CST 2020
+#1 SMP PREEMPT Fri Jan 12 15:15:21 KST 2018
+#1 SMP PREEMPT Fri Jun 7 10:19:18 CST 2019
+#1 SMP PREEMPT Fri Aug 20 21:35:51 CST 2021
+#1 SMP PREEMPT Tue Nov 13 18:05:56 KST 2018
+#1 SMP PREEMPT Wed May 27 09:46:58 CST 2020
+#1 SMP PREEMPT Thu Dec 17 14:11:20 CST 2020
+#1 SMP PREEMPT Thu Jun 10 22:05:16 CST 2021
+#1 SMP PREEMPT Tue Mar 27 13:40:12 KST 2018
+#1 SMP PREEMPT Fri Feb 25 02:39:50 JST 2022
+#1 SMP PREEMPT Wed May 30 15:03:20 CDT 2018
+#1 SMP PREEMPT Wed Nov 4 01:12:38 KST 2020
+#1 SMP PREEMPT Wed Feb 13 05:01:32 KST 2019
+#1 SMP PREEMPT Tue Apr 9 12:05:38 CST 2019
+#1 SMP PREEMPT Tue May 25 14:59:00 CST 2021
+#1 SMP PREEMPT Tue Dec 22 01:55:59 JST 2020
+#1 SMP PREEMPT Sat Jul 20 02:36:42 CST 2019
+#1 SMP PREEMPT Fri May 17 15:31:29 -03 2019
+#2 SMP PREEMPT Fri May 17 19:34:54 CST 2019
+#1 SMP PREEMPT Fri Mar 2 15:43:05 CST 2018
+#1 SMP PREEMPT Thu Dec 16 15:20:25 CST 2021
+#1 SMP PREEMPT Wed Mar 7 03:45:20 CST 2018
+#1 SMP PREEMPT Tue Mar 31 02:58:52 CST 2020
+#1 SMP PREEMPT Sat Aug 11 00:05:01 UTC 2018
+#1 SMP PREEMPT Fri Jul 26 21:58:49 CST 2019
+#1 SMP PREEMPT Mon Dec 13 16:28:17 CST 2021
+#1 SMP PREEMPT Tue Mar 17 20:24:55 PDT 2020
+#1 SMP PREEMPT Wed Mar 24 03:51:47 IST 2021
+#1 SMP PREEMPT Sat Oct 17 12:35:08 KST 2020
+#1 SMP PREEMPT Wed Jan 17 02:15:21 JST 2018
+#1 SMP PREEMPT Sat Oct 10 11:58:33 CST 2020
+#1 SMP PREEMPT Tue Sep 11 11:02:47 CST 2018
+#1 SMP PREEMPT Thu Jan 27 23:38:12 UTC 2022
+#2 SMP PREEMPT Fri Dec 25 17:24:43 CST 2020
+#3 SMP PREEMPT Tue Feb 13 17:44:31 KST 2018
+#1 SMP PREEMPT Tue Apr 2 23:59:12 CST 2019
+#1 SMP PREEMPT Mon Jan 10 06:58:42 UTC 2022
+#1 SMP PREEMPT Fri Jul 6 01:38:33 KST 2018
+#33 SMP PREEMPT Thu Jun 3 13:01:31 CST 2021
+#1 SMP PREEMPT Sat May 23 22:07:42 KST 2020
+#2 SMP PREEMPT Mon Jun 1 17:18:25 CST 2020
+#1 SMP PREEMPT Thu Jun 24 16:26:50 CST 2021
+#2 SMP PREEMPT Sat May 1 13:55:35 KST 2021
+#1 SMP PREEMPT Mon Jan 28 21:10:22 KST 2019
+#1 SMP PREEMPT Thu Sep 30 13:02:57 HKT 2021
+#2 SMP PREEMPT Fri Oct 18 17:17:21 CST 2019
+#1 SMP PREEMPT Thu May 27 20:35:42 PDT 2021
+#1 SMP PREEMPT Tue Dec 24 00:28:37 CST 2019
+#2 SMP PREEMPT Thu Jun 18 15:15:33 KST 2020
+#1 SMP PREEMPT Tue Mar 23 16:06:45 CST 2021
+#2 SMP PREEMPT Mon Jul 20 14:10:00 KST 2020
+#1 SMP PREEMPT Thu Oct 15 10:41:44 KST 2020
+#1 SMP PREEMPT Fri Sep 21 18:44:10 CST 2018
+#1 SMP PREEMPT Mon Dec 7 04:02:55 JST 2020
+#1 SMP PREEMPT Wed Nov 4 22:39:53 CET 2020
+#1 SMP PREEMPT Thu Nov 18 15:43:03 UTC 2021
+#1 SMP PREEMPT Fri Dec 14 17:36:58 CST 2018
+#1 SMP PREEMPT Wed Jul 11 04:03:11 CST 2018
+#1 SMP PREEMPT Fri Mar 25 17:36:12 KST 2022
+#1 SMP PREEMPT Wed Jul 11 10:49:52 2018
+#1 SMP PREEMPT Wed Sep 9 01:55:39 CST 2020
+#1 SMP PREEMPT Thu Nov 30 17:49:46 KST 2017
+#1 SMP PREEMPT Fri Feb 9 08:40:55 CET 2018
+#2 SMP PREEMPT Tue Apr 28 17:28:50 CST 2020
+#1 SMP PREEMPT Fri Sep 17 20:03:04 CST 2021
+#1 SMP PREEMPT Mon Jan 21 18:26:23 CST 2019
+#1 SMP PREEMPT Thu Jan 28 23:04:50 CST 2021
+#1 SMP PREEMPT Tue Mar 27 11:52:22 CST 2018
+#1 SMP PREEMPT Wed Mar 6 13:52:26 CST 2019
+#1 SMP PREEMPT Wed May 27 16:01:55 KST 2020
+#1 SMP PREEMPT Thu Oct 24 04:16:30 CST 2019
+#1 SMP PREEMPT Thu Dec 5 21:54:32 CST 2019
+#1 SMP PREEMPT Fri Aug 14 17:29:01 CST 2020
+#1 SMP PREEMPT Fri Apr 13 15:57:01 KST 2018
+#1 SMP PREEMPT Tue Oct 19 22:04:13 CST 2021
+#1 SMP PREEMPT Fri Jan 7 03:15:34 CST 2022
+#1 SMP PREEMPT Wed Jul 10 22:20:46 CST 2019
+#1 SMP PREEMPT Fri Apr 9 09:52:54 CST 2021
+#1 SMP PREEMPT Sat Sep 5 14:17:06 CST 2020
+#1 SMP PREEMPT Thu May 30 22:03:12 CST 2019
+#1 SMP PREEMPT Mon Apr 26 23:59:31 CST 2021
+#1 SMP PREEMPT Fri Nov 22 00:17:42 CST 2019
+#1 SMP PREEMPT Mon Apr 19 20:29:49 PDT 2021
+#1 SMP PREEMPT Thu Dec 3 09:23:00 CST 2020
+#3 SMP PREEMPT Fri Nov 9 00:46:34 CST 2018
+#2 SMP PREEMPT Tue Apr 21 11:16:41 KST 2020
+#1 SMP PREEMPT Thu Apr 21 23:23:56 CST 2022
+#1 SMP PREEMPT Wed Oct 14 04:53:16 CST 2020
+#1 SMP PREEMPT Thu Apr 28 12:32:32 UTC 2022
+#1 SMP PREEMPT Wed Nov 4 13:27:42 CST 2020
+#1 SMP PREEMPT Fri Jul 19 15:48:09 2019
+#1 SMP PREEMPT Thu Aug 19 23:08:22 CST 2021
+#1 SMP PREEMPT Fri Apr 19 18:21:12 KST 2019
+#1 SMP PREEMPT Tue Jul 7 20:21:01 CST 2020
+#2 SMP PREEMPT Thu Nov 8 18:44:53 CST 2018
+#1 SMP PREEMPT Mon Jan 6 11:18:25 CST 2020
+#2 SMP PREEMPT Wed Oct 28 16:19:54 KST 2020
+#8 SMP PREEMPT Thu Mar 4 22:17:06 CST 2021
+#2 SMP PREEMPT Tue Aug 7 15:34:27 CST 2018
+#1 SMP PREEMPT Fri Jun 29 18:42:50 WEST 2018
+#2 SMP PREEMPT Fri Mar 4 17:30:22 CST 2022
+#1 SMP PREEMPT Wed Sep 8 00:58:58 IST 2021
+#1 SMP PREEMPT Tue Sep 17 23:30:48 PDT 2019
+#1 SMP PREEMPT Mon Dec 18 16:49:31 KST 2017
+#1 SMP PREEMPT Tue Dec 14 12:55:35 EST 2021
+#1 SMP PREEMPT Thu Sep 16 16:48:49 UTC 2021
+#1 SMP PREEMPT Mon Feb 28 15:03:04 CST 2022
+#2 SMP PREEMPT Mon Sep 30 21:30:14 KST 2019
+#1 SMP PREEMPT Mon Aug 19 11:23:18 2019
+#1 SMP PREEMPT Tue May 7 19:08:39 CST 2019
+#1 SMP PREEMPT Thu May 17 03:38:07 CST 2018
+#1 SMP PREEMPT Thu May 20 11:30:57 UTC 2021
+#1 SMP PREEMPT Wed May 15 19:17:42 CST 2019
+#1 SMP PREEMPT Thu Nov 29 22:52:20 CST 2018
+#62 SMP PREEMPT Wed Mar 9 19:19:59 CST 2022
+#1 SMP PREEMPT Fri Dec 4 15:14:53 CST 2020
+#1 SMP PREEMPT Thu Dec 20 13:45:25 CST 2018
+#2 SMP PREEMPT Thu Dec 12 20:24:46 CST 2019
+#1 SMP PREEMPT Thu Jun 7 15:00:00 CST 2018
+#1 SMP PREEMPT Tue Jul 24 11:24:56 CEST 2018
+#2 SMP PREEMPT Thu May 23 19:16:45 KST 2019
+#2 SMP PREEMPT Sat Jan 11 05:41:33 KST 2020
+#2 SMP PREEMPT Mon Jun 11 12:39:18 CST 2018
+#1 SMP PREEMPT Tue Jun 2 10:32:02 CST 2020
+#1 SMP PREEMPT Thu Feb 13 10:23:31 2020
+#1 SMP PREEMPT Mon Nov 26 12:35:33 GMT+2 2018
+#1 SMP PREEMPT Tue Mar 26 04:14:13 PDT 2019
+#1 SMP PREEMPT Thu Nov 14 21:11:49 CST 2019
+#1 SMP PREEMPT Sat Jul 4 20:17:01 CST 2020
+#1 SMP PREEMPT Wed Mar 27 12:33:10 CDT 2019
+#1 SMP PREEMPT Fri Jul 13 17:19:33 KST 2018
+#1 SMP PREEMPT Mon Apr 11 18:01:56 CST 2022
+#1 SMP PREEMPT Thu Mar 14 18:39:37 CST 2019
+#1 SMP PREEMPT Mon Jan 17 17:22:28 CST 2022
+#1 SMP PREEMPT Fri Jul 26 10:52:34 CST 2019
+#1 SMP PREEMPT Sat Dec 28 16:02:05 CST 2019
+#2 SMP PREEMPT Wed Nov 6 13:12:51 CST 2019
+#2 SMP PREEMPT Fri Oct 18 19:40:48 CST 2019
+#13 SMP PREEMPT Mon Oct 29 21:49:28 CST 2018
+#1 SMP PREEMPT Tue Nov 17 21:47:49 CST 2020
+#1 SMP PREEMPT Mon Nov 2 22:16:55 KST 2020
+#1 SMP PREEMPT Thu Jul 11 11:58:22 CDT 2019
+#1 SMP PREEMPT Thu Dec 27 10:16:01 KST 2018
+#1 SMP PREEMPT Sat Nov 7 22:04:03 CST 2020
+#1 SMP PREEMPT Thu Mar 7 03:44:06 CST 2019
+#1 SMP PREEMPT Thu Aug 13 23:41:25 CST 2020
+#1 SMP PREEMPT Sat Sep 15 02:17:53 CST 2018
+#1 SMP PREEMPT Tue Jan 14 11:53:28 CST 2020
+#1 SMP PREEMPT Sat Oct 12 17:34:37 CST 2019
+#1 SMP PREEMPT Thu Mar 18 03:30:09 CST 2021
+#1 SMP PREEMPT Wed Jun 12 12:07:47 CST 2019
+#1 SMP PREEMPT Tue Feb 27 11:01:35 KST 2018
+#3 SMP PREEMPT Wed May 27 16:24:55 KST 2020
+#2 SMP PREEMPT Thu Jul 23 16:54:04 CST 2020
+#1 SMP PREEMPT Mon Oct 8 13:50:40 CDT 2018
+#1 SMP PREEMPT Tue Jun 9 23:36:29 CST 2020
+#1 SMP PREEMPT Mon Nov 5 11:11:16 CST 2018
+#1 SMP PREEMPT Wed Oct 2 17:00:15 2019
+#1 SMP PREEMPT Wed Jan 5 15:28:34 EST 2022
+#1 SMP PREEMPT Thu Nov 22 14:03:12 CST 2018
+#1 SMP PREEMPT Thu Jun 4 01:10:47 KST 2020
+#1 SMP PREEMPT Thu Oct 10 16:31:50 CST 2019
+#1 SMP PREEMPT Fri Apr 12 23:25:23 JST 2019
+#1 SMP PREEMPT Wed Jul 22 19:23:33 KST 2020
+#2 SMP PREEMPT Thu Oct 21 15:56:54 CST 2021
+#2 SMP PREEMPT Tue Jan 8 13:05:46 CST 2019
+#1 SMP PREEMPT Fri Mar 1 12:35:37 2019
+#1 SMP PREEMPT Mon Nov 11 21:13:17 2019
+#1 SMP PREEMPT Wed Nov 27 19:08:00 KST 2019
+#1 SMP PREEMPT Thu Apr 30 13:04:21 CST 2020
+#1 SMP PREEMPT Fri Dec 8 09:54:59 KST 2017
+#1 SMP PREEMPT Fri Aug 27 14:17:02 CST 2021
+#1 SMP PREEMPT Sat Jan 13 00:04:54 KST 2018
+#1 SMP PREEMPT Thu Oct 3 05:03:41 PDT 2019
+#1 SMP PREEMPT Fri Apr 8 18:00:51 WIB 2022
+#1 SMP PREEMPT Tue Jul 7 02:48:19 CST 2020
+#1 SMP PREEMPT Wed Jul 31 04:19:10 CST 2019
+#1 SMP PREEMPT Fri Jul 16 16:29:20 CST 2021
+#1 SMP PREEMPT Fri Sep 25 21:36:49 CST 2020
+#1 SMP PREEMPT Wed Apr 25 09:45:24 CST 2018
+#1 SMP PREEMPT Mon Nov 30 01:52:07 CST 2020
+#1 SMP PREEMPT Thu Mar 7 22:23:50 CST 2019
+#1 SMP PREEMPT Wed Aug 26 16:24:35 CST 2020
+#1 SMP PREEMPT Tue Oct 27 16:24:56 KST 2020
+#1 SMP PREEMPT Mon Nov 12 23:56:44 PST 2018
+#2 SMP PREEMPT Thu Nov 19 21:21:07 KST 2020
+#1 SMP PREEMPT Tue Oct 30 00:39:41 CST 2018
+#1 SMP PREEMPT Fri Oct 2 16:08:06 KST 2020
+#1 SMP PREEMPT Tue Nov 17 22:19:48 CST 2020
+#1 SMP PREEMPT Fri Apr 2 22:12:26 +07 2021
+#1 SMP PREEMPT Thu Apr 18 22:11:13 KST 2019
+#1 SMP PREEMPT Fri Oct 18 11:27:05 CST 2019
+#1 SMP PREEMPT Wed Apr 24 02:34:51 CST 2019
+#2 SMP PREEMPT Sun Jul 25 18:46:07 CST 2021
+#1 SMP PREEMPT Tue Sep 4 11:17:45 KST 2018
+#2 SMP PREEMPT Wed Nov 20 00:56:14 KST 2019
+#1 SMP PREEMPT Fri Apr 17 01:26:29 WIB 2020
+#1 SMP PREEMPT Fri Jul 2 12:34:45 CST 2021
+#1 SMP PREEMPT Sat May 7 01:24:35 JST 2022
+#7 SMP PREEMPT Fri Aug 31 19:26:53 CST 2018
+#1 SMP PREEMPT Tue Aug 20 16:03:20 CST 2019
+#1 SMP PREEMPT Fri Nov 16 19:28:30 KST 2018
+#4 SMP PREEMPT Sun Mar 22 07:38:01 CST 2020
+#1 SMP PREEMPT Mon Mar 28 19:22:01 CST 2022
+#1 SMP PREEMPT Mon Feb 17 14:52:56 WIB 2020
+#1 SMP PREEMPT Mon Sep 13 19:51:02 CST 2021
+#1 SMP PREEMPT Fri Oct 12 17:08:12 CST 2018
+#1 SMP PREEMPT Thu Oct 24 21:16:46 KST 2019
+#1 SMP PREEMPT Sun Apr 28 21:50:55 CST 2019
+#1 SMP PREEMPT Fri Jun 12 17:08:40 CST 2020
+#1 SMP PREEMPT Thu Jul 4 00:12:42 CST 2019
+#1 SMP PREEMPT Wed Aug 8 22:09:34 CST 2018
+#1 SMP PREEMPT Thu Mar 19 22:45:38 CST 2020
+#1 SMP PREEMPT Tue Aug 4 11:39:19 CST 2020
+#1 SMP PREEMPT Wed Jul 15 02:37:05 CST 2020
+#1 SMP PREEMPT Tue Jul 13 10:38:49 CST 2021
+#1 SMP PREEMPT Thu Apr 25 00:35:29 PDT 2019
+#1 SMP PREEMPT Mon Mar 7 02:38:04 CST 2022
+#1 SMP PREEMPT Thu Jun 25 14:28:26 IST 2020
+#1 SMP PREEMPT Wed Jul 21 02:20:23 UTC 2021
+#1 SMP PREEMPT Thu Apr 30 10:21:22 CDT 2020
+#2 SMP PREEMPT Tue Apr 27 19:26:36 KST 2021
+#1 SMP PREEMPT Wed Sep 1 15:31:48 CEST 2021
+#1 SMP PREEMPT Thu Sep 6 19:47:37 HKT 2018
+#1 SMP PREEMPT Wed Sep 18 20:30:36 CST 2019
+#1 SMP PREEMPT Wed Nov 3 14:21:57 CST 2021
+#131 SMP PREEMPT Thu Nov 15 11:08:10 CST 2018
+#5 SMP PREEMPT Fri Nov 2 13:58:17 CST 2018
+#1 SMP PREEMPT Wed Jan 17 03:32:21 CST 2018
+#1 SMP PREEMPT Fri Mar 20 06:04:38 CST 2020
+#2 SMP PREEMPT Tue Nov 17 15:37:26 KST 2020
+#1 SMP PREEMPT Sat Jan 9 04:02:08 IST 2021
+#155 SMP PREEMPT Mon Nov 9 15:11:49 CST 2020
+#1 SMP PREEMPT Sun Jul 14 21:22:48 PDT 2019
+#1 SMP PREEMPT Tue Oct 20 09:35:37 KST 2020
+#3 SMP PREEMPT Tue Jun 8 05:12:15 JST 2021
+#1 SMP PREEMPT Thu May 20 12:32:12 CST 2021
+#1 SMP PREEMPT Thu Apr 28 20:31:24 CST 2022
+#1 SMP PREEMPT Thu Nov 19 19:02:27 KST 2020
+#1 SMP PREEMPT Thu Oct 24 11:59:45 CST 2019
+#1 SMP PREEMPT Mon Nov 6 18:23:39 CST 2017
+#1 SMP PREEMPT Thu Sep 10 10:34:19 KST 2020
+#1 SMP PREEMPT Wed Dec 22 01:26:30 CST 2021
+#1 SMP PREEMPT Fri Mar 23 04:47:40 KST 2018
+#1 SMP PREEMPT Thu Dec 20 11:14:30 CST 2018
+#2 SMP PREEMPT Thu Sep 2 15:46:45 CST 2021
+#2 SMP PREEMPT Wed Aug 26 00:00:41 CST 2020
+#1 SMP PREEMPT Tue Oct 23 15:31:59 CST 2018
+#1 SMP PREEMPT Thu Jun 6 09:33:44 CDT 2019
+#1 SMP PREEMPT Tue Aug 24 02:30:56 CST 2021
+#1 SMP PREEMPT Mon Mar 25 21:21:02 CST 2019
+#80 SMP PREEMPT Fri Sep 28 13:49:52 CST 2018
+#2 SMP PREEMPT Tue Oct 9 22:41:26 CST 2018
+#24 SMP PREEMPT Sat Dec 21 11:06:06 CST 2019
+#2 SMP PREEMPT Wed Nov 20 07:22:58 CST 2019
+#2 SMP PREEMPT Fri Mar 16 20:28:54 CST 2018
+#1 SMP PREEMPT Sat Mar 26 16:40:58 CST 2022
+#1 SMP PREEMPT Fri Mar 11 19:34:48 CST 2022
+#1 SMP PREEMPT Wed Jan 15 12:50:05 CST 2020
+#1 SMP PREEMPT Mon Feb 3 19:28:52 KST 2020
+#1 SMP PREEMPT Wed Dec 1 13:30:54 CST 2021
+#1 SMP PREEMPT Tue Feb 5 04:19:34 KST 2019
+#1 SMP PREEMPT Wed Apr 6 22:08:01 CST 2022
+#1 SMP PREEMPT Sun Apr 17 22:56:32 CST 2022
+#1 SMP PREEMPT Tue Dec 3 00:12:05 PST 2019
+#1 SMP PREEMPT Sat Mar 2 13:54:00 CST 2019
+#1 SMP PREEMPT Thu May 31 21:37:23 IST 2018
+#1 SMP PREEMPT Thu Apr 30 13:45:58 CST 2020
+#1 SMP PREEMPT Mon May 25 22:40:01 CST 2020
+#1 SMP PREEMPT Mon Nov 29 20:52:02 CST 2021
+#1 SMP PREEMPT Sat Oct 31 00:14:12 KST 2020
+#2 SMP PREEMPT Thu Sep 12 09:41:28 CST 2019
+#2 SMP PREEMPT Thu Aug 29 00:09:32 CST 2019
+#1 SMP PREEMPT Thu Jan 4 16:27:30 KST 2018
+#1 SMP PREEMPT Sat Jan 1 00:49:54 PST 2022
+#1 SMP PREEMPT Mon Dec 13 09:55:46 CST 2021
+#1 SMP PREEMPT Thu Dec 30 12:00:42 CST 2021
+#1 SMP PREEMPT Mon Dec 28 09:39:19 CST 2020
+#1 SMP PREEMPT Tue Mar 12 00:18:13 PDT 2019
+#1 SMP PREEMPT Mon Jun 22 21:35:46 CST 2020
+#1 SMP PREEMPT Tue Mar 22 23:04:49 CST 2022
+#1 SMP PREEMPT Thu Dec 28 14:41:25 KST 2017
+#1 SMP PREEMPT Tue Dec 15 18:56:02 CST 2020
+#1 SMP PREEMPT Thu Feb 21 01:41:50 CST 2019
+#1 SMP Wed Sep 5 18:30:45 PDT 2018
+#1 SMP PREEMPT Fri Sep 18 22:13:16 CST 2020
+#2 SMP PREEMPT Sat Aug 31 20:52:09 CST 2019
+#1 SMP PREEMPT Fri Aug 7 22:13:08 CST 2020
+#1 SMP PREEMPT Wed Dec 11 12:12:26 CST 2019
+#1 SMP PREEMPT Tue Apr 12 01:13:43 CST 2022
+#1 SMP PREEMPT Fri Nov 6 18:34:09 CST 2020
+#1 SMP PREEMPT Wed Aug 28 18:33:58 CST 2019
+#5 SMP PREEMPT Wed Aug 4 21:41:59 CST 2021
+#1 SMP PREEMPT Wed Apr 29 19:23:20 CST 2020
+#1 SMP PREEMPT Tue Jul 21 13:16:37 CST 2020
+#2 SMP PREEMPT Wed Jul 29 09:53:31 CST 2020
+#2 SMP PREEMPT Fri Nov 2 10:10:17 CST 2018
+#1 SMP PREEMPT Tue Nov 9 13:14:43 CST 2021
+#1 SMP PREEMPT Wed Oct 28 13:42:57 CST 2020
+#1 SMP PREEMPT Tue Feb 2 19:50:07 CST 2021
+#1 SMP PREEMPT Fri Aug 17 05:27:06 KST 2018
+#1 SMP PREEMPT Fri Dec 25 18:40:46 CST 2020
+#2 SMP PREEMPT Wed Jul 28 18:09:48 CST 2021
+#1 SMP PREEMPT Mon Jun 29 18:40:59 CST 2020
+#1 SMP PREEMPT Mon Jun 24 18:20:54 CST 2019
+#2 SMP PREEMPT Tue Aug 25 22:26:03 KST 2020
+#1 SMP PREEMPT Thu Jan 2 11:51:01 CST 2020
+#1 SMP PREEMPT Fri Aug 10 14:26:15 CST 2018
+#1 SMP PREEMPT Tue Mar 2 20:34:23 PST 2021
+#1 SMP PREEMPT Tue Jul 10 10:26:47 KST 2018
+#2 SMP PREEMPT Tue Oct 23 14:43:03 CST 2018
+#1 SMP PREEMPT Thu Mar 10 19:17:38 CST 2022
+#1 SMP PREEMPT Mon Oct 18 14:55:13 CST 2021
+#1 SMP PREEMPT Tue Aug 20 01:22:22 CST 2019
+#2 SMP PREEMPT Mon Apr 22 21:38:31 CST 2019
+#1 SMP PREEMPT Mon Oct 1 10:38:43 KST 2018
+#1 SMP PREEMPT Tue Jun 22 16:25:51 KST 2021
+#1 SMP PREEMPT Tue Feb 26 18:38:22 CST 2019
+#1 SMP PREEMPT Mon Mar 9 11:56:00 CST 2020
+#1 SMP PREEMPT Fri Oct 25 13:05:05 KST 2019
+#1 SMP Thu Dec 12 12:34:10 UTC 2019
+#1 SMP PREEMPT Sun Jan 5 23:58:14 PST 2020
+#1 SMP PREEMPT Wed Aug 5 19:00:01 CST 2020
+#1 SMP PREEMPT Wed Apr 14 00:56:01 CST 2021
+#1 SMP PREEMPT Wed May 18 01:43:41 CST 2022
+#4 SMP PREEMPT Wed Jan 9 10:15:39 CST 2019
+#1 SMP PREEMPT Fri Oct 15 15:36:18 CST 2021
+#2 SMP PREEMPT Wed May 8 11:00:19 KST 2019
+#1 SMP PREEMPT Thu Sep 26 03:06:07 JST 2019
+#1 SMP Fri Nov 23 00:12:44 KST 2018
+#1 SMP PREEMPT Mon Nov 12 16:00:09 EST 2018
+#1 SMP PREEMPT Fri Mar 13 18:03:53 CST 2020
+#1 SMP PREEMPT Sat Feb 29 16:49:07 CST 2020
+#1 SMP PREEMPT Mon May 10 15:50:12 CST 2021
+#1 SMP PREEMPT Sat May 16 02:08:35 CST 2020
+#1 SMP PREEMPT Mon May 23 22:41:27 CST 2022
+#1 SMP PREEMPT Sat Mar 13 11:30:45 CST 2021
+#1 SMP PREEMPT Sun Jun 28 18:49:25 CST 2020
+#1 SMP PREEMPT Mon May 2 12:17:27 CST 2022
+#1 SMP PREEMPT Fri Jan 7 06:27:03 CST 2022
+#1 SMP PREEMPT Mon Dec 23 16:31:17 CST 2019
+#1 SMP PREEMPT Sun Aug 11 22:24:31 CST 2019
+#1 SMP PREEMPT Fri Jun 22 15:49:03 KST 2018
+#1 SMP PREEMPT Mon Mar 28 04:21:56 CST 2022
+#2 SMP PREEMPT Fri Jan 25 10:45:42 KST 2019
+#1 SMP PREEMPT Fri Sep 21 03:53:30 CDT 2018
+#1 SMP PREEMPT Wed Nov 11 11:57:40 CST 2020
+#1 SMP PREEMPT Tue Jun 4 21:08:38 CST 2019
+#2 SMP PREEMPT Tue Oct 12 18:08:42 CST 2021
+#1 SMP PREEMPT Wed Oct 30 04:20:26 CST 2019
+#2 SMP PREEMPT Fri Dec 29 17:14:13 CST 2017
+#1 SMP PREEMPT Wed Dec 25 17:45:25 CST 2019
+#2 SMP PREEMPT Wed Apr 6 11:42:57 CST 2022
+#1 SMP PREEMPT Sat Oct 20 04:11:20 CST 2018
+#1 SMP PREEMPT Fri Aug 10 09:44:08 PDT 2018
+#1 SMP PREEMPT 2021-05-24 12:41:06
+#1 SMP PREEMPT Mon Dec 27 12:46:40 CST 2021 f2fs-hash:a54920e5c9
+#1 SMP PREEMPT Thu Nov 7 14:37:03 CST 2019
+#2 SMP PREEMPT Thu Jan 31 17:40:27 CST 2019
+#1 SMP PREEMPT Tue Apr 14 22:19:45 CST 2020
+#2 SMP PREEMPT Thu Aug 5 11:33:31 CST 2021
+#1 SMP PREEMPT Thu Nov 30 21:36:46 CST 2017
+#1 SMP PREEMPT Mon May 14 03:08:34 2018
+#1 SMP PREEMPT Thu Mar 31 11:48:22 CST 2022
+#1 SMP PREEMPT Wed Jan 23 10:01:11 CST 2019
+#1 SMP PREEMPT Fri Sep 11 15:12:46 JST 2020
+#1 SMP PREEMPT Wed Jun 5 18:54:49 KST 2019
+#1 SMP PREEMPT Tue Apr 26 19:13:12 CST 2022
+#1 SMP PREEMPT Sun Jan 9 23:36:07 CST 2022
+#1 SMP PREEMPT Mon Sep 28 22:57:44 CST 2020
+#1 SMP PREEMPT Wed Oct 9 19:18:04 CST 2019
+#1 SMP PREEMPT Wed May 12 00:30:22 KST 2021
+#1 SMP PREEMPT Wed Aug 5 12:12:19 CST 2020
+#2 SMP PREEMPT Wed Jun 3 20:38:20 CST 2020
+#1 SMP PREEMPT Tue Aug 6 15:53:31 CST 2019
+#1 SMP PREEMPT Wed Jul 4 14:17:34 CDT 2018
+#2 SMP PREEMPT Thu Jan 24 01:56:07 KST 2019
+#1 SMP PREEMPT Tue Aug 28 17:27:24 KST 2018
+#2 SMP PREEMPT Wed Dec 16 11:27:34 CST 2020
+#1 SMP PREEMPT Fri Mar 4 15:31:25 CST 2022
+#1 SMP PREEMPT Tue Dec 3 11:36:31 CST 2019
+#1 SMP PREEMPT Tue Nov 24 23:46:20 CST 2020
+#4 SMP PREEMPT Tue Oct 8 18:50:43 CST 2019
+#2 SMP PREEMPT Thu Dec 10 22:16:39 CST 2020
+#1 SMP PREEMPT Wed Sep 8 15:45:15 CST 2021
+#1 SMP PREEMPT Fri Jun 5 00:10:06 KST 2020
+#1 SMP PREEMPT Thu Sep 19 04:55:10 CST 2019
+#1 SMP PREEMPT Thu Aug 27 17:23:05 CST 2020
+#2 SMP PREEMPT Thu Jun 18 09:56:52 CST 2020
+#1 SMP PREEMPT Fri Apr 17 19:07:31 CST 2020
+#2 SMP PREEMPT Sat May 23 23:37:32 CST 2020
+#1 SMP PREEMPT Fri Mar 13 17:35:04 CST 2020
+#1 SMP PREEMPT Wed Jan 2 15:22:54 KST 2019
+#1 SMP PREEMPT Tue Jul 7 22:55:40 CST 2020
+#1 SMP PREEMPT Fri Dec 25 02:05:13 JST 2020
+#1 SMP PREEMPT Fri May 13 19:09:35 PDT 2022
+#1 SMP PREEMPT Wed Jul 25 20:16:13 CST 2018
+#1 SMP PREEMPT Sat Jan 8 15:02:54 CST 2022
+#1 SMP PREEMPT Fri Aug 21 00:43:58 CST 2020
+#1 SMP PREEMPT Thu Dec 19 19:18:55 CST 2019
+#1 SMP PREEMPT Thu Jul 11 19:13:45 CST 2019
+#1 SMP PREEMPT Thu Jul 2 04:27:23 CST 2020
+#1 SMP PREEMPT Sat Feb 29 20:52:37 KST 2020
+#1 SMP PREEMPT Fri Sep 18 15:24:01 CST 2020
+#1 SMP PREEMPT Mon Jan 13 17:12:48 IST 2020
+#1 SMP PREEMPT Wed Sep 9 20:27:46 CST 2020
+#1 SMP PREEMPT Sat Nov 7 20:23:00 CST 2020
+#1 SMP PREEMPT Fri May 18 19:53:03 KST 2018
+#1 SMP PREEMPT Tue Nov 9 00:32:31 CST 2021
+#2 SMP PREEMPT Wed Oct 30 16:11:11 CST 2019
+#1 SMP PREEMPT Fri Nov 30 10:27:55 CST 2018
+#2 SMP PREEMPT Wed Dec 1 15:40:02 CST 2021
+#1 SMP PREEMPT Tue Apr 5 09:31:59 KST 2022
+#6 SMP PREEMPT Mon Sep 28 15:23:21 CST 2020
+#1 SMP PREEMPT Tue Dec 15 15:14:49 CST 2020
+#1 SMP PREEMPT Wed Oct 28 11:42:10 CDT 2020
+#1 SMP PREEMPT Sat Jun 23 08:31:59 JST 2018
+#1 SMP PREEMPT Sun Jan 13 03:05:22 CST 2019
+#1 SMP PREEMPT Tue Jul 21 05:16:33 CDT 2020
+#1 SMP PREEMPT Wed Nov 4 12:07:22 CST 2020
+#1 SMP PREEMPT Fri Aug 28 01:29:41 IST 2020
+#1 SMP PREEMPT Tue Oct 22 21:50:38 KST 2019
+#1 SMP PREEMPT Fri Dec 3 15:50:19 CST 2021
+#4 SMP PREEMPT Wed Jan 9 18:13:57 CST 2019
+#1 SMP PREEMPT Tue Dec 26 17:33:05 CST 2017
+#1 SMP PREEMPT Thu Oct 29 12:34:41 KST 2020
+#1 SMP PREEMPT Tue Nov 5 17:23:53 CST 2019
+#1 SMP PREEMPT Tue Dec 14 20:57:51 CST 2021
+#1 SMP PREEMPT Sat Mar 12 10:20:38 CST 2022
+#2 SMP PREEMPT Thu Sep 17 20:35:32 KST 2020
+#1 SMP PREEMPT Thu Jul 9 22:55:37 CST 2020
+#1 SMP PREEMPT Mon Apr 18 12:20:32 CST 2022
+#1 SMP PREEMPT Tue May 11 23:06:38 CST 2021
+#1 SMP PREEMPT Tue Apr 6 18:12:23 CST 2021
+#1 SMP PREEMPT Fri Jul 13 13:24:19 CST 2018
+#1 SMP PREEMPT Wed May 30 18:10:56 KST 2018
+#1 SMP PREEMPT Wed Mar 11 18:00:08 PDT 2020
+#1 SMP PREEMPT Wed Jul 21 01:26:48 PDT 2021
+#1 SMP PREEMPT Mon Jul 22 16:23:13 KST 2019
+#1 SMP PREEMPT Mon Sep 9 11:53:43 2019
+#1 SMP PREEMPT Thu Feb 17 00:55:34 CST 2022
+#1 SMP PREEMPT Wed Dec 8 11:49:32 CST 2021
+#1 SMP PREEMPT Thu Feb 17 11:54:24 CST 2022
+#1 SMP PREEMPT Wed Jun 24 10:37:43 CST 2020
+#1 SMP PREEMPT Mon Jun 21 23:30:24 CST 2021
+#1 SMP PREEMPT Fri Jan 10 03:42:08 JST 2020
+#1 SMP PREEMPT Wed Jul 4 03:08:41 CDT 2018
+#1 SMP PREEMPT Wed Dec 23 17:56:39 CST 2020
+#1 SMP PREEMPT Mon Apr 29 21:17:08 CST 2019
+#2 SMP PREEMPT Thu Feb 6 19:53:06 CST 2020
+#1 SMP PREEMPT Wed Dec 27 17:31:54 KST 2017
+#1 SMP PREEMPT Wed Mar 9 04:16:30 JST 2022
+#1 SMP PREEMPT Tue Dec 17 10:58:56 CST 2019
+#2 SMP PREEMPT Wed Jun 27 01:18:19 KST 2018
+#1 SMP PREEMPT Sat Mar 7 03:13:44 CST 2020
+#2 SMP PREEMPT Wed Oct 14 03:37:56 JST 2020
+#0 SMP PREEMPT Thu Jul 2 03:22:48 UTC 2020
+#1 SMP PREEMPT Tue Dec 21 04:32:41 PST 2021
+#1 SMP PREEMPT Thu Mar 4 18:40:33 CST 2021
+#1 SMP PREEMPT Fri Jul 10 13:42:42 JST 2020
+#1 SMP PREEMPT Wed Aug 14 10:09:28 KST 2019
+#1 SMP PREEMPT Fri Feb 11 14:34:45 KST 2022
+#1 SMP PREEMPT Sun Dec 30 00:02:00 EST 2018
+#1 SMP PREEMPT Thu Mar 10 19:51:24 CST 2022
+#1 SMP PREEMPT Sun Apr 24 15:04:54 CST 2022
+#1 SMP PREEMPT Mon Oct 18 03:38:23 CST 2021
+#1 SMP PREEMPT Tue Jun 22 20:53:57 KST 2021
+#1 SMP PREEMPT Wed Jul 21 20:16:11 CST 2021
+#1 SMP PREEMPT Thu Mar 31 02:20:23 CST 2022
+#1 SMP PREEMPT Thu May 19 23:56:12 CST 2022
+#1 SMP PREEMPT Fri Jul 26 04:03:32 CST 2019
+#1 SMP PREEMPT Mon Mar 11 23:32:12 CST 2019
+#4 SMP PREEMPT Mon Sep 21 13:50:14 CST 2020
+#1 SMP PREEMPT Tue Jun 22 01:32:16 CST 2021
+#1 SMP PREEMPT Wed Feb 27 10:00:17 KST 2019
+#1 SMP PREEMPT Mon Mar 7 21:28:55 CST 2022
+#1 SMP PREEMPT Fri Feb 8 10:57:55 CST 2019
+#1 SMP PREEMPT Tue Aug 21 18:15:05 CST 2018
+#2 SMP PREEMPT Tue Jun 23 08:27:20 CST 2020
+#2 SMP PREEMPT Thu Jun 13 10:04:38 KST 2019
+#1 SMP PREEMPT Fri Jul 3 01:17:30 CST 2020
+#1 SMP PREEMPT Mon Oct 29 19:33:35 GMT+3 2018
+#1 SMP PREEMPT Mon Oct 11 13:48:47 CST 2021
+#5 SMP PREEMPT Sat Mar 30 01:46:13 CST 2019
+#1 SMP PREEMPT Fri Apr 8 12:13:28 CST 2022
+#1 SMP PREEMPT Wed Jun 13 10:53:25 2018
+#1 SMP PREEMPT Wed Aug 12 17:14:56 CST 2020
+#2 SMP PREEMPT Thu Mar 19 21:27:10 CST 2020
+#1 SMP PREEMPT Sat Feb 22 01:00:49 CST 2020
+#1 SMP PREEMPT Sun Jun 6 23:37:22 CST 2021
+#1 SMP PREEMPT Thu Jul 30 21:26:30 KST 2020
+#1 SMP PREEMPT Thu Oct 10 16:39:25 CST 2019
+#1 SMP PREEMPT Wed Apr 20 23:43:54 CST 2022
+#1 SMP PREEMPT Fri May 15 01:46:48 KST 2020
+#1 SMP PREEMPT Tue Jan 25 04:09:06 CST 2022
+#1 SMP PREEMPT Tue May 10 17:56:09 CST 2022
+#1 SMP PREEMPT Mon Jul 13 20:40:56 CST 2020
+#1 SMP PREEMPT Tue Jan 21 17:29:28 CST 2020
+#1 SMP PREEMPT Tue Oct 8 21:25:42 CST 2019
+#1 SMP PREEMPT Mon Apr 11 21:22:22 CST 2022
+#1 SMP PREEMPT Thu Jun 13 20:57:43 CST 2019
+#1 SMP PREEMPT Wed May 13 11:52:37 CST 2020
+#1 SMP PREEMPT Tue Apr 20 15:48:08 CST 2021
+#1 SMP PREEMPT Wed Nov 25 23:00:22 CST 2020
+#2 SMP PREEMPT Tue Mar 12 01:45:09 CST 2019
+#1 SMP PREEMPT Mon Sep 30 15:35:34 KST 2019
+#1 SMP PREEMPT Wed Aug 19 21:11:57 CST 2020
+#1 SMP PREEMPT Wed Jun 20 21:19:10 KST 2018
+#1 SMP PREEMPT Tue Oct 12 20:46:08 CST 2021
+#1 SMP PREEMPT Tue Feb 6 21:09:50 KST 2018
+#1 SMP PREEMPT Thu Apr 30 13:56:09 CDT 2020
+#1 SMP PREEMPT Sat Oct 20 20:54:31 CST 2018
+#1 SMP PREEMPT Fri Jul 19 16:37:14 CST 2019
+#1 SMP PREEMPT Thu Aug 30 21:46:32 CST 2018
+#1 SMP PREEMPT Wed Jul 15 21:32:02 -03 2020
+#1 SMP PREEMPT Mon Apr 15 09:30:12 CDT 2019
+#2 SMP PREEMPT Mon Mar 25 19:45:34 KST 2019
+#1 SMP PREEMPT Mon Aug 27 10:31:25 HKT 2018
+#1 SMP PREEMPT Fri Jan 10 14:45:01 CST 2020
+#1 SMP PREEMPT Fri Feb 19 15:26:34 +07 2021
+#1 SMP PREEMPT Fri Sep 21 16:31:19 CST 2018
+#1 SMP PREEMPT Thu Jan 27 05:28:49 CST 2022
+#1 SMP PREEMPT Tue Jul 28 03:01:27 CST 2020
+#1 SMP PREEMPT Tue Jan 18 10:53:41 CST 2022
+#1 SMP PREEMPT Thu Mar 31 05:25:25 CST 2022
+#1 SMP PREEMPT Wed May 12 21:17:05 CST 2021
+#1 SMP PREEMPT Thu Nov 5 15:11:15 CST 2020
+#1 SMP PREEMPT Thu Aug 22 10:13:49 CST 2019
+#1 SMP PREEMPT Fri Nov 12 11:22:36 JST 2021
+#2 SMP PREEMPT Tue Jul 6 08:36:10 CST 2021
+#4 SMP PREEMPT Mon Aug 19 19:02:08 CST 2019
+#1 SMP PREEMPT Fri Jan 11 00:08:02 CST 2019
+#1 SMP PREEMPT Thu Aug 30 11:12:08 CDT 2018
+#1 SMP PREEMPT Tue Jan 9 23:31:52 KST 2018
+#2 SMP PREEMPT Tue Nov 23 22:46:26 CST 2021
+#1 SMP PREEMPT Tue Jan 16 23:22:53 CST 2018
+#1 SMP PREEMPT Thu Mar 24 18:07:50 CST 2022
+#1 SMP PREEMPT Tue Dec 3 11:12:15 CST 2019
+#2 SMP PREEMPT Fri Dec 21 11:32:19 CST 2018
+#1 SMP PREEMPT Sat Oct 19 04:10:37 CST 2019
+#1 SMP PREEMPT Thu Apr 5 20:49:56 CST 2018
+#1 SMP PREEMPT Tue Oct 15 20:50:10 CST 2019
+#1 SMP PREEMPT Wed Mar 30 19:59:39 CST 2022
+#1 SMP PREEMPT Wed Dec 5 22:53:26 2018
+#1 SMP PREEMPT Fri Aug 6 13:23:07 KST 2021
+#1 SMP PREEMPT Sat Feb 16 11:58:56 CST 2019
+#1 SMP PREEMPT Fri Mar 4 21:06:00 CST 2022
+#1 SMP PREEMPT Thu Nov 7 17:44:18 CST 2019
+#1 SMP PREEMPT Fri Mar 4 10:13:03 CST 2022
+#6 SMP PREEMPT Tue Oct 5 20:03:09 UTC 2021
+#1 SMP PREEMPT Fri Jan 12 04:32:55 CST 2018
+#1 SMP PREEMPT Thu Jul 16 18:22:09 CST 2020
+#1 SMP PREEMPT Mon Jun 25 21:12:13 HKT 2018
+#1 SMP PREEMPT Tue Mar 17 21:35:53 CST 2020
+#1 SMP PREEMPT Thu Aug 9 16:26:05 CST 2018
+#4 SMP PREEMPT Tue Apr 13 18:47:32 CST 2021
+#2 SMP PREEMPT Tue Dec 25 09:45:32 CST 2018
+#1 SMP PREEMPT Thu Oct 28 20:31:33 UTC 2021
+#2 SMP PREEMPT Mon Sep 14 22:19:14 KST 2020
+#1 SMP PREEMPT Fri Apr 15 19:10:48 WIB 2022
+#1 SMP PREEMPT Tue Sep 11 18:14:50 CST 2018
+#2 SMP PREEMPT Thu Mar 19 18:32:34 CST 2020
+#1 SMP PREEMPT Mon May 13 19:54:00 CST 2019
+#6 SMP PREEMPT Wed Dec 22 20:30:02 UTC 2021
+#1 SMP PREEMPT Mon Oct 21 22:18:33 CST 2019
+#1 SMP PREEMPT Mon Jun 1 09:37:11 CST 2020
+#1 SMP PREEMPT Tue Apr 12 02:11:21 CST 2022
+#1 SMP PREEMPT Wed Jul 31 04:47:37 CST 2019
+#1 SMP PREEMPT Tue Aug 14 15:32:07 CST 2018
+#2 SMP PREEMPT Mon Apr 27 14:15:14 CST 2020
+#1 SMP PREEMPT Mon Jul 6 11:23:05 CST 2020
+#1 SMP PREEMPT Tue Mar 23 16:13:21 KST 2021
+#1 SMP PREEMPT Mon Jun 4 14:32:46 KST 2018
+#1 SMP PREEMPT Thu Nov 28 15:14:40 KST 2019
+#1 SMP PREEMPT Fri Apr 26 01:17:05 JST 2019
+#1 SMP PREEMPT Sun Apr 24 03:02:24 CST 2022
+#1 SMP PREEMPT Tue Jul 21 10:43:14 KST 2020
+#1 SMP PREEMPT Wed Nov 28 18:03:33 CST 2018
+#1 SMP PREEMPT Tue Mar 17 11:31:13 KST 2020
+#1 SMP PREEMPT Tue Mar 22 21:16:14 CST 2022
+#1 SMP PREEMPT Wed Feb 2 00:45:09 JST 2022
+#1 SMP PREEMPT Mon Mar 12 11:50:44 CST 2018
+#1 SMP PREEMPT Tue Jul 3 11:51:47 CST 2018
+#1 SMP PREEMPT Tue May 24 18:18:00 CST 2022
+#1 SMP PREEMPT Thu Mar 3 21:27:07 CST 2022
+#1 SMP PREEMPT Wed Jan 5 21:28:37 CST 2022
+#1 SMP PREEMPT Wed Jul 21 23:24:20 CST 2021
+#1 SMP PREEMPT Sun Mar 1 02:46:33 KST 2020
+#1 SMP PREEMPT Fri Dec 4 18:35:48 WIB 2020
+#1 SMP PREEMPT Mon May 10 17:41:26 IST 2021
+#1 SMP PREEMPT Wed Nov 7 13:30:09 KST 2018
+#1 SMP PREEMPT Mon Feb 1 18:55:23 CST 2021
+#1 SMP PREEMPT Fri Sep 4 14:52:50 CST 2020
+#1 SMP PREEMPT Mon Jan 24 11:57:09 CST 2022
+#1 SMP PREEMPT Tue Apr 26 11:48:18 CST 2022
+#1 SMP PREEMPT Sun Jun 30 12:03:56 KST 2019
+#2 SMP PREEMPT Fri Feb 21 01:56:32 CST 2020
+#4 SMP PREEMPT Fri Sep 24 17:46:09 CST 2021
+#1 SMP PREEMPT Fri May 8 23:17:55 KST 2020
+#1 SMP PREEMPT Wed Apr 7 14:06:45 CST 2021
+#1 SMP PREEMPT Tue Nov 28 17:53:31 JST 2017
+#1 SMP PREEMPT Mon May 20 12:41:03 CST 2019
+#1 SMP PREEMPT Thu Jul 15 18:57:14 CST 2021
+#1 SMP PREEMPT Sat Nov 27 01:32:13 CST 2021
+#1 SMP PREEMPT Mon Feb 26 11:04:02 CST 2018
+#1 SMP PREEMPT Tue Dec 19 21:52:03 CST 2017
+#1 SMP PREEMPT Tue Jul 30 02:17:03 JST 2019
+#1 SMP PREEMPT Thu Nov 29 11:08:37 CST 2018
+#1 SMP PREEMPT Wed Jan 15 14:44:51 CST 2020
+#1 SMP PREEMPT Tue May 10 15:52:34 CST 2022
+#1 SMP PREEMPT Wed Sep 22 16:56:01 CST 2021
+#1 SMP PREEMPT Mon Jan 24 01:18:47 CST 2022
+#1 SMP PREEMPT Wed Nov 3 12:21:29 CST 2021
+#1 SMP PREEMPT Wed Mar 24 22:31:19 CST 2021
+#1 SMP PREEMPT Thu Jun 3 18:47:21 CST 2021
+#1 SMP PREEMPT Tue Jan 15 14:36:34 CST 2019
+#1 SMP PREEMPT Tue Jun 30 18:42:51 CST 2020
+#2 SMP PREEMPT Sat Aug 8 17:54:24 CST 2020
+#2 SMP PREEMPT Tue May 12 18:25:12 CST 2020
+#1 SMP PREEMPT Mon Jul 13 17:56:34 CST 2020
+#1 SMP PREEMPT Sat Mar 2 07:25:29 CST 2019
+#1 SMP PREEMPT Sat Dec 1 16:37:56 JST 2018
+#1 SMP PREEMPT Fri Jul 30 15:58:22 KST 2021
+#1 SMP PREEMPT Mon Dec 3 22:43:01 PST 2018
+#1 SMP PREEMPT Tue Jan 22 18:24:05 CST 2019
+#26 SMP PREEMPT Sat Nov 27 18:04:27 CST 2021
+#1 SMP PREEMPT Fri Nov 12 16:04:32 CST 2021
+#1 SMP PREEMPT Tue Mar 13 21:29:54 KST 2018
+#1 SMP PREEMPT Mon Aug 20 14:53:54 KST 2018
+#2 SMP PREEMPT Sat Jun 29 19:39:38 CST 2019
+#1 SMP PREEMPT Fri Oct 30 16:36:44 CST 2020
+#1 SMP PREEMPT Fri Dec 10 14:20:58 CST 2021
+#1 SMP PREEMPT Mon Jun 22 20:47:18 CST 2020
+#1 SMP PREEMPT Fri Jun 5 05:02:13 CDT 2020
+#1 SMP PREEMPT Fri Aug 6 17:39:41 CST 2021
+#1 SMP PREEMPT Tue Sep 24 13:48:22 KST 2019
+#2 SMP PREEMPT Sun Apr 19 22:13:53 CST 2020
+#1 SMP PREEMPT Tue Jun 9 21:53:37 KST 2020
+#1 SMP PREEMPT Sun Oct 11 02:10:58 CST 2020
+#1 SMP PREEMPT Wed Nov 14 13:40:45 CST 2018
+#1 SMP PREEMPT Wed Oct 17 05:17:50 KST 2018
+#1 SMP PREEMPT Fri Dec 1 16:47:48 IST 2017
+#1 SMP PREEMPT Thu Nov 29 17:25:27 CST 2018
+#4 SMP PREEMPT Thu Nov 14 01:44:23 PST 2019
+#1 SMP PREEMPT Mon Mar 30 19:55:56 CDT 2020
+#1 SMP PREEMPT Sun May 10 05:35:16 WIB 2020
+#1 SMP PREEMPT Wed Aug 18 15:34:34 CST 2021
+#1 SMP PREEMPT Sat Dec 8 03:06:00 CST 2018
+#1 SMP PREEMPT Tue Mar 6 10:05:04 CET 2018
+#1 SMP PREEMPT Thu Oct 29 00:25:42 WIB 2020
+#1 SMP PREEMPT Thu Dec 27 13:19:55 CST 2018
+#1 SMP PREEMPT Mon Oct 25 20:58:06 CST 2021
+#1 SMP PREEMPT Fri Jan 18 18:11:02 KST 2019
+#2 SMP PREEMPT Mon Jun 25 11:03:00 KST 2018
+#1 SMP PREEMPT Tue Oct 15 20:58:39 CST 2019
+#1 SMP PREEMPT Wed Dec 29 07:48:01 PST 2021
+#1 SMP PREEMPT Tue Sep 7 22:50:22 CST 2021
+#1 SMP PREEMPT Thu Jul 29 20:35:44 PDT 2021
+#4 SMP PREEMPT Mon Nov 1 10:24:39 CST 2021
+#1 SMP PREEMPT Thu Sep 3 17:35:49 KST 2020
+#1 SMP PREEMPT Wed Feb 14 13:20:39 KST 2018
+#1 SMP PREEMPT Wed May 19 13:27:42 CST 2021
+#1 SMP PREEMPT Sat Dec 5 18:20:03 CST 2020
+#1 SMP PREEMPT Thu Jan 25 14:05:29 CST 2018
+#1 SMP PREEMPT Thu Sep 17 19:48:16 KST 2020
+#2 SMP PREEMPT Wed Apr 28 17:18:39 CST 2021
+#2 SMP PREEMPT Thu Jul 9 16:50:00 CST 2020
+#1 SMP PREEMPT Fri Oct 11 13:59:13 CST 2019
+#1 SMP PREEMPT Tue Aug 14 16:08:28 CST 2018
+#1 SMP PREEMPT Fri Mar 27 22:01:27 CST 2020
+#1 SMP PREEMPT Sat Oct 10 03:34:16 CDT 2020
+#1 SMP PREEMPT Fri Jul 9 12:21:34 CST 2021
+#1 SMP PREEMPT Sun Oct 3 21:35:39 PDT 2021
+#1 SMP PREEMPT Fri Jan 17 12:10:30 CST 2020
+#1 SMP PREEMPT Fri Mar 9 10:25:06 CST 2018
+#1 SMP PREEMPT Mon Aug 10 22:10:58 CST 2020
+#1 SMP PREEMPT Fri Aug 6 15:31:42 CST 2021
+#1 SMP PREEMPT Tue Aug 7 11:26:04 CST 2018
+#1 SMP PREEMPT Tue Nov 17 22:22:15 2020
+#1 SMP PREEMPT Tue Jan 29 10:30:55 KST 2019
+#1 SMP PREEMPT Mon Jul 2 16:00:47 JST 2018
+#176 SMP PREEMPT Thu Sep 16 17:17:23 CST 2021
+#1 SMP PREEMPT Sat Jan 22 12:53:17 CST 2022
+#1 SMP PREEMPT Mon Aug 24 01:48:25 PDT 2020
+#2 SMP PREEMPT Wed Nov 20 19:17:04 CST 2019
+#1 SMP PREEMPT Thu Jan 10 18:47:42 CST 2019
+#1 SMP PREEMPT Tue Dec 10 10:52:32 CST 2019
+#1 SMP PREEMPT Thu Feb 17 20:28:36 CST 2022 f2fs-hash:a54920e5c9
+#1 SMP PREEMPT Tue Oct 22 06:05:53 CST 2019
+#1 SMP PREEMPT Tue Sep 11 23:26:27 PDT 2018
+#1 SMP PREEMPT Mon Jun 15 18:14:17 2020
+#1 SMP PREEMPT Fri May 28 15:43:56 CST 2021
+#1 SMP PREEMPT Fri Apr 1 03:05:13 CST 2022
+#1 SMP PREEMPT Wed Sep 8 18:35:13 CST 2021
+#1 SMP PREEMPT Thu Mar 12 13:13:22 CST 2020
+#1 SMP PREEMPT Thu Jul 1 16:34:16 CST 2021
+#1 SMP PREEMPT Thu Oct 28 14:05:51 IST 2021
+#1 SMP PREEMPT Tue Nov 20 01:33:28 CST 2018
+#1 SMP PREEMPT Wed Apr 18 16:50:59 KST 2018
+#1 SMP PREEMPT Thu Jul 5 10:35:23 CST 2018
+#1 SMP PREEMPT Wed Aug 11 21:41:49 +07 2021
+#2 SMP PREEMPT Wed Nov 13 05:11:41 CST 2019
+#1 SMP PREEMPT Thu Jul 18 21:52:58 CST 2019
+#1 SMP PREEMPT Mon Dec 17 00:11:57 PST 2018
+#1 SMP PREEMPT Mon Jun 22 13:06:05 CDT 2020
+#1 SMP PREEMPT Tue Oct 22 11:38:16 WIB 2019
+#1 SMP PREEMPT Mon Feb 14 12:04:29 CST 2022
+#1 SMP PREEMPT Thu Jan 31 11:16:58 CST 2019
+#1 SMP PREEMPT Thu Jul 19 20:34:48 2018
+#1 SMP PREEMPT Mon Dec 6 17:57:22 CST 2021
+#1 SMP PREEMPT Mon Feb 3 18:44:01 KST 2020
+#1 SMP PREEMPT Fri Jun 11 16:30:58 KST 2021
+#1 SMP PREEMPT Tue Apr 28 09:50:30 EDT 2020
+#1 SMP PREEMPT Mon Jul 9 18:47:22 KST 2018
+#1 SMP PREEMPT Mon Dec 25 11:24:15 CST 2017
+#1 SMP PREEMPT Tue May 12 01:58:06 KST 2020
+#1 SMP PREEMPT Fri May 13 16:41:36 CST 2022
+#2 SMP PREEMPT Tue Jan 16 15:22:54 CST 2018
+#1 SMP PREEMPT Thu Feb 27 00:35:45 CST 2020
+#1 SMP PREEMPT Wed May 9 10:29:54 HKT 2018
+#1 SMP PREEMPT Thu Dec 20 04:49:31 CST 2018
+#1 SMP PREEMPT Fri Sep 25 16:37:29 KST 2020
+#2 SMP PREEMPT Mon Dec 24 19:57:31 KST 2018
+#1 SMP PREEMPT Fri Dec 29 12:16:24 CST 2017
+#1 SMP PREEMPT Thu Oct 31 20:56:14 CST 2019
+#1 SMP PREEMPT Thu Sep 19 17:34:05 HKT 2019
+#1 SMP PREEMPT Wed Jun 19 12:09:23 CST 2019
+#1 SMP PREEMPT Thu Oct 21 14:23:47 CST 2021
+#1 SMP PREEMPT Fri Nov 12 17:53:19 CST 2021
+#1 SMP PREEMPT Mon Oct 28 23:13:02 CST 2019
+#1 SMP PREEMPT Mon Aug 24 02:30:29 KST 2020
+#1 SMP PREEMPT Mon Oct 19 14:31:43 CST 2020
+#1 SMP PREEMPT Mon Sep 3 20:53:17 CST 2018
+#1 SMP PREEMPT Wed Dec 2 23:09:40 CST 2020
+#1 SMP PREEMPT Tue Apr 16 18:12:15 CST 2019
+#1 SMP PREEMPT Thu Oct 22 15:13:16 CST 2020
+#1 SMP PREEMPT Thu Dec 23 04:25:56 CST 2021
+#1 SMP PREEMPT Tue Nov 26 23:09:56 KST 2019
+#1 SMP PREEMPT Thu Jul 9 10:08:00 CST 2020
+#1 SMP PREEMPT Tue Dec 11 13:08:41 CST 2018
+#1 SMP PREEMPT Thu Nov 7 10:32:46 CST 2019
+#1 SMP PREEMPT Fri Jul 23 10:29:56 CST 2021
+#1 SMP PREEMPT Sun Nov 1 19:36:37 PST 2020
+#1 SMP PREEMPT Thu Aug 9 16:07:28 KST 2018
+#1 SMP PREEMPT Thu Sep 26 10:11:30 KST 2019
+#1 SMP PREEMPT Sun Nov 15 03:53:01 JST 2020
+#1 SMP PREEMPT Fri Jun 4 18:48:06 CDT 2021
+#2 SMP PREEMPT Thu Sep 6 11:17:57 CST 2018
+#1 SMP PREEMPT Wed Apr 6 23:13:19 CST 2022
+#1 SMP PREEMPT Wed Oct 21 23:57:12 CST 2020
+#1 SMP PREEMPT Sat Oct 10 17:18:39 CST 2020
+#2 SMP PREEMPT Mon May 10 19:27:18 KST 2021
+#1 SMP PREEMPT Sat May 28 00:47:05 CST 2022
+#1 SMP PREEMPT Wed Mar 9 09:53:44 UTC 2022
+#2 SMP PREEMPT Fri Dec 11 02:07:47 CST 2020
+#1 SMP PREEMPT Wed Aug 5 09:07:54 CST 2020
+#1 SMP PREEMPT Wed Mar 9 01:25:18 CST 2022
+#1 SMP PREEMPT Fri May 6 11:17:21 CST 2022
+#1 SMP PREEMPT Mon Jun 17 12:00:06 CST 2019
+#1 SMP PREEMPT Thu Mar 10 00:19:08 IST 2022
+#1 SMP PREEMPT Tue May 11 19:17:04 KST 2021
+#2 SMP PREEMPT Thu May 28 02:57:17 CST 2020
+#1 SMP PREEMPT Mon Sep 9 20:24:35 KST 2019
+#1 SMP PREEMPT Fri Jul 13 12:49:37 KST 2018
+#1 SMP PREEMPT Tue May 19 01:47:11 CST 2020
+#1 SMP PREEMPT Tue Sep 17 23:26:43 PDT 2019
+#1 SMP PREEMPT Wed Nov 13 11:42:48 JST 2019
+#1 SMP PREEMPT Tue Sep 29 14:43:23 KST 2020
+#2 SMP PREEMPT Sun Aug 18 03:41:38 CST 2019
+#1 SMP PREEMPT Fri Jul 13 13:16:10 EDT 2018
+#1 SMP PREEMPT Sat Mar 30 19:20:54 CST 2019
+#2 SMP PREEMPT Thu Jun 20 16:11:11 KST 2019
+#1 SMP PREEMPT Fri Feb 22 13:23:15 CST 2019
+#1 SMP PREEMPT Mon Sep 9 11:34:39 CST 2019
+#1 SMP PREEMPT Fri Dec 20 19:44:42 KST 2019
+#1 SMP PREEMPT Tue Apr 6 13:06:04 KST 2021
+#1 SMP PREEMPT Fri Apr 23 15:11:31 CST 2021
+#1 SMP PREEMPT Sun Feb 11 15:42:41 KST 2018
+#1 SMP PREEMPT Wed Jul 1 21:16:36 CST 2020
+#1 SMP PREEMPT Wed Sep 29 17:35:06 CDT 2021
+#1 SMP PREEMPT Sat Sep 18 21:47:11 CST 2021
+#1 SMP PREEMPT Tue Jul 2 17:27:44 CST 2019
+#1 SMP PREEMPT Wed Jan 27 12:28:51 KST 2021
+#1 SMP PREEMPT Mon Dec 2 23:51:16 PST 2019
+#1 SMP PREEMPT Tue Dec 24 20:59:42 HKT 2019
+#1 SMP PREEMPT Thu Sep 13 16:57:15 CST 2018
+#1 SMP PREEMPT Wed Feb 20 19:43:35 CST 2019
+#1 SMP PREEMPT Mon May 10 21:53:39 CST 2021
+#2 SMP PREEMPT Tue Apr 7 14:22:51 CST 2020
+#1 SMP PREEMPT Mon Apr 12 23:30:00 KST 2021
+#2 SMP PREEMPT Thu Oct 8 22:25:27 CST 2020
+#1 SMP PREEMPT Sat Jun 22 11:28:01 CST 2019
+#1 SMP PREEMPT Thu May 14 20:28:24 JST 2020
+#1 SMP PREEMPT Mon Oct 22 15:51:21 CST 2018
+#1 SMP PREEMPT Fri Apr 5 18:11:39 -03 2019
+#1 SMP PREEMPT Tue Jul 28 21:43:57 KST 2020
+#1 SMP PREEMPT Tue Jan 22 10:55:50 CST 2019
+#1 SMP PREEMPT Wed Oct 7 14:42:08 KST 2020
+#1 SMP PREEMPT Sat Sep 12 04:28:32 CST 2020
+#1 SMP PREEMPT Tue Oct 22 16:24:43 KST 2019
+#1 SMP PREEMPT Wed Jan 9 09:38:53 CST 2019
+#2 SMP PREEMPT Fri May 25 18:27:24 CST 2018
+#1 SMP PREEMPT Fri Dec 4 19:39:21 KST 2020
+#4 SMP PREEMPT Wed Mar 3 00:54:58 CST 2021
+#1 SMP PREEMPT Mon Dec 17 21:31:34 KST 2018
+#1 SMP PREEMPT Thu May 5 15:45:10 CST 2022
+#1 SMP PREEMPT Fri Aug 21 18:49:40 KST 2020
+#1 SMP PREEMPT Wed May 9 22:58:03 CST 2018
+#1 SMP PREEMPT Fri Aug 27 10:38:54 CST 2021
+#1 SMP PREEMPT Wed Jun 3 20:52:28 CST 2020
+#1 SMP PREEMPT Tue Mar 26 16:21:41 KST 2019
+#4 SMP PREEMPT Thu Dec 13 18:39:00 CST 2018
+#2 SMP PREEMPT Sun Apr 25 16:36:41 KST 2021
+#1 SMP PREEMPT Mon Dec 3 10:45:10 CST 2018
+#1 SMP PREEMPT Thu Nov 15 20:20:11 KST 2018
+#1 SMP PREEMPT Mon Sep 30 05:49:34 CST 2019
+#1 SMP PREEMPT Fri Apr 1 03:05:13 CST 2022
+#1 SMP PREEMPT Mon Oct 5 19:18:33 KST 2020
+#1 SMP PREEMPT Thu May 13 15:59:07 KST 2021
+#1 SMP PREEMPT Thu May 30 12:22:04 CST 2019
+#1 SMP PREEMPT Fri Apr 16 14:01:02 KST 2021
+#1 SMP PREEMPT Tue Dec 22 12:04:46 CST 2020
+#1 SMP PREEMPT Fri Dec 7 13:46:47 EST 2018
+#3 SMP PREEMPT Fri Sep 4 06:16:29 KST 2020
+#1 SMP PREEMPT Wed Dec 5 13:53:13 CST 2018
+#1 SMP PREEMPT Tue Apr 9 10:50:29 CST 2019
+#1 SMP PREEMPT Fri Jan 21 13:03:49 CST 2022
+#2 SMP PREEMPT Wed Nov 6 17:45:08 CST 2019
+#145 SMP PREEMPT Wed Dec 29 15:28:11 CST 2021
+#1 SMP PREEMPT Wed Dec 13 15:49:06 KST 2017
+#1 SMP PREEMPT Sun Jan 19 17:51:26 CST 2020
+#1 SMP PREEMPT Thu Dec 6 16:20:30 CST 2018
+#1 SMP PREEMPT Thu Feb 10 19:17:19 KST 2022
+#1 SMP PREEMPT Sat May 19 08:13:08 CST 2018
+#1 SMP PREEMPT Wed Sep 25 23:49:25 PDT 2019
+#1 SMP PREEMPT Tue Jun 23 21:10:06 CST 2020
+#1 SMP PREEMPT Mon Jan 17 22:22:57 CST 2022
+#1 SMP PREEMPT Sat Apr 17 20:22:21 KST 2021
+#1 SMP PREEMPT Sat Mar 21 10:25:29 CST 2020
+#1 SMP PREEMPT Fri Feb 28 19:18:04 IST 2020
+#1 SMP PREEMPT Fri Jan 25 20:42:24 EST 2019
+#5 SMP PREEMPT Fri May 13 13:45:26 UTC 2022
+#1 SMP PREEMPT Thu Nov 28 03:24:06 CST 2019
+#1 SMP PREEMPT Tue Jun 8 22:09:05 PDT 2021
+#1 SMP PREEMPT Mon Mar 5 22:46:02 CST 2018
+#1 SMP PREEMPT Tue May 4 14:42:48 +07 2021
+#1 SMP PREEMPT Fri Jul 23 22:34:11 CST 2021
+#1 SMP PREEMPT Tue Jul 3 22:19:40 KST 2018
+#1 SMP PREEMPT Wed Feb 5 22:57:26 PST 2020
+#1 SMP PREEMPT Fri Mar 15 19:36:26 2019
+#1 SMP PREEMPT Wed Aug 5 18:19:17 CST 2020
+#1 SMP PREEMPT Mon Jun 25 22:16:47 PDT 2018
+#1 SMP PREEMPT Thu May 2 00:10:01 CST 2019
+#1 SMP PREEMPT Mon Oct 12 11:26:36 CST 2020
+#1 SMP PREEMPT Wed Sep 19 18:49:56 CST 2018
+#1 SMP PREEMPT Fri Jul 6 15:08:51 KST 2018
+#2 SMP PREEMPT Tue Jul 28 14:34:04 KST 2020
+#1 SMP PREEMPT Fri Mar 20 02:08:40 CST 2020
+#1 SMP PREEMPT Thu May 31 20:40:31 CST 2018
+#1 SMP PREEMPT Thu Jun 11 17:58:52 CST 2020
+#6 SMP PREEMPT Tue May 17 21:20:43 CST 2022
+#2 SMP PREEMPT Tue Jun 23 21:41:55 KST 2020
+#1 SMP PREEMPT Wed Feb 16 11:23:54 CST 2022
+#1 SMP PREEMPT Tue Jun 1 04:13:04 JST 2021
+#1 SMP PREEMPT Thu Mar 5 16:31:19 CST 2020
+#1 SMP PREEMPT Fri Apr 16 13:54:24 KST 2021
+#1 SMP PREEMPT Fri Jul 19 16:33:34 KST 2019
+#2 SMP PREEMPT Wed Nov 4 03:12:00 JST 2020
+#1 SMP PREEMPT Tue Sep 15 23:54:30 CST 2020
+#1 SMP PREEMPT Tue Dec 5 13:00:02 KST 2017
+#1 SMP PREEMPT Tue Dec 21 20:30:49 UTC 2021
+#1 SMP PREEMPT Mon Dec 24 22:59:04 KST 2018
+#1 SMP PREEMPT Mon Jun 15 21:24:59 KST 2020
+#1 SMP PREEMPT Tue Oct 22 18:38:52 CST 2019
+#1 SMP PREEMPT Thu Aug 2 16:16:30 CST 2018
+#1 SMP PREEMPT Wed May 1 06:39:28 CST 2019
+#1 SMP PREEMPT Wed Oct 20 12:18:30 CST 2021
+#1 SMP PREEMPT Thu Oct 24 17:14:22 +07 2019
+#1 SMP PREEMPT Wed Aug 28 03:25:13 CDT 2019
+#2 SMP PREEMPT Fri Nov 15 19:39:45 KST 2019
+#1 SMP PREEMPT Sat Jun 29 09:27:29 CST 2019
+#1 SMP PREEMPT Fri Apr 27 16:43:11 CST 2018
+#1 SMP PREEMPT Wed Dec 5 15:53:33 CST 2018
+#1 SMP PREEMPT Fri Dec 28 16:25:37 CST 2018
+#1 SMP PREEMPT Wed Jun 16 18:38:52 KST 2021
+#1 SMP PREEMPT Fri Mar 2 21:41:09 CST 2018
+#2 SMP PREEMPT Tue Sep 11 16:19:24 CST 2018
+#2 SMP PREEMPT Thu Mar 19 12:44:02 CST 2020
+#1 SMP PREEMPT Mon Oct 29 17:32:49 CST 2018
+#1 SMP PREEMPT Tue Sep 11 20:20:25 CST 2018
+#1 SMP PREEMPT Sat Oct 19 12:16:34 KST 2019
+#1 SMP PREEMPT Tue Mar 27 18:30:48 +03 2018
+#1 SMP PREEMPT Mon Oct 4 19:47:21 PDT 2021
+#1 SMP PREEMPT Tue Apr 13 21:21:34 CST 2021
+#1 SMP PREEMPT Wed Aug 11 23:08:55 CST 2021
+#1 SMP PREEMPT Mon Apr 18 17:29:04 CST 2022
+#2 SMP PREEMPT Mon Mar 11 09:37:12 CST 2019
+#1 SMP PREEMPT Sat May 9 04:16:25 CST 2020
+#1 SMP PREEMPT Thu Dec 26 02:26:15 CST 2019
+#2 SMP PREEMPT Thu Sep 19 21:40:36 CST 2019
+#1 SMP PREEMPT Fri Feb 25 16:29:34 CST 2022
+#1 SMP PREEMPT Wed Jan 30 07:15:18 MSK 2019
+#1 SMP PREEMPT Tue May 21 22:20:11 CST 2019
+#2 SMP PREEMPT Tue Sep 17 20:54:47 KST 2019
+#1 SMP PREEMPT Fri Aug 20 12:36:00 KST 2021
+#1 SMP PREEMPT Thu Apr 23 13:12:39 CST 2020
+#1 SMP PREEMPT Thu Apr 28 16:41:58 CST 2022
+#1 SMP PREEMPT Sat May 4 11:45:15 CST 2019
+#2 SMP PREEMPT Fri Jun 22 17:44:47 CST 2018
+#1 SMP PREEMPT Tue Apr 14 20:13:53 CST 2020
+#1 SMP PREEMPT Sat Jan 30 15:16:50 CST 2021
+#1 SMP PREEMPT Wed Dec 23 02:45:30 CST 2020
+#1 SMP PREEMPT Wed Apr 1 02:10:45 CST 2020
+#4 SMP PREEMPT Fri Feb 18 17:39:53 UTC 2022
+#1 SMP PREEMPT Thu Oct 3 15:14:30 KST 2019
+#1 SMP PREEMPT Mon Aug 12 12:48:59 CST 2019
+#1 SMP PREEMPT Thu Jul 2 20:22:03 KST 2020
+#1 SMP PREEMPT Mon Jan 8 11:08:38 KST 2018
+#1 SMP PREEMPT Fri May 8 11:05:55 CST 2020
+#1 SMP PREEMPT Mon Mar 14 10:43:58 CST 2022
+#1 SMP PREEMPT Thu Apr 7 11:54:55 CST 2022
+#1 SMP PREEMPT Thu Feb 28 22:00:12 CST 2019
+#1 SMP PREEMPT Fri Dec 14 21:00:05 CST 2018
+#1 SMP PREEMPT Mon Aug 31 17:54:53 CST 2020
+#1 SMP PREEMPT Mon Mar 16 17:33:41 CST 2020
+#1 SMP PREEMPT Wed Jul 18 22:24:58 PDT 2018
+#1 SMP PREEMPT Thu Mar 5 22:03:40 CST 2020
+#1 SMP PREEMPT Fri Dec 31 01:11:08 CST 2021
+#1 SMP PREEMPT Tue Sep 14 21:43:04 PDT 2021
+#1 SMP PREEMPT Fri Dec 20 19:44:41 CST 2019
+#1 SMP PREEMPT Thu Oct 7 05:27:17 PDT 2021
+#1 SMP PREEMPT Wed Jun 20 19:28:11 CST 2018
+#1 SMP PREEMPT Wed May 6 11:27:19 CST 2020
+#1 SMP PREEMPT Thu Dec 24 03:39:22 CST 2020
+#1 SMP PREEMPT Sun Aug 8 21:59:37 PDT 2021
+#1 SMP PREEMPT Wed Jul 17 12:34:44 CST 2019
+#1 SMP PREEMPT Mon Jul 9 12:41:59 KST 2018
+#2 SMP PREEMPT Tue Jul 23 20:39:24 KST 2019
+#5 SMP PREEMPT Thu Jul 30 20:44:16 CST 2020
+#1 SMP PREEMPT Fri Oct 11 22:14:34 CST 2019
+#1 SMP PREEMPT Sun Jan 27 00:18:20 HKT 2019
+#1 SMP PREEMPT Wed Oct 21 12:49:53 KST 2020
+#1 SMP PREEMPT Thu Apr 7 12:15:44 UTC 2022
+#1 SMP PREEMPT Tue Aug 20 18:31:37 CST 2019
+#1 SMP PREEMPT Thu Dec 28 02:59:19 CST 2017
+#1 SMP PREEMPT Fri May 15 05:59:54 CDT 2020
+#1 SMP PREEMPT Fri Oct 30 13:02:10 CST 2020
+#2 SMP PREEMPT Wed May 29 18:04:27 KST 2019
+#1 SMP PREEMPT Wed Mar 10 13:17:34 CST 2021
+#1 SMP PREEMPT Thu May 6 19:10:18 CST 2021
+#2 SMP PREEMPT Sun Sep 29 19:27:00 CST 2019
+#1 SMP PREEMPT Thu Dec 28 16:24:20 CST 2017
+#1 SMP PREEMPT Wed Apr 8 11:35:54 UTC 2020
+#1 SMP PREEMPT Thu Nov 11 21:19:47 CST 2021
+#1 SMP PREEMPT Tue Apr 7 15:16:57 CST 2020
+#2 SMP PREEMPT Sat Nov 14 03:57:44 JST 2020
+#1 SMP PREEMPT Tue Jan 11 04:00:13 CST 2022
+#1 SMP PREEMPT Mon Mar 8 22:27:16 CST 2021
+#1 SMP PREEMPT Sat May 18 11:48:42 CST 2019
+#1 SMP PREEMPT Mon Dec 17 22:08:44 CST 2018
+#1 SMP PREEMPT Sat Aug 29 09:50:26 KST 2020
+#1 SMP PREEMPT Tue Oct 16 02:17:52 PDT 2018
+#1 SMP PREEMPT Sat Jan 26 17:12:40 CST 2019
+#1 SMP PREEMPT Wed Feb 27 21:02:58 CST 2019
+#1 SMP PREEMPT Mon Jun 7 15:55:55 CST 2021
+#1 SMP PREEMPT Wed Aug 18 22:07:21 KST 2021
+#1 SMP PREEMPT Tue May 15 18:17:21 CST 2018
+#1 SMP PREEMPT Fri May 13 18:35:41 CST 2022
+#4 SMP PREEMPT Mon Jul 2 23:13:42 CST 2018
+#1 SMP PREEMPT Tue Nov 30 15:42:18 JST 2021
+#1 SMP PREEMPT Thu Jul 26 11:05:04 CST 2018
+#1 SMP PREEMPT Thu Aug 27 21:06:09 CST 2020
+#1 SMP PREEMPT Tue Jan 4 01:22:46 PST 2022
+#1 SMP PREEMPT Fri May 28 00:24:20 KST 2021
+#1 SMP PREEMPT Wed May 16 18:11:26 JST 2018
+#1 SMP PREEMPT Mon Apr 18 14:22:33 JST 2022
+#2 SMP PREEMPT Thu Oct 10 19:27:59 KST 2019
+#2 SMP PREEMPT Wed Mar 18 16:09:45 CST 2020
+#2 SMP PREEMPT Wed Aug 21 18:15:03 CST 2019
+#2 SMP PREEMPT Mon Jan 22 12:49:47 CST 2018
+#1 SMP PREEMPT Mon Feb 1 10:47:55 UTC 2021
+#1 SMP PREEMPT Thu Nov 7 18:14:39 CST 2019
+#1 SMP PREEMPT Thu Oct 18 17:41:52 CST 2018
+#1 SMP PREEMPT Tue Dec 26 18:59:05 CST 2017
+#1 SMP PREEMPT Wed Apr 20 05:41:51 CST 2022
+#1 SMP PREEMPT Tue Jan 23 19:16:24 KST 2018
+#1 SMP PREEMPT Thu Mar 7 20:38:24 KST 2019
+#1 SMP PREEMPT Fri May 11 15:36:10 CST 2018
+#1 SMP PREEMPT Sat May 15 07:36:22 KST 2021
+#45 SMP PREEMPT Tue Apr 2 09:58:47 CST 2019
+#1 SMP PREEMPT Sun May 8 02:34:55 JST 2022
+#2 SMP PREEMPT Fri Nov 6 11:15:41 CST 2020
+#1 SMP PREEMPT Fri Aug 2 18:57:47 CST 2019
+#1 SMP PREEMPT Sat Jun 29 04:29:05 CST 2019
+#1 SMP PREEMPT Sat Jul 24 00:18:37 CST 2021
+#1 SMP PREEMPT Tue Jan 30 17:32:21 JST 2018
+#1 SMP PREEMPT Tue May 26 22:15:43 CST 2020
+#1 SMP PREEMPT Wed Feb 27 22:07:34 CST 2019
+#1 SMP PREEMPT Wed Mar 28 18:51:36 CST 2018
+#1 SMP PREEMPT Sun Dec 23 01:10:10 CST 2018
+#1 SMP PREEMPT Thu Mar 7 16:47:49 HKT 2019
+#1 SMP PREEMPT Thu Nov 26 20:11:53 JST 2020
+#1 SMP PREEMPT Mon Jun 21 15:15:15 CST 2021
+#1 SMP PREEMPT Tue Jun 5 19:22:16 KST 2018
+#1 SMP PREEMPT Fri Jul 16 15:31:04 CST 2021
+#1 SMP PREEMPT Thu Sep 19 00:14:01 EDT 2019
+#1 SMP PREEMPT Wed Oct 28 22:45:01 CST 2020
+#1 SMP PREEMPT Mon Dec 6 09:13:04 CST 2021
+#1 SMP PREEMPT Tue May 26 21:33:09 CST 2020
+#1 SMP PREEMPT Tue Mar 6 03:50:43 CST 2018
+#1 SMP PREEMPT Thu Feb 7 06:11:12 CST 2019
+#1 SMP PREEMPT Tue May 7 14:08:48 CST 2019
+#1 SMP PREEMPT Mon Jan 13 11:35:29 PST 2020
+#1 SMP PREEMPT Mon Aug 31 20:42:00 CST 2020
+#2 SMP PREEMPT Wed Aug 26 04:14:37 UTC 2020
+#1 SMP PREEMPT Tue Jan 18 20:52:09 CST 2022
+#1 SMP PREEMPT Wed Apr 25 02:02:52 CST 2018
+#1 SMP PREEMPT Wed Dec 11 19:04:27 CST 2019
+#1 SMP PREEMPT Wed Jan 19 13:53:25 UTC 2022
+#1 SMP PREEMPT Sat Aug 1 06:11:46 CST 2020
+#1 SMP PREEMPT Fri Jun 14 14:47:56 2019
+#1 SMP PREEMPT Wed Dec 6 16:01:05 CST 2017
+#1 SMP PREEMPT Tue May 22 04:23:45 PDT 2018
+#2 SMP PREEMPT Tue Mar 6 15:41:53 CST 2018
+#1 SMP PREEMPT Sat Aug 1 11:29:02 CST 2020
+#1 SMP PREEMPT Wed Dec 18 00:41:53 PST 2019
+#1 SMP PREEMPT Thu May 13 23:41:24 JST 2021
+#1 SMP PREEMPT Fri Dec 7 20:53:43 CST 2018
+#2 SMP PREEMPT Mon May 18 00:20:14 CST 2020
+#1 SMP PREEMPT Tue Dec 4 09:26:13 CST 2018
+#1 SMP PREEMPT Thu Nov 29 06:15:13 CST 2018
+#1 SMP PREEMPT Fri Sep 6 07:50:09 CDT 2019
+#1 SMP PREEMPT Fri Mar 8 13:02:14 CST 2019
+#1 SMP PREEMPT Sat Dec 8 17:11:36 IST 2018
+#1 SMP PREEMPT Fri May 29 12:38:00 CST 2020
+#1 SMP PREEMPT Fri Mar 20 18:35:43 CST 2020
+#1 SMP PREEMPT Tue Jan 7 15:29:06 CST 2020
+#2 SMP PREEMPT Mon Sep 7 19:16:23 CST 2020
+#2 SMP PREEMPT Fri Nov 2 15:42:33 CST 2018
+#1 SMP PREEMPT Sat Mar 19 23:25:50 KST 2022
+#1 SMP PREEMPT Thu Aug 6 14:55:29 CDT 2020
+#1 SMP PREEMPT Mon Sep 28 13:11:45 CST 2020
+#1 SMP PREEMPT Sat Apr 2 22:12:00 CST 2022
+#1 SMP PREEMPT Mon Apr 26 20:39:55 CST 2021
+#1 SMP PREEMPT Wed Nov 17 15:05:30 CST 2021
+#1 SMP PREEMPT Wed Mar 3 08:10:49 KST 2021
+#1 SMP PREEMPT Tue Jun 23 17:49:43 CST 2020
+#1 SMP PREEMPT Fri Mar 12 11:02:02 KST 2021
+#1 SMP PREEMPT Wed Apr 7 12:55:16 +07 2021
+#1 SMP PREEMPT Fri Dec 24 18:03:39 CST 2021
+#1 SMP PREEMPT Wed Jan 10 16:23:39 KST 2018
+#2 SMP PREEMPT Tue Nov 17 16:02:53 KST 2020
+#1 SMP PREEMPT Sat Aug 11 15:02:36 CST 2018
+#2 SMP PREEMPT Sat May 16 08:37:48 CST 2020
+#1 SMP PREEMPT Wed May 27 00:02:25 CST 2020
+#1 SMP PREEMPT Tue Jan 29 11:16:33 CST 2019
+#1 SMP PREEMPT Mon Dec 11 18:31:07 CST 2017
+#1 SMP PREEMPT Fri Jan 4 13:38:17 CST 2019
+#1 SMP PREEMPT Sun Mar 20 10:26:34 CST 2022
+#1 SMP PREEMPT Thu Mar 3 00:29:47 CST 2022 f2fs-hash:a54920e5c95
+#1 SMP PREEMPT Mon Jul 30 07:14:14 CST 2018
+#2 SMP PREEMPT Wed Oct 7 23:36:16 CST 2020
+#1 SMP PREEMPT Fri Jan 31 13:11:04 IST 2020
+#2 SMP PREEMPT Wed Jun 5 21:18:54 CST 2019
+#1 SMP PREEMPT Thu Jul 2 23:50:50 KST 2020
+#1 SMP PREEMPT Fri Sep 21 12:34:11 CST 2018
+#1 SMP PREEMPT Thu Nov 18 17:36:48 CST 2021
+#1 SMP PREEMPT Wed Jan 5 19:26:23 CST 2022
+#1 SMP PREEMPT Tue Dec 15 22:49:29 KST 2020
+#2 SMP PREEMPT Fri Apr 26 14:39:22 CST 2019
+#1 SMP PREEMPT Tue Aug 25 11:00:58 KST 2020
+#1 SMP PREEMPT Thu Nov 18 23:42:40 CST 2021
+#1 SMP PREEMPT Fri Aug 17 23:50:37 KST 2018
+#1 SMP PREEMPT Tue Apr 24 19:15:12 CST 2018
+#2 SMP PREEMPT Wed Feb 26 10:49:00 CST 2020
+#1 SMP PREEMPT Fri Jul 27 18:02:52 CST 2018
+#2 SMP PREEMPT Thu Nov 19 21:50:19 KST 2020
+#1 SMP PREEMPT Wed Aug 11 15:37:00 CST 2021
+#1 SMP PREEMPT Fri Feb 28 15:02:16 CST 2020
+#1 SMP PREEMPT Wed May 20 02:41:11 CST 2020
+#1 SMP PREEMPT Tue Aug 28 22:03:06 CST 2018
+#1 SMP PREEMPT Thu Jun 21 04:10:48 GMT 2018
+#1 SMP PREEMPT Thu Mar 10 18:55:39 CST 2022
+#1 SMP PREEMPT Fri Sep 11 15:06:12 JST 2020
+#1 SMP PREEMPT Sun May 31 02:47:12 CDT 2020
+#1 SMP PREEMPT Mon Jun 21 12:58:01 KST 2021
+#3 SMP PREEMPT Fri Jul 27 16:58:07 CST 2018
+#2 SMP PREEMPT Fri May 11 10:37:47 CST 2018
+#1 SMP PREEMPT Fri Mar 22 00:45:09 CST 2019
+#1 SMP PREEMPT Fri Mar 20 21:13:43 KST 2020
+#1 SMP PREEMPT Tue Jun 8 21:20:17 PDT 2021
+#1 SMP PREEMPT Mon Apr 27 17:12:29 CST 2020
+#2 SMP PREEMPT Thu Jan 31 22:56:15 CST 2019
+#1 SMP PREEMPT Mon Mar 21 22:05:00 CST 2022
+#1 SMP PREEMPT Mon Sep 3 15:38:55 CST 2018
+#1 SMP PREEMPT Mon Dec 14 18:48:27 CST 2020
+#1 SMP PREEMPT Sat Sep 29 03:45:01 CST 2018
+#1 SMP PREEMPT Tue Jan 11 13:00:58 CST 2022
+#1 SMP PREEMPT Wed Mar 30 12:25:57 CST 2022
+#1 SMP PREEMPT Fri May 20 04:45:52 CST 2022
+#1 SMP PREEMPT Wed Jul 31 04:47:37 CST 2019
+#1 SMP PREEMPT Fri Apr 12 17:42:25 CST 2019
+#1 SMP PREEMPT Mon Jul 6 22:16:40 CST 2020
+#1 SMP PREEMPT Wed Jun 30 01:34:44 KST 2021
+#1 SMP PREEMPT Wed Apr 1 19:01:41 -03 2020
+#1 SMP PREEMPT Tue Feb 16 20:23:05 PST 2021
+#1 SMP PREEMPT Thu May 5 13:06:12 CST 2022
+#2 SMP PREEMPT Mon Dec 14 17:31:35 CST 2020
+#1 SMP PREEMPT Tue Dec 11 00:08:03 PST 2018
+#1 SMP PREEMPT Thu May 13 22:08:52 CST 2021
+#1 SMP PREEMPT Wed Dec 9 21:31:56 CST 2020
+#1 SMP PREEMPT Wed Feb 24 02:49:10 JST 2021
+#1 SMP PREEMPT Thu Jun 28 04:03:33 CST 2018
+#1 SMP PREEMPT Tue Mar 15 18:17:20 CST 2022
+#1 SMP PREEMPT Fri Dec 11 13:54:32 IST 2020
+#1 SMP PREEMPT Mon Jun 17 08:54:50 EDT 2019
+#1 SMP PREEMPT Mon Jun 8 22:13:29 CST 2020
+#1 SMP PREEMPT Sat Jul 13 11:19:58 CST 2019
+#1 SMP PREEMPT Tue Apr 12 10:31:07 CST 2022
+#2 SMP PREEMPT Thu Jun 13 03:53:16 CST 2019
+#1 SMP PREEMPT Wed Apr 17 17:24:06 CST 2019
+#1 SMP PREEMPT Mon May 27 17:04:20 CST 2019
+#2 SMP PREEMPT Tue Jan 11 16:50:18 CST 2022
+#1 SMP PREEMPT Mon Dec 9 19:53:52 CST 2019
+#1 SMP PREEMPT Fri May 4 06:28:49 EDT 2018
+#1 SMP PREEMPT Mon Mar 12 04:31:11 CST 2018
+#1 SMP PREEMPT Fri Mar 29 22:11:27 KST 2019
+#1 SMP PREEMPT Fri Jun 14 18:03:26 CST 2019
+#1 SMP PREEMPT Tue Oct 9 15:45:36 CST 2018
+#11 SMP PREEMPT Fri Jun 12 16:37:18 CST 2020
+#2 SMP PREEMPT Thu Feb 14 12:01:51 CST 2019
+#1 SMP PREEMPT Tue Oct 9 20:39:02 PDT 2018
+#1 SMP PREEMPT Sat Dec 29 13:09:42 CST 2018
+#1 SMP PREEMPT Tue Dec 31 03:12:02 KST 2019
+#1 SMP PREEMPT Wed Feb 23 00:46:47 KST 2022
+#1 SMP PREEMPT Thu Jan 9 19:46:49 CST 2020
+#1 SMP PREEMPT Wed Jun 26 12:23:51 CST 2019
+#1 SMP PREEMPT Tue Apr 14 20:19:02 CST 2020
+#1 SMP PREEMPT Thu Feb 24 16:35:12 CST 2022
+#1 SMP Thu Jan 23 01:00:08 JST 2020
+#1 SMP PREEMPT Mon Nov 1 23:19:54 CST 2021
+#3 SMP PREEMPT Fri Jul 16 04:06:10 KST 2021
+#1 SMP PREEMPT Wed Dec 13 21:36:44 CST 2017
+#1 SMP PREEMPT Tue Sep 11 23:11:27 PDT 2018
+#1 SMP PREEMPT Fri Apr 15 02:31:51 UTC 2022
+#1 SMP PREEMPT Fri Sep 4 14:35:54 CST 2020
+#1 SMP PREEMPT Fri Apr 27 00:09:44 CST 2018
+#2 SMP PREEMPT Wed Apr 8 06:02:26 CST 2020
+#1 SMP PREEMPT Fri Mar 1 14:24:41 CST 2019
+#1 SMP PREEMPT Tue Mar 12 16:52:14 BRT 2019
+#1 SMP PREEMPT Mon Oct 26 11:10:17 KST 2020
+#1 SMP PREEMPT Thu Aug 13 04:06:00 CST 2020
+#2 SMP PREEMPT Tue Dec 3 01:10:35 CST 2019
+#1 SMP PREEMPT Thu Apr 14 18:51:43 CST 2022
+#1 SMP PREEMPT Tue Aug 28 18:36:35 PDT 2018
+#1 SMP PREEMPT Wed May 30 09:25:52 CST 2018
+#1 SMP PREEMPT Fri Sep 25 21:36:10 CST 2020
+#1 SMP PREEMPT Thu Feb 17 19:47:32 CST 2022
+#1 SMP PREEMPT Fri Mar 2 16:11:41 CST 2018
+#1 SMP Fri Dec 21 05:22:13 PST 2018
+#1 SMP PREEMPT Fri Jan 14 01:02:52 CST 2022
+#1 SMP PREEMPT Sat Feb 12 04:00:53 KST 2022
+#1 SMP PREEMPT Wed Apr 29 11:53:04 CST 2020
+#1 SMP PREEMPT Mon Mar 14 21:25:28 CST 2022
+#1 SMP PREEMPT Fri May 6 13:21:59 CST 2022
+#1 SMP PREEMPT Mon Jan 25 16:07:47 CST 2021
+#2 SMP PREEMPT Sat Jun 16 06:26:52 CST 2018
+#1 SMP PREEMPT Fri May 17 04:51:17 PDT 2019
+#1 SMP PREEMPT Tue Oct 13 14:03:22 CST 2020
+#1 SMP PREEMPT Mon Dec 21 01:21:48 CST 2020
+#1 SMP PREEMPT Fri May 7 01:30:58 CST 2021
+#1 SMP PREEMPT Wed Dec 5 13:40:58 KST 2018
+#2 SMP PREEMPT Fri Mar 29 10:06:23 CST 2019
+#1 SMP PREEMPT Fri Nov 5 18:46:42 CST 2021
+#1 SMP PREEMPT Wed Nov 3 15:59:24 CST 2021
+#1 SMP PREEMPT Fri Jun 22 02:26:14 CDT 2018
+#1 SMP PREEMPT Tue May 19 16:38:51 CST 2020
+#1 SMP PREEMPT Sun Dec 3 16:50:36 EST 2017
+#1 SMP PREEMPT Mon Oct 12 22:24:47 CST 2020
+#1 SMP PREEMPT Thu Apr 28 16:41:58 CST 2022
+#1 SMP PREEMPT Wed Feb 23 19:49:46 CST 2022
+#1 SMP PREEMPT Thu Dec 3 19:00:29 KST 2020
+#1 SMP PREEMPT Sun Feb 16 22:02:20 CST 2020
+#1 SMP PREEMPT Sun Jun 14 18:24:57 CST 2020
+#1 SMP PREEMPT Sun May 12 05:47:35 CST 2019
+#1 SMP PREEMPT Wed Aug 18 21:59:53 CST 2021
+#1 SMP PREEMPT Tue Oct 22 18:38:52 CST 2019
+#1 SMP PREEMPT Mon Jun 15 09:54:23 CST 2020
+#2 SMP PREEMPT Wed Aug 28 22:22:24 KST 2019
+#1 SMP PREEMPT Wed Jul 21 01:19:38 CDT 2021
+#1 SMP PREEMPT Wed Feb 16 11:23:54 CST 2022
+#2 SMP PREEMPT Tue Nov 19 15:27:16 CST 2019
+#1 SMP PREEMPT Mon Jun 4 17:19:25 CST 2018
+#1 SMP PREEMPT Mon Apr 11 04:09:19 JST 2022
+#1 SMP PREEMPT Tue Oct 30 16:07:15 CST 2018
+#1 SMP PREEMPT Thu Nov 7 21:29:47 CST 2019
+#1 SMP PREEMPT Wed Oct 13 11:02:37 CST 2021
+#1 SMP PREEMPT Fri Dec 14 16:09:56 +03 2018
+#1 SMP PREEMPT Fri Apr 29 17:00:51 CST 2022
+#1 SMP PREEMPT Thu Jan 28 11:22:58 +07 2021
+#1 SMP PREEMPT Wed Apr 3 04:09:25 PDT 2019
+#1 SMP PREEMPT Wed Mar 6 14:07:06 IST 2019
+#1 SMP PREEMPT Wed Mar 18 07:40:58 JST 2020
+#1 SMP PREEMPT Thu Feb 10 11:23:07 CST 2022
+#1 SMP PREEMPT Thu Jul 12 05:26:16 CST 2018
+#1 SMP PREEMPT Wed Sep 18 00:19:01 CST 2019
+#1 SMP PREEMPT Tue Oct 27 01:55:57 CST 2020
+#1 SMP PREEMPT Fri Jan 11 18:33:06 CST 2019
+#1 SMP PREEMPT Tue Nov 12 23:29:25 2019
+#1 SMP PREEMPT Sun Feb 21 06:15:28 CST 2021
+#1 SMP PREEMPT Tue Mar 8 08:35:27 KST 2022
+#1 SMP PREEMPT Tue May 28 17:29:50 CST 2019
+#1 SMP PREEMPT Wed Jul 11 20:05:15 CST 2018
+#2 SMP PREEMPT Thu Jul 18 19:22:21 KST 2019
+#1 SMP PREEMPT Fri Mar 1 12:35:37 2019
+#1 SMP PREEMPT Mon Feb 19 17:29:10 KST 2018
+#1 SMP PREEMPT Wed Jan 30 19:27:32 CST 2019
+#1 SMP PREEMPT Fri Jun 11 21:36:10 CST 2021
+#1 SMP PREEMPT Mon Jun 8 20:27:29 CST 2020
+#1 SMP PREEMPT Wed Jan 13 12:29:41 CST 2021
+#1 SMP PREEMPT Fri Nov 29 14:23:33 CST 2019
+#1 SMP PREEMPT Wed Jan 12 03:34:59 CST 2022
+#1 SMP PREEMPT Tue Feb 27 10:09:01 CST 2018
+#1 SMP PREEMPT Mon Jul 24 17:58:45 UTC 2017
+#2 SMP PREEMPT Fri Apr 2 13:53:56 CST 2021
+#1 SMP PREEMPT Wed Oct 30 15:04:33 CDT 2019
+#1 SMP PREEMPT Thu May 5 19:15:56 CST 2022
+#2 SMP PREEMPT Mon Dec 7 16:57:10 KST 2020
+#1 SMP PREEMPT Wed Oct 9 16:38:23 CST 2019
+#1 SMP PREEMPT Thu Aug 5 08:12:27 UTC 2021
+#1 SMP PREEMPT Tue Jul 16 12:10:28 CST 2019
+#1 SMP PREEMPT Wed Jul 31 16:30:15 CST 2019
+#1 SMP PREEMPT Mon Aug 20 17:38:55 KST 2018
+#1 SMP PREEMPT Fri Jan 14 21:53:40 CST 2022
+#1 SMP PREEMPT Fri Aug 24 01:47:53 JST 2018
+#4 SMP PREEMPT Fri Feb 18 17:39:53 UTC 2022
+#1 SMP PREEMPT Wed Jun 17 23:22:57 CST 2020
+#1 SMP PREEMPT Tue Apr 17 23:42:26 CST 2018
+#1 SMP PREEMPT Wed Nov 17 17:27:48 UTC 2021
+#1 SMP PREEMPT Fri Nov 20 13:00:42 KST 2020
+#1 SMP PREEMPT Fri Feb 25 00:30:31 CST 2022
+#1 SMP PREEMPT Thu Apr 28 08:52:21 CST 2022
+#1 SMP PREEMPT Fri Oct 29 16:44:44 CST 2021
+#2 SMP PREEMPT Wed May 26 21:25:30 CST 2021
+#1 SMP PREEMPT Fri Jan 3 22:42:22 CST 2020
+#1 SMP PREEMPT Sat Feb 29 14:30:23 CST 2020
+#1 SMP PREEMPT Wed Feb 27 01:02:26 CST 2019
+#1 SMP PREEMPT Tue Jun 30 02:23:08 CST 2020
+#1 SMP PREEMPT Wed May 18 15:47:21 JST 2022
+#2 SMP PREEMPT Tue Dec 17 00:59:25 CST 2019
+#1 SMP PREEMPT Thu Mar 21 23:28:46 CST 2019
+#1 SMP PREEMPT Fri Dec 28 19:14:27 CST 2018
+#1 SMP PREEMPT Fri Nov 1 11:38:57 UTC 2019
+#1 SMP PREEMPT Tue Mar 1 11:26:46 CST 2022
+#1 SMP PREEMPT Fri Feb 18 08:31:43 UTC 2022
+#1 SMP PREEMPT Sat Feb 23 01:32:00 IST 2019
+#1 SMP PREEMPT Sat Dec 9 12:29:16 CST 2017
+#1 SMP PREEMPT Wed Aug 14 20:46:42 CST 2019
+#1 SMP PREEMPT Thu Oct 17 13:38:52 KST 2019
+#1 SMP PREEMPT Fri Sep 20 19:03:42 CST 2019
+#1 SMP PREEMPT Tue Aug 25 15:52:31 KST 2020
+#1 SMP PREEMPT Tue Jan 30 17:29:26 JST 2018
+#1 SMP PREEMPT Thu Jan 10 12:07:33 CST 2019
+#1 SMP PREEMPT Tue Nov 16 20:44:03 +07 2021
+#1 SMP PREEMPT Fri Sep 4 18:52:58 CST 2020
+#1 SMP PREEMPT Tue Jun 2 14:23:31 CST 2020
+#1 SMP PREEMPT Thu May 20 22:35:07 JST 2021
+#1 SMP PREEMPT Fri Nov 8 16:18:17 CST 2019
+#1 SMP PREEMPT Mon Feb 10 22:39:16 KST 2020
+#2 SMP PREEMPT Tue Jan 11 10:32:49 CST 2022
+#1 SMP PREEMPT Sat May 22 00:30:20 CST 2021
+#1 SMP PREEMPT Wed Aug 5 21:50:30 KST 2020
+#1 SMP PREEMPT Wed Sep 11 17:43:34 KST 2019
+#1 SMP PREEMPT Wed May 19 22:57:41 CST 2021
+#1 SMP PREEMPT Tue Oct 22 12:33:18 CST 2019
+#1 SMP PREEMPT Thu Jul 26 06:13:26 CST 2018
+#1 SMP PREEMPT Thu Jun 21 18:35:09 CST 2018
+#1 SMP PREEMPT Wed Apr 21 02:50:01 IST 2021
+#1 SMP PREEMPT Wed Apr 29 11:07:28 CST 2020
+#1 SMP PREEMPT Fri Mar 16 17:09:37 2018
+#1 SMP PREEMPT Fri Jun 15 15:37:16 CST 2018
+#1 SMP PREEMPT Wed Jun 10 02:05:24 CST 2020
+#1 SMP PREEMPT Mon Apr 4 04:55:27 JST 2022
+#1 SMP PREEMPT Tue Jun 16 12:24:00 CST 2020
+#1 SMP PREEMPT Mon Nov 5 19:58:12 KST 2018
+#1 SMP PREEMPT Mon Dec 14 18:10:42 CST 2020
+#1 SMP PREEMPT Fri Mar 9 16:28:02 KST 2018
+#1 SMP PREEMPT Thu Feb 6 20:10:40 KST 2020
+#1 SMP PREEMPT Fri Dec 3 16:13:29 CST 2021
+#1 SMP PREEMPT Fri Dec 11 13:54:32 IST 2020
+#1 SMP PREEMPT Thu Apr 26 10:54:22 JST 2018
+#2 SMP PREEMPT Wed Jan 6 16:24:13 CST 2021
+#1 SMP PREEMPT Fri Oct 16 18:19:19 CST 2020
+#1 SMP PREEMPT Wed Mar 31 16:25:15 +07 2021
+#1 SMP PREEMPT Wed Mar 24 17:13:11 CST 2021
+#1 SMP PREEMPT Wed Dec 8 22:50:59 CST 2021
+#2 SMP PREEMPT Thu Sep 26 22:59:55 CST 2019
+#2 SMP PREEMPT Wed Feb 12 23:31:42 CST 2020
+#1 SMP PREEMPT Sat Jul 25 04:51:10 KST 2020
+#2 SMP PREEMPT Thu Aug 30 04:28:44 CST 2018
+#1 SMP PREEMPT Thu Feb 27 10:58:15 CST 2020
+#1 SMP PREEMPT Sun Jul 19 19:09:44 UTC 2020
+#1 SMP PREEMPT Sat Mar 5 05:26:22 CST 2022
+#1 SMP PREEMPT Mon Mar 25 19:33:03 CST 2019
+#14 SMP PREEMPT Fri Apr 15 12:36:43 CST 2022
+#0 SMP PREEMPT Fri Sep 18 08:56:16 UTC 2020
+#1 SMP PREEMPT Sat Oct 26 16:18:52 KST 2019
+#1 SMP PREEMPT Wed Aug 22 20:26:30 CST 2018
+#76 SMP PREEMPT Mon Jan 11 16:20:56 CST 2021
+#16 SMP PREEMPT Wed Apr 3 21:36:56 CST 2019
+#1 SMP PREEMPT Sat Oct 10 13:18:28 KST 2020
+#1 SMP PREEMPT Mon Jun 17 01:58:55 CDT 2019
+#1 SMP PREEMPT Tue Dec 21 10:26:12 CST 2021
+#1 SMP PREEMPT Sun Aug 1 08:59:12 CST 2021
+#1 SMP PREEMPT Wed Mar 24 11:33:18 KST 2021
+#1 SMP PREEMPT Fri Nov 19 12:05:27 CST 2021
+#1 SMP PREEMPT Wed Jun 9 14:05:16 PDT 2021
+#1 SMP PREEMPT Fri May 13 12:40:34 CST 2022
+#1 SMP PREEMPT Mon Jan 28 12:08:06 KST 2019
+#1 SMP PREEMPT Mon Apr 8 21:23:11 CST 2019
+#1 SMP PREEMPT Wed Jul 31 04:19:10 CST 2019
+#1 SMP PREEMPT Mon Aug 6 13:31:21 CST 2018
+#1 SMP PREEMPT Sun Dec 12 18:26:49 CST 2021
+#2 SMP PREEMPT Mon Jul 13 16:01:26 KST 2020
+#1 SMP PREEMPT Mon Jul 6 13:13:38 CST 2020
+#1 SMP PREEMPT Thu Dec 13 19:25:02 CST 2018
+#1 SMP PREEMPT Mon Nov 27 11:15:46 2017
+#2 SMP PREEMPT Fri Oct 18 23:24:16 CST 2019
+#1 SMP PREEMPT Fri Sep 7 03:23:13 CST 2018
+#1 SMP PREEMPT Sat Oct 13 14:00:26 CST 2018
+#1 SMP PREEMPT Tue Feb 19 13:36:51 CET 2019
+#2 SMP PREEMPT Fri Oct 18 23:17:25 CST 2019
+#2 SMP PREEMPT Thu Feb 8 17:06:08 KST 2018
+#1 SMP PREEMPT Tue Nov 27 16:42:53 CST 2018
+#1 SMP PREEMPT Sun Apr 14 21:51:28 PDT 2019
+#1 SMP PREEMPT Tue May 22 04:29:30 PDT 2018
+#1 SMP PREEMPT Wed Jul 24 18:28:52 CST 2019
+#1 SMP PREEMPT Thu Nov 7 13:47:39 JST 2019
+#1 SMP PREEMPT Tue Feb 2 00:18:10 JST 2021
+#1 SMP PREEMPT Fri Jan 3 05:19:06 CST 2020
+#1 SMP PREEMPT Wed Feb 12 12:55:49 CST 2020
+#1 SMP PREEMPT Mon Jun 14 19:28:12 KST 2021
+#1 SMP PREEMPT Fri Jul 26 04:03:32 CST 2019
+#1 SMP PREEMPT Thu Mar 21 18:27:51 CST 2019
+#1 SMP PREEMPT Fri Oct 23 18:35:04 CST 2020
+#2 SMP PREEMPT Thu Sep 3 14:40:53 KST 2020
+#1 SMP PREEMPT Wed Feb 16 21:19:21 CST 2022
+#1 SMP PREEMPT Sun Feb 6 16:05:45 CST 2022
+#1 SMP PREEMPT Fri Dec 25 18:38:46 CST 2020
+#1 SMP PREEMPT Tue Oct 1 16:57:30 KST 2019
+#2 SMP PREEMPT Fri Feb 14 00:12:18 CST 2020
+#1 SMP PREEMPT Fri Oct 8 15:58:59 IST 2021
+#1 SMP PREEMPT Tue May 12 23:34:37 KST 2020
+#2 SMP PREEMPT Fri Apr 29 19:12:05 CST 2022
+#1 SMP PREEMPT Wed Sep 11 22:15:34 CST 2019
+#2 SMP PREEMPT Fri Mar 20 23:11:23 CST 2020
+#2 SMP PREEMPT Mon Apr 25 21:31:23 CST 2022
+#1 SMP PREEMPT Tue Apr 27 16:21:05 CST 2021
+#1 SMP PREEMPT Wed Apr 10 13:19:55 CST 2019
+#1 SMP PREEMPT Wed Apr 13 19:18:49 KST 2022
+#1 SMP PREEMPT Mon Oct 18 17:40:09 UTC 2021
+#1 SMP PREEMPT Tue Nov 19 14:46:00 CST 2019
+#2 SMP PREEMPT Thu Aug 9 18:44:00 CST 2018
+#2 SMP PREEMPT Mon Aug 24 21:35:10 CST 2020
+#1 SMP PREEMPT Wed Sep 15 01:37:06 PDT 2021
+#1 SMP PREEMPT Tue Jan 11 16:05:41 UTC 2022
+#1 SMP PREEMPT Sat Feb 16 18:58:13 CST 2019
+#1 SMP PREEMPT Tue Jul 31 18:45:40 CST 2018
+#2 SMP PREEMPT Tue Aug 14 11:33:17 -03 2018
+#1 SMP PREEMPT Mon Feb 10 11:03:43 CST 2020
+#1 SMP PREEMPT Thu Feb 20 15:00:17 CST 2020
+#1 SMP PREEMPT Wed May 22 18:31:12 CST 2019
+#1 SMP PREEMPT Thu Mar 24 22:24:04 KST 2022
+#1 SMP PREEMPT Fri Aug 3 21:07:54 IST 2018
+#1 SMP PREEMPT Tue Dec 26 18:18:29 CST 2017
+#1 SMP PREEMPT Sun Dec 12 10:53:37 CST 2021
+#1 SMP PREEMPT Fri Nov 12 18:47:22 PST 2021
+#1 SMP PREEMPT Fri Oct 16 10:42:21 HKT 2020
+#1 SMP PREEMPT Tue Sep 8 11:22:04 KST 2020
+#2 SMP PREEMPT Wed Jul 1 15:37:07 KST 2020
+#1 SMP PREEMPT Tue Dec 1 02:15:54 IST 2020
+#1 SMP PREEMPT Fri Jul 3 18:31:09 KST 2020
+#1 SMP PREEMPT Sat Apr 27 23:44:11 CST 2019
+#2 SMP PREEMPT Tue May 15 01:25:46 KST 2018
+#1 SMP PREEMPT Tue Dec 11 23:45:12 PST 2018
+#1 SMP PREEMPT Thu Mar 31 03:05:02 CST 2022
+#1 SMP PREEMPT Mon Sep 28 18:22:24 CST 2020
+#10 SMP PREEMPT Fri Jan 4 20:40:00 CST 2019
+#2 SMP PREEMPT Thu Feb 28 03:06:29 JST 2019
+#1 SMP PREEMPT Mon Nov 12 23:42:28 PST 2018
+#2 SMP PREEMPT Fri May 17 13:06:46 CST 2019
+#1 SMP PREEMPT Fri Apr 19 01:04:30 CST 2019
+#1 SMP PREEMPT Thu Mar 22 17:06:14 CST 2018
+#1 SMP PREEMPT Sat Oct 23 14:33:59 KST 2021
+#1 SMP PREEMPT Thu Jan 3 10:47:49 CST 2019
+#1 SMP PREEMPT Fri Aug 31 18:57:12 CST 2018
+#1 SMP PREEMPT Mon Nov 5 11:28:50 PST 2018
+#2 SMP PREEMPT Wed Jul 8 18:13:37 CST 2020
+#1 SMP PREEMPT Fri Oct 29 11:15:39 +08 2021
+#2 SMP PREEMPT Fri Sep 20 06:38:26 CST 2019
+#2 SMP PREEMPT Fri Jan 17 17:34:03 KST 2020
+#2 SMP PREEMPT Wed Nov 27 21:20:27 KST 2019
+#1 SMP PREEMPT Thu Jun 14 15:05:27 CST 2018
+#2 SMP PREEMPT Tue Oct 8 15:02:10 CST 2019
+#1 SMP PREEMPT Sat May 7 12:03:05 CST 2022
+#2 SMP PREEMPT Thu Mar 29 20:42:19 CST 2018
+#1 SMP PREEMPT Tue Jan 29 12:12:11 CST 2019
+#1 SMP PREEMPT Tue Oct 13 18:15:15 KST 2020
+#1 SMP PREEMPT Fri Jun 22 14:38:59 KST 2018
+#1 SMP PREEMPT Thu Jul 29 11:02:51 CST 2021
+#2 SMP PREEMPT Tue Apr 9 12:12:40 CST 2019
+#1 SMP PREEMPT Mon Jul 5 02:06:09 JST 2021
+#1 SMP PREEMPT Tue Oct 27 11:48:00 CST 2020
+#1 SMP PREEMPT Mon Mar 7 09:49:07 CST 2022
+#1 SMP PREEMPT Wed Jun 3 18:22:51 CST 2020
+#1 SMP PREEMPT Tue Aug 31 23:10:51 CST 2021
+#1 SMP PREEMPT Tue Feb 15 15:53:23 CST 2022 f2fs-hash:c001757c47
+#1 SMP PREEMPT Sun May 10 11:58:49 CST 2020
+#1 SMP PREEMPT Thu Dec 19 16:19:55 CST 2019
+#2 SMP PREEMPT Wed May 27 20:38:13 CST 2020
+#1 SMP PREEMPT Thu Apr 12 00:04:48 CST 2018
+#1 SMP PREEMPT Sat Aug 1 17:44:00 CST 2020
+#1 SMP PREEMPT Tue Sep 29 01:10:17 CDT 2020
+#1 SMP PREEMPT Tue Jul 2 20:59:20 CST 2019
+#4 SMP PREEMPT Fri Nov 16 10:33:30 CST 2018
+#1 SMP PREEMPT Tue Jan 29 17:12:46 IST 2019
+#2 SMP PREEMPT Fri Dec 28 17:09:16 CST 2018
+#2 SMP PREEMPT Mon Nov 23 18:39:09 KST 2020
+#1 SMP PREEMPT Thu Mar 15 22:19:42 CST 2018
+#1 SMP PREEMPT Fri Jul 3 12:38:52 CST 2020
+#1 SMP PREEMPT Sat Oct 20 11:29:44 KST 2018
+#1 SMP PREEMPT Sun Sep 12 04:37:13 CDT 2021
+#1 SMP PREEMPT Wed Jul 14 09:48:34 CDT 2021
+#2 SMP PREEMPT Thu Sep 3 11:10:48 KST 2020
+#1 SMP PREEMPT Wed Jul 17 23:05:44 CST 2019
+#1 SMP PREEMPT Thu Jun 6 22:51:03 CST 2019
+#1 SMP PREEMPT Mon Dec 7 11:41:29 CST 2020
+#1 SMP PREEMPT Mon Jan 1 22:23:25 KST 2018
+#1 SMP PREEMPT Fri Feb 28 09:48:18 CST 2020
+#1 SMP PREEMPT Fri Jun 21 20:14:01 KST 2019
+#1 SMP PREEMPT Wed Mar 27 16:54:33 KST 2019
+#1 SMP PREEMPT Tue Aug 20 14:20:39 KST 2019
+#1 SMP PREEMPT Wed Mar 28 08:43:52 CDT 2018
+#1 SMP PREEMPT Fri Mar 1 12:35:37 2019
+#1 SMP PREEMPT Fri Nov 26 23:36:02 CST 2021
+#1 SMP PREEMPT Sun Sep 12 04:37:13 CDT 2021
+#1 SMP PREEMPT Thu Dec 16 12:43:16 CST 2021
+#1 SMP PREEMPT Tue Oct 6 16:28:20 JST 2020
+#1 SMP PREEMPT Thu Sep 2 20:35:11 KST 2021
+#2 SMP PREEMPT Mon Oct 11 01:49:13 UTC 2021
+#1 SMP PREEMPT Sat Dec 5 14:38:34 CST 2020
+#1 SMP PREEMPT Tue Jan 8 12:05:02 CST 2019
+#1 SMP PREEMPT Fri Aug 28 18:36:13 KST 2020
+#1 SMP PREEMPT Thu Mar 22 11:05:20 CST 2018
+#1 SMP PREEMPT Tue Sep 17 23:55:12 PDT 2019
+#1 SMP PREEMPT Mon Jun 24 12:03:23 CST 2019
+#1 SMP PREEMPT Wed Jul 15 01:29:46 PDT 2020
+#1 SMP PREEMPT Thu Nov 30 16:44:11 HKT 2017
+#1 SMP PREEMPT Tue Oct 20 23:51:24 CST 2020
+#1 SMP PREEMPT Tue Nov 9 14:50:33 CST 2021
+#1 SMP PREEMPT Wed Apr 28 17:07:12 CST 2021
+#2 SMP PREEMPT Fri Aug 17 09:10:09 CST 2018
+#1 SMP PREEMPT Fri Mar 9 16:51:56 CST 2018
+#2 SMP PREEMPT Thu Apr 18 05:34:46 KST 2019
+#1 SMP PREEMPT Wed Sep 2 05:06:51 CST 2020
+#1 SMP PREEMPT Tue Jul 2 23:42:17 CDT 2019
+#1 SMP PREEMPT Fri Feb 19 11:33:45 IST 2021
+#1 SMP PREEMPT Thu Feb 10 17:47:16 CST 2022
+#2 SMP PREEMPT Tue Apr 23 18:08:33 CST 2019
+#1 SMP PREEMPT Tue Jan 18 17:38:04 UTC 2022
+#1 SMP PREEMPT Thu Jan 7 09:39:05 UTC 2021
+#1 SMP PREEMPT Sat Jun 5 19:35:51 CST 2021
+#1 SMP PREEMPT Thu Nov 22 03:59:04 CST 2018
+#1 SMP PREEMPT Wed Feb 5 20:06:40 KST 2020
+#1 SMP PREEMPT Mon Dec 17 00:17:47 PST 2018
+#1 SMP PREEMPT Sat Apr 10 19:12:48 CST 2021
+#1 SMP PREEMPT Fri Jan 5 02:43:50 CST 2018
+#1 SMP PREEMPT Wed Sep 9 00:40:31 PDT 2020
+#1 SMP PREEMPT Fri Oct 15 21:00:55 CST 2021
+#1 SMP PREEMPT Tue Apr 3 16:31:44 CST 2018
+#2 SMP PREEMPT Mon Mar 18 12:44:19 KST 2019
+#1 SMP PREEMPT Mon Nov 9 16:17:52 KST 2020
+#1 SMP PREEMPT Thu May 13 10:44:30 CST 2021
+#1 SMP PREEMPT Wed Oct 17 17:16:01 KST 2018
+#2 SMP PREEMPT Tue Jul 24 22:14:15 CST 2018
+#1 SMP PREEMPT Thu Oct 31 12:37:29 CST 2019
+#1 SMP PREEMPT Tue Nov 5 23:42:33 CST 2019
+#1 SMP PREEMPT Sat Feb 3 12:13:54 CST 2018
+#1 SMP PREEMPT Wed Dec 1 20:44:27 CST 2021
+#1 SMP PREEMPT Wed Jan 12 13:37:27 CST 2022
+#1 SMP PREEMPT Mon Jun 3 12:45:00 GMT 2019
+#1 SMP PREEMPT Wed Dec 6 05:42:55 CST 2017
+#1 SMP PREEMPT Tue Jan 7 12:28:38 CST 2020
+#1 SMP PREEMPT Thu Jul 9 12:19:08 KST 2020
+#1 SMP PREEMPT Sat Mar 12 10:20:38 CST 2022
+#1 SMP PREEMPT Mon Mar 5 15:12:40 JST 2018
+#1 SMP PREEMPT Tue Mar 9 14:16:15 KST 2021
+#1 SMP PREEMPT Tue Nov 9 19:21:39 CST 2021
+#2 SMP PREEMPT Mon Nov 29 17:37:16 CST 2021
+#1 SMP PREEMPT Mon Sep 14 23:21:57 KST 2020
+#1 SMP PREEMPT Sat Dec 11 04:19:28 CST 2021
+#1 SMP PREEMPT Fri Oct 1 16:37:54 KST 2021
+#1 SMP PREEMPT Fri Feb 23 17:14:50 BRT 2018
+#1 SMP PREEMPT Mon Jan 14 15:10:56 CST 2019
+#1 SMP PREEMPT Fri Aug 10 09:39:25 PDT 2018
+#1 SMP PREEMPT Thu Mar 11 00:26:31 IST 2021
+#1 SMP PREEMPT Wed Mar 16 03:36:46 CST 2022
+#1 SMP PREEMPT Wed Aug 7 04:11:46 CST 2019
+#1 SMP PREEMPT Tue Jan 14 21:38:23 HKT 2020
+#1 SMP PREEMPT Thu Nov 30 18:57:09 CST 2017
+#1 SMP Tue Mar 31 04:53:35 PDT 2020
+#1 SMP PREEMPT Sat Jul 6 00:22:54 CST 2019
+#2 SMP PREEMPT Fri Oct 5 09:41:24 CST 2018
+#1 SMP PREEMPT Wed Feb 23 05:51:51 UTC 2022
+#1 SMP PREEMPT Tue Apr 20 16:11:58 CST 2021
+#1 SMP PREEMPT Tue May 28 18:34:40 2019
+#1 SMP PREEMPT Thu Sep 3 16:42:14 KST 2020
+#1 SMP PREEMPT Wed Apr 27 18:45:53 CST 2022
+#1 SMP PREEMPT Thu Mar 4 00:32:38 CST 2021
+#1 SMP PREEMPT Sat May 22 05:22:48 CST 2021
+#1 SMP PREEMPT Mon Jul 26 16:14:59 CST 2021
+#1 SMP PREEMPT Thu Dec 7 22:53:31 CST 2017
+#4 SMP PREEMPT Mon Jun 28 07:09:08 CDT 2021
+#1 SMP PREEMPT Fri Nov 20 12:01:22 CST 2020
+#1 SMP PREEMPT Fri Jul 26 13:10:38 KST 2019
+#1 SMP PREEMPT Thu Jan 7 01:11:37 CST 2021
+#1 SMP PREEMPT Mon Dec 13 23:59:24 CST 2021
+#1 SMP PREEMPT Mon Jan 10 06:58:42 UTC 2022
+#116 SMP PREEMPT Fri Mar 13 17:40:40 CST 2020
+#1 SMP PREEMPT Wed Mar 2 10:08:04 CST 2022
+#1 SMP PREEMPT Wed Nov 28 19:29:56 KST 2018
+#2 SMP PREEMPT Mon Mar 2 19:53:47 KST 2020
+#1 SMP PREEMPT Tue Dec 26 20:52:43 KST 2017
+#1 SMP PREEMPT Sat May 8 18:43:30 CST 2021
+#1 SMP PREEMPT Tue Apr 17 17:17:47 CST 2018
+#1 SMP PREEMPT Tue Nov 6 18:09:44 CST 2018
+#1 SMP PREEMPT Thu Mar 28 19:50:34 KST 2019
+#1 SMP PREEMPT Mon Mar 19 10:46:16 KST 2018
+#2 SMP PREEMPT Tue Dec 8 19:49:06 KST 2020
+#1 SMP PREEMPT Fri Jun 5 12:37:59 CST 2020
+#2 SMP PREEMPT Fri Jul 5 01:27:39 CST 2019
+#2 SMP PREEMPT Fri Aug 28 16:40:36 KST 2020
+#1 SMP PREEMPT Sat Aug 1 16:56:58 CST 2020
+#2 SMP PREEMPT Thu Mar 19 19:40:21 CST 2020
+#1 SMP PREEMPT Thu Jan 17 22:12:13 CST 2019
+#1 SMP PREEMPT Mon Apr 26 14:09:55 CST 2021
+#1 SMP PREEMPT Fri Mar 6 09:41:12 CST 2020
+#8 SMP PREEMPT Wed Jul 4 15:58:07 CST 2018
+#1 SMP PREEMPT Wed Apr 1 21:23:40 CST 2020
+#1 SMP PREEMPT Thu Jun 27 14:23:30 CST 2019
+#1 SMP PREEMPT Tue Feb 27 18:57:14 CST 2018
+#1 SMP PREEMPT Thu Mar 17 22:22:50 JST 2022
+#1 SMP PREEMPT Tue Aug 4 11:46:04 CST 2020
+#2 SMP PREEMPT Mon Jan 14 10:53:33 CST 2019
+#1 SMP PREEMPT Thu Oct 31 10:44:12 CST 2019
+#1 SMP PREEMPT Fri Aug 20 21:57:38 CST 2021
+#1 SMP PREEMPT Fri Mar 23 14:50:46 CST 2018
+#1 SMP PREEMPT Tue Jul 10 20:04:39 CST 2018
+#1 SMP PREEMPT Mon Jan 7 14:58:06 CST 2019
+#1 SMP PREEMPT Thu Aug 27 00:22:46 CST 2020
+#2 SMP PREEMPT Fri May 15 05:47:07 CST 2020
+#1 SMP PREEMPT Thu Apr 23 01:25:03 PDT 2020
+#1 SMP PREEMPT Sat Feb 10 02:38:59 CST 2018
+#1 SMP PREEMPT Sun Jun 28 13:05:59 CST 2020
+#1 SMP PREEMPT Thu Jan 31 13:06:55 CST 2019
+#17 SMP PREEMPT Thu Mar 29 10:51:49 CST 2018
+#1 SMP PREEMPT Sat Sep 28 19:39:27 CST 2019
+#1 SMP PREEMPT Wed Aug 29 19:47:48 KST 2018
+#1 SMP PREEMPT Fri Mar 11 15:16:06 CST 2022
+#1 SMP PREEMPT Mon May 13 12:16:37 CEST 2019
+#1 SMP PREEMPT Thu Jul 2 16:42:50 CST 2020
+#1 SMP PREEMPT Wed Oct 31 16:40:34 CST 2018
+#1 SMP PREEMPT Mon Mar 4 03:45:49 CST 2019
+#4 SMP PREEMPT Thu Nov 29 17:54:44 CST 2018
+#1 SMP PREEMPT Wed Oct 21 15:48:48 CST 2020
+#1 SMP PREEMPT Wed Dec 23 23:15:55 CST 2020
+#1 SMP PREEMPT Tue Aug 10 11:40:03 KST 2021
+#1 SMP PREEMPT Fri Jan 8 15:12:06 CST 2021
+#1 SMP PREEMPT Fri Apr 8 18:52:57 CST 2022
+#1 SMP PREEMPT Tue Oct 9 17:27:46 IST 2018
+#1 SMP PREEMPT Fri Oct 30 12:38:15 CST 2020
+#1 SMP PREEMPT Wed Jun 27 13:25:25 CST 2018
+#1 SMP PREEMPT Mon Dec 16 21:04:33 KST 2019
+#1 SMP PREEMPT Wed Jul 8 23:07:19 GMT 2020
+#1 SMP PREEMPT Wed Sep 30 14:20:52 CST 2020
+#1 SMP PREEMPT Sat Jul 4 03:24:42 CST 2020
+#1 SMP PREEMPT Wed Jan 10 19:42:03 CST 2018
+#1 SMP PREEMPT Tue Mar 23 22:26:56 CST 2021
+#1 SMP PREEMPT Tue May 26 15:47:02 CST 2020
+#1 SMP PREEMPT Fri Sep 14 12:28:10 CST 2018
+#1 SMP PREEMPT Thu Apr 28 01:25:24 CST 2022
+#1 SMP PREEMPT Fri Dec 4 15:59:17 CST 2020
+#1 SMP PREEMPT Wed Dec 11 01:52:57 JST 2019
+#1 SMP PREEMPT Mon Sep 24 20:30:17 PDT 2018
+#1 SMP PREEMPT Mon Feb 28 09:41:50 HKT 2022
+#2 SMP PREEMPT Tue May 12 15:59:50 CST 2020
+#1 SMP PREEMPT Sat Dec 25 18:06:21 CST 2021
+#1 SMP PREEMPT Thu Oct 29 12:25:00 CST 2020
+#1 SMP PREEMPT Fri Jun 22 20:25:33 CST 2018
+#1 SMP PREEMPT Wed May 25 03:55:23 WIB 2022 f2fs-hash:33ad71498b
+#3 SMP Thu Feb 27 18:36:58 CET 2020
+#1 SMP PREEMPT Thu Jun 27 10:17:25 CST 2019
+#1 SMP PREEMPT Thu Mar 10 16:39:18 CST 2022
+#1 SMP PREEMPT Fri Apr 3 21:45:31 KST 2020
+#1 SMP Fri Mar 27 16:17:24 JST 2020
+#1 SMP PREEMPT Thu May 20 21:05:43 +07 2021
+#1 SMP PREEMPT Thu Sep 13 22:04:30 CST 2018
+#2 SMP PREEMPT Thu Jun 27 02:33:00 KST 2019
+#1 SMP PREEMPT Sat Feb 26 02:14:28 CST 2022
+#1 SMP PREEMPT Tue Apr 14 17:20:27 CST 2020
+#1 SMP PREEMPT Thu Apr 23 22:42:18 CST 2020
+#2 SMP PREEMPT Sun Aug 5 03:13:17 CST 2018
+#1 SMP PREEMPT Tue Jun 19 04:22:12 PDT 2018
+#1 SMP Fri Jan 8 11:25:51 KST 2021
+#1 SMP PREEMPT Wed Mar 3 16:05:35 CST 2021
+#1 SMP PREEMPT Tue Jul 17 11:50:13 KST 2018
+#1 SMP PREEMPT Fri Jun 12 10:57:54 CST 2020
+#1 SMP PREEMPT Mon Dec 20 18:38:48 CST 2021
+#1 SMP PREEMPT Wed Mar 25 23:31:23 CST 2020
+#2 SMP PREEMPT Mon Jul 6 22:04:34 KST 2020
+#1 SMP PREEMPT Mon Jul 8 13:24:52 IST 2019
+#1 SMP PREEMPT Mon Jun 22 16:39:11 JST 2020
+#1 SMP PREEMPT Tue Aug 18 18:55:00 CST 2020
+#1 SMP PREEMPT Thu Mar 21 11:25:21 CST 2019
+#1 SMP PREEMPT Fri Jun 25 15:27:37 CST 2021
+#1 SMP PREEMPT Tue May 17 23:14:55 CST 2022
+#2 SMP PREEMPT Wed Mar 9 15:00:27 CST 2022
+#1 SMP PREEMPT Wed Jan 9 03:19:37 CST 2019
+#1 SMP PREEMPT Thu Jul 11 15:59:43 CST 2019
+#1 SMP PREEMPT Fri Nov 6 05:45:44 KST 2020
+#9 SMP PREEMPT Sat Aug 24 23:19:51 CST 2019
+#1 SMP PREEMPT Mon Mar 28 10:31:06 UTC 2022
+#1 SMP PREEMPT Mon Sep 23 23:22:30 CST 2019
+#2 SMP PREEMPT Mon Oct 21 15:45:22 CST 2019
+#1 SMP PREEMPT Wed Dec 13 12:33:31 JST 2017
+#1 SMP PREEMPT Tue Dec 7 11:06:24 CST 2021
+#1 SMP PREEMPT Thu Nov 4 13:20:40 IST 2021
+#1 SMP PREEMPT Mon Jan 13 19:03:04 KST 2020
+#1 SMP PREEMPT Thu Feb 28 03:50:09 PST 2019
+#1 SMP PREEMPT Sat Jun 26 23:44:55 CST 2021
+#2 SMP PREEMPT Tue Dec 15 22:50:05 CST 2020
+#1 SMP PREEMPT Fri Apr 3 19:53:13 JST 2020
+#1 SMP PREEMPT Tue Sep 1 14:13:52 CDT 2020
+#1 SMP PREEMPT Tue Apr 28 09:03:01 KST 2020
+#1 SMP PREEMPT Thu Jul 2 09:34:06 CST 2020
+#1 SMP PREEMPT Tue Sep 22 09:58:23 CST 2020
+#1 SMP PREEMPT Wed May 22 23:34:03 CDT 2019
+#4 SMP PREEMPT Sun Jan 12 15:33:32 CST 2020
+#1 SMP PREEMPT Sun Jul 19 18:25:34 CST 2020
+#1 SMP PREEMPT Mon Aug 20 18:24:13 KST 2018
+#1 SMP PREEMPT Thu Aug 1 06:10:51 CST 2019
+#1 SMP PREEMPT Mon May 6 12:04:57 CST 2019
+#1 SMP PREEMPT Thu Apr 18 15:46:21 CST 2019
+#1 SMP PREEMPT Mon Feb 24 01:32:14 PST 2020
+#1 SMP PREEMPT Tue Aug 18 18:48:57 IST 2020
+#1 SMP PREEMPT Thu Jun 20 04:55:23 PDT 2019
+#12 SMP PREEMPT Wed Apr 7 10:12:42 CST 2021
+#1 SMP PREEMPT Tue Apr 21 23:53:24 CST 2020
+#2 SMP PREEMPT Wed Aug 26 16:33:32 KST 2020
+#1 SMP PREEMPT Thu Jan 25 15:33:48 KST 2018
+#1 SMP PREEMPT Mon Feb 5 14:04:59 CET 2018
+#1 SMP PREEMPT Wed Feb 13 17:20:36 CST 2019
+#1 SMP PREEMPT Tue Sep 29 00:09:59 CST 2020
+#1 SMP PREEMPT Wed Oct 10 22:32:42 KST 2018
+#1 SMP PREEMPT Thu Mar 21 19:48:41 CST 2019
+#1 SMP PREEMPT Fri Jul 19 15:18:20 2019
+#1 SMP PREEMPT Thu Jun 13 20:52:29 CST 2019
+#1 SMP PREEMPT Mon Mar 7 11:59:28 CST 2022
+#1 SMP PREEMPT Mon Nov 27 11:15:46 2017
+#1 SMP PREEMPT Tue Jan 7 21:18:15 CST 2020
+#1 SMP PREEMPT Mon Oct 28 12:14:26 CST 2019
+#1 SMP PREEMPT Fri Apr 15 18:58:52 CST 2022
+#1 SMP PREEMPT Thu Mar 8 17:33:53 EST 2018
+#1 SMP PREEMPT Tue Jul 24 21:37:04 CST 2018
+#1 SMP PREEMPT Thu Mar 8 13:37:55 CST 2018
+#1 SMP PREEMPT Mon Mar 29 22:40:48 JST 2021
+#1 SMP PREEMPT Mon May 7 16:37:21 KST 2018
+#1 SMP PREEMPT Mon Mar 7 07:23:52 EST 2022
+#2 SMP PREEMPT Thu Jun 28 17:30:54 CST 2018
+#1 SMP PREEMPT Fri Nov 12 23:36:54 CST 2021
+#1 SMP PREEMPT Tue Nov 26 15:52:45 KST 2019
+#1 SMP PREEMPT Fri Jul 12 11:56:50 CST 2019
+#1 SMP PREEMPT Wed Sep 22 20:53:28 CST 2021
+#1 SMP PREEMPT Thu Jan 11 18:48:40 CST 2018
+#1 SMP PREEMPT Wed Mar 9 05:13:19 UTC 2022
+#1 SMP PREEMPT Tue Dec 26 19:27:53 CST 2017
+#1 SMP PREEMPT Fri Dec 31 19:06:22 CST 2021
+#1 SMP PREEMPT Mon Dec 25 16:12:21 CST 2017
+#1 SMP PREEMPT Thu Dec 19 16:26:40 CST 2019
+#10 SMP PREEMPT Fri Nov 16 14:58:42 CST 2018
+#1 SMP PREEMPT Thu Dec 14 14:05:31 KST 2017
+#1 SMP PREEMPT Mon May 17 12:22:33 KST 2021
+#1 SMP PREEMPT Thu Dec 20 20:40:58 CST 2018
+#1 SMP PREEMPT Fri May 15 02:30:03 CST 2020
+#2 SMP PREEMPT Sat Aug 22 15:13:09 CST 2020
+#1 SMP PREEMPT Sat Apr 16 14:50:33 WIB 2022
+#1 SMP PREEMPT Tue Sep 4 19:14:55 CST 2018
+#1 SMP PREEMPT Mon Feb 5 15:40:30 CST 2018
+#1 SMP PREEMPT Thu Jan 2 14:14:57 CST 2020
+#1 SMP PREEMPT Fri Sep 11 06:20:18 CST 2020
+#1 SMP PREEMPT Mon Jun 1 12:52:07 PDT 2020
+#1 SMP PREEMPT Wed Dec 23 20:30:38 KST 2020
+#1 SMP PREEMPT Thu Jan 10 17:33:44 KST 2019
+#1 SMP PREEMPT Thu Jun 13 22:09:01 CST 2019
+#1 SMP PREEMPT Thu Feb 24 20:34:43 CST 2022
+#1 SMP PREEMPT Fri May 15 15:34:12 KST 2020
+#2 SMP PREEMPT Thu May 28 12:50:33 KST 2020
+#1 SMP PREEMPT Tue Nov 2 21:46:32 PDT 2021
+#2 SMP PREEMPT Sun Apr 15 00:45:17 CST 2018
+#1 SMP PREEMPT Wed Nov 10 14:45:24 CST 2021
+#1 SMP PREEMPT Mon Dec 17 18:17:32 CST 2018
+#1 SMP PREEMPT Tue Aug 11 10:02:54 CST 2020
+#1 SMP PREEMPT Mon May 18 18:43:03 WIB 2020
+#1 SMP PREEMPT Wed Oct 21 15:02:14 CST 2020
+#1 SMP PREEMPT Sat Apr 17 00:24:53 EDT 2021
+#1 SMP PREEMPT Tue Aug 4 12:43:05 CST 2020
+#2 SMP PREEMPT Fri Oct 19 19:51:18 CST 2018
+#1 SMP PREEMPT Thu Mar 24 02:22:07 PDT 2022
+#1 SMP PREEMPT Wed Aug 11 15:48:39 CST 2021
+#3 SMP PREEMPT Wed Jan 27 22:12:45 KST 2021
+#1 SMP PREEMPT Sat Aug 18 06:12:55 CST 2018
+#1 SMP PREEMPT Thu Mar 5 18:31:47 CST 2020
+#1 SMP PREEMPT Fri Mar 18 16:48:51 CST 2022
+#1 SMP PREEMPT Tue May 26 16:34:35 CST 2020
+#1 SMP PREEMPT Tue Jul 23 05:06:49 CST 2019
+#1 SMP PREEMPT Tue May 15 03:02:37 EDT 2018
+#1 SMP PREEMPT Tue Sep 24 13:15:36 CST 2019
+#1 SMP PREEMPT Wed Nov 25 23:24:55 IST 2020
+#1 SMP PREEMPT Wed Jul 21 16:23:14 KST 2021
+#1 SMP PREEMPT Mon Jan 10 16:05:49 UTC 2022
+#1 SMP PREEMPT Tue Jun 16 19:24:19 KST 2020
+#1 SMP PREEMPT Fri Dec 31 01:12:28 PST 2021
+#1 SMP PREEMPT Tue Mar 29 14:31:49 CST 2022
+#1 SMP PREEMPT Sat Dec 5 19:02:18 CST 2020
+#1 SMP PREEMPT Sat Jul 6 10:01:09 CST 2019
+#1 SMP PREEMPT Thu Aug 2 01:00:42 UTC 2018
+#2 SMP PREEMPT Wed Sep 5 18:41:32 CST 2018
+#1 SMP PREEMPT Mon May 24 16:57:26 +07 2021
+#2 SMP PREEMPT Thu Sep 19 03:03:08 CST 2019
+#1 SMP PREEMPT Tue Jan 25 20:58:38 CST 2022
+#1 SMP PREEMPT Fri Feb 9 15:46:11 CST 2018
+#2 SMP PREEMPT Thu May 7 06:42:45 CST 2020
+#2 SMP Sat Feb 1 06:38:07 UTC 2020
+#2 SMP PREEMPT Tue May 21 12:18:29 JST 2019
+#1 SMP PREEMPT Fri Dec 31 11:32:37 KST 2021
+#1 SMP PREEMPT Thu Jul 19 22:41:52 CST 2018
+#4 SMP PREEMPT Wed Mar 17 14:46:10 CST 2021
+#2 SMP PREEMPT Wed Oct 28 00:43:31 CST 2020
+#2 SMP PREEMPT Thu Sep 10 20:53:04 KST 2020
+#1 SMP PREEMPT Wed Mar 24 10:18:11 JST 2021
+#1 SMP PREEMPT Wed Nov 17 14:18:49 CST 2021
+#1 SMP PREEMPT Thu Oct 21 03:34:33 CST 2021
+#1 SMP PREEMPT Sat Mar 10 01:36:57 CST 2018
+#1 SMP PREEMPT Sat Sep 18 21:30:59 CST 2021
+#1 SMP PREEMPT Mon May 17 12:22:42 CST 2021
+#1 SMP PREEMPT Sat Jan 6 14:53:51 CST 2018
+#1 SMP PREEMPT Mon Jan 29 20:42:50 HKT 2018
+#1 SMP PREEMPT Wed Jun 24 18:09:14 CST 2020
+#2 SMP PREEMPT Wed Sep 18 17:49:44 KST 2019
+#1 SMP PREEMPT Tue Jul 14 08:07:00 CDT 2020
+#1 SMP PREEMPT Wed Sep 12 06:02:25 CST 2018
+#1 SMP PREEMPT Mon Jan 14 17:39:35 CST 2019
+#1 SMP PREEMPT Mon Aug 2 12:01:01 +07 2021
+#1 SMP PREEMPT Wed Sep 4 16:42:25 KST 2019
+#2 SMP PREEMPT Thu Aug 23 10:28:09 CST 2018
+#1 SMP PREEMPT Tue Oct 29 13:04:59 HKT 2019
+#1 SMP PREEMPT Fri May 31 11:29:56 JST 2019
+#2 SMP PREEMPT Thu Sep 26 12:21:17 CST 2019
+#1 SMP PREEMPT Fri Mar 11 10:28:53 CST 2022
+#1 SMP PREEMPT Sat Aug 1 17:54:28 CST 2020
+#1 SMP PREEMPT Thu Mar 19 19:52:01 CST 2020
+#1 SMP PREEMPT Tue Jan 11 19:28:01 UTC 2022
+#1 SMP PREEMPT Sun Jul 19 07:44:16 CST 2020
+#1 SMP PREEMPT Tue Feb 23 16:06:09 CST 2021
+#1 SMP PREEMPT Sun Jan 5 16:17:12 CST 2020
+#1 SMP PREEMPT Sat Nov 14 14:58:54 CST 2020
+#1 SMP PREEMPT Fri May 8 01:06:03 CST 2020
+#1 SMP PREEMPT Fri Apr 30 17:37:06 KST 2021
+#2 SMP PREEMPT Mon Sep 2 12:37:17 KST 2019
+#1 SMP PREEMPT Fri May 31 19:18:55 CST 2019
+#2 SMP PREEMPT Tue Dec 29 16:15:47 CST 2020
+#1 SMP PREEMPT Wed Jun 30 17:39:26 CST 2021
+#2 SMP PREEMPT Sun Jul 5 20:49:56 CST 2020
+#2 SMP PREEMPT Wed Oct 2 18:28:18 KST 2019
+#2 SMP PREEMPT Tue Jul 23 21:28:58 CST 2019
+#1 SMP PREEMPT Wed Jan 19 07:09:48 JST 2022
+#1 SMP PREEMPT Sat Jan 13 22:27:04 KST 2018
+#1 SMP PREEMPT Sun Dec 27 13:34:39 CST 2020
+#2 SMP PREEMPT Wed Jul 1 21:02:01 KST 2020
+#1 SMP PREEMPT Tue Apr 9 14:56:33 EDT 2019
+#1 SMP PREEMPT Thu Sep 12 12:11:44 CST 2019
+#1 SMP PREEMPT Wed May 2 12:32:39 CST 2018
+#1 SMP PREEMPT Wed May 6 17:54:51 KST 2020
+#1 SMP PREEMPT Mon Aug 20 21:31:18 CST 2018
+#1 SMP PREEMPT Thu Oct 28 17:05:55 CST 2021
+#2 SMP PREEMPT Wed May 27 14:51:12 CST 2020
+#1 SMP PREEMPT Thu Apr 22 21:26:24 KST 2021
+#1 SMP PREEMPT Wed Apr 13 21:39:02 CST 2022
+#1 SMP PREEMPT Tue Mar 3 02:01:03 KST 2020
+#1 SMP PREEMPT Thu Jun 25 05:37:46 CST 2020
+#2 SMP PREEMPT Tue Feb 22 19:59:02 CST 2022
+#1 SMP PREEMPT Fri Mar 4 03:44:23 CST 2022
+#1 SMP PREEMPT Sun Apr 22 02:23:53 CST 2018
+#1 SMP PREEMPT Sat Sep 11 17:39:56 CST 2021
+#1 SMP PREEMPT Fri Jun 25 18:03:48 CDT 2021
+#9 SMP PREEMPT Thu Sep 6 17:44:52 CST 2018
+#1 SMP PREEMPT Thu May 5 20:13:32 CST 2022
+#1 SMP PREEMPT Wed Apr 7 14:48:01 KST 2021
+#1 SMP PREEMPT Wed Dec 4 17:53:33 HKT 2019
+#1 SMP PREEMPT Tue Oct 19 18:59:04 CST 2021
+#1 SMP PREEMPT Wed May 9 03:05:37 CST 2018
+#1 SMP PREEMPT Tue Sep 17 23:53:06 PDT 2019
+#1 SMP PREEMPT Fri Sep 25 09:59:02 CST 2020
+#1 SMP PREEMPT Tue Nov 19 10:20:42 CST 2019
+#1 SMP PREEMPT Wed May 22 02:16:40 CST 2019
+#1 SMP PREEMPT Fri Nov 1 16:19:37 CST 2019
+#1 SMP PREEMPT Thu Mar 8 13:37:55 CST 2018
+#4 SMP PREEMPT Tue Nov 17 12:48:39 CST 2020
+#2 SMP PREEMPT Thu Apr 30 09:49:52 CST 2020
+#1 SMP PREEMPT Tue Jun 2 06:32:02 CDT 2020
+#1 SMP PREEMPT Fri Apr 9 23:57:41 CST 2021
+#1 SMP PREEMPT Tue Mar 3 16:54:44 CST 2020
+#1 SMP PREEMPT Mon Oct 21 00:37:37 PDT 2019
+#1 SMP PREEMPT Fri Sep 18 19:21:37 CST 2020
+#1 SMP PREEMPT Sat Aug 21 17:47:03 CST 2021
+#1 SMP PREEMPT Thu Dec 27 16:08:14 CST 2018
+#1 SMP PREEMPT Mon Nov 30 12:19:47 CST 2020
+#2 SMP PREEMPT Sat Mar 16 13:03:23 CST 2019
+#1 SMP PREEMPT Mon Jun 1 12:46:45 PDT 2020
+#2 SMP PREEMPT Tue Jul 10 16:46:23 CST 2018
+#1 SMP PREEMPT Wed Dec 1 04:57:58 CST 2021
+#1 SMP PREEMPT Thu May 31 19:00:26 CST 2018
+#1 SMP PREEMPT Mon May 16 00:00:39 CST 2022
+#2 SMP PREEMPT Thu Sep 17 04:54:42 KST 2020
+#2 SMP PREEMPT Fri Jun 22 12:58:29 CST 2018
+#1 SMP PREEMPT Wed Dec 5 22:21:19 CST 2018
+#1 SMP PREEMPT Mon Apr 15 19:12:41 CST 2019
+#1 SMP PREEMPT Fri Mar 4 17:36:46 CST 2022
+#2 SMP PREEMPT Fri May 24 18:53:40 KST 2019
+#1 SMP PREEMPT Wed Apr 22 17:43:11 CST 2020
+#2 SMP PREEMPT Thu May 28 15:05:16 CST 2020
+#1 SMP PREEMPT Wed Nov 4 22:23:09 CST 2020
+#1 SMP PREEMPT Wed Apr 13 23:17:14 CST 2022
+#1 SMP PREEMPT Sat Aug 8 02:20:52 CST 2020
+#1 SMP PREEMPT Wed Aug 1 21:08:58 PDT 2018
+#1 SMP PREEMPT Fri Nov 26 16:41:27 CST 2021
+#1 SMP PREEMPT Tue Jun 15 10:08:27 CST 2021
+#1 SMP PREEMPT Tue Aug 10 20:32:57 CST 2021 f2fs-hash:cf69bad37b
+#1 SMP PREEMPT Thu Feb 17 21:15:40 UTC 2022
+#1 SMP PREEMPT Wed Feb 16 22:07:05 CST 2022
+#1 SMP PREEMPT Mon Jun 21 19:16:06 KST 2021
+#1 SMP PREEMPT Mon Dec 16 01:05:09 JST 2019
+#1 SMP PREEMPT Thu Sep 23 23:18:54 CST 2021
+#2 SMP PREEMPT Tue Apr 14 10:12:56 CST 2020
+#2 SMP PREEMPT Mon Jun 10 02:11:33 CST 2019
+#1 SMP PREEMPT Thu Jul 11 16:31:32 CST 2019
+#2 SMP PREEMPT Sat Apr 20 14:04:57 CST 2019
+#1 SMP PREEMPT Tue Feb 22 20:37:23 CST 2022
+#1 SMP PREEMPT Wed Jul 11 01:24:15 EDT 2018
+#1 SMP PREEMPT Fri Aug 9 11:04:31 CST 2019
+#1 SMP PREEMPT Thu Aug 15 01:31:46 PDT 2019
+#2 SMP PREEMPT Sat Oct 17 16:00:31 KST 2020
+#1 SMP PREEMPT Wed Aug 1 20:04:54 CST 2018
+#2 SMP PREEMPT Fri Oct 12 16:12:18 CST 2018
+#1 SMP PREEMPT Fri Jul 20 18:44:40 CST 2018
+#2 SMP PREEMPT Tue Jan 7 17:21:14 CST 2020
+#1 SMP Fri Nov 16 19:06:20 KST 2018
+#1 SMP PREEMPT Mon Mar 7 01:19:14 CST 2022
+#2 SMP PREEMPT Tue Dec 25 21:52:20 CST 2018
+#1 SMP PREEMPT Wed Oct 16 15:47:38 KST 2019
+#1 SMP PREEMPT Wed Mar 13 19:56:13 JST 2019
+#1 SMP PREEMPT Sat Jan 23 03:30:31 CST 2021
+#1 SMP PREEMPT Sat Oct 19 17:14:42 CST 2019
+#4 SMP PREEMPT Thu Dec 10 20:21:19 CST 2020
+#1 SMP PREEMPT Mon Jul 6 20:07:59 KST 2020
+#1 SMP PREEMPT Sat Jan 23 03:18:25 UTC 2021
+#1 SMP PREEMPT Tue Apr 27 19:00:35 CST 2021
+#1 SMP PREEMPT Tue Apr 2 02:54:35 PDT 2019
+#2 SMP PREEMPT Sat Apr 13 04:03:58 KST 2019
+#18 SMP PREEMPT Tue Mar 24 20:04:06 CST 2020
+#2 SMP PREEMPT Sun Feb 9 06:01:20 CST 2020
+#1 SMP PREEMPT Mon Aug 13 15:50:51 KST 2018
+#1 SMP PREEMPT Thu Jul 18 18:21:18 CST 2019
+#1 SMP PREEMPT Tue Apr 9 07:11:20 JST 2019
+#1 SMP PREEMPT Fri Nov 1 17:50:33 -03 2019
+#1 SMP PREEMPT Sat Jun 9 12:09:02 HKT 2018
+#1 SMP PREEMPT Tue Mar 27 10:19:44 CST 2018
+#1 SMP PREEMPT Wed Jan 6 10:57:50 CST 2021
+#1 SMP PREEMPT Fri Aug 14 20:10:41 IST 2020
+#2 SMP PREEMPT Thu Mar 5 21:03:14 CST 2020
+#2 SMP PREEMPT Sat Jan 20 10:36:29 CST 2018
+#1 SMP PREEMPT Thu Aug 8 18:35:09 CST 2019
+#2 SMP PREEMPT Tue Jan 4 17:36:55 CST 2022
+#1 SMP PREEMPT Fri Apr 16 08:34:03 KST 2021
+#1 SMP PREEMPT Tue Jun 2 14:21:13 KST 2020
+#2 SMP PREEMPT Fri Dec 29 12:16:59 BRST 2017
+#1 SMP PREEMPT Mon Jul 8 08:49:27 CST 2019
+#1 SMP PREEMPT Mon Dec 28 08:39:00 +07 2020
+#1 SMP PREEMPT Thu Mar 1 10:33:13 CST 2018
+#1 SMP PREEMPT Wed Oct 16 10:44:25 UTC 2019
+#1 SMP PREEMPT Fri Jul 20 10:22:47 CST 2018
+#1 SMP PREEMPT Fri Apr 24 14:05:28 PDT 2020
+#1 SMP PREEMPT Thu Apr 12 22:04:40 CST 2018
+#1 SMP PREEMPT Fri Jul 10 09:39:22 UTC 2020
+#1 SMP PREEMPT Wed Aug 4 16:04:10 UTC 2021
+#1 SMP PREEMPT Tue Nov 24 18:23:15 KST 2020
+#1 SMP PREEMPT Mon Nov 15 15:13:45 UTC 2021
+#1 SMP PREEMPT Sun Mar 4 03:10:06 CST 2018
+#1 SMP PREEMPT Tue Sep 4 23:35:34 PDT 2018
+#1 SMP PREEMPT Tue Oct 9 20:39:16 PDT 2018
+#1 SMP PREEMPT Fri Nov 24 15:21:45 CST 2017
+#1 SMP PREEMPT Fri Jun 12 21:24:48 CST 2020
+#1 SMP PREEMPT Thu May 30 14:12:03 CST 2019
+#1 SMP PREEMPT Mon Jun 24 21:36:06 CST 2019
+#1 SMP PREEMPT Mon May 3 15:33:53 KST 2021
+#1 SMP PREEMPT Wed Oct 23 23:43:32 CST 2019
+#1 SMP PREEMPT Fri May 14 09:13:53 CST 2021
+#1 SMP PREEMPT Fri Aug 17 19:33:51 CST 2018
+#3 SMP PREEMPT Thu Dec 6 00:29:57 CST 2018
+#1 SMP PREEMPT Thu Sep 2 19:43:02 CST 2021
+#1 SMP PREEMPT Tue Oct 1 00:57:55 PDT 2019
+#2 SMP PREEMPT Fri Mar 20 17:28:20 CST 2020
+#1 SMP PREEMPT Wed Dec 27 14:07:55 CST 2017
+#1 SMP PREEMPT Tue Sep 17 17:32:42 CST 2019
+#1 SMP PREEMPT Wed Mar 2 12:51:34 CST 2022
+#2 SMP PREEMPT Fri Feb 1 07:17:42 UTC 2019
+#2 SMP PREEMPT Mon Nov 2 18:23:48 CST 2020
+#2 SMP PREEMPT Wed Sep 8 19:05:58 CST 2021
+#1 SMP PREEMPT Tue Sep 22 22:50:48 CST 2020
+#1 SMP PREEMPT Wed Jul 18 04:39:22 PDT 2018
+#2 SMP PREEMPT Fri Mar 29 07:37:48 CST 2019
+#1 SMP PREEMPT Tue Jun 26 12:30:09 PDT 2018
+#1 SMP PREEMPT Thu Jan 11 03:15:58 CST 2018
+#1 SMP PREEMPT Tue Apr 14 18:43:12 CST 2020
+#1 SMP PREEMPT Fri Apr 23 01:50:20 CST 2021
+#1 SMP PREEMPT Wed Jul 10 22:20:46 CST 2019
+#1 SMP PREEMPT Thu Nov 7 20:50:15 CST 2019
+#1 SMP PREEMPT Wed Mar 28 15:51:27 CST 2018
+#2 SMP PREEMPT Thu Apr 15 15:30:09 CST 2021
+#1 SMP PREEMPT Thu Nov 26 03:35:35 EST 2020
+#1 SMP PREEMPT Mon Dec 31 14:54:46 CST 2018
+#1 SMP PREEMPT Thu Aug 6 23:17:40 CST 2020
+#1 SMP PREEMPT Thu Dec 5 16:33:33 CST 2019
+#1 SMP PREEMPT Fri Jan 14 12:40:17 CST 2022
+#1 SMP PREEMPT Sun May 10 12:21:19 CST 2020
+#1 SMP PREEMPT Mon Oct 28 20:16:23 KST 2019
+#1 SMP PREEMPT Tue Apr 2 20:56:03 CST 2019
+#1 SMP PREEMPT Sun Sep 26 10:08:30 CST 2021
+#1 SMP PREEMPT Fri Nov 20 20:21:08 CST 2020
+#1 SMP PREEMPT Mon Aug 23 18:20:32 CST 2021
+#2 SMP PREEMPT Fri Apr 2 13:21:32 +07 2021
+#1 SMP PREEMPT Sun Dec 29 08:40:32 CST 2019
+#1 SMP PREEMPT Sat Mar 21 01:24:20 JST 2020
+#1 SMP PREEMPT Tue Aug 4 10:11:55 CST 2020
+#2 SMP PREEMPT Wed Mar 11 13:39:19 CET 2020
+#1 SMP PREEMPT Fri Nov 19 17:09:51 CST 2021
+#1 SMP PREEMPT Fri Jan 19 00:01:07 CST 2018
+#1 SMP PREEMPT Wed May 11 18:31:27 CST 2022
+#1 SMP PREEMPT Wed Apr 14 20:18:20 CST 2021
+#1 SMP PREEMPT Tue Mar 13 19:22:48 CST 2018
+#1 SMP PREEMPT Sat Mar 12 08:52:50 UTC 2022
+#1 SMP PREEMPT Wed Jul 18 04:30:35 PDT 2018
+#1 SMP PREEMPT Wed Jan 19 21:29:53 CST 2022
+#1 SMP PREEMPT Thu Feb 10 17:02:37 CST 2022
+#1 SMP PREEMPT Tue Mar 16 19:32:16 CST 2021
+#1 SMP PREEMPT Thu Sep 9 20:17:14 WIB 2021
+#1 SMP PREEMPT Tue Dec 18 11:01:33 CET 2018
+#1 SMP PREEMPT Thu Jan 10 15:52:26 CST 2019
+#1 SMP PREEMPT Wed May 15 16:39:24 EDT 2019
+#1 SMP PREEMPT Tue Nov 27 15:38:44 CST 2018
+#1 SMP PREEMPT Fri Nov 19 21:58:43 CST 2021
+#1 SMP PREEMPT Wed Mar 7 03:24:59 CST 2018
+#2 SMP PREEMPT Fri Jan 5 01:47:50 CST 2018
+#1 SMP PREEMPT Fri Apr 12 09:03:39 UTC 2019
+#2 SMP PREEMPT Fri Oct 12 18:30:04 CST 2018
+#1 SMP PREEMPT Tue Mar 22 18:25:45 IST 2022
+#1 SMP PREEMPT Mon Dec 7 17:14:23 CST 2020
+#1 SMP PREEMPT Mon Oct 28 10:02:25 CST 2019
+#1 SMP PREEMPT Fri Jan 1 03:48:29 CST 2021
+#1 SMP PREEMPT Sat Nov 7 11:37:43 CST 2020
+#1 SMP PREEMPT Thu May 23 02:54:22 CST 2019
+#2 SMP PREEMPT Wed Mar 23 19:11:22 CST 2022
+#21 SMP PREEMPT Tue Sep 25 17:08:06 CST 2018
+#1 SMP PREEMPT Sat May 16 18:34:51 UTC 2020
+#1 SMP PREEMPT Fri Jun 18 18:55:49 KST 2021
+#1 SMP PREEMPT Wed Jun 20 18:55:47 KST 2018
+#1 SMP PREEMPT Wed Feb 27 10:17:22 CST 2019
+#1 SMP PREEMPT Fri Apr 17 18:52:39 2020
+#1 SMP PREEMPT Fri Dec 21 02:50:14 CST 2018
+#1 SMP PREEMPT Fri Mar 8 14:31:29 KST 2019
+#1 SMP PREEMPT Thu May 24 14:07:18 CST 2018
+#1 SMP PREEMPT Tue Jan 8 17:20:54 GMT+2 2019
+#1 SMP PREEMPT Fri Jul 23 09:47:05 +07 2021
+#1 SMP PREEMPT Sat Jan 4 14:51:55 CST 2020
+#1 SMP PREEMPT Fri May 24 12:41:01 KST 2019
+#1 SMP PREEMPT Wed Jul 18 16:47:25 CST 2018
+#1 SMP PREEMPT Tue Feb 13 18:05:30 CST 2018
+#1 SMP PREEMPT Wed Aug 15 12:26:58 KST 2018
+#1 SMP PREEMPT Thu Nov 15 22:03:32 CST 2018
+#1 SMP PREEMPT Mon Jun 7 14:39:25 CST 2021
+#1 SMP PREEMPT Mon Nov 11 21:13:17 2019
+#1 SMP PREEMPT Wed May 30 15:14:30 CST 2018
+#1 SMP PREEMPT Thu May 23 17:54:27 JST 2019
+#1 SMP PREEMPT Tue Apr 19 11:06:42 HKT 2022
+#2 SMP PREEMPT Tue Oct 26 15:02:48 CST 2021
+#2 SMP PREEMPT Fri Jul 27 16:05:27 CST 2018
+#1 SMP PREEMPT Sat Jul 4 17:25:13 CST 2020
+#2 SMP PREEMPT Tue Apr 19 19:33:58 KST 2022
+#1 SMP PREEMPT Fri Jun 8 16:45:46 CST 2018
+#1 SMP PREEMPT Sat Nov 6 19:00:56 CDT 2021
+#1 SMP PREEMPT Mon Aug 10 14:51:10 KST 2020
+#1 SMP PREEMPT Thu Mar 29 15:19:33 CST 2018
+#1 SMP PREEMPT Fri Jan 4 01:52:58 CST 2019
+#1 SMP PREEMPT Fri Jul 13 13:10:34 KST 2018
+#1 SMP PREEMPT Wed Jun 12 22:08:30 CST 2019
+#1 SMP PREEMPT Sun Apr 28 12:42:50 CST 2019
+#1 SMP PREEMPT Mon Sep 27 18:43:25 IST 2021
+#2 SMP PREEMPT Fri Jun 19 02:16:54 CST 2020
+#1 SMP PREEMPT Thu Feb 10 17:47:16 CST 2022
+#1 SMP PREEMPT Mon Dec 2 15:36:03 KST 2019
+#1 SMP PREEMPT Thu Jul 16 23:57:27 CST 2020
+#2 SMP PREEMPT Thu Sep 3 16:20:31 CST 2020
+#1 SMP PREEMPT Thu Nov 29 11:45:42 KST 2018
+#1 SMP PREEMPT Tue Apr 19 23:32:10 CST 2022
+#1 SMP PREEMPT Thu Aug 1 22:19:53 CST 2019
+#1 SMP PREEMPT Sat Jun 29 09:50:23 CST 2019
+#1 SMP PREEMPT Sun Jun 6 01:53:33 CST 2021
+#1 SMP PREEMPT Wed Jul 17 12:34:44 CST 2019
+#1 SMP PREEMPT Sat Nov 14 02:08:38 IST 2020
+#1 SMP PREEMPT Sat Oct 10 20:45:49 CST 2020
+#1 SMP PREEMPT Wed Oct 21 00:55:42 CST 2020
+#1 SMP PREEMPT Fri Jul 3 01:17:30 CST 2020
+#1 SMP PREEMPT Fri Mar 2 14:28:19 CST 2018
+#1 SMP PREEMPT Mon Jan 14 14:22:29 CST 2019
+#2 SMP PREEMPT Tue May 14 11:14:57 CST 2019
+#1 SMP PREEMPT Tue Nov 9 10:52:08 CST 2021
+#1 SMP PREEMPT Fri Dec 28 09:31:30 CST 2018
+#1 SMP PREEMPT Tue Jun 2 01:00:20 PDT 2020
+#1 SMP PREEMPT Tue Mar 3 13:51:24 GMT 2020
+#1 SMP PREEMPT Wed Nov 4 20:03:09 CST 2020
+#2 SMP PREEMPT Sat Sep 7 20:14:56 CST 2019
+#2 SMP PREEMPT Fri Feb 26 13:04:51 CST 2021
+#1 SMP PREEMPT Mon Apr 22 23:28:09 CST 2019
+#2 SMP PREEMPT Tue Dec 19 06:02:22 CST 2017
+#1 SMP PREEMPT Tue Feb 26 13:14:30 KST 2019
+#1 SMP PREEMPT Fri Jul 26 17:44:16 CST 2019
+#1 SMP PREEMPT Sat Mar 20 13:18:01 CST 2021
+#1 SMP PREEMPT Mon Dec 25 17:24:44 CST 2017
+#1 SMP PREEMPT Wed Jun 9 16:30:54 KST 2021
+#1 SMP PREEMPT Wed Nov 11 01:11:37 CST 2020
+#1 SMP PREEMPT Mon Oct 28 10:02:25 CST 2019
+#1 SMP PREEMPT Fri Jun 18 16:41:38 CST 2021
+#276 SMP PREEMPT Wed Jul 3 01:28:29 CST 2019
+#1 SMP PREEMPT Fri Apr 26 15:54:35 CST 2019
+#1 SMP PREEMPT Fri Feb 9 17:39:06 JST 2018
+#1 SMP PREEMPT Tue Nov 19 23:55:31 PST 2019
+#1 SMP PREEMPT Tue Nov 13 20:33:30 CST 2018
+#1 SMP PREEMPT Tue Dec 29 19:46:04 CST 2020
+#1 SMP PREEMPT Mon May 9 15:48:40 CST 2022
+#1 SMP PREEMPT Wed Feb 26 12:54:48 KST 2020
+#1 SMP PREEMPT Sat Sep 18 20:51:52 PDT 2021
+#1 SMP PREEMPT Fri Dec 4 19:26:50 CST 2020
+#1 SMP PREEMPT Thu Apr 15 13:31:57 CST 2021
+#1 SMP PREEMPT Tue Apr 13 11:35:30 PDT 2021
+#1 SMP PREEMPT Tue Feb 15 03:12:12 UTC 2022
+#1 SMP PREEMPT Fri Jul 26 04:03:32 CST 2019
+#1 SMP PREEMPT Fri Aug 24 19:13:07 CST 2018
+#1 SMP PREEMPT Wed Apr 6 16:54:23 CST 2022
+#1 SMP PREEMPT Mon Jan 6 13:14:18 CST 2020
+#1 SMP PREEMPT Mon Dec 28 15:34:03 IST 2020
+#2 SMP PREEMPT Wed Dec 25 14:11:23 CST 2019
+#1 SMP PREEMPT Sun Mar 31 01:49:46 CST 2019
+#1 SMP PREEMPT Thu Jul 12 10:58:36 2018
+#1 SMP PREEMPT Wed Sep 9 07:33:30 CDT 2020
+#2 SMP PREEMPT Tue Aug 18 03:12:29 CST 2020
+#2 SMP PREEMPT Fri Sep 20 21:11:31 CST 2019
+#1 SMP PREEMPT Thu Jul 9 16:20:23 CST 2020
+#1 SMP PREEMPT Thu Apr 14 00:44:12 IST 2022
+#1 SMP PREEMPT Fri Mar 2 18:19:02 CST 2018
+#1 SMP PREEMPT Mon May 6 17:56:45 KST 2019
+#1 SMP PREEMPT Mon May 9 20:24:43 CST 2022
+#1 SMP PREEMPT Sun Apr 24 16:54:14 CST 2022
+#1 SMP PREEMPT Mon Jul 20 13:23:26 CST 2020
+#1 SMP PREEMPT Mon May 18 12:33:25 KST 2020
+#1 SMP PREEMPT Sat May 5 12:33:57 KST 2018
+#1 SMP PREEMPT Thu May 6 21:21:39 KST 2021
+#1 SMP PREEMPT Mon May 3 15:47:46 +07 2021
+#1 SMP PREEMPT Fri Mar 26 23:14:24 CST 2021
+#1 SMP PREEMPT Fri Sep 21 19:18:38 KST 2018
+#1 SMP PREEMPT Mon Jun 7 21:31:40 KST 2021
+#1 SMP PREEMPT Fri Jul 26 15:13:02 CST 2019
+#1 SMP PREEMPT Thu Dec 19 02:45:29 KST 2019
+#1 SMP PREEMPT Wed Nov 11 06:43:08 EST 2020
+#1 SMP PREEMPT Sat Jul 3 01:16:35 JST 2021
+#1 SMP PREEMPT Fri Mar 1 15:24:46 CST 2019
+#2 SMP PREEMPT Wed Apr 21 21:06:00 CST 2021
+#1 SMP PREEMPT Wed Sep 5 13:49:35 KST 2018
+#1 SMP PREEMPT Tue Nov 24 19:22:39 IST 2020
+#1 SMP PREEMPT Thu Apr 7 03:35:55 PDT 2022
+#1 SMP PREEMPT Tue Aug 13 23:39:49 CST 2019
+#1 SMP PREEMPT Tue May 7 00:19:26 CST 2019
+#1 SMP PREEMPT Thu Mar 31 13:47:00 UTC 2022
+#1 SMP PREEMPT Wed May 9 10:22:09 CST 2018
+#1 SMP PREEMPT Mon Feb 15 22:00:30 PST 2021
+#1 SMP PREEMPT Tue Jul 16 15:11:14 CST 2019
+#1 SMP PREEMPT Mon Nov 2 18:28:33 KST 2020
+#1 SMP PREEMPT Thu Feb 17 21:23:19 CST 2022
+#1 SMP PREEMPT Tue Feb 23 17:10:53 CST 2021
+#3 SMP PREEMPT Wed Jan 19 11:40:09 -02 2022
+#1 SMP PREEMPT Tue Jan 29 11:23:39 CST 2019
+#1 SMP PREEMPT Sat Jul 4 20:28:54 CST 2020
+#1 SMP PREEMPT Wed Oct 30 14:51:46 CST 2019
+#1 SMP PREEMPT Fri Sep 11 11:07:39 CST 2020
+#1 SMP PREEMPT Tue Jun 23 13:47:16 CST 2020
+#1 SMP PREEMPT Thu Apr 25 00:31:36 PDT 2019
+#1 SMP PREEMPT Sun Nov 14 00:10:08 CST 2021
+#1 SMP PREEMPT Mon Nov 1 01:24:44 CST 2021
+#1 SMP PREEMPT Mon Jan 13 14:53:00 +07 2020
+#1 SMP PREEMPT Tue Mar 24 01:33:34 CST 2020
+#1 SMP PREEMPT Fri Jan 14 04:47:38 CST 2022
+#1 SMP PREEMPT Tue May 7 11:30:09 CST 2019
+#6 SMP PREEMPT Tue Oct 5 20:03:09 UTC 2021
+#1 SMP PREEMPT Tue Mar 16 12:37:13 CST 2021
+#1 SMP PREEMPT Fri Jun 5 22:57:56 KST 2020
+#1 SMP PREEMPT Thu Apr 19 12:55:51 CST 2018
+#1 SMP PREEMPT Thu Sep 13 22:04:30 CST 2018
+#2 SMP PREEMPT Mon Jul 6 17:54:27 KST 2020
+#1 SMP PREEMPT Mon Nov 26 19:43:11 KST 2018
+#1 SMP PREEMPT Fri Apr 16 00:20:44 CST 2021
+#1 SMP PREEMPT Tue Oct 9 20:39:28 PDT 2018
+#1 SMP PREEMPT Tue Aug 3 15:34:53 KST 2021
+#4 SMP PREEMPT Wed Apr 17 18:07:30 CST 2019
+#1 SMP PREEMPT Thu Oct 24 18:04:22 CST 2019
+#2 SMP PREEMPT Wed Jan 3 07:14:53 CST 2018
+#1 SMP PREEMPT Sat Mar 5 05:43:08 CST 2022
+#1 SMP PREEMPT Wed Dec 13 12:40:46 KST 2017
+#1 SMP PREEMPT Tue Aug 17 17:35:42 CST 2021
+#1 SMP PREEMPT Fri Feb 25 21:57:13 CST 2022
+#1 SMP PREEMPT Thu Oct 28 20:31:33 UTC 2021
+#22 SMP PREEMPT Fri Mar 4 20:51:36 KST 2022
+#1 SMP PREEMPT Wed May 11 03:58:34 CDT 2022
+#1 SMP PREEMPT Wed Apr 3 02:06:21 CST 2019
+#1 SMP PREEMPT Mon Apr 11 19:33:36 PDT 2022
+#1 SMP PREEMPT Fri Mar 6 16:06:06 CST 2020
+#1 SMP PREEMPT Wed Mar 13 18:50:58 CST 2019
+#1 SMP PREEMPT Tue Sep 21 01:28:02 IST 2021
+#1 SMP PREEMPT Thu Oct 14 16:33:52 CST 2021
+#1 SMP PREEMPT Fri Nov 29 00:21:30 CST 2019
+#1 SMP PREEMPT Thu Mar 17 09:29:01 CST 2022
+#1 SMP PREEMPT Fri Jan 21 19:53:51 UTC 2022
+#1 SMP PREEMPT Sat Nov 24 04:41:59 KST 2018
+#1 SMP PREEMPT Sat Sep 1 12:08:52 CST 2018
+#3 SMP PREEMPT Fri May 28 18:42:39 JST 2021
+#2 SMP PREEMPT Wed May 16 20:24:33 CST 2018
+#1 SMP PREEMPT Thu Dec 16 01:54:29 CST 2021
+#1 SMP PREEMPT Wed Apr 3 17:27:28 CST 2019
+#1 SMP PREEMPT Wed May 20 00:54:35 CST 2020
+#1 SMP PREEMPT Sat Nov 27 08:13:55 PST 2021
+#1 SMP PREEMPT Sat Jun 13 10:31:54 CST 2020
+#2 SMP Tue Jul 14 13:25:06 CEST 2020
+#1 SMP PREEMPT Thu Mar 7 14:36:06 CST 2019
+#1 SMP PREEMPT Wed Dec 18 17:01:25 CST 2019
+#1 SMP PREEMPT Fri Jan 25 18:52:06 CST 2019
+#1 SMP PREEMPT Thu Jun 20 10:40:46 CST 2019
+#1 SMP PREEMPT Sat May 8 14:37:35 KST 2021
+#1 SMP PREEMPT Wed Aug 28 19:20:29 CST 2019
+#1 SMP PREEMPT Tue Oct 22 00:49:38 PDT 2019
+#1 SMP PREEMPT Mon Jun 8 09:48:06 EDT 2020
+#1 SMP PREEMPT Thu Aug 6 15:33:35 CST 2020
+#1 SMP PREEMPT Tue Nov 13 17:37:01 CST 2018
+#6 SMP PREEMPT Wed Dec 22 20:30:02 UTC 2021
+#2 SMP PREEMPT Fri Jul 2 18:52:58 CST 2021
+#1 SMP PREEMPT Sat Sep 12 11:28:05 CST 2020
+#1 SMP PREEMPT Fri May 24 18:02:08 CST 2019
+#1 SMP PREEMPT Sat Apr 9 02:38:54 CST 2022
+#1 SMP PREEMPT Tue Feb 22 23:17:27 CST 2022
+#1 SMP PREEMPT Fri Mar 19 14:06:31 CST 2021
+#4 SMP PREEMPT Wed Mar 3 00:54:58 CST 2021
+#1 SMP PREEMPT Tue Nov 26 02:38:35 KST 2019
+#3 SMP PREEMPT Tue Jul 27 05:01:10 JST 2021
+#1 SMP PREEMPT Thu May 12 19:21:04 CST 2022
+#1 SMP PREEMPT Tue Jun 8 13:17:28 CST 2021
+#1 SMP PREEMPT Wed Aug 14 10:11:29 CST 2019
+#1 SMP PREEMPT Thu Feb 25 05:54:54 JST 2021
+#2 SMP PREEMPT Mon Dec 16 15:52:01 KST 2019
+#1 SMP PREEMPT Fri May 7 09:06:54 KST 2021
+#1 SMP PREEMPT Thu Apr 2 22:58:12 2020
+#1 SMP PREEMPT Mon Aug 19 11:16:11 KST 2019
+#1 SMP PREEMPT Tue Jun 22 02:10:26 PDT 2021
+#1 SMP PREEMPT Mon Feb 11 22:10:21 IST 2019
+#1 SMP PREEMPT Thu Dec 21 02:04:32 CST 2017
+#2 SMP PREEMPT Thu Nov 19 19:33:32 KST 2020
+#1 SMP PREEMPT Wed Dec 25 18:50:02 CST 2019
+#6 SMP PREEMPT Fri Oct 22 14:02:46 UTC 2021
+#1 SMP PREEMPT Tue Apr 7 20:34:09 CST 2020
+#1 SMP PREEMPT Fri Nov 2 21:05:05 CST 2018
+#1 SMP PREEMPT Thu Apr 16 16:11:18 KST 2020
+#3 SMP PREEMPT Wed Jul 14 05:10:21 JST 2021
+#2 SMP PREEMPT Tue Dec 21 17:42:08 CST 2021
+#1 SMP PREEMPT Tue Sep 21 11:14:03 CST 2021
+#1 SMP PREEMPT Fri Jul 12 04:24:24 CST 2019
+#1 SMP PREEMPT Fri Aug 24 17:54:49 CST 2018
+#1 SMP PREEMPT Mon Dec 28 21:26:04 CST 2020
+#1 SMP PREEMPT Mon Apr 6 10:19:58 CST 2020
+#1 SMP PREEMPT Thu Jan 16 03:19:37 CST 2020
+#1 SMP PREEMPT Fri Mar 9 14:54:15 KST 2018
+#1 SMP PREEMPT Mon Oct 18 23:17:49 CST 2021
+#1 SMP PREEMPT Thu Mar 22 17:10:36 WIB 2018
+#1 SMP PREEMPT Tue Oct 15 13:14:41 2019
+#1 SMP PREEMPT Mon Jul 8 06:28:00 CST 2019
+#1 SMP PREEMPT Tue Nov 27 10:33:44 CST 2018
+#1 SMP PREEMPT Tue Apr 26 09:47:50 CST 2022
+#1 SMP PREEMPT Wed Aug 12 18:06:46 HKT 2020
+#2 SMP PREEMPT Fri Dec 20 17:02:27 CST 2019
+#1 SMP PREEMPT Tue Apr 13 12:48:30 PDT 2021
+#1 SMP PREEMPT Tue Dec 3 20:59:37 KST 2019
+#1 SMP PREEMPT Wed Jan 19 20:56:59 CST 2022
+#1 SMP PREEMPT Wed Jun 9 15:33:57 UTC 2021
+#1 SMP PREEMPT Fri Jan 18 00:12:17 CST 2019
+#1 SMP PREEMPT Wed Sep 26 21:12:09 CST 2018
+#1 SMP PREEMPT Mon Dec 30 21:25:18 CST 2019
+#4 SMP PREEMPT Fri Jul 30 19:06:06 CST 2021
+#1 SMP PREEMPT Sat Sep 19 15:32:45 CST 2020
+#1 SMP PREEMPT Fri Jul 10 15:42:39 CST 2020
+#1 SMP PREEMPT Tue Nov 2 22:20:57 PDT 2021
+#1 SMP PREEMPT Sat Sep 19 06:44:52 CST 2020
+#1 SMP PREEMPT Mon Nov 2 22:40:41 IST 2020
+#1 SMP PREEMPT Wed Nov 28 17:47:15 CST 2018
+#1 SMP PREEMPT Thu Nov 29 14:55:45 CST 2018
+#1 SMP PREEMPT Mon Mar 18 04:29:20 CDT 2019
+#1 SMP PREEMPT Mon May 10 14:16:03 CST 2021
+#1 SMP PREEMPT Mon Nov 22 23:08:59 CST 2021
+#1 SMP PREEMPT Tue Nov 3 22:21:38 KST 2020
+#1 SMP PREEMPT Fri Jul 3 06:27:26 CST 2020
+#1 SMP PREEMPT Fri Jun 4 07:06:53 CST 2021
+#1 SMP PREEMPT Thu Feb 18 05:01:21 KST 2021
+#2 SMP PREEMPT Wed Mar 13 12:11:54 KST 2019
+#1 SMP PREEMPT Wed Dec 22 06:57:33 CST 2021
+#1 SMP PREEMPT Mon Sep 28 16:28:32 CST 2020
+#1 SMP PREEMPT Wed Jan 3 20:05:58 CST 2018
+#1 SMP PREEMPT Thu Jul 29 02:16:12 CST 2021
+#1 SMP PREEMPT Tue Aug 17 10:56:31 CST 2021
+#1 SMP PREEMPT Wed May 23 07:21:42 KST 2018
+#1 SMP PREEMPT Wed Mar 18 18:16:47 CST 2020
+#1 SMP PREEMPT Thu Jan 28 17:21:58 IST 2021
+#1 SMP PREEMPT Fri May 14 11:33:05 CST 2021
+#1 SMP PREEMPT Thu Jul 15 08:41:28 HKT 2021
+#1 SMP PREEMPT Tue Sep 4 13:12:55 CST 2018
+#1 SMP PREEMPT Tue Dec 21 14:45:43 CST 2021
+#1 SMP PREEMPT Wed Nov 14 18:33:31 CST 2018
+#1 SMP PREEMPT Wed Jul 18 04:27:36 PDT 2018
+#1 SMP PREEMPT Tue Oct 13 14:03:22 CST 2020
+#1 SMP PREEMPT Fri Sep 28 03:36:27 CST 2018
+#1 SMP PREEMPT Mon Jun 28 08:59:40 KST 2021
+#2 SMP PREEMPT Fri Apr 2 15:14:59 KST 2021
+#1 SMP PREEMPT Thu Sep 6 12:01:51 KST 2018
+#1 SMP PREEMPT Wed Apr 18 10:30:19 2018
+#1 SMP PREEMPT Thu Oct 22 00:20:59 CST 2020
+#1 SMP PREEMPT Thu Nov 22 21:34:53 KST 2018
+#1 SMP PREEMPT Fri May 11 00:20:58 CST 2018
+#2 SMP PREEMPT Thu Oct 10 18:21:33 KST 2019
+#1 SMP PREEMPT Wed Jan 8 16:07:02 CST 2020
+#1 SMP PREEMPT Tue Apr 17 13:03:50 KST 2018
+#1 SMP PREEMPT Fri Sep 7 04:52:18 KST 2018
+#1 SMP PREEMPT Tue Oct 22 16:24:43 KST 2019
+#1 SMP PREEMPT Mon Jul 8 10:34:20 CDT 2019
+#1 SMP PREEMPT Mon Aug 10 23:03:06 CST 2020
+#1 SMP PREEMPT Mon Mar 11 15:05:34 CST 2019
+#1 SMP PREEMPT Tue Jun 22 09:06:12 EDT 2021
+#1 SMP PREEMPT Thu Nov 19 13:32:06 CST 2020
+#1 SMP PREEMPT Fri Oct 16 17:54:12 CST 2020
+#1 SMP PREEMPT Thu Nov 14 12:05:48 CST 2019
+#1 SMP PREEMPT Wed Nov 25 21:13:30 CST 2020
+#1 SMP PREEMPT Wed Dec 15 07:09:47 UTC 2021
+#1 SMP PREEMPT Thu Jul 29 21:25:19 CST 2021
+#1 SMP PREEMPT Thu Nov 19 13:25:14 CST 2020
+#1 SMP PREEMPT Mon Jul 27 03:32:53 JST 2020
+#1 SMP PREEMPT Wed Jan 31 03:37:49 CST 2018
+#1 SMP PREEMPT Thu Aug 20 01:17:39 CST 2020
+#2 SMP PREEMPT Thu Jun 13 18:34:49 KST 2019
+#1 SMP PREEMPT Wed Feb 5 22:58:29 PST 2020
+#1 SMP PREEMPT Sat May 7 16:09:33 CST 2022
+#2 SMP PREEMPT Sat Feb 24 15:43:59 CST 2018
+#1 SMP PREEMPT Thu Oct 18 15:13:51 CST 2018
+#1 SMP PREEMPT Mon Jul 27 16:38:08 CST 2020
+#1 SMP PREEMPT Fri May 13 10:25:03 CST 2022
+#1 SMP PREEMPT Fri Dec 6 19:51:56 CST 2019
+#1 SMP PREEMPT Wed Jun 17 19:17:01 CST 2020
+#1 SMP PREEMPT Thu Jan 31 03:46:26 KST 2019
+#2 SMP PREEMPT Mon Jun 29 04:58:41 CST 2020
+#1 SMP PREEMPT Tue Nov 5 18:52:01 JST 2019
+#2 SMP PREEMPT Sat Nov 13 14:19:30 CST 2021
+#1 SMP PREEMPT Thu Nov 30 15:18:34 GMT 2017
+#1 SMP PREEMPT Fri Apr 15 01:32:13 CST 2022
+#1 SMP PREEMPT Tue Sep 4 19:58:43 KST 2018
+#1 SMP PREEMPT Tue Oct 23 08:31:39 CST 2018
+#1 SMP PREEMPT Wed May 18 12:16:31 +07 2022
+#2 SMP PREEMPT Fri Mar 19 19:16:00 KST 2021
+#1 SMP PREEMPT Thu Feb 4 20:26:10 PST 2021
+#3 SMP PREEMPT Sat Jan 19 15:49:45 CST 2019
+#1 SMP PREEMPT Mon Mar 5 19:34:52 KST 2018
+#1 SMP PREEMPT Fri Mar 20 10:39:56 CST 2020
+#1 SMP PREEMPT Tue Dec 15 16:02:07 CST 2020
+#2 SMP PREEMPT Wed Mar 6 20:05:18 CST 2019
+#1 SMP PREEMPT Fri Jul 26 03:31:03 CST 2019
+#2 SMP PREEMPT Thu Oct 22 19:21:26 CST 2020
+#1 SMP PREEMPT Wed Apr 1 18:08:30 CST 2020
+#1 SMP PREEMPT Thu Jan 30 15:20:34 KST 2020
+#2 SMP PREEMPT Mon Sep 3 22:23:04 CST 2018
+#1 SMP PREEMPT Tue Aug 14 16:24:22 CST 2018
+#1 SMP PREEMPT Thu Aug 6 01:39:23 CST 2020
+#4 SMP Wed Nov 21 10:21:13 KST 2018
+#1 SMP PREEMPT Fri Dec 24 16:23:38 CST 2021
+#1 SMP PREEMPT Tue Jul 21 16:56:33 KST 2020
+#1 SMP PREEMPT Thu May 24 13:54:11 KST 2018
+#1 SMP PREEMPT Sat Apr 27 23:40:19 PDT 2019
+#1 SMP PREEMPT Wed Mar 13 11:47:30 CST 2019
+#2 SMP PREEMPT Thu Oct 18 17:34:17 CST 2018
+#1 SMP PREEMPT Tue Aug 6 06:12:39 PDT 2019
+#1 SMP PREEMPT Fri May 8 12:58:12 CST 2020
+#1 SMP PREEMPT Tue Jun 11 15:29:47 CST 2019
+#1 SMP PREEMPT Fri Jul 20 13:54:10 CST 2018
+#1 SMP PREEMPT Sun May 24 12:39:16 CDT 2020
+#1 SMP PREEMPT Wed May 2 11:07:00 CST 2018
+#1 SMP PREEMPT Sat Apr 4 06:47:28 CDT 2020
+#1 SMP PREEMPT Mon Jun 21 21:55:22 CST 2021
+#1 SMP PREEMPT Tue Feb 12 04:00:20 CST 2019
+#1 SMP PREEMPT Tue May 17 18:34:47 CST 2022
+#0 SMP PREEMPT Thu May 6 10:10:27 UTC 2021
+#4 SMP PREEMPT Tue Jul 6 12:58:46 CDT 2021
+#1 SMP PREEMPT Wed Jan 15 20:09:09 CST 2020
+#1 SMP PREEMPT Tue Jul 3 15:45:59 KST 2018
+#1 SMP PREEMPT Tue May 26 16:33:06 KST 2020
+#1 SMP PREEMPT Thu Sep 16 03:31:23 JST 2021
+#1 SMP PREEMPT Tue Oct 22 17:58:41 KST 2019
+#1 SMP PREEMPT Fri Apr 10 01:57:08 CST 2020
+#1 SMP PREEMPT Wed Oct 17 20:11:08 CST 2018
+#1 SMP PREEMPT Tue Dec 10 10:06:10 JST 2019
+#1 SMP PREEMPT Wed Jan 13 00:16:21 CST 2021
+#1 SMP PREEMPT Fri Jul 10 00:37:13 CST 2020
+#1 SMP PREEMPT Mon Mar 9 17:21:58 CST 2020
+#2 SMP PREEMPT Tue Apr 7 18:54:12 CST 2020
+#1 SMP PREEMPT Fri Apr 1 17:06:51 CST 2022
+#1 SMP PREEMPT Wed Nov 4 16:50:20 CST 2020
+#1 SMP PREEMPT Tue Nov 5 18:57:42 JST 2019
+#1 SMP PREEMPT Fri Jul 5 05:10:40 KST 2019
+#1 SMP PREEMPT Wed Mar 24 01:27:55 KST 2021
+#1 SMP PREEMPT Wed Jul 4 22:45:14 CST 2018
+#2 SMP PREEMPT Mon Nov 11 17:41:42 KST 2019
+#2 SMP PREEMPT Wed Mar 13 12:55:20 CST 2019
+#1 SMP PREEMPT Wed Nov 10 02:40:46 JST 2021
+#1 SMP PREEMPT Mon Jan 8 20:16:18 KST 2018
+#1 SMP PREEMPT Fri Nov 8 23:25:36 CST 2019
+#1 SMP PREEMPT Tue Nov 30 17:02:23 CST 2021
+#1 SMP PREEMPT Wed Oct 17 19:51:19 CST 2018
+#1 SMP PREEMPT Wed Sep 1 14:38:39 CST 2021
+#1 SMP PREEMPT Wed Jan 9 21:38:56 KST 2019
+#2 SMP PREEMPT Thu Jan 28 16:20:07 KST 2021
+#2 SMP PREEMPT Tue Dec 15 11:24:55 CST 2020
+#2 SMP PREEMPT Fri Nov 29 23:53:50 CST 2019
+#1 SMP PREEMPT Thu Sep 13 13:11:52 CST 2018
+#1 SMP PREEMPT Mon Mar 12 09:29:07 KST 2018
+#2 SMP PREEMPT Sat May 16 23:51:22 CST 2020
+#1 SMP PREEMPT Thu Apr 19 12:55:51 CST 2018
+#1 SMP PREEMPT Wed Apr 3 01:06:36 CST 2019
+#1 SMP PREEMPT Tue Mar 31 02:58:52 CST 2020
+#1 SMP PREEMPT Tue Jul 31 20:40:25 CST 2018
+#1 SMP PREEMPT Wed Jun 23 17:01:44 KST 2021
+#1 SMP PREEMPT Sat Jan 26 15:55:12 CST 2019
+#0 SMP PREEMPT Fri Sep 7 17:20:04 UTC 2018
+#1 SMP PREEMPT Fri Jan 3 17:27:28 CST 2020
+#1 SMP PREEMPT Wed May 29 00:45:40 PDT 2019
+#0 SMP PREEMPT Mon Jul 5 01:53:20 UTC 2021
+#1 SMP PREEMPT Wed May 12 12:01:23 CST 2021
+#1 SMP PREEMPT Thu Oct 22 20:44:36 2020
+#1 SMP PREEMPT Tue Dec 3 15:19:32 KST 2019
+#1 SMP PREEMPT Tue Jun 1 14:23:58 CST 2021
+#2 SMP PREEMPT Tue Jan 18 09:52:37 CST 2022
+#1 SMP PREEMPT Fri Sep 21 16:19:40 KST 2018
+#1 SMP PREEMPT Fri Jun 18 16:46:05 CST 2021
+#1 SMP PREEMPT Thu Sep 5 02:17:19 CST 2019
+#1 SMP PREEMPT Sat Jul 14 10:32:30 CST 2018
+#1 SMP PREEMPT Mon Nov 30 17:07:44 CST 2020
+#1 SMP PREEMPT Thu Apr 19 18:16:12 CST 2018
+#1 SMP PREEMPT Thu Apr 28 02:16:47 CST 2022
+#1 SMP PREEMPT Tue Nov 27 14:54:30 CST 2018
+#2 SMP PREEMPT Mon Dec 9 15:54:38 CST 2019
+#2 SMP PREEMPT Tue Aug 18 02:37:30 CST 2020
+#1 SMP PREEMPT Thu Mar 10 04:39:09 CST 2022
+#2 SMP PREEMPT Fri Jun 4 15:48:01 CST 2021
+#1 SMP PREEMPT Thu Dec 10 20:00:14 PST 2020
+#1 SMP PREEMPT Sat Jan 22 02:07:34 CST 2022
+#2 SMP PREEMPT Tue Aug 6 14:55:13 CST 2019
+#1 SMP PREEMPT Tue Apr 16 22:52:33 CST 2019
+#1 SMP PREEMPT Thu Oct 14 21:59:28 CST 2021
+#1 SMP PREEMPT Wed Dec 18 15:59:55 WIB 2019
+#1 SMP PREEMPT Mon Jan 20 04:35:05 CST 2020
+#1 SMP PREEMPT Thu Oct 28 20:37:06 UTC 2021
+#1 SMP PREEMPT Wed Dec 8 13:51:09 CST 2021
+#1 SMP PREEMPT Thu Aug 1 13:08:27 CST 2019
+#1 SMP PREEMPT Fri May 13 11:57:32 CST 2022
+#1 SMP PREEMPT Fri Sep 27 15:57:45 KST 2019
+#1 SMP PREEMPT Wed May 11 07:25:57 UTC 2022
+#1 SMP PREEMPT Thu Mar 29 14:58:09 CST 2018
+#1 SMP PREEMPT Tue Dec 1 02:15:54 IST 2020
+#1 SMP PREEMPT Tue Dec 14 03:12:59 CST 2021
+#1 SMP PREEMPT Thu May 20 06:07:03 CDT 2021
+#1 SMP PREEMPT Fri Jun 19 22:37:18 KST 2020
+#1 SMP PREEMPT Mon Mar 12 13:13:24 KST 2018
+#1 SMP PREEMPT Thu Jan 17 00:18:20 CST 2019
+#1 SMP PREEMPT Wed Dec 18 16:18:18 CST 2019
+#1 SMP PREEMPT Wed Nov 11 10:33:24 CET 2020
+#1 SMP PREEMPT Thu Dec 30 04:45:12 CST 2021
+#2 SMP PREEMPT Mon Jul 9 18:21:12 CST 2018
+#1 SMP PREEMPT Thu Feb 8 18:03:21 KST 2018
+#1 SMP PREEMPT Mon Nov 30 23:46:36 CST 2020
+#1 SMP PREEMPT Fri Oct 26 03:35:51 CST 2018
+#1 SMP PREEMPT Thu Dec 17 05:16:05 CST 2020
+#1 SMP PREEMPT Sat Apr 7 23:16:07 KST 2018
+#1 SMP PREEMPT Fri Jan 22 10:55:11 CST 2021
+#1 SMP PREEMPT Wed Dec 2 05:31:49 KST 2020
+#1 SMP PREEMPT Sat Jan 26 10:41:48 CST 2019
+#1 SMP PREEMPT Sun Dec 12 01:51:34 CST 2021
+#1 SMP PREEMPT Wed Aug 1 08:09:09 CDT 2018
+#1 SMP PREEMPT Fri Oct 29 20:06:46 IST 2021
+#1 SMP PREEMPT Tue Jul 16 15:34:48 KST 2019
+#1 SMP PREEMPT Wed Apr 8 12:00:46 CST 2020
+#1 SMP PREEMPT Tue Dec 29 17:39:20 KST 2020
+#1 SMP PREEMPT Mon Aug 9 16:23:16 KST 2021
+#1 SMP PREEMPT Fri Feb 21 13:41:04 CST 2020
+#1 SMP PREEMPT Mon Jul 22 03:25:31 CST 2019
+#1 SMP PREEMPT Mon May 27 20:46:20 CST 2019
+#1 SMP PREEMPT Mon Jun 4 21:44:34 HKT 2018
+#1 SMP PREEMPT Sat Nov 14 23:22:31 CST 2020
+#1 SMP PREEMPT Sat Nov 9 21:44:31 CST 2019
+#1 SMP PREEMPT Mon Oct 28 10:02:25 CST 2019
+#2 SMP PREEMPT Fri Oct 16 17:37:42 KST 2020
+#1 SMP PREEMPT Mon Jul 24 17:58:45 UTC 2017
+#1 SMP PREEMPT Thu Jul 8 19:31:47 CST 2021
+#1 SMP PREEMPT Tue Oct 12 13:15:05 CST 2021
+#1 SMP PREEMPT Tue Sep 17 00:03:29 CST 2019
+#1 SMP PREEMPT Fri Mar 11 03:11:16 UTC 2022
+#1 SMP PREEMPT Wed May 30 14:01:48 BRT 2018
+#4 SMP PREEMPT Mon Aug 2 17:47:15 CDT 2021
+#1 SMP PREEMPT Thu May 27 21:28:07 KST 2021
+#1 SMP PREEMPT Fri Dec 1 12:35:51 CST 2017
+#1 SMP PREEMPT Mon Jan 15 20:16:57 WIB 2018
+#1 SMP PREEMPT Fri Jun 12 17:41:31 JST 2020
+#1 SMP PREEMPT Sat Feb 12 22:33:59 CST 2022
+#1 SMP PREEMPT Fri Mar 11 16:37:28 CST 2022
+#2 SMP PREEMPT Fri Dec 6 02:56:52 CST 2019
+#1 SMP PREEMPT Tue Sep 8 15:05:21 WIB 2020
+#1 SMP PREEMPT Wed Jun 10 16:46:44 CST 2020
+#2 SMP Wed Nov 7 12:54:27 CET 2018
+#1 SMP PREEMPT Thu Jul 22 14:43:36 CST 2021
+#1 SMP PREEMPT Thu Jan 11 11:09:11 KST 2018
+#2 SMP PREEMPT Thu Nov 29 15:12:13 CST 2018
+#1 SMP PREEMPT Wed Jan 19 19:31:18 UTC 2022
+#1 SMP PREEMPT Sat Nov 20 00:13:38 CST 2021
+#1 SMP PREEMPT Thu Jan 7 00:48:48 CST 2021
+#1 SMP PREEMPT Thu Sep 13 22:04:15 CST 2018
+#1 SMP PREEMPT Thu May 21 18:13:58 KST 2020
+#1 SMP PREEMPT Thu Jan 6 23:07:11 CST 2022
+#2 SMP PREEMPT Tue Sep 29 16:37:13 KST 2020
+#1 SMP PREEMPT Thu May 5 15:38:55 IST 2022
+#64 SMP PREEMPT Tue Oct 22 09:39:25 CST 2019
+#1 SMP PREEMPT Thu May 14 02:15:46 CST 2020
+#1 SMP PREEMPT Tue Nov 5 12:21:03 KST 2019
+#1 SMP PREEMPT Thu Oct 22 06:45:34 CST 2020
+#1 SMP PREEMPT Sat Feb 29 10:14:02 +07 2020
+#1 SMP PREEMPT Thu Nov 7 16:23:09 KST 2019
+#3 SMP PREEMPT Tue Apr 2 20:48:49 CST 2019
+#1 SMP PREEMPT Tue Jun 16 10:35:34 CST 2020
+#2 SMP PREEMPT Thu Mar 21 14:13:29 KST 2019
+#1 SMP PREEMPT Mon Jan 7 11:22:49 CST 2019
+#23 SMP PREEMPT Thu Sep 10 09:15:20 CST 2020
+#2 SMP PREEMPT Thu May 7 00:47:11 CST 2020
+#1 SMP PREEMPT Fri Aug 24 20:47:30 CST 2018
+#1 SMP PREEMPT Wed Nov 24 21:09:07 CST 2021
+#2 SMP PREEMPT Thu Feb 27 22:13:09 CST 2020
+#1 SMP PREEMPT Wed Apr 22 11:28:10 CDT 2020
+#1 SMP PREEMPT Wed Mar 10 21:43:13 +07 2021
+#1 SMP PREEMPT Mon Sep 2 18:21:18 KST 2019
+#1 SMP PREEMPT Fri Jan 4 19:26:07 CST 2019
+#1 SMP PREEMPT Wed Sep 29 02:07:16 CST 2021
+#1 SMP PREEMPT Tue May 26 00:51:16 KST 2020
+#1 SMP PREEMPT Tue Sep 14 20:22:21 PDT 2021
+#1 SMP PREEMPT Fri Dec 8 16:58:51 CST 2017
+#1 SMP PREEMPT Wed Apr 25 11:07:50 CST 2018
+#1 SMP PREEMPT Tue Jun 4 08:12:42 CST 2019
+#1 SMP PREEMPT Fri Jul 19 03:49:44 CST 2019
+#4 SMP PREEMPT Thu Jun 13 22:38:45 CST 2019
+#1 SMP PREEMPT Tue Apr 20 18:07:24 CST 2021
+#1 SMP PREEMPT Fri Jul 3 01:34:10 CST 2020
+#1 SMP PREEMPT Mon May 18 17:45:14 KST 2020
+#1 SMP PREEMPT Thu Apr 30 13:24:09 CDT 2020
+#1 SMP PREEMPT Tue Feb 9 18:30:42 PST 2021
+#1 SMP PREEMPT Fri Feb 28 15:28:00 KST 2020
+#1 SMP PREEMPT Fri Apr 2 18:32:59 KST 2021
+#1 SMP PREEMPT Thu Feb 13 10:24:45 2020
+#1 SMP PREEMPT Mon Mar 25 21:28:22 CST 2019
+#1 SMP PREEMPT Wed Dec 23 14:42:02 CST 2020
+#2 SMP PREEMPT Wed May 20 00:38:16 CST 2020
+#1 SMP PREEMPT Thu May 6 13:56:54 JST 2021
+#2 SMP PREEMPT Tue Nov 17 17:02:16 KST 2020
+#1 SMP PREEMPT Mon Feb 17 09:07:25 CST 2020
+#2 SMP PREEMPT Thu Jun 21 17:04:42 CST 2018
+#1 SMP PREEMPT Mon Dec 14 16:21:40 CST 2020
+#1 SMP PREEMPT Sat Nov 27 01:01:10 CST 2021
+#1 SMP PREEMPT Wed Nov 6 16:49:31 IST 2019
+#4 SMP PREEMPT Fri Oct 9 09:52:50 CST 2020
+#2 SMP PREEMPT Thu Oct 11 23:05:11 CST 2018
+#1 SMP PREEMPT Sat Dec 11 14:39:40 CST 2021
+#1 SMP PREEMPT Thu Jul 2 11:39:58 KST 2020
+#1 SMP PREEMPT Tue May 5 20:49:39 CST 2020
+#1 SMP PREEMPT Wed Jan 8 11:41:52 KST 2020
+#1 SMP PREEMPT Tue Jun 4 21:24:36 KST 2019
+#1 SMP PREEMPT Tue Nov 13 17:32:12 KST 2018
+#2 SMP PREEMPT Fri Nov 30 06:55:01 CST 2018
+#1 SMP PREEMPT Mon Oct 7 15:05:25 CST 2019
+#1 SMP PREEMPT Thu Apr 9 17:09:52 CST 2020
+#2 SMP PREEMPT Tue Aug 18 16:46:32 KST 2020
+#1 SMP PREEMPT Tue Jun 9 15:34:57 KST 2020
+#2 SMP PREEMPT Sat Feb 5 00:12:52 PST 2022
+#1 SMP PREEMPT Mon Apr 27 14:49:17 CST 2020
+#1 SMP PREEMPT Tue Nov 13 23:48:57 KST 2018
+#1 SMP PREEMPT Wed May 20 21:19:15 KST 2020
+#1 SMP PREEMPT Mon Sep 30 22:47:53 KST 2019
+#1 SMP PREEMPT Wed Jun 27 19:24:49 CST 2018
+#1 SMP PREEMPT Tue Apr 27 11:48:36 CDT 2021
+#1 SMP PREEMPT Fri Feb 15 19:06:12 CST 2019
+#1 SMP PREEMPT Mon Jul 26 09:41:38 CST 2021
+#1 SMP PREEMPT Wed May 18 20:01:48 WIB 2022
+#2 SMP PREEMPT Wed Oct 28 11:52:09 KST 2020
+#1 SMP PREEMPT 2021-06-02 12:17:23
+#1 SMP PREEMPT Thu Jun 13 10:55:52 2019
+#1 SMP PREEMPT Mon May 28 06:47:49 CDT 2018
+#1 SMP PREEMPT Fri May 8 15:13:38 CST 2020
+#1 SMP PREEMPT Fri Aug 7 22:52:53 KST 2020
+#1 SMP PREEMPT Mon Apr 27 23:26:25 CST 2020
+#1 SMP PREEMPT Fri Nov 8 21:43:37 CST 2019
+#1 SMP PREEMPT Wed Sep 1 04:19:44 CDT 2021
+#1 SMP PREEMPT Wed Dec 20 17:35:30 CST 2017
+#1 SMP PREEMPT Fri Oct 18 22:00:10 CST 2019
+#2 SMP PREEMPT Mon Mar 2 18:49:11 CST 2020
+#1 SMP PREEMPT Mon Sep 30 22:49:52 KST 2019
+#1 SMP PREEMPT Tue Jan 15 20:20:26 KST 2019
+#1 SMP PREEMPT Thu Jul 5 22:00:11 CST 2018
+#1 SMP PREEMPT Fri Feb 7 17:56:41 KST 2020
+#1 SMP PREEMPT Tue Dec 14 20:29:24 CST 2021
+#1 SMP PREEMPT Sun Sep 30 20:08:50 CST 2018
+#1 SMP PREEMPT Sun Apr 3 16:13:32 KST 2022
+#1 SMP PREEMPT Fri Dec 1 18:21:52 CST 2017
+#1 SMP PREEMPT Wed Mar 17 12:41:35 KST 2021
+#5 SMP PREEMPT Sun Mar 13 15:44:16 CST 2022
+#1 SMP PREEMPT Wed May 2 10:59:34 CST 2018
+#2 SMP PREEMPT Thu Dec 31 16:26:19 CST 2020
+#1 SMP PREEMPT Wed Dec 6 17:42:00 CST 2017
+#1 SMP PREEMPT Wed May 2 18:28:01 CST 2018
+#1 SMP PREEMPT Thu Jul 2 01:26:02 CST 2020
+#1 SMP PREEMPT Thu Mar 1 14:28:17 KST 2018
+#1 SMP PREEMPT Fri Dec 22 13:23:56 CST 2017
+#1 SMP PREEMPT Thu Feb 27 19:32:53 CST 2020
+#1 SMP PREEMPT Tue Jan 18 02:32:14 CST 2022
+#1 SMP PREEMPT Thu Mar 17 17:29:47 CST 2022
+#1 SMP PREEMPT Tue Aug 31 16:30:30 IST 2021
+#1 SMP PREEMPT Fri Dec 21 20:13:13 CST 2018
+#2 SMP PREEMPT Fri Mar 29 14:27:07 KST 2019
+#1 SMP PREEMPT Thu Jan 10 21:13:21 CST 2019
+#1 SMP PREEMPT Sat Jan 1 11:03:52 CST 2022
+#1 SMP PREEMPT Wed Aug 4 15:12:08 UTC 2021
+#2 SMP PREEMPT Sat Oct 12 17:25:38 KST 2019
+#1 SMP PREEMPT Wed Nov 14 10:33:53 UTC 2018
+#1 SMP PREEMPT Wed Aug 5 19:53:06 CST 2020
+#1 SMP PREEMPT Mon Jan 10 17:30:22 CST 2022
+#1 SMP PREEMPT Thu Apr 26 15:08:51 CST 2018
+#1 SMP PREEMPT Tue Jan 14 14:11:35 CST 2020
+#1 SMP PREEMPT Thu Oct 25 00:11:03 KST 2018
+#1 SMP PREEMPT Fri Aug 28 12:16:25 CST 2020
+#1 SMP PREEMPT Mon Jan 24 21:58:35 CST 2022
+#1 SMP PREEMPT Thu Jun 13 21:16:00 KST 2019
+#1 SMP PREEMPT Thu Jun 13 18:27:29 CST 2019
+#2 SMP PREEMPT Fri Apr 24 11:30:46 KST 2020
+#2 SMP PREEMPT Mon Sep 2 12:00:32 KST 2019
+#1 SMP PREEMPT Wed Oct 21 23:49:12 CST 2020
+#1 SMP PREEMPT Tue May 7 20:50:29 CDT 2019
+#1 SMP PREEMPT Mon Dec 24 08:12:51 UTC 2018
+#1 SMP PREEMPT Thu May 21 14:13:50 PDT 2020
+#1 SMP PREEMPT Fri Jul 26 04:03:32 CST 2019
+#1 SMP PREEMPT Fri Sep 17 14:27:36 CST 2021
+#2 SMP PREEMPT Thu Aug 20 01:33:21 CST 2020
+#1 SMP PREEMPT Thu Jan 25 18:47:12 IST 2018
+#1 SMP PREEMPT Mon Sep 3 19:03:27 CST 2018
+#1 SMP PREEMPT Sun May 10 03:20:45 CST 2020
+#1 SMP PREEMPT Tue Jul 23 22:38:34 CST 2019
+#1 SMP PREEMPT Mon May 16 12:41:53 CST 2022
+#1 SMP Thu Oct 11 09:52:59 KST 2018
+#1 SMP PREEMPT Fri May 1 19:18:22 KST 2020
+#2 SMP PREEMPT Wed Apr 17 17:54:39 CST 2019
+#1 SMP PREEMPT Fri Oct 30 15:47:33 KST 2020
+#2 SMP PREEMPT Fri Oct 19 12:04:12 CST 2018
+#1 SMP PREEMPT Thu Dec 30 05:06:35 CST 2021
+#1 SMP PREEMPT Thu Dec 14 15:58:09 KST 2017
+#6 SMP PREEMPT Thu Dec 9 20:50:05 UTC 2021
+#1 SMP PREEMPT Mon May 2 03:49:25 CDT 2022
+#1 SMP PREEMPT Wed Jun 3 18:23:59 KST 2020
+#2 SMP PREEMPT Thu Dec 26 11:01:20 CST 2019
+#2 SMP PREEMPT Tue Jan 7 17:44:57 -02 2020
+#1 SMP PREEMPT Wed Apr 21 16:28:24 CST 2021
+#1 SMP PREEMPT Mon Dec 2 23:49:32 PST 2019
+#1 SMP PREEMPT Sat Feb 12 16:48:09 CST 2022
+#1 SMP PREEMPT Wed Nov 24 20:47:24 CST 2021
+#1 SMP PREEMPT Fri Nov 2 10:22:36 CST 2018
+#2 SMP PREEMPT Tue Jun 4 14:22:12 CST 2019
+#1 SMP PREEMPT Fri Oct 8 13:08:43 CST 2021
+#1 SMP PREEMPT Sat Aug 18 15:44:36 CST 2018
+#1 SMP PREEMPT Wed Mar 21 09:52:44 CST 2018
+#2 SMP PREEMPT Wed Sep 16 12:30:55 CST 2020
+#2 SMP PREEMPT Mon Apr 6 05:31:46 CST 2020
+#1 SMP PREEMPT Sat Jun 29 20:41:06 CST 2019
+#2 SMP PREEMPT Fri Dec 14 19:21:17 CST 2018
+#1 SMP PREEMPT Thu Nov 12 18:17:57 CST 2020
+#1 SMP PREEMPT Thu Jan 3 19:31:16 CST 2019
+#2 SMP PREEMPT Thu May 30 13:37:28 CST 2019
+#2 SMP PREEMPT Fri May 7 17:20:46 KST 2021
+#1 SMP PREEMPT Mon Dec 10 15:50:02 EST 2018
+#2 SMP PREEMPT Thu Sep 5 10:55:21 CST 2019
+#1 SMP PREEMPT Thu Jan 28 23:31:14 CST 2021
+#1 SMP PREEMPT Tue Feb 15 16:01:42 KST 2022
+#2 SMP PREEMPT Fri Feb 26 13:04:51 CST 2021
+#2 SMP PREEMPT Wed Nov 14 02:30:15 CST 2018
+#1 SMP PREEMPT Wed May 22 13:56:39 CST 2019
+#2 SMP PREEMPT Wed Mar 30 06:14:02 CDT 2022
+#1 SMP PREEMPT Thu Oct 28 16:16:15 CST 2021
+#1 SMP PREEMPT Tue Jun 22 02:15:04 CST 2021
+#2 SMP PREEMPT Wed Oct 6 09:39:45 IST 2021
+#2 SMP PREEMPT Wed Jun 24 03:09:34 CST 2020
+#1 SMP PREEMPT Sat Dec 9 02:28:25 WIB 2017
+#1 SMP PREEMPT Mon Apr 27 12:13:51 CST 2020
+#1 SMP PREEMPT Mon Jan 22 11:20:55 CST 2018
+#1 SMP PREEMPT Wed Jan 20 11:33:35 -03 2021
+#1 SMP PREEMPT Sat Apr 10 13:47:55 CST 2021
+#1 SMP PREEMPT Fri Jul 6 15:24:51 KST 2018
+#1 SMP PREEMPT Thu Dec 10 23:37:34 KST 2020
+#1 SMP PREEMPT Sun Mar 3 14:36:44 CST 2019
+#45 SMP PREEMPT Thu Oct 18 16:24:24 CST 2018
+#1 SMP PREEMPT Tue Oct 13 21:22:55 CST 2020
+#2 SMP PREEMPT Tue Jul 31 10:43:08 CST 2018
+#2 SMP PREEMPT Mon Jun 22 18:56:36 CST 2020
+#1 SMP PREEMPT Wed Jun 10 04:34:08 CST 2020
+#1 SMP PREEMPT Thu Aug 30 16:21:42 CEST 2018
+#1 SMP PREEMPT Tue Mar 2 20:22:07 PST 2021
+#1 SMP PREEMPT Wed Oct 27 10:44:55 CST 2021
+#1 SMP PREEMPT Sat Jan 11 00:28:08 CST 2020
+#1 SMP PREEMPT Fri Apr 26 06:13:49 CST 2019
+#1 SMP PREEMPT Tue Jan 2 11:12:03 IST 2018
+#96 SMP PREEMPT Fri May 31 16:07:01 CST 2019
+#1 SMP PREEMPT Fri Jun 5 22:29:50 IST 2020
+#1 SMP PREEMPT Fri Nov 20 12:51:02 CST 2020
+#1 SMP PREEMPT Thu Mar 3 01:25:15 CST 2022
+#1 SMP PREEMPT Mon Aug 12 16:27:48 CST 2019
+#1 SMP PREEMPT Tue May 11 12:13:39 IST 2021
+#1 SMP PREEMPT Fri May 31 00:31:18 CST 2019
+#1 SMP PREEMPT Wed May 29 15:46:34 EDT 2019
+#1 SMP PREEMPT Mon Nov 26 17:09:06 CST 2018
+#1 SMP PREEMPT Fri Mar 2 03:45:13 CST 2018
+#4 SMP Fri Apr 5 14:42:48 CEST 2019
+#1 SMP PREEMPT Thu Dec 12 15:23:40 CST 2019
+#2 SMP PREEMPT Mon May 25 11:54:17 CST 2020
+#1 SMP PREEMPT Fri Jul 5 13:08:34 CEST 2019
+#2 SMP PREEMPT Wed Dec 19 13:12:19 CST 2018
+#1 SMP PREEMPT Fri Aug 17 14:16:52 KST 2018
+#1 SMP PREEMPT Fri Jun 29 16:31:04 CST 2018
+#2 SMP PREEMPT Tue Oct 30 17:09:39 CST 2018
+#1 SMP PREEMPT Fri Jun 21 00:42:41 PDT 2019
+#1 SMP PREEMPT Thu Oct 11 14:55:21 CST 2018
+#1 SMP PREEMPT Wed Feb 12 22:08:58 CST 2020
+#1 SMP PREEMPT Mon Aug 17 17:23:17 KST 2020
+#1 SMP PREEMPT Thu Aug 26 19:01:09 CST 2021
+#1 SMP PREEMPT Sat Jan 22 11:38:48 CST 2022
+#1 SMP PREEMPT Sat Jun 26 23:58:22 CST 2021
+#1 SMP Mon Jun 22 22:49:32 KST 2020
+#1 SMP PREEMPT Fri Dec 6 18:22:30 KST 2019
+#1 SMP PREEMPT Thu Jan 10 14:24:13 CST 2019
+#1 SMP PREEMPT Wed Nov 17 17:23:50 UTC 2021
+#1 SMP PREEMPT Mon Aug 17 12:48:16 CST 2020
+#1 SMP PREEMPT Sun Jul 19 14:07:46 CST 2020
+#1 SMP PREEMPT Fri Oct 16 22:59:30 CST 2020
+#1 SMP PREEMPT Sat Aug 15 08:32:43 IST 2020
+#1 SMP PREEMPT Thu Dec 2 12:09:44 CST 2021
+#2 SMP PREEMPT Thu Dec 26 18:40:59 CST 2019
+#1 SMP PREEMPT Tue Mar 3 09:58:24 CST 2020
+#1 SMP PREEMPT Sat Jun 12 04:15:52 KST 2021
+#1 SMP PREEMPT Thu Apr 22 01:25:16 UTC 2021
+#1 SMP PREEMPT Thu Sep 27 21:15:06 CST 2018
+#1 SMP PREEMPT Tue Sep 10 13:06:43 KST 2019
+#2 SMP PREEMPT Wed Aug 15 11:34:43 CST 2018
+#2 SMP PREEMPT Sat Mar 30 14:39:40 CST 2019
+#1 SMP PREEMPT Wed Jul 11 10:49:52 2018
+#1 SMP PREEMPT Wed Nov 27 13:03:08 CST 2019
+#2 SMP PREEMPT Wed Jun 2 17:22:03 CST 2021
+#1 SMP PREEMPT Fri Mar 1 11:06:18 CST 2019
+#1 SMP PREEMPT Mon Jul 19 19:56:23 PDT 2021
+#1 SMP PREEMPT Fri Aug 17 22:24:38 CST 2018
+#1 SMP PREEMPT Thu Apr 22 07:11:00 UTC 2021
+#1 SMP PREEMPT Thu Feb 17 13:54:26 CST 2022
+#2 SMP PREEMPT Tue Mar 24 12:22:24 CST 2020
+#1 SMP PREEMPT Fri Mar 1 03:29:20 CST 2019
+#1 SMP PREEMPT Tue Sep 11 21:17:10 CST 2018
+#10 SMP PREEMPT Tue May 28 18:18:35 CST 2019
+#2 SMP PREEMPT Thu Mar 21 16:10:39 KST 2019
+#1 SMP PREEMPT Tue Aug 10 15:36:26 KST 2021
+#1 SMP PREEMPT Wed Apr 20 11:11:00 UTC 2022
+#1 SMP PREEMPT Thu Oct 22 20:48:39 JST 2020
+#1 SMP PREEMPT Fri Nov 1 21:07:55 CST 2019
+#5 SMP PREEMPT Fri Feb 2 02:30:40 CST 2018
+#1 SMP PREEMPT Tue Mar 19 08:17:56 KST 2019
+#1 SMP PREEMPT Mon May 23 15:38:50 CST 2022
+#1 SMP PREEMPT Fri Dec 15 19:03:21 CST 2017
+#1 SMP PREEMPT Wed Dec 18 18:39:32 -03 2019
+#1 SMP PREEMPT Tue Jun 26 12:48:41 KST 2018
+#1 SMP PREEMPT Mon Jun 7 12:00:07 CST 2021
+#1 SMP PREEMPT Wed Feb 26 17:03:09 CST 2020
+#1 SMP PREEMPT Sun Jun 27 23:32:30 CST 2021
+#2 SMP Fri Jul 3 19:50:04 UTC 2020
+#1 SMP PREEMPT Sat Mar 16 01:19:58 KST 2019
+#1 SMP PREEMPT Mon Jul 6 21:47:39 CDT 2020
+#1 SMP PREEMPT Wed Oct 14 06:11:14 CDT 2020
+#2 SMP PREEMPT Wed Jul 10 20:34:48 CST 2019
+#9 SMP PREEMPT Sun Dec 29 19:32:19 CST 2019
+#1 SMP PREEMPT Wed Sep 4 16:18:51 CST 2019
+#1 SMP PREEMPT Tue Jun 1 01:01:08 KST 2021
+#1 SMP PREEMPT Thu Oct 18 18:34:16 KST 2018
+#1 SMP PREEMPT Tue Jul 14 22:01:02 CST 2020
+#1 SMP PREEMPT Mon May 10 01:26:47 CDT 2021
+#1 SMP PREEMPT Thu Aug 9 14:19:34 CST 2018
+#1 SMP PREEMPT Fri Oct 16 08:01:09 CDT 2020
+#1 SMP PREEMPT Fri Mar 22 11:04:15 KST 2019
+#2 SMP PREEMPT Mon Mar 28 11:38:28 CST 2022
+#1 SMP PREEMPT Fri Jan 15 00:40:25 IST 2021
+#1 SMP PREEMPT Wed Jun 16 17:31:49 CST 2021
+#1 SMP PREEMPT Wed Oct 28 22:49:37 KST 2020
+#1 SMP PREEMPT Thu Mar 19 17:42:26 CST 2020
+#1 SMP PREEMPT Tue May 19 18:03:24 KST 2020
+#1 SMP PREEMPT Sat Jan 9 01:43:19 IST 2021
+#1 SMP PREEMPT Wed Apr 27 12:48:40 CST 2022
+#7 SMP PREEMPT Tue Sep 25 13:11:50 CST 2018
+#1 SMP PREEMPT Tue Apr 10 14:24:46 CST 2018
+#1 SMP PREEMPT Mon Mar 28 23:33:34 CST 2022
+#1 SMP PREEMPT Mon Oct 1 17:24:02 KST 2018
+#1 SMP PREEMPT Wed Sep 15 02:41:26 CST 2021
+#1 SMP PREEMPT Mon Jul 27 12:55:31 KST 2020
+#1 SMP PREEMPT Thu Feb 22 17:24:48 CST 2018
+#2 SMP PREEMPT Wed May 15 16:02:50 CST 2019
+#1 SMP PREEMPT Thu Oct 24 14:55:50 +07 2019
+#1 SMP PREEMPT Fri Nov 9 23:10:52 KST 2018
+#1 SMP PREEMPT Tue Nov 23 09:45:07 CST 2021
+#6 SMP PREEMPT Mon Apr 26 16:00:08 IST 2021
+#2 SMP PREEMPT Fri Aug 27 20:34:02 CST 2021
+#1 SMP PREEMPT Fri May 15 18:31:59 KST 2020
+#13 SMP PREEMPT Mon Oct 21 20:37:21 CST 2019
+#1 SMP PREEMPT Thu Sep 23 20:09:28 CST 2021
+#1 SMP PREEMPT Fri Nov 5 22:45:28 CST 2021
+#2 SMP PREEMPT Wed Oct 23 22:33:58 KST 2019
+#1 SMP PREEMPT Wed Feb 28 04:01:58 CST 2018
+#1 SMP PREEMPT Wed Jun 24 22:47:38 KST 2020
+#1 SMP PREEMPT Thu Mar 10 02:41:04 CST 2022
+#2 SMP PREEMPT Thu May 9 11:16:17 KST 2019
+#1 SMP PREEMPT Tue May 14 18:09:26 CST 2019
+#1 SMP PREEMPT Wed Feb 21 12:19:21 KST 2018
+#40 SMP PREEMPT Thu Sep 2 12:33:06 CST 2021
+#1 SMP PREEMPT Tue Jun 16 15:57:40 CST 2020
+#1 SMP PREEMPT Fri Jan 26 15:45:38 CST 2018
+#2 SMP PREEMPT Mon Apr 22 14:00:32 KST 2019
+#1 SMP PREEMPT Wed Jan 15 12:50:05 CST 2020
+#2 SMP PREEMPT Thu Apr 30 18:57:22 PDT 2020
+#1 SMP PREEMPT Thu Dec 16 17:49:31 CST 2021
+#2 SMP PREEMPT Thu Nov 11 19:04:07 CST 2021
+#1 SMP PREEMPT Wed Jul 31 04:11:21 JST 2019
+#1 SMP PREEMPT Thu May 3 23:18:14 IST 2018
+#1 SMP PREEMPT Wed Sep 1 20:44:48 UTC 2021
+#1 SMP PREEMPT Fri Mar 5 23:51:54 CST 2021
+#1 SMP PREEMPT Mon Mar 7 13:04:48 CST 2022
+#1 SMP PREEMPT Thu Dec 10 19:53:36 PST 2020
+#1 SMP PREEMPT Tue Mar 5 18:50:27 CST 2019
+#1 SMP PREEMPT Thu Mar 21 08:34:19 KST 2019
+#1 SMP PREEMPT Mon Sep 7 19:25:47 WIB 2020
+#1 SMP PREEMPT Tue Aug 31 17:33:34 CST 2021
+#1 SMP PREEMPT Mon Sep 7 21:06:14 CST 2020
+#1 SMP PREEMPT Tue Dec 15 12:27:03 CST 2020
+#2 SMP PREEMPT Mon Oct 22 12:34:18 UTC 2018
+#1 SMP PREEMPT Sat Feb 12 11:58:17 CST 2022
+#1 SMP PREEMPT Mon Jan 10 06:58:42 UTC 2022
+#1 SMP PREEMPT Tue Sep 17 18:21:25 CST 2019
+#2 SMP PREEMPT Tue Apr 3 15:12:36 IST 2018
+#1 SMP PREEMPT Tue Jun 19 16:47:40 CST 2018
+#1 SMP PREEMPT Tue Oct 30 15:57:29 CST 2018
+#2 SMP PREEMPT Sun May 5 08:49:07 CST 2019
+#1 SMP PREEMPT Fri Jul 31 00:46:47 CST 2020
+#1 SMP PREEMPT Sat Jul 27 01:22:45 CST 2019
+#2 SMP PREEMPT Wed Aug 29 12:58:09 CST 2018
+#1 SMP PREEMPT Tue Aug 25 22:09:07 CST 2020
+#1 SMP PREEMPT Wed Jul 15 18:57:47 CST 2020
+#1 SMP PREEMPT Sat Jul 13 03:56:25 CST 2019
+#1 SMP PREEMPT Sat Mar 14 04:55:47 KST 2020
+#1 SMP PREEMPT Thu Nov 15 04:27:49 PST 2018
+#1 SMP PREEMPT Mon Dec 18 16:34:49 KST 2017
+#1 SMP PREEMPT Mon Jul 5 19:29:25 CST 2021
+#1 SMP PREEMPT Thu Mar 7 01:32:02 CST 2019
+#1 SMP PREEMPT Fri Jan 21 20:21:45 KST 2022
+#2 SMP PREEMPT Wed Jul 3 20:58:37 KST 2019
+#1 SMP PREEMPT Sat Oct 10 13:27:00 KST 2020
+#1 SMP PREEMPT Mon Jul 20 15:30:43 CST 2020
+#1 SMP PREEMPT Tue Nov 28 23:42:46 KST 2017
+#2 SMP PREEMPT Tue Apr 16 19:12:59 CST 2019
+#1 SMP PREEMPT Mon Jul 9 11:08:50 GMT 2018
+#1 SMP PREEMPT Wed Sep 1 22:02:38 PDT 2021
+#1 SMP PREEMPT Wed Jun 26 05:10:21 CST 2019
+#1 SMP PREEMPT Tue Oct 22 23:36:09 CST 2019
+#1 SMP PREEMPT Wed Sep 26 18:16:52 CST 2018
+#2 SMP PREEMPT Mon Feb 14 15:43:39 CST 2022
+#1 SMP PREEMPT Mon Nov 12 17:01:05 IST 2018
+#1 SMP PREEMPT Thu Aug 30 01:21:17 EDT 2018
+#1 SMP PREEMPT Thu Apr 18 23:02:23 CST 2019
+#1 SMP PREEMPT Mon Aug 6 11:04:31 KST 2018
+#1 SMP PREEMPT Fri Nov 12 11:19:10 CST 2021
+#1 SMP PREEMPT Thu Mar 5 14:48:05 KST 2020
+#1 SMP PREEMPT Wed Feb 23 19:46:19 CST 2022
+#1 SMP PREEMPT Fri Oct 23 08:03:36 KST 2020
+#1 SMP PREEMPT Thu Sep 26 16:06:40 CST 2019
+#1 SMP PREEMPT Wed Oct 6 06:15:40 JST 2021
+#1 SMP PREEMPT Thu Feb 10 21:51:48 CST 2022
+#1 SMP PREEMPT Wed Sep 30 04:27:26 CDT 2020
+#1 SMP PREEMPT Mon Oct 25 17:41:33 CST 2021
+#1 SMP PREEMPT Tue Mar 22 22:01:07 CST 2022
+#2 SMP PREEMPT Sat Jan 25 03:24:47 JST 2020
+#1 SMP PREEMPT Fri Apr 19 19:26:23 CST 2019
+#1 SMP PREEMPT Wed Dec 13 04:54:37 KST 2017
+#1 SMP PREEMPT Wed Mar 27 00:23:50 PDT 2019
+#1 SMP PREEMPT Wed Nov 20 01:11:31 CST 2019
+#1 SMP PREEMPT Tue Oct 1 11:07:51 CST 2019
+#2 SMP PREEMPT Tue Sep 22 18:09:07 CST 2020
+#2 SMP PREEMPT Fri Sep 20 00:48:01 CST 2019
+#1 SMP PREEMPT Fri Mar 8 20:16:54 CST 2019
+#1 SMP PREEMPT Wed Sep 16 05:20:36 EDT 2020
+#1 SMP PREEMPT Wed Aug 12 14:27:12 KST 2020
+#1 SMP PREEMPT Sat Dec 29 11:37:17 CET 2018
+#1 SMP PREEMPT Mon Jun 21 01:58:04 PDT 2021
+#1 SMP PREEMPT Thu Aug 26 22:09:08 CST 2021
+#1 SMP PREEMPT Wed Feb 13 18:39:13 KST 2019
+#1 SMP PREEMPT Tue Mar 19 11:16:46 KST 2019
+#1 SMP PREEMPT Tue Apr 26 11:40:17 PDT 2022
+#1 SMP PREEMPT Wed Jan 30 23:07:01 PST 2019
+#1 SMP PREEMPT Fri Apr 24 02:42:25 CST 2020
+#1 SMP PREEMPT Fri Dec 4 10:07:49 CST 2020
+#1 SMP PREEMPT Fri Dec 24 16:45:12 CST 2021
+#1 SMP PREEMPT Mon Jun 11 23:40:27 CST 2018
+#1 SMP PREEMPT Mon Aug 6 16:20:47 CST 2018
+#1 SMP PREEMPT Tue Jan 9 20:32:02 UTC 2018
+#5 SMP PREEMPT Wed Nov 28 18:37:20 CST 2018
+#1 SMP PREEMPT Mon Oct 22 23:45:08 PDT 2018
+#1 SMP PREEMPT Wed Jun 13 22:21:55 PDT 2018
+#1 SMP PREEMPT Wed Mar 31 00:04:37 CST 2021
+#1 SMP PREEMPT Sat Jan 29 00:10:15 UTC 2022
+#2 SMP PREEMPT Thu Dec 20 10:09:35 CST 2018
+#2 SMP PREEMPT Mon May 6 10:30:02 CST 2019
+#1 SMP PREEMPT Tue Jan 22 19:01:48 CST 2019
+#1 SMP PREEMPT Sat Aug 15 01:00:12 CST 2020
+#173 SMP PREEMPT Thu Mar 17 15:02:27 CST 2022
+#1 SMP PREEMPT Tue Apr 28 09:50:30 EDT 2020
+#1 SMP PREEMPT Tue Apr 7 20:43:56 PDT 2020
+#1 SMP PREEMPT Fri Jul 9 10:04:44 UTC 2021
+#2 SMP PREEMPT Tue Dec 17 18:02:42 CST 2019
+#1 SMP PREEMPT Tue Nov 5 17:13:20 CST 2019
+#1 SMP PREEMPT Mon Sep 2 21:30:44 CST 2019
+#1 SMP PREEMPT Mon Nov 1 16:15:42 CST 2021
+#2 SMP PREEMPT Fri Jun 19 11:57:07 CST 2020
+#1 SMP PREEMPT Fri Jul 3 11:43:33 CST 2020
+#1 SMP PREEMPT Thu Aug 29 00:31:21 PDT 2019
+#1 SMP PREEMPT Thu Dec 5 11:17:29 CST 2019
+#2 SMP PREEMPT Wed Sep 22 18:24:21 CST 2021
+#1 SMP PREEMPT Thu Jul 16 10:17:46 CST 2020
+#1 SMP PREEMPT Thu Apr 9 13:10:28 CST 2020
+#2 SMP PREEMPT Wed Apr 3 14:32:33 KST 2019
+#1 SMP PREEMPT Wed Jul 15 13:15:24 CST 2020
+#2 SMP PREEMPT Tue May 24 10:16:57 CST 2022
+#1 SMP PREEMPT Tue Jan 2 18:37:41 CST 2018
+#1 SMP PREEMPT Fri Nov 16 04:09:13 CST 2018
+#1 SMP PREEMPT Tue Apr 3 00:10:43 CST 2018
+#1 SMP PREEMPT Sat Mar 17 18:06:26 CST 2018
+#1 SMP PREEMPT Tue Mar 10 18:26:14 IST 2020
+#1 SMP PREEMPT Sun Jul 11 22:24:46 CST 2021
+#1 SMP PREEMPT Fri Mar 18 17:49:49 WIB 2022
+#1 SMP PREEMPT Wed Dec 9 11:13:29 CST 2020
+#1 SMP PREEMPT Mon Oct 21 17:06:35 KST 2019
+#1 SMP PREEMPT Tue Jun 11 16:02:48 CST 2019
+#1 SMP PREEMPT Mon Feb 21 11:19:12 CST 2022
+#1 SMP PREEMPT Sat Oct 23 17:39:05 CEST 2021
+#1 SMP PREEMPT Tue Apr 7 21:59:56 CST 2020
+#1 SMP PREEMPT Mon Feb 17 10:03:34 CST 2020
+#1 SMP PREEMPT Mon Sep 23 17:07:54 CST 2019
+#2 SMP PREEMPT Thu Nov 5 10:56:54 CST 2020
+#1 SMP PREEMPT Thu Apr 26 16:43:05 KST 2018
+#1 SMP PREEMPT Sat Jul 11 02:58:52 CST 2020
+#1 SMP PREEMPT Mon Jan 31 08:15:15 CST 2022
+#2 SMP PREEMPT Wed Aug 15 10:13:48 CST 2018
+#1 SMP PREEMPT Thu Aug 9 16:58:44 KST 2018
+#1 SMP PREEMPT Mon Nov 9 11:52:25 CST 2020
+#2 SMP PREEMPT Mon Dec 13 11:17:01 CST 2021
+#1 SMP PREEMPT Mon Mar 4 16:13:41 KST 2019
+#1 SMP PREEMPT Tue Nov 28 14:43:24 CST 2017
+#1 SMP PREEMPT Tue Mar 15 19:56:10 CST 2022
+#1 SMP PREEMPT Mon Feb 21 22:47:59 CST 2022
+#1 SMP PREEMPT Mon Jun 7 09:32:21 +07 2021
+#1 SMP PREEMPT Fri Jan 12 16:57:49 CET 2018
+#1 SMP PREEMPT Fri Aug 30 18:19:28 KST 2019
+#1 SMP PREEMPT Wed Oct 28 23:14:23 CST 2020
+#1 SMP PREEMPT Wed May 13 18:10:54 IST 2020
+#2 SMP PREEMPT Fri Jan 3 13:52:54 UTC 2020
+#1 SMP PREEMPT Thu Apr 21 12:54:40 CST 2022
+#2 SMP PREEMPT Sun Jul 7 05:38:49 CST 2019
+#1 SMP PREEMPT Fri Feb 21 11:19:28 HKT 2020
+#2 SMP PREEMPT Sat Dec 12 15:40:26 CST 2020
+#2 SMP PREEMPT Tue Oct 8 20:33:56 KST 2019
+#2 SMP PREEMPT Wed Jan 12 09:09:52 CST 2022
+#1 SMP PREEMPT Mon May 23 22:39:39 CST 2022
+#1 SMP PREEMPT Tue Mar 22 13:13:39 PDT 2022
+#1 SMP PREEMPT Thu Nov 15 22:59:19 PST 2018
+#2 SMP PREEMPT Mon Aug 10 15:11:58 CST 2020
+#1 SMP PREEMPT Wed Oct 21 22:04:12 CST 2020
+#2 SMP PREEMPT Mon Mar 23 18:47:12 KST 2020
+#1 SMP PREEMPT Thu Mar 3 20:28:30 CST 2022
+#1 SMP PREEMPT Tue Oct 16 18:16:09 CST 2018
+#1 SMP PREEMPT Wed Mar 21 10:31:52 CST 2018
+#1 SMP PREEMPT Wed Dec 19 11:16:47 CST 2018
+#1 SMP PREEMPT Mon Apr 8 05:48:14 CST 2019
+#2 SMP PREEMPT Sat Aug 29 19:52:23 CST 2020
+#1 SMP PREEMPT Thu Sep 17 00:54:53 CST 2020
+#1 SMP PREEMPT Thu Feb 3 02:56:22 PST 2022
+#1 SMP PREEMPT Fri Jan 14 05:02:22 CST 2022
+#2 SMP PREEMPT Thu Mar 8 14:39:53 CST 2018
+#1 SMP PREEMPT Thu Nov 18 16:40:39 UTC 2021
+#1 SMP PREEMPT Fri Apr 2 18:49:04 CST 2021
+#1 SMP PREEMPT Wed Jan 9 14:56:07 CST 2019
+#1 SMP PREEMPT Thu Dec 16 10:49:35 HKT 2021
+#2 SMP PREEMPT Fri Apr 3 11:26:02 CST 2020
+#1 SMP PREEMPT Thu Jul 5 15:43:53 KST 2018
+#1 SMP PREEMPT Fri Jan 18 21:28:56 HKT 2019
+#1 SMP PREEMPT Thu Aug 23 11:24:04 2018
+#1 SMP PREEMPT Thu Mar 11 17:34:46 CST 2021
+#1 SMP PREEMPT Tue Jun 16 00:32:26 CST 2020
+#1 SMP PREEMPT Mon Feb 22 22:32:10 CST 2021
+#1 SMP PREEMPT Wed Oct 20 15:14:27 CST 2021
+#1 SMP PREEMPT Mon Nov 11 17:35:31 CST 2019
+#1 SMP PREEMPT Wed Dec 8 18:44:04 CST 2021
+#1 SMP PREEMPT Fri May 18 16:54:38 CST 2018
+#2 SMP PREEMPT Sat Aug 1 18:31:31 CST 2020
+#1 SMP PREEMPT Thu Aug 13 11:59:35 CST 2020
+#1 SMP PREEMPT Thu Dec 2 12:25:51 CST 2021
+#2 SMP PREEMPT Fri Oct 9 11:13:20 CST 2020
+#2 SMP PREEMPT Fri Aug 21 23:47:24 -03 2020
+#1 SMP PREEMPT Wed Sep 22 20:32:14 CST 2021
+#0 SMP PREEMPT Fri Sep 7 17:20:04 UTC 2018
+#1 SMP PREEMPT Thu Sep 23 19:46:52 CST 2021
+#1 SMP PREEMPT Fri Apr 15 07:47:11 CST 2022
+#1 SMP PREEMPT Mon Sep 20 20:57:27 IST 2021
+#1 SMP PREEMPT Fri Jul 6 10:43:02 JST 2018
+#1 SMP PREEMPT Tue Mar 17 00:01:04 PDT 2020
+#1 SMP PREEMPT Fri Mar 27 16:29:38 WIB 2020
+#1 SMP PREEMPT Wed Sep 9 18:57:10 KST 2020
+#1 SMP PREEMPT Thu Feb 3 16:29:33 KST 2022
+#2 SMP PREEMPT Sat Mar 9 21:10:12 CST 2019
+#1 SMP PREEMPT Mon Apr 16 19:37:23 KST 2018
+#1 SMP PREEMPT Sat Oct 19 01:24:30 CST 2019
+#1 SMP PREEMPT Thu Sep 27 22:55:31 CST 2018
+#1 SMP PREEMPT Tue Dec 3 00:02:41 PST 2019
+#2 SMP PREEMPT Tue Aug 17 19:08:38 KST 2021
+#1 SMP PREEMPT Wed Jan 9 22:08:18 CST 2019
+#1 SMP PREEMPT Thu Apr 14 21:17:09 CST 2022
+#1 SMP PREEMPT Fri Jan 25 12:41:43 KST 2019
+#1 SMP PREEMPT Tue Sep 29 20:33:03 IST 2020
+#1 SMP PREEMPT Thu Mar 24 16:29:14 CST 2022
+#1 SMP PREEMPT Mon May 25 09:45:52 IST 2020
+#13 SMP Mon Oct 21 19:32:40 IST 2019
+#1 SMP PREEMPT Fri Mar 6 21:36:17 CST 2020
+#1 SMP PREEMPT Tue Jun 8 21:42:37 CST 2021
+#1 SMP PREEMPT Fri Feb 11 18:06:43 CST 2022
+#1 SMP PREEMPT Thu Aug 26 21:55:57 CST 2021
+#1 SMP PREEMPT Sun Mar 3 01:52:34 CST 2019
+#1 SMP PREEMPT Wed Apr 29 14:34:07 CST 2020
+#1 SMP PREEMPT Mon Mar 30 10:33:32 CST 2020
+#1 SMP PREEMPT Sat May 7 14:15:39 CST 2022
+#1 SMP PREEMPT Wed Jun 26 15:50:36 CST 2019
+#1 SMP PREEMPT Sun Jan 27 09:54:33 CST 2019
+#1 SMP PREEMPT Wed Aug 1 13:45:11 CST 2018
+#1 SMP PREEMPT Wed Apr 22 01:07:49 CST 2020
+#1 SMP PREEMPT Wed Jun 3 14:27:04 CST 2020
+#1 SMP PREEMPT Sat Sep 8 19:07:02 CST 2018
+#1 SMP PREEMPT Sun Jan 30 21:12:57 CST 2022
+#1 SMP PREEMPT Wed Jun 10 17:11:57 JST 2020
+#1 SMP PREEMPT Tue Jul 21 02:48:07 CST 2020
+#1 SMP PREEMPT Thu Aug 13 17:03:31 CST 2020
+#1 SMP PREEMPT Tue Jul 20 20:25:41 CST 2021
+#1 SMP PREEMPT Tue Sep 28 21:23:01 PDT 2021
+#1 SMP PREEMPT Mon Mar 22 20:49:24 WIB 2021
+#2 SMP PREEMPT Thu Sep 20 19:10:48 CST 2018
+#1 SMP PREEMPT Sat Feb 16 21:11:01 CST 2019
+#1 SMP PREEMPT Sat Apr 24 23:02:24 CST 2021
+#1 SMP PREEMPT Thu Jan 16 03:19:37 CST 2020
+#1 SMP PREEMPT Wed Jul 8 15:42:04 CST 2020
+#1 SMP PREEMPT Fri Jul 19 13:31:28 KST 2019
+#1 SMP PREEMPT Sat Nov 28 11:37:28 CST 2020
+#2 SMP PREEMPT Fri Jan 14 00:38:04 CST 2022
+#1 SMP PREEMPT Thu Mar 26 00:07:39 PDT 2020
+#1 SMP PREEMPT Fri Dec 3 02:21:39 CST 2021
+#1 SMP PREEMPT Mon Oct 4 15:58:14 JST 2021
+#1 SMP PREEMPT Wed Feb 19 23:07:21 CST 2020
+#6 SMP PREEMPT Fri Oct 22 14:02:46 UTC 2021
+#1 SMP PREEMPT Sun Jul 14 21:59:22 PDT 2019
+#1 SMP PREEMPT Sat Dec 29 18:34:20 CST 2018
+#1 SMP PREEMPT Sun Jun 21 00:38:35 CST 2020
+#2 SMP PREEMPT Tue Nov 24 01:52:33 CST 2020
+#1 SMP PREEMPT Tue May 25 00:44:09 CST 2021
+#1 SMP PREEMPT Fri Jan 10 18:20:32 IST 2020
+#2 SMP PREEMPT Wed Sep 12 12:48:42 CST 2018
+#1 SMP PREEMPT Mon Sep 24 20:12:42 PDT 2018
+#446 SMP PREEMPT Mon Jan 18 10:52:44 CST 2021
+#1 SMP PREEMPT Thu May 24 09:30:10 CST 2018
+#2 SMP PREEMPT Tue Dec 4 19:20:36 CST 2018
+#1 SMP PREEMPT Tue Nov 2 19:02:10 CST 2021
+#1 SMP PREEMPT Tue Jan 7 22:37:23 CST 2020
+#1 SMP PREEMPT Mon Oct 21 14:27:19 CST 2019
+#1 SMP PREEMPT Sat Oct 19 09:36:52 CST 2019
+#1 SMP PREEMPT Wed Feb 7 03:55:49 CST 2018
+#1 SMP PREEMPT Tue Feb 6 18:04:14 CST 2018
+#2 SMP PREEMPT Fri Jun 7 11:50:08 KST 2019
+#1 SMP PREEMPT Wed Apr 21 22:11:26 CST 2021
+#1 SMP PREEMPT Wed Dec 19 15:51:43 CST 2018
+#1 SMP PREEMPT Mon Sep 17 16:13:17 CST 2018
+#1 SMP PREEMPT Sun Aug 1 22:12:13 CST 2021
+#1 SMP PREEMPT Fri Dec 1 17:44:18 CST 2017
+#1 SMP PREEMPT Mon Apr 16 11:55:11 EDT 2018
+#1 SMP PREEMPT Mon May 11 02:39:25 PDT 2020
+#1 SMP PREEMPT Tue Aug 6 00:25:27 PDT 2019
+#2 SMP PREEMPT Wed Jun 12 10:50:35 KST 2019
+#1 SMP PREEMPT Tue Mar 5 17:29:06 CST 2019
+#1 SMP PREEMPT Thu Sep 13 05:23:08 CST 2018
+#2 SMP PREEMPT Mon Dec 9 18:13:00 CST 2019
+#2 SMP PREEMPT Thu Apr 29 17:28:51 EEST 2021
+#1 SMP PREEMPT Fri Oct 26 15:21:12 KST 2018
+#1 SMP PREEMPT Thu Apr 30 05:17:44 KST 2020
+#1 SMP PREEMPT Wed Aug 5 18:27:30 CST 2020
+#1 SMP PREEMPT Fri Jan 4 10:20:40 CST 2019
+#1 SMP PREEMPT Thu Sep 6 23:38:27 PDT 2018
+#1 SMP PREEMPT Sat Aug 31 19:08:51 CST 2019
+#1 SMP PREEMPT Thu Feb 24 01:15:51 CST 2022
+#2 SMP PREEMPT Fri Oct 30 21:37:52 KST 2020
+#1 SMP PREEMPT Wed Mar 6 11:47:32 CST 2019
+#1 SMP PREEMPT Sun Nov 24 01:29:25 CST 2019
+#1 SMP PREEMPT Sun Apr 17 21:26:38 CST 2022
+#1 SMP PREEMPT Sat Oct 10 03:34:16 CDT 2020
+#1 SMP PREEMPT Thu Sep 13 02:09:24 CST 2018
+#1 SMP PREEMPT Fri May 17 03:50:51 PDT 2019
+#1 SMP PREEMPT Thu Jan 11 09:29:39 CST 2018
+#1 SMP PREEMPT Tue Apr 9 21:52:01 PDT 2019
+#1 SMP PREEMPT Tue Feb 15 12:02:52 CST 2022
+#1 SMP PREEMPT Thu Jan 24 15:16:30 2019
+#1 SMP PREEMPT Fri May 8 02:11:51 CST 2020
+#1 SMP PREEMPT Thu Oct 15 04:18:18 CST 2020
+#1 SMP PREEMPT Tue Sep 17 17:32:44 CST 2019
+#1 SMP PREEMPT Mon Oct 12 10:42:47 CST 2020
+#1 SMP PREEMPT Thu May 24 19:16:11 CST 2018
+#1 SMP PREEMPT Tue Jul 14 19:52:19 UTC 2020
+#1 SMP PREEMPT Wed Apr 24 23:53:38 CST 2019
+#1 SMP PREEMPT Fri Nov 6 13:41:59 CST 2020
+#2 SMP PREEMPT Tue Dec 3 05:26:36 KST 2019
+#1 SMP PREEMPT Thu Jul 12 14:21:38 KST 2018
+#2 SMP PREEMPT Mon Aug 17 13:32:19 CEST 2020
+#1 SMP PREEMPT Wed Apr 6 01:16:20 CST 2022
+#2 SMP PREEMPT Tue Oct 8 18:18:37 KST 2019
+#1 SMP PREEMPT Fri Jul 26 12:11:51 CST 2019
+#1 SMP PREEMPT Wed Apr 28 15:02:25 KST 2021
+#1 SMP PREEMPT Fri Sep 11 14:32:30 KST 2020
+#1 SMP PREEMPT Tue Apr 16 02:11:53 CST 2019
+#2 SMP PREEMPT Wed Aug 14 01:50:43 KST 2019
+#1 SMP PREEMPT Mon Nov 11 11:13:09 CST 2019
+#1 SMP PREEMPT Mon Mar 11 19:58:50 CST 2019
+#2 SMP PREEMPT Mon Jul 20 11:09:14 CST 2020
+#1 SMP PREEMPT Thu Jan 14 17:00:25 CST 2021
+#2 SMP PREEMPT Mon Mar 29 06:49:29 KST 2021
+#1 SMP PREEMPT Sun Apr 14 21:38:51 PDT 2019
+#1 SMP PREEMPT Mon Oct 21 14:49:48 CST 2019
+#3 SMP PREEMPT Wed Jul 14 20:01:04 KST 2021
+#1 SMP PREEMPT Sat Sep 7 15:28:28 CST 2019
+#1 SMP PREEMPT Wed Sep 5 20:03:43 CST 2018
+#1 SMP PREEMPT Mon Jun 25 17:27:20 CST 2018
+#1 SMP PREEMPT Mon Jan 31 04:01:49 CST 2022
+#1 SMP PREEMPT Thu Dec 16 11:52:06 CST 2021
+#0 SMP PREEMPT Wed Nov 24 10:04:17 UTC 2021
+#1 SMP PREEMPT Sat Jul 27 14:52:19 KST 2019
+#1 SMP PREEMPT Sat Feb 27 14:39:26 CST 2021
+#1 SMP PREEMPT Sat May 5 18:09:32 IST 2018
+#1 SMP PREEMPT Sun Jul 28 16:17:02 CST 2019
+#2 SMP PREEMPT Sat Jun 19 13:36:09 CEST 2021
+#1 SMP PREEMPT Fri Aug 20 23:35:40 CST 2021
+#1 SMP PREEMPT Sat Jan 13 17:20:00 KST 2018
+#4 SMP PREEMPT Sun Apr 28 03:33:29 CST 2019
+#2 SMP PREEMPT Fri Sep 4 05:28:00 KST 2020
+#1 SMP PREEMPT Thu Feb 1 14:45:31 CST 2018
+#1 SMP PREEMPT Sat Apr 21 01:27:23 KST 2018
+#1 SMP PREEMPT Wed Aug 22 19:55:43 CST 2018
+#1 SMP PREEMPT Tue Mar 29 15:43:37 CST 2022
+#1 SMP PREEMPT Tue Oct 22 14:16:08 CST 2019
+#1 SMP PREEMPT Thu Sep 5 18:28:58 IST 2019
+#1 SMP PREEMPT Sat Apr 23 02:10:20 CST 2022
+#2 SMP PREEMPT Wed Jan 8 01:49:56 CST 2020
+#1 SMP PREEMPT Tue Jul 31 12:25:24 CST 2018
+#1 SMP PREEMPT Wed Jul 17 20:41:06 IST 2019
+#1 SMP PREEMPT Wed Mar 21 05:12:33 IST 2018
+#1 SMP PREEMPT Mon Nov 26 17:11:23 CST 2018
+#1 SMP PREEMPT Tue Dec 31 18:56:59 CST 2019
+#1 SMP PREEMPT Fri Aug 6 11:05:24 CST 2021
+#1 SMP PREEMPT Fri Oct 8 13:07:42 CST 2021
+#1 SMP PREEMPT Wed Aug 15 16:37:22 CST 2018
+#1 SMP PREEMPT Sat Sep 28 11:01:44 CST 2019
+#1 SMP PREEMPT Thu Aug 26 15:54:22 CST 2021
+#1 SMP PREEMPT Tue Sep 10 14:59:41 2019
+#15 SMP PREEMPT Thu Sep 16 09:40:06 CST 2021
+#1 SMP PREEMPT Wed Nov 11 06:10:47 CST 2020
+#1 SMP PREEMPT Thu Feb 28 18:27:33 CST 2019
+#1 SMP PREEMPT Fri Dec 29 19:00:19 KST 2017
+#1 SMP PREEMPT Wed Aug 8 02:54:26 CST 2018
+#1 SMP PREEMPT Fri Oct 11 20:38:38 CST 2019
+#1 SMP PREEMPT Thu Jan 4 20:00:36 CST 2018
+#1 SMP PREEMPT Fri Dec 1 14:43:17 KST 2017
+#1 SMP PREEMPT Sun May 12 22:35:44 PDT 2019
+#1 SMP PREEMPT Fri Sep 24 11:45:17 CST 2021
+#1 SMP PREEMPT Thu Jan 21 23:50:28 CST 2021
+#1 SMP PREEMPT Fri Sep 7 00:39:31 CST 2018
+#1 SMP PREEMPT Tue May 11 18:21:53 CST 2021
+#1 SMP PREEMPT Wed Apr 8 18:40:48 KST 2020
+#1 SMP PREEMPT Tue Jul 2 14:03:12 KST 2019
+#1 SMP PREEMPT Sun Mar 27 01:40:20 CST 2022
+#1 SMP PREEMPT Mon May 13 22:21:48 EEST 2019
+#1 SMP PREEMPT Wed Jun 17 00:55:29 CST 2020
+#1 SMP PREEMPT Thu May 3 14:40:17 KST 2018
+#2 SMP PREEMPT Thu Aug 5 12:56:54 KST 2021
+#1 SMP PREEMPT Wed Jan 22 16:57:19 KST 2020
+#1 SMP PREEMPT Thu Oct 10 20:41:02 CST 2019
+#4 SMP PREEMPT Tue Dec 22 21:21:04 CST 2020
+#1 SMP PREEMPT Wed Jun 5 12:45:24 CST 2019
+#1 SMP PREEMPT Tue Jul 2 14:01:23 CST 2019
+#1 SMP PREEMPT Mon Aug 20 21:15:48 CST 2018
+#1 SMP PREEMPT Sat May 30 04:14:18 CST 2020
+#1 SMP PREEMPT Tue Nov 20 18:55:52 CST 2018
+#2 SMP PREEMPT Wed Aug 29 22:10:58 CST 2018
+#2 SMP PREEMPT Thu Jul 9 15:44:56 CST 2020
+#3 SMP PREEMPT Fri Feb 15 12:18:13 CST 2019
+#1 SMP PREEMPT Tue Nov 27 13:49:10 CST 2018
+#1 SMP PREEMPT Wed Mar 16 11:35:43 CST 2022
+#2 SMP PREEMPT Tue Feb 25 15:58:10 CST 2020
+#1 SMP PREEMPT Sat Oct 20 15:18:36 IST 2018
+#1 SMP PREEMPT Tue Aug 4 15:41:48 CST 2020
+#1 SMP PREEMPT Wed Feb 28 18:41:20 CST 2018
+#1 SMP PREEMPT Fri Oct 18 15:50:19 CST 2019
+#1 SMP PREEMPT Fri Mar 6 18:18:56 KST 2020
+#1 SMP PREEMPT Wed Aug 18 11:19:01 CST 2021
+#1 SMP PREEMPT Sun Aug 30 01:32:58 CST 2020
+#1 SMP PREEMPT Thu Nov 26 15:28:42 CST 2020
+#1 SMP PREEMPT Tue May 29 14:39:24 2018
+#1 SMP PREEMPT Fri Mar 4 09:15:25 CST 2022
+#1 SMP PREEMPT Fri Sep 3 20:31:58 CST 2021
+#1 SMP PREEMPT Tue Dec 19 13:23:23 EST 2017
+#1 SMP PREEMPT Tue May 17 13:19:37 CST 2022
+#2 SMP PREEMPT Mon Aug 10 08:49:00 UTC 2020
+#1 SMP PREEMPT Mon Feb 28 16:28:23 CST 2022
+#1 SMP PREEMPT Mon Apr 8 00:50:50 PDT 2019
+#1 SMP PREEMPT Mon Dec 2 13:54:55 KST 2019
+#2 SMP PREEMPT Mon Oct 11 17:45:14 CST 2021
+#1 SMP PREEMPT Mon Mar 30 22:18:39 UTC 2020
+#1 SMP PREEMPT Thu May 9 16:16:41 CEST 2019
+#1 SMP PREEMPT Thu Apr 28 17:54:36 CST 2022
+#1 SMP PREEMPT Fri Feb 22 21:27:31 CST 2019
+#1 SMP PREEMPT Tue Nov 5 17:26:46 CST 2019
+#1 SMP PREEMPT Sat Mar 16 00:46:04 KST 2019
+#1 SMP PREEMPT Wed Feb 27 04:43:06 KST 2019
+#1 SMP PREEMPT Fri Mar 11 19:15:44 CST 2022
+#1 SMP PREEMPT Wed Nov 4 12:34:19 CST 2020
+#1 SMP PREEMPT Fri Feb 25 20:35:18 CST 2022
+#1 SMP PREEMPT Mon Jun 15 07:47:50 CST 2020
+#1 SMP PREEMPT Wed Jan 19 17:02:45 CST 2022
+#1 SMP PREEMPT Thu Apr 21 01:19:18 CST 2022
+#1 SMP PREEMPT Wed Feb 7 13:57:29 CST 2018
+#1 SMP PREEMPT Mon Jul 22 11:24:14 WIB 2019
+#1 SMP PREEMPT Wed Dec 1 19:08:40 CST 2021
+#2 SMP PREEMPT Mon May 14 18:23:24 CST 2018
+#1 SMP PREEMPT Wed Oct 3 23:38:41 PDT 2018
+#1 SMP PREEMPT Wed Dec 6 10:46:03 CST 2017
+#1 SMP PREEMPT Sun Apr 14 21:35:38 PDT 2019
+#2 SMP PREEMPT Tue Jun 15 19:14:12 KST 2021
+#2 SMP PREEMPT Thu Aug 22 22:16:40 EDT 2019
+#1 SMP PREEMPT Thu Mar 10 18:46:19 UTC 2022
+#1 SMP PREEMPT Thu Jul 9 03:56:06 CST 2020
+#1 SMP PREEMPT Thu Feb 11 17:17:28 IST 2021
+#1 SMP PREEMPT Wed Jan 5 10:05:42 CST 2022
+#1 SMP PREEMPT Tue Jun 2 20:25:35 KST 2020
+#2 SMP PREEMPT Tue Oct 20 18:56:41 KST 2020
+#1 SMP PREEMPT Thu Nov 7 15:25:27 CST 2019
+#1 SMP PREEMPT Thu Jul 2 20:10:22 CST 2020
+#1 SMP PREEMPT Fri May 7 22:02:21 KST 2021
+#1 SMP PREEMPT Thu Apr 15 00:30:25 CST 2021
+#1 SMP PREEMPT Tue Dec 28 01:00:51 CST 2021
+#13 SMP PREEMPT Mon Jul 13 15:47:46 CST 2020
+#1 SMP PREEMPT Wed Jan 12 22:20:57 CST 2022
+#1 SMP PREEMPT Thu Jan 20 02:23:15 CST 2022
+#1 SMP PREEMPT Fri Aug 20 00:54:00 CST 2021
+#1 SMP PREEMPT Wed Aug 18 10:47:50 CST 2021
+#1 SMP PREEMPT Thu Sep 20 12:47:16 BRT 2018
+#1 SMP PREEMPT Wed Aug 1 01:15:20 CST 2018
+#1 SMP PREEMPT Wed Apr 3 09:06:51 CST 2019
+#1 SMP PREEMPT Wed Aug 29 21:22:36 CST 2018
+#1 SMP PREEMPT Sat Nov 7 22:14:10 CST 2020
+#1 SMP PREEMPT Tue Apr 26 22:15:13 CST 2022
+#2 SMP PREEMPT Fri Sep 27 12:37:45 CST 2019
+#1 SMP PREEMPT Sat Jan 11 14:25:26 CST 2020
+#2 SMP PREEMPT Thu Oct 22 16:56:58 KST 2020
+#1 SMP PREEMPT Thu Jan 11 21:37:21 JST 2018
+#1 SMP PREEMPT Sun Jun 7 03:12:31 CST 2020
+#1 SMP PREEMPT Tue Jul 13 03:15:15 CST 2021
+#1 SMP Wed Nov 29 07:40:05 UTC 2017
+#1 SMP PREEMPT Tue Jan 2 23:29:12 CST 2018
+#1 SMP PREEMPT Mon May 21 18:43:23 BRT 2018
+#1 SMP PREEMPT Fri Apr 30 15:52:26 CST 2021
+#1 SMP PREEMPT Wed Oct 24 16:29:21 CST 2018
+#2 SMP PREEMPT Sat Dec 19 14:05:58 CST 2020
+#1 SMP PREEMPT Tue Mar 22 18:53:12 CST 2022
+#1 SMP PREEMPT Fri Aug 14 22:41:54 CST 2020
+#1 SMP PREEMPT Fri Sep 27 14:15:01 CDT 2019
+#1 SMP PREEMPT Wed Aug 15 17:35:26 CST 2018
+#1 SMP PREEMPT Fri Feb 14 10:23:47 CST 2020
+#1 SMP PREEMPT Fri Mar 9 15:29:13 CST 2018
+#1 SMP PREEMPT Mon Mar 29 15:25:23 KST 2021
+#1 SMP PREEMPT Sun Apr 24 17:24:22 CST 2022
+#1 SMP PREEMPT Sat Oct 9 20:27:12 CST 2021
+#1 SMP PREEMPT Sat May 7 00:20:35 CST 2022
+#1 SMP PREEMPT Fri May 17 18:04:47 CST 2019
+#1 SMP PREEMPT Fri Oct 22 17:49:16 CST 2021
+#1 SMP PREEMPT Wed Jan 12 17:59:02 CST 2022
+#2 SMP PREEMPT Mon Jun 1 04:03:20 CST 2020
+#1 SMP PREEMPT Mon Jul 30 16:46:41 CST 2018
+#1 SMP PREEMPT Sat Jan 8 14:25:28 CST 2022
+#1 SMP PREEMPT Fri Jul 12 22:49:08 CST 2019
+#1 SMP PREEMPT Sun Sep 30 12:37:41 CST 2018
+#1 SMP PREEMPT Sat Dec 11 21:11:15 CST 2021
+#1 SMP PREEMPT Thu Mar 10 01:39:13 IST 2022
+#1 SMP PREEMPT Wed Jun 13 22:18:55 PDT 2018
+#1 SMP PREEMPT Fri May 20 15:47:08 KST 2022
+#1 SMP PREEMPT Thu Jul 15 16:23:36 CST 2021
+#1 SMP PREEMPT Thu Jun 20 17:25:16 KST 2019
+#1 SMP PREEMPT Wed Apr 7 16:00:10 KST 2021
+#1 SMP PREEMPT Thu Dec 21 21:29:13 CST 2017
+#1 SMP PREEMPT Fri Oct 16 23:02:24 CST 2020
+#1 SMP PREEMPT Wed Apr 7 09:23:26 KST 2021
+#1 SMP PREEMPT Mon Aug 5 17:50:49 KST 2019
+#1 SMP PREEMPT Tue Nov 17 11:49:49 CST 2020
+#1 SMP PREEMPT Wed Sep 12 16:52:12 BRT 2018
+#2 SMP PREEMPT Tue May 7 21:57:39 KST 2019
+#1 SMP PREEMPT Mon May 20 11:06:14 IST 2019
+#1 SMP PREEMPT Sat May 9 13:48:59 IST 2020
+#1 SMP PREEMPT Mon Jan 4 18:54:16 CST 2021
+#1 SMP PREEMPT Thu Apr 18 14:38:06 EDT 2019
+#1 SMP PREEMPT Thu Mar 18 20:23:42 CST 2021
+#98 SMP PREEMPT Thu Nov 16 18:13:20 CST 2017
+#1 SMP PREEMPT Tue Dec 5 20:15:07 CST 2017
+#1 SMP PREEMPT Mon Aug 3 21:25:47 CST 2020
+#1 SMP PREEMPT Wed Oct 28 22:40:58 CST 2020
+#1 SMP PREEMPT Fri Nov 9 10:47:35 KST 2018
+#1 SMP PREEMPT Fri Dec 21 17:53:16 CST 2018
+#2 SMP PREEMPT Wed Nov 14 21:01:06 CST 2018
+#1 SMP PREEMPT Fri Apr 15 08:48:59 CST 2022
+#1 SMP PREEMPT Thu Feb 10 23:51:08 CST 2022
+#1 SMP PREEMPT Fri Dec 21 14:43:02 CST 2018
+#1 SMP PREEMPT Mon Jul 6 13:43:49 CDT 2020
+#1 SMP PREEMPT Fri Jul 16 19:01:53 CST 2021
+#1 SMP PREEMPT Wed Sep 16 23:36:49 CST 2020
+#1 SMP PREEMPT Fri Dec 20 21:43:09 CST 2019
+#1 SMP PREEMPT Fri Nov 29 17:29:30 GMT 2019
+#1 SMP PREEMPT Mon Jan 27 02:16:02 JST 2020
+#1 SMP PREEMPT Mon Jun 25 20:36:41 KST 2018
+#1 SMP PREEMPT Thu Jun 21 22:45:11 WIB 2018
+#1 SMP PREEMPT Fri Sep 7 00:55:31 PDT 2018
+#1 SMP PREEMPT Fri Jul 3 01:09:48 CST 2020
+#7 SMP PREEMPT Fri May 14 11:39:51 CST 2021
+#1 SMP PREEMPT Thu Nov 29 20:16:18 CST 2018
+#1 SMP PREEMPT Tue Nov 6 00:44:52 KST 2018
+#1 SMP PREEMPT Sat Oct 17 04:43:35 CDT 2020
+#1 SMP PREEMPT Tue Jun 25 20:56:59 CST 2019
+#1 SMP PREEMPT Fri Apr 9 15:36:41 CST 2021
+#1 SMP PREEMPT Thu Jan 7 10:03:23 IST 2021
+#1 SMP PREEMPT Tue Dec 15 19:02:49 CST 2020
+#33 SMP PREEMPT Mon Jul 30 15:08:47 CST 2018
+#1 SMP PREEMPT Thu Jul 22 13:23:33 KST 2021
+#2 SMP PREEMPT Mon Dec 16 12:52:00 CST 2019
+#1 SMP PREEMPT Fri May 4 17:41:09 CST 2018
+#1 SMP PREEMPT Fri Feb 12 15:20:36 CST 2021
+#1 SMP PREEMPT Tue Dec 25 17:42:24 -07 2018
+#1 SMP PREEMPT Tue Feb 22 22:15:42 CST 2022
+#1 SMP PREEMPT Tue Jan 4 20:24:37 CST 2022
+#2 SMP PREEMPT Sat Jun 29 22:52:25 CST 2019
+#1 SMP PREEMPT Tue Mar 30 19:37:15 KST 2021
+#1 SMP PREEMPT Mon Jul 1 18:57:10 CST 2019
+#1 SMP PREEMPT Sun Apr 10 03:59:30 CST 2022
+#1 SMP PREEMPT Sun Nov 15 23:13:14 CST 2020
+#1 SMP PREEMPT Thu Dec 2 18:41:39 CST 2021
+#1 SMP PREEMPT Fri Dec 7 14:54:13 CST 2018
+#2 SMP PREEMPT Mon Jul 2 11:31:11 CST 2018
+#1 SMP PREEMPT Tue Jun 15 11:41:34 CST 2021
+#1 SMP PREEMPT Thu Feb 21 01:45:05 CST 2019
+#1 SMP PREEMPT Sat Dec 11 16:20:49 CST 2021
+#1 SMP PREEMPT Mon Feb 5 16:46:14 CST 2018
+#1 SMP PREEMPT Sat Oct 31 21:35:06 CST 2020
+#2 SMP PREEMPT Fri Sep 25 00:46:03 KST 2020
+#1 SMP PREEMPT Fri May 14 11:20:39 CST 2021
+#1 SMP PREEMPT Wed Nov 10 12:20:04 CST 2021
+#1 SMP PREEMPT Fri Nov 22 17:21:07 CST 2019
+#1 SMP PREEMPT Tue Oct 1 17:54:57 KST 2019
+#1 SMP PREEMPT Mon Oct 8 10:21:03 KST 2018
+#1 SMP PREEMPT Wed Dec 13 09:55:47 CST 2017
+#1 SMP PREEMPT Fri Jan 21 17:24:02 CST 2022
+#1 SMP PREEMPT Thu Dec 12 22:48:05 PST 2019
+#1 SMP PREEMPT Wed Apr 29 22:30:06 CST 2020
+#2 SMP PREEMPT Wed Jan 20 18:46:42 KST 2021
+#1 SMP PREEMPT Tue Jun 11 18:29:26 KST 2019
+#1 SMP PREEMPT Tue Sep 11 00:33:42 CST 2018
+#1 SMP PREEMPT Wed Apr 15 01:31:05 PDT 2020
+#1 SMP PREEMPT Fri May 21 22:49:18 CST 2021
+#1 SMP PREEMPT Mon May 3 18:00:17 KST 2021
+#1 SMP PREEMPT Wed Dec 6 22:17:23 UTC 2017
+#1 SMP PREEMPT Wed Nov 7 10:54:02 KST 2018
+#1 SMP PREEMPT Tue Aug 3 21:00:25 CST 2021
+#1 SMP PREEMPT Wed Jul 22 14:51:57 CST 2020
+#1 SMP PREEMPT Mon Nov 4 11:52:40 CST 2019
+#1 SMP PREEMPT Thu Jan 16 14:03:00 CST 2020
+#1 SMP PREEMPT Wed May 23 02:45:23 CST 2018
+#1 SMP PREEMPT Sun Jul 8 20:23:38 CDT 2018
+#1 SMP PREEMPT Tue Jan 21 17:31:28 KST 2020
+#1 SMP PREEMPT Mon May 16 10:06:52 UTC 2022
+#1 SMP PREEMPT Sun Mar 24 02:25:32 CST 2019
+#1 SMP PREEMPT Tue Jun 26 00:25:23 CST 2018
+#2 SMP PREEMPT Mon Jul 9 17:26:20 CST 2018
+#1 SMP PREEMPT Mon Apr 9 19:16:14 IST 2018
+#1 SMP Sat May 9 12:21:46 PDT 2020
+#1 SMP PREEMPT Mon Dec 11 12:52:31 CST 2017
+#1 SMP PREEMPT Tue Jan 18 12:31:32 CST 2022
+#2 SMP PREEMPT Wed Mar 16 21:37:14 CST 2022
+#1 SMP PREEMPT Thu May 14 06:01:17 CST 2020
+#1 SMP PREEMPT Tue Jul 31 22:54:18 PDT 2018
+#1 SMP PREEMPT Thu Dec 26 11:15:35 +07 2019
+#1 SMP PREEMPT Thu Jan 18 11:26:02 JST 2018
+#1 SMP PREEMPT Wed Jan 31 03:57:22 CST 2018
+#1 SMP PREEMPT Wed Feb 13 03:30:41 PST 2019
+#1 SMP PREEMPT Sun Jan 27 09:59:49 CST 2019
+#2 SMP PREEMPT Wed Feb 27 13:38:34 CST 2019
+#2 SMP PREEMPT Tue May 14 12:50:05 CST 2019
+#1 SMP PREEMPT Thu Jul 26 03:41:50 CST 2018
+#2 SMP PREEMPT Wed Feb 12 20:02:06 CST 2020
+#1 SMP PREEMPT Wed Aug 8 01:00:18 CST 2018
+#1 SMP PREEMPT Wed Jun 6 15:31:09 CST 2018
+#1 SMP PREEMPT Tue Mar 20 21:16:58 CST 2018
+#1 SMP PREEMPT Fri Dec 15 14:49:46 KST 2017
+#1 SMP PREEMPT Tue Jun 2 00:41:36 PDT 2020
+#1 SMP PREEMPT Tue Mar 19 14:27:30 KST 2019
+#1 SMP PREEMPT Sun Apr 28 01:45:58 CST 2019
+#1 SMP PREEMPT Sat Jan 15 01:00:26 UTC 2022
+#1 SMP PREEMPT Tue Jan 8 07:21:37 CST 2019
+#1 SMP PREEMPT Wed Oct 21 12:48:00 CST 2020
+#1 SMP PREEMPT Fri Apr 30 00:11:01 CST 2021
+#1 SMP PREEMPT Sat Feb 27 12:57:40 CST 2021
+#1 SMP PREEMPT Thu Dec 26 17:08:55 CST 2019
+#1 SMP PREEMPT Thu Oct 31 17:56:09 CST 2019
+#1 SMP PREEMPT Tue Jul 3 20:12:15 2018
+#1 SMP PREEMPT Fri Mar 22 00:27:32 KST 2019
+#2 SMP PREEMPT Thu Sep 3 14:42:00 CST 2020
+#1 SMP PREEMPT Wed Oct 10 17:50:53 2018
+#1 SMP PREEMPT Fri May 8 17:48:19 2020
+#1 SMP PREEMPT Sat Feb 1 01:51:59 KST 2020
+#1 SMP PREEMPT Tue Aug 10 00:58:12 CST 2021
+#5 SMP PREEMPT Sat Mar 30 01:46:13 CST 2019
+#1 SMP PREEMPT Mon Nov 15 17:07:20 CST 2021
+#1 SMP PREEMPT Fri Feb 11 14:28:52 CST 2022
+#1 SMP PREEMPT Wed Nov 18 22:34:16 CST 2020
+#2 SMP PREEMPT Thu Dec 3 10:08:32 IST 2020
+#1 SMP PREEMPT Sat Dec 19 15:01:23 CST 2020
+#1 SMP PREEMPT Fri Sep 21 21:11:29 CST 2018
+#1 SMP PREEMPT Fri Feb 22 17:21:20 CST 2019
+#1 SMP PREEMPT Tue Oct 12 21:24:09 CST 2021
+#1 SMP PREEMPT Tue Oct 12 17:50:34 CST 2021
+#1 SMP PREEMPT Mon Apr 26 12:33:41 CST 2021
+#2 SMP PREEMPT Thu Mar 1 19:23:17 CST 2018
+#1 SMP PREEMPT Mon Apr 11 15:47:29 KST 2022
+#1 SMP PREEMPT Mon Jul 6 12:37:45 CST 2020
+#1 SMP PREEMPT Fri Jan 10 22:55:33 CST 2020
+#1 SMP PREEMPT Wed Sep 4 17:36:49 CST 2019
+#1 SMP PREEMPT Mon Jun 7 23:10:49 KST 2021
+#1 SMP PREEMPT Fri Sep 10 09:40:30 CST 2021
+#1 SMP PREEMPT Fri Jan 29 21:49:50 CST 2021
+#1 SMP PREEMPT Fri Aug 3 10:20:47 CST 2018
+#1 SMP PREEMPT Tue Oct 9 13:52:00 CDT 2018
+#2 SMP PREEMPT Thu Mar 5 11:11:06 KST 2020
+#1 SMP PREEMPT Tue Jun 4 19:06:46 CST 2019
+#2 SMP PREEMPT Sun Apr 25 07:51:14 KST 2021
+#2 SMP PREEMPT Tue Aug 11 23:01:32 CST 2020
+#1 SMP PREEMPT Mon Aug 24 04:13:41 GMT 2020
+#2 SMP PREEMPT Thu Apr 2 03:13:09 CST 2020
+#1 SMP PREEMPT Wed Aug 8 13:43:42 CST 2018
+#1 SMP PREEMPT Thu Feb 24 18:33:42 CST 2022
+#1 SMP PREEMPT Mon Dec 31 18:13:00 IST 2018
+#1 SMP PREEMPT Sat Sep 19 12:34:18 IST 2020
+#1 SMP PREEMPT Wed Nov 28 00:40:01 CST 2018
+#1 SMP PREEMPT Sat May 21 03:15:27 JST 2022
+#1 SMP PREEMPT Tue Aug 14 22:07:06 CST 2018
+#1 SMP PREEMPT Mon Jun 8 03:55:05 PDT 2020
+#1 SMP PREEMPT Thu May 6 20:14:36 CST 2021
+#1 SMP PREEMPT Thu Jul 19 21:33:23 CST 2018
+#1 SMP PREEMPT Wed Nov 7 11:32:10 KST 2018
+#1 SMP PREEMPT Tue Jun 30 12:27:27 CST 2020
+#1 SMP PREEMPT Wed Oct 9 03:58:52 CST 2019
+#1 SMP PREEMPT Tue Jul 3 23:18:04 CST 2018
+#1 SMP PREEMPT Wed Aug 8 19:03:07 JST 2018
+#1 SMP PREEMPT Tue Dec 11 23:51:33 PST 2018
+#2 SMP PREEMPT Wed Apr 8 14:12:24 UTC 2020
+#2 SMP PREEMPT Fri Jun 25 17:28:04 CST 2021
+#1 SMP PREEMPT Thu Jun 13 05:29:01 CST 2019
+#1 SMP PREEMPT Thu Feb 24 18:39:58 CST 2022
+#2 SMP PREEMPT Thu Mar 7 18:12:58 CST 2019
+#1 SMP PREEMPT Mon Sep 30 01:28:11 CST 2019
+#1 SMP PREEMPT Fri Sep 24 01:55:05 CST 2021
+#1 SMP PREEMPT Mon Apr 27 13:32:30 CDT 2020
+#1 SMP PREEMPT Fri Sep 20 04:57:49 CST 2019
+#1 SMP PREEMPT Fri Mar 27 17:46:38 CST 2020
+#1 SMP PREEMPT Thu May 26 19:28:58 CST 2022
+#1 SMP PREEMPT Tue Feb 12 11:01:24 KST 2019
+#1 SMP PREEMPT Mon Mar 7 14:49:29 CST 2022
+#1 SMP PREEMPT Sat May 1 17:48:38 PDT 2021
+#1 SMP PREEMPT Fri Jul 9 22:09:40 CST 2021
+#1 SMP PREEMPT Wed Jul 29 16:54:30 CST 2020
+#1 SMP PREEMPT Fri Aug 9 00:29:17 CST 2019
+#1 SMP PREEMPT Fri May 18 21:29:32 CST 2018
+#1 SMP PREEMPT Mon Dec 27 21:24:34 CST 2021
+#1 SMP PREEMPT Mon Dec 3 16:45:11 KST 2018
+#1 SMP PREEMPT Mon Mar 1 08:42:10 IST 2021
+#1 SMP PREEMPT Fri Feb 5 15:59:15 JST 2021
+#1 SMP PREEMPT Sun Oct 11 18:20:46 CST 2020
+#1 SMP PREEMPT Mon Sep 23 15:06:32 CDT 2019
+#1 SMP PREEMPT Sun Jun 28 13:19:42 CST 2020
+#28 SMP PREEMPT Wed Sep 30 13:13:19 CST 2020
+#1 SMP PREEMPT Thu Jan 7 11:33:15 CST 2021
+#1 SMP PREEMPT Sat May 7 13:59:38 UTC 2022
+#2 SMP PREEMPT Tue Dec 15 23:25:05 CST 2020
+#1 SMP PREEMPT Sat Oct 10 03:34:16 CDT 2020
+#2 SMP PREEMPT Wed Dec 15 12:09:37 CST 2021
+#2 SMP PREEMPT Mon Jul 27 12:53:39 CST 2020
+#1 SMP PREEMPT Mon Dec 23 12:01:26 2019
+#1 SMP PREEMPT Sat Feb 16 11:35:50 CST 2019
+#1 SMP PREEMPT Thu Dec 6 04:08:09 CST 2018
+#1 SMP PREEMPT Mon Dec 30 20:54:15 CST 2019
+#1 SMP PREEMPT Wed Apr 7 16:44:52 CST 2021
+#1 SMP PREEMPT Fri Mar 4 15:31:25 CST 2022
+#2 SMP PREEMPT Thu Nov 30 00:16:43 CST 2017
+#2 SMP PREEMPT Wed Jun 19 13:57:56 WIB 2019
+#1 SMP PREEMPT Thu Feb 10 17:02:37 CST 2022
+#1 SMP PREEMPT Wed Oct 21 20:45:29 CST 2020
+#1 SMP PREEMPT Wed Apr 20 13:13:40 CST 2022
+#1 SMP PREEMPT Tue Mar 26 16:56:45 CST 2019
+#1 SMP PREEMPT Fri Oct 18 19:53:10 CST 2019
+#1 SMP PREEMPT Fri Apr 24 13:36:11 HKT 2020
+#1 SMP PREEMPT Mon Feb 28 18:15:06 CST 2022
+#1 SMP PREEMPT Wed Mar 6 00:25:29 PST 2019
+#2 SMP Mon Oct 22 16:02:21 CST 2018
+#1 SMP PREEMPT Sat Dec 18 12:21:41 CST 2021
+#2 SMP PREEMPT Thu Jul 23 22:30:06 CST 2020
+#1 SMP PREEMPT Mon Jan 7 19:24:18 CST 2019
+#1 SMP PREEMPT Tue Sep 18 19:51:43 CST 2018
+#1 SMP PREEMPT Tue Dec 14 16:27:56 CST 2021
+#1 SMP PREEMPT Tue Dec 8 00:48:28 CST 2020
+#1 SMP PREEMPT Wed Nov 7 13:50:22 CST 2018
+#1 SMP PREEMPT Fri Apr 26 00:52:42 CST 2019
+#1 SMP PREEMPT Wed Mar 27 12:33:10 CDT 2019
+#1 SMP PREEMPT Fri Aug 17 18:27:24 IST 2018
+#1 SMP PREEMPT Thu Mar 10 10:52:20 CST 2022
+#1 SMP PREEMPT Tue Mar 27 08:52:49 KST 2018
+#1 SMP PREEMPT Mon Jul 16 18:29:59 EDT 2018
+#1 SMP PREEMPT Thu Jul 23 04:05:38 CST 2020
+#1 SMP PREEMPT Thu Oct 15 13:00:06 KST 2020
+#1 SMP PREEMPT Mon Jan 25 12:32:23 CST 2021
+#1 SMP PREEMPT Thu Dec 26 07:47:50 CST 2019
+#1 SMP PREEMPT Wed Apr 14 20:05:01 CST 2021
+#1 SMP PREEMPT Sat Sep 18 15:15:58 CST 2021
+#2 SMP PREEMPT Sun Dec 22 12:50:11 CST 2019
+#3 SMP PREEMPT Fri Apr 24 18:16:58 CST 2020
+#1 SMP PREEMPT Sat Apr 17 11:51:16 CST 2021
+#1 SMP PREEMPT Mon Nov 29 20:52:02 CST 2021
+#1 SMP PREEMPT Wed Jan 13 00:35:27 CST 2021
+#1 SMP PREEMPT Fri Oct 30 17:51:00 KST 2020
+#1 SMP PREEMPT Mon Jan 7 23:16:15 EST 2019
+#1 SMP PREEMPT Tue May 15 21:32:11 CST 2018
+#1 SMP PREEMPT Thu Jun 13 23:16:46 KST 2019
+#1 SMP PREEMPT Wed Sep 25 03:24:24 HKT 2019
+#1 SMP PREEMPT Wed Oct 16 19:31:34 JST 2019
+#2 SMP PREEMPT Fri Nov 29 09:03:09 KST 2019
+#1 SMP PREEMPT Fri Jan 17 00:22:07 CST 2020
+#1 SMP PREEMPT Mon Apr 2 16:59:42 KST 2018
+#1 SMP PREEMPT Mon Jul 6 20:44:28 WIB 2020
+#1 SMP PREEMPT Wed Sep 29 06:09:54 CST 2021
+#1 SMP PREEMPT Wed Dec 18 12:19:37 KST 2019
+#1 SMP PREEMPT Mon Oct 26 13:59:44 CST 2020
+#1 SMP PREEMPT Mon Sep 6 00:55:38 CST 2021
+#1 SMP PREEMPT Thu May 13 12:27:49 CST 2021
+#1 SMP PREEMPT Wed May 18 21:44:16 CST 2022
+#1 SMP PREEMPT Mon Apr 18 20:42:03 CST 2022
+#1 SMP PREEMPT Fri Jan 19 05:59:03 CST 2018
+#1 SMP PREEMPT Fri Mar 15 19:36:26 2019
+#1 SMP PREEMPT Fri Mar 1 16:03:39 CST 2019
+#1 SMP PREEMPT Wed Dec 11 09:43:09 CST 2019
+#1 SMP PREEMPT Fri Jun 8 18:21:03 KST 2018
+#1 SMP PREEMPT Thu Nov 18 21:41:37 CST 2021
+#2 SMP PREEMPT Fri Feb 18 14:42:14 CST 2022
+#1 SMP PREEMPT Fri Sep 27 16:24:12 CST 2019
+#1 SMP PREEMPT Thu Apr 8 19:36:09 HKT 2021
+#1 SMP PREEMPT Thu Jul 9 14:48:21 CST 2020
+#2 SMP PREEMPT Thu Sep 13 19:29:19 CST 2018
+#1 SMP PREEMPT Wed Oct 31 20:23:33 KST 2018
+#1 SMP PREEMPT Mon Dec 20 15:21:31 CST 2021
+#1 SMP PREEMPT Mon Oct 19 13:46:23 CST 2020
+#1 SMP PREEMPT Tue Nov 28 15:10:06 KST 2017
+#1 SMP PREEMPT Mon Nov 5 01:29:51 CST 2018
+#1 SMP PREEMPT Wed Feb 16 13:26:39 CST 2022
+#1 SMP PREEMPT Fri Feb 1 10:35:57 CST 2019
+#1 SMP PREEMPT Sat Jun 12 02:58:58 CST 2021
+#2 SMP PREEMPT Mon Jun 15 20:14:24 CST 2020
+#1 SMP PREEMPT Fri Feb 18 20:03:36 CST 2022
+#1 SMP PREEMPT Fri Mar 18 10:25:14 CST 2022
+#1 SMP PREEMPT Wed May 19 01:45:03 JST 2021
+#1 SMP PREEMPT Wed May 19 01:37:06 IST 2021
+#3 SMP Thu Feb 27 20:52:06 CET 2020
+#1 SMP PREEMPT Fri Jul 20 12:13:36 CST 2018
+#1 SMP PREEMPT Tue Jan 7 00:44:35 CST 2020
+#1 SMP PREEMPT Wed Jun 19 01:51:01 CST 2019
+#1 SMP PREEMPT Wed Mar 2 11:06:07 CST 2022
+#1 SMP PREEMPT Fri Jul 12 15:44:54 CST 2019
+#1 SMP PREEMPT Mon Mar 28 10:22:22 UTC 2022
+#2 SMP PREEMPT Fri Apr 17 10:25:43 KST 2020
+#1 SMP PREEMPT Thu Mar 22 01:57:21 JST 2018
+#1 SMP PREEMPT Fri Oct 15 11:35:22 CST 2021
+#1 SMP PREEMPT Tue Mar 2 18:38:40 CST 2021
+#7 SMP PREEMPT Tue Nov 30 14:21:52 CST 2021
+#1 SMP PREEMPT Mon Oct 14 18:42:54 KST 2019
+#1 SMP PREEMPT Fri Mar 29 19:08:06 CST 2019
+#1 SMP PREEMPT Sat Nov 10 16:41:01 CST 2018
+#1 SMP PREEMPT Tue Aug 3 21:58:55 PDT 2021
+#2 SMP PREEMPT Fri Apr 12 16:08:32 CST 2019
+#1 SMP PREEMPT Wed Jun 17 04:53:07 CST 2020
+#1 SMP PREEMPT Fri Feb 26 11:05:28 JST 2021
+#1 SMP PREEMPT Fri Mar 27 20:51:50 KST 2020
+#2 SMP PREEMPT Tue Sep 7 18:48:38 KST 2021
+#1 SMP PREEMPT Fri Oct 23 10:30:28 CST 2020
+#2 SMP PREEMPT Wed Oct 23 19:53:33 KST 2019
+#1 SMP PREEMPT Fri Sep 6 11:54:10 CST 2019
+#1 SMP PREEMPT Fri May 24 22:35:53 KST 2019
+#1 SMP PREEMPT Tue Mar 26 10:12:14 CST 2019
+#2 SMP PREEMPT Fri May 29 00:11:02 CST 2020
+#2 SMP Fri Apr 16 16:22:11 UTC 2021
+#1 SMP PREEMPT Thu Dec 10 19:20:53 PST 2020
+#1 SMP PREEMPT Tue Mar 12 19:20:37 CST 2019
+#1 SMP PREEMPT Fri Nov 5 11:54:28 CST 2021
+#1 SMP PREEMPT Fri Apr 30 13:32:20 CST 2021
+#1 SMP PREEMPT Wed Jan 10 13:23:35 CST 2018
+#1 SMP PREEMPT Thu Nov 18 13:23:21 PST 2021
+#1 SMP PREEMPT Wed Jul 25 23:20:10 PDT 2018
+#1 SMP PREEMPT Fri Nov 29 17:53:12 CST 2019
+#2 SMP PREEMPT Tue Dec 18 14:30:32 CST 2018
+#2 SMP PREEMPT Fri Jun 28 16:06:41 CST 2019
+#1 SMP PREEMPT Wed Nov 6 17:49:01 2019
+#1 SMP PREEMPT Wed Dec 19 12:51:49 CST 2018
+#1 SMP PREEMPT Sat Aug 22 13:49:38 CST 2020
+#1 SMP PREEMPT Tue Sep 18 14:57:46 CST 2018
+#2 SMP PREEMPT Sat Jul 11 17:04:49 KST 2020
+#1 SMP PREEMPT Mon Dec 18 10:43:53 KST 2017
+#1 SMP PREEMPT Tue Apr 12 00:41:45 CST 2022
+#1 SMP PREEMPT Wed Apr 25 19:46:06 CST 2018
+#2 SMP PREEMPT Fri Jul 27 10:49:20 CST 2018
+#1 SMP PREEMPT Tue Dec 19 10:55:47 CST 2017
+#1 SMP PREEMPT Thu Jun 25 01:52:09 KST 2020
+#1 SMP PREEMPT Wed Mar 30 23:24:03 CST 2022
+#1 SMP PREEMPT Tue Mar 22 16:08:51 CST 2022
+#1 SMP PREEMPT Mon Jun 15 15:55:39 CST 2020
+#2 SMP PREEMPT Fri Dec 6 16:11:14 CST 2019
+#1 SMP PREEMPT Tue Aug 28 11:40:51 -03 2018
+#1 SMP PREEMPT Wed Feb 20 10:48:27 CST 2019
+#1 SMP PREEMPT Fri Apr 2 14:50:35 KST 2021
+#1 SMP PREEMPT Wed Dec 11 16:33:16 CST 2019
+#1 SMP PREEMPT Tue Jan 7 02:35:34 KST 2020
+#1 SMP PREEMPT Thu Mar 15 17:05:42 CST 2018
+#1 SMP PREEMPT Mon Nov 12 12:18:01 CST 2018
+#1 SMP PREEMPT Mon Jan 29 18:46:15 CST 2018
+#2 SMP PREEMPT Wed Jan 30 23:09:49 KST 2019
+#1 SMP PREEMPT Wed Nov 28 20:32:08 CST 2018
+#1 SMP PREEMPT Fri Feb 26 03:16:39 CST 2021
+#1 SMP PREEMPT Sat Apr 25 15:47:22 CST 2020
+#1 SMP PREEMPT Wed Apr 25 21:51:25 CST 2018
+#1 SMP PREEMPT Wed Jan 15 00:09:03 PST 2020
+#2 SMP PREEMPT Fri Apr 2 17:57:39 HKT 2021
+#1 SMP PREEMPT Sun Feb 20 21:11:27 CST 2022
+#2 SMP PREEMPT Tue Mar 3 17:09:19 KST 2020
+#2 SMP PREEMPT Mon Aug 24 21:41:47 CST 2020
+#1 SMP PREEMPT Wed Jun 20 12:13:36 CST 2018
+#1 SMP PREEMPT Thu Mar 15 14:07:50 CST 2018
+#1 SMP PREEMPT Mon Dec 6 16:57:47 CST 2021
+#1 SMP PREEMPT Thu Jun 14 16:44:12 KST 2018
+#1 SMP PREEMPT Sat Dec 29 12:03:12 CST 2018
+#2 SMP PREEMPT Tue May 28 00:27:11 CST 2019
+#1 SMP PREEMPT Mon Mar 22 12:17:39 CST 2021
+#1 SMP PREEMPT Thu Mar 31 05:52:10 PDT 2022
+#1 SMP PREEMPT Tue Jan 25 18:27:44 KST 2022
+#1 SMP PREEMPT Wed Feb 3 10:45:20 CST 2021
+#1 SMP PREEMPT Tue May 11 20:08:58 CST 2021
+#1 SMP PREEMPT Mon May 20 15:33:06 CST 2019
+#1 SMP PREEMPT Thu Jul 4 20:17:03 JST 2019
+#1 SMP PREEMPT Thu Mar 18 09:29:56 KST 2021
+#1 SMP PREEMPT Thu Oct 18 06:05:22 CST 2018
+#1 SMP PREEMPT Fri Apr 9 23:22:23 CST 2021
+#2 SMP PREEMPT Fri Jul 24 18:12:26 KST 2020
+#4 SMP PREEMPT Wed Jul 11 00:28:08 CST 2018
+#1 SMP PREEMPT Mon Jul 19 21:16:21 KST 2021
+#1 SMP PREEMPT Thu Mar 8 09:59:10 CST 2018
+#1 SMP PREEMPT Tue Dec 25 12:09:33 CST 2018
+#1 SMP PREEMPT Thu Oct 11 01:20:41 PDT 2018
+#1 SMP PREEMPT Sat Feb 15 18:49:21 KST 2020
+#1 SMP PREEMPT Wed Nov 7 16:47:59 CST 2018
+#29 SMP PREEMPT Fri May 7 15:54:07 CST 2021
+#1 SMP PREEMPT Tue Jul 3 12:33:51 CST 2018
+#1 SMP PREEMPT Tue Feb 22 21:02:55 CST 2022
+#1 SMP PREEMPT Thu Feb 15 02:10:07 JST 2018
+#1 SMP PREEMPT Wed Jun 10 20:37:07 CST 2020
+#1 SMP PREEMPT Tue Sep 14 12:04:16 IST 2021
+#1 SMP PREEMPT Tue Jan 8 17:51:13 CST 2019
+#1 SMP PREEMPT Fri Jul 26 17:44:16 CST 2019
+#2 SMP PREEMPT Fri Jan 7 13:09:59 CST 2022
+#1 SMP PREEMPT Wed Nov 25 06:25:05 CST 2020
+#1 SMP PREEMPT Wed Oct 30 22:09:21 CST 2019
+#1 SMP PREEMPT Thu Apr 23 01:51:28 KST 2020
+#1 SMP PREEMPT Tue Jan 14 12:38:27 CST 2020
+#1 SMP PREEMPT Fri Mar 13 16:47:08 CST 2020
+#1 SMP PREEMPT Wed Sep 9 22:17:41 CST 2020
+#1 SMP PREEMPT Wed Aug 22 02:24:00 PDT 2018
+#1 SMP PREEMPT Sat Dec 22 09:23:28 CST 2018
+#1 SMP PREEMPT Mon May 10 15:34:51 CST 2021
+#1 SMP PREEMPT Mon May 27 17:54:22 KST 2019
+#2 SMP PREEMPT Thu Sep 16 20:51:59 IST 2021
+#1 SMP PREEMPT Wed Jul 11 18:06:35 CST 2018
+#6 SMP PREEMPT Tue Jun 15 14:02:22 CST 2021
+#1 SMP PREEMPT Thu Oct 31 04:07:39 CST 2019
+#1 SMP PREEMPT Tue Mar 27 18:24:52 CST 2018
+#1 SMP PREEMPT Fri Sep 18 18:29:07 CST 2020
+#1 SMP PREEMPT Fri Mar 6 10:51:43 WIB 2020
+#2 SMP Mon Nov 25 14:49:19 IST 2019
+#1 SMP PREEMPT Mon Jan 29 17:46:51 CST 2018
+#1 SMP PREEMPT Sat Dec 19 21:08:32 CST 2020
+#2 SMP PREEMPT Fri Sep 27 00:03:19 CST 2019
+#1 SMP PREEMPT Thu Jul 26 17:20:49 CST 2018
+#5 SMP PREEMPT Fri May 13 13:45:26 UTC 2022
+#1 SMP PREEMPT Fri Dec 13 17:46:41 CST 2019
+#1 SMP PREEMPT Fri Jul 17 10:45:06 CST 2020
+#1 SMP PREEMPT Wed Oct 2 04:37:26 JST 2019
+#1 SMP PREEMPT Mon Jan 7 11:39:13 CST 2019
+#1 SMP PREEMPT Tue May 10 18:03:54 CST 2022
+#1 SMP PREEMPT Tue Dec 18 13:13:26 CST 2018
+#1 SMP PREEMPT Fri Apr 24 01:19:23 CST 2020
+#1 SMP PREEMPT Fri Nov 29 19:40:55 CST 2019
+#1 SMP PREEMPT Thu Mar 5 23:40:22 CST 2020
+#1 SMP PREEMPT Fri Mar 9 16:18:50 CST 2018
+#1 SMP PREEMPT Wed Jan 8 22:52:34 PST 2020
+#1 SMP PREEMPT Tue Apr 6 21:35:09 CST 2021
+#1 SMP PREEMPT Thu Mar 12 19:25:54 KST 2020
+#1 SMP PREEMPT Thu May 12 16:38:00 CST 2022
+#1 SMP PREEMPT Wed Aug 28 10:35:36 CST 2019
+#1 SMP PREEMPT Fri Jan 25 11:11:28 JST 2019
+#1 SMP PREEMPT Wed Dec 2 15:20:09 CST 2020
+#1 SMP PREEMPT Thu Jul 16 17:23:16 KST 2020
+#1 SMP PREEMPT Sat Dec 1 13:05:11 CST 2018
+#1 SMP PREEMPT Thu May 28 18:56:24 KST 2020
+#2 SMP PREEMPT Sat Jul 7 16:38:33 CST 2018
+#1 SMP PREEMPT Fri Jan 18 00:55:54 KST 2019
+#1 SMP PREEMPT Wed Jul 28 14:13:22 KST 2021
+#1 SMP PREEMPT Wed Mar 4 18:56:37 CST 2020
+#1 SMP PREEMPT Tue Dec 12 19:44:03 KST 2017
+#1 SMP PREEMPT Thu May 20 11:38:30 CST 2021
+#6 SMP PREEMPT Tue Oct 5 20:03:09 UTC 2021
+#1 SMP PREEMPT Tue May 12 13:08:02 JST 2020
+#1 SMP PREEMPT Sun May 20 01:14:34 CST 2018
+#1 SMP PREEMPT Thu Feb 10 11:08:30 CST 2022
+#1 SMP PREEMPT Thu Aug 13 17:02:07 CST 2020
+#1 SMP PREEMPT Mon Jan 10 06:58:42 UTC 2022
+#1 SMP PREEMPT Sun Jun 30 05:20:11 JST 2019
+#2 SMP PREEMPT Tue Apr 26 15:11:47 +07 2022
+#1 SMP PREEMPT Mon Dec 2 04:00:59 CST 2019
+#1 SMP PREEMPT Fri Dec 8 14:24:09 CST 2017
+#1 SMP PREEMPT Thu Mar 15 18:30:31 CST 2018
+#1 SMP PREEMPT Mon Jan 14 15:02:15 CST 2019
+#1 SMP PREEMPT Tue Oct 22 13:07:37 CDT 2019
+#1 SMP PREEMPT Thu Aug 29 03:37:50 CDT 2019
+#1 SMP PREEMPT Sat Nov 28 17:51:46 CST 2020
+#1 SMP PREEMPT Thu Mar 17 09:14:04 KST 2022
+#1 SMP PREEMPT Wed Feb 23 19:20:59 CST 2022
+#1 SMP PREEMPT Wed Sep 30 07:57:32 JST 2020
+#1 SMP PREEMPT Mon Dec 28 12:58:07 KST 2020
+#1 SMP PREEMPT Wed Sep 11 16:50:06 CST 2019
+#1 SMP PREEMPT Wed Jul 1 06:25:48 CDT 2020
+#1 SMP PREEMPT Mon Sep 9 12:03:08 CST 2019
+#1 SMP PREEMPT Tue Feb 18 18:25:49 KST 2020
+#1 SMP PREEMPT Fri Jul 17 12:09:45 CST 2020
+#1 SMP PREEMPT Wed Dec 1 14:08:46 CST 2021
+#1 SMP PREEMPT Fri May 13 21:40:08 CST 2022
+#1 SMP PREEMPT Fri Apr 8 18:31:37 CST 2022
+#1 SMP PREEMPT Wed Jan 19 15:14:49 CST 2022
+#6 SMP PREEMPT Fri Jan 21 14:04:04 UTC 2022
+#1 SMP PREEMPT Thu Mar 17 12:59:42 CST 2022
+#1 SMP PREEMPT Fri Nov 20 15:11:00 KST 2020
+#2 SMP PREEMPT Wed Jan 24 16:02:01 CST 2018
+#1 SMP PREEMPT Tue Jul 31 10:08:10 +03 2018
+#1 SMP PREEMPT Tue Apr 2 10:28:48 CST 2019
+#1 SMP PREEMPT Wed Jun 6 03:24:10 CST 2018
+#1 SMP PREEMPT Wed Aug 26 12:11:09 IST 2020
+#1 SMP PREEMPT Thu Oct 18 17:52:29 KST 2018
+#1 SMP PREEMPT Wed May 26 23:31:18 CST 2021
+#1 SMP PREEMPT Wed Nov 14 18:20:04 CST 2018
+#1 SMP PREEMPT Wed Dec 1 17:29:08 CST 2021
+#1 SMP PREEMPT Sat Jan 22 01:15:48 CST 2022
+#1 SMP PREEMPT Thu Sep 19 12:51:13 CST 2019
+#1 SMP PREEMPT Mon Mar 2 11:00:24 CST 2020
+#2 SMP PREEMPT Mon May 25 11:03:03 CST 2020
+#1 SMP PREEMPT Fri Jul 6 17:11:16 CST 2018
+#1 SMP PREEMPT Fri May 10 20:56:09 CST 2019
+#1 SMP PREEMPT Sun Mar 27 19:24:12 PDT 2022
+#2 SMP PREEMPT Mon Feb 24 15:57:11 HKT 2020
+#49 SMP PREEMPT Thu Jan 3 20:20:03 CST 2019
+#1 SMP PREEMPT Thu Dec 14 10:39:06 CST 2017
+#2 SMP PREEMPT Mon Apr 29 21:12:18 CST 2019
+#1 SMP PREEMPT Tue May 24 18:55:38 KST 2022
+#1 SMP PREEMPT Wed Aug 29 17:44:51 CST 2018
+#1 SMP PREEMPT Tue Feb 2 12:04:10 CST 2021
+#1 SMP PREEMPT Fri Jul 27 20:51:08 CST 2018
+#2 SMP PREEMPT Mon Mar 18 11:43:45 CST 2019
+#2 SMP PREEMPT Wed Mar 23 18:35:08 CDT 2022
+#1 SMP PREEMPT Fri Mar 1 01:31:53 CST 2019
+#2 SMP PREEMPT Thu Mar 28 06:15:10 CST 2019
+#1 SMP PREEMPT Wed Aug 22 11:48:47 2018
+#1 SMP PREEMPT Thu Jul 8 11:46:20 CST 2021
+#1 SMP PREEMPT Wed Aug 5 00:25:55 KST 2020
+#1 SMP PREEMPT Mon Nov 16 16:13:04 KST 2020
+#1 SMP PREEMPT Tue Apr 9 15:45:39 CST 2019
+#1 SMP PREEMPT Wed Dec 29 09:41:14 CST 2021
+#1 SMP PREEMPT Fri Sep 20 02:00:40 CST 2019
+#1 SMP PREEMPT Fri May 10 20:32:26 UTC 2019
+#8 SMP PREEMPT Mon Oct 11 10:32:32 CST 2021
+#2 SMP PREEMPT Thu Aug 22 18:07:49 KST 2019
+#1 SMP PREEMPT Tue May 28 22:02:18 CST 2019
+#1 SMP PREEMPT Wed Oct 24 05:26:35 KST 2018
+#1 SMP PREEMPT Fri Oct 30 15:27:33 JST 2020
+#20 SMP PREEMPT Sat Dec 11 15:13:29 CST 2021
+#5 SMP PREEMPT Fri Dec 18 18:10:22 CST 2020
+#1 SMP PREEMPT Sun Feb 27 14:47:14 PST 2022
+#1 SMP PREEMPT Wed Nov 24 11:44:44 CST 2021
+#2 SMP PREEMPT Sun Apr 26 02:57:51 CST 2020
+#1 SMP PREEMPT Mon May 10 23:02:27 CST 2021
+#1 SMP PREEMPT Tue Dec 11 23:34:58 EST 2018
+#1 SMP PREEMPT Tue Dec 15 16:52:54 CST 2020
+#1 SMP PREEMPT Thu Nov 7 10:53:41 CST 2019
+#1 SMP PREEMPT Thu Jun 11 16:33:06 CST 2020
+#1 SMP PREEMPT Mon Sep 3 17:34:41 KST 2018
+#1 SMP PREEMPT Wed Jan 3 12:45:19 CST 2018
+#2 SMP PREEMPT Sat Oct 26 16:49:49 KST 2019
+#1 SMP PREEMPT Sat Mar 5 02:06:01 IST 2022
+#1 SMP PREEMPT Thu May 7 22:30:06 CST 2020
+#1 SMP PREEMPT Thu Jun 18 01:07:19 CST 2020
+#1 SMP PREEMPT Wed Apr 11 19:21:59 CST 2018
+#1 SMP PREEMPT Fri Jan 5 11:19:59 KST 2018
+#1 SMP PREEMPT Fri Apr 10 08:54:41 CDT 2020
+#1 SMP PREEMPT Tue Jul 27 19:58:21 KST 2021
+#1 SMP PREEMPT Mon Aug 5 10:21:39 KST 2019
+#1 SMP PREEMPT Mon Apr 23 15:44:43 CST 2018
+#1 SMP PREEMPT Tue Sep 8 06:17:51 CST 2020
+#1 SMP PREEMPT Sat Jun 30 01:11:52 CST 2018
+#2 SMP PREEMPT Thu Jun 21 15:00:07 CST 2018
+#1 SMP PREEMPT Thu Jun 18 20:40:46 KST 2020
+#1 SMP PREEMPT Thu Nov 18 19:11:51 CST 2021
+#1 SMP PREEMPT Fri Nov 9 18:48:37 KST 2018
+#1 SMP PREEMPT Wed Aug 26 11:13:34 CST 2020
+#1 SMP PREEMPT Thu Jul 16 11:17:50 KST 2020
+#1 SMP PREEMPT Thu Jan 25 19:19:28 CST 2018
+#1 SMP PREEMPT Thu Jun 20 05:02:42 PDT 2019
+#1 SMP PREEMPT Tue Apr 10 06:53:14 CDT 2018
+#1 SMP PREEMPT Mon Jun 22 13:06:05 CDT 2020
+#1 SMP PREEMPT Mon Sep 7 14:34:52 CST 2020
+#1 SMP PREEMPT Mon May 17 18:52:39 CST 2021
+#1 SMP PREEMPT Wed Feb 20 20:19:46 IST 2019
+#1 SMP PREEMPT Tue Jun 18 15:29:29 KST 2019
+#2 SMP PREEMPT Wed Oct 23 14:44:39 CST 2019
+#1 SMP PREEMPT Sat Mar 23 07:23:49 CST 2019
+#1 SMP PREEMPT Sat May 8 10:26:43 CST 2021
+#1 SMP PREEMPT Tue Jun 26 21:28:26 KST 2018
+#1 SMP PREEMPT Tue Mar 22 16:08:51 CST 2022
+#2 SMP PREEMPT Wed May 20 03:38:24 CST 2020
+#1 SMP PREEMPT Fri Mar 13 19:23:35 CST 2020
+#1 SMP PREEMPT Tue Jul 24 10:32:30 CST 2018
+#1 SMP PREEMPT Wed Sep 9 12:01:38 KST 2020
+#1 SMP PREEMPT Mon Nov 18 22:33:47 CST 2019
+#1 SMP PREEMPT Fri May 14 14:46:14 CST 2021
+#1 SMP PREEMPT Sun Sep 24 09:23:53 CST 2017
+#2 SMP PREEMPT Tue Jul 28 20:02:14 KST 2020
+#2 SMP PREEMPT Tue Dec 4 02:37:59 CST 2018
+#1 SMP PREEMPT Wed Oct 17 10:52:11 CST 2018
+#1 SMP PREEMPT Fri Nov 27 19:55:29 CST 2020
+#1 SMP PREEMPT Tue Nov 27 23:17:57 CST 2018
+#1 SMP PREEMPT Wed Oct 14 06:11:14 CDT 2020
+#2 SMP PREEMPT Sat Mar 28 08:21:28 KST 2020
+#9 SMP PREEMPT Wed Apr 10 10:44:25 CST 2019
+#2 SMP PREEMPT Mon May 24 22:37:18 KST 2021
+#1 SMP PREEMPT Wed Dec 22 10:10:54 KST 2021
+#1 SMP PREEMPT Tue Sep 18 00:59:41 PDT 2018
+#1 SMP PREEMPT Tue Oct 9 20:42:21 JST 2018
+#1 SMP PREEMPT Thu Mar 21 17:55:46 CST 2019
+#2 SMP PREEMPT Thu Feb 20 18:48:49 KST 2020
+#1 SMP PREEMPT Thu Jul 9 14:52:11 CST 2020
+#1 SMP PREEMPT Wed Oct 20 12:59:29 CST 2021
+#1 SMP PREEMPT Wed Sep 1 01:54:26 CDT 2021
+#1 SMP PREEMPT Fri Dec 4 11:26:34 CST 2020
+#1 SMP PREEMPT Tue Nov 2 18:41:10 CST 2021
+#1 SMP PREEMPT Sat Sep 21 20:40:03 CST 2019
+#1 SMP PREEMPT Thu Mar 31 00:56:11 CST 2022
+#2 SMP PREEMPT Mon Feb 17 12:27:33 KST 2020
+#1 SMP PREEMPT Tue Mar 1 11:00:33 CST 2022
+#1 SMP PREEMPT Fri Oct 26 17:27:24 CST 2018
+#1 SMP PREEMPT Wed Jan 30 23:01:08 PST 2019
+#2 SMP PREEMPT Fri Oct 30 22:16:52 CST 2020
+#1 SMP PREEMPT Sat May 5 04:10:32 CST 2018
+#1 SMP PREEMPT Wed Sep 25 14:48:25 CST 2019
+#1 SMP PREEMPT Thu Oct 17 22:48:22 CST 2019
+#1 SMP PREEMPT Tue Aug 11 15:52:34 CST 2020
+#1 SMP PREEMPT Wed Oct 3 23:45:39 PDT 2018
+#1 SMP PREEMPT Thu Mar 25 15:07:25 CDT 2021
+#2 SMP PREEMPT Fri Jul 5 19:52:57 CST 2019
+#2 SMP PREEMPT Mon Sep 28 18:17:28 KST 2020
+#1 SMP PREEMPT Tue Dec 4 17:01:56 CST 2018
+#2 SMP PREEMPT Mon Sep 21 15:46:04 CST 2020
+#2 SMP PREEMPT Wed Nov 7 10:19:28 CST 2018
+#1 SMP PREEMPT Fri Jun 4 17:19:21 +07 2021
+#1 SMP PREEMPT Tue Aug 3 10:43:08 CST 2021
+#1 SMP PREEMPT Wed Mar 21 16:37:26 KST 2018
+#1 SMP PREEMPT Sat Mar 2 05:09:03 CST 2019
+#1 SMP PREEMPT Fri Dec 18 21:49:48 KST 2020
+#1 SMP PREEMPT Mon Apr 25 20:16:14 KST 2022
+#1 SMP PREEMPT Mon Apr 1 10:27:20 CST 2019
+#1 SMP PREEMPT Fri Mar 29 21:18:52 CST 2019
+#1 SMP PREEMPT Mon Oct 8 15:24:54 CST 2018
+#1 SMP PREEMPT Sat Jun 27 13:35:44 KST 2020
+#1 SMP PREEMPT Tue Jun 30 14:13:55 KST 2020
+#2 SMP PREEMPT Wed Jun 5 06:17:41 CST 2019
+#1 SMP PREEMPT Wed Feb 28 12:27:07 JST 2018
+#1 SMP PREEMPT Sat Nov 7 23:14:35 KST 2020
+#1 SMP PREEMPT Sat Jan 26 02:31:10 CST 2019
+#1 SMP PREEMPT Sat Jul 4 18:02:42 CST 2020
+#1 SMP PREEMPT Fri May 15 12:41:27 KST 2020
+#1 SMP PREEMPT Mon Dec 25 23:17:38 CST 2017
+#1 SMP PREEMPT Wed Feb 6 01:33:03 CST 2019
+#3 SMP PREEMPT Mon Nov 25 17:44:51 CST 2019
+#2 SMP PREEMPT Wed Aug 22 15:25:41 CST 2018
+#1 SMP PREEMPT Tue May 24 15:31:45 CST 2022
+#1 SMP PREEMPT Sat Jul 20 21:42:44 CST 2019
+#2 SMP PREEMPT Fri Aug 31 19:58:23 CST 2018
+#2 SMP PREEMPT Mon Apr 8 05:52:35 CST 2019
+#1 SMP PREEMPT Tue Sep 29 12:45:16 CST 2020
+#3 SMP PREEMPT Tue Jun 23 14:43:11 KST 2020
+#2 SMP PREEMPT Wed May 22 00:20:22 CST 2019
+#1 SMP PREEMPT Tue Nov 26 21:07:31 KST 2019
+#1 SMP PREEMPT Mon Mar 7 10:51:27 CST 2022
+#1 SMP PREEMPT Fri Oct 9 15:31:21 CST 2020
+#4 SMP PREEMPT Mon Sep 30 15:17:32 CST 2019
+#2 SMP PREEMPT Tue May 11 01:23:15 KST 2021
+#1 SMP PREEMPT Fri Mar 25 13:24:50 CST 2022
+#1 SMP PREEMPT Wed May 9 16:49:08 CST 2018
+#1 SMP PREEMPT Mon May 28 03:17:38 CST 2018
+#1 SMP PREEMPT Sat Jun 9 15:58:47 CST 2018
+#1 SMP PREEMPT Wed Mar 10 20:47:56 KST 2021
+#1 SMP PREEMPT Fri Apr 10 15:31:22 CST 2020
+#1 SMP PREEMPT Sun Oct 7 17:05:18 CST 2018
+#1 SMP PREEMPT Tue Nov 19 09:24:08 CST 2019
+#1 SMP PREEMPT Mon Feb 14 15:08:37 CST 2022
+#2 SMP PREEMPT Sun Jan 19 17:43:15 CST 2020
+#1 SMP PREEMPT Mon May 13 13:56:36 CST 2019
+#1 SMP PREEMPT Fri Jun 22 20:55:37 CST 2018
+#1 SMP PREEMPT Thu Oct 21 16:44:51 CST 2021
+#1 SMP PREEMPT Fri Aug 3 19:28:21 CST 2018
+#1 SMP PREEMPT Wed Feb 28 22:10:20 PST 2018
+#1 SMP PREEMPT Fri Apr 17 21:23:44 CST 2020
+#1 SMP PREEMPT Thu Dec 6 22:35:30 CST 2018
+#1 SMP PREEMPT Tue Sep 24 21:57:03 CST 2019
+#1 SMP PREEMPT Sat Sep 28 09:19:20 +07 2019
+#1 SMP PREEMPT Fri Feb 18 21:25:36 CST 2022
+#1 SMP PREEMPT Thu Feb 7 13:01:21 JST 2019
+#1 SMP PREEMPT Mon Sep 28 22:35:44 CST 2020
+#1 SMP PREEMPT Wed Apr 10 19:47:34 CST 2019
+#1 SMP PREEMPT Sun Sep 27 03:43:36 GMT 2020
+#1 SMP PREEMPT Tue May 19 16:38:51 CST 2020
+#1 SMP PREEMPT Fri Jul 31 18:28:27 CST 2020
+#1 SMP PREEMPT Tue Aug 17 15:40:29 CST 2021
+#1 SMP PREEMPT Thu May 12 19:19:13 CST 2022
+#1 SMP PREEMPT Mon Nov 19 11:34:31 CST 2018
+#1 SMP PREEMPT Sat Apr 17 00:52:10 CST 2021
+#1 SMP PREEMPT Thu Sep 13 18:53:19 KST 2018
+#1 SMP PREEMPT Mon Jun 17 14:06:38 CST 2019
+#1 SMP PREEMPT Thu Jun 6 12:06:46 CDT 2019
+#1 SMP PREEMPT Fri Oct 18 00:24:07 CST 2019
+#1 SMP PREEMPT Sat Dec 25 16:34:59 CST 2021
+#1 SMP PREEMPT Thu Jul 16 21:07:49 CST 2020
+#1 SMP PREEMPT Tue Oct 20 00:07:20 IST 2020
+#45 SMP PREEMPT Tue Apr 2 09:58:47 CST 2019
+#1 SMP PREEMPT Tue Apr 28 02:08:30 PDT 2020
+#1 SMP PREEMPT Mon Sep 10 11:30:41 CST 2018
+#1 SMP PREEMPT Wed Jan 6 21:40:34 CST 2021
+#1 SMP PREEMPT Tue Oct 15 15:03:40 +07 2019
+#1 SMP PREEMPT Wed Aug 7 15:07:49 CST 2019
+#2 SMP PREEMPT Fri Jul 10 14:38:41 CST 2020
+#1 SMP PREEMPT Thu Jun 21 11:00:46 2018
+#6 SMP PREEMPT Wed Dec 22 20:30:02 UTC 2021
+#1 SMP PREEMPT Wed Jun 6 03:08:23 CST 2018
+#1 SMP PREEMPT Thu Dec 19 13:19:54 +07 2019
+#2 SMP PREEMPT Thu Sep 20 03:41:49 CST 2018
+#1 SMP PREEMPT Fri Dec 24 18:03:39 CST 2021
+#1 SMP PREEMPT Tue Oct 15 15:06:14 CST 2019
+#2 SMP PREEMPT Thu Oct 17 06:53:21 CST 2019
+#1 SMP PREEMPT Thu May 2 11:59:04 CEST 2019
+#4 SMP PREEMPT Fri Nov 22 17:33:42 CST 2019
+#1 SMP PREEMPT Wed Apr 29 17:33:23 CST 2020
+#1 SMP PREEMPT Wed Jul 4 17:38:45 CST 2018
+#1 SMP PREEMPT Fri Jan 18 11:40:03 CST 2019
+#1 SMP PREEMPT Mon Jun 11 17:31:42 CST 2018
+#1 SMP PREEMPT Thu Mar 28 10:55:05 CST 2019
+#1 SMP PREEMPT Wed Apr 21 10:22:20 KST 2021
+#1 SMP PREEMPT Tue Sep 29 19:06:05 KST 2020
+#2 SMP PREEMPT Thu Jun 4 14:05:56 CST 2020
+#1 SMP PREEMPT Wed May 15 18:30:10 CST 2019
+#1 SMP PREEMPT Tue Jan 21 22:02:18 KST 2020
+#1 SMP PREEMPT Wed Sep 11 00:09:42 CST 2019
+#1 SMP PREEMPT Thu Mar 3 13:26:19 CST 2022
+#1 SMP PREEMPT Fri Jan 31 11:17:56 KST 2020
+#1 SMP PREEMPT Wed Apr 7 02:09:55 +07 2021
+#1 SMP PREEMPT Thu Jul 2 04:27:23 CST 2020
+#188 SMP PREEMPT Mon Jul 19 16:36:56 CST 2021
+#1 SMP PREEMPT Tue Mar 30 15:25:25 CST 2021
+#9 SMP PREEMPT Sat Oct 12 12:41:44 CST 2019
+#1 SMP PREEMPT Fri Sep 4 21:12:16 CST 2020
+#1 SMP PREEMPT Tue Dec 17 11:17:46 CST 2019
+#2 SMP PREEMPT Fri Jan 8 22:13:22 CST 2021
+#1 SMP PREEMPT Sat Sep 5 23:38:17 CST 2020
+#1 SMP PREEMPT Tue Mar 29 11:40:00 CST 2022 f2fs-hash:203dd133ea
+#2 SMP PREEMPT Fri Aug 21 16:49:13 KST 2020
+#1 SMP PREEMPT Wed Oct 20 01:49:18 CST 2021
+#1 SMP PREEMPT Fri Oct 26 15:02:44 CST 2018
+#2 SMP PREEMPT Sat Jun 22 06:06:51 KST 2019
+#1 SMP PREEMPT Fri May 21 14:26:49 CST 2021
+#1 SMP PREEMPT Tue Mar 20 18:58:19 KST 2018
+#1 SMP PREEMPT Wed Oct 21 14:08:48 KST 2020
+#1 SMP PREEMPT Fri May 8 20:56:16 CST 2020
+#1 SMP PREEMPT Sun May 24 19:08:04 IST 2020
+#1 SMP PREEMPT Mon Jul 26 19:08:04 KST 2021
+#2 SMP PREEMPT Sat Jun 6 17:24:12 CST 2020
+#1 SMP PREEMPT Tue Apr 20 14:21:06 KST 2021
+#1 SMP PREEMPT Tue Oct 9 12:09:11 KST 2018
+#1 SMP PREEMPT Mon Dec 28 00:32:05 CST 2020
+#1 SMP PREEMPT Wed Dec 12 01:43:58 CST 2018
+#2 SMP PREEMPT Fri Jul 3 11:38:30 CST 2020
+#1 SMP PREEMPT Tue Jun 18 11:28:03 CST 2019
+#2 SMP PREEMPT Fri Apr 26 20:28:23 KST 2019
+#1 SMP PREEMPT Tue Feb 25 01:00:28 CST 2020
+#1 SMP PREEMPT Wed Mar 11 17:58:04 PDT 2020
+#1 SMP PREEMPT Tue Jun 23 17:49:43 CST 2020
+#1 SMP PREEMPT Wed Mar 28 19:07:49 KST 2018
+#2 SMP PREEMPT Fri Dec 7 10:34:21 CST 2018
+#1 SMP PREEMPT Wed Jul 22 02:47:40 CDT 2020
+#2 SMP PREEMPT Sat Oct 5 22:29:28 CST 2019
+#1 SMP PREEMPT Fri Apr 12 14:53:11 CST 2019
+#1 SMP PREEMPT Tue Nov 16 16:15:36 CST 2021
+#1 SMP PREEMPT Sat Oct 9 10:27:38 CST 2021
+#1 SMP PREEMPT Thu Jan 24 15:54:56 CST 2019
+#1 SMP PREEMPT Fri Dec 21 12:14:39 KST 2018
+#2 SMP PREEMPT Fri Aug 30 17:17:02 CST 2019
+#1 SMP PREEMPT Wed Apr 25 17:48:53 CST 2018
+#1 SMP PREEMPT Tue Apr 12 16:04:21 CST 2022
+#1 SMP PREEMPT Wed Feb 10 20:45:19 CST 2021
+#1 SMP PREEMPT Wed Nov 3 11:16:11 CST 2021
+#1 SMP PREEMPT Mon Jan 21 17:29:33 JST 2019
+#1 SMP PREEMPT Tue Sep 24 19:39:56 KST 2019
+#1 SMP PREEMPT Tue Feb 22 20:20:56 CST 2022
+#2 SMP PREEMPT Sat May 23 00:02:26 CST 2020
+#1 SMP PREEMPT Wed May 29 03:08:00 CST 2019
+#5 SMP PREEMPT Sun Jul 4 08:29:56 CST 2021
+#1 SMP PREEMPT Sat Dec 25 20:03:25 CST 2021
+#1 SMP PREEMPT Wed Nov 24 17:56:48 CST 2021
+#1 SMP PREEMPT Fri Apr 30 11:04:11 UTC 2021
+#1 SMP PREEMPT Wed Aug 11 02:39:11 CST 2021 f2fs-hash:3571f543c4
+#1 SMP PREEMPT Tue Aug 18 18:26:08 CST 2020
+#1 SMP PREEMPT Thu Nov 21 16:15:46 KST 2019
+#1 SMP PREEMPT Tue Aug 31 18:16:56 CST 2021
+#1 SMP PREEMPT Fri Sep 7 20:11:10 KST 2018
+#2 SMP PREEMPT Thu Oct 29 22:43:53 KST 2020
+#1 SMP PREEMPT Mon Mar 22 21:44:52 CST 2021
+#1 SMP PREEMPT Sat Apr 28 12:33:37 CST 2018
+#1 SMP PREEMPT Mon Nov 26 17:54:27 GMT+2 2018
+#2 SMP PREEMPT Fri Mar 27 18:27:47 KST 2020
+#2 SMP PREEMPT Tue Apr 26 17:24:52 KST 2022
+#1 SMP PREEMPT Thu May 9 10:59:53 CST 2019
+#105 SMP PREEMPT Thu Aug 12 18:57:20 CST 2021
+#1 SMP PREEMPT Wed Dec 13 12:33:02 JST 2017
+#1 SMP PREEMPT Thu Nov 5 19:25:01 KST 2020
+#2 SMP PREEMPT Mon Apr 25 14:38:44 KST 2022
+#40 SMP PREEMPT Wed Jan 5 09:20:43 HKT 2022
+#1 SMP PREEMPT Mon Nov 1 18:15:06 CST 2021
+#1 SMP PREEMPT Wed Feb 9 19:11:46 PST 2022
+#1 SMP PREEMPT Fri Jun 5 18:19:29 CST 2020
+#1 SMP PREEMPT Fri Oct 16 19:06:33 CST 2020
+#2 SMP PREEMPT Fri Apr 12 14:20:17 KST 2019
+#1 SMP PREEMPT Mon Dec 27 15:09:34 CST 2021
+#1 SMP PREEMPT Fri Jul 20 18:24:42 CST 2018
+#150 SMP PREEMPT Thu Jan 3 09:32:56 CST 2019
+#1 SMP PREEMPT Sat Dec 25 03:55:50 CST 2021 f2fs-hash:a54920e5c9
+#1 SMP PREEMPT Sun Apr 10 19:21:10 CST 2022
+#1 SMP PREEMPT Mon Mar 2 16:13:56 CST 2020
+#1 SMP PREEMPT Thu Sep 3 16:07:01 CST 2020
+#2 SMP PREEMPT Tue Jun 9 15:33:33 CST 2020
+#2 SMP PREEMPT Wed Aug 19 17:23:43 KST 2020
+#1 SMP PREEMPT Tue Jul 2 00:31:48 CST 2019
+#54 SMP PREEMPT Mon Aug 9 17:30:01 CST 2021
+#1 SMP PREEMPT Fri Jul 31 09:47:29 CST 2020
+#1 SMP PREEMPT Tue Jan 14 12:24:53 CST 2020
+#1 SMP PREEMPT Fri Mar 19 16:49:44 KST 2021
+#1 SMP PREEMPT Tue Jun 8 13:56:11 KST 2021
+#55 SMP PREEMPT Fri Aug 9 16:02:30 CST 2019
+#1 SMP PREEMPT Mon Feb 5 19:00:59 KST 2018
+#1 SMP PREEMPT Wed Feb 19 11:03:57 KST 2020
+#1 SMP PREEMPT Thu Mar 8 03:43:05 IST 2018
+#1 SMP PREEMPT Wed Aug 22 14:20:54 CST 2018
+#3 SMP PREEMPT Sat Aug 29 19:29:12 KST 2020
+#2 SMP PREEMPT Thu Dec 10 15:53:44 CST 2020
+#11 SMP PREEMPT Thu Dec 10 15:52:52 CST 2020
+#1 SMP PREEMPT Sat Feb 12 13:14:35 CST 2022
+#1 SMP PREEMPT Thu Feb 21 21:03:50 KST 2019
+#1 SMP PREEMPT Thu Jan 3 10:03:27 IST 2019
+#1 SMP PREEMPT Wed Jan 13 18:14:00 CST 2021
+#1 SMP PREEMPT Wed Apr 22 10:43:13 CST 2020
+#1 SMP PREEMPT Tue Mar 17 12:45:16 KST 2020
+#1 SMP PREEMPT Tue Nov 27 20:12:11 IST 2018
+#1 SMP PREEMPT Mon Mar 22 11:57:58 KST 2021
+#1 SMP PREEMPT Thu Jul 23 00:02:43 CST 2020
+#1 SMP PREEMPT Fri Jul 5 05:39:57 CST 2019
+#1 SMP PREEMPT Fri Apr 10 00:57:22 PDT 2020
+#1 SMP PREEMPT Thu Nov 28 21:30:18 KST 2019
+#1 SMP PREEMPT Thu Jun 18 13:55:51 KST 2020
+#22 SMP PREEMPT Wed Dec 16 18:26:29 CST 2020
+#2 SMP PREEMPT Tue Nov 5 19:37:54 KST 2019
+#1 SMP PREEMPT Thu Nov 11 15:48:46 UTC 2021
+#1 SMP PREEMPT Thu Dec 10 21:42:24 CST 2020
+#1 SMP PREEMPT Tue Nov 6 10:12:39 CST 2018
+#1 SMP PREEMPT Wed Sep 4 18:57:26 KST 2019
+#1 SMP PREEMPT Wed Dec 25 21:34:04 JST 2019
+#1 SMP PREEMPT Wed Apr 24 03:34:02 CST 2019
+#1 SMP PREEMPT Thu Apr 7 17:41:54 CST 2022
+#1 SMP PREEMPT Thu Feb 3 23:02:14 IST 2022
+#2 SMP PREEMPT Sun Jan 23 15:44:35 CST 2022
+#1 SMP PREEMPT Wed Aug 5 19:53:19 CST 2020
+#1 SMP PREEMPT Thu Mar 21 04:37:29 KST 2019
+#1 SMP PREEMPT Thu Apr 25 22:21:33 CST 2019
+#1 SMP PREEMPT Thu May 19 15:43:37 CST 2022
+#1 SMP PREEMPT Mon Apr 29 19:37:05 CST 2019
+#1 SMP PREEMPT Wed Nov 27 12:05:49 CST 2019
+#1 SMP PREEMPT Wed Oct 28 13:31:26 CST 2020
+#1 SMP PREEMPT Thu Aug 22 11:10:44 2019
+#1 SMP PREEMPT Thu Jul 9 16:23:17 CST 2020
+#1 SMP PREEMPT Mon Oct 1 02:42:42 JST 2018
+#1 SMP PREEMPT Fri Apr 16 14:39:31 KST 2021
+#1 SMP PREEMPT Fri Sep 3 10:58:34 CST 2021
+#1 SMP PREEMPT Thu Apr 7 15:42:53 CST 2022
+#1 SMP PREEMPT Mon Apr 9 15:04:54 KST 2018
+#1 SMP PREEMPT Mon Nov 19 22:44:47 CST 2018
+#1 SMP PREEMPT Tue Apr 16 22:52:33 CST 2019
+#1 SMP PREEMPT Wed Nov 25 14:29:26 KST 2020
+#1 SMP PREEMPT Fri Apr 12 15:48:29 IST 2019
+#1 SMP PREEMPT Wed May 20 02:56:36 PDT 2020
+#1 SMP PREEMPT Thu Dec 28 04:27:43 CST 2017
+#1 SMP PREEMPT Mon Sep 20 10:10:52 CST 2021
+#2 SMP PREEMPT Wed Mar 4 15:16:30 CST 2020
+#1 SMP PREEMPT Wed Mar 6 14:21:56 CST 2019
+#1 SMP PREEMPT Mon Feb 11 18:49:13 KST 2019
+#1 SMP PREEMPT Mon Dec 3 16:34:18 CST 2018
+#1 SMP PREEMPT Thu Dec 2 18:40:27 CST 2021
+#1 SMP PREEMPT Wed Feb 9 01:39:25 CST 2022
+#1 SMP PREEMPT Thu Oct 25 16:36:08 KST 2018
+#2 SMP PREEMPT Thu Oct 31 16:14:05 CST 2019
+#1 SMP PREEMPT Fri Nov 12 13:10:13 CST 2021
+#1 SMP PREEMPT Tue Feb 22 15:51:30 UTC 2022
+#3 SMP PREEMPT Mon Oct 29 15:11:07 CST 2018
+#1 SMP PREEMPT Thu Jan 13 06:37:13 CST 2022
+#1 SMP PREEMPT Tue Mar 15 00:18:32 CST 2022
+#1 SMP PREEMPT Thu May 31 13:29:03 EEST 2018
+#2 SMP PREEMPT Thu Jul 18 12:15:34 KST 2019
+#1 SMP PREEMPT Sun Jan 6 23:54:51 PST 2019
+#1 SMP PREEMPT Thu Sep 26 22:34:28 CST 2019
+#1 SMP PREEMPT Sat Oct 31 08:35:59 CDT 2020
+#1 SMP PREEMPT Wed Dec 19 18:46:27 CST 2018
+#2 SMP PREEMPT Fri Aug 2 11:02:21 CST 2019
+#2 SMP PREEMPT Thu Aug 13 14:11:33 CST 2020
+#18 SMP PREEMPT Mon Jan 10 21:01:39 CST 2022
+#1 SMP PREEMPT Tue Jun 29 22:56:39 CST 2021
+#1 SMP PREEMPT Fri Jan 10 11:11:18 CST 2020
+#1 SMP PREEMPT Fri Dec 13 02:50:06 KST 2019
+#2 SMP PREEMPT Fri Mar 5 11:31:35 CST 2021
+#1 SMP PREEMPT Tue Jul 3 18:04:44 CST 2018
+#1 SMP PREEMPT Fri May 28 18:34:26 CST 2021
+#1 SMP PREEMPT Tue Oct 19 09:24:35 CST 2021
+#1 SMP PREEMPT Wed Dec 23 15:38:35 CST 2020
+#1 SMP PREEMPT Fri Apr 5 09:12:53 CDT 2019
+#1 SMP PREEMPT Tue May 28 15:15:05 CST 2019
+#1 SMP PREEMPT Wed Apr 25 11:42:10 KST 2018
+#1 SMP PREEMPT Wed Sep 18 19:44:14 WIB 2019
+#1 SMP PREEMPT Thu Apr 30 23:47:42 CST 2020
+#8 SMP PREEMPT Sat Mar 9 16:20:21 CST 2019
+#1 SMP PREEMPT Tue Dec 11 14:59:06 KST 2018
+#2 SMP PREEMPT Sat Jun 15 15:46:53 CST 2019
+#1 SMP PREEMPT Fri Aug 9 00:22:27 CST 2019
+#1 SMP PREEMPT Fri Sep 10 03:12:12 CST 2021
+#1 SMP PREEMPT Tue May 7 15:37:13 KST 2019
+#1 SMP PREEMPT Mon Jan 10 10:28:14 CST 2022
+#1 SMP PREEMPT Wed Feb 13 19:34:47 PST 2019
+#1 SMP PREEMPT Wed Feb 26 11:13:59 CST 2020
+#1 SMP PREEMPT Wed Aug 28 23:26:10 CST 2019
+#1 SMP PREEMPT Tue Jan 2 04:29:35 EST 2018
+#1 SMP PREEMPT Mon Oct 5 21:52:29 KST 2020
+#1 SMP PREEMPT Tue Feb 22 20:37:23 CST 2022
+#1 SMP PREEMPT Fri May 21 01:07:46 JST 2021
+#1 SMP PREEMPT Tue Dec 15 15:14:49 CST 2020
+#1 SMP PREEMPT Wed Feb 13 01:15:29 JST 2019
+#1 SMP PREEMPT Wed May 29 17:23:13 KST 2019
+#1 SMP PREEMPT Mon May 7 03:50:52 CST 2018
+#1 SMP PREEMPT Wed May 30 14:32:09 CST 2018
+#1 SMP PREEMPT Sat Sep 12 23:44:29 CST 2020
+#1 SMP PREEMPT Mon Dec 17 19:03:28 KST 2018
+#2 SMP PREEMPT Mon Aug 26 22:37:43 CST 2019
+#1 SMP PREEMPT Tue Jan 8 15:58:08 KST 2019
+#2 SMP PREEMPT Mon Mar 2 19:14:05 KST 2020
+#2 SMP PREEMPT Mon Nov 15 20:29:38 HKT 2021
+#1 SMP PREEMPT Tue Oct 24 18:28:19 2017
+#2 SMP PREEMPT Fri Aug 28 20:05:08 KST 2020
+#1 SMP PREEMPT Sat Aug 11 01:22:39 CST 2018
+#1 SMP PREEMPT Wed Jul 7 10:03:19 UTC 2021
+#1 SMP PREEMPT Tue Jan 21 16:46:52 CST 2020
+#1 SMP PREEMPT Mon Dec 4 19:41:30 CST 2017
+#1 SMP PREEMPT Wed Sep 18 13:33:33 CST 2019
+#1 SMP PREEMPT Fri Mar 27 22:58:32 CST 2020
+#1 SMP PREEMPT Thu Aug 30 10:10:14 CDT 2018
+#1 SMP PREEMPT Thu Jan 20 23:50:30 CST 2022
+#1 SMP PREEMPT Thu Sep 10 11:44:45 CST 2020
+#2 SMP PREEMPT Thu Aug 30 20:24:01 CST 2018
+#1 SMP PREEMPT Fri Mar 4 15:46:23 EST 2022
+#2 SMP PREEMPT Fri Oct 22 01:51:37 CST 2021
+#1 SMP PREEMPT Sat Feb 12 17:24:51 CST 2022
+#1 SMP PREEMPT Thu Feb 4 19:46:21 PST 2021
+#1 SMP PREEMPT Wed Jun 9 18:58:10 KST 2021
+#1 SMP PREEMPT Sat Oct 27 01:17:24 EDT 2018
+#1 SMP PREEMPT Thu Aug 20 08:47:36 KST 2020
+#2 SMP PREEMPT Wed Jan 23 12:35:33 CST 2019
+#1 SMP PREEMPT Tue Jan 23 17:49:58 CST 2018
+#2 SMP PREEMPT Fri Aug 28 11:27:02 CST 2020
+#1 SMP PREEMPT Fri Dec 11 19:20:46 CST 2020
+#1 SMP PREEMPT Wed Nov 22 15:46:29 CST 2017
+#1 SMP PREEMPT Thu Apr 14 21:53:19 CST 2022
+#8 SMP PREEMPT Tue Nov 12 15:55:51 CST 2019
+#1 SMP PREEMPT Wed Feb 12 06:27:52 KST 2020
+#1 SMP PREEMPT Tue May 25 15:04:39 CST 2021
+#1 SMP PREEMPT Thu May 19 19:09:49 CST 2022
+#1 SMP PREEMPT Sat Oct 10 13:27:00 KST 2020
+#3 SMP PREEMPT Tue Jun 16 09:00:44 KST 2020
+#2 SMP PREEMPT Mon Apr 27 14:56:11 KST 2020
+#1 SMP PREEMPT Wed May 18 23:38:47 CST 2022
+#1 SMP PREEMPT Tue Apr 27 00:25:32 CST 2021
+#1 SMP PREEMPT Mon Oct 19 19:43:25 KST 2020
+#1 SMP PREEMPT Thu Jun 24 15:02:04 KST 2021
+#1 SMP PREEMPT Thu Mar 4 16:12:08 JST 2021
+#1 SMP PREEMPT Wed Aug 14 17:26:18 CST 2019
+#1 SMP PREEMPT Tue Sep 22 00:21:57 CST 2020
+#1 SMP PREEMPT Sat Jan 1 22:28:16 PST 2022
+#1 SMP PREEMPT Thu Oct 18 11:48:48 CDT 2018
+#1 SMP PREEMPT Wed Apr 13 00:35:00 CST 2022
+#1 SMP PREEMPT Wed Sep 23 22:53:09 CST 2020
+#2 SMP PREEMPT Thu Mar 14 00:56:55 KST 2019
+#2 SMP PREEMPT Sat Apr 4 01:08:19 CST 2020
+#1 SMP PREEMPT Fri Oct 23 18:28:20 CST 2020
+#1 SMP PREEMPT Sat Aug 17 23:39:21 CST 2019
+#1 SMP PREEMPT Mon Aug 10 22:53:49 CST 2020
+#1 SMP PREEMPT Fri Jun 18 20:18:54 CST 2021
+#1 SMP PREEMPT Wed Apr 7 08:58:19 KST 2021
+#1 SMP PREEMPT Tue Sep 22 03:42:51 CST 2020
+#1 SMP PREEMPT Wed Apr 4 04:58:21 CST 2018
+#1 SMP PREEMPT Fri Sep 24 11:39:49 CST 2021
+#1 SMP PREEMPT Mon Aug 6 20:25:02 CST 2018
+#1 SMP PREEMPT Tue Mar 22 05:37:00 CDT 2022
+#2 SMP PREEMPT Sat Dec 28 04:20:19 CST 2019
+#1 SMP PREEMPT Wed Nov 7 03:59:46 CST 2018
+#1 SMP PREEMPT Wed Jul 18 10:47:40 CST 2018
+#1 SMP PREEMPT Tue Aug 21 20:24:47 HKT 2018
+#1 SMP PREEMPT Tue Apr 7 22:44:43 CST 2020
+#1 SMP PREEMPT Fri Jan 10 12:02:39 CST 2020
+#1 SMP PREEMPT Wed Jul 11 19:42:40 PDT 2018
+#1 SMP PREEMPT Fri Apr 16 19:24:11 CST 2021
+#1 SMP PREEMPT Fri Sep 28 08:47:02 CST 2018
+#1 SMP PREEMPT Fri Jun 28 20:56:45 CST 2019
+#1 SMP PREEMPT Wed Sep 25 17:55:20 KST 2019
+#1 SMP PREEMPT Mon Jan 10 11:50:10 CST 2022
+#1 SMP PREEMPT Tue Oct 22 22:12:51 CST 2019
+#2 SMP Tue Apr 28 02:53:13 UTC 2020
+#1 SMP PREEMPT Tue Aug 7 14:20:35 CST 2018
+#1 SMP PREEMPT Fri Apr 8 12:03:29 CST 2022
+#1 SMP PREEMPT Mon Oct 5 21:52:29 KST 2020
+#1 SMP PREEMPT Tue Aug 17 15:27:10 CST 2021
+#1 SMP PREEMPT Sat Mar 31 12:09:09 CST 2018
+#1 SMP PREEMPT Sun Sep 26 10:53:23 CST 2021
+#2 SMP PREEMPT Wed Mar 17 22:44:45 KST 2021
+#1 SMP PREEMPT Wed Jul 22 17:09:30 CST 2020
+#1 SMP PREEMPT Sun Sep 27 20:13:23 CST 2020
+#1 SMP PREEMPT Fri Feb 1 20:34:50 JST 2019
+#1 SMP PREEMPT Mon Nov 8 20:51:05 PST 2021
+#0 SMP PREEMPT Tue Mar 8 09:05:39 UTC 2022
+#3 SMP PREEMPT Fri Jul 3 18:16:25 CST 2020
+#5 SMP PREEMPT Fri Dec 27 16:29:28 CST 2019
+#1 SMP PREEMPT Wed Nov 20 12:22:42 CST 2019
+#1 SMP PREEMPT Fri Jul 3 15:51:29 CST 2020
+#1 SMP PREEMPT Fri Oct 26 19:38:31 KST 2018
+#1 SMP PREEMPT Thu Jul 29 16:13:35 CST 2021
+#1 SMP PREEMPT Sat Mar 27 20:41:01 PDT 2021
+#1 SMP PREEMPT Fri May 15 07:50:49 KST 2020
+#1 SMP PREEMPT Tue Apr 26 01:16:42 CST 2022
+#2 SMP PREEMPT Mon Jul 22 14:24:39 CST 2019
+#1 SMP PREEMPT Wed Jan 30 11:41:34 KST 2019
+#2 SMP PREEMPT Mon Jan 14 16:52:51 CST 2019
+#2 SMP PREEMPT Sat Nov 3 23:15:43 CST 2018
+#1 SMP PREEMPT Thu Sep 27 21:55:21 CST 2018
+#1 SMP PREEMPT Mon Jun 3 12:57:56 -03 2019
+#1 SMP PREEMPT Mon Apr 8 13:05:24 CST 2019
+#1 SMP PREEMPT Tue Dec 17 20:15:31 CST 2019
+#1 SMP PREEMPT Wed Sep 9 13:05:10 KST 2020
+#1 SMP PREEMPT Tue Aug 25 06:41:09 CST 2020
+#2 SMP PREEMPT Thu Jun 18 20:36:17 CST 2020
+#1 SMP PREEMPT Wed Aug 12 10:58:00 CST 2020
+#1 SMP PREEMPT Wed Feb 16 18:35:43 CST 2022
+#2 SMP PREEMPT Thu Jul 8 18:29:30 KST 2021
+#1 SMP PREEMPT Tue May 19 16:36:34 KST 2020
+#1 SMP PREEMPT Wed Oct 24 02:10:23 PDT 2018
+#1 SMP PREEMPT Fri Sep 17 16:29:18 CST 2021
+#1 SMP PREEMPT Tue Jun 5 13:04:14 KST 2018
+#1 SMP PREEMPT Thu May 12 23:40:03 CST 2022
+#1 SMP PREEMPT Mon Oct 21 00:57:05 PDT 2019
+#1 SMP PREEMPT Wed Oct 28 21:47:53 KST 2020
+#1 SMP PREEMPT Sat Dec 28 12:03:53 CST 2019
+#1 SMP PREEMPT Tue Jan 14 16:19:52 CST 2020
+#1 SMP PREEMPT Wed Mar 28 10:43:43 CST 2018
+#1 SMP PREEMPT Tue Jan 29 16:23:50 CST 2019
+#1 SMP PREEMPT Tue Apr 12 19:57:57 CST 2022
+#1 SMP PREEMPT Fri Feb 15 20:34:29 CST 2019
+#1 SMP PREEMPT Wed Jan 17 03:46:19 CST 2018
+#1 SMP PREEMPT Fri May 6 21:36:34 JST 2022
+#1 SMP PREEMPT Mon Dec 4 19:59:43 KST 2017
+#1 SMP PREEMPT Thu Dec 19 14:20:46 CST 2019
+#1 SMP PREEMPT Sat Jan 19 02:53:02 CST 2019
+#3 SMP PREEMPT Tue Jun 29 06:38:52 JST 2021
+#2 SMP PREEMPT Fri Nov 1 17:48:04 CST 2019
+#7 SMP PREEMPT Mon Sep 16 19:31:06 CST 2019
+#1 SMP PREEMPT Thu May 21 14:09:20 PDT 2020
+#4 SMP PREEMPT Thu Sep 2 16:08:09 CST 2021
+#1 SMP PREEMPT Mon Mar 12 15:57:09 KST 2018
+#1 SMP PREEMPT Sat May 16 10:29:07 CST 2020
+#1 SMP PREEMPT Fri Jan 17 01:22:05 2020
+#1 SMP PREEMPT Mon May 24 14:23:59 IST 2021
+#1 SMP PREEMPT Fri Aug 2 15:55:04 IST 2019
+#1 SMP PREEMPT Tue Sep 3 04:58:41 CST 2019
+#1 SMP PREEMPT Fri Dec 7 06:36:34 KST 2018
+#2 SMP PREEMPT Fri Mar 29 22:35:27 KST 2019
+#1 SMP PREEMPT Fri May 24 22:22:51 CST 2019
+#1 SMP PREEMPT Thu Jul 11 23:15:35 CST 2019
+#1 SMP PREEMPT Thu Sep 26 20:47:03 CST 2019
+#1 SMP PREEMPT Thu Feb 14 06:16:42 KST 2019
+#1 SMP PREEMPT Tue Oct 20 22:53:53 CST 2020
+#1 SMP PREEMPT Thu Apr 26 13:38:35 KST 2018
+#2 SMP PREEMPT Mon Mar 25 14:31:12 CST 2019
+#1 SMP PREEMPT Mon Mar 2 12:28:56 KST 2020
+#1 SMP PREEMPT Fri Mar 19 23:11:11 +07 2021
+#2 SMP PREEMPT Tue Jun 23 10:54:39 CST 2020
+#1 SMP PREEMPT Thu Dec 9 19:29:49 CST 2021
+#1 SMP PREEMPT Tue Mar 23 18:34:15 CST 2021
+#1 SMP PREEMPT Wed Mar 23 19:35:57 KST 2022
+#1 SMP PREEMPT Tue Dec 1 18:02:16 CST 2020
+#1 SMP PREEMPT Mon Apr 2 12:18:58 CST 2018
+#1 SMP PREEMPT Sat Sep 15 15:17:03 CST 2018
+#1 SMP PREEMPT Sat Mar 27 23:51:18 CST 2021
+#1 SMP PREEMPT Sun Jun 20 22:01:21 PDT 2021
+#1 SMP PREEMPT Thu Dec 31 17:58:50 CST 2020
+#1 SMP PREEMPT Sat Nov 24 02:38:32 CST 2018
+#1 SMP PREEMPT Sat Mar 27 23:51:18 CST 2021
+#1 SMP PREEMPT Thu Jan 20 21:13:34 KST 2022
+#1 SMP PREEMPT Mon Feb 21 20:35:57 CST 2022
+#1 SMP PREEMPT Fri Aug 30 11:46:04 CST 2019
+#1 SMP PREEMPT Fri Mar 20 20:34:14 CST 2020
+#1 SMP PREEMPT Mon Nov 12 23:59:34 PST 2018
+#1 SMP PREEMPT Tue May 4 16:51:13 +07 2021
+#2 SMP PREEMPT Fri Dec 4 15:03:22 CST 2020
+#1 SMP PREEMPT Mon Jan 22 22:35:13 PST 2018
+#5 SMP PREEMPT Mon May 31 06:57:00 CDT 2021
+#1 SMP PREEMPT Thu Jan 10 15:28:20 CST 2019
+#1 SMP PREEMPT Thu Mar 14 18:35:52 CST 2019
+#1 SMP PREEMPT Sat May 8 15:30:12 CST 2021
+#1 SMP PREEMPT Mon Nov 4 15:25:08 EST 2019
+#1 SMP PREEMPT Thu May 13 20:20:30 CST 2021
+#1 SMP PREEMPT Tue Nov 21 17:33:29 CST 2017
+#1 SMP PREEMPT Wed Jul 25 19:03:41 CST 2018
+#1 SMP PREEMPT Mon Oct 22 17:24:05 CST 2018
+#1 SMP PREEMPT Tue Oct 27 01:49:17 KST 2020
+#1 SMP PREEMPT Fri Apr 30 18:55:48 CST 2021
+#1 SMP PREEMPT Thu May 30 04:48:11 KST 2019
+#1 SMP PREEMPT Fri May 8 11:15:32 IST 2020
+#1 SMP PREEMPT Mon Dec 17 19:09:46 CST 2018
+#1 SMP PREEMPT Fri Sep 11 15:09:53 JST 2020
+#1 SMP PREEMPT Fri Jan 7 15:11:51 CST 2022
+#2 SMP PREEMPT Fri Oct 26 15:53:09 CST 2018
+#1 SMP PREEMPT Thu Apr 16 21:41:28 CST 2020
+#1 SMP PREEMPT Fri Nov 8 15:43:47 CST 2019
+#1 SMP PREEMPT Mon Jun 11 18:18:54 KST 2018
+#1 SMP PREEMPT Tue Sep 3 00:42:03 PDT 2019
+#1 SMP PREEMPT Wed May 6 16:26:43 CST 2020
+#1 SMP PREEMPT Mon Jul 23 00:17:41 CST 2018
+#1 SMP PREEMPT Tue Jul 16 10:28:35 KST 2019
+#1 SMP PREEMPT Sat Aug 1 08:52:07 CST 2020
+#1 SMP PREEMPT Wed May 8 15:52:08 KST 2019
+#1 SMP PREEMPT Tue Sep 11 18:41:13 CST 2018
+#2 SMP PREEMPT Thu Mar 19 14:35:50 CST 2020
+#1 SMP PREEMPT Fri Nov 12 17:49:39 CST 2021
+#1 SMP PREEMPT Thu Jul 30 05:18:09 CST 2020
+#1 SMP PREEMPT Wed May 5 10:42:09 CDT 2021
+#1 SMP PREEMPT Mon May 20 13:54:52 HKT 2019
+#1 SMP PREEMPT Mon Jun 25 21:48:13 CST 2018
+#1 SMP PREEMPT Tue Mar 26 19:47:21 KST 2019
+#2 SMP PREEMPT Wed May 11 14:14:31 CST 2022
+#2 SMP PREEMPT Tue Dec 3 01:02:50 CST 2019
+#1 SMP PREEMPT Fri Jul 6 16:34:13 CST 2018
+#1 SMP PREEMPT Tue Nov 30 11:54:42 PST 2021
+#1 SMP PREEMPT Fri Nov 22 00:19:34 CST 2019
+#1 SMP PREEMPT Fri Dec 24 00:19:01 CST 2021
+#1 SMP PREEMPT Wed Dec 19 16:45:09 EST 2018
+#1 SMP PREEMPT Tue Jun 22 20:54:15 CST 2021
+#1 SMP PREEMPT Wed Feb 16 19:18:48 CST 2022
+#1 SMP PREEMPT Wed Jul 31 04:47:37 CST 2019
+#2 SMP PREEMPT Wed Dec 1 15:27:04 CST 2021
+#1 SMP PREEMPT Thu Nov 22 06:09:24 KST 2018
+#1 SMP PREEMPT Wed Aug 4 16:04:10 UTC 2021
+#1 SMP PREEMPT Fri Nov 1 06:21:11 CST 2019
+#21 SMP PREEMPT Tue Apr 19 17:24:44 HKT 2022
+#1 SMP PREEMPT Tue Oct 26 20:22:22 PDT 2021
+#1 SMP PREEMPT Tue Nov 12 14:29:29 CST 2019
+#1 SMP PREEMPT Tue Jun 26 13:52:23 CST 2018
+#1 SMP PREEMPT Tue Sep 10 23:47:50 CST 2019
+#1 SMP PREEMPT Tue Feb 26 14:26:07 CST 2019
+#1 SMP PREEMPT Wed Dec 16 18:02:23 KST 2020
+#1 SMP PREEMPT Tue Mar 8 16:08:29 CST 2022
+#1 SMP PREEMPT Wed Feb 23 11:34:37 CST 2022
+#2 SMP PREEMPT Fri Mar 20 03:49:04 CST 2020
+#1 SMP PREEMPT Mon May 23 16:14:20 CST 2022
+#1 SMP PREEMPT Thu Mar 4 11:47:53 PST 2021
+#1 SMP PREEMPT Tue Dec 7 10:57:11 CST 2021
+#1 SMP PREEMPT Fri Aug 7 00:34:53 KST 2020
+#1 SMP PREEMPT Wed Jun 30 15:56:50 +07 2021
+#2 SMP PREEMPT Sat May 15 02:12:26 CST 2021
+#1 SMP PREEMPT Tue Dec 29 09:31:47 CST 2020
+#1 SMP PREEMPT Tue Aug 18 21:25:05 CST 2020
+#1 SMP PREEMPT Sat Jan 30 12:09:16 CST 2021
+#1 SMP PREEMPT Fri Jun 21 05:02:08 CST 2019
+#1 SMP PREEMPT Mon Apr 11 15:53:15 CST 2022
+#1 SMP PREEMPT Tue Apr 30 16:57:59 CST 2019
+#1 SMP PREEMPT Tue Jun 19 15:17:53 -03 2018
+#1 SMP PREEMPT Tue Nov 2 17:33:56 CST 2021
+#1 SMP PREEMPT Mon Feb 21 17:10:59 CST 2022
+#1 SMP PREEMPT Fri Mar 2 17:51:43 CST 2018
+#1 SMP PREEMPT Wed Mar 16 03:47:25 CST 2022
+#1 SMP PREEMPT Wed Nov 27 16:46:05 IST 2019
+#1 SMP PREEMPT Mon May 27 23:29:02 CST 2019
+#1 SMP PREEMPT Fri Mar 11 01:42:25 CST 2022
+#1 SMP PREEMPT Mon Sep 28 21:45:07 WIB 2020
+#1 SMP PREEMPT Thu Jan 24 21:06:25 CST 2019
+#1 SMP PREEMPT Mon Jan 22 17:07:26 CST 2018
+#1 SMP PREEMPT Mon Aug 6 10:23:26 KST 2018
+#1 SMP PREEMPT Tue May 26 11:23:57 EDT 2020
+#1 SMP PREEMPT Fri Dec 20 10:27:58 CST 2019
+#1 SMP PREEMPT Fri Dec 11 13:50:54 +07 2020
+#2 SMP PREEMPT Tue Oct 23 19:05:07 CST 2018
+#1 SMP PREEMPT Thu Nov 28 20:21:53 CST 2019
+#1 SMP PREEMPT Mon Mar 4 13:44:25 CST 2019
+#2 SMP PREEMPT Thu Sep 19 21:32:35 KST 2019
+#1 SMP PREEMPT Fri Feb 25 16:14:24 CST 2022
+#2 SMP PREEMPT Thu Mar 26 01:10:21 KST 2020
+#1 SMP PREEMPT Tue Mar 29 16:15:09 CST 2022
+#1 SMP PREEMPT Wed Jan 22 01:08:50 PST 2020
+#2 SMP PREEMPT Thu May 16 21:09:35 CST 2019
+#1 SMP PREEMPT Mon Mar 29 15:13:42 KST 2021
+#1 SMP PREEMPT Thu Oct 3 05:10:48 PDT 2019
+#1 SMP PREEMPT Mon May 23 17:54:30 IST 2022
+#1 SMP PREEMPT Sat Feb 12 13:21:58 CST 2022
+#1 SMP PREEMPT Wed Mar 6 16:30:51 KST 2019
+#1 SMP PREEMPT Sun May 30 12:44:59 CST 2021
+#1 SMP PREEMPT Wed Feb 13 11:55:47 CST 2019
+#1 SMP PREEMPT Wed Jan 12 17:18:21 CST 2022
+#1 SMP PREEMPT Tue Oct 12 16:37:59 CST 2021
+#2 SMP PREEMPT Sat May 18 00:19:53 CST 2019
+#2 SMP PREEMPT Sun Dec 30 15:28:10 CST 2018
+#1 SMP PREEMPT Mon Dec 16 12:15:42 CST 2019
+#1 SMP PREEMPT Mon Apr 4 04:40:20 CST 2022
+#1 SMP PREEMPT Fri Nov 1 18:44:59 CST 2019
+#1 SMP PREEMPT Thu May 10 18:32:01 CST 2018
+#1 SMP PREEMPT Sat Feb 6 11:58:28 IST 2021
+#2 SMP PREEMPT Tue Nov 3 06:41:52 CST 2020
+#1 SMP PREEMPT Thu Jul 2 01:26:02 CST 2020
+#2 SMP PREEMPT Tue Nov 19 13:22:06 KST 2019
+#1 SMP PREEMPT Thu Sep 12 17:45:35 CST 2019
+#1 SMP PREEMPT Thu Dec 5 21:11:37 CST 2019
+#2 SMP PREEMPT Tue Dec 10 11:12:20 CST 2019
+#5 SMP PREEMPT Mon Nov 30 17:52:59 CST 2020
+#1 SMP PREEMPT Sat Nov 16 01:38:31 CST 2019
+#1 SMP PREEMPT Fri Jul 12 17:04:38 CST 2019
+#1 SMP PREEMPT Mon May 4 13:55:55 KST 2020
+#1 SMP PREEMPT Thu Dec 20 15:02:45 CST 2018
+#1 SMP PREEMPT Tue Mar 8 16:09:20 CST 2022
+#1 SMP PREEMPT Fri Aug 9 12:18:28 CEST 2019
+#1 SMP PREEMPT Mon Apr 29 08:57:03 KST 2019
+#1 SMP PREEMPT Thu Mar 28 19:39:04 KST 2019
+#1 SMP PREEMPT Fri Mar 26 11:53:44 CST 2021
+#2 SMP PREEMPT Thu Nov 21 14:31:06 HKT 2019
+#1 SMP PREEMPT Tue Apr 6 00:20:37 JST 2021
+#1 SMP PREEMPT Tue Dec 4 20:53:40 CST 2018
+#1 SMP PREEMPT Fri Sep 7 01:47:14 CST 2018
+#1 SMP PREEMPT Sat Jul 24 00:52:21 KST 2021
+#1 SMP PREEMPT Mon Mar 1 22:57:32 CST 2021
+#1 SMP PREEMPT Tue Sep 29 17:27:12 CST 2020
+#1 SMP PREEMPT Sat May 14 00:52:28 CST 2022
+#1 SMP PREEMPT Tue Jan 8 22:46:18 CST 2019
+#1 SMP PREEMPT Tue May 26 10:57:25 EDT 2020
+#1 SMP PREEMPT Mon Dec 6 09:15:55 JST 2021
+#1 SMP PREEMPT Thu Oct 15 20:28:07 KST 2020
+#1 SMP PREEMPT Tue Jul 28 13:42:51 JST 2020
+#1 SMP PREEMPT Mon Jul 22 17:22:04 CST 2019
+#1 SMP PREEMPT Mon Mar 7 14:43:37 CST 2022
+#3 SMP PREEMPT Tue Oct 22 23:54:02 CST 2019
+#1 SMP PREEMPT Wed Apr 22 01:07:26 JST 2020
+#1 SMP PREEMPT Mon Sep 17 22:56:35 KST 2018
+#1 SMP PREEMPT Fri Sep 27 13:33:43 CDT 2019
+#4 SMP PREEMPT Sat Sep 1 12:13:00 CST 2018
+#1 SMP PREEMPT Mon Mar 16 21:38:27 CST 2020
+#1 SMP PREEMPT Tue Nov 12 19:40:18 CST 2019
+#1 SMP PREEMPT Wed Oct 20 05:37:11 CST 2021
+#4 SMP PREEMPT Wed Mar 11 12:48:24 CST 2020
+#1 SMP PREEMPT Fri Aug 13 15:30:12 CST 2021
+#1 SMP PREEMPT Tue Apr 16 22:52:33 CST 2019
+#1 SMP PREEMPT Thu Feb 17 19:36:24 PST 2022
+#1 SMP PREEMPT Thu Dec 31 15:35:35 CST 2020
+#2 SMP PREEMPT Tue Nov 23 20:04:03 IST 2021
+#2 SMP Sun Mar 7 10:28:48 UTC 2021
+#1 SMP PREEMPT Sat Apr 25 14:23:45 WIB 2020
+#1 SMP PREEMPT Tue Dec 18 10:22:44 CET 2018
+#1 SMP PREEMPT Thu Aug 8 17:49:23 KST 2019
+#1 SMP PREEMPT Sun May 16 20:55:52 CST 2021
+#1 SMP PREEMPT Wed Nov 17 17:54:21 CST 2021
+#1 SMP PREEMPT Wed Jan 15 23:28:05 CST 2020
+#1 SMP PREEMPT Wed Oct 28 22:13:11 CST 2020
+#340 SMP PREEMPT Wed Sep 11 21:26:26 CST 2019
+#1 SMP PREEMPT Wed Mar 4 23:05:54 PST 2020
+#1 SMP PREEMPT Mon Jun 8 18:22:35 CST 2020
+#1 SMP PREEMPT Fri Apr 30 09:17:42 CST 2021
+#1 SMP PREEMPT Wed Feb 28 19:57:23 CST 2018
+#1 SMP PREEMPT Thu Sep 27 16:54:45 CST 2018
+#1 SMP PREEMPT Fri Apr 5 13:58:19 KST 2019
+#1 SMP PREEMPT Wed Jun 23 19:52:43 CST 2021
+#1 SMP PREEMPT Tue Jan 21 15:26:52 KST 2020
+#1 SMP PREEMPT Tue Jul 3 18:10:10 CST 2018
+#2 SMP PREEMPT Thu Nov 14 12:19:05 CST 2019
+#2 SMP Thu Apr 25 22:00:12 UTC 2019
+#1 SMP PREEMPT Sat May 21 12:54:41 CST 2022
+#1 SMP PREEMPT Wed May 27 01:49:03 PDT 2020
+#2 SMP PREEMPT Fri Mar 15 15:32:21 CST 2019
+#1 SMP PREEMPT Thu Apr 14 22:09:32 CST 2022
+#1 SMP PREEMPT Mon Jun 8 20:38:00 CST 2020
+#1 SMP PREEMPT Fri Mar 19 19:07:39 CST 2021
+#2 SMP PREEMPT Tue May 25 10:43:20 CST 2021
+#1 SMP PREEMPT Wed Apr 3 21:24:37 CST 2019
+#1 SMP PREEMPT Fri Aug 30 18:28:28 KST 2019
+#1 SMP PREEMPT Mon Sep 17 09:19:34 CST 2018
+#1 SMP PREEMPT Thu Dec 14 03:43:09 CST 2017
+#1 SMP PREEMPT Sun Jan 27 09:47:22 CST 2019
+#1 SMP PREEMPT Wed Jun 23 10:29:43 KST 2021
+#1 SMP PREEMPT Wed Feb 28 13:33:25 2018
+#1 SMP PREEMPT Wed Sep 26 11:59:02 BRT 2018
+#1 SMP PREEMPT Tue Dec 14 22:52:11 CST 2021
+#1 SMP PREEMPT Fri Dec 11 18:48:16 CST 2020
+#5 SMP PREEMPT Fri May 13 13:45:26 UTC 2022
+#1 SMP PREEMPT Fri Sep 20 03:52:38 CST 2019
+#2 SMP PREEMPT Wed Jun 20 20:12:12 CST 2018
+#4 SMP PREEMPT Thu Mar 24 21:45:41 UTC 2022
+#1 SMP PREEMPT Tue Aug 25 10:31:52 CST 2020
+#1 SMP PREEMPT Tue Nov 12 19:57:16 CST 2019
+#1 SMP PREEMPT Sun Jun 14 17:53:17 CST 2020
+#1 SMP PREEMPT Mon May 16 12:37:04 KST 2022
+#2 SMP PREEMPT Wed Aug 4 21:42:13 CST 2021
+#1 SMP PREEMPT Tue May 15 05:56:48 CST 2018
+#1 SMP PREEMPT Fri Nov 8 02:14:23 UTC 2019
+#1 SMP PREEMPT Fri Jun 4 11:35:27 CST 2021
+#1 SMP PREEMPT Tue Jun 26 19:42:41 CST 2018
+#1 SMP PREEMPT Sun May 16 07:07:11 CST 2021
+#1 SMP PREEMPT Mon Jan 29 17:05:20 KST 2018
+#1 SMP PREEMPT Sun Sep 30 10:59:57 CST 2018
+#1 SMP PREEMPT Fri Oct 16 01:49:57 CST 2020
+#1 SMP PREEMPT Wed Sep 25 15:29:30 CST 2019
+#1 SMP PREEMPT Wed Mar 2 22:52:39 CST 2022
+#1 SMP PREEMPT Sun Sep 2 18:49:49 CST 2018
+#1 SMP PREEMPT Mon Sep 17 13:53:29 KST 2018
+#1 SMP PREEMPT Thu Mar 10 18:51:27 UTC 2022
+#1 SMP PREEMPT Wed Jun 3 10:13:03 CST 2020
+#1 SMP PREEMPT Wed Feb 13 16:05:13 CST 2019
+#2 SMP PREEMPT Fri Jul 31 17:16:12 KST 2020
+#1 SMP PREEMPT Fri Jul 3 01:17:30 CST 2020
+#1 SMP PREEMPT Wed Sep 18 18:27:47 CST 2019
+#1 SMP PREEMPT Tue Dec 24 20:59:00 CST 2019
+#1 SMP PREEMPT Wed Jul 8 05:40:26 CST 2020
+#1 SMP PREEMPT Wed Jan 12 14:07:27 CST 2022 f2fs-hash:c001757c47
+#1 SMP PREEMPT Mon May 9 07:58:32 UTC 2022
+#2 SMP PREEMPT Tue Sep 7 11:21:13 CST 2021
+#1 SMP PREEMPT Tue Apr 27 11:38:11 KST 2021
+#1 SMP PREEMPT Mon May 20 22:49:24 CST 2019
+#1 SMP PREEMPT Tue Sep 17 11:35:02 CST 2019
+#1 SMP PREEMPT Fri May 7 23:57:33 CST 2021
+#1 SMP PREEMPT Thu Apr 9 12:20:19 CST 2020
+#1 SMP PREEMPT Tue Aug 4 12:06:24 CST 2020
+#1 SMP PREEMPT Mon May 28 11:48:52 KST 2018
+#1 SMP PREEMPT Tue Jun 4 10:32:16 CST 2019
+#1 SMP PREEMPT Fri Apr 22 12:24:51 CST 2022
+#1 SMP PREEMPT Fri May 13 11:11:21 CST 2022
+#1 SMP PREEMPT Wed Mar 2 14:47:35 +07 2022
+#1 SMP PREEMPT Fri Nov 30 06:43:41 CST 2018
+#1 SMP PREEMPT Tue May 25 15:04:39 CST 2021
+#1 SMP PREEMPT Sat Jan 11 23:56:03 CST 2020
+#2 SMP PREEMPT Sat Aug 25 13:51:31 CST 2018
+#1 SMP PREEMPT Fri May 17 12:34:58 2019
+#1 SMP PREEMPT Thu Jan 3 18:39:30 CST 2019
+#1 SMP PREEMPT Mon Nov 5 11:05:49 CST 2018
+#3 SMP PREEMPT Thu Aug 6 14:58:36 KST 2020
+#1 SMP PREEMPT Mon Oct 14 15:49:04 CST 2019
+#1 SMP PREEMPT Thu Apr 28 06:20:28 KST 2022
+#1 SMP PREEMPT Wed Feb 12 08:24:18 CST 2020
+#1 SMP PREEMPT Fri Sep 4 19:06:02 CST 2020
+#2 SMP PREEMPT Mon Oct 22 10:52:44 CST 2018
+#1 SMP PREEMPT Sat Jan 15 23:08:40 CST 2022
+#2 SMP PREEMPT Fri Jun 15 17:23:14 CST 2018
+#1 SMP PREEMPT Tue Jan 5 17:51:02 CST 2021
+#3 SMP PREEMPT Thu Nov 26 13:49:22 CST 2020
+#23 SMP PREEMPT Wed Aug 4 11:46:28 CST 2021
+#1 SMP PREEMPT Tue May 24 15:31:45 CST 2022
+#1 SMP PREEMPT Mon Jun 3 19:10:57 CST 2019
+#1 SMP PREEMPT Wed Nov 27 20:14:08 KST 2019
+#1 SMP PREEMPT Thu Apr 5 20:12:29 KST 2018
+#1 SMP PREEMPT Thu Feb 1 20:56:19 CST 2018
+#2 SMP PREEMPT Tue Apr 17 18:20:35 CST 2018
+#1 SMP PREEMPT Tue Jul 10 09:34:09 CST 2018
+#1 SMP PREEMPT Sun Dec 20 05:49:44 KST 2020
+#1 SMP PREEMPT Tue Jul 28 19:23:54 CST 2020
+#1 SMP PREEMPT Tue Mar 26 23:39:17 CST 2019
+#1 SMP PREEMPT Wed Aug 4 12:09:36 CST 2021
+#1 SMP PREEMPT Wed Nov 18 22:00:19 CST 2020
+#1 SMP PREEMPT Wed Nov 25 12:55:14 CST 2020
+#1 SMP PREEMPT Mon Feb 10 13:45:15 CST 2020
+#1 SMP PREEMPT Thu Nov 23 22:01:09 IST 2017
+#1 SMP PREEMPT Tue Mar 27 10:12:14 CST 2018
+#1 SMP PREEMPT Sat Aug 10 03:27:19 KST 2019
+#1 SMP PREEMPT Wed Feb 12 11:13:58 CST 2020
+#1 SMP PREEMPT Wed Jan 12 15:01:26 CST 2022
+#1 SMP PREEMPT Sat Oct 9 18:01:12 CST 2021
+#2 SMP PREEMPT Thu Aug 1 13:31:37 CST 2019
+#1 SMP PREEMPT Tue Apr 27 12:02:37 CST 2021
+#1 SMP PREEMPT Thu Aug 12 14:59:16 CST 2021
+#2 SMP PREEMPT Sat Jan 18 09:46:55 KST 2020
+#1 SMP PREEMPT Wed Jul 15 11:02:11 CST 2020
+#1 SMP PREEMPT Wed Feb 19 04:26:58 CST 2020
+#1 SMP PREEMPT Tue Jul 21 21:21:24 CST 2020
+#1 SMP PREEMPT Tue May 28 20:03:24 KST 2019
+#2 SMP PREEMPT Fri Nov 6 00:11:10 CST 2020
+#2 SMP PREEMPT Thu Oct 28 19:28:41 CST 2021
+#1 SMP PREEMPT Sat Dec 26 21:26:13 CST 2020
+#2 SMP PREEMPT Thu Aug 1 16:55:24 UTC 2019
+#1 SMP PREEMPT Tue Oct 8 19:00:04 CST 2019
+#34 SMP PREEMPT Thu Jun 17 09:07:06 CST 2021
+#1 SMP PREEMPT Mon Aug 6 13:23:22 CST 2018
+#1 SMP PREEMPT Thu Feb 14 12:49:55 CST 2019
+#1 SMP PREEMPT Tue Jan 23 22:53:00 CST 2018
+#1 SMP PREEMPT Mon Apr 8 17:05:26 HKT 2019
+#1 SMP PREEMPT Tue Mar 3 11:30:09 GMT 2020
+#2 SMP PREEMPT Thu Mar 22 18:07:03 CST 2018
+#1 SMP PREEMPT Tue Sep 4 18:05:09 KST 2018
+#1 SMP PREEMPT Thu Nov 5 09:21:14 CST 2020
+#1 SMP PREEMPT Thu Sep 19 11:40:32 CST 2019
+#2 SMP PREEMPT Mon Apr 26 08:02:33 KST 2021
+#1 SMP PREEMPT Tue Nov 5 20:06:27 CST 2019
+#1 SMP PREEMPT Wed May 16 01:37:26 CST 2018
+#1 SMP PREEMPT Thu Apr 2 18:29:36 PDT 2020
+#1 SMP PREEMPT Tue Jul 3 18:35:38 BRT 2018
+#1 SMP PREEMPT Thu Mar 3 00:03:04 CST 2022
+#1 SMP PREEMPT Fri Oct 9 11:26:04 CDT 2020
+#1 SMP PREEMPT Mon Jul 26 11:46:33 KST 2021
+#1 SMP PREEMPT Thu Jan 28 02:53:32 CST 2021
+#1 SMP PREEMPT Fri Jul 5 05:38:27 CST 2019
+#1 SMP PREEMPT Fri Jun 15 03:28:23 JST 2018
+#1 SMP PREEMPT Fri Sep 14 13:06:30 CST 2018
+#1 SMP PREEMPT Fri Nov 29 19:18:20 CST 2019
+#1 SMP PREEMPT Mon Jan 24 11:42:21 CST 2022
+#1 SMP PREEMPT Wed Oct 28 19:40:04 KST 2020
+#1 SMP PREEMPT Thu Jan 6 14:48:39 CST 2022
+#2 SMP PREEMPT Wed Apr 15 14:44:10 CST 2020
+#1 SMP PREEMPT Sat May 12 19:35:36 HKT 2018
+#1 SMP PREEMPT Mon May 2 11:21:56 CDT 2022
+#1 SMP PREEMPT Mon Jun 4 10:52:49 KST 2018
+#1 SMP PREEMPT Thu Feb 17 23:22:56 CST 2022
+#1 repo:r-tv-dev SMP PREEMPT Tue Nov 23 18:53:00 CST 2021
+#1 SMP PREEMPT Fri May 8 17:59:28 CST 2020
diff --git a/libraries/sts-common-util/util/tests/src/com/android/sts/common/util/StsLogicTest.java b/libraries/sts-common-util/util/tests/src/com/android/sts/common/util/StsLogicTest.java
new file mode 100644
index 0000000..3530e6d
--- /dev/null
+++ b/libraries/sts-common-util/util/tests/src/com/android/sts/common/util/StsLogicTest.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2022 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.sts.common.util;
+
+import static org.junit.Assert.*;
+
+import com.android.compatibility.common.util.BusinessLogicMapStore;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+
+import java.time.LocalDate;
+import java.util.Arrays;
+import java.util.Optional;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class StsLogicTest extends BaseHostJUnit4Test {
+
+    private static final long[] PLATFORM_BUG = {1_000_000_000};
+    private static final long[] KERNEL_BUG = {2_000_000_000};
+
+    static {
+        new BusinessLogicSetStore()
+                .putSet(
+                        "kernel_bugs",
+                        Arrays.stream(KERNEL_BUG).mapToObj(Long::toString).toArray(String[]::new));
+        new BusinessLogicMapStore().putMap("security_bulletins", null);
+        new BusinessLogicMapStore().putMap("sts_modification_times", null);
+    }
+
+    @Test
+    public final void testGetDeviceSplForPlatformBug() throws Exception {
+        StsLogic logic =
+                new StsLogicMock()
+                        .setCveBugIds(PLATFORM_BUG)
+                        .setPlatformSpl("2022-01-01")
+                        .setKernelBuildDate("2020-01-01")
+                        .setShouldUseKernelSpl(true);
+        assertEquals(
+                "should use platform SPL because this is not a kernel test."
+                        + BusinessLogicSetStore.getSet("kernel_bugs"),
+                logic.getPlatformSpl(),
+                logic.getDeviceSpl());
+    }
+
+    @Test
+    public final void testGetDeviceSplUsesKernelBuildDate() throws Exception {
+        StsLogic logic =
+                new StsLogicMock()
+                        .setCveBugIds(KERNEL_BUG)
+                        .setPlatformSpl("2022-01-01")
+                        .setKernelBuildDate("2020-01-01")
+                        .setShouldUseKernelSpl(true);
+        assertEquals(
+                "should use kernel build date because the kernel stopped being updated.",
+                logic.getKernelBuildDate().get(),
+                logic.getDeviceSpl());
+    }
+
+    @Test
+    public final void testGetDeviceSplForKernelBugWithBadKernelBuildDate() throws Exception {
+        StsLogic logic =
+                new StsLogicMock()
+                        .setCveBugIds(KERNEL_BUG)
+                        .setPlatformSpl("2022-01-01")
+                        .setKernelBuildDate(null)
+                        .setShouldUseKernelSpl(true);
+        assertEquals(
+                "should use platform SPL because the kernel build date couldn't be read.",
+                logic.getPlatformSpl(),
+                logic.getDeviceSpl());
+    }
+
+    @Test
+    public final void testGetDeviceSplUsesPlatformSplWhenShouldntUseKernel() throws Exception {
+        StsLogic logic =
+                new StsLogicMock()
+                        .setCveBugIds(KERNEL_BUG)
+                        .setPlatformSpl("2022-01-01")
+                        .setKernelBuildDate("2020-01-01")
+                        .setShouldUseKernelSpl(false);
+        assertEquals(
+                "should use platform SPL because the option to use kernel build date was off.",
+                logic.getPlatformSpl(),
+                logic.getDeviceSpl());
+    }
+
+    @Test
+    public final void testGetDeviceSplSkipsKernelBuildDateWhenRecent() throws Exception {
+        StsLogic logic =
+                new StsLogicMock()
+                        .setCveBugIds(KERNEL_BUG)
+                        .setPlatformSpl("2022-02-28")
+                        .setKernelBuildDate("2022-01-01")
+                        .setShouldUseKernelSpl(true);
+        assertEquals(
+                "should use platform spl because the kernel is too new.",
+                logic.getPlatformSpl(),
+                logic.getDeviceSpl());
+    }
+
+    private static class StsLogicMock implements StsLogic {
+
+        private long[] cveBugIds = PLATFORM_BUG;
+        private LocalDate platformSpl;
+        private LocalDate releaseBulletinSpl;
+        private Optional<LocalDate> kernelBuildDate;
+        private boolean shouldUseKernelSpl = false;
+
+        {
+            setPlatformSpl("2022-01-01");
+            setReleaseBulletinSpl("2022-01-01");
+            setKernelBuildDate("2022-01-01");
+        }
+
+        public StsLogicMock setCveBugIds(long... cveBugIds) {
+            this.cveBugIds = cveBugIds;
+            return this;
+        }
+
+        public StsLogicMock setPlatformSpl(String platformSpl) {
+            this.platformSpl = SplUtils.localDateFromSplString(platformSpl);
+            return this;
+        }
+
+        public StsLogicMock setReleaseBulletinSpl(String releaseBulletinSpl) {
+            this.releaseBulletinSpl = SplUtils.localDateFromSplString(releaseBulletinSpl);
+            return this;
+        }
+
+        public StsLogicMock setKernelBuildDate(String kernelBuildDate) {
+            if (kernelBuildDate == null) {
+                this.kernelBuildDate = Optional.empty();
+                return this;
+            }
+            this.kernelBuildDate = Optional.of(SplUtils.localDateFromSplString(kernelBuildDate));
+            return this;
+        }
+
+        public StsLogicMock setShouldUseKernelSpl(boolean shouldUseKernelSpl) {
+            this.shouldUseKernelSpl = shouldUseKernelSpl;
+            return this;
+        }
+
+        @Override
+        public Description getTestDescription() {
+            throw new UnsupportedOperationException(
+                    "Please override the method that provides the details from the test"
+                            + " Description");
+        }
+
+        @Override
+        public long[] getCveBugIds() {
+            return this.cveBugIds;
+        }
+
+        @Override
+        public LocalDate getPlatformSpl() {
+            return this.platformSpl;
+        }
+
+        @Override
+        public Optional<LocalDate> getKernelBuildDate() {
+            return this.kernelBuildDate;
+        }
+
+        @Override
+        public boolean shouldUseKernelSpl() {
+            return this.shouldUseKernelSpl;
+        }
+
+        @Override
+        public LocalDate getReleaseBulletinSpl() {
+            return this.releaseBulletinSpl;
+        }
+
+        @Override
+        public void logInfo(String logTag, String format, Object... args) {
+            // log nothing
+        }
+
+        @Override
+        public void logDebug(String logTag, String format, Object... args) {
+            // log nothing
+        }
+
+        @Override
+        public void logWarn(String logTag, String format, Object... args) {
+            // log nothing
+        }
+
+        @Override
+        public void logError(String logTag, String format, Object... args) {
+            // log nothing
+        }
+    }
+}
diff --git a/libraries/sts-common-util/util/tests/src/com/android/sts/common/util/UnameVersionTest.java b/libraries/sts-common-util/util/tests/src/com/android/sts/common/util/UnameVersionTest.java
new file mode 100644
index 0000000..69a902d
--- /dev/null
+++ b/libraries/sts-common-util/util/tests/src/com/android/sts/common/util/UnameVersionTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 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.sts.common.util;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class UnameVersionTest extends BaseHostJUnit4Test {
+
+    // https://plx.corp.google.com/scripts2/script_62._8e8d32_0000_2455_b2ca_3c286d390792
+    // manually removed csv header
+    // manually removed because no year:
+    //     "#1 repo:AndroidTV_11_AML_Genesis SMP PREEMPT Fri Sep 10 05:36:27"
+    private static final String UNAME_VERSIONS_RESOURCE = "edi_uname_versions.txt";
+
+    @Test
+    public final void testParseBuildTimestamp() throws Exception {
+        List<String> violations = new ArrayList<>();
+        BufferedReader reader =
+                new BufferedReader(
+                        new InputStreamReader(
+                                getClass()
+                                        .getClassLoader()
+                                        .getResourceAsStream(UNAME_VERSIONS_RESOURCE)));
+        String version = null;
+        while ((version = reader.readLine()) != null) {
+            Optional<LocalDate> ts = UnameVersion.parseBuildTimestamp(version);
+            if (!ts.isPresent()) {
+                violations.add(version);
+            }
+        }
+        assertTrue(violations.toString(), violations.isEmpty());
+    }
+}
diff --git a/libraries/system-helpers/sysui-helper/Android.bp b/libraries/system-helpers/sysui-helper/Android.bp
index b61c1bc..e632915 100644
--- a/libraries/system-helpers/sysui-helper/Android.bp
+++ b/libraries/system-helpers/sysui-helper/Android.bp
@@ -23,6 +23,7 @@
     static_libs: [
         "ub-uiautomator",
         "androidx.test.rules",
+        "androidx.test.uiautomator",
         "activity-helper",
         "commands-helper",
         "device-helper",
diff --git a/libraries/system-helpers/sysui-helper/src/android/system/helpers/LockscreenHelper.java b/libraries/system-helpers/sysui-helper/src/android/system/helpers/LockscreenHelper.java
index 9717c46..ad9b413 100644
--- a/libraries/system-helpers/sysui-helper/src/android/system/helpers/LockscreenHelper.java
+++ b/libraries/system-helpers/sysui-helper/src/android/system/helpers/LockscreenHelper.java
@@ -50,6 +50,9 @@
     private static final int SWIPE_MARGIN_BOTTOM = 100;
     private static final int DEFAULT_FLING_STEPS = 5;
     private static final int DEFAULT_SCROLL_STEPS = 15;
+    private static final long MAX_SCREEN_LOCK_WAIT_TIME_MS = 5_000;
+    private static final BySelector SCREEN_LOCK =
+            By.res("com.android.systemui", "keyguard_bottom_area");
     private static final String PIN_ENTRY = "com.android.systemui:id/pinEntry";
     private static final String SET_PIN_COMMAND = "locksettings set-pin %s";
     private static final String SET_PASSWORD_COMMAND = "locksettings set-password %s";
@@ -414,6 +417,10 @@
         mDevice.wait(Until.findObject(By.text("DONE")), LONG_TIMEOUT).click();
     }
 
+    public void waitLockscreenVisible() {
+        mDevice.wait(Until.hasObject(SCREEN_LOCK), MAX_SCREEN_LOCK_WAIT_TIME_MS);
+    }
+
     /* Returns screen coordinates for each pattern dot
      * for the current device
      * Represented as follows by chars
diff --git a/libraries/system-helpers/sysui-helper/src/android/system/helpers/NavigationControlHelper.java b/libraries/system-helpers/sysui-helper/src/android/system/helpers/NavigationControlHelper.java
new file mode 100644
index 0000000..0abf187
--- /dev/null
+++ b/libraries/system-helpers/sysui-helper/src/android/system/helpers/NavigationControlHelper.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.system.helpers;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+
+public class NavigationControlHelper {
+
+    private static final BySelector NAVIGATION_BAR_VIEW =
+            By.res("com.android.systemui", "navigation_bar_view");
+
+    private static final UiDevice sDevice =
+            UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+    public static void assertNavigationBarNotVisible() {
+        final UiObject2 navBarObject = sDevice.findObject(NAVIGATION_BAR_VIEW);
+        if (navBarObject != null) {
+            throw new AssertionError("Navigation bar is visible, expected: invisible");
+        }
+    }
+}
diff --git a/libraries/system-helpers/sysui-helper/src/android/system/helpers/QuickSettingsHelper.java b/libraries/system-helpers/sysui-helper/src/android/system/helpers/QuickSettingsHelper.java
index 4955cd3..8655366 100644
--- a/libraries/system-helpers/sysui-helper/src/android/system/helpers/QuickSettingsHelper.java
+++ b/libraries/system-helpers/sysui-helper/src/android/system/helpers/QuickSettingsHelper.java
@@ -16,64 +16,107 @@
 
 package android.system.helpers;
 
+import static android.content.Context.CONTEXT_IGNORE_SECURITY;
+
 import android.app.Instrumentation;
-import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.graphics.Point;
 import android.provider.Settings;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
 
 import org.junit.Assert;
 
-/**
- * Implement common helper methods for Quick settings.
- */
-public class QuickSettingsHelper {
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
-    private UiDevice mDevice = null;
-    private ContentResolver mResolver;
-    private Instrumentation mInstrumentation;
+/** Implement common helper methods for Quick settings. */
+public class QuickSettingsHelper {
+    private static final String LOG_TAG = QuickSettingsHelper.class.getSimpleName();
     private static final int LONG_TIMEOUT = 2000;
     private static final int SHORT_TIMEOUT = 500;
+    private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
+    private static final String QS_DEFAULT_TILES_RES = "quick_settings_tiles_default";
+    private static final BySelector FOOTER_SELECTOR = By.res(SYSTEMUI_PACKAGE, "qs_footer");
+    private static final String SYSUI_QS_TILES_SETTING = "sysui_qs_tiles";
 
-    public QuickSettingsHelper(UiDevice device, Instrumentation inst, ContentResolver resolver) {
+    @NonNull private final UiDevice mDevice;
+    @NonNull private final Instrumentation mInstrumentation;
+    private List<String> mDefaultQSTileList = null;
+    private List<String> mPreviousQSTileList = null;
+
+    public QuickSettingsHelper(@NonNull UiDevice device, @NonNull Instrumentation inst) {
         this.mDevice = device;
         mInstrumentation = inst;
-        mResolver = resolver;
+        try {
+            obtainDefaultQSTiles();
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "obtainDefaultQSTiles fails!", e);
+        }
+    }
+
+    private void obtainDefaultQSTiles() throws PackageManager.NameNotFoundException {
+        final Context sysUIContext =
+                mInstrumentation
+                        .getContext()
+                        .createPackageContext(SYSTEMUI_PACKAGE, CONTEXT_IGNORE_SECURITY);
+        final int qsTileListResId =
+                sysUIContext
+                        .getResources()
+                        .getIdentifier(QS_DEFAULT_TILES_RES, "string", SYSTEMUI_PACKAGE);
+        final String defaultQSTiles = sysUIContext.getString(qsTileListResId);
+        mDefaultQSTileList = Arrays.asList(defaultQSTiles.split(","));
     }
 
     public enum QuickSettingDefaultTiles {
-        WIFI("Wi-Fi"), SIM("Mobile data"), DND("Do not disturb"), FLASHLIGHT("Flashlight"), SCREEN(
-                "Auto-rotate screen"), BLUETOOTH("Bluetooth"), AIRPLANE("Airplane mode"),
-                BRIGHTNESS("Display brightness");
+        WIFI("Wi-Fi"),
+        SIM("Mobile data"),
+        DND("Do not disturb"),
+        FLASHLIGHT("Flashlight"),
+        SCREEN("Auto-rotate screen"),
+        BLUETOOTH("Bluetooth"),
+        AIRPLANE("Airplane mode"),
+        BRIGHTNESS("Display brightness");
 
         private final String name;
 
-        private QuickSettingDefaultTiles(String name) {
+        QuickSettingDefaultTiles(String name) {
             this.name = name;
         }
 
         public String getName() {
             return this.name;
         }
-    };
+    }
 
     public enum QuickSettingEditMenuTiles {
-        LOCATION("Location"), HOTSPOT("Hotspot"), INVERTCOLORS("Invert colors"),
-                DATASAVER("Data Saver"), CAST("Cast"), NEARBY("Nearby");
+        LOCATION("Location"),
+        HOTSPOT("Hotspot"),
+        INVERTCOLORS("Invert colors"),
+        DATASAVER("Data Saver"),
+        CAST("Cast"),
+        NEARBY("Nearby");
 
         private final String name;
 
-        private QuickSettingEditMenuTiles(String name) {
+        QuickSettingEditMenuTiles(String name) {
             this.name = name;
         }
 
         public String getName() {
             return this.name;
         }
-    };
+    }
 
     public void addQuickSettingTileFromEditMenu(String quickSettingTile,
             String quickSettingTileToReplace, String quickSettingTileToCheckForInCSV)
@@ -106,29 +149,86 @@
         mDevice.wait(Until.findObject(By.descContains("Navigate up")), LONG_TIMEOUT);
         // Retrieve the quick settings CSV string and verify that the newly
         // added item is present.
-        String quickSettingsList = Settings.Secure.getString
-                (mInstrumentation.getContext().getContentResolver(),
-                "sysui_qs_tiles");
-        Assert.assertTrue(quickSettingTile + " not present in qs tiles after addition.",
+        String quickSettingsList =
+                Settings.Secure.getString(
+                        mInstrumentation.getContext().getContentResolver(), SYSUI_QS_TILES_SETTING);
+        Assert.assertTrue(
+                quickSettingTile + " not present in qs tiles after addition.",
                 quickSettingsList.contains(quickSettingTileToCheckForInCSV));
     }
 
-    public void setQuickSettingsDefaultTiles() throws Exception {
-        modifyListOfQuickSettingsTiles
-                ("wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location");
+    /** Sets default quick settings tile list pre-load in SystemUI resource. */
+    public void setQuickSettingsDefaultTiles() {
+        modifyQSTileList(mDefaultQSTileList);
     }
 
-    public void modifyListOfQuickSettingsTiles(String commaSeparatedList) throws Exception {
-        Settings.Secure.putString(mInstrumentation.getContext().getContentResolver(),
-                "sysui_qs_tiles", commaSeparatedList);
-        Thread.sleep(LONG_TIMEOUT);
+    /** Gets the default list of QuickSettings */
+    public List<String> getQSDefaultTileList() {
+        return mDefaultQSTileList;
     }
 
-    public void launchQuickSetting() throws Exception {
+    /**
+     * Set the tileName to be the first item for QS tiles.
+     *
+     * @param tileName tile name that will been set to the first position.
+     */
+    public void setFirstQS(@NonNull String tileName) {
+        String previousQSTiles =
+                Settings.Secure.getString(
+                        mInstrumentation.getContext().getContentResolver(), SYSUI_QS_TILES_SETTING);
+        mPreviousQSTileList = Arrays.asList(previousQSTiles.split(","));
+
+        ArrayList<String> list = new ArrayList<>(mPreviousQSTileList);
+        for (int i = 0; i < list.size(); ++i) {
+            if (TextUtils.equals(tileName, list.get(i))) {
+                list.remove(i);
+                break;
+            }
+        }
+        list.add(0, tileName);
+        modifyQSTileList(list);
+    }
+
+    /** Reset to previous QS tile list if exist */
+    public void resetToPreviousQSTileList() {
+        if (mPreviousQSTileList == null) {
+            return;
+        }
+        modifyQSTileList(mPreviousQSTileList);
+    }
+
+    /**
+     * Sets customized tile list to secure settings entry 'sysui_qs_tiles' directly.
+     *
+     * @param list The quick settings tile list to be set
+     */
+    public void modifyQSTileList(@NonNull List<String> list) {
+        if (list.isEmpty()) {
+            return;
+        }
+
+        try {
+            Settings.Secure.putString(
+                    mInstrumentation.getContext().getContentResolver(),
+                    SYSUI_QS_TILES_SETTING,
+                    String.join(",", list));
+            Thread.sleep(LONG_TIMEOUT);
+        } catch (Resources.NotFoundException | InterruptedException e) {
+            Log.e(LOG_TAG, "modifyQSTileList fails!", e);
+        }
+    }
+
+    /** Opens quick settings panel through {@link UiDevice#openQuickSettings()} */
+    public void launchQuickSetting() {
         mDevice.pressHome();
-        swipeDown();
-        Thread.sleep(LONG_TIMEOUT);
-        swipeDown();
+        mDevice.openQuickSettings();
+        // Quick Settings isn't always open when this is complete. Explicitly wait for the Quick
+        // Settings footer to make sure that the buttons are accessible when the bar is open and
+        // this call is complete.
+        mDevice.wait(Until.findObject(FOOTER_SELECTOR), SHORT_TIMEOUT);
+        // Wait an extra bit for the animation to complete. If we return to early, future callers
+        // that are trying to find the location of the footer will get incorrect coordinates
+        mDevice.waitForIdle(LONG_TIMEOUT);
     }
 
     public void swipeUp() throws Exception {
@@ -138,8 +238,12 @@
     }
 
     public void swipeDown() throws Exception {
-        mDevice.swipe(mDevice.getDisplayWidth() / 2, 0, mDevice.getDisplayWidth() / 2,
-                mDevice.getDisplayHeight() / 2 + 50, 20);
+        mDevice.swipe(
+                mDevice.getDisplayWidth() / 2,
+                0,
+                mDevice.getDisplayWidth() / 2,
+                mDevice.getDisplayHeight(),
+                20);
         Thread.sleep(SHORT_TIMEOUT);
     }
 
diff --git a/libraries/system-helpers/sysui-helper/src/android/system/helpers/ThemeHelper.java b/libraries/system-helpers/sysui-helper/src/android/system/helpers/ThemeHelper.java
new file mode 100644
index 0000000..357d4e4
--- /dev/null
+++ b/libraries/system-helpers/sysui-helper/src/android/system/helpers/ThemeHelper.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.system.helpers;
+
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapRegionDecoder;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.text.TextUtils;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.uiautomator.UiDevice;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/** A common helper class for theme scenario tests. */
+public class ThemeHelper {
+
+    private static final float COLOR_TOLERANCE = 0.01F;
+
+    @NonNull private final UiDevice mUiDevice;
+    @NonNull private final Context mContext;
+    @NonNull private final WallpaperManager mWallpaperManager;
+
+    public ThemeHelper(@NonNull UiDevice uiDevice, @NonNull Context context) {
+        mUiDevice = uiDevice;
+        mContext = context;
+        mWallpaperManager =
+                Objects.requireNonNull(mContext.getSystemService(WallpaperManager.class));
+    }
+
+    /**
+     * Sets wallpaper
+     *
+     * @param color the color for wallpaper
+     * @throws IOException exception during setWallpaper
+     */
+    public void setWallpaper(@ColorInt int color) throws IOException {
+        final Bitmap bitmap =
+                Bitmap.createBitmap(
+                        mUiDevice.getDisplayWidth(),
+                        mUiDevice.getDisplayHeight(),
+                        Bitmap.Config.ARGB_8888);
+        bitmap.eraseColor(color);
+
+        final byte[] byteArray;
+        try {
+            final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+            final boolean compressResult =
+                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);
+            if (!compressResult) {
+                throw new IOException("Fail to compress bitmap");
+            }
+            byteArray = outStream.toByteArray();
+            mWallpaperManager.setStream(
+                    new ByteArrayInputStream(byteArray), null, false, FLAG_SYSTEM);
+        } finally {
+            bitmap.recycle();
+        }
+    }
+
+    /**
+     * Gets primary color from WallpaperColors
+     *
+     * @return primary color
+     */
+    @ColorInt
+    public int getWallpaperPrimaryColor() {
+        return mWallpaperManager.getWallpaperColors(FLAG_SYSTEM).getPrimaryColor().toArgb();
+    }
+
+    private void deleteFileIfExist(@Nullable String fileAbsPath) {
+        if (TextUtils.isEmpty(fileAbsPath)) {
+            return;
+        }
+        new File(fileAbsPath).deleteOnExit();
+    }
+
+    /**
+     * Gets highest count of color inside this bitmap
+     *
+     * @param bitmap the bitmap for analysis
+     * @return most rendered color
+     */
+    @NonNull
+    public Color getMostColor(@NonNull Bitmap bitmap) {
+        HashMap<Integer, Integer> colors = new HashMap<>();
+        for (int x = 0; x < bitmap.getWidth(); ++x) {
+            for (int y = 0; y < bitmap.getHeight(); ++y) {
+                @ColorInt int color = bitmap.getColor(x, y).toArgb();
+                colors.put(color, colors.containsKey(color) ? colors.get(color) + 1 : 1);
+            }
+        }
+        List<Map.Entry<Integer, Integer>> colorList = new ArrayList<>(colors.entrySet());
+        final Map.Entry<Integer, Integer> mostColorCountEntry =
+                colorList.stream().max(Comparator.comparingInt(Map.Entry::getValue)).get();
+        return Color.valueOf(mostColorCountEntry.getKey());
+    }
+
+    private String takeScreenshotToFile(@NonNull String filename) {
+        File f = new File(mContext.getFilesDir(), filename);
+        mUiDevice.takeScreenshot(f);
+        if (f.exists()) {
+            return f.getAbsolutePath();
+        }
+        return null;
+    }
+
+    /**
+     * Takes a screenshot and calculates in the specific rect
+     *
+     * @param rect the rect for calculating the most rendered color
+     * @return most rendered color
+     * @throws IOException exception if taking screenshot fails
+     */
+    public Color getScreenshotMostColorAsRect(@NonNull Rect rect) throws IOException {
+        String fileAbsPath = null;
+        Bitmap bitmap = null;
+        BitmapRegionDecoder bitmapRegionDecoder = null;
+        try {
+            fileAbsPath = takeScreenshotToFile("test1");
+            bitmapRegionDecoder = BitmapRegionDecoder.newInstance(fileAbsPath);
+            bitmap = bitmapRegionDecoder.decodeRegion(rect, null);
+            return getMostColor(bitmap);
+        } finally {
+            if (bitmap != null) {
+                bitmap.recycle();
+            }
+            if (bitmapRegionDecoder != null) {
+                bitmapRegionDecoder.recycle();
+            }
+            deleteFileIfExist(fileAbsPath);
+        }
+    }
+
+    /**
+     * Gets current activated Quick Setting background color.
+     *
+     * @return color The theme color for activated quick setting tile background
+     */
+    @NonNull
+    public Color getSysuiActivatedThemeColor() {
+        return Color.valueOf(mContext.getColor(android.R.color.system_accent1_100));
+    }
+
+    /**
+     * Validate the colors in between are similar or not.
+     *
+     * @param color1 the first color
+     * @param color2 the second color
+     * @return true if the colors in between are similar, false otherwise
+     */
+    public boolean isSimilarColor(@NonNull Color color1, @NonNull Color color2) {
+        return color1.alpha() == color2.alpha()
+                && Math.abs(color1.red() - color2.red()) < COLOR_TOLERANCE
+                && Math.abs(color1.green() - color2.green()) < COLOR_TOLERANCE
+                && Math.abs(color1.blue() - color2.blue()) < COLOR_TOLERANCE;
+    }
+}
diff --git a/scripts/perf-setup/mt6761-setup.sh b/scripts/perf-setup/mt6761-setup.sh
new file mode 100755
index 0000000..fe603f5
--- /dev/null
+++ b/scripts/perf-setup/mt6761-setup.sh
@@ -0,0 +1,47 @@
+#!/system/bin/sh
+#
+# Copyright (C) 2021 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.
+
+# Exit on errors, or whenever an unset variable is substituted
+set -eu
+
+stop thermal_manager
+setprop vendor.powerhal.init 0
+stop power-hal-1-0
+start power-hal-1-0
+
+echo 'Locking CPU to 1125000 (56% relative to max freq)'
+echo 11 > /proc/ppm/policy/ut_fix_freq_idx
+
+expected_gpu_freq='390000'
+echo "Locking GPU to ${expected_gpu_freq} (min freq)"
+echo "${expected_gpu_freq}" > /proc/gpufreq/gpufreq_opp_freq
+
+expected_cpu_freqs='1125000, 1125000, 1125000, 1125000'
+cpu_freqs="$(cat /sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_cur_freq | sed -z 's/\n/, /g')"
+echo "Set CPU frequencies to ${cpu_freqs}"
+if [[ "${cpu_freqs}" -ne "${expected_cpu_freqs}" ]]; then
+  echo "Failed to set CPUs to expected frequencies: ${expected_cpu_freqs}"
+  exit 1
+fi
+
+gpu_freq="$(cat /proc/gpufreq/gpufreq_opp_freq | tail -n 1 | cut -d '=, ' -f 5)"
+echo "Set GPU frequency to ${gpu_freq}"
+if [[ "${gpu_freq}" -ne "${expected_gpu_freq}" ]]; then
+  echo "Failed to set GPU to expected frequency: ${expected_gpu_freq}"
+  exit 1
+fi
+
+
diff --git a/scripts/perf-setup/r4o6-setup.sh b/scripts/perf-setup/r4o6-setup.sh
new file mode 100755
index 0000000..f684ce2
--- /dev/null
+++ b/scripts/perf-setup/r4o6-setup.sh
@@ -0,0 +1,63 @@
+#!/system/bin/sh
+#
+# Copyright (C) 2022 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.
+
+# Performance test setup for 2021 devices
+
+echo "Disabling Tskin thermal mitigation..."
+setprop persist.vendor.disable.thermal.control 1
+
+echo "Disabling TJ thermal mitigation..."
+setprop persist.vendor.disable.thermal.tj.control 1
+
+echo "Clearing cooling device states..."
+for i in /sys/devices/virtual/thermal/cooling_device*/user_vote; do echo 0 > "$i" 2>/dev/null; done
+for i in /sys/devices/virtual/thermal/cooling_device*/cur_state; do echo 0 > "$i" 2>/dev/null; done
+
+echo "Disabling powerhints..."
+setprop vendor.powerhal.init 0
+setprop ctl.restart vendor.power-hal-aidl
+
+echo "Locking LITTLE CPUs to the max freq..."
+echo 1803000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
+echo 1803000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq
+cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
+
+echo "Locking MID CPUs to the max freq..."
+echo 2253000 > /sys/devices/system/cpu/cpu4/cpufreq/scaling_max_freq
+echo 2253000 > /sys/devices/system/cpu/cpu4/cpufreq/scaling_min_freq
+cat /sys/devices/system/cpu/cpu4/cpufreq/scaling_cur_freq
+
+echo "Locking BIG CPUs to the max freq..."
+echo 2802000 > /sys/devices/system/cpu/cpu6/cpufreq/scaling_max_freq
+echo 2802000 > /sys/devices/system/cpu/cpu6/cpufreq/scaling_min_freq
+cat /sys/devices/system/cpu/cpu6/cpufreq/scaling_cur_freq
+
+echo "Locking GPU to the max freq..."
+echo 848000 > /sys/devices/platform/1c500000.mali/scaling_max_freq
+echo 848000 > /sys/devices/platform/1c500000.mali/scaling_min_freq
+cat /sys/devices/platform/1c500000.mali/cur_freq
+
+echo "Locking MIF to the max freq..."
+echo 3172000 > /sys/devices/platform/17000010.devfreq_mif/devfreq/17000010.devfreq_mif/exynos_data/debug_scaling_devfreq_max
+echo 3172000 > /sys/devices/platform/17000010.devfreq_mif/devfreq/17000010.devfreq_mif/exynos_data/debug_scaling_devfreq_min
+cat /sys/devices/platform/17000010.devfreq_mif/devfreq/17000010.devfreq_mif/cur_freq
+
+echo "Locking INT to the max freq..."
+echo 533000 > /sys/devices/platform/17000020.devfreq_int/devfreq/17000020.devfreq_int/exynos_data/debug_scaling_devfreq_max
+echo 533000 > /sys/devices/platform/17000020.devfreq_int/devfreq/17000020.devfreq_int/exynos_data/debug_scaling_devfreq_min
+cat /sys/devices/platform/17000020.devfreq_int/devfreq/17000020.devfreq_int/cur_freq
+
+
diff --git a/scripts/perf-setup/tests/Android.bp b/scripts/perf-setup/tests/Android.bp
new file mode 100644
index 0000000..1e87fc8
--- /dev/null
+++ b/scripts/perf-setup/tests/Android.bp
@@ -0,0 +1,32 @@
+//Copyright (C) 2021 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "PerfSetupTests",
+
+    srcs: ["src/**/*.java"],
+    sdk_version: "test_current",
+    min_sdk_version: "21",
+
+    static_libs: ["androidx.test.runner"],
+    certificate: "platform",
+
+    test_config: "configs/perf-setup.xml",
+
+    test_suites: ["device-tests"],
+}
diff --git a/scripts/perf-setup/tests/AndroidManifest.xml b/scripts/perf-setup/tests/AndroidManifest.xml
new file mode 100644
index 0000000..90d4e04
--- /dev/null
+++ b/scripts/perf-setup/tests/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.test.perfsetup" >
+
+    <application/>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.test.perfsetup"
+                     android:label="Perf Setup Test"/>
+
+</manifest>
diff --git a/tests/example/instrumentation/AndroidTest.xml b/scripts/perf-setup/tests/configs/perf-setup.xml
similarity index 60%
copy from tests/example/instrumentation/AndroidTest.xml
copy to scripts/perf-setup/tests/configs/perf-setup.xml
index 05ad581..67f2a15 100644
--- a/tests/example/instrumentation/AndroidTest.xml
+++ b/scripts/perf-setup/tests/configs/perf-setup.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2021 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.
@@ -14,19 +14,24 @@
      limitations under the License.
 -->
 <configuration description="Runs sample instrumentation test.">
-    <option name="test-suite-tag" value="apct" />
-    <option name="test-suite-tag" value="apct-instrumentation" />
-    <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup" />
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
     <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
-        <option name="test-file-name" value="HelloWorldTests.apk" />
+        <option name="test-file-name" value="PerfSetupTests.apk" />
     </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer" />
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer" />
 
-    <option name="test-suite-tag" value="apct" />
-    <option name="test-tag" value="SampleInstrumentationTest" />
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="push" value="perf-setup.sh->/data/local/tmp/perf-setup.sh" />
+        <option name="cleanup" value="true" />
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="chmod 755 /data/local/tmp/perf-setup.sh" />
+        <option name="run-command" value="/data/local/tmp/perf-setup.sh" />,
+        <option name="throw-if-cmd-fail" value="true" />
+    </target_preparer>
+
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="android.test.example.helloworld" />
+        <option name="package" value="android.test.perfsetup" />
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
     </test>
 </configuration>
diff --git a/scripts/perf-setup/tests/src/android/test/perfsetup/PerfSetupTest.java b/scripts/perf-setup/tests/src/android/test/perfsetup/PerfSetupTest.java
new file mode 100644
index 0000000..81e4216
--- /dev/null
+++ b/scripts/perf-setup/tests/src/android/test/perfsetup/PerfSetupTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.perfsetup;
+
+import android.util.Log;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class PerfSetupTest {
+
+    private static final String TAG = PerfSetupTest.class.getSimpleName();
+
+    @BeforeClass
+    public static void beforeClass() {
+        Log.d(TAG, "beforeClass()");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        Log.d(TAG, "afterClass()");
+    }
+
+    @Before
+    public void before() {
+        Log.d(TAG, "before()");
+    }
+
+    @After
+    public void after() {
+        Log.d(TAG, "after()");
+    }
+
+    @Test
+    @SmallTest
+    public void testPerfSetup() {
+        Log.d(TAG, "testPerfSetup()");
+    }
+}
diff --git a/scripts/perfetto-setup/Android.mk b/scripts/perfetto-setup/Android.mk
index 1bc2f02..9944cad 100644
--- a/scripts/perfetto-setup/Android.mk
+++ b/scripts/perfetto-setup/Android.mk
@@ -35,6 +35,16 @@
 include $(BUILD_PREBUILT)
 
 include $(CLEAR_VARS)
+LOCAL_MODULE := long_trace_binder_config.textproto
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp
+LOCAL_PREBUILT_MODULE_FILE := prebuilts/tools/linux-x86_64/perfetto/configs/long_trace_binder_config.textproto
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
 LOCAL_MODULE := trace_config.textproto
 LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
 LOCAL_LICENSE_CONDITIONS := notice
diff --git a/tests/automotive/functional/appgrid/Android.bp b/tests/automotive/functional/appgrid/Android.bp
index 45cdcb8..54bb764 100644
--- a/tests/automotive/functional/appgrid/Android.bp
+++ b/tests/automotive/functional/appgrid/Android.bp
@@ -30,5 +30,5 @@
         "platform-test-options",
     ],
     srcs: ["src/**/*.java"],
-    test_suites: ["catbox","ats"],
+    test_suites: ["catbox"],
 }
diff --git a/tests/automotive/functional/dialer/Android.bp b/tests/automotive/functional/dialer/Android.bp
index d4571aa..0993fd1 100644
--- a/tests/automotive/functional/dialer/Android.bp
+++ b/tests/automotive/functional/dialer/Android.bp
@@ -31,5 +31,5 @@
         "platform-test-options",
     ],
     srcs: ["src/**/*.java"],
-    test_suites: ["catbox","ats"],
+    test_suites: ["catbox"],
 }
diff --git a/tests/automotive/functional/home/Android.bp b/tests/automotive/functional/home/Android.bp
index f62fb48..6659538 100644
--- a/tests/automotive/functional/home/Android.bp
+++ b/tests/automotive/functional/home/Android.bp
@@ -30,5 +30,5 @@
         "platform-test-options",
     ],
     srcs: ["src/**/*.java"],
-    test_suites: ["catbox","general-tests","ats"],
+    test_suites: ["catbox","general-tests"],
 }
diff --git a/tests/automotive/functional/lockscreen/Android.bp b/tests/automotive/functional/lockscreen/Android.bp
index 5668218..1ebe583 100644
--- a/tests/automotive/functional/lockscreen/Android.bp
+++ b/tests/automotive/functional/lockscreen/Android.bp
@@ -31,5 +31,5 @@
         "platform-test-options",
     ],
     srcs: ["src/**/*.java"],
-    test_suites: ["catbox","ats"],
+    test_suites: ["catbox"],
 }
diff --git a/tests/automotive/functional/mediacenter/Android.bp b/tests/automotive/functional/mediacenter/Android.bp
index 9d50197..5e3b35c 100644
--- a/tests/automotive/functional/mediacenter/Android.bp
+++ b/tests/automotive/functional/mediacenter/Android.bp
@@ -31,5 +31,5 @@
         "platform-test-options",
     ],
     srcs: ["src/**/*.java"],
-    test_suites: ["catbox","ats"],
+    test_suites: ["catbox"],
 }
diff --git a/tests/automotive/functional/multiuser/Android.bp b/tests/automotive/functional/multiuser/Android.bp
index a9526a9..c97679f 100644
--- a/tests/automotive/functional/multiuser/Android.bp
+++ b/tests/automotive/functional/multiuser/Android.bp
@@ -41,6 +41,6 @@
     ],
     srcs: ["src/**/*.java"],
     certificate: "platform",
-    test_suites: ["catbox","ats"],
+    test_suites: ["catbox"],
     privileged: true,
 }
diff --git a/tests/automotive/functional/multiuser/AndroidManifest.xml b/tests/automotive/functional/multiuser/AndroidManifest.xml
index 3a54471..868d7ff 100644
--- a/tests/automotive/functional/multiuser/AndroidManifest.xml
+++ b/tests/automotive/functional/multiuser/AndroidManifest.xml
@@ -16,6 +16,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="android.platform.tests" >
     <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="24" />
+    <uses-permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" />
+    <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
     <uses-permission android:name="android.permission.DUMP" />
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
diff --git a/tests/automotive/functional/navigationbar/Android.bp b/tests/automotive/functional/navigationbar/Android.bp
index 1d393bf..f728028 100644
--- a/tests/automotive/functional/navigationbar/Android.bp
+++ b/tests/automotive/functional/navigationbar/Android.bp
@@ -33,5 +33,5 @@
         "hamcrest-library",
     ],
     srcs: ["src/**/*.java"],
-    test_suites: ["catbox","ats"],
+    test_suites: ["catbox"],
 }
diff --git a/tests/automotive/functional/notifications/Android.bp b/tests/automotive/functional/notifications/Android.bp
index ea68de3..0fe19d4 100644
--- a/tests/automotive/functional/notifications/Android.bp
+++ b/tests/automotive/functional/notifications/Android.bp
@@ -29,5 +29,5 @@
         "hamcrest-library",
     ],
     srcs: ["src/**/*.java"],
-    test_suites: ["catbox","general-tests","ats"],
+    test_suites: ["catbox","general-tests"],
 }
diff --git a/tests/automotive/functional/settings/Android.bp b/tests/automotive/functional/settings/Android.bp
index 3367649..47322f5 100644
--- a/tests/automotive/functional/settings/Android.bp
+++ b/tests/automotive/functional/settings/Android.bp
@@ -30,5 +30,5 @@
         "platform-test-options",
     ],
     srcs: ["src/**/*.java"],
-    test_suites: ["catbox","general-tests","ats"],
+    test_suites: ["catbox","general-tests"],
 }
diff --git a/tests/automotive/functional/uxrestriction/Android.bp b/tests/automotive/functional/uxrestriction/Android.bp
index 4359d16..078c45d 100644
--- a/tests/automotive/functional/uxrestriction/Android.bp
+++ b/tests/automotive/functional/uxrestriction/Android.bp
@@ -32,5 +32,5 @@
         "automotive-app-grid-helper",
     ],
     srcs: ["src/**/*.java"],
-    test_suites: ["catbox","ats"],
+    test_suites: ["catbox"],
 }
diff --git a/tests/automotive/health/appgrid/tests/Android.bp b/tests/automotive/health/appgrid/tests/Android.bp
index 72daed8..ae5a377 100644
--- a/tests/automotive/health/appgrid/tests/Android.bp
+++ b/tests/automotive/health/appgrid/tests/Android.bp
@@ -34,5 +34,5 @@
         "platform-test-options",
     ],
     srcs: ["src/**/*.java"],
-    test_suites: ["catbox", "ats"],
+    test_suites: ["catbox"],
 }
diff --git a/tests/automotive/health/dial/tests/Android.bp b/tests/automotive/health/dial/tests/Android.bp
index 8213cdc..497f865 100644
--- a/tests/automotive/health/dial/tests/Android.bp
+++ b/tests/automotive/health/dial/tests/Android.bp
@@ -34,5 +34,5 @@
         "platform-test-options",
     ],
     srcs: ["src/**/*.java"],
-    test_suites: ["catbox","ats"],
+    test_suites: ["catbox"],
 }
diff --git a/tests/automotive/health/mediacenter/tests/Android.bp b/tests/automotive/health/mediacenter/tests/Android.bp
index 6866027..e9ddf15 100644
--- a/tests/automotive/health/mediacenter/tests/Android.bp
+++ b/tests/automotive/health/mediacenter/tests/Android.bp
@@ -34,5 +34,5 @@
         "platform-test-options",
     ],
     srcs: ["src/**/*.java"],
-    test_suites: ["catbox", "ats"],
+    test_suites: ["catbox"],
 }
diff --git a/tests/automotive/health/multiuser/tests/Android.bp b/tests/automotive/health/multiuser/tests/Android.bp
index 00ecd5e..8409848 100644
--- a/tests/automotive/health/multiuser/tests/Android.bp
+++ b/tests/automotive/health/multiuser/tests/Android.bp
@@ -41,6 +41,6 @@
     ],
     srcs: ["src/**/*.java"],
     certificate: "platform",
-    test_suites: ["catbox","ats"],
+    test_suites: ["catbox"],
     privileged: true,
 }
diff --git a/tests/automotive/health/notification/tests/Android.bp b/tests/automotive/health/notification/tests/Android.bp
index f44f925..f041ebc 100644
--- a/tests/automotive/health/notification/tests/Android.bp
+++ b/tests/automotive/health/notification/tests/Android.bp
@@ -34,5 +34,5 @@
         "platform-test-options",
     ],
     srcs: ["src/**/*.java"],
-    test_suites: ["catbox","ats"],
+    test_suites: ["catbox"],
 }
diff --git a/tests/automotive/health/settings/tests/Android.bp b/tests/automotive/health/settings/tests/Android.bp
index d380a79..0f102f5 100644
--- a/tests/automotive/health/settings/tests/Android.bp
+++ b/tests/automotive/health/settings/tests/Android.bp
@@ -34,5 +34,5 @@
         "platform-test-options",
     ],
     srcs: ["src/**/*.java"],
-    test_suites: ["catbox","ats"],
+    test_suites: ["catbox"],
 }
diff --git a/tests/bootdoa/fatal_allowlist b/tests/bootdoa/fatal_allowlist
index 2bb3e86..0b8ba88 100644
--- a/tests/bootdoa/fatal_allowlist
+++ b/tests/bootdoa/fatal_allowlist
@@ -1,2 +1,9 @@
+# b/181137192#comment21
 pcie\s*:\s*pcie_init.
+# b/181137192#comment38
 WMA\s-->\swmi_unified_attach\s-\ssuccess
+# b/222596773
+page\slast\spinned[0-9\S\s]+
+PFN\s[0-9]+[A-Za-z0-9\S\s]+swapbacked\S
+# b/222779442
+page_pinner[A-Za-z\S\s]+never\sset\S+
\ No newline at end of file
diff --git a/tests/example/instrumentation/Android.bp b/tests/example/instrumentation/Android.bp
index 20c6830..e1bfeb0 100644
--- a/tests/example/instrumentation/Android.bp
+++ b/tests/example/instrumentation/Android.bp
@@ -26,5 +26,11 @@
     static_libs: ["androidx.test.runner"],
     certificate: "platform",
 
+    test_config: "configs/hello-world.xml",
+
+    test_options: {
+        extra_test_configs: ["configs/hallo-welt.xml",],
+    },
+
     test_suites: ["device-tests"],
 }
diff --git a/tests/example/instrumentation/HelloWorldTests_HalloWelt.config b/tests/example/instrumentation/HelloWorldTests_HalloWelt.config
deleted file mode 100644
index 4c08548..0000000
--- a/tests/example/instrumentation/HelloWorldTests_HalloWelt.config
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<configuration description="Runs only the HalloWelt test.">
-    <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup" />
-    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
-        <option name="test-file-name" value="HelloWorldTests.apk" />
-    </target_preparer>
-
-    <option name="test-suite-tag" value="apct" />
-    <option name="test-tag" value="SampleInstrumentationTest" />
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="android.test.example.helloworld" />
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
-        <option name="class" value="android.test.example.helloworld.HelloWorldTest" />
-        <option name="method" value="testHalloWelt" />
-    </test>
-</configuration>
diff --git a/tests/example/instrumentation/AndroidTest.xml b/tests/example/instrumentation/configs/hallo-welt.xml
similarity index 84%
copy from tests/example/instrumentation/AndroidTest.xml
copy to tests/example/instrumentation/configs/hallo-welt.xml
index 05ad581..ea78bb5 100644
--- a/tests/example/instrumentation/AndroidTest.xml
+++ b/tests/example/instrumentation/configs/hallo-welt.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2021 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.
@@ -13,9 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="Runs sample instrumentation test.">
-    <option name="test-suite-tag" value="apct" />
-    <option name="test-suite-tag" value="apct-instrumentation" />
+<configuration description="Runs only the HalloWelt test.">
     <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup" />
     <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
         <option name="test-file-name" value="HelloWorldTests.apk" />
@@ -28,5 +26,7 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.test.example.helloworld" />
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="class" value="android.test.example.helloworld.HelloWorldTest" />
+        <option name="method" value="testHalloWelt" />
     </test>
 </configuration>
diff --git a/tests/example/instrumentation/AndroidTest.xml b/tests/example/instrumentation/configs/hello-world.xml
similarity index 88%
rename from tests/example/instrumentation/AndroidTest.xml
rename to tests/example/instrumentation/configs/hello-world.xml
index 05ad581..d22666b 100644
--- a/tests/example/instrumentation/AndroidTest.xml
+++ b/tests/example/instrumentation/configs/hello-world.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2021 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.
@@ -14,8 +14,6 @@
      limitations under the License.
 -->
 <configuration description="Runs sample instrumentation test.">
-    <option name="test-suite-tag" value="apct" />
-    <option name="test-suite-tag" value="apct-instrumentation" />
     <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup" />
     <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
         <option name="test-file-name" value="HelloWorldTests.apk" />
diff --git a/tests/functional/devicehealthchecks/assets/bug_map b/tests/functional/devicehealthchecks/assets/bug_map
index 9cbe057..3a7bdaa 100644
--- a/tests/functional/devicehealthchecks/assets/bug_map
+++ b/tests/functional/devicehealthchecks/assets/bug_map
@@ -1,22 +1,27 @@
 <test_name> <regex.no.spaces> <only_bug_number>
 system_app_anr com.google.android.apps.wellbeing*.*ContextManagerRestartBroadcastReceiver_Receiver[\s\S]*cf_x86_phone 166183732
 system_app_anr com.google.android.apps.dreamliner/.dnd.DockConditionProviderService 166174264
-system_app_anr com.google.android.gms[\s\S]*executing\sservice\scom.google.android.gms/.nearby.sharing.SharingTileService 193719277
+system_app_anr com.google.android.gms[\s\S]*executing\sservice\scom.google.android.gms/.nearby.sharing.SharingTileService 174699066
+system_app_anr com.google.android.euicc[\s\S]*executing\sservice\scom.google.android.euicc/com.android.euicc.service.EuiccServiceImpl 174479972
+system_app_anr act=android.intent.action.LOCALE_CHANGED\sflg=0x11200010\scmp=com.google.android.gms[\S]{2}chimera.GmsIntentOperationService\$PersistentTrustedReceiver[\S\s]*crosshatch 174755579
 system_app_anr act=android.hardware.usb.action.USB_STATE[\S\s]*cmp=com.google.android.projection.gearhead[\s\S]*ConnectivityEventHandlerImpl\$ConnectivityEventBroadcastReceiver 130956983
 system_app_anr com.google.android.apps.wellbeing*.*ContextManagerRestartBroadcastReceiver_Receiver[\s\S]*cf_x86_64_phone 166183732
+system_app_anr act=android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION\sflg=0x10000010\scmp=com.google.android.gms/.statementservice.IntentFilterVerificationReceiver 194231517
 system_app_anr executing\sservice\scom.google.android.as/com.google.android.apps.miphone.aiai.echo.notificationintelligence.scheduler.impl.NotificationJobService 192300119
 system_app_anr executing\sservice\scom.google.android.as/com.google.android.apps.miphone.aiai.echo.scheduler.EchoJobService 192300119
 system_app_anr executing\sservice\scom.google.android.as/com.google.android.apps.miphone.aiai.actions.service.ActionRankingDataTtlService 192300119
 system_app_anr executing\sservice\scom.android.se/.SecureElementService 199457346
-system_app_anr act=android.telephony.action.CARRIER_CONFIG_CHANGED\sflg=0x15000010\scmp=com.android.phone/.otasp.OtaspSimStateReceiver 205896452
 system_app_crash -1\|android\|26\|null\|1000 155073214
 system_app_crash -1\|android\|32\|null\|1000 155073214
 system_app_crash android.database.sqlite.SQLiteCloseable.acquireReference 159658068
 system_app_crash com.google.android.gms.backup.component.D2dTransportService 31428310
 system_app_crash Unable\sto\sinstantiate\sapplication\sorg.chromium.chrome.browser.ChromeApplication 161275381
 system_app_crash com.google.android.as.*\s.*\s.*\s.*\s*.*act=android.net.wifi.STATE_CHANGE 161559360
-system_app_crash com.google.android.googlequicksearchbox:search.*\s.*\s.*\s.*\s.*11.26.*\s.*\s.*\s.*\s.*\s\s.*ConcurrentModificationException.*\s.*\s.*\s.*apps.gsa.shared.util.debug.a.g.a 171971925
+system_app_crash com.google.android.googlequicksearchbox[A-Za-z0-9\S\s]+11.26[A-Za-z0-9\S]+ 171971925
 system_app_crash v10804[\s\S]*com.google.android.apps.scone.wifiscorer.WifiScorerService[\s\S]*android.content.Intent.getAction 174277223
+system_app_crash Unable\sto\sget\sprovider\sandroidx.core.content.FileProvider[\S\s]+Couldn't\sfind\smeta-data\sfor\sprovider\swith\sauthority\scom.verizon.mips.services 166457582
+system_app_crash Unable\sto\sget\sprovider\scom.google.android.systemui.keyguard.KeyguardSliceProviderGoogle[\S\s]*com.android.wm.shell.common.magnetictarget.MagnetizedObject.MagneticTarget.setMagneticFieldRadiusPx 181262334
+system_app_crash com.google.android.apps.youtube.music[\S\s]+NullPointerException[\S\s]+com.google.android.apps.youtube.music.mediabrowser.MusicBrowserService.d 211914566
 system_app_native_crash HwBinder*.*com.android.bluetooth 155074413
 system_app_native_crash ReferenceQueueD*.*com.google.android.apps.safetyhub 162103095
 system_app_native_crash Binder*.*com.google.android.apps.safetyhub 162104694
@@ -35,4 +40,5 @@
 SYSTEM_TOMBSTONE com.qualcomm.qti.services.secureui:sui_service\s<<<[\S\s]*null\spointer[\S\s]*libsecureui_svcsock_system 174754036
 SYSTEM_TOMBSTONE >>>\s/vendor/bin/hw/android.hardware.camera.provider@2.6-service-google\s<<<[\S\s]+libGLESv2_adreno 173663856
 SYSTEM_TOMBSTONE (coral|flame|sunfish)[\s\S]+enableInternal\s+>>>\s+com.android.nfc\s<<<[\s\S]+JNI\sFatalError\scalled:\senableInternal 187522327
+SYSTEM_TOMBSTONE applyRouting\s+>>>\scom.android.nfc\s<<<[\S\s]+JNI\sFatalError\scalled:\sapplyRouting 187061650
 SYSTEM_TOMBSTONE android.hardwar*.*/vendor/bin/hw/android.hardware.sensors@2.0-service 163430194
diff --git a/tests/functional/devicehealthchecks/src/com/android/devicehealthchecks/SensorsBootCheck.java b/tests/functional/devicehealthchecks/src/com/android/devicehealthchecks/SensorsBootCheck.java
index c4c1a61..64e6f1c 100644
--- a/tests/functional/devicehealthchecks/src/com/android/devicehealthchecks/SensorsBootCheck.java
+++ b/tests/functional/devicehealthchecks/src/com/android/devicehealthchecks/SensorsBootCheck.java
@@ -87,7 +87,7 @@
 
         // TODO: test heart rate and other related sensor types.
 
-        Assert.assertEquals(errorDetails.toString(), numErrors, 0);
+        Assert.assertEquals(errorDetails.toString(), 0, numErrors);
     }
 
     private boolean isSensorMissing(String featureString, int sensorType, StringBuilder errString) {
diff --git a/tests/health/scenarios/src/android/platform/test/scenario/annotation/LargeScreenOnly.java b/tests/health/scenarios/src/android/platform/test/scenario/annotation/HubUi.java
similarity index 79%
rename from tests/health/scenarios/src/android/platform/test/scenario/annotation/LargeScreenOnly.java
rename to tests/health/scenarios/src/android/platform/test/scenario/annotation/HubUi.java
index 3711d3c..fc0ca30 100644
--- a/tests/health/scenarios/src/android/platform/test/scenario/annotation/LargeScreenOnly.java
+++ b/tests/health/scenarios/src/android/platform/test/scenario/annotation/HubUi.java
@@ -22,10 +22,10 @@
 import java.lang.annotation.Target;
 
 /**
- * Identifies scenario that should be run only on large screen devices. Note that this annotation
- * doesn't do any filtering for screen size, it's up to the test author to annotate the test
- * correctly.
+ * Identifies scenario tests that should only be run on devices support HubUi. Note that this
+ * annotation doesn't do any filtering for device type, it's up to the test author to annotate the
+ * test correctly.
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.TYPE, ElementType.METHOD})
-public @interface LargeScreenOnly {}
+public @interface HubUi {}
diff --git a/tests/health/scenarios/src/android/platform/test/scenario/generic/OpenAppsFromHome.java b/tests/health/scenarios/src/android/platform/test/scenario/generic/OpenAppsFromHome.java
index a6642ec..064a337 100644
--- a/tests/health/scenarios/src/android/platform/test/scenario/generic/OpenAppsFromHome.java
+++ b/tests/health/scenarios/src/android/platform/test/scenario/generic/OpenAppsFromHome.java
@@ -24,8 +24,8 @@
 import android.platform.test.rule.UnlockScreenRule;
 import android.platform.test.scenario.annotation.Scenario;
 
-import com.android.launcher3.tapl.AllApps;
 import com.android.launcher3.tapl.AppIcon;
+import com.android.launcher3.tapl.HomeAllApps;
 import com.android.launcher3.tapl.LauncherInstrumentation;
 
 import org.junit.AfterClass;
@@ -67,14 +67,14 @@
     @BeforeClass
     public static void setup() throws IOException {
         sLauncher = new LauncherInstrumentation();
-        final AllApps allApps = sLauncher.pressHome().switchToAllApps();
+        final HomeAllApps allApps = sLauncher.goHome().switchToAllApps();
         allApps.getAppIcon(sNameOption.get()).dragToWorkspace(false, false);
         sAppIcon = sLauncher.getWorkspace().getWorkspaceAppIcon(sNameOption.get());
     }
 
     @NoMetricBefore
     public void openWorkspace() {
-        sLauncher.pressHome();
+        sLauncher.goHome();
     }
 
     @Test
@@ -86,6 +86,6 @@
     @AfterClass
     public static void closeAppAndRemoveIcon() throws IOException {
         sLauncher.getDevice().executeShellCommand("pm clear com.google.android.apps.nexuslauncher");
-        sLauncher.pressHome();
+        sLauncher.goHome();
     }
 }
diff --git a/tests/jank/UbSystemUiJankTests/src/android/platform/systemui/tests/jank/LauncherJankTests.java b/tests/jank/UbSystemUiJankTests/src/android/platform/systemui/tests/jank/LauncherJankTests.java
index 06ec239..de3b4cc 100644
--- a/tests/jank/UbSystemUiJankTests/src/android/platform/systemui/tests/jank/LauncherJankTests.java
+++ b/tests/jank/UbSystemUiJankTests/src/android/platform/systemui/tests/jank/LauncherJankTests.java
@@ -35,7 +35,6 @@
 
 import com.android.launcher3.tapl.AllApps;
 import com.android.launcher3.tapl.LauncherInstrumentation;
-import com.android.launcher3.tapl.Overview;
 import com.android.launcher3.tapl.Widgets;
 import com.android.launcher3.tapl.Workspace;
 
@@ -90,7 +89,7 @@
     }
 
     public void resetAndOpenRecents() throws UiObjectNotFoundException, RemoteException {
-        mLauncher.pressHome().switchToOverview();
+        mLauncher.goHome().switchToOverview();
     }
 
     public void prepareOpenAllAppsContainer() throws IOException {
@@ -107,19 +106,8 @@
         super.afterTest(metrics);
     }
 
-    /** Starts from the home screen, and measures jank while opening the all apps container. */
-    @JankTest(expectedFrames=100, beforeTest="prepareOpenAllAppsContainer",
-            beforeLoop="resetAndOpenRecents", afterTest="afterTestOpenAllAppsContainer")
-    @GfxMonitor(processName="#getLauncherPackage")
-    public void testOpenAllAppsContainer() throws UiObjectNotFoundException {
-        Overview overview = mLauncher.getOverview();
-        for (int i = 0; i < INNER_LOOP * 2; i++) {
-            overview = overview.switchToAllApps().switchBackToOverview();
-        }
-    }
-
     public void openAllApps() throws UiObjectNotFoundException, IOException {
-        mLauncher.pressHome().switchToAllApps();
+        mLauncher.goHome().switchToAllApps();
         TimeResultLogger.writeTimeStampLogStart(String.format("%s-%s",
                 getClass().getSimpleName(), getName()), TIMESTAMP_FILE);
     }
@@ -145,7 +133,7 @@
     }
 
     public void makeHomeScrollable() throws UiObjectNotFoundException, IOException {
-        mLauncher.pressHome().ensureWorkspaceIsScrollable();
+        mLauncher.goHome().ensureWorkspaceIsScrollable();
         TimeResultLogger.writeTimeStampLogStart(String.format("%s-%s",
                 getClass().getSimpleName(), getName()), TIMESTAMP_FILE);
     }
@@ -171,7 +159,7 @@
     }
 
     public void openAllWidgets() throws UiObjectNotFoundException, IOException {
-        mLauncher.pressHome().openAllWidgets();
+        mLauncher.goHome().openAllWidgets();
         TimeResultLogger.writeTimeStampLogStart(String.format("%s-%s",
                 getClass().getSimpleName(), getName()), TIMESTAMP_FILE);
     }
@@ -219,7 +207,7 @@
     public void testOpenCloseMessagesApp() throws Exception {
         for (int i = 0; i < INNER_LOOP; i++) {
             mLauncherStrategy.launch("Messages", "com.google.android.apps.messaging");
-            mLauncher.pressHome();
+            mLauncher.goHome();
         }
     }
 
@@ -250,7 +238,7 @@
     public void testAppToHome() throws Exception {
         for (int i = 0; i < INNER_LOOP; i++) {
             startAppFast("com.google.android.apps.messaging");
-            mLauncher.pressHome();
+            mLauncher.goHome();
         }
     }
 }
diff --git a/tests/jank/UbSystemUiJankTests/src/android/platform/systemui/tests/jank/SystemUiJankTests.java b/tests/jank/UbSystemUiJankTests/src/android/platform/systemui/tests/jank/SystemUiJankTests.java
index 43228af..8815ae7 100644
--- a/tests/jank/UbSystemUiJankTests/src/android/platform/systemui/tests/jank/SystemUiJankTests.java
+++ b/tests/jank/UbSystemUiJankTests/src/android/platform/systemui/tests/jank/SystemUiJankTests.java
@@ -185,7 +185,7 @@
     // Makes sure Recents is opened on the most recent task.
     public void resetRecents() throws RemoteException {
         mDevice.wakeUp();
-        mLauncher.pressHome().switchToOverview();
+        mLauncher.goHome().switchToOverview();
     }
 
     public void prepareNotifications(int groupMode) throws Exception {
diff --git a/tests/perf/PerfTransitionTest/src/com/android/apptransition/tests/AppTransitionTests.java b/tests/perf/PerfTransitionTest/src/com/android/apptransition/tests/AppTransitionTests.java
index b18427d..12bb781 100644
--- a/tests/perf/PerfTransitionTest/src/com/android/apptransition/tests/AppTransitionTests.java
+++ b/tests/perf/PerfTransitionTest/src/com/android/apptransition/tests/AppTransitionTests.java
@@ -285,7 +285,7 @@
                             mTraceDumpInterval, mRootTraceSubDir,
                             String.format("%s-%d", appName, launchCount - 1));
                 }
-                mLauncher.getBackground().switchToOverview();
+                mLauncher.getLaunchedAppState().switchToOverview();
                 sleep(mPostLaunchTimeout);
                 if (null != mAtraceLogger && launchCount > 0) {
                     mAtraceLogger.atraceStop();
@@ -324,7 +324,7 @@
                     "\\/")[0]);
             for (int launchCount = 0; launchCount <= mLaunchIterations; launchCount++) {
                 sleep(mPostLaunchTimeout);
-                final Workspace workspace = mLauncher.pressHome();
+                final Workspace workspace = mLauncher.goHome();
                 if (null != mAtraceLogger) {
                     mAtraceLogger.atraceStart(mTraceCategoriesSet, mTraceBufferSize,
                             mTraceDumpInterval, mRootTraceSubDir,
diff --git a/tests/perf/PerfTransitionTest/src/com/android/apptransition/tests/LatencyTests.java b/tests/perf/PerfTransitionTest/src/com/android/apptransition/tests/LatencyTests.java
index 8f7bae6..fac77cd 100644
--- a/tests/perf/PerfTransitionTest/src/com/android/apptransition/tests/LatencyTests.java
+++ b/tests/perf/PerfTransitionTest/src/com/android/apptransition/tests/LatencyTests.java
@@ -26,6 +26,7 @@
 import android.system.helpers.LockscreenHelper;
 import android.system.helpers.OverviewHelper;
 import android.system.helpers.SettingsHelper;
+import android.util.Log;
 import android.view.IWindowManager;
 import android.view.Surface;
 import android.view.WindowManagerGlobal;
@@ -35,10 +36,12 @@
 
 import com.android.launcher3.tapl.LauncherInstrumentation;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -54,6 +57,10 @@
             + "com.android.systemui.latency.ACTION_FINGERPRINT_WAKE";
     private static final String TURN_ON_SCREEN_COMMAND = "am broadcast -a "
             + "com.android.systemui.latency.ACTION_TURN_ON_SCREEN";
+    private static final String ENABLE_LATENCY_TEST =
+            "device_config put latency_tracker enabled true";
+    private static final String RESET_ENABLE_LATENCY_TEST =
+            "device_config delete latency_tracker enabled";
     private static final String AM_START_COMMAND_TEMPLATE = "am start -a %s";
     private static final String PIN = "1234";
     private static final String KEY_TRACE_DIRECTORY = "trace_directory";
@@ -87,6 +94,11 @@
     @Before
     public void setUp() throws Exception {
         mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        try {
+            mDevice.executeShellCommand(ENABLE_LATENCY_TEST);
+        } catch (IOException ioe) {
+            Log.e("LatencyTests", "Failed to enable latency commands");
+        }
         Bundle mArgs = InstrumentationRegistry.getArguments();
         mIterationCount = Integer.parseInt(mArgs.getString(KEY_ITERATION_COUNT,
                 Integer.toString(DEFAULT_ITERATION_COUNT)));
@@ -114,6 +126,15 @@
         mLauncher = new LauncherInstrumentation(getInstrumentation());
     }
 
+    @After
+    public void tearDown() {
+        try {
+            mDevice.executeShellCommand(RESET_ENABLE_LATENCY_TEST);
+        } catch (IOException ioe) {
+            Log.e("LatencyTests", "Failed to reset latency commands");
+        }
+    }
+
     /**
      * Test to track how long it takes to expand the notification shade when swiping.
      * <p>
@@ -319,7 +340,7 @@
                         mTraceDumpInterval, mRootTrace,
                         String.format("%s-%d", TEST_APPTORECENTS, i));
             }
-            mLauncher.getBackground().switchToOverview();
+            mLauncher.getLaunchedAppState().switchToOverview();
 
             // Make sure all the animations are really done.
             SystemClock.sleep(200);