Refactor AssetManager

Bug: 64071469
Test: atest CtsContentTestCases
Change-Id: Ia6856157e8813856268fba003e1e591d690cb26e
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 251b2e7..70d5216 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -145,6 +145,7 @@
         "tests/TypeWrappers_test.cpp",
         "tests/ZipUtils_test.cpp",
     ],
+    static_libs: ["libgmock"],
     target: {
         android: {
             srcs: [
@@ -171,6 +172,7 @@
 
         // Actual benchmarks.
         "tests/AssetManager2_bench.cpp",
+        "tests/AttributeResolution_bench.cpp",
         "tests/SparseEntry_bench.cpp",
         "tests/Theme_bench.cpp",
     ],
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index da0205d..8f58f74 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define ATRACE_TAG ATRACE_TAG_RESOURCES
-
 #include "androidfw/ApkAssets.h"
 
 #include <algorithm>
@@ -27,7 +25,6 @@
 #include "android-base/utf8.h"
 #include "utils/Compat.h"
 #include "utils/FileMap.h"
-#include "utils/Trace.h"
 #include "ziparchive/zip_archive.h"
 
 #include "androidfw/Asset.h"
@@ -105,8 +102,6 @@
 std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
     unique_fd fd, const std::string& path, std::unique_ptr<Asset> idmap_asset,
     std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library) {
-  ATRACE_CALL();
-
   ::ZipArchiveHandle unmanaged_handle;
   int32_t result;
   if (fd >= 0) {
@@ -163,7 +158,6 @@
 }
 
 std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const {
-  ATRACE_CALL();
   CHECK(zip_handle_ != nullptr);
 
   ::ZipString name(path.c_str());
@@ -231,12 +225,16 @@
   while ((result = ::Next(cookie, &entry, &name)) == 0) {
     StringPiece full_file_path(reinterpret_cast<const char*>(name.name), name.name_length);
     StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
-    auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
-    if (iter != leaf_file_path.end()) {
-      dirs.insert(
-          leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string());
-    } else if (!leaf_file_path.empty()) {
-      f(leaf_file_path, kFileTypeRegular);
+
+    if (!leaf_file_path.empty()) {
+      auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
+      if (iter != leaf_file_path.end()) {
+        std::string dir =
+            leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
+        dirs.insert(std::move(dir));
+      } else {
+        f(leaf_file_path, kFileTypeRegular);
+      }
     }
   }
   ::EndIteration(cookie);
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 415d3e3..d9f1293 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -36,6 +36,31 @@
 
 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_));
 }
@@ -44,6 +69,7 @@
                                  bool invalidate_caches) {
   apk_assets_ = apk_assets;
   BuildDynamicRefTable();
+  RebuildFilterList();
   if (invalidate_caches) {
     InvalidateCaches(static_cast<uint32_t>(-1));
   }
@@ -74,12 +100,14 @@
       if (idx == 0xff) {
         package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size());
         package_groups_.push_back({});
-        package_groups_.back().dynamic_ref_table.mAssignedPackageId = package_id;
+        DynamicRefTable& ref_table = package_groups_.back().dynamic_ref_table;
+        ref_table.mAssignedPackageId = package_id;
+        ref_table.mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
       }
       PackageGroup* package_group = &package_groups_[idx];
 
       // Add the package and to the set of packages with the same ID.
-      package_group->packages_.push_back(package.get());
+      package_group->packages_.push_back(ConfiguredPackage{package.get(), {}});
       package_group->cookies_.push_back(static_cast<ApkAssetsCookie>(i));
 
       // Add the package name -> build time ID mappings.
@@ -94,7 +122,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]->GetPackageName();
+    const std::string& package_name = iter->packages_[0].loaded_package_->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);
@@ -105,20 +133,33 @@
 void AssetManager2::DumpToLog() const {
   base::ScopedLogSeverity _log(base::INFO);
 
+  LOG(INFO) << base::StringPrintf("AssetManager2(this=%p)", this);
+
   std::string list;
+  for (const auto& apk_assets : apk_assets_) {
+    base::StringAppendF(&list, "%s,", apk_assets->GetPath().c_str());
+  }
+  LOG(INFO) << "ApkAssets: " << list;
+
+  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->GetPackageName().c_str(), package->GetPackageId());
-      }
-      LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list;
+    list = "";
+    for (const auto& package : package_group.packages_) {
+      const LoadedPackage* loaded_package = package.loaded_package_;
+      base::StringAppendF(&list, "%s(%02x%s), ", loaded_package->GetPackageName().c_str(),
+                          loaded_package->GetPackageId(),
+                          (loaded_package->IsDynamic() ? " dynamic" : ""));
+    }
+    LOG(INFO) << base::StringPrintf("PG (%02x): ",
+                                    package_group.dynamic_ref_table.mAssignedPackageId)
+              << list;
   }
 }
 
@@ -157,53 +198,55 @@
   configuration_ = configuration;
 
   if (diff) {
+    RebuildFilterList();
     InvalidateCaches(static_cast<uint32_t>(diff));
   }
 }
 
 std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
-                                                                   bool exclude_mipmap) {
-  ATRACE_CALL();
+                                                                   bool exclude_mipmap) const {
+  ATRACE_NAME("AssetManager::GetResourceConfigurations");
   std::set<ResTable_config> configurations;
   for (const PackageGroup& package_group : package_groups_) {
-    for (const LoadedPackage* package : package_group.packages_) {
-      if (exclude_system && package->IsSystem()) {
+    for (const ConfiguredPackage& package : package_group.packages_) {
+      if (exclude_system && package.loaded_package_->IsSystem()) {
         continue;
       }
-      package->CollectConfigurations(exclude_mipmap, &configurations);
+      package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations);
     }
   }
   return configurations;
 }
 
 std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system,
-                                                        bool merge_equivalent_languages) {
-  ATRACE_CALL();
+                                                        bool merge_equivalent_languages) const {
+  ATRACE_NAME("AssetManager::GetResourceLocales");
   std::set<std::string> locales;
   for (const PackageGroup& package_group : package_groups_) {
-    for (const LoadedPackage* package : package_group.packages_) {
-      if (exclude_system && package->IsSystem()) {
+    for (const ConfiguredPackage& package : package_group.packages_) {
+      if (exclude_system && package.loaded_package_->IsSystem()) {
         continue;
       }
-      package->CollectLocales(merge_equivalent_languages, &locales);
+      package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales);
     }
   }
   return locales;
 }
 
-std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) {
+std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename,
+                                           Asset::AccessMode mode) const {
   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) {
+                                           Asset::AccessMode mode) const {
   const std::string new_path = "assets/" + filename;
   return OpenNonAsset(new_path, cookie, mode);
 }
 
-std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) {
-  ATRACE_CALL();
+std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) const {
+  ATRACE_NAME("AssetManager::OpenDir");
 
   std::string full_path = "assets/" + dirname;
   std::unique_ptr<SortedVector<AssetDir::FileInfo>> files =
@@ -236,8 +279,7 @@
 // is inconsistent for split APKs.
 std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
                                                    Asset::AccessMode mode,
-                                                   ApkAssetsCookie* out_cookie) {
-  ATRACE_CALL();
+                                                   ApkAssetsCookie* out_cookie) const {
   for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) {
     std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode);
     if (asset) {
@@ -255,8 +297,8 @@
 }
 
 std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
-                                                   ApkAssetsCookie cookie, Asset::AccessMode mode) {
-  ATRACE_CALL();
+                                                   ApkAssetsCookie cookie,
+                                                   Asset::AccessMode mode) const {
   if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
     return {};
   }
@@ -264,14 +306,13 @@
 }
 
 ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
-                                         bool stop_at_first_match, FindEntryResult* out_entry) {
-  ATRACE_CALL();
-
+                                         bool /*stop_at_first_match*/,
+                                         FindEntryResult* out_entry) const {
   // Might use this if density_override != 0.
   ResTable_config density_override_config;
 
   // Select our configuration or generate a density override configuration.
-  ResTable_config* desired_config = &configuration_;
+  const ResTable_config* desired_config = &configuration_;
   if (density_override != 0 && density_override != configuration_.density) {
     density_override_config = configuration_;
     density_override_config.density = density_override;
@@ -285,55 +326,135 @@
 
   const uint32_t package_id = get_package_id(resid);
   const uint8_t type_idx = get_type_id(resid) - 1;
-  const uint16_t entry_id = get_entry_id(resid);
+  const uint16_t entry_idx = get_entry_id(resid);
 
-  const uint8_t idx = package_ids_[package_id];
-  if (idx == 0xff) {
+  const uint8_t package_idx = package_ids_[package_id];
+  if (package_idx == 0xff) {
     LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid);
     return kInvalidCookie;
   }
 
-  FindEntryResult best_entry;
-  ApkAssetsCookie best_cookie = kInvalidCookie;
-  uint32_t cumulated_flags = 0u;
-
-  const PackageGroup& package_group = package_groups_[idx];
+  const PackageGroup& package_group = package_groups_[package_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)) {
+
+  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;
+
+  // 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)) {
       continue;
     }
 
-    cumulated_flags |= current_entry.type_flags;
+    uint16_t local_entry_idx = entry_idx;
 
-    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 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;
+          }
+        }
       }
     }
   }
 
-  if (best_cookie == kInvalidCookie) {
+  if (UNLIKELY(best_cookie == kInvalidCookie)) {
     return kInvalidCookie;
   }
 
-  *out_entry = best_entry;
+  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->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) {
-  ATRACE_CALL();
-
+bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const {
   FindEntryResult entry;
   ApkAssetsCookie cookie =
       FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, &entry);
@@ -341,7 +462,8 @@
     return false;
   }
 
-  const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid);
+  const LoadedPackage* package =
+      apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
   if (package == nullptr) {
     return false;
   }
@@ -369,7 +491,7 @@
   return true;
 }
 
-bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) {
+bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const {
   FindEntryResult entry;
   ApkAssetsCookie cookie =
       FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry);
@@ -383,9 +505,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) {
-  ATRACE_CALL();
-
+                                           uint32_t* out_flags) const {
   FindEntryResult entry;
   ApkAssetsCookie cookie =
       FindEntry(resid, density_override, false /* stop_at_first_match */, &entry);
@@ -402,7 +522,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;
   }
@@ -414,7 +534,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;
 }
@@ -422,16 +542,13 @@
 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) {
-  ATRACE_CALL();
+                                                uint32_t* out_last_reference) const {
   constexpr const int kMaxIterations = 20;
 
   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 = in_out_value->data;
-    }
+    *out_last_reference = 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);
@@ -450,7 +567,7 @@
 }
 
 const ResolvedBag* AssetManager2::GetBag(uint32_t resid) {
-  ATRACE_CALL();
+  ATRACE_NAME("AssetManager::GetBag");
 
   auto cached_iter = cached_bags_.find(resid);
   if (cached_iter != cached_bags_.end()) {
@@ -492,7 +609,8 @@
         // 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;
         }
       }
@@ -524,7 +642,8 @@
   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;
   }
 
@@ -543,7 +662,8 @@
     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;
       }
     }
@@ -582,7 +702,8 @@
     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;
       }
     }
@@ -638,7 +759,7 @@
 
 uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
                                       const std::string& fallback_type,
-                                      const std::string& fallback_package) {
+                                      const std::string& fallback_package) const {
   StringPiece package_name, type, entry;
   if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) {
     return 0u;
@@ -670,7 +791,8 @@
   const static std::u16string kAttrPrivate16 = u"^attr-private";
 
   for (const PackageGroup& package_group : package_groups_) {
-    for (const LoadedPackage* package : package_group.packages_) {
+    for (const ConfiguredPackage& package_impl : package_group.packages_) {
+      const LoadedPackage* package = package_impl.loaded_package_;
       if (package_name != package->GetPackageName()) {
         // All packages in the same group are expected to have the same package name.
         break;
@@ -692,6 +814,32 @@
   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.
@@ -743,7 +891,7 @@
 };
 
 bool Theme::ApplyStyle(uint32_t resid, bool force) {
-  ATRACE_CALL();
+  ATRACE_NAME("Theme::ApplyStyle");
 
   const ResolvedBag* bag = asset_manager_->GetBag(resid);
   if (bag == nullptr) {
@@ -872,7 +1020,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) {
+                                                 uint32_t* out_last_ref) const {
   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/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp
index 60e3845..f912af4 100644
--- a/libs/androidfw/AttributeResolution.cpp
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -20,13 +20,18 @@
 
 #include <log/log.h>
 
+#include "androidfw/AssetManager2.h"
 #include "androidfw/AttributeFinder.h"
-#include "androidfw/ResourceTypes.h"
 
 constexpr bool kDebugStyles = false;
 
 namespace android {
 
+// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0.
+static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) {
+  return cookie != kInvalidCookie ? static_cast<uint32_t>(cookie + 1) : static_cast<uint32_t>(-1);
+}
+
 class XmlAttributeFinder
     : public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> {
  public:
@@ -44,58 +49,53 @@
 };
 
 class BagAttributeFinder
-    : public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> {
+    : public BackTrackingAttributeFinder<BagAttributeFinder, const ResolvedBag::Entry*> {
  public:
-  BagAttributeFinder(const ResTable::bag_entry* start,
-                     const ResTable::bag_entry* end)
-      : BackTrackingAttributeFinder(start, end) {}
+  BagAttributeFinder(const ResolvedBag* bag)
+      : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr,
+                                    bag != nullptr ? bag->entries + bag->entry_count : nullptr) {
+  }
 
-  inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const {
-    return entry->map.name.ident;
+  inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const {
+    return entry->key;
   }
 };
 
-bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr,
-                  uint32_t def_style_res, uint32_t* src_values,
-                  size_t src_values_length, uint32_t* attrs,
-                  size_t attrs_length, uint32_t* out_values,
-                  uint32_t* out_indices) {
+bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res,
+                  uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
+                  size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
   if (kDebugStyles) {
     ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme,
           def_style_attr, def_style_res);
   }
 
-  const ResTable& res = theme->getResTable();
+  AssetManager2* assetmanager = theme->GetAssetManager();
   ResTable_config config;
   Res_value value;
 
   int indices_idx = 0;
 
   // Load default style from attribute, if specified...
-  uint32_t def_style_bag_type_set_flags = 0;
+  uint32_t def_style_flags = 0u;
   if (def_style_attr != 0) {
     Res_value value;
-    if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) {
+    if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
       if (value.dataType == Res_value::TYPE_REFERENCE) {
         def_style_res = value.data;
       }
     }
   }
 
-  // Now lock down the resource object and start pulling stuff from it.
-  res.lock();
-
   // Retrieve the default style bag, if requested.
-  const ResTable::bag_entry* def_style_start = nullptr;
-  uint32_t def_style_type_set_flags = 0;
-  ssize_t bag_off = def_style_res != 0
-                        ? res.getBagLocked(def_style_res, &def_style_start,
-                                           &def_style_type_set_flags)
-                        : -1;
-  def_style_type_set_flags |= def_style_bag_type_set_flags;
-  const ResTable::bag_entry* const def_style_end =
-      def_style_start + (bag_off >= 0 ? bag_off : 0);
-  BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end);
+  const ResolvedBag* default_style_bag = nullptr;
+  if (def_style_res != 0) {
+    default_style_bag = assetmanager->GetBag(def_style_res);
+    if (default_style_bag != nullptr) {
+      def_style_flags |= default_style_bag->type_spec_flags;
+    }
+  }
+
+  BagAttributeFinder def_style_attr_finder(default_style_bag);
 
   // Now iterate through all of the attributes that the client has requested,
   // filling in each with whatever data we can find.
@@ -106,7 +106,7 @@
       ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
     }
 
-    ssize_t block = -1;
+    ApkAssetsCookie cookie = kInvalidCookie;
     uint32_t type_set_flags = 0;
 
     value.dataType = Res_value::TYPE_NULL;
@@ -122,15 +122,14 @@
       value.dataType = Res_value::TYPE_ATTRIBUTE;
       value.data = src_values[ii];
       if (kDebugStyles) {
-        ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType,
-              value.data);
+        ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data);
       }
     } else {
-      const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident);
-      if (def_style_entry != def_style_end) {
-        block = def_style_entry->stringBlock;
-        type_set_flags = def_style_type_set_flags;
-        value = def_style_entry->map.value;
+      const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident);
+      if (entry != def_style_attr_finder.end()) {
+        cookie = entry->cookie;
+        type_set_flags = def_style_flags;
+        value = entry->value;
         if (kDebugStyles) {
           ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
         }
@@ -140,22 +139,26 @@
     uint32_t resid = 0;
     if (value.dataType != Res_value::TYPE_NULL) {
       // Take care of resolving the found resource to its final value.
-      ssize_t new_block =
-          theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
-      if (new_block >= 0) block = new_block;
+      ApkAssetsCookie new_cookie =
+          theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid);
+      if (new_cookie != kInvalidCookie) {
+        cookie = new_cookie;
+      }
       if (kDebugStyles) {
         ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
       }
     } else if (value.data != Res_value::DATA_NULL_EMPTY) {
-      // If we still don't have a value for this attribute, try to find
-      // it in the theme!
-      ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
-      if (new_block >= 0) {
+      // If we still don't have a value for this attribute, try to find it in the theme!
+      ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags);
+      if (new_cookie != kInvalidCookie) {
         if (kDebugStyles) {
           ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
         }
-        new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
-        if (new_block >= 0) block = new_block;
+        new_cookie =
+            assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid);
+        if (new_cookie != kInvalidCookie) {
+          cookie = new_cookie;
+        }
         if (kDebugStyles) {
           ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
         }
@@ -169,7 +172,7 @@
       }
       value.dataType = Res_value::TYPE_NULL;
       value.data = Res_value::DATA_NULL_UNDEFINED;
-      block = -1;
+      cookie = kInvalidCookie;
     }
 
     if (kDebugStyles) {
@@ -179,9 +182,7 @@
     // Write the final value back to Java.
     out_values[STYLE_TYPE] = value.dataType;
     out_values[STYLE_DATA] = value.data;
-    out_values[STYLE_ASSET_COOKIE] =
-        block != -1 ? static_cast<uint32_t>(res.getTableCookie(block))
-                    : static_cast<uint32_t>(-1);
+    out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
     out_values[STYLE_RESOURCE_ID] = resid;
     out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
     out_values[STYLE_DENSITY] = config.density;
@@ -195,90 +196,80 @@
     out_values += STYLE_NUM_ENTRIES;
   }
 
-  res.unlock();
-
   if (out_indices != nullptr) {
     out_indices[0] = indices_idx;
   }
   return true;
 }
 
-void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
-                uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length,
+void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
+                uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length,
                 uint32_t* out_values, uint32_t* out_indices) {
   if (kDebugStyles) {
-    ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p",
-          theme, def_style_attr, def_style_res, xml_parser);
+    ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme,
+          def_style_attr, def_style_resid, xml_parser);
   }
 
-  const ResTable& res = theme->getResTable();
+  AssetManager2* assetmanager = theme->GetAssetManager();
   ResTable_config config;
   Res_value value;
 
   int indices_idx = 0;
 
   // Load default style from attribute, if specified...
-  uint32_t def_style_bag_type_set_flags = 0;
+  uint32_t def_style_flags = 0u;
   if (def_style_attr != 0) {
     Res_value value;
-    if (theme->getAttribute(def_style_attr, &value,
-                            &def_style_bag_type_set_flags) >= 0) {
+    if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
       if (value.dataType == Res_value::TYPE_REFERENCE) {
-        def_style_res = value.data;
+        def_style_resid = value.data;
       }
     }
   }
 
-  // Retrieve the style class associated with the current XML tag.
-  int style = 0;
-  uint32_t style_bag_type_set_flags = 0;
+  // Retrieve the style resource ID associated with the current XML tag's style attribute.
+  uint32_t style_resid = 0u;
+  uint32_t style_flags = 0u;
   if (xml_parser != nullptr) {
     ssize_t idx = xml_parser->indexOfStyle();
     if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) {
       if (value.dataType == value.TYPE_ATTRIBUTE) {
-        if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) {
+        // Resolve the attribute with out theme.
+        if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) {
           value.dataType = Res_value::TYPE_NULL;
         }
       }
+
       if (value.dataType == value.TYPE_REFERENCE) {
-        style = value.data;
+        style_resid = value.data;
       }
     }
   }
 
-  // Now lock down the resource object and start pulling stuff from it.
-  res.lock();
-
   // Retrieve the default style bag, if requested.
-  const ResTable::bag_entry* def_style_attr_start = nullptr;
-  uint32_t def_style_type_set_flags = 0;
-  ssize_t bag_off = def_style_res != 0
-                        ? res.getBagLocked(def_style_res, &def_style_attr_start,
-                                           &def_style_type_set_flags)
-                        : -1;
-  def_style_type_set_flags |= def_style_bag_type_set_flags;
-  const ResTable::bag_entry* const def_style_attr_end =
-      def_style_attr_start + (bag_off >= 0 ? bag_off : 0);
-  BagAttributeFinder def_style_attr_finder(def_style_attr_start,
-                                           def_style_attr_end);
+  const ResolvedBag* default_style_bag = nullptr;
+  if (def_style_resid != 0) {
+    default_style_bag = assetmanager->GetBag(def_style_resid);
+    if (default_style_bag != nullptr) {
+      def_style_flags |= default_style_bag->type_spec_flags;
+    }
+  }
+
+  BagAttributeFinder def_style_attr_finder(default_style_bag);
 
   // Retrieve the style class bag, if requested.
-  const ResTable::bag_entry* style_attr_start = nullptr;
-  uint32_t style_type_set_flags = 0;
-  bag_off =
-      style != 0
-          ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags)
-          : -1;
-  style_type_set_flags |= style_bag_type_set_flags;
-  const ResTable::bag_entry* const style_attr_end =
-      style_attr_start + (bag_off >= 0 ? bag_off : 0);
-  BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end);
+  const ResolvedBag* xml_style_bag = nullptr;
+  if (style_resid != 0) {
+    xml_style_bag = assetmanager->GetBag(style_resid);
+    if (xml_style_bag != nullptr) {
+      style_flags |= xml_style_bag->type_spec_flags;
+    }
+  }
+
+  BagAttributeFinder xml_style_attr_finder(xml_style_bag);
 
   // Retrieve the XML attributes, if requested.
-  static const ssize_t kXmlBlock = 0x10000000;
   XmlAttributeFinder xml_attr_finder(xml_parser);
-  const size_t xml_attr_end =
-      xml_parser != nullptr ? xml_parser->getAttributeCount() : 0;
 
   // Now iterate through all of the attributes that the client has requested,
   // filling in each with whatever data we can find.
@@ -289,8 +280,8 @@
       ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
     }
 
-    ssize_t block = kXmlBlock;
-    uint32_t type_set_flags = 0;
+    ApkAssetsCookie cookie = kInvalidCookie;
+    uint32_t type_set_flags = 0u;
 
     value.dataType = Res_value::TYPE_NULL;
     value.data = Res_value::DATA_NULL_UNDEFINED;
@@ -302,7 +293,7 @@
 
     // Walk through the xml attributes looking for the requested attribute.
     const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident);
-    if (xml_attr_idx != xml_attr_end) {
+    if (xml_attr_idx != xml_attr_finder.end()) {
       // We found the attribute we were looking for.
       xml_parser->getAttributeValue(xml_attr_idx, &value);
       if (kDebugStyles) {
@@ -312,12 +303,12 @@
 
     if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
       // Walk through the style class values looking for the requested attribute.
-      const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident);
-      if (style_attr_entry != style_attr_end) {
+      const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident);
+      if (entry != xml_style_attr_finder.end()) {
         // We found the attribute we were looking for.
-        block = style_attr_entry->stringBlock;
-        type_set_flags = style_type_set_flags;
-        value = style_attr_entry->map.value;
+        cookie = entry->cookie;
+        type_set_flags = style_flags;
+        value = entry->value;
         if (kDebugStyles) {
           ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data);
         }
@@ -326,25 +317,25 @@
 
     if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
       // Walk through the default style values looking for the requested attribute.
-      const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident);
-      if (def_style_attr_entry != def_style_attr_end) {
+      const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident);
+      if (entry != def_style_attr_finder.end()) {
         // We found the attribute we were looking for.
-        block = def_style_attr_entry->stringBlock;
-        type_set_flags = style_type_set_flags;
-        value = def_style_attr_entry->map.value;
+        cookie = entry->cookie;
+        type_set_flags = def_style_flags;
+        value = entry->value;
         if (kDebugStyles) {
           ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
         }
       }
     }
 
-    uint32_t resid = 0;
+    uint32_t resid = 0u;
     if (value.dataType != Res_value::TYPE_NULL) {
       // Take care of resolving the found resource to its final value.
-      ssize_t new_block =
-          theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
-      if (new_block >= 0) {
-        block = new_block;
+      ApkAssetsCookie new_cookie =
+          theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid);
+      if (new_cookie != kInvalidCookie) {
+        cookie = new_cookie;
       }
 
       if (kDebugStyles) {
@@ -352,14 +343,15 @@
       }
     } else if (value.data != Res_value::DATA_NULL_EMPTY) {
       // If we still don't have a value for this attribute, try to find it in the theme!
-      ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
-      if (new_block >= 0) {
+      ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags);
+      if (new_cookie != kInvalidCookie) {
         if (kDebugStyles) {
           ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
         }
-        new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
-        if (new_block >= 0) {
-          block = new_block;
+        new_cookie =
+            assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid);
+        if (new_cookie != kInvalidCookie) {
+          cookie = new_cookie;
         }
 
         if (kDebugStyles) {
@@ -375,7 +367,7 @@
       }
       value.dataType = Res_value::TYPE_NULL;
       value.data = Res_value::DATA_NULL_UNDEFINED;
-      block = kXmlBlock;
+      cookie = kInvalidCookie;
     }
 
     if (kDebugStyles) {
@@ -385,9 +377,7 @@
     // Write the final value back to Java.
     out_values[STYLE_TYPE] = value.dataType;
     out_values[STYLE_DATA] = value.data;
-    out_values[STYLE_ASSET_COOKIE] =
-        block != kXmlBlock ? static_cast<uint32_t>(res.getTableCookie(block))
-                           : static_cast<uint32_t>(-1);
+    out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
     out_values[STYLE_RESOURCE_ID] = resid;
     out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
     out_values[STYLE_DENSITY] = config.density;
@@ -402,36 +392,28 @@
     out_values += STYLE_NUM_ENTRIES;
   }
 
-  res.unlock();
-
   // out_indices must NOT be nullptr.
   out_indices[0] = indices_idx;
 }
 
-bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser,
-                        uint32_t* attrs, size_t attrs_length,
-                        uint32_t* out_values, uint32_t* out_indices) {
+bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs,
+                        size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
   ResTable_config config;
   Res_value value;
 
   int indices_idx = 0;
 
-  // Now lock down the resource object and start pulling stuff from it.
-  res->lock();
-
   // Retrieve the XML attributes, if requested.
   const size_t xml_attr_count = xml_parser->getAttributeCount();
   size_t ix = 0;
   uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix);
 
-  static const ssize_t kXmlBlock = 0x10000000;
-
   // Now iterate through all of the attributes that the client has requested,
   // filling in each with whatever data we can find.
   for (size_t ii = 0; ii < attrs_length; ii++) {
     const uint32_t cur_ident = attrs[ii];
-    ssize_t block = kXmlBlock;
-    uint32_t type_set_flags = 0;
+    ApkAssetsCookie cookie = kInvalidCookie;
+    uint32_t type_set_flags = 0u;
 
     value.dataType = Res_value::TYPE_NULL;
     value.data = Res_value::DATA_NULL_UNDEFINED;
@@ -450,28 +432,27 @@
       cur_xml_attr = xml_parser->getAttributeNameResID(ix);
     }
 
-    uint32_t resid = 0;
+    uint32_t resid = 0u;
     if (value.dataType != Res_value::TYPE_NULL) {
       // Take care of resolving the found resource to its final value.
-      // printf("Resolving attribute reference\n");
-      ssize_t new_block = res->resolveReference(&value, block, &resid,
-                                                &type_set_flags, &config);
-      if (new_block >= 0) block = new_block;
+      ApkAssetsCookie new_cookie =
+          assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid);
+      if (new_cookie != kInvalidCookie) {
+        cookie = new_cookie;
+      }
     }
 
     // Deal with the special @null value -- it turns back to TYPE_NULL.
     if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
       value.dataType = Res_value::TYPE_NULL;
       value.data = Res_value::DATA_NULL_UNDEFINED;
-      block = kXmlBlock;
+      cookie = kInvalidCookie;
     }
 
     // Write the final value back to Java.
     out_values[STYLE_TYPE] = value.dataType;
     out_values[STYLE_DATA] = value.data;
-    out_values[STYLE_ASSET_COOKIE] =
-        block != kXmlBlock ? static_cast<uint32_t>(res->getTableCookie(block))
-                           : static_cast<uint32_t>(-1);
+    out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
     out_values[STYLE_RESOURCE_ID] = resid;
     out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
     out_values[STYLE_DENSITY] = config.density;
@@ -485,8 +466,6 @@
     out_values += STYLE_NUM_ENTRIES;
   }
 
-  res->unlock();
-
   if (out_indices != nullptr) {
     out_indices[0] = indices_idx;
   }
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 28548e2..04d506a 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -44,44 +44,6 @@
 
 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
@@ -95,21 +57,22 @@
   }
 
   void AddType(const ResTable_type* type) {
-    ResTable_config config;
-    config.copyFromDtoH(type->config);
-    types_.push_back(Type{config, type});
+    types_.push_back(type);
   }
 
   TypeSpecPtr Build() {
     // Check for overflow.
-    if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) {
+    using ElementType = const ResTable_type*;
+    if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(ElementType) <
+        types_.size()) {
       return {};
     }
-    TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type)));
+    TypeSpec* type_spec =
+        (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType)));
     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(Type));
+    memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType));
     return TypeSpecPtr(type_spec);
   }
 
@@ -118,7 +81,7 @@
 
   const ResTable_typeSpec* header_;
   const IdmapEntry_header* idmap_header_;
-  std::vector<Type> types_;
+  std::vector<const ResTable_type*> types_;
 };
 
 }  // namespace
@@ -162,18 +125,17 @@
   return true;
 }
 
-static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset,
-                                size_t entry_idx) {
+static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) {
   // Check that the offset is aligned.
   if (entry_offset & 0x03) {
-    LOG(ERROR) << "Entry offset at index " << entry_idx << " is not 4-byte aligned.";
+    LOG(ERROR) << "Entry at offset " << entry_offset << " 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 offset at index " << entry_idx << " is too large.";
+    LOG(ERROR) << "Entry at offset " << entry_offset << " is too large.";
     return false;
   }
 
@@ -181,7 +143,7 @@
 
   entry_offset += dtohl(type->entriesStart);
   if (entry_offset > chunk_size - sizeof(ResTable_entry)) {
-    LOG(ERROR) << "Entry offset at index " << entry_idx
+    LOG(ERROR) << "Entry at offset " << entry_offset
                << " is too large. No room for ResTable_entry.";
     return false;
   }
@@ -191,13 +153,13 @@
 
   const size_t entry_size = dtohs(entry->size);
   if (entry_size < sizeof(*entry)) {
-    LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx
+    LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
                << " is too small.";
     return false;
   }
 
   if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) {
-    LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx
+    LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
                << " is too large.";
     return false;
   }
@@ -205,7 +167,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 index " << entry_idx
+      LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset
                  << " for type " << (int)type->id << ".";
       return false;
     }
@@ -214,12 +176,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 index " << entry_idx << " is too small.";
+      LOG(ERROR) << "Res_value at offset " << entry_offset << " 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 index " << entry_idx
+      LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset
                  << " is too large.";
       return false;
     }
@@ -228,119 +190,76 @@
     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 index " << entry_idx << " start at unaligned offset.";
+      LOG(ERROR) << "Map entries at offset " << entry_offset << " 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 index " << entry_idx << ".";
+      LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << ".";
       return false;
     }
   }
   return true;
 }
 
-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;
+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);
+}
 
-  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;
+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);
 
-    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);
+  // Check if there is the desired entry in this type.
 
-      // 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*>(
+  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 uint32_t offset = dtohl(entry_offsets[entry_idx]);
-        if (offset == ResTable_type::NO_ENTRY) {
-          continue;
-        }
+    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;
+                         });
 
-        // There is an entry for this resource, record it.
-        best_offset = offset;
-      }
-
-      best_config = &type->configuration;
-      best_type = type_chunk;
+    if (result == sparse_indices_end || dtohs(result->idx) != entry_index) {
+      // No entry found.
+      return ResTable_type::NO_ENTRY;
     }
+
+    // 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;
   }
 
-  if (best_type == nullptr) {
-    return false;
+  // This type is encoded as a dense array.
+  if (entry_index >= entry_count) {
+    // This entry cannot be here.
+    return ResTable_type::NO_ENTRY;
   }
 
-  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 uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
+      reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
+  return dtohl(entry_offsets[entry_index]);
 }
 
-bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config,
-                              FindEntryResult* out_entry) const {
-  ATRACE_CALL();
-
-  // 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;
+const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk,
+                                                        uint32_t offset) {
+  if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) {
+    return nullptr;
   }
-
-  // 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);
+  return reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type_chunk) +
+                                                 offset + dtohl(type_chunk->entriesStart));
 }
 
 void LoadedPackage::CollectConfigurations(bool exclude_mipmap,
@@ -348,7 +267,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 util::unique_cptr<TypeSpec>& type_spec = type_specs_[i];
+    const TypeSpecPtr& type_spec = type_specs_[i];
     if (type_spec != nullptr) {
       if (exclude_mipmap) {
         const int type_idx = type_spec->type_spec->id - 1;
@@ -369,8 +288,11 @@
         }
       }
 
-      for (size_t j = 0; j < type_spec->type_count; j++) {
-        out_configs->insert(type_spec->types[j].configuration);
+      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);
       }
     }
   }
@@ -380,10 +302,12 @@
   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];
+    const TypeSpecPtr& 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;
+      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);
         if (configuration.locale != 0) {
           configuration.getBcp47Locale(temp_locale, canonicalize);
           std::string locale(temp_locale);
@@ -411,17 +335,17 @@
     return 0u;
   }
 
-  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);
+  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 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->type) + dtohs(type->type->header.headerSize));
+          reinterpret_cast<const uint8_t*>(type) + dtohs(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->type) +
-                                                    dtohl(type->type->entriesStart) + offset);
+        const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
+            reinterpret_cast<const uint8_t*>(type) + dtohl(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).
@@ -433,8 +357,7 @@
   return 0u;
 }
 
-const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const {
-  const uint8_t package_id = get_package_id(resid);
+const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const {
   for (const auto& loaded_package : packages_) {
     if (loaded_package->GetPackageId() == package_id) {
       return loaded_package.get();
@@ -446,7 +369,7 @@
 std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
                                                          const LoadedIdmap* loaded_idmap,
                                                          bool system, bool load_as_shared_library) {
-  ATRACE_CALL();
+  ATRACE_NAME("LoadedPackage::Load");
   std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage());
 
   // typeIdOffset was added at some point, but we still must recognize apps built before this
@@ -486,14 +409,10 @@
   util::ReadUtf16StringFromDevice(header->name, arraysize(header->name),
                                   &loaded_package->package_name_);
 
-  // A TypeSpec builder. We use this to accumulate the set of Types
-  // available for a TypeSpec, and later build a single, contiguous block
-  // of memory that holds all the Types together with the TypeSpec.
-  std::unique_ptr<TypeSpecPtrBuilder> types_builder;
-
-  // Keep track of the last seen type index. Since type IDs are 1-based,
-  // this records their index, which is 0-based (type ID - 1).
-  uint8_t last_type_idx = 0;
+  // A map of TypeSpec builders, each associated with an type index.
+  // We use these to accumulate the set of Types available for a TypeSpec, and later build a single,
+  // contiguous block of memory that holds all the Types together with the TypeSpec.
+  std::unordered_map<int, std::unique_ptr<TypeSpecPtrBuilder>> type_builder_map;
 
   ChunkIterator iter(chunk.data_ptr(), chunk.data_size());
   while (iter.HasNext()) {
@@ -525,30 +444,6 @@
       } break;
 
       case RES_TABLE_TYPE_SPEC_TYPE: {
-        ATRACE_NAME("LoadTableTypeSpec");
-
-        // Starting a new TypeSpec, so finish the old one if there was one.
-        if (types_builder) {
-          TypeSpecPtr type_spec_ptr = types_builder->Build();
-          if (type_spec_ptr == nullptr) {
-            LOG(ERROR) << "Too many type configurations, overflow detected.";
-            return {};
-          }
-
-          // We only add the type to the package if there is no IDMAP, or if the type is
-          // overlaying something.
-          if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) {
-            // If this is an overlay, insert it at the target type ID.
-            if (type_spec_ptr->idmap_entries != nullptr) {
-              last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1;
-            }
-            loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr);
-          }
-
-          types_builder = {};
-          last_type_idx = 0;
-        }
-
         const ResTable_typeSpec* type_spec = child_chunk.header<ResTable_typeSpec>();
         if (type_spec == nullptr) {
           LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small.";
@@ -583,8 +478,6 @@
           return {};
         }
 
-        last_type_idx = type_spec->id - 1;
-
         // If this is an overlay, associate the mapping of this type to the target type
         // from the IDMAP.
         const IdmapEntry_header* idmap_entry_header = nullptr;
@@ -592,7 +485,13 @@
           idmap_entry_header = loaded_idmap->GetEntryMapForType(type_spec->id);
         }
 
-        types_builder = util::make_unique<TypeSpecPtrBuilder>(type_spec, idmap_entry_header);
+        std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type_spec->id - 1];
+        if (builder_ptr == nullptr) {
+          builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec, idmap_entry_header);
+        } else {
+          LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x",
+                                       type_spec->id);
+        }
       } break;
 
       case RES_TABLE_TYPE_TYPE: {
@@ -607,12 +506,15 @@
         }
 
         // Type chunks must be preceded by their TypeSpec chunks.
-        if (!types_builder || type->id - 1 != last_type_idx) {
-          LOG(ERROR) << "RES_TABLE_TYPE_TYPE found without preceding RES_TABLE_TYPE_SPEC_TYPE.";
+        std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type->id - 1];
+        if (builder_ptr != nullptr) {
+          builder_ptr->AddType(type);
+        } else {
+          LOG(ERROR) << StringPrintf(
+              "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.",
+              type->id);
           return {};
         }
-
-        types_builder->AddType(type);
       } break;
 
       case RES_TABLE_LIBRARY_TYPE: {
@@ -638,7 +540,7 @@
                                           arraysize(entry_iter->packageName), &package_name);
 
           if (dtohl(entry_iter->packageId) >= std::numeric_limits<uint8_t>::max()) {
-            LOG(ERROR) << base::StringPrintf(
+            LOG(ERROR) << StringPrintf(
                 "Package ID %02x in RES_TABLE_LIBRARY_TYPE too large for package '%s'.",
                 dtohl(entry_iter->packageId), package_name.c_str());
             return {};
@@ -651,14 +553,20 @@
       } break;
 
       default:
-        LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+        LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
         break;
     }
   }
 
-  // Finish the last TypeSpec.
-  if (types_builder) {
-    TypeSpecPtr type_spec_ptr = types_builder->Build();
+  if (iter.HadError()) {
+    LOG(ERROR) << iter.GetLastError();
+    return {};
+  }
+
+  // Flatten and construct the TypeSpecs.
+  for (auto& entry : type_builder_map) {
+    uint8_t type_idx = static_cast<uint8_t>(entry.first);
+    TypeSpecPtr type_spec_ptr = entry.second->Build();
     if (type_spec_ptr == nullptr) {
       LOG(ERROR) << "Too many type configurations, overflow detected.";
       return {};
@@ -669,43 +577,17 @@
     if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) {
       // If this is an overlay, insert it at the target type ID.
       if (type_spec_ptr->idmap_entries != nullptr) {
-        last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1;
+        type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1;
       }
-      loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr);
+      loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr);
     }
   }
 
-  if (iter.HadError()) {
-    LOG(ERROR) << iter.GetLastError();
-    return {};
-  }
   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) {
-  ATRACE_CALL();
   const ResTable_header* header = chunk.header<ResTable_header>();
   if (header == nullptr) {
     LOG(ERROR) << "RES_TABLE_TYPE too small.";
@@ -752,7 +634,7 @@
       } break;
 
       default:
-        LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+        LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
         break;
     }
   }
@@ -767,7 +649,7 @@
 std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data,
                                                    const LoadedIdmap* loaded_idmap, bool system,
                                                    bool load_as_shared_library) {
-  ATRACE_CALL();
+  ATRACE_NAME("LoadedArsc::LoadTable");
 
   // Not using make_unique because the constructor is private.
   std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
@@ -784,7 +666,7 @@
         break;
 
       default:
-        LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+        LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
         break;
     }
   }
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index b033137..ef08897 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -69,6 +69,8 @@
   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 {
@@ -127,7 +129,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);
+                                                      bool exclude_mipmap = false) const;
 
   // Returns all the locales for which there are resources defined. This includes resource
   // locales in all the ApkAssets set for this AssetManager.
@@ -136,24 +138,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);
+                                           bool merge_equivalent_languages = false) const;
 
   // 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);
+  std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode) const;
 
   // 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);
+                              Asset::AccessMode mode) const;
 
   // 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);
+  std::unique_ptr<AssetDir> OpenDir(const std::string& dirname) const;
 
   // Searches the set of APKs loaded by this AssetManager and opens the first one found.
   // `mode` controls how the file is opened.
@@ -161,24 +163,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);
+                                      ApkAssetsCookie* out_cookie = nullptr) const;
 
   // 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);
+                                      Asset::AccessMode mode) const;
 
   // 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);
+  bool GetResourceName(uint32_t resid, ResourceName* out_name) const;
 
   // 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);
+  bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const;
 
   // Finds the resource ID assigned to `resource_name`.
   // `resource_name` must be of the form '[package:][type/]entry'.
@@ -186,7 +188,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 std::string& fallback_package = {}) const;
 
   // 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`.
@@ -199,7 +201,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);
+                              uint32_t* out_flags) const;
 
   // Resolves the resource reference in `in_out_value` if the data type is
   // Res_value::TYPE_REFERENCE.
@@ -215,7 +217,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);
+                                   uint32_t* out_last_reference) const;
 
   // 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.
@@ -233,9 +235,9 @@
   std::unique_ptr<Theme> NewTheme();
 
   template <typename Func>
-  void ForEachPackage(Func func) {
+  void ForEachPackage(Func func) const {
     for (const PackageGroup& package_group : package_groups_) {
-      func(package_group.packages_.front()->GetPackageName(),
+      func(package_group.packages_.front().loaded_package_->GetPackageName(),
            package_group.dynamic_ref_table.mAssignedPackageId);
     }
   }
@@ -260,7 +262,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);
+                            FindEntryResult* out_entry) const;
 
   // Assigns package IDs to all shared library ApkAssets.
   // Should be called whenever the ApkAssets are changed.
@@ -270,13 +272,43 @@
   // 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 {
-    std::vector<const LoadedPackage*> packages_;
+    // 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<ApkAssetsCookie> cookies_;
+
+    // A library reference table that contains build-package ID to runtime-package ID mappings.
     DynamicRefTable dynamic_ref_table;
   };
 
@@ -350,7 +382,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);
+                                            uint32_t* out_last_ref = nullptr) const;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(Theme);
diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h
index f281921..03fad49 100644
--- a/libs/androidfw/include/androidfw/AttributeFinder.h
+++ b/libs/androidfw/include/androidfw/AttributeFinder.h
@@ -58,6 +58,7 @@
   BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end);
 
   Iterator Find(uint32_t attr);
+  inline Iterator end();
 
  private:
   void JumpToClosestAttribute(uint32_t package_id);
@@ -201,6 +202,11 @@
   return end_;
 }
 
+template <typename Derived, typename Iterator>
+Iterator BackTrackingAttributeFinder<Derived, Iterator>::end() {
+  return end_;
+}
+
 }  // namespace android
 
 #endif  // ANDROIDFW_ATTRIBUTE_FINDER_H
diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h
index 69b76041..35ef98d 100644
--- a/libs/androidfw/include/androidfw/AttributeResolution.h
+++ b/libs/androidfw/include/androidfw/AttributeResolution.h
@@ -17,7 +17,8 @@
 #ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H
 #define ANDROIDFW_ATTRIBUTERESOLUTION_H
 
-#include <androidfw/ResourceTypes.h>
+#include "androidfw/AssetManager2.h"
+#include "androidfw/ResourceTypes.h"
 
 namespace android {
 
@@ -42,19 +43,19 @@
 
 // `out_values` must NOT be nullptr.
 // `out_indices` may be nullptr.
-bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res,
+bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid,
                   uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
                   size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
 
 // `out_values` must NOT be nullptr.
 // `out_indices` is NOT optional and must NOT be nullptr.
-void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
-                uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length,
+void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
+                uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length,
                 uint32_t* out_values, uint32_t* out_indices);
 
 // `out_values` must NOT be nullptr.
 // `out_indices` may be nullptr.
-bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs,
+bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs,
                         size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
 
 }  // namespace android
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 965e2db..35ae5fc 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -41,32 +41,40 @@
   int package_id = 0;
 };
 
-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 = nullptr;
+// 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;
 
-  // The configuration for which the resulting entry was defined.
-  const ResTable_config* config = nullptr;
+  // 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;
 
-  // Stores the resulting bitmask of configuration axis with which the resource value varies.
-  uint32_t type_flags = 0u;
+  // 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 dynamic package ID map for the package from which this resource came from.
-  const DynamicRefTable* dynamic_ref_table = nullptr;
+  // Trick to easily access a variable number of Type structs
+  // proceeding this struct, and to ensure their alignment.
+  const ResTable_type* types[0];
 
-  // 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;
+  inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const {
+    if (entry_index >= dtohl(type_spec->entryCount)) {
+      return 0u;
+    }
 
-  // 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;
+    const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec + 1);
+    return flags[entry_index];
+  }
 };
 
-struct TypeSpec;
-class LoadedArsc;
+// 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>;
 
 class LoadedPackage {
  public:
@@ -76,9 +84,6 @@
 
   ~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.
@@ -86,6 +91,12 @@
   // 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_;
@@ -135,14 +146,32 @@
   // 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_;
@@ -152,7 +181,7 @@
   bool system_ = false;
   bool overlay_ = false;
 
-  ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_;
+  ByteBucketArray<TypeSpecPtr> type_specs_;
   std::vector<DynamicPackageEntry> dynamic_package_map_;
 };
 
@@ -180,25 +209,20 @@
     return &global_string_pool_;
   }
 
-  // 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;
-
-  // 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_;
-  }
+  // 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;
 
   // 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_;
   }
 
+  // Returns true if this is a system provided resource.
+  inline bool IsSystem() const {
+    return system_;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
 
diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h
new file mode 100644
index 0000000..64924f4
--- /dev/null
+++ b/libs/androidfw/include/androidfw/MutexGuard.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROIDFW_MUTEXGUARD_H
+#define ANDROIDFW_MUTEXGUARD_H
+
+#include <mutex>
+#include <type_traits>
+
+#include "android-base/macros.h"
+
+namespace android {
+
+template <typename T>
+class ScopedLock;
+
+// Owns the guarded object and protects access to it via a mutex.
+// The guarded object is inaccessible via this class.
+// The mutex is locked and the object accessed via the ScopedLock<T> class.
+//
+// NOTE: The template parameter T should not be a raw pointer, since ownership
+// is ambiguous and error-prone. Instead use an std::unique_ptr<>.
+//
+// Example use:
+//
+//   Guarded<std::string> shared_string("hello");
+//   {
+//     ScopedLock<std::string> locked_string(shared_string);
+//     *locked_string += " world";
+//   }
+//
+template <typename T>
+class Guarded {
+  static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer");
+
+ public:
+  explicit Guarded() : guarded_() {
+  }
+
+  template <typename U = T>
+  explicit Guarded(const T& guarded,
+                   typename std::enable_if<std::is_copy_constructible<U>::value>::type = void())
+      : guarded_(guarded) {
+  }
+
+  template <typename U = T>
+  explicit Guarded(T&& guarded,
+                   typename std::enable_if<std::is_move_constructible<U>::value>::type = void())
+      : guarded_(std::move(guarded)) {
+  }
+
+ private:
+  friend class ScopedLock<T>;
+
+  DISALLOW_COPY_AND_ASSIGN(Guarded);
+
+  std::mutex lock_;
+  T guarded_;
+};
+
+template <typename T>
+class ScopedLock {
+ public:
+  explicit ScopedLock(Guarded<T>& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) {
+  }
+
+  T& operator*() {
+    return guarded_;
+  }
+
+  T* operator->() {
+    return &guarded_;
+  }
+
+  T* get() {
+    return &guarded_;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ScopedLock);
+
+  std::lock_guard<std::mutex> lock_;
+  T& guarded_;
+};
+
+}  // namespace android
+
+#endif  // ANDROIDFW_MUTEXGUARD_H
diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h
index c2eae85..d94779b 100644
--- a/libs/androidfw/include/androidfw/ResourceUtils.h
+++ b/libs/androidfw/include/androidfw/ResourceUtils.h
@@ -28,7 +28,7 @@
                          StringPiece* out_entry);
 
 inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) {
-  return resid | (static_cast<uint32_t>(package_id) << 24);
+  return (resid & 0x00ffffffu) | (static_cast<uint32_t>(package_id) << 24);
 }
 
 inline uint8_t get_package_id(uint32_t resid) {
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
index 6c43a67..e2b9f00 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -26,58 +26,56 @@
 
 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_NE(nullptr, loaded_apk);
+  ASSERT_THAT(loaded_apk, NotNull());
 
   const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
-  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);
+  ASSERT_THAT(loaded_arsc, NotNull());
+  ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull());
+  ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull());
 }
 
 TEST(ApkAssetsTest, LoadApkFromFd) {
   const std::string path = GetTestDataPath() + "/basic/basic.apk";
   unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY));
-  ASSERT_GE(fd.get(), 0);
+  ASSERT_THAT(fd.get(), Ge(0));
 
   std::unique_ptr<const ApkAssets> loaded_apk =
       ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/);
-  ASSERT_NE(nullptr, loaded_apk);
+  ASSERT_THAT(loaded_apk, NotNull());
 
   const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
-  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);
+  ASSERT_THAT(loaded_arsc, NotNull());
+  ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull());
+  ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull());
 }
 
 TEST(ApkAssetsTest, LoadApkAsSharedLibrary) {
   std::unique_ptr<const ApkAssets> loaded_apk =
       ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk");
-  ASSERT_NE(nullptr, loaded_apk);
+  ASSERT_THAT(loaded_apk, NotNull());
+
   const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
-  ASSERT_NE(nullptr, loaded_arsc);
-  ASSERT_EQ(1u, loaded_arsc->GetPackages().size());
+  ASSERT_THAT(loaded_arsc, NotNull());
+  ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u));
   EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic());
 
   loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk");
-  ASSERT_NE(nullptr, loaded_apk);
+  ASSERT_THAT(loaded_apk, NotNull());
 
   loaded_arsc = loaded_apk->GetLoadedArsc();
-  ASSERT_NE(nullptr, loaded_arsc);
-  ASSERT_EQ(1u, loaded_arsc->GetPackages().size());
+  ASSERT_THAT(loaded_arsc, NotNull());
+  ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u));
   EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic());
 }
 
@@ -86,19 +84,22 @@
   ResTable target_table;
   const std::string target_path = GetTestDataPath() + "/basic/basic.apk";
   ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents));
-  ASSERT_EQ(NO_ERROR, target_table.add(contents.data(), contents.size(), 0, true /*copyData*/));
+  ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/),
+              Eq(NO_ERROR));
 
   ResTable overlay_table;
   const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk";
   ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents));
-  ASSERT_EQ(NO_ERROR, overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/));
+  ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/),
+              Eq(NO_ERROR));
 
   util::unique_cptr<void> idmap_data;
   void* temp_data;
   size_t idmap_len;
 
-  ASSERT_EQ(NO_ERROR, target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(),
-                                               overlay_path.c_str(), &temp_data, &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));
   idmap_data.reset(temp_data);
 
   TemporaryFile tf;
@@ -108,37 +109,30 @@
   // Open something so that the destructor of TemporaryFile closes a valid fd.
   tf.fd = open("/dev/null", O_WRONLY);
 
-  std::unique_ptr<const ApkAssets> loaded_overlay_apk = ApkAssets::LoadOverlay(tf.path);
-  ASSERT_NE(nullptr, loaded_overlay_apk);
+  ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull());
 }
 
 TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) {
   std::unique_ptr<const ApkAssets> loaded_apk =
       ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
-  ASSERT_NE(nullptr, loaded_apk);
+  ASSERT_THAT(loaded_apk, 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);
-  }
+  { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); }
 }
 
 TEST(ApkAssetsTest, OpenUncompressedAssetFd) {
   std::unique_ptr<const ApkAssets> loaded_apk =
       ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
-  ASSERT_NE(nullptr, loaded_apk);
+  ASSERT_THAT(loaded_apk, NotNull());
 
   auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN);
-  ASSERT_NE(nullptr, asset);
+  ASSERT_THAT(asset, NotNull());
 
   off64_t start, length;
   unique_fd fd(asset->openFileDescriptor(&start, &length));
-  EXPECT_GE(fd.get(), 0);
+  ASSERT_THAT(fd.get(), Ge(0));
 
   lseek64(fd.get(), start, SEEK_SET);
 
@@ -146,7 +140,7 @@
   buffer.resize(length);
   ASSERT_TRUE(base::ReadFully(fd.get(), &*buffer.begin(), length));
 
-  EXPECT_EQ("This should be uncompressed.\n\n", buffer);
+  EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n"));
 }
 
 }  // namespace android
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
index 85e8f25..437e147 100644
--- a/libs/androidfw/tests/AssetManager2_bench.cpp
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -81,17 +81,18 @@
 }
 BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld);
 
-static void BM_AssetManagerGetResource(benchmark::State& state) {
-  GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/,
-                       basic::R::integer::number1, state);
+static void BM_AssetManagerGetResource(benchmark::State& state, uint32_t resid) {
+  GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid, state);
 }
-BENCHMARK(BM_AssetManagerGetResource);
+BENCHMARK_CAPTURE(BM_AssetManagerGetResource, number1, basic::R::integer::number1);
+BENCHMARK_CAPTURE(BM_AssetManagerGetResource, deep_ref, basic::R::integer::deep_ref);
 
-static void BM_AssetManagerGetResourceOld(benchmark::State& state) {
-  GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/,
-                          basic::R::integer::number1, state);
+static void BM_AssetManagerGetResourceOld(benchmark::State& state, uint32_t resid) {
+  GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid,
+                          state);
 }
-BENCHMARK(BM_AssetManagerGetResourceOld);
+BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, number1, basic::R::integer::number1);
+BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, deep_ref, basic::R::integer::deep_ref);
 
 static void BM_AssetManagerGetLibraryResource(benchmark::State& state) {
   GetResourceBenchmark(
@@ -196,7 +197,7 @@
 static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) {
   AssetManager assets;
   if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/,
-                           false /*isSystemAssets*/)) {
+                           true /*isSystemAssets*/)) {
     state.SkipWithError("Failed to load assets");
     return;
   }
@@ -211,4 +212,44 @@
 }
 BENCHMARK(BM_AssetManagerGetResourceLocalesOld);
 
+static void BM_AssetManagerSetConfigurationFramework(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()});
+
+  ResTable_config config;
+  memset(&config, 0, sizeof(config));
+
+  while (state.KeepRunning()) {
+    config.sdkVersion = ~config.sdkVersion;
+    assets.SetConfiguration(config);
+  }
+}
+BENCHMARK(BM_AssetManagerSetConfigurationFramework);
+
+static void BM_AssetManagerSetConfigurationFrameworkOld(benchmark::State& state) {
+  AssetManager assets;
+  if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/,
+                           true /*isSystemAssets*/)) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  const ResTable& table = assets.getResources(true);
+
+  ResTable_config config;
+  memset(&config, 0, sizeof(config));
+
+  while (state.KeepRunning()) {
+    config.sdkVersion = ~config.sdkVersion;
+    assets.setConfiguration(config);
+  }
+}
+BENCHMARK(BM_AssetManagerSetConfigurationFrameworkOld);
+
 }  // namespace android
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 92462a6..7cac2b3 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -36,6 +36,10 @@
 namespace lib_two = com::android::lib_two;
 namespace libclient = com::android::libclient;
 
+using ::testing::Eq;
+using ::testing::NotNull;
+using ::testing::StrEq;
+
 namespace android {
 
 class AssetManager2Test : public ::testing::Test {
@@ -59,11 +63,14 @@
     libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk");
     ASSERT_NE(nullptr, libclient_assets_);
 
-    appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk");
+    appaslib_assets_ = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk");
     ASSERT_NE(nullptr, appaslib_assets_);
 
     system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/);
     ASSERT_NE(nullptr, system_assets_);
+
+    app_assets_ = ApkAssets::Load(GetTestDataPath() + "/app/app.apk");
+    ASSERT_THAT(app_assets_, NotNull());
   }
 
  protected:
@@ -75,6 +82,7 @@
   std::unique_ptr<const ApkAssets> libclient_assets_;
   std::unique_ptr<const ApkAssets> appaslib_assets_;
   std::unique_ptr<const ApkAssets> system_assets_;
+  std::unique_ptr<const ApkAssets> app_assets_;
 };
 
 TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) {
@@ -233,6 +241,25 @@
   assetmanager.SetApkAssets(
       {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
 
+  const ResolvedBag* bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03));
+  ASSERT_NE(nullptr, bag);
+  ASSERT_GE(bag->entry_count, 2u);
+
+  // First two attributes come from lib_one.
+  EXPECT_EQ(1, bag->entries[0].cookie);
+  EXPECT_EQ(0x03, get_package_id(bag->entries[0].key));
+  EXPECT_EQ(1, bag->entries[1].cookie);
+  EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
+}
+
+TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) {
+  AssetManager2 assetmanager;
+
+  // libclient is built with lib_one and then lib_two in order.
+  // Reverse the order to test that proper package ID re-assignment is happening.
+  assetmanager.SetApkAssets(
+      {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
+
   const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::Theme);
   ASSERT_NE(nullptr, bag);
   ASSERT_GE(bag->entry_count, 2u);
@@ -446,8 +473,68 @@
             assetmanager.GetResourceId("main", "layout", "com.android.basic"));
 }
 
-TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {}
+TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({system_assets_.get()});
 
-TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {}
+  std::unique_ptr<Asset> asset = assetmanager.Open("file.txt", Asset::ACCESS_BUFFER);
+  ASSERT_THAT(asset, NotNull());
+
+  const char* data = reinterpret_cast<const char*>(asset->getBuffer(false /*wordAligned*/));
+  ASSERT_THAT(data, NotNull());
+  EXPECT_THAT(std::string(data, asset->getLength()), StrEq("file\n"));
+}
+
+TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({system_assets_.get(), app_assets_.get()});
+
+  std::unique_ptr<Asset> asset = assetmanager.Open("file.txt", Asset::ACCESS_BUFFER);
+  ASSERT_THAT(asset, NotNull());
+
+  const char* data = reinterpret_cast<const char*>(asset->getBuffer(false /*wordAligned*/));
+  ASSERT_THAT(data, NotNull());
+  EXPECT_THAT(std::string(data, asset->getLength()), StrEq("app override file\n"));
+}
+
+TEST_F(AssetManager2Test, OpenDir) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({system_assets_.get()});
+
+  std::unique_ptr<AssetDir> asset_dir = assetmanager.OpenDir("");
+  ASSERT_THAT(asset_dir, NotNull());
+  ASSERT_THAT(asset_dir->getFileCount(), Eq(2u));
+
+  EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("file.txt")));
+  EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular));
+
+  EXPECT_THAT(asset_dir->getFileName(1), Eq(String8("subdir")));
+  EXPECT_THAT(asset_dir->getFileType(1), Eq(FileType::kFileTypeDirectory));
+
+  asset_dir = assetmanager.OpenDir("subdir");
+  ASSERT_THAT(asset_dir, NotNull());
+  ASSERT_THAT(asset_dir->getFileCount(), Eq(1u));
+
+  EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("subdir_file.txt")));
+  EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular));
+}
+
+TEST_F(AssetManager2Test, OpenDirFromManyApks) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({system_assets_.get(), app_assets_.get()});
+
+  std::unique_ptr<AssetDir> asset_dir = assetmanager.OpenDir("");
+  ASSERT_THAT(asset_dir, NotNull());
+  ASSERT_THAT(asset_dir->getFileCount(), Eq(3u));
+
+  EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("app_file.txt")));
+  EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular));
+
+  EXPECT_THAT(asset_dir->getFileName(1), Eq(String8("file.txt")));
+  EXPECT_THAT(asset_dir->getFileType(1), Eq(FileType::kFileTypeRegular));
+
+  EXPECT_THAT(asset_dir->getFileName(2), Eq(String8("subdir")));
+  EXPECT_THAT(asset_dir->getFileType(2), Eq(FileType::kFileTypeDirectory));
+}
 
 }  // namespace android
diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp
new file mode 100644
index 0000000..fa300c5
--- /dev/null
+++ b/libs/androidfw/tests/AttributeResolution_bench.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "benchmark/benchmark.h"
+
+//#include "android-base/stringprintf.h"
+#include "androidfw/ApkAssets.h"
+#include "androidfw/AssetManager.h"
+#include "androidfw/AssetManager2.h"
+#include "androidfw/AttributeResolution.h"
+#include "androidfw/ResourceTypes.h"
+
+#include "BenchmarkHelpers.h"
+#include "data/basic/R.h"
+#include "data/styles/R.h"
+
+namespace app = com::android::app;
+namespace basic = com::android::basic;
+
+namespace android {
+
+constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk";
+constexpr const static uint32_t Theme_Material_Light = 0x01030237u;
+
+static void BM_ApplyStyle(benchmark::State& state) {
+  std::unique_ptr<const ApkAssets> styles_apk =
+      ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
+  if (styles_apk == nullptr) {
+    state.SkipWithError("failed to load assets");
+    return;
+  }
+
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({styles_apk.get()});
+
+  std::unique_ptr<Asset> asset =
+      assetmanager.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER);
+  if (asset == nullptr) {
+    state.SkipWithError("failed to load layout");
+    return;
+  }
+
+  ResXMLTree xml_tree;
+  if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) {
+    state.SkipWithError("corrupt xml layout");
+    return;
+  }
+
+  // Skip to the first tag.
+  while (xml_tree.next() != ResXMLParser::START_TAG) {
+  }
+
+  std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+  theme->ApplyStyle(app::R::style::StyleTwo);
+
+  std::array<uint32_t, 6> attrs{{app::R::attr::attr_one, app::R::attr::attr_two,
+                                 app::R::attr::attr_three, app::R::attr::attr_four,
+                                 app::R::attr::attr_five, app::R::attr::attr_empty}};
+  std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
+  std::array<uint32_t, attrs.size() + 1> indices;
+
+  while (state.KeepRunning()) {
+    ApplyStyle(theme.get(), &xml_tree, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(),
+               attrs.size(), values.data(), indices.data());
+  }
+}
+BENCHMARK(BM_ApplyStyle);
+
+static void BM_ApplyStyleFramework(benchmark::State& state) {
+  std::unique_ptr<const ApkAssets> framework_apk = ApkAssets::Load(kFrameworkPath);
+  if (framework_apk == nullptr) {
+    state.SkipWithError("failed to load framework assets");
+    return;
+  }
+
+  std::unique_ptr<const ApkAssets> basic_apk =
+      ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
+  if (basic_apk == nullptr) {
+    state.SkipWithError("failed to load assets");
+    return;
+  }
+
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({framework_apk.get(), basic_apk.get()});
+
+  ResTable_config device_config;
+  memset(&device_config, 0, sizeof(device_config));
+  device_config.language[0] = 'e';
+  device_config.language[1] = 'n';
+  device_config.country[0] = 'U';
+  device_config.country[1] = 'S';
+  device_config.orientation = ResTable_config::ORIENTATION_PORT;
+  device_config.smallestScreenWidthDp = 700;
+  device_config.screenWidthDp = 700;
+  device_config.screenHeightDp = 1024;
+  device_config.sdkVersion = 27;
+
+  Res_value value;
+  ResTable_config config;
+  uint32_t flags = 0u;
+  ApkAssetsCookie cookie =
+      assetmanager.GetResource(basic::R::layout::layoutt, false /*may_be_bag*/,
+                               0u /*density_override*/, &value, &config, &flags);
+  if (cookie == kInvalidCookie) {
+    state.SkipWithError("failed to find R.layout.layout");
+    return;
+  }
+
+  size_t len = 0u;
+  const char* layout_path =
+      assetmanager.GetStringPoolForCookie(cookie)->string8At(value.data, &len);
+  if (layout_path == nullptr || len == 0u) {
+    state.SkipWithError("failed to lookup layout path");
+    return;
+  }
+
+  std::unique_ptr<Asset> asset = assetmanager.OpenNonAsset(
+      StringPiece(layout_path, len).to_string(), cookie, Asset::ACCESS_BUFFER);
+  if (asset == nullptr) {
+    state.SkipWithError("failed to load layout");
+    return;
+  }
+
+  ResXMLTree xml_tree;
+  if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) {
+    state.SkipWithError("corrupt xml layout");
+    return;
+  }
+
+  // Skip to the first tag.
+  while (xml_tree.next() != ResXMLParser::START_TAG) {
+  }
+
+  std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+  theme->ApplyStyle(Theme_Material_Light);
+
+  std::array<uint32_t, 92> attrs{
+      {0x0101000e, 0x01010034, 0x01010095, 0x01010096, 0x01010097, 0x01010098, 0x01010099,
+       0x0101009a, 0x0101009b, 0x010100ab, 0x010100af, 0x010100b0, 0x010100b1, 0x0101011f,
+       0x01010120, 0x0101013f, 0x01010140, 0x0101014e, 0x0101014f, 0x01010150, 0x01010151,
+       0x01010152, 0x01010153, 0x01010154, 0x01010155, 0x01010156, 0x01010157, 0x01010158,
+       0x01010159, 0x0101015a, 0x0101015b, 0x0101015c, 0x0101015d, 0x0101015e, 0x0101015f,
+       0x01010160, 0x01010161, 0x01010162, 0x01010163, 0x01010164, 0x01010165, 0x01010166,
+       0x01010167, 0x01010168, 0x01010169, 0x0101016a, 0x0101016b, 0x0101016c, 0x0101016d,
+       0x0101016e, 0x0101016f, 0x01010170, 0x01010171, 0x01010217, 0x01010218, 0x0101021d,
+       0x01010220, 0x01010223, 0x01010224, 0x01010264, 0x01010265, 0x01010266, 0x010102c5,
+       0x010102c6, 0x010102c7, 0x01010314, 0x01010315, 0x01010316, 0x0101035e, 0x0101035f,
+       0x01010362, 0x01010374, 0x0101038c, 0x01010392, 0x01010393, 0x010103ac, 0x0101045d,
+       0x010104b6, 0x010104b7, 0x010104d6, 0x010104d7, 0x010104dd, 0x010104de, 0x010104df,
+       0x01010535, 0x01010536, 0x01010537, 0x01010538, 0x01010546, 0x01010567, 0x011100c9,
+       0x011100ca}};
+
+  std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
+  std::array<uint32_t, attrs.size() + 1> indices;
+  while (state.KeepRunning()) {
+    ApplyStyle(theme.get(), &xml_tree, 0x01010084u /*def_style_attr*/, 0u /*def_style_res*/,
+               attrs.data(), attrs.size(), values.data(), indices.data());
+  }
+}
+BENCHMARK(BM_ApplyStyleFramework);
+
+}  // namespace android
diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp
index 2d73ce8..c8dbe20 100644
--- a/libs/androidfw/tests/AttributeResolution_test.cpp
+++ b/libs/androidfw/tests/AttributeResolution_test.cpp
@@ -21,6 +21,8 @@
 #include "android-base/file.h"
 #include "android-base/logging.h"
 #include "android-base/macros.h"
+#include "androidfw/AssetManager2.h"
+#include "androidfw/ResourceUtils.h"
 
 #include "TestHelpers.h"
 #include "data/styles/R.h"
@@ -32,15 +34,14 @@
 class AttributeResolutionTest : public ::testing::Test {
  public:
   virtual void SetUp() override {
-    std::string contents;
-    ASSERT_TRUE(ReadFileFromZipToString(
-        GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents));
-    ASSERT_EQ(NO_ERROR, table_.add(contents.data(), contents.size(),
-                                   1 /*cookie*/, true /*copyData*/));
+    styles_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
+    ASSERT_NE(nullptr, styles_assets_);
+    assetmanager_.SetApkAssets({styles_assets_.get()});
   }
 
  protected:
-  ResTable table_;
+  std::unique_ptr<const ApkAssets> styles_assets_;
+  AssetManager2 assetmanager_;
 };
 
 class AttributeResolutionXmlTest : public AttributeResolutionTest {
@@ -48,13 +49,12 @@
   virtual void SetUp() override {
     AttributeResolutionTest::SetUp();
 
-    std::string contents;
-    ASSERT_TRUE(
-        ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk",
-                                "res/layout/layout.xml", &contents));
+    std::unique_ptr<Asset> asset =
+        assetmanager_.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER);
+    ASSERT_NE(nullptr, asset);
 
-    ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(),
-                                          true /*copyData*/));
+    ASSERT_EQ(NO_ERROR,
+              xml_parser_.setTo(asset->getBuffer(true), asset->getLength(), true /*copyData*/));
 
     // Skip to the first tag.
     while (xml_parser_.next() != ResXMLParser::START_TAG) {
@@ -65,15 +65,50 @@
   ResXMLTree xml_parser_;
 };
 
+TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) {
+  AssetManager2 assetmanager;
+  auto apk_assets = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/styles/styles.apk");
+  ASSERT_NE(nullptr, apk_assets);
+  assetmanager.SetApkAssets({apk_assets.get()});
+
+  std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+
+  std::array<uint32_t, 2> attrs{
+      {fix_package_id(R::attr::attr_one, 0x02), fix_package_id(R::attr::attr_two, 0x02)}};
+  std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
+  std::array<uint32_t, attrs.size() + 1> indices;
+  ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/,
+             fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(), values.data(),
+             indices.data());
+
+  const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
+
+  const uint32_t* values_cursor = values.data();
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
+  EXPECT_EQ(1u, values_cursor[STYLE_DATA]);
+  EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+  EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+  EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+  EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+  values_cursor += STYLE_NUM_ENTRIES;
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
+  EXPECT_EQ(2u, values_cursor[STYLE_DATA]);
+  EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+  EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+  EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+  EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+}
+
 TEST_F(AttributeResolutionTest, Theme) {
-  ResTable::Theme theme(table_);
-  ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
+  std::unique_ptr<Theme> theme = assetmanager_.NewTheme();
+  ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo));
 
   std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
                                  R::attr::attr_four, R::attr::attr_empty}};
   std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
 
-  ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/,
+  ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/,
                            nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(),
                            attrs.size(), values.data(), nullptr /*out_indices*/));
 
@@ -126,8 +161,8 @@
                                  R::attr::attr_four, R::attr::attr_empty}};
   std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
 
-  ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(),
-                                 nullptr /*out_indices*/));
+  ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(),
+                                 values.data(), nullptr /*out_indices*/));
 
   uint32_t* values_cursor = values.data();
   EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
@@ -171,15 +206,15 @@
 }
 
 TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) {
-  ResTable::Theme theme(table_);
-  ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
+  std::unique_ptr<Theme> theme = assetmanager_.NewTheme();
+  ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo));
 
   std::array<uint32_t, 6> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
                                  R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}};
   std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
   std::array<uint32_t, attrs.size() + 1> indices;
 
-  ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(),
+  ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(),
              attrs.size(), values.data(), indices.data());
 
   const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp
index 7149bee..faddfe5 100644
--- a/libs/androidfw/tests/BenchmarkHelpers.cpp
+++ b/libs/androidfw/tests/BenchmarkHelpers.cpp
@@ -33,19 +33,21 @@
     }
   }
 
+  // Make sure to force creation of the ResTable first, or else the configuration doesn't get set.
+  const ResTable& table = assetmanager.getResources(true);
   if (config != nullptr) {
     assetmanager.setConfiguration(*config);
   }
 
-  const ResTable& table = assetmanager.getResources(true);
-
   Res_value value;
   ResTable_config selected_config;
   uint32_t flags;
+  uint32_t last_ref = 0u;
 
   while (state.KeepRunning()) {
-    table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags,
-                      &selected_config);
+    ssize_t block = table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags,
+                                      &selected_config);
+    table.resolveReference(&value, block, &last_ref, &flags, &selected_config);
   }
 }
 
@@ -72,10 +74,12 @@
   Res_value value;
   ResTable_config selected_config;
   uint32_t flags;
+  uint32_t last_id = 0u;
 
   while (state.KeepRunning()) {
-    assetmanager.GetResource(resid, false /* may_be_bag */, 0u /* density_override */, &value,
-                             &selected_config, &flags);
+    ApkAssetsCookie cookie = assetmanager.GetResource(
+        resid, false /* may_be_bag */, 0u /* density_override */, &value, &selected_config, &flags);
+    assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_id);
   }
 }
 
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 37ddafb..cae632d 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -16,6 +16,9 @@
 
 #include "androidfw/LoadedArsc.h"
 
+#include "android-base/file.h"
+#include "androidfw/ResourceUtils.h"
+
 #include "TestHelpers.h"
 #include "data/basic/R.h"
 #include "data/libclient/R.h"
@@ -27,6 +30,14 @@
 namespace libclient = com::android::libclient;
 namespace sparse = com::android::sparse;
 
+using ::android::base::ReadFileToString;
+using ::testing::Eq;
+using ::testing::Ge;
+using ::testing::IsNull;
+using ::testing::NotNull;
+using ::testing::SizeIs;
+using ::testing::StrEq;
+
 namespace android {
 
 TEST(LoadedArscTest, LoadSinglePackageArsc) {
@@ -35,39 +46,24 @@
                                       &contents));
 
   std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
-  ASSERT_NE(nullptr, loaded_arsc);
+  ASSERT_THAT(loaded_arsc, NotNull());
 
-  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 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));
 
-  ResTable_config config;
-  memset(&config, 0, sizeof(config));
-  config.sdkVersion = 24;
+  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);
 
-  FindEntryResult entry;
+  const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
+  ASSERT_THAT(type_spec, NotNull());
+  ASSERT_THAT(type_spec->type_count, Ge(1u));
 
-  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);
+  const ResTable_type* type = type_spec->types[0];
+  ASSERT_THAT(type, NotNull());
+  ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
 }
 
 TEST(LoadedArscTest, LoadSparseEntryApp) {
@@ -76,15 +72,22 @@
                                       &contents));
 
   std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
-  ASSERT_NE(nullptr, loaded_arsc);
+  ASSERT_THAT(loaded_arsc, NotNull());
 
-  ResTable_config config;
-  memset(&config, 0, sizeof(config));
-  config.sdkVersion = 26;
+  const LoadedPackage* package =
+      loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9));
+  ASSERT_THAT(package, NotNull());
 
-  FindEntryResult entry;
-  ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry));
-  ASSERT_NE(nullptr, entry.entry);
+  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());
 }
 
 TEST(LoadedArscTest, LoadSharedLibrary) {
@@ -93,14 +96,13 @@
                                       &contents));
 
   std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
-  ASSERT_NE(nullptr, loaded_arsc);
+  ASSERT_THAT(loaded_arsc, NotNull());
 
   const auto& packages = loaded_arsc->GetPackages();
-  ASSERT_EQ(1u, packages.size());
-
+  ASSERT_THAT(packages, SizeIs(1u));
   EXPECT_TRUE(packages[0]->IsDynamic());
-  EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName());
-  EXPECT_EQ(0, packages[0]->GetPackageId());
+  EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.lib_one"));
+  EXPECT_THAT(packages[0]->GetPackageId(), Eq(0));
 
   const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap();
 
@@ -114,25 +116,23 @@
                                       "resources.arsc", &contents));
 
   std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
-  ASSERT_NE(nullptr, loaded_arsc);
+  ASSERT_THAT(loaded_arsc, NotNull());
 
   const auto& packages = loaded_arsc->GetPackages();
-  ASSERT_EQ(1u, packages.size());
-
+  ASSERT_THAT(packages, SizeIs(1u));
   EXPECT_FALSE(packages[0]->IsDynamic());
-  EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName());
-  EXPECT_EQ(0x7f, packages[0]->GetPackageId());
+  EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.libclient"));
+  EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f));
 
   const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap();
 
   // The library has two dependencies.
-  ASSERT_EQ(2u, dynamic_pkg_map.size());
+  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));
 
-  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);
+  EXPECT_THAT(dynamic_pkg_map[1].package_name, StrEq("com.android.lib_two"));
+  EXPECT_THAT(dynamic_pkg_map[1].package_id, Eq(0x03));
 }
 
 TEST(LoadedArscTest, LoadAppAsSharedLibrary) {
@@ -143,13 +143,12 @@
   std::unique_ptr<const LoadedArsc> loaded_arsc =
       LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/,
                        true /*load_as_shared_library*/);
-  ASSERT_NE(nullptr, loaded_arsc);
+  ASSERT_THAT(loaded_arsc, NotNull());
 
   const auto& packages = loaded_arsc->GetPackages();
-  ASSERT_EQ(1u, packages.size());
-
+  ASSERT_THAT(packages, SizeIs(1u));
   EXPECT_TRUE(packages[0]->IsDynamic());
-  EXPECT_EQ(0x7f, packages[0]->GetPackageId());
+  EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f));
 }
 
 TEST(LoadedArscTest, LoadFeatureSplit) {
@@ -157,21 +156,67 @@
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc",
                                       &contents));
   std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
-  ASSERT_NE(nullptr, loaded_arsc);
+  ASSERT_THAT(loaded_arsc, NotNull());
 
-  ResTable_config desired_config;
-  memset(&desired_config, 0, sizeof(desired_config));
+  const LoadedPackage* package =
+      loaded_arsc->GetPackageById(get_package_id(basic::R::string::test3));
+  ASSERT_THAT(package, NotNull());
 
-  FindEntryResult entry;
-  ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry));
+  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());
 
   size_t len;
-  const char16_t* type_name16 = entry.type_string_ref.string16(&len);
-  ASSERT_NE(nullptr, type_name16);
-  ASSERT_NE(0u, 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"));
 
-  std::string type_name = util::Utf16ToUtf8(StringPiece16(type_name16, len));
-  EXPECT_EQ(std::string("string"), type_name);
+  ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull());
+}
+
+// AAPT(2) generates resource tables with chunks in a certain order. The rule is that
+// a RES_TABLE_TYPE_TYPE with id `i` must always be preceded by a RES_TABLE_TYPE_SPEC_TYPE with
+// id `i`. The RES_TABLE_TYPE_SPEC_TYPE does not need to be directly preceding, however.
+//
+// AAPT(2) generates something like:
+//   RES_TABLE_TYPE_SPEC_TYPE id=1
+//   RES_TABLE_TYPE_TYPE id=1
+//   RES_TABLE_TYPE_SPEC_TYPE id=2
+//   RES_TABLE_TYPE_TYPE id=2
+//
+// But the following is valid too:
+//   RES_TABLE_TYPE_SPEC_TYPE id=1
+//   RES_TABLE_TYPE_SPEC_TYPE id=2
+//   RES_TABLE_TYPE_TYPE id=1
+//   RES_TABLE_TYPE_TYPE id=2
+//
+TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) {
+  std::string contents;
+  ASSERT_TRUE(
+      ReadFileFromZipToString(GetTestDataPath() + "/out_of_order_types/out_of_order_types.apk",
+                              "resources.arsc", &contents));
+
+  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+  ASSERT_THAT(loaded_arsc, NotNull());
+
+  ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u));
+  const auto& package = loaded_arsc->GetPackages()[0];
+  ASSERT_THAT(package, NotNull());
+
+  const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0);
+  ASSERT_THAT(type_spec, NotNull());
+  ASSERT_THAT(type_spec->type_count, Ge(1u));
+  ASSERT_THAT(type_spec->types[0], NotNull());
+
+  type_spec = package->GetTypeSpecByTypeIndex(1);
+  ASSERT_THAT(type_spec, NotNull());
+  ASSERT_THAT(type_spec->type_count, Ge(1u));
+  ASSERT_THAT(type_spec->types[0], NotNull());
 }
 
 class MockLoadedIdmap : public LoadedIdmap {
@@ -199,23 +244,33 @@
 };
 
 TEST(LoadedArscTest, LoadOverlay) {
-  std::string contents, overlay_contents;
-  ASSERT_TRUE(
-      ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents));
+  std::string contents;
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc",
-                                      &overlay_contents));
+                                      &contents));
 
   MockLoadedIdmap loaded_idmap;
 
   std::unique_ptr<const LoadedArsc> loaded_arsc =
-      LoadedArsc::Load(StringPiece(overlay_contents), &loaded_idmap);
-  ASSERT_NE(nullptr, loaded_arsc);
+      LoadedArsc::Load(StringPiece(contents), &loaded_idmap);
+  ASSERT_THAT(loaded_arsc, NotNull());
 
-  ResTable_config desired_config;
-  memset(&desired_config, 0, sizeof(desired_config));
+  const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u);
+  ASSERT_THAT(package, NotNull());
 
-  FindEntryResult entry;
-  ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry));
+  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());
 }
 
 // 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 43a9955..df0c642 100644
--- a/libs/androidfw/tests/TestHelpers.h
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -20,6 +20,7 @@
 #include <string>
 
 #include "androidfw/ResourceTypes.h"
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
 #include "CommonHelpers.h"
diff --git a/libs/androidfw/tests/data/app/app.apk b/libs/androidfw/tests/data/app/app.apk
index ccb0824..c8ad86d 100644
--- a/libs/androidfw/tests/data/app/app.apk
+++ b/libs/androidfw/tests/data/app/app.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/app/assets/app_file.txt b/libs/androidfw/tests/data/app/assets/app_file.txt
new file mode 100644
index 0000000..b214e06
--- /dev/null
+++ b/libs/androidfw/tests/data/app/assets/app_file.txt
@@ -0,0 +1 @@
+app file
diff --git a/libs/androidfw/tests/data/app/assets/file.txt b/libs/androidfw/tests/data/app/assets/file.txt
new file mode 100644
index 0000000..0811542
--- /dev/null
+++ b/libs/androidfw/tests/data/app/assets/file.txt
@@ -0,0 +1 @@
+app override file
diff --git a/libs/androidfw/tests/data/app/build b/libs/androidfw/tests/data/app/build
index d418158..09af842 100755
--- a/libs/androidfw/tests/data/app/build
+++ b/libs/androidfw/tests/data/app/build
@@ -17,4 +17,11 @@
 
 set -e
 
-aapt package -I ../system/system.apk -M AndroidManifest.xml -S res -F app.apk -f
+aapt2 compile --dir res -o compiled.flata
+aapt2 link \
+    --manifest AndroidManifest.xml \
+    -I ../system/system.apk \
+    -A assets \
+    -o app.apk \
+    compiled.flata
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h
index 94a2a14..b7e814f 100644
--- a/libs/androidfw/tests/data/basic/R.h
+++ b/libs/androidfw/tests/data/basic/R.h
@@ -34,6 +34,7 @@
   struct layout {
     enum : uint32_t {
       main = 0x7f020000,
+      layoutt = 0x7f020001,
     };
   };
 
@@ -55,6 +56,7 @@
       number2 = 0x7f040001,
       ref1 = 0x7f040002,
       ref2 = 0x7f040003,
+      deep_ref = 0x7f040004,
 
       // From feature
       number3 = 0x80030000,
diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk
index 18ef75e..1733b6a 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/res/layout/layout.xml b/libs/androidfw/tests/data/basic/res/layout/layout.xml
new file mode 100644
index 0000000..045ede4
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/layout/layout.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/ok"
+    android:layout_width="0sp"
+    android:layout_height="fill_parent"
+    android:layout_weight="1"
+    android:layout_marginStart="2dip"
+    android:layout_marginEnd="2dip"
+    android:textAppearance="?android:attr/textAppearanceMedium"
+    android:textStyle="bold"
+    android:text="@android:string/ok" />
\ No newline at end of file
diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml
index 6c47459..b343562 100644
--- a/libs/androidfw/tests/data/basic/res/values/values.xml
+++ b/libs/androidfw/tests/data/basic/res/values/values.xml
@@ -22,6 +22,7 @@
     <attr name="attr2" format="reference|integer" />
 
     <public type="layout" name="main" id="0x7f020000" />
+    <public type="layout" name="layout" id="0x7f020001" />
 
     <public type="string" name="test1" id="0x7f030000" />
     <string name="test1">test1</string>
@@ -43,6 +44,18 @@
     <public type="integer" name="ref2" id="0x7f040003" />
     <integer name="ref2">12000</integer>
 
+    <public type="integer" name="deep_ref" id="0x7f040004" />
+    <integer name="deep_ref">@integer/deep_ref_1</integer>
+    <integer name="deep_ref_1">@integer/deep_ref_2</integer>
+    <integer name="deep_ref_2">@integer/deep_ref_3</integer>
+    <integer name="deep_ref_3">@integer/deep_ref_4</integer>
+    <integer name="deep_ref_4">@integer/deep_ref_5</integer>
+    <integer name="deep_ref_5">@integer/deep_ref_6</integer>
+    <integer name="deep_ref_6">@integer/deep_ref_7</integer>
+    <integer name="deep_ref_7">@integer/deep_ref_8</integer>
+    <integer name="deep_ref_8">@integer/deep_ref_9</integer>
+    <integer name="deep_ref_9">100</integer>
+
     <public type="style" name="Theme1" id="0x7f050000" />
     <style name="Theme1">
         <item name="com.android.basic:attr1">100</item>
diff --git a/libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml b/libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml
new file mode 100644
index 0000000..34016db
--- /dev/null
+++ b/libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.app" />
diff --git a/libs/androidfw/tests/data/out_of_order_types/build b/libs/androidfw/tests/data/out_of_order_types/build
new file mode 100755
index 0000000..8496f81
--- /dev/null
+++ b/libs/androidfw/tests/data/out_of_order_types/build
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -e
+
+aapt2 compile --dir res -o compiled.flata
+aapt2 link --manifest AndroidManifest.xml -o out_of_order_types.apk compiled.flata
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt b/libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt
new file mode 100644
index 0000000..eca8f47
--- /dev/null
+++ b/libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt
@@ -0,0 +1,43 @@
+00000000: 0200 0c00 ac02 0000 0100 0000 0100 1c00  ................
+00000010: 1c00 0000 0000 0000 0000 0000 0001 0000  ................
+00000020: 1c00 0000 0000 0000 0002 2001 8402 0000  .......... .....
+00000030: 7f00 0000 6300 6f00 6d00 2e00 6100 6e00  ....c.o.m...a.n.
+00000040: 6400 7200 6f00 6900 6400 2e00 6100 7000  d.r.o.i.d...a.p.
+00000050: 7000 0000 0000 0000 0000 0000 0000 0000  p...............
+00000060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+00000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+00000080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+00000090: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+000000a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+000000b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+000000c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+000000d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+000000e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+000000f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+00000100: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+00000110: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+00000120: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+00000130: 0000 0000 2001 0000 0000 0000 6401 0000  .... .......d...
+00000140: 0000 0000 0000 0000 0100 1c00 4400 0000  ............D...
+00000150: 0200 0000 0000 0000 0000 0000 2400 0000  ............$...
+00000160: 0000 0000 0000 0000 0c00 0000 0400 6200  ..............b.
+00000170: 6f00 6f00 6c00 0000 0700 6900 6e00 7400  o.o.l.....i.n.t.
+00000180: 6500 6700 6500 7200 0000 0000 0100 1c00  e.g.e.r.........
+00000190: 2800 0000 0100 0000 0000 0000 0001 0000  (...............
+000001a0: 2000 0000 0000 0000 0000 0000 0404 7465   .............te
+000001b0: 7374 0000 0202 1000 1400 0000 0100 0000  st..............
+000001c0: 0100 0000 0000 0000 0202 1000 1400 0000
+000001d0: 0200 0000 0100 0000 0000 0000 0102 5400
+000001e0: 6800 0000 0100 0000 0100 0000 5800 0000
+000001f0: 4000 0000 0000 0000 0000 0000 0000 0000
+00000200: 0000 0000 0000 0000 0000 0000 0000 0000
+00000210: 0000 0000 0000 0000 0000 0000 0000 0000
+00000220: 0000 0000 0000 0000 0000 0000 0000 0000
+00000230: 0000 0000 0800 0000 0000 0000 0800 0012
+00000240: ffff ffff 0102 5400 6800 0000 0200 0000  ......T.h.......
+00000250: 0100 0000 5800 0000 4000 0000 0000 0000  ....X...@.......
+00000260: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+00000270: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+00000280: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+00000290: 0000 0000 0000 0000 0000 0000 0800 0000  ................
+000002a0: 0000 0000 0800 0010 0100 0000            ............
diff --git a/libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk b/libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk
new file mode 100644
index 0000000..75146e0
--- /dev/null
+++ b/libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/out_of_order_types/res/values/values.xml b/libs/androidfw/tests/data/out_of_order_types/res/values/values.xml
new file mode 100644
index 0000000..7c54fba
--- /dev/null
+++ b/libs/androidfw/tests/data/out_of_order_types/res/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <bool name="test">true</bool>
+    <integer name="test">1</integer>
+</resources>
diff --git a/libs/androidfw/tests/data/system/assets/file.txt b/libs/androidfw/tests/data/system/assets/file.txt
new file mode 100644
index 0000000..f73f309
--- /dev/null
+++ b/libs/androidfw/tests/data/system/assets/file.txt
@@ -0,0 +1 @@
+file
diff --git a/libs/androidfw/tests/data/system/assets/subdir/subdir_file.txt b/libs/androidfw/tests/data/system/assets/subdir/subdir_file.txt
new file mode 100644
index 0000000..3f74eb6
--- /dev/null
+++ b/libs/androidfw/tests/data/system/assets/subdir/subdir_file.txt
@@ -0,0 +1 @@
+subdir file
diff --git a/libs/androidfw/tests/data/system/build b/libs/androidfw/tests/data/system/build
index bfbdf4c..b65145a 100755
--- a/libs/androidfw/tests/data/system/build
+++ b/libs/androidfw/tests/data/system/build
@@ -17,4 +17,6 @@
 
 set -e
 
-aapt package -x -M AndroidManifest.xml -S res -F system.apk -f
+aapt2 compile --dir res -o compiled.flata
+aapt2 link --manifest AndroidManifest.xml -A assets -o system.apk compiled.flata
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/system/res/values-sv/values.xml b/libs/androidfw/tests/data/system/res/values-sv/values.xml
index b97bdb6..5f60d21 100644
--- a/libs/androidfw/tests/data/system/res/values-sv/values.xml
+++ b/libs/androidfw/tests/data/system/res/values-sv/values.xml
@@ -15,6 +15,5 @@
 -->
 
 <resources>
-    <public type="integer" name="number" id="0x01030000" />
     <integer name="number">1</integer>
 </resources>
diff --git a/libs/androidfw/tests/data/system/res/values/themes.xml b/libs/androidfw/tests/data/system/res/values/themes.xml
index 35d43c7..7893c94 100644
--- a/libs/androidfw/tests/data/system/res/values/themes.xml
+++ b/libs/androidfw/tests/data/system/res/values/themes.xml
@@ -18,6 +18,7 @@
     <public name="background" type="attr" id="0x01010000"/>
     <public name="foreground" type="attr" id="0x01010001"/>
     <public name="Theme.One" type="style" id="0x01020000"/>
+    <public type="integer" name="number" id="0x01030000" />
 
     <attr name="background" format="color|reference"/>
     <attr name="foreground" format="color|reference"/>
diff --git a/libs/androidfw/tests/data/system/system.apk b/libs/androidfw/tests/data/system/system.apk
index 1299016..9045d6c 100644
--- a/libs/androidfw/tests/data/system/system.apk
+++ b/libs/androidfw/tests/data/system/system.apk
Binary files differ