Revert "libandroidfw: Improve performance of AssetManager2"

This reverts commit 64ee69d0f1f412edee2eb7a0c846deebbfa37ef9.

Bug:72511998
Change-Id: Ie6092011cffeb0b7ca5ad586c45b3d94d6a6c71b
(cherry picked from commit 16af2f4cf0610575d1e6eec17f34970ac4c7d5b2)
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 70d5216..7c9078b 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -145,7 +145,6 @@
         "tests/TypeWrappers_test.cpp",
         "tests/ZipUtils_test.cpp",
     ],
-    static_libs: ["libgmock"],
     target: {
         android: {
             srcs: [
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index a558ff7..2fc8e95 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -36,31 +36,6 @@
 
 namespace android {
 
-struct FindEntryResult {
-  // A pointer to the resource table entry for this resource.
-  // If the size of the entry is > sizeof(ResTable_entry), it can be cast to
-  // a ResTable_map_entry and processed as a bag/map.
-  const ResTable_entry* entry;
-
-  // The configuration for which the resulting entry was defined. This is already swapped to host
-  // endianness.
-  ResTable_config config;
-
-  // The bitmask of configuration axis with which the resource value varies.
-  uint32_t type_flags;
-
-  // The dynamic package ID map for the package from which this resource came from.
-  const DynamicRefTable* dynamic_ref_table;
-
-  // The string pool reference to the type's name. This uses a different string pool than
-  // the global string pool, but this is hidden from the caller.
-  StringPoolRef type_string_ref;
-
-  // The string pool reference to the entry's name. This uses a different string pool than
-  // the global string pool, but this is hidden from the caller.
-  StringPoolRef entry_string_ref;
-};
-
 AssetManager2::AssetManager2() {
   memset(&configuration_, 0, sizeof(configuration_));
 }
@@ -69,7 +44,6 @@
                                  bool invalidate_caches) {
   apk_assets_ = apk_assets;
   BuildDynamicRefTable();
-  RebuildFilterList();
   if (invalidate_caches) {
     InvalidateCaches(static_cast<uint32_t>(-1));
   }
@@ -105,7 +79,7 @@
       PackageGroup* package_group = &package_groups_[idx];
 
       // Add the package and to the set of packages with the same ID.
-      package_group->packages_.push_back(ConfiguredPackage{package.get(), {}});
+      package_group->packages_.push_back(package.get());
       package_group->cookies_.push_back(static_cast<ApkAssetsCookie>(i));
 
       // Add the package name -> build time ID mappings.
@@ -120,7 +94,7 @@
   // Now assign the runtime IDs so that we have a build-time to runtime ID map.
   const auto package_groups_end = package_groups_.end();
   for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) {
-    const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName();
+    const std::string& package_name = iter->packages_[0]->GetPackageName();
     for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) {
       iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()),
                                           iter->dynamic_ref_table.mAssignedPackageId);
@@ -134,20 +108,17 @@
   std::string list;
   for (size_t i = 0; i < package_ids_.size(); i++) {
     if (package_ids_[i] != 0xff) {
-      base::StringAppendF(&list, "%02x -> %d, ", (int)i, package_ids_[i]);
+      base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]);
     }
   }
   LOG(INFO) << "Package ID map: " << list;
 
-  for (const auto& package_group : package_groups_) {
-    list = "";
-    for (const auto& package : package_group.packages_) {
-      base::StringAppendF(&list, "%s(%02x), ", package.loaded_package_->GetPackageName().c_str(),
-                          package.loaded_package_->GetPackageId());
-    }
-    LOG(INFO) << base::StringPrintf("PG (%02x): ",
-                                    package_group.dynamic_ref_table.mAssignedPackageId)
-              << list;
+  for (const auto& package_group: package_groups_) {
+      list = "";
+      for (const auto& package : package_group.packages_) {
+        base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId());
+      }
+      LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list;
   }
 }
 
@@ -186,54 +157,52 @@
   configuration_ = configuration;
 
   if (diff) {
-    RebuildFilterList();
     InvalidateCaches(static_cast<uint32_t>(diff));
   }
 }
 
 std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
-                                                                   bool exclude_mipmap) const {
+                                                                   bool exclude_mipmap) {
   ATRACE_CALL();
   std::set<ResTable_config> configurations;
   for (const PackageGroup& package_group : package_groups_) {
-    for (const ConfiguredPackage& package : package_group.packages_) {
-      if (exclude_system && package.loaded_package_->IsSystem()) {
+    for (const LoadedPackage* package : package_group.packages_) {
+      if (exclude_system && package->IsSystem()) {
         continue;
       }
-      package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations);
+      package->CollectConfigurations(exclude_mipmap, &configurations);
     }
   }
   return configurations;
 }
 
 std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system,
-                                                        bool merge_equivalent_languages) const {
+                                                        bool merge_equivalent_languages) {
   ATRACE_CALL();
   std::set<std::string> locales;
   for (const PackageGroup& package_group : package_groups_) {
-    for (const ConfiguredPackage& package : package_group.packages_) {
-      if (exclude_system && package.loaded_package_->IsSystem()) {
+    for (const LoadedPackage* package : package_group.packages_) {
+      if (exclude_system && package->IsSystem()) {
         continue;
       }
-      package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales);
+      package->CollectLocales(merge_equivalent_languages, &locales);
     }
   }
   return locales;
 }
 
-std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename,
-                                           Asset::AccessMode mode) const {
+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);
 }
 
 std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie,
-                                           Asset::AccessMode mode) const {
+                                           Asset::AccessMode mode) {
   const std::string new_path = "assets/" + filename;
   return OpenNonAsset(new_path, cookie, mode);
 }
 
-std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) const {
+std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) {
   ATRACE_CALL();
 
   std::string full_path = "assets/" + dirname;
@@ -267,7 +236,7 @@
 // is inconsistent for split APKs.
 std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
                                                    Asset::AccessMode mode,
-                                                   ApkAssetsCookie* out_cookie) const {
+                                                   ApkAssetsCookie* out_cookie) {
   ATRACE_CALL();
   for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) {
     std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode);
@@ -286,8 +255,7 @@
 }
 
 std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
-                                                   ApkAssetsCookie cookie,
-                                                   Asset::AccessMode mode) const {
+                                                   ApkAssetsCookie cookie, Asset::AccessMode mode) {
   ATRACE_CALL();
   if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
     return {};
@@ -296,13 +264,12 @@
 }
 
 ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
-                                         bool /*stop_at_first_match*/,
-                                         FindEntryResult* out_entry) const {
+                                         bool stop_at_first_match, FindEntryResult* out_entry) {
   // Might use this if density_override != 0.
   ResTable_config density_override_config;
 
   // Select our configuration or generate a density override configuration.
-  const ResTable_config* desired_config = &configuration_;
+  ResTable_config* desired_config = &configuration_;
   if (density_override != 0 && density_override != configuration_.density) {
     density_override_config = configuration_;
     density_override_config.density = density_override;
@@ -316,135 +283,53 @@
 
   const uint32_t package_id = get_package_id(resid);
   const uint8_t type_idx = get_type_id(resid) - 1;
-  const uint16_t entry_idx = get_entry_id(resid);
+  const uint16_t entry_id = get_entry_id(resid);
 
-  const uint8_t package_idx = package_ids_[package_id];
-  if (package_idx == 0xff) {
+  const uint8_t idx = package_ids_[package_id];
+  if (idx == 0xff) {
     LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid);
     return kInvalidCookie;
   }
 
-  const PackageGroup& package_group = package_groups_[package_idx];
-  const size_t package_count = package_group.packages_.size();
-
+  FindEntryResult best_entry;
   ApkAssetsCookie best_cookie = kInvalidCookie;
-  const LoadedPackage* best_package = nullptr;
-  const ResTable_type* best_type = nullptr;
-  const ResTable_config* best_config = nullptr;
-  ResTable_config best_config_copy;
-  uint32_t best_offset = 0u;
-  uint32_t type_flags = 0u;
+  uint32_t cumulated_flags = 0u;
 
-  // If desired_config is the same as the set configuration, then we can use our filtered list
-  // and we don't need to match the configurations, since they already matched.
-  const bool use_fast_path = desired_config == &configuration_;
-
-  for (size_t pi = 0; pi < package_count; pi++) {
-    const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
-    const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_;
-    ApkAssetsCookie cookie = package_group.cookies_[pi];
-
-    // If the type IDs are offset in this package, we need to take that into account when searching
-    // for a type.
-    const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx);
-    if (UNLIKELY(type_spec == nullptr)) {
+  const PackageGroup& package_group = package_groups_[idx];
+  const size_t package_count = package_group.packages_.size();
+  FindEntryResult current_entry;
+  for (size_t i = 0; i < package_count; i++) {
+    const LoadedPackage* loaded_package = package_group.packages_[i];
+    if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, &current_entry)) {
       continue;
     }
 
-    uint16_t local_entry_idx = entry_idx;
+    cumulated_flags |= current_entry.type_flags;
 
-    // If there is an IDMAP supplied with this package, translate the entry ID.
-    if (type_spec->idmap_entries != nullptr) {
-      if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) {
-        // There is no mapping, so the resource is not meant to be in this overlay package.
-        continue;
-      }
-    }
-
-    type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx);
-
-    // If the package is an overlay, then even configurations that are the same MUST be chosen.
-    const bool package_is_overlay = loaded_package->IsOverlay();
-
-    const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
-    if (use_fast_path) {
-      const std::vector<ResTable_config>& candidate_configs = filtered_group.configurations;
-      const size_t type_count = candidate_configs.size();
-      for (uint32_t i = 0; i < type_count; i++) {
-        const ResTable_config& this_config = candidate_configs[i];
-
-        // We can skip calling ResTable_config::match() because we know that all candidate
-        // configurations that do NOT match have been filtered-out.
-        if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) ||
-            (package_is_overlay && this_config.compare(*best_config) == 0)) {
-          // The configuration matches and is better than the previous selection.
-          // Find the entry value if it exists for this configuration.
-          const ResTable_type* type_chunk = filtered_group.types[i];
-          const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx);
-          if (offset == ResTable_type::NO_ENTRY) {
-            continue;
-          }
-
-          best_cookie = cookie;
-          best_package = loaded_package;
-          best_type = type_chunk;
-          best_config = &this_config;
-          best_offset = offset;
-        }
-      }
-    } else {
-      // This is the slower path, which doesn't use the filtered list of configurations.
-      // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness
-      // and fill in any new fields that did not exist when the APK was compiled.
-      // Furthermore when selecting configurations we can't just record the pointer to the
-      // ResTable_config, we must copy it.
-      const auto iter_end = type_spec->types + type_spec->type_count;
-      for (auto iter = type_spec->types; iter != iter_end; ++iter) {
-        ResTable_config this_config;
-        this_config.copyFromDtoH((*iter)->config);
-
-        if (this_config.match(*desired_config)) {
-          if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) ||
-              (package_is_overlay && this_config.compare(*best_config) == 0)) {
-            // The configuration matches and is better than the previous selection.
-            // Find the entry value if it exists for this configuration.
-            const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx);
-            if (offset == ResTable_type::NO_ENTRY) {
-              continue;
-            }
-
-            best_cookie = cookie;
-            best_package = loaded_package;
-            best_type = *iter;
-            best_config_copy = this_config;
-            best_config = &best_config_copy;
-            best_offset = offset;
-          }
-        }
+    const ResTable_config* current_config = current_entry.config;
+    const ResTable_config* best_config = best_entry.config;
+    if (best_cookie == kInvalidCookie ||
+        current_config->isBetterThan(*best_config, desired_config) ||
+        (loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) {
+      best_entry = current_entry;
+      best_cookie = package_group.cookies_[i];
+      if (stop_at_first_match) {
+        break;
       }
     }
   }
 
-  if (UNLIKELY(best_cookie == kInvalidCookie)) {
+  if (best_cookie == kInvalidCookie) {
     return kInvalidCookie;
   }
 
-  const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
-  if (UNLIKELY(best_entry == nullptr)) {
-    return kInvalidCookie;
-  }
-
-  out_entry->entry = best_entry;
-  out_entry->config = *best_config;
-  out_entry->type_flags = type_flags;
-  out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
-  out_entry->entry_string_ref =
-      StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
+  *out_entry = best_entry;
   out_entry->dynamic_ref_table = &package_group.dynamic_ref_table;
+  out_entry->type_flags = cumulated_flags;
   return best_cookie;
 }
 
-bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const {
+bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) {
   ATRACE_CALL();
 
   FindEntryResult entry;
@@ -454,8 +339,7 @@
     return false;
   }
 
-  const LoadedPackage* package =
-      apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
+  const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid);
   if (package == nullptr) {
     return false;
   }
@@ -483,7 +367,7 @@
   return true;
 }
 
-bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const {
+bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) {
   FindEntryResult entry;
   ApkAssetsCookie cookie =
       FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry);
@@ -497,7 +381,7 @@
 ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
                                            uint16_t density_override, Res_value* out_value,
                                            ResTable_config* out_selected_config,
-                                           uint32_t* out_flags) const {
+                                           uint32_t* out_flags) {
   ATRACE_CALL();
 
   FindEntryResult entry;
@@ -516,7 +400,7 @@
     // 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 = entry.config;
+    *out_selected_config = *entry.config;
     *out_flags = entry.type_flags;
     return cookie;
   }
@@ -528,7 +412,7 @@
   // Convert the package ID to the runtime assigned package ID.
   entry.dynamic_ref_table->lookupResourceValue(out_value);
 
-  *out_selected_config = entry.config;
+  *out_selected_config = *entry.config;
   *out_flags = entry.type_flags;
   return cookie;
 }
@@ -536,7 +420,7 @@
 ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
                                                 ResTable_config* in_out_selected_config,
                                                 uint32_t* in_out_flags,
-                                                uint32_t* out_last_reference) const {
+                                                uint32_t* out_last_reference) {
   ATRACE_CALL();
   constexpr const int kMaxIterations = 20;
 
@@ -604,8 +488,7 @@
         // Attributes, arrays, etc don't have a resource id as the name. They specify
         // other data, which would be wrong to change via a lookup.
         if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
-          LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key,
-                                           resid);
+          LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid);
           return nullptr;
         }
       }
@@ -637,8 +520,7 @@
   const ResolvedBag* parent_bag = GetBag(parent_resid);
   if (parent_bag == nullptr) {
     // Failed to get the parent that should exist.
-    LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid,
-                                     resid);
+    LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid);
     return nullptr;
   }
 
@@ -657,8 +539,7 @@
     uint32_t child_key = dtohl(map_entry->name.ident);
     if (!is_internal_resid(child_key)) {
       if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) {
-        LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key,
-                                         resid);
+        LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid);
         return nullptr;
       }
     }
@@ -697,8 +578,7 @@
     uint32_t new_key = dtohl(map_entry->name.ident);
     if (!is_internal_resid(new_key)) {
       if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
-        LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key,
-                                         resid);
+        LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid);
         return nullptr;
       }
     }
@@ -754,7 +634,7 @@
 
 uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
                                       const std::string& fallback_type,
-                                      const std::string& fallback_package) const {
+                                      const std::string& fallback_package) {
   StringPiece package_name, type, entry;
   if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) {
     return 0u;
@@ -786,8 +666,7 @@
   const static std::u16string kAttrPrivate16 = u"^attr-private";
 
   for (const PackageGroup& package_group : package_groups_) {
-    for (const ConfiguredPackage& package_impl : package_group.packages_) {
-      const LoadedPackage* package = package_impl.loaded_package_;
+    for (const LoadedPackage* package : package_group.packages_) {
       if (package_name != package->GetPackageName()) {
         // All packages in the same group are expected to have the same package name.
         break;
@@ -809,32 +688,6 @@
   return 0u;
 }
 
-void AssetManager2::RebuildFilterList() {
-  for (PackageGroup& group : package_groups_) {
-    for (ConfiguredPackage& impl : group.packages_) {
-      // Destroy it.
-      impl.filtered_configs_.~ByteBucketArray();
-
-      // Re-create it.
-      new (&impl.filtered_configs_) ByteBucketArray<FilteredConfigGroup>();
-
-      // Create the filters here.
-      impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec* spec, uint8_t type_index) {
-        FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_index);
-        const auto iter_end = spec->types + spec->type_count;
-        for (auto iter = spec->types; iter != iter_end; ++iter) {
-          ResTable_config this_config;
-          this_config.copyFromDtoH((*iter)->config);
-          if (this_config.match(configuration_)) {
-            group.configurations.push_back(this_config);
-            group.types.push_back(*iter);
-          }
-        }
-      });
-    }
-  }
-}
-
 void AssetManager2::InvalidateCaches(uint32_t diff) {
   if (diff == 0xffffffffu) {
     // Everything must go.
@@ -1015,7 +868,7 @@
 ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
                                                  ResTable_config* in_out_selected_config,
                                                  uint32_t* in_out_type_spec_flags,
-                                                 uint32_t* out_last_ref) const {
+                                                 uint32_t* out_last_ref) {
   if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) {
     uint32_t new_flags;
     cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags);
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 1d2c597..e08848f 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -44,6 +44,44 @@
 
 constexpr const static int kAppPackageId = 0x7f;
 
+// Element of a TypeSpec array. See TypeSpec.
+struct Type {
+  // The configuration for which this type defines entries.
+  // This is already converted to host endianness.
+  ResTable_config configuration;
+
+  // Pointer to the mmapped data where entry definitions are kept.
+  const ResTable_type* type;
+};
+
+// TypeSpec is going to be immediately proceeded by
+// an array of Type structs, all in the same block of memory.
+struct TypeSpec {
+  // Pointer to the mmapped data where flags are kept.
+  // Flags denote whether the resource entry is public
+  // and under which configurations it varies.
+  const ResTable_typeSpec* type_spec;
+
+  // Pointer to the mmapped data where the IDMAP mappings for this type
+  // exist. May be nullptr if no IDMAP exists.
+  const IdmapEntry_header* idmap_entries;
+
+  // The number of types that follow this struct.
+  // There is a type for each configuration
+  // that entries are defined for.
+  size_t type_count;
+
+  // Trick to easily access a variable number of Type structs
+  // proceeding this struct, and to ensure their alignment.
+  const Type types[0];
+};
+
+// TypeSpecPtr points to the block of memory that holds
+// a TypeSpec struct, followed by an array of Type structs.
+// TypeSpecPtr is a managed pointer that knows how to delete
+// itself.
+using TypeSpecPtr = util::unique_cptr<TypeSpec>;
+
 namespace {
 
 // Builder that helps accumulate Type structs and then create a single
@@ -57,22 +95,21 @@
   }
 
   void AddType(const ResTable_type* type) {
-    types_.push_back(type);
+    ResTable_config config;
+    config.copyFromDtoH(type->config);
+    types_.push_back(Type{config, type});
   }
 
   TypeSpecPtr Build() {
     // Check for overflow.
-    using ElementType = const ResTable_type*;
-    if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(ElementType) <
-        types_.size()) {
+    if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) {
       return {};
     }
-    TypeSpec* type_spec =
-        (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType)));
+    TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type)));
     type_spec->type_spec = header_;
     type_spec->idmap_entries = idmap_header_;
     type_spec->type_count = types_.size();
-    memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType));
+    memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type));
     return TypeSpecPtr(type_spec);
   }
 
@@ -81,7 +118,7 @@
 
   const ResTable_typeSpec* header_;
   const IdmapEntry_header* idmap_header_;
-  std::vector<const ResTable_type*> types_;
+  std::vector<Type> types_;
 };
 
 }  // namespace
@@ -125,17 +162,18 @@
   return true;
 }
 
-static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) {
+static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset,
+                                size_t entry_idx) {
   // Check that the offset is aligned.
   if (entry_offset & 0x03) {
-    LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned.";
+    LOG(ERROR) << "Entry offset at index " << entry_idx << " is not 4-byte aligned.";
     return false;
   }
 
   // Check that the offset doesn't overflow.
   if (entry_offset > std::numeric_limits<uint32_t>::max() - dtohl(type->entriesStart)) {
     // Overflow in offset.
-    LOG(ERROR) << "Entry at offset " << entry_offset << " is too large.";
+    LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large.";
     return false;
   }
 
@@ -143,7 +181,7 @@
 
   entry_offset += dtohl(type->entriesStart);
   if (entry_offset > chunk_size - sizeof(ResTable_entry)) {
-    LOG(ERROR) << "Entry at offset " << entry_offset
+    LOG(ERROR) << "Entry offset at index " << entry_idx
                << " is too large. No room for ResTable_entry.";
     return false;
   }
@@ -153,13 +191,13 @@
 
   const size_t entry_size = dtohs(entry->size);
   if (entry_size < sizeof(*entry)) {
-    LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
+    LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx
                << " is too small.";
     return false;
   }
 
   if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) {
-    LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
+    LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx
                << " is too large.";
     return false;
   }
@@ -167,7 +205,7 @@
   if (entry_size < sizeof(ResTable_map_entry)) {
     // There needs to be room for one Res_value struct.
     if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) {
-      LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset
+      LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << entry_idx
                  << " for type " << (int)type->id << ".";
       return false;
     }
@@ -176,12 +214,12 @@
         reinterpret_cast<const Res_value*>(reinterpret_cast<const uint8_t*>(entry) + entry_size);
     const size_t value_size = dtohs(value->size);
     if (value_size < sizeof(Res_value)) {
-      LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small.";
+      LOG(ERROR) << "Res_value at index " << entry_idx << " is too small.";
       return false;
     }
 
     if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) {
-      LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset
+      LOG(ERROR) << "Res_value size " << value_size << " at index " << entry_idx
                  << " is too large.";
       return false;
     }
@@ -190,76 +228,117 @@
     const size_t map_entry_count = dtohl(map->count);
     size_t map_entries_start = entry_offset + entry_size;
     if (map_entries_start & 0x03) {
-      LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset.";
+      LOG(ERROR) << "Map entries at index " << entry_idx << " start at unaligned offset.";
       return false;
     }
 
     // Each entry is sizeof(ResTable_map) big.
     if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) {
-      LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << ".";
+      LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << entry_idx << ".";
       return false;
     }
   }
   return true;
 }
 
-const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk,
-                                              uint16_t entry_index) {
-  uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index);
-  if (entry_offset == ResTable_type::NO_ENTRY) {
-    return nullptr;
-  }
-  return GetEntryFromOffset(type_chunk, entry_offset);
-}
+bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx,
+                              const ResTable_config& config, FindEntryResult* out_entry) const {
+  const ResTable_config* best_config = nullptr;
+  const ResTable_type* best_type = nullptr;
+  uint32_t best_offset = 0;
 
-uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) {
-  // The configuration matches and is better than the previous selection.
-  // Find the entry value if it exists for this configuration.
-  const size_t entry_count = dtohl(type_chunk->entryCount);
-  const size_t offsets_offset = dtohs(type_chunk->header.headerSize);
+  for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) {
+    const Type* type = &type_spec_ptr->types[i];
+    const ResTable_type* type_chunk = type->type;
 
-  // Check if there is the desired entry in this type.
+    if (type->configuration.match(config) &&
+        (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) {
+      // The configuration matches and is better than the previous selection.
+      // Find the entry value if it exists for this configuration.
+      const size_t entry_count = dtohl(type_chunk->entryCount);
+      const size_t offsets_offset = dtohs(type_chunk->header.headerSize);
 
-  if (type_chunk->flags & ResTable_type::FLAG_SPARSE) {
-    // This is encoded as a sparse map, so perform a binary search.
-    const ResTable_sparseTypeEntry* sparse_indices =
-        reinterpret_cast<const ResTable_sparseTypeEntry*>(
+      // Check if there is the desired entry in this type.
+
+      if (type_chunk->flags & ResTable_type::FLAG_SPARSE) {
+        // This is encoded as a sparse map, so perform a binary search.
+        const ResTable_sparseTypeEntry* sparse_indices =
+            reinterpret_cast<const ResTable_sparseTypeEntry*>(
+                reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
+        const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count;
+        const ResTable_sparseTypeEntry* result =
+            std::lower_bound(sparse_indices, sparse_indices_end, entry_idx,
+                             [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) {
+                               return dtohs(entry.idx) < entry_idx;
+                             });
+
+        if (result == sparse_indices_end || dtohs(result->idx) != entry_idx) {
+          // No entry found.
+          continue;
+        }
+
+        // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as
+        // the real offset divided by 4.
+        best_offset = uint32_t{dtohs(result->offset)} * 4u;
+      } else {
+        if (entry_idx >= entry_count) {
+          // This entry cannot be here.
+          continue;
+        }
+
+        const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
             reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
-    const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count;
-    const ResTable_sparseTypeEntry* result =
-        std::lower_bound(sparse_indices, sparse_indices_end, entry_index,
-                         [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) {
-                           return dtohs(entry.idx) < entry_idx;
-                         });
+        const uint32_t offset = dtohl(entry_offsets[entry_idx]);
+        if (offset == ResTable_type::NO_ENTRY) {
+          continue;
+        }
 
-    if (result == sparse_indices_end || dtohs(result->idx) != entry_index) {
-      // No entry found.
-      return ResTable_type::NO_ENTRY;
+        // There is an entry for this resource, record it.
+        best_offset = offset;
+      }
+
+      best_config = &type->configuration;
+      best_type = type_chunk;
     }
-
-    // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as
-    // the real offset divided by 4.
-    return uint32_t{dtohs(result->offset)} * 4u;
   }
 
-  // This type is encoded as a dense array.
-  if (entry_index >= entry_count) {
-    // This entry cannot be here.
-    return ResTable_type::NO_ENTRY;
+  if (best_type == nullptr) {
+    return false;
   }
 
-  const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
-      reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
-  return dtohl(entry_offsets[entry_index]);
+  if (UNLIKELY(!VerifyResTableEntry(best_type, best_offset, entry_idx))) {
+    return false;
+  }
+
+  const ResTable_entry* best_entry = reinterpret_cast<const ResTable_entry*>(
+      reinterpret_cast<const uint8_t*>(best_type) + best_offset + dtohl(best_type->entriesStart));
+
+  const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec_ptr->type_spec + 1);
+  out_entry->type_flags = dtohl(flags[entry_idx]);
+  out_entry->entry = best_entry;
+  out_entry->config = best_config;
+  out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1);
+  out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index));
+  return true;
 }
 
-const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk,
-                                                        uint32_t offset) {
-  if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) {
-    return nullptr;
+bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config,
+                              FindEntryResult* out_entry) const {
+  // If the type IDs are offset in this package, we need to take that into account when searching
+  // for a type.
+  const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_];
+  if (UNLIKELY(ptr == nullptr)) {
+    return false;
   }
-  return reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type_chunk) +
-                                                 offset + dtohl(type_chunk->entriesStart));
+
+  // If there is an IDMAP supplied with this package, translate the entry ID.
+  if (ptr->idmap_entries != nullptr) {
+    if (!LoadedIdmap::Lookup(ptr->idmap_entries, entry_idx, &entry_idx)) {
+      // There is no mapping, so the resource is not meant to be in this overlay package.
+      return false;
+    }
+  }
+  return FindEntry(ptr, entry_idx, config, out_entry);
 }
 
 void LoadedPackage::CollectConfigurations(bool exclude_mipmap,
@@ -267,7 +346,7 @@
   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 TypeSpecPtr& type_spec = type_specs_[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;
@@ -288,11 +367,8 @@
         }
       }
 
-      const auto iter_end = type_spec->types + type_spec->type_count;
-      for (auto iter = type_spec->types; iter != iter_end; ++iter) {
-        ResTable_config config;
-        config.copyFromDtoH((*iter)->config);
-        out_configs->insert(config);
+      for (size_t j = 0; j < type_spec->type_count; j++) {
+        out_configs->insert(type_spec->types[j].configuration);
       }
     }
   }
@@ -302,12 +378,10 @@
   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 TypeSpecPtr& type_spec = type_specs_[i];
+    const util::unique_cptr<TypeSpec>& type_spec = type_specs_[i];
     if (type_spec != nullptr) {
-      const auto iter_end = type_spec->types + type_spec->type_count;
-      for (auto iter = type_spec->types; iter != iter_end; ++iter) {
-        ResTable_config configuration;
-        configuration.copyFromDtoH((*iter)->config);
+      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);
@@ -335,17 +409,17 @@
     return 0u;
   }
 
-  const auto iter_end = type_spec->types + type_spec->type_count;
-  for (auto iter = type_spec->types; iter != iter_end; ++iter) {
-    const ResTable_type* type = *iter;
-    size_t entry_count = dtohl(type->entryCount);
+  for (size_t ti = 0; ti < type_spec->type_count; ti++) {
+    const Type* type = &type_spec->types[ti];
+    size_t entry_count = dtohl(type->type->entryCount);
     for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) {
       const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
-          reinterpret_cast<const uint8_t*>(type) + dtohs(type->header.headerSize));
+          reinterpret_cast<const uint8_t*>(type->type) + dtohs(type->type->header.headerSize));
       const uint32_t offset = dtohl(entry_offsets[entry_idx]);
       if (offset != ResTable_type::NO_ENTRY) {
-        const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
-            reinterpret_cast<const uint8_t*>(type) + dtohl(type->entriesStart) + offset);
+        const ResTable_entry* entry =
+            reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type->type) +
+                                                    dtohl(type->type->entriesStart) + offset);
         if (dtohl(entry->key.index) == static_cast<uint32_t>(key_idx)) {
           // The package ID will be overridden by the caller (due to runtime assignment of package
           // IDs for shared libraries).
@@ -357,7 +431,8 @@
   return 0u;
 }
 
-const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const {
+const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const {
+  const uint8_t package_id = get_package_id(resid);
   for (const auto& loaded_package : packages_) {
     if (loaded_package->GetPackageId() == package_id) {
       return loaded_package.get();
@@ -605,6 +680,26 @@
   return std::move(loaded_package);
 }
 
+bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config,
+                           FindEntryResult* out_entry) const {
+  ATRACE_CALL();
+
+  const uint8_t package_id = get_package_id(resid);
+  const uint8_t type_id = get_type_id(resid);
+  const uint16_t entry_id = get_entry_id(resid);
+
+  if (UNLIKELY(type_id == 0)) {
+    LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
+    return false;
+  }
+
+  for (const auto& loaded_package : packages_) {
+    if (loaded_package->GetPackageId() == package_id) {
+      return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry);
+    }
+  }
+  return false;
+}
 
 bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
                            bool load_as_shared_library) {
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index ef08897..b033137 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -69,8 +69,6 @@
   Entry entries[0];
 };
 
-struct FindEntryResult;
-
 // AssetManager2 is the main entry point for accessing assets and resources.
 // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets.
 class AssetManager2 {
@@ -129,7 +127,7 @@
   // 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) const;
+                                                      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.
@@ -138,24 +136,24 @@
   // 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) const;
+                                           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.
   //
   // NOTE: The loaded APKs are searched in reverse order.
-  std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode) const;
+  std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode);
 
   // Opens a file within the assets/ directory of the APK specified by `cookie`.
   // `mode` controls how the file is opened.
   std::unique_ptr<Asset> Open(const std::string& filename, ApkAssetsCookie cookie,
-                              Asset::AccessMode mode) const;
+                              Asset::AccessMode mode);
 
   // Opens the directory specified by `dirname`. The result is an AssetDir that is the combination
   // of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded.
   // The entries are sorted by their ASCII name.
-  std::unique_ptr<AssetDir> OpenDir(const std::string& dirname) const;
+  std::unique_ptr<AssetDir> OpenDir(const std::string& dirname);
 
   // Searches the set of APKs loaded by this AssetManager and opens the first one found.
   // `mode` controls how the file is opened.
@@ -163,24 +161,24 @@
   //
   // NOTE: The loaded APKs are searched in reverse order.
   std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, Asset::AccessMode mode,
-                                      ApkAssetsCookie* out_cookie = nullptr) const;
+                                      ApkAssetsCookie* out_cookie = nullptr);
 
   // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened.
   // This is typically used to open a specific AndroidManifest.xml, or a binary XML file
   // referenced by a resource lookup with GetResource().
   std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie,
-                                      Asset::AccessMode mode) const;
+                                      Asset::AccessMode mode);
 
   // Populates the `out_name` parameter with resource name information.
   // Utf8 strings are preferred, and only if they are unavailable are
   // the Utf16 variants populated.
   // Returns false if the resource was not found or the name was missing/corrupt.
-  bool GetResourceName(uint32_t resid, ResourceName* out_name) const;
+  bool GetResourceName(uint32_t resid, ResourceName* out_name);
 
   // Populates `out_flags` with the bitmask of configuration axis that this resource varies with.
   // See ResTable_config for the list of configuration axis.
   // Returns false if the resource was not found.
-  bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const;
+  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'.
@@ -188,7 +186,7 @@
   // 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 = {}) const;
+                         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`.
@@ -201,7 +199,7 @@
   // this function logs if the resource was a map/bag type before returning kInvalidCookie.
   ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override,
                               Res_value* out_value, ResTable_config* out_selected_config,
-                              uint32_t* out_flags) const;
+                              uint32_t* out_flags);
 
   // Resolves the resource reference in `in_out_value` if the data type is
   // Res_value::TYPE_REFERENCE.
@@ -217,7 +215,7 @@
   // it was not found.
   ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
                                    ResTable_config* in_out_selected_config, uint32_t* in_out_flags,
-                                   uint32_t* out_last_reference) const;
+                                   uint32_t* 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.
@@ -235,9 +233,9 @@
   std::unique_ptr<Theme> NewTheme();
 
   template <typename Func>
-  void ForEachPackage(Func func) const {
+  void ForEachPackage(Func func) {
     for (const PackageGroup& package_group : package_groups_) {
-      func(package_group.packages_.front().loaded_package_->GetPackageName(),
+      func(package_group.packages_.front()->GetPackageName(),
            package_group.dynamic_ref_table.mAssignedPackageId);
     }
   }
@@ -262,7 +260,7 @@
   // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly
   // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds.
   ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
-                            FindEntryResult* out_entry) const;
+                            FindEntryResult* out_entry);
 
   // Assigns package IDs to all shared library ApkAssets.
   // Should be called whenever the ApkAssets are changed.
@@ -272,43 +270,13 @@
   // bitmask `diff`.
   void InvalidateCaches(uint32_t diff);
 
-  // Triggers the re-construction of lists of types that match the set configuration.
-  // This should always be called when mutating the AssetManager's configuration or ApkAssets set.
-  void RebuildFilterList();
-
   // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
   // have a longer lifetime.
   std::vector<const ApkAssets*> apk_assets_;
 
-  // A collection of configurations and their associated ResTable_type that match the current
-  // AssetManager configuration.
-  struct FilteredConfigGroup {
-    std::vector<ResTable_config> configurations;
-    std::vector<const ResTable_type*> types;
-  };
-
-  // Represents an single package.
-  struct ConfiguredPackage {
-    // A pointer to the immutable, loaded package info.
-    const LoadedPackage* loaded_package_;
-
-    // A mutable AssetManager-specific list of configurations that match the AssetManager's
-    // current configuration. This is used as an optimization to avoid checking every single
-    // candidate configuration when looking up resources.
-    ByteBucketArray<FilteredConfigGroup> filtered_configs_;
-  };
-
-  // Represents a logical package, which can be made up of many individual packages. Each package
-  // in a PackageGroup shares the same package name and package ID.
   struct PackageGroup {
-    // The set of packages that make-up this group.
-    std::vector<ConfiguredPackage> packages_;
-
-    // The cookies associated with each package in the group. They share the same order as
-    // packages_.
+    std::vector<const LoadedPackage*> packages_;
     std::vector<ApkAssetsCookie> cookies_;
-
-    // A library reference table that contains build-package ID to runtime-package ID mappings.
     DynamicRefTable dynamic_ref_table;
   };
 
@@ -382,7 +350,7 @@
   ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
                                             ResTable_config* in_out_selected_config = nullptr,
                                             uint32_t* in_out_type_spec_flags = nullptr,
-                                            uint32_t* out_last_ref = nullptr) const;
+                                            uint32_t* out_last_ref = nullptr);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(Theme);
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 35ae5fc..1775f50 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -41,40 +41,33 @@
   int package_id = 0;
 };
 
-// TypeSpec is going to be immediately proceeded by
-// an array of Type structs, all in the same block of memory.
-struct TypeSpec {
-  // Pointer to the mmapped data where flags are kept.
-  // Flags denote whether the resource entry is public
-  // and under which configurations it varies.
-  const ResTable_typeSpec* type_spec;
+struct FindEntryResult {
+  // A pointer to the resource table entry for this resource.
+  // If the size of the entry is > sizeof(ResTable_entry), it can be cast to
+  // a ResTable_map_entry and processed as a bag/map.
+  const ResTable_entry* entry;
 
-  // Pointer to the mmapped data where the IDMAP mappings for this type
-  // exist. May be nullptr if no IDMAP exists.
-  const IdmapEntry_header* idmap_entries;
+  // The configuration for which the resulting entry was defined. This points to a structure that
+  // is already swapped to host endianness.
+  const ResTable_config* config;
 
-  // The number of types that follow this struct.
-  // There is a type for each configuration that entries are defined for.
-  size_t type_count;
+  // The bitmask of configuration axis with which the resource value varies.
+  uint32_t type_flags;
 
-  // Trick to easily access a variable number of Type structs
-  // proceeding this struct, and to ensure their alignment.
-  const ResTable_type* types[0];
+  // The dynamic package ID map for the package from which this resource came from.
+  const DynamicRefTable* dynamic_ref_table;
 
-  inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const {
-    if (entry_index >= dtohl(type_spec->entryCount)) {
-      return 0u;
-    }
+  // The string pool reference to the type's name. This uses a different string pool than
+  // the global string pool, but this is hidden from the caller.
+  StringPoolRef type_string_ref;
 
-    const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec + 1);
-    return flags[entry_index];
-  }
+  // The string pool reference to the entry's name. This uses a different string pool than
+  // the global string pool, but this is hidden from the caller.
+  StringPoolRef entry_string_ref;
 };
 
-// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of
-// ResTable_type pointers.
-// TypeSpecPtr is a managed pointer that knows how to delete itself.
-using TypeSpecPtr = util::unique_cptr<TypeSpec>;
+struct TypeSpec;
+class LoadedArsc;
 
 class LoadedPackage {
  public:
@@ -84,6 +77,9 @@
 
   ~LoadedPackage();
 
+  bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config,
+                 FindEntryResult* out_entry) const;
+
   // Finds the entry with the specified type name and entry name. The names are in UTF-16 because
   // the underlying ResStringPool API expects this. For now this is acceptable, but since
   // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change.
@@ -91,12 +87,6 @@
   // for patching the correct package ID to the resource ID.
   uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const;
 
-  static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index);
-
-  static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index);
-
-  static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset);
-
   // Returns the string pool where type names are stored.
   inline const ResStringPool* GetTypeStringPool() const {
     return &type_string_pool_;
@@ -146,32 +136,14 @@
   // 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;
 
-  // type_idx is TT - 1 from 0xPPTTEEEE.
-  inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const {
-    // If the type IDs are offset in this package, we need to take that into account when searching
-    // for a type.
-    return type_specs_[type_index - type_id_offset_].get();
-  }
-
-  template <typename Func>
-  void ForEachTypeSpec(Func f) const {
-    for (size_t i = 0; i < type_specs_.size(); i++) {
-      const TypeSpecPtr& ptr = type_specs_[i];
-      if (ptr != nullptr) {
-        uint8_t type_id = ptr->type_spec->id;
-        if (ptr->idmap_entries != nullptr) {
-          type_id = ptr->idmap_entries->target_type_id;
-        }
-        f(ptr.get(), type_id - 1);
-      }
-    }
-  }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
 
   LoadedPackage();
 
+  bool FindEntry(const util::unique_cptr<TypeSpec>& type_spec_ptr, uint16_t entry_idx,
+                 const ResTable_config& config, FindEntryResult* out_entry) const;
+
   ResStringPool type_string_pool_;
   ResStringPool key_string_pool_;
   std::string package_name_;
@@ -181,7 +153,7 @@
   bool system_ = false;
   bool overlay_ = false;
 
-  ByteBucketArray<TypeSpecPtr> type_specs_;
+  ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_;
   std::vector<DynamicPackageEntry> dynamic_package_map_;
 };
 
@@ -209,20 +181,25 @@
     return &global_string_pool_;
   }
 
-  // Gets a pointer to the package with the specified package ID, or nullptr if no such package
-  // exists.
-  const LoadedPackage* GetPackageById(uint8_t package_id) const;
+  // Finds the resource with ID `resid` with the best value for configuration `config`.
+  // The parameter `out_entry` will be filled with the resulting resource entry.
+  // The resource entry can be a simple entry (ResTable_entry) or a complex bag
+  // (ResTable_entry_map).
+  bool FindEntry(uint32_t resid, const ResTable_config& config, FindEntryResult* out_entry) const;
 
-  // 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_;
-  }
+  // 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_;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
 
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
index e2b9f00..6c43a67 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -26,56 +26,58 @@
 
 using ::android::base::unique_fd;
 using ::com::android::basic::R;
-using ::testing::Eq;
-using ::testing::Ge;
-using ::testing::NotNull;
-using ::testing::SizeIs;
-using ::testing::StrEq;
 
 namespace android {
 
 TEST(ApkAssetsTest, LoadApk) {
   std::unique_ptr<const ApkAssets> loaded_apk =
       ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
-  ASSERT_THAT(loaded_apk, NotNull());
+  ASSERT_NE(nullptr, loaded_apk);
 
   const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
-  ASSERT_THAT(loaded_arsc, NotNull());
-  ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull());
-  ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull());
+  ASSERT_NE(nullptr, loaded_arsc);
+
+  const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000);
+  ASSERT_NE(nullptr, loaded_package);
+
+  std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml");
+  ASSERT_NE(nullptr, asset);
 }
 
 TEST(ApkAssetsTest, LoadApkFromFd) {
   const std::string path = GetTestDataPath() + "/basic/basic.apk";
   unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY));
-  ASSERT_THAT(fd.get(), Ge(0));
+  ASSERT_GE(fd.get(), 0);
 
   std::unique_ptr<const ApkAssets> loaded_apk =
       ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/);
-  ASSERT_THAT(loaded_apk, NotNull());
+  ASSERT_NE(nullptr, loaded_apk);
 
   const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
-  ASSERT_THAT(loaded_arsc, NotNull());
-  ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull());
-  ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull());
+  ASSERT_NE(nullptr, loaded_arsc);
+
+  const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000);
+  ASSERT_NE(nullptr, loaded_package);
+
+  std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml");
+  ASSERT_NE(nullptr, asset);
 }
 
 TEST(ApkAssetsTest, LoadApkAsSharedLibrary) {
   std::unique_ptr<const ApkAssets> loaded_apk =
       ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk");
-  ASSERT_THAT(loaded_apk, NotNull());
-
+  ASSERT_NE(nullptr, loaded_apk);
   const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
-  ASSERT_THAT(loaded_arsc, NotNull());
-  ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u));
+  ASSERT_NE(nullptr, loaded_arsc);
+  ASSERT_EQ(1u, loaded_arsc->GetPackages().size());
   EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic());
 
   loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk");
-  ASSERT_THAT(loaded_apk, NotNull());
+  ASSERT_NE(nullptr, loaded_apk);
 
   loaded_arsc = loaded_apk->GetLoadedArsc();
-  ASSERT_THAT(loaded_arsc, NotNull());
-  ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u));
+  ASSERT_NE(nullptr, loaded_arsc);
+  ASSERT_EQ(1u, loaded_arsc->GetPackages().size());
   EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic());
 }
 
@@ -84,22 +86,19 @@
   ResTable target_table;
   const std::string target_path = GetTestDataPath() + "/basic/basic.apk";
   ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents));
-  ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/),
-              Eq(NO_ERROR));
+  ASSERT_EQ(NO_ERROR, target_table.add(contents.data(), contents.size(), 0, true /*copyData*/));
 
   ResTable overlay_table;
   const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk";
   ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents));
-  ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/),
-              Eq(NO_ERROR));
+  ASSERT_EQ(NO_ERROR, overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/));
 
   util::unique_cptr<void> idmap_data;
   void* temp_data;
   size_t idmap_len;
 
-  ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(),
-                                       overlay_path.c_str(), &temp_data, &idmap_len),
-              Eq(NO_ERROR));
+  ASSERT_EQ(NO_ERROR, target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(),
+                                               overlay_path.c_str(), &temp_data, &idmap_len));
   idmap_data.reset(temp_data);
 
   TemporaryFile tf;
@@ -109,30 +108,37 @@
   // Open something so that the destructor of TemporaryFile closes a valid fd.
   tf.fd = open("/dev/null", O_WRONLY);
 
-  ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull());
+  std::unique_ptr<const ApkAssets> loaded_overlay_apk = ApkAssets::LoadOverlay(tf.path);
+  ASSERT_NE(nullptr, loaded_overlay_apk);
 }
 
 TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) {
   std::unique_ptr<const ApkAssets> loaded_apk =
       ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
-  ASSERT_THAT(loaded_apk, NotNull());
+  ASSERT_NE(nullptr, loaded_apk);
 
-  { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); }
+  {
+    std::unique_ptr<Asset> assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER);
+    ASSERT_NE(nullptr, assets);
+  }
 
-  { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); }
+  {
+    std::unique_ptr<Asset> assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER);
+    ASSERT_NE(nullptr, assets);
+  }
 }
 
 TEST(ApkAssetsTest, OpenUncompressedAssetFd) {
   std::unique_ptr<const ApkAssets> loaded_apk =
       ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
-  ASSERT_THAT(loaded_apk, NotNull());
+  ASSERT_NE(nullptr, loaded_apk);
 
   auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN);
-  ASSERT_THAT(asset, NotNull());
+  ASSERT_NE(nullptr, asset);
 
   off64_t start, length;
   unique_fd fd(asset->openFileDescriptor(&start, &length));
-  ASSERT_THAT(fd.get(), Ge(0));
+  EXPECT_GE(fd.get(), 0);
 
   lseek64(fd.get(), start, SEEK_SET);
 
@@ -140,7 +146,7 @@
   buffer.resize(length);
   ASSERT_TRUE(base::ReadFully(fd.get(), &*buffer.begin(), length));
 
-  EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n"));
+  EXPECT_EQ("This should be uncompressed.\n\n", buffer);
 }
 
 }  // namespace android
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index bedebd6..37ddafb 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -16,8 +16,6 @@
 
 #include "androidfw/LoadedArsc.h"
 
-#include "androidfw/ResourceUtils.h"
-
 #include "TestHelpers.h"
 #include "data/basic/R.h"
 #include "data/libclient/R.h"
@@ -29,13 +27,6 @@
 namespace libclient = com::android::libclient;
 namespace sparse = com::android::sparse;
 
-using ::testing::Eq;
-using ::testing::Ge;
-using ::testing::IsNull;
-using ::testing::NotNull;
-using ::testing::SizeIs;
-using ::testing::StrEq;
-
 namespace android {
 
 TEST(LoadedArscTest, LoadSinglePackageArsc) {
@@ -44,24 +35,39 @@
                                       &contents));
 
   std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
-  ASSERT_THAT(loaded_arsc, NotNull());
+  ASSERT_NE(nullptr, loaded_arsc);
 
-  const LoadedPackage* package =
-      loaded_arsc->GetPackageById(get_package_id(app::R::string::string_one));
-  ASSERT_THAT(package, NotNull());
-  EXPECT_THAT(package->GetPackageName(), StrEq("com.android.app"));
-  EXPECT_THAT(package->GetPackageId(), Eq(0x7f));
+  const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages();
+  ASSERT_EQ(1u, packages.size());
+  EXPECT_EQ(std::string("com.android.app"), packages[0]->GetPackageName());
+  EXPECT_EQ(0x7f, packages[0]->GetPackageId());
 
-  const uint8_t type_index = get_type_id(app::R::string::string_one) - 1;
-  const uint16_t entry_index = get_entry_id(app::R::string::string_one);
+  ResTable_config config;
+  memset(&config, 0, sizeof(config));
+  config.sdkVersion = 24;
 
-  const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
-  ASSERT_THAT(type_spec, NotNull());
-  ASSERT_THAT(type_spec->type_count, Ge(1u));
+  FindEntryResult entry;
 
-  const ResTable_type* type = type_spec->types[0];
-  ASSERT_THAT(type, NotNull());
-  ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
+  ASSERT_TRUE(loaded_arsc->FindEntry(app::R::string::string_one, config, &entry));
+  ASSERT_NE(nullptr, entry.entry);
+}
+
+TEST(LoadedArscTest, FindDefaultEntry) {
+  std::string contents;
+  ASSERT_TRUE(
+      ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents));
+
+  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+  ASSERT_NE(nullptr, loaded_arsc);
+
+  ResTable_config desired_config;
+  memset(&desired_config, 0, sizeof(desired_config));
+  desired_config.language[0] = 'd';
+  desired_config.language[1] = 'e';
+
+  FindEntryResult entry;
+  ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry));
+  ASSERT_NE(nullptr, entry.entry);
 }
 
 TEST(LoadedArscTest, LoadSparseEntryApp) {
@@ -70,22 +76,15 @@
                                       &contents));
 
   std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
-  ASSERT_THAT(loaded_arsc, NotNull());
+  ASSERT_NE(nullptr, loaded_arsc);
 
-  const LoadedPackage* package =
-      loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9));
-  ASSERT_THAT(package, NotNull());
+  ResTable_config config;
+  memset(&config, 0, sizeof(config));
+  config.sdkVersion = 26;
 
-  const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1;
-  const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9);
-
-  const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
-  ASSERT_THAT(type_spec, NotNull());
-  ASSERT_THAT(type_spec->type_count, Ge(1u));
-
-  const ResTable_type* type = type_spec->types[0];
-  ASSERT_THAT(type, NotNull());
-  ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
+  FindEntryResult entry;
+  ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry));
+  ASSERT_NE(nullptr, entry.entry);
 }
 
 TEST(LoadedArscTest, LoadSharedLibrary) {
@@ -94,13 +93,14 @@
                                       &contents));
 
   std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
-  ASSERT_THAT(loaded_arsc, NotNull());
+  ASSERT_NE(nullptr, loaded_arsc);
 
   const auto& packages = loaded_arsc->GetPackages();
-  ASSERT_THAT(packages, SizeIs(1u));
+  ASSERT_EQ(1u, packages.size());
+
   EXPECT_TRUE(packages[0]->IsDynamic());
-  EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.lib_one"));
-  EXPECT_THAT(packages[0]->GetPackageId(), Eq(0));
+  EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName());
+  EXPECT_EQ(0, packages[0]->GetPackageId());
 
   const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap();
 
@@ -114,23 +114,25 @@
                                       "resources.arsc", &contents));
 
   std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
-  ASSERT_THAT(loaded_arsc, NotNull());
+  ASSERT_NE(nullptr, loaded_arsc);
 
   const auto& packages = loaded_arsc->GetPackages();
-  ASSERT_THAT(packages, SizeIs(1u));
+  ASSERT_EQ(1u, packages.size());
+
   EXPECT_FALSE(packages[0]->IsDynamic());
-  EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.libclient"));
-  EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f));
+  EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName());
+  EXPECT_EQ(0x7f, packages[0]->GetPackageId());
 
   const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap();
 
   // The library has two dependencies.
-  ASSERT_THAT(dynamic_pkg_map, SizeIs(2u));
-  EXPECT_THAT(dynamic_pkg_map[0].package_name, StrEq("com.android.lib_one"));
-  EXPECT_THAT(dynamic_pkg_map[0].package_id, Eq(0x02));
+  ASSERT_EQ(2u, dynamic_pkg_map.size());
 
-  EXPECT_THAT(dynamic_pkg_map[1].package_name, StrEq("com.android.lib_two"));
-  EXPECT_THAT(dynamic_pkg_map[1].package_id, Eq(0x03));
+  EXPECT_EQ(std::string("com.android.lib_one"), dynamic_pkg_map[0].package_name);
+  EXPECT_EQ(0x02, dynamic_pkg_map[0].package_id);
+
+  EXPECT_EQ(std::string("com.android.lib_two"), dynamic_pkg_map[1].package_name);
+  EXPECT_EQ(0x03, dynamic_pkg_map[1].package_id);
 }
 
 TEST(LoadedArscTest, LoadAppAsSharedLibrary) {
@@ -141,12 +143,13 @@
   std::unique_ptr<const LoadedArsc> loaded_arsc =
       LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/,
                        true /*load_as_shared_library*/);
-  ASSERT_THAT(loaded_arsc, NotNull());
+  ASSERT_NE(nullptr, loaded_arsc);
 
   const auto& packages = loaded_arsc->GetPackages();
-  ASSERT_THAT(packages, SizeIs(1u));
+  ASSERT_EQ(1u, packages.size());
+
   EXPECT_TRUE(packages[0]->IsDynamic());
-  EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f));
+  EXPECT_EQ(0x7f, packages[0]->GetPackageId());
 }
 
 TEST(LoadedArscTest, LoadFeatureSplit) {
@@ -154,27 +157,21 @@
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc",
                                       &contents));
   std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
-  ASSERT_THAT(loaded_arsc, NotNull());
+  ASSERT_NE(nullptr, loaded_arsc);
 
-  const LoadedPackage* package =
-      loaded_arsc->GetPackageById(get_package_id(basic::R::string::test3));
-  ASSERT_THAT(package, NotNull());
+  ResTable_config desired_config;
+  memset(&desired_config, 0, sizeof(desired_config));
 
-  uint8_t type_index = get_type_id(basic::R::string::test3) - 1;
-  uint8_t entry_index = get_entry_id(basic::R::string::test3);
-
-  const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
-  ASSERT_THAT(type_spec, NotNull());
-  ASSERT_THAT(type_spec->type_count, Ge(1u));
-  ASSERT_THAT(type_spec->types[0], NotNull());
+  FindEntryResult entry;
+  ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry));
 
   size_t len;
-  const char16_t* type_name16 =
-      package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len);
-  ASSERT_THAT(type_name16, NotNull());
-  EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string"));
+  const char16_t* type_name16 = entry.type_string_ref.string16(&len);
+  ASSERT_NE(nullptr, type_name16);
+  ASSERT_NE(0u, len);
 
-  ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull());
+  std::string type_name = util::Utf16ToUtf8(StringPiece16(type_name16, len));
+  EXPECT_EQ(std::string("string"), type_name);
 }
 
 class MockLoadedIdmap : public LoadedIdmap {
@@ -202,33 +199,23 @@
 };
 
 TEST(LoadedArscTest, LoadOverlay) {
-  std::string contents;
+  std::string contents, overlay_contents;
+  ASSERT_TRUE(
+      ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents));
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc",
-                                      &contents));
+                                      &overlay_contents));
 
   MockLoadedIdmap loaded_idmap;
 
   std::unique_ptr<const LoadedArsc> loaded_arsc =
-      LoadedArsc::Load(StringPiece(contents), &loaded_idmap);
-  ASSERT_THAT(loaded_arsc, NotNull());
+      LoadedArsc::Load(StringPiece(overlay_contents), &loaded_idmap);
+  ASSERT_NE(nullptr, loaded_arsc);
 
-  const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u);
-  ASSERT_THAT(package, NotNull());
+  ResTable_config desired_config;
+  memset(&desired_config, 0, sizeof(desired_config));
 
-  const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1);
-  ASSERT_THAT(type_spec, NotNull());
-  ASSERT_THAT(type_spec->type_count, Ge(1u));
-  ASSERT_THAT(type_spec->types[0], NotNull());
-
-  // The entry being overlaid doesn't exist at the original entry index.
-  ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull());
-
-  // Since this is an overlay, the actual entry ID must be mapped.
-  ASSERT_THAT(type_spec->idmap_entries, NotNull());
-  uint16_t target_entry_id = 0u;
-  ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id));
-  ASSERT_THAT(target_entry_id, Eq(0x0u));
-  ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull());
+  FindEntryResult entry;
+  ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry));
 }
 
 // structs with size fields (like Res_value, ResTable_entry) should be
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
index df0c642..43a9955 100644
--- a/libs/androidfw/tests/TestHelpers.h
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -20,7 +20,6 @@
 #include <string>
 
 #include "androidfw/ResourceTypes.h"
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
 #include "CommonHelpers.h"