check-flagged-apis: parse API signature files

Teach check-flagged-apis to extract flagged APIs from API signature files.

To keep things simple, only consider fields for now: support for classes
and methods will be added in a later CL.

Note: `m frameworks-base-api-current.txt` will generate an API signature
file that includes both the platform and mainline APIs.

Bug: 334870672
Test: atest --host check-flagged-apis-test
Test: check-flagged-apis --api-signature out/target/product/mainline_x86/obj/ETC/frameworks-base-api-current.txt_intermediates/frameworks-base-api-current.txt
Change-Id: Ic244b896672569f44af793796189b34c1f9d0c36
diff --git a/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt b/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt
index af8be11..5fede7b 100644
--- a/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt
+++ b/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt
@@ -17,8 +17,16 @@
 
 package com.android.checkflaggedapis
 
+import com.android.tools.metalava.model.BaseItemVisitor
+import com.android.tools.metalava.model.FieldItem
+import com.android.tools.metalava.model.text.ApiFile
 import com.github.ajalt.clikt.core.CliktCommand
 import com.github.ajalt.clikt.core.ProgramResult
+import com.github.ajalt.clikt.parameters.options.help
+import com.github.ajalt.clikt.parameters.options.option
+import com.github.ajalt.clikt.parameters.options.required
+import com.github.ajalt.clikt.parameters.types.path
+import java.io.InputStream
 
 /**
  * Class representing the fully qualified name of a class, method or field.
@@ -70,11 +78,58 @@
   override fun toString(): String = name.toString()
 }
 
-class CheckCommand : CliktCommand() {
+class CheckCommand :
+    CliktCommand(
+        help =
+            """
+Check that all flagged APIs are used in the correct way.
+
+This tool reads the API signature file and checks that all flagged APIs are used in the correct way.
+
+The tool will exit with a non-zero exit code if any flagged APIs are found to be used in the incorrect way.
+""") {
+  private val apiSignaturePath by
+      option("--api-signature")
+          .help(
+              """
+              Path to API signature file.
+              Usually named *current.txt.
+              Tip: `m frameworks-base-api-current.txt` will generate a file that includes all platform and mainline APIs.
+              """)
+          .path(mustExist = true, canBeDir = false, mustBeReadable = true)
+          .required()
+
   override fun run() {
-    println("hello world")
+    @Suppress("UNUSED_VARIABLE")
+    val flaggedSymbols =
+        apiSignaturePath.toFile().inputStream().use {
+          parseApiSignature(apiSignaturePath.toString(), it)
+        }
     throw ProgramResult(0)
   }
 }
 
+internal fun parseApiSignature(path: String, input: InputStream): Set<Pair<Symbol, Flag>> {
+  // TODO(334870672): add support for classes and metods
+  val output = mutableSetOf<Pair<Symbol, Flag>>()
+  val visitor =
+      object : BaseItemVisitor() {
+        override fun visitField(field: FieldItem) {
+          val flag =
+              field.modifiers
+                  .findAnnotation("android.annotation.FlaggedApi")
+                  ?.findAttribute("value")
+                  ?.value
+                  ?.value() as? String
+          if (flag != null) {
+            val symbol = Symbol.create(field.baselineElementId())
+            output.add(Pair(symbol, Flag(flag)))
+          }
+        }
+      }
+  val codebase = ApiFile.parseApi(path, input)
+  codebase.accept(visitor)
+  return output
+}
+
 fun main(args: Array<String>) = CheckCommand().main(args)