Enable CTS verification of overlayable API

Allows retrieval of a string representation of overlayable resources
that can be compared during CTS testing to verify that the overlayable
resources on device match the expected overlayable API.

Bug: 135052616
Test: libandroidfw_tests
Test: atest OverlayHostTest
Change-Id: I613f28c202a0904a917577f932d072111c1aa7bd
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 01caf01..eec49df 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -224,6 +224,62 @@
   return &loaded_package->GetOverlayableMap();
 }
 
+bool AssetManager2::GetOverlayablesToString(const android::StringPiece& package_name,
+                                            std::string* out) const {
+  uint8_t package_id = 0U;
+  for (const auto& apk_assets : apk_assets_) {
+    const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc();
+    if (loaded_arsc == nullptr) {
+      continue;
+    }
+
+    const auto& loaded_packages = loaded_arsc->GetPackages();
+    if (loaded_packages.empty()) {
+      continue;
+    }
+
+    const auto& loaded_package = loaded_packages[0];
+    if (loaded_package->GetPackageName() == package_name) {
+      package_id = GetAssignedPackageId(loaded_package.get());
+      break;
+    }
+  }
+
+  if (package_id == 0U) {
+    ANDROID_LOG(ERROR) << base::StringPrintf("No package with name '%s", package_name.data());
+    return false;
+  }
+
+  const size_t idx = package_ids_[package_id];
+  if (idx == 0xff) {
+    return false;
+  }
+
+  std::string output;
+  for (const ConfiguredPackage& package : package_groups_[idx].packages_) {
+    const LoadedPackage* loaded_package = package.loaded_package_;
+    for (auto it = loaded_package->begin(); it != loaded_package->end(); it++) {
+      const OverlayableInfo* info = loaded_package->GetOverlayableInfo(*it);
+      if (info != nullptr) {
+        ResourceName res_name;
+        if (!GetResourceName(*it, &res_name)) {
+          ANDROID_LOG(ERROR) << base::StringPrintf(
+              "Unable to retrieve name of overlayable resource 0x%08x", *it);
+          return false;
+        }
+
+        const std::string name = ToFormattedResourceString(&res_name);
+        output.append(base::StringPrintf(
+            "resource='%s' overlayable='%s' actor='%s' policy='0x%08x'\n",
+            name.c_str(), info->name.c_str(), info->actor.c_str(), info->policy_flags));
+      }
+    }
+  }
+
+  *out = std::move(output);
+  return true;
+}
+
 void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
   const int diff = configuration_.diff(configuration);
   configuration_ = configuration;
@@ -1073,7 +1129,7 @@
   }
 }
 
-uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) {
+uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const {
   for (auto& package_group : package_groups_) {
     for (auto& package2 : package_group.packages_) {
       if (package2.loaded_package_ == package) {
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 1e2b36cb1..de46081 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -124,6 +124,10 @@
   // This may be nullptr if the APK represented by `cookie` has no resource table.
   const DynamicRefTable* GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const;
 
+  // Returns a string representation of the overlayable API of a package.
+  bool GetOverlayablesToString(const android::StringPiece& package_name,
+                               std::string* out) const;
+
   const std::unordered_map<std::string, std::string>*
     GetOverlayableMapForPackage(uint32_t package_id) const;
 
@@ -308,7 +312,7 @@
   const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids);
 
   // Retrieve the assigned package id of the package if loaded into this AssetManager
-  uint8_t GetAssignedPackageId(const LoadedPackage* package);
+  uint8_t GetAssignedPackageId(const LoadedPackage* package) const;
 
   // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
   // have a longer lifetime.
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 40c8e46..1591024 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -707,7 +707,7 @@
   EXPECT_EQ("", resultDisabled);
 }
 
-TEST_F(AssetManager2Test, GetOverlayableMap) {
+TEST_F(AssetManager2Test, GetOverlayablesToString) {
   ResTable_config desired_config;
   memset(&desired_config, 0, sizeof(desired_config));
 
@@ -721,6 +721,12 @@
   ASSERT_EQ(2, map->size());
   ASSERT_EQ(map->at("OverlayableResources1"), "overlay://theme");
   ASSERT_EQ(map->at("OverlayableResources2"), "overlay://com.android.overlayable");
+
+  std::string api;
+  ASSERT_TRUE(assetmanager.GetOverlayablesToString("com.android.overlayable", &api));
+  ASSERT_EQ(api.find("not_overlayable"), std::string::npos);
+  ASSERT_NE(api.find("resource='com.android.overlayable:string/overlayable2' overlayable='OverlayableResources1' actor='overlay://theme' policy='0x0000000a'\n"),
+            std::string::npos);
 }
 
 }  // namespace android