Skip to content
Snippets Groups Projects
Commit dd35c2f8 authored by Yiming Pan's avatar Yiming Pan
Browse files

APIs in a nested class can be flagged by outer class.

If a class is not an inner class, use its @FlaggedApi annotation value.
Otherwise, use the flag value of the closest outer class that is annotated by
@FlaggedApi.

Bug: 331294167
Test: atest extract-flagged-apis-test
Change-Id: I9d40d3e7c5065a2a737d5420c4235445c6d16654
parent 22be9ae7
No related branches found
No related tags found
No related merge requests found
......@@ -17,6 +17,7 @@
package android.platform.coverage
import com.android.tools.metalava.model.ClassItem
import com.android.tools.metalava.model.Item
import com.android.tools.metalava.model.MethodItem
import com.android.tools.metalava.model.text.ApiFile
import java.io.File
......@@ -44,20 +45,9 @@ fun extractFlaggedApisFromClass(
builder: FlagApiMap.Builder
) {
if (methods.isEmpty()) return
val classFlag =
classItem.modifiers
.findAnnotation("android.annotation.FlaggedApi")
?.findAttribute("value")
?.value
?.value() as? String
val classFlag = getClassFlag(classItem)
for (method in methods) {
val methodFlag =
method.modifiers
.findAnnotation("android.annotation.FlaggedApi")
?.findAttribute("value")
?.value
?.value() as? String
?: classFlag
val methodFlag = getFlagAnnotation(method) ?: classFlag
val api =
JavaMethod.newBuilder()
.setPackageName(packageName)
......@@ -81,3 +71,23 @@ fun addFlaggedApi(builder: FlagApiMap.Builder, api: JavaMethod.Builder, flag: St
builder.putFlagToApi(flag, apis)
}
}
fun getClassFlag(classItem: ClassItem): String? {
var classFlag = getFlagAnnotation(classItem)
var cur = classItem
// If a class is not an inner class, use its @FlaggedApi annotation value.
// Otherwise, use the flag value of the closest outer class that is annotated by @FlaggedApi.
while (cur.isInnerClass() && classFlag == null) {
cur = cur.parent() as ClassItem
classFlag = getFlagAnnotation(cur)
}
return classFlag
}
fun getFlagAnnotation(item: Item): String? {
return item.modifiers
.findAnnotation("android.annotation.FlaggedApi")
?.findAttribute("value")
?.value
?.value() as? String
}
......@@ -141,6 +141,84 @@ class ExtractFlaggedApisTest {
assertThat(result).isEqualTo(expected.build())
}
@Test
fun extractFlaggedApis_unflaggedNestedClassShouldUseOuterClassFlag() {
val apiText =
"""
// Signature format: 2.0
package android.location.provider {
@FlaggedApi(Flags.FLAG_NEW_GEOCODER) public final class ForwardGeocodeRequest implements android.os.Parcelable {
method public int describeContents();
}
public static final class ForwardGeocodeRequest.Builder {
method @NonNull public android.location.provider.ForwardGeocodeRequest build();
}
}
"""
.trimIndent()
Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND)
val process = Runtime.getRuntime().exec(createCommand())
process.waitFor()
val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8)
val result = TextFormat.parse(content, FlagApiMap::class.java)
val expected = FlagApiMap.newBuilder()
val api1 =
JavaMethod.newBuilder()
.setPackageName("android.location.provider")
.setClassName("ForwardGeocodeRequest")
.setMethodName("describeContents")
addFlaggedApi(expected, api1, "Flags.FLAG_NEW_GEOCODER")
val api2 =
JavaMethod.newBuilder()
.setPackageName("android.location.provider")
.setClassName("ForwardGeocodeRequest.Builder")
.setMethodName("build")
addFlaggedApi(expected, api2, "Flags.FLAG_NEW_GEOCODER")
assertThat(result).ignoringRepeatedFieldOrder().isEqualTo(expected.build())
}
@Test
fun extractFlaggedApis_unflaggedNestedClassShouldUseOuterClassFlag_deeplyNested() {
val apiText =
"""
// Signature format: 2.0
package android.package.xyz {
@FlaggedApi(outer_class_flag) public final class OuterClass {
method public int apiInOuterClass();
}
public final class OuterClass.Deeply.NestedClass {
method public void apiInNestedClass();
}
}
"""
.trimIndent()
Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND)
val process = Runtime.getRuntime().exec(createCommand())
process.waitFor()
val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8)
val result = TextFormat.parse(content, FlagApiMap::class.java)
val expected = FlagApiMap.newBuilder()
val api1 =
JavaMethod.newBuilder()
.setPackageName("android.package.xyz")
.setClassName("OuterClass")
.setMethodName("apiInOuterClass")
addFlaggedApi(expected, api1, "outer_class_flag")
val api2 =
JavaMethod.newBuilder()
.setPackageName("android.package.xyz")
.setClassName("OuterClass.Deeply.NestedClass")
.setMethodName("apiInNestedClass")
addFlaggedApi(expected, api2, "outer_class_flag")
assertThat(result).ignoringRepeatedFieldOrder().isEqualTo(expected.build())
}
private fun addFlaggedApi(builder: FlagApiMap.Builder, api: JavaMethod.Builder, flag: String) {
if (builder.containsFlagToApi(flag)) {
val updatedApis =
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment