Support modularization in "hiddenapi list" tool

Previously, the "hiddenapi list" tool assumed that it was generating
flags for the whole bootclasspath and so did not need to differentiate
between classes that are provided by the bootclasspath fragment and
classes that are simply used by it. That meant that it would output
flags for this bootclasspath fragment plus all the other parts of the
bootclasspath on which this depends.

e.g. When generating the flags for i18n it would also output the flags
for art too.

This change adds the --dependency-stub-dex option to specify those dex
files that contain the API stubs provided by other bootclasspath
fragments and used by this one. A file specified in the
--dependency-stub-dex option is treated in a similar way to the files
added to the --boot-dex but its members are ignored when generating the
output file.

Bug: 179354495
Test: - enable hiddenapi processing in migrate packages/modules/RuntimeI18N
      m out/soong/hiddenapi/hiddenapi-flags.csv
      - the previous command will verify that the I18N's generated
        all-stubs.flags matches the subset of monolithic flags that overlap.
      - check the I18N's generated all-stubs.flags to make sure it is
        complete.
Merged-In: I7307500eff1141d161625ec69696802ff3ab82a9
Change-Id: I7307500eff1141d161625ec69696802ff3ab82a9
(cherry picked from commit 6d8d68efbe2101e2d5fdf078556cbc1f286c37c7)
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index 3805825..c249a7a 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -86,6 +86,10 @@
   UsageError("        Disable check that all dex entries have been assigned a flag");
   UsageError("");
   UsageError("  Command \"list\": dump lists of public and private API");
+  UsageError("    --dependency-stub-dex=<filename>: dex file containing API stubs provided");
+  UsageError("      by other parts of the bootclasspath. These are used to resolve");
+  UsageError("      dependencies in dex files specified in --boot-dex but do not appear in");
+  UsageError("      the output");
   UsageError("    --boot-dex=<filename>: dex file which belongs to boot class path");
   UsageError("    --public-stub-classpath=<filenames>:");
   UsageError("    --system-stub-classpath=<filenames>:");
@@ -913,7 +917,12 @@
         for (int i = 1; i < argc; ++i) {
           const char* raw_option = argv[i];
           const std::string_view option(raw_option);
-          if (StartsWith(option, "--boot-dex=")) {
+          if (StartsWith(option, "--dependency-stub-dex=")) {
+            const std::string path(std::string(option.substr(strlen("--dependency-stub-dex="))));
+            dependency_stub_dex_paths_.push_back(path);
+            // Add path to the boot dex path to resolve dependencies.
+            boot_dex_paths_.push_back(path);
+          } else if (StartsWith(option, "--boot-dex=")) {
             boot_dex_paths_.push_back(std::string(option.substr(strlen("--boot-dex="))));
           } else if (StartsWith(option, "--public-stub-classpath=")) {
             stub_classpaths_.push_back(std::make_pair(
@@ -1031,6 +1040,10 @@
     return api_flag_map;
   }
 
+  // A special flag added to the set of flags in boot_members to indicate that
+  // it should be excluded from the output.
+  static constexpr std::string_view kExcludeFromOutput{"exclude-from-output"};
+
   void ListApi() {
     if (boot_dex_paths_.empty()) {
       Usage("No boot DEX files specified");
@@ -1058,6 +1071,16 @@
       boot_members[boot_member.GetApiEntry()] = {};
     });
 
+    // Open all dependency API stub dex files.
+    ClassPath dependency_classpath(dependency_stub_dex_paths_,
+                                   /* open_writable= */ false,
+                                   /* ignore_empty= */ false);
+
+    // Mark all dependency API stub dex members as coming from the dependency.
+    dependency_classpath.ForEachDexMember([&](const DexMember& boot_member) {
+      boot_members[boot_member.GetApiEntry()] = {kExcludeFromOutput};
+    });
+
     // Resolve each SDK dex member against the framework and mark it white.
     for (const auto& cp_entry : stub_classpaths_) {
       // Ignore any empty stub jars as it just means that they provide no APIs
@@ -1097,9 +1120,14 @@
     // Write into public/private API files.
     std::ofstream file_flags(api_flags_path_.c_str());
     for (const auto& entry : boot_members) {
-      if (entry.second.empty()) {
+      std::set<std::string_view> flags = entry.second;
+      if (flags.empty()) {
+        // There are no flags so it cannot be from the dependency stub API dex
+        // files so just output the signature.
         file_flags << entry.first << std::endl;
-      } else {
+      } else if (flags.find(kExcludeFromOutput) == flags.end()) {
+        // The entry has flags and is not from the dependency stub API dex so
+        // output it.
         file_flags << entry.first << ",";
         file_flags << android::base::Join(entry.second, ",") << std::endl;
       }
@@ -1114,6 +1142,10 @@
   // Paths to DEX files which should be processed.
   std::vector<std::string> boot_dex_paths_;
 
+  // Paths to DEX files containing API stubs provided by other parts of the
+  // boot class path which the DEX files in boot_dex_paths depend.
+  std::vector<std::string> dependency_stub_dex_paths_;
+
   // Output paths where modified DEX files should be written.
   std::vector<std::string> output_dex_paths_;