DO NOT MERGE: Protect against system checkers RuntimeException

a system checker runtime exception would kill the
invocation, changing this to only consider the
system checker failed.

If the root cause of the RuntimeException was a device
issue, a DNAE would eventually be triggered and properly
clean up the invocation.

Test: unit tests
run cts, manually fail a checker
Bug: 111752857

Change-Id: I3e2f5772b92e62a9dac377c47a8aab4eaed479e2
(cherry picked from commit 528eaf7d6c996309cdf558b9b6f698ad572d5bd5)
diff --git a/src/com/android/tradefed/testtype/suite/ITestSuite.java b/src/com/android/tradefed/testtype/suite/ITestSuite.java
index cbf1305..c72ba11 100644
--- a/src/com/android/tradefed/testtype/suite/ITestSuite.java
+++ b/src/com/android/tradefed/testtype/suite/ITestSuite.java
@@ -535,7 +535,13 @@
                 continue;
             }
 
-            StatusCheckerResult result = checker.preExecutionCheck(device);
+            StatusCheckerResult result = new StatusCheckerResult(CheckStatus.FAILED);
+            try {
+                result = checker.preExecutionCheck(device);
+            } catch (RuntimeException e) {
+                // Catch RuntimeException to avoid leaking throws that go to the invocation.
+                result.setErrorMessage(e.getMessage());
+            }
             if (!CheckStatus.SUCCESS.equals(result.getStatus())) {
                 String errorMessage =
                         (result.getErrorMessage() == null) ? "" : result.getErrorMessage();
@@ -577,7 +583,13 @@
                 continue;
             }
 
-            StatusCheckerResult result = checker.postExecutionCheck(device);
+            StatusCheckerResult result = new StatusCheckerResult(CheckStatus.FAILED);
+            try {
+                result = checker.postExecutionCheck(device);
+            } catch (RuntimeException e) {
+                // Catch RuntimeException to avoid leaking throws that go to the invocation.
+                result.setErrorMessage(e.getMessage());
+            }
             if (!CheckStatus.SUCCESS.equals(result.getStatus())) {
                 String errorMessage =
                         (result.getErrorMessage() == null) ? "" : result.getErrorMessage();
diff --git a/tests/src/com/android/tradefed/testtype/suite/ITestSuiteTest.java b/tests/src/com/android/tradefed/testtype/suite/ITestSuiteTest.java
index f70f1f0..cd73b60 100644
--- a/tests/src/com/android/tradefed/testtype/suite/ITestSuiteTest.java
+++ b/tests/src/com/android/tradefed/testtype/suite/ITestSuiteTest.java
@@ -289,6 +289,35 @@
 
     /**
      * Test for {@link ITestSuite#run(ITestInvocationListener)} when the System status checker is
+     * failing with a runtime exception. RuntimeException is interpreted as a checker failure.
+     */
+    @Test
+    public void testRun_failedSystemChecker_runtimeException() throws Exception {
+        final byte[] fakeData = "fakeData".getBytes();
+        InputStreamSource fakeSource = new ByteArrayInputStreamSource(fakeData);
+        List<ISystemStatusChecker> sysChecker = new ArrayList<ISystemStatusChecker>();
+        sysChecker.add(mMockSysChecker);
+        mTestSuite.setSystemStatusChecker(sysChecker);
+
+        EasyMock.expect(mMockSysChecker.preExecutionCheck(EasyMock.eq(mMockDevice)))
+                .andThrow(new RuntimeException("I failed."));
+        EasyMock.expect(mMockDevice.getBugreport()).andReturn(fakeSource).times(2);
+        mMockListener.testLog(
+                (String) EasyMock.anyObject(),
+                EasyMock.eq(LogDataType.BUGREPORT),
+                EasyMock.eq(fakeSource));
+        EasyMock.expectLastCall().times(2);
+
+        EasyMock.expect(mMockSysChecker.postExecutionCheck(EasyMock.eq(mMockDevice)))
+                .andThrow(new RuntimeException("I failed post."));
+        expectTestRun(mMockListener);
+        replayMocks();
+        mTestSuite.run(mMockListener);
+        verifyMocks();
+    }
+
+    /**
+     * Test for {@link ITestSuite#run(ITestInvocationListener)} when the System status checker is
      * passing pre-check but failing post-check and we enable reporting a failure for it.
      */
     @Test