Downgrade "multiple public alternatives" to a warning.

Also add support for reporting warnings which do not fail the build.

Bug: 161156950
Test: atest class2nonsdklisttest
Change-Id: I7bd9e19eae8558f24abd1f0a6f10f9130284e076
diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotatedClassContext.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotatedClassContext.java
index a018e1f..8b5a8e1c 100644
--- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotatedClassContext.java
+++ b/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotatedClassContext.java
@@ -41,14 +41,22 @@
         return String.format(Locale.US, signatureFormatString, getClassDescriptor());
     }
 
-    @Override
-    public void reportError(String message, Object... args) {
+    private String buildReportString(String message, Object... args) {
         Formatter error = new Formatter();
         error
-            .format("%s: %s: ", definingClass.getSourceFileName(), definingClass.getClassName())
-            .format(Locale.US, message, args);
+                .format("%s: %s: ", definingClass.getSourceFileName(), definingClass.getClassName())
+                .format(Locale.US, message, args);
+        return error.toString();
+    }
 
-        status.error(error.toString());
+    @Override
+    public void reportError(String message, Object... args) {
+        status.error(buildReportString(message, args));
+    }
+
+    @Override
+    public void reportWarning(String message, Object... args) {
+        status.warning(buildReportString(message, args));
     }
 
 }
diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotatedMemberContext.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotatedMemberContext.java
index 377862c..6276040 100644
--- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotatedMemberContext.java
+++ b/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotatedMemberContext.java
@@ -46,14 +46,22 @@
             getClassDescriptor(), member.getName(), member.getSignature());
     }
 
-    @Override
-    public void reportError(String message, Object... args) {
+    private String buildReportString(String message, Object... args) {
         Formatter error = new Formatter();
         error
-            .format("%s: %s.%s: ", definingClass.getSourceFileName(),
-                definingClass.getClassName(), member.getName())
-            .format(Locale.US, message, args);
+                .format("%s: %s.%s: ", definingClass.getSourceFileName(),
+                        definingClass.getClassName(), member.getName())
+                .format(Locale.US, message, args);
+        return error.toString();
+    }
 
-        status.error(error.toString());
+    @Override
+    public void reportError(String message, Object... args) {
+        status.error(buildReportString(message, args));
+    }
+
+    @Override
+    public void reportWarning(String message, Object... args) {
+        status.warning(buildReportString(message, args));
     }
 }
diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationContext.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationContext.java
index ccd3e7a..7ea6807 100644
--- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationContext.java
+++ b/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationContext.java
@@ -20,7 +20,7 @@
 
 /**
  */
-public abstract class AnnotationContext implements ErrorReporter {
+public abstract class AnnotationContext implements StatusReporter {
 
   public final Status status;
   public final JavaClass definingClass;
diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/ApiResolver.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/ApiResolver.java
index ba8eb08..461bb69 100644
--- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/ApiResolver.java
+++ b/tools/class2nonsdklist/src/com/android/class2nonsdklist/ApiResolver.java
@@ -62,7 +62,7 @@
     public void resolvePublicAlternatives(String publicAlternativesString, String signature,
                                           Integer maxSdkVersion)
             throws JavadocLinkSyntaxError, AlternativeNotFoundError,
-                    RequiredAlternativeNotSpecifiedError {
+                    RequiredAlternativeNotSpecifiedError, MultipleAlternativesFoundWarning {
         if (Strings.isNullOrEmpty(publicAlternativesString) && maxSdkVersion != null
                 && maxSdkVersion >= MIN_SDK_REQUIRING_PUBLIC_ALTERNATIVES) {
             throw new RequiredAlternativeNotSpecifiedError();
@@ -96,7 +96,7 @@
                     if (almostMatches.size() == 0) {
                         throw new MemberAlternativeNotFoundError(alternative);
                     } else if (almostMatches.size() > 1) {
-                        throw new MultipleAlternativesFoundError(alternative, almostMatches);
+                        throw new MultipleAlternativesFoundWarning(alternative, almostMatches);
                     }
                 }
             }
diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/MultipleAlternativesFoundError.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/MultipleAlternativesFoundWarning.java
similarity index 82%
rename from tools/class2nonsdklist/src/com/android/class2nonsdklist/MultipleAlternativesFoundError.java
rename to tools/class2nonsdklist/src/com/android/class2nonsdklist/MultipleAlternativesFoundWarning.java
index 9347dee..738c906 100644
--- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/MultipleAlternativesFoundError.java
+++ b/tools/class2nonsdklist/src/com/android/class2nonsdklist/MultipleAlternativesFoundWarning.java
@@ -20,11 +20,11 @@
 
 import java.util.List;
 
-public class MultipleAlternativesFoundError extends AlternativeNotFoundError {
+public class MultipleAlternativesFoundWarning extends Exception {
     public final ApiComponents alternative;
     public final List<ApiComponents> almostMatches;
 
-    public MultipleAlternativesFoundError(ApiComponents alternative,
+    public MultipleAlternativesFoundWarning(ApiComponents alternative,
             List<ApiComponents> almostMatches) {
         this.alternative = alternative;
         this.almostMatches = almostMatches;
@@ -32,7 +32,8 @@
 
     @Override
     public String toString() {
-        return "Alternative " + alternative + " returned multiple matches: "
+        return "Alternative " + alternative + " returned multiple matches. Consider adding method" +
+                " parameters to make the match unique. Matches: "
                 + Joiner.on(", ").join(almostMatches);
     }
 }
diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/Status.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/Status.java
index 1fd3972..1460a4f 100644
--- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/Status.java
+++ b/tools/class2nonsdklist/src/com/android/class2nonsdklist/Status.java
@@ -23,6 +23,9 @@
     // Highlight "Error:" in red.
     private static final String ERROR = "\u001B[31mError: \u001B[0m";
 
+    // Highlight "Warning:" in yellow.
+    private static final String WARNING = "\u001B[33mWarning: \u001B[0m";
+
     private final boolean mDebug;
     private boolean mHasErrors;
 
@@ -48,6 +51,11 @@
         mHasErrors = true;
     }
 
+    public void warning(String message, Object... args) {
+        System.err.print(WARNING);
+        System.err.println(String.format(Locale.US, message, args));
+    }
+
     public boolean ok() {
         return !mHasErrors;
     }
diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/ErrorReporter.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/StatusReporter.java
similarity index 78%
rename from tools/class2nonsdklist/src/com/android/class2nonsdklist/ErrorReporter.java
rename to tools/class2nonsdklist/src/com/android/class2nonsdklist/StatusReporter.java
index 0254e42..5215490 100644
--- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/ErrorReporter.java
+++ b/tools/class2nonsdklist/src/com/android/class2nonsdklist/StatusReporter.java
@@ -16,10 +16,16 @@
 
 package com.android.class2nonsdklist;
 
-public interface ErrorReporter {
+public interface StatusReporter {
     /**
      * Report an error in this context. The final error message will include
      * the class and member names, and the source file name.
      */
     void reportError(String message, Object... args);
+
+    /**
+     * Report a warning in this context. The final message will include the
+     * class and member names, and the source file name.
+     */
+    void reportWarning(String message, Object... args);
 }
diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/UnsupportedAppUsageAnnotationHandler.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/UnsupportedAppUsageAnnotationHandler.java
index 81e51d9..2bb0da5 100644
--- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/UnsupportedAppUsageAnnotationHandler.java
+++ b/tools/class2nonsdklist/src/com/android/class2nonsdklist/UnsupportedAppUsageAnnotationHandler.java
@@ -163,6 +163,8 @@
         try {
             mApiResolver.resolvePublicAlternatives(publicAlternativesString, signature,
                     maxTargetSdk);
+        } catch (MultipleAlternativesFoundWarning e) {
+            context.reportWarning(e.toString());
         } catch (JavadocLinkSyntaxError | AlternativeNotFoundError e) {
             context.reportError(e.toString());
         } catch (RequiredAlternativeNotSpecifiedError e) {
diff --git a/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/ApiResolverTest.java b/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/ApiResolverTest.java
index 3fd62bb..b51efcd 100644
--- a/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/ApiResolverTest.java
+++ b/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/ApiResolverTest.java
@@ -30,9 +30,7 @@
 
 public class ApiResolverTest extends AnnotationHandlerTestBase {
     @Test
-    public void testFindPublicAlternativeExactly()
-            throws JavadocLinkSyntaxError, AlternativeNotFoundError,
-            RequiredAlternativeNotSpecifiedError {
+    public void testFindPublicAlternativeExactly() throws Exception {
         Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>(
                 Arrays.asList("La/b/C;->foo(I)V", "La/b/C;->bar(I)V")));
         ApiResolver resolver = new ApiResolver(publicApis);
@@ -40,9 +38,7 @@
     }
 
     @Test
-    public void testFindPublicAlternativeDeducedPackageName()
-            throws JavadocLinkSyntaxError, AlternativeNotFoundError,
-            RequiredAlternativeNotSpecifiedError {
+    public void testFindPublicAlternativeDeducedPackageName() throws Exception {
         Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>(
                 Arrays.asList("La/b/C;->foo(I)V", "La/b/C;->bar(I)V")));
         ApiResolver resolver = new ApiResolver(publicApis);
@@ -50,9 +46,7 @@
     }
 
     @Test
-    public void testFindPublicAlternativeDeducedPackageAndClassName()
-            throws JavadocLinkSyntaxError, AlternativeNotFoundError,
-            RequiredAlternativeNotSpecifiedError {
+    public void testFindPublicAlternativeDeducedPackageAndClassName() throws Exception {
         Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>(
                 Arrays.asList("La/b/C;->foo(I)V", "La/b/C;->bar(I)V")));
         ApiResolver resolver = new ApiResolver(publicApis);
@@ -60,9 +54,7 @@
     }
 
     @Test
-    public void testFindPublicAlternativeDeducedParameterTypes()
-            throws JavadocLinkSyntaxError, AlternativeNotFoundError,
-            RequiredAlternativeNotSpecifiedError {
+    public void testFindPublicAlternativeDeducedParameterTypes() throws Exception {
         Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>(
                 Arrays.asList("La/b/C;->foo(I)V", "La/b/C;->bar(I)V")));
         ApiResolver resolver = new ApiResolver(publicApis);
@@ -70,12 +62,12 @@
     }
 
     @Test
-    public void testFindPublicAlternativeFailDueToMultipleParameterTypes()
+    public void testFindPublicAlternativeWarnsOnMultipleParameterTypes()
             throws SignatureSyntaxError {
         Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>(
                 Arrays.asList("La/b/C;->foo(I)V", "La/b/C;->bar(I)I", "La/b/C;->foo(II)V")));
         ApiResolver resolver = new ApiResolver(publicApis);
-        MultipleAlternativesFoundError e = expectThrows(MultipleAlternativesFoundError.class,
+        MultipleAlternativesFoundWarning e = expectThrows(MultipleAlternativesFoundWarning.class,
                 () -> resolver.resolvePublicAlternatives("{@link #foo}", "La/b/C;->bar()V", 1));
         assertThat(e.almostMatches).containsExactly(
                 ApiComponents.fromDexSignature("La/b/C;->foo(I)V"),
@@ -111,9 +103,7 @@
     }
 
     @Test
-    public void testPublicAlternativesJustPackageAndClassName()
-            throws JavadocLinkSyntaxError, AlternativeNotFoundError,
-            RequiredAlternativeNotSpecifiedError {
+    public void testPublicAlternativesJustPackageAndClassName() throws Exception {
         Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>(
                 Arrays.asList("La/b/C;->bar(I)V")));
         ApiResolver resolver = new ApiResolver(publicApis);
@@ -121,9 +111,7 @@
     }
 
     @Test
-    public void testPublicAlternativesJustClassName()
-            throws JavadocLinkSyntaxError, AlternativeNotFoundError,
-            RequiredAlternativeNotSpecifiedError {
+    public void testPublicAlternativesJustClassName() throws Exception {
         Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>(
                 Arrays.asList("La/b/C;->bar(I)V")));
         ApiResolver resolver = new ApiResolver(publicApis);
@@ -131,9 +119,7 @@
     }
 
     @Test
-    public void testNoPublicAlternativesButHasExplanation()
-            throws JavadocLinkSyntaxError, AlternativeNotFoundError,
-            RequiredAlternativeNotSpecifiedError {
+    public void testNoPublicAlternativesButHasExplanation() throws Exception {
         Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>());
         ApiResolver resolver = new ApiResolver(publicApis);
         resolver.resolvePublicAlternatives("Foo {@code bar}", "La/b/C;->bar()V", 1);
@@ -148,9 +134,7 @@
     }
 
     @Test
-    public void testNoPublicAlternativesSpecifiedWithMaxLessThanQ()
-            throws JavadocLinkSyntaxError, AlternativeNotFoundError,
-            RequiredAlternativeNotSpecifiedError {
+    public void testNoPublicAlternativesSpecifiedWithMaxLessThanQ() throws Exception {
         Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>());
         ApiResolver resolver = new ApiResolver(publicApis);
         resolver.resolvePublicAlternatives(null, "La/b/C;->bar()V", 28);