Ignore empty stub dex jars

Some modules do not provide APIs for all API surfaces, e.g.
sdk-extensions does not provide any public APIs, only system and above.
That means that the public stub dex jars are empty of classes*.dex
files. This change prevents "hiddenapi list" from failing in that
situation.

Bug: 179354495
Test: - migrate packages/modules/SdkExtensions to use bootclasspath_fragment
      - add the fragment to the platform-bootclasspath
      m out/soong/hiddenapi/hiddenapi-flags.csv
      - the previous command will verify that the SdkExtensions' generated
        all-stubs.flags matches the subset of monolithic flags that overlap.
      - check the SdkExtensions' generated all-stubs.flags to make sure it is
        complete.
Change-Id: Ib2f18b5ba4adec21b783a274fda8a3b797609f35
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index a14b92c..3805825 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -231,8 +231,8 @@
 
 class ClassPath final {
  public:
-  ClassPath(const std::vector<std::string>& dex_paths, bool open_writable) {
-    OpenDexFiles(dex_paths, open_writable);
+  ClassPath(const std::vector<std::string>& dex_paths, bool open_writable, bool ignore_empty) {
+    OpenDexFiles(dex_paths, open_writable, ignore_empty);
   }
 
   template<typename Fn>
@@ -270,7 +270,9 @@
   }
 
  private:
-  void OpenDexFiles(const std::vector<std::string>& dex_paths, bool open_writable) {
+  void OpenDexFiles(const std::vector<std::string>& dex_paths,
+                    bool open_writable,
+                    bool ignore_empty) {
     ArtDexFileLoader dex_loader;
     std::string error_msg;
 
@@ -305,7 +307,10 @@
                                        /* verify_checksum= */ true,
                                        &error_msg,
                                        &dex_files_);
-        CHECK(success) << "Open failed for '" << filename << "' " << error_msg;
+        // If requested ignore a jar with no classes.dex files.
+        if (!success && ignore_empty && error_msg != "Entry not found") {
+          CHECK(success) << "Open failed for '" << filename << "' " << error_msg;
+        }
       }
     }
   }
@@ -956,7 +961,9 @@
       const std::string& input_path = boot_dex_paths_[i];
       const std::string& output_path = output_dex_paths_[i];
 
-      ClassPath boot_classpath({ input_path }, /* open_writable= */ false);
+      ClassPath boot_classpath({ input_path },
+                               /* open_writable= */ false,
+                               /* ignore_empty= */ false);
       std::vector<const DexFile*> input_dex_files = boot_classpath.GetDexFiles();
       CHECK_EQ(input_dex_files.size(), 1u);
       const DexFile& input_dex = *input_dex_files[0];
@@ -1041,7 +1048,9 @@
     std::set<std::string> unresolved;
 
     // Open all dex files.
-    ClassPath boot_classpath(boot_dex_paths_, /* open_writable= */ false);
+    ClassPath boot_classpath(boot_dex_paths_,
+                             /* open_writable= */ false,
+                             /* ignore_empty= */ false);
     Hierarchy boot_hierarchy(boot_classpath);
 
     // Mark all boot dex members private.
@@ -1051,8 +1060,12 @@
 
     // 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
+      // for the current kind, e.g. framework-sdkextensions does not provide
+      // any public APIs.
       ClassPath stub_classpath(android::base::Split(cp_entry.first, ":"),
-                               /* open_writable= */ false);
+                               /* open_writable= */ false,
+                               /* ignore_empty= */ true);
       Hierarchy stub_hierarchy(stub_classpath);
       const ApiStubs::Kind stub_api = cp_entry.second;