| Yiming Pan | a95134c | 2023-11-15 00:26:25 +0000 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2023 The Android Open Source Project | 
|  | 3 | * | 
|  | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 5 | * you may not use this file except in compliance with the License. | 
|  | 6 | * You may obtain a copy of the License at | 
|  | 7 | * | 
|  | 8 | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 9 | * | 
|  | 10 | * Unless required by applicable law or agreed to in writing, software | 
|  | 11 | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 13 | * See the License for the specific language governing permissions and | 
|  | 14 | * limitations under the License. | 
|  | 15 | */ | 
|  | 16 |  | 
|  | 17 | package android.platform.coverage | 
|  | 18 |  | 
| Yiming Pan | 4649135 | 2024-03-26 08:01:26 +0000 | [diff] [blame] | 19 | import com.android.tools.metalava.model.ClassItem | 
| Yiming Pan | dd35c2f | 2024-04-10 23:07:29 +0000 | [diff] [blame] | 20 | import com.android.tools.metalava.model.Item | 
| Yiming Pan | 4649135 | 2024-03-26 08:01:26 +0000 | [diff] [blame] | 21 | import com.android.tools.metalava.model.MethodItem | 
| Yiming Pan | a95134c | 2023-11-15 00:26:25 +0000 | [diff] [blame] | 22 | import com.android.tools.metalava.model.text.ApiFile | 
|  | 23 | import java.io.File | 
|  | 24 | import java.io.FileWriter | 
|  | 25 |  | 
|  | 26 | /** Usage: extract-flagged-apis <api text file> <output .pb file> */ | 
|  | 27 | fun main(args: Array<String>) { | 
| Yiming Pan | 4649135 | 2024-03-26 08:01:26 +0000 | [diff] [blame] | 28 | val cb = ApiFile.parseApi(listOf(File(args[0]))) | 
|  | 29 | val builder = FlagApiMap.newBuilder() | 
| Yiming Pan | 090155e | 2023-12-08 08:29:41 +0000 | [diff] [blame] | 30 | for (pkg in cb.getPackages().packages) { | 
| Yiming Pan | 4649135 | 2024-03-26 08:01:26 +0000 | [diff] [blame] | 31 | val packageName = pkg.qualifiedName() | 
| Yiming Pan | 823c15d | 2024-04-01 07:30:27 +0000 | [diff] [blame] | 32 | pkg.allClasses().forEach { | 
|  | 33 | extractFlaggedApisFromClass(it, it.methods(), packageName, builder) | 
|  | 34 | extractFlaggedApisFromClass(it, it.constructors(), packageName, builder) | 
|  | 35 | } | 
| Yiming Pan | a95134c | 2023-11-15 00:26:25 +0000 | [diff] [blame] | 36 | } | 
|  | 37 | val flagApiMap = builder.build() | 
|  | 38 | FileWriter(args[1]).use { it.write(flagApiMap.toString()) } | 
|  | 39 | } | 
| Yiming Pan | 4649135 | 2024-03-26 08:01:26 +0000 | [diff] [blame] | 40 |  | 
|  | 41 | fun extractFlaggedApisFromClass( | 
|  | 42 | classItem: ClassItem, | 
|  | 43 | methods: List<MethodItem>, | 
|  | 44 | packageName: String, | 
|  | 45 | builder: FlagApiMap.Builder | 
|  | 46 | ) { | 
| Yiming Pan | 823c15d | 2024-04-01 07:30:27 +0000 | [diff] [blame] | 47 | if (methods.isEmpty()) return | 
| Yiming Pan | dd35c2f | 2024-04-10 23:07:29 +0000 | [diff] [blame] | 48 | val classFlag = getClassFlag(classItem) | 
| Yiming Pan | 4649135 | 2024-03-26 08:01:26 +0000 | [diff] [blame] | 49 | for (method in methods) { | 
| Yiming Pan | dd35c2f | 2024-04-10 23:07:29 +0000 | [diff] [blame] | 50 | val methodFlag = getFlagAnnotation(method) ?: classFlag | 
| Yiming Pan | 4649135 | 2024-03-26 08:01:26 +0000 | [diff] [blame] | 51 | val api = | 
|  | 52 | JavaMethod.newBuilder() | 
|  | 53 | .setPackageName(packageName) | 
|  | 54 | .setClassName(classItem.fullName()) | 
|  | 55 | .setMethodName(method.name()) | 
|  | 56 | for (param in method.parameters()) { | 
|  | 57 | api.addParameters(param.type().toTypeString()) | 
|  | 58 | } | 
|  | 59 | if (methodFlag != null) { | 
|  | 60 | addFlaggedApi(builder, api, methodFlag) | 
|  | 61 | } | 
|  | 62 | } | 
|  | 63 | } | 
|  | 64 |  | 
|  | 65 | fun addFlaggedApi(builder: FlagApiMap.Builder, api: JavaMethod.Builder, flag: String) { | 
|  | 66 | if (builder.containsFlagToApi(flag)) { | 
|  | 67 | val updatedApis = builder.getFlagToApiOrThrow(flag).toBuilder().addJavaMethods(api).build() | 
|  | 68 | builder.putFlagToApi(flag, updatedApis) | 
|  | 69 | } else { | 
|  | 70 | val apis = FlaggedApis.newBuilder().addJavaMethods(api).build() | 
|  | 71 | builder.putFlagToApi(flag, apis) | 
|  | 72 | } | 
|  | 73 | } | 
| Yiming Pan | dd35c2f | 2024-04-10 23:07:29 +0000 | [diff] [blame] | 74 |  | 
|  | 75 | fun getClassFlag(classItem: ClassItem): String? { | 
|  | 76 | var classFlag = getFlagAnnotation(classItem) | 
|  | 77 | var cur = classItem | 
| Paul Duffin | ecb807b | 2024-07-02 15:03:00 +0100 | [diff] [blame] | 78 | // If a class is not a nested class, use its @FlaggedApi annotation value. | 
| Yiming Pan | dd35c2f | 2024-04-10 23:07:29 +0000 | [diff] [blame] | 79 | // Otherwise, use the flag value of the closest outer class that is annotated by @FlaggedApi. | 
| Paul Duffin | ecb807b | 2024-07-02 15:03:00 +0100 | [diff] [blame] | 80 | while (classFlag == null) { | 
|  | 81 | cur = cur.containingClass() ?: break | 
| Yiming Pan | dd35c2f | 2024-04-10 23:07:29 +0000 | [diff] [blame] | 82 | classFlag = getFlagAnnotation(cur) | 
|  | 83 | } | 
|  | 84 | return classFlag | 
|  | 85 | } | 
|  | 86 |  | 
|  | 87 | fun getFlagAnnotation(item: Item): String? { | 
|  | 88 | return item.modifiers | 
|  | 89 | .findAnnotation("android.annotation.FlaggedApi") | 
|  | 90 | ?.findAttribute("value") | 
|  | 91 | ?.value | 
|  | 92 | ?.value() as? String | 
|  | 93 | } |