/* * 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.CallableItem import com.android.tools.metalava.model.ClassItem import com.android.tools.metalava.model.Item import com.android.tools.metalava.model.text.ApiFile import java.io.File import java.io.FileWriter /** Usage: extract-flagged-apis */ fun main(args: Array) { 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, callables: List, packageName: String, builder: FlagApiMap.Builder ) { if (callables.isEmpty()) return val classFlag = getClassFlag(classItem) for (callable in callables) { val callableFlag = getFlagAnnotation(callable) ?: classFlag val api = JavaMethod.newBuilder() .setPackageName(packageName) .setClassName(classItem.fullName()) .setMethodName(callable.name()) for (param in callable.parameters()) { api.addParameters(param.type().toTypeString()) } if (callableFlag != null) { addFlaggedApi(builder, api, callableFlag) } } } 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 a nested class, use its @FlaggedApi annotation value. // Otherwise, use the flag value of the closest outer class that is annotated by @FlaggedApi. while (classFlag == null) { cur = cur.containingClass() ?: break classFlag = getFlagAnnotation(cur) } return classFlag } fun getFlagAnnotation(item: Item): String? { return item.modifiers .findAnnotation("android.annotation.FlaggedApi") ?.findAttribute("value") ?.value ?.value() as? String }