Merge "New implementation of AssetManager/ResTable"
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index d501d25..fb89835 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -24,10 +24,14 @@
         "-Wunreachable-code",
     ],
     srcs: [
+        "ApkAssets.cpp",
         "Asset.cpp",
         "AssetDir.cpp",
         "AssetManager.cpp",
+        "AssetManager2.cpp",
         "AttributeResolution.cpp",
+        "ChunkIterator.cpp",
+        "LoadedArsc.cpp",
         "LocaleData.cpp",
         "misc.cpp",
         "ObbFile.cpp",
@@ -65,7 +69,16 @@
             shared: {
                 enabled: false,
             },
-            shared_libs: ["libz-host"],
+            static_libs: [
+                "libziparchive",
+                "libbase",
+                "liblog",
+                "libcutils",
+                "libutils",
+            ],
+            shared_libs: [
+                "libz-host",
+            ],
         },
         windows: {
             enabled: true,
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
new file mode 100644
index 0000000..55f4c3c
--- /dev/null
+++ b/libs/androidfw/ApkAssets.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_RESOURCES
+
+#include "androidfw/ApkAssets.h"
+
+#include "android-base/logging.h"
+#include "utils/Trace.h"
+#include "ziparchive/zip_archive.h"
+
+#include "androidfw/Asset.h"
+#include "androidfw/Util.h"
+
+namespace android {
+
+std::unique_ptr<ApkAssets> ApkAssets::Load(const std::string& path) {
+  ATRACE_NAME("ApkAssets::Load");
+  ::ZipArchiveHandle unmanaged_handle;
+  int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
+  if (result != 0) {
+    LOG(ERROR) << ::ErrorCodeString(result);
+    return {};
+  }
+
+  // Wrap the handle in a unique_ptr so it gets automatically closed.
+  std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets());
+  loaded_apk->zip_handle_.reset(unmanaged_handle);
+
+  ::ZipString entry_name("resources.arsc");
+  ::ZipEntry entry;
+  result = ::FindEntry(loaded_apk->zip_handle_.get(), entry_name, &entry);
+  if (result != 0) {
+    LOG(ERROR) << ::ErrorCodeString(result);
+    return {};
+  }
+
+  if (entry.method == kCompressDeflated) {
+    LOG(WARNING) << "resources.arsc is compressed.";
+  }
+
+  loaded_apk->resources_asset_ =
+      loaded_apk->Open("resources.arsc", Asset::AccessMode::ACCESS_BUFFER);
+  if (loaded_apk->resources_asset_ == nullptr) {
+    return {};
+  }
+
+  loaded_apk->path_ = path;
+  loaded_apk->loaded_arsc_ =
+      LoadedArsc::Load(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/),
+                       loaded_apk->resources_asset_->getLength());
+  if (loaded_apk->loaded_arsc_ == nullptr) {
+    return {};
+  }
+  return loaded_apk;
+}
+
+std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode /*mode*/) const {
+  ATRACE_NAME("ApkAssets::Open");
+  CHECK(zip_handle_ != nullptr);
+
+  ::ZipString name(path.c_str());
+  ::ZipEntry entry;
+  int32_t result = ::FindEntry(zip_handle_.get(), name, &entry);
+  if (result != 0) {
+    LOG(ERROR) << "No entry '" << path << "' found in APK.";
+    return {};
+  }
+
+  if (entry.method == kCompressDeflated) {
+    auto compressed_asset = util::make_unique<_CompressedAsset>();
+    if (compressed_asset->openChunk(::GetFileDescriptor(zip_handle_.get()), entry.offset,
+                                    entry.method, entry.uncompressed_length,
+                                    entry.compressed_length) != NO_ERROR) {
+      LOG(ERROR) << "Failed to decompress '" << path << "'.";
+      return {};
+    }
+    return std::move(compressed_asset);
+  } else {
+    auto uncompressed_asset = util::make_unique<_FileAsset>();
+    if (uncompressed_asset->openChunk(path.c_str(), ::GetFileDescriptor(zip_handle_.get()),
+                                      entry.offset, entry.uncompressed_length) != NO_ERROR) {
+      LOG(ERROR) << "Failed to mmap '" << path << "'.";
+      return {};
+    }
+    return std::move(uncompressed_asset);
+  }
+  return {};
+}
+
+}  // namespace android
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
new file mode 100644
index 0000000..8d65925
--- /dev/null
+++ b/libs/androidfw/AssetManager2.cpp
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_RESOURCES
+
+#include "androidfw/AssetManager2.h"
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+#include "utils/ByteOrder.h"
+#include "utils/Trace.h"
+
+#ifdef _WIN32
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+namespace android {
+
+AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); }
+
+bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets,
+                                 bool invalidate_caches) {
+  apk_assets_ = apk_assets;
+  if (invalidate_caches) {
+    InvalidateCaches(static_cast<uint32_t>(-1));
+  }
+  return true;
+}
+
+const std::vector<const ApkAssets*> AssetManager2::GetApkAssets() const { return apk_assets_; }
+
+const ResStringPool* AssetManager2::GetStringPoolForCookie(ApkAssetsCookie cookie) const {
+  if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
+    return nullptr;
+  }
+  return apk_assets_[cookie]->GetLoadedArsc()->GetStringPool();
+}
+
+void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
+  const int diff = configuration_.diff(configuration);
+  configuration_ = configuration;
+
+  if (diff) {
+    InvalidateCaches(static_cast<uint32_t>(diff));
+  }
+}
+
+const ResTable_config& AssetManager2::GetConfiguration() const { return configuration_; }
+
+std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) {
+  const std::string new_path = "assets/" + filename;
+  return OpenNonAsset(new_path, mode);
+}
+
+std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie,
+                                           Asset::AccessMode mode) {
+  const std::string new_path = "assets/" + filename;
+  return OpenNonAsset(new_path, cookie, mode);
+}
+
+// Search in reverse because that's how we used to do it and we need to preserve behaviour.
+// This is unfortunate, because ClassLoaders delegate to the parent first, so the order
+// is inconsistent for split APKs.
+std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
+                                                   Asset::AccessMode mode,
+                                                   ApkAssetsCookie* out_cookie) {
+  ATRACE_CALL();
+  for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) {
+    std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode);
+    if (asset) {
+      if (out_cookie != nullptr) {
+        *out_cookie = i;
+      }
+      return asset;
+    }
+  }
+
+  if (out_cookie != nullptr) {
+    *out_cookie = kInvalidCookie;
+  }
+  return {};
+}
+
+std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
+                                                   ApkAssetsCookie cookie, Asset::AccessMode mode) {
+  ATRACE_CALL();
+  if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
+    return {};
+  }
+  return apk_assets_[cookie]->Open(filename, mode);
+}
+
+ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
+                                         bool stop_at_first_match, LoadedArsc::Entry* out_entry,
+                                         ResTable_config* out_selected_config,
+                                         uint32_t* out_flags) {
+  ATRACE_CALL();
+
+  // 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_;
+  if (density_override != 0 && density_override != configuration_.density) {
+    density_override_config = configuration_;
+    density_override_config.density = density_override;
+    desired_config = &density_override_config;
+  }
+
+  LoadedArsc::Entry best_entry;
+  ResTable_config best_config;
+  int32_t best_index = -1;
+  uint32_t cumulated_flags = 0;
+
+  const size_t apk_asset_count = apk_assets_.size();
+  for (size_t i = 0; i < apk_asset_count; i++) {
+    const LoadedArsc* loaded_arsc = apk_assets_[i]->GetLoadedArsc();
+
+    LoadedArsc::Entry current_entry;
+    ResTable_config current_config;
+    uint32_t flags = 0;
+    if (!loaded_arsc->FindEntry(resid, *desired_config, &current_entry, &current_config, &flags)) {
+      continue;
+    }
+
+    cumulated_flags |= flags;
+
+    if (best_index == -1 || current_config.isBetterThan(best_config, desired_config)) {
+      best_entry = current_entry;
+      best_config = current_config;
+      best_index = static_cast<int32_t>(i);
+      if (stop_at_first_match) {
+        break;
+      }
+    }
+  }
+
+  if (best_index == -1) {
+    return kInvalidCookie;
+  }
+
+  *out_entry = best_entry;
+  *out_selected_config = best_config;
+  *out_flags = cumulated_flags;
+  return best_index;
+}
+
+bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) {
+  ATRACE_CALL();
+
+  LoadedArsc::Entry entry;
+  ResTable_config config;
+  uint32_t flags = 0u;
+  ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
+                                     true /* stop_at_first_match */, &entry, &config, &flags);
+  if (cookie == kInvalidCookie) {
+    return false;
+  }
+
+  const std::string* package_name =
+      apk_assets_[cookie]->GetLoadedArsc()->GetPackageNameForId(resid);
+  if (package_name == nullptr) {
+    return false;
+  }
+
+  out_name->package = package_name->data();
+  out_name->package_len = package_name->size();
+
+  out_name->type = entry.type_string_ref.string8(&out_name->type_len);
+  out_name->type16 = nullptr;
+  if (out_name->type == nullptr) {
+    out_name->type16 = entry.type_string_ref.string16(&out_name->type_len);
+    if (out_name->type16 == nullptr) {
+      return false;
+    }
+  }
+
+  out_name->entry = entry.entry_string_ref.string8(&out_name->entry_len);
+  out_name->entry16 = nullptr;
+  if (out_name->entry == nullptr) {
+    out_name->entry16 = entry.entry_string_ref.string16(&out_name->entry_len);
+    if (out_name->entry16 == nullptr) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) {
+  LoadedArsc::Entry entry;
+  ResTable_config config;
+  ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
+                                     false /* stop_at_first_match */, &entry, &config, out_flags);
+  return cookie != kInvalidCookie;
+}
+
+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();
+
+  LoadedArsc::Entry entry;
+  ResTable_config config;
+  uint32_t flags = 0u;
+  ApkAssetsCookie cookie =
+      FindEntry(resid, density_override, false /* stop_at_first_match */, &entry, &config, &flags);
+  if (cookie == kInvalidCookie) {
+    return kInvalidCookie;
+  }
+
+  if (dtohl(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) {
+    if (!may_be_bag) {
+      LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
+    }
+    return kInvalidCookie;
+  }
+
+  const Res_value* device_value = reinterpret_cast<const Res_value*>(
+      reinterpret_cast<const uint8_t*>(entry.entry) + dtohs(entry.entry->size));
+  out_value->copyFrom_dtoh(*device_value);
+  *out_selected_config = config;
+  *out_flags = flags;
+  return cookie;
+}
+
+const ResolvedBag* AssetManager2::GetBag(uint32_t resid) {
+  ATRACE_CALL();
+
+  auto cached_iter = cached_bags_.find(resid);
+  if (cached_iter != cached_bags_.end()) {
+    return cached_iter->second.get();
+  }
+
+  LoadedArsc::Entry entry;
+  ResTable_config config;
+  uint32_t flags = 0u;
+  ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
+                                     false /* stop_at_first_match */, &entry, &config, &flags);
+  if (cookie == kInvalidCookie) {
+    return nullptr;
+  }
+
+  // Check that the size of the entry header is at least as big as
+  // the desired ResTable_map_entry. Also verify that the entry
+  // was intended to be a map.
+  if (dtohs(entry.entry->size) < sizeof(ResTable_map_entry) ||
+      (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) {
+    // Not a bag, nothing to do.
+    return nullptr;
+  }
+
+  const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry.entry);
+  const ResTable_map* map_entry =
+      reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(map) + map->size);
+  const ResTable_map* const map_entry_end = map_entry + dtohl(map->count);
+
+  const uint32_t parent = dtohl(map->parent.ident);
+  if (parent == 0) {
+    // There is no parent, meaning there is nothing to inherit and we can do a simple
+    // copy of the entries in the map.
+    const size_t entry_count = map_entry_end - map_entry;
+    util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
+        malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))};
+    ResolvedBag::Entry* new_entry = new_bag->entries;
+    for (; map_entry != map_entry_end; ++map_entry) {
+      new_entry->cookie = cookie;
+      new_entry->value.copyFrom_dtoh(map_entry->value);
+      new_entry->key = dtohl(map_entry->name.ident);
+      new_entry->key_pool = nullptr;
+      new_entry->type_pool = nullptr;
+      ++new_entry;
+    }
+    new_bag->type_spec_flags = flags;
+    new_bag->entry_count = static_cast<uint32_t>(entry_count);
+    ResolvedBag* result = new_bag.get();
+    cached_bags_[resid] = std::move(new_bag);
+    return result;
+  }
+
+  // Get the parent and do a merge of the keys.
+  const ResolvedBag* parent_bag = GetBag(parent);
+  if (parent_bag == nullptr) {
+    // Failed to get the parent that should exist.
+    return nullptr;
+  }
+
+  // Combine flags from the parent and our own bag.
+  flags |= parent_bag->type_spec_flags;
+
+  // Create the max possible entries we can make. Once we construct the bag,
+  // we will realloc to fit to size.
+  const size_t max_count = parent_bag->entry_count + dtohl(map->count);
+  ResolvedBag* new_bag = reinterpret_cast<ResolvedBag*>(
+      malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry))));
+  ResolvedBag::Entry* new_entry = new_bag->entries;
+
+  const ResolvedBag::Entry* parent_entry = parent_bag->entries;
+  const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count;
+
+  // The keys are expected to be in sorted order. Merge the two bags.
+  while (map_entry != map_entry_end && parent_entry != parent_entry_end) {
+    const uint32_t child_key = dtohl(map_entry->name.ident);
+    if (child_key <= parent_entry->key) {
+      // Use the child key if it comes before the parent
+      // or is equal to the parent (overrides).
+      new_entry->cookie = cookie;
+      new_entry->value.copyFrom_dtoh(map_entry->value);
+      new_entry->key = child_key;
+      new_entry->key_pool = nullptr;
+      new_entry->type_pool = nullptr;
+      ++map_entry;
+    } else {
+      // Take the parent entry as-is.
+      memcpy(new_entry, parent_entry, sizeof(*new_entry));
+    }
+
+    if (child_key >= parent_entry->key) {
+      // Move to the next parent entry if we used it or it was overridden.
+      ++parent_entry;
+    }
+    // Increment to the next entry to fill.
+    ++new_entry;
+  }
+
+  // Finish the child entries if they exist.
+  while (map_entry != map_entry_end) {
+    new_entry->cookie = cookie;
+    new_entry->value.copyFrom_dtoh(map_entry->value);
+    new_entry->key = dtohl(map_entry->name.ident);
+    new_entry->key_pool = nullptr;
+    new_entry->type_pool = nullptr;
+    ++map_entry;
+    ++new_entry;
+  }
+
+  // Finish the parent entries if they exist.
+  if (parent_entry != parent_entry_end) {
+    // Take the rest of the parent entries as-is.
+    const size_t num_entries_to_copy = parent_entry_end - parent_entry;
+    memcpy(new_entry, parent_entry, num_entries_to_copy * sizeof(*new_entry));
+    new_entry += num_entries_to_copy;
+  }
+
+  // Resize the resulting array to fit.
+  const size_t actual_count = new_entry - new_bag->entries;
+  if (actual_count != max_count) {
+    new_bag = reinterpret_cast<ResolvedBag*>(
+        realloc(new_bag, sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry))));
+  }
+
+  util::unique_cptr<ResolvedBag> final_bag{new_bag};
+  final_bag->type_spec_flags = flags;
+  final_bag->entry_count = static_cast<uint32_t>(actual_count);
+  ResolvedBag* result = final_bag.get();
+  cached_bags_[resid] = std::move(final_bag);
+  return result;
+}
+
+void AssetManager2::InvalidateCaches(uint32_t diff) {
+  if (diff == 0xffffffffu) {
+    // Everything must go.
+    cached_bags_.clear();
+    return;
+  }
+
+  // Be more conservative with what gets purged. Only if the bag has other possible
+  // variations with respect to what changed (diff) should we remove it.
+  for (auto iter = cached_bags_.cbegin(); iter != cached_bags_.cend();) {
+    if (diff & iter->second->type_spec_flags) {
+      iter = cached_bags_.erase(iter);
+    } else {
+      ++iter;
+    }
+  }
+}
+
+std::unique_ptr<Theme> AssetManager2::NewTheme() { return std::unique_ptr<Theme>(new Theme(this)); }
+
+bool Theme::ApplyStyle(uint32_t resid, bool force) {
+  ATRACE_CALL();
+
+  const ResolvedBag* bag = asset_manager_->GetBag(resid);
+  if (bag == nullptr) {
+    return false;
+  }
+
+  // Merge the flags from this style.
+  type_spec_flags_ |= bag->type_spec_flags;
+
+  // On the first iteration, verify the attribute IDs and
+  // update the entry count in each type.
+  const auto bag_iter_end = end(bag);
+  for (auto bag_iter = begin(bag); bag_iter != bag_iter_end; ++bag_iter) {
+    const uint32_t attr_resid = bag_iter->key;
+
+    // If the resource ID passed in is not a style, the key can be
+    // some other identifier that is not a resource ID.
+    if (!util::is_valid_resid(attr_resid)) {
+      return false;
+    }
+
+    const uint32_t package_idx = util::get_package_id(attr_resid);
+
+    // The type ID is 1-based, so subtract 1 to get an index.
+    const uint32_t type_idx = util::get_type_id(attr_resid) - 1;
+    const uint32_t entry_idx = util::get_entry_id(attr_resid);
+
+    std::unique_ptr<Package>& package = packages_[package_idx];
+    if (package == nullptr) {
+      package.reset(new Package());
+    }
+
+    util::unique_cptr<Type>& type = package->types[type_idx];
+    if (type == nullptr) {
+      // Set the initial capacity to take up a total amount of 1024 bytes.
+      constexpr uint32_t kInitialCapacity = (1024u - sizeof(Type)) / sizeof(Entry);
+      const uint32_t initial_capacity = std::max(entry_idx, kInitialCapacity);
+      type.reset(
+          reinterpret_cast<Type*>(calloc(sizeof(Type) + (initial_capacity * sizeof(Entry)), 1)));
+      type->entry_capacity = initial_capacity;
+    }
+
+    // Set the entry_count to include this entry. We will populate
+    // and resize the array as necessary in the next pass.
+    if (entry_idx + 1 > type->entry_count) {
+      // Increase the entry count to include this.
+      type->entry_count = entry_idx + 1;
+    }
+  }
+
+  // On the second pass, we will realloc to fit the entry counts
+  // and populate the structures.
+  for (auto bag_iter = begin(bag); bag_iter != bag_iter_end; ++bag_iter) {
+    const uint32_t attr_resid = bag_iter->key;
+    const uint32_t package_idx = util::get_package_id(attr_resid);
+    const uint32_t type_idx = util::get_type_id(attr_resid) - 1;
+    const uint32_t entry_idx = util::get_entry_id(attr_resid);
+    Package* package = packages_[package_idx].get();
+    util::unique_cptr<Type>& type = package->types[type_idx];
+    if (type->entry_count != type->entry_capacity) {
+      // Resize to fit the actual entries that will be included.
+      Type* type_ptr = type.release();
+      type.reset(reinterpret_cast<Type*>(
+          realloc(type_ptr, sizeof(Type) + (type_ptr->entry_count * sizeof(Entry)))));
+      if (type->entry_capacity < type->entry_count) {
+        // Clear the newly allocated memory (which does not get zero initialized).
+        // We need to do this because we |= type_spec_flags.
+        memset(type->entries + type->entry_capacity, 0,
+               sizeof(Entry) * (type->entry_count - type->entry_capacity));
+      }
+      type->entry_capacity = type->entry_count;
+    }
+    Entry& entry = type->entries[entry_idx];
+    if (force || entry.value.dataType == Res_value::TYPE_NULL) {
+      entry.cookie = bag_iter->cookie;
+      entry.type_spec_flags |= bag->type_spec_flags;
+      entry.value = bag_iter->value;
+    }
+  }
+  return true;
+}
+
+ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value,
+                                    uint32_t* out_flags) const {
+  constexpr const int kMaxIterations = 20;
+
+  uint32_t type_spec_flags = 0u;
+
+  for (int iterations_left = kMaxIterations; iterations_left > 0; iterations_left--) {
+    if (!util::is_valid_resid(resid)) {
+      return kInvalidCookie;
+    }
+
+    const uint32_t package_idx = util::get_package_id(resid);
+
+    // Type ID is 1-based, subtract 1 to get the index.
+    const uint32_t type_idx = util::get_type_id(resid) - 1;
+    const uint32_t entry_idx = util::get_entry_id(resid);
+
+    const Package* package = packages_[package_idx].get();
+    if (package == nullptr) {
+      return kInvalidCookie;
+    }
+
+    const Type* type = package->types[type_idx].get();
+    if (type == nullptr) {
+      return kInvalidCookie;
+    }
+
+    if (entry_idx >= type->entry_count) {
+      return kInvalidCookie;
+    }
+
+    const Entry& entry = type->entries[entry_idx];
+    type_spec_flags |= entry.type_spec_flags;
+
+    switch (entry.value.dataType) {
+      case Res_value::TYPE_ATTRIBUTE:
+        resid = entry.value.data;
+        break;
+
+      case Res_value::TYPE_NULL:
+        return kInvalidCookie;
+
+      default:
+        *out_value = entry.value;
+        if (out_flags != nullptr) {
+          *out_flags = type_spec_flags;
+        }
+        return entry.cookie;
+    }
+  }
+
+  LOG(WARNING) << base::StringPrintf("Too many (%d) attribute references, stopped at: 0x%08x",
+                                     kMaxIterations, resid);
+  return kInvalidCookie;
+}
+
+void Theme::Clear() {
+  type_spec_flags_ = 0u;
+  for (std::unique_ptr<Package>& package : packages_) {
+    package.reset();
+  }
+}
+
+bool Theme::SetTo(const Theme& o) {
+  if (this == &o) {
+    return true;
+  }
+
+  if (asset_manager_ != o.asset_manager_) {
+    return false;
+  }
+
+  type_spec_flags_ = o.type_spec_flags_;
+
+  for (size_t p = 0; p < arraysize(packages_); p++) {
+    const Package* package = o.packages_[p].get();
+    if (package == nullptr) {
+      packages_[p].reset();
+      continue;
+    }
+
+    for (size_t t = 0; t < arraysize(package->types); t++) {
+      const Type* type = package->types[t].get();
+      if (type == nullptr) {
+        packages_[p]->types[t].reset();
+        continue;
+      }
+
+      const size_t type_alloc_size = sizeof(Type) + (type->entry_capacity * sizeof(Entry));
+      void* copied_data = malloc(type_alloc_size);
+      memcpy(copied_data, type, type_alloc_size);
+      packages_[p]->types[t].reset(reinterpret_cast<Type*>(copied_data));
+    }
+  }
+  return true;
+}
+
+}  // namespace android
diff --git a/libs/androidfw/Chunk.h b/libs/androidfw/Chunk.h
new file mode 100644
index 0000000..e87b940
--- /dev/null
+++ b/libs/androidfw/Chunk.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 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 CHUNK_H_
+#define CHUNK_H_
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "utils/ByteOrder.h"
+
+#ifdef _WIN32
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+#include "androidfw/ResourceTypes.h"
+
+namespace android {
+
+// Helpful wrapper around a ResChunk_header that provides getter methods
+// that handle endianness conversions and provide access to the data portion
+// of the chunk.
+class Chunk {
+ public:
+  explicit Chunk(const ResChunk_header* chunk) : device_chunk_(chunk) {}
+
+  // Returns the type of the chunk. Caller need not worry about endianness.
+  inline int type() const { return dtohs(device_chunk_->type); }
+
+  // Returns the size of the entire chunk. This can be useful for skipping
+  // over the entire chunk. Caller need not worry about endianness.
+  inline size_t size() const { return dtohl(device_chunk_->size); }
+
+  // Returns the size of the header. Caller need not worry about endianness.
+  inline size_t header_size() const { return dtohs(device_chunk_->headerSize); }
+
+  template <typename T>
+  inline const T* header() const {
+    if (header_size() >= sizeof(T)) {
+      return reinterpret_cast<const T*>(device_chunk_);
+    }
+    return nullptr;
+  }
+
+  inline const void* data_ptr() const {
+    return reinterpret_cast<const uint8_t*>(device_chunk_) + header_size();
+  }
+
+  inline size_t data_size() const { return size() - header_size(); }
+
+ private:
+  const ResChunk_header* device_chunk_;
+};
+
+// Provides a Java style iterator over an array of ResChunk_header's.
+// Validation is performed while iterating.
+// The caller should check if there was an error during chunk validation
+// by calling HadError() and GetLastError() to get the reason for failure.
+// Example:
+//
+//   ChunkIterator iter(data_ptr, data_len);
+//   while (iter.HasNext()) {
+//     const Chunk chunk = iter.Next();
+//     ...
+//   }
+//
+//   if (iter.HadError()) {
+//     LOG(ERROR) << iter.GetLastError();
+//   }
+//
+class ChunkIterator {
+ public:
+  ChunkIterator(const void* data, size_t len)
+      : next_chunk_(reinterpret_cast<const ResChunk_header*>(data)),
+        len_(len),
+        last_error_(nullptr) {
+    CHECK(next_chunk_ != nullptr) << "data can't be nullptr";
+    VerifyNextChunk();
+  }
+
+  Chunk Next();
+  inline bool HasNext() const { return !HadError() && len_ != 0; };
+  inline bool HadError() const { return last_error_ != nullptr; }
+  inline std::string GetLastError() const { return last_error_; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ChunkIterator);
+
+  // Returns false if there was an error.
+  bool VerifyNextChunk();
+
+  const ResChunk_header* next_chunk_;
+  size_t len_;
+  const char* last_error_;
+};
+
+}  // namespace android
+
+#endif /* CHUNK_H_ */
diff --git a/libs/androidfw/ChunkIterator.cpp b/libs/androidfw/ChunkIterator.cpp
new file mode 100644
index 0000000..747aa4a
--- /dev/null
+++ b/libs/androidfw/ChunkIterator.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 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 "Chunk.h"
+
+#include "android-base/logging.h"
+
+namespace android {
+
+Chunk ChunkIterator::Next() {
+  CHECK(len_ != 0) << "called Next() after last chunk";
+
+  const ResChunk_header* this_chunk = next_chunk_;
+
+  // We've already checked the values of this_chunk, so safely increment.
+  next_chunk_ = reinterpret_cast<const ResChunk_header*>(
+      reinterpret_cast<const uint8_t*>(this_chunk) + dtohl(this_chunk->size));
+  len_ -= dtohl(this_chunk->size);
+
+  if (len_ != 0) {
+    // Prepare the next chunk.
+    VerifyNextChunk();
+  }
+  return Chunk(this_chunk);
+}
+
+// Returns false if there was an error.
+bool ChunkIterator::VerifyNextChunk() {
+  const uintptr_t header_start = reinterpret_cast<uintptr_t>(next_chunk_);
+
+  // This data must be 4-byte aligned, since we directly
+  // access 32-bit words, which must be aligned on
+  // certain architectures.
+  if (header_start & 0x03) {
+    last_error_ = "header not aligned on 4-byte boundary";
+    return false;
+  }
+
+  if (len_ < sizeof(ResChunk_header)) {
+    last_error_ = "not enough space for header";
+    return false;
+  }
+
+  const size_t header_size = dtohs(next_chunk_->headerSize);
+  const size_t size = dtohl(next_chunk_->size);
+  if (header_size < sizeof(ResChunk_header)) {
+    last_error_ = "header size too small";
+    return false;
+  }
+
+  if (header_size > size) {
+    last_error_ = "header size is larger than entire chunk";
+    return false;
+  }
+
+  if (size > len_) {
+    last_error_ = "chunk size is bigger than given data";
+    return false;
+  }
+
+  if ((size | header_size) & 0x03) {
+    last_error_ = "header sizes are not aligned on 4-byte boundary";
+    return false;
+  }
+  return true;
+}
+
+}  // namespace android
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
new file mode 100644
index 0000000..94d0d46
--- /dev/null
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_RESOURCES
+
+#include "androidfw/LoadedArsc.h"
+
+#include <cstddef>
+#include <limits>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+#include "utils/ByteOrder.h"
+#include "utils/Trace.h"
+
+#ifdef _WIN32
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+#include "Chunk.h"
+#include "androidfw/ByteBucketArray.h"
+#include "androidfw/Util.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+
+namespace {
+
+// 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;
+
+  // 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>;
+
+// Builder that helps accumulate Type structs and then create a single
+// contiguous block of memory to store both the TypeSpec struct and
+// the Type structs.
+class TypeSpecPtrBuilder {
+ public:
+  TypeSpecPtrBuilder(const ResTable_typeSpec* header) : header_(header) {}
+
+  void AddType(const ResTable_type* type) {
+    ResTable_config config;
+    config.copyFromDtoH(type->config);
+    types_.push_back(Type{config, type});
+  }
+
+  TypeSpecPtr Build() {
+    // Check for overflow.
+    if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) {
+      return {};
+    }
+    TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type)));
+    type_spec->type_spec = header_;
+    type_spec->type_count = types_.size();
+    memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type));
+    return TypeSpecPtr(type_spec);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder);
+
+  const ResTable_typeSpec* header_;
+  std::vector<Type> types_;
+};
+
+}  // namespace
+
+class LoadedPackage {
+ public:
+  LoadedPackage() = default;
+
+  bool FindEntry(uint8_t type_id, uint16_t entry_id, const ResTable_config& config,
+                 LoadedArsc::Entry* out_entry, ResTable_config* out_selected_config,
+                 uint32_t* out_flags) const;
+
+  ResStringPool type_string_pool_;
+  ResStringPool key_string_pool_;
+  std::string package_name_;
+  int package_id_ = -1;
+
+  ByteBucketArray<TypeSpecPtr> type_specs_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
+};
+
+bool LoadedPackage::FindEntry(uint8_t type_id, uint16_t entry_id, const ResTable_config& config,
+                              LoadedArsc::Entry* out_entry, ResTable_config* out_selected_config,
+                              uint32_t* out_flags) const {
+  ATRACE_NAME("LoadedPackage::FindEntry");
+  const TypeSpecPtr& ptr = type_specs_[type_id];
+  if (ptr == nullptr) {
+    return false;
+  }
+
+  // Don't bother checking if the entry ID is larger than
+  // the number of entries.
+  if (entry_id >= dtohl(ptr->type_spec->entryCount)) {
+    return false;
+  }
+
+  const ResTable_config* best_config = nullptr;
+  const ResTable_type* best_type = nullptr;
+  uint32_t best_offset = 0;
+
+  for (uint32_t i = 0; i < ptr->type_count; i++) {
+    const Type* type = &ptr->types[i];
+
+    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.
+      size_t entry_count = dtohl(type->type->entryCount);
+      if (entry_id < entry_count) {
+        const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
+            reinterpret_cast<const uint8_t*>(type->type) + dtohs(type->type->header.headerSize));
+        const uint32_t offset = dtohl(entry_offsets[entry_id]);
+        if (offset != ResTable_type::NO_ENTRY) {
+          // There is an entry for this resource, record it.
+          best_config = &type->configuration;
+          best_type = type->type;
+          best_offset = offset + dtohl(type->type->entriesStart);
+        }
+      }
+    }
+  }
+
+  if (best_type == nullptr) {
+    return false;
+  }
+
+  const uint32_t* flags = reinterpret_cast<const uint32_t*>(ptr->type_spec + 1);
+  *out_flags = dtohl(flags[entry_id]);
+  *out_selected_config = *best_config;
+
+  const ResTable_entry* best_entry = reinterpret_cast<const ResTable_entry*>(
+      reinterpret_cast<const uint8_t*>(best_type) + best_offset);
+  out_entry->entry = best_entry;
+  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;
+}
+
+// The destructor gets generated into arbitrary translation units
+// if left implicit, which causes the compiler to complain about
+// forward declarations and incomplete types.
+LoadedArsc::~LoadedArsc() {}
+
+bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, Entry* out_entry,
+                           ResTable_config* out_selected_config, uint32_t* out_flags) const {
+  ATRACE_NAME("LoadedArsc::FindEntry");
+  const uint8_t package_id = util::get_package_id(resid);
+  const uint8_t type_id = util::get_type_id(resid);
+  const uint16_t entry_id = util::get_entry_id(resid);
+
+  if (type_id == 0) {
+    LOG(ERROR) << "Invalid ID 0x" << std::hex << resid << std::dec << ".";
+    return false;
+  }
+
+  for (const auto& loaded_package : packages_) {
+    if (loaded_package->package_id_ == package_id) {
+      return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry,
+                                       out_selected_config, out_flags);
+    }
+  }
+  return false;
+}
+
+const std::string* LoadedArsc::GetPackageNameForId(uint32_t resid) const {
+  const uint8_t package_id = util::get_package_id(resid);
+  for (const auto& loaded_package : packages_) {
+    if (loaded_package->package_id_ == package_id) {
+      return &loaded_package->package_name_;
+    }
+  }
+  return nullptr;
+}
+
+static bool VerifyType(const Chunk& chunk) {
+  ATRACE_CALL();
+  const ResTable_type* header = chunk.header<ResTable_type>();
+
+  const size_t entry_count = dtohl(header->entryCount);
+  if (entry_count > std::numeric_limits<uint16_t>::max()) {
+    LOG(ERROR) << "Too many entries in RES_TABLE_TYPE_TYPE.";
+    return false;
+  }
+
+  // Make sure that there is enough room for the entry offsets.
+  const size_t offsets_offset = chunk.header_size();
+  const size_t entries_offset = dtohl(header->entriesStart);
+  const size_t offsets_length = sizeof(uint32_t) * entry_count;
+
+  if (offsets_offset + offsets_length > entries_offset) {
+    LOG(ERROR) << "Entry offsets overlap actual entry data.";
+    return false;
+  }
+
+  if (entries_offset > chunk.size()) {
+    LOG(ERROR) << "Entry offsets extend beyond chunk.";
+    return false;
+  }
+
+  if (entries_offset & 0x03) {
+    LOG(ERROR) << "Entries start at unaligned address.";
+    return false;
+  }
+
+  // Check each entry offset.
+  const uint32_t* offsets =
+      reinterpret_cast<const uint32_t*>(reinterpret_cast<const uint8_t*>(header) + offsets_offset);
+  for (size_t i = 0; i < entry_count; i++) {
+    uint32_t offset = dtohl(offsets[i]);
+    if (offset != ResTable_type::NO_ENTRY) {
+      // Check that the offset is aligned.
+      if (offset & 0x03) {
+        LOG(ERROR) << "Entry offset at index " << i << " is not 4-byte aligned.";
+        return false;
+      }
+
+      // Check that the offset doesn't overflow.
+      if (offset > std::numeric_limits<uint32_t>::max() - entries_offset) {
+        // Overflow in offset.
+        LOG(ERROR) << "Entry offset at index " << i << " is too large.";
+        return false;
+      }
+
+      offset += entries_offset;
+      if (offset > chunk.size() - sizeof(ResTable_entry)) {
+        LOG(ERROR) << "Entry offset at index " << i << " is too large. No room for ResTable_entry.";
+        return false;
+      }
+
+      const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
+          reinterpret_cast<const uint8_t*>(header) + offset);
+      const size_t entry_size = dtohs(entry->size);
+      if (entry_size < sizeof(*entry)) {
+        LOG(ERROR) << "ResTable_entry size " << entry_size << " is too small.";
+        return false;
+      }
+
+      // Check the declared entrySize.
+      if (entry_size > chunk.size() || offset > chunk.size() - entry_size) {
+        LOG(ERROR) << "ResTable_entry size " << entry_size << " is too large.";
+        return false;
+      }
+
+      // If this is a map entry, then keep validating.
+      if (entry_size >= sizeof(ResTable_map_entry)) {
+        const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry);
+        const size_t map_entry_count = dtohl(map->count);
+
+        size_t map_entries_start = offset + entry_size;
+        if (map_entries_start & 0x03) {
+          LOG(ERROR) << "Map entries 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.";
+          return false;
+        }
+
+        // Great, all the map entries fit!.
+      } else {
+        // There needs to be room for one Res_value struct.
+        if (offset + entry_size > chunk.size() - sizeof(Res_value)) {
+          LOG(ERROR) << "No room for Res_value after ResTable_entry.";
+          return false;
+        }
+
+        const Res_value* value = 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 is too small.";
+          return false;
+        }
+
+        if (value_size > chunk.size() || offset + entry_size > chunk.size() - value_size) {
+          LOG(ERROR) << "Res_value size is too large.";
+          return false;
+        }
+      }
+    }
+  }
+  return true;
+}
+
+static bool LoadPackage(const Chunk& chunk, LoadedPackage* loaded_package) {
+  ATRACE_CALL();
+  const ResTable_package* header = chunk.header<ResTable_package>();
+  if (header == nullptr) {
+    LOG(ERROR) << "Chunk RES_TABLE_PACKAGE_TYPE is too small.";
+    return false;
+  }
+
+  loaded_package->package_id_ = dtohl(header->id);
+
+  // 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;
+
+  ChunkIterator iter(chunk.data_ptr(), chunk.data_size());
+  while (iter.HasNext()) {
+    const Chunk child_chunk = iter.Next();
+    switch (child_chunk.type()) {
+      case RES_STRING_POOL_TYPE: {
+        const uintptr_t pool_address =
+            reinterpret_cast<uintptr_t>(child_chunk.header<ResChunk_header>());
+        const uintptr_t header_address = reinterpret_cast<uintptr_t>(header);
+        if (pool_address == header_address + dtohl(header->typeStrings)) {
+          // This string pool is the type string pool.
+          status_t err = loaded_package->type_string_pool_.setTo(
+              child_chunk.header<ResStringPool_header>(), child_chunk.size());
+          if (err != NO_ERROR) {
+            LOG(ERROR) << "Corrupt package type string pool.";
+            return false;
+          }
+        } else if (pool_address == header_address + dtohl(header->keyStrings)) {
+          // This string pool is the key string pool.
+          status_t err = loaded_package->key_string_pool_.setTo(
+              child_chunk.header<ResStringPool_header>(), child_chunk.size());
+          if (err != NO_ERROR) {
+            LOG(ERROR) << "Corrupt package key string pool.";
+            return false;
+          }
+        } else {
+          LOG(WARNING) << "Too many string pool chunks found in package.";
+        }
+      } 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 false;
+          }
+
+          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) << "Chunk RES_TABLE_TYPE_SPEC_TYPE is too small.";
+          return false;
+        }
+
+        if (type_spec->id == 0) {
+          LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE has invalid ID 0.";
+          return false;
+        }
+
+        // The data portion of this chunk contains entry_count 32bit entries,
+        // each one representing a set of flags.
+        // Here we only validate that the chunk is well formed.
+        const size_t entry_count = dtohl(type_spec->entryCount);
+
+        // There can only be 2^16 entries in a type, because that is the ID
+        // space for entries (EEEE) in the resource ID 0xPPTTEEEE.
+        if (entry_count > std::numeric_limits<uint16_t>::max()) {
+          LOG(ERROR) << "Too many entries in RES_TABLE_TYPE_SPEC_TYPE: " << entry_count << ".";
+          return false;
+        }
+
+        if (entry_count * sizeof(uint32_t) > chunk.data_size()) {
+          LOG(ERROR) << "Chunk too small to hold entries in RES_TABLE_TYPE_SPEC_TYPE.";
+          return false;
+        }
+
+        last_type_idx = type_spec->id - 1;
+        types_builder = util::make_unique<TypeSpecPtrBuilder>(type_spec);
+      } break;
+
+      case RES_TABLE_TYPE_TYPE: {
+        const ResTable_type* type = child_chunk.header<ResTable_type>();
+        if (type == nullptr) {
+          LOG(ERROR) << "Chunk RES_TABLE_TYPE_TYPE is too small.";
+          return false;
+        }
+
+        if (type->id == 0) {
+          LOG(ERROR) << "Chunk RES_TABLE_TYPE_TYPE has invalid ID 0.";
+          return false;
+        }
+
+        // Type chunks must be preceded by their TypeSpec chunks.
+        if (!types_builder || type->id - 1 != last_type_idx) {
+          LOG(ERROR) << "Found RES_TABLE_TYPE_TYPE chunk without "
+                        "RES_TABLE_TYPE_SPEC_TYPE.";
+          return false;
+        }
+
+        if (!VerifyType(child_chunk)) {
+          return false;
+        }
+
+        types_builder->AddType(type);
+      } break;
+
+      default:
+        LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+        break;
+    }
+  }
+
+  // Finish the last TypeSpec.
+  if (types_builder) {
+    TypeSpecPtr type_spec_ptr = types_builder->Build();
+    if (type_spec_ptr == nullptr) {
+      LOG(ERROR) << "Too many type configurations, overflow detected.";
+      return false;
+    }
+    loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr);
+  }
+
+  if (iter.HadError()) {
+    LOG(ERROR) << iter.GetLastError();
+    return false;
+  }
+  return true;
+}
+
+bool LoadedArsc::LoadTable(const Chunk& chunk) {
+  ATRACE_CALL();
+  const ResTable_header* header = chunk.header<ResTable_header>();
+  if (header == nullptr) {
+    LOG(ERROR) << "Chunk RES_TABLE_TYPE is too small.";
+    return false;
+  }
+
+  const size_t package_count = dtohl(header->packageCount);
+  size_t packages_seen = 0;
+
+  packages_.reserve(package_count);
+
+  ChunkIterator iter(chunk.data_ptr(), chunk.data_size());
+  while (iter.HasNext()) {
+    const Chunk child_chunk = iter.Next();
+    switch (child_chunk.type()) {
+      case RES_STRING_POOL_TYPE:
+        // Only use the first string pool. Ignore others.
+        if (global_string_pool_.getError() == NO_INIT) {
+          status_t err = global_string_pool_.setTo(child_chunk.header<ResStringPool_header>(),
+                                                   child_chunk.size());
+          if (err != NO_ERROR) {
+            LOG(ERROR) << "Corrupt string pool.";
+            return false;
+          }
+        } else {
+          LOG(WARNING) << "Multiple string pool chunks found in resource table.";
+        }
+        break;
+
+      case RES_TABLE_PACKAGE_TYPE: {
+        if (packages_seen + 1 > package_count) {
+          LOG(ERROR) << "More package chunks were found than the " << package_count
+                     << " declared in the "
+                        "header.";
+          return false;
+        }
+        packages_seen++;
+
+        std::unique_ptr<LoadedPackage> loaded_package = util::make_unique<LoadedPackage>();
+        if (!LoadPackage(child_chunk, loaded_package.get())) {
+          return false;
+        }
+        packages_.push_back(std::move(loaded_package));
+      } break;
+
+      default:
+        LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+        break;
+    }
+  }
+
+  if (iter.HadError()) {
+    LOG(ERROR) << iter.GetLastError();
+    return false;
+  }
+  return true;
+}
+
+std::unique_ptr<LoadedArsc> LoadedArsc::Load(const void* data, size_t len) {
+  ATRACE_CALL();
+
+  // Not using make_unique because the constructor is private.
+  std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
+
+  ChunkIterator iter(data, len);
+  while (iter.HasNext()) {
+    const Chunk chunk = iter.Next();
+    switch (chunk.type()) {
+      case RES_TABLE_TYPE:
+        if (!loaded_arsc->LoadTable(chunk)) {
+          return {};
+        }
+        break;
+
+      default:
+        LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+        break;
+    }
+  }
+
+  if (iter.HadError()) {
+    LOG(ERROR) << iter.GetLastError();
+    return {};
+  }
+  return loaded_arsc;
+}
+
+}  // namespace android
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 7fbfffe..a30c849 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -140,7 +140,7 @@
     patch->colorsOffset = patch->yDivsOffset + (patch->numYDivs * sizeof(int32_t));
 }
 
-inline void Res_value::copyFrom_dtoh(const Res_value& src)
+void Res_value::copyFrom_dtoh(const Res_value& src)
 {
     size = dtohs(src.size);
     res0 = src.res0;
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
new file mode 100644
index 0000000..a3d67f0
--- /dev/null
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 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 APKASSETS_H_
+#define APKASSETS_H_
+
+#include <memory>
+#include <string>
+
+#include "android-base/macros.h"
+#include "ziparchive/zip_archive.h"
+
+#include "androidfw/Asset.h"
+#include "androidfw/LoadedArsc.h"
+
+namespace android {
+
+// Holds an APK.
+class ApkAssets {
+ public:
+  static std::unique_ptr<ApkAssets> Load(const std::string& path);
+
+  std::unique_ptr<Asset> Open(const std::string& path,
+                              Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const;
+
+  inline const std::string& GetPath() const { return path_; }
+
+  inline const LoadedArsc* GetLoadedArsc() const { return loaded_arsc_.get(); }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ApkAssets);
+
+  ApkAssets() = default;
+
+  struct ZipArchivePtrCloser {
+    void operator()(::ZipArchiveHandle handle) { ::CloseArchive(handle); }
+  };
+
+  using ZipArchivePtr =
+      std::unique_ptr<typename std::remove_pointer<::ZipArchiveHandle>::type, ZipArchivePtrCloser>;
+  ZipArchivePtr zip_handle_;
+  std::string path_;
+  std::unique_ptr<Asset> resources_asset_;
+  std::unique_ptr<LoadedArsc> loaded_arsc_;
+};
+
+}  // namespace android
+
+#endif /* APKASSETS_H_ */
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
new file mode 100644
index 0000000..66d5034
--- /dev/null
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2016 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_ASSETMANAGER2_H_
+#define ANDROIDFW_ASSETMANAGER2_H_
+
+#include "android-base/macros.h"
+
+#include <limits>
+#include <unordered_map>
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/Asset.h"
+#include "androidfw/AssetManager.h"
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/Util.h"
+
+namespace android {
+
+class Theme;
+
+using ApkAssetsCookie = int32_t;
+
+enum : ApkAssetsCookie {
+  kInvalidCookie = -1,
+};
+
+// Holds a bag that has been merged with its parent, if one exists.
+struct ResolvedBag {
+  // A single key-value entry in a bag.
+  struct Entry {
+    // The key, as described in ResTable_map::name.
+    uint32_t key;
+
+    Res_value value;
+
+    // Which ApkAssets this entry came from.
+    ApkAssetsCookie cookie;
+
+    ResStringPool* key_pool;
+    ResStringPool* type_pool;
+  };
+
+  // Denotes the configuration axis that this bag varies with.
+  // If a configuration changes with respect to one of these axis,
+  // the bag should be reloaded.
+  uint32_t type_spec_flags;
+
+  // The number of entries in this bag. Access them by indexing into `entries`.
+  uint32_t entry_count;
+
+  // The array of entries for this bag. An empty array is a neat trick to force alignment
+  // of the Entry structs that follow this structure and avoids a bunch of casts.
+  Entry entries[0];
+};
+
+// AssetManager2 is the main entry point for accessing assets and resources.
+// AssetManager2 provides caching of resources retrieved via the underlying
+// ApkAssets.
+class AssetManager2 : public ::AAssetManager {
+ public:
+  struct ResourceName {
+    const char* package = nullptr;
+    size_t package_len = 0u;
+
+    const char* type = nullptr;
+    const char16_t* type16 = nullptr;
+    size_t type_len = 0u;
+
+    const char* entry = nullptr;
+    const char16_t* entry16 = nullptr;
+    size_t entry_len = 0u;
+  };
+
+  AssetManager2();
+
+  // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets
+  // are not owned by the AssetManager, and must have a longer lifetime.
+  //
+  // Only pass invalidate_caches=false when it is known that the structure
+  // change in ApkAssets is due to a safe addition of resources with completely
+  // new resource IDs.
+  bool SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches = true);
+
+  const std::vector<const ApkAssets*> GetApkAssets() const;
+
+  // Returns the string pool for the given asset cookie.
+  // Use the string pool returned here with a valid Res_value object of
+  // type Res_value::TYPE_STRING.
+  const ResStringPool* GetStringPoolForCookie(ApkAssetsCookie cookie) const;
+
+  // Sets/resets the configuration for this AssetManager. This will cause all
+  // caches that are related to the configuration change to be invalidated.
+  void SetConfiguration(const ResTable_config& configuration);
+
+  const ResTable_config& GetConfiguration() 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);
+
+  // 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);
+
+  // Searches the set of APKs loaded by this AssetManager and opens the first one found.
+  // `mode` controls how the file is opened.
+  // `out_cookie` is populated with the cookie of the APK this file was found in.
+  //
+  // 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);
+
+  // 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);
+
+  // 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);
+
+  // 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);
+
+  // Retrieves the best matching resource with ID `resid`. The resource value is filled into
+  // `out_value` and the configuration for the selected value is populated in `out_selected_config`.
+  // `out_flags` holds the same flags as retrieved with GetResourceFlags().
+  // If `density_override` is non-zero, the configuration to match against is overridden with that
+  // density.
+  //
+  // Returns a valid cookie if the resource was found. If the resource was not found, or if the
+  // resource was a map/bag type, then kInvalidCookie is returned. If `may_be_bag` is false,
+  // 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);
+
+  // Retrieves the best matching bag/map resource with ID `resid`.
+  // This method will resolve all parent references for this bag and merge keys with the child.
+  // To iterate over the keys, use the following idiom:
+  //
+  //  const AssetManager2::ResolvedBag* bag = asset_manager->GetBag(id);
+  //  if (bag != nullptr) {
+  //    for (auto iter = begin(bag); iter != end(bag); ++iter) {
+  //      ...
+  //    }
+  //  }
+  const ResolvedBag* GetBag(uint32_t resid);
+
+  // Creates a new Theme from this AssetManager.
+  std::unique_ptr<Theme> NewTheme();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AssetManager2);
+
+  // Finds the best entry for `resid` amongst all the ApkAssets. The entry can be a simple
+  // Res_value, or a complex map/bag type.
+  //
+  // `density_override` overrides the density of the current configuration when doing a search.
+  //
+  // When `stop_at_first_match` is true, the first match found is selected and the search
+  // terminates. This is useful for methods that just look up the name of a resource and don't
+  // care about the value. In this case, the value of `out_flags` is incomplete and should not
+  // be used.
+  //
+  // `out_flags` stores the resulting bitmask of configuration axis with which the resource
+  // value varies.
+  ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
+                            LoadedArsc::Entry* out_entry, ResTable_config* out_selected_config,
+                            uint32_t* out_flags);
+
+  // Purge all resources that are cached and vary by the configuration axis denoted by the
+  // bitmask `diff`.
+  void InvalidateCaches(uint32_t diff);
+
+  // 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_;
+
+  // The current configuration set for this AssetManager. When this changes, cached resources
+  // may need to be purged.
+  ResTable_config configuration_;
+
+  // Cached set of bags. These are cached because they can inherit keys from parent bags,
+  // which involves some calculation.
+  std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
+};
+
+class Theme {
+  friend class AssetManager2;
+
+ public:
+  // Applies the style identified by `resid` to this theme. This can be called
+  // multiple times with different styles. By default, any theme attributes that
+  // are already defined before this call are not overridden. If `force` is set
+  // to true, this behavior is changed and all theme attributes from the style at
+  // `resid` are applied.
+  // Returns false if the style failed to apply.
+  bool ApplyStyle(uint32_t resid, bool force = false);
+
+  // Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme.
+  // Returns false if the AssetManagers of the Themes were not compatible.
+  bool SetTo(const Theme& o);
+
+  void Clear();
+
+  inline const AssetManager2* GetAssetManager() const { return asset_manager_; }
+
+  // Returns a bit mask of configuration changes that will impact this
+  // theme (and thus require completely reloading it).
+  inline uint32_t GetChangingConfigurations() const { return type_spec_flags_; }
+
+  // Retrieve a value in the theme. If the theme defines this value,
+  // returns an asset cookie indicating which ApkAssets it came from
+  // and populates `out_value` with the value. If `out_flags` is non-null,
+  // populates it with a bitmask of the configuration axis the resource
+  // varies with.
+  //
+  // If the attribute is not found, returns kInvalidCookie.
+  //
+  // NOTE: This function does not do reference traversal. If you want
+  // to follow references to other resources to get the "real" value to
+  // use, you need to call ResolveReference() after this function.
+  ApkAssetsCookie GetAttribute(uint32_t resid, Res_value* out_value,
+                               uint32_t* out_flags = nullptr) const;
+
+  // This is like AssetManager2::ResolveReference(), but also takes
+  // care of resolving attribute references to the theme.
+  ApkAssetsCookie ResolveAttributeReference(Res_value* in_out_value, ApkAssetsCookie src_cookie,
+                                            uint32_t* out_last_ref = nullptr,
+                                            uint32_t* in_out_type_spec_flags = nullptr,
+                                            ResTable_config* out_selected_config = nullptr) const;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Theme);
+
+  // Called by AssetManager2.
+  explicit inline Theme(AssetManager2* asset_manager) : asset_manager_(asset_manager) {}
+
+  struct Entry {
+    ApkAssetsCookie cookie;
+    uint32_t type_spec_flags;
+    Res_value value;
+  };
+
+  struct Type {
+    // Use uint32_t for fewer cycles when loading from memory.
+    uint32_t entry_count;
+    uint32_t entry_capacity;
+    Entry entries[0];
+  };
+
+  static constexpr const size_t kPackageCount = std::numeric_limits<uint8_t>::max() + 1;
+  static constexpr const size_t kTypeCount = std::numeric_limits<uint8_t>::max() + 1;
+
+  struct Package {
+    // Each element of Type will be a dynamically sized object
+    // allocated to have the entries stored contiguously with the Type.
+    util::unique_cptr<Type> types[kTypeCount];
+  };
+
+  AssetManager2* asset_manager_;
+  uint32_t type_spec_flags_ = 0u;
+  std::unique_ptr<Package> packages_[kPackageCount];
+};
+
+inline const ResolvedBag::Entry* begin(const ResolvedBag* bag) { return bag->entries; }
+
+inline const ResolvedBag::Entry* end(const ResolvedBag* bag) {
+  return bag->entries + bag->entry_count;
+}
+
+}  // namespace android
+
+#endif /* ANDROIDFW_ASSETMANAGER2_H_ */
diff --git a/libs/androidfw/include/androidfw/ByteBucketArray.h b/libs/androidfw/include/androidfw/ByteBucketArray.h
index 87c6b12..d84a207 100644
--- a/libs/androidfw/include/androidfw/ByteBucketArray.h
+++ b/libs/androidfw/include/androidfw/ByteBucketArray.h
@@ -17,9 +17,10 @@
 #ifndef __BYTE_BUCKET_ARRAY_H
 #define __BYTE_BUCKET_ARRAY_H
 
-#include <utils/Log.h>
-#include <stdint.h>
-#include <string.h>
+#include <cstdint>
+#include <cstring>
+
+#include "android-base/logging.h"
 
 namespace android {
 
@@ -27,71 +28,65 @@
  * Stores a sparsely populated array. Has a fixed size of 256
  * (number of entries that a byte can represent).
  */
-template<typename T>
+template <typename T>
 class ByteBucketArray {
-public:
-    ByteBucketArray() : mDefault() {
-        memset(mBuckets, 0, sizeof(mBuckets));
+ public:
+  ByteBucketArray() : default_() { memset(buckets_, 0, sizeof(buckets_)); }
+
+  ~ByteBucketArray() {
+    for (size_t i = 0; i < kNumBuckets; i++) {
+      if (buckets_[i] != NULL) {
+        delete[] buckets_[i];
+      }
+    }
+    memset(buckets_, 0, sizeof(buckets_));
+  }
+
+  inline size_t size() const { return kNumBuckets * kBucketSize; }
+
+  inline const T& get(size_t index) const { return (*this)[index]; }
+
+  const T& operator[](size_t index) const {
+    if (index >= size()) {
+      return default_;
     }
 
-    ~ByteBucketArray() {
-        for (size_t i = 0; i < NUM_BUCKETS; i++) {
-            if (mBuckets[i] != NULL) {
-                delete [] mBuckets[i];
-            }
-        }
-        memset(mBuckets, 0, sizeof(mBuckets));
+    uint8_t bucket_index = static_cast<uint8_t>(index) >> 4;
+    T* bucket = buckets_[bucket_index];
+    if (bucket == NULL) {
+      return default_;
+    }
+    return bucket[0x0f & static_cast<uint8_t>(index)];
+  }
+
+  T& editItemAt(size_t index) {
+    CHECK(index < size()) << "ByteBucketArray.getOrCreate(index=" << index
+                          << ") with size=" << size();
+
+    uint8_t bucket_index = static_cast<uint8_t>(index) >> 4;
+    T* bucket = buckets_[bucket_index];
+    if (bucket == NULL) {
+      bucket = buckets_[bucket_index] = new T[kBucketSize]();
+    }
+    return bucket[0x0f & static_cast<uint8_t>(index)];
+  }
+
+  bool set(size_t index, const T& value) {
+    if (index >= size()) {
+      return false;
     }
 
-    inline size_t size() const {
-        return NUM_BUCKETS * BUCKET_SIZE;
-    }
+    editItemAt(index) = value;
+    return true;
+  }
 
-    inline const T& get(size_t index) const {
-        return (*this)[index];
-    }
+ private:
+  enum { kNumBuckets = 16, kBucketSize = 16 };
 
-    const T& operator[](size_t index) const {
-        if (index >= size()) {
-            return mDefault;
-        }
-
-        uint8_t bucketIndex = static_cast<uint8_t>(index) >> 4;
-        T* bucket = mBuckets[bucketIndex];
-        if (bucket == NULL) {
-            return mDefault;
-        }
-        return bucket[0x0f & static_cast<uint8_t>(index)];
-    }
-
-    T& editItemAt(size_t index) {
-        ALOG_ASSERT(index < size(), "ByteBucketArray.getOrCreate(index=%u) with size=%u",
-                (uint32_t) index, (uint32_t) size());
-
-        uint8_t bucketIndex = static_cast<uint8_t>(index) >> 4;
-        T* bucket = mBuckets[bucketIndex];
-        if (bucket == NULL) {
-            bucket = mBuckets[bucketIndex] = new T[BUCKET_SIZE]();
-        }
-        return bucket[0x0f & static_cast<uint8_t>(index)];
-    }
-
-    bool set(size_t index, const T& value) {
-        if (index >= size()) {
-            return false;
-        }
-
-        editItemAt(index) = value;
-        return true;
-    }
-
-private:
-    enum { NUM_BUCKETS = 16, BUCKET_SIZE = 16 };
-
-    T*  mBuckets[NUM_BUCKETS];
-    T   mDefault;
+  T* buckets_[kNumBuckets];
+  T default_;
 };
 
-} // namespace android
+}  // namespace android
 
-#endif // __BYTE_BUCKET_ARRAY_H
+#endif  // __BYTE_BUCKET_ARRAY_H
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
new file mode 100644
index 0000000..e2e56c8
--- /dev/null
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 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 LOADEDARSC_H_
+#define LOADEDARSC_H_
+
+#include <memory>
+#include <vector>
+
+#include "android-base/macros.h"
+
+#include "androidfw/ResourceTypes.h"
+
+namespace android {
+
+class Chunk;
+class LoadedPackage;
+
+// Read-only view into a resource table. This class validates all data
+// when loading, including offsets and lengths.
+class LoadedArsc {
+ public:
+  // Load the resource table from memory. The data's lifetime must out-live the
+  // object returned from this method.
+  static std::unique_ptr<LoadedArsc> Load(const void* data, size_t len);
+
+  ~LoadedArsc();
+
+  // Returns the string pool where all string resource values
+  // (Res_value::dataType == Res_value::TYPE_STRING) are indexed.
+  inline const ResStringPool* GetStringPool() const { return &global_string_pool_; }
+
+  struct Entry {
+    // 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;
+
+    // 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;
+  };
+
+  // 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, Entry* out_entry,
+                 ResTable_config* selected_config, uint32_t* out_flags) const;
+
+  // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist.
+  const std::string* GetPackageNameForId(uint32_t resid) const;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
+
+  LoadedArsc() = default;
+  bool LoadTable(const Chunk& chunk);
+
+  ResStringPool global_string_pool_;
+  std::vector<std::unique_ptr<LoadedPackage>> packages_;
+};
+
+}  // namespace android
+
+#endif /* LOADEDARSC_H_ */
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 33b91b9..c118b57 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -265,7 +265,7 @@
     uint8_t res0;
         
     // Type of the data value.
-    enum {
+    enum : uint8_t {
         // The 'data' is either 0 or 1, specifying this resource is either
         // undefined or empty, respectively.
         TYPE_NULL = 0x00,
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
new file mode 100644
index 0000000..5266d09
--- /dev/null
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 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 UTIL_H_
+#define UTIL_H_
+
+#include <cstdlib>
+#include <memory>
+
+#include "android-base/macros.h"
+
+namespace android {
+namespace util {
+
+/**
+ * Makes a std::unique_ptr<> with the template parameter inferred by the
+ * compiler.
+ * This will be present in C++14 and can be removed then.
+ */
+template <typename T, class... Args>
+std::unique_ptr<T> make_unique(Args&&... args) {
+  return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
+}
+
+// Based on std::unique_ptr, but uses free() to release malloc'ed memory
+// without incurring the size increase of holding on to a custom deleter.
+template <typename T>
+class unique_cptr {
+ public:
+  using pointer = typename std::add_pointer<T>::type;
+
+  constexpr unique_cptr() : ptr_(nullptr) {}
+  constexpr unique_cptr(std::nullptr_t) : ptr_(nullptr) {}
+  explicit unique_cptr(pointer ptr) : ptr_(ptr) {}
+  unique_cptr(unique_cptr&& o) : ptr_(o.ptr_) { o.ptr_ = nullptr; }
+
+  ~unique_cptr() { std::free(reinterpret_cast<void*>(ptr_)); }
+
+  inline unique_cptr& operator=(unique_cptr&& o) {
+    if (&o == this) {
+      return *this;
+    }
+
+    std::free(reinterpret_cast<void*>(ptr_));
+    ptr_ = o.ptr_;
+    o.ptr_ = nullptr;
+    return *this;
+  }
+
+  inline unique_cptr& operator=(std::nullptr_t) {
+    std::free(reinterpret_cast<void*>(ptr_));
+    ptr_ = nullptr;
+    return *this;
+  }
+
+  pointer release() {
+    pointer result = ptr_;
+    ptr_ = nullptr;
+    return result;
+  }
+
+  inline pointer get() const { return ptr_; }
+
+  void reset(pointer ptr = pointer()) {
+    if (ptr == ptr_) {
+      return;
+    }
+
+    pointer old_ptr = ptr_;
+    ptr_ = ptr;
+    std::free(reinterpret_cast<void*>(old_ptr));
+  }
+
+  inline void swap(unique_cptr& o) { std::swap(ptr_, o.ptr_); }
+
+  inline explicit operator bool() const { return ptr_ != nullptr; }
+
+  inline typename std::add_lvalue_reference<T>::type operator*() const { return *ptr_; }
+
+  inline pointer operator->() const { return ptr_; }
+
+  inline bool operator==(const unique_cptr& o) const { return ptr_ == o.ptr_; }
+
+  inline bool operator==(std::nullptr_t) const { return ptr_ == nullptr; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(unique_cptr);
+
+  pointer ptr_;
+};
+
+inline uint8_t get_package_id(uint32_t resid) {
+  return static_cast<uint8_t>((resid >> 24) & 0x000000ffu);
+}
+
+// The type ID is 1-based, so if the returned value is 0 it is invalid.
+inline uint8_t get_type_id(uint32_t resid) {
+  return static_cast<uint8_t>((resid >> 16) & 0x000000ffu);
+}
+
+inline uint16_t get_entry_id(uint32_t resid) { return static_cast<uint16_t>(resid & 0x0000ffffu); }
+
+inline bool is_internal_id(uint32_t resid) {
+  return (resid & 0xffff0000u) != 0 && (resid & 0x00ff0000u) == 0;
+}
+
+inline bool is_valid_resid(uint32_t resid) {
+  return (resid & 0x00ff0000u) != 0 && (resid & 0xff000000u) != 0;
+}
+
+}  // namespace util
+}  // namespace android
+
+#endif /* UTIL_H_ */
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index d91a133..6754cd8 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -21,22 +21,31 @@
 LOCAL_PATH:= $(call my-dir)
 
 testFiles := \
+    ApkAssets_test.cpp \
     AppAsLib_test.cpp \
     Asset_test.cpp \
+    AssetManager2_test.cpp \
     AttributeFinder_test.cpp \
     AttributeResolution_test.cpp \
     ByteBucketArray_test.cpp \
     Config_test.cpp \
     ConfigLocale_test.cpp \
     Idmap_test.cpp \
-    Main.cpp \
+    LoadedArsc_test.cpp \
     ResTable_test.cpp \
     Split_test.cpp \
     TestHelpers.cpp \
+    TestMain.cpp \
     Theme_test.cpp \
     TypeWrappers_test.cpp \
     ZipUtils_test.cpp
 
+benchmarkFiles := \
+    AssetManager2_bench.cpp \
+    BenchMain.cpp \
+    TestHelpers.cpp \
+    Theme_bench.cpp
+
 androidfw_test_cflags := \
     -Wall \
     -Werror \
@@ -89,5 +98,25 @@
 LOCAL_PICKUP_FILES := $(LOCAL_PATH)/data
 
 include $(BUILD_NATIVE_TEST)
+
+# ==========================================================
+# Build the device benchmarks: libandroidfw_benchmarks
+# ==========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libandroidfw_benchmarks
+LOCAL_CFLAGS := $(androidfw_test_cflags)
+LOCAL_SRC_FILES := $(benchmarkFiles)
+LOCAL_STATIC_LIBRARIES := \
+    libgoogle-benchmark
+LOCAL_SHARED_LIBRARIES := \
+    libandroidfw \
+    libbase \
+    libcutils \
+    libutils \
+    libziparchive
+LOCAL_PICKUP_FILES := $(LOCAL_PATH)/data
+
+include $(BUILD_NATIVE_TEST)
 endif # Not SDK_ONLY
 
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
new file mode 100644
index 0000000..3a1fc8f
--- /dev/null
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 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 "androidfw/ApkAssets.h"
+
+#include "TestHelpers.h"
+#include "data/basic/R.h"
+
+using com::android::basic::R;
+
+namespace android {
+
+TEST(ApkAssetsTest, LoadApk) {
+  std::unique_ptr<ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
+  ASSERT_NE(nullptr, loaded_apk);
+
+  std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml");
+  ASSERT_NE(nullptr, asset);
+}
+
+}  // namespace android
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
new file mode 100644
index 0000000..9ff9478
--- /dev/null
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2016 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 "androidfw/ApkAssets.h"
+#include "androidfw/AssetManager.h"
+#include "androidfw/AssetManager2.h"
+#include "androidfw/ResourceTypes.h"
+
+#include "TestHelpers.h"
+#include "data/basic/R.h"
+#include "data/styles/R.h"
+
+namespace basic = com::android::basic;
+namespace app = com::android::app;
+
+namespace android {
+
+constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk";
+
+static void BM_AssetManagerLoadAssets(benchmark::State& state) {
+  std::string path = GetTestDataPath() + "/basic/basic.apk";
+  while (state.KeepRunning()) {
+    std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path);
+    AssetManager2 assets;
+    assets.SetApkAssets({apk.get()});
+  }
+}
+BENCHMARK(BM_AssetManagerLoadAssets);
+
+static void BM_AssetManagerLoadAssetsOld(benchmark::State& state) {
+  String8 path((GetTestDataPath() + "/basic/basic.apk").data());
+  while (state.KeepRunning()) {
+    AssetManager assets;
+    assets.addAssetPath(path, nullptr /* cookie */, false /* appAsLib */,
+                        false /* isSystemAsset */);
+
+    // Force creation.
+    assets.getResources(true);
+  }
+}
+BENCHMARK(BM_AssetManagerLoadAssetsOld);
+
+static void BM_AssetManagerLoadFrameworkAssets(benchmark::State& state) {
+  std::string path = kFrameworkPath;
+  while (state.KeepRunning()) {
+    std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path);
+    AssetManager2 assets;
+    assets.SetApkAssets({apk.get()});
+  }
+}
+BENCHMARK(BM_AssetManagerLoadFrameworkAssets);
+
+static void BM_AssetManagerLoadFrameworkAssetsOld(benchmark::State& state) {
+  String8 path(kFrameworkPath);
+  while (state.KeepRunning()) {
+    AssetManager assets;
+    assets.addAssetPath(path, nullptr /* cookie */, false /* appAsLib */,
+                        false /* isSystemAsset */);
+
+    // Force creation.
+    assets.getResources(true);
+  }
+}
+BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld);
+
+static void BM_AssetManagerGetResource(benchmark::State& state) {
+  std::unique_ptr<ApkAssets> apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
+  if (apk == nullptr) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  AssetManager2 assets;
+  assets.SetApkAssets({apk.get()});
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  while (state.KeepRunning()) {
+    assets.GetResource(basic::R::integer::number1, false /* may_be_bag */,
+                       0u /* density_override */, &value, &selected_config, &flags);
+  }
+}
+BENCHMARK(BM_AssetManagerGetResource);
+
+static void BM_AssetManagerGetResourceOld(benchmark::State& state) {
+  AssetManager assets;
+  if (!assets.addAssetPath(String8((GetTestDataPath() + "/basic/basic.apk").data()),
+                           nullptr /* cookie */, false /* appAsLib */,
+                           false /* isSystemAssets */)) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  const ResTable& table = assets.getResources(true);
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  while (state.KeepRunning()) {
+    table.getResource(basic::R::integer::number1, &value, false /* may_be_bag */,
+                      0u /* density_override */, &flags, &selected_config);
+  }
+}
+BENCHMARK(BM_AssetManagerGetResourceOld);
+
+constexpr static const uint32_t kStringOkId = 0x0104000au;
+
+static void BM_AssetManagerGetResourceFrameworkLocale(benchmark::State& state) {
+  std::unique_ptr<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));
+  memcpy(config.language, "fr", 2);
+  assets.SetConfiguration(config);
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  while (state.KeepRunning()) {
+    assets.GetResource(kStringOkId, false /* may_be_bag */, 0u /* density_override */, &value,
+                       &selected_config, &flags);
+  }
+}
+BENCHMARK(BM_AssetManagerGetResourceFrameworkLocale);
+
+static void BM_AssetManagerGetResourceFrameworkLocaleOld(benchmark::State& state) {
+  AssetManager assets;
+  if (!assets.addAssetPath(String8((GetTestDataPath() + "/basic/basic.apk").data()),
+                           nullptr /* cookie */, false /* appAsLib */,
+                           false /* isSystemAssets */)) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  ResTable_config config;
+  memset(&config, 0, sizeof(config));
+  memcpy(config.language, "fr", 2);
+  assets.setConfiguration(config, nullptr);
+
+  const ResTable& table = assets.getResources(true);
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  while (state.KeepRunning()) {
+    table.getResource(kStringOkId, &value, false /* may_be_bag */, 0u /* density_override */,
+                      &flags, &selected_config);
+  }
+}
+BENCHMARK(BM_AssetManagerGetResourceFrameworkLocaleOld);
+
+static void BM_AssetManagerGetBag(benchmark::State& state) {
+  std::unique_ptr<ApkAssets> apk = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
+  if (apk == nullptr) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  AssetManager2 assets;
+  assets.SetApkAssets({apk.get()});
+
+  while (state.KeepRunning()) {
+    const ResolvedBag* bag = assets.GetBag(app::R::style::StyleTwo);
+    const auto bag_end = end(bag);
+    for (auto iter = begin(bag); iter != bag_end; ++iter) {
+      uint32_t key = iter->key;
+      Res_value value = iter->value;
+      benchmark::DoNotOptimize(key);
+      benchmark::DoNotOptimize(value);
+    }
+  }
+}
+BENCHMARK(BM_AssetManagerGetBag);
+
+static void BM_AssetManagerGetBagOld(benchmark::State& state) {
+  AssetManager assets;
+  if (!assets.addAssetPath(String8((GetTestDataPath() + "/styles/styles.apk").data()),
+                           nullptr /* cookie */, false /* appAsLib */,
+                           false /* isSystemAssets */)) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  const ResTable& table = assets.getResources(true);
+
+  while (state.KeepRunning()) {
+    const ResTable::bag_entry* bag_begin;
+    const ssize_t N = table.lockBag(app::R::style::StyleTwo, &bag_begin);
+    const ResTable::bag_entry* const bag_end = bag_begin + N;
+    for (auto iter = bag_begin; iter != bag_end; ++iter) {
+      uint32_t key = iter->map.name.ident;
+      Res_value value = iter->map.value;
+      benchmark::DoNotOptimize(key);
+      benchmark::DoNotOptimize(value);
+    }
+    table.unlockBag(bag_begin);
+  }
+}
+BENCHMARK(BM_AssetManagerGetBagOld);
+
+}  // namespace android
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
new file mode 100644
index 0000000..39c5381
--- /dev/null
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2016 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 "androidfw/AssetManager2.h"
+#include "androidfw/AssetManager.h"
+
+#include "android-base/logging.h"
+
+#include "TestHelpers.h"
+#include "data/basic/R.h"
+#include "data/styles/R.h"
+
+namespace basic = com::android::basic;
+namespace app = com::android::app;
+
+namespace android {
+
+class AssetManager2Test : public ::testing::Test {
+ public:
+  void SetUp() override {
+    basic_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
+    ASSERT_NE(nullptr, basic_assets_);
+
+    basic_de_fr_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic_de_fr.apk");
+    ASSERT_NE(nullptr, basic_de_fr_assets_);
+
+    style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
+    ASSERT_NE(nullptr, style_assets_);
+  }
+
+ protected:
+  std::unique_ptr<ApkAssets> basic_assets_;
+  std::unique_ptr<ApkAssets> basic_de_fr_assets_;
+  std::unique_ptr<ApkAssets> style_assets_;
+};
+
+TEST_F(AssetManager2Test, FindsResourcesFromSingleApkAssets) {
+  ResTable_config desired_config;
+  memset(&desired_config, 0, sizeof(desired_config));
+  desired_config.language[0] = 'd';
+  desired_config.language[1] = 'e';
+
+  AssetManager2 assetmanager;
+  assetmanager.SetConfiguration(desired_config);
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ApkAssetsCookie cookie =
+      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
+                               0 /*density_override*/, &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+
+  // Came from our ApkAssets.
+  EXPECT_EQ(0, cookie);
+
+  // It is the default config.
+  EXPECT_EQ(0, selected_config.language[0]);
+  EXPECT_EQ(0, selected_config.language[1]);
+
+  // It is a string.
+  EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+}
+
+TEST_F(AssetManager2Test, FindsResourcesFromMultipleApkAssets) {
+  ResTable_config desired_config;
+  memset(&desired_config, 0, sizeof(desired_config));
+  desired_config.language[0] = 'd';
+  desired_config.language[1] = 'e';
+
+  AssetManager2 assetmanager;
+  assetmanager.SetConfiguration(desired_config);
+  assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()});
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ApkAssetsCookie cookie =
+      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
+                               0 /*density_override*/, &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+
+  // Came from our de_fr ApkAssets.
+  EXPECT_EQ(1, cookie);
+
+  // The configuration is german.
+  EXPECT_EQ('d', selected_config.language[0]);
+  EXPECT_EQ('e', selected_config.language[1]);
+
+  // It is a string.
+  EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+}
+
+TEST_F(AssetManager2Test, FindsBagResourcesFromSingleApkAssets) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  const ResolvedBag* bag = assetmanager.GetBag(basic::R::array::integerArray1);
+  ASSERT_NE(nullptr, bag);
+  ASSERT_EQ(3u, bag->entry_count);
+
+  EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[0].value.dataType);
+  EXPECT_EQ(1u, bag->entries[0].value.data);
+  EXPECT_EQ(0, bag->entries[0].cookie);
+
+  EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[1].value.dataType);
+  EXPECT_EQ(2u, bag->entries[1].value.data);
+  EXPECT_EQ(0, bag->entries[1].cookie);
+
+  EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[2].value.dataType);
+  EXPECT_EQ(3u, bag->entries[2].value.data);
+  EXPECT_EQ(0, bag->entries[2].cookie);
+}
+
+TEST_F(AssetManager2Test, MergesStylesWithParentFromSingleApkAssets) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({style_assets_.get()});
+
+  const ResolvedBag* bag_one = assetmanager.GetBag(app::R::style::StyleOne);
+  ASSERT_NE(nullptr, bag_one);
+  ASSERT_EQ(2u, bag_one->entry_count);
+
+  EXPECT_EQ(app::R::attr::attr_one, bag_one->entries[0].key);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[0].value.dataType);
+  EXPECT_EQ(1u, bag_one->entries[0].value.data);
+  EXPECT_EQ(0, bag_one->entries[0].cookie);
+
+  EXPECT_EQ(app::R::attr::attr_two, bag_one->entries[1].key);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[1].value.dataType);
+  EXPECT_EQ(2u, bag_one->entries[1].value.data);
+  EXPECT_EQ(0, bag_one->entries[1].cookie);
+
+  const ResolvedBag* bag_two = assetmanager.GetBag(app::R::style::StyleTwo);
+  ASSERT_NE(nullptr, bag_two);
+  ASSERT_EQ(5u, bag_two->entry_count);
+
+  // attr_one is inherited from StyleOne.
+  EXPECT_EQ(app::R::attr::attr_one, bag_two->entries[0].key);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[0].value.dataType);
+  EXPECT_EQ(1u, bag_two->entries[0].value.data);
+  EXPECT_EQ(0, bag_two->entries[0].cookie);
+
+  // attr_two should be overridden from StyleOne by StyleTwo.
+  EXPECT_EQ(app::R::attr::attr_two, bag_two->entries[1].key);
+  EXPECT_EQ(Res_value::TYPE_STRING, bag_two->entries[1].value.dataType);
+  EXPECT_EQ(0, bag_two->entries[1].cookie);
+  EXPECT_EQ(std::string("string"), GetStringFromPool(assetmanager.GetStringPoolForCookie(0),
+                                                     bag_two->entries[1].value.data));
+
+  // The rest are new attributes.
+
+  EXPECT_EQ(app::R::attr::attr_three, bag_two->entries[2].key);
+  EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, bag_two->entries[2].value.dataType);
+  EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[2].value.data);
+  EXPECT_EQ(0, bag_two->entries[2].cookie);
+
+  EXPECT_EQ(app::R::attr::attr_five, bag_two->entries[3].key);
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, bag_two->entries[3].value.dataType);
+  EXPECT_EQ(app::R::string::string_one, bag_two->entries[3].value.data);
+  EXPECT_EQ(0, bag_two->entries[3].cookie);
+
+  EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[4].key);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[4].value.dataType);
+  EXPECT_EQ(3u, bag_two->entries[4].value.data);
+  EXPECT_EQ(0, bag_two->entries[4].cookie);
+}
+
+TEST_F(AssetManager2Test, FindsBagResourcesFromMultipleApkAssets) {}
+
+TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {}
+
+TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {}
+
+}  // namespace android
diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp
index 7550517..1ff2ed4 100644
--- a/libs/androidfw/tests/AttributeResolution_test.cpp
+++ b/libs/androidfw/tests/AttributeResolution_test.cpp
@@ -205,4 +205,5 @@
   EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
 }
 
-}  // namespace android
+} // namespace android
+
diff --git a/libs/androidfw/tests/BenchMain.cpp b/libs/androidfw/tests/BenchMain.cpp
new file mode 100644
index 0000000..105c5f9
--- /dev/null
+++ b/libs/androidfw/tests/BenchMain.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 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 <iostream>
+
+#include "benchmark/benchmark.h"
+
+#include "TestHelpers.h"
+
+int main(int argc, char** argv) {
+  ::benchmark::Initialize(&argc, argv);
+  ::android::InitializeTest(&argc, argv);
+
+  std::cerr << "using --testdata=" << ::android::GetTestDataPath() << "\n";
+
+  size_t result = ::benchmark::RunSpecifiedBenchmarks();
+  return result == 0 ? 1 : 0;
+}
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
new file mode 100644
index 0000000..47b3894
--- /dev/null
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 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 "androidfw/LoadedArsc.h"
+
+#include "android-base/file.h"
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+
+#include "TestHelpers.h"
+#include "data/basic/R.h"
+#include "data/styles/R.h"
+
+namespace app = com::android::app;
+namespace basic = com::android::basic;
+
+namespace android {
+
+TEST(LoadedArscTest, LoadSinglePackageArsc) {
+  base::ScopedLogSeverity _log(base::LogSeverity::DEBUG);
+  std::string contents;
+  ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", "resources.arsc",
+                                      &contents));
+
+  std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+  ASSERT_NE(nullptr, loaded_arsc);
+
+  ResTable_config config;
+  memset(&config, 0, sizeof(config));
+  config.sdkVersion = 24;
+
+  LoadedArsc::Entry entry;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ASSERT_TRUE(
+      loaded_arsc->FindEntry(app::R::string::string_one, config, &entry, &selected_config, &flags));
+  ASSERT_NE(nullptr, entry.entry);
+}
+
+TEST(LoadedArscTest, FindDefaultEntry) {
+  base::ScopedLogSeverity _log(base::LogSeverity::DEBUG);
+  std::string contents;
+  ASSERT_TRUE(
+      ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents));
+
+  std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+  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';
+
+  LoadedArsc::Entry entry;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry,
+                                     &selected_config, &flags));
+  ASSERT_NE(nullptr, entry.entry);
+}
+
+// structs with size fields (like Res_value, ResTable_entry) should be
+// backwards and forwards compatible (aka checking the size field against
+// sizeof(Res_value) might not be backwards compatible.
+TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); }
+
+}  // namespace android
diff --git a/libs/androidfw/tests/Main.cpp b/libs/androidfw/tests/Main.cpp
deleted file mode 100644
index 6a50691..0000000
--- a/libs/androidfw/tests/Main.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2016 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 <libgen.h>
-
-#include <iostream>
-#include <memory>
-#include <string>
-
-#include "android-base/file.h"
-#include "android-base/strings.h"
-#include "gtest/gtest.h"
-
-#include "TestHelpers.h"
-
-// Extract the directory of the current executable path.
-static std::string GetExecutableDir() {
-  const std::string path = android::base::GetExecutablePath();
-  std::unique_ptr<char, decltype(&std::free)> mutable_path = {
-      strdup(path.c_str()), std::free};
-  std::string executable_dir = dirname(mutable_path.get());
-  return executable_dir;
-}
-
-int main(int argc, char** argv) {
-  ::testing::InitGoogleTest(&argc, argv);
-
-  // Set the default test data path to be the executable path directory.
-  android::SetTestDataPath(GetExecutableDir());
-
-  const char* command = argv[0];
-  ++argv;
-  --argc;
-
-  while (argc > 0) {
-    const std::string arg = *argv;
-    if (android::base::StartsWith(arg, "--testdata=")) {
-      android::SetTestDataPath(arg.substr(strlen("--testdata=")));
-    } else if (arg == "-h" || arg == "--help") {
-      std::cerr
-          << "\nAdditional options specific to this test:\n"
-             "  --testdata=[PATH]\n"
-             "      Specify the location of test data used within the tests.\n";
-      return 1;
-    } else {
-      std::cerr << command << ": Unrecognized argument '" << *argv << "'.\n";
-      return 1;
-    }
-
-    --argc;
-    ++argv;
-  }
-
-  std::cerr << "using --testdata=" << android::GetTestDataPath() << "\n";
-  return RUN_ALL_TESTS();
-}
diff --git a/libs/androidfw/tests/TestHelpers.cpp b/libs/androidfw/tests/TestHelpers.cpp
index 2c834b1..1e763a5 100644
--- a/libs/androidfw/tests/TestHelpers.cpp
+++ b/libs/androidfw/tests/TestHelpers.cpp
@@ -16,15 +16,51 @@
 
 #include "TestHelpers.h"
 
+#include <libgen.h>
 #include <unistd.h>
 
+#include <memory>
+#include <string>
+
+#include "android-base/file.h"
 #include "android-base/logging.h"
+#include "android-base/strings.h"
 #include "ziparchive/zip_archive.h"
 
 namespace android {
 
 static std::string sTestDataPath;
 
+// Extract the directory of the current executable path.
+static std::string GetExecutableDir() {
+  const std::string path = base::GetExecutablePath();
+  std::unique_ptr<char, decltype(&std::free)> mutable_path = {strdup(path.c_str()), std::free};
+  std::string executable_dir = dirname(mutable_path.get());
+  return executable_dir;
+}
+
+void InitializeTest(int* argc, char** argv) {
+  // Set the default test data path to be the executable path directory.
+  SetTestDataPath(GetExecutableDir());
+
+  for (int i = 1; i < *argc; i++) {
+    const std::string arg = argv[i];
+    if (base::StartsWith(arg, "--testdata=")) {
+      SetTestDataPath(arg.substr(strlen("--testdata=")));
+      for (int j = i; j != *argc; j++) {
+        argv[j] = argv[j + 1];
+      }
+      --(*argc);
+      --i;
+    } else if (arg == "-h" || arg == "--help") {
+      std::cerr << "\nAdditional options specific to this test:\n"
+                   "  --testdata=[PATH]\n"
+                   "      Specify the location of test data used within the tests.\n";
+      exit(1);
+    }
+  }
+}
+
 void SetTestDataPath(const std::string& path) { sTestDataPath = path; }
 
 const std::string& GetTestDataPath() {
@@ -90,4 +126,9 @@
   return ::testing::AssertionSuccess() << actual_str.string();
 }
 
+std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx) {
+  String8 str = pool->string8ObjectAt(idx);
+  return std::string(str.string(), str.length());
+}
+
 }  // namespace android
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
index d9cee22..a11ea84 100644
--- a/libs/androidfw/tests/TestHelpers.h
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -35,6 +35,8 @@
 
 namespace android {
 
+void InitializeTest(int* argc, char** argv);
+
 enum { MAY_NOT_BE_BAG = false };
 
 void SetTestDataPath(const std::string& path);
@@ -56,6 +58,8 @@
 ::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id,
                                          const char* expected_str);
 
+std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx);
+
 }  // namespace android
 
 #endif  // TEST_HELPERS_H_
diff --git a/libs/androidfw/tests/TestMain.cpp b/libs/androidfw/tests/TestMain.cpp
new file mode 100644
index 0000000..d1c0f60
--- /dev/null
+++ b/libs/androidfw/tests/TestMain.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 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 <iostream>
+
+#include "TestHelpers.h"
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  ::android::InitializeTest(&argc, argv);
+
+  std::cerr << "using --testdata=" << ::android::GetTestDataPath() << "\n";
+
+  return RUN_ALL_TESTS();
+}
diff --git a/libs/androidfw/tests/Theme_bench.cpp b/libs/androidfw/tests/Theme_bench.cpp
new file mode 100644
index 0000000..c471be6
--- /dev/null
+++ b/libs/androidfw/tests/Theme_bench.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 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 "androidfw/ApkAssets.h"
+#include "androidfw/AssetManager.h"
+#include "androidfw/AssetManager2.h"
+#include "androidfw/ResourceTypes.h"
+
+namespace android {
+
+constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk";
+constexpr const static uint32_t kStyleId = 0x01030237u;  // android:style/Theme.Material.Light
+constexpr const static uint32_t kAttrId = 0x01010030u;   // android:attr/colorForeground
+
+static void BM_ThemeApplyStyleFramework(benchmark::State& state) {
+  std::unique_ptr<ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
+  if (apk == nullptr) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  AssetManager2 assets;
+  assets.SetApkAssets({apk.get()});
+
+  while (state.KeepRunning()) {
+    auto theme = assets.NewTheme();
+    theme->ApplyStyle(kStyleId, false /* force */);
+  }
+}
+BENCHMARK(BM_ThemeApplyStyleFramework);
+
+static void BM_ThemeApplyStyleFrameworkOld(benchmark::State& state) {
+  AssetManager assets;
+  if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /* cookie */, false /* appAsLib */,
+                           true /* isSystemAsset */)) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  const ResTable& res_table = assets.getResources(true);
+
+  while (state.KeepRunning()) {
+    std::unique_ptr<ResTable::Theme> theme{new ResTable::Theme(res_table)};
+    theme->applyStyle(kStyleId, false /* force */);
+  }
+}
+BENCHMARK(BM_ThemeApplyStyleFrameworkOld);
+
+static void BM_ThemeGetAttribute(benchmark::State& state) {
+  std::unique_ptr<ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
+
+  AssetManager2 assets;
+  assets.SetApkAssets({apk.get()});
+
+  auto theme = assets.NewTheme();
+  theme->ApplyStyle(kStyleId, false /* force */);
+
+  Res_value value;
+  uint32_t flags;
+
+  while (state.KeepRunning()) {
+    theme->GetAttribute(kAttrId, &value, &flags);
+  }
+}
+BENCHMARK(BM_ThemeGetAttribute);
+
+static void BM_ThemeGetAttributeOld(benchmark::State& state) {
+  AssetManager assets;
+  assets.addAssetPath(String8(kFrameworkPath), nullptr /* cookie */, false /* appAsLib */,
+                      true /* isSystemAsset */);
+  const ResTable& res_table = assets.getResources(true);
+  std::unique_ptr<ResTable::Theme> theme{new ResTable::Theme(res_table)};
+  theme->applyStyle(kStyleId, false /* force */);
+
+  Res_value value;
+  uint32_t flags;
+
+  while (state.KeepRunning()) {
+    theme->getAttribute(kAttrId, &value, &flags);
+  }
+}
+BENCHMARK(BM_ThemeGetAttributeOld);
+
+}  // namespace android
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index 3774657..c0011b6d 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -14,59 +14,221 @@
  * limitations under the License.
  */
 
-#include "androidfw/ResourceTypes.h"
+#include "androidfw/AssetManager2.h"
 
-#include "utils/String16.h"
-#include "utils/String8.h"
+#include "android-base/logging.h"
 
 #include "TestHelpers.h"
-#include "data/app/R.h"
-#include "data/system/R.h"
+#include "data/styles/R.h"
 
 namespace app = com::android::app;
 
 namespace android {
 
-/**
- * TODO(adamlesinski): Enable when fixed.
- */
-TEST(ThemeTest, DISABLED_shouldCopyThemeFromDifferentResTable) {
-  ResTable table;
+class ThemeTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
+    ASSERT_NE(nullptr, style_assets_);
+  }
 
-  std::string system_contents;
-  ASSERT_TRUE(ReadFileFromZipToString("/system/system.apk", "resources.arsc",
-                                      &system_contents));
-  ASSERT_EQ(NO_ERROR,
-            table.add(system_contents.data(), system_contents.size()));
+ protected:
+  std::unique_ptr<ApkAssets> style_assets_;
+};
 
-  std::string app_contents;
-  ASSERT_TRUE(ReadFileFromZipToString("/basic/basic.apk", "resources.arsc",
-                                      &app_contents));
-  ASSERT_EQ(NO_ERROR, table.add(app_contents.data(), app_contents.size()));
+TEST_F(ThemeTest, EmptyTheme) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({style_assets_.get()});
 
-  ResTable::Theme theme1(table);
-  ASSERT_EQ(NO_ERROR, theme1.applyStyle(app::R::style::Theme_One));
-  Res_value val;
-  ASSERT_GE(theme1.getAttribute(android::R::attr::background, &val), 0);
-  ASSERT_EQ(Res_value::TYPE_INT_COLOR_RGB8, val.dataType);
-  ASSERT_EQ(uint32_t(0xffff0000), val.data);
-  ASSERT_GE(theme1.getAttribute(app::R::attr::number, &val), 0);
-  ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
-  ASSERT_EQ(uint32_t(1), val.data);
+  std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+  EXPECT_EQ(0u, theme->GetChangingConfigurations());
+  EXPECT_EQ(&assetmanager, theme->GetAssetManager());
 
-  ResTable table2;
-  ASSERT_EQ(NO_ERROR,
-            table2.add(system_contents.data(), system_contents.size()));
-  ASSERT_EQ(NO_ERROR, table2.add(app_contents.data(), app_contents.size()));
+  Res_value value;
+  uint32_t flags;
+  EXPECT_EQ(kInvalidCookie, theme->GetAttribute(app::R::attr::attr_one, &value, &flags));
+}
 
-  ResTable::Theme theme2(table2);
-  ASSERT_EQ(NO_ERROR, theme2.setTo(theme1));
-  ASSERT_GE(theme2.getAttribute(android::R::attr::background, &val), 0);
-  ASSERT_EQ(Res_value::TYPE_INT_COLOR_RGB8, val.dataType);
-  ASSERT_EQ(uint32_t(0xffff0000), val.data);
-  ASSERT_GE(theme2.getAttribute(app::R::attr::number, &val), 0);
-  ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
-  ASSERT_EQ(uint32_t(1), val.data);
+TEST_F(ThemeTest, SingleThemeNoParent) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({style_assets_.get()});
+
+  std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleOne));
+
+  Res_value value;
+  uint32_t flags;
+  ApkAssetsCookie cookie;
+
+  cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(1u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+  cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(2u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+}
+
+TEST_F(ThemeTest, SingleThemeWithParent) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({style_assets_.get()});
+
+  std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
+
+  Res_value value;
+  uint32_t flags;
+  ApkAssetsCookie cookie;
+
+  cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(1u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+  cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+  EXPECT_EQ(0, cookie);
+  EXPECT_EQ(std::string("string"),
+            GetStringFromPool(assetmanager.GetStringPoolForCookie(0), value.data));
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+  // This attribute should point to an attr_indirect, so the result should be 3.
+  cookie = theme->GetAttribute(app::R::attr::attr_three, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(3u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+}
+
+TEST_F(ThemeTest, MultipleThemesOverlaidNotForce) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({style_assets_.get()});
+
+  std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree));
+
+  Res_value value;
+  uint32_t flags;
+  ApkAssetsCookie cookie;
+
+  // attr_one is still here from the base.
+  cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(1u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+  // check for the new attr_six
+  cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(6u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+  // check for the old attr_five (force=true was not used).
+  cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+  EXPECT_EQ(app::R::string::string_one, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+}
+
+TEST_F(ThemeTest, MultipleThemesOverlaidForced) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({style_assets_.get()});
+
+  std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree, true /* force */));
+
+  Res_value value;
+  uint32_t flags;
+  ApkAssetsCookie cookie;
+
+  // attr_one is still here from the base.
+  cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(1u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+  // check for the new attr_six
+  cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(6u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+  // check for the new attr_five (force=true was used).
+  cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(5u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+}
+
+TEST_F(ThemeTest, CopyThemeSameAssetManager) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({style_assets_.get()});
+
+  std::unique_ptr<Theme> theme_one = assetmanager.NewTheme();
+  ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne));
+
+  Res_value value;
+  uint32_t flags;
+  ApkAssetsCookie cookie;
+
+  // attr_one is still here from the base.
+  cookie = theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(1u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+  // attr_six is not here.
+  EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags));
+
+  std::unique_ptr<Theme> theme_two = assetmanager.NewTheme();
+  ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree));
+
+  // Copy the theme to theme_one.
+  ASSERT_TRUE(theme_one->SetTo(*theme_two));
+
+  // Clear theme_two to make sure we test that there WAS a copy.
+  theme_two->Clear();
+
+  // attr_one is now not here.
+  EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags));
+
+  // attr_six is now here because it was copied.
+  cookie = theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(6u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+}
+
+TEST_F(ThemeTest, FailToCopyThemeWithDifferentAssetManager) {
+  AssetManager2 assetmanager_one;
+  assetmanager_one.SetApkAssets({style_assets_.get()});
+
+  AssetManager2 assetmanager_two;
+  assetmanager_two.SetApkAssets({style_assets_.get()});
+
+  auto theme_one = assetmanager_one.NewTheme();
+  ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne));
+
+  auto theme_two = assetmanager_two.NewTheme();
+  ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleTwo));
+
+  EXPECT_FALSE(theme_one->SetTo(*theme_two));
 }
 
 }  // namespace android
diff --git a/libs/androidfw/tests/data/styles/R.h b/libs/androidfw/tests/data/styles/R.h
index 4127aa0..68527c7 100644
--- a/libs/androidfw/tests/data/styles/R.h
+++ b/libs/androidfw/tests/data/styles/R.h
@@ -32,6 +32,7 @@
       attr_four = 0x7f010003u,
       attr_five = 0x7f010004u,
       attr_indirect = 0x7f010005u,
+      attr_six = 0x7f010006u,
     };
   };
 
@@ -45,6 +46,7 @@
     enum : uint32_t {
       StyleOne = 0x7f020000u,
       StyleTwo = 0x7f020001u,
+      StyleThree = 0x7f020002u,
     };
   };
 };
diff --git a/libs/androidfw/tests/data/styles/res/values/styles.xml b/libs/androidfw/tests/data/styles/res/values/styles.xml
index 70c54f6..da592f8 100644
--- a/libs/androidfw/tests/data/styles/res/values/styles.xml
+++ b/libs/androidfw/tests/data/styles/res/values/styles.xml
@@ -39,6 +39,7 @@
     <public type="style" name="StyleOne" id="0x7f020000" />
     <style name="StyleOne">
         <item name="attr_one">1</item>
+        <item name="attr_two">2</item>
     </style>
 
     <public type="style" name="StyleTwo" id="0x7f020001" />
@@ -48,5 +49,14 @@
         <item name="attr_three">?attr/attr_indirect</item>
         <item name="attr_five">@string/string_one</item>
     </style>
+    
+    <public type="attr" name="attr_six" id="0x7f010006" />
+    <attr name="attr_six" />
+    
+    <public type="style" name="StyleThree" id="0x7f020002" />
+    <style name="StyleThree">
+        <item name="attr_six">6</item>
+        <item name="attr_five">5</item>
+    </style>
 
 </resources>
diff --git a/libs/androidfw/tests/data/styles/styles.apk b/libs/androidfw/tests/data/styles/styles.apk
index 6064c48..d4ccb83 100644
--- a/libs/androidfw/tests/data/styles/styles.apk
+++ b/libs/androidfw/tests/data/styles/styles.apk
Binary files differ