Create the first API for getting tombstones from device

API for a local device to get the tombstones off the device

Test: unit tests
Bug: 132908268
Change-Id: Ic4d18a2ecff8a8de56e60672ded6a66ee2b0336e
diff --git a/src/com/android/tradefed/device/INativeDevice.java b/src/com/android/tradefed/device/INativeDevice.java
index 79c6f8c..99a1b04 100644
--- a/src/com/android/tradefed/device/INativeDevice.java
+++ b/src/com/android/tradefed/device/INativeDevice.java
@@ -1313,4 +1313,16 @@
      * returned by {@link System#currentTimeMillis()}.
      */
     public long getLastExpectedRebootTimeMillis();
+
+    /**
+     * Fetch and return the list of tombstones from the devices. Requires root.
+     *
+     * <p>method is best-effort so if one tombstone fails to be pulled for any reason it will be
+     * missing from the list. Only a {@link DeviceNotAvailableException} will terminate the method
+     * early.
+     *
+     * @return A list of tombstone files, empty if no tombstone.
+     * @see <a href="https://source.android.com/devices/tech/debug">Tombstones documentation</a>
+     */
+    public List<File> getTombstones() throws DeviceNotAvailableException;
 }
diff --git a/src/com/android/tradefed/device/NativeDevice.java b/src/com/android/tradefed/device/NativeDevice.java
index 5cd16be..92c2c83 100644
--- a/src/com/android/tradefed/device/NativeDevice.java
+++ b/src/com/android/tradefed/device/NativeDevice.java
@@ -183,6 +183,9 @@
     /** Pattern to find an executable file. */
     private static final Pattern EXE_FILE = Pattern.compile("^[-l]r.x.+");
 
+    /** Path of the device containing the tombstones */
+    private static final String TOMBSTONE_PATH = "/data/tombstones/";
+
     /** The time in ms to wait for a command to complete. */
     private long mCmdTimeout = 2 * 60 * 1000L;
     /** The time in ms to wait for a 'long' command to complete. */
@@ -4364,6 +4367,23 @@
         return mLastTradefedRebootTime;
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public List<File> getTombstones() throws DeviceNotAvailableException {
+        List<File> tombstones = new ArrayList<>();
+        if (!isAdbRoot()) {
+            CLog.w("Device was not root, cannot collect tombstones.");
+            return tombstones;
+        }
+        for (String tombName : getChildren(TOMBSTONE_PATH)) {
+            File tombFile = pullFile(TOMBSTONE_PATH + tombName);
+            if (tombFile != null) {
+                tombstones.add(tombFile);
+            }
+        }
+        return tombstones;
+    }
+
     /** Validate that pid is an integer and not empty. */
     private boolean checkValidPid(String output) {
         if (output.isEmpty()) {
diff --git a/tests/src/com/android/tradefed/device/NativeDeviceTest.java b/tests/src/com/android/tradefed/device/NativeDeviceTest.java
index 28e24ab..27d5ce6 100644
--- a/tests/src/com/android/tradefed/device/NativeDeviceTest.java
+++ b/tests/src/com/android/tradefed/device/NativeDeviceTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
 
 import com.android.ddmlib.AdbCommandRejectedException;
@@ -2579,4 +2580,34 @@
                 };
         assertFalse(mTestDevice.isExecutable("/system"));
     }
+
+    /** Test {@link NativeDevice#getTombstones()}. */
+    @Test
+    public void testGetTombstones_notRoot() throws Exception {
+        TestableAndroidNativeDevice spy = Mockito.spy(mTestDevice);
+        doReturn(false).when(spy).isAdbRoot();
+
+        EasyMock.replay(mMockIDevice);
+        List<File> result = spy.getTombstones();
+        assertEquals(0, result.size());
+        EasyMock.verify(mMockIDevice);
+    }
+
+    /** Test {@link NativeDevice#getTombstones()}. */
+    @Test
+    public void testGetTombstones() throws Exception {
+        TestableAndroidNativeDevice spy = Mockito.spy(mTestDevice);
+        doReturn(true).when(spy).isAdbRoot();
+
+        String[] tombs = new String[] {"tomb1", "tomb2"};
+        doReturn(tombs).when(spy).getChildren("/data/tombstones/");
+
+        doReturn(new File("tomb1_test")).when(spy).pullFile("/data/tombstones/tomb1");
+        doReturn(new File("tomb2_test")).when(spy).pullFile("/data/tombstones/tomb2");
+
+        EasyMock.replay(mMockIDevice);
+        List<File> result = spy.getTombstones();
+        assertEquals(2, result.size());
+        EasyMock.verify(mMockIDevice);
+    }
 }