Signature tests: catch and log NoClassDefFoundError

Avoids failing the tests if a NoClassDefFoundError is detected when
attempting to retrieve a class or its constructors, methods or fields
using reflection. Instead log the failure and continue.

This should not make the tests any less rigorous or risk allowing
access to implementation details of the platform as if the test cannot
retrieve them using reflection then an app will be unable to do so
either.

Bug: 206650495
Test: m CtsSystemApiAnnotationTestCases
Merged-In: I94716a359aaa8c88429143130566ec4db13a7ad8
Change-Id: I94716a359aaa8c88429143130566ec4db13a7ad8
(cherry picked from commit 4dbc2330349f6002cc11be4d2cefe5aa9c4c1a7d)
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/BootClassPathClassesProvider.java b/tests/signature/api-check/src/java/android/signature/cts/api/BootClassPathClassesProvider.java
index e0f22e2..d037c6c 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/BootClassPathClassesProvider.java
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/BootClassPathClassesProvider.java
@@ -17,6 +17,7 @@
 package android.signature.cts.api;
 
 import android.os.Debug;
+import android.util.Log;
 import android.signature.cts.ClassProvider;
 import android.signature.cts.DexField;
 import android.signature.cts.DexMethod;
@@ -34,6 +35,8 @@
 
 @SuppressWarnings("deprecation")
 public class BootClassPathClassesProvider extends ClassProvider {
+    private static final String TAG = "BootClassPathClassesProvider";
+
     private static boolean sJvmtiAttached = false;
 
     @Override
@@ -53,6 +56,9 @@
                         // It could be that a class failed to verify.
                         // No process will be able to load it, so it's ok to silently ignore.
                         return null;
+                    } catch (NoClassDefFoundError e) {
+                        Log.w(TAG, "Could not load class " + classname, e);
+                        return null;
                     }
                 })
                 .filter(Objects::nonNull);
diff --git a/tests/signature/lib/common/src/android/signature/cts/ApiPresenceChecker.java b/tests/signature/lib/common/src/android/signature/cts/ApiPresenceChecker.java
index fc4335c..743fd5a 100644
--- a/tests/signature/lib/common/src/android/signature/cts/ApiPresenceChecker.java
+++ b/tests/signature/lib/common/src/android/signature/cts/ApiPresenceChecker.java
@@ -18,6 +18,7 @@
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -138,15 +139,24 @@
      * @return a {@link Map} of fieldName to {@link Field}
      */
     private static Map<String, Field> buildFieldMap(Class<?> testClass) {
+        try {
+            return buildFieldMapImpl(testClass);
+        } catch (NoClassDefFoundError e) {
+            LogHelper.loge("AbstractApiChecker: Could not retrieve fields of " + testClass, e);
+            return Collections.emptyMap();
+        }
+    }
+
+    private static Map<String, Field> buildFieldMapImpl(Class<?> testClass) {
         Map<String, Field> fieldMap = new HashMap<>();
         // Scan the superclass
         if (testClass.getSuperclass() != null) {
-            fieldMap.putAll(buildFieldMap(testClass.getSuperclass()));
+            fieldMap.putAll(buildFieldMapImpl(testClass.getSuperclass()));
         }
 
         // Scan the interfaces
         for (Class<?> interfaceClass : testClass.getInterfaces()) {
-            fieldMap.putAll(buildFieldMap(interfaceClass));
+            fieldMap.putAll(buildFieldMapImpl(interfaceClass));
         }
 
         // Check the fields in the test class
diff --git a/tests/signature/lib/common/src/android/signature/cts/LogHelper.java b/tests/signature/lib/common/src/android/signature/cts/LogHelper.java
index 5505e4a..e2f5750 100644
--- a/tests/signature/lib/common/src/android/signature/cts/LogHelper.java
+++ b/tests/signature/lib/common/src/android/signature/cts/LogHelper.java
@@ -19,7 +19,7 @@
  */
 public class LogHelper {
 
-    static void loge(String message, Exception exception) {
-        System.out.println(String.format("%s: %s", message, exception));
+    static void loge(String message, Throwable throwable) {
+        System.out.println(String.format("%s: %s", message, throwable));
     }
 }
diff --git a/tests/signature/lib/common/src/android/signature/cts/ReflectionHelper.java b/tests/signature/lib/common/src/android/signature/cts/ReflectionHelper.java
index 0890bc9..693e27e 100644
--- a/tests/signature/lib/common/src/android/signature/cts/ReflectionHelper.java
+++ b/tests/signature/lib/common/src/android/signature/cts/ReflectionHelper.java
@@ -30,6 +30,7 @@
 import java.lang.reflect.TypeVariable;
 import java.lang.reflect.WildcardType;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -41,6 +42,7 @@
  * Uses reflection to obtain runtime representations of elements in the API.
  */
 public class ReflectionHelper {
+    private static final String TAG = "ReflectionHelper";
 
     /**
      * Finds the reflected class for the class under test.
@@ -135,6 +137,16 @@
     static Constructor<?> findMatchingConstructor(Class<?> runtimeClass,
             JDiffConstructor jdiffDes, Map<Constructor, String> mismatchReasons) {
 
+        try {
+            return findMatchingConstructorImpl(runtimeClass, jdiffDes, mismatchReasons);
+        } catch (NoClassDefFoundError e) {
+            LogHelper.loge(TAG + ": Could not retrieve constructors of " + runtimeClass, e);
+            return null;
+        }
+    }
+
+    static Constructor<?> findMatchingConstructorImpl(Class<?> runtimeClass,
+            JDiffConstructor jdiffDes, Map<Constructor, String> mismatchReasons) {
         for (Constructor<?> c : runtimeClass.getDeclaredConstructors()) {
             Type[] params = c.getGenericParameterTypes();
             boolean isStaticClass = ((runtimeClass.getModifiers() & Modifier.STATIC) != 0);
@@ -231,6 +243,16 @@
      */
     static Method findMatchingMethod(
             Class<?> runtimeClass, JDiffMethod method, Map<Method, String> mismatchReasons) {
+        try {
+            return findMatchingMethodImpl(runtimeClass, method, mismatchReasons);
+        } catch (NoClassDefFoundError e) {
+            LogHelper.loge(TAG + ": Could not retrieve methods of " + runtimeClass, e);
+            return null;
+        }
+    }
+
+    static Method findMatchingMethodImpl(
+            Class<?> runtimeClass, JDiffMethod method, Map<Method, String> mismatchReasons) {
 
         // Search through the class to find the methods just in case the method was actually
         // declared in a superclass which is not part of the API and so was made to appear as if
@@ -452,6 +474,17 @@
      */
     public static Set<Constructor<?>> getAnnotatedConstructors(Class<?> clazz,
             String annotationSpec) {
+        try {
+            return getAnnotatedConstructorsImpl(clazz, annotationSpec);
+        } catch (NoClassDefFoundError e) {
+            LogHelper.loge(TAG + ": Could not retrieve constructors of " + clazz
+                + " annotated with " + annotationSpec, e);
+            return Collections.emptySet();
+        }
+    }
+
+    private static Set<Constructor<?>> getAnnotatedConstructorsImpl(Class<?> clazz,
+        String annotationSpec) {
         Set<Constructor<?>> result = new HashSet<>();
         if (annotationSpec != null) {
             for (Constructor<?> c : clazz.getDeclaredConstructors()) {
@@ -474,6 +507,16 @@
      * Returns a list of methods which are annotated with the given annotation class.
      */
     public static Set<Method> getAnnotatedMethods(Class<?> clazz, String annotationSpec) {
+        try {
+            return getAnnotatedMethodsImpl(clazz, annotationSpec);
+        } catch (NoClassDefFoundError e) {
+            LogHelper.loge(TAG + ": Could not retrieve methods of " + clazz
+                + " annotated with " + annotationSpec, e);
+            return Collections.emptySet();
+        }
+    }
+
+    private static Set<Method> getAnnotatedMethodsImpl(Class<?> clazz, String annotationSpec) {
         Set<Method> result = new HashSet<>();
         if (annotationSpec != null) {
             for (Method m : clazz.getDeclaredMethods()) {
@@ -490,6 +533,16 @@
      * Returns a list of fields which are annotated with the given annotation class.
      */
     public static Set<Field> getAnnotatedFields(Class<?> clazz, String annotationSpec) {
+        try {
+            return getAnnotatedFieldsImpl(clazz, annotationSpec);
+        } catch (NoClassDefFoundError e) {
+            LogHelper.loge(TAG + ": Could not retrieve fields of " + clazz
+                + " annotated with " + annotationSpec, e);
+            return Collections.emptySet();
+        }
+    }
+
+    private static Set<Field> getAnnotatedFieldsImpl(Class<?> clazz, String annotationSpec) {
         Set<Field> result = new HashSet<>();
         if (annotationSpec != null) {
             for (Field f : clazz.getDeclaredFields()) {