Merge "Fix FreeMemListener and TotalPssListener"
diff --git a/build/tasks/tests/instrumentation_test_list.mk b/build/tasks/tests/instrumentation_test_list.mk
index d3f2e68..84790cb 100644
--- a/build/tasks/tests/instrumentation_test_list.mk
+++ b/build/tasks/tests/instrumentation_test_list.mk
@@ -69,16 +69,6 @@
     SettingsUITests \
     ExtServicesUnitTests
 
-# Android Things specific tests
-ifeq ($(PRODUCT_IOT),true)
-
-instrumentation_tests += \
-    AndroidThingsTests \
-    ThingsIntegrationTests \
-    WifiSetupUnitTests
-
-endif  # PRODUCT_IOT == true
-
 # Storage Manager may not exist on device
 ifneq ($(filter StorageManager, $(PRODUCT_PACKAGES)),)
 
diff --git a/build/tasks/tests/native_test_list.mk b/build/tasks/tests/native_test_list.mk
index c3b9159..e1abfd9 100644
--- a/build/tasks/tests/native_test_list.mk
+++ b/build/tasks/tests/native_test_list.mk
@@ -58,7 +58,6 @@
     libbpf_android_test \
     libcutils_test \
     libcutils_test_static \
-    libdemangle_test \
     libgui_test \
     libhidl_test \
     libinput_tests \
@@ -130,18 +129,3 @@
     NeuralNetworksTest_static \
     SurfaceFlinger_test \
     lmkd_unit_test
-
-# Android Things specific tests
-ifeq ($(PRODUCT_IOT),true)
-
-native_tests += \
-    crash_reporter_tests \
-    libandroidthings_pio_tests \
-    libbrillo_test \
-    libchrome_test \
-    libusersensors_binder_test \
-    peripheralman_unittests \
-    sensors_userdriver_test \
-    userinputdriver_test
-
-endif  # PRODUCT_IOT == true
diff --git a/libraries/annotations/src/android/platform/test/annotations/SystemUserOnly.java b/libraries/annotations/src/android/platform/test/annotations/SystemUserOnly.java
new file mode 100644
index 0000000..fc318fe
--- /dev/null
+++ b/libraries/annotations/src/android/platform/test/annotations/SystemUserOnly.java
@@ -0,0 +1,29 @@
+/*
+ * 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.test.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Marks that the test is valid only when running against system user. */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface SystemUserOnly {
+    /** The reason why the test has to be run against system user. */
+    String reason() default "";
+}
diff --git a/libraries/app-helpers/handheld/Android.bp b/libraries/app-helpers/handheld/Android.bp
new file mode 100644
index 0000000..5815fa2
--- /dev/null
+++ b/libraries/app-helpers/handheld/Android.bp
@@ -0,0 +1,24 @@
+// 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.
+
+java_library {
+    name: "handheld-app-helpers",
+    sdk_version: "test_current",
+    static_libs: [
+        "app-helpers-handheld-interfaces",
+        "business-card-app-helper",
+        "performance-launch-app-helper"
+    ],
+    srcs: ["src/**/*.java"],
+}
diff --git a/libraries/app-helpers/handheld/business-card-app-helper/Android.bp b/libraries/app-helpers/handheld/business-card-app-helper/Android.bp
new file mode 100644
index 0000000..91cf865
--- /dev/null
+++ b/libraries/app-helpers/handheld/business-card-app-helper/Android.bp
@@ -0,0 +1,24 @@
+//
+// 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.
+//
+
+java_library {
+    name: "business-card-app-helper",
+    libs: [
+        "app-helpers-handheld-interfaces",
+    ],
+    srcs: ["src/**/*.java"],
+    sdk_version: "test_current",
+}
diff --git a/libraries/app-helpers/handheld/business-card-app-helper/src/android/platform/helpers/BusinessCardHelperImpl.java b/libraries/app-helpers/handheld/business-card-app-helper/src/android/platform/helpers/BusinessCardHelperImpl.java
new file mode 100644
index 0000000..1d8ae1d
--- /dev/null
+++ b/libraries/app-helpers/handheld/business-card-app-helper/src/android/platform/helpers/BusinessCardHelperImpl.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+import android.app.Instrumentation;
+
+public class BusinessCardHelperImpl extends AbstractStandardAppHelper
+        implements IBusinessCardHelper {
+    private static final String UI_PACKAGE_NAME = "com.example.android.businesscard";
+
+    public BusinessCardHelperImpl(Instrumentation instrumentation) {
+        super(instrumentation);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getPackage() {
+        return UI_PACKAGE_NAME;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getLauncherName() {
+        return "Business Card";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void dismissInitialDialogs() {
+        // There are no initial dialogs to dismiss for Business Card.
+    }
+}
diff --git a/libraries/app-helpers/handheld/performance-launch-app-helper/Android.bp b/libraries/app-helpers/handheld/performance-launch-app-helper/Android.bp
new file mode 100644
index 0000000..1d339a1
--- /dev/null
+++ b/libraries/app-helpers/handheld/performance-launch-app-helper/Android.bp
@@ -0,0 +1,24 @@
+//
+// 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.
+//
+
+java_library {
+    name: "performance-launch-app-helper",
+    libs: [
+        "app-helpers-handheld-interfaces",
+    ],
+    srcs: ["src/**/*.java"],
+    sdk_version: "test_current",
+}
diff --git a/libraries/app-helpers/handheld/performance-launch-app-helper/src/android/platform/helpers/PerformanceLaunchHelperImpl.java b/libraries/app-helpers/handheld/performance-launch-app-helper/src/android/platform/helpers/PerformanceLaunchHelperImpl.java
new file mode 100644
index 0000000..8385dc8
--- /dev/null
+++ b/libraries/app-helpers/handheld/performance-launch-app-helper/src/android/platform/helpers/PerformanceLaunchHelperImpl.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+import android.app.Instrumentation;
+
+public class PerformanceLaunchHelperImpl extends AbstractStandardAppHelper
+        implements IPerformanceLaunchHelper {
+    private static final String UI_PACKAGE_NAME = "com.android.performanceLaunch";
+
+    public PerformanceLaunchHelperImpl(Instrumentation instrumentation) {
+        super(instrumentation);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getPackage() {
+        return UI_PACKAGE_NAME;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getLauncherName() {
+        return "PerformanceLaunch";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void dismissInitialDialogs() {
+        // There are no initial dialogs to dismiss for Business Card.
+    }
+}
diff --git a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IBusinessCardHelper.java b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IBusinessCardHelper.java
new file mode 100644
index 0000000..bf08f08
--- /dev/null
+++ b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IBusinessCardHelper.java
@@ -0,0 +1,20 @@
+/*
+ * 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;
+
+/** An App Helper interface for Business Card. */
+public interface IBusinessCardHelper extends IAppHelper {}
diff --git a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IPerformanceLaunchHelper.java b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IPerformanceLaunchHelper.java
new file mode 100644
index 0000000..6a0eb5f
--- /dev/null
+++ b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IPerformanceLaunchHelper.java
@@ -0,0 +1,20 @@
+/*
+ * 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;
+
+/** An App Helper interface for Performance Launch. */
+public interface IPerformanceLaunchHelper extends IAppHelper {}
diff --git a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IYTMusicHelper.java b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IYTMusicHelper.java
index 1908529..cd83af6 100644
--- a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IYTMusicHelper.java
+++ b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IYTMusicHelper.java
@@ -34,6 +34,15 @@
     public void openAlbums();
 
     /**
+     * Setup expectations: YT Music is open and the Library tab is visible.
+     *
+     * <p>This method will select the songs. This method blocks until the process is complete.
+     */
+    public default void openSongs() {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
+    /**
      * Setup expectations: YT Music is open and the Albums page is visible.
      *
      * <p>This method will browse the device files. The method will block until the process is
@@ -42,6 +51,15 @@
     public void browseDeviceFiles();
 
     /**
+     * Setup expectations: YT Music is open and the song list is visible.
+     *
+     * <p>This method will select the song. The method will block until the song is playing.
+     */
+    public default void selectSong(String song) {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
+    /**
      * Setup expectations: YT Music is open and the album list is visible.
      *
      * <p>This method will select the album and select the song. The method will block until the
diff --git a/libraries/collectors-helper/memory/src/com/android/helpers/FreeMemHelper.java b/libraries/collectors-helper/memory/src/com/android/helpers/FreeMemHelper.java
index 1a5fd7b..da0ff2c 100644
--- a/libraries/collectors-helper/memory/src/com/android/helpers/FreeMemHelper.java
+++ b/libraries/collectors-helper/memory/src/com/android/helpers/FreeMemHelper.java
@@ -43,7 +43,7 @@
  * freeMemHelper.getMetrics();
  * freeMemHelper.stopCollecting();
  */
-public final class FreeMemHelper implements ICollectorHelper<Long> {
+public class FreeMemHelper implements ICollectorHelper<Long> {
     private static final String TAG = FreeMemHelper.class.getSimpleName();
     private static final String SEPARATOR = "\\s+";
     private static final String DUMPSYS_MEMIFNO = "dumpsys meminfo";
diff --git a/libraries/collectors-helper/power/Android.bp b/libraries/collectors-helper/power/Android.bp
new file mode 100644
index 0000000..8d68a83
--- /dev/null
+++ b/libraries/collectors-helper/power/Android.bp
@@ -0,0 +1,31 @@
+// 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.
+
+// Used for collecting perfetto traces.
+java_library {
+    name: "power-helper",
+    defaults: ["tradefed_errorprone_defaults"],
+
+    srcs: ["src/**/*.java"],
+
+    static_libs: [
+        "collector-helper-utilities",
+        "androidx.test.runner",
+        "guava",
+        "ub-uiautomator",
+    ],
+
+    sdk_version: "current",
+}
+
diff --git a/libraries/collectors-helper/power/src/com/android/helpers/PwrStatsUtilHelper.java b/libraries/collectors-helper/power/src/com/android/helpers/PwrStatsUtilHelper.java
new file mode 100644
index 0000000..7782857
--- /dev/null
+++ b/libraries/collectors-helper/power/src/com/android/helpers/PwrStatsUtilHelper.java
@@ -0,0 +1,141 @@
+/*
+ * 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 com.android.helpers;
+
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.test.InstrumentationRegistry;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * PwrStatsUtilHelper runs and collects power stats HAL metrics from the Pixel-specific
+ * pwrstats_util tool that is already included on the device in userdebug builds.
+ */
+public class PwrStatsUtilHelper implements ICollectorHelper<Long> {
+    private static final String LOG_TAG = PwrStatsUtilHelper.class.getSimpleName();
+
+    private UiDevice mDevice = null;
+
+    @VisibleForTesting protected int mUtilPid = 0;
+    @VisibleForTesting protected File mLogFile;
+
+    @Override
+    public boolean startCollecting() {
+        Log.i(LOG_TAG, "Starting pwrstats_util collection...");
+        // Create temp log file for pwrstats_util
+        try {
+            mLogFile = File.createTempFile("pwrstats_output", ".txt");
+        } catch (IOException e) {
+            Log.e(LOG_TAG, e.toString());
+            return false;
+        }
+        mLogFile.delete();
+
+        // Kick off pwrstats_util and get its PID
+        final String pid_regex = "^pid = (\\d+)$";
+        final Pattern pid_pattern = Pattern.compile(pid_regex);
+        String output;
+        try {
+            output = executeShellCommand("pwrstats_util -d " + mLogFile.getAbsolutePath());
+        } catch (IOException e) {
+            Log.e(LOG_TAG, e.toString());
+            return false;
+        }
+        Matcher m = pid_pattern.matcher(output);
+        if (m.find()) {
+            mUtilPid = Integer.parseInt(m.group(1));
+        }
+        return true;
+    }
+
+    @Override
+    public boolean stopCollecting() {
+        Log.i(LOG_TAG, "Ending pwrstats_util collection...");
+        try {
+            executeShellCommand("kill -INT " + mUtilPid);
+        } catch (IOException e) {
+            Log.e(LOG_TAG, e.toString());
+            return false;
+        }
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException e) {
+            Log.e(LOG_TAG, e.toString());
+        }
+        return true;
+    }
+
+    @Override
+    public Map<String, Long> getMetrics() {
+        stopCollecting();
+
+        Log.i(LOG_TAG, "Getting metrics...");
+        return processMetricsFromLogFile();
+    }
+
+    @VisibleForTesting
+    /* Execute a shell command and return its output. */
+    protected String executeShellCommand(String command) throws IOException {
+        if (mDevice == null) {
+            mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        }
+
+        Log.i(LOG_TAG, "Running '" + command + "'");
+        return mDevice.executeShellCommand(command);
+    }
+
+    /* Parse each of the metrics into a key value pair and delete the log file. */
+    private Map<String, Long> processMetricsFromLogFile() {
+        final String PATTERN = "^(.*)=(\\d+)$";
+        final Pattern r = Pattern.compile(PATTERN);
+        Map<String, Long> metrics = new HashMap<>();
+        BufferedReader br = null;
+        try {
+            br = new BufferedReader(new FileReader(mLogFile));
+            // First line is elapsed time
+            String line = br.readLine();
+            Matcher m;
+            while ((line = br.readLine()) != null) {
+                m = r.matcher(line);
+                if (m.find()) {
+                    Log.i(LOG_TAG, m.group(1) + "=" + m.group(2));
+                    metrics.put(m.group(1), Long.parseLong(m.group(2)));
+                }
+            }
+            mLogFile.delete();
+        } catch (IOException e) {
+            Log.e(LOG_TAG, e.toString());
+        }
+
+        try {
+            if (br != null) br.close();
+        } catch (IOException e) {
+            Log.e(LOG_TAG, e.toString());
+        }
+        return metrics;
+    }
+}
diff --git a/libraries/collectors-helper/power/test/Android.bp b/libraries/collectors-helper/power/test/Android.bp
new file mode 100644
index 0000000..fa1d4c8
--- /dev/null
+++ b/libraries/collectors-helper/power/test/Android.bp
@@ -0,0 +1,29 @@
+// 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.
+
+java_library {
+    name: "power-helper-test",
+    defaults: ["tradefed_errorprone_defaults"],
+
+    srcs: ["src/**/*.java"],
+
+    static_libs: [
+        "androidx.test.runner",
+        "junit",
+        "mockito-target",
+        "power-helper",
+    ],
+
+    sdk_version: "current",
+}
diff --git a/libraries/collectors-helper/power/test/src/com/android/helpers/PwrStatsUtilHelperTest.java b/libraries/collectors-helper/power/test/src/com/android/helpers/PwrStatsUtilHelperTest.java
new file mode 100644
index 0000000..f4052f7
--- /dev/null
+++ b/libraries/collectors-helper/power/test/src/com/android/helpers/PwrStatsUtilHelperTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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 com.android.helpers;
+
+import android.support.test.uiautomator.UiDevice;
+
+
+import java.io.IOException;
+import java.util.Map;
+
+import static org.mockito.ArgumentMatchers.matches;
+import static org.mockito.Mockito.doReturn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(JUnit4.class)
+public final class PwrStatsUtilHelperTest {
+    private @Spy PwrStatsUtilHelper mHelper;
+    private @Mock UiDevice mUiDevice;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void successfulRun() {
+        final String pid_output = "pid = 11386\n";
+        final String metric_output =
+                "elapsed time: 14.8746s\n"
+                        + "SLPI__Sleep=14505\n"
+                        + "SLPI_ISLAND__uImage=14498\n"
+                        + "SoC__CXSD=0\n"
+                        + "SoC__AOSD=0\n";
+        try {
+            doReturn(metric_output).when(mHelper).executeShellCommand(matches("kill -INT \\d+"));
+            doReturn(pid_output).when(mHelper).executeShellCommand(matches("pwrstats_util -d .*"));
+        } catch (IOException e) {
+            assertTrue(e.toString(), false);
+        }
+
+        // Validate startCollecting()
+        assertTrue(mHelper.startCollecting());
+        assertNotNull(mHelper.mLogFile);
+        assertTrue(
+                "Expecting PID " + mHelper.mUtilPid + " to be greater than 0",
+                (mHelper.mUtilPid > 0));
+
+        // Validate getMetrics()
+        Map<String, Long> metrics = mHelper.getMetrics();
+        assertEquals(metrics.size(), 4);
+
+        // Validate that stopCollecting() works when run after getMetrics()
+        assertTrue(mHelper.stopCollecting());
+    }
+}
diff --git a/libraries/collectors-helper/system/Android.bp b/libraries/collectors-helper/system/Android.bp
new file mode 100644
index 0000000..9baeeed
--- /dev/null
+++ b/libraries/collectors-helper/system/Android.bp
@@ -0,0 +1,33 @@
+// 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.
+
+// Used for collecting the any of the metrics from the system.
+java_library {
+    name: "system-metric-helper",
+    defaults: ["tradefed_errorprone_defaults"],
+
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    static_libs: [
+        "androidx.test.runner",
+        "collector-helper-utilities",
+        "guava",
+        "ub-uiautomator",
+    ],
+
+    sdk_version: "current",
+}
+
diff --git a/libraries/collectors-helper/system/src/com/android/helpers/ProcLoadHelper.java b/libraries/collectors-helper/system/src/com/android/helpers/ProcLoadHelper.java
new file mode 100644
index 0000000..025d53e
--- /dev/null
+++ b/libraries/collectors-helper/system/src/com/android/helpers/ProcLoadHelper.java
@@ -0,0 +1,158 @@
+/*
+ * 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 com.android.helpers;
+
+import android.os.SystemClock;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** An {@link ProcLoadHelper} to check for cpu load in last minute is lesser or equal
+ *  to given threshold for a given timeout and collect the cpu load as metric.
+ */
+public class ProcLoadHelper implements ICollectorHelper<Double> {
+
+    private static final String LOG_TAG = ProcLoadHelper.class.getSimpleName();
+    private static final String LOAD_CMD = "cat /proc/loadavg";
+    public static final String LAST_MINUTE_LOAD_METRIC_KEY = "proc_loadavg_last_minute";
+
+    private static final Pattern LOAD_OUTPUT_PATTERN = Pattern.compile(
+            "(?<LASTMINUTELOAD>.*)\\s.*\\s.*\\s.*\\s.*");
+
+    private double mProcLoadThreshold = 0;
+    private long mProcLoadWaitTimeInMs = 0;
+    // Default to 500 msecs timeout.
+    private long mProcLoadIntervalInMs = 500;
+    private double mRecentLoad = 0;
+    private UiDevice mDevice;
+
+    /** Wait untill the proc/load reaches below the threshold or timeout expires */
+    @Override
+    public boolean startCollecting() {
+        mRecentLoad = 0;
+        long remainingWaitTime = mProcLoadWaitTimeInMs;
+        while (true) {
+            mRecentLoad = getProcLoadInLastMinute();
+            Log.i(LOG_TAG, String.format("Average cpu load in last minute is : %s", mRecentLoad));
+            if (mRecentLoad <= mProcLoadThreshold) {
+                break;
+            } else {
+                if (remainingWaitTime <= 0) {
+                    Log.i(LOG_TAG, "Timeout because proc/loadavg never went below the threshold.");
+                    return false;
+                }
+                long currWaitTime = (mProcLoadIntervalInMs < remainingWaitTime)
+                        ? mProcLoadIntervalInMs : remainingWaitTime;
+                Log.d(LOG_TAG, String.format("Waiting for %s msecs", currWaitTime));
+                SystemClock.sleep(currWaitTime);
+                remainingWaitTime = remainingWaitTime - mProcLoadIntervalInMs;
+            }
+        }
+        return true;
+    }
+
+    /** Collect the proc/load_avg last minute cpu load average metric. */
+    @Override
+    public Map<String, Double> getMetrics() {
+        // Adding the last recorded load in the metric that will be reported.
+        Map<String, Double> result = new HashMap<>();
+        Log.i(LOG_TAG, String.format("proc/loadavg in last minute before test is : %s",
+                mRecentLoad));
+        result.put(LAST_MINUTE_LOAD_METRIC_KEY, mRecentLoad);
+        return result;
+    }
+
+    /** Do nothing, because nothing is needed to disable cpuy load avg. */
+    @Override
+    public boolean stopCollecting() {
+        return true;
+    }
+
+    /**
+     * Parse the last minute cpu load from proc/loadavg
+     *
+     * @return cpu load in last minute. Returns -1 in case if it is failed to parse.
+     */
+    private double getProcLoadInLastMinute() {
+        try {
+            String output = getDevice().executeShellCommand(LOAD_CMD);
+            Log.i(LOG_TAG, String.format("Output of proc_loadavg is : %s", output));
+            // Output of the load command
+            // 1.39 1.10 1.21 2/2679 6380
+            // 1.39 is the proc load in the last minute.
+
+            Matcher match = null;
+            if ((match = matches(LOAD_OUTPUT_PATTERN, output.trim())) != null) {
+                Log.i(LOG_TAG, String.format("Current load is : %s",
+                        match.group("LASTMINUTELOAD")));
+                return Double.parseDouble(match.group("LASTMINUTELOAD"));
+            } else {
+                Log.w(LOG_TAG, "Not able to parse the proc/loadavg");
+            }
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to get proc/loadavg.", e);
+        }
+
+        return -1;
+    }
+
+    /** Returns the {@link UiDevice} under test. */
+    private UiDevice getDevice() {
+        if (mDevice == null) {
+            mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        }
+        return mDevice;
+    }
+
+    /**
+     * Checks whether {@code line} matches the given {@link Pattern}.
+     *
+     * @return The resulting {@link Matcher} obtained by matching the {@code line} against
+     *         {@code pattern}, or null if the {@code line} does not match.
+     */
+    private static Matcher matches(Pattern pattern, String line) {
+        Matcher ret = pattern.matcher(line);
+        return ret.matches() ? ret : null;
+    }
+
+    /**
+     * Sets the threshold value which the device cpu load average should be lesser than or equal.
+     */
+    public void setProcLoadThreshold(double procLoadThreshold) {
+        mProcLoadThreshold = procLoadThreshold;
+    }
+
+    /**
+     * Sets the timeout in msecs checking for threshold before proceeding with the testing.
+     */
+    public void setProcLoadWaitTimeInMs(long procLoadWaitTimeInMs) {
+        mProcLoadWaitTimeInMs = procLoadWaitTimeInMs;
+    }
+
+    /**
+     * Sets the interval time in msecs to check continuosly untill the timeout expires.
+     */
+    public void setProcLoadIntervalInMs(long procLoadIntervalInMs) {
+        mProcLoadIntervalInMs = procLoadIntervalInMs;
+    }
+}
diff --git a/libraries/collectors-helper/system/test/Android.bp b/libraries/collectors-helper/system/test/Android.bp
new file mode 100644
index 0000000..4d94e5a
--- /dev/null
+++ b/libraries/collectors-helper/system/test/Android.bp
@@ -0,0 +1,30 @@
+// 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.
+
+java_library {
+    name: "system-helper-test",
+    defaults: ["tradefed_errorprone_defaults"],
+
+    srcs: ["src/**/*.java"],
+
+    static_libs: [
+        "androidx.test.runner",
+        "system-metric-helper",
+        "junit",
+        "mockito-target",
+        "truth-prebuilt",
+    ],
+
+    sdk_version: "current",
+}
diff --git a/libraries/collectors-helper/system/test/src/com/android/helpers/tests/ProcLoadHelperTest.java b/libraries/collectors-helper/system/test/src/com/android/helpers/tests/ProcLoadHelperTest.java
new file mode 100644
index 0000000..a66d8bc
--- /dev/null
+++ b/libraries/collectors-helper/system/test/src/com/android/helpers/tests/ProcLoadHelperTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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 com.android.helpers.tests;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.helpers.ProcLoadHelper;
+import static com.android.helpers.ProcLoadHelper.LAST_MINUTE_LOAD_METRIC_KEY;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+
+/**
+ * Android Unit tests for {@link ProcLoadHelperTest}.
+ *
+ * To run:
+ * atest CollectorsHelperTest:com.android.helpers.tests.ProcLoadHelperTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class ProcLoadHelperTest {
+
+    private ProcLoadHelper mLoadHelper;
+
+    @Before
+    public void setUp() {
+        mLoadHelper = new ProcLoadHelper();
+    }
+
+    /** Test to verify succesfull threshold flow. **/
+    @Test
+    public void testThresholdSuccess() {
+        // By setting the threshold higher(i.e 100) the current load should be always
+        // lesser than the the 100 which should proceed with add the current
+        // load avg during the last minute in the metrics.
+        mLoadHelper.setProcLoadThreshold(100);
+        mLoadHelper.setProcLoadWaitTimeInMs(1100L);
+        mLoadHelper.setProcLoadIntervalInMs(100L);
+        assertTrue(mLoadHelper.startCollecting());
+        Map<String, Double> procLoadMetric = mLoadHelper.getMetrics();
+        assertTrue(procLoadMetric.containsKey(LAST_MINUTE_LOAD_METRIC_KEY));
+        assertTrue(procLoadMetric.get(LAST_MINUTE_LOAD_METRIC_KEY) > 0);
+    }
+
+    /** Test to verify threshold not met workflow. */
+    @Test
+    public void testMetricAfterTimeout() {
+        // By setting the threshold lower(i.e 0) the cpu utilization will never be met
+        // which should result in timeout.
+        mLoadHelper.setProcLoadThreshold(0);
+        mLoadHelper.setProcLoadWaitTimeInMs(4000L);
+        mLoadHelper.setProcLoadIntervalInMs(200L);
+        assertFalse(mLoadHelper.startCollecting());
+        Map<String, Double> procLoadMetric = mLoadHelper.getMetrics();
+        assertTrue(procLoadMetric.get(LAST_MINUTE_LOAD_METRIC_KEY) > 0);
+    }
+
+    /** Test to verify the timeout if the threshold did not meet. */
+    @Test
+    public void testWaitForTimeout() {
+        // By setting the threshold lower(i.e 0) the cpu utilization will never be met
+        // which should result in timeout.
+        mLoadHelper.setProcLoadThreshold(0);
+        mLoadHelper.setProcLoadWaitTimeInMs(4000L);
+        mLoadHelper.setProcLoadIntervalInMs(200L);
+        long startTime = System.currentTimeMillis();
+        assertFalse(mLoadHelper.startCollecting());
+        long waitTime = System.currentTimeMillis() - startTime;
+        assertTrue((waitTime > 4000L && waitTime < 6000L));
+        Map<String, Double> procLoadMetric = mLoadHelper.getMetrics();
+        assertTrue(procLoadMetric.get(LAST_MINUTE_LOAD_METRIC_KEY) > 0);
+    }
+
+    /** Test to verify metric exist and exits with the default threshold, timeout and interval */
+    @Test
+    public void testDefaults() {
+        long startTime = System.currentTimeMillis();
+        assertFalse(mLoadHelper.startCollecting());
+        Map<String, Double> procLoadMetric = mLoadHelper.getMetrics();
+        assertTrue(procLoadMetric.containsKey(LAST_MINUTE_LOAD_METRIC_KEY));
+        assertTrue(procLoadMetric.get(LAST_MINUTE_LOAD_METRIC_KEY) > 0);
+    }
+}
diff --git a/libraries/device-collectors/src/main/Android.bp b/libraries/device-collectors/src/main/Android.bp
index 9e144ed..9ae62aa 100644
--- a/libraries/device-collectors/src/main/Android.bp
+++ b/libraries/device-collectors/src/main/Android.bp
@@ -24,7 +24,9 @@
         "junit",
         "memory-helper",
         "perfetto-helper",
+        "power-helper",
         "ub-uiautomator",
+        "system-metric-helper",
     ],
 
     sdk_version: "current",
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/FreeMemListener.java b/libraries/device-collectors/src/main/java/android/device/collectors/FreeMemListener.java
new file mode 100644
index 0000000..4b4bc51
--- /dev/null
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/FreeMemListener.java
@@ -0,0 +1,40 @@
+/*
+ * 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.device.collectors.annotations.OptionClass;
+import android.os.Bundle;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.helpers.FreeMemHelper;
+
+/**
+ * A {@link FreeMemListener} that captures and records free memory available
+ * in the device.
+ */
+@OptionClass(alias = "freemem-listener")
+public class FreeMemListener extends BaseCollectionListener<Long> {
+
+    public FreeMemListener() {
+        createHelperInstance(new FreeMemHelper());
+    }
+
+    @VisibleForTesting
+    public FreeMemListener(Bundle args, FreeMemHelper helper) {
+        super(args, helper);
+    }
+}
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/ProcLoadListener.java b/libraries/device-collectors/src/main/java/android/device/collectors/ProcLoadListener.java
new file mode 100644
index 0000000..adc4c7b
--- /dev/null
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/ProcLoadListener.java
@@ -0,0 +1,92 @@
+/*
+ * 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.device.collectors.annotations.OptionClass;
+import android.os.Bundle;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.helpers.ProcLoadHelper;
+
+/**
+ * A {@link ProcLoadListener} that waits until the proc/load threshold is met
+ * or timeout expires.
+ *
+ * Options:
+ * <p>-e proc-loadavg-threshold 1 : The threshold the system cpu load has
+ * to be less than or equal in last minute.
+ *
+ * <p>-e proc-loadavg-timeout 1000 :
+ * Timeout to wait before the threshold is met.
+ *
+ * <p>-e proc-loadavg-interval 100 :
+ * Interval frequency to check if the threshold is met or not.
+ *
+ */
+@OptionClass(alias = "procload-collector")
+public class ProcLoadListener extends BaseCollectionListener<Double> {
+
+    private static final String TAG = ProcLoadListener.class.getSimpleName();
+    @VisibleForTesting
+    static final String PROC_LOAD_THRESHOLD = "proc-loadavg-threshold";
+    @VisibleForTesting
+    static final String PROC_THRESHOLD_TIMEOUT = "proc-loadavg-timeout";
+    @VisibleForTesting
+    static final String PROC_LOAD_INTERVAL = "proc-loadavg-interval";
+
+    private ProcLoadHelper mProcLoadHelper = new ProcLoadHelper();
+
+    public ProcLoadListener() {
+        createHelperInstance(mProcLoadHelper);
+    }
+
+    /**
+     * Constructor to simulate receiving the instrumentation arguments. Should not be used except
+     * for testing.
+     */
+    @VisibleForTesting
+    public ProcLoadListener(Bundle args, ProcLoadHelper helper) {
+        super(args, helper);
+        mProcLoadHelper = helper;
+        createHelperInstance(mProcLoadHelper);
+    }
+
+    /**
+     * Adds the options for total pss collector.
+     */
+    @Override
+    public void setupAdditionalArgs() {
+        Bundle args = getArgsBundle();
+
+        if (args.getString(PROC_LOAD_THRESHOLD) != null) {
+            mProcLoadHelper.setProcLoadThreshold(Double.parseDouble(args
+                    .getString(PROC_LOAD_THRESHOLD)));
+        }
+
+        if (args.getString(PROC_THRESHOLD_TIMEOUT) != null) {
+            mProcLoadHelper.setProcLoadWaitTimeInMs(Long.parseLong(args
+                    .getString(PROC_THRESHOLD_TIMEOUT)));
+        }
+
+        if (args.getString(PROC_LOAD_INTERVAL) != null) {
+            mProcLoadHelper.setProcLoadIntervalInMs(Long.parseLong(args
+                    .getString(PROC_LOAD_INTERVAL)));
+        }
+
+    }
+}
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/PwrStatsUtilListener.java b/libraries/device-collectors/src/main/java/android/device/collectors/PwrStatsUtilListener.java
new file mode 100644
index 0000000..d797b66
--- /dev/null
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/PwrStatsUtilListener.java
@@ -0,0 +1,33 @@
+/*
+ * 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.device.collectors.annotations.OptionClass;
+
+import com.android.helpers.PwrStatsUtilHelper;
+
+/**
+ * A {@link PwrStatsUtilListener} that captures power stats during the test method.
+ *
+ * <p>Do NOT throw exception anywhere in this class. We don't want to halt the test when metrics
+ * collection fails.
+ */
+@OptionClass(alias = "pwrstats-util-collector")
+public class PwrStatsUtilListener extends BaseCollectionListener<Long> {
+    public PwrStatsUtilListener() {
+        createHelperInstance(new PwrStatsUtilHelper());
+    }
+}
diff --git a/libraries/device-collectors/src/test/java/android/device/collectors/FreeMemListenerTest.java b/libraries/device-collectors/src/test/java/android/device/collectors/FreeMemListenerTest.java
new file mode 100644
index 0000000..c399125
--- /dev/null
+++ b/libraries/device-collectors/src/test/java/android/device/collectors/FreeMemListenerTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.Instrumentation;
+import android.os.Bundle;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.helpers.FreeMemHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Android Unit tests for {@link FreeMemListener}.
+ *
+ * To run:
+ * atest CollectorDeviceLibTest:android.device.collectors.FreeMemListenerTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class FreeMemListenerTest {
+
+    @Mock
+    private Instrumentation mInstrumentation;
+    @Mock
+    private FreeMemHelper mFreeMemHelper;
+
+    private FreeMemListener mListener;
+    private Description mRunDesc;
+    private Description mTest1Desc;
+    private DataRecord mDataRecord;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mRunDesc = Description.createSuiteDescription("run");
+        mTest1Desc = Description.createTestDescription("run", "test1");
+    }
+
+    private FreeMemListener initListener() {
+        FreeMemListener listener = new FreeMemListener(new Bundle(), mFreeMemHelper);
+        listener.setInstrumentation(mInstrumentation);
+        mDataRecord = listener.createDataRecord();
+        return listener;
+    }
+
+    @Test
+    public void testFreeMemHelperCalls() throws Exception {
+        mListener = initListener();
+        mListener.testRunStarted(mRunDesc);
+
+        // Test test start behavior
+        mListener.testStarted(mTest1Desc);
+        verify(mFreeMemHelper, times(1)).startCollecting();
+        mListener.onTestEnd(mDataRecord, mTest1Desc);
+        verify(mFreeMemHelper, times(1)).stopCollecting();
+    }
+}
diff --git a/libraries/device-collectors/src/test/java/android/device/collectors/ProcLoadListenerTest.java b/libraries/device-collectors/src/test/java/android/device/collectors/ProcLoadListenerTest.java
new file mode 100644
index 0000000..f90470e
--- /dev/null
+++ b/libraries/device-collectors/src/test/java/android/device/collectors/ProcLoadListenerTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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 android.device.collectors.ProcLoadListener.PROC_LOAD_THRESHOLD;
+import static android.device.collectors.ProcLoadListener.PROC_THRESHOLD_TIMEOUT;
+import static android.device.collectors.ProcLoadListener.PROC_LOAD_INTERVAL;
+
+
+import static org.mockito.Mockito.verify;
+
+import android.app.Instrumentation;
+import android.os.Bundle;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.helpers.ProcLoadHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Android Unit tests for {@link ProcLoadListener}.
+ *
+ * To run:
+ * atest CollectorDeviceLibTest:android.device.collectors.ProcLoadListenerTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class ProcLoadListenerTest {
+
+    @Mock
+    private Instrumentation mInstrumentation;
+    @Mock
+    private ProcLoadHelper mProcLoadHelper;
+
+    private ProcLoadListener mListener;
+    private Description mRunDesc;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mRunDesc = Description.createSuiteDescription("run");
+    }
+
+    private ProcLoadListener initListener(Bundle b) {
+        ProcLoadListener listener = new ProcLoadListener(b, mProcLoadHelper);
+        listener.setInstrumentation(mInstrumentation);
+        return listener;
+    }
+
+    @Test
+    public void testLoadProcAdditionalOptions() throws Exception {
+        Bundle b = new Bundle();
+        b.putString(PROC_LOAD_THRESHOLD, "1");
+        b.putString(PROC_THRESHOLD_TIMEOUT, "3");
+        b.putString(PROC_LOAD_INTERVAL, "2");
+        mListener = initListener(b);
+        mListener.testRunStarted(mRunDesc);
+
+        verify(mProcLoadHelper).setProcLoadThreshold(1);
+        verify(mProcLoadHelper).setProcLoadWaitTimeInMs(3L);
+        verify(mProcLoadHelper).setProcLoadIntervalInMs(2L);
+    }
+}
diff --git a/libraries/health/runners/longevity/platform/Android.bp b/libraries/health/runners/longevity/platform/Android.bp
index b183da4..8cc34e0 100644
--- a/libraries/health/runners/longevity/platform/Android.bp
+++ b/libraries/health/runners/longevity/platform/Android.bp
@@ -37,5 +37,5 @@
         "guava",
         "platform-test-composers",
     ],
-    sdk_version: "26",
+    min_sdk_version: "26",
 }
diff --git a/libraries/health/runners/longevity/platform/samples/Android.bp b/libraries/health/runners/longevity/platform/samples/Android.bp
index 3cea772..9c59016 100644
--- a/libraries/health/runners/longevity/platform/samples/Android.bp
+++ b/libraries/health/runners/longevity/platform/samples/Android.bp
@@ -43,7 +43,7 @@
 
 android_test {
     name: "LongevityPlatformLibSamples",
-    sdk_version: "24",
+    min_sdk_version: "24",
     static_libs: [
         "androidx.test.runner",
         "common-platform-scenarios",
diff --git a/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/LongevityClassRunner.java b/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/LongevityClassRunner.java
index 34382b2..a94e27c 100644
--- a/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/LongevityClassRunner.java
+++ b/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/LongevityClassRunner.java
@@ -46,11 +46,13 @@
  */
 public class LongevityClassRunner extends BlockJUnit4ClassRunner {
     @VisibleForTesting static final String FILTER_OPTION = "exclude-class";
-    @VisibleForTesting static final String ITERATION_SEP = "@";
+    @VisibleForTesting static final String ITERATION_SEP_OPTION = "iteration-separator";
+    @VisibleForTesting static final String ITERATION_SEP_DEFAULT = "@";
     // A constant to indicate that the iteration number is not set.
     @VisibleForTesting static final int ITERATION_NOT_SET = -1;
 
     private String[] mExcludedClasses;
+    private String mIterationSep = ITERATION_SEP_DEFAULT;
 
     private boolean mTestFailed = true;
     private boolean mTestAttempted = false;
@@ -68,6 +70,10 @@
                 args.containsKey(FILTER_OPTION)
                         ? args.getString(FILTER_OPTION).split(",")
                         : new String[] {};
+        mIterationSep =
+                args.containsKey(ITERATION_SEP_OPTION)
+                        ? args.getString(ITERATION_SEP_OPTION)
+                        : mIterationSep;
     }
 
     /** Set the iteration of the test that this runner is running. */
@@ -268,7 +274,7 @@
             return original;
         }
         return Description.createTestDescription(
-                String.join(ITERATION_SEP, original.getClassName(), String.valueOf(mIteration)),
+                String.join(mIterationSep, original.getClassName(), String.valueOf(mIteration)),
                 original.getMethodName());
     }
 }
diff --git a/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/LongevitySuite.java b/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/LongevitySuite.java
index 850a830..74a22ab 100644
--- a/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/LongevitySuite.java
+++ b/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/LongevitySuite.java
@@ -30,11 +30,13 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.test.InstrumentationRegistry;
 
+import java.lang.reflect.Field;
 import java.util.function.BiFunction;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.junit.internal.runners.ErrorReportingRunner;
 import org.junit.runner.Description;
 import org.junit.runner.Runner;
 import org.junit.runner.notification.RunNotifier;
@@ -138,6 +140,11 @@
             } catch (Throwable t) {
                 throw new InitializationError(t);
             }
+            // If a scenario is an ErrorReportingRunner, an InitializationError has occurred when
+            // initializing the runner. Throw out a new error with the causes.
+            if (runner instanceof ErrorReportingRunner) {
+                throw new InitializationError(getCauses((ErrorReportingRunner) runner));
+            }
             // All scenarios must extend BlockJUnit4ClassRunner.
             if (!(runner instanceof BlockJUnit4ClassRunner)) {
                 throw new InitializationError(
@@ -232,4 +239,30 @@
                 mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
         return batteryInfo.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
     }
+
+    /** Gets the first cause out of a {@link ErrorReportingRunner}. Also logs the rest. */
+    private static List<Throwable> getCauses(ErrorReportingRunner runner) {
+        // Reflection is used for this operation as the runner itself does not allow the errors
+        // to be read directly, and masks everything as an InitializationError in its description,
+        // which is not very useful.
+        // It is ok to throw RuntimeException here as we have already entered a failure state. It is
+        // helpful to know that a ErrorReportingRunner has occurred even if we can't decipher it.
+        try {
+            Field causesField = runner.getClass().getDeclaredField("causes");
+            causesField.setAccessible(true);
+            return (List<Throwable>) causesField.get(runner);
+        } catch (NoSuchFieldException e) {
+            throw new RuntimeException(
+                    String.format(
+                            "Unable to find a \"causes\" field in the ErrorReportingRunner %s.",
+                            runner.getDescription()),
+                    e);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(
+                    String.format(
+                            "Unable to access the \"causes\" field in the ErrorReportingRunner %s.",
+                            runner.getDescription()),
+                    e);
+        }
+    }
 }
diff --git a/libraries/health/runners/longevity/platform/tests/Android.bp b/libraries/health/runners/longevity/platform/tests/Android.bp
index 33d8587..a9e6a03 100644
--- a/libraries/health/runners/longevity/platform/tests/Android.bp
+++ b/libraries/health/runners/longevity/platform/tests/Android.bp
@@ -44,7 +44,7 @@
 android_test {
     name: "LongevityPlatformLibTests",
 
-    sdk_version: "26",
+    min_sdk_version: "26",
     static_libs: [
         "androidx.test.runner",
         "common-platform-scenarios",
diff --git a/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/LongevityClassRunnerTest.java b/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/LongevityClassRunnerTest.java
index 70669b4..4e8d76c 100644
--- a/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/LongevityClassRunnerTest.java
+++ b/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/LongevityClassRunnerTest.java
@@ -383,12 +383,15 @@
                 captor.getValue()
                         .getClassName()
                         .matches(
-                                String.join(LongevityClassRunner.ITERATION_SEP, "^.*", "[0-9]+$")));
+                                String.join(
+                                        LongevityClassRunner.ITERATION_SEP_DEFAULT,
+                                        "^.*",
+                                        "[0-9]+$")));
     }
 
-    /** Test that the runner reports iteration when set. */
+    /** Test that the runner reports iteration when set and the default separator was used. */
     @Test
-    public void testReportIteration_withIteration() throws Throwable {
+    public void testReportIteration_withIteration_withDefaultSeparator() throws Throwable {
         ArgumentCaptor<Description> captor = ArgumentCaptor.forClass(Description.class);
         RunNotifier notifier = mock(RunNotifier.class);
         mRunner = spy(new LongevityClassRunner(NoOpTest.class));
@@ -399,7 +402,27 @@
                 "Description class name should contain the iteration number.",
                 captor.getValue()
                         .getClassName()
-                        .matches(String.join(LongevityClassRunner.ITERATION_SEP, "^.*", "7$")));
+                        .matches(
+                                String.join(
+                                        LongevityClassRunner.ITERATION_SEP_DEFAULT, "^.*", "7$")));
+    }
+
+    /** Test that the runner reports iteration when set and a custom separator was supplied. */
+    @Test
+    public void testReportIteration_withIteration_withCustomSeparator() throws Throwable {
+        String sep = "--";
+        Bundle args = new Bundle();
+        args.putString(LongevityClassRunner.ITERATION_SEP_OPTION, sep);
+
+        ArgumentCaptor<Description> captor = ArgumentCaptor.forClass(Description.class);
+        RunNotifier notifier = mock(RunNotifier.class);
+        mRunner = spy(new LongevityClassRunner(NoOpTest.class, args));
+        mRunner.setIteration(7);
+        mRunner.run(notifier);
+        verify(notifier).fireTestStarted(captor.capture());
+        Assert.assertTrue(
+                "Description class name should contain the iteration number.",
+                captor.getValue().getClassName().matches(String.join(sep, "^.*", "7$")));
     }
 
     private List<FrameworkMethod> getMethodNameMatcher(String methodName) {
diff --git a/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/LongevitySuiteTest.java b/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/LongevitySuiteTest.java
index d50d536..4a53202 100644
--- a/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/LongevitySuiteTest.java
+++ b/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/LongevitySuiteTest.java
@@ -17,6 +17,7 @@
 
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -31,15 +32,19 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
+import org.junit.internal.runners.ErrorReportingRunner;
 import org.junit.runner.Runner;
 import org.junit.runner.RunWith;
 import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
 import org.junit.runners.JUnit4;
 import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.RunnerBuilder;
 import org.junit.runners.Suite.SuiteClasses;
 import org.mockito.Mockito;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -168,6 +173,37 @@
         Assert.assertEquals(runners.get(2).getIteration(), 2);
     }
 
+    /**
+     * Test that when a builder ends up being a {@link ErrorReportingRunner}, the underlying error
+     * is reported as an InitializationError.
+     */
+    @Test
+    public void testThrowingErrorReportingRunnerCauses() throws Throwable {
+        RunnerBuilder builder = mock(RunnerBuilder.class);
+        InitializationError expected =
+                new InitializationError(
+                        Arrays.asList(
+                                new RuntimeException("Cause 1"), new RuntimeException("Cause 2")));
+        when(builder.runnerForClass(IterationSuite.TestOne.class))
+                .thenReturn(new BlockJUnit4ClassRunner(IterationSuite.TestOne.class));
+        when(builder.runnerForClass(IterationSuite.TestTwo.class))
+                .thenReturn(new ErrorReportingRunner(IterationSuite.TestTwo.class, expected));
+        try {
+            mSuite =
+                    new LongevitySuite(
+                            IterationSuite.class,
+                            builder,
+                            mInstrumentation,
+                            mContext,
+                            new Bundle());
+            Assert.fail("An InitializationError should have been thrown");
+        } catch (InitializationError e) {
+            Assert.assertEquals(e.getCauses().size(), 2);
+            Assert.assertEquals(e.getCauses().get(0).getMessage(), "Cause 1");
+            Assert.assertEquals(e.getCauses().get(1).getMessage(), "Cause 2");
+        }
+    }
+
     /** Sample device-side test cases. */
     @RunWith(LongevitySuite.class)
     @SuiteClasses({
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 ce58081..472d7dd 100644
--- a/libraries/launcher-helper/src/android/support/test/launcherhelper/NexusLauncherStrategy.java
+++ b/libraries/launcher-helper/src/android/support/test/launcherhelper/NexusLauncherStrategy.java
@@ -57,7 +57,7 @@
      */
     @Override
     public BySelector getAllAppsSelector() {
-        return By.res(getSupportedLauncherPackage(), "apps_view");
+        return By.res(getSupportedLauncherPackage(), "apps_list_view");
     }
 
     /**
diff --git a/tests/health/scenarios/Android.bp b/tests/health/scenarios/Android.bp
index 3afce78..56e87e4 100644
--- a/tests/health/scenarios/Android.bp
+++ b/tests/health/scenarios/Android.bp
@@ -14,12 +14,15 @@
 
 java_library_static {
     name: "common-platform-scenarios",
-    sdk_version: "24",
+    min_sdk_version: "24",
     srcs: [ "src/**/*.java" ],
     libs: [
         "androidx.test.runner",
         "junit",
         "platform-test-options",
         "ub-uiautomator",
+        "app-helpers-handheld-interfaces",
+        "platform-test-rules",
+        "handheld-app-helpers"
     ],
 }
diff --git a/tests/health/scenarios/src/android/platform/test/scenario/businesscard/DismissDialogs.java b/tests/health/scenarios/src/android/platform/test/scenario/businesscard/DismissDialogs.java
new file mode 100644
index 0000000..b48dc36
--- /dev/null
+++ b/tests/health/scenarios/src/android/platform/test/scenario/businesscard/DismissDialogs.java
@@ -0,0 +1,56 @@
+/*
+ * 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.test.scenario.businesscard;
+
+import android.platform.helpers.HelperAccessor;
+import android.platform.helpers.IBusinessCardHelper;
+import android.platform.test.rule.NaturalOrientationRule;
+import android.platform.test.scenario.annotation.AppSetup;
+import android.platform.test.scenario.annotation.Scenario;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Dismisses initial dialogs. */
+@AppSetup
+@Scenario
+@RunWith(JUnit4.class)
+public class DismissDialogs {
+    // Class-level rules
+    @ClassRule public static NaturalOrientationRule orientationRule = new NaturalOrientationRule();
+
+    private static HelperAccessor<IBusinessCardHelper> sHelper =
+            new HelperAccessor<>(IBusinessCardHelper.class);
+
+    @BeforeClass
+    public static void openApp() {
+        sHelper.get().open();
+    }
+
+    @Test
+    public void testDismissDialogs() {
+        sHelper.get().dismissInitialDialogs();
+    }
+
+    @AfterClass
+    public static void closeApp() {
+        sHelper.get().exit();
+    }
+}
diff --git a/tests/health/scenarios/src/android/platform/test/scenario/businesscard/OpenApp.java b/tests/health/scenarios/src/android/platform/test/scenario/businesscard/OpenApp.java
new file mode 100644
index 0000000..a9b8473
--- /dev/null
+++ b/tests/health/scenarios/src/android/platform/test/scenario/businesscard/OpenApp.java
@@ -0,0 +1,48 @@
+/*
+ * 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.test.scenario.businesscard;
+
+import android.platform.helpers.HelperAccessor;
+import android.platform.helpers.IBusinessCardHelper;
+import android.platform.test.rule.NaturalOrientationRule;
+import android.platform.test.scenario.annotation.Scenario;
+
+import org.junit.AfterClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Opens the Business Card application and exits after. */
+@Scenario
+@RunWith(JUnit4.class)
+public class OpenApp {
+    // Class-level rules
+    @ClassRule public static NaturalOrientationRule orientationRule = new NaturalOrientationRule();
+
+    private static HelperAccessor<IBusinessCardHelper> sHelper =
+            new HelperAccessor<>(IBusinessCardHelper.class);
+
+    @Test
+    public void testOpen() {
+        sHelper.get().open();
+    }
+
+    @AfterClass
+    public static void closeApp() {
+        sHelper.get().exit();
+    }
+}
diff --git a/tests/health/scenarios/src/android/platform/test/scenario/performancelaunch/DismissDialogs.java b/tests/health/scenarios/src/android/platform/test/scenario/performancelaunch/DismissDialogs.java
new file mode 100644
index 0000000..59d3f4d
--- /dev/null
+++ b/tests/health/scenarios/src/android/platform/test/scenario/performancelaunch/DismissDialogs.java
@@ -0,0 +1,56 @@
+/*
+ * 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.test.scenario.performancelaunch;
+
+import android.platform.helpers.HelperAccessor;
+import android.platform.helpers.IPerformanceLaunchHelper;
+import android.platform.test.rule.NaturalOrientationRule;
+import android.platform.test.scenario.annotation.AppSetup;
+import android.platform.test.scenario.annotation.Scenario;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Dismisses initial dialogs. */
+@AppSetup
+@Scenario
+@RunWith(JUnit4.class)
+public class DismissDialogs {
+    // Class-level rules
+    @ClassRule public static NaturalOrientationRule orientationRule = new NaturalOrientationRule();
+
+    private static HelperAccessor<IPerformanceLaunchHelper> sHelper =
+            new HelperAccessor<>(IPerformanceLaunchHelper.class);
+
+    @BeforeClass
+    public static void openApp() {
+        sHelper.get().open();
+    }
+
+    @Test
+    public void testDismissDialogs() {
+        sHelper.get().dismissInitialDialogs();
+    }
+
+    @AfterClass
+    public static void closeApp() {
+        sHelper.get().exit();
+    }
+}
diff --git a/tests/health/scenarios/src/android/platform/test/scenario/performancelaunch/OpenApp.java b/tests/health/scenarios/src/android/platform/test/scenario/performancelaunch/OpenApp.java
new file mode 100644
index 0000000..ca4b835
--- /dev/null
+++ b/tests/health/scenarios/src/android/platform/test/scenario/performancelaunch/OpenApp.java
@@ -0,0 +1,48 @@
+/*
+ * 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.test.scenario.performancelaunch;
+
+import android.platform.helpers.HelperAccessor;
+import android.platform.helpers.IPerformanceLaunchHelper;
+import android.platform.test.rule.NaturalOrientationRule;
+import android.platform.test.scenario.annotation.Scenario;
+
+import org.junit.AfterClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Opens the Performance Launch application and exits after. */
+@Scenario
+@RunWith(JUnit4.class)
+public class OpenApp {
+    // Class-level rules
+    @ClassRule public static NaturalOrientationRule orientationRule = new NaturalOrientationRule();
+
+    private static HelperAccessor<IPerformanceLaunchHelper> sHelper =
+            new HelperAccessor<>(IPerformanceLaunchHelper.class);
+
+    @Test
+    public void testOpen() {
+        sHelper.get().open();
+    }
+
+    @AfterClass
+    public static void closeApp() {
+        sHelper.get().exit();
+    }
+}
diff --git a/tests/health/scenarios/tests/Android.bp b/tests/health/scenarios/tests/Android.bp
index a108c82..af33979 100644
--- a/tests/health/scenarios/tests/Android.bp
+++ b/tests/health/scenarios/tests/Android.bp
@@ -53,6 +53,8 @@
         "platform-test-composers",
         "platform-test-options",
         "platform-test-rules",
+        "handheld-app-helpers",
+        "launcher-helper-lib",
     ],
     srcs: ["src/**/*.java"],
     test_suites: ["device-tests"],