AssetManager2: Add other support methods

- Add GetResourceConfigurations()
- Add GetResourceLocales()
- Add ResolveReference()
- Add stub for GetResourceId()
- Change LoadedArsc and ApkAssets factory method to return const

Test: make libandroidfw_tests
Change-Id: Ia797dc9381a523b1a3e7029048a413e544730379
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 9a08f63..fe68ec0 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -27,16 +27,17 @@
 
 namespace android {
 
-std::unique_ptr<ApkAssets> ApkAssets::Load(const std::string& path) {
-  return ApkAssets::LoadImpl(path, false /*load_as_shared_library*/);
+std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) {
+  return ApkAssets::LoadImpl(path, system, false /*load_as_shared_library*/);
 }
 
-std::unique_ptr<ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path) {
-  return ApkAssets::LoadImpl(path, true /*load_as_shared_library*/);
+std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path,
+                                                                bool system) {
+  return ApkAssets::LoadImpl(path, system, true /*load_as_shared_library*/);
 }
 
-std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(const std::string& path,
-                                               bool load_as_shared_library) {
+std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(const std::string& path, bool system,
+                                                     bool load_as_shared_library) {
   ATRACE_CALL();
   ::ZipArchiveHandle unmanaged_handle;
   int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
@@ -70,11 +71,13 @@
   loaded_apk->path_ = path;
   loaded_apk->loaded_arsc_ =
       LoadedArsc::Load(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/),
-                       loaded_apk->resources_asset_->getLength(), load_as_shared_library);
+                       loaded_apk->resources_asset_->getLength(), system, load_as_shared_library);
   if (loaded_apk->loaded_arsc_ == nullptr) {
     return {};
   }
-  return loaded_apk;
+
+  // Need to force a move for mingw32.
+  return std::move(loaded_apk);
 }
 
 std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode /*mode*/) const {
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index d2eff65..542a125 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -18,6 +18,8 @@
 
 #include "androidfw/AssetManager2.h"
 
+#include <set>
+
 #include "android-base/logging.h"
 #include "android-base/stringprintf.h"
 #include "utils/ByteOrder.h"
@@ -143,6 +145,36 @@
   }
 }
 
+std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
+                                                                   bool exclude_mipmap) {
+  ATRACE_CALL();
+  std::set<ResTable_config> configurations;
+  for (const PackageGroup& package_group : package_groups_) {
+    for (const LoadedPackage* package : package_group.packages_) {
+      if (exclude_system && package->IsSystem()) {
+        continue;
+      }
+      package->CollectConfigurations(exclude_mipmap, &configurations);
+    }
+  }
+  return configurations;
+}
+
+std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system,
+                                                        bool merge_equivalent_languages) {
+  ATRACE_CALL();
+  std::set<std::string> locales;
+  for (const PackageGroup& package_group : package_groups_) {
+    for (const LoadedPackage* package : package_group.packages_) {
+      if (exclude_system && package->IsSystem()) {
+        continue;
+      }
+      package->CollectLocales(merge_equivalent_languages, &locales);
+    }
+  }
+  return locales;
+}
+
 std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) {
   const std::string new_path = "assets/" + filename;
   return OpenNonAsset(new_path, mode);
@@ -325,8 +357,15 @@
   if (dtohl(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) {
     if (!may_be_bag) {
       LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
+      return kInvalidCookie;
     }
-    return kInvalidCookie;
+
+    // Create a reference since we can't represent this complex type as a Res_value.
+    out_value->dataType = Res_value::TYPE_REFERENCE;
+    out_value->data = resid;
+    *out_selected_config = config;
+    *out_flags = flags;
+    return cookie;
   }
 
   const Res_value* device_value = reinterpret_cast<const Res_value*>(
@@ -341,6 +380,37 @@
   return cookie;
 }
 
+ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
+                                                ResTable_config* in_out_selected_config,
+                                                uint32_t* in_out_flags,
+                                                ResTable_ref* out_last_reference) {
+  ATRACE_CALL();
+  constexpr const int kMaxIterations = 20;
+
+  out_last_reference->ident = 0u;
+  for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE &&
+                              in_out_value->data != 0u && iteration < kMaxIterations;
+       iteration++) {
+    if (out_last_reference != nullptr) {
+      out_last_reference->ident = in_out_value->data;
+    }
+    uint32_t new_flags = 0u;
+    cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/,
+                         in_out_value, in_out_selected_config, &new_flags);
+    if (cookie == kInvalidCookie) {
+      return kInvalidCookie;
+    }
+    if (in_out_flags != nullptr) {
+      *in_out_flags |= new_flags;
+    }
+    if (out_last_reference->ident == in_out_value->data) {
+      // This reference can't be resolved, so exit now and let the caller deal with it.
+      return cookie;
+    }
+  }
+  return cookie;
+}
+
 const ResolvedBag* AssetManager2::GetBag(uint32_t resid) {
   ATRACE_CALL();
 
@@ -501,6 +571,15 @@
   return result;
 }
 
+uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
+                                      const std::string& fallback_type,
+                                      const std::string& fallback_package) {
+  (void)resource_name;
+  (void)fallback_type;
+  (void)fallback_package;
+  return 0u;
+}
+
 void AssetManager2::InvalidateCaches(uint32_t diff) {
   if (diff == 0xffffffffu) {
     // Everything must go.
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index c7d0fa5..cb589ec 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -321,6 +321,57 @@
   return true;
 }
 
+void LoadedPackage::CollectConfigurations(bool exclude_mipmap,
+                                          std::set<ResTable_config>* out_configs) const {
+  const static std::u16string kMipMap = u"mipmap";
+  const size_t type_count = type_specs_.size();
+  for (size_t i = 0; i < type_count; i++) {
+    const util::unique_cptr<TypeSpec>& type_spec = type_specs_[i];
+    if (type_spec != nullptr) {
+      if (exclude_mipmap) {
+        const int type_idx = type_spec->type_spec->id - 1;
+        size_t type_name_len;
+        const char16_t* type_name16 = type_string_pool_.stringAt(type_idx, &type_name_len);
+        if (type_name16 != nullptr) {
+          if (kMipMap.compare(0, std::u16string::npos, type_name16, type_name_len) == 0) {
+            // This is a mipmap type, skip collection.
+            continue;
+          }
+        }
+        const char* type_name = type_string_pool_.string8At(type_idx, &type_name_len);
+        if (type_name != nullptr) {
+          if (strncmp(type_name, "mipmap", type_name_len) == 0) {
+            // This is a mipmap type, skip collection.
+            continue;
+          }
+        }
+      }
+
+      for (size_t j = 0; j < type_spec->type_count; j++) {
+        out_configs->insert(type_spec->types[j].configuration);
+      }
+    }
+  }
+}
+
+void LoadedPackage::CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const {
+  char temp_locale[RESTABLE_MAX_LOCALE_LEN];
+  const size_t type_count = type_specs_.size();
+  for (size_t i = 0; i < type_count; i++) {
+    const util::unique_cptr<TypeSpec>& type_spec = type_specs_[i];
+    if (type_spec != nullptr) {
+      for (size_t j = 0; j < type_spec->type_count; j++) {
+        const ResTable_config& configuration = type_spec->types[j].configuration;
+        if (configuration.locale != 0) {
+          configuration.getBcp47Locale(temp_locale, canonicalize);
+          std::string locale(temp_locale);
+          out_locales->insert(std::move(locale));
+        }
+      }
+    }
+  }
+}
+
 std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) {
   ATRACE_CALL();
   std::unique_ptr<LoadedPackage> loaded_package{new LoadedPackage()};
@@ -574,6 +625,7 @@
         if (loaded_package->package_id_ == kAppPackageId) {
           loaded_package->dynamic_ = load_as_shared_library;
         }
+        loaded_package->system_ = system_;
         packages_.push_back(std::move(loaded_package));
       } break;
 
@@ -590,12 +642,13 @@
   return true;
 }
 
-std::unique_ptr<LoadedArsc> LoadedArsc::Load(const void* data, size_t len,
-                                             bool load_as_shared_library) {
+std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const void* data, size_t len, bool system,
+                                                   bool load_as_shared_library) {
   ATRACE_CALL();
 
   // Not using make_unique because the constructor is private.
   std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
+  loaded_arsc->system_ = system;
 
   ChunkIterator iter(data, len);
   while (iter.HasNext()) {
@@ -617,7 +670,9 @@
     LOG(ERROR) << iter.GetLastError();
     return {};
   }
-  return loaded_arsc;
+
+  // Need to force a move for mingw32.
+  return std::move(loaded_arsc);
 }
 
 }  // namespace android
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 9d4fd29..6d1578c 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -31,8 +31,9 @@
 // Holds an APK.
 class ApkAssets {
  public:
-  static std::unique_ptr<ApkAssets> Load(const std::string& path);
-  static std::unique_ptr<ApkAssets> LoadAsSharedLibrary(const std::string& path);
+  static std::unique_ptr<const ApkAssets> Load(const std::string& path, bool system = false);
+  static std::unique_ptr<const ApkAssets> LoadAsSharedLibrary(const std::string& path,
+                                                              bool system = false);
 
   std::unique_ptr<Asset> Open(const std::string& path,
                               Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const;
@@ -44,7 +45,8 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(ApkAssets);
 
-  static std::unique_ptr<ApkAssets> LoadImpl(const std::string& path, bool load_as_shared_library);
+  static std::unique_ptr<const ApkAssets> LoadImpl(const std::string& path, bool system,
+                                                   bool load_as_shared_library);
 
   ApkAssets() = default;
 
@@ -57,7 +59,7 @@
   ZipArchivePtr zip_handle_;
   std::string path_;
   std::unique_ptr<Asset> resources_asset_;
-  std::unique_ptr<LoadedArsc> loaded_arsc_;
+  std::unique_ptr<const LoadedArsc> loaded_arsc_;
 };
 
 }  // namespace android
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 8655339..81cdc46 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -21,6 +21,7 @@
 
 #include <array>
 #include <limits>
+#include <set>
 #include <unordered_map>
 
 #include "androidfw/ApkAssets.h"
@@ -112,6 +113,24 @@
 
   inline const ResTable_config& GetConfiguration() const { return configuration_; }
 
+  // Returns all configurations for which there are resources defined. This includes resource
+  // configurations in all the ApkAssets set for this AssetManager.
+  // If `exclude_system` is set to true, resource configurations from system APKs
+  // ('android' package, other libraries) will be excluded from the list.
+  // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap'
+  // will be excluded from the list.
+  std::set<ResTable_config> GetResourceConfigurations(bool exclude_system = false,
+                                                      bool exclude_mipmap = false);
+
+  // Returns all the locales for which there are resources defined. This includes resource
+  // locales in all the ApkAssets set for this AssetManager.
+  // If `exclude_system` is set to true, resource locales from system APKs
+  // ('android' package, other libraries) will be excluded from the list.
+  // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized
+  // and de-duped in the resulting list.
+  std::set<std::string> GetResourceLocales(bool exclude_system = false,
+                                           bool merge_equivalent_languages = false);
+
   // Searches the set of APKs loaded by this AssetManager and opens the first one found located
   // in the assets/ directory.
   // `mode` controls how the file is opened.
@@ -149,6 +168,14 @@
   // Returns false if the resource was not found.
   bool GetResourceFlags(uint32_t resid, uint32_t* out_flags);
 
+  // Finds the resource ID assigned to `resource_name`.
+  // `resource_name` must be of the form '[package:][type/]entry'.
+  // If no package is specified in `resource_name`, then `fallback_package` is used as the package.
+  // If no type is specified in `resource_name`, then `fallback_type` is used as the type.
+  // Returns 0x0 if no resource by that name was found.
+  uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {},
+                         const std::string& fallback_package = {});
+
   // Retrieves the best matching resource with ID `resid`. The resource value is filled into
   // `out_value` and the configuration for the selected value is populated in `out_selected_config`.
   // `out_flags` holds the same flags as retrieved with GetResourceFlags().
@@ -162,6 +189,22 @@
                               Res_value* out_value, ResTable_config* out_selected_config,
                               uint32_t* out_flags);
 
+  // Resolves the resource reference in `in_out_value` if the data type is
+  // Res_value::TYPE_REFERENCE.
+  // `cookie` is the ApkAssetsCookie of the reference in `in_out_value`.
+  // `in_out_value` is the reference to resolve. The result is placed back into this object.
+  // `in_out_flags` is the type spec flags returned from calls to GetResource() or
+  // GetResourceFlags(). Configuration flags of the values pointed to by the reference
+  // are OR'd together with `in_out_flags`.
+  // `in_out_config` is populated with the configuration for which the resolved value was defined.
+  // `out_last_reference` is populated with the last reference ID before resolving to an actual
+  // value.
+  // Returns the cookie of the APK the resolved resource was defined in, or kInvalidCookie if
+  // it was not found.
+  ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
+                                   ResTable_config* in_out_selected_config, uint32_t* in_out_flags,
+                                   ResTable_ref* out_last_reference);
+
   // Retrieves the best matching bag/map resource with ID `resid`.
   // This method will resolve all parent references for this bag and merge keys with the child.
   // To iterate over the keys, use the following idiom:
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index e8cb164..91a7cb7 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -18,6 +18,7 @@
 #define LOADEDARSC_H_
 
 #include <memory>
+#include <set>
 #include <vector>
 
 #include "android-base/macros.h"
@@ -68,20 +69,38 @@
                  LoadedArscEntry* out_entry, ResTable_config* out_selected_config,
                  uint32_t* out_flags) const;
 
+  // Returns the string pool where type names are stored.
   inline const ResStringPool* GetTypeStringPool() const { return &type_string_pool_; }
 
+  // Returns the string pool where the names of resource entries are stored.
   inline const ResStringPool* GetKeyStringPool() const { return &key_string_pool_; }
 
   inline const std::string& GetPackageName() const { return package_name_; }
 
   inline int GetPackageId() const { return package_id_; }
 
+  // Returns true if this package is dynamic (shared library) and needs to have an ID assigned.
   inline bool IsDynamic() const { return dynamic_; }
 
+  // Returns true if this package originates from a system provided resource.
+  inline bool IsSystem() const { return system_; }
+
+  // Returns the map of package name to package ID used in this LoadedPackage. At runtime, a
+  // package could have been assigned a different package ID than what this LoadedPackage was
+  // compiled with. AssetManager rewrites the package IDs so that they are compatible at runtime.
   inline const std::vector<DynamicPackageEntry>& GetDynamicPackageMap() const {
     return dynamic_package_map_;
   }
 
+  // Populates a set of ResTable_config structs, possibly excluding configurations defined for
+  // the mipmap type.
+  void CollectConfigurations(bool exclude_mipmap, std::set<ResTable_config>* out_configs) const;
+
+  // Populates a set of strings representing locales.
+  // If `canonicalize` is set to true, each locale is transformed into its canonical format
+  // before being inserted into the set. This may cause some equivalent locales to de-dupe.
+  void CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
 
@@ -93,8 +112,9 @@
   ResStringPool key_string_pool_;
   std::string package_name_;
   int package_id_ = -1;
-  bool dynamic_ = false;
   int type_id_offset_ = 0;
+  bool dynamic_ = false;
+  bool system_ = false;
 
   ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_;
   std::vector<DynamicPackageEntry> dynamic_package_map_;
@@ -104,10 +124,14 @@
 // when loading, including offsets and lengths.
 class LoadedArsc {
  public:
-  // Load the resource table from memory. The data's lifetime must out-live the
-  // object returned from this method.
-  static std::unique_ptr<LoadedArsc> Load(const void* data, size_t len,
-                                          bool load_as_shared_library = false);
+  // Load a resource table from memory pointed to by `data` of size `len`.
+  // The lifetime of `data` must out-live the LoadedArsc returned from this method.
+  // If `system` is set to true, the LoadedArsc is considered as a system provided resource.
+  // If `load_as_shared_library` is set to true, the application package (0x7f) is treated
+  // as a shared library (0x00). When loaded into an AssetManager, the package will be assigned an
+  // ID.
+  static std::unique_ptr<const LoadedArsc> Load(const void* data, size_t len, bool system = false,
+                                                bool load_as_shared_library = false);
 
   ~LoadedArsc();
 
@@ -125,6 +149,10 @@
   // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist.
   const LoadedPackage* GetPackageForId(uint32_t resid) const;
 
+  // Returns true if this is a system provided resource.
+  inline bool IsSystem() const { return system_; }
+
+  // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc.
   inline const std::vector<std::unique_ptr<const LoadedPackage>>& GetPackages() const {
     return packages_;
   }
@@ -137,6 +165,7 @@
 
   ResStringPool global_string_pool_;
   std::vector<std::unique_ptr<const LoadedPackage>> packages_;
+  bool system_ = false;
 };
 
 }  // namespace android
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 56c22e6..04a5d95 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1188,6 +1188,8 @@
     int compare(const ResTable_config& o) const;
     int compareLogical(const ResTable_config& o) const;
 
+    inline bool operator<(const ResTable_config& o) const { return compare(o) < 0; }
+
     // Flags indicating a set of config values.  These flag constants must
     // match the corresponding ones in android.content.pm.ActivityInfo and
     // attrs_manifest.xml.
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index fd96730..96b42bf 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -94,8 +94,12 @@
 
   inline bool operator==(const unique_cptr& o) const { return ptr_ == o.ptr_; }
 
+  inline bool operator!=(const unique_cptr& o) const { return ptr_ != o.ptr_; }
+
   inline bool operator==(std::nullptr_t) const { return ptr_ == nullptr; }
 
+  inline bool operator!=(std::nullptr_t) const { return ptr_ != nullptr; }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(unique_cptr);
 
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
index 0203712..6b4a719 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -24,7 +24,8 @@
 namespace android {
 
 TEST(ApkAssetsTest, LoadApk) {
-  std::unique_ptr<ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
+  std::unique_ptr<const ApkAssets> loaded_apk =
+      ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
   ASSERT_NE(nullptr, loaded_apk);
   EXPECT_NE(nullptr, loaded_apk->GetLoadedArsc());
 
@@ -33,7 +34,7 @@
 }
 
 TEST(ApkAssetsTest, LoadApkAsSharedLibrary) {
-  std::unique_ptr<ApkAssets> loaded_apk =
+  std::unique_ptr<const ApkAssets> loaded_apk =
       ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk");
   ASSERT_NE(nullptr, loaded_apk);
   const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
index b3c2dc3..273290a 100644
--- a/libs/androidfw/tests/AssetManager2_bench.cpp
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -38,7 +38,7 @@
 static void BM_AssetManagerLoadAssets(benchmark::State& state) {
   std::string path = GetTestDataPath() + "/basic/basic.apk";
   while (state.KeepRunning()) {
-    std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path);
+    std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path);
     AssetManager2 assets;
     assets.SetApkAssets({apk.get()});
   }
@@ -61,7 +61,7 @@
 static void BM_AssetManagerLoadFrameworkAssets(benchmark::State& state) {
   std::string path = kFrameworkPath;
   while (state.KeepRunning()) {
-    std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path);
+    std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path);
     AssetManager2 assets;
     assets.SetApkAssets({apk.get()});
   }
@@ -84,10 +84,10 @@
 static void GetResourceBenchmark(const std::vector<std::string>& paths,
                                  const ResTable_config* config, uint32_t resid,
                                  benchmark::State& state) {
-  std::vector<std::unique_ptr<ApkAssets>> apk_assets;
+  std::vector<std::unique_ptr<const ApkAssets>> apk_assets;
   std::vector<const ApkAssets*> apk_assets_ptrs;
   for (const std::string& path : paths) {
-    std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path);
+    std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path);
     if (apk == nullptr) {
       state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str());
       return;
@@ -187,7 +187,7 @@
 BENCHMARK(BM_AssetManagerGetResourceFrameworkLocaleOld);
 
 static void BM_AssetManagerGetBag(benchmark::State& state) {
-  std::unique_ptr<ApkAssets> apk = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
+  std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
   if (apk == nullptr) {
     state.SkipWithError("Failed to load assets");
     return;
@@ -234,4 +234,40 @@
 }
 BENCHMARK(BM_AssetManagerGetBagOld);
 
+static void BM_AssetManagerGetResourceLocales(benchmark::State& state) {
+  std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
+  if (apk == nullptr) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  AssetManager2 assets;
+  assets.SetApkAssets({apk.get()});
+
+  while (state.KeepRunning()) {
+    std::set<std::string> locales =
+        assets.GetResourceLocales(false /*exclude_system*/, true /*merge_equivalent_languages*/);
+    benchmark::DoNotOptimize(locales);
+  }
+}
+BENCHMARK(BM_AssetManagerGetResourceLocales);
+
+static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) {
+  AssetManager assets;
+  if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/,
+                           false /*isSystemAssets*/)) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  const ResTable& table = assets.getResources(true);
+
+  while (state.KeepRunning()) {
+    Vector<String8> locales;
+    table.getLocales(&locales, true /*includeSystemLocales*/, true /*mergeEquivalentLangs*/);
+    benchmark::DoNotOptimize(locales);
+  }
+}
+BENCHMARK(BM_AssetManagerGetResourceLocalesOld);
+
 }  // namespace android
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 543456a..557d8d4 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -26,6 +26,7 @@
 #include "data/lib_two/R.h"
 #include "data/libclient/R.h"
 #include "data/styles/R.h"
+#include "data/system/R.h"
 
 namespace app = com::android::app;
 namespace appaslib = com::android::appaslib::app;
@@ -59,16 +60,20 @@
 
     appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk");
     ASSERT_NE(nullptr, appaslib_assets_);
+
+    system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/);
+    ASSERT_NE(nullptr, system_assets_);
   }
 
  protected:
-  std::unique_ptr<ApkAssets> basic_assets_;
-  std::unique_ptr<ApkAssets> basic_de_fr_assets_;
-  std::unique_ptr<ApkAssets> style_assets_;
-  std::unique_ptr<ApkAssets> lib_one_assets_;
-  std::unique_ptr<ApkAssets> lib_two_assets_;
-  std::unique_ptr<ApkAssets> libclient_assets_;
-  std::unique_ptr<ApkAssets> appaslib_assets_;
+  std::unique_ptr<const ApkAssets> basic_assets_;
+  std::unique_ptr<const ApkAssets> basic_de_fr_assets_;
+  std::unique_ptr<const ApkAssets> style_assets_;
+  std::unique_ptr<const ApkAssets> lib_one_assets_;
+  std::unique_ptr<const ApkAssets> lib_two_assets_;
+  std::unique_ptr<const ApkAssets> libclient_assets_;
+  std::unique_ptr<const ApkAssets> appaslib_assets_;
+  std::unique_ptr<const ApkAssets> system_assets_;
 };
 
 TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) {
@@ -291,6 +296,131 @@
   EXPECT_EQ(0, bag_two->entries[4].cookie);
 }
 
+TEST_F(AssetManager2Test, ResolveReferenceToResource) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+  ApkAssetsCookie cookie =
+      assetmanager.GetResource(basic::R::integer::ref1, false /*may_be_bag*/,
+                               0u /*density_override*/, &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+  EXPECT_EQ(basic::R::integer::ref2, value.data);
+
+  ResTable_ref last_ref;
+  cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(12000u, value.data);
+  EXPECT_EQ(basic::R::integer::ref2, last_ref.ident);
+}
+
+TEST_F(AssetManager2Test, ResolveReferenceToBag) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+  ApkAssetsCookie cookie =
+      assetmanager.GetResource(basic::R::integer::number2, true /*may_be_bag*/,
+                               0u /*density_override*/, &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+  EXPECT_EQ(basic::R::array::integerArray1, value.data);
+
+  ResTable_ref last_ref;
+  cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+  EXPECT_EQ(basic::R::array::integerArray1, value.data);
+  EXPECT_EQ(basic::R::array::integerArray1, last_ref.ident);
+}
+
+static bool IsConfigurationPresent(const std::set<ResTable_config>& configurations,
+                                   const ResTable_config& configuration) {
+  return configurations.count(configuration) > 0;
+}
+
+TEST_F(AssetManager2Test, GetResourceConfigurations) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({system_assets_.get(), basic_de_fr_assets_.get()});
+
+  std::set<ResTable_config> configurations = assetmanager.GetResourceConfigurations();
+
+  // We expect the locale sv from the system assets, and de and fr from basic_de_fr assets.
+  // And one extra for the default configuration.
+  EXPECT_EQ(4u, configurations.size());
+
+  ResTable_config expected_config;
+  memset(&expected_config, 0, sizeof(expected_config));
+  expected_config.language[0] = 's';
+  expected_config.language[1] = 'v';
+  EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+
+  expected_config.language[0] = 'd';
+  expected_config.language[1] = 'e';
+  EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+
+  expected_config.language[0] = 'f';
+  expected_config.language[1] = 'r';
+  EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+
+  // Take out the system assets.
+  configurations = assetmanager.GetResourceConfigurations(true /* exclude_system */);
+
+  // We expect de and fr from basic_de_fr assets.
+  EXPECT_EQ(2u, configurations.size());
+
+  expected_config.language[0] = 's';
+  expected_config.language[1] = 'v';
+  EXPECT_FALSE(IsConfigurationPresent(configurations, expected_config));
+
+  expected_config.language[0] = 'd';
+  expected_config.language[1] = 'e';
+  EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+
+  expected_config.language[0] = 'f';
+  expected_config.language[1] = 'r';
+  EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+}
+
+TEST_F(AssetManager2Test, GetResourceLocales) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({system_assets_.get(), basic_de_fr_assets_.get()});
+
+  std::set<std::string> locales = assetmanager.GetResourceLocales();
+
+  // We expect the locale sv from the system assets, and de and fr from basic_de_fr assets.
+  EXPECT_EQ(3u, locales.size());
+  EXPECT_GT(locales.count("sv"), 0u);
+  EXPECT_GT(locales.count("de"), 0u);
+  EXPECT_GT(locales.count("fr"), 0u);
+
+  locales = assetmanager.GetResourceLocales(true /*exclude_system*/);
+  // We expect the de and fr locales from basic_de_fr assets.
+  EXPECT_EQ(2u, locales.size());
+  EXPECT_GT(locales.count("de"), 0u);
+  EXPECT_GT(locales.count("fr"), 0u);
+}
+
+TEST_F(AssetManager2Test, GetResourceId) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  EXPECT_EQ(basic::R::layout::main,
+            assetmanager.GetResourceId("com.android.basic:layout/main", "", ""));
+  EXPECT_EQ(basic::R::layout::main,
+            assetmanager.GetResourceId("layout/main", "", "com.android.basic"));
+  EXPECT_EQ(basic::R::layout::main,
+            assetmanager.GetResourceId("main", "layout", "com.android.basic"));
+}
+
 TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {}
 
 TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {}
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index f8aa61a..756869f 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -32,7 +32,8 @@
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", "resources.arsc",
                                       &contents));
 
-  std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+  std::unique_ptr<const LoadedArsc> loaded_arsc =
+      LoadedArsc::Load(contents.data(), contents.size());
   ASSERT_NE(nullptr, loaded_arsc);
 
   const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages();
@@ -58,7 +59,8 @@
   ASSERT_TRUE(
       ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents));
 
-  std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+  std::unique_ptr<const LoadedArsc> loaded_arsc =
+      LoadedArsc::Load(contents.data(), contents.size());
   ASSERT_NE(nullptr, loaded_arsc);
 
   ResTable_config desired_config;
@@ -80,7 +82,8 @@
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc",
                                       &contents));
 
-  std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+  std::unique_ptr<const LoadedArsc> loaded_arsc =
+      LoadedArsc::Load(contents.data(), contents.size());
   ASSERT_NE(nullptr, loaded_arsc);
 
   const auto& packages = loaded_arsc->GetPackages();
@@ -101,7 +104,8 @@
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/libclient/libclient.apk",
                                       "resources.arsc", &contents));
 
-  std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+  std::unique_ptr<const LoadedArsc> loaded_arsc =
+      LoadedArsc::Load(contents.data(), contents.size());
   ASSERT_NE(nullptr, loaded_arsc);
 
   const auto& packages = loaded_arsc->GetPackages();
@@ -128,8 +132,8 @@
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib.apk",
                                       "resources.arsc", &contents));
 
-  std::unique_ptr<LoadedArsc> loaded_arsc =
-      LoadedArsc::Load(contents.data(), contents.size(), true /*load_as_shared_library*/);
+  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(
+      contents.data(), contents.size(), false /*system*/, true /*load_as_shared_library*/);
   ASSERT_NE(nullptr, loaded_arsc);
 
   const auto& packages = loaded_arsc->GetPackages();
@@ -143,7 +147,8 @@
   std::string contents;
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc",
                                       &contents));
-  std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+  std::unique_ptr<const LoadedArsc> loaded_arsc =
+      LoadedArsc::Load(contents.data(), contents.size());
   ASSERT_NE(nullptr, loaded_arsc);
 
   ResTable_config desired_config;
diff --git a/libs/androidfw/tests/Theme_bench.cpp b/libs/androidfw/tests/Theme_bench.cpp
index c471be6..594c39e 100644
--- a/libs/androidfw/tests/Theme_bench.cpp
+++ b/libs/androidfw/tests/Theme_bench.cpp
@@ -28,7 +28,7 @@
 constexpr const static uint32_t kAttrId = 0x01010030u;   // android:attr/colorForeground
 
 static void BM_ThemeApplyStyleFramework(benchmark::State& state) {
-  std::unique_ptr<ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
+  std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
   if (apk == nullptr) {
     state.SkipWithError("Failed to load assets");
     return;
@@ -62,7 +62,7 @@
 BENCHMARK(BM_ThemeApplyStyleFrameworkOld);
 
 static void BM_ThemeGetAttribute(benchmark::State& state) {
-  std::unique_ptr<ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
+  std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
 
   AssetManager2 assets;
   assets.SetApkAssets({apk.get()});
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index 59cb18a..daed28b 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -46,10 +46,10 @@
   }
 
  protected:
-  std::unique_ptr<ApkAssets> style_assets_;
-  std::unique_ptr<ApkAssets> libclient_assets_;
-  std::unique_ptr<ApkAssets> lib_one_assets_;
-  std::unique_ptr<ApkAssets> lib_two_assets_;
+  std::unique_ptr<const ApkAssets> style_assets_;
+  std::unique_ptr<const ApkAssets> libclient_assets_;
+  std::unique_ptr<const ApkAssets> lib_one_assets_;
+  std::unique_ptr<const ApkAssets> lib_two_assets_;
 };
 
 TEST_F(ThemeTest, EmptyTheme) {
diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h
index 9352b5c..8e9741e 100644
--- a/libs/androidfw/tests/data/basic/R.h
+++ b/libs/androidfw/tests/data/basic/R.h
@@ -53,6 +53,8 @@
     enum : uint32_t {
       number1 = 0x7f040000,
       number2 = 0x7f040001,
+      ref1 = 0x7f040002,
+      ref2 = 0x7f040003,
 
       // From feature
       number3 = 0x7f090000,
diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk
index 2c9771b..7ee6734 100644
--- a/libs/androidfw/tests/data/basic/basic.apk
+++ b/libs/androidfw/tests/data/basic/basic.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/basic_de_fr.apk b/libs/androidfw/tests/data/basic/basic_de_fr.apk
index 0481444..e45258c 100644
--- a/libs/androidfw/tests/data/basic/basic_de_fr.apk
+++ b/libs/androidfw/tests/data/basic/basic_de_fr.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk
index a8d06e7..4ae1a7c 100644
--- a/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk
+++ b/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk
index d1dfb14..a240d4c 100644
--- a/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk
+++ b/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk
index dca6f2f..fd3d9b2 100644
--- a/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk
+++ b/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml
index 11f6b8a..638c983 100644
--- a/libs/androidfw/tests/data/basic/res/values/values.xml
+++ b/libs/androidfw/tests/data/basic/res/values/values.xml
@@ -37,6 +37,12 @@
     <public type="integer" name="number2" id="0x7f040001" />
     <integer name="number2">@array/integerArray1</integer>
 
+    <public type="integer" name="ref1" id="0x7f040002" />
+    <integer name="ref1">@integer/ref2</integer>
+
+    <public type="integer" name="ref2" id="0x7f040003" />
+    <integer name="ref2">12000</integer>
+
     <public type="style" name="Theme1" id="0x7f050000" />
     <style name="Theme1">
         <item name="com.android.basic:attr1">100</item>