malloc debug root to clean up am: dd415afd33 am: 70bfc2201d am: 9328c744e5 am: 3ab696f0bb am: 06f445cc2d

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

Change-Id: Id5440bd12d9f9cc4784d3becf9d82795cc23b64e
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
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 12665b2..214ccc0 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
@@ -17,9 +17,6 @@
 package com.android.sts.common;
 
 import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeNoException;
 
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
@@ -48,71 +45,89 @@
         Pattern.compile("^.*HAS INVALID TAG.*$", Pattern.MULTILINE),
     };
 
-    private ITestDevice device;
-    private String processName;
-    private AutoCloseable setMallocDebugOptionsProperty;
-    private AutoCloseable setAttachedProgramProperty;
-    private AutoCloseable killProcess;
+    private ITestDevice mDevice;
+    private String mProcessName;
+    private boolean mWasAdbRoot;
+    private AutoCloseable mSetMallocDebugOptionsProperty;
+    private AutoCloseable mSetAttachedProgramProperty;
+    private AutoCloseable mProcessKill;
 
     private MallocDebug(
             ITestDevice device, String mallocDebugOption, String processName, boolean isService)
             throws DeviceNotAvailableException, TimeoutException, ProcessUtil.KillException {
-        this.device = device;
-        this.processName = processName;
+        mDevice = device;
+        mProcessName = processName;
+        mWasAdbRoot = device.isAdbRoot();
 
-        // It's an error if this is called while something else is also doing malloc debug.
-        assertNull(
-                MALLOC_DEBUG_OPTIONS_PROP + " is already set!",
-                device.getProperty(MALLOC_DEBUG_OPTIONS_PROP));
-        CommandUtil.runAndCheck(device, "logcat -c");
+        String previousProperty = device.getProperty(MALLOC_DEBUG_OPTIONS_PROP);
+        if (previousProperty != null) {
+            // log if this is called while something else is also doing malloc debug.
+            CLog.w("%s is already set! <%s>", MALLOC_DEBUG_OPTIONS_PROP, previousProperty);
+        }
 
         try {
-            this.setMallocDebugOptionsProperty =
+            mSetMallocDebugOptionsProperty =
                     SystemUtil.withProperty(device, MALLOC_DEBUG_OPTIONS_PROP, mallocDebugOption);
-            this.setAttachedProgramProperty =
+            mSetAttachedProgramProperty =
                     SystemUtil.withProperty(device, MALLOC_DEBUG_PROGRAM_PROP, processName);
 
+            CommandUtil.runAndCheck(device, "logcat -c");
+
             // Kill and wait for the process to come back if we're attaching to a service
-            this.killProcess = null;
+            mProcessKill = null;
             if (isService) {
-                this.killProcess = ProcessUtil.withProcessKill(device, processName, null);
+                mProcessKill = ProcessUtil.withProcessKill(device, processName, null);
                 ProcessUtil.waitProcessRunning(device, processName);
             }
         } catch (Throwable e1) {
             try {
-                if (setMallocDebugOptionsProperty != null) {
-                    setMallocDebugOptionsProperty.close();
+                if (mSetMallocDebugOptionsProperty != null) {
+                    mSetMallocDebugOptionsProperty.close();
                 }
-                if (setAttachedProgramProperty != null) {
-                    setAttachedProgramProperty.close();
+                if (mSetAttachedProgramProperty != null) {
+                    mSetAttachedProgramProperty.close();
                 }
             } catch (Exception e2) {
                 CLog.e(e2);
-                fail(
+                throw new IllegalStateException(
                         "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);
+                                + " this may not work as expected!",
+                        e2);
             }
-            assumeNoException("Could not enable malloc debug", e1);
+            throw new IllegalStateException("Could not enable malloc debug", e1);
         }
     }
 
     @Override
     public void close() throws Exception {
-        device.waitForDeviceAvailable();
-        setMallocDebugOptionsProperty.close();
-        setAttachedProgramProperty.close();
-        if (killProcess != null) {
+        mDevice.waitForDeviceAvailable();
+        boolean isAdbRoot = mDevice.isAdbRoot();
+        if (mWasAdbRoot) {
+            // regain root permissions to teardown
+            mDevice.enableAdbRoot();
+        }
+        try {
+            mSetMallocDebugOptionsProperty.close();
+            mSetAttachedProgramProperty.close();
+        } catch (Exception e) {
+            throw new IllegalStateException("Could not disable malloc debug", e);
+        }
+        if (mProcessKill != null) {
             try {
-                killProcess.close();
-                ProcessUtil.waitProcessRunning(device, processName);
+                mProcessKill.close();
+                ProcessUtil.waitProcessRunning(mDevice, mProcessName);
             } catch (TimeoutException e) {
-                assumeNoException(
-                        "Could not restart '" + processName + "' after disabling malloc debug", e);
+                throw new IllegalStateException(
+                        "Could not restart '" + mProcessName + "' after disabling malloc debug", e);
             }
         }
-        String logcat = CommandUtil.runAndCheck(device, "logcat -d *:S malloc_debug:V").getStdout();
+        String logcat =
+                CommandUtil.runAndCheck(mDevice, "logcat -d *:S malloc_debug:V").getStdout();
+        if (!isAdbRoot) {
+            // restore nonroot status if the try-with-resources body unrooted
+            mDevice.disableAdbRoot();
+        }
         assertNoMallocDebugErrors(logcat);
     }
 
@@ -129,7 +144,7 @@
     public static AutoCloseable withLibcMallocDebugOnService(
             ITestDevice device, String mallocDebugOptions, String processName)
             throws DeviceNotAvailableException, IllegalArgumentException, TimeoutException,
-                ProcessUtil.KillException {
+                    ProcessUtil.KillException {
         if (processName == null || processName.isEmpty()) {
             throw new IllegalArgumentException("Service processName can't be empty");
         }
@@ -149,7 +164,7 @@
     public static AutoCloseable withLibcMallocDebugOnNewProcess(
             ITestDevice device, String mallocDebugOptions, String processName)
             throws DeviceNotAvailableException, IllegalArgumentException, TimeoutException,
-                ProcessUtil.KillException {
+                    ProcessUtil.KillException {
         if (processName == null || processName.isEmpty()) {
             throw new IllegalArgumentException("processName can't be empty");
         }
diff --git a/libraries/sts-common-util/host-side/tests/src/com/android/sts/common/MallocDebugTest.java b/libraries/sts-common-util/host-side/tests/src/com/android/sts/common/MallocDebugTest.java
index 03b1934..99faf82 100644
--- a/libraries/sts-common-util/host-side/tests/src/com/android/sts/common/MallocDebugTest.java
+++ b/libraries/sts-common-util/host-side/tests/src/com/android/sts/common/MallocDebugTest.java
@@ -16,6 +16,11 @@
 
 package com.android.sts.common;
 
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
@@ -54,4 +59,59 @@
     public void testMallocDebugWithErrors() throws Exception {
         MallocDebug.assertNoMallocDebugErrors(logcatWithErrors);
     }
+
+    @Test(expected = IllegalStateException.class)
+    public void testMallocDebugAutocloseableNonRoot() throws Exception {
+        assertTrue(getDevice().disableAdbRoot());
+        try (AutoCloseable mallocDebug =
+                MallocDebug.withLibcMallocDebugOnNewProcess(
+                        getDevice(), "backtrace guard", "native-poc")) {
+            // empty
+        }
+    }
+
+    @Test
+    public void testMallocDebugAutocloseableRoot() throws Exception {
+        assertTrue("must test with rootable device", getDevice().enableAdbRoot());
+        try (AutoCloseable mallocDebug =
+                MallocDebug.withLibcMallocDebugOnNewProcess(
+                        getDevice(), "backtrace guard", "native-poc")) {
+            // empty
+        }
+    }
+
+    @Test
+    public void testMallocDebugAutocloseableNonRootCleanup() throws Exception {
+        assertTrue("must test with rootable device", getDevice().enableAdbRoot());
+        try (AutoCloseable mallocDebug =
+                MallocDebug.withLibcMallocDebugOnNewProcess(
+                        getDevice(), "backtrace guard", "native-poc")) {
+            assertTrue("could not disable root", getDevice().disableAdbRoot());
+        }
+        assertFalse(
+                "device should not be root after autoclose if the body unrooted",
+                getDevice().isAdbRoot());
+    }
+
+    @Test
+    public void testMallocDebugAutoseablePriorValueNoException() throws Exception {
+        assertTrue("must test with rootable device", getDevice().enableAdbRoot());
+        final String oldValue = "TEST_VALUE_OLD";
+        final String newValue = "TEST_VALUE_NEW";
+        assertTrue(
+                "could not set property",
+                getDevice().setProperty("libc.debug.malloc.options", oldValue));
+        assertWithMessage("test property was not properly set on device")
+                .that(getDevice().getProperty("libc.debug.malloc.options"))
+                .isEqualTo(oldValue);
+        try (AutoCloseable mallocDebug =
+                MallocDebug.withLibcMallocDebugOnNewProcess(getDevice(), newValue, "native-poc")) {
+            assertWithMessage("new property was not set during malloc debug body")
+                    .that(getDevice().getProperty("libc.debug.malloc.options"))
+                    .isEqualTo(newValue);
+        }
+        assertWithMessage("prior property was not restored after test")
+                .that(getDevice().getProperty("libc.debug.malloc.options"))
+                .isEqualTo(oldValue);
+    }
 }