Snap for 9266841 from 9d145a6def0dbbf56402c11246eba0f22066a417 to mainline-tzdata4-release

Change-Id: I7dc1d52a01bc8cde274659a5a96b34a2fd3198c7
diff --git a/build/tasks/tests/platform_test_list.mk b/build/tasks/tests/platform_test_list.mk
index b395fc5..5723c27 100644
--- a/build/tasks/tests/platform_test_list.mk
+++ b/build/tasks/tests/platform_test_list.mk
@@ -83,6 +83,7 @@
     PermissionUtils \
     PlatformCommonScenarioTests \
     PowerPerfTest \
+    SdkSandboxPerfScenarioTests \
     SettingsUITests \
     SimpleServiceTestApp1 \
     SimpleServiceTestApp2 \
diff --git a/libraries/app-helpers/interfaces/common/src/android/platform/helpers/IYouTubeHelper.java b/libraries/app-helpers/interfaces/common/src/android/platform/helpers/IYouTubeHelper.java
index 6fd7e71..faa1035 100644
--- a/libraries/app-helpers/interfaces/common/src/android/platform/helpers/IYouTubeHelper.java
+++ b/libraries/app-helpers/interfaces/common/src/android/platform/helpers/IYouTubeHelper.java
@@ -243,25 +243,4 @@
      * @return true if pip mode in launhcer.
      */
     public boolean isYouTubePipModeOnLauncher();
-
-    /**
-     * Setup expectation: YouTube is open and the video player is visible on any page.
-     *
-     * @return true if the video is playing.
-     */
-    public boolean isVideoPlaying();
-
-    /**
-     * Setup expectation: YouTube app is open.
-     *
-     * @return true if the floaty player is visible.
-     */
-    public boolean isFloatyPlayerVisible();
-
-    /**
-     * Setup expectation: YouTube is open and the floaty player is visible.
-     *
-     * <p>Clicks the floaty bar's close button to close video player.
-     */
-    public void closeFloatyPlayer();
 }
diff --git a/libraries/collectors-helper/adservices/src/com/android/helpers/JSScriptEngineLatencyHelper.java b/libraries/collectors-helper/adservices/src/com/android/helpers/JSScriptEngineLatencyHelper.java
index 0aba674..f2d1af5 100644
--- a/libraries/collectors-helper/adservices/src/com/android/helpers/JSScriptEngineLatencyHelper.java
+++ b/libraries/collectors-helper/adservices/src/com/android/helpers/JSScriptEngineLatencyHelper.java
@@ -16,8 +16,6 @@
 
 package com.android.helpers;
 
-import android.util.Log;
-
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableMap;
 
@@ -26,136 +24,93 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.time.Clock;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.function.Supplier;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-public class JSScriptEngineLatencyHelper implements ICollectorHelper<Long> {
-    @VisibleForTesting public static final String SANDBOX_INIT_TIME_AVG = "SANDBOX_INIT_TIME_AVG";
-    public static final String ISOLATE_CREATE_TIME_AVG = "ISOLATE_CREATE_TIME_AVG";
-    public static final String WEBVIEW_EXECUTION_TIME_AVG = "WEBVIEW_EXECUTION_TIME_AVG";
-    public static final String JAVA_EXECUTION_TIME_AVG = "JAVA_EXECUTION_TIME_AVG";
-    public static final String NUM_ITERATIONS = "NUM_ITERATIONS";
+public final class JSScriptEngineLatencyHelper {
 
-    private static final String TAG = JSScriptEngineLatencyHelper.class.getSimpleName();
-    private static final DateTimeFormatter LOG_TIME_FORMATTER =
-            DateTimeFormatter.ofPattern("MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
-
-    private static final Pattern sLatencyMetricPattern =
-            Pattern.compile("JSScriptEngine: \\((.*): (\\d+)\\)");
-    private static final String SANDBOX_INIT_TIME = "SANDBOX_INIT_TIME";
-    private static final String ISOLATE_CREATE_TIME = "ISOLATE_CREATE_TIME";
-    private static final String WEBVIEW_EXECUTION_TIME = "WEBVIEW_EXECUTION_TIME";
-    private static final String JAVA_EXECUTION_TIME = "JAVA_EXECUTION_TIME";
-
-    private Instant instant;
-    private final Clock mClock;
-    private final Supplier<MetricsEventStreamReader> metricsEventStreamSupplier;
-
-    public JSScriptEngineLatencyHelper() {
-        mClock = Clock.systemUTC();
-        metricsEventStreamSupplier = () -> new MetricsEventStreamReader();
+    public static LatencyHelper getLogcatCollector() {
+        return LatencyHelper.getLogcatLatencyHelper(
+                new JSScriptEngineProcessInputForLatencyMetrics());
     }
 
     @VisibleForTesting
-    public JSScriptEngineLatencyHelper(
-            Supplier<MetricsEventStreamReader> metricsEventStreamSupplier, Clock clock) {
-        this.metricsEventStreamSupplier = metricsEventStreamSupplier;
-        mClock = clock;
+    public static LatencyHelper getCollector(
+            LatencyHelper.InputStreamFilter inputStreamFilter, Clock clock) {
+        return new LatencyHelper(
+                new JSScriptEngineProcessInputForLatencyMetrics(), inputStreamFilter, clock);
     }
 
-    @Override
-    public boolean startCollecting() {
-        instant = mClock.instant();
-        return true;
+    private static class JSScriptEngineProcessInputForLatencyMetrics
+            implements LatencyHelper.ProcessInputForLatencyMetrics {
+        private static final String SANDBOX_INIT_TIME_AVG = "SANDBOX_INIT_TIME_AVG";
+        private static final String ISOLATE_CREATE_TIME_AVG = "ISOLATE_CREATE_TIME_AVG";
+        private static final String WEBVIEW_EXECUTION_TIME_AVG = "WEBVIEW_EXECUTION_TIME_AVG";
+        private static final String JAVA_EXECUTION_TIME_AVG = "JAVA_EXECUTION_TIME_AVG";
+        private static final String NUM_ITERATIONS = "NUM_ITERATIONS";
+
+        private static final String SANDBOX_INIT_TIME = "SANDBOX_INIT_TIME";
+        private static final String ISOLATE_CREATE_TIME = "ISOLATE_CREATE_TIME";
+        private static final String WEBVIEW_EXECUTION_TIME = "WEBVIEW_EXECUTION_TIME";
+        private static final String JAVA_EXECUTION_TIME = "JAVA_EXECUTION_TIME";
+
+        @Override
+        public String getTestLabel() {
+            return "JSScriptEngine";
     }
 
-    @Override
-    public Map<String, Long> getMetrics() {
-        try {
-            return processOutput(metricsEventStreamSupplier.get().getMetricsEvents(instant));
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to collect JSScriptEngine metrics.", e);
+        @Override
+        public Map<String, Long> processInput(InputStream inputStream) throws IOException {
+            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
+            Pattern latencyMetricPattern = Pattern.compile(getTestLabel() + ": \\((.*): (\\d+)\\)");
+
+            List<Long> sandboxInitTimes = new ArrayList<>();
+            List<Long> isolateCreateTimes = new ArrayList<>();
+            List<Long> javaProcessTimes = new ArrayList<>();
+            List<Long> webviewProcessTimes = new ArrayList<>();
+
+            String line = "";
+            while ((line = bufferedReader.readLine()) != null) {
+                Matcher matcher = latencyMetricPattern.matcher(line);
+                while (matcher.find()) {
+                    /**
+                     * The lines from Logcat will look like: 06-13 18:09:24.058 20765 20781 D
+                     * JSScriptEngine: (JAVA_PROCESS_TIME: 43)
+                     */
+                    String metric = matcher.group(1);
+                    long latency = Long.parseLong(matcher.group(2));
+                    if (SANDBOX_INIT_TIME.equals(metric)) {
+                        sandboxInitTimes.add(latency);
+                    } else if (ISOLATE_CREATE_TIME.equals(metric)) {
+                        isolateCreateTimes.add(latency);
+                    } else if (JAVA_EXECUTION_TIME.equals(metric)) {
+                        javaProcessTimes.add(latency);
+                    } else if (WEBVIEW_EXECUTION_TIME.equals(metric)) {
+                        webviewProcessTimes.add(latency);
+                    }
         }
-
-        return Collections.emptyMap();
-    }
-
-    private Map<String, Long> processOutput(InputStream inputStream) throws IOException {
-        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
-
-        List<Long> sandboxInitTimes = new ArrayList<>();
-        List<Long> isolateCreateTimes = new ArrayList<>();
-        List<Long> javaProcessTimes = new ArrayList<>();
-        List<Long> webviewProcessTimes = new ArrayList<>();
-
-        String line = "";
-        while ((line = bufferedReader.readLine()) != null) {
-            Matcher matcher = sLatencyMetricPattern.matcher(line);
-            while (matcher.find()) {
-                /**
-                 * The lines from Logcat will look like: 06-13 18:09:24.058 20765 20781 D
-                 * JSScriptEngine: (JAVA_PROCESS_TIME: 43)
-                 */
-                String metric = matcher.group(1);
-                long latency = Long.parseLong(matcher.group(2));
-                if (SANDBOX_INIT_TIME.equals(metric)) {
-                    sandboxInitTimes.add(latency);
-                } else if (ISOLATE_CREATE_TIME.equals(metric)) {
-                    isolateCreateTimes.add(latency);
-                } else if (JAVA_EXECUTION_TIME.equals(metric)) {
-                    javaProcessTimes.add(latency);
-                } else if (WEBVIEW_EXECUTION_TIME.equals(metric)) {
-                    webviewProcessTimes.add(latency);
-                }
             }
-        }
 
-        // Just getting average for now.
-        int defaultMetricVal = 0;
-        long sandboxInitTimeAvg = getAverage(sandboxInitTimes, defaultMetricVal);
-        long isolateCreateTimeAvg = getAverage(isolateCreateTimes, defaultMetricVal);
-        long webviewProcessTimeAvg = getAverage(webviewProcessTimes, defaultMetricVal);
-        long javaProcessTimeAvg = getAverage(javaProcessTimes, defaultMetricVal);
+            // Just getting average for now.
+            int defaultMetricVal = 0;
+            long sandboxInitTimeAvg = getAverage(sandboxInitTimes, defaultMetricVal);
+            long isolateCreateTimeAvg = getAverage(isolateCreateTimes, defaultMetricVal);
+            long webviewProcessTimeAvg = getAverage(webviewProcessTimes, defaultMetricVal);
+            long javaProcessTimeAvg = getAverage(javaProcessTimes, defaultMetricVal);
 
-        return ImmutableMap.of(
-                SANDBOX_INIT_TIME_AVG, sandboxInitTimeAvg,
-                ISOLATE_CREATE_TIME_AVG, isolateCreateTimeAvg,
-                WEBVIEW_EXECUTION_TIME_AVG, webviewProcessTimeAvg,
-                JAVA_EXECUTION_TIME_AVG, javaProcessTimeAvg,
-                NUM_ITERATIONS, (long) javaProcessTimes.size());
+            return ImmutableMap.of(
+                    SANDBOX_INIT_TIME_AVG, sandboxInitTimeAvg,
+                    ISOLATE_CREATE_TIME_AVG, isolateCreateTimeAvg,
+                    WEBVIEW_EXECUTION_TIME_AVG, webviewProcessTimeAvg,
+                    JAVA_EXECUTION_TIME_AVG, javaProcessTimeAvg,
+                    NUM_ITERATIONS, (long) javaProcessTimes.size());
     }
 
     private Long getAverage(List<Long> list, long defaultValue) {
-        return (long) list.stream().mapToDouble(d -> d).average().orElse(defaultValue);
+            return (long) list.stream().mapToDouble(d -> d).average().orElse(defaultValue);
     }
-
-    @Override
-    public boolean stopCollecting() {
-        return true;
-    }
-
-    @VisibleForTesting
-    public static class MetricsEventStreamReader {
-        /** Return JSScriptEngine logs that will be used to build the test metrics. */
-        public InputStream getMetricsEvents(Instant startTime) throws IOException {
-            ProcessBuilder pb =
-                    new ProcessBuilder(
-                            Arrays.asList(
-                                    "logcat",
-                                    "-s",
-                                    "JSScriptEngine:D",
-                                    "-t",
-                                    LOG_TIME_FORMATTER.format(startTime)));
-            return pb.start().getInputStream();
-        }
     }
 }
diff --git a/libraries/collectors-helper/adservices/src/com/android/helpers/LatencyHelper.java b/libraries/collectors-helper/adservices/src/com/android/helpers/LatencyHelper.java
new file mode 100644
index 0000000..69c7c5c
--- /dev/null
+++ b/libraries/collectors-helper/adservices/src/com/android/helpers/LatencyHelper.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.helpers;
+
+import android.util.Log;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+
+public class LatencyHelper implements ICollectorHelper<Long> {
+    private static final String TAG = LatencyHelper.class.getSimpleName();
+    private static final DateTimeFormatter LOG_TIME_FORMATTER =
+            DateTimeFormatter.ofPattern("MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
+    private final Clock mClock;
+    private final ProcessInputForLatencyMetrics mEventStreamProcessor;
+    private InputStreamFilter mInputStreamFilter;
+    private Instant mInstant;
+
+    public LatencyHelper(
+            ProcessInputForLatencyMetrics eventStreamProcessor,
+            InputStreamFilter inputStreamFilter) {
+        mEventStreamProcessor = eventStreamProcessor;
+        mInputStreamFilter = inputStreamFilter;
+        mClock = Clock.systemUTC();
+    }
+
+    @VisibleForTesting
+    public LatencyHelper(
+            ProcessInputForLatencyMetrics eventStreamProcessor,
+            InputStreamFilter inputStreamFilter,
+            Clock clock) {
+        mEventStreamProcessor = eventStreamProcessor;
+        mInputStreamFilter = inputStreamFilter;
+        mClock = clock;
+    }
+
+    public static LatencyHelper getLogcatLatencyHelper(
+            ProcessInputForLatencyMetrics eventStreamProcessor) {
+        return new LatencyHelper(eventStreamProcessor, new LogcatStreamFilter());
+    }
+
+    @Override
+    public boolean startCollecting() {
+        mInstant = mClock.instant();
+        return true;
+    }
+
+    @Override
+    public Map<String, Long> getMetrics() {
+        try {
+            return mEventStreamProcessor.processInput(
+                    mInputStreamFilter.getStream(mEventStreamProcessor.getTestLabel(), mInstant));
+        } catch (Exception e) {
+            Log.e(
+                    TAG,
+                    "Failed to collect " + mEventStreamProcessor.getTestLabel() + " metrics.",
+                    e);
+        }
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public boolean stopCollecting() {
+        return true;
+    }
+
+    public interface ProcessInputForLatencyMetrics {
+        String getTestLabel();
+
+        Map<String, Long> processInput(InputStream inputStream) throws IOException;
+    }
+
+    public interface InputStreamFilter {
+        InputStream getStream(String filterLabel, Instant startTime) throws IOException;
+    }
+
+    public static class LogcatStreamFilter implements InputStreamFilter {
+        @Override
+        public InputStream getStream(String filterLabel, Instant startTime) throws IOException {
+            ProcessBuilder pb =
+                    new ProcessBuilder(
+                            Arrays.asList(
+                                    "logcat",
+                                    "-s",
+                                    filterLabel + ":D",
+                                    "-t",
+                                    LOG_TIME_FORMATTER.format(startTime)));
+            return pb.start().getInputStream();
+        }
+    }
+}
diff --git a/libraries/collectors-helper/adservices/src/com/android/helpers/SelectAdsLatencyHelper.java b/libraries/collectors-helper/adservices/src/com/android/helpers/SelectAdsLatencyHelper.java
new file mode 100644
index 0000000..884acc5
--- /dev/null
+++ b/libraries/collectors-helper/adservices/src/com/android/helpers/SelectAdsLatencyHelper.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.helpers;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.time.Clock;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class SelectAdsLatencyHelper {
+
+    public static LatencyHelper getLogcatCollector() {
+        return LatencyHelper.getLogcatLatencyHelper(new SelectAdsProcessInputForLatencyMetrics());
+    }
+
+    @VisibleForTesting
+    public static LatencyHelper getCollector(
+            LatencyHelper.InputStreamFilter inputStreamFilter, Clock clock) {
+        return new LatencyHelper(
+                new SelectAdsProcessInputForLatencyMetrics(), inputStreamFilter, clock);
+    }
+
+    private static class SelectAdsProcessInputForLatencyMetrics
+            implements LatencyHelper.ProcessInputForLatencyMetrics {
+        private static final String LOG_LABEL_P50_5G = "SELECT_ADS_LATENCY_P50_5G";
+        private static final String LOG_LABEL_P50_4GPLUS = "SELECT_ADS_LATENCY_P50_4GPLUS";
+        private static final String LOG_LABEL_P50_4G = "SELECT_ADS_LATENCY_P50_4G";
+        private static final String LOG_LABEL_P90_5G = "SELECT_ADS_LATENCY_P90_5G";
+        private static final String LOG_LABEL_P90_4GPLUS = "SELECT_ADS_LATENCY_P90_4GPLUS";
+        private static final String LOG_LABEL_P90_4G = "SELECT_ADS_LATENCY_P90_4G";
+
+        @Override
+        public String getTestLabel() {
+            return "SelectAds";
+        }
+
+        @Override
+        public Map<String, Long> processInput(InputStream inputStream) throws IOException {
+            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
+            Pattern latencyMetricPattern =
+                    Pattern.compile(getTestLabel() + ": \\((.*): (\\d+) ms\\)");
+
+            String line = "";
+            Map<String, Long> output = new HashMap<String, Long>();
+            while ((line = bufferedReader.readLine()) != null) {
+                Matcher matcher = latencyMetricPattern.matcher(line);
+                while (matcher.find()) {
+                    /**
+                     * The lines from Logcat will look like: 06-13 18:09:24.058 20765 20781 D
+                     * SelectAds: (SELECT_ADS_LATENCY_P50_5G: 1900 ms)
+                     */
+                    String metric = matcher.group(1);
+                    long latency = Long.parseLong(matcher.group(2));
+                    switch (metric) {
+                        case LOG_LABEL_P50_5G:
+                            output.put(LOG_LABEL_P50_5G, latency);
+                            break;
+                        case LOG_LABEL_P50_4GPLUS:
+                            output.put(LOG_LABEL_P50_4GPLUS, latency);
+                            break;
+                        case LOG_LABEL_P50_4G:
+                            output.put(LOG_LABEL_P50_4G, latency);
+                            break;
+                        case LOG_LABEL_P90_5G:
+                            output.put(LOG_LABEL_P90_5G, latency);
+                            break;
+                        case LOG_LABEL_P90_4GPLUS:
+                            output.put(LOG_LABEL_P90_4GPLUS, latency);
+                            break;
+                        case LOG_LABEL_P90_4G:
+                            output.put(LOG_LABEL_P90_4G, latency);
+                            break;
+                    }
+                }
+            }
+            return output;
+        }
+    }
+}
diff --git a/libraries/collectors-helper/adservices/src/com/android/helpers/TopicsLatencyHelper.java b/libraries/collectors-helper/adservices/src/com/android/helpers/TopicsLatencyHelper.java
index e7d14ac..05b07f4 100644
--- a/libraries/collectors-helper/adservices/src/com/android/helpers/TopicsLatencyHelper.java
+++ b/libraries/collectors-helper/adservices/src/com/android/helpers/TopicsLatencyHelper.java
@@ -16,121 +16,70 @@
 
 package com.android.helpers;
 
-import android.util.Log;
+import com.google.common.annotations.VisibleForTesting;
 
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.time.Clock;
-import java.time.Instant;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Map;
 import java.util.HashMap;
-import java.util.function.Supplier;
+import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.time.format.DateTimeFormatter;
-import java.time.ZoneId;
-
-import com.google.common.annotations.VisibleForTesting;
 
 /**
  * TopicsLatencyHelper consist of helper methods to collect Topics API call latencies
  *
  * <p>TODO(b/234452723): Change metric collector to use either statsd or perfetto instead of logcat
  */
-public class TopicsLatencyHelper implements ICollectorHelper<Long> {
+public class TopicsLatencyHelper {
 
-    private static final String TAG = "TopicsLatencyHelper";
-
-    private static final String TOPICS_HOT_START_LATENCY_METRIC = "TOPICS_HOT_START_LATENCY_METRIC";
-    private static final String TOPICS_COLD_START_LATENCY_METRIC =
-            "TOPICS_COLD_START_LATENCY_METRIC";
-
-    private static final DateTimeFormatter LOG_TIME_FORMATTER =
-            DateTimeFormatter.ofPattern("MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
-
-    private static final Pattern sLatencyMetricPattern =
-            Pattern.compile("GetTopicsApiCall: \\((.*): (\\d+)\\)");
-
-    private Instant mInstant;
-    private final Clock mClock;
-    private final Supplier<MetricsEventStreamReader> mMetricsEventStreamReaderSupplier;
-
-    public TopicsLatencyHelper() {
-        mClock = Clock.systemUTC();
-        mMetricsEventStreamReaderSupplier = () -> new MetricsEventStreamReader();
+    public static LatencyHelper getLogcatCollector() {
+        return LatencyHelper.getLogcatLatencyHelper(new TopicsProcessInputForLatencyMetrics());
     }
 
     @VisibleForTesting
-    public TopicsLatencyHelper(
-            Supplier<MetricsEventStreamReader> metricsEventStreamSupplier, Clock clock) {
-        this.mMetricsEventStreamReaderSupplier = metricsEventStreamSupplier;
-        mClock = clock;
+    public static LatencyHelper getCollector(LatencyHelper.InputStreamFilter inputStreamFilter) {
+        return new LatencyHelper(new TopicsProcessInputForLatencyMetrics(), inputStreamFilter);
     }
 
-    @Override
-    public boolean startCollecting() {
-        mInstant = mClock.instant();
-        return true;
+    private static class TopicsProcessInputForLatencyMetrics
+            implements LatencyHelper.ProcessInputForLatencyMetrics {
+
+        private static final String TOPICS_HOT_START_LATENCY_METRIC =
+                "TOPICS_HOT_START_LATENCY_METRIC";
+        private static final String TOPICS_COLD_START_LATENCY_METRIC =
+                "TOPICS_COLD_START_LATENCY_METRIC";
+
+        @Override
+        public String getTestLabel() {
+            return "GetTopicsApiCall";
     }
 
-    @Override
-    public Map<String, Long> getMetrics() {
-        try {
-            return processOutput(
-                    mMetricsEventStreamReaderSupplier.get().getMetricsEvents(mInstant));
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to collect TopicsManager metrics.", e);
+        @Override
+        public Map<String, Long> processInput(InputStream inputStream) throws IOException {
+            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
+            Pattern latencyMetricPattern = Pattern.compile(getTestLabel() + ": \\((.*): (\\d+)\\)");
+
+            String line = "";
+            Map<String, Long> output = new HashMap<String, Long>();
+            while ((line = bufferedReader.readLine()) != null) {
+                Matcher matcher = latencyMetricPattern.matcher(line);
+                while (matcher.find()) {
+                    /**
+                     * The lines from Logcat will look like: 06-13 18:09:24.058 20765 20781 D
+                     * GetTopicsApiCall: (TOPICS_HOT_START_LATENCY_METRIC: 14)
+                     */
+                    String metric = matcher.group(1);
+                    long latency = Long.parseLong(matcher.group(2));
+                    if (TOPICS_HOT_START_LATENCY_METRIC.equals(metric)) {
+                        output.put(TOPICS_HOT_START_LATENCY_METRIC, latency);
+                    } else if (TOPICS_COLD_START_LATENCY_METRIC.equals(metric)) {
+                        output.put(TOPICS_COLD_START_LATENCY_METRIC, latency);
+                    }
         }
-
-        return Collections.emptyMap();
-    }
-
-    private Map<String, Long> processOutput(InputStream inputStream) throws IOException {
-        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
-
-        String line = "";
-        Map<String, Long> output = new HashMap<String, Long>();
-        while ((line = bufferedReader.readLine()) != null) {
-            Matcher matcher = sLatencyMetricPattern.matcher(line);
-            while (matcher.find()) {
-                /**
-                 * The lines from Logcat will look like: 06-13 18:09:24.058 20765 20781 D
-                 * GetTopicsApiCall: (TOPICS_HOT_START_LATENCY_METRIC: 14)
-                 */
-                String metric = matcher.group(1);
-                long latency = Long.parseLong(matcher.group(2));
-                if (TOPICS_HOT_START_LATENCY_METRIC.equals(metric)) {
-                    output.put(TOPICS_HOT_START_LATENCY_METRIC, latency);
-                } else if (TOPICS_COLD_START_LATENCY_METRIC.equals(metric)) {
-                    output.put(TOPICS_COLD_START_LATENCY_METRIC, latency);
-                }
             }
-        }
-        return output;
+            return output;
     }
-
-    @Override
-    public boolean stopCollecting() {
-        return true;
-    }
-
-    @VisibleForTesting
-    public static class MetricsEventStreamReader {
-        /** Return GetTopicsApiCall logs that will be used to build the test metrics. */
-        public InputStream getMetricsEvents(Instant startTime) throws IOException {
-            ProcessBuilder pb =
-                    new ProcessBuilder(
-                            Arrays.asList(
-                                    "logcat",
-                                    "-s",
-                                    "GetTopicsApiCall:D",
-                                    "-t",
-                                    LOG_TIME_FORMATTER.format(startTime)));
-            return pb.start().getInputStream();
-        }
     }
 }
diff --git a/libraries/collectors-helper/adservices/test/src/com/android/helpers/JSScriptEngineLatencyHelperTest.java b/libraries/collectors-helper/adservices/test/src/com/android/helpers/JSScriptEngineLatencyHelperTest.java
index bb952f3..63835e2 100644
--- a/libraries/collectors-helper/adservices/test/src/com/android/helpers/JSScriptEngineLatencyHelperTest.java
+++ b/libraries/collectors-helper/adservices/test/src/com/android/helpers/JSScriptEngineLatencyHelperTest.java
@@ -36,11 +36,18 @@
  * To run, use atest CollectorsHelperAospTest:com.android.helpers.JSScriptEngineLatencyHelperTest
  */
 public class JSScriptEngineLatencyHelperTest {
-    @Mock private JSScriptEngineLatencyHelper.MetricsEventStreamReader mMetricsEventStreamReader;
+    private static final String TEST_FILTER_LABEL = "JSScriptEngine";
+    private static final String SANDBOX_INIT_TIME_AVG = "SANDBOX_INIT_TIME_AVG";
+    private static final String ISOLATE_CREATE_TIME_AVG = "ISOLATE_CREATE_TIME_AVG";
+    private static final String WEBVIEW_EXECUTION_TIME_AVG = "WEBVIEW_EXECUTION_TIME_AVG";
+    private static final String JAVA_EXECUTION_TIME_AVG = "JAVA_EXECUTION_TIME_AVG";
+    private static final String NUM_ITERATIONS = "NUM_ITERATIONS";
+
+    @Mock private LatencyHelper.InputStreamFilter mInputStreamFilter;
 
     @Mock private Clock mClock;
 
-    private JSScriptEngineLatencyHelper mJSScriptEngineLatencyHelper;
+    private LatencyHelper mJSScriptEngineLatencyHelper;
     private Instant mInstant = Clock.systemUTC().instant();
 
     @Before
@@ -49,7 +56,7 @@
         when(mClock.getZone()).thenReturn(ZoneId.of("UTC")); // Used by DateTimeFormatter.
         when(mClock.instant()).thenReturn(mInstant);
         mJSScriptEngineLatencyHelper =
-                new JSScriptEngineLatencyHelper(() -> mMetricsEventStreamReader, mClock);
+                JSScriptEngineLatencyHelper.getCollector(mInputStreamFilter, mClock);
         mJSScriptEngineLatencyHelper.startCollecting();
     }
 
@@ -65,24 +72,20 @@
                     + "06-13 18:09:24.152 20765 D JSScriptEngine: (JAVA_EXECUTION_TIME: 19)\n"
                     + "06-29 02:47:31.824 31075 D JSScriptEngine: (WEBVIEW_EXECUTION_TIME: 29)";
 
-        when(mMetricsEventStreamReader.getMetricsEvents(mInstant))
+        when(mInputStreamFilter.getStream(TEST_FILTER_LABEL, mInstant))
                 .thenReturn(new ByteArrayInputStream(logcatOutput.getBytes()));
         Map<String, Long> actual = mJSScriptEngineLatencyHelper.getMetrics();
 
-        assertThat(actual.get(JSScriptEngineLatencyHelper.SANDBOX_INIT_TIME_AVG))
-                .isEqualTo((102 + 45) / 2);
-        assertThat(actual.get(JSScriptEngineLatencyHelper.ISOLATE_CREATE_TIME_AVG))
-                .isEqualTo((1 + 1) / 2);
-        assertThat(actual.get(JSScriptEngineLatencyHelper.JAVA_EXECUTION_TIME_AVG))
-                .isEqualTo((43 + 19) / 2);
-        assertThat(actual.get(JSScriptEngineLatencyHelper.WEBVIEW_EXECUTION_TIME_AVG))
-                .isEqualTo((23 + 29) / 2);
-        assertThat(actual.get(JSScriptEngineLatencyHelper.NUM_ITERATIONS)).isEqualTo(2);
+        assertThat(actual.get(SANDBOX_INIT_TIME_AVG)).isEqualTo((102 + 45) / 2);
+        assertThat(actual.get(ISOLATE_CREATE_TIME_AVG)).isEqualTo((1 + 1) / 2);
+        assertThat(actual.get(JAVA_EXECUTION_TIME_AVG)).isEqualTo((43 + 19) / 2);
+        assertThat(actual.get(WEBVIEW_EXECUTION_TIME_AVG)).isEqualTo((23 + 29) / 2);
+        assertThat(actual.get(NUM_ITERATIONS)).isEqualTo(2);
     }
 
     @Test
     public void testWithEmptyLogcat() throws IOException {
-        when(mMetricsEventStreamReader.getMetricsEvents(mInstant))
+        when(mInputStreamFilter.getStream(TEST_FILTER_LABEL, mInstant))
                 .thenReturn(new ByteArrayInputStream("".getBytes()));
         Map<String, Long> actual = mJSScriptEngineLatencyHelper.getMetrics();
         for (Long val : actual.values()) {
@@ -92,7 +95,8 @@
 
     @Test
     public void testInputStreamThrowsException() throws IOException {
-        when(mMetricsEventStreamReader.getMetricsEvents(mInstant)).thenThrow(new IOException());
+        when(mInputStreamFilter.getStream(TEST_FILTER_LABEL, mInstant))
+                .thenThrow(new IOException());
         Map<String, Long> actual = mJSScriptEngineLatencyHelper.getMetrics();
         for (Long val : actual.values()) {
             assertThat(val).isEqualTo(0);
diff --git a/libraries/collectors-helper/adservices/test/src/com/android/helpers/SelectAdsLatencyHelperTest.java b/libraries/collectors-helper/adservices/test/src/com/android/helpers/SelectAdsLatencyHelperTest.java
new file mode 100644
index 0000000..be09f1b
--- /dev/null
+++ b/libraries/collectors-helper/adservices/test/src/com/android/helpers/SelectAdsLatencyHelperTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.helpers;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.util.Map;
+
+public class SelectAdsLatencyHelperTest {
+    private static final String TEST_FILTER_LABEL = "SelectAds";
+    private static final String LOG_LABEL_P50_5G = "SELECT_ADS_LATENCY_P50_5G";
+    private static final String LOG_LABEL_P50_4GPLUS = "SELECT_ADS_LATENCY_P50_4GPLUS";
+    private static final String LOG_LABEL_P50_4G = "SELECT_ADS_LATENCY_P50_4G";
+    private static final String LOG_LABEL_P90_5G = "SELECT_ADS_LATENCY_P90_5G";
+    private static final String LOG_LABEL_P90_4GPLUS = "SELECT_ADS_LATENCY_P90_4GPLUS";
+    private static final String LOG_LABEL_P90_4G = "SELECT_ADS_LATENCY_P90_4G";
+    @Mock private LatencyHelper.InputStreamFilter mInputStreamFilter;
+
+    @Mock private Clock mClock;
+
+    private LatencyHelper mSelectAdsLatencyHelper;
+    private Instant mInstant = Clock.systemUTC().instant();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mClock.getZone()).thenReturn(ZoneId.of("UTC")); // Used by DateTimeFormatter.
+        when(mClock.instant()).thenReturn(mInstant);
+        mSelectAdsLatencyHelper = SelectAdsLatencyHelper.getCollector(mInputStreamFilter, mClock);
+        mSelectAdsLatencyHelper.startCollecting();
+    }
+
+    @Test
+    public void testInitTimeInLogcat() throws IOException {
+        String logcatOutput =
+                "06-13 18:09:24.022 20765 D SelectAds: (SELECT_ADS_LATENCY_P50_5G: 102 ms)\n"
+                    + "06-29 02:47:32.030 31075 D SelectAds: (SELECT_ADS_LATENCY_P50_4GPLUS: 1"
+                    + " ms)\n"
+                    + "06-13 18:09:24.058 20765 D SelectAds: (SELECT_ADS_LATENCY_P50_4G: 43 ms)\n"
+                    + "06-13 18:09:24.058 31075 D SelectAds: (SELECT_ADS_LATENCY_P90_5G: 23 ms)\n"
+                    + "06-13 18:09:24.129 20765 D SelectAds: (SELECT_ADS_LATENCY_P90_4GPLUS: 45"
+                    + " ms)\n"
+                    + "06-13 18:09:24.130 31075 D SelectAds: (SELECT_ADS_LATENCY_P90_4G: 1 ms)";
+
+        when(mInputStreamFilter.getStream(TEST_FILTER_LABEL, mInstant))
+                .thenReturn(new ByteArrayInputStream(logcatOutput.getBytes()));
+        Map<String, Long> actual = mSelectAdsLatencyHelper.getMetrics();
+
+        assertThat(actual.get(LOG_LABEL_P50_5G)).isEqualTo(102);
+        assertThat(actual.get(LOG_LABEL_P50_4GPLUS)).isEqualTo(1);
+        assertThat(actual.get(LOG_LABEL_P50_4G)).isEqualTo(43);
+        assertThat(actual.get(LOG_LABEL_P90_5G)).isEqualTo(23);
+        assertThat(actual.get(LOG_LABEL_P90_4GPLUS)).isEqualTo(45);
+        assertThat(actual.get(LOG_LABEL_P90_4G)).isEqualTo(1);
+    }
+
+    @Test
+    public void testWithEmptyLogcat() throws IOException {
+        when(mInputStreamFilter.getStream(TEST_FILTER_LABEL, mInstant))
+                .thenReturn(new ByteArrayInputStream("".getBytes()));
+        Map<String, Long> actual = mSelectAdsLatencyHelper.getMetrics();
+        for (Long val : actual.values()) {
+            assertThat(val).isEqualTo(0);
+        }
+    }
+
+    @Test
+    public void testInputStreamThrowsException() throws IOException {
+        when(mInputStreamFilter.getStream(TEST_FILTER_LABEL, mInstant))
+                .thenThrow(new IOException());
+        Map<String, Long> actual = mSelectAdsLatencyHelper.getMetrics();
+        for (Long val : actual.values()) {
+            assertThat(val).isEqualTo(0);
+        }
+    }
+}
diff --git a/libraries/collectors-helper/adservices/test/src/com/android/helpers/TopicsLatencyHelperTest.java b/libraries/collectors-helper/adservices/test/src/com/android/helpers/TopicsLatencyHelperTest.java
index 6f8b9a7..32f5b68 100644
--- a/libraries/collectors-helper/adservices/test/src/com/android/helpers/TopicsLatencyHelperTest.java
+++ b/libraries/collectors-helper/adservices/test/src/com/android/helpers/TopicsLatencyHelperTest.java
@@ -17,26 +17,22 @@
 package com.android.helpers;
 
 import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.doReturn;
+
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
 
 import androidx.test.runner.AndroidJUnit4;
 
-import static com.android.helpers.TopicsLatencyHelper.MetricsEventStreamReader;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.time.Clock;
-import java.util.Map;
-import java.util.function.Supplier;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Map;
+
 /**
  * Android Unit tests for {@link TopicsLatencyHelper}.
  *
@@ -44,7 +40,6 @@
  */
 @RunWith(AndroidJUnit4.class)
 public class TopicsLatencyHelperTest {
-    private static final String TAG = TopicsLatencyHelper.class.getSimpleName();
     private static final String TOPICS_HOT_START_LATENCY_METRIC = "TOPICS_HOT_START_LATENCY_METRIC";
     private static final String TOPICS_COLD_START_LATENCY_METRIC =
             "TOPICS_COLD_START_LATENCY_METRIC";
@@ -56,17 +51,14 @@
             "06-13 18:09:24.058 20765 20781 D\n"
                     + " GetTopicsApiCall: (TOPICS_COLD_START_LATENCY_METRIC: 200)";
 
-    private TopicsLatencyHelper mTopicsLatencyHelper;
+    private LatencyHelper mTopicsLatencyHelper;
 
-    private @Mock MetricsEventStreamReader mMetricsEventStreamReader;
-    private @Mock Supplier<MetricsEventStreamReader> mMetricsEventStreamReaderSupplier;
+    @Mock private LatencyHelper.InputStreamFilter mInputStreamFilter;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mTopicsLatencyHelper =
-                new TopicsLatencyHelper(mMetricsEventStreamReaderSupplier, Clock.systemUTC());
-        when(mMetricsEventStreamReaderSupplier.get()).thenReturn(mMetricsEventStreamReader);
+        mTopicsLatencyHelper = TopicsLatencyHelper.getCollector(mInputStreamFilter);
     }
 
     /** Test getting metrics for single package. */
@@ -77,7 +69,7 @@
                         + "\n"
                         + SAMPLE_TOPICS_COLD_START_LATENCY_OUTPUT;
         InputStream targetStream = new ByteArrayInputStream(outputString.getBytes());
-        doReturn(targetStream).when(mMetricsEventStreamReader).getMetricsEvents(any());
+        doReturn(targetStream).when(mInputStreamFilter).getStream(any(), any());
         Map<String, Long> topicsLatencyMetrics = mTopicsLatencyHelper.getMetrics();
         assertThat(topicsLatencyMetrics.get(TOPICS_HOT_START_LATENCY_METRIC)).isEqualTo(14);
         assertThat(topicsLatencyMetrics.get(TOPICS_COLD_START_LATENCY_METRIC)).isEqualTo(200);
@@ -88,7 +80,7 @@
     public void testEmptyLogcat_noMetrics() throws Exception {
         String outputString = "";
         InputStream targetStream = new ByteArrayInputStream(outputString.getBytes());
-        doReturn(targetStream).when(mMetricsEventStreamReader).getMetricsEvents(any());
+        doReturn(targetStream).when(mInputStreamFilter).getStream(any(), any());
         Map<String, Long> topicsLatencyMetrics = mTopicsLatencyHelper.getMetrics();
         assertThat(topicsLatencyMetrics.containsKey(TOPICS_COLD_START_LATENCY_METRIC)).isFalse();
         assertThat(topicsLatencyMetrics.containsKey(TOPICS_HOT_START_LATENCY_METRIC)).isFalse();
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/JSScriptEngineListener.java b/libraries/device-collectors/src/main/java/android/device/collectors/JSScriptEngineListener.java
index 10f5b40..b6f937e 100644
--- a/libraries/device-collectors/src/main/java/android/device/collectors/JSScriptEngineListener.java
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/JSScriptEngineListener.java
@@ -23,6 +23,6 @@
 @OptionClass(alias = "jsscriptengine-latency-listener")
 public class JSScriptEngineListener extends BaseCollectionListener<Integer> {
     public JSScriptEngineListener() {
-        createHelperInstance(new JSScriptEngineLatencyHelper());
+        createHelperInstance(JSScriptEngineLatencyHelper.getLogcatCollector());
     }
 }
diff --git a/libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/CVE_2020_0215/PocActivity.java b/libraries/device-collectors/src/main/java/android/device/collectors/SelectAdsLatencyListener.java
similarity index 61%
copy from libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/CVE_2020_0215/PocActivity.java
copy to libraries/device-collectors/src/main/java/android/device/collectors/SelectAdsLatencyListener.java
index 5825043..2155180 100644
--- a/libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/CVE_2020_0215/PocActivity.java
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/SelectAdsLatencyListener.java
@@ -14,16 +14,14 @@
  * limitations under the License.
  */
 
-package android.security.sts.CVE_2020_0215;
+package android.device.collectors;
 
-import android.app.Activity;
-import android.os.Bundle;
+import android.device.collectors.annotations.OptionClass;
+import com.android.helpers.SelectAdsLatencyHelper;
 
-public class PocActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_main);
+@OptionClass(alias = "selectads-latency-listener")
+public class SelectAdsLatencyListener extends BaseCollectionListener<Long> {
+    public SelectAdsLatencyListener() {
+        createHelperInstance(SelectAdsLatencyHelper.getLogcatCollector());
     }
 }
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/TopicsLatencyCollector.java b/libraries/device-collectors/src/main/java/android/device/collectors/TopicsLatencyCollector.java
index ba382a1..4bfa0cf 100644
--- a/libraries/device-collectors/src/main/java/android/device/collectors/TopicsLatencyCollector.java
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/TopicsLatencyCollector.java
@@ -24,6 +24,6 @@
 public class TopicsLatencyCollector extends BaseCollectionListener<Long> {
 
     public TopicsLatencyCollector() {
-        createHelperInstance(new TopicsLatencyHelper());
+        createHelperInstance(TopicsLatencyHelper.getLogcatCollector());
     }
 }
diff --git a/libraries/health/rules/Android.bp b/libraries/health/rules/Android.bp
index a472fed..3c0436b 100644
--- a/libraries/health/rules/Android.bp
+++ b/libraries/health/rules/Android.bp
@@ -31,6 +31,7 @@
         "flickerlib",
         "statsd-helper",
         "health-testing-utils",
+        "ub-uiautomator",
     ],
     srcs: [
         "src/**/*.java",
@@ -53,6 +54,7 @@
         "statsd-helper",
         "launcher-aosp-tapl",
         "health-testing-utils",
+        "ub-uiautomator",
     ],
     srcs: ["src/**/*.java"],
     exclude_srcs: [
diff --git a/libraries/health/rules/src/android/platform/test/rule/WaitForIdleTimeoutRule.java b/libraries/health/rules/src/android/platform/test/rule/WaitForIdleTimeoutRule.java
new file mode 100644
index 0000000..88c2d07
--- /dev/null
+++ b/libraries/health/rules/src/android/platform/test/rule/WaitForIdleTimeoutRule.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.platform.test.rule;
+
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.test.uiautomator.Configurator;
+
+import org.junit.runner.Description;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * This rule allows overriding of timeout in waitForIdle() for entire CUJs.
+ *
+ * <p>It is useful for CUJs where the app can't reach an idle state and should only be used as
+ * temporary fixes.
+ *
+ * <p>Since this is meant to be a hotfix, it is disabled by default and must be turned on explicitly
+ * in tests.
+ */
+public class WaitForIdleTimeoutRule extends TestWatcher {
+    private static final String LOG_TAG = WaitForIdleTimeoutRule.class.getSimpleName();
+
+    @VisibleForTesting static final String DISABLE_OPTION = "wait-for-idle-timeout-disable";
+    private boolean mDisabled = true;
+
+    @VisibleForTesting static final String TIMEOUT_OPTION = "wait-for-idle-timeout";
+    private long mWaitForIdleTimeout = 500L;
+
+    private List<TimeoutSetter> mTimeoutSetters = new ArrayList();
+
+    @Override
+    protected void starting(Description description) {
+        mDisabled =
+                Boolean.parseBoolean(
+                        getArguments().getString(DISABLE_OPTION, String.valueOf(mDisabled)));
+        if (mDisabled) {
+            return;
+        }
+
+        mWaitForIdleTimeout =
+                Long.parseLong(
+                        getArguments()
+                                .getString(TIMEOUT_OPTION, String.valueOf(mWaitForIdleTimeout)));
+
+        mTimeoutSetters.addAll(getTimeoutSetters());
+
+        for (TimeoutSetter setter : mTimeoutSetters) {
+            setter.storeInitialAndSetNewTimeout(mWaitForIdleTimeout);
+        }
+    }
+
+    @Override
+    protected void finished(Description description) {
+        if (mDisabled) {
+            return;
+        }
+
+        for (TimeoutSetter setter : mTimeoutSetters) {
+            setter.restoreTimeout();
+        }
+    }
+
+    @VisibleForTesting
+    protected List<TimeoutSetter> getTimeoutSetters() {
+        return Arrays.asList(new AndroidXTimeoutSetter(), new AndroidSupportTimeoutSetter());
+    }
+
+    // Prevents code duplication when dealing with both android.support.test and androidx.test.
+    @VisibleForTesting
+    abstract static class TimeoutSetter {
+        protected abstract String getDescription();
+
+        protected abstract void setTimeout(long ms);
+
+        protected abstract long getTimeout();
+
+        private long mInitialTimeout = 0L;
+
+        public final void storeInitialAndSetNewTimeout(long ms) {
+            mInitialTimeout = getTimeout();
+            Log.i(
+                    LOG_TAG,
+                    String.format(
+                            "waitForIdle() timeout in %s was initially %d ms.",
+                            getDescription(), mInitialTimeout));
+
+            Log.i(
+                    LOG_TAG,
+                    String.format(
+                            "Trying to set waitForIdle() timeout in %s to %d ms.",
+                            getDescription(), ms));
+            setTimeout(ms);
+            if (getTimeout() != ms) {
+                Log.w(
+                        LOG_TAG,
+                        String.format(
+                                "Failed to set waitForIdle() timeout in %s to %d ms.",
+                                getDescription(), ms));
+            }
+
+            logCurrentTimeout();
+        }
+
+        public final void restoreTimeout() {
+            Log.i(
+                    LOG_TAG,
+                    String.format(
+                            "Trying to restore waitForIdle() timeout in %s back to %d ms.",
+                            getDescription(), mInitialTimeout));
+            setTimeout(mInitialTimeout);
+            if (getTimeout() != mInitialTimeout) {
+                Log.w(
+                        LOG_TAG,
+                        String.format(
+                                "Failed to restore waitForIdle() timeout in %s back to %d ms.",
+                                getDescription(), mInitialTimeout));
+            }
+
+            logCurrentTimeout();
+        }
+
+        private void logCurrentTimeout() {
+            Log.i(
+                    LOG_TAG,
+                    String.format(
+                            "waitForIdle() timeout in %s is now %d ms.",
+                            getDescription(), getTimeout()));
+        }
+    }
+
+    private static class AndroidXTimeoutSetter extends TimeoutSetter {
+        @Override
+        protected String getDescription() {
+            return "androidx.test.uiautomator";
+        }
+
+        @Override
+        protected void setTimeout(long ms) {
+            Configurator.getInstance().setWaitForIdleTimeout(ms);
+            return;
+        }
+
+        @Override
+        protected long getTimeout() {
+            return Configurator.getInstance().getWaitForIdleTimeout();
+        }
+    }
+
+    private static class AndroidSupportTimeoutSetter extends TimeoutSetter {
+        @Override
+        protected String getDescription() {
+            return "android.support.test.uiautomator";
+        }
+
+        @Override
+        protected void setTimeout(long ms) {
+            android.support.test.uiautomator.Configurator.getInstance().setWaitForIdleTimeout(ms);
+            return;
+        }
+
+        @Override
+        protected long getTimeout() {
+            return android.support.test.uiautomator.Configurator.getInstance()
+                    .getWaitForIdleTimeout();
+        }
+    }
+}
diff --git a/libraries/health/rules/tests/src/android/platform/test/rule/WaitForIdleTimeoutRuleTest.java b/libraries/health/rules/tests/src/android/platform/test/rule/WaitForIdleTimeoutRuleTest.java
new file mode 100644
index 0000000..01b01fe
--- /dev/null
+++ b/libraries/health/rules/tests/src/android/platform/test/rule/WaitForIdleTimeoutRuleTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.rule;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.os.Bundle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/** Tests for {@link WaitForIdleTimeoutRule}. */
+public class WaitForIdleTimeoutRuleTest {
+    private static List<String> sLogs = new ArrayList<>();
+
+    private static final String TEST_STATEMENT_LOG = "Running test statement.";
+    private static final Statement TEST_STATEMENT =
+            new Statement() {
+                @Override
+                public void evaluate() {
+                    sLogs.add(TEST_STATEMENT_LOG);
+                }
+            };
+    private static final Description DESCRIPTION =
+            Description.createTestDescription("class", "method");
+
+    private static class TestableTimeoutSetter extends WaitForIdleTimeoutRule.TimeoutSetter {
+        public static final long INITIAL_TIMEOUT = 7L;
+        public static final String SET_TIMEOUT_LOG_TEMPLATE = "Setting timeout to %d ms.";
+
+        private long mTimeout = INITIAL_TIMEOUT;
+
+        @Override
+        protected String getDescription() {
+            return "TestableTimeoutSetter";
+        }
+
+        @Override
+        protected void setTimeout(long ms) {
+            sLogs.add(String.format(SET_TIMEOUT_LOG_TEMPLATE, ms));
+            mTimeout = ms;
+        }
+
+        @Override
+        protected long getTimeout() {
+            return mTimeout;
+        }
+    }
+
+    @Before
+    public void setUp() {
+        sLogs.clear();
+    }
+
+    @Test
+    public void testSetsAndRestoresTimeout() throws Throwable {
+        long timeout = 777L;
+        WaitForIdleTimeoutRule rule = createRule(timeout, false);
+
+        rule.apply(TEST_STATEMENT, DESCRIPTION).evaluate();
+
+        assertThat(sLogs)
+                .containsExactly(
+                        String.format(TestableTimeoutSetter.SET_TIMEOUT_LOG_TEMPLATE, timeout),
+                        TEST_STATEMENT_LOG,
+                        String.format(
+                                TestableTimeoutSetter.SET_TIMEOUT_LOG_TEMPLATE,
+                                TestableTimeoutSetter.INITIAL_TIMEOUT));
+    }
+
+    @Test
+    public void testDisable() throws Throwable {
+        // The rule is disabled by default.
+        WaitForIdleTimeoutRule rule = createRule(777L, null);
+
+        rule.apply(TEST_STATEMENT, DESCRIPTION).evaluate();
+
+        assertThat(sLogs).containsExactly(TEST_STATEMENT_LOG);
+    }
+
+    private WaitForIdleTimeoutRule createRule(long timeout, Boolean disabled) {
+        Bundle args = new Bundle();
+        args.putString(WaitForIdleTimeoutRule.TIMEOUT_OPTION, String.valueOf(timeout));
+        if (disabled != null) {
+            args.putString(WaitForIdleTimeoutRule.DISABLE_OPTION, String.valueOf(disabled));
+        }
+
+        WaitForIdleTimeoutRule rule = spy(new WaitForIdleTimeoutRule());
+        doReturn(args).when(rule).getArguments();
+        doReturn(Arrays.asList(new TestableTimeoutSetter())).when(rule).getTimeoutSetters();
+
+        return rule;
+    }
+}
diff --git a/libraries/health/runners/longevity/platform/Android.bp b/libraries/health/runners/longevity/platform/Android.bp
index 0ad4e4a..017aae2 100644
--- a/libraries/health/runners/longevity/platform/Android.bp
+++ b/libraries/health/runners/longevity/platform/Android.bp
@@ -40,6 +40,7 @@
         "common-platform-scenarios",
         "guava",
         "platform-test-composers",
+        "platform-test-rules",
         "microbenchmark-device-lib"
     ],
     min_sdk_version: "26",
diff --git a/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/LongevityClassRunner.java b/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/LongevityClassRunner.java
index 9e3db39..bb781c5 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
@@ -18,6 +18,7 @@
 
 import android.os.Bundle;
 import android.platform.test.microbenchmark.Microbenchmark;
+import android.platform.test.rule.DynamicRuleChain;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.test.InstrumentationRegistry;
@@ -28,6 +29,7 @@
 import org.junit.BeforeClass;
 import org.junit.internal.runners.statements.RunAfters;
 import org.junit.internal.runners.statements.RunBefores;
+import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runner.notification.StoppedByUserException;
 import org.junit.runners.BlockJUnit4ClassRunner;
@@ -48,12 +50,20 @@
  * longevity tests.
  */
 public class LongevityClassRunner extends BlockJUnit4ClassRunner {
+    // Use these options to inject rules at runtime via the command line. For details, please see
+    // documentation for DynamicRuleChain.
+    @VisibleForTesting static final String DYNAMIC_OUTER_CLASS_RULES_OPTION = "outer-class-rules";
+    @VisibleForTesting static final String DYNAMIC_INNER_CLASS_RULES_OPTION = "inner-class-rules";
+    @VisibleForTesting static final String DYNAMIC_OUTER_TEST_RULES_OPTION = "outer-test-rules";
+    @VisibleForTesting static final String DYNAMIC_INNER_TEST_RULES_OPTION = "inner-test-rules";
+
     @VisibleForTesting static final String FILTER_OPTION = "exclude-class";
     @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 final Bundle mArguments;
     private final String[] mExcludedClasses;
     private String mIterationSep = ITERATION_SEP_DEFAULT;
 
@@ -69,6 +79,7 @@
     @VisibleForTesting
     LongevityClassRunner(Class<?> klass, Bundle args) throws InitializationError {
         super(klass);
+        mArguments = args;
         mExcludedClasses =
                 args.containsKey(FILTER_OPTION)
                         ? args.getString(FILTER_OPTION).split(",")
@@ -92,6 +103,28 @@
         return mIteration;
     }
 
+    /** Add {@link DynamicRuleChain} to the existing class rules. */
+    @Override
+    protected List<TestRule> classRules() {
+        List<TestRule> classRules = new ArrayList<>();
+        // Inner dynamic class rules should be included first because RunRules applies rules inside
+        // -out.
+        classRules.add(new DynamicRuleChain(DYNAMIC_INNER_CLASS_RULES_OPTION, mArguments));
+        classRules.addAll(super.classRules());
+        classRules.add(new DynamicRuleChain(DYNAMIC_OUTER_CLASS_RULES_OPTION, mArguments));
+        return classRules;
+    }
+
+    /** Add {@link DynamicRuleChain} to the existing test rules. */
+    protected List<TestRule> getTestRules(Object target) {
+        List<TestRule> testRules = new ArrayList<>();
+        // Inner dynamic rules should be included first because RunRules applies rules inside-out.
+        testRules.add(new DynamicRuleChain(DYNAMIC_INNER_TEST_RULES_OPTION, mArguments));
+        testRules.addAll(super.getTestRules(target));
+        testRules.add(new DynamicRuleChain(DYNAMIC_OUTER_TEST_RULES_OPTION, mArguments));
+        return testRules;
+    }
+
     /**
      * Override the parent {@code withBeforeClasses} method to be a no-op.
      *
diff --git a/libraries/health/runners/longevity/platform/tests/Android.bp b/libraries/health/runners/longevity/platform/tests/Android.bp
index b3ece27..934516f 100644
--- a/libraries/health/runners/longevity/platform/tests/Android.bp
+++ b/libraries/health/runners/longevity/platform/tests/Android.bp
@@ -55,8 +55,10 @@
         "guava",
         "lib-test-profile-text-protos",
         "longevity-device-lib",
+        "microbenchmark-device-lib",
         "mockito-target",
         "platform-test-composers",
+        "platform-test-rules",
         "truth-prebuilt",
         "ub-uiautomator",
     ],
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 93a666c..575c189 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
@@ -16,6 +16,7 @@
 
 package android.platform.test.longevity;
 
+import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.argThat;
 import static org.mockito.Mockito.atLeastOnce;
@@ -29,7 +30,9 @@
 import static org.mockito.MockitoAnnotations.initMocks;
 
 import android.os.Bundle;
+import android.platform.test.rule.TestWatcher;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import org.junit.Assert;
@@ -37,6 +40,8 @@
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.Description;
 import org.junit.runner.RunWith;
@@ -52,6 +57,59 @@
 
 /** Unit tests for the {@link LongevityClassRunner}. */
 public class LongevityClassRunnerTest {
+    // Used to test things that are generated reflectively, e.g. dynamic rules.
+    private static final List<String> sLogs = new ArrayList<>();
+
+    public static class LoggingRule extends TestWatcher {
+        private final String mName;
+
+        public LoggingRule(String name) {
+            mName = name;
+        }
+
+        @Override
+        public void starting(Description description) {
+            sLogs.add(String.format("%s starting", mName));
+        }
+
+        @Override
+        public void finished(Description description) {
+            sLogs.add(String.format("%s finished", mName));
+        }
+    }
+
+    public static class InjectedRule1 extends LoggingRule {
+        public InjectedRule1() {
+            super("Injected rule 1");
+        }
+    }
+
+    public static class InjectedRule2 extends LoggingRule {
+        public InjectedRule2() {
+            super("Injected rule 2");
+        }
+    }
+
+    // We use two @Test methods here to distinguish between testing for class
+    // rules vs. test rules. This is for testing only; we still advocate for
+    // one @Test method per class.
+    @RunWith(JUnit4.class)
+    public static class LoggingTestWithRules {
+        @ClassRule public static LoggingRule classRule = new LoggingRule("Hardcoded class rule");
+
+        @Rule public LoggingRule testRule = new LoggingRule("Hardcoded test rule");
+
+        @Test
+        public void test1() {
+            sLogs.add("Test 1 execution");
+        }
+
+        @Test
+        public void test2() {
+            sLogs.add("Test 2 execution");
+        }
+    }
+
     // A sample test class to test the runner with.
     @RunWith(JUnit4.class)
     public static class NoOpTest {
@@ -106,6 +164,7 @@
     @Before
     public void setUp() {
         initMocks(this);
+        sLogs.clear();
     }
 
     /**
@@ -424,6 +483,70 @@
                 captor.getValue().getClassName().matches(String.join(sep, "^.*", "7$")));
     }
 
+    @Test
+    public void testDynamicClassRules() throws Throwable {
+        Bundle args = new Bundle();
+        args.putString(
+                LongevityClassRunner.DYNAMIC_OUTER_CLASS_RULES_OPTION,
+                InjectedRule1.class.getName());
+        args.putString(
+                LongevityClassRunner.DYNAMIC_INNER_CLASS_RULES_OPTION,
+                InjectedRule2.class.getName());
+
+        RunNotifier notifier = mock(RunNotifier.class);
+        mRunner = new LongevityClassRunner(LoggingTestWithRules.class, args);
+        mRunner.run(notifier);
+        verifyForAssertionFailures(notifier);
+        assertThat(sLogs)
+                .containsExactly(
+                        "Injected rule 1 starting",
+                        "Hardcoded class rule starting",
+                        "Injected rule 2 starting",
+                        "Hardcoded test rule starting",
+                        "Test 1 execution",
+                        "Hardcoded test rule finished",
+                        "Hardcoded test rule starting",
+                        "Test 2 execution",
+                        "Hardcoded test rule finished",
+                        "Injected rule 2 finished",
+                        "Hardcoded class rule finished",
+                        "Injected rule 1 finished");
+    }
+
+    @Test
+    public void testDynamicTestRules() throws Throwable {
+        Bundle args = new Bundle();
+        args.putString(
+                LongevityClassRunner.DYNAMIC_OUTER_TEST_RULES_OPTION,
+                InjectedRule1.class.getName());
+        args.putString(
+                LongevityClassRunner.DYNAMIC_INNER_TEST_RULES_OPTION,
+                InjectedRule2.class.getName());
+
+        RunNotifier notifier = mock(RunNotifier.class);
+        mRunner = new LongevityClassRunner(LoggingTestWithRules.class, args);
+        mRunner.run(notifier);
+        verifyForAssertionFailures(notifier);
+        assertThat(sLogs)
+                .containsExactly(
+                        "Hardcoded class rule starting",
+                        "Injected rule 1 starting",
+                        "Hardcoded test rule starting",
+                        "Injected rule 2 starting",
+                        "Test 1 execution",
+                        "Injected rule 2 finished",
+                        "Hardcoded test rule finished",
+                        "Injected rule 1 finished",
+                        "Injected rule 1 starting",
+                        "Hardcoded test rule starting",
+                        "Injected rule 2 starting",
+                        "Test 2 execution",
+                        "Injected rule 2 finished",
+                        "Hardcoded test rule finished",
+                        "Injected rule 1 finished",
+                        "Hardcoded class rule finished");
+    }
+
     private List<FrameworkMethod> getMethodNameMatcher(String methodName) {
         return argThat(
                 l ->
diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/FridaUtils.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/FridaUtils.java
index 2790be3..bee66d2 100644
--- a/libraries/sts-common-util/host-side/src/com/android/sts/common/FridaUtils.java
+++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/FridaUtils.java
@@ -195,7 +195,13 @@
     public void close() throws DeviceNotAvailableException, TimeoutException {
         device.enableAdbRoot();
         for (Integer pid : runningPids) {
-            ProcessUtil.killPid(device, pid.intValue(), 10_000L);
+            try {
+                ProcessUtil.killPid(device, pid.intValue(), 10_000L);
+            } catch (ProcessUtil.KillException e) {
+                if (e.getReason() != ProcessUtil.KillException.Reason.NO_SUCH_PROCESS) {
+                    CLog.e(e);
+                }
+            }
         }
         for (String file : fridaFiles) {
             device.deleteFile(file);
diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/MallocDebug.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/MallocDebug.java
index 1b9e19f..20d4bf7 100644
--- a/libraries/sts-common-util/host-side/src/com/android/sts/common/MallocDebug.java
+++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/MallocDebug.java
@@ -23,6 +23,7 @@
 
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
 import com.google.common.collect.ImmutableList;
 
 import java.util.concurrent.TimeoutException;
@@ -50,7 +51,7 @@
 
     private MallocDebug(
             ITestDevice device, String mallocDebugOption, String processName, boolean isService)
-            throws DeviceNotAvailableException, TimeoutException {
+            throws DeviceNotAvailableException, TimeoutException, ProcessUtil.KillException {
         this.device = device;
         this.processName = processName;
 
@@ -81,13 +82,14 @@
                     setAttachedProgramProperty.close();
                 }
             } catch (Exception e2) {
+                CLog.e(e2);
                 fail(
                         "Could not enable malloc debug. Additionally, there was an"
                                 + " exception while trying to reset device state. Tests after"
                                 + " this may not work as expected!\n"
                                 + e2);
             }
-            throw e1;
+            assumeNoException("Could not enable malloc debug", e1);
         }
     }
 
@@ -121,7 +123,8 @@
      */
     public static AutoCloseable withLibcMallocDebugOnService(
             ITestDevice device, String mallocDebugOptions, String processName)
-            throws DeviceNotAvailableException, IllegalArgumentException, TimeoutException {
+            throws DeviceNotAvailableException, IllegalArgumentException, TimeoutException,
+                ProcessUtil.KillException {
         if (processName == null || processName.isEmpty()) {
             throw new IllegalArgumentException("Service processName can't be empty");
         }
@@ -140,7 +143,8 @@
      */
     public static AutoCloseable withLibcMallocDebugOnNewProcess(
             ITestDevice device, String mallocDebugOptions, String processName)
-            throws DeviceNotAvailableException, IllegalArgumentException, TimeoutException {
+            throws DeviceNotAvailableException, IllegalArgumentException, TimeoutException,
+                ProcessUtil.KillException {
         if (processName == null || processName.isEmpty()) {
             throw new IllegalArgumentException("processName can't be empty");
         }
@@ -161,7 +165,7 @@
      */
     public static AutoCloseable withLibcMallocDebugOnAllNewProcesses(
             ITestDevice device, String mallocDebugOptions)
-            throws DeviceNotAvailableException, TimeoutException {
+            throws DeviceNotAvailableException, TimeoutException, ProcessUtil.KillException {
         return new MallocDebug(device, mallocDebugOptions, null, false);
     }
 
diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/ProcessUtil.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/ProcessUtil.java
index ddb84de..67e985a 100644
--- a/libraries/sts-common-util/host-side/src/com/android/sts/common/ProcessUtil.java
+++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/ProcessUtil.java
@@ -33,21 +33,31 @@
 import java.util.stream.Collectors;
 
 public final class ProcessUtil {
+    public static class KillException extends Exception {
+        public enum Reason {
+            UNKNOWN,
+            INVALID_SIGNAL,
+            INSUFFICIENT_PERMISSIONS,
+            NO_SUCH_PROCESS;
+        }
+
+        private Reason reason;
+
+        public KillException(String errorMessage, Reason r) {
+            super(errorMessage);
+            this.reason = r;
+        }
+
+        public Reason getReason() {
+            return this.reason;
+        }
+    }
 
     private static final String LOG_TAG = ProcessUtil.class.getSimpleName();
 
     public static final long PROCESS_WAIT_TIMEOUT_MS = 10_000;
     public static final long PROCESS_POLL_PERIOD_MS = 250;
 
-    static final Pattern[] mallocDebugErrorPatterns = {
-        Pattern.compile("^.*HAS A CORRUPTED FRONT GUARD.*$", Pattern.MULTILINE),
-        Pattern.compile("^.*HAS A CORRUPTED REAR GUARD.*$", Pattern.MULTILINE),
-        Pattern.compile("^.*USED AFTER FREE.*$", Pattern.MULTILINE),
-        Pattern.compile("^.*leaked block of size.*$", Pattern.MULTILINE),
-        Pattern.compile("^.*UNKNOWN POINTER \\(free\\).*$", Pattern.MULTILINE),
-        Pattern.compile("^.*HAS INVALID TAG.*$", Pattern.MULTILINE),
-    };
-
     private ProcessUtil() {}
 
     /**
@@ -168,7 +178,8 @@
      * @param pid the id of the process to wait until exited
      */
     public static void waitPidExited(ITestDevice device, int pid)
-            throws TimeoutException, DeviceNotAvailableException {
+            throws TimeoutException, DeviceNotAvailableException,
+                KillException {
         waitPidExited(device, pid, PROCESS_WAIT_TIMEOUT_MS);
     }
 
@@ -181,7 +192,8 @@
      * @param timeoutMs how long to wait before throwing a TimeoutException
      */
     public static void waitPidExited(ITestDevice device, int pid, long timeoutMs)
-            throws TimeoutException, DeviceNotAvailableException {
+            throws TimeoutException, DeviceNotAvailableException,
+                KillException {
         long endTime = System.currentTimeMillis() + timeoutMs;
         CommandResult res = null;
         while (true) {
@@ -190,7 +202,9 @@
             if (res.getStatus() != CommandStatus.SUCCESS) {
                 String err = res.getStderr();
                 if (!err.contains("No such process")) {
-                    throw new RuntimeException("kill -0 returned stderr: " + err);
+                    throw new KillException(
+                            "kill -0 returned stderr: " + err,
+                            KillException.Reason.NO_SUCH_PROCESS);
                 }
                 // the process is most likely killed
                 return;
@@ -214,7 +228,8 @@
      * @param timeoutMs how long to wait before throwing a TimeoutException
      */
     public static void killPid(ITestDevice device, int pid, long timeoutMs)
-            throws DeviceNotAvailableException, TimeoutException {
+            throws DeviceNotAvailableException, TimeoutException,
+                KillException {
         killPid(device, pid, 9, timeoutMs);
     }
 
@@ -227,8 +242,22 @@
      * @param timeoutMs how long to wait before throwing a TimeoutException
      */
     public static void killPid(ITestDevice device, int pid, int signal, long timeoutMs)
-            throws DeviceNotAvailableException, TimeoutException {
-        CommandUtil.runAndCheck(device, String.format("kill -%d %d", signal, pid));
+            throws DeviceNotAvailableException, TimeoutException,
+                KillException {
+        CommandResult res =
+            device.executeShellV2Command(String.format("kill -%d %d", signal, pid));
+        if (res.getStatus() != CommandStatus.SUCCESS) {
+            String err = res.getStderr();
+            if (err.contains("invalid signal specification")) {
+                throw new KillException(err, KillException.Reason.INVALID_SIGNAL);
+            } else if (err.contains("Operation not permitted")) {
+                throw new KillException(err, KillException.Reason.INSUFFICIENT_PERMISSIONS);
+            } else if (err.contains("No such process")) {
+                throw new KillException(err, KillException.Reason.NO_SUCH_PROCESS);
+            } else {
+                throw new KillException(err, KillException.Reason.UNKNOWN);
+            }
+        }
         waitPidExited(device, pid, timeoutMs);
     }
 
@@ -241,7 +270,8 @@
      * @return whether any processes were killed
      */
     public static boolean killAll(ITestDevice device, String pgrepRegex, long timeoutMs)
-            throws DeviceNotAvailableException, TimeoutException {
+            throws DeviceNotAvailableException, TimeoutException,
+                KillException {
         return killAll(device, pgrepRegex, timeoutMs, true);
     }
 
@@ -257,7 +287,8 @@
      */
     public static boolean killAll(
             ITestDevice device, String pgrepRegex, long timeoutMs, boolean expectExist)
-            throws DeviceNotAvailableException, TimeoutException {
+            throws DeviceNotAvailableException, TimeoutException,
+                KillException {
         Optional<Map<Integer, String>> pids = pidsOf(device, pgrepRegex);
         if (!pids.isPresent()) {
             // no pids to kill
@@ -267,9 +298,18 @@
             }
             return false;
         }
+
         for (int pid : pids.get().keySet()) {
-            killPid(device, pid, timeoutMs);
+            try {
+                killPid(device, pid, timeoutMs);
+            } catch (KillException e) {
+                // ignore pids that do not exist
+                if (e.getReason() != KillException.Reason.NO_SUCH_PROCESS) {
+                    throw e;
+                }
+            }
         }
+
         return true;
     }
 
@@ -284,7 +324,7 @@
      */
     public static AutoCloseable withProcessKill(
             final ITestDevice device, final String pgrepRegex, final Runnable beforeCloseKill)
-            throws DeviceNotAvailableException, TimeoutException {
+            throws DeviceNotAvailableException, TimeoutException, KillException {
         return withProcessKill(device, pgrepRegex, beforeCloseKill, PROCESS_WAIT_TIMEOUT_MS);
     }
 
@@ -303,11 +343,17 @@
             final String pgrepRegex,
             final Runnable beforeCloseKill,
             final long timeoutMs)
-            throws DeviceNotAvailableException, TimeoutException {
+            throws DeviceNotAvailableException, TimeoutException,
+                KillException {
         return new AutoCloseable() {
             {
-                if (!killAll(device, pgrepRegex, timeoutMs, /*expectExist*/ false)) {
-                    Log.d(LOG_TAG, String.format("did not kill any processes for %s", pgrepRegex));
+                try {
+                    if (!killAll(device, pgrepRegex, timeoutMs, /*expectExist*/ false)) {
+                        Log.d(LOG_TAG,
+                            String.format("did not kill any processes for %s", pgrepRegex));
+                    }
+                } catch (KillException e) {
+                    Log.d(LOG_TAG, "failed to kill a process");
                 }
             }
 
@@ -316,7 +362,13 @@
                 if (beforeCloseKill != null) {
                     beforeCloseKill.run();
                 }
-                killAll(device, pgrepRegex, timeoutMs, /*expectExist*/ false);
+                try {
+                    killAll(device, pgrepRegex, timeoutMs, /*expectExist*/ false);
+                } catch (KillException e) {
+                    if (e.getReason() != KillException.Reason.NO_SUCH_PROCESS) {
+                        throw e;
+                    }
+                }
             }
         };
     }
diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/RootcanalUtils.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/RootcanalUtils.java
index 1934eb4..c293c91 100644
--- a/libraries/sts-common-util/host-side/src/com/android/sts/common/RootcanalUtils.java
+++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/RootcanalUtils.java
@@ -101,6 +101,10 @@
             throw new AssertionError("Device unavailable when cleaning up", e);
         } catch (TimeoutException e) {
             CLog.w("Could not kill rootcanal HAL during cleanup");
+        } catch (ProcessUtil.KillException e) {
+            if (e.getReason() != ProcessUtil.KillException.Reason.NO_SUCH_PROCESS) {
+                CLog.w("Could not kill rootcanal HAL during cleanup: " + e.getMessage());
+            }
         }
     }
 
@@ -213,6 +217,8 @@
                     device, "android\\.hardware\\.bluetooth@1\\.1-service\\.sim", 10_000);
         } catch (TimeoutException e) {
             assumeNoException("Could not start virtual BT HAL", e);
+        } catch (ProcessUtil.KillException e) {
+            assumeNoException("Failed to kill process", e);
         }
 
         // Reenable Bluetooth and enable RootCanal control channel
diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/SecurityTestCase.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/SecurityTestCase.java
index bc4160d..7fcdeb6 100644
--- a/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/SecurityTestCase.java
+++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/tradefed/testtype/SecurityTestCase.java
@@ -122,10 +122,7 @@
         }
     }
 
-    /**
-     * Makes sure the phone is online, and the ensure the current boottime is within 2 seconds (due
-     * to rounding) of the previous boottime to check if The phone has crashed.
-     */
+    /** Makes sure the phone is online and checks if the device crashed */
     @After
     public void tearDown() throws Exception {
         try {
@@ -153,7 +150,7 @@
                             "The device has unexpectedly rebooted (%s seconds after last recorded boot time, bootreason: %s)",
                             currentKernelStartTime - lastKernelStartTime, bootReason)
                     .that(currentKernelStartTime)
-                    .isLessThan(lastKernelStartTime + 2);
+                    .isLessThan(lastKernelStartTime + 10);
         }
     }
 
diff --git a/libraries/sts-common-util/host-side/src/com/android/sts/common/util/TombstoneUtils.java b/libraries/sts-common-util/host-side/src/com/android/sts/common/util/TombstoneUtils.java
index 786a366..77c8972 100644
--- a/libraries/sts-common-util/host-side/src/com/android/sts/common/util/TombstoneUtils.java
+++ b/libraries/sts-common-util/host-side/src/com/android/sts/common/util/TombstoneUtils.java
@@ -99,7 +99,8 @@
                                                         try {
                                                             ProcessUtil.waitPidExited(device, pid);
                                                         } catch (TimeoutException
-                                                                | DeviceNotAvailableException e) {
+                                                                | DeviceNotAvailableException
+                                                                | ProcessUtil.KillException e) {
                                                             CLog.w(e);
                                                         }
                                                     });
diff --git a/libraries/sts-common-util/sts-sdk/Android.mk b/libraries/sts-common-util/sts-sdk/Android.mk
index 5c9734f..2c0b8cd 100644
--- a/libraries/sts-common-util/sts-sdk/Android.mk
+++ b/libraries/sts-common-util/sts-sdk/Android.mk
@@ -20,7 +20,9 @@
 		sed -i 's~{{PLATFORM_SDK_VERSION}}~$(PLATFORM_SDK_VERSION)~g' $${tmplfile}; \
 		mv $${tmplfile} $${tmplfile/.template/}; \
 	done
-	$(SOONG_ZIP) -o $@ -C $(STS_SDK_TMP_DIR) -D $(STS_SDK_TMP_DIR)
+	# Build system can't cleanly handle hidden files
+	mv $(STS_SDK_TMP_DIR)/dotidea $(STS_SDK_TMP_DIR)/.idea
+	$(SOONG_ZIP) -o $@ -C $(STS_SDK_TMP_DIR) -D $(STS_SDK_TMP_DIR) -D $(STS_SDK_TMP_DIR)/.idea
 
 sts_sdk_sample_files :=
 
diff --git a/libraries/sts-common-util/sts-sdk/package/build.gradle b/libraries/sts-common-util/sts-sdk/package/build.gradle
index ee00121..ee23982 100644
--- a/libraries/sts-common-util/sts-sdk/package/build.gradle
+++ b/libraries/sts-common-util/sts-sdk/package/build.gradle
@@ -5,10 +5,29 @@
 
 task clean(type: Delete) {
     delete layout.buildDirectory
+    delete project('native-poc').layout.projectDirectory.dir('.cxx')
 }
 
-ext {
-    testAppApkName = 'CVE_2020_0215.apk'
+ext.copyArtifacts = { nativeDir ->
+    copy {
+        from project('sts-test').layout.buildDirectory.file('testcases')
+        from project('native-poc').layout.buildDirectory.file(nativeDir)
+        from project('test-app').layout.buildDirectory.file('testcases')
+        into layout.buildDirectory.dir('android-sts/testcases')
+    }
+
+    // TODO: figure out variants
+    copy {
+        from project('test-app').layout.buildDirectory.file('outputs/apk/debug')
+        rename '(.*).apk', 'sts_test_app_package.apk'
+        include '**/*.apk'
+        into layout.buildDirectory.dir('android-sts/testcases')
+    }
+
+    copy {
+        from project('sts-test').layout.projectDirectory.file('libs')
+        into layout.buildDirectory.dir('android-sts/tools')
+    }
 }
 
 task assembleStsARM {
@@ -18,23 +37,19 @@
     dependsOn ':test-app:assemble'
 
     doLast {
-        copy {
-            from project('sts-test').layout.buildDirectory.file('testcases')
-            from project('native-poc').layout.buildDirectory.file('testcases')
-            from project('test-app').layout.buildDirectory.file('testcases')
-            into layout.buildDirectory.dir('android-sts/testcases')
-        }
-
-        copy {
-            from project('test-app').layout.buildDirectory.file('outputs/apk/debug')
-            rename '(.*).apk', "${testAppApkName}"
-            include '**/*.apk'
-            into layout.buildDirectory.dir('android-sts/testcases')
-        }
-
-        copy {
-            from project('sts-test').layout.projectDirectory.file('libs')
-            into layout.buildDirectory.dir('android-sts/tools')
-        }
+        copyArtifacts('testcases_arm')
     }
 }
+
+task assembleStsx86 {
+    dependsOn ':sts-test:copyHostSideTest'
+    dependsOn ':native-poc:copyx86'
+    dependsOn ':native-poc:copyx86_64'
+    dependsOn ':test-app:assemble'
+
+    doLast {
+        copyArtifacts('testcases_x86')
+    }
+}
+
+
diff --git a/libraries/sts-common-util/sts-sdk/package/dotidea/runConfigurations/assemble_STS_arm.xml b/libraries/sts-common-util/sts-sdk/package/dotidea/runConfigurations/assemble_STS_arm.xml
new file mode 100644
index 0000000..c79a3b0
--- /dev/null
+++ b/libraries/sts-common-util/sts-sdk/package/dotidea/runConfigurations/assemble_STS_arm.xml
@@ -0,0 +1,23 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="assembleStsArm" type="GradleRunConfiguration" factoryName="Gradle">
+    <ExternalSystemSettings>
+      <option name="executionName" />
+      <option name="externalProjectPath" value="$PROJECT_DIR$" />
+      <option name="externalSystemIdString" value="GRADLE" />
+      <option name="scriptParameters" value="" />
+      <option name="taskDescriptions">
+        <list />
+      </option>
+      <option name="taskNames">
+        <list>
+          <option value="assembleStsARM" />
+        </list>
+      </option>
+      <option name="vmOptions" />
+    </ExternalSystemSettings>
+    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
+    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
+    <DebugAllEnabled>false</DebugAllEnabled>
+    <method v="2" />
+  </configuration>
+</component>
diff --git a/libraries/sts-common-util/sts-sdk/package/dotidea/runConfigurations/assemble_STS_x86.xml b/libraries/sts-common-util/sts-sdk/package/dotidea/runConfigurations/assemble_STS_x86.xml
new file mode 100644
index 0000000..790e427
--- /dev/null
+++ b/libraries/sts-common-util/sts-sdk/package/dotidea/runConfigurations/assemble_STS_x86.xml
@@ -0,0 +1,23 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="assembleStsx86" type="GradleRunConfiguration" factoryName="Gradle">
+    <ExternalSystemSettings>
+      <option name="executionName" />
+      <option name="externalProjectPath" value="$PROJECT_DIR$" />
+      <option name="externalSystemIdString" value="GRADLE" />
+      <option name="scriptParameters" value="" />
+      <option name="taskDescriptions">
+        <list />
+      </option>
+      <option name="taskNames">
+        <list>
+          <option value="assembleStsx86" />
+        </list>
+      </option>
+      <option name="vmOptions" />
+    </ExternalSystemSettings>
+    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
+    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
+    <DebugAllEnabled>false</DebugAllEnabled>
+    <method v="2" />
+  </configuration>
+</component>
diff --git a/libraries/sts-common-util/sts-sdk/package/dotidea/runConfigurations/clean.xml b/libraries/sts-common-util/sts-sdk/package/dotidea/runConfigurations/clean.xml
new file mode 100644
index 0000000..9480f95
--- /dev/null
+++ b/libraries/sts-common-util/sts-sdk/package/dotidea/runConfigurations/clean.xml
@@ -0,0 +1,23 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="clean" type="GradleRunConfiguration" factoryName="Gradle">
+    <ExternalSystemSettings>
+      <option name="executionName" />
+      <option name="externalProjectPath" value="$PROJECT_DIR$" />
+      <option name="externalSystemIdString" value="GRADLE" />
+      <option name="scriptParameters" value="" />
+      <option name="taskDescriptions">
+        <list />
+      </option>
+      <option name="taskNames">
+        <list>
+          <option value="clean" />
+        </list>
+      </option>
+      <option name="vmOptions" />
+    </ExternalSystemSettings>
+    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
+    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
+    <DebugAllEnabled>false</DebugAllEnabled>
+    <method v="2" />
+  </configuration>
+</component>
diff --git a/libraries/sts-common-util/sts-sdk/package/dotidea/runConfigurations/run_STS.xml b/libraries/sts-common-util/sts-sdk/package/dotidea/runConfigurations/run_STS.xml
new file mode 100644
index 0000000..8105af9
--- /dev/null
+++ b/libraries/sts-common-util/sts-sdk/package/dotidea/runConfigurations/run_STS.xml
@@ -0,0 +1,12 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="run STS" type="ShConfigurationType">
+    <option name="SCRIPT_TEXT" value="./sts-tradefed run commandAndExit sts-dynamic-develop -m hostsidetest" />
+    <option name="INDEPENDENT_SCRIPT_PATH" value="true" />
+    <option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
+    <option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$/build/android-sts/tools" />
+    <option name="EXECUTE_IN_TERMINAL" value="false" />
+    <option name="EXECUTE_SCRIPT_FILE" value="false" />
+    <envs />
+    <method v="2" />
+  </configuration>
+</component>
diff --git a/libraries/sts-common-util/sts-sdk/package/native-poc/build.gradle.template b/libraries/sts-common-util/sts-sdk/package/native-poc/build.gradle.template
index f10e314..dd2a03a 100644
--- a/libraries/sts-common-util/sts-sdk/package/native-poc/build.gradle.template
+++ b/libraries/sts-common-util/sts-sdk/package/native-poc/build.gradle.template
@@ -16,16 +16,17 @@
     }
 }
 
-task copyArm32(type: Copy) {
-    dependsOn 'externalNativeBuildDebug'
-    from layout.buildDirectory.file('intermediates/cmake/debug/obj/armeabi-v7a/nativepoc')
-    rename ('nativepoc', "${project.name.replaceFirst(/-native/, '')}_sts32")
-    into layout.buildDirectory.dir('testcases')
+ext.copyArtifact = { arch, suffix, outputDir ->
+    tasks.register("copy${arch}", Copy) {
+        dependsOn 'externalNativeBuildDebug'
+        from layout.buildDirectory.file('intermediates/cmake/debug/obj/armeabi-v7a/nativepoc')
+        rename ('nativepoc', "${project.name.replaceFirst(/-native/, '')}${suffix}")
+        into layout.buildDirectory.dir(outputDir)
+    }
 }
 
-task copyArm64(type: Copy) {
-    dependsOn 'externalNativeBuildDebug'
-    from layout.buildDirectory.file('intermediates/cmake/debug/obj/arm64-v8a/nativepoc')
-    rename ('nativepoc', "${project.name.replaceFirst(/-native/, '')}_sts64")
-    into layout.buildDirectory.dir('testcases')
-}
+copyArtifact('Arm32', '_sts32', 'testcases_arm')
+copyArtifact('Arm64', '_sts64', 'testcases_arm')
+copyArtifact('x86', '_sts32', 'testcases_x86')
+copyArtifact('x86_64', '_sts64', 'testcases_x86')
+
diff --git a/libraries/sts-common-util/sts-sdk/package/sts-test/src/main/java/android/security/sts/CVE_2020_0215.java b/libraries/sts-common-util/sts-sdk/package/sts-test/src/main/java/android/security/sts/StsHostSideTestCase.java
similarity index 75%
rename from libraries/sts-common-util/sts-sdk/package/sts-test/src/main/java/android/security/sts/CVE_2020_0215.java
rename to libraries/sts-common-util/sts-sdk/package/sts-test/src/main/java/android/security/sts/StsHostSideTestCase.java
index 7588fc8..03c01a8 100644
--- a/libraries/sts-common-util/sts-sdk/package/sts-test/src/main/java/android/security/sts/CVE_2020_0215.java
+++ b/libraries/sts-common-util/sts-sdk/package/sts-test/src/main/java/android/security/sts/StsHostSideTestCase.java
@@ -18,7 +18,6 @@
 
 import static com.android.sts.common.CommandUtil.runAndCheck;
 
-import android.platform.test.annotations.AsbSecurityTest;
 
 import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
 import com.android.tradefed.device.ITestDevice;
@@ -28,16 +27,15 @@
 import org.junit.Test;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2020_0215 extends StsExtraBusinessLogicHostTestBase {
+public class StsHostSideTestCase extends StsExtraBusinessLogicHostTestBase {
 
-    static final String TEST_APP = "CVE_2020_0215.apk";
-    static final String TEST_PKG = "android.security.sts.CVE_2020_0215";
+    static final String TEST_APP = "sts_test_app_package.apk";
+    static final String TEST_PKG = "android.security.sts.sts_test_app_package";
     static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
 
-    /** b/140417248 */
-    @AsbSecurityTest(cveBugId = 140417248)
     @Test
-    public void testPocCVE_2020_0215() throws Exception {
+    public void testPoc() throws Exception {
+        // Note: this test is for CVE-2020-0215
         ITestDevice device = getDevice();
         device.enableAdbRoot();
         uninstallPackage(device, TEST_PKG);
@@ -47,6 +45,7 @@
         runAndCheck(device, "input keyevent KEYCODE_HOME");
 
         installPackage(TEST_APP);
-        runDeviceTests(TEST_PKG, TEST_CLASS, "testCVE_2020_0215");
+        runDeviceTests(TEST_PKG, TEST_CLASS, "testDeviceSideMethod");
     }
 }
+
diff --git a/libraries/sts-common-util/sts-sdk/package/test-app/build.gradle b/libraries/sts-common-util/sts-sdk/package/test-app/build.gradle
index 4327486..17881cc 100644
--- a/libraries/sts-common-util/sts-sdk/package/test-app/build.gradle
+++ b/libraries/sts-common-util/sts-sdk/package/test-app/build.gradle
@@ -6,7 +6,7 @@
     compileSdk 31
 
     defaultConfig {
-        applicationId "android.security.sts.CVE_2020_0215"
+        applicationId "android.security.sts.sts_test_app_package"
         minSdk 29
         targetSdk 31
         versionCode 1
diff --git a/libraries/sts-common-util/sts-sdk/package/test-app/src/main/AndroidManifest.xml b/libraries/sts-common-util/sts-sdk/package/test-app/src/main/AndroidManifest.xml
index 884fdf5..b7f8ac8 100644
--- a/libraries/sts-common-util/sts-sdk/package/test-app/src/main/AndroidManifest.xml
+++ b/libraries/sts-common-util/sts-sdk/package/test-app/src/main/AndroidManifest.xml
@@ -16,12 +16,12 @@
   -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.security.sts.CVE_2020_0215"
+    package="android.security.sts.sts_test_app_package"
     android:versionCode="1"
     android:versionName="1.0">
     <instrumentation
         android:name="androidx.test.runner.AndroidJUnitRunner"
-        android:targetPackage="android.security.sts.CVE_2020_0215" />
+        android:targetPackage="android.security.sts.sts_test_app_package" />
     <application
         android:supportsRtl="true">
         <activity
diff --git a/libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/CVE_2020_0215/DeviceTest.java b/libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/sts_test_app_package/DeviceTest.java
similarity index 96%
rename from libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/CVE_2020_0215/DeviceTest.java
rename to libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/sts_test_app_package/DeviceTest.java
index bd2e8a7..da1f7bf 100644
--- a/libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/CVE_2020_0215/DeviceTest.java
+++ b/libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/sts_test_app_package/DeviceTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.security.sts.CVE_2020_0215;
+package android.security.sts.sts_test_app_package;
 
 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -46,7 +46,7 @@
     }
 
     @Test
-    public void testCVE_2020_0215() {
+    public void testDeviceSideMethod() {
         try {
             mAppContext = getApplicationContext();
             UiDevice device = UiDevice.getInstance(getInstrumentation());
diff --git a/libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/CVE_2020_0215/PocActivity.java b/libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/sts_test_app_package/PocActivity.java
similarity index 94%
rename from libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/CVE_2020_0215/PocActivity.java
rename to libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/sts_test_app_package/PocActivity.java
index 5825043..27d682d 100644
--- a/libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/CVE_2020_0215/PocActivity.java
+++ b/libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/sts_test_app_package/PocActivity.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.security.sts.CVE_2020_0215;
+package android.security.sts.sts_test_app_package;
 
 import android.app.Activity;
 import android.os.Bundle;
diff --git a/libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/CVE_2020_0215/PocReceiver.java b/libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/sts_test_app_package/PocReceiver.java
similarity index 96%
rename from libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/CVE_2020_0215/PocReceiver.java
rename to libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/sts_test_app_package/PocReceiver.java
index a7d3762..ac87925 100644
--- a/libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/CVE_2020_0215/PocReceiver.java
+++ b/libraries/sts-common-util/sts-sdk/package/test-app/src/main/java/android/security/sts/sts_test_app_package/PocReceiver.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.security.sts.CVE_2020_0215;
+package android.security.sts.sts_test_app_package;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
diff --git a/libraries/sts-common-util/sts-sdk/package/test-app/src/main/res/values/strings.xml b/libraries/sts-common-util/sts-sdk/package/test-app/src/main/res/values/strings.xml
index 85a302d..286e6fd 100644
--- a/libraries/sts-common-util/sts-sdk/package/test-app/src/main/res/values/strings.xml
+++ b/libraries/sts-common-util/sts-sdk/package/test-app/src/main/res/values/strings.xml
@@ -17,5 +17,5 @@
 
 <resources>
     <string name="RESULT_KEY">result</string>
-    <string name="SHARED_PREFERENCE">CVE_2020_0215</string>
+    <string name="SHARED_PREFERENCE">sts_test_app_failure</string>
 </resources>