Merge "Update test steps for CrashHelperTest."
diff --git a/build/tasks/tests/instrumentation_metric_test_list.mk b/build/tasks/tests/instrumentation_metric_test_list.mk
index 6da9982..30cf5c9 100644
--- a/build/tasks/tests/instrumentation_metric_test_list.mk
+++ b/build/tasks/tests/instrumentation_metric_test_list.mk
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 instrumentation_metric_tests := \
+    AutofillPerfTests \
     crashcollector \
     CorePerfTests \
     DocumentsUIAppPerfTests \
@@ -20,7 +21,8 @@
     RsBlasBenchmark \
     ImageProcessingJB \
     MultiUserPerfTests \
-    NeuralNetworksApiBenchmark
+    NeuralNetworksApiBenchmark \
+    TextClassifierPerfTests
 
     # TODO(b/72332760): Uncomment when fixed
     #DocumentsUIPerfTests
diff --git a/build/tasks/tests/instrumentation_test_list.mk b/build/tasks/tests/instrumentation_test_list.mk
index 84790cb..786284e 100644
--- a/build/tasks/tests/instrumentation_test_list.mk
+++ b/build/tasks/tests/instrumentation_test_list.mk
@@ -19,6 +19,7 @@
     LongevityPlatformLibTests \
     ManagedProvisioningTests \
     FrameworksCoreTests \
+    FrameworksMockingCoreTests \
     BinderProxyCountingTestApp \
     BinderProxyCountingTestService \
     FrameworksNetTests \
@@ -50,7 +51,7 @@
     BlockedNumberProviderTest \
     DownloadAppFunctionalTests \
     NotificationFunctionalTests \
-    DexLoggerIntegrationTests \
+    DynamicCodeLoggerIntegrationTests \
     UsbTests \
     DownloadProviderTests \
     EmergencyInfoUnitTests \
@@ -67,7 +68,12 @@
     FrameworksLocationTests \
     FrameworksPrivacyLibraryTests \
     SettingsUITests \
-    ExtServicesUnitTests
+    ExtServicesUnitTests\
+    NexusLauncherOutOfProcTests\
+    NexusLauncherDebug\
+    NexusLauncherTests\
+    FrameworksNetSmokeTests\
+
 
 # Storage Manager may not exist on device
 ifneq ($(filter StorageManager, $(PRODUCT_PACKAGES)),)
diff --git a/build/tasks/tests/native_metric_test_list.mk b/build/tasks/tests/native_metric_test_list.mk
index 1cf090e..ecddd6c 100644
--- a/build/tasks/tests/native_metric_test_list.mk
+++ b/build/tasks/tests/native_metric_test_list.mk
@@ -24,7 +24,8 @@
     minikin_perftests \
     mmapPerf \
     netd_benchmark \
-    skia_nanobench
+    skia_nanobench \
+    libhwbinder_benchmark
 
 ifneq ($(strip $(BOARD_PERFSETUP_SCRIPT)),)
 native_metric_tests += perf-setup.sh
diff --git a/build/tasks/tests/native_test_list.mk b/build/tasks/tests/native_test_list.mk
index bbb6d19..9e7ffcd 100644
--- a/build/tasks/tests/native_test_list.mk
+++ b/build/tasks/tests/native_test_list.mk
@@ -32,6 +32,7 @@
     bsdiff_unittest \
     camera_client_test \
     clatd_test \
+    confirmationui_invocation_test \
     crashcollector \
     debuggerd_test \
     dumpstate_test \
@@ -121,10 +122,14 @@
     update_engine_unittests \
     vintf_object_test \
     wificond_unit_test \
-    wifilogd_unit_test \
     ziparchive-tests \
+    BufferHub_test \
+    BufferHubServer_test \
+    GraphicBuffer_test \
     NeuralNetworksTest_mt_static \
     NeuralNetworksTest_operations \
     NeuralNetworksTest_static \
+    NeuralNetworksTest_static_asan \
     SurfaceFlinger_test \
-    lmkd_unit_test
+    lmkd_unit_test \
+    vrflinger_test
diff --git a/build/tasks/tests/platform_test_list.mk b/build/tasks/tests/platform_test_list.mk
index 8dbc633..2e41d4e 100644
--- a/build/tasks/tests/platform_test_list.mk
+++ b/build/tasks/tests/platform_test_list.mk
@@ -21,6 +21,7 @@
     BandwidthTests \
     BluetoothTests \
     BootHelperApp \
+    BusinessCard \
     CalculatorFunctionalTests \
     CalendarTests \
     camera_client_test \
@@ -31,9 +32,10 @@
     CtsCameraTestCases \
     CtsHardwareTestCases \
     DataIdleTest \
+    Development \
     DeviceHealthChecks \
     DeviceHealthTests \
-    DexLoggerIntegrationTests \
+    DynamicCodeLoggerIntegrationTests \
     DialerJankTests \
     DownloadManagerTestApp \
     DummyIME \
@@ -49,11 +51,11 @@
     FrameworkPerf \
     FrameworkPermissionTests \
     FrameworksCoreTests \
+    FrameworksMockingCoreTests \
     FrameworksPrivacyLibraryTests \
     FrameworksUtilTests \
     InternalLocTestApp \
     JankMicroBenchmarkTests \
-    LauncherRotationStressTest \
     MemoryUsage \
     MultiDexLegacyTestApp \
     MultiDexLegacyTestApp2 \
@@ -78,6 +80,7 @@
     PermissionFunctionalTests \
     PermissionTestAppMV1 \
     PermissionUtils \
+    PlatformScenarioTests \
     PowerPerfTest \
     SettingsUITests \
     SimpleTestApp \
@@ -102,6 +105,11 @@
     VersatileTestApp_Internal \
     VersatileTestApp_None \
     VoiceInteraction \
+    WifiStrengthScannerUtil \
+
+ifneq ($(strip $(BOARD_PERFSETUP_SCRIPT)),)
+platform_tests += perf-setup.sh
+endif
 
 ifneq ($(filter vsoc_x86 vsoc_x86_64, $(TARGET_DEVICE)),)
   platform_tests += \
diff --git a/emu_test/run_test.sh b/emu_test/run_test.sh
new file mode 100755
index 0000000..f2d5c40
--- /dev/null
+++ b/emu_test/run_test.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+# It is to be used with BYOB setup to run tests on cloud VMs.
+# It will run UI and boot tests on them.
+#
+# It takes 3 command line arguments.
+# DIST_DIR => Absolute path for the distribution directory.
+# API => API number for the system image
+# ORI => branch code for the system image
+#
+# It will return 0 if it is able to execute tests, otherwise
+# it will return 1.
+#
+# For the test results please refer to go/dashboard-adt
+#
+# Owner: akagrawal@google.com
+
+DIST_DIR=$1
+API=$2
+ORI=$3
+
+function run_with_timeout () {
+   ( $1 $2 $3 $4 ) & pid=$!
+   ( sleep $5 && kill -HUP $pid ) 2>/dev/null & watcher=$!
+   if wait $pid 2>/dev/null; then
+      pkill -HUP -P $watcher
+      wait $watcher
+   else
+      echo "Test time out."
+      exit 1
+   fi
+}
+
+echo "Checkout adt-infra repo"
+# $ADT_INFRA has to be set on the build machine. It should have absolute path
+# where adt-infra needs to be checked out.
+rm -rf $ADT_INFRA
+git clone https://android.googlesource.com/platform/external/adt-infra -b emu-master-dev $ADT_INFRA
+
+BUILD_DIR="out/prebuilt_cached/builds"
+
+export ANDROID_HOME=$SDK_SYS_IMAGE
+export ANDROID_SDK_ROOT=$SDK_SYS_IMAGE
+
+echo "Setup builds"
+$ADT_INFRA/emu_test/utils/setup_builds.sh $BUILD_DIR $API
+
+echo "Run Boot tests from $ADT_INFRA"
+cmd="$ADT_INFRA/emu_test/utils/run_boot_test.sh"
+run_with_timeout $cmd $DIST_DIR $ORI $API 5400
+
+echo "Run UI tests from $ADT_INFRA"
+cmd="$ADT_INFRA/emu_test/utils/run_ui_test.sh"
+run_with_timeout $cmd $DIST_DIR $ORI $API 10800
+
+echo "Cleanup prebuilts"
+rm -rf /buildbot/prebuilt/*
+
+exit 0
diff --git a/emu_test/run_test_suite.sh b/emu_test/run_test_suite.sh
new file mode 100755
index 0000000..1a9bade
--- /dev/null
+++ b/emu_test/run_test_suite.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+# It is to be used with BYOB setup to run CTS tests.
+#
+# It takes 1 command line argument.
+# DIST_DIR => Absolute path for the distribution directory.
+#
+# It will return 0 if it is able to execute tests, otherwise
+# it will return 1.
+#
+# Owner: akagrawal@google.com
+
+DIST_DIR=$1
+BUILD_ID=$2
+
+BUILD_DIR="out/prebuilt_cached/builds"
+
+if [ ! -d "$BUILD_DIR/test_suite" ];
+then
+    echo "Test suite does not exist"
+    exit 1
+fi
+#for cts, android-cts.zip
+#for gts, android-gts.zip
+if [[ `ls $BUILD_DIR/test_suite` == *"cts"* ]]
+then
+    TEST_SUITE="android-cts.zip"
+elif [[ `ls $BUILD_DIR/test_suite` == *"gts"* ]]
+then
+    TEST_SUITE="android-gts.zip"
+else
+    echo "Test suite does not exist"
+    exit 1
+fi
+echo "$TEST_SUITE"
+
+mkdir -p $BUILD_DIR/emulator
+fetch_artifacts.py -build_target linux-sdk_tools_linux -branch aosp-emu-master-dev -image_path gs://android-build-emu/builds -dest $BUILD_DIR/emulator/
+EMU_BIN=`ls $BUILD_DIR/emulator`
+echo "$EMU_BIN"
+
+if [ -d "$BUILD_DIR/gphone_x86-user" ];
+then
+    SYS_IMAGE=`ls $BUILD_DIR/gphone_x86-user`
+    if [[ $TEST_SUITE == *"cts"* ]]
+    then
+        echo "Run CTS with $SYS_IMAGE"
+    elif [[ $TEST_SUITE == *"gts"* ]]
+    then
+        echo "Run GTS with $SYS_IMAGE"
+    fi
+fi
+
+if [ -d "$BUILD_DIR/gphone_x86_64-user" ];
+then
+    SYS_IMAGE_64=`ls $BUILD_DIR/gphone_x86_64-user`
+    if [[ $TEST_SUITE == *"cts"* ]]
+    then
+        echo "Run CTS with $SYS_IMAGE_64"
+    elif [[ $TEST_SUITE == *"gts"* ]]
+    then
+        echo "Run GTS with $SYS_IMAGE_64"
+    fi
+fi
+
+echo "Cleanup prebuilts"
+rm -rf /buildbot/prebuilt/*
+
+exit 0
diff --git a/libraries/aoa-helper/src/com/android/helper/aoa/AoaDevice.java b/libraries/aoa-helper/src/com/android/helper/aoa/AoaDevice.java
index c638b0d..e56c4ac 100644
--- a/libraries/aoa-helper/src/com/android/helper/aoa/AoaDevice.java
+++ b/libraries/aoa-helper/src/com/android/helper/aoa/AoaDevice.java
@@ -62,6 +62,9 @@
     static final byte ACCESSORY_SET_HID_REPORT_DESC = 56;
     static final byte ACCESSORY_SEND_HID_EVENT = 57;
 
+    // Maximum attempts at restarting in accessory mode
+    static final int ACCESSORY_START_MAX_RETRIES = 5;
+
     // Touch types
     static final byte TOUCH_UP = 0b00;
     static final byte TOUCH_DOWN = 0b11;
@@ -87,11 +90,11 @@
     AoaDevice(@Nonnull UsbHelper helper, @Nonnull UsbDevice delegate) {
         mHelper = helper;
         mDelegate = delegate;
-        initialize();
+        initialize(0);
     }
 
     // Configure the device, switching to accessory mode if necessary and registering the HIDs
-    private void initialize() {
+    private void initialize(int attempt) {
         if (!isValid()) {
             throw new UsbException("Invalid device connection");
         }
@@ -103,12 +106,16 @@
 
         if (isAccessoryMode()) {
             registerHIDs();
+        } else if (attempt >= ACCESSORY_START_MAX_RETRIES) {
+            throw new UsbException("Failed to start accessory mode");
         } else {
-            // restart in accessory mode
+            // restart in accessory mode and try to initialize again
             mHelper.checkResult(
                     mDelegate.controlTransfer(OUTPUT, ACCESSORY_START, 0, 0, new byte[0]));
             sleep(CONFIGURE_DELAY);
-            resetConnection();
+            mDelegate.close();
+            mDelegate = mHelper.getDevice(mSerialNumber, CONNECTION_TIMEOUT);
+            initialize(attempt + 1);
         }
     }
 
@@ -150,7 +157,7 @@
     public void resetConnection() {
         close();
         mDelegate = mHelper.getDevice(mSerialNumber, CONNECTION_TIMEOUT);
-        initialize();
+        initialize(0);
     }
 
     /** @return true if connection is non-null, but does not check if resetting is necessary */
diff --git a/libraries/aoa-helper/tests/src/com/android/helper/aoa/AoaDeviceTest.java b/libraries/aoa-helper/tests/src/com/android/helper/aoa/AoaDeviceTest.java
index 503734b..8368cf7 100644
--- a/libraries/aoa-helper/tests/src/com/android/helper/aoa/AoaDeviceTest.java
+++ b/libraries/aoa-helper/tests/src/com/android/helper/aoa/AoaDeviceTest.java
@@ -19,6 +19,7 @@
 import static com.android.helper.aoa.AoaDevice.ACCESSORY_SEND_HID_EVENT;
 import static com.android.helper.aoa.AoaDevice.ACCESSORY_SET_HID_REPORT_DESC;
 import static com.android.helper.aoa.AoaDevice.ACCESSORY_START;
+import static com.android.helper.aoa.AoaDevice.ACCESSORY_START_MAX_RETRIES;
 import static com.android.helper.aoa.AoaDevice.ACCESSORY_UNREGISTER_HID;
 import static com.android.helper.aoa.AoaDevice.DEVICE_NOT_FOUND;
 import static com.android.helper.aoa.AoaDevice.FLING_STEPS;
@@ -34,6 +35,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyByte;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -117,7 +119,6 @@
         // not in accessory mode initially
         when(mDelegate.getVendorId())
                 .thenReturn(INVALID_VID)
-                .thenReturn(INVALID_VID)
                 .thenReturn(GOOGLE_VID);
 
         mDevice = createDevice();
@@ -132,6 +133,20 @@
     }
 
     @Test
+    public void testRetriesAccessoryMode() {
+        // never in accessory mode
+        when(mDelegate.getVendorId()).thenReturn(INVALID_VID);
+
+        try {
+            mDevice = createDevice();
+            fail("UsbException expected");
+        } catch (UsbException e) {
+            // retried starting accessory mode before giving up
+            verifyRequest(times(ACCESSORY_START_MAX_RETRIES), ACCESSORY_START);
+        }
+    }
+
+    @Test
     public void testResetConnection() {
         mDevice = createDevice();
         clearInvocations(mDelegate);
diff --git a/libraries/app-helpers/core/Android.bp b/libraries/app-helpers/core/Android.bp
index 48a1210..63a1241 100644
--- a/libraries/app-helpers/core/Android.bp
+++ b/libraries/app-helpers/core/Android.bp
@@ -18,9 +18,8 @@
     name: "app-helpers-core",
     libs: [
         "ub-uiautomator",
-        "android-support-test",
-        "launcher-helper-lib",
         "androidx.test.runner",
+        "launcher-helper-lib",
     ],
     srcs: ["src/**/*.java"],
     sdk_version: "test_current",
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 289e5fd..f1e3c1a 100644
--- a/libraries/app-helpers/core/src/android/platform/helpers/AbstractStandardAppHelper.java
+++ b/libraries/app-helpers/core/src/android/platform/helpers/AbstractStandardAppHelper.java
@@ -56,6 +56,8 @@
     private static final String ERROR_NOT_FOUND =
         "Element %s %s is not found in the application %s";
 
+    private static final long EXIT_WAIT_TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+
     private static File sScreenshotDirectory;
 
     public UiDevice mDevice;
@@ -111,13 +113,13 @@
         if (mFavorShellCommands) {
             String output = null;
             try {
+                Log.i(LOG_TAG, String.format("Sending command to launch: %s", pkg));
                 Intent intent =
                         mInstrumentation
                                 .getContext()
                                 .getPackageManager()
                                 .getLaunchIntentForPackage(pkg);
                 mInstrumentation.getContext().startActivity(intent);
-                Log.i(LOG_TAG, String.format("Sent command to launch: %s", pkg));
             } catch (ActivityNotFoundException e) {
                 removeDialogWatchers();
                 throw new RuntimeException(String.format("Failed to find package: %s", pkg), e);
@@ -147,12 +149,10 @@
      */
     @Override
     public void exit() {
+        Log.i(LOG_TAG, "Exiting the current application.");
         if (mPressHomeToExit) {
             mDevice.pressHome();
             mDevice.waitForIdle();
-            if (!mDevice.hasObject(getLauncherStrategy().getWorkspaceSelector())) {
-                throw new IllegalStateException("Pressing Home failed to exit the app.");
-            }
         } else {
             int maxBacks = 4;
             while (!mDevice.hasObject(getLauncherStrategy().getWorkspaceSelector())
@@ -166,6 +166,10 @@
                 mDevice.pressHome();
             }
         }
+        if (!mDevice.wait(
+                Until.hasObject(mLauncherStrategy.getWorkspaceSelector()), EXIT_WAIT_TIMEOUT)) {
+            throw new IllegalStateException("Failed to exit the app to launcher.");
+        }
     }
 
     /**
diff --git a/libraries/app-helpers/core/src/android/platform/helpers/HelperTest.java b/libraries/app-helpers/core/src/android/platform/helpers/HelperTest.java
index aec7789..223c6b3 100644
--- a/libraries/app-helpers/core/src/android/platform/helpers/HelperTest.java
+++ b/libraries/app-helpers/core/src/android/platform/helpers/HelperTest.java
@@ -20,7 +20,7 @@
 import android.platform.helpers.HelperManager;
 import android.platform.helpers.IAppHelper;
 import android.platform.helpers.listeners.FailureScreenshotTestWatcher;
-import android.support.test.InstrumentationRegistry;
+import androidx.test.InstrumentationRegistry;
 import android.support.test.uiautomator.UiDevice;
 import android.util.Log;
 
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoAccountsHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoAccountsHelper.java
new file mode 100644
index 0000000..720776b
--- /dev/null
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoAccountsHelper.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 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.helpers;
+
+public interface IAutoAccountsHelper extends IAppHelper {
+
+    /**
+     * Setup expectation: Accounts setting is open.
+     *
+     * This method is to add an account.
+     */
+    void addAccount(String email, String password);
+
+    /**
+     * Setup expectation: Accounts setting is open.
+     *
+     * This method is to remove an account by email.
+     */
+    void removeAccount(String email);
+
+
+    /**
+     * Setup expectation: Accounts setting is open.
+     *
+     * check if an email exists.
+     */
+    boolean doesEmailExist(String email);
+}
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoAppInfoSettingsHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoAppInfoSettingsHelper.java
new file mode 100644
index 0000000..d08d310
--- /dev/null
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoAppInfoSettingsHelper.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 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.helpers;
+
+/**
+ * Helper class for functional tests of App Info settings
+ */
+
+public interface IAutoAppInfoSettingsHelper extends IAppHelper {
+
+    /** enum for enable/disable state */
+    public enum State {
+        ENABLE,
+        DISABLE
+    }
+
+    /**
+     * Setup expectation: Apps & notifications setting is open
+     *
+     * This method is to open an application in App info setting
+     *
+     * @param application - name of the application
+     */
+    void selectApp(String application);
+
+    /**
+     * Setup expectation: Apps & notifications setting is open
+     *
+     * This method is to click on Show all apps menu
+     */
+    void showAllApps();
+
+    /**
+     * Setup expectation: An application in Apps & notifications setting is open
+     *
+     * <p>This method is to enable/disable an application
+     *
+     * @param state - ENABLE: to enable, DISABLE: to disable
+     */
+    void enableDisableApplication(State state);
+
+    /**
+     * Setup expectation: An application in Apps & notifications setting is open
+     *
+     * This method is to check whether an application is running in background from UI
+     */
+    boolean isCurrentApplicationRunning();
+
+    /**
+     * Setup expectation: An application in Apps & notifications setting is open
+     *
+     * This method is to force stop the application
+     */
+    void forceStop();
+
+    /**
+     * Setup expectation: Apps & notifications setting is open
+     *
+     * <p>This method is to add a permission to an application
+     *
+     * @param permission - name of the permission
+     * @param state - ENABLE: to enable, DISABLE: to disable
+     */
+    void setAppPermission(String permission, State state);
+
+    /**
+     * Setup expectation: An application in Apps & notifications setting is open
+     *
+     * Get the current enabled permission summary in String format for an application
+     */
+    String getCurrentPermissions();
+}
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoDateTimeSettingsHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoDateTimeSettingsHelper.java
new file mode 100644
index 0000000..6882b92
--- /dev/null
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoDateTimeSettingsHelper.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 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.helpers;
+
+/**
+ * Helper class for functional tests of date & time
+ */
+
+import java.time.LocalDate;
+
+public interface IAutoDateTimeSettingsHelper extends IAppHelper {
+    /**
+     * Setup expectation: Date & time setting is open
+     *
+     * Set the device date
+     *
+     * @param date - input LocalDate object
+     */
+    void setDate(LocalDate date);
+
+    /**
+     * Setup expectation: Date & time setting is open
+     *
+     * Get the current date displayed on the UI in LocalDate object
+     */
+    LocalDate getDate();
+
+    /**
+     * Setup expectation: Date & time setting is open
+     *
+     * Set the device time
+     *
+     * @param hour - input hour
+     * @param minute - input minute
+     * @param AM_PM - input am/pm
+     */
+    void setTime(int hour, int minute, boolean is_am);
+
+    /**
+     * Setup expectation: Date & time setting is open
+     *
+     * Get the current time displayed on the UI
+     * The return string format will match the UI format exactly
+     */
+    String getTime();
+
+    /**
+     * Setup expectation: Date & time setting is open
+     *
+     * Set the device time zone
+     *
+     * @param timezone - city selected for timezone
+     */
+    void setTimeZone(String timezone);
+
+    /**
+     * Setup expectation: Date & time setting is open
+     *
+     * Get the current timezone displayed on the UI
+     */
+    String getTimeZone();
+
+    /**
+     * Setup expectation: Date & time setting is open
+     *
+     * Check if the 24 hour format menu switch widget is toggoled on
+     */
+    boolean isUseTwentyFourHourFormatSwitchWidgetOn();
+
+    /**
+     * Setup expectation: Date & time setting is open
+     *
+     * Toggle on/off 24 hour format widget switch
+     */
+    boolean toggleTwentyFourHourFormatSwitch();
+}
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoDialHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoDialHelper.java
index 1b7a62c..6e09316 100644
--- a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoDialHelper.java
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoDialHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -18,92 +18,106 @@
 
 public interface IAutoDialHelper extends IAppHelper {
     /**
-     * Setup expectations: The app is open and the drawer is open
+     * Setup expectations: The app is open and the dialpad is open
      *
-     * This method is used to dial the phonenumber on dialpad
+     * <p>This method is used to dial the phonenumber on dialpad
      *
-     * @param phoneNumber  phone number to dial.
+     * @param phoneNumber phone number to dial.
      */
     void dialANumber(String phoneNumber);
 
     /**
      * Setup expectations: The app is open and there is an ongoing call.
      *
-     * This method is used to end call.
+     * <p>This method is used to end call using softkey.
      */
     void endCall();
 
     /**
-     * Setup expectations: The app is open and the drawer is open.
+     * Setup expectations: The app is open.
      *
-     * This method is used to open call history details.
+     * <p>This method is used to open call history details.
      */
     void openCallHistory();
 
     /**
-     * Setup expectations: The app is open and the drawer is open.
+     * Setup expectations: The app is open.
      *
-     * This method is used to open missed call details.
+     * <p>This method dials a contact from the contact list.
+     *
+     * @param contactName to dial.
      */
-    void openMissedCall();
+    void callContact(String contactName);
 
     /**
-     * Setup expectations: The app is open and in "Dial a number" drawer option
+     * Setup expectations: The app is open and in Dialpad.
      *
-     * This method is used to delete the number entered on dialpad using backspace
+     * <p>This method is used to delete the number entered on dialpad using backspace
      */
     void deleteDialedNumber();
 
     /**
-     * Setup expectations: The app is open and in "Dial a number" drawer option
+     * Setup expectations: The app is open and in Dialpad
      *
-     * This method is used to get the number entered on dialpad
+     * <p>This method is used to get the number entered on dialing screen.
      */
     String getDialedNumber();
 
     /**
+     * Setup expectations: The app is open and in Dialpad
+     *
+     * <p>This method is used to get the number entered on dialpad
+     */
+    String getDialInNumber();
+
+    /**
      * Setup expectations: The app is open and there is an ongoing call.
      *
-     * This method is used to get the name of the contact for the ongoing call
+     * <p>This method is used to get the name of the contact for the ongoing call
      */
     String getDialedContactName();
 
     /**
+     * Setup expectations: The app is open and Call History is open.
+     *
+     * <p>This method is used to get the most recent call history.
+     */
+    String getRecentCallHistory();
+
+    /**
      * Setup expectations: The app is open and phonenumber is entered on the dialpad
      *
-     * This method is used to make a call
-     *
+     * <p>This method is used to make/receive a call using softkey
      */
     void makeCall();
 
     /**
      * Setup expectations: The app is open
      *
-     * This method is used to dial a number from call history, missed call[s], recent
-     * call[s] list
+     * <p>This method is used to dial a number from a list (Favorites, Call History, Contact)
      *
-     * @param phoneNumber  phoneNumber to be dialed
+     * @param contact (number or name) dial.
      */
-    void dialNumberFromList(String phoneNumber);
+    void dialFromList(String contact);
 
     /**
      * Setup expectations: The app is open and there is an ongoing call
      *
-     * This method is used to enter number on the in-call dialpad
+     * <p>This method is used to enter number on the in-call dialpad
      */
     void inCallDialPad(String phoneNumber);
 
     /**
      * Setup expectations: The app is open and there is an ongoing call
      *
-     * This method is used to mute the ongoing call
+     * <p>This method is used to mute the ongoing call
      */
     void muteCall();
 
     /**
      * Setup expectations: The app is open and there is an ongoing call
      *
-     * This method is used to unmute the ongoing call
+     * <p>This method is used to unmute the ongoing call
      */
     void unmuteCall();
 }
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoGooglePlayHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoGooglePlayHelper.java
new file mode 100644
index 0000000..5ac61d7
--- /dev/null
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoGooglePlayHelper.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 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.helpers;
+
+public interface IAutoGooglePlayHelper extends IAppHelper {
+
+    /**
+     * Setup expectations: Google Play app is open.
+     *
+     * This method is used to search a app on Google Play.
+     */
+    void searchApp(String appName);
+
+    /**
+     * Setup expectations: Google Play app is open.
+     *
+     * This method is used to install a app.
+     */
+    void installApp();
+
+    /**
+     * Setup expectations: Google Play app is open.
+     *
+     * This method is used to cancel a download.
+     */
+    void cancelDownload();
+
+    /**
+     * Setup expectations: Google Play app is open.
+     *
+     * This method is used to open a installed app.
+     */
+    void openApp();
+
+    /**
+     * Setup expectations: Google Play app is open.
+     *
+     * This method is used to return back to Google Play main page
+     */
+    void returnToMainPage();
+}
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoHomeHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoHomeHelper.java
new file mode 100644
index 0000000..7ea0284
--- /dev/null
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoHomeHelper.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 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.helpers;
+
+public interface IAutoHomeHelper extends IAppHelper {
+
+    /**
+     * Setup expectations: Should be on home screen.
+     *
+     * Checks if exists a map widget.
+     */
+    boolean hasMapWidget();
+
+    /**
+     * Setup expectations: Should be on home screen.
+     *
+     * Checks if exists a weather widget.
+     */
+    boolean hasWeatherWidget();
+
+    /**
+     * Setup expectations: Should be on home screen.
+     *
+     * @return to get current user name shown on home screen.
+     */
+    String getUserName();
+
+    /**
+     * Setup expectations: Should be on home screen.
+     *
+     * @return to get current date in LocalDate format.
+     */
+    String getDate();
+
+    /**
+     * Setup expectations: Should be on home screen.
+     *
+     * <p>Checks if exists a media widget.
+     */
+    boolean hasMediaWidget();
+}
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMapsHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMapsHelper.java
new file mode 100644
index 0000000..ca8f6c2
--- /dev/null
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMapsHelper.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 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.helpers;
+
+public interface IAutoMapsHelper extends IAppHelper {
+    /**
+     * Setup expectations: Maps app is open
+     *
+     * This method is used to search an address.
+     *
+     */
+    void search(String address);
+
+    /**
+     * Setup expectations: Maps app is open
+     *
+     * This method is used to start navigation.
+     *
+     */
+    void startNavigation();
+
+    /**
+     * Setup expectations: Maps app is open
+     *
+     * This method is used to stop navigation.
+     *
+     */
+    void stopNavigation();
+}
\ No newline at end of file
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaHelper.java
index d3cd909..035d68f 100644
--- a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaHelper.java
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaHelper.java
@@ -88,10 +88,9 @@
     void openNowPlayingWith(String trackName);
 
     /**
-     * Setup expectations: Radio app is open.
+     * Setup expectations: Media app is open.
      *
      * @return to get current playing track name.
      */
     String getMediaTrackName();
-
 }
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoRadioHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoRadioHelper.java
index 467b48d..cc43b8b 100644
--- a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoRadioHelper.java
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoRadioHelper.java
@@ -1,6 +1,6 @@
 
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -35,14 +35,14 @@
     /**
      * Setup expectations: Radio app is open.
      *
-     * This method is used to select next station.
+     * This method is used to select next station using application softkey.
      */
     void clickNextStation();
 
     /**
      * Setup expectations: Radio app is open.
      *
-     * This method is used to select previous station.
+     * This method is used to select previous station using application softkey.
      */
     void clickPreviousStation();
 
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoSettingHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoSettingHelper.java
index 05c11f1..9f06a66 100644
--- a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoSettingHelper.java
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoSettingHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -22,6 +22,27 @@
 public interface IAutoSettingHelper extends IAppHelper {
 
     /**
+     * enum for Day/Night mode.
+     *
+     * <p>The values of DAY_MODE(0) and NIGHT_MODE(2) are determined by the returned value of
+     * UiModeManager.getNightMode()
+     */
+    public enum DayNightMode {
+        DAY_MODE(0),
+        NIGHT_MODE(2);
+
+        private final int value;
+
+        DayNightMode(int value) {
+            this.value = value;
+        }
+
+        public int getValue() {
+            return value;
+        }
+    }
+
+    /**
      * enum for changing(increasing, decreasing) value.
      */
     enum ChangeType{
@@ -29,14 +50,21 @@
         DECREASE
     }
 
-     /**
+    /**
      * Setup expectations: The app is open and the settings facet is open
      *
-     * @param setting  option to open.
+     * @param setting option to open.
      */
     void openSetting(String setting);
 
     /**
+     * Setup expectations: The app is open
+     *
+     * <p>Open quick settings page
+     */
+    void openQuickSettings();
+
+    /**
      * Setup expectations: The app is open and wifi setting options is selected
      *
      * @param option to turn on/off wifi
@@ -129,6 +157,19 @@
      */
     void changeSeekbarLevel(int index, ChangeType changeType);
 
+    /**
+     * Setup expectations: quick settings facet is open.
+     *
+     * <p>set day/night mode.
+     *
+     * @param mode determines to set day mode or night mode.
+     */
+    void setDayNightMode(DayNightMode mode);
 
-
+    /**
+     * Setup expectations: quick settings facet is open.
+     *
+     * <p>get day/night mode status.
+     */
+    DayNightMode getDayNightModeStatus();
 }
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoSystemSettingsHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoSystemSettingsHelper.java
new file mode 100644
index 0000000..5139717
--- /dev/null
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoSystemSettingsHelper.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019 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.helpers;
+
+/**
+ * Helper class for functional tests of system settings
+ */
+
+import java.util.Date;
+
+public interface IAutoSystemSettingsHelper extends IAppHelper {
+    /**
+     * Setup expectation: System setting is open.
+     *
+     * Set display language.
+     *
+     * @param language - input language.
+     */
+    void setDisplayLanguage(String language);
+
+    /**
+     * Setup expectation: System setting is open.
+     *
+     * Get current display language.
+     */
+    String getCurrentLanguage();
+
+    /**
+     * Setup expectation: System setting is open.
+     *
+     * Get device model number from UI.
+     */
+    String getDeviceModel();
+
+    /**
+     * Setup expectation: System setting is open.
+     *
+     * Get android version from UI.
+     */
+    String getAndroidVersion();
+
+    /**
+     * Setup expectation: System setting is open.
+     *
+     * Get android security patch level from UI.
+     */
+    Date getAndroidSecurityPatchLevel();
+
+    /**
+     * Setup expectation: System setting is open.
+     *
+     * Get kernel version from UI.
+     */
+    String getKernelVersion();
+
+    /**
+     * Setup expectation: System setting is open.
+     *
+     * Get build number from UI.
+     */
+    String getBuildNumber();
+
+    /**
+     * Setup expectation: System setting is open.
+     *
+     * Reset network connection [ Wifi & Bluetooth ].
+     */
+    void resetNetwork();
+
+
+    /**
+     * Setup expectation: System setting is open.
+     *
+     * Reset application preferences.
+     */
+    void resetAppPreferences();
+
+    /**
+     * Setup expectation: System setting is open.
+     *
+     * Open Languages & input menu.
+     */
+    void openLanguagesInputMenu();
+}
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoVehicleHardKeysHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoVehicleHardKeysHelper.java
new file mode 100644
index 0000000..73b5c76
--- /dev/null
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoVehicleHardKeysHelper.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2019 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.helpers;
+
+public interface IAutoVehicleHardKeysHelper extends IAppHelper {
+
+    /**
+     * Setup expectations: incoming call in progress.
+     *
+     * Recieve phone call
+     */
+    void pressRecieveCallKey();
+
+    /**
+     * Setup expectations: The app is open and there is an ongoing call.
+     *
+     * End call using hardkey.
+     */
+    void pressEndCallKey();
+
+    /**
+     * Setup expectations: The Media app is active.
+     *
+     * Change media track using hardkey.
+     */
+    void pressMediaNextTrackKey();
+
+    /**
+     * Setup expectations: The Media app is active.
+     *
+     * Change media track using hardkey.
+     */
+    void pressMediaPreviousTrackKey();
+
+    /**
+     * Setup expectations: The Media app is active.
+     *
+     * Increase media volume.
+     */
+    void tuneVolumeUpKey();
+
+    /**
+     * Setup expectations: The Media app is active.
+     *
+     * Decrease media volume.
+     */
+    void tuneVolumeDownKey();
+
+    /**
+     * Increase brightness.
+     */
+    void pressBrightnessUpKey();
+
+    /**
+     * Decrease brighness.
+     */
+    void pressBrightnessDownKey();
+
+    /**
+     * Launch assistant.
+     */
+    void pressAssistantKey();
+
+    /**
+     * Setup expectations: The media app is active.
+     *
+     * Tune volume knob to mute media.
+     */
+    void tuneMuteKey();
+
+    /**
+     * Switch off screen.
+     */
+    void pressScreenOffKey();
+
+    /**
+     * Select content.
+     */
+    void tuneKnobKey();
+
+    /**
+     * Open selected content.
+     */
+    void pressKnobButtonKey();
+
+    /**
+     * Increase/decrease volume
+     */
+    void tuneVolumeKnobKey();
+
+    /**
+     * Mute media by pressing volume knob.
+     */
+    void pressVolumeKnobButtonKey();
+}
diff --git a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IGmailHelper.java b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IGmailHelper.java
index 5c7c531..aafca21 100644
--- a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IGmailHelper.java
+++ b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IGmailHelper.java
@@ -17,7 +17,6 @@
 package android.platform.helpers;
 
 import android.support.test.uiautomator.Direction;
-
 import java.util.List;
 
 public interface IGmailHelper extends IAppHelper {
@@ -154,6 +153,13 @@
     public void scrollNavigationDrawer(Direction dir);
 
     /**
+     * Setup expectations: Gmail is open and the navigation drawer is open.
+     *
+     * This method will fling the navigation drawer and block until idle. Only accepts UP and DOWN.
+     */
+    public void flingNavigationDrawer(Direction dir);
+
+    /**
      * Setup expectations: Gmail is open and a mailbox is open.
      *
      * This method will scroll the mailbox view.
diff --git a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IGoogleCameraHelper.java b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IGoogleCameraHelper.java
index b50f65d..b628320 100644
--- a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IGoogleCameraHelper.java
+++ b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IGoogleCameraHelper.java
@@ -41,35 +41,44 @@
     public static final int NUM_FLASH_MODES = 3;
 
     /**
-     * Setup expectations: GoogleCamera is open and idle in video mode.
+     * Setup expectations: GoogleCamera is open and idle in camera/video mode.
+     *
+     * This method will change to portrait mode and block until the transition is complete.
+     */
+    public default void goToPortraitMode() {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
+    /**
+     * Setup expectations: GoogleCamera is open and idle in portrait/video mode.
      *
      * This method will change to camera mode and block until the transition is complete.
      */
     public void goToCameraMode();
 
     /**
-     * Setup expectations: GoogleCamera is open and idle in camera mode.
+     * Setup expectations: GoogleCamera is open and idle in camera/portrait mode.
      *
      * This method will change to video mode and block until the transition is complete.
      */
     public void goToVideoMode();
 
     /**
-     * Setup expectations: GoogleCamera is open and idle in either camera/video mode.
+     * Setup expectations: GoogleCamera is open and idle in either camera/portrait/video mode.
      *
      * This method will change to back camera and block until the transition is complete.
      */
     public void goToBackCamera();
 
     /**
-     * Setup expectations: GoogleCamera is open and idle in either camera/video mode.
+     * Setup expectations: GoogleCamera is open and idle in either camera/portrait/video mode.
      *
      * This method will change to front camera and block until the transition is complete.
      */
     public void goToFrontCamera();
 
     /**
-     * Setup expectation: in Camera mode with the capture button present.
+     * Setup expectation: in Camera/Portrait mode with the capture button present.
      *
      * This method will capture a photo and block until the transaction is complete.
      */
@@ -129,7 +138,7 @@
 
     /**
      *
-     * Setup expectations: GoogleCamera is open and idle in either camera/video mode.
+     * Setup expectations: GoogleCamera is open and idle in either camera/portrait/video mode.
      *
      * This method will set EIS to on(true), or off(false).
      * @param mode the boolean value of the mode denoted above.
@@ -137,7 +146,7 @@
     public void setEIS(boolean mode);
 
     /**
-     * Setup expectation: GoogleCamera is open and idle in either camera/video mode.
+     * Setup expectation: GoogleCamera is open and idle in either camera/portrait/video mode.
      *
      * This method will set front video capture resolution to one of the following:
      * - SD 480p  (mode == VIDEO_SD_480)
@@ -149,7 +158,7 @@
     public void selectFrontVideoResolution(int mode);
 
     /**
-     * Setup expectation: GoogleCamera is open and idle in either camera/video mode.
+     * Setup expectation: GoogleCamera is open and idle in either camera/portrait/video mode.
      *
      * This method will set back video capture resolution to one of the following:
      * - SD 480p  (mode == VIDEO_SD_480)
@@ -173,7 +182,7 @@
     public void setFrameRate(int mode);
 
     /**
-     * Setup expectation: GoogleCamera is open and idle in camera or video mode.
+     * Setup expectation: GoogleCamera is open and idle in either camera/portrait/video mode.
      *
      * This method will set flash to one of the following:
      * - on   (mode == FLASH_ON)
diff --git a/libraries/aupt-lib/AndroidManifest.xml b/libraries/aupt-lib/AndroidManifest.xml
index b5b71a4..d043d88 100644
--- a/libraries/aupt-lib/AndroidManifest.xml
+++ b/libraries/aupt-lib/AndroidManifest.xml
@@ -20,6 +20,7 @@
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <application>
         <uses-library android:name="android.test.runner"/>
     </application>
diff --git a/libraries/car-helpers/README.md b/libraries/car-helpers/README.md
new file mode 100644
index 0000000..72d93d2
--- /dev/null
+++ b/libraries/car-helpers/README.md
@@ -0,0 +1,5 @@
+# Car Helpers Library
+
+Utitily package built on top of Android Auto Embedded platform API's to
+facilitate integration tests. Implementation in this package should always use
+AOSP Car libraries.
diff --git a/libraries/flicker/Android.bp b/libraries/car-helpers/multiuser-helper/Android.bp
similarity index 74%
rename from libraries/flicker/Android.bp
rename to libraries/car-helpers/multiuser-helper/Android.bp
index 8872016..0286bd2 100644
--- a/libraries/flicker/Android.bp
+++ b/libraries/car-helpers/multiuser-helper/Android.bp
@@ -1,4 +1,3 @@
-//
 // Copyright (C) 2018 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,16 +11,15 @@
 // 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.
-//
 
-java_library {
-    name: "flickerlib",
-    platform_apis: true,
-    srcs: ["src/**/*.java"],
-    static_libs: [
-        "ub-janktesthelper",
-        "cts-amwm-util",
-        "platformprotosnano",
-        "layersprotosnano",
+
+java_library_static {
+    name: "multi-user-helper",
+    srcs: [
+        "src/**/*.java",
     ],
-}
+    static_libs: [
+        "androidx.test.runner",
+        "android.car.userlib",
+    ],
+}
\ No newline at end of file
diff --git a/libraries/car-helpers/multiuser-helper/src/android/platform/helpers/MultiUserHelper.java b/libraries/car-helpers/multiuser-helper/src/android/platform/helpers/MultiUserHelper.java
new file mode 100644
index 0000000..a51d1e3
--- /dev/null
+++ b/libraries/car-helpers/multiuser-helper/src/android/platform/helpers/MultiUserHelper.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+// TODO(b/129771420): Use different package name for car-helper packages
+package android.platform.helpers;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.UserSwitchObserver;
+import android.car.userlib.CarUserManagerHelper;
+import android.content.pm.UserInfo;
+import android.os.RemoteException;
+import androidx.test.InstrumentationRegistry;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Helper class that is used by integration test only. It is wrapping around {@link
+ * CarUserManagerHelper} to expose user management functions. Unlike {@link CarUserManagerHelper} ,
+ * some user management functions such as user switch will be synchronous calls in order to meet
+ * testing requirements.
+ */
+public class MultiUserHelper {
+    /** Exposing default guest user name to integration tests */
+    public static final String DEFAULT_GUEST_NAME = "Guest";
+
+    private static final String TAG = MultiUserHelper.class.getSimpleName();
+
+    /** For testing purpose we allow a wide range of switching time. */
+    private static final int USER_SWITCH_TIMEOUT_SECOND = 300;
+
+    private static MultiUserHelper sMultiUserHelper;
+    private CarUserManagerHelper mUserManagerHelper;
+
+    private MultiUserHelper() {
+        mUserManagerHelper = new CarUserManagerHelper(InstrumentationRegistry.getTargetContext());
+    }
+
+    /**
+     * It will always be used as a singleton class
+     *
+     * @return MultiUserHelper instance
+     */
+    public static MultiUserHelper getInstance() {
+        if (sMultiUserHelper == null) {
+            sMultiUserHelper = new MultiUserHelper();
+        }
+        return sMultiUserHelper;
+    }
+
+    @Nullable
+    public UserInfo createNewAdminUser(String userName) {
+        return mUserManagerHelper.createNewAdminUser(userName);
+    }
+
+    @Nullable
+    public UserInfo createNewNonAdminUser(String userName) {
+        return mUserManagerHelper.createNewNonAdminUser(userName);
+    }
+
+    @Nullable
+    public UserInfo createNewOrFindExistingGuest(String guestName) {
+        return mUserManagerHelper.createNewOrFindExistingGuest(guestName);
+    }
+
+    /**
+     * Switch to the target user at API level. Always wait until user switch complete.
+     *
+     * <p>User switch complete only means the user ready at API level. It doesn't mean the UI is
+     * completely ready for the target user. It doesn't include unlocking user data and loading car
+     * launcher page
+     *
+     * @param id Id of the user to switch to
+     * @throws Exception
+     */
+    public void switchToUserId(int id) throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        registerUserSwitchObserver(latch, id);
+        if (!mUserManagerHelper.switchToUserId(id)) {
+            throw new Exception(String.format("Failed to switch to user: %d", id));
+        }
+        if (!latch.await(USER_SWITCH_TIMEOUT_SECOND, TimeUnit.SECONDS)) {
+            throw new Exception(
+                    String.format(
+                            "Timeout while switching to user %d after %d seconds",
+                            id, USER_SWITCH_TIMEOUT_SECOND));
+        }
+    }
+
+    /**
+     * Remove the target user. For now it is a non-blocking call.
+     *
+     * @param userInfo
+     * @return
+     */
+    public boolean removeUser(UserInfo userInfo) {
+        return mUserManagerHelper.removeUser(userInfo, DEFAULT_GUEST_NAME);
+    }
+
+    public UserInfo getCurrentForegroundUserInfo() {
+        return mUserManagerHelper.getCurrentForegroundUserInfo();
+    }
+
+    public int getInitialUser() {
+        return mUserManagerHelper.getInitialUser();
+    }
+
+    private void registerUserSwitchObserver(final CountDownLatch switchLatch, final int userId)
+            throws RemoteException {
+        ActivityManager.getService()
+                .registerUserSwitchObserver(
+                        new UserSwitchObserver() {
+                            @Override
+                            public void onUserSwitchComplete(int newUserId) {
+                                if (switchLatch != null && userId == newUserId) {
+                                    switchLatch.countDown();
+                                }
+                            }
+                        },
+                        TAG);
+    }
+}
diff --git a/libraries/collectors-helper/statsd/src/com/android/helpers/StatsdHelper.java b/libraries/collectors-helper/statsd/src/com/android/helpers/StatsdHelper.java
index 1333eeb..4d01473 100644
--- a/libraries/collectors-helper/statsd/src/com/android/helpers/StatsdHelper.java
+++ b/libraries/collectors-helper/statsd/src/com/android/helpers/StatsdHelper.java
@@ -75,7 +75,9 @@
                     .addAtomMatcher(getSimpleAtomMatcher(atomUniqueId, atomId));
         }
         try {
+            adoptShellIdentity();
             getStatsManager().addConfig(configId, statsConfigBuilder.build().toByteArray());
+            dropShellIdentity();
         } catch (Exception e) {
             Log.e(LOG_TAG, "Not able to setup the event config.", e);
             return false;
@@ -112,7 +114,7 @@
                     .setWhat(atomUniqueId)
                     .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build())
                     .setMaxNumGaugeAtomsPerBucket(MAX_ATOMS)
-                    .setSamplingType(GaugeMetric.SamplingType.ALL_CONDITION_CHANGES)
+                    .setSamplingType(GaugeMetric.SamplingType.FIRST_N_SAMPLES)
                     .setTriggerEvent(appBreadCrumbUniqueId)
                     .setBucket(TimeUnit.CTS);
 
@@ -122,8 +124,13 @@
         }
 
         try {
+            adoptShellIdentity();
             getStatsManager().addConfig(configId,
                     statsConfigBuilder.build().toByteArray());
+            StatsLog.logEvent(0);
+            // Dump the counters before the test started.
+            SystemClock.sleep(METRIC_DELAY_MS);
+            dropShellIdentity();
         } catch (Exception e) {
             Log.e(LOG_TAG, "Not able to setup the gauge config.", e);
             return false;
@@ -173,8 +180,10 @@
         List<EventMetricData> eventData = new ArrayList<>();
         try {
             if (getConfigId() != -1) {
+                adoptShellIdentity();
                 reportList = ConfigMetricsReportList.parser()
                         .parseFrom(getStatsManager().getReports(getConfigId()));
+                dropShellIdentity();
             }
         } catch (InvalidProtocolBufferException | StatsUnavailableException se) {
             Log.e(LOG_TAG, "Retreiving event metrics failed.", se);
@@ -195,15 +204,17 @@
      * Returns the list of GaugeMetric data tracked under the config.
      */
     public List<GaugeMetricData> getGaugeMetrics() {
-        // Dump the metric after the test is completed.
-        StatsLog.logEvent(0);
-        SystemClock.sleep(METRIC_DELAY_MS);
         ConfigMetricsReportList reportList = null;
         List<GaugeMetricData> gaugeData = new ArrayList<>();
         try {
             if (getConfigId() != -1) {
+                adoptShellIdentity();
+                StatsLog.logEvent(0);
+                // Dump the the counters after the test completed.
+                SystemClock.sleep(METRIC_DELAY_MS);
                 reportList = ConfigMetricsReportList.parser()
                         .parseFrom(getStatsManager().getReports(getConfigId()));
+                dropShellIdentity();
             }
         } catch (InvalidProtocolBufferException | StatsUnavailableException se) {
             Log.e(LOG_TAG, "Retreiving gauge metrics failed.", se);
@@ -228,7 +239,9 @@
     public boolean removeStatsConfig() {
         Log.i(LOG_TAG, "Removing statsd config-id: " + getConfigId());
         try {
+            adoptShellIdentity();
             getStatsManager().removeConfig(getConfigId());
+            dropShellIdentity();
             Log.i(LOG_TAG, "Successfully removed config-id: " + getConfigId());
             return true;
         } catch (StatsUnavailableException e) {
@@ -288,4 +301,21 @@
     private int getUniqueId() {
         return UUID.randomUUID().hashCode();
     }
+
+    /**
+     * Adopts shell permission identity needed to access StatsManager service
+     */
+    public static void adoptShellIdentity() {
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .adoptShellPermissionIdentity();
+    }
+
+    /**
+     * Drop shell permission identity
+     */
+    public static void dropShellIdentity() {
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .dropShellPermissionIdentity();
+    }
+
 }
diff --git a/libraries/collectors-helper/statsd/test/src/com/android/helpers/CpuUsageHelperTest.java b/libraries/collectors-helper/statsd/test/src/com/android/helpers/CpuUsageHelperTest.java
index bedc851..37e0ac4 100644
--- a/libraries/collectors-helper/statsd/test/src/com/android/helpers/CpuUsageHelperTest.java
+++ b/libraries/collectors-helper/statsd/test/src/com/android/helpers/CpuUsageHelperTest.java
@@ -78,7 +78,8 @@
         assertTrue(mCpuUsageHelper.startCollecting());
         mHelper.get().open();
         Map<String, Long> cpuUsage = mCpuUsageHelper.getMetrics();
-        assertTrue(cpuUsage.size() > 0);
+        // Which includes two default total usage per pkg and per freq
+        assertTrue(cpuUsage.size() > 2);
         assertTrue(mCpuUsageHelper.stopCollecting());
         mHelper.get().exit();
     }
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/IncidentReportListener.java b/libraries/device-collectors/src/main/java/android/device/collectors/IncidentReportListener.java
new file mode 100644
index 0000000..ac276ba
--- /dev/null
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/IncidentReportListener.java
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+package android.device.collectors;
+
+import android.device.collectors.annotations.OptionClass;
+import android.os.Environment;
+import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+
+/**
+ * A {@link BaseMetricListener} that captures a full incident report at the end of a test run in
+ * proto format and records it to a fixed file location.
+ *
+ * Note: This class requires both {@code READ_EXTERNAL_STORAGE} and {@code WRITE_EXTERNAL_STORAGE}
+ * permissions.
+ */
+@OptionClass(alias = "incident-collector")
+public class IncidentReportListener extends BaseMetricListener {
+    private static final String LOG_TAG = IncidentReportListener.class.getSimpleName();
+    private static final String REDIRECT_INCIDENT_REPORT_CMD = "incident -b -p %s";
+    // Separate directory name for conveniently calling {@code this#createAndEmptyDirectory}, which
+    // is already conditioned on the external storage directory path. If improved, remove constant.
+    private static final String DIRECTORY_NAME = "incidents";
+    static final Path REPORT_DIRECTORY =
+            Environment.getExternalStorageDirectory().toPath().resolve(DIRECTORY_NAME);
+    static final Path FINAL_REPORT_PATH = REPORT_DIRECTORY.resolve("final.pb");
+    static final String FINAL_REPORT_KEY = "incident-report-final";
+
+    public IncidentReportListener() {
+        super();
+    }
+
+    @Override
+    public void onTestRunStart(DataRecord runData, Description description) {
+        Log.i(LOG_TAG, "Removing all contents from report directory.");
+        File result = createAndEmptyDirectory(DIRECTORY_NAME);
+        if (result == null) {
+            throw new RuntimeException(String.format(
+                    "Couldn't create destination folder: %s.", REPORT_DIRECTORY.toString()));
+        }
+    }
+
+    @Override
+    public void onTestEnd(DataRecord testData, Description description) {
+        // TODO(b/119120269): Collect per-test incidents for granular debugging.
+    }
+
+    @Override
+    public void onTestRunEnd(DataRecord runData, Result result) {
+        // Fail fast if the parent directory was not successfully created.
+        if (!REPORT_DIRECTORY.toFile().exists()) {
+            throw new IllegalStateException(String.format(
+                    "Report destination folder does not exist: %s", REPORT_DIRECTORY.toString()));
+        }
+        // Construct and execute the incident report command with output.
+        String fullReportCmd = String.format(REDIRECT_INCIDENT_REPORT_CMD, "EXPLICIT");
+        Log.v(LOG_TAG, String.format("Collecting full incident report: %s.", fullReportCmd));
+        // Adopt shell permissions for just the report collection command.
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .adoptShellPermissionIdentity();
+        byte[] output = executeCommandBlocking(fullReportCmd);
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .dropShellPermissionIdentity();
+        if (output == null) {
+            // Null output signals the command did not successfully execute.
+            throw new RuntimeException("Failed to run the incident report command.");
+        } if (output.length == 0) {
+            // Empty output signals the command did not return any content.
+            throw new RuntimeException("The incident report command returned no data.");
+        } else {
+            // Write the report bytes to the destination and fail if it already exists.
+            try {
+                Files.write(FINAL_REPORT_PATH, output, StandardOpenOption.CREATE_NEW);
+            } catch (IOException e) {
+                throw new RuntimeException("Unable to write full incident report.", e);
+            }
+            // Ensure the newly created report exists at the expected destination.
+            File destination = FINAL_REPORT_PATH.toFile();
+            if (!destination.exists()) {
+                throw new RuntimeException("Unable to find the full incident report available.");
+            }
+            // Declare success given the file exists and is non-empty.
+            runData.addFileMetric(FINAL_REPORT_KEY, destination);
+        }
+    }
+}
diff --git a/libraries/device-collectors/src/main/platform-collectors/Android.bp b/libraries/device-collectors/src/main/platform-collectors/Android.bp
index 70ff87d..719211c 100644
--- a/libraries/device-collectors/src/main/platform-collectors/Android.bp
+++ b/libraries/device-collectors/src/main/platform-collectors/Android.bp
@@ -45,6 +45,7 @@
 
     static_libs: [
         "collector-device-lib",
+        "libprotobuf-java-lite",
         "statsd-config-protos",
         "statsd-helper",
     ],
diff --git a/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/README.md b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/README.md
index 25c6630..acd59ed 100644
--- a/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/README.md
+++ b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/README.md
@@ -14,3 +14,32 @@
 1. Create a directory under this directory for the new config (e.g. `app-start`).
 2. Put the new config in the subdirectory using the directory name + `.pb` extension.
 3. Write a README file explaining what the config does and put it under the new subdirectory.
+
+# (Internal only) Creating a config
+
+_This section is subject to change as the Android Metrics team evolve their tools._
+
+To create a config, follow these steps:
+
+1. Follow the
+[Add your metrics to a config](http://go/westworld-modulefooding#add-your-metrics-to-a-config)
+section (and this section only) in the Android Metrics documentation to create a new config file.
+2. Validate the config following the
+[Validate and sent a changelist for review](http://go/westworld-modulefooding#validate-and-send-a-changelist-for-review)
+section, but skip the sending CL for review part.
+2. Build the config parsing utility:
+`blaze build -c opt java/com/google/wireless/android/stats/westworld:parse_definitions`
+3. Use the utility to create the binary config:
+```
+blaze-bin/java/com/google/wireless/android/stats/westworld/parse_definitions \
+--action=WRITE_STATSD_CONFIG \
+--definitions_dir=wireless/android/stats/platform/westworld/public/definitions/westworld/ \
+--config_name=<You config's name defined in step 1> \
+--allowed_sources=<Comma separated list of allowed log sources> \
+--output_file=<Output path of your config>
+```
+Common allowed sources include `AID_ROOT`, `AID_SYSTEM`, `AID_RADIO`, `AID_BLUETOOTH`,
+`AID_GRAPHICS`, `AID_STATSD` and `AID_INCIDENTD`.
+
+Once the config file is generated, it can be checked in following the steps in the "Checking in a
+config" section.
diff --git a/libraries/device-collectors/src/main/platform-collectors/src/android/device/collectors/StatsdListener.java b/libraries/device-collectors/src/main/platform-collectors/src/android/device/collectors/StatsdListener.java
new file mode 100644
index 0000000..8b58662
--- /dev/null
+++ b/libraries/device-collectors/src/main/platform-collectors/src/android/device/collectors/StatsdListener.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2019 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.app.StatsManager;
+import android.app.StatsManager.StatsUnavailableException;
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.os.Bundle;
+import android.os.Environment;
+import android.util.Log;
+import androidx.annotation.VisibleForTesting;
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/** A device-side metric listener that collects statsd-based metrics using bundled config files. */
+public class StatsdListener extends BaseMetricListener {
+    private static final String LOG_TAG = StatsdListener.class.getSimpleName();
+
+    // TODO(harrytczhang): Add option and support for per-test collection.
+    static final String OPTION_CONFIGS_TEST_RUN = "statsd-configs-per-run";
+
+    // Sub-directory within the test APK's assets/ directory to look for configs.
+    static final String CONFIG_SUB_DIRECTORY = "statsd-configs";
+    // File extension for all statsd configs.
+    static final String PROTO_EXTENSION = ".pb";
+
+    // Parent directory for all statsd reports.
+    static final String REPORT_PATH_ROOT = "statsd-reports";
+    // Sub-directory for test run reports.
+    static final String REPORT_PATH_TEST_RUN = "test-run";
+
+    // Configs used for tests and test runs, respectively.
+    private Map<String, StatsdConfig> mTestRunConfigs = new HashMap<String, StatsdConfig>();
+
+    // Map to associate config names with their config Ids.
+    private Map<String, Long> mTestRunConfigIds = new HashMap<String, Long>();
+
+    // Cached stats manager instance.
+    private StatsManager mStatsManager;
+
+    /** Registers the test run configs with {@link StatsManager} before the test run starts. */
+    @Override
+    public void onTestRunStart(DataRecord runData, Description description) {
+        // The argument parsing has to be performed here as the instrumentation has not yet been
+        // registered when the constructor of this class is called.
+        mTestRunConfigs.putAll(getConfigsFromOption(OPTION_CONFIGS_TEST_RUN));
+
+        mTestRunConfigIds = registerConfigsWithStatsManager(mTestRunConfigs);
+    }
+
+    /**
+     * Dumps the test run stats reports to the test run subdirectory after the test run ends.
+     *
+     * <p>Dumps the stats regardless of whether all the tests pass.
+     */
+    @Override
+    public void onTestRunEnd(DataRecord runData, Result result) {
+        Map<String, File> configReports =
+                pullReportsAndRemoveConfigs(
+                        mTestRunConfigIds, Paths.get(REPORT_PATH_ROOT, REPORT_PATH_TEST_RUN));
+        for (String configName : configReports.keySet()) {
+            runData.addFileMetric(configName, configReports.get(configName));
+        }
+    }
+
+    /**
+     * Register a set of statsd configs and return their config IDs in a {@link Map}.
+     *
+     * @param configs Map of (config name, config proto message)
+     * @return Map of (config name, config id)
+     */
+    private Map<String, Long> registerConfigsWithStatsManager(
+            final Map<String, StatsdConfig> configs) {
+        Map<String, Long> configIds = new HashMap<String, Long>();
+        adoptShellPermissionIdentity();
+        for (String configName : configs.keySet()) {
+            long configId = getUniqueIdForConfig(configs.get(configName));
+            StatsdConfig newConfig = configs.get(configName).toBuilder().setId(configId).build();
+            try {
+                addStatsConfig(configId, newConfig.toByteArray());
+                configIds.put(configName, configId);
+            } catch (StatsUnavailableException e) {
+                Log.e(
+                        LOG_TAG,
+                        String.format(
+                                "Failed to add statsd config %s due to %s.",
+                                configName, e.toString()));
+            }
+        }
+        dropShellPermissionIdentity();
+        return configIds;
+    }
+
+    /**
+     * For a set of statsd config ids, retrieve the config reports from {@link StatsManager}, remove
+     * the config and dump the reports into the designated directory on the device's external
+     * storage.
+     *
+     * @param configIds Map of (config name, config Id)
+     * @param directory relative directory on external storage to dump the report in. Each report
+     *     will be named after its config.
+     * @return Map of (config name, config report file)
+     */
+    private Map<String, File> pullReportsAndRemoveConfigs(
+            final Map<String, Long> configIds, Path directory) {
+        File externalStorage = Environment.getExternalStorageDirectory();
+        File saveDirectory = new File(externalStorage, directory.toString());
+        if (!saveDirectory.isDirectory()) {
+            saveDirectory.mkdirs();
+        }
+        Map<String, File> savedConfigFiles = new HashMap<String, File>();
+        adoptShellPermissionIdentity();
+        for (String configName : configIds.keySet()) {
+            // Dump the metric report to external storage.
+            ConfigMetricsReportList reportList;
+            try {
+                reportList =
+                        ConfigMetricsReportList.parseFrom(
+                                getStatsReports(configIds.get(configName)));
+                File reportFile = new File(saveDirectory, configName + PROTO_EXTENSION);
+                writeToFile(reportFile, reportList.toByteArray());
+                savedConfigFiles.put(configName, reportFile);
+            } catch (StatsUnavailableException e) {
+                Log.e(
+                        LOG_TAG,
+                        String.format(
+                                "Failed to retrieve metrics for config %s due to %s.",
+                                configName, e.toString()));
+            } catch (InvalidProtocolBufferException e) {
+                Log.e(
+                        LOG_TAG,
+                        String.format(
+                                "Unable to parse report for config %s. Details: %s.",
+                                configName, e.toString()));
+            } catch (IOException e) {
+                Log.e(
+                        LOG_TAG,
+                        String.format(
+                                "Failed to write metric report for config %s to device. "
+                                        + "Details: %s.",
+                                configName, e.toString()));
+            }
+
+            // Remove the statsd config.
+            try {
+                removeStatsConfig(configIds.get(configName));
+            } catch (StatsUnavailableException e) {
+                Log.e(
+                        LOG_TAG,
+                        String.format(
+                                "Unable to remove config %s due to %s.", configName, e.toString()));
+            }
+        }
+        dropShellPermissionIdentity();
+        return savedConfigFiles;
+    }
+
+    /**
+     * Adopt shell permission identity to communicate with {@link StatsManager}.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    protected void adoptShellPermissionIdentity() {
+        InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation()
+                .adoptShellPermissionIdentity();
+    }
+
+    /**
+     * Drop shell permission identity once communication with {@link StatsManager} is done.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    protected void dropShellPermissionIdentity() {
+        InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation()
+                .dropShellPermissionIdentity();
+    }
+
+    /** Returns the cached {@link StatsManager} instance; if none exists, request and cache it. */
+    private StatsManager getStatsManager() {
+        if (mStatsManager == null) {
+            mStatsManager =
+                    (StatsManager)
+                            InstrumentationRegistry.getTargetContext()
+                                    .getSystemService(Context.STATS_MANAGER);
+        }
+        return mStatsManager;
+    }
+
+    /**
+     * Forwarding logic for {@link StatsManager} as it is final and cannot be mocked.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    protected void addStatsConfig(long configKey, byte[] config) throws StatsUnavailableException {
+        getStatsManager().addConfig(configKey, config);
+    }
+
+    /**
+     * Forwarding logic for {@link StatsManager} as it is final and cannot be mocked.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    protected void removeStatsConfig(long configKey) throws StatsUnavailableException {
+        mStatsManager.removeConfig(configKey);
+    }
+
+    /**
+     * Forwarding logic for {@link StatsManager} as it is final and cannot be mocked.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    protected byte[] getStatsReports(long configKey) throws StatsUnavailableException {
+        return mStatsManager.getReports(configKey);
+    }
+
+    /**
+     * Allow tests to stub out getting instrumentation arguments.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    protected Bundle getArguments() {
+        return InstrumentationRegistry.getArguments();
+    }
+
+    /**
+     * Allow tests to stub out file I/O.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    protected File writeToFile(File f, byte[] content) throws IOException {
+        Files.write(f.toPath(), content);
+        return f;
+    }
+
+    /**
+     * Allow tests to override the random ID generation. The config is passed in to allow a specific
+     * ID to be associated with a config in the test.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    protected long getUniqueIdForConfig(StatsdConfig config) {
+        return (long) UUID.randomUUID().hashCode();
+    }
+
+    /**
+     * Allow tests to stub out {@link AssetManager} interactions as that class is final and cannot .
+     * be mocked.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    protected InputStream openConfigWithAssetManager(AssetManager manager, String configName)
+            throws IOException {
+        String configFilePath =
+                Paths.get(CONFIG_SUB_DIRECTORY, configName + PROTO_EXTENSION).toString();
+        return manager.open(configFilePath);
+    }
+
+    /**
+     * Parse a config from its name using {@link AssetManager}.
+     *
+     * <p>The option name is passed in for better error messaging.
+     */
+    private StatsdConfig parseConfigFromName(
+            final AssetManager manager, String optionName, String configName) {
+        try (InputStream configStream = openConfigWithAssetManager(manager, configName)) {
+            try {
+                return StatsdConfig.parseFrom(configStream);
+            } catch (IOException e) {
+                throw new RuntimeException(
+                        String.format(
+                                "Cannot parse profile %s in option %s.", configName, optionName),
+                        e);
+            }
+        } catch (IOException e) {
+            throw new IllegalArgumentException(
+                    String.format(
+                            "Config name %s in option %s does not exist", configName, optionName));
+        }
+    }
+
+    /**
+     * Parse the suppplied option to get a set of statsd configs keyed by their names.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    protected Map<String, StatsdConfig> getConfigsFromOption(String optionName) {
+        List<String> configNames =
+                Arrays.asList(getArguments().getString(optionName, "").split(","))
+                        .stream()
+                        .map(s -> s.trim())
+                        .filter(s -> !s.isEmpty())
+                        .distinct()
+                        .collect(Collectors.toList());
+        // Look inside the APK assets for the configuration file.
+        final AssetManager manager = InstrumentationRegistry.getContext().getAssets();
+        return configNames
+                .stream()
+                .collect(
+                        Collectors.toMap(
+                                Function.identity(),
+                                configName ->
+                                        parseConfigFromName(manager, optionName, configName)));
+    }
+}
diff --git a/libraries/device-collectors/src/test/Android.bp b/libraries/device-collectors/src/test/java/Android.bp
similarity index 96%
rename from libraries/device-collectors/src/test/Android.bp
rename to libraries/device-collectors/src/test/java/Android.bp
index 9916986..025b393 100644
--- a/libraries/device-collectors/src/test/Android.bp
+++ b/libraries/device-collectors/src/test/java/Android.bp
@@ -19,7 +19,7 @@
     name: "CollectorDeviceLibTest",
     defaults: ["tradefed_errorprone_defaults"],
 
-    srcs: ["java/**/*.java"],
+    srcs: ["android/**/*.java"],
     static_libs: [
         "androidx.test.runner",
         "collector-device-lib",
@@ -33,3 +33,4 @@
 
     test_suites: ["general-tests"],
 }
+
diff --git a/libraries/device-collectors/src/test/AndroidManifest.xml b/libraries/device-collectors/src/test/java/AndroidManifest.xml
similarity index 100%
rename from libraries/device-collectors/src/test/AndroidManifest.xml
rename to libraries/device-collectors/src/test/java/AndroidManifest.xml
diff --git a/libraries/device-collectors/src/test/AndroidTest.xml b/libraries/device-collectors/src/test/java/AndroidTest.xml
similarity index 100%
rename from libraries/device-collectors/src/test/AndroidTest.xml
rename to libraries/device-collectors/src/test/java/AndroidTest.xml
diff --git a/libraries/device-collectors/src/test/java/android/device/collectors/IncidentReportListenerTest.java b/libraries/device-collectors/src/test/java/android/device/collectors/IncidentReportListenerTest.java
new file mode 100644
index 0000000..e64acc1
--- /dev/null
+++ b/libraries/device-collectors/src/test/java/android/device/collectors/IncidentReportListenerTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+package android.device.collectors;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.matches;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.app.Instrumentation;
+import android.os.Bundle;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+
+/**
+ * Android Unit tests for {@link IncidentReportListener}.
+ *
+ * To run:
+ * atest CollectorDeviceLibTest:android.device.collectors.IncidentReportListenerTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class IncidentReportListenerTest {
+    // A {@code File} representing the fixed-location report directory.
+    private static final File REPORT_DEST = IncidentReportListener.REPORT_DIRECTORY.toFile();
+    // A {@code File} representing the fixed-location full report.
+    private static final File REPORT_FILE = IncidentReportListener.FINAL_REPORT_PATH.toFile();
+    // A {@code Description} to pass when faking a test run start call.
+    private static final Description FAKE_DESCRIPTION = Description.createSuiteDescription("run");
+    // A non-empty array that represents an empty incident report.
+    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+    // A non-empty array that represents a valid incident report.
+    private static final byte[] NONEMPTY_BYTE_ARRAY = "full".getBytes();
+
+    private IncidentReportListener mListener;
+
+    @Mock
+    private Instrumentation mInstrumentation;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mListener = spy(new IncidentReportListener());
+        mListener.setInstrumentation(mInstrumentation);
+    }
+
+    @After
+    public void tearDown() {
+        REPORT_FILE.delete();
+        REPORT_DEST.delete();
+    }
+
+    /** Tests the collector rewrites the directory at the start of a test run. */
+    @Test
+    public void testCreateDirectoryAtStart() throws Exception {
+        doReturn(NONEMPTY_BYTE_ARRAY).when(mListener)
+                .executeCommandBlocking(matches("rm -rf .*"));
+        assertFalse(REPORT_DEST.exists());
+        assertFalse(REPORT_FILE.exists());
+        mListener.testRunStarted(FAKE_DESCRIPTION);
+        assertTrue(REPORT_DEST.exists());
+    }
+
+    /** Tests the collector writes a new full report at the end of a test run. */
+    @Test
+    public void testCollectFullReportAtEnd() throws Exception {
+        doReturn(NONEMPTY_BYTE_ARRAY)
+                .when(mListener)
+                .executeCommandBlocking(matches("incident -b -p .*"));
+        doReturn(NONEMPTY_BYTE_ARRAY).when(mListener)
+                .executeCommandBlocking(matches("rm -rf .*"));
+        mListener.testRunStarted(FAKE_DESCRIPTION);
+        assertFalse(REPORT_FILE.exists());
+        mListener.testRunFinished(new Result());
+        assertTrue(REPORT_FILE.exists());
+        assertTrue(REPORT_FILE.isFile());
+        assertNotEquals(REPORT_FILE.getTotalSpace(), 0);
+    }
+
+    /** Tests the collector throws if the output directory is not created or does not exist. */
+    @Test
+    public void testFailCollectIfNoDestinationDirectory() throws Exception {
+        doReturn(NONEMPTY_BYTE_ARRAY)
+                .when(mListener)
+                .executeCommandBlocking(matches("incident -b -p .*"));
+        doReturn(null).when(mListener).createAndEmptyDirectory(anyString());
+        // Ensure the test run start throws exception failing to create the folder.
+        try {
+            mListener.onTestRunStart(mListener.createDataRecord(), FAKE_DESCRIPTION);
+            fail("Exception should throw failing to create destination.");
+        } catch (RuntimeException e) {
+            assertTrue(e.getMessage().contains("Couldn't create"));
+            assertTrue(e.getMessage().contains("destination folder"));
+        }
+        assertFalse(REPORT_DEST.exists());
+        // Ensure the test run end throws exception without the available folder.
+        try {
+            mListener.onTestRunEnd(mListener.createDataRecord(), new Result());
+            fail("Exception should throw for missing directory.");
+        } catch (RuntimeException e) {
+            assertTrue(e.getMessage().contains("destination folder"));
+            assertTrue(e.getMessage().contains("does not exist"));
+        }
+    }
+
+    /** Tests the collector fails to write a new full report if the report data is empty. */
+    @Test
+    public void testCollectEmptyReportFails() throws Exception {
+        doReturn(EMPTY_BYTE_ARRAY)
+                .when(mListener)
+                .executeCommandBlocking(matches("incident -b -p .*"));
+        doReturn(NONEMPTY_BYTE_ARRAY).when(mListener)
+                .executeCommandBlocking(matches("rm -rf .*"));
+        mListener.testRunStarted(FAKE_DESCRIPTION);
+        try {
+            mListener.onTestRunEnd(mListener.createDataRecord(), new Result());
+            fail("Exception should throw for empty results.");
+        } catch (RuntimeException e) {
+            assertTrue(e.getMessage().contains("no data"));
+        }
+    }
+
+    /** Tests the collector includes the full report in the {@code DataRecord}. */
+    @Test
+    public void testReportMetricsAsData() throws Exception {
+        doReturn(NONEMPTY_BYTE_ARRAY)
+                .when(mListener)
+                .executeCommandBlocking(matches("incident -b -p .*"));
+        doReturn(NONEMPTY_BYTE_ARRAY).when(mListener)
+                .executeCommandBlocking(matches("rm -rf .*"));
+        mListener.testRunStarted(FAKE_DESCRIPTION);
+        DataRecord record = mListener.createDataRecord();
+        mListener.onTestRunEnd(record, new Result());
+        assertTrue(record.hasMetrics());
+        Bundle metrics = record.createBundleFromMetrics();
+        assertTrue(metrics.containsKey(IncidentReportListener.FINAL_REPORT_KEY));
+        assertEquals(metrics.getString(IncidentReportListener.FINAL_REPORT_KEY),
+                REPORT_FILE.getAbsolutePath());
+    }
+}
diff --git a/libraries/device-collectors/src/test/Android.bp b/libraries/device-collectors/src/test/platform/Android.bp
similarity index 78%
copy from libraries/device-collectors/src/test/Android.bp
copy to libraries/device-collectors/src/test/platform/Android.bp
index 9916986..a6c93ec 100644
--- a/libraries/device-collectors/src/test/Android.bp
+++ b/libraries/device-collectors/src/test/platform/Android.bp
@@ -1,5 +1,5 @@
 //
-// Copyright (C) 2017 The Android Open Source Project
+// Copyright (C) 2019 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,20 +16,19 @@
 
 // Android Unit Test target
 android_test {
-    name: "CollectorDeviceLibTest",
+    name: "CollectorDeviceLibPlatformTest",
     defaults: ["tradefed_errorprone_defaults"],
 
-    srcs: ["java/**/*.java"],
+    srcs: ["android/**/*.java"],
     static_libs: [
         "androidx.test.runner",
-        "collector-device-lib",
+        "collector-device-lib-platform",
         "junit",
         "mockito-target-minus-junit4",
         "perfetto-helper",
+        "platformprotoslite",
         "ub-uiautomator",
     ],
-
-    sdk_version: "current",
-
-    test_suites: ["general-tests"],
+    platform_apis: true,
 }
+
diff --git a/libraries/device-collectors/src/test/platform/AndroidManifest.xml b/libraries/device-collectors/src/test/platform/AndroidManifest.xml
new file mode 100644
index 0000000..65ca9cf
--- /dev/null
+++ b/libraries/device-collectors/src/test/platform/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.device.collectors.platform">
+    <uses-sdk android:minSdkVersion="1"
+          android:targetSdkVersion="26"/>
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.device.collectors.platform"
+                     android:label="Unit tests for device collectors using platform APIs.">
+    </instrumentation>
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+</manifest>
diff --git a/libraries/device-collectors/src/test/platform/android/device/collectors/StatsdListenerTest.java b/libraries/device-collectors/src/test/platform/android/device/collectors/StatsdListenerTest.java
new file mode 100644
index 0000000..411623c
--- /dev/null
+++ b/libraries/device-collectors/src/test/platform/android/device/collectors/StatsdListenerTest.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2019 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.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.AssetManager;
+import android.os.Bundle;
+
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.os.StatsLog.ConfigMetricsReportList.ConfigKey;
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.nio.file.Paths;
+import java.util.Map;
+
+/** Unit tests for {@link StatsdListener}. */
+public class StatsdListenerTest {
+    private StatsdListener mListener;
+
+    private static final String CONFIG_NAME_1 = "config-1";
+    private static final String CONFIG_NAME_2 = "config-2";
+
+    private static final long CONFIG_ID_1 = 1;
+    private static final long CONFIG_ID_2 = 2;
+
+    private static final StatsdConfig CONFIG_1 =
+            StatsdConfig.newBuilder().setId(CONFIG_ID_1).build();
+    private static final StatsdConfig CONFIG_2 =
+            StatsdConfig.newBuilder().setId(CONFIG_ID_2).build();
+
+    private static final ConfigMetricsReportList REPORT_1 =
+            ConfigMetricsReportList.newBuilder()
+                    .setConfigKey(ConfigKey.newBuilder().setUid(0).setId(CONFIG_ID_1))
+                    .build();
+    private static final ConfigMetricsReportList REPORT_2 =
+            ConfigMetricsReportList.newBuilder()
+                    .setConfigKey(ConfigKey.newBuilder().setUid(0).setId(CONFIG_ID_2))
+                    .build();
+
+    private static final ImmutableMap<String, StatsdConfig> CONFIG_MAP =
+            ImmutableMap.of(CONFIG_NAME_1, CONFIG_1, CONFIG_NAME_2, CONFIG_2);
+
+    @Rule public ExpectedException mExpectedException = ExpectedException.none();
+
+    @Before
+    public void setUp() throws Exception {
+        mListener = spy(new StatsdListener());
+        // Stub the report collection to isolate collector from StatsManager.
+        doNothing().when(mListener).addStatsConfig(anyLong(), any());
+        doReturn(REPORT_1.toByteArray()).when(mListener).getStatsReports(eq(CONFIG_ID_1));
+        doReturn(REPORT_2.toByteArray()).when(mListener).getStatsReports(eq(CONFIG_ID_2));
+        doNothing().when(mListener).removeStatsConfig(anyLong());
+        // Stub calls to permission APIs.
+        doNothing().when(mListener).adoptShellPermissionIdentity();
+        doNothing().when(mListener).dropShellPermissionIdentity();
+        // Stub file I/O.
+        doAnswer(invocation -> invocation.getArgument(0)).when(mListener).writeToFile(any(), any());
+        // Stub randome UUID generation.
+        doReturn(CONFIG_ID_1).when(mListener).getUniqueIdForConfig(eq(CONFIG_1));
+        doReturn(CONFIG_ID_2).when(mListener).getUniqueIdForConfig(eq(CONFIG_2));
+    }
+
+    /** Test that the collector has correct interactions with statsd for per-run collection. */
+    @Test
+    public void testPerRunCollection_statsdInteraction() throws Exception {
+        doReturn(CONFIG_MAP)
+                .when(mListener)
+                .getConfigsFromOption(eq(StatsdListener.OPTION_CONFIGS_TEST_RUN));
+
+        DataRecord runData = new DataRecord();
+        Description description = Description.createSuiteDescription("TestRun");
+
+        mListener.onTestRunStart(runData, description);
+        verify(mListener, times(1)).addStatsConfig(eq(CONFIG_ID_1), eq(CONFIG_1.toByteArray()));
+        verify(mListener, times(1)).addStatsConfig(eq(CONFIG_ID_2), eq(CONFIG_2.toByteArray()));
+
+        mListener.onTestRunEnd(runData, new Result());
+        verify(mListener, times(1)).getStatsReports(eq(CONFIG_ID_1));
+        verify(mListener, times(1)).getStatsReports(eq(CONFIG_ID_2));
+        verify(mListener, times(1)).removeStatsConfig(eq(CONFIG_ID_1));
+        verify(mListener, times(1)).removeStatsConfig(eq(CONFIG_ID_2));
+    }
+
+    /** Test that the collector dumps reports and report them as metrics. */
+    @Test
+    public void testPerRunCollection_metrics() throws Exception {
+        doReturn(CONFIG_MAP)
+                .when(mListener)
+                .getConfigsFromOption(eq(StatsdListener.OPTION_CONFIGS_TEST_RUN));
+
+        // Mock the DataRecord class as its content is not directly visible.
+        DataRecord runData = mock(DataRecord.class);
+        Description description = Description.createSuiteDescription("TestRun");
+
+        mListener.onTestRunStart(runData, description);
+        mListener.onTestRunEnd(runData, new Result());
+
+        verify(mListener, times(1))
+                .writeToFile(
+                        getFileNameMatcher(
+                                Paths.get(
+                                                StatsdListener.REPORT_PATH_ROOT,
+                                                StatsdListener.REPORT_PATH_TEST_RUN)
+                                        .toString(),
+                                CONFIG_NAME_1 + StatsdListener.PROTO_EXTENSION),
+                        any());
+        verify(runData, times(1))
+                .addFileMetric(
+                        eq(CONFIG_NAME_1),
+                        getFileNameMatcher(
+                                Paths.get(
+                                                StatsdListener.REPORT_PATH_ROOT,
+                                                StatsdListener.REPORT_PATH_TEST_RUN)
+                                        .toString(),
+                                CONFIG_NAME_1 + StatsdListener.PROTO_EXTENSION));
+        verify(mListener, times(1))
+                .writeToFile(
+                        getFileNameMatcher(
+                                Paths.get(
+                                                StatsdListener.REPORT_PATH_ROOT,
+                                                StatsdListener.REPORT_PATH_TEST_RUN)
+                                        .toString(),
+                                CONFIG_NAME_2 + StatsdListener.PROTO_EXTENSION),
+                        any());
+        verify(runData, times(1))
+                .addFileMetric(
+                        eq(CONFIG_NAME_2),
+                        getFileNameMatcher(
+                                Paths.get(
+                                                StatsdListener.REPORT_PATH_ROOT,
+                                                StatsdListener.REPORT_PATH_TEST_RUN)
+                                        .toString(),
+                                CONFIG_NAME_2 + StatsdListener.PROTO_EXTENSION));
+    }
+
+    /** Test that the collector parses the configs from arguments correctly for valid configs. */
+    @Test
+    public void testParsingConfigFromArguments_validConfig() throws Exception {
+        // Stub two configs for testing.
+        ByteArrayInputStream config1Stream = new ByteArrayInputStream(CONFIG_1.toByteArray());
+        doReturn(config1Stream)
+                .when(mListener)
+                .openConfigWithAssetManager(any(AssetManager.class), eq(CONFIG_NAME_1));
+
+        ByteArrayInputStream config2Stream = new ByteArrayInputStream(CONFIG_2.toByteArray());
+        doReturn(config2Stream)
+                .when(mListener)
+                .openConfigWithAssetManager(any(AssetManager.class), eq(CONFIG_NAME_2));
+
+        Bundle args = new Bundle();
+        args.putString(
+                StatsdListener.OPTION_CONFIGS_TEST_RUN,
+                String.join(",", CONFIG_NAME_1, CONFIG_NAME_2));
+        doReturn(args).when(mListener).getArguments();
+
+        Map<String, StatsdConfig> configs =
+                mListener.getConfigsFromOption(StatsdListener.OPTION_CONFIGS_TEST_RUN);
+        Assert.assertTrue(configs.containsKey(CONFIG_NAME_1));
+        Assert.assertEquals(configs.get(CONFIG_NAME_1).getId(), CONFIG_ID_1);
+        Assert.assertTrue(configs.containsKey(CONFIG_NAME_2));
+        Assert.assertEquals(configs.get(CONFIG_NAME_2).getId(), CONFIG_ID_2);
+        Assert.assertEquals(configs.size(), 2);
+    }
+
+    /** Test that the colletor fails and throws the right exception for an invalid config. */
+    @Test
+    public void testParsingConfigFromArguments_malformedConfig() throws Exception {
+        // Set up an invalid config for testing.
+        ByteArrayInputStream configStream = new ByteArrayInputStream("not a config".getBytes());
+        doReturn(configStream)
+                .when(mListener)
+                .openConfigWithAssetManager(any(AssetManager.class), eq(CONFIG_NAME_1));
+
+        Bundle args = new Bundle();
+        args.putString(StatsdListener.OPTION_CONFIGS_TEST_RUN, CONFIG_NAME_1);
+        doReturn(args).when(mListener).getArguments();
+
+        mExpectedException.expectMessage("Cannot parse");
+        Map<String, StatsdConfig> configs =
+                mListener.getConfigsFromOption(StatsdListener.OPTION_CONFIGS_TEST_RUN);
+    }
+
+    /** Test that the collector fails and throws the right exception for a nonexistent config. */
+    @Test
+    public void testParsingConfigFromArguments_nonexistentConfig() {
+        Bundle args = new Bundle();
+        args.putString(StatsdListener.OPTION_CONFIGS_TEST_RUN, "nah");
+        doReturn(args).when(mListener).getArguments();
+
+        mExpectedException.expectMessage("does not exist");
+        Map<String, StatsdConfig> configs =
+                mListener.getConfigsFromOption(StatsdListener.OPTION_CONFIGS_TEST_RUN);
+    }
+
+    /** Test that the collector has no effect when no config arguments are supplied. */
+    @Test
+    public void testNoConfigArguments() throws Exception {
+        doReturn(new Bundle()).when(mListener).getArguments();
+
+        // Mock the DataRecord class as its content is not directly visible.
+        DataRecord runData = mock(DataRecord.class);
+        Description description = Description.createSuiteDescription("TestRun");
+
+        mListener.onTestRunStart(runData, description);
+        mListener.onTestRunEnd(runData, new Result());
+
+        verify(runData, never()).addFileMetric(any(), any());
+        verify(mListener, never()).addStatsConfig(anyLong(), any());
+        verify(mListener, never()).getStatsReports(anyLong());
+        verify(mListener, never()).removeStatsConfig(anyLong());
+    }
+
+    private File getFileNameMatcher(String parentName, String filename) {
+        return argThat(f -> f.getParent().contains(parentName) && f.getName().equals(filename));
+    }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java b/libraries/flicker/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java
deleted file mode 100644
index c55d068..0000000
--- a/libraries/flicker/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.wm.flicker.monitor;
-
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.util.Log;
-
-/**
- * Captures Layers trace from SurfaceFlinger.
- */
-public class LayersTraceMonitor extends TraceMonitor {
-    private static final String TAG = "LayersTraceMonitor";
-    private IBinder mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger");
-
-    public LayersTraceMonitor() {
-        traceFileName = "layers_trace.pb";
-    }
-
-    @Override
-    public void start() {
-        setEnabled(true);
-    }
-
-    @Override
-    public void stop() {
-        setEnabled(false);
-    }
-
-    @Override
-    public boolean isEnabled() throws RemoteException {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeInterfaceToken("android.ui.ISurfaceComposer");
-        mSurfaceFlinger.transact(/* LAYER_TRACE_STATUS_CODE */ 1026,
-                data, reply, 0 /* flags */);
-        return reply.readBoolean();
-    }
-
-    private void setEnabled(boolean isEnabled) {
-        Parcel data = null;
-        try {
-            if (mSurfaceFlinger != null) {
-                data = Parcel.obtain();
-                data.writeInterfaceToken("android.ui.ISurfaceComposer");
-                data.writeInt(isEnabled ? 1 : 0);
-                mSurfaceFlinger.transact( /* LAYER_TRACE_CONTROL_CODE */ 1025,
-                        data, null, 0 /* flags */);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Could not set layer tracing." + e.toString());
-        } finally {
-            if (data != null) {
-                data.recycle();
-            }
-        }
-    }
-}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java b/libraries/flicker/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
deleted file mode 100644
index 4710684..0000000
--- a/libraries/flicker/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.wm.flicker.monitor;
-
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
-
-import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
-
-import android.os.Environment;
-import androidx.annotation.VisibleForTesting;
-import android.util.Log;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-
-/**
- * Captures screen contents and saves it as a mp4 video file.
- */
-public class ScreenRecorder {
-    private static final String TAG = "FLICKER";
-    private static final String OUTPUT_DIR =
-            Environment.getExternalStorageDirectory().getPath();
-    @VisibleForTesting
-    static final Path DEFAULT_OUTPUT_PATH = Paths.get(OUTPUT_DIR ,  "transition.mp4");
-    private Thread recorderThread;
-
-    @VisibleForTesting
-    static Path getPath(String testTag) {
-        return Paths.get(OUTPUT_DIR, testTag + ".mp4");
-    }
-
-    private static Path getPath(String testTag, int iteration) {
-        return Paths.get(OUTPUT_DIR, testTag + "_" + Integer.toString(iteration) + ".mp4");
-    }
-
-    public void start() {
-        String command = "screenrecord " + DEFAULT_OUTPUT_PATH;
-        recorderThread = new Thread(() -> {
-            try {
-                Runtime.getRuntime().exec(command);
-            } catch (IOException e) {
-                Log.e(TAG, "Error executing " + command, e);
-            }
-        });
-        recorderThread.start();
-    }
-
-    public void stop() {
-        runShellCommand("killall -s 2 screenrecord");
-        try {
-            recorderThread.join();
-        } catch (InterruptedException e) {
-            // ignore
-        }
-    }
-
-    public Path save(String testTag, int iteration) throws IOException {
-        return Files.move(DEFAULT_OUTPUT_PATH, getPath(testTag, iteration),
-                REPLACE_EXISTING);
-    }
-
-    public Path save(String testTag) throws IOException {
-        return Files.move(DEFAULT_OUTPUT_PATH, getPath(testTag), REPLACE_EXISTING);
-    }
-}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/TraceMonitor.java b/libraries/flicker/src/com/android/server/wm/flicker/monitor/TraceMonitor.java
deleted file mode 100644
index 45b122a..0000000
--- a/libraries/flicker/src/com/android/server/wm/flicker/monitor/TraceMonitor.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.wm.flicker.monitor;
-
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
-
-import android.os.Environment;
-import android.os.RemoteException;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.File;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Locale;
-
-/**
- * Base class for monitors containing common logic to read the trace
- * as a byte array and save the trace to another location.
- */
-public abstract class TraceMonitor {
-    public static final String TAG = "FLICKER";
-    private static final String TRACE_DIR = "/data/misc/wmtrace/";
-    private static final String OUTPUT_DIR =
-            Environment.getExternalStorageDirectory().getPath();
-
-    String traceFileName;
-
-    abstract void start();
-
-    abstract void stop();
-
-    abstract boolean isEnabled() throws RemoteException;
-
-    /**
-     * 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 iteration suffix added to trace name used to identify trace
-     * @return Path to saved trace file
-     */
-    public Path saveTraceFile(String testTag, int iteration) {
-        Path traceFileCopy = getOutputTraceFilePath(testTag, iteration);
-        String copyCommand = String.format(Locale.getDefault(), "mv %s%s %s", TRACE_DIR,
-                traceFileName, traceFileCopy.toString());
-        runShellCommand(copyCommand);
-        return traceFileCopy;
-    }
-
-    @VisibleForTesting
-    Path getOutputTraceFilePath(String testTag, int iteration) {
-        return Paths.get(OUTPUT_DIR, traceFileName + "_" + testTag + "_" + iteration);
-    }
-}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java b/libraries/flicker/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java
deleted file mode 100644
index ae160b6..0000000
--- a/libraries/flicker/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.wm.flicker.monitor;
-
-import android.os.RemoteException;
-import android.view.IWindowManager;
-import android.view.WindowManagerGlobal;
-
-/**
- * Captures WindowManager trace from WindowManager.
- */
-public class WindowManagerTraceMonitor extends TraceMonitor {
-    private IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
-
-    public WindowManagerTraceMonitor() {
-        traceFileName = "wm_trace.pb";
-    }
-
-    @Override
-    public void start() {
-        try {
-            wm.startWindowTrace();
-        } catch (RemoteException e) {
-            throw new RuntimeException("Could not start trace", e);
-        }
-    }
-
-    @Override
-    public void stop() {
-        try {
-            wm.stopWindowTrace();
-        } catch (RemoteException e) {
-            throw new RuntimeException("Could not stop trace", e);
-        }
-    }
-
-    @Override
-    public boolean isEnabled() throws RemoteException{
-        return wm.isWindowTraceEnabled();
-    }
-}
diff --git a/libraries/flicker/test/Android.bp b/libraries/flicker/test/Android.bp
deleted file mode 100644
index 489c745..0000000
--- a/libraries/flicker/test/Android.bp
+++ /dev/null
@@ -1,33 +0,0 @@
-//
-// 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.
-//
-
-android_test {
-    name: "FlickerLibTest",
-    // sign this with platform cert, so this test is allowed to call private platform apis
-    certificate: "platform",
-    platform_apis: true,
-    test_suites: ["tests"],
-    srcs: ["src/**/*.java"],
-    libs: ["android.test.runner"],
-    static_libs: [
-        "android-support-test",
-        "platform-test-annotations",
-        "truth-prebuilt",
-        "platformprotosnano",
-        "layersprotosnano",
-        "flickerlib",
-    ],
-}
diff --git a/libraries/flicker/test/AndroidManifest.xml b/libraries/flicker/test/AndroidManifest.xml
deleted file mode 100644
index b96b3fd..0000000
--- a/libraries/flicker/test/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright 2018 Google Inc. All Rights Reserved.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
-          android:sharedUserId="android.uid.system"
-          package="com.android.server.wm.flicker">
-
-    <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27"/>
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.HARDWARE_TEST" tools:ignore="ProtectedPermissions" />
-    <application android:label="FlickerLibTest">
-        <uses-library android:name="android.test.runner"/>
-    </application>
-
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="com.android.server.wm.flicker"
-                     android:label="WindowManager Flicker Lib Test">
-    </instrumentation>
-
-</manifest>
\ No newline at end of file
diff --git a/libraries/flicker/test/AndroidTest.xml b/libraries/flicker/test/AndroidTest.xml
deleted file mode 100644
index 2175621..0000000
--- a/libraries/flicker/test/AndroidTest.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright 2018 Google Inc. All Rights Reserved.
- -->
-<configuration description="Config for WindowManager Flicker Tests">
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true"/>
-        <option name="test-file-name" value="FlickerLibTest.apk"/>
-    </target_preparer>
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="package" value="com.android.server.wm.flicker"/>
-        <option name="hidden-api-checks" value="false" />
-    </test>
-</configuration>
diff --git a/libraries/flicker/test/assets/testdata/layers_trace_emptyregion.pb b/libraries/flicker/test/assets/testdata/layers_trace_emptyregion.pb
deleted file mode 100644
index 98ee6f3..0000000
--- a/libraries/flicker/test/assets/testdata/layers_trace_emptyregion.pb
+++ /dev/null
Binary files differ
diff --git a/libraries/flicker/test/assets/testdata/wm_trace_emptyregion.pb b/libraries/flicker/test/assets/testdata/wm_trace_emptyregion.pb
deleted file mode 100644
index 341bf55..0000000
--- a/libraries/flicker/test/assets/testdata/wm_trace_emptyregion.pb
+++ /dev/null
Binary files differ
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.java b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.java
deleted file mode 100644
index f14466f..0000000
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.wm.flicker.monitor;
-
-import static android.os.SystemClock.sleep;
-import static android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto.MAGIC_NUMBER_H;
-import static android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto.MAGIC_NUMBER_L;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto;
-
-import com.google.common.io.Files;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.File;
-
-/**
- * Contains {@link LayersTraceMonitor} tests.
- * To run this test: {@code atest FlickerLibTest:LayersTraceMonitorTest}
- */
-public class LayersTraceMonitorTest {
-    private LayersTraceMonitor mLayersTraceMonitor;
-
-    @Before
-    public void setup() {
-        mLayersTraceMonitor = new LayersTraceMonitor();
-    }
-
-    @After
-    public void teardown() {
-        mLayersTraceMonitor.stop();
-        mLayersTraceMonitor.getOutputTraceFilePath("captureLayersTrace", 0).toFile().delete();
-    }
-
-    @Test
-    public void canStartLayersTrace() throws Exception {
-        mLayersTraceMonitor.start();
-        assertThat(mLayersTraceMonitor.isEnabled()).isTrue();
-    }
-
-    @Test
-    public void canStopLayersTrace() throws Exception {
-        mLayersTraceMonitor.start();
-        assertThat(mLayersTraceMonitor.isEnabled()).isTrue();
-        mLayersTraceMonitor.stop();
-        assertThat(mLayersTraceMonitor.isEnabled()).isFalse();
-    }
-
-    @Test
-    public void captureLayersTrace() throws Exception {
-        mLayersTraceMonitor.start();
-        mLayersTraceMonitor.stop();
-        File testFile = mLayersTraceMonitor.saveTraceFile("captureLayersTrace", 0).toFile();
-        assertThat(testFile.exists()).isTrue();
-        byte[] trace = Files.toByteArray(testFile);
-        assertThat(trace.length).isGreaterThan(0);
-        LayersTraceFileProto mLayerTraceFileProto = LayersTraceFileProto.parseFrom(trace);
-        assertThat(mLayerTraceFileProto.magicNumber).isEqualTo(
-                (long) MAGIC_NUMBER_H << 32 | MAGIC_NUMBER_L);
-    }
-}
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java
deleted file mode 100644
index 7de93b7..0000000
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.wm.flicker.monitor;
-
-import static android.os.SystemClock.sleep;
-
-import static com.android.server.wm.flicker.monitor.ScreenRecorder.DEFAULT_OUTPUT_PATH;
-import static com.android.server.wm.flicker.monitor.ScreenRecorder.getPath;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * Contains {@link ScreenRecorder} tests.
- * To run this test: {@code atest FlickerLibTest:ScreenRecorderTest}
- */
-public class ScreenRecorderTest {
-    private static final String TEST_VIDEO_FILENAME = "test.mp4";
-    private ScreenRecorder mScreenRecorder;
-
-    @Before
-    public void setup() {
-        mScreenRecorder = new ScreenRecorder();
-    }
-
-    @After
-    public void teardown() {
-        DEFAULT_OUTPUT_PATH.toFile().delete();
-        getPath(TEST_VIDEO_FILENAME).toFile().delete();
-    }
-
-    @Test
-    public void videoIsRecorded() {
-        mScreenRecorder.start();
-        sleep(100);
-        mScreenRecorder.stop();
-        File file = DEFAULT_OUTPUT_PATH.toFile();
-        assertThat(file.exists()).isTrue();
-    }
-
-    @Test
-    public void videoCanBeSaved() throws IOException {
-        mScreenRecorder.start();
-        sleep(100);
-        mScreenRecorder.stop();
-        mScreenRecorder.save(TEST_VIDEO_FILENAME);
-        File file = getPath(TEST_VIDEO_FILENAME).toFile();
-        assertThat(file.exists()).isTrue();
-    }
-}
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.java b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.java
deleted file mode 100644
index 3d45302..0000000
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.wm.flicker.monitor;
-
-import static com.android.server.wm.nano.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
-import static com.android.server.wm.nano.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.android.server.wm.nano.WindowManagerTraceFileProto;
-
-import com.google.common.io.Files;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.File;
-
-/**
- * Contains {@link WindowManagerTraceMonitor} tests.
- * To run this test: {@code atest FlickerLibTest:WindowManagerTraceMonitorTest}
- */
-public class WindowManagerTraceMonitorTest {
-    private WindowManagerTraceMonitor mWindowManagerTraceMonitor;
-
-    @Before
-    public void setup() {
-        mWindowManagerTraceMonitor = new WindowManagerTraceMonitor();
-    }
-
-    @After
-    public void teardown() {
-        mWindowManagerTraceMonitor.stop();
-        mWindowManagerTraceMonitor.getOutputTraceFilePath("captureWindowTrace",
-                0).toFile().delete();
-    }
-
-    @Test
-    public void canStartWindowTrace() throws Exception {
-        mWindowManagerTraceMonitor.start();
-        assertThat(mWindowManagerTraceMonitor.isEnabled()).isTrue();
-    }
-
-    @Test
-    public void canStopWindowTrace() throws Exception {
-        mWindowManagerTraceMonitor.start();
-        assertThat(mWindowManagerTraceMonitor.isEnabled()).isTrue();
-        mWindowManagerTraceMonitor.stop();
-        assertThat(mWindowManagerTraceMonitor.isEnabled()).isFalse();
-    }
-
-    @Test
-    public void captureWindowTrace() throws Exception {
-        mWindowManagerTraceMonitor.start();
-        mWindowManagerTraceMonitor.stop();
-        File testFile = mWindowManagerTraceMonitor.saveTraceFile("captureWindowTrace", 0).toFile();
-        assertThat(testFile.exists()).isTrue();
-        byte[] trace = Files.toByteArray(testFile);
-        assertThat(trace.length).isGreaterThan(0);
-        WindowManagerTraceFileProto mWindowTraceFileProto = WindowManagerTraceFileProto.parseFrom(
-                trace);
-        assertThat(mWindowTraceFileProto.magicNumber).isEqualTo(
-                (long) MAGIC_NUMBER_H << 32 | MAGIC_NUMBER_L);
-    }
-}
diff --git a/libraries/health/composers/host/src/android/host/test/composer/Iterate.java b/libraries/health/composers/host/src/android/host/test/composer/Iterate.java
index 61df80e..7ad4e36 100644
--- a/libraries/health/composers/host/src/android/host/test/composer/Iterate.java
+++ b/libraries/health/composers/host/src/android/host/test/composer/Iterate.java
@@ -23,8 +23,8 @@
 public class Iterate<U> extends IterateBase<Map<String, String>, U> {
     @Override
     protected int getIterationsArgument(Map<String, String> args) {
-        if (args.containsKey(ITERATIONS_OPTION_NAME)) {
-            String iterations = args.get(ITERATIONS_OPTION_NAME);
+        if (args.containsKey(getOptionName())) {
+            String iterations = args.get(getOptionName());
             try {
                 return Integer.parseInt(iterations);
             } catch (NumberFormatException e) {
diff --git a/libraries/health/composers/host/src/android/host/test/composer/IterateBase.java b/libraries/health/composers/host/src/android/host/test/composer/IterateBase.java
index 80b5b58..cfa3702 100644
--- a/libraries/health/composers/host/src/android/host/test/composer/IterateBase.java
+++ b/libraries/health/composers/host/src/android/host/test/composer/IterateBase.java
@@ -33,6 +33,7 @@
     protected static final OrderOptions ORDER_DEFAULT_VALUE = OrderOptions.CYCLIC;
 
     protected final int mDefaultValue;
+    protected String mOptionName = ITERATIONS_OPTION_NAME;
 
     public IterateBase() {
         this(ITERATIONS_DEFAULT_VALUE);
@@ -68,4 +69,14 @@
 
     /** Returns the order that the iteration should happen in from {@code args}. */
     protected abstract OrderOptions getOrdersArgument(T args);
+
+    /** Returns the option name to supply values to this iterator. */
+    protected String getOptionName() {
+        return mOptionName;
+    }
+
+    /** Sets the option name for supply values to this iterator. */
+    public void setOptionName(String name) {
+        mOptionName = name;
+    }
 }
diff --git a/libraries/health/composers/host/tests/src/android/host/test/composer/IterateTest.java b/libraries/health/composers/host/tests/src/android/host/test/composer/IterateTest.java
index f19d90f..08df2c0 100644
--- a/libraries/health/composers/host/tests/src/android/host/test/composer/IterateTest.java
+++ b/libraries/health/composers/host/tests/src/android/host/test/composer/IterateTest.java
@@ -33,6 +33,11 @@
             if (mIterations != null) {
                 args.put(IterateTestBase.ITERATIONS_OPTION_NAME, String.valueOf(mIterations));
             }
+            if (mAlternateIterations != null) {
+                args.put(
+                        IterateTestBase.ITERATIONS_OPTION_ALTERNATE_NAME,
+                        String.valueOf(mAlternateIterations));
+            }
             if (mOrder != null) {
                 args.put(IterateTestBase.ORDER_OPTION_NAME, mOrder);
             }
diff --git a/libraries/health/composers/host/tests/src/android/host/test/composer/IterateTestBase.java b/libraries/health/composers/host/tests/src/android/host/test/composer/IterateTestBase.java
index ca9c2ef..a883db2 100644
--- a/libraries/health/composers/host/tests/src/android/host/test/composer/IterateTestBase.java
+++ b/libraries/health/composers/host/tests/src/android/host/test/composer/IterateTestBase.java
@@ -27,7 +27,6 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.ImmutableList;
 
-import org.junit.Assert;
 import org.junit.Test;
 import org.junit.Rule;
 import org.junit.rules.ExpectedException;
@@ -38,6 +37,7 @@
 public abstract class IterateTestBase<T> {
     protected static final int NUM_TESTS = 10;
     protected static final String ITERATIONS_OPTION_NAME = "iterations";
+    protected static final String ITERATIONS_OPTION_ALTERNATE_NAME = "iterationsAlternate";
     protected static final String ORDER_OPTION_NAME = "order";
     protected static final String ORDER_VAL_CYCLIC = "cyclic";
     protected static final String ORDER_VAL_SEQUENTIAL = "sequential";
@@ -60,8 +60,10 @@
         Map<Integer, Long> countMap = output.stream()
                 .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
         // Ensure each of the integers have N entries.
-        boolean respected = countMap.entrySet().stream()
-                .noneMatch(entry -> (entry.getValue() != EXPECTED_ITERATIONS));
+        boolean respected =
+                countMap.entrySet()
+                        .stream()
+                        .allMatch(entry -> (entry.getValue() == EXPECTED_ITERATIONS));
         assertThat(respected).isTrue();
     }
 
@@ -104,6 +106,31 @@
         assertThat(testRunsFollowInputOrder).isTrue();
     }
 
+    /** Unit test that the option name change is respected. */
+    @Test
+    public void testOverrideOptionName() {
+        // Apply the iteration function.
+        IterateBase<T, Integer> iterator = getIterate();
+        iterator.setOptionName(ITERATIONS_OPTION_ALTERNATE_NAME);
+        List<Integer> output =
+                iterator.apply(
+                        getArgumentsBuilder()
+                                .setIteration(1)
+                                .setAlternateIteration(EXPECTED_ITERATIONS)
+                                .build(),
+                        SIMPLE_INPUT);
+        // Count occurrences of each integer into a map.
+        Map<Integer, Long> countMap =
+                output.stream()
+                        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
+        // Ensure each of the integers have N entries.
+        boolean respected =
+                countMap.entrySet()
+                        .stream()
+                        .allMatch(entry -> (entry.getValue() == EXPECTED_ITERATIONS));
+        assertThat(respected).isTrue();
+    }
+
     /**
      * Unit test that an exception is thrown for an invalid order argument.
      */
@@ -128,6 +155,7 @@
     // Test arguments builder and factory method.
     protected abstract class ArgumentsBuilder {
         protected Integer mIterations;
+        protected Integer mAlternateIterations;
         protected String mOrder;
 
         public ArgumentsBuilder setIteration(Integer iterations) {
@@ -135,6 +163,11 @@
             return this;
         }
 
+        public ArgumentsBuilder setAlternateIteration(Integer iterations) {
+            mAlternateIterations = iterations;
+            return this;
+        }
+
         public ArgumentsBuilder setOrder(String order) {
             mOrder = order;
             return this;
diff --git a/libraries/health/composers/platform/src/android/platform/test/composer/Iterate.java b/libraries/health/composers/platform/src/android/platform/test/composer/Iterate.java
index ca5d703..a832d32 100644
--- a/libraries/health/composers/platform/src/android/platform/test/composer/Iterate.java
+++ b/libraries/health/composers/platform/src/android/platform/test/composer/Iterate.java
@@ -24,8 +24,7 @@
 public class Iterate<U> extends IterateBase<Bundle, U> {
     @Override
     protected int getIterationsArgument(Bundle args) {
-        return Integer.parseInt(
-                args.getString(ITERATIONS_OPTION_NAME, String.valueOf(mDefaultValue)));
+        return Integer.parseInt(args.getString(getOptionName(), String.valueOf(mDefaultValue)));
     }
 
     @Override
diff --git a/libraries/health/composers/platform/tests/AndroidManifest.xml b/libraries/health/composers/platform/tests/AndroidManifest.xml
index f023c7a..abf6559 100644
--- a/libraries/health/composers/platform/tests/AndroidManifest.xml
+++ b/libraries/health/composers/platform/tests/AndroidManifest.xml
@@ -26,4 +26,3 @@
         android:targetPackage="android.platform.test.composer.tests"
         android:label="Platform Composer Library Tests" />
 </manifest>
-
diff --git a/libraries/health/composers/platform/tests/src/android/platform/test/composer/IterateTest.java b/libraries/health/composers/platform/tests/src/android/platform/test/composer/IterateTest.java
index 65c2dc9..d3c2554 100644
--- a/libraries/health/composers/platform/tests/src/android/platform/test/composer/IterateTest.java
+++ b/libraries/health/composers/platform/tests/src/android/platform/test/composer/IterateTest.java
@@ -34,6 +34,11 @@
             if (mIterations != null) {
                 args.putString(IterateTestBase.ITERATIONS_OPTION_NAME, String.valueOf(mIterations));
             }
+            if (mAlternateIterations != null) {
+                args.putString(
+                        IterateTestBase.ITERATIONS_OPTION_ALTERNATE_NAME,
+                        String.valueOf(mAlternateIterations));
+            }
             if (mOrder != null) {
                 args.putString(IterateTestBase.ORDER_OPTION_NAME, String.valueOf(mOrder));
             }
diff --git a/libraries/health/runners/longevity/README.md b/libraries/health/runners/longevity/README.md
index f91a576..1c394d6 100644
--- a/libraries/health/runners/longevity/README.md
+++ b/libraries/health/runners/longevity/README.md
@@ -56,7 +56,7 @@
 **Run a suite using the sample profile under assets/**
 
 `adb shell am instrument -w -r -e profile sample_profile
--e class android.platform.test.longevity.samples.SimpleProfileSuite
+-e class android.platform.test.longevity.samples.SimpleProfile
 android.platform.test.longevity.samples/androidx.test.runner.AndroidJUnitRunner`
 
 ## Options
diff --git a/libraries/health/runners/longevity/platform/samples/src/android/platform/test/longevity/samples/SimpleProfile.java b/libraries/health/runners/longevity/platform/samples/src/android/platform/test/longevity/samples/SimpleProfile.java
index 0cade7f..1b9d9b0 100644
--- a/libraries/health/runners/longevity/platform/samples/src/android/platform/test/longevity/samples/SimpleProfile.java
+++ b/libraries/health/runners/longevity/platform/samples/src/android/platform/test/longevity/samples/SimpleProfile.java
@@ -18,20 +18,15 @@
 
 import android.platform.test.longevity.ProfileSuite;
 
-import org.junit.Assert;
-import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite.SuiteClasses;
 
 @RunWith(ProfileSuite.class)
 @SuiteClasses({
     SimpleSuite.PassingTest.class,
-    SimpleSuite.FailingTest.class
+    SimpleSuite.FailingTest.class,
 })
-
-/**
- * Sample device-side test cases using a profile.
- */
+/** Sample device-side test cases using a profile. */
 public class SimpleProfile {
-    // no local test cases.
+    // No local test cases.
 }
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 4bc30de..37bed59 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
@@ -1,4 +1,3 @@
-
 // Copyright (C) 2018 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,6 +26,7 @@
         TIMESTAMPED = 1;
     }
     optional Schedule schedule = 1 [default = TIMESTAMPED];
+
     // Information for each scenario.
     message Scenario {
         oneof schedule {
diff --git a/libraries/health/runners/microbenchmark/src/android/platform/test/microbenchmark/Microbenchmark.java b/libraries/health/runners/microbenchmark/src/android/platform/test/microbenchmark/Microbenchmark.java
index 980b11e..fa76c10 100644
--- a/libraries/health/runners/microbenchmark/src/android/platform/test/microbenchmark/Microbenchmark.java
+++ b/libraries/health/runners/microbenchmark/src/android/platform/test/microbenchmark/Microbenchmark.java
@@ -21,10 +21,12 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.test.InstrumentationRegistry;
 
+
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -85,8 +87,23 @@
      */
     @Override
     protected Statement methodInvoker(FrameworkMethod method, Object test) {
-        Statement start = super.methodInvoker(method, test);
-        // Wrap the inner-most test method with trace points.
+        // Iterate on the test method multiple times for more data. If unset, defaults to 1.
+        Iterate<Statement> methodIterator = new Iterate<Statement>();
+        methodIterator.setOptionName("method-iterations");
+        final List<Statement> testMethodStatement =
+                methodIterator.apply(
+                        mArguments,
+                        Arrays.asList(new Statement[] {super.methodInvoker(method, test)}));
+        Statement start =
+                new Statement() {
+                    @Override
+                    public void evaluate() throws Throwable {
+                        for (Statement method : testMethodStatement) {
+                            method.evaluate();
+                        }
+                    }
+                };
+        // Wrap the multiple-iteration test method with trace points.
         start = getTracePointRule().apply(start, describeChild(method));
         // Invoke special @TightMethodRules that wrap @Test methods.
         List<TestRule> tightMethodRules =
diff --git a/libraries/health/runners/microbenchmark/tests/src/android/platform/test/microbenchmark/MicrobenchmarkTest.java b/libraries/health/runners/microbenchmark/tests/src/android/platform/test/microbenchmark/MicrobenchmarkTest.java
index c644fc8..9a43f8c 100644
--- a/libraries/health/runners/microbenchmark/tests/src/android/platform/test/microbenchmark/MicrobenchmarkTest.java
+++ b/libraries/health/runners/microbenchmark/tests/src/android/platform/test/microbenchmark/MicrobenchmarkTest.java
@@ -167,16 +167,57 @@
         loggingRunner.setOperationLog(new ArrayList<String>());
         Result result = new JUnitCore().run(loggingRunner);
         assertThat(result.wasSuccessful()).isTrue();
-        assertThat(loggingRunner.getOperationLog()).containsExactly(
-                "before",
-                "tight before",
-                "begin: testMethod("
-                    + "android.platform.test.microbenchmark.MicrobenchmarkTest$LoggingTest)",
-                "test",
-                "end",
-                "tight after",
-                "after")
-            .inOrder();
+        assertThat(loggingRunner.getOperationLog())
+                .containsExactly(
+                        "before",
+                        "tight before",
+                        "begin: testMethod("
+                                + "android.platform.test.microbenchmark.MicrobenchmarkTest"
+                                + "$LoggingTest)",
+                        "test",
+                        "end",
+                        "tight after",
+                        "after")
+                .inOrder();
+    }
+
+    /**
+     * Test method iteration will iterate the inner-most test method N times.
+     *
+     * <p>Before --> TightBefore --> Trace (begin) --> Test x N --> Trace(end) --> TightAfter -->
+     * After
+     */
+    @Test
+    public void testMultipleMethodIterations() throws InitializationError {
+        Bundle args = new Bundle();
+        args.putString("iterations", "1");
+        args.putString("method-iterations", "10");
+        args.putString("rename-iterations", "false");
+        LoggingMicrobenchmark loggingRunner = new LoggingMicrobenchmark(LoggingTest.class, args);
+        loggingRunner.setOperationLog(new ArrayList<String>());
+        Result result = new JUnitCore().run(loggingRunner);
+        assertThat(result.wasSuccessful()).isTrue();
+        assertThat(loggingRunner.getOperationLog())
+                .containsExactly(
+                        "before",
+                        "tight before",
+                        "begin: testMethod("
+                                + "android.platform.test.microbenchmark.MicrobenchmarkTest"
+                                + "$LoggingTest)",
+                        "test",
+                        "test",
+                        "test",
+                        "test",
+                        "test",
+                        "test",
+                        "test",
+                        "test",
+                        "test",
+                        "test",
+                        "end",
+                        "tight after",
+                        "after")
+                .inOrder();
     }
 
     /**
diff --git a/libraries/junitxml/src/com/android/junitxml/XmlRunListener.java b/libraries/junitxml/src/com/android/junitxml/XmlRunListener.java
index 7c40a04..c3541a2 100644
--- a/libraries/junitxml/src/com/android/junitxml/XmlRunListener.java
+++ b/libraries/junitxml/src/com/android/junitxml/XmlRunListener.java
@@ -218,10 +218,10 @@
                 && !mSkippedTests.contains(description) && !mIgnoredTests
                 .contains(description)) {
             currentTest = mDocument.createElement(ELEMENT_TESTCASE);
-            final String displayName = description.getDisplayName();
+            final String methodName = description.getMethodName();
             currentTest.setAttribute(
                     ATTR_TESTCASE_NAME,
-                    displayName == null ? TESTCASE_NAME_UNKNOWN : displayName);
+                    methodName == null ? TESTCASE_NAME_UNKNOWN : methodName);
             // a TestSuite can contain Tests from multiple classes,
             // even tests with the same name - disambiguate them.
             currentTest.setAttribute(ATTR_TESTCASE_CLASSNAME, description.getClassName());
diff --git a/libraries/launcher-helper/Android.bp b/libraries/launcher-helper/Android.bp
index 418ff95..269973f 100644
--- a/libraries/launcher-helper/Android.bp
+++ b/libraries/launcher-helper/Android.bp
@@ -26,6 +26,7 @@
         "dpad-util",
         "commands-helper",
         "activity-helper",
+        "launcher-aosp-tapl",
     ],
     sdk_version: "test_current",
     srcs: ["src/**/*.java"],
diff --git a/libraries/launcher-helper/src/android/support/test/launcherhelper/AutoLauncherStrategy.java b/libraries/launcher-helper/src/android/support/test/launcherhelper/AutoLauncherStrategy.java
index eb5f281..27e9885 100644
--- a/libraries/launcher-helper/src/android/support/test/launcherhelper/AutoLauncherStrategy.java
+++ b/libraries/launcher-helper/src/android/support/test/launcherhelper/AutoLauncherStrategy.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -30,18 +30,19 @@
 
     private static final String LOG_TAG = AutoLauncherStrategy.class.getSimpleName();
     private static final String CAR_LENSPICKER = "com.android.car.carlauncher";
+    private static final String OPEN_APP_GRID_COMMAND =
+            "am start -n com.android.car.carlauncher/.AppGridActivity";
 
     private static final long APP_INIT_WAIT = 10000;
+    private static final int UI_WAIT_TIME = 5000;
     private static final int OPEN_FACET_RETRY_TIME = 5;
 
     //todo: Remove x and y axis and use resource ID's.
     private static final int FACET_APPS = 560;
     private static final int MAP_FACET = 250;
 
-    private static final BySelector R_ID_LENSPICKER_PAGE_DOWN =
-            By.res(CAR_LENSPICKER, "page_down");
-    private static final BySelector R_ID_LENSPICKER_LIST =
-            By.res(CAR_LENSPICKER, "list_view");
+    private static final BySelector UP_BTN = By.res(CAR_LENSPICKER, "page_up");
+    private static final BySelector DOWN_BTN = By.res(CAR_LENSPICKER, "page_down");
 
     protected UiDevice mDevice;
     private Instrumentation mInstrumentation;
@@ -110,34 +111,43 @@
         UiDevice.getInstance(mInstrumentation).pressHome();
     }
 
+    @Override
+    public void openAssistantFacet() {
+        CommandsHelper.getInstance(mInstrumentation).executeShellCommand(
+                "am start -n com.google.android.googlequicksearchbox/"
+                        + "com.google.android.apps.gsa.binaries.auto.app.voiceplate"
+                        + ".VoicePlateActivity");
+    }
+
+    @Override
+    public boolean checkApplicationExists(String appName) {
+        CommandsHelper.getInstance(mInstrumentation).executeShellCommand(
+                OPEN_APP_GRID_COMMAND);
+        UiObject2 up = mDevice.wait(Until.findObject(UP_BTN), APP_INIT_WAIT);
+        UiObject2 down = mDevice.wait(Until.findObject(DOWN_BTN), APP_INIT_WAIT);
+        while (up.isEnabled()) {
+            up.click();
+            up = mDevice.wait(Until.findObject(UP_BTN), UI_WAIT_TIME);
+        }
+        UiObject2 object = mDevice.wait(Until.findObject(By.text(appName)), UI_WAIT_TIME);
+        while (down.isEnabled() && object == null) {
+            down.click();
+            object = mDevice.wait(
+                Until.findObject(By.text(appName)), UI_WAIT_TIME);
+            down = mDevice.wait(Until.findObject(DOWN_BTN), UI_WAIT_TIME);
+        }
+        return object != null;
+    }
+
+    @Override
     public void openApp(String appName) {
-        do {
-            // TODO: Switch this to intents. It doesn't appear to work via intents on my system.
-            CommandsHelper.getInstance(mInstrumentation).executeShellCommand(
-                    "am start -n com.android.car.carlauncher/.AppGridActivity");
-        }
-        //R_ID_LENSPICKER_LIST to open app if scrollContainer not avilable.
-        while (!mDevice.hasObject(R_ID_LENSPICKER_PAGE_DOWN)
-                && !mDevice.hasObject(R_ID_LENSPICKER_LIST));
-
-        UiObject2 scrollContainer = mDevice.findObject(R_ID_LENSPICKER_PAGE_DOWN);
-
-        if (scrollContainer != null) {
-
-            if (!mDevice.hasObject(By.text(appName))) {
-                do {
-                    scrollContainer.scroll(Direction.DOWN, 1.0f);
-                }
-                while (!mDevice.hasObject(By.text(appName)) && scrollContainer.isEnabled());
-            }
-        }
-
-        UiObject2 application = mDevice.wait(Until.findObject(By.text(appName)), APP_INIT_WAIT);
-        if (application != null) {
-            application.click();
+        if (checkApplicationExists(appName)) {
+            UiObject2 app = mDevice.wait(
+                    Until.findObject(By.text(appName)), APP_INIT_WAIT);
+            app.click();
             mDevice.waitForIdle();
         } else {
-            Assert.fail("Unable to find application " + appName);
+            throw new RuntimeException(String.format("Application %s not found", appName));
         }
     }
 
@@ -214,6 +224,7 @@
     @SuppressWarnings("unused")
     @Override
     public long launch(String appName, String packageName) {
+        openApp(appName);
         return 0;
     }
 }
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 d6706ba..ed1d8ae 100644
--- a/libraries/launcher-helper/src/android/support/test/launcherhelper/CommonLauncherHelper.java
+++ b/libraries/launcher-helper/src/android/support/test/launcherhelper/CommonLauncherHelper.java
@@ -26,6 +26,10 @@
 import android.support.test.uiautomator.Until;
 import android.util.Log;
 
+import com.android.launcher3.tapl.AppIcon;
+import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.tapl.Workspace;
+
 /**
  * A helper class for generic launcher interactions that can be abstracted across different types
  * of launchers.
@@ -176,6 +180,39 @@
             Log.w(LOG_TAG, "no new window detected after app launch attempt.");
             return ILauncherStrategy.LAUNCH_FAILED_TIMESTAMP;
         }
+        return verifyAppStart(packageName, ready);
+    }
+
+    /**
+     * Triggers app launch by interacting with its launcher icon as described, optionally verify
+     * that the frontend UI has the expected app package name
+     *
+     * @param launcher root TAPL object
+     * @param appName
+     * @param packageName
+     * @return the SystemClock#uptimeMillis timestamp just before launching the application.
+     */
+    public long launchApp(LauncherInstrumentation launcher, String appName, String packageName) {
+        unlockDeviceIfAsleep();
+
+        if (isAppOpen(packageName)) {
+            // Application is already open
+            return 0;
+        }
+
+        // Go to the home page
+        final Workspace workspace = launcher.pressHome();
+        AppIcon icon = workspace.tryGetWorkspaceAppIcon(appName);
+        if (icon == null) {
+            icon = workspace.switchToAllApps().getAppIcon(appName);
+        }
+
+        final long ready = SystemClock.uptimeMillis();
+        icon.launch(packageName);
+        return ready;
+    }
+
+    private long verifyAppStart(String packageName, long appStartTime) {
         mDevice.waitForIdle();
         if (packageName != null) {
             Log.w(LOG_TAG, String.format(
@@ -183,12 +220,12 @@
             boolean success = mDevice.wait(Until.hasObject(
                     By.pkg(packageName).depth(0)), APP_LAUNCH_TIMEOUT);
             if (success) {
-                return ready;
+                return appStartTime;
             } else {
                 return ILauncherStrategy.LAUNCH_FAILED_TIMESTAMP;
             }
         } else {
-            return ready;
+            return appStartTime;
         }
     }
 
diff --git a/libraries/launcher-helper/src/android/support/test/launcherhelper/IAutoLauncherStrategy.java b/libraries/launcher-helper/src/android/support/test/launcherhelper/IAutoLauncherStrategy.java
index 238ecab..7c14cd7 100644
--- a/libraries/launcher-helper/src/android/support/test/launcherhelper/IAutoLauncherStrategy.java
+++ b/libraries/launcher-helper/src/android/support/test/launcherhelper/IAutoLauncherStrategy.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 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,4 +51,25 @@
      * Open Home Facet to select Dial/Media cards.
      */
     void openHomeFacet();
+
+    /**
+     * Open Google Assistant Facet.
+     */
+    void openAssistantFacet();
+
+    /**
+     * This method is to check if an application is visible on UI
+     *
+     * @param appName check app from all apps facet.
+     */
+    boolean checkApplicationExists(String appName);
+
+    /**
+     * This method is to open an application if it appears on UI
+     *
+     * It throws an exception when the target application is not found
+     *
+     * @param appName application to be opened.
+     */
+    void openApp(String appName);
 }
diff --git a/libraries/launcher-helper/src/android/support/test/launcherhelper/LauncherStrategyFactory.java b/libraries/launcher-helper/src/android/support/test/launcherhelper/LauncherStrategyFactory.java
index 6601d90..f812b99 100644
--- a/libraries/launcher-helper/src/android/support/test/launcherhelper/LauncherStrategyFactory.java
+++ b/libraries/launcher-helper/src/android/support/test/launcherhelper/LauncherStrategyFactory.java
@@ -34,6 +34,7 @@
     private UiDevice mUiDevice;
     private Map<String, ILauncherStrategy> mInstanceMap;
     private Set<Class <? extends ILauncherStrategy>> mKnownLauncherStrategies;
+    private boolean mIsDeviceSet;
 
     private LauncherStrategyFactory(UiDevice uiDevice) {
         mUiDevice = uiDevice;
@@ -73,7 +74,6 @@
         if (!mKnownLauncherStrategies.contains(launcherStrategy)) {
             try {
                 ILauncherStrategy strategy = launcherStrategy.newInstance();
-                strategy.setUiDevice(mUiDevice);
                 mInstanceMap.put(strategy.getSupportedLauncherPackage(), strategy);
             } catch (InstantiationException | IllegalAccessException e) {
                 Log.e(LOG_TAG, "exception while creating instance: "
@@ -94,7 +94,12 @@
     public ILauncherStrategy getLauncherStrategy() {
         String launcherPkg = mUiDevice.getLauncherPackageName();
         if (mInstanceMap.containsKey(launcherPkg)) {
-            return mInstanceMap.get(launcherPkg);
+            ILauncherStrategy strategy = mInstanceMap.get(launcherPkg);
+            if (!mIsDeviceSet) {
+                strategy.setUiDevice(mUiDevice);
+                mIsDeviceSet = true;
+            }
+            return strategy;
         } else {
             throw new RuntimeException(String.format(
                     "Could not find a launcher strategy for package, %s", launcherPkg));
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 472d7dd..1f6cc0a 100644
--- a/libraries/launcher-helper/src/android/support/test/launcherhelper/NexusLauncherStrategy.java
+++ b/libraries/launcher-helper/src/android/support/test/launcherhelper/NexusLauncherStrategy.java
@@ -17,6 +17,7 @@
 
 import android.graphics.Point;
 import android.os.Build;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.BySelector;
 import android.support.test.uiautomator.Direction;
@@ -24,6 +25,8 @@
 import android.support.test.uiautomator.UiObject2;
 import android.support.test.uiautomator.Until;
 
+import com.android.launcher3.tapl.LauncherInstrumentation;
+
 import junit.framework.Assert;
 
 import java.io.IOException;
@@ -34,6 +37,7 @@
 public class NexusLauncherStrategy extends BaseLauncher3Strategy {
 
     private static final String LAUNCHER_PKG = "com.google.android.apps.nexuslauncher";
+    private LauncherInstrumentation mLauncher;
 
     @Override
     public void setUiDevice(UiDevice uiDevice) {
@@ -45,6 +49,14 @@
         } catch (IOException e) {
             Assert.fail("Failed to set swipe_up_to_switch_apps_enabled, caused by: " + e);
         }
+        try {
+            mLauncher = new LauncherInstrumentation(InstrumentationRegistry.getInstrumentation());
+
+        } catch (IllegalStateException | NoClassDefFoundError e) {
+            mLauncher =
+                    new LauncherInstrumentation(
+                            androidx.test.InstrumentationRegistry.getInstrumentation());
+        }
     }
 
     @Override
@@ -57,7 +69,7 @@
      */
     @Override
     public BySelector getAllAppsSelector() {
-        return By.res(getSupportedLauncherPackage(), "apps_list_view");
+        return By.res(getSupportedLauncherPackage(), "apps_view");
     }
 
     /**
@@ -155,4 +167,12 @@
         }
         return allWidgetsContainer;
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public long launch(String appName, String packageName) {
+        return CommonLauncherHelper.getInstance(mDevice).launchApp(mLauncher, appName, packageName);
+    }
 }
diff --git a/libraries/metrics-helper/Android.bp b/libraries/metrics-helper/Android.bp
index a85f37e..ec8a500 100644
--- a/libraries/metrics-helper/Android.bp
+++ b/libraries/metrics-helper/Android.bp
@@ -18,6 +18,6 @@
 
     name: "metrics-helper-lib",
     srcs: ["src/**/*.java"],
-    static_libs: ["junit"],
-
+    static_libs: ["junit", "framework-protos", ],
+    sdk_version: "test_current",
 }
diff --git a/libraries/system-helpers/accessibility-helper/src/android/system/helpers/AccessibilityHelper.java b/libraries/system-helpers/accessibility-helper/src/android/system/helpers/AccessibilityHelper.java
index c14d2e4..f72b303 100644
--- a/libraries/system-helpers/accessibility-helper/src/android/system/helpers/AccessibilityHelper.java
+++ b/libraries/system-helpers/accessibility-helper/src/android/system/helpers/AccessibilityHelper.java
@@ -172,7 +172,7 @@
     private UiObject2 getSettingFromList(String settingName)
             throws UiObjectNotFoundException {
         UiScrollable listScrollable = new UiScrollable(
-                new UiSelector().resourceId(SETTINGS_PACKAGE+":id/list"));
+                new UiSelector().resourceId(SETTINGS_PACKAGE + ":id/recycler_view"));
         if (listScrollable != null) {
             listScrollable.scrollToBeginning(100);
             listScrollable.scrollIntoView(
diff --git a/libraries/system-helpers/activity-helper/src/android/system/helpers/ActivityHelper.java b/libraries/system-helpers/activity-helper/src/android/system/helpers/ActivityHelper.java
index 28ad7ee..d16aa50 100644
--- a/libraries/system-helpers/activity-helper/src/android/system/helpers/ActivityHelper.java
+++ b/libraries/system-helpers/activity-helper/src/android/system/helpers/ActivityHelper.java
@@ -133,22 +133,22 @@
     /**
      * Clear recent apps by click 'CLEAR ALL' button in the recents view.
      *
+     * @param maxScroll max number of scroll in recents view.
      * @throws Exception
      */
-    public void clearRecentsByClearAll() throws Exception {
+    public void clearRecentsByClearAll(int maxScroll) throws Exception {
         int retry = 5;
         while (!mDevice.wait(Until.hasObject(By.res(NEXUS_LAUNCHER, "overview_panel")),
                 ONE_SECOND * 5) && --retry > 0) {
             mDevice.pressRecentApps();
             Thread.sleep(ONE_SECOND);
         }
-        int maxTries = 20;
         while (mDevice.findObject(By.res(NEXUS_LAUNCHER, "overview_panel"))
-                .isScrollable() && maxTries-- >= 0) {
+                .isScrollable() && maxScroll-- >= 0) {
             UiScrollable thumbnailScrollable = new UiScrollable(new UiSelector().resourceId(
                     NEXUS_LAUNCHER + ":id/overview_panel"));
             thumbnailScrollable.setAsHorizontalList();
-            thumbnailScrollable.scrollToBeginning(100);
+            thumbnailScrollable.scrollBackward();
             if (!mDevice.wait(Until.hasObject(By.text(
                     Pattern.compile("CLEAR ALL", Pattern.CASE_INSENSITIVE))), ONE_SECOND * 2)) {
                 continue;
@@ -166,6 +166,15 @@
     }
 
     /**
+     * Clear recents apps by click 'CLEAR ALL' button in recents view, default max scroll 20.
+     *
+     * @throws Exception
+     */
+    public void clearRecentsByClearAll() throws Exception {
+        clearRecentsByClearAll(20);
+    }
+
+    /**
      * Enable/disable bmgr service
      *
      * @param enable true to enable, false to disable
diff --git a/scripts/perf-setup/b1c1-setup.sh b/scripts/perf-setup/b1c1-setup.sh
index 43c1e4a..77c3ae2 100755
--- a/scripts/perf-setup/b1c1-setup.sh
+++ b/scripts/perf-setup/b1c1-setup.sh
@@ -16,10 +16,9 @@
 
 # performance test setup for 2018 devices
 
-stop thermal-engine
-stop perfd
 stop vendor.thermal-engine
-stop vendor.perfd
+setprop vendor.powerhal.init 0
+setprop ctl.interface_restart android.hardware.power@1.0::IPower/default
 
 cpubase=/sys/devices/system/cpu
 gov=cpufreq/scaling_governor
@@ -27,15 +26,17 @@
 cpu=4
 top=8
 
-# Enable the gold cores at max frequency.
+cpufreq=2092800
+# Set the golden cores at 2092800.
 # 825600 902400 979200 1056000 1209600 1286400 1363200 1459200 1536000
 # 1612800 1689600 1766400 1843200 1920000 1996800 2092800 2169600
 # 2246400 2323200 2400000 2476800 2553600 2649600
 while [ $((cpu < $top)) -eq 1 ]; do
   echo 1 > $cpubase/cpu${cpu}/online
   echo performance > $cpubase/cpu${cpu}/$gov
+  echo $cpufreq > /sys/devices/system/cpu/cpu$cpu/cpufreq/scaling_max_freq
   S=`cat $cpubase/cpu${cpu}/cpufreq/scaling_cur_freq`
-  echo "setting cpu $cpu to max at $S kHz"
+  echo "set cpu $cpu to $S kHz"
   cpu=$(($cpu + 1))
 done
 
@@ -49,19 +50,22 @@
   cpu=$(($cpu + 1))
 done
 
-echo "setting GPU bus split"
+echo "disable GPU bus split"
 echo 0 > /sys/class/kgsl/kgsl-3d0/bus_split
-echo "setting GPU force clocks"
+echo "enable GPU force clock on"
 echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on
-echo "setting GPU idle timer"
+echo "set GPU idle timer to 10000"
 echo 10000 > /sys/class/kgsl/kgsl-3d0/idle_timer
 
 # 0 381 572 762 1144 1571 2086 2597 2929 3879 4943 5931 6881
 echo performance > /sys/class/devfreq/soc:qcom,gpubw/governor
-echo -n "setting GPU bus frequency to max at "
+echo -n "set GPU bus frequency to max at "
 cat /sys/class/devfreq/soc:qcom,gpubw/cur_freq
 
 # 257000000 342000000 414000000 520000000 596000000 675000000 710000000
+G=596000000
 echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor
-echo -n "setting GPU frequency to max at "
+echo $G > /sys/class/kgsl/kgsl-3d0/devfreq/min_freq
+echo $G > /sys/class/kgsl/kgsl-3d0/devfreq/max_freq
+echo -n "set GPU frequency to "
 cat /sys/class/kgsl/kgsl-3d0/devfreq/cur_freq
diff --git a/scripts/perf-setup/b4s4-setup.sh b/scripts/perf-setup/b4s4-setup.sh
index 7bf714f..c7498e4 100755
--- a/scripts/perf-setup/b4s4-setup.sh
+++ b/scripts/perf-setup/b4s4-setup.sh
@@ -16,10 +16,9 @@
 
 # performance test setup for b4/s4 devices
 
-stop thermal-engine
-stop perfd
 stop vendor.thermal-engine
-stop vendor.perfd
+setprop vendor.powerhal.init 0
+setprop ctl.interface_restart android.hardware.power@1.0::IPower/default
 
 cpubase=/sys/devices/system/cpu
 gov=cpufreq/scaling_governor
diff --git a/scripts/perf-setup/c2f2-setup.sh b/scripts/perf-setup/c2f2-setup.sh
new file mode 100755
index 0000000..d83cef4
--- /dev/null
+++ b/scripts/perf-setup/c2f2-setup.sh
@@ -0,0 +1,75 @@
+#!/system/bin/sh
+#
+# 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.
+
+# performance test setup for 2018 devices
+
+stop vendor.thermal-engine
+setprop vendor.powerhal.init 0
+setprop ctl.interface_restart android.hardware.power@1.0::IPower/default
+
+cpubase=/sys/devices/system/cpu
+gov=cpufreq/scaling_governor
+
+cpu=4
+top=7
+cpufreq=2016000
+# Set the bigcores at 2016000.
+while [ $((cpu < $top)) -eq 1 ]; do
+  echo 1 > $cpubase/cpu${cpu}/online
+  echo performance > $cpubase/cpu${cpu}/$gov
+  echo $cpufreq > /sys/devices/system/cpu/cpu$cpu/cpufreq/scaling_max_freq
+  S=`cat $cpubase/cpu${cpu}/cpufreq/scaling_cur_freq`
+  echo "set cpu $cpu to $S kHz"
+  cpu=$(($cpu + 1))
+done
+
+cpu=0
+top=4
+# Disable the silver cores.
+while [ $((cpu < $top)) -eq 1 ]; do
+  echo "disable cpu $cpu"
+  echo 0 > $cpubase/cpu${cpu}/online
+  cpu=$(($cpu + 1))
+done
+
+cpu=7
+top=8
+# Disable the big+ core.
+while [ $((cpu < $top)) -eq 1 ]; do
+  echo "disable cpu $cpu"
+  echo 0 > $cpubase/cpu${cpu}/online
+  cpu=$(($cpu + 1))
+done
+
+echo "disable GPU bus split"
+echo 0 > /sys/class/kgsl/kgsl-3d0/bus_split
+echo "enable GPU force clock on"
+echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on
+echo "set GPU idle timer to 10000"
+echo 10000 > /sys/class/kgsl/kgsl-3d0/idle_timer
+
+# 0 381 572 762 1144 1571 2086 2597 2929 3879 4943 5931 6881
+echo performance > /sys/class/devfreq/soc:qcom,gpubw/governor
+echo -n "set GPU bus frequency to max at "
+cat /sys/class/devfreq/soc:qcom,gpubw/cur_freq
+
+# 257000000 345000000 427000000 585000000
+G=427000000
+echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor
+echo $G > /sys/class/kgsl/kgsl-3d0/devfreq/min_freq
+echo $G > /sys/class/kgsl/kgsl-3d0/devfreq/max_freq
+echo -n "set GPU frequency to "
+cat /sys/class/kgsl/kgsl-3d0/devfreq/cur_freq
diff --git a/scripts/perf-setup/wahoo-setup.sh b/scripts/perf-setup/wahoo-setup.sh
index f875c0d..f6df6be 100644
--- a/scripts/perf-setup/wahoo-setup.sh
+++ b/scripts/perf-setup/wahoo-setup.sh
@@ -4,6 +4,8 @@
 stop perfd
 stop vendor.thermal-engine
 stop vendor.perfd
+setprop vendor.powerhal.init 0
+setprop ctl.interface_restart android.hardware.power@1.0::IPower/default
 
 cpubase=/sys/devices/system/cpu
 gov=cpufreq/scaling_governor
diff --git a/tests/functional/notificationtests/src/com/android/notification/functional/NotificationDNDTests.java b/tests/functional/notificationtests/src/com/android/notification/functional/NotificationDNDTests.java
deleted file mode 100644
index cfcafda..0000000
--- a/tests/functional/notificationtests/src/com/android/notification/functional/NotificationDNDTests.java
+++ /dev/null
@@ -1,189 +0,0 @@
-
-package com.android.notification.functional;
-
-import android.app.AppGlobals;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.media.AudioManager;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.provider.Settings;
-import android.provider.Settings.Global;
-import android.provider.Settings.SettingNotFoundException;
-import android.service.notification.StatusBarNotification;
-import android.service.notification.ZenModeConfig;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
-
-import com.android.server.notification.NotificationManagerService;
-import com.android.server.notification.ConditionProviders;
-import com.android.server.notification.ManagedServices.UserProfiles;
-
-import java.util.concurrent.Callable;
-import java.util.concurrent.FutureTask;
-
-import com.android.server.notification.ZenModeHelper;
-import com.android.server.notification.NotificationRecord;
-import com.android.server.notification.ZenModeFiltering;
-
-public class NotificationDNDTests extends InstrumentationTestCase {
-    private static final String LOG_TAG = NotificationDNDTests.class.getSimpleName();
-    private static final int SHORT_TIMEOUT = 1000;
-    private static final int LONG_TIMEOUT = 2000;
-    private static final int NOTIFICATION_ID = 1;
-    private static final String NOTIFICATION_CONTENT_TEXT = "INLINE REPLY TEST";
-    private NotificationManager mNotificationManager;
-    private UiDevice mDevice = null;
-    private Context mContext;
-    private NotificationHelper mHelper;
-    private ContentResolver mResolver;
-    private ZenModeHelper mZenHelper;
-    private boolean isGranted = false;
-
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        mDevice = UiDevice.getInstance(getInstrumentation());
-        mContext = getInstrumentation().getTargetContext();
-        mNotificationManager = (NotificationManager) mContext
-                .getSystemService(Context.NOTIFICATION_SERVICE);
-        mHelper = new NotificationHelper(mDevice, getInstrumentation(), mNotificationManager);
-        mResolver = mContext.getContentResolver();
-        ConditionProviders cps = new ConditionProviders(mContext,
-                new UserProfiles(), AppGlobals.getPackageManager());
-        Callable<ZenModeHelper> callable = new Callable<ZenModeHelper>() {
-            @Override
-            public ZenModeHelper call() throws Exception {
-                return new ZenModeHelper(mContext, Looper.getMainLooper(), cps);
-            }
-        };
-        FutureTask<ZenModeHelper> task = new FutureTask<>(callable);
-        getInstrumentation().runOnMainSync(task);
-        mZenHelper = task.get();
-        mDevice.setOrientationNatural();
-        mHelper.unlockScreen();
-        mDevice.pressHome();
-        mNotificationManager.cancelAll();
-        isGranted = mNotificationManager.isNotificationPolicyAccessGranted();
-    }
-
-    @Override
-    public void tearDown() throws Exception {
-        mNotificationManager.cancelAll();
-        mDevice.pressHome();
-        mDevice.unfreezeRotation();
-        super.tearDown();
-    }
-
-    @LargeTest
-    public void testDND() throws Exception {
-        if (!isGranted) {
-            grantPolicyAccess(true);
-        }
-        int setting = mNotificationManager.getCurrentInterruptionFilter();
-        try {
-            mNotificationManager
-                    .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
-            mHelper.sendNotificationsWithInlineReply(NOTIFICATION_ID, true);
-            Thread.sleep(LONG_TIMEOUT);
-            NotificationRecord nr = new NotificationRecord(mContext,
-                    mHelper.getStatusBarNotification(NOTIFICATION_ID), mHelper.getDefaultChannel());
-            ZenModeConfig mConfig = mZenHelper.getConfig();
-            ZenModeFiltering zF = new ZenModeFiltering(mContext);
-            assertTrue(zF.shouldIntercept(mNotificationManager.getZenMode(), mConfig, nr));
-        } finally {
-            mNotificationManager.setInterruptionFilter(setting);
-            if (!isGranted) {
-                grantPolicyAccess(false);
-            }
-        }
-    }
-
-    @LargeTest
-    public void testPriority() throws Exception {
-        int setting = mNotificationManager.getCurrentInterruptionFilter();
-        if (!isGranted) {
-            grantPolicyAccess(true);
-        }
-        mNotificationManager
-                .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
-        try {
-            mHelper.showAppNotificationSettings(mContext);
-            mDevice.wait(Until.findObject(By.textContains("Uncategorized")), LONG_TIMEOUT)
-                    .click();
-            mDevice.wait(Until.findObject(By.textContains("Override Do Not Disturb")), LONG_TIMEOUT)
-                    .click();
-            Thread.sleep(LONG_TIMEOUT);
-            mHelper.sendNotificationsWithInlineReply(NOTIFICATION_ID, true);
-            Thread.sleep(LONG_TIMEOUT);
-            NotificationRecord nr = new NotificationRecord(mContext,
-                    mHelper.getStatusBarNotification(NOTIFICATION_ID), mHelper.getDefaultChannel());
-            ZenModeConfig mConfig = mZenHelper.getConfig();
-            ZenModeFiltering zF = new ZenModeFiltering(mContext);
-            assertFalse(zF.shouldIntercept(mZenHelper.getZenMode(), mConfig, nr));
-        } finally {
-            mHelper.showAppNotificationSettings(mContext);
-            mDevice.wait(Until.findObject(By.textContains("Uncategorized")), LONG_TIMEOUT)
-                    .click();
-            mDevice.wait(Until.findObject(By.textContains("Advanced")), LONG_TIMEOUT)
-                    .click();
-            mDevice.wait(Until.findObject(By.textContains("Override Do Not Disturb")), LONG_TIMEOUT)
-                    .click();
-            mNotificationManager.setInterruptionFilter(setting);
-            if (!isGranted) {
-                grantPolicyAccess(false);
-            }
-        }
-    }
-
-    @LargeTest
-    public void testBlockNotification() throws Exception {
-        try {
-            mHelper.showAppNotificationSettings(mContext);
-            mDevice.wait(Until.findObject(
-                    By.textContains("Show notifications")), LONG_TIMEOUT).click();
-            Thread.sleep(LONG_TIMEOUT);
-            mHelper.sendNotificationsWithInlineReply(NOTIFICATION_ID, true);
-            Thread.sleep(LONG_TIMEOUT);
-            if (mHelper.checkNotificationExistence(NOTIFICATION_ID, true)) {
-                fail(String.format("Notification %s has not been blocked", NOTIFICATION_ID));
-            }
-        } finally {
-            mHelper.showAppNotificationSettings(mContext);
-            mDevice.wait(Until.findObject(
-                    By.textContains("Show notifications")), LONG_TIMEOUT).click();
-        }
-    }
-
-    private void grantPolicyAccess(boolean grant) throws Exception {
-        NotificationHelper.launchSettingsPage(mContext,
-                android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
-        Thread.sleep(LONG_TIMEOUT);
-        if (grant) {
-            mDevice.wait(Until.findObject(
-                    By.text("com.android.notification.functional")), LONG_TIMEOUT).click();
-            Thread.sleep(SHORT_TIMEOUT);
-            mDevice.wait(Until.findObject(By.text("Allow")), LONG_TIMEOUT).click();
-        } else {
-            mDevice.wait(Until.findObject(
-                    By.text("com.android.notification.functional")), LONG_TIMEOUT).click();
-            Thread.sleep(SHORT_TIMEOUT);
-            mDevice.wait(Until.findObject(By.text("Turn off")), LONG_TIMEOUT).click();
-        }
-        Thread.sleep(LONG_TIMEOUT);
-        mDevice.pressHome();
-    }
-}
diff --git a/tests/functional/systemmetrics/AndroidManifest.xml b/tests/functional/systemmetrics/AndroidManifest.xml
deleted file mode 100644
index 319d067..0000000
--- a/tests/functional/systemmetrics/AndroidManifest.xml
+++ /dev/null
@@ -1,36 +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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.systemmetrics.functional" >
-
-    <application>
-        <uses-library android:name="android.test.runner" />
-        <activity android:name=".ReportedDrawnActivity"/>
-    </application>
-
-    <uses-sdk
-        android:minSdkVersion="21"
-        android:targetSdkVersion="24" />
-
-    <uses-permission android:name="android.permission.READ_LOGS" />
-
-    <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
-        android:label="Android System Metrics Functional Tests"
-        android:targetPackage="com.android.systemmetrics.functional" />
-
-</manifest>
diff --git a/tests/functional/systemmetrics/AndroidTest.xml b/tests/functional/systemmetrics/AndroidTest.xml
deleted file mode 100644
index 3798dba..0000000
--- a/tests/functional/systemmetrics/AndroidTest.xml
+++ /dev/null
@@ -1,26 +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 Android System Metrics Functional Tests.">
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="test-file-name" value="SystemMetricsFunctionalTests.apk" />
-        <option name="cleanup-apks" value="true" />
-    </target_preparer>
-    <option name="test-tag" value="SystemMetricsFunctionalTests" />
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="com.android.systemmetrics.functional" />
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
-    </test>
-</configuration>
diff --git a/tests/functional/systemmetrics/src/com/android/systemmetrics/functional/AppStartTests.java b/tests/functional/systemmetrics/src/com/android/systemmetrics/functional/AppStartTests.java
deleted file mode 100644
index 57ef432..0000000
--- a/tests/functional/systemmetrics/src/com/android/systemmetrics/functional/AppStartTests.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemmetrics.functional;
-
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DELAY_MS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_PROCESS_RUNNING;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_REPORTED_DRAWN;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_REPORTED_DRAWN_MS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_STARTING_WINDOW_DELAY_MS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CLASS_NAME;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_COLD_LAUNCH;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH;
-import static junit.framework.TestCase.assertTrue;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-import android.content.Context;
-import android.content.Intent;
-import android.metrics.LogMaker;
-import android.metrics.MetricsReader;
-import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
-import android.support.test.metricshelper.MetricsAsserts;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.Until;
-import android.text.TextUtils;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Queue;
-
-/**
- * runtest --path platform_testing/tests/functional/systemmetrics/
- */
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class AppStartTests {
-    private static final String LOG_TAG = AppStartTests.class.getSimpleName();
-    private static final String SETTINGS_PACKAGE = "com.android.settings";
-    private static final int LONG_TIMEOUT_MS = 2000;
-    private UiDevice mDevice = null;
-    private Context mContext;
-    private MetricsReader mMetricsReader;
-    private int mPreUptime;
-
-    @Before
-    public void setUp() throws Exception {
-        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-        mContext = InstrumentationRegistry.getTargetContext();
-        mDevice.setOrientationNatural();
-        mMetricsReader = new MetricsReader();
-        mMetricsReader.checkpoint(); // clear out old logs
-        mPreUptime = (int) (SystemClock.uptimeMillis() / 1000);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mDevice.unfreezeRotation();
-        mContext.startActivity(new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME));
-        mDevice.waitForIdle();
-    }
-
-    @Test
-    public void testStartApp() throws Exception {
-        Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(SETTINGS_PACKAGE);
-
-        // Clear out any previous instances
-        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
-
-        assertNotNull("component name is null", intent.getComponent());
-        String className = intent.getComponent().getClassName();
-        String packageName = intent.getComponent().getPackageName();
-        assertTrue("className is empty", !TextUtils.isEmpty(className));
-        assertTrue("packageName is empty", !TextUtils.isEmpty(packageName));
-
-
-        mContext.startActivity(intent);
-        mDevice.wait(Until.hasObject(By.pkg(SETTINGS_PACKAGE).depth(0)), LONG_TIMEOUT_MS);
-
-        int postUptime = (int) (SystemClock.uptimeMillis() / 1000);
-
-        Queue<LogMaker> startLogs = MetricsAsserts.findMatchingLogs(mMetricsReader,
-                new LogMaker(APP_TRANSITION));
-        boolean found = false;
-        for (LogMaker log : startLogs) {
-            String actualClassName = (String) log.getTaggedData(
-                    FIELD_CLASS_NAME);
-            String actualPackageName = log.getPackageName();
-            if (className.equals(actualClassName) && packageName.equals(actualPackageName)) {
-                found = true;
-                int startUptime = ((Number)
-                        log.getTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS))
-                        .intValue();
-                assertTrue("must be either cold or warm launch",
-                        TYPE_TRANSITION_COLD_LAUNCH == log.getType()
-                                || TYPE_TRANSITION_WARM_LAUNCH == log.getType());
-                assertTrue("reported uptime should be after the app was started",
-                        mPreUptime <= startUptime);
-                assertTrue("reported uptime should be before assertion time",
-                        startUptime <= postUptime);
-                assertNotNull("log should have delay",
-                        log.getTaggedData(APP_TRANSITION_DELAY_MS));
-                assertEquals("transition should be started because of starting window",
-                        1 /* APP_TRANSITION_STARTING_WINDOW */, log.getSubtype());
-                assertNotNull("log should have starting window delay",
-                        log.getTaggedData(APP_TRANSITION_STARTING_WINDOW_DELAY_MS));
-                assertNotNull("log should have windows drawn delay",
-                        log.getTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS));
-            }
-        }
-        assertTrue("did not find the app start start log for: "
-                + intent.getComponent().flattenToShortString(), found);
-    }
-
-    @Test
-    public void testReportedDrawn() throws Exception {
-        Intent intent = new Intent(mContext, ReportedDrawnActivity.class).setFlags(
-                Intent.FLAG_ACTIVITY_NEW_TASK);
-        mContext.startActivity(intent);
-        mDevice.wait(Until.hasObject(By.pkg(mContext.getPackageName()).depth(0)), LONG_TIMEOUT_MS);
-
-        String className = intent.getComponent().getClassName();
-        String packageName = intent.getComponent().getPackageName();
-
-        // Sleep until activity under test has reported drawn (after 500ms)
-        SystemClock.sleep(1000);
-        Queue<LogMaker> startLogs = MetricsAsserts.findMatchingLogs(mMetricsReader,
-                new LogMaker(APP_TRANSITION_REPORTED_DRAWN));
-        boolean found = false;
-        for (LogMaker log : startLogs) {
-            String actualClassName = (String) log.getTaggedData(
-                    FIELD_CLASS_NAME);
-            String actualPackageName = log.getPackageName();
-            if (className.equals(actualClassName) && packageName.equals(actualPackageName)) {
-                found = true;
-                assertTrue((long) log.getTaggedData(APP_TRANSITION_REPORTED_DRAWN_MS) > 500L);
-                assertEquals((int) log.getTaggedData(APP_TRANSITION_PROCESS_RUNNING), 1);
-                assertEquals(TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE, log.getType());
-            }
-        }
-        assertTrue("did not find the app start start log for: "
-                    + intent.getComponent().flattenToShortString(), found);
-    }
-}
diff --git a/tests/functional/systemmetrics/src/com/android/systemmetrics/functional/ReportedDrawnActivity.java b/tests/functional/systemmetrics/src/com/android/systemmetrics/functional/ReportedDrawnActivity.java
deleted file mode 100644
index b1cdaae..0000000
--- a/tests/functional/systemmetrics/src/com/android/systemmetrics/functional/ReportedDrawnActivity.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemmetrics.functional;
-
-import android.app.Activity;
-import android.os.Handler;
-
-public class ReportedDrawnActivity extends Activity {
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        new Handler().postDelayed(this::reportFullyDrawn, 500);
-    }
-}
diff --git a/tests/functional/testapks/permissiontestappmv1/Android.bp b/tests/functional/testapks/permissiontestappmv1/Android.bp
index aaab568..127de97 100644
--- a/tests/functional/testapks/permissiontestappmv1/Android.bp
+++ b/tests/functional/testapks/permissiontestappmv1/Android.bp
@@ -4,7 +4,7 @@
     // omit gradle 'build' dir
     srcs: ["src/**/*.java"],
 
-    static_libs: ["android-support-v4"],
+    static_libs: ["androidx.legacy_legacy-support-v4"],
     resource_dirs: ["res"],
     sdk_version: "current",
     certificate: "platform",
diff --git a/tests/functional/testapks/permissiontestappmv1/src/com/android/permissontestappmv1/MainActivity.java b/tests/functional/testapks/permissiontestappmv1/src/com/android/permissontestappmv1/MainActivity.java
index a6cd8a8..8e2d4a9 100644
--- a/tests/functional/testapks/permissiontestappmv1/src/com/android/permissontestappmv1/MainActivity.java
+++ b/tests/functional/testapks/permissiontestappmv1/src/com/android/permissontestappmv1/MainActivity.java
@@ -17,8 +17,8 @@
 package com.android.permissiontestappmv1;
 
 import android.provider.Settings;
-import android.support.v4.app.ActivityCompat;
-import android.support.v4.content.ContextCompat;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
 import android.os.Bundle;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Button;
diff --git a/tests/health/scenarios/Android.bp b/tests/health/scenarios/Android.bp
index 56e87e4..a20135e 100644
--- a/tests/health/scenarios/Android.bp
+++ b/tests/health/scenarios/Android.bp
@@ -18,11 +18,11 @@
     srcs: [ "src/**/*.java" ],
     libs: [
         "androidx.test.runner",
+        "app-helpers-handheld-interfaces",
+        "handheld-app-helpers",
         "junit",
         "platform-test-options",
-        "ub-uiautomator",
-        "app-helpers-handheld-interfaces",
         "platform-test-rules",
-        "handheld-app-helpers"
+        "ub-uiautomator",
     ],
 }
diff --git a/tests/health/scenarios/src/android/platform/test/scenario/system/ScreenOff.java b/tests/health/scenarios/src/android/platform/test/scenario/system/ScreenOff.java
index 1bba21e..cf6ea9e 100644
--- a/tests/health/scenarios/src/android/platform/test/scenario/system/ScreenOff.java
+++ b/tests/health/scenarios/src/android/platform/test/scenario/system/ScreenOff.java
@@ -1,8 +1,20 @@
-/**
- * Copyright 2018 Google Inc. All Rights Reserved.
+/*
+ * 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.
  */
 
-package android.platform.test.scenario.common.system;
+package android.platform.test.scenario.system;
 
 import android.os.RemoteException;
 import android.os.SystemClock;
diff --git a/tests/health/scenarios/src/android/platform/test/scenario/system/ScreenOn.java b/tests/health/scenarios/src/android/platform/test/scenario/system/ScreenOn.java
index 7db2549..24c1461 100644
--- a/tests/health/scenarios/src/android/platform/test/scenario/system/ScreenOn.java
+++ b/tests/health/scenarios/src/android/platform/test/scenario/system/ScreenOn.java
@@ -1,8 +1,20 @@
-/**
- * Copyright 2018 Google Inc. All Rights Reserved.
+/*
+ * 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.
  */
 
-package android.platform.test.scenario.common.system;
+package android.platform.test.scenario.system;
 
 import android.os.RemoteException;
 import android.os.SystemClock;
diff --git a/tests/health/scenarios/tests/Android.bp b/tests/health/scenarios/tests/Android.bp
index af33979..69b1ff1 100644
--- a/tests/health/scenarios/tests/Android.bp
+++ b/tests/health/scenarios/tests/Android.bp
@@ -41,6 +41,19 @@
         "&& exit 1 ; fi ; done && jar cf $(out) -C $$(dirname $(out)) $$assets_dir",
 }
 
+java_library_static {
+    name: "common-platform-scenario-tests",
+    sdk_version: "test_current",
+    srcs: [ "src/**/*.java" ],
+    libs: [
+        "common-platform-scenarios",
+        "guava",
+        "longevity-device-lib",
+        "microbenchmark-device-lib",
+        "platform-test-rules",
+    ],
+}
+
 android_test {
     name: "PlatformCommonScenarioTests",
     min_sdk_version: "24",
@@ -48,13 +61,13 @@
         "common-platform-scenarios",
         "common-profile-text-protos",
         "guava",
+        "handheld-app-helpers",
+        "launcher-helper-lib",
         "longevity-device-lib",
         "microbenchmark-device-lib",
         "platform-test-composers",
         "platform-test-options",
         "platform-test-rules",
-        "handheld-app-helpers",
-        "launcher-helper-lib",
     ],
     srcs: ["src/**/*.java"],
     test_suites: ["device-tests"],
diff --git a/tests/health/scenarios/tests/AndroidManifest.xml b/tests/health/scenarios/tests/AndroidManifest.xml
index 41fb065..c4e29e1 100644
--- a/tests/health/scenarios/tests/AndroidManifest.xml
+++ b/tests/health/scenarios/tests/AndroidManifest.xml
@@ -20,6 +20,7 @@
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <application>
         <uses-library android:name="android.test.runner"/>
     </application>
diff --git a/tests/jank/UbSystemUiJankTests/Android.bp b/tests/jank/UbSystemUiJankTests/Android.bp
index d65c409..4fb8c00 100644
--- a/tests/jank/UbSystemUiJankTests/Android.bp
+++ b/tests/jank/UbSystemUiJankTests/Android.bp
@@ -24,9 +24,12 @@
         "timeresult-helper-lib",
         "sysui-helper",
         "collector-device-lib",
+        "launcher-aosp-tapl",
     ],
 
     libs: ["android.test.base"],
-
+    optimize: {
+        enabled: false,
+    },
     test_suites: ["device-tests"],
 }
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 9042d6e..b946cfa 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
@@ -16,9 +16,6 @@
 
 package android.platform.systemui.tests.jank;
 
-import android.content.pm.PackageManager;
-import android.graphics.Point;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.RemoteException;
@@ -28,15 +25,16 @@
 import android.support.test.launcherhelper.ILauncherStrategy;
 import android.support.test.launcherhelper.LauncherStrategyFactory;
 import android.support.test.timeresulthelper.TimeResultLogger;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.Direction;
 import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
 import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.Until;
 import android.system.helpers.OverviewHelper;
 
+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;
+
 import java.io.File;
 import java.io.IOException;
 
@@ -46,14 +44,11 @@
  */
 public class LauncherJankTests extends JankTestBase {
 
-    private static final int TIMEOUT = 5000;
     // short transitions should be repeated within the test function, otherwise frame stats
     // captured are not really meaningful in a statistical sense
     private static final int INNER_LOOP = 3;
-    private static final int FLING_SPEED = 12000;
-    private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
     private UiDevice mDevice;
-    private PackageManager pm;
+    private LauncherInstrumentation mLauncher;
     private ILauncherStrategy mLauncherStrategy = null;
     private static final File TIMESTAMP_FILE = new File(Environment.getExternalStorageDirectory()
             .getAbsolutePath(),"autotester.log");
@@ -61,15 +56,18 @@
             .getAbsolutePath(),"results.log");
 
     @Override
-    public void setUp() {
+    public void setUp() throws Exception {
+        androidx.test.InstrumentationRegistry.registerInstance(getInstrumentation(), new Bundle());
         mDevice = UiDevice.getInstance(getInstrumentation());
-        pm = getInstrumentation().getContext().getPackageManager();
         try {
             mDevice.setOrientationNatural();
         } catch (RemoteException e) {
             throw new RuntimeException("failed to freeze device orientaion", e);
         }
         mLauncherStrategy = LauncherStrategyFactory.getInstance(mDevice).getLauncherStrategy();
+        mLauncher = new LauncherInstrumentation(getInstrumentation());
+        mDevice.executeShellCommand("pm disable com.google.android.music");
+        mDevice.pressHome();
     }
 
     public String getLauncherPackage() {
@@ -78,6 +76,7 @@
 
     @Override
     protected void tearDown() throws Exception {
+        mDevice.executeShellCommand("pm enable com.google.android.music");
         mDevice.unfreezeRotation();
         super.tearDown();
     }
@@ -86,49 +85,8 @@
         mLauncherStrategy.open();
     }
 
-    private BySelector getAppListViewSelector() {
-        return By.res(getLauncherPackage(), "apps_list_view");
-    }
-
-    /** Swipes from Recents to All Apps. */
-    private void fromRecentsToAllApps() {
-        assertTrue(mDevice.hasObject(SystemUiJankTests.getLauncherOverviewSelector(mDevice)));
-        assertTrue(!mDevice.hasObject(getAppListViewSelector()));
-
-        // Swipe from the hotseat to near the top, e.g. 10% of the screen.
-        UiObject2 predictionRow = mDevice.wait(
-                Until.findObject(By.res(getLauncherPackage(), "prediction_row")),
-                2500);
-        Point start = predictionRow.getVisibleCenter();
-        int endY = (int) (mDevice.getDisplayHeight() * 0.1f);
-        mDevice.swipe(start.x, start.y, start.x, endY, (start.y - endY) / 100); // 100 px/step
-
-        UiObject2 allAppsContainer = mDevice.wait(Until.findObject(getAppListViewSelector()), 2500);
-        assertNotNull("openAllApps: did not find all apps container", allAppsContainer);
-        assertTrue(!mDevice.hasObject(SystemUiJankTests.getLauncherOverviewSelector(mDevice)));
-    }
-
-    /** Swipes from All Apps to Recents. */
-    private void fromAllAppsToRecents() { // add state assertions
-        assertTrue(!mDevice.hasObject(SystemUiJankTests.getLauncherOverviewSelector(mDevice)));
-        assertTrue(mDevice.hasObject(getAppListViewSelector()));
-
-        // Swipe from the search box to the bottom.
-        UiObject2 qsb = mDevice.wait(
-                Until.findObject(By.res(getLauncherPackage(), "search_container_all_apps")), 2500);
-        Point start = qsb.getVisibleCenter();
-        int endY = (int) (mDevice.getDisplayHeight() * 0.6);
-        mDevice.swipe(start.x, start.y, start.x, endY, (endY - start.y) / 100); // 100 px/step
-
-        UiObject2 recentsView = mDevice.wait(
-                Until.findObject(SystemUiJankTests.getLauncherOverviewSelector(mDevice)), 2500);
-        assertNotNull(recentsView);
-        assertTrue(!mDevice.hasObject(getAppListViewSelector()));
-    }
-
     public void resetAndOpenRecents() throws UiObjectNotFoundException, RemoteException {
-        goHome();
-        mDevice.pressRecentApps();
+        mLauncher.pressHome().switchToOverview();
     }
 
     public void prepareOpenAllAppsContainer() throws IOException {
@@ -150,16 +108,14 @@
             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++) {
-            fromRecentsToAllApps();
-            mDevice.waitForIdle();
-            fromAllAppsToRecents();
-            mDevice.waitForIdle();
+            overview = overview.switchToAllApps().switchBackToOverview();
         }
     }
 
     public void openAllApps() throws UiObjectNotFoundException, IOException {
-        mLauncherStrategy.openAllApps(false);
+        mLauncher.pressHome().switchToAllApps();
         TimeResultLogger.writeTimeStampLogStart(String.format("%s-%s",
                 getClass().getSimpleName(), getName()), TIMESTAMP_FILE);
     }
@@ -177,41 +133,15 @@
             expectedFrames=100)
     @GfxMonitor(processName="#getLauncherPackage")
     public void testAllAppsContainerSwipe() {
-        UiObject2 allApps = mDevice.findObject(mLauncherStrategy.getAllAppsSelector());
-        final int allAppsHeight = allApps.getVisibleBounds().height();
-        Direction dir = mLauncherStrategy.getAllAppsScrollDirection();
+        final AllApps allApps = mLauncher.getAllApps();
         for (int i = 0; i < INNER_LOOP * 2; i++) {
-            if (dir == Direction.DOWN) {
-                // Start the gesture in the center to avoid starting at elements near the top.
-                allApps.setGestureMargins(0, 0, 0, allAppsHeight / 2);
-            }
-            allApps.fling(dir, FLING_SPEED);
-            if (dir == Direction.DOWN) {
-                // Start the gesture in the center, for symmetry.
-                allApps.setGestureMargins(0, allAppsHeight / 2, 0, 0);
-            }
-            allApps.fling(Direction.reverse(dir), FLING_SPEED);
+            allApps.flingForward();
+            allApps.flingBackward();
         }
     }
 
     public void makeHomeScrollable() throws UiObjectNotFoundException, IOException {
-        goHome();
-        UiObject2 homeScreen = mDevice.findObject(mLauncherStrategy.getWorkspaceSelector());
-        Rect r = homeScreen.getVisibleBounds();
-        if (!homeScreen.isScrollable()) {
-            // Add the Chrome icon to the first launcher screen.
-            // This is specifically for Bullhead, where you can't add an icon
-            // to the second launcher screen without one on the first.
-            UiObject2 chrome = mDevice.findObject(By.text("Chrome"));
-            Point dest = new Point(mDevice.getDisplayWidth()/2, r.centerY());
-            chrome.drag(dest, 2000);
-            // Drag Camera icon to next screen
-            UiObject2 camera = mDevice.findObject(By.text("Camera"));
-            dest = new Point(mDevice.getDisplayWidth(), r.centerY());
-            camera.drag(dest, 2000);
-        }
-        mDevice.waitForIdle();
-        assertTrue("home screen workspace still not scrollable", homeScreen.isScrollable());
+        mLauncher.pressHome().ensureWorkspaceIsScrollable();
         TimeResultLogger.writeTimeStampLogStart(String.format("%s-%s",
                 getClass().getSimpleName(), getName()), TIMESTAMP_FILE);
     }
@@ -229,16 +159,15 @@
               expectedFrames=100)
     @GfxMonitor(processName="#getLauncherPackage")
     public void testHomeScreenSwipe() {
-        UiObject2 workspace = mDevice.findObject(mLauncherStrategy.getWorkspaceSelector());
-        Direction dir = mLauncherStrategy.getWorkspaceScrollDirection();
+        final Workspace workspace = mLauncher.getWorkspace();
         for (int i = 0; i < INNER_LOOP * 2; i++) {
-            workspace.fling(dir);
-            workspace.fling(Direction.reverse(dir));
+            workspace.flingForward();
+            workspace.flingBackward();
         }
     }
 
     public void openAllWidgets() throws UiObjectNotFoundException, IOException {
-        mLauncherStrategy.openAllWidgets(true);
+        mLauncher.pressHome().openAllWidgets();
         TimeResultLogger.writeTimeStampLogStart(String.format("%s-%s",
                 getClass().getSimpleName(), getName()), TIMESTAMP_FILE);
     }
@@ -256,11 +185,10 @@
               expectedFrames=100)
     @GfxMonitor(processName="#getLauncherPackage")
     public void testWidgetsContainerFling() {
-        UiObject2 allWidgets = mDevice.findObject(mLauncherStrategy.getAllWidgetsSelector());
-        Direction dir = mLauncherStrategy.getAllWidgetsScrollDirection();
+        final Widgets widgets = mLauncher.getAllWidgets();
         for (int i = 0; i < INNER_LOOP; i++) {
-            allWidgets.fling(dir, FLING_SPEED);
-            allWidgets.fling(Direction.reverse(dir), FLING_SPEED);
+            widgets.flingForward();
+            widgets.flingBackward();
         }
     }
 
@@ -277,11 +205,6 @@
         super.afterTest(metrics);
     }
 
-    private void pressUiHome() throws RemoteException {
-        mDevice.findObject(By.res(SYSTEMUI_PACKAGE, "home")).click();
-        mDevice.waitForIdle();
-    }
-
     /**
      * Opens and closes the Messages app repeatedly, measuring jank for synchronized app
      * transitions.
@@ -292,36 +215,7 @@
     public void testOpenCloseMessagesApp() throws Exception {
         for (int i = 0; i < INNER_LOOP; i++) {
             mLauncherStrategy.launch("Messages", "com.google.android.apps.messaging");
-            pressUiHome();
-        }
-    }
-
-    public void beforeOpenMessagesAppFromRecents() throws UiObjectNotFoundException, IOException {
-        goHome();
-        mLauncherStrategy.launch("Messages", "com.google.android.apps.messaging");
-        TimeResultLogger.writeTimeStampLogStart(String.format("%s-%s",
-                getClass().getSimpleName(), getName()), TIMESTAMP_FILE);
-    }
-
-    public void afterOpenMessagesAppFromRecents(Bundle metrics) throws IOException {
-        TimeResultLogger.writeTimeStampLogEnd(String.format("%s-%s",
-                getClass().getSimpleName(), getName()), TIMESTAMP_FILE);
-        TimeResultLogger.writeResultToFile(String.format("%s-%s",
-                getClass().getSimpleName(), getName()), RESULTS_FILE, metrics);
-        super.afterTest(metrics);
-    }
-
-    /**
-     * Opens the Messages app repeatedly from recents, measuring jank for synchronized app
-     * transitions.
-     */
-    @JankTest(beforeTest="beforeOpenMessagesAppFromRecents",
-            afterTest="afterOpenMessagesAppFromRecents", expectedFrames=80)
-    @GfxMonitor(processName="#getLauncherPackage")
-    public void testOpenMessagesAppFromRecents() throws Exception {
-        for (int i = 0; i < INNER_LOOP; i++) {
-            SystemUiJankTests.openRecents(getInstrumentation().getTargetContext(), mDevice);
-            mLauncherStrategy.launch("Messages", "com.google.android.apps.messaging");
+            mLauncher.pressHome();
         }
     }
 }
diff --git a/tests/jank/UbSystemUiJankTests/src/android/platform/systemui/tests/jank/SettingsJankTests.java b/tests/jank/UbSystemUiJankTests/src/android/platform/systemui/tests/jank/SettingsJankTests.java
index 4271e04..460d67b 100644
--- a/tests/jank/UbSystemUiJankTests/src/android/platform/systemui/tests/jank/SettingsJankTests.java
+++ b/tests/jank/UbSystemUiJankTests/src/android/platform/systemui/tests/jank/SettingsJankTests.java
@@ -50,10 +50,13 @@
             .getAbsolutePath(), "autotester.log");
     private static final File RESULTS_FILE = new File(Environment.getExternalStorageDirectory()
             .getAbsolutePath(), "results.log");
+    // Note: Margin of 150 selected arbitrarily
+    private static final int VIEW_MARGIN = 150;
 
     private UiDevice mDevice;
 
     public void setUp() {
+        androidx.test.InstrumentationRegistry.registerInstance(getInstrumentation(), new Bundle());
         mDevice = UiDevice.getInstance(getInstrumentation());
 
         try {
@@ -82,6 +85,7 @@
 
     public void flingSettingsToStart() throws IOException {
         UiObject2 list = mDevice.wait(Until.findObject(SETTINGS_DASHBOARD), TIMEOUT);
+        list.setGestureMargin(VIEW_MARGIN);
         int count = 0;
         while (!list.isScrollable() && count <= 5) {
             mDevice.wait(Until.findObject(By.text("SEE ALL")), TIMEOUT).click();
@@ -108,6 +112,7 @@
     @GfxMonitor(processName=SETTINGS_PACKAGE)
     public void testSettingsFling() {
         UiObject2 list = mDevice.findObject(SETTINGS_DASHBOARD);
+        list.setGestureMargin(VIEW_MARGIN);
         for (int i = 0; i < INNER_LOOP; i++) {
             list.fling(Direction.DOWN);
             mDevice.waitForIdle();
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 7bf055e..c5b9192 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
@@ -17,7 +17,6 @@
 package android.platform.systemui.tests.jank;
 
 import static android.support.test.InstrumentationRegistry.getInstrumentation;
-import static android.system.helpers.OverviewHelper.isRecentsInLauncher;
 
 import static org.junit.Assert.assertNotNull;
 
@@ -31,6 +30,7 @@
 import android.content.Intent;
 import android.graphics.Rect;
 import android.graphics.drawable.Icon;
+import android.hardware.display.AmbientDisplayConfiguration;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.RemoteException;
@@ -41,38 +41,33 @@
 import android.support.test.jank.GfxMonitor;
 import android.support.test.jank.JankTest;
 import android.support.test.jank.JankTestBase;
+import android.support.test.launcherhelper.LauncherStrategyFactory;
 import android.support.test.timeresulthelper.TimeResultLogger;
 import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.Direction;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject;
 import android.support.test.uiautomator.UiObject2;
 import android.support.test.uiautomator.UiSelector;
-import android.support.test.uiautomator.Until;
 import android.system.helpers.LockscreenHelper;
 import android.system.helpers.OverviewHelper;
 import android.widget.Button;
 import android.widget.ImageView;
 
-import com.android.internal.hardware.AmbientDisplayConfiguration;
+import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.tapl.Overview;
 
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
 public class SystemUiJankTests extends JankTestBase {
 
     private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
     private static final String SETTINGS_PACKAGE = "com.android.settings";
-    private static final BySelector RECENTS = By.res(SYSTEMUI_PACKAGE, "recents_view");
-    private static final String LOG_TAG = SystemUiJankTests.class.getSimpleName();
     private static final int SWIPE_MARGIN = 5;
     private static final int DEFAULT_SCROLL_STEPS = 130;
     private static final int BRIGHTNESS_SCROLL_STEPS = 30;
-    private static final int DEFAULT_FLING_SPEED = 15000;
 
     // short transitions should be repeated within the test function, otherwise frame stats
     // captured are not really meaningful in a statistical sense
@@ -122,11 +117,13 @@
                         new UiSelector().className(Button.class).descriptionContains("CLEAR ALL");
 
     private UiDevice mDevice;
+    private LauncherInstrumentation mLauncher;
     private ArrayList<String> mLaunchedPackages;
     private NotificationManager mNotificationManager;
     private int mInitialDozeAlwaysOn;
 
     public void setUp() throws Exception {
+        androidx.test.InstrumentationRegistry.registerInstance(getInstrumentation(), new Bundle());
         mDevice = UiDevice.getInstance(getInstrumentation());
         try {
             mDevice.setOrientationNatural();
@@ -137,6 +134,8 @@
                 NotificationManager.class);
         InstrumentationRegistry.registerInstance(getInstrumentation(), getArguments());
         blockNotifications();
+        // Need to run strategy initialization code as a precondition for tests.
+        LauncherStrategyFactory.getInstance(mDevice);
 
         // Enable AOD, otherwise we won't test all animations. Having AOD off also adds
         // unpredictable fluctuations since the display can take up to 200ms to turn on.
@@ -145,6 +144,9 @@
         mInitialDozeAlwaysOn = configuration.alwaysOnEnabled(UserHandle.USER_SYSTEM) ? 1 : 0;
         ContentResolver contentResolver = getInstrumentation().getContext().getContentResolver();
         Settings.Secure.putInt(contentResolver, Settings.Secure.DOZE_ALWAYS_ON, 1);
+
+        mLauncher = new LauncherInstrumentation(getInstrumentation());
+        mDevice.executeShellCommand("pm disable com.google.android.music");
     }
 
     public void goHome() {
@@ -154,6 +156,7 @@
 
     @Override
     protected void tearDown() throws Exception {
+        mDevice.executeShellCommand("pm enable com.google.android.music");
         mDevice.unfreezeRotation();
         unblockNotifications();
         ContentResolver contentResolver = getInstrumentation().getContext().getContentResolver();
@@ -178,45 +181,10 @@
         super.afterTest(metrics);
     }
 
-    public static BySelector getLauncherOverviewSelector(UiDevice device) {
-        return By.res(device.getLauncherPackageName(), "overview_panel");
-    }
-
-    private BySelector getLauncherOverviewSelector() {
-        return getLauncherOverviewSelector(mDevice);
-    }
-
-    public static void openRecents(Context context, UiDevice device) {
-        final UiObject2 recentsButton = device.findObject(By.res(SYSTEMUI_PACKAGE, "recent_apps"));
-        if (recentsButton == null) {
-            int height = device.getDisplayHeight();
-            UiObject2 navBar = device.findObject(By.res(SYSTEMUI_PACKAGE, "navigation_bar_frame"));
-
-            // Swipe from nav bar to 2/3rd down the screen.
-            device.swipe(
-                    navBar.getVisibleBounds().centerX(), navBar.getVisibleBounds().centerY(),
-                    navBar.getVisibleBounds().centerX(), height * 2 / 3,
-                    (navBar.getVisibleBounds().centerY() - height * 2 / 3) / 100); // 100 px/step
-        } else {
-            recentsButton.click();
-        }
-
-        // use a long timeout to wait until recents populated
-        if (device.wait(
-                Until.findObject(isRecentsInLauncher()
-                        ? getLauncherOverviewSelector(device) : RECENTS),
-                10000) == null) {
-            fail("Recents didn't appear");
-        }
-        device.waitForIdle();
-    }
-
-    public void resetRecentsToBottom() throws RemoteException {
+    // Makes sure Recents is opened on the most recent task.
+    public void resetRecents() throws RemoteException {
         mDevice.wakeUp();
-        // Rather than trying to scroll back to the bottom, just re-open the recents list
-        mDevice.pressHome();
-        mDevice.waitForIdle();
-        openRecents(getInstrumentation().getTargetContext(), mDevice);
+        mLauncher.pressHome().switchToOverview();
     }
 
     public void prepareNotifications(int groupMode) throws Exception {
@@ -241,9 +209,14 @@
     }
 
     private void postNotifications(int groupMode, int sleepBetweenDuration, int maxCount) {
-        Context context = getInstrumentation().getContext();
-        Builder builder = new Builder(context)
+        Builder builder = new Builder(getInstrumentation().getContext())
                 .setContentTitle(NOTIFICATION_TEXT);
+        postNotifications(builder, groupMode, sleepBetweenDuration, maxCount);
+    }
+
+    private void postNotifications(Builder builder, int groupMode, int sleepBetweenDuration,
+            int maxCount) {
+        Context context = getInstrumentation().getContext();
         if (groupMode == GROUP_MODE_GROUPED) {
             builder.setGroup("key");
         }
@@ -271,6 +244,41 @@
         }
     }
 
+    private Builder createSmartSuggestionsNotificationBuilder() {
+        Context context = getInstrumentation().getContext();
+        Builder builder = new Builder(context)
+                .setContentTitle(NOTIFICATION_TEXT)
+                .setContentText(NOTIFICATION_TEXT)
+                .setSmallIcon(ICONS[0]);
+        // Add one reply and two actions
+        RemoteInput remoteInput = new RemoteInput.Builder("reply")
+                .setLabel(NOTIFICATION_TEXT)
+                .setChoices(new String[]{"Yes!"})
+                .build();
+        for (Action action : createSmartActions("Click", "Tap")) {
+            builder.addAction(action);
+        }
+        return builder;
+    }
+
+    private List<Action> createSmartActions(String ...actionTitles) {
+        List<Action> actions = new ArrayList<>();
+        for (String title : actionTitles) {
+            actions.add(createSmartAction(title));
+        }
+        return actions;
+    }
+
+    private Action createSmartAction(String actionTitle) {
+        Context context = getInstrumentation().getContext();
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0 , new Intent(),
+                PendingIntent.FLAG_UPDATE_CURRENT);
+        Icon icon = Icon.createWithResource(context, ICONS[0]);
+        return new Action.Builder(icon, actionTitle, pendingIntent)
+                .setContextual(true)
+                .build();
+    }
+
     private void postInlineReplyNotification() {
         RemoteInput remoteInput = new RemoteInput.Builder("reply")
                 .setLabel(NOTIFICATION_TEXT)
@@ -313,85 +321,34 @@
      * Returns the package that provides Recents.
      */
     public String getPackageForRecents() {
-        return isRecentsInLauncher() ? mDevice.getLauncherPackageName() : SYSTEMUI_PACKAGE;
+        return mDevice.getLauncherPackageName();
     }
 
-    /** Starts from the bottom of the recent apps list and measures jank while flinging up. */
-    @JankTest(beforeTest = "populateRecentApps", beforeLoop = "resetRecentsToBottom",
+    /** Starts from the most recent of the recent apps list and measures jank while flinging. */
+    @JankTest(beforeTest = "populateRecentApps", beforeLoop = "resetRecents",
             afterTest = "forceStopPackages", expectedFrames = 100, defaultIterationCount = 5)
     @GfxMonitor(processName = "#getPackageForRecents")
     public void testRecentAppsFling() {
-        final UiObject2 recents;
-        final Direction firstFling, secondFling;
-
-        if (isRecentsInLauncher()) {
-            recents = mDevice.findObject(getLauncherOverviewSelector());
-            firstFling = Direction.RIGHT;
-            secondFling = Direction.LEFT;
-        } else {
-            recents = mDevice.findObject(RECENTS);
-            final Rect r = recents.getVisibleBounds();
-            final int margin = r.height() / 4; // top & bottom edges for fling gesture = 25% height
-            recents.setGestureMargins(0, margin, 0, margin);
-            firstFling = Direction.UP;
-            secondFling = Direction.DOWN;
-        }
-
+        final Overview overview = mLauncher.getOverview();
         for (int i = 0; i < INNER_LOOP; i++) {
-            recents.fling(firstFling, DEFAULT_FLING_SPEED);
-            mDevice.waitForIdle();
-            recents.fling(secondFling, DEFAULT_FLING_SPEED);
-            mDevice.waitForIdle();
+            overview.flingForward();
+            overview.flingBackward();
         }
     }
 
     /**
      * Measures jank when dismissing a task in recents.
      */
-    @JankTest(beforeTest = "populateRecentApps", beforeLoop = "resetRecentsToBottom",
+    @JankTest(beforeTest = "populateRecentApps", beforeLoop = "resetRecents",
             afterTest = "forceStopPackages", expectedFrames = 10, defaultIterationCount = 5)
     @GfxMonitor(processName = "#getPackageForRecents")
     public void testRecentAppsDismiss() {
-        if (isRecentsInLauncher()) {
-            final UiObject2 overviewPanel = mDevice.findObject(getLauncherOverviewSelector());
-            // Bring some task onto the screen.
-            overviewPanel.fling(Direction.RIGHT, DEFAULT_FLING_SPEED);
-            mDevice.waitForIdle();
+        final Overview overview = mLauncher.getOverview();
+        // Bring some task onto the screen.
+        overview.flingForward();
 
-            for (int i = 0; i < INNER_LOOP; i++) {
-                final List<UiObject2> taskViews = mDevice.findObjects(
-                        By.res(mDevice.getLauncherPackageName(), "snapshot"));
-
-                if (taskViews.size() == 0) {
-                    fail("Unable to find a task to dismiss");
-                }
-
-                // taskViews contains up to 3 task views: the 'main' (having the widest visible
-                // part) one in the center, and parts of its right and left siblings. Find the
-                // main task view by its width.
-                final UiObject2 widestTask = Collections.max(taskViews,
-                        (t1, t2) -> Integer.compare(t1.getVisibleBounds().width(),
-                                t2.getVisibleBounds().width()));
-
-                // Dismiss the task via flinging it up.
-                widestTask.fling(Direction.DOWN);
-                mDevice.waitForIdle();
-            }
-
-        } else {
-            // Wait until dismiss views are fully faded in.
-            mDevice.findObject(new UiSelector().resourceId("com.android.systemui:id/dismiss_task"))
-                    .waitForExists(5000);
-            for (int i = 0; i < INNER_LOOP; i++) {
-                List<UiObject2> dismissViews = mDevice.findObjects(
-                        By.res(SYSTEMUI_PACKAGE, "dismiss_task"));
-                if (dismissViews.size() == 0) {
-                    fail("Unable to find dismiss view");
-                }
-                dismissViews.get(dismissViews.size() - 1).click();
-                mDevice.waitForIdle();
-                SystemClock.sleep(500);
-            }
+        for (int i = 0; i < INNER_LOOP; i++) {
+            overview.getCurrentTask().dismiss();
         }
     }
 
@@ -744,43 +701,22 @@
         }
     }
 
-    public void beforeCameraFromLockscreen() throws Exception {
-        TimeResultLogger.writeTimeStampLogStart(String.format("%s-%s",
-                getClass().getSimpleName(), getName()), TIMESTAMP_FILE);
-    }
-
-    public void beforeCameraFromLockscreenLoop() throws Exception {
-        mDevice.pressHome();
-        mDevice.sleep();
-        // Make sure we don't trigger the camera launch double-tap shortcut
-        SystemClock.sleep(300);
-        mDevice.wakeUp();
-        mDevice.waitForIdle();
-    }
-
-    public void afterCameraFromLockscreen(Bundle metrics) throws Exception {
-        TimeResultLogger.writeTimeStampLogEnd(String.format("%s-%s",
-                getClass().getSimpleName(), getName()), TIMESTAMP_FILE);
-        mDevice.pressHome();
-        TimeResultLogger.writeResultToFile(String.format("%s-%s",
-                getClass().getSimpleName(), getName()), RESULTS_FILE, metrics);
-        super.afterTest(metrics);
-    }
-
     /**
-     * Measures jank when launching the camera from lockscreen.
+     * Measures jank when a notification with smart suggestions (replies and actions) is appearing.
      */
-    @JankTest(expectedFrames = 10,
+    @JankTest(expectedFrames = 800, // When added this test produced ~1000 frames on a Pixel 2.
             defaultIterationCount = 5,
-            beforeTest = "beforeCameraFromLockscreen",
-            afterTest = "afterCameraFromLockscreen",
-            beforeLoop = "beforeCameraFromLockscreenLoop")
+            beforeTest = "beforeNotificationAppear",
+            afterTest = "afterNotificationAppear")
     @GfxMonitor(processName = SYSTEMUI_PACKAGE)
-    public void testCameraFromLockscreen() throws Exception {
-        mDevice.swipe(mDevice.getDisplayWidth() - SWIPE_MARGIN,
-                mDevice.getDisplayHeight() - SWIPE_MARGIN, SWIPE_MARGIN, SWIPE_MARGIN,
-                DEFAULT_SCROLL_STEPS);
-        mDevice.waitForIdle();
+    public void testSmartReplyNotificationsAppear() throws Exception {
+        for (int i = 0; i < INNER_LOOP; i++) {
+            postNotifications(
+                    createSmartSuggestionsNotificationBuilder(), GROUP_MODE_UNGROUPED, 250, 10);
+            mDevice.waitForIdle();
+            cancelNotifications(250);
+            mDevice.waitForIdle();
+        }
     }
 
     public void beforeAmbientWakeUp() throws Exception {
diff --git a/tests/perf/PerfTransitionTest/Android.bp b/tests/perf/PerfTransitionTest/Android.bp
index d4cac46..e052713 100644
--- a/tests/perf/PerfTransitionTest/Android.bp
+++ b/tests/perf/PerfTransitionTest/Android.bp
@@ -27,6 +27,7 @@
         "sysui-helper",
         "collector-device-lib",
         "settings-helper",
+        "launcher-aosp-tapl",
     ],
 
     test_suites: ["device-tests"],
diff --git a/tests/perf/PerfTransitionTest/AndroidManifest.xml b/tests/perf/PerfTransitionTest/AndroidManifest.xml
index fb86d2b..9b0a5b8 100644
--- a/tests/perf/PerfTransitionTest/AndroidManifest.xml
+++ b/tests/perf/PerfTransitionTest/AndroidManifest.xml
@@ -23,6 +23,7 @@
     <uses-permission android:name="android.permission.REAL_GET_TASKS" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.SET_ORIENTATION" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
 
    <uses-sdk
         android:minSdkVersion="22"
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 2f77068..6d5136f 100644
--- a/tests/perf/PerfTransitionTest/src/com/android/apptransition/tests/AppTransitionTests.java
+++ b/tests/perf/PerfTransitionTest/src/com/android/apptransition/tests/AppTransitionTests.java
@@ -16,10 +16,6 @@
 
 package com.android.apptransition.tests;
 
-import static android.system.helpers.OverviewHelper.isRecentsInLauncher;
-
-import android.app.ActivityManager;
-import android.app.IActivityManager;
 import android.app.Instrumentation;
 import android.content.Context;
 import android.content.Intent;
@@ -32,14 +28,12 @@
 import android.support.test.launcherhelper.ILauncherStrategy;
 import android.support.test.launcherhelper.LauncherStrategyFactory;
 import android.support.test.rule.logging.AtraceLogger;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.Direction;
 import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
 import android.util.Log;
 
+import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.tapl.Workspace;
+
 import org.junit.After;
 import org.junit.Assume;
 import org.junit.Before;
@@ -58,7 +52,7 @@
 import java.util.Map;
 import java.util.Set;
 
-public class AppTransitionTests {
+public class AppTransitionTests extends Instrumentation {
 
     private static final String TAG = AppTransitionTests.class.getSimpleName();
     private static final int JOIN_TIMEOUT = 10000;
@@ -66,8 +60,8 @@
     private static final String DEFAULT_POST_LAUNCH_TIMEOUT = "5000";
     private static final String DEFAULT_LAUNCH_COUNT = "10";
     private static final String SUCCESS_MESSAGE = "Status: ok";
-    private static final String HOT_LAUNCH_MESSAGE = "Warning: Activity not started, its current"
-            + " task has been brought to the front";
+    private static final String HOT_LAUNCH_MESSAGE = "LaunchState: HOT";
+    private static final String TOTAL_TIME_MESSAGE = "TotalTime:";
     private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh";
     private static final String APP_LAUNCH_CMD = "am start -W -n";
     private static final String FORCE_STOP = "am force-stop ";
@@ -78,7 +72,7 @@
     private static final String COLD_LAUNCH = "cold_launch";
     private static final String HOT_LAUNCH = "hot_launch";
     private static final String NOT_SURE = "not_sure";
-    private static final String ACTIVITY = "Activity";
+    private static final String ACTIVITY = "Activity:";
     private static final String KEY_TRACE_DIRECTORY = "trace_directory";
     private static final String KEY_TRACE_CATEGORY = "trace_categories";
     private static final String KEY_TRACE_BUFFERSIZE = "trace_bufferSize";
@@ -88,13 +82,8 @@
     private static final String DEFAULT_TRACE_BUFFER_SIZE = "20000";
     private static final String DEFAULT_TRACE_DUMP_INTERVAL = "10";
     private static final String DELIMITER = ",";
-    private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
-    private static final BySelector RECENTS = By.res(SYSTEMUI_PACKAGE, "recents_view");
-    private static final int TASK_SWITCH_SWIPE_SPEED = 500;
-    private Context mContext;
     private UiDevice mDevice;
-    private PackageManager mPackageManager;
-    private IActivityManager mActivityManager;
+    private LauncherInstrumentation mLauncher;
     private ILauncherStrategy mLauncherStrategy = null;
     private Map<String, Intent> mAppLaunchIntentsMapping = null;
     private String mTraceDirectoryStr = null;
@@ -113,18 +102,16 @@
     private AtraceLogger mAtraceLogger = null;
     private String mComponentName = null;
     private Map<String,String> mPreAppsComponentName = new HashMap<String, String>();
-    private float mDisplayDensity;
     private boolean mHasLeanback = false;
 
     @Before
     public void setUp() throws Exception {
-        mPackageManager = getInstrumentation().getContext().getPackageManager();
-        mContext = getInstrumentation().getContext();
+        androidx.test.InstrumentationRegistry.registerInstance(this, new Bundle());
         mArgs = InstrumentationRegistry.getArguments();
-        mActivityManager = ActivityManager.getService();
         mDevice = UiDevice.getInstance(getInstrumentation());
         LauncherStrategyFactory factory = LauncherStrategyFactory.getInstance(mDevice);
         mLauncherStrategy = factory.getLauncherStrategy();
+        mLauncher = new LauncherInstrumentation(getInstrumentation());
         mHasLeanback = hasLeanback(getInstrumentation().getTargetContext());
 
         // Inject an instance of instrumentation only if leanback. This enables to launch any app
@@ -146,7 +133,6 @@
         }
         mAppsList = mAppsList.replaceAll("%"," ");
         mAppListArray = mAppsList.split(DELIMITER);
-        mDisplayDensity = mContext.getResources().getDisplayMetrics().density;
 
         // Parse the trace parameters
         mTraceDirectoryStr = mArgs.getString(KEY_TRACE_DIRECTORY);
@@ -300,7 +286,7 @@
                             mTraceDumpInterval, mRootTraceSubDir,
                             String.format("%s-%d", appName, launchCount - 1));
                 }
-                pressUiRecentApps();
+                mLauncher.getBackground().switchToOverview();
                 sleep(mPostLaunchTimeout);
                 if (null != mAtraceLogger && launchCount > 0) {
                     mAtraceLogger.atraceStop();
@@ -338,20 +324,19 @@
             // To bring the app to launch as first item from recents task.
             mLauncherStrategy.launch(appName, mPreAppsComponentName.get(appName).split(
                     "\\/")[0]);
-            sleep(mPostLaunchTimeout);
             for (int launchCount = 0; launchCount <= mLaunchIterations; launchCount++) {
+                sleep(mPostLaunchTimeout);
+                final Workspace workspace = mLauncher.pressHome();
                 if (null != mAtraceLogger) {
                     mAtraceLogger.atraceStart(mTraceCategoriesSet, mTraceBufferSize,
                             mTraceDumpInterval, mRootTraceSubDir,
                             String.format("%s-%d", appName, (launchCount)));
                 }
-                openMostRecentTask();
+                workspace.switchToOverview().getCurrentTask().open();
                 sleep(mPostLaunchTimeout);
                 if (null != mAtraceLogger) {
                     mAtraceLogger.atraceStop();
                 }
-                mDevice.pressHome();
-                sleep(mPostLaunchTimeout);
             }
             updateResult(appName);
         }
@@ -375,77 +360,6 @@
         return appLaunchTime;
     }
 
-    private BySelector getLauncherOverviewSelector() {
-        return By.res(mDevice.getLauncherPackageName(), "overview_panel");
-    }
-
-    /**
-     * Shows and returns the recents view.
-     *
-     * @throws RemoteException if press recents is not successful
-     */
-    private UiObject2 pressUiRecentApps() throws RemoteException {
-        final UiObject2 recentsButton = mDevice.findObject(By.res(SYSTEMUI_PACKAGE, "recent_apps"));
-        if (recentsButton == null) {
-            int height = mDevice.getDisplayHeight();
-            UiObject2 navBar = mDevice.findObject(By.res(SYSTEMUI_PACKAGE, "navigation_bar_frame"));
-
-            // Swipe from nav bar to 2/3rd down the screen.
-            mDevice.swipe(
-                    navBar.getVisibleBounds().centerX(), navBar.getVisibleBounds().centerY(),
-                    navBar.getVisibleBounds().centerX(), height * 2 / 3,
-                    (navBar.getVisibleBounds().centerY() - height * 2 / 3) / 100); // 100 px/step
-        } else {
-            recentsButton.click();
-        }
-
-        final UiObject2 recentsView = mDevice.wait(
-                Until.findObject(isRecentsInLauncher() ? getLauncherOverviewSelector() : RECENTS),
-                5000);
-
-        if (recentsView == null) {
-            throw new RuntimeException("Recents didn't appear");
-        }
-        mDevice.waitForIdle();
-        return recentsView;
-    }
-
-    /**
-     * To open the home screen.
-     * @throws RemoteException if press home is not successful
-     */
-    private void pressUiHome() throws RemoteException {
-        mDevice.findObject(By.res(SYSTEMUI_PACKAGE, "home")).click();
-    }
-
-    /**
-     * Open recents view and click on the most recent task.
-     * @throws RemoteException if press recents is not successful
-     */
-    public void openMostRecentTask() throws RemoteException {
-        final UiObject2 recentsView = pressUiRecentApps();
-
-        if (isRecentsInLauncher()) {
-            final List<UiObject2> taskViews = mDevice.findObjects(
-                    By.res(mDevice.getLauncherPackageName(), "snapshot"));
-
-            if (taskViews.size() != 2) {
-                // We expect to see in the overview: the active task in the middle (#0), the next
-                // task on the right (#1).
-                throw new RuntimeException(
-                        "Unexpected number of visible tasks: " + taskViews.size());
-            }
-
-            // Click at the first task.
-            taskViews.get(0).click();
-        } else {
-            List<UiObject2> recentsTasks = recentsView.getChildren().get(0)
-                    .getChildren();
-            UiObject2 mostRecentTask = recentsTasks.get(recentsTasks.size() - 1);
-            mostRecentTask.click();
-        }
-    }
-
     /**
      * Create sub directory under the trace root directory to store the trace files captured during
      * the app transition.
@@ -593,38 +507,52 @@
             mCmpName = null;
             try {
                 InputStream inputStream = new FileInputStream(parcelDesc.getFileDescriptor());
+                /* SAMPLE OUTPUT : Cold launch
+                Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
+                Status: ok
+                LaunchState: COLD
+                Activity: com.google.android.calculator/com.android.calculator2.Calculator
+                TotalTime: 357
+                WaitTime: 377
+                Complete*/
+                /* SAMPLE OUTPUT : Hot launch
+                Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
+                Warning: Activity not started, its current task has been brought to the front
+                Status: ok
+                LaunchState: HOT
+                Activity: com.google.android.calculator/com.android.calculator2.CalculatorGoogle
+                TotalTime: 60
+                WaitTime: 67
+                Complete*/
                 StringBuilder appLaunchOuput = new StringBuilder();
                 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
                         inputStream));
                 String line = null;
-                int lineCount = 1;
+                final boolean maybeHotLaunch = mLaunchMode.contains(HOT_LAUNCH) ||
+                        mLaunchMode.contains(NOT_SURE);
                 while ((line = bufferedReader.readLine()) != null) {
-                    if (lineCount == 2) {
-                        if ((mLaunchMode.contains(COLD_LAUNCH) || mLaunchMode.contains(NOT_SURE))
-                                && line.contains(SUCCESS_MESSAGE)) {
-                            launchSuccess = true;
-                        } else if ((mLaunchMode.contains(HOT_LAUNCH) || mLaunchMode
-                                .contains(NOT_SURE)) && line.contains(HOT_LAUNCH_MESSAGE)) {
-                            launchSuccess = true;
-                        }
+                    if (line.startsWith(SUCCESS_MESSAGE)) {
+                        launchSuccess = true;
                     }
-                    if ((launchSuccess && (mLaunchMode.contains(COLD_LAUNCH)
-                            || mLaunchMode.contains(NOT_SURE)) && lineCount == 4) ||
-                            (launchSuccess && (mLaunchMode.contains(HOT_LAUNCH) ||
-                                    mLaunchMode.contains(NOT_SURE)) && lineCount == 5)) {
+                    if (!launchSuccess) {
+                        continue;
+                    }
+
+                    if (line.startsWith(HOT_LAUNCH_MESSAGE) && (!maybeHotLaunch)){
+                        Log.w(TAG, "Error did not expect a hot launch");
+                        break;
+                    }
+
+                    if (line.startsWith(TOTAL_TIME_MESSAGE)) {
                         String launchSplit[] = line.split(":");
                         launchTime = launchSplit[1].trim();
                     }
                     // Needed to update the component name if the very first launch activity
                     // is different from hot launch activity (i.e YouTube)
-                    if ((launchSuccess && (mLaunchMode.contains(HOT_LAUNCH) ||
-                            mLaunchMode.contains(NOT_SURE)) && lineCount == 3)) {
+                    if (maybeHotLaunch && line.startsWith(ACTIVITY)) {
                         String activitySplit[] = line.split(":");
-                        if (activitySplit[0].contains(ACTIVITY)) {
-                            mCmpName = activitySplit[1].trim();
-                        }
+                        mCmpName = activitySplit[1].trim();
                     }
-                    lineCount++;
                 }
                 inputStream.close();
             } catch (IOException e) {
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 a620f2a..39c4faf 100644
--- a/tests/perf/PerfTransitionTest/src/com/android/apptransition/tests/LatencyTests.java
+++ b/tests/perf/PerfTransitionTest/src/com/android/apptransition/tests/LatencyTests.java
@@ -16,20 +16,15 @@
 package com.android.apptransition.tests;
 
 import static android.support.test.InstrumentationRegistry.getInstrumentation;
-import static android.system.helpers.OverviewHelper.isRecentsInLauncher;
 
+import android.app.Instrumentation;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.launcherhelper.LauncherStrategyFactory;
 import android.support.test.rule.logging.AtraceLogger;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.Direction;
 import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
 import android.system.helpers.LockscreenHelper;
 import android.system.helpers.OverviewHelper;
 import android.system.helpers.SettingsHelper;
@@ -37,6 +32,8 @@
 import android.view.Surface;
 import android.view.WindowManagerGlobal;
 
+import com.android.launcher3.tapl.LauncherInstrumentation;
+
 import org.junit.Before;
 import org.junit.Test;
 
@@ -47,7 +44,7 @@
 /**
  * Tests to test various latencies in the system.
  */
-public class LatencyTests {
+public class LatencyTests extends Instrumentation {
 
     private static final int DEFAULT_ITERATION_COUNT = 10;
     private static final String KEY_ITERATION_COUNT = "iteration_count";
@@ -74,11 +71,8 @@
     private static final String TEST_APPTORECENTS = "testAppToRecents";
     private static final String TEST_ROTATION_LATENCY = "testRotationLatency";
     private static final String TEST_SETTINGS_SEARCH = "testSettingsSearch";
-    private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
-    private static final BySelector RECENTS = By.res(SYSTEMUI_PACKAGE, "recents_view");
 
     private String mTraceDirectoryStr = null;
-    private Bundle mArgs;
     private File mRootTrace = null;
     private int mTraceBufferSize = 0;
     private int mTraceDumpInterval = 0;
@@ -86,10 +80,12 @@
     private AtraceLogger mAtraceLogger = null;
 
     private UiDevice mDevice;
+    private LauncherInstrumentation mLauncher;
     private int mIterationCount;
 
     @Before
     public void setUp() throws Exception {
+        androidx.test.InstrumentationRegistry.registerInstance(this, new Bundle());
         mDevice = UiDevice.getInstance(getInstrumentation());
         Bundle mArgs = InstrumentationRegistry.getArguments();
         mIterationCount = Integer.parseInt(mArgs.getString(KEY_ITERATION_COUNT,
@@ -113,6 +109,9 @@
                 }
             }
         }
+        // Need to run strategy initialization code as a precondition for tests.
+        LauncherStrategyFactory.getInstance(mDevice);
+        mLauncher = new LauncherInstrumentation(getInstrumentation());
     }
 
     /**
@@ -320,7 +319,7 @@
                         mTraceDumpInterval, mRootTrace,
                         String.format("%s-%d", TEST_APPTORECENTS, i));
             }
-            pressUiRecentApps();
+            mLauncher.getBackground().switchToOverview();
 
             // Make sure all the animations are really done.
             SystemClock.sleep(200);
@@ -356,41 +355,6 @@
         }
     }
 
-    private BySelector getLauncherOverviewSelector() {
-        return By.res(mDevice.getLauncherPackageName(), "overview_panel");
-    }
-
-    /**
-     * Shows and returns the recents view.
-     *
-     * @throws RemoteException if press recents is not successful
-     */
-    private UiObject2 pressUiRecentApps() throws RemoteException {
-        final UiObject2 recentsButton = mDevice.findObject(By.res(SYSTEMUI_PACKAGE, "recent_apps"));
-        if (recentsButton == null) {
-            int height = mDevice.getDisplayHeight();
-            UiObject2 navBar = mDevice.findObject(By.res(SYSTEMUI_PACKAGE, "navigation_bar_frame"));
-
-            // Swipe from nav bar to 2/3rd down the screen.
-            mDevice.swipe(
-                    navBar.getVisibleBounds().centerX(), navBar.getVisibleBounds().centerY(),
-                    navBar.getVisibleBounds().centerX(), height * 2 / 3,
-                    (navBar.getVisibleBounds().centerY() - height * 2 / 3) / 100); // 100 px/step
-        } else {
-            recentsButton.click();
-        }
-
-        final UiObject2 recentsView = mDevice.wait(
-                Until.findObject(isRecentsInLauncher() ? getLauncherOverviewSelector() : RECENTS),
-                5000);
-
-        if (recentsView == null) {
-            throw new RuntimeException("Recents didn't appear");
-        }
-        mDevice.waitForIdle();
-        return recentsView;
-    }
-
     /**
      * Create trace directory for the latency tests to store the trace files.
      */
diff --git a/tests/perf/PerformanceAppTest/src/com/android/performanceapp/tests/AppLaunchTests.java b/tests/perf/PerformanceAppTest/src/com/android/performanceapp/tests/AppLaunchTests.java
index 91d913b..216fcd4 100644
--- a/tests/perf/PerformanceAppTest/src/com/android/performanceapp/tests/AppLaunchTests.java
+++ b/tests/perf/PerformanceAppTest/src/com/android/performanceapp/tests/AppLaunchTests.java
@@ -16,11 +16,13 @@
 
 package com.android.performanceapp.tests;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -32,6 +34,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
@@ -57,10 +60,14 @@
     private static final String ACTIVITYLIST = "activitylist";
     private static final String LAUNCHCOUNT = "launchcount";
     private static final String RECORDTRACE = "recordtrace";
-    private static final String ATRACE_START = "atrace --async_start am view gfx";
-    private static final String ATRACE_DUMP = "atrace --async_dump";
+    private static final String ATRACE_CATEGORIES = "tracecategory";
+    private static final String DEFAULT_CATEGORIES = "am,view,gfx";
+    private static final String ATRACE_START = "atrace --async_start -b 100000 %s";
     private static final String ATRACE_STOP = "atrace --async_stop";
     private static final String FORCE_STOP = "am force-stop ";
+    private static final String TARGET_URL = "instanturl";
+    private static final String URL_PREFIX = "http://";
+    private static final int BUFFER_SIZE = 8192;
 
     private Context mContext;
     private Bundle mResult;
@@ -68,12 +75,14 @@
     private String mSimpleperfBin;
     private String mSimpleperfEvt;
     private String mSimpleperfDir;
+    private String mAtraceCategory;
     private String mDispatcher;
     private int mLaunchCount;
     private String mCustomActivityList;
+    private String mTargetUrl;
     private PackageInfo mPackageInfo;
     private boolean mRecordTrace = true;
-    private List<String> mActivityList;
+    private List<String> mActivityList = new ArrayList<>();
 
     /**
      * {@inheritDoc}
@@ -89,6 +98,13 @@
         assertNotNull("Target package name not set", mTargetPackageName);
         mSimpleperfEvt = args.getString(SIMPLEPERF_EVT);
         mDispatcher = args.getString(DISPATCHER);
+
+        mAtraceCategory = args.getString(ATRACE_CATEGORIES);
+        if (mAtraceCategory == null || mAtraceCategory.isEmpty()) {
+            mAtraceCategory = DEFAULT_CATEGORIES;
+        }
+        mAtraceCategory = mAtraceCategory.replace(",", " ");
+
         if (mDispatcher != null && !mDispatcher.isEmpty()) {
             mSimpleperfBin = args.getString(SIMPLEPERF_BIN);
             mSimpleperfEvt = args.getString(SIMPLEPERF_EVT);
@@ -96,8 +112,14 @@
         }
         mCustomActivityList = args.getString(ACTIVITYLIST);
         if (mCustomActivityList == null || mCustomActivityList.isEmpty()) {
-            // Get full list of activities from the target package
-            mActivityList = getActivityList("");
+            // Look for instant app configs exist.
+            mTargetUrl = args.getString(TARGET_URL);
+            if (mTargetUrl != null && !mTargetUrl.isEmpty()) {
+                mActivityList.add(args.getString(TARGET_URL));
+            } else {
+                // Get full list of activities from the target package
+                mActivityList = getActivityList("");
+            }
         } else {
             // Get only the user defined list of activities from the target package
             mActivityList = getActivityList(mCustomActivityList);
@@ -123,32 +145,44 @@
             if (mSimpleperfDir == null)
                 mSimpleperfDir = "/sdcard/perf_simpleperf/";
             File simpleperfDir = new File(mSimpleperfDir);
-            assertTrue("Unable to create the directory to store simpleperf data", simpleperfDir.mkdir());
+            assertTrue("Unable to create the directory to store simpleperf data",
+                    simpleperfDir.mkdir());
         }
         for (int count = 0; count < mLaunchCount; count++) {
             for (String activityName : mActivityList) {
-                ComponentName cn = new ComponentName(mTargetPackageName,
-                        mDispatcher != null ? mDispatcher : activityName);
                 Intent intent = new Intent(Intent.ACTION_MAIN);
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
-                intent.setComponent(cn);
+                if (activityName.startsWith(URL_PREFIX)) {
+                    intent = new Intent(Intent.ACTION_VIEW, Uri.parse(activityName));
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                } else {
+                    ComponentName cn = new ComponentName(mTargetPackageName,
+                            mDispatcher != null ? mDispatcher : activityName);
+                    intent = new Intent(Intent.ACTION_MAIN);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+                    intent.setComponent(cn);
 
-                if (mDispatcher != null) {
-                    intent.putExtra("ACTIVITY_NAME", activityName);
-                    intent.putExtra("SIMPLEPERF_DIR", mSimpleperfDir);
-                    intent.putExtra("SIMPLEPERF_EVT", mSimpleperfEvt);
-                    intent.putExtra("SIMPLEPERF_BIN", mSimpleperfBin);
+                    if (mDispatcher != null) {
+                        intent.putExtra("ACTIVITY_NAME", activityName);
+                        intent.putExtra("SIMPLEPERF_DIR", mSimpleperfDir);
+                        intent.putExtra("SIMPLEPERF_EVT", mSimpleperfEvt);
+                        intent.putExtra("SIMPLEPERF_BIN", mSimpleperfBin);
+                    }
                 }
 
                 // Start the atrace
                 if (mRecordTrace) {
-                    assertNotNull(
-                            "Unable to start atrace async",
-                            getInstrumentation().getUiAutomation()
-                                    .executeShellCommand(ATRACE_START));
-                    // Sleep for 10 secs to make sure atrace command is started
-                    Thread.sleep(10 * 1000);
+                    ByteArrayOutputStream startStream = new ByteArrayOutputStream();
+                    try {
+                        writeDataToByteStream(getInstrumentation().getUiAutomation()
+                                .executeShellCommand(String.format(ATRACE_START, mAtraceCategory)),
+                                startStream);
+                    } finally {
+                        startStream.close();
+                    }
+
+                    // Sleep for 5 secs to make sure atrace command is started
+                    Thread.sleep(5 * 1000);
                 }
 
                 // Launch the activity
@@ -170,37 +204,46 @@
                     int processId = getProcessId(mTargetPackageName);
                     assertTrue("Not able to retrive the process id for the package:"
                             + mTargetPackageName, processId > 0);
-                    String fileName = String.format("%s-%d-%d", activityName, count, processId);
-                    ParcelFileDescriptor parcelFile =
-                            getInstrumentation().getUiAutomation().executeShellCommand(ATRACE_DUMP);
-                    assertNotNull("Unable to get the File descriptor to standard out",
-                            parcelFile);
-                    InputStream inputStream = new FileInputStream(parcelFile.getFileDescriptor());
-                    File file = new File(logsDir, fileName);
-                    FileOutputStream outputStream = new FileOutputStream(file);
-                    try {
-                        byte[] buffer = new byte[1024];
-                        int length;
-                        while ((length = inputStream.read(buffer)) > 0) {
-                            outputStream.write(buffer, 0, length);
-                        }
-                    } catch (IOException e) {
-                        Log.w(TAG, "Error writing atrace info to file", e);
+                    String fileName = new String();
+                    if (!activityName.startsWith(URL_PREFIX)) {
+                        fileName = String.format("%s-%d-%d", activityName, count, processId);
+                    } else {
+                        fileName = String.format("%s-%d-%d", Uri.parse(activityName).getHost(),
+                                count, processId);
                     }
-                    inputStream.close();
-                    outputStream.close();
 
-                    // Stop the atrace
-                    assertNotNull(
-                            "Unable to stop the atrace",
-                            getInstrumentation().getUiAutomation().executeShellCommand(ATRACE_STOP));
+                    ByteArrayOutputStream stopStream = new ByteArrayOutputStream();
+                    File file = new File(logsDir, fileName);
+                    OutputStream fileOutputStream = new FileOutputStream(file);
+                    try {
+                        writeDataToByteStream(
+                                getInstrumentation().getUiAutomation()
+                                        .executeShellCommand(ATRACE_STOP),
+                                stopStream);
+                        fileOutputStream.write(stopStream.toByteArray());
+                    } finally {
+                        stopStream.close();
+                        fileOutputStream.close();
+                    }
 
                     // To keep track of the activity name,list of atrace file name
-                    registerTraceFileNames(activityName, fileName);
+                    if (!activityName.startsWith(URL_PREFIX)) {
+                        registerTraceFileNames(activityName, fileName);
+                    } else {
+                        registerTraceFileNames(Uri.parse(activityName).getHost(), fileName);
+                    }
                 }
-                assertNotNull("Unable to stop recent activity launched",
-                        getInstrumentation().getUiAutomation().executeShellCommand(
-                                FORCE_STOP + mTargetPackageName));
+
+                ByteArrayOutputStream killStream = new ByteArrayOutputStream();
+                try {
+                    writeDataToByteStream(getInstrumentation().getUiAutomation()
+                            .executeShellCommand(
+                                    FORCE_STOP + mTargetPackageName),
+                            killStream);
+                } finally {
+                    killStream.close();
+                }
+
                 Thread.sleep(5 * 1000);
             }
         }
@@ -282,5 +325,26 @@
             mResult.putString(activityName, "" + absPath);
         }
     }
-}
 
+    /**
+     * Method to write data into byte array
+     * @param pfDescriptor Used to read the content returned by shell command
+     * @param outputStream Write the data to this output stream read from pfDescriptor
+     * @throws IOException
+     */
+    private void writeDataToByteStream(
+            ParcelFileDescriptor pfDescriptor, ByteArrayOutputStream outputStream)
+            throws IOException {
+        InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfDescriptor);
+        try {
+            byte[] buffer = new byte[BUFFER_SIZE];
+            int length;
+            while ((length = inputStream.read(buffer)) >= 0) {
+                outputStream.write(buffer, 0, length);
+            }
+        } finally {
+            inputStream.close();
+        }
+    }
+
+}
diff --git a/tests/perf/PerformanceLaunch/AndroidManifest.xml b/tests/perf/PerformanceLaunch/AndroidManifest.xml
index b5ef11c..675617e 100644
--- a/tests/perf/PerformanceLaunch/AndroidManifest.xml
+++ b/tests/perf/PerformanceLaunch/AndroidManifest.xml
@@ -21,10 +21,6 @@
 
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
-    <uses-sdk
-        android:minSdkVersion="22"
-        android:targetSdkVersion="22" />
-
     <application
         android:allowBackup="true"
         android:icon="@drawable/ic_launcher"
diff --git a/tests/functional/systemmetrics/Android.bp b/utils/esimutility/Android.bp
similarity index 77%
rename from tests/functional/systemmetrics/Android.bp
rename to utils/esimutility/Android.bp
index 6f403f1..c2fa435 100644
--- a/tests/functional/systemmetrics/Android.bp
+++ b/utils/esimutility/Android.bp
@@ -1,3 +1,4 @@
+//
 // Copyright (C) 2016 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -11,17 +12,13 @@
 // 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.
+//
 
 android_test {
-    name: "SystemMetricsFunctionalTests",
-    srcs: ["src/**/*.java"],
-    static_libs: [
-        "android-support-test",
-        "metrics-helper-lib",
-        "ub-uiautomator",
-    ],
+    name: "ESimUtility",
 
+    certificate: "platform",
     platform_apis: true,
 
-    test_suites: ["device-tests"],
+    srcs: ["src/**/*.java"],
 }
diff --git a/utils/esimutility/AndroidManifest.xml b/utils/esimutility/AndroidManifest.xml
new file mode 100644
index 0000000..2786a29
--- /dev/null
+++ b/utils/esimutility/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2016 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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.test.util.esimutility">
+    <uses-sdk android:minSdkVersion="23"
+              android:targetSdkVersion="23" />
+
+    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name=".ESimUtilityInstrumentation"
+        android:targetPackage="com.android.test.util.esimutility"
+        android:label="up/down/pass-through card_power">
+    </instrumentation>
+</manifest>
diff --git a/utils/esimutility/src/com/android/test/util/esimutility/ESimUtilityInstrumentation.java b/utils/esimutility/src/com/android/test/util/esimutility/ESimUtilityInstrumentation.java
new file mode 100644
index 0000000..65429e9
--- /dev/null
+++ b/utils/esimutility/src/com/android/test/util/esimutility/ESimUtilityInstrumentation.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.util.esimutility;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.os.Bundle;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+public class ESimUtilityInstrumentation extends Instrumentation {
+    private static final String TAG = ESimUtilityInstrumentation.class.getCanonicalName();
+    private Bundle mArguments;
+
+    @Override
+    public void onCreate(Bundle arguments) {
+        super.onCreate(arguments);
+        mArguments = arguments;
+        start();
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        try {
+            Log.d(TAG, "starting instrumentation");
+            TelephonyManager telephonyManager = (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
+            updateStart();
+            telephonyManager.setSimPowerState(getState());
+            Log.d(TAG, "ending instrumentation");
+            updateAllGood();
+        } catch (Exception e) {
+            updateWithException(e);
+        }
+    }
+
+    private void updateAllGood() {
+        Bundle allGood = new Bundle();
+        allGood.putString("all_is", "good");
+        finish(Activity.RESULT_OK, allGood);
+    }
+
+    private void updateWithException(Exception e) {
+        Bundle result = new Bundle();
+        result.putString("sim_utility_exception", e.getMessage());
+        StackTraceElement[] elements = e.getStackTrace();
+        StringBuilder builder = new StringBuilder();
+        for (StackTraceElement element: elements) {
+            builder.append(System.lineSeparator());
+            builder.append("        ").append(element.toString());
+        }
+        result.putString("sim_utility_exception_stack_trace", builder.toString());
+        finish(Activity.RESULT_CANCELED, result);
+    }
+
+    private void updateStart() {
+        Bundle result = new Bundle();
+        result.putString("setting_state_to", getRawState());
+        sendStatus(Activity.RESULT_OK, result);
+    }
+
+    public String getRawState() {
+        return mArguments.getString("state", "no-provided");
+    }
+
+    public int getState() {
+        switch (getRawState()) {
+            case "down":
+                return TelephonyManager.CARD_POWER_DOWN;
+            case "up":
+                return TelephonyManager.CARD_POWER_UP;
+            case "pass-through":
+                return TelephonyManager.CARD_POWER_UP_PASS_THROUGH;
+        }
+
+        throw new IllegalArgumentException(
+                "Invalid or missing state option. Use as: -e state " + "up/down/pass-through");
+    }
+}
diff --git a/utils/permissions/AndroidManifest.xml b/utils/permissions/AndroidManifest.xml
index 6255468..2a5d740 100644
--- a/utils/permissions/AndroidManifest.xml
+++ b/utils/permissions/AndroidManifest.xml
@@ -25,6 +25,7 @@
     <uses-permission android:name="android.permission.GRANT_REVOKE_PERMISSIONS" />
     <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
     <uses-permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/utils/permissions/src/com/android/permissionutils/GrantPermissionUtil.java b/utils/permissions/src/com/android/permissionutils/GrantPermissionUtil.java
index d769950..1ce2345 100644
--- a/utils/permissions/src/com/android/permissionutils/GrantPermissionUtil.java
+++ b/utils/permissions/src/com/android/permissionutils/GrantPermissionUtil.java
@@ -16,12 +16,13 @@
 
 package com.android.permissionutils;
 
+import static android.os.Process.myUserHandle;
+
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PermissionInfo;
-import android.os.UserHandle;
 import android.util.Log;
 import java.util.ArrayList;
 import java.util.List;
@@ -38,7 +39,7 @@
             List<String> missingPermissions = getMissingPermissions(context, pkgInfo);
             if (!missingPermissions.isEmpty()) {
                 for (String permission : missingPermissions) {
-                    pm.grantRuntimePermission(pkgInfo.packageName, permission, UserHandle.OWNER);
+                    pm.grantRuntimePermission(pkgInfo.packageName, permission, myUserHandle());
                 }
             }
         }