Merge tag 'android-11.0.0_r38' of https://android.googlesource.com/platform//platform_testing into HEAD

Android 11.0.0 Release 38 (RQ3A.210605.005)

Change-Id: Icf12ab16e91e6002211120b735c3364132a9aee3
diff --git a/libraries/annotations/src/android/platform/test/annotations/RootPermissionTest.java b/libraries/annotations/src/android/platform/test/annotations/RootPermissionTest.java
index 25496ef..f520ef6 100644
--- a/libraries/annotations/src/android/platform/test/annotations/RootPermissionTest.java
+++ b/libraries/annotations/src/android/platform/test/annotations/RootPermissionTest.java
@@ -34,4 +34,10 @@
     // TODO : Remove the default value. Need one in the interim whilst we undertake
     // the effort to annotate the existing tests with a corresponding patch-level.
     String minPatchLevel() default "";
+
+    // Denotes the CVE ID(s), comma-separated, to which this test applies.
+    String cve() default "";
+
+    // Denotes the scope (platform/kernel/vendor) to which this test applies.
+    String scope() default "";
 }
diff --git a/libraries/annotations/src/android/platform/test/annotations/SecurityTest.java b/libraries/annotations/src/android/platform/test/annotations/SecurityTest.java
index 56ae4b2..1154c6f 100644
--- a/libraries/annotations/src/android/platform/test/annotations/SecurityTest.java
+++ b/libraries/annotations/src/android/platform/test/annotations/SecurityTest.java
@@ -33,4 +33,10 @@
     // TODO : Remove the default value. Need one in the interim whilst we undertake
     // the effort to annotate the existing tests with a corresponding patch-level.
     String minPatchLevel() default "";
+
+    // Denotes the CVE ID(s), comma-separated, to which this test applies.
+    String cve() default "";
+
+    // Denotes the scope (platform/kernel/vendor) to which this test applies.
+    String scope() default "";
 }
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoAppInfoSettingsHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoAppInfoSettingsHelper.java
index d08d310..cc6532b 100644
--- a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoAppInfoSettingsHelper.java
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoAppInfoSettingsHelper.java
@@ -83,4 +83,22 @@
      * Get the current enabled permission summary in String format for an application
      */
     String getCurrentPermissions();
+
+    /**
+     * Setup expectation: None
+     *
+     * <p>This method is to check if an application has been disabled.
+     *
+     * @param packageName - package of the application to be checked.
+     */
+    boolean isApplicationDisabled(String packageName);
+
+    /**
+     * Setup expectation: None
+     *
+     * <p>This method is to check open an application.
+     *
+     * @param appName - Name of the app to be opened.
+     */
+    void openApp(String packageName);
 }
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoDialHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoDialHelper.java
index 77ec8a3..37cd8d8 100644
--- a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoDialHelper.java
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoDialHelper.java
@@ -230,4 +230,17 @@
      * <p>This method is used to check if phone is paired.
      */
     boolean isPhonePaired();
+    /**
+     * Setup expectations: The app is open.
+     *
+     * <p>This method is used to open contact list
+     */
+    void openContacts();
+
+    /**
+     * This method is used to open the Phone App
+     *
+     * @param No parameters.
+     */
+    void OpenPhoneApp();
 }
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoGooglePlayHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoGooglePlayHelper.java
index d3330db..331eec3 100644
--- a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoGooglePlayHelper.java
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoGooglePlayHelper.java
@@ -19,6 +19,13 @@
 public interface IAutoGooglePlayHelper extends IAppHelper, Scrollable {
 
     /**
+     * Setup expectations: Launch Google Play Store app.
+     *
+     * <p>This method is used to Open Google Play Store app.
+     */
+    void openGooglePlayStore();
+
+    /**
      * Setup expectations: Google Play app is open.
      *
      * <p>This method is used to search an app and click it in Google Play.
@@ -60,4 +67,11 @@
      */
     @Deprecated
     void openApp();
+
+    /**
+     * Setup expectations: None.
+     *
+     * <p>This method is used to check if the given application package is installed.
+     */
+    boolean checkIfApplicationExists(String packageName);
 }
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaCenterHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaCenterHelper.java
index 92180a6..81d53d8 100644
--- a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaCenterHelper.java
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaCenterHelper.java
@@ -110,4 +110,11 @@
      * @param - menuOptions used to pass multiple level of menu options in one go.
      */
     void selectMediaTrack(String... menuOptions);
+
+    /**
+     * Setup expectations: Launch Media App.
+     *
+     * @param - String packagename - Android media package to be opened.
+     */
+    void openMediaApp(String packagename);
 }
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaHelper.java
index 569c5a3..960921f 100644
--- a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaHelper.java
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaHelper.java
@@ -128,4 +128,11 @@
      * else returns false
      */
     boolean isPlaying();
+
+    /**
+     * Setup expectations: Launch Media App.
+     *
+     * @param - String packagename - Android media package to be opened.
+     */
+    void openMediaApp(String packagename);
 }
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoSettingHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoSettingHelper.java
index 2c1e527..73ffb18 100644
--- a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoSettingHelper.java
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoSettingHelper.java
@@ -237,4 +237,20 @@
      * @param name of the setting menu.
      */
     boolean isSettingMenuEnabled(String menu);
+
+    /**
+     * Setup expectations: Setting is open.
+     *
+     * <p>Get the current page title text.
+     */
+    String getPageTitleText();
+
+    /**
+     * Setup expectations: Setting is open.
+     *
+     * <p>Find the setting menu and perform a click action.
+     *
+     * @param name of the setting menu.
+     */
+    void findSettingMenuAndClick(String setting);
 }
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/utility/IAutoNavigationBarHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/utility/IAutoNavigationBarHelper.java
new file mode 100644
index 0000000..227d29f
--- /dev/null
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/utility/IAutoNavigationBarHelper.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.helpers;
+
+public interface IAutoNavigationBarHelper extends IAppHelper {
+    /**
+     * Setup expectation: None.
+     *
+     * <p>This method is to open Home page using Facet.
+     */
+    void openHomeFacet();
+    /**
+     * Setup expectation: None.
+     *
+     * <p>This method is to open Maps application using Facet.
+     */
+    void openMapsFacet();
+    /**
+     * Setup expectation: None.
+     *
+     * <p>This method is to open Media application using Facet.
+     */
+    void openMediaFacet();
+    /**
+     * Setup expectation: Media Application is open.
+     *
+     * <p>This method is to open given Media application.
+     */
+    void openMediaFacet(String appName);
+    /**
+     * Setup expectation: None.
+     *
+     * <p>This method is to open Dial application using Facet.
+     */
+    void openDialFacet();
+    /**
+     * Setup expectation: None.
+     *
+     * <p>This method is to open App Grid using Facet.
+     */
+    void openAppGridFacet();
+    /**
+     * Setup expectation: None.
+     *
+     * <p>This method is to open Notifications using Facet.
+     */
+    void openNotificationsFacet();
+    /**
+     * Setup expectation: None.
+     *
+     * <p>This method is to open Assistant application using Facet.
+     */
+    void openAssistantFacet();
+    /**
+     * Setup expectation: None.
+     *
+     * <p>This method is to open Quick Settings.
+     */
+    void openQuickSettings();
+}
diff --git a/libraries/app-helpers/interfaces/common/src/android/platform/helpers/INewsHelper.java b/libraries/app-helpers/interfaces/common/src/android/platform/helpers/INewsHelper.java
index a06b058..34e6d45 100644
--- a/libraries/app-helpers/interfaces/common/src/android/platform/helpers/INewsHelper.java
+++ b/libraries/app-helpers/interfaces/common/src/android/platform/helpers/INewsHelper.java
@@ -16,7 +16,9 @@
 
 package android.platform.helpers;
 
+import android.graphics.Rect;
 import android.support.test.uiautomator.Direction;
+import android.support.test.uiautomator.UiObject2;
 
 public interface INewsHelper extends IAppHelper {
     /**
@@ -25,4 +27,22 @@
      * <p>Scroll the page by specified direction.
      */
     public void scrollPage(Direction direction);
+
+    /**
+     * Setup expectation: On the home screen.
+     *
+     * <p>Get the UiObject2 of News scroll container.
+     */
+    public default UiObject2 getNewsScrollContainer() {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
+    /**
+     * Setup expectation: On the home screen.
+     *
+     * <p>Scroll the page to view the news.
+     */
+    public default void scrollPage(Rect bounds, Direction dir, float percent) {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
 }
diff --git a/libraries/app-helpers/interfaces/common/src/android/platform/helpers/ISettingsIntelligenceHelper.java b/libraries/app-helpers/interfaces/common/src/android/platform/helpers/ISettingsIntelligenceHelper.java
index 6f9e551..bb26e95 100644
--- a/libraries/app-helpers/interfaces/common/src/android/platform/helpers/ISettingsIntelligenceHelper.java
+++ b/libraries/app-helpers/interfaces/common/src/android/platform/helpers/ISettingsIntelligenceHelper.java
@@ -18,6 +18,21 @@
 
 public interface ISettingsIntelligenceHelper extends IAppHelper {
 
+    public static final String PAGE_ACTION_HOME = "";
+    public static final String PAGE_ACTION_APPLICATION = "android.settings.APPLICATION_SETTINGS";
+    public static final String PAGE_ACTION_BATTERY = "android.intent.action.POWER_USAGE_SUMMARY";
+    public static final String PAGE_ACTION_BLUETOOTH = "android.settings.BLUETOOTH_SETTINGS";
+    public static final String PAGE_ACTION_LOCATION = "android.settings.LOCATION_SOURCE_SETTINGS";
+    public static final String PAGE_ACTION_STORAGE = "android.settings.INTERNAL_STORAGE_SETTINGS";
+    public static final String PAGE_ACTION_WIFI = "android.settings.WIFI_SETTINGS";
+
+    /**
+     * Sets the action representing the Settings page to open when open() is called.
+     *
+     * @param pageAction One of the PAGE_ACTION* constants.
+     */
+    void setPageAction(String pageAction);
+
     /**
      * Setup expectations: Settings search page is open
      *
@@ -31,4 +46,4 @@
      * <p>This method opens search page.
      */
     void openSearch();
-}
+}
\ No newline at end of file
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/GcaEventLogCollector.java b/libraries/device-collectors/src/main/java/android/device/collectors/GcaEventLogCollector.java
new file mode 100644
index 0000000..c75b660
--- /dev/null
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/GcaEventLogCollector.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.device.collectors;
+
+import android.device.collectors.annotations.OptionClass;
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Strings;
+
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+
+/**
+ * A {@link GcaEventLogCollector} that captures Google Camera App (GCA) on-device event log protos
+ * and save them to external storage at run_listeners/camera_events directory. This collector
+ * disables selinux at the start of the test run and recover at the end. So if your tests require
+ * selinux enforcing, please do not use this collector.
+ */
+@OptionClass(alias = "gca-proto-log-collector")
+public class GcaEventLogCollector extends BaseMetricListener {
+
+    private static final String TAG = GcaEventLogCollector.class.getSimpleName();
+    private static final String DEST_DIR = "run_listeners/camera_events";
+    private static final String CAMERA_EVENT_LOG_PATTERN =
+            "/data/user/0/%s/files/camera_events/session.pb";
+
+    @VisibleForTesting
+    static final String COLLECT_TEST_FAILURE_CAMERA_LOGS = "collect_test_failure_camera_logs";
+
+    @VisibleForTesting
+    static final String COLLECT_CAMERA_LOGS_PER_RUN = "collect_camera_logs_per_run";
+
+    private static final String GOOGLE_CAMERA_APP_PACKAGE = "google_camera_app_package";
+
+    enum SeLinuxEnforceProperty {
+        ENFORCING("1"),
+        PERMISSIVE("0");
+
+        private String property;
+
+        public String getProperty() {
+            return property;
+        }
+
+        private SeLinuxEnforceProperty(String property) {
+            this.property = property;
+        }
+    }
+
+    private String mGcaPkg = "com.google.android.GoogleCamera";
+    private boolean mIsCollectPerRun = false;
+    private boolean mCollectMetricsForFailedTest = false;
+    private boolean mIsTestFailed;
+    private File mDestDir;
+    private String mEventLogPath;
+    private SeLinuxEnforceProperty mOrigSeLinuxEnforceProp;
+    private String mOrigCameraEventProp;
+
+    // Map to keep track of test iterations for multiple test iterations.
+    private HashMap<Description, Integer> mTestIterations = new HashMap<>();
+
+    public GcaEventLogCollector() {
+        super();
+    }
+
+    @VisibleForTesting
+    GcaEventLogCollector(Bundle args) {
+        super(args);
+    }
+
+    @Override
+    public void onTestRunStart(DataRecord runData, Description description) {
+        setupAdditionalArgs();
+        mDestDir = createAndEmptyDirectory(DEST_DIR);
+        mEventLogPath = String.format(CAMERA_EVENT_LOG_PATTERN, mGcaPkg);
+        cleanUpEventLog();
+        mOrigSeLinuxEnforceProp = getSeLinuxEnforceProperty();
+        mOrigCameraEventProp = getCameraEventLogProperty();
+        setSeLinuxEnforceProperty(SeLinuxEnforceProperty.PERMISSIVE);
+        setCameraEventLogProperty("1");
+        killGoogleCameraApp();
+    }
+
+    @Override
+    public void onTestRunEnd(DataRecord runData, Result result) {
+        try {
+            if (mIsCollectPerRun) {
+                collectEventLog(runData, null);
+            }
+            setCameraEventLogProperty(mOrigCameraEventProp);
+            killGoogleCameraApp();
+        } finally {
+            setSeLinuxEnforceProperty(mOrigSeLinuxEnforceProp);
+        }
+    }
+
+    @Override
+    public void onTestStart(DataRecord runData, Description description) {
+        mIsTestFailed = false;
+        if (!mIsCollectPerRun) {
+            killGoogleCameraApp();
+            cleanUpEventLog();
+        }
+        // Keep track of test iterations.
+        mTestIterations.computeIfPresent(description, (desc, iteration) -> iteration + 1);
+        mTestIterations.computeIfAbsent(description, desc -> 1);
+    }
+
+    @Override
+    public void onTestEnd(DataRecord testData, Description description) {
+        if (mIsCollectPerRun) {
+            return;
+        }
+        if (!mCollectMetricsForFailedTest && mIsTestFailed) {
+            return;
+        }
+        collectEventLog(testData, description);
+    }
+
+    @Override
+    public void onTestFail(DataRecord runData, Description description, Failure failure) {
+        mIsTestFailed = true;
+    }
+
+    private void setupAdditionalArgs() {
+        Bundle args = getArgsBundle();
+        if (!Strings.isNullOrEmpty(args.getString(GOOGLE_CAMERA_APP_PACKAGE))) {
+            mGcaPkg = args.getString(GOOGLE_CAMERA_APP_PACKAGE);
+        }
+        mIsCollectPerRun =
+                Boolean.parseBoolean(args.getString(COLLECT_CAMERA_LOGS_PER_RUN, "false"));
+        mCollectMetricsForFailedTest =
+                Boolean.parseBoolean(args.getString(COLLECT_TEST_FAILURE_CAMERA_LOGS, "false"));
+    }
+
+    private void collectEventLog(DataRecord testData, Description description) {
+        logListResult(mEventLogPath);
+        String fileName = buildCollectedEventLogFileName(description);
+        Log.d(TAG, String.format("Destination event log file name: %s", fileName));
+        copyEventLogToSharedStorage(fileName);
+        testData.addFileMetric(fileName, new File(mDestDir, fileName));
+    }
+
+    @VisibleForTesting
+    protected void copyEventLogToSharedStorage(String fileName) {
+        String cmd =
+                String.format("cp %s %s/%s", mEventLogPath, mDestDir.getAbsolutePath(), fileName);
+        Log.d(TAG, "Copy command: " + cmd);
+        executeCommandBlocking(cmd);
+        logListResult(mDestDir.getAbsolutePath());
+    }
+
+    private void cleanUpEventLog() {
+        executeCommandBlocking("rm " + mEventLogPath);
+        logListResult(mEventLogPath);
+    }
+
+    private String getCameraEventLogProperty() {
+        String res =
+                new String(
+                                executeCommandBlocking("getprop camera.use_local_logger"),
+                                StandardCharsets.UTF_8)
+                        .trim();
+        return res.isEmpty() ? "0" : res;
+    }
+
+    private void setCameraEventLogProperty(String value) {
+        if (!getCameraEventLogProperty().equals(value)) {
+            Log.d(TAG, "Setting property camera.use_local_logger to " + value);
+            executeCommandBlocking("setprop camera.use_local_logger " + value);
+        }
+    }
+
+    private void killGoogleCameraApp() {
+        executeCommandBlocking("am force-stop " + mGcaPkg);
+    }
+
+    private SeLinuxEnforceProperty getSeLinuxEnforceProperty() {
+        String res =
+                new String(executeCommandBlocking("getenforce"), StandardCharsets.UTF_8).trim();
+        return SeLinuxEnforceProperty.valueOf(res.toUpperCase());
+    }
+
+    private void setSeLinuxEnforceProperty(SeLinuxEnforceProperty value) {
+        if (getSeLinuxEnforceProperty() != value) {
+            Log.d(TAG, "Setting SeLinux enforcing to " + value.getProperty());
+            executeCommandBlocking("setenforce " + value.getProperty());
+        }
+    }
+
+    private String buildCollectedEventLogFileName(@Nullable Description description) {
+        long timestamp = System.currentTimeMillis();
+        if (description == null) {
+            return String.format("session_run_%d.pb", timestamp);
+        }
+        int iteration = mTestIterations.get(description);
+        return String.format(
+                "session_%s_%s%s_%d.pb",
+                description.getClassName(),
+                description.getMethodName(),
+                iteration == 1 ? "" : ("_" + String.valueOf(iteration)),
+                timestamp);
+    }
+
+    private void logListResult(String targetPath) {
+        byte[] lsResult = executeCommandBlocking("ls " + targetPath);
+        Log.d(
+                TAG,
+                String.format(
+                        "List path %s result: %s",
+                        targetPath, new String(lsResult, StandardCharsets.UTF_8)));
+    }
+}
diff --git a/libraries/device-collectors/src/test/java/Android.bp b/libraries/device-collectors/src/test/java/Android.bp
index 025b393..244e1bd 100644
--- a/libraries/device-collectors/src/test/java/Android.bp
+++ b/libraries/device-collectors/src/test/java/Android.bp
@@ -21,6 +21,7 @@
 
     srcs: ["android/**/*.java"],
     static_libs: [
+        "androidx.test.ext.junit",
         "androidx.test.runner",
         "collector-device-lib",
         "junit",
diff --git a/libraries/device-collectors/src/test/java/android/device/collectors/GcaEventLogCollectorTest.java b/libraries/device-collectors/src/test/java/android/device/collectors/GcaEventLogCollectorTest.java
new file mode 100644
index 0000000..b06f686
--- /dev/null
+++ b/libraries/device-collectors/src/test/java/android/device/collectors/GcaEventLogCollectorTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.device.collectors;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.times;
+
+import android.app.Instrumentation;
+import android.device.collectors.util.SendToInstrumentation;
+import android.os.Bundle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.RunWith;
+import org.junit.runner.notification.Failure;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class GcaEventLogCollectorTest {
+
+    private static final Description RUN_DESCRIPTION =
+            Description.createSuiteDescription("test-run");
+    private static final Description TEST_DESCRIPTION =
+            Description.createTestDescription("TestClass", "testCase1");
+    private static final Description TEST_FAILURE_DESCRIPTION =
+            Description.createTestDescription("TestClass", "testCaseFailed");
+    @Mock private Instrumentation mMockInstrumentation;
+    private File mEventLogDir;
+    private File mDestDir;
+    private GcaEventLogCollector mCollector;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mEventLogDir = new File("/sdcard", "camera_events");
+        mDestDir = new File("/sdcard", "camera_events_dest");
+    }
+
+    @After
+    public void tearDown() {
+        mCollector.recursiveDelete(mEventLogDir);
+        mCollector.recursiveDelete(mDestDir);
+    }
+
+    private GcaEventLogCollector initListener(Bundle bundle) {
+        mCollector = new GcaEventLogCollector(bundle);
+        GcaEventLogCollector listener = Mockito.spy(mCollector);
+        listener.setInstrumentation(mMockInstrumentation);
+        Mockito.doReturn(mDestDir).when(listener).createAndEmptyDirectory(Mockito.anyString());
+        Mockito.doReturn(new byte[10]).when(listener).executeCommandBlocking(Mockito.anyString());
+        Mockito.doNothing().when(listener).copyEventLogToSharedStorage(Mockito.anyString());
+        return listener;
+    }
+
+    @Test
+    public void testGcaEventLogCollectionOnTestEnd_includeFailure() throws Exception {
+        Bundle bundle = new Bundle();
+        bundle.putString(GcaEventLogCollector.COLLECT_TEST_FAILURE_CAMERA_LOGS, "true");
+        GcaEventLogCollector listener = initListener(bundle);
+
+        listener.testRunStarted(RUN_DESCRIPTION);
+        // Test case 1
+        listener.testStarted(TEST_DESCRIPTION);
+        listener.testFinished(TEST_DESCRIPTION);
+        // Test case 2 - Failed
+        listener.testStarted(TEST_FAILURE_DESCRIPTION);
+        Failure failure =
+                new Failure(TEST_FAILURE_DESCRIPTION, new RuntimeException("Test Failed"));
+        listener.testFailure(failure);
+        listener.testFinished(TEST_FAILURE_DESCRIPTION);
+        listener.testRunFinished(new Result());
+
+        Bundle resultBundle = new Bundle();
+        listener.instrumentationRunFinished(System.out, resultBundle, new Result());
+        assertEquals(0, resultBundle.size());
+
+        ArgumentCaptor<Bundle> capture = ArgumentCaptor.forClass(Bundle.class);
+        Mockito.verify(mMockInstrumentation, times(2))
+                .sendStatus(
+                        Mockito.eq(SendToInstrumentation.INST_STATUS_IN_PROGRESS),
+                        capture.capture());
+        List<Bundle> capturedBundle = capture.getAllValues();
+        // Include the log collected from the failed test.
+        assertEquals(2, capturedBundle.size());
+        Bundle check = capturedBundle.get(0);
+        assertEquals(1, check.size());
+        for (String key : check.keySet()) {
+            assertTrue(
+                    key.contains(
+                            String.format(
+                                    "session_%s_%s",
+                                    TEST_DESCRIPTION.getClassName(),
+                                    TEST_DESCRIPTION.getMethodName())));
+        }
+        Bundle checkFail = capturedBundle.get(1);
+        assertEquals(1, checkFail.size());
+        for (String key : checkFail.keySet()) {
+            assertTrue(
+                    key.contains(
+                            String.format(
+                                    "session_%s_%s",
+                                    TEST_FAILURE_DESCRIPTION.getClassName(),
+                                    TEST_FAILURE_DESCRIPTION.getMethodName())));
+        }
+    }
+
+    @Test
+    public void testGcaEventLogCollectOnTestEnd_skipFailure() throws Exception {
+        Bundle bundle = new Bundle();
+        bundle.putString(GcaEventLogCollector.COLLECT_TEST_FAILURE_CAMERA_LOGS, "false");
+        GcaEventLogCollector listener = initListener(bundle);
+
+        listener.testRunStarted(RUN_DESCRIPTION);
+        // 1st iteration
+        listener.testStarted(TEST_DESCRIPTION);
+        listener.testFinished(TEST_DESCRIPTION);
+        // 2nd iteration
+        listener.testStarted(TEST_DESCRIPTION);
+        listener.testFinished(TEST_DESCRIPTION);
+        // Failed test
+        listener.testStarted(TEST_FAILURE_DESCRIPTION);
+        Failure failure =
+                new Failure(TEST_FAILURE_DESCRIPTION, new RuntimeException("Test Failed"));
+        listener.testFailure(failure);
+        listener.testFinished(TEST_FAILURE_DESCRIPTION);
+        listener.testRunFinished(new Result());
+
+        Bundle resultBundle = new Bundle();
+        listener.instrumentationRunFinished(System.out, resultBundle, new Result());
+
+        assertEquals(0, resultBundle.size());
+
+        ArgumentCaptor<Bundle> capture = ArgumentCaptor.forClass(Bundle.class);
+        Mockito.verify(mMockInstrumentation, times(2))
+                .sendStatus(
+                        Mockito.eq(SendToInstrumentation.INST_STATUS_IN_PROGRESS),
+                        capture.capture());
+        List<Bundle> capturedBundle = capture.getAllValues();
+        // Include the log collected from the failed test.
+        assertEquals(2, capturedBundle.size());
+        Bundle check1 = capturedBundle.get(0);
+        assertEquals(1, check1.size());
+        for (String key : check1.keySet()) {
+            assertTrue(
+                    key.contains(
+                            String.format(
+                                    "session_%s_%s",
+                                    TEST_DESCRIPTION.getClassName(),
+                                    TEST_DESCRIPTION.getMethodName())));
+        }
+        Bundle check2 = capturedBundle.get(1);
+        assertEquals(1, check2.size());
+        for (String key : check2.keySet()) {
+            assertTrue(
+                    key.contains(
+                            String.format(
+                                    "session_%s_%s_2_",
+                                    TEST_DESCRIPTION.getClassName(),
+                                    TEST_DESCRIPTION.getMethodName())));
+        }
+    }
+
+    @Test
+    public void testGcaEventLogCollectionOnTestRunEnd_runEndOnly() throws Exception {
+        Bundle bundle = new Bundle();
+        bundle.putString(GcaEventLogCollector.COLLECT_CAMERA_LOGS_PER_RUN, "true");
+        GcaEventLogCollector listener = initListener(bundle);
+
+        listener.testRunStarted(RUN_DESCRIPTION);
+        // Test case 1
+        listener.testStarted(TEST_DESCRIPTION);
+        listener.testFinished(TEST_DESCRIPTION);
+        // Test case 2
+        listener.testStarted(TEST_FAILURE_DESCRIPTION);
+        Failure failure =
+                new Failure(TEST_FAILURE_DESCRIPTION, new RuntimeException("Test Failed"));
+        listener.testFailure(failure);
+        listener.testFinished(TEST_FAILURE_DESCRIPTION);
+        listener.testRunFinished(new Result());
+
+        Bundle resultBundle = new Bundle();
+        listener.instrumentationRunFinished(System.out, resultBundle, new Result());
+
+        assertEquals(1, resultBundle.size());
+        for (String key : resultBundle.keySet()) {
+            assertTrue(key.contains("session_run_"));
+        }
+    }
+}
diff --git a/libraries/health/runners/microbenchmark/src/android/platform/test/microbenchmark/Microbenchmark.java b/libraries/health/runners/microbenchmark/src/android/platform/test/microbenchmark/Microbenchmark.java
index 78a03e8..ad12eb8 100644
--- a/libraries/health/runners/microbenchmark/src/android/platform/test/microbenchmark/Microbenchmark.java
+++ b/libraries/health/runners/microbenchmark/src/android/platform/test/microbenchmark/Microbenchmark.java
@@ -15,9 +15,16 @@
  */
 package android.platform.test.microbenchmark;
 
+import static android.content.Context.BATTERY_SERVICE;
+import static android.os.BatteryManager.BATTERY_PROPERTY_CAPACITY;
+import static android.os.BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER;
+
+import android.os.BatteryManager;
 import android.os.Bundle;
+import android.os.SystemClock;
 import android.platform.test.composer.Iterate;
 import android.platform.test.rule.TracePointRule;
+import android.util.Log;
 import androidx.annotation.VisibleForTesting;
 import androidx.test.InstrumentationRegistry;
 
@@ -50,6 +57,8 @@
  */
 public class Microbenchmark extends BlockJUnit4ClassRunner {
 
+    private static final String LOG_TAG = Microbenchmark.class.getSimpleName();
+
     @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.
@@ -60,12 +69,28 @@
                 @Override
                 public void evaluate() throws Throwable {}
             };
+    private static final String MIN_BATTERY_LEVEL_OPTION = "min-battery";
+
+    // Options for aligning with the battery charge (coulomb) counter for power tests. We want to
+    // start microbenchmarks just after the coulomb counter has decremented to account for the
+    // counter being quantized. The counter most accurately reflects the true value just after it
+    // decrements.
+    private static final String ALIGN_WITH_CHARGE_COUNTER_OPTION = "align-with-charge-counter";
+    private static final String COUNTER_DECREMENT_TIMEOUT_OPTION = "counter-decrement-timeout_ms";
+
+    private static final String TERMINATE_ON_TEST_FAIL_OPTION = "terminate-on-test-fail";
 
     private final String mIterationSep;
     private final Bundle mArguments;
     private final boolean mRenameIterations;
+    private final int mMinBatteryLevel;
+    private final int mCounterDecrementTimeoutMs;
+    private final boolean mAlignWithChargeCounter;
+    private final boolean mTerminateOnTestFailure;
     private final Map<Description, Integer> mIterations = new HashMap<>();
 
+    private final BatteryManager mBatteryManager;
+
     /**
      * Called reflectively on classes annotated with {@code @RunWith(Microbenchmark.class)}.
      */
@@ -73,9 +98,7 @@
         this(klass, InstrumentationRegistry.getArguments());
     }
 
-    /**
-     * Do not call. Called explicitly from tests to provide an arguments.
-     */
+    /** Do not call. Called explicitly from tests to provide an arguments. */
     @VisibleForTesting
     Microbenchmark(Class<?> klass, Bundle arguments) throws InitializationError {
         super(klass);
@@ -86,6 +109,48 @@
                 arguments.containsKey(ITERATION_SEP_OPTION)
                         ? arguments.getString(ITERATION_SEP_OPTION)
                         : ITERATION_SEP_DEFAULT;
+        mMinBatteryLevel = Integer.parseInt(arguments.getString(MIN_BATTERY_LEVEL_OPTION, "-1"));
+        mCounterDecrementTimeoutMs =
+                Integer.parseInt(arguments.getString(COUNTER_DECREMENT_TIMEOUT_OPTION, "30000"));
+        mAlignWithChargeCounter =
+                Boolean.parseBoolean(
+                        arguments.getString(ALIGN_WITH_CHARGE_COUNTER_OPTION, "false"));
+
+        mTerminateOnTestFailure =
+                Boolean.parseBoolean(
+                        arguments.getString(TERMINATE_ON_TEST_FAIL_OPTION, "false"));
+
+        // Get the battery manager for later use.
+        mBatteryManager =
+                (BatteryManager)
+                        InstrumentationRegistry.getContext().getSystemService(BATTERY_SERVICE);
+    }
+
+    @Override
+    public void run(final RunNotifier notifier) {
+        if (mAlignWithChargeCounter) {
+            // Try to wait until the coulomb counter has just decremented to start the test.
+            int startChargeCounter = getBatteryChargeCounter();
+            long startTimestamp = SystemClock.uptimeMillis();
+            while (startChargeCounter == getBatteryChargeCounter()) {
+                if (SystemClock.uptimeMillis() - startTimestamp > mCounterDecrementTimeoutMs) {
+                    Log.d(
+                            LOG_TAG,
+                            "Timed out waiting for the counter to change. Continuing anyway.");
+                    break;
+                } else {
+                    Log.d(
+                            LOG_TAG,
+                            String.format(
+                                    "Charge counter still reads: %d. Waiting.",
+                                    startChargeCounter));
+                    SystemClock.sleep(getCounterPollingInterval());
+                }
+            }
+        }
+        Log.d(LOG_TAG, String.format("The charge counter reads: %d.", getBatteryChargeCounter()));
+
+        super.run(notifier);
     }
 
     /**
@@ -228,6 +293,10 @@
      */
     @Override
     protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
+        if (isBatteryLevelTooLow()) {
+            throw new TerminateEarlyException("battery level is below threshold.");
+        }
+
         // Update the number of iterations this method has been run.
         if (mRenameIterations) {
             Description original = super.describeChild(method);
@@ -261,6 +330,9 @@
                 eachNotifier.fireTestStarted();
                 eachNotifier.addFailure(e);
                 eachNotifier.fireTestFinished();
+                if(mTerminateOnTestFailure) {
+                    throw new TerminateEarlyException("test failed.");
+                }
                 return;
             }
 
@@ -271,14 +343,17 @@
             statement = withAfters(method, test, statement);
             statement = withRules(method, test, statement);
 
+            boolean testFailed = false;
             // Fire test events from inside to exclude "no metric" methods.
             eachNotifier.fireTestStarted();
             try {
                 statement.evaluate();
             } catch (AssumptionViolatedException e) {
                 eachNotifier.addFailedAssumption(e);
+                testFailed = true;
             } catch (Throwable e) {
                 eachNotifier.addFailure(e);
+                testFailed = true;
             } finally {
                 eachNotifier.fireTestFinished();
             }
@@ -292,9 +367,44 @@
                 }
             } catch (AssumptionViolatedException e) {
                 eachNotifier.addFailedAssumption(e);
+                testFailed = true;
             } catch (Throwable e) {
                 eachNotifier.addFailure(e);
+                testFailed = true;
+            }
+
+            if(mTerminateOnTestFailure && testFailed) {
+                throw new TerminateEarlyException("test failed.");
             }
         }
     }
+
+    /* Checks if the battery level is below the specified level where the test should terminate. */
+    @VisibleForTesting
+    public boolean isBatteryLevelTooLow() {
+        return mBatteryManager.getIntProperty(BATTERY_PROPERTY_CAPACITY) < mMinBatteryLevel;
+    }
+
+    /* Gets the current battery charge counter (coulomb counter). */
+    @VisibleForTesting
+    public int getBatteryChargeCounter() {
+        return mBatteryManager.getIntProperty(BATTERY_PROPERTY_CHARGE_COUNTER);
+    }
+
+    /* Gets the polling interval to check for changes in the battery charge counter. */
+    @VisibleForTesting
+    public long getCounterPollingInterval() {
+        return 100;
+    }
+
+    /**
+     * A {@code RuntimeException} class for terminating test runs early for some specified reason.
+     */
+    @VisibleForTesting
+    static class TerminateEarlyException extends RuntimeException {
+        public TerminateEarlyException(String message) {
+            super(String.format("Terminating early because %s", message));
+        }
+    }
+
 }
diff --git a/libraries/health/runners/microbenchmark/tests/Android.bp b/libraries/health/runners/microbenchmark/tests/Android.bp
index 3e7c103..df83ba5 100644
--- a/libraries/health/runners/microbenchmark/tests/Android.bp
+++ b/libraries/health/runners/microbenchmark/tests/Android.bp
@@ -18,6 +18,7 @@
     sdk_version: "test_current",
     static_libs: [
         "microbenchmark-device-lib",
+        "mockito-target",
         "truth-prebuilt",
     ],
     srcs: ["src/**/*.java"],
diff --git a/libraries/health/runners/microbenchmark/tests/src/android/platform/test/microbenchmark/MicrobenchmarkTest.java b/libraries/health/runners/microbenchmark/tests/src/android/platform/test/microbenchmark/MicrobenchmarkTest.java
index 9a43f8c..a497a42 100644
--- a/libraries/health/runners/microbenchmark/tests/src/android/platform/test/microbenchmark/MicrobenchmarkTest.java
+++ b/libraries/health/runners/microbenchmark/tests/src/android/platform/test/microbenchmark/MicrobenchmarkTest.java
@@ -16,8 +16,15 @@
 package android.platform.test.microbenchmark;
 
 import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 
 import android.os.Bundle;
+import android.os.SystemClock;
+import android.platform.test.microbenchmark.Microbenchmark.TerminateEarlyException;
 import android.platform.test.rule.TracePointRule;
 
 import org.junit.After;
@@ -28,9 +35,13 @@
 import org.junit.runner.JUnitCore;
 import org.junit.runner.Result;
 import org.junit.runner.RunWith;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunNotifier;
 import org.junit.runners.JUnit4;
 import org.junit.runners.model.InitializationError;
 import org.junit.runners.model.Statement;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -220,6 +231,152 @@
                 .inOrder();
     }
 
+    /** Test that the microbenchmark will terminate if the battery is too low. */
+    @Test
+    public void testBatteryIsTooLow() throws InitializationError {
+        Microbenchmark runner = Mockito.spy(new Microbenchmark(LoggingTest.class, new Bundle()));
+        doReturn(true).when(runner).isBatteryLevelTooLow();
+
+        RunNotifier notifier = Mockito.mock(RunNotifier.class);
+        runner.run(notifier);
+
+        ArgumentCaptor<Failure> failureCaptor = ArgumentCaptor.forClass(Failure.class);
+        verify(notifier).fireTestFailure(failureCaptor.capture());
+
+        Failure failure = failureCaptor.getValue();
+        Throwable throwable = failure.getException();
+        assertTrue(
+                String.format(
+                        "Exception was not a TerminateEarlyException. Instead, it was: %s",
+                        throwable.getClass()),
+                throwable instanceof TerminateEarlyException);
+        assertThat(throwable)
+                .hasMessageThat()
+                .matches("Terminating early because battery level is below threshold.");
+    }
+
+    /** Test that the microbenchmark will align starting with the battery charge counter. */
+    @Test
+    public void testAlignWithBatteryChargeCounter() throws InitializationError {
+        Bundle args = new Bundle();
+        args.putString("align-with-charge-counter", "true");
+        args.putString("counter-decrement-timeout_ms", "5000");
+
+        Microbenchmark runner = Mockito.spy(new Microbenchmark(LoggingTest.class, args));
+        doReturn(99999)
+                .doReturn(99999)
+                .doReturn(99999)
+                .doReturn(88888)
+                .when(runner)
+                .getBatteryChargeCounter();
+        doReturn(10L).when(runner).getCounterPollingInterval();
+
+        RunNotifier notifier = Mockito.mock(RunNotifier.class);
+
+        Thread thread =
+                new Thread(
+                        new Runnable() {
+                            public void run() {
+                                runner.run(notifier);
+                            }
+                        });
+
+        thread.start();
+        SystemClock.sleep(20);
+        verify(notifier, never()).fireTestStarted(any(Description.class));
+        SystemClock.sleep(20);
+        verify(notifier).fireTestStarted(any(Description.class));
+    }
+
+    /** Test that the microbenchmark counter alignment will time out if there's no change. */
+    @Test
+    public void testAlignWithBatteryChargeCounter_timesOut() throws InitializationError {
+        Bundle args = new Bundle();
+        args.putString("align-with-charge-counter", "true");
+        args.putString("counter-decrement-timeout_ms", "30");
+
+        Microbenchmark runner = Mockito.spy(new Microbenchmark(LoggingTest.class, args));
+        doReturn(99999).when(runner).getBatteryChargeCounter();
+        doReturn(10L).when(runner).getCounterPollingInterval();
+
+        RunNotifier notifier = Mockito.mock(RunNotifier.class);
+
+        Thread thread =
+                new Thread(
+                        new Runnable() {
+                            public void run() {
+                                runner.run(notifier);
+                            }
+                        });
+
+        thread.start();
+        SystemClock.sleep(20);
+        verify(notifier, never()).fireTestStarted(any(Description.class));
+        SystemClock.sleep(30);
+        verify(notifier).fireTestStarted(any(Description.class));
+    }
+
+    /**
+     * Test successive iteration will not be executed when the terminate on test fail
+     * option is enabled.
+     */
+    @Test
+    public void testTerminateOnTestFailOptionEnabled() throws InitializationError {
+        Bundle args = new Bundle();
+        args.putString("iterations", "2");
+        args.putString("rename-iterations", "false");
+        args.putString("terminate-on-test-fail", "true");
+        LoggingMicrobenchmark loggingRunner = new LoggingMicrobenchmark(
+                LoggingFailedTest.class, args);
+        loggingRunner.setOperationLog(new ArrayList<String>());
+        Result result = new JUnitCore().run(loggingRunner);
+        assertThat(result.wasSuccessful()).isFalse();
+        assertThat(loggingRunner.getOperationLog())
+                .containsExactly(
+                        "before",
+                        "tight before",
+                        "begin: testMethod("
+                                + "android.platform.test.microbenchmark.MicrobenchmarkTest"
+                                + "$LoggingFailedTest)",
+                        "end",
+                        "after")
+                .inOrder();
+    }
+
+    /**
+     * Test successive iteration will be executed when the terminate on test fail
+     * option is disabled.
+     */
+    @Test
+    public void testTerminateOnTestFailOptionDisabled() throws InitializationError {
+        Bundle args = new Bundle();
+        args.putString("iterations", "2");
+        args.putString("rename-iterations", "false");
+        args.putString("terminate-on-test-fail", "false");
+        LoggingMicrobenchmark loggingRunner = new LoggingMicrobenchmark(
+                LoggingFailedTest.class, args);
+        loggingRunner.setOperationLog(new ArrayList<String>());
+        Result result = new JUnitCore().run(loggingRunner);
+        assertThat(result.wasSuccessful()).isFalse();
+        assertThat(loggingRunner.getOperationLog())
+                .containsExactly(
+                        "before",
+                        "tight before",
+                        "begin: testMethod("
+                                + "android.platform.test.microbenchmark.MicrobenchmarkTest"
+                                + "$LoggingFailedTest)",
+                        "end",
+                        "after",
+                        "before",
+                        "tight before",
+                        "begin: testMethod("
+                                + "android.platform.test.microbenchmark.MicrobenchmarkTest"
+                                + "$LoggingFailedTest)",
+                        "end",
+                        "after")
+                .inOrder();
+    }
+
     /**
      * An extensions of the {@link Microbenchmark} runner that logs the start and end of collecting
      * traces. It also passes the operation log to the provided test {@code Class}, if it is a
@@ -315,4 +472,11 @@
             }
         }
     }
+
+    public static class LoggingFailedTest extends LoggingTest {
+        @Test
+        public void testMethod() {
+            throw new RuntimeException("I failed.");
+        }
+    }
 }
diff --git a/tests/functional/devicehealthchecks/assets/bug_map b/tests/functional/devicehealthchecks/assets/bug_map
index 1448d8d..86974f6 100644
--- a/tests/functional/devicehealthchecks/assets/bug_map
+++ b/tests/functional/devicehealthchecks/assets/bug_map
@@ -14,9 +14,13 @@
 system_app_crash com.google.android.apps.youtube.music.mediabrowser.MusicBrowserService.a 157917208
 system_app_crash android.database.sqlite.SQLiteCloseable.acquireReference 159658068
 system_app_crash com.google.android.gms.backup.component.D2dTransportService 31428310
-system_app_native_crash com.google.android.apps.safetyhub 154358781
+system_app_crash Unable\sto\sinstantiate\sapplication\sorg.chromium.chrome.browser.ChromeApplication 161275381
+system_app_crash com.google.android.as.*\s.*\s.*\s.*\s*.*act=android.net.wifi.STATE_CHANGE 161559360
+system_app_crash com.google.android.googlequicksearchbox:search.*\s.*\s.*\s.*\s.*11.26.*\s.*\s.*\s.*\s.*\s\s.*ConcurrentModificationException.*\s.*\s.*\s.*apps.gsa.shared.util.debug.a.g.a 171971925
+system_app_native_crash com.google.android.apps.safetyhub[\s\S]*Scudo\sERROR:\smisaligned\spointer\swhen\sdeallocating\saddress 154358781
 system_app_native_crash com.google.android.providers.media.module 154416156
 system_server_crash void.com.android.server.location.gnss.GnssBatchingProvider.enable 159504970
 SYSTEM_TOMBSTONE android.hardware.vibrator-service.drv2624 151884322
 SYSTEM_TOMBSTONE /vendor/bin/hw/android.hardware.gnss@1.0-service-qti 129282808
 SYSTEM_TOMBSTONE /system/product/priv-app/SafetyHubLprPrebuilt/SafetyHubLprPrebuilt.apk 158504050
+SYSTEM_TOMBSTONE AsyncTask\s+#1\s+>>>\s+com.android.nfc\s+<<<[\S\s]*nfc_main_handle_hal_evt 179110580