| /* | |
| * Copyright (C) 2023 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 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 | |
| import java.io.FileWriter | |
| /** Usage: extract-flagged-apis <api text file> <output .pb file> */ | |
| fun main(args: Array<String>) { | |
| val cb = ApiFile.parseApi(listOf(File(args[0]))) | |
| val builder = FlagApiMap.newBuilder() | |
| for (pkg in cb.getPackages().packages) { | |
| val packageName = pkg.qualifiedName() | |
| pkg.allClasses().forEach { | |
| extractFlaggedApisFromClass(it, it.methods(), packageName, builder) | |
| extractFlaggedApisFromClass(it, it.constructors(), packageName, builder) | |
| } | |
| } | |
| val flagApiMap = builder.build() | |
| FileWriter(args[1]).use { it.write(flagApiMap.toString()) } | |
| } | |
| fun extractFlaggedApisFromClass( | |
| classItem: ClassItem, | |
| methods: List<MethodItem>, | |
| packageName: String, | |
| builder: FlagApiMap.Builder | |
| ) { | |
| if (methods.isEmpty()) return | |
| val classFlag = getClassFlag(classItem) | |
| for (method in methods) { | |
| val methodFlag = getFlagAnnotation(method) ?: classFlag | |
| val api = | |
| JavaMethod.newBuilder() | |
| .setPackageName(packageName) | |
| .setClassName(classItem.fullName()) | |
| .setMethodName(method.name()) | |
| for (param in method.parameters()) { | |
| api.addParameters(param.type().toTypeString()) | |
| } | |
| if (methodFlag != null) { | |
| addFlaggedApi(builder, api, methodFlag) | |
| } | |
| } | |
| } | |
| fun addFlaggedApi(builder: FlagApiMap.Builder, api: JavaMethod.Builder, flag: String) { | |
| if (builder.containsFlagToApi(flag)) { | |
| val updatedApis = builder.getFlagToApiOrThrow(flag).toBuilder().addJavaMethods(api).build() | |
| builder.putFlagToApi(flag, updatedApis) | |
| } else { | |
| val apis = FlaggedApis.newBuilder().addJavaMethods(api).build() | |
| 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 | |
| } |