Require publicAlternatives for APIs on max-sdk-q
Greylisted APIs can be added to the max-sdk-q list, and public
alternative information is required when doing this.
Bug: 133829620
Test: atest class2greylisttest
Change-Id: Iadcf75d3eb606a6137e755d1245ae2611a416003
diff --git a/tools/class2greylist/src/com/android/class2greylist/ApiResolver.java b/tools/class2greylist/src/com/android/class2greylist/ApiResolver.java
index f07a0af..b120978 100644
--- a/tools/class2greylist/src/com/android/class2greylist/ApiResolver.java
+++ b/tools/class2greylist/src/com/android/class2greylist/ApiResolver.java
@@ -31,6 +31,7 @@
private static final Pattern LINK_TAG_PATTERN = Pattern.compile("\\{@link ([^\\}]+)\\}");
private static final Pattern CODE_TAG_PATTERN = Pattern.compile("\\{@code ([^\\}]+)\\}");
+ private static final Integer MIN_SDK_REQUIRING_PUBLIC_ALTERNATIVES = 29;
public ApiResolver() {
mPotentialPublicAlternatives = null;
@@ -58,8 +59,14 @@
* @param publicAlternativesString String containing public alternative explanations.
* @param signature Signature of the member that has the annotation.
*/
- public void resolvePublicAlternatives(String publicAlternativesString, String signature)
- throws JavadocLinkSyntaxError, AlternativeNotFoundError {
+ public void resolvePublicAlternatives(String publicAlternativesString, String signature,
+ Integer maxSdkVersion)
+ throws JavadocLinkSyntaxError, AlternativeNotFoundError,
+ RequiredAlternativeNotSpecifiedError {
+ if (Strings.isNullOrEmpty(publicAlternativesString) && maxSdkVersion != null
+ && maxSdkVersion >= MIN_SDK_REQUIRING_PUBLIC_ALTERNATIVES) {
+ throw new RequiredAlternativeNotSpecifiedError();
+ }
if (publicAlternativesString != null && mPotentialPublicAlternatives != null) {
// Grab all instances of type {@link foo}
Matcher matcher = LINK_TAG_PATTERN.matcher(publicAlternativesString);
diff --git a/tools/class2greylist/src/com/android/class2greylist/RequiredAlternativeNotSpecifiedError.java b/tools/class2greylist/src/com/android/class2greylist/RequiredAlternativeNotSpecifiedError.java
new file mode 100644
index 0000000..a65f1fe
--- /dev/null
+++ b/tools/class2greylist/src/com/android/class2greylist/RequiredAlternativeNotSpecifiedError.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.class2greylist;
+
+/**
+ * Exception to be thrown when a greylisted private api gets restricted to max-FOO (where FOO is Q
+ * or later), without providing a public API alternative.
+ */
+public class RequiredAlternativeNotSpecifiedError extends Exception {
+
+}
+
diff --git a/tools/class2greylist/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandler.java b/tools/class2greylist/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandler.java
index 17ec732..3ce00df 100644
--- a/tools/class2greylist/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandler.java
+++ b/tools/class2greylist/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandler.java
@@ -159,10 +159,17 @@
mSdkVersionToFlagMap.keySet());
return;
}
+
try {
- mApiResolver.resolvePublicAlternatives(publicAlternativesString, signature);
+ mApiResolver.resolvePublicAlternatives(publicAlternativesString, signature,
+ maxTargetSdk);
} catch (JavadocLinkSyntaxError | AlternativeNotFoundError e) {
context.reportError(e.toString());
+ } catch (RequiredAlternativeNotSpecifiedError e) {
+ context.reportError("Signature %s moved to %s without specifying public "
+ + "alternatives; Refer to go/unsupportedappusage-public-alternatives "
+ + "for details.",
+ signature, mSdkVersionToFlagMap.get(maxTargetSdk));
}
// Consume this annotation if it matches the predicate.
diff --git a/tools/class2greylist/test/src/com/android/class2greylist/ApiResolverTest.java b/tools/class2greylist/test/src/com/android/class2greylist/ApiResolverTest.java
index 8790879..888a9b5 100644
--- a/tools/class2greylist/test/src/com/android/class2greylist/ApiResolverTest.java
+++ b/tools/class2greylist/test/src/com/android/class2greylist/ApiResolverTest.java
@@ -17,12 +17,10 @@
package com.android.class2greylist;
import static com.google.common.truth.Truth.assertThat;
+
import static org.testng.Assert.expectThrows;
import static org.testng.Assert.assertThrows;
-import static org.junit.Assert.fail;
-import org.hamcrest.Description;
-import org.hamcrest.TypeSafeMatcher;
import org.junit.Test;
import java.util.Arrays;
@@ -33,48 +31,52 @@
public class ApiResolverTest extends AnnotationHandlerTestBase {
@Test
public void testFindPublicAlternativeExactly()
- throws JavadocLinkSyntaxError, AlternativeNotFoundError {
- Set<String> publicApis = Collections.unmodifiableSet(new HashSet<String>(
+ throws JavadocLinkSyntaxError, AlternativeNotFoundError,
+ RequiredAlternativeNotSpecifiedError {
+ 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);
- resolver.resolvePublicAlternatives("{@link a.b.C#foo(int)}", "Lb/c/D;->bar()V");
+ resolver.resolvePublicAlternatives("{@link a.b.C#foo(int)}", "Lb/c/D;->bar()V", 1);
}
@Test
public void testFindPublicAlternativeDeducedPackageName()
- throws JavadocLinkSyntaxError, AlternativeNotFoundError {
- Set<String> publicApis = Collections.unmodifiableSet(new HashSet<String>(
+ throws JavadocLinkSyntaxError, AlternativeNotFoundError,
+ RequiredAlternativeNotSpecifiedError {
+ 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);
- resolver.resolvePublicAlternatives("{@link C#foo(int)}", "La/b/D;->bar()V");
+ resolver.resolvePublicAlternatives("{@link C#foo(int)}", "La/b/D;->bar()V", 1);
}
@Test
public void testFindPublicAlternativeDeducedPackageAndClassName()
- throws JavadocLinkSyntaxError, AlternativeNotFoundError {
- Set<String> publicApis = Collections.unmodifiableSet(new HashSet<String>(
+ throws JavadocLinkSyntaxError, AlternativeNotFoundError,
+ RequiredAlternativeNotSpecifiedError {
+ 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);
- resolver.resolvePublicAlternatives("{@link #foo(int)}", "La/b/C;->bar()V");
+ resolver.resolvePublicAlternatives("{@link #foo(int)}", "La/b/C;->bar()V", 1);
}
@Test
public void testFindPublicAlternativeDeducedParameterTypes()
- throws JavadocLinkSyntaxError, AlternativeNotFoundError {
- Set<String> publicApis = Collections.unmodifiableSet(new HashSet<String>(
+ throws JavadocLinkSyntaxError, AlternativeNotFoundError,
+ RequiredAlternativeNotSpecifiedError {
+ 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);
- resolver.resolvePublicAlternatives("{@link #foo}", "La/b/C;->bar()V");
+ resolver.resolvePublicAlternatives("{@link #foo}", "La/b/C;->bar()V", 1);
}
@Test
public void testFindPublicAlternativeFailDueToMultipleParameterTypes()
- throws JavadocLinkSyntaxError, AlternativeNotFoundError, SignatureSyntaxError {
- Set<String> publicApis = Collections.unmodifiableSet(new HashSet<String>(
+ 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,
- () -> resolver.resolvePublicAlternatives("{@link #foo}", "La/b/C;->bar()V"));
+ () -> resolver.resolvePublicAlternatives("{@link #foo}", "La/b/C;->bar()V", 1));
assertThat(e.almostMatches).containsExactly(
ApiComponents.fromDexSignature("La/b/C;->foo(I)V"),
ApiComponents.fromDexSignature("La/b/C;->foo(II)V")
@@ -82,59 +84,76 @@
}
@Test
- public void testFindPublicAlternativeFailNoAlternative()
- throws JavadocLinkSyntaxError, AlternativeNotFoundError {
- Set<String> publicApis = Collections.unmodifiableSet(new HashSet<String>(
+ public void testFindPublicAlternativeFailNoAlternative() {
+ Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList("La/b/C;->bar(I)V")));
ApiResolver resolver = new ApiResolver(publicApis);
assertThrows(MemberAlternativeNotFoundError.class, ()
- -> resolver.resolvePublicAlternatives("{@link #foo(int)}", "La/b/C;->bar()V"));
+ -> resolver.resolvePublicAlternatives("{@link #foo(int)}", "La/b/C;->bar()V", 1));
}
@Test
- public void testFindPublicAlternativeFailNoAlternativeNoParameterTypes()
- throws JavadocLinkSyntaxError, AlternativeNotFoundError {
+ public void testFindPublicAlternativeFailNoAlternativeNoParameterTypes() {
- Set<String> publicApis = Collections.unmodifiableSet(new HashSet<String>(
+ Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList("La/b/C;->bar(I)V")));
ApiResolver resolver = new ApiResolver(publicApis);
assertThrows(MemberAlternativeNotFoundError.class,
- () -> resolver.resolvePublicAlternatives("{@link #foo}", "La/b/C;->bar()V"));
+ () -> resolver.resolvePublicAlternatives("{@link #foo}", "La/b/C;->bar()V", 1));
}
@Test
- public void testNoPublicClassAlternatives()
- throws JavadocLinkSyntaxError, AlternativeNotFoundError {
- Set<String> publicApis = Collections.unmodifiableSet(new HashSet<String>());
+ public void testNoPublicClassAlternatives() {
+ Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>());
ApiResolver resolver = new ApiResolver(publicApis);
expectThrows(NoAlternativesSpecifiedError.class,
- () -> resolver.resolvePublicAlternatives("Foo", "La/b/C;->bar()V"));
+ () -> resolver.resolvePublicAlternatives("Foo", "La/b/C;->bar()V", 1));
}
@Test
public void testPublicAlternativesJustPackageAndClassName()
- throws JavadocLinkSyntaxError, AlternativeNotFoundError {
- Set<String> publicApis = Collections.unmodifiableSet(new HashSet<String>(
+ throws JavadocLinkSyntaxError, AlternativeNotFoundError,
+ RequiredAlternativeNotSpecifiedError {
+ Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList("La/b/C;->bar(I)V")));
ApiResolver resolver = new ApiResolver(publicApis);
- resolver.resolvePublicAlternatives("Foo {@link a.b.C}", "Lb/c/D;->bar()V");
+ resolver.resolvePublicAlternatives("Foo {@link a.b.C}", "Lb/c/D;->bar()V", 1);
}
@Test
public void testPublicAlternativesJustClassName()
- throws JavadocLinkSyntaxError, AlternativeNotFoundError {
- Set<String> publicApis = Collections.unmodifiableSet(new HashSet<String>(
+ throws JavadocLinkSyntaxError, AlternativeNotFoundError,
+ RequiredAlternativeNotSpecifiedError {
+ Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList("La/b/C;->bar(I)V")));
ApiResolver resolver = new ApiResolver(publicApis);
- resolver.resolvePublicAlternatives("Foo {@link C}", "La/b/D;->bar()V");
+ resolver.resolvePublicAlternatives("Foo {@link C}", "La/b/D;->bar()V", 1);
}
@Test
public void testNoPublicAlternativesButHasExplanation()
- throws JavadocLinkSyntaxError, AlternativeNotFoundError {
- Set<String> publicApis = Collections.unmodifiableSet(new HashSet<String>());
+ throws JavadocLinkSyntaxError, AlternativeNotFoundError,
+ RequiredAlternativeNotSpecifiedError {
+ Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>());
ApiResolver resolver = new ApiResolver(publicApis);
- resolver.resolvePublicAlternatives("Foo {@code bar}", "La/b/C;->bar()V");
+ resolver.resolvePublicAlternatives("Foo {@code bar}", "La/b/C;->bar()V", 1);
+ }
+
+ @Test
+ public void testNoPublicAlternativesSpecifiedWithMaxSdk() {
+ Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>());
+ ApiResolver resolver = new ApiResolver(publicApis);
+ assertThrows(RequiredAlternativeNotSpecifiedError.class,
+ () -> resolver.resolvePublicAlternatives(null, "La/b/C;->bar()V", 29));
+ }
+
+ @Test
+ public void testNoPublicAlternativesSpecifiedWithMaxLessThanQ()
+ throws JavadocLinkSyntaxError, AlternativeNotFoundError,
+ RequiredAlternativeNotSpecifiedError {
+ Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>());
+ ApiResolver resolver = new ApiResolver(publicApis);
+ resolver.resolvePublicAlternatives(null, "La/b/C;->bar()V", 28);
}
}
\ No newline at end of file