libandroidfw: Add new support for shared libraries

This adds support for shared resource libraries in the new
ResTable/AssetManager implementation.

The dynamic package map encoded in resources.arsc is parsed
and stored with LoadedArsc, and combined to form a resolved table
in AssetManager2.

Benchmarks show that this implementation is an order of magnitude
faster on angler-userdebug (make libandroidfw_benchmarks).

Test: libandroidfw_tests
Change-Id: I57c80248728b63b162bf8269ac9495b53c3e7fa0
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index fb89835..ecf6bd4 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -38,6 +38,7 @@
         "ResourceTypes.cpp",
         "StreamingZipInflater.cpp",
         "TypeWrappers.cpp",
+        "Util.cpp",
         "ZipFileRO.cpp",
         "ZipUtils.cpp",
     ],
@@ -82,6 +83,9 @@
         },
         windows: {
             enabled: true,
+            cppflags: ["-Wno-missing-field-initializers"],  // The Windows compiler warns
+                                                            // incorrectly for value
+                                                            // initialization with {}.
         },
     },
 }
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 55f4c3c..9a08f63 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -28,7 +28,16 @@
 namespace android {
 
 std::unique_ptr<ApkAssets> ApkAssets::Load(const std::string& path) {
-  ATRACE_NAME("ApkAssets::Load");
+  return ApkAssets::LoadImpl(path, false /*load_as_shared_library*/);
+}
+
+std::unique_ptr<ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path) {
+  return ApkAssets::LoadImpl(path, true /*load_as_shared_library*/);
+}
+
+std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(const std::string& path,
+                                               bool load_as_shared_library) {
+  ATRACE_CALL();
   ::ZipArchiveHandle unmanaged_handle;
   int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
   if (result != 0) {
@@ -61,7 +70,7 @@
   loaded_apk->path_ = path;
   loaded_apk->loaded_arsc_ =
       LoadedArsc::Load(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/),
-                       loaded_apk->resources_asset_->getLength());
+                       loaded_apk->resources_asset_->getLength(), load_as_shared_library);
   if (loaded_apk->loaded_arsc_ == nullptr) {
     return {};
   }
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 8d65925..d2eff65 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -36,13 +36,84 @@
 bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets,
                                  bool invalidate_caches) {
   apk_assets_ = apk_assets;
+  BuildDynamicRefTable();
   if (invalidate_caches) {
     InvalidateCaches(static_cast<uint32_t>(-1));
   }
   return true;
 }
 
-const std::vector<const ApkAssets*> AssetManager2::GetApkAssets() const { return apk_assets_; }
+void AssetManager2::BuildDynamicRefTable() {
+  package_groups_.clear();
+  package_ids_.fill(0xff);
+
+  // 0x01 is reserved for the android package.
+  int next_package_id = 0x02;
+  const size_t apk_assets_count = apk_assets_.size();
+  for (size_t i = 0; i < apk_assets_count; i++) {
+    const ApkAssets* apk_asset = apk_assets_[i];
+    for (const std::unique_ptr<const LoadedPackage>& package :
+         apk_asset->GetLoadedArsc()->GetPackages()) {
+      // Get the package ID or assign one if a shared library.
+      int package_id;
+      if (package->IsDynamic()) {
+        package_id = next_package_id++;
+      } else {
+        package_id = package->GetPackageId();
+      }
+
+      // Add the mapping for package ID to index if not present.
+      uint8_t idx = package_ids_[package_id];
+      if (idx == 0xff) {
+        package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size());
+        package_groups_.push_back({});
+        package_groups_.back().dynamic_ref_table.mAssignedPackageId = package_id;
+      }
+      PackageGroup* package_group = &package_groups_[idx];
+
+      // Add the package and to the set of packages with the same ID.
+      package_group->packages_.push_back(package.get());
+      package_group->cookies_.push_back(static_cast<ApkAssetsCookie>(i));
+
+      // Add the package name -> build time ID mappings.
+      for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) {
+        String16 package_name(entry.package_name.c_str(), entry.package_name.size());
+        package_group->dynamic_ref_table.mEntries.replaceValueFor(
+            package_name, static_cast<uint8_t>(entry.package_id));
+      }
+    }
+  }
+
+  // Now assign the runtime IDs so that we have a build-time to runtime ID map.
+  const auto package_groups_end = package_groups_.end();
+  for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) {
+    const std::string& package_name = iter->packages_[0]->GetPackageName();
+    for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) {
+      iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()),
+                                          iter->dynamic_ref_table.mAssignedPackageId);
+    }
+  }
+}
+
+void AssetManager2::DumpToLog() const {
+  base::ScopedLogSeverity _log(base::INFO);
+
+  std::string list;
+  for (size_t i = 0; i < package_ids_.size(); i++) {
+    if (package_ids_[i] != 0xff) {
+      base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]);
+    }
+  }
+  LOG(INFO) << "Package ID map: " << list;
+
+  for (const auto& package_group: package_groups_) {
+      list = "";
+      for (const auto& package : package_group.packages_) {
+        base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId());
+      }
+      LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list;
+  }
+}
 
 const ResStringPool* AssetManager2::GetStringPoolForCookie(ApkAssetsCookie cookie) const {
   if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
@@ -51,6 +122,18 @@
   return apk_assets_[cookie]->GetLoadedArsc()->GetStringPool();
 }
 
+const DynamicRefTable* AssetManager2::GetDynamicRefTableForPackage(uint32_t package_id) const {
+  if (package_id >= package_ids_.size()) {
+    return nullptr;
+  }
+
+  const size_t idx = package_ids_[package_id];
+  if (idx == 0xff) {
+    return nullptr;
+  }
+  return &package_groups_[idx].dynamic_ref_table;
+}
+
 void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
   const int diff = configuration_.diff(configuration);
   configuration_ = configuration;
@@ -60,8 +143,6 @@
   }
 }
 
-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);
@@ -106,7 +187,7 @@
 }
 
 ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
-                                         bool stop_at_first_match, LoadedArsc::Entry* out_entry,
+                                         bool stop_at_first_match, LoadedArscEntry* out_entry,
                                          ResTable_config* out_selected_config,
                                          uint32_t* out_flags) {
   ATRACE_CALL();
@@ -122,48 +203,66 @@
     desired_config = &density_override_config;
   }
 
-  LoadedArsc::Entry best_entry;
+  const uint32_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) << base::StringPrintf("Invalid ID 0x%08x.", resid);
+    return kInvalidCookie;
+  }
+
+  const uint8_t idx = package_ids_[package_id];
+  if (idx == 0xff) {
+    LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid);
+    return kInvalidCookie;
+  }
+
+  LoadedArscEntry best_entry;
   ResTable_config best_config;
-  int32_t best_index = -1;
-  uint32_t cumulated_flags = 0;
+  ApkAssetsCookie best_cookie = kInvalidCookie;
+  uint32_t cumulated_flags = 0u;
 
-  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;
+  const PackageGroup& package_group = package_groups_[idx];
+  const size_t package_count = package_group.packages_.size();
+  for (size_t i = 0; i < package_count; i++) {
+    LoadedArscEntry current_entry;
     ResTable_config current_config;
-    uint32_t flags = 0;
-    if (!loaded_arsc->FindEntry(resid, *desired_config, &current_entry, &current_config, &flags)) {
+    uint32_t current_flags = 0;
+
+    const LoadedPackage* loaded_package = package_group.packages_[i];
+    if (!loaded_package->FindEntry(type_id - 1, entry_id, *desired_config, &current_entry,
+                                   &current_config, &current_flags)) {
       continue;
     }
 
-    cumulated_flags |= flags;
+    cumulated_flags |= current_flags;
 
-    if (best_index == -1 || current_config.isBetterThan(best_config, desired_config)) {
+    if (best_cookie == kInvalidCookie || current_config.isBetterThan(best_config, desired_config)) {
       best_entry = current_entry;
       best_config = current_config;
-      best_index = static_cast<int32_t>(i);
+      best_cookie = package_group.cookies_[i];
       if (stop_at_first_match) {
         break;
       }
     }
   }
 
-  if (best_index == -1) {
+  if (best_cookie == kInvalidCookie) {
     return kInvalidCookie;
   }
 
   *out_entry = best_entry;
+  out_entry->dynamic_ref_table = &package_group.dynamic_ref_table;
   *out_selected_config = best_config;
   *out_flags = cumulated_flags;
-  return best_index;
+  return best_cookie;
 }
 
 bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) {
   ATRACE_CALL();
 
-  LoadedArsc::Entry entry;
+  LoadedArscEntry entry;
   ResTable_config config;
   uint32_t flags = 0u;
   ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
@@ -172,14 +271,13 @@
     return false;
   }
 
-  const std::string* package_name =
-      apk_assets_[cookie]->GetLoadedArsc()->GetPackageNameForId(resid);
-  if (package_name == nullptr) {
+  const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid);
+  if (package == nullptr) {
     return false;
   }
 
-  out_name->package = package_name->data();
-  out_name->package_len = package_name->size();
+  out_name->package = package->GetPackageName().data();
+  out_name->package_len = package->GetPackageName().size();
 
   out_name->type = entry.type_string_ref.string8(&out_name->type_len);
   out_name->type16 = nullptr;
@@ -202,7 +300,7 @@
 }
 
 bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) {
-  LoadedArsc::Entry entry;
+  LoadedArscEntry entry;
   ResTable_config config;
   ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
                                      false /* stop_at_first_match */, &entry, &config, out_flags);
@@ -215,7 +313,7 @@
                                            uint32_t* out_flags) {
   ATRACE_CALL();
 
-  LoadedArsc::Entry entry;
+  LoadedArscEntry entry;
   ResTable_config config;
   uint32_t flags = 0u;
   ApkAssetsCookie cookie =
@@ -234,6 +332,10 @@
   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);
+
+  // Convert the package ID to the runtime assigned package ID.
+  entry.dynamic_ref_table->lookupResourceValue(out_value);
+
   *out_selected_config = config;
   *out_flags = flags;
   return cookie;
@@ -247,7 +349,7 @@
     return cached_iter->second.get();
   }
 
-  LoadedArsc::Entry entry;
+  LoadedArscEntry entry;
   ResTable_config config;
   uint32_t flags = 0u;
   ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
@@ -270,8 +372,8 @@
       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) {
+  uint32_t parent_resid = dtohl(map->parent.ident);
+  if (parent_resid == 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;
@@ -279,9 +381,18 @@
         malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))};
     ResolvedBag::Entry* new_entry = new_bag->entries;
     for (; map_entry != map_entry_end; ++map_entry) {
+      uint32_t new_key = dtohl(map_entry->name.ident);
+      if (!util::is_internal_resid(new_key)) {
+        // Attributes, arrays, etc don't have a resource id as the name. They specify
+        // other data, which would be wrong to change via a lookup.
+        if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
+          LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid);
+          return nullptr;
+        }
+      }
       new_entry->cookie = cookie;
       new_entry->value.copyFrom_dtoh(map_entry->value);
-      new_entry->key = dtohl(map_entry->name.ident);
+      new_entry->key = new_key;
       new_entry->key_pool = nullptr;
       new_entry->type_pool = nullptr;
       ++new_entry;
@@ -293,10 +404,14 @@
     return result;
   }
 
+  // In case the parent is a dynamic reference, resolve it.
+  entry.dynamic_ref_table->lookupResourceId(&parent_resid);
+
   // Get the parent and do a merge of the keys.
-  const ResolvedBag* parent_bag = GetBag(parent);
+  const ResolvedBag* parent_bag = GetBag(parent_resid);
   if (parent_bag == nullptr) {
     // Failed to get the parent that should exist.
+    LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid);
     return nullptr;
   }
 
@@ -315,7 +430,14 @@
 
   // 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);
+    uint32_t child_key = dtohl(map_entry->name.ident);
+    if (!util::is_internal_resid(child_key)) {
+      if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) {
+        LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid);
+        return nullptr;
+      }
+    }
+
     if (child_key <= parent_entry->key) {
       // Use the child key if it comes before the parent
       // or is equal to the parent (overrides).
@@ -340,9 +462,16 @@
 
   // Finish the child entries if they exist.
   while (map_entry != map_entry_end) {
+    uint32_t new_key = dtohl(map_entry->name.ident);
+    if (!util::is_internal_resid(new_key)) {
+      if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
+        LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid);
+        return nullptr;
+      }
+    }
     new_entry->cookie = cookie;
     new_entry->value.copyFrom_dtoh(map_entry->value);
-    new_entry->key = dtohl(map_entry->name.ident);
+    new_entry->key = new_key;
     new_entry->key_pool = nullptr;
     new_entry->type_pool = nullptr;
     ++map_entry;
@@ -511,12 +640,43 @@
     type_spec_flags |= entry.type_spec_flags;
 
     switch (entry.value.dataType) {
+      case Res_value::TYPE_NULL:
+        return kInvalidCookie;
+
       case Res_value::TYPE_ATTRIBUTE:
         resid = entry.value.data;
         break;
 
-      case Res_value::TYPE_NULL:
-        return kInvalidCookie;
+      case Res_value::TYPE_DYNAMIC_ATTRIBUTE: {
+        // Resolve the dynamic attribute to a normal attribute
+        // (with the right package ID).
+        resid = entry.value.data;
+        const DynamicRefTable* ref_table =
+            asset_manager_->GetDynamicRefTableForPackage(package_idx);
+        if (ref_table == nullptr || ref_table->lookupResourceId(&resid) != NO_ERROR) {
+          LOG(ERROR) << base::StringPrintf("Failed to resolve dynamic attribute 0x%08x", resid);
+          return kInvalidCookie;
+        }
+      } break;
+
+      case Res_value::TYPE_DYNAMIC_REFERENCE: {
+        // Resolve the dynamic reference to a normal reference
+        // (with the right package ID).
+        out_value->dataType = Res_value::TYPE_REFERENCE;
+        out_value->data = entry.value.data;
+        const DynamicRefTable* ref_table =
+            asset_manager_->GetDynamicRefTableForPackage(package_idx);
+        if (ref_table == nullptr || ref_table->lookupResourceId(&out_value->data) != NO_ERROR) {
+          LOG(ERROR) << base::StringPrintf("Failed to resolve dynamic reference 0x%08x",
+                                           out_value->data);
+          return kInvalidCookie;
+        }
+
+        if (out_flags != nullptr) {
+          *out_flags = type_spec_flags;
+        }
+        return entry.cookie;
+      }
 
       default:
         *out_value = entry.value;
@@ -550,14 +710,14 @@
 
   type_spec_flags_ = o.type_spec_flags_;
 
-  for (size_t p = 0; p < arraysize(packages_); p++) {
+  for (size_t p = 0; p < packages_.size(); 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++) {
+    for (size_t t = 0; t < package->types.size(); t++) {
       const Type* type = package->types[t].get();
       if (type == nullptr) {
         packages_[p]->types[t].reset();
diff --git a/libs/androidfw/ChunkIterator.cpp b/libs/androidfw/ChunkIterator.cpp
index 747aa4a..d8adbe5 100644
--- a/libs/androidfw/ChunkIterator.cpp
+++ b/libs/androidfw/ChunkIterator.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Chunk.h"
+#include "androidfw/Chunk.h"
 
 #include "android-base/logging.h"
 
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 94d0d46..e17a3a6 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -32,15 +32,15 @@
 #endif
 #endif
 
-#include "Chunk.h"
 #include "androidfw/ByteBucketArray.h"
+#include "androidfw/Chunk.h"
 #include "androidfw/Util.h"
 
 using android::base::StringPrintf;
 
 namespace android {
 
-namespace {
+constexpr const static int kAppPackageId = 0x7f;
 
 // Element of a TypeSpec array. See TypeSpec.
 struct Type {
@@ -76,6 +76,8 @@
 // itself.
 using TypeSpecPtr = util::unique_cptr<TypeSpec>;
 
+namespace {
+
 // 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.
@@ -110,37 +112,18 @@
 
 }  // 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,
+bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config,
+                              LoadedArscEntry* out_entry, ResTable_config* out_selected_config,
                               uint32_t* out_flags) const {
-  ATRACE_NAME("LoadedPackage::FindEntry");
-  const TypeSpecPtr& ptr = type_specs_[type_id];
+  ATRACE_CALL();
+  const TypeSpecPtr& ptr = type_specs_[type_idx];
   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)) {
+  if (entry_idx >= dtohl(ptr->type_spec->entryCount)) {
     return false;
   }
 
@@ -156,10 +139,10 @@
       // 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) {
+      if (entry_idx < 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]);
+        const uint32_t offset = dtohl(entry_offsets[entry_idx]);
         if (offset != ResTable_type::NO_ENTRY) {
           // There is an entry for this resource, record it.
           best_config = &type->configuration;
@@ -175,7 +158,7 @@
   }
 
   const uint32_t* flags = reinterpret_cast<const uint32_t*>(ptr->type_spec + 1);
-  *out_flags = dtohl(flags[entry_id]);
+  *out_flags = dtohl(flags[entry_idx]);
   *out_selected_config = *best_config;
 
   const ResTable_entry* best_entry = reinterpret_cast<const ResTable_entry*>(
@@ -191,9 +174,10 @@
 // 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");
+bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config,
+                           LoadedArscEntry* out_entry, ResTable_config* out_selected_config,
+                           uint32_t* out_flags) const {
+  ATRACE_CALL();
   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);
@@ -212,11 +196,11 @@
   return false;
 }
 
-const std::string* LoadedArsc::GetPackageNameForId(uint32_t resid) const {
+const LoadedPackage* LoadedArsc::GetPackageForId(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 loaded_package.get();
     }
   }
   return nullptr;
@@ -334,15 +318,24 @@
   return true;
 }
 
-static bool LoadPackage(const Chunk& chunk, LoadedPackage* loaded_package) {
+std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) {
   ATRACE_CALL();
+  std::unique_ptr<LoadedPackage> loaded_package{new LoadedPackage()};
+
   const ResTable_package* header = chunk.header<ResTable_package>();
   if (header == nullptr) {
     LOG(ERROR) << "Chunk RES_TABLE_PACKAGE_TYPE is too small.";
-    return false;
+    return {};
   }
 
   loaded_package->package_id_ = dtohl(header->id);
+  if (loaded_package->package_id_ == 0) {
+    // Package ID of 0 means this is a shared library.
+    loaded_package->dynamic_ = true;
+  }
+
+  util::ReadUtf16StringFromDevice(header->name, arraysize(header->name),
+                                  &loaded_package->package_name_);
 
   // A TypeSpec builder. We use this to accumulate the set of Types
   // available for a TypeSpec, and later build a single, contiguous block
@@ -367,7 +360,7 @@
               child_chunk.header<ResStringPool_header>(), child_chunk.size());
           if (err != NO_ERROR) {
             LOG(ERROR) << "Corrupt package type string pool.";
-            return false;
+            return {};
           }
         } else if (pool_address == header_address + dtohl(header->keyStrings)) {
           // This string pool is the key string pool.
@@ -375,7 +368,7 @@
               child_chunk.header<ResStringPool_header>(), child_chunk.size());
           if (err != NO_ERROR) {
             LOG(ERROR) << "Corrupt package key string pool.";
-            return false;
+            return {};
           }
         } else {
           LOG(WARNING) << "Too many string pool chunks found in package.";
@@ -390,7 +383,7 @@
           TypeSpecPtr type_spec_ptr = types_builder->Build();
           if (type_spec_ptr == nullptr) {
             LOG(ERROR) << "Too many type configurations, overflow detected.";
-            return false;
+            return {};
           }
 
           loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr);
@@ -402,12 +395,12 @@
         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;
+          return {};
         }
 
         if (type_spec->id == 0) {
           LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE has invalid ID 0.";
-          return false;
+          return {};
         }
 
         // The data portion of this chunk contains entry_count 32bit entries,
@@ -419,12 +412,12 @@
         // 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;
+          return {};
         }
 
         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;
+          return {};
         }
 
         last_type_idx = type_spec->id - 1;
@@ -435,28 +428,63 @@
         const ResTable_type* type = child_chunk.header<ResTable_type>();
         if (type == nullptr) {
           LOG(ERROR) << "Chunk RES_TABLE_TYPE_TYPE is too small.";
-          return false;
+          return {};
         }
 
         if (type->id == 0) {
           LOG(ERROR) << "Chunk RES_TABLE_TYPE_TYPE has invalid ID 0.";
-          return false;
+          return {};
         }
 
         // 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;
+          return {};
         }
 
         if (!VerifyType(child_chunk)) {
-          return false;
+          return {};
         }
 
         types_builder->AddType(type);
       } break;
 
+      case RES_TABLE_LIBRARY_TYPE: {
+        const ResTable_lib_header* lib = child_chunk.header<ResTable_lib_header>();
+        if (lib == nullptr) {
+          LOG(ERROR) << "Chunk RES_TABLE_LIBRARY_TYPE is too small.";
+          return {};
+        }
+
+        if (child_chunk.data_size() / sizeof(ResTable_lib_entry) < dtohl(lib->count)) {
+          LOG(ERROR) << "Chunk too small to hold entries in RES_TABLE_LIBRARY_TYPE.";
+          return {};
+        }
+
+        loaded_package->dynamic_package_map_.reserve(dtohl(lib->count));
+
+        const ResTable_lib_entry* const entry_begin =
+            reinterpret_cast<const ResTable_lib_entry*>(child_chunk.data_ptr());
+        const ResTable_lib_entry* const entry_end = entry_begin + dtohl(lib->count);
+        for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) {
+          std::string package_name;
+          util::ReadUtf16StringFromDevice(entry_iter->packageName,
+                                          arraysize(entry_iter->packageName), &package_name);
+
+          if (dtohl(entry_iter->packageId) >= std::numeric_limits<uint8_t>::max()) {
+            LOG(ERROR) << base::StringPrintf(
+                "Package ID %02x in RES_TABLE_LIBRARY_TYPE too large for package '%s'.",
+                dtohl(entry_iter->packageId), package_name.c_str());
+            return {};
+          }
+
+          loaded_package->dynamic_package_map_.emplace_back(std::move(package_name),
+                                                            dtohl(entry_iter->packageId));
+        }
+
+      } break;
+
       default:
         LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type());
         break;
@@ -468,19 +496,19 @@
     TypeSpecPtr type_spec_ptr = types_builder->Build();
     if (type_spec_ptr == nullptr) {
       LOG(ERROR) << "Too many type configurations, overflow detected.";
-      return false;
+      return {};
     }
     loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr);
   }
 
   if (iter.HadError()) {
     LOG(ERROR) << iter.GetLastError();
-    return false;
+    return {};
   }
-  return true;
+  return loaded_package;
 }
 
-bool LoadedArsc::LoadTable(const Chunk& chunk) {
+bool LoadedArsc::LoadTable(const Chunk& chunk, bool load_as_shared_library) {
   ATRACE_CALL();
   const ResTable_header* header = chunk.header<ResTable_header>();
   if (header == nullptr) {
@@ -520,10 +548,15 @@
         }
         packages_seen++;
 
-        std::unique_ptr<LoadedPackage> loaded_package = util::make_unique<LoadedPackage>();
-        if (!LoadPackage(child_chunk, loaded_package.get())) {
+        std::unique_ptr<LoadedPackage> loaded_package = LoadedPackage::Load(child_chunk);
+        if (!loaded_package) {
           return false;
         }
+
+        // Mark the package as dynamic if we are forcefully loading the Apk as a shared library.
+        if (loaded_package->package_id_ == kAppPackageId) {
+          loaded_package->dynamic_ = load_as_shared_library;
+        }
         packages_.push_back(std::move(loaded_package));
       } break;
 
@@ -540,7 +573,8 @@
   return true;
 }
 
-std::unique_ptr<LoadedArsc> LoadedArsc::Load(const void* data, size_t len) {
+std::unique_ptr<LoadedArsc> LoadedArsc::Load(const void* data, size_t len,
+                                             bool load_as_shared_library) {
   ATRACE_CALL();
 
   // Not using make_unique because the constructor is private.
@@ -551,7 +585,7 @@
     const Chunk chunk = iter.Next();
     switch (chunk.type()) {
       case RES_TABLE_TYPE:
-        if (!loaded_arsc->LoadTable(chunk)) {
+        if (!loaded_arsc->LoadTable(chunk, load_as_shared_library)) {
           return {};
         }
         break;
diff --git a/libs/androidfw/Util.cpp b/libs/androidfw/Util.cpp
new file mode 100644
index 0000000..202bc8e
--- /dev/null
+++ b/libs/androidfw/Util.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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/Util.h"
+
+#include <string>
+
+#include "utils/ByteOrder.h"
+#include "utils/Unicode.h"
+
+#ifdef _WIN32
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+namespace android {
+namespace util {
+
+void ReadUtf16StringFromDevice(const uint16_t* src, size_t len, std::string* out) {
+  char buf[5];
+  while (*src && len != 0) {
+    char16_t c = static_cast<char16_t>(dtohs(*src));
+    utf16_to_utf8(&c, 1, buf, sizeof(buf));
+    out->append(buf, strlen(buf));
+    ++src;
+    --len;
+  }
+}
+
+} // namespace util
+} // namespace android
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index a3d67f0..9d4fd29 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -32,6 +32,7 @@
 class ApkAssets {
  public:
   static std::unique_ptr<ApkAssets> Load(const std::string& path);
+  static std::unique_ptr<ApkAssets> LoadAsSharedLibrary(const std::string& path);
 
   std::unique_ptr<Asset> Open(const std::string& path,
                               Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const;
@@ -43,6 +44,8 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(ApkAssets);
 
+  static std::unique_ptr<ApkAssets> LoadImpl(const std::string& path, bool load_as_shared_library);
+
   ApkAssets() = default;
 
   struct ZipArchivePtrCloser {
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 66d5034..8655339 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -19,6 +19,7 @@
 
 #include "android-base/macros.h"
 
+#include <array>
 #include <limits>
 #include <unordered_map>
 
@@ -95,18 +96,21 @@
   // new resource IDs.
   bool SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches = true);
 
-  const std::vector<const ApkAssets*> GetApkAssets() const;
+  inline const std::vector<const ApkAssets*> GetApkAssets() const { return apk_assets_; }
 
   // 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;
 
+  // Returns the DynamicRefTable for the given package ID.
+  const DynamicRefTable* GetDynamicRefTableForPackage(uint32_t package_id) 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;
+  inline const ResTable_config& GetConfiguration() const { return configuration_; }
 
   // Searches the set of APKs loaded by this AssetManager and opens the first one found located
   // in the assets/ directory.
@@ -173,6 +177,8 @@
   // Creates a new Theme from this AssetManager.
   std::unique_ptr<Theme> NewTheme();
 
+  void DumpToLog() const;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(AssetManager2);
 
@@ -189,9 +195,13 @@
   // `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,
+                            LoadedArscEntry* out_entry, ResTable_config* out_selected_config,
                             uint32_t* out_flags);
 
+  // Assigns package IDs to all shared library ApkAssets.
+  // Should be called whenever the ApkAssets are changed.
+  void BuildDynamicRefTable();
+
   // Purge all resources that are cached and vary by the configuration axis denoted by the
   // bitmask `diff`.
   void InvalidateCaches(uint32_t diff);
@@ -200,6 +210,22 @@
   // have a longer lifetime.
   std::vector<const ApkAssets*> apk_assets_;
 
+  struct PackageGroup {
+    std::vector<const LoadedPackage*> packages_;
+    std::vector<ApkAssetsCookie> cookies_;
+    DynamicRefTable dynamic_ref_table;
+  };
+
+  // DynamicRefTables for shared library package resolution.
+  // These are ordered according to apk_assets_. The mappings may change depending on what is
+  // in apk_assets_, therefore they must be stored in the AssetManager and not in the
+  // immutable ApkAssets class.
+  std::vector<PackageGroup> package_groups_;
+
+  // An array mapping package ID to index into package_groups. This keeps the lookup fast
+  // without taking too much memory.
+  std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> package_ids_;
+
   // The current configuration set for this AssetManager. When this changes, cached resources
   // may need to be purged.
   ResTable_config configuration_;
@@ -279,12 +305,12 @@
   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];
+    std::array<util::unique_cptr<Type>, kTypeCount> types;
   };
 
   AssetManager2* asset_manager_;
   uint32_t type_spec_flags_ = 0u;
-  std::unique_ptr<Package> packages_[kPackageCount];
+  std::array<std::unique_ptr<Package>, kPackageCount> packages_;
 };
 
 inline const ResolvedBag::Entry* begin(const ResolvedBag* bag) { return bag->entries; }
diff --git a/libs/androidfw/Chunk.h b/libs/androidfw/include/androidfw/Chunk.h
similarity index 100%
rename from libs/androidfw/Chunk.h
rename to libs/androidfw/include/androidfw/Chunk.h
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index e2e56c8..8362008 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -22,12 +22,82 @@
 
 #include "android-base/macros.h"
 
+#include "androidfw/ByteBucketArray.h"
+#include "androidfw/Chunk.h"
 #include "androidfw/ResourceTypes.h"
+#include "androidfw/Util.h"
 
 namespace android {
 
-class Chunk;
-class LoadedPackage;
+class DynamicPackageEntry {
+ public:
+  DynamicPackageEntry() = default;
+  DynamicPackageEntry(std::string&& package_name, int package_id)
+      : package_name(std::move(package_name)), package_id(package_id) {}
+
+  std::string package_name;
+  int package_id = 0;
+};
+
+struct LoadedArscEntry {
+  // 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 dynamic package ID map for the package from which this resource came from.
+  const DynamicRefTable* dynamic_ref_table = 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;
+};
+
+struct TypeSpec;
+class LoadedArsc;
+
+class LoadedPackage {
+  friend class LoadedArsc;
+
+ public:
+  bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config,
+                 LoadedArscEntry* out_entry, ResTable_config* out_selected_config,
+                 uint32_t* out_flags) const;
+
+  inline const ResStringPool* GetTypeStringPool() const { return &type_string_pool_; }
+
+  inline const ResStringPool* GetKeyStringPool() const { return &key_string_pool_; }
+
+  inline const std::string& GetPackageName() const { return package_name_; }
+
+  inline int GetPackageId() const { return package_id_; }
+
+  inline bool IsDynamic() const { return dynamic_; }
+
+  inline const std::vector<DynamicPackageEntry>& GetDynamicPackageMap() const {
+    return dynamic_package_map_;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
+
+  static std::unique_ptr<LoadedPackage> Load(const Chunk& chunk);
+
+  LoadedPackage() = default;
+
+  ResStringPool type_string_pool_;
+  ResStringPool key_string_pool_;
+  std::string package_name_;
+  int package_id_ = -1;
+  bool dynamic_ = false;
+
+  ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_;
+  std::vector<DynamicPackageEntry> dynamic_package_map_;
+};
 
 // Read-only view into a resource table. This class validates all data
 // when loading, including offsets and lengths.
@@ -35,7 +105,8 @@
  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);
+  static std::unique_ptr<LoadedArsc> Load(const void* data, size_t len,
+                                          bool load_as_shared_library = false);
 
   ~LoadedArsc();
 
@@ -43,39 +114,28 @@
   // (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,
+  bool FindEntry(uint32_t resid, const ResTable_config& config, LoadedArscEntry* 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;
+  const LoadedPackage* GetPackageForId(uint32_t resid) const;
+
+  inline const std::vector<std::unique_ptr<const LoadedPackage>>& GetPackages() const {
+    return packages_;
+  }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
 
   LoadedArsc() = default;
-  bool LoadTable(const Chunk& chunk);
+  bool LoadTable(const Chunk& chunk, bool load_as_shared_library);
 
   ResStringPool global_string_pool_;
-  std::vector<std::unique_ptr<LoadedPackage>> packages_;
+  std::vector<std::unique_ptr<const LoadedPackage>> packages_;
 };
 
 }  // namespace android
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index dadfe89..cdcf971 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1521,6 +1521,8 @@
     uint16_t packageName[128];
 };
 
+class AssetManager2;
+
 /**
  * Holds the shared library ID table. Shared libraries are assigned package IDs at
  * build time, but they may be loaded in a different order, so we need to maintain
@@ -1531,7 +1533,9 @@
  */
 class DynamicRefTable
 {
+    friend class AssetManager2;
 public:
+    DynamicRefTable() = default;
     DynamicRefTable(uint8_t packageId, bool appAsLib);
 
     // Loads an unmapped reference table from the package.
@@ -1546,18 +1550,18 @@
 
     // Performs the actual conversion of build-time resource ID to run-time
     // resource ID.
-    inline status_t lookupResourceId(uint32_t* resId) const;
-    inline status_t lookupResourceValue(Res_value* value) const;
+    status_t lookupResourceId(uint32_t* resId) const;
+    status_t lookupResourceValue(Res_value* value) const;
 
     inline const KeyedVector<String16, uint8_t>& entries() const {
         return mEntries;
     }
 
 private:
-    const uint8_t                   mAssignedPackageId;
+    uint8_t                         mAssignedPackageId = 0;
     uint8_t                         mLookupTable[256];
     KeyedVector<String16, uint8_t>  mEntries;
-    bool                            mAppAsLib;
+    bool                            mAppAsLib = false;
 };
 
 bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue);
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index 5266d09..fd96730 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -102,6 +102,10 @@
   pointer ptr_;
 };
 
+inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) {
+  return resid | (static_cast<uint32_t>(package_id) << 24);
+}
+
 inline uint8_t get_package_id(uint32_t resid) {
   return static_cast<uint8_t>((resid >> 24) & 0x000000ffu);
 }
@@ -113,7 +117,7 @@
 
 inline uint16_t get_entry_id(uint32_t resid) { return static_cast<uint16_t>(resid & 0x0000ffffu); }
 
-inline bool is_internal_id(uint32_t resid) {
+inline bool is_internal_resid(uint32_t resid) {
   return (resid & 0xffff0000u) != 0 && (resid & 0x00ff0000u) == 0;
 }
 
@@ -121,6 +125,8 @@
   return (resid & 0x00ff0000u) != 0 && (resid & 0xff000000u) != 0;
 }
 
+void ReadUtf16StringFromDevice(const uint16_t* src, size_t len, std::string* out);
+
 }  // namespace util
 }  // namespace android
 
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
index 3a1fc8f..0203712 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -26,9 +26,28 @@
 TEST(ApkAssetsTest, LoadApk) {
   std::unique_ptr<ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
   ASSERT_NE(nullptr, loaded_apk);
+  EXPECT_NE(nullptr, loaded_apk->GetLoadedArsc());
 
   std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml");
   ASSERT_NE(nullptr, asset);
 }
 
+TEST(ApkAssetsTest, LoadApkAsSharedLibrary) {
+  std::unique_ptr<ApkAssets> loaded_apk =
+      ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk");
+  ASSERT_NE(nullptr, loaded_apk);
+  const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
+  ASSERT_NE(nullptr, loaded_arsc);
+  ASSERT_EQ(1u, loaded_arsc->GetPackages().size());
+  EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic());
+
+  loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk");
+  ASSERT_NE(nullptr, loaded_apk);
+
+  loaded_arsc = loaded_apk->GetLoadedArsc();
+  ASSERT_NE(nullptr, loaded_arsc);
+  ASSERT_EQ(1u, loaded_arsc->GetPackages().size());
+  EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic());
+}
+
 }  // namespace android
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
index 9ff9478..b3c2dc3 100644
--- a/libs/androidfw/tests/AssetManager2_bench.cpp
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -16,6 +16,7 @@
 
 #include "benchmark/benchmark.h"
 
+#include "android-base/stringprintf.h"
 #include "androidfw/ApkAssets.h"
 #include "androidfw/AssetManager.h"
 #include "androidfw/AssetManager2.h"
@@ -23,10 +24,12 @@
 
 #include "TestHelpers.h"
 #include "data/basic/R.h"
+#include "data/libclient/R.h"
 #include "data/styles/R.h"
 
-namespace basic = com::android::basic;
 namespace app = com::android::app;
+namespace basic = com::android::basic;
+namespace libclient = com::android::libclient;
 
 namespace android {
 
@@ -78,101 +81,108 @@
 }
 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;
+static void GetResourceBenchmark(const std::vector<std::string>& paths,
+                                 const ResTable_config* config, uint32_t resid,
+                                 benchmark::State& state) {
+  std::vector<std::unique_ptr<ApkAssets>> apk_assets;
+  std::vector<const ApkAssets*> apk_assets_ptrs;
+  for (const std::string& path : paths) {
+    std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path);
+    if (apk == nullptr) {
+      state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str());
+      return;
+    }
+    apk_assets_ptrs.push_back(apk.get());
+    apk_assets.push_back(std::move(apk));
   }
 
-  AssetManager2 assets;
-  assets.SetApkAssets({apk.get()});
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets(apk_assets_ptrs);
+  if (config != nullptr) {
+    assetmanager.SetConfiguration(*config);
+  }
 
   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);
+    assetmanager.GetResource(resid, false /* may_be_bag */, 0u /* density_override */, &value,
+                             &selected_config, &flags);
   }
 }
+
+static void GetResourceBenchmarkOld(const std::vector<std::string>& paths,
+                                    const ResTable_config* config, uint32_t resid,
+                                    benchmark::State& state) {
+  AssetManager assetmanager;
+  for (const std::string& path : paths) {
+    if (!assetmanager.addAssetPath(String8(path.c_str()), nullptr /* cookie */,
+                                   false /* appAsLib */, false /* isSystemAssets */)) {
+      state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str());
+      return;
+    }
+  }
+
+  if (config != nullptr) {
+    assetmanager.setConfiguration(*config);
+  }
+
+  const ResTable& table = assetmanager.getResources(true);
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  while (state.KeepRunning()) {
+    table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags,
+                      &selected_config);
+  }
+}
+
+static void BM_AssetManagerGetResource(benchmark::State& state) {
+  GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/,
+                       basic::R::integer::number1, state);
+}
 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);
-  }
+  GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/,
+                          basic::R::integer::number1, state);
 }
 BENCHMARK(BM_AssetManagerGetResourceOld);
 
+static void BM_AssetManagerGetLibraryResource(benchmark::State& state) {
+  GetResourceBenchmark(
+      {GetTestDataPath() + "/lib_two/lib_two.apk", GetTestDataPath() + "/lib_one/lib_one.apk",
+       GetTestDataPath() + "/libclient/libclient.apk"},
+      nullptr /*config*/, libclient::R::string::foo_one, state);
+}
+BENCHMARK(BM_AssetManagerGetLibraryResource);
+
+static void BM_AssetManagerGetLibraryResourceOld(benchmark::State& state) {
+  GetResourceBenchmarkOld(
+      {GetTestDataPath() + "/lib_two/lib_two.apk", GetTestDataPath() + "/lib_one/lib_one.apk",
+       GetTestDataPath() + "/libclient/libclient.apk"},
+      nullptr /*config*/, libclient::R::string::foo_one, state);
+}
+BENCHMARK(BM_AssetManagerGetLibraryResourceOld);
+
 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);
-  }
+  GetResourceBenchmark({kFrameworkPath}, &config, kStringOkId, state);
 }
 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);
-  }
+  GetResourceBenchmarkOld({kFrameworkPath}, &config, kStringOkId, state);
 }
 BENCHMARK(BM_AssetManagerGetResourceFrameworkLocaleOld);
 
@@ -202,8 +212,7 @@
 static void BM_AssetManagerGetBagOld(benchmark::State& state) {
   AssetManager assets;
   if (!assets.addAssetPath(String8((GetTestDataPath() + "/styles/styles.apk").data()),
-                           nullptr /* cookie */, false /* appAsLib */,
-                           false /* isSystemAssets */)) {
+                           nullptr /*cookie*/, false /*appAsLib*/, false /*isSystemAssets*/)) {
     state.SkipWithError("Failed to load assets");
     return;
   }
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 39c5381..543456a 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -20,11 +20,19 @@
 #include "android-base/logging.h"
 
 #include "TestHelpers.h"
+#include "data/appaslib/R.h"
 #include "data/basic/R.h"
+#include "data/lib_one/R.h"
+#include "data/lib_two/R.h"
+#include "data/libclient/R.h"
 #include "data/styles/R.h"
 
-namespace basic = com::android::basic;
 namespace app = com::android::app;
+namespace appaslib = com::android::appaslib::app;
+namespace basic = com::android::basic;
+namespace lib_one = com::android::lib_one;
+namespace lib_two = com::android::lib_two;
+namespace libclient = com::android::libclient;
 
 namespace android {
 
@@ -39,15 +47,31 @@
 
     style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
     ASSERT_NE(nullptr, style_assets_);
+
+    lib_one_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_one/lib_one.apk");
+    ASSERT_NE(nullptr, lib_one_assets_);
+
+    lib_two_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_two/lib_two.apk");
+    ASSERT_NE(nullptr, lib_two_assets_);
+
+    libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk");
+    ASSERT_NE(nullptr, libclient_assets_);
+
+    appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk");
+    ASSERT_NE(nullptr, appaslib_assets_);
   }
 
  protected:
   std::unique_ptr<ApkAssets> basic_assets_;
   std::unique_ptr<ApkAssets> basic_de_fr_assets_;
   std::unique_ptr<ApkAssets> style_assets_;
+  std::unique_ptr<ApkAssets> lib_one_assets_;
+  std::unique_ptr<ApkAssets> lib_two_assets_;
+  std::unique_ptr<ApkAssets> libclient_assets_;
+  std::unique_ptr<ApkAssets> appaslib_assets_;
 };
 
-TEST_F(AssetManager2Test, FindsResourcesFromSingleApkAssets) {
+TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) {
   ResTable_config desired_config;
   memset(&desired_config, 0, sizeof(desired_config));
   desired_config.language[0] = 'd';
@@ -77,7 +101,7 @@
   EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
 }
 
-TEST_F(AssetManager2Test, FindsResourcesFromMultipleApkAssets) {
+TEST_F(AssetManager2Test, FindsResourceFromMultipleApkAssets) {
   ResTable_config desired_config;
   memset(&desired_config, 0, sizeof(desired_config));
   desired_config.language[0] = 'd';
@@ -99,7 +123,7 @@
   // Came from our de_fr ApkAssets.
   EXPECT_EQ(1, cookie);
 
-  // The configuration is german.
+  // The configuration is German.
   EXPECT_EQ('d', selected_config.language[0]);
   EXPECT_EQ('e', selected_config.language[1]);
 
@@ -107,7 +131,72 @@
   EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
 }
 
-TEST_F(AssetManager2Test, FindsBagResourcesFromSingleApkAssets) {
+TEST_F(AssetManager2Test, FindsResourceFromSharedLibrary) {
+  AssetManager2 assetmanager;
+
+  // libclient is built with lib_one and then lib_two in order.
+  // Reverse the order to test that proper package ID re-assignment is happening.
+  assetmanager.SetApkAssets(
+      {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ApkAssetsCookie cookie =
+      assetmanager.GetResource(libclient::R::string::foo_one, false /*may_be_bag*/,
+                               0 /*density_override*/, &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+
+  // Reference comes from libclient.
+  EXPECT_EQ(2, cookie);
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+
+  // Lookup the reference.
+  cookie = assetmanager.GetResource(value.data, false /* may_be_bag */, 0 /* density_override*/,
+                                    &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(1, cookie);
+  EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+  EXPECT_EQ(std::string("Foo from lib_one"),
+            GetStringFromPool(assetmanager.GetStringPoolForCookie(cookie), value.data));
+
+  cookie = assetmanager.GetResource(libclient::R::string::foo_two, false /*may_be_bag*/,
+                                    0 /*density_override*/, &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+
+  // Reference comes from libclient.
+  EXPECT_EQ(2, cookie);
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+
+  // Lookup the reference.
+  cookie = assetmanager.GetResource(value.data, false /* may_be_bag */, 0 /* density_override*/,
+                                    &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(0, cookie);
+  EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+  EXPECT_EQ(std::string("Foo from lib_two"),
+            GetStringFromPool(assetmanager.GetStringPoolForCookie(cookie), value.data));
+}
+
+TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({appaslib_assets_.get()});
+
+  // The appaslib package will have been assigned the package ID 0x02.
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+  ApkAssetsCookie cookie = assetmanager.GetResource(
+      util::fix_package_id(appaslib::R::integer::number1, 0x02), false /*may_be_bag*/,
+      0u /*density_override*/, &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+  EXPECT_EQ(util::fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data);
+}
+
+TEST_F(AssetManager2Test, FindsBagResourceFromSingleApkAssets) {
   AssetManager2 assetmanager;
   assetmanager.SetApkAssets({basic_assets_.get()});
 
@@ -128,6 +217,27 @@
   EXPECT_EQ(0, bag->entries[2].cookie);
 }
 
+TEST_F(AssetManager2Test, FindsBagResourceFromMultipleApkAssets) {}
+
+TEST_F(AssetManager2Test, FindsBagResourceFromSharedLibrary) {
+  AssetManager2 assetmanager;
+
+  // libclient is built with lib_one and then lib_two in order.
+  // Reverse the order to test that proper package ID re-assignment is happening.
+  assetmanager.SetApkAssets(
+      {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
+
+  const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::Theme);
+  ASSERT_NE(nullptr, bag);
+  ASSERT_GE(bag->entry_count, 2u);
+
+  // First two attributes come from lib_one.
+  EXPECT_EQ(1, bag->entries[0].cookie);
+  EXPECT_EQ(0x03, util::get_package_id(bag->entries[0].key));
+  EXPECT_EQ(1, bag->entries[1].cookie);
+  EXPECT_EQ(0x03, util::get_package_id(bag->entries[1].key));
+}
+
 TEST_F(AssetManager2Test, MergesStylesWithParentFromSingleApkAssets) {
   AssetManager2 assetmanager;
   assetmanager.SetApkAssets({style_assets_.get()});
@@ -181,8 +291,6 @@
   EXPECT_EQ(0, bag_two->entries[4].cookie);
 }
 
-TEST_F(AssetManager2Test, FindsBagResourcesFromMultipleApkAssets) {}
-
 TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {}
 
 TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {}
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 47b3894..045507e 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -16,21 +16,18 @@
 
 #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/libclient/R.h"
 #include "data/styles/R.h"
 
 namespace app = com::android::app;
 namespace basic = com::android::basic;
+namespace libclient = com::android::libclient;
 
 namespace android {
 
 TEST(LoadedArscTest, LoadSinglePackageArsc) {
-  base::ScopedLogSeverity _log(base::LogSeverity::DEBUG);
   std::string contents;
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", "resources.arsc",
                                       &contents));
@@ -38,11 +35,16 @@
   std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
   ASSERT_NE(nullptr, loaded_arsc);
 
+  const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages();
+  ASSERT_EQ(1u, packages.size());
+  EXPECT_EQ(std::string("com.android.app"), packages[0]->GetPackageName());
+  EXPECT_EQ(0x7f, packages[0]->GetPackageId());
+
   ResTable_config config;
   memset(&config, 0, sizeof(config));
   config.sdkVersion = 24;
 
-  LoadedArsc::Entry entry;
+  LoadedArscEntry entry;
   ResTable_config selected_config;
   uint32_t flags;
 
@@ -52,7 +54,6 @@
 }
 
 TEST(LoadedArscTest, FindDefaultEntry) {
-  base::ScopedLogSeverity _log(base::LogSeverity::DEBUG);
   std::string contents;
   ASSERT_TRUE(
       ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents));
@@ -65,7 +66,7 @@
   desired_config.language[0] = 'd';
   desired_config.language[1] = 'e';
 
-  LoadedArsc::Entry entry;
+  LoadedArscEntry entry;
   ResTable_config selected_config;
   uint32_t flags;
 
@@ -74,6 +75,70 @@
   ASSERT_NE(nullptr, entry.entry);
 }
 
+TEST(LoadedArscTest, LoadSharedLibrary) {
+  std::string contents;
+  ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc",
+                                      &contents));
+
+  std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+  ASSERT_NE(nullptr, loaded_arsc);
+
+  const auto& packages = loaded_arsc->GetPackages();
+  ASSERT_EQ(1u, packages.size());
+
+  EXPECT_TRUE(packages[0]->IsDynamic());
+  EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName());
+  EXPECT_EQ(0, packages[0]->GetPackageId());
+
+  const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap();
+
+  // The library has no dependencies.
+  ASSERT_TRUE(dynamic_pkg_map.empty());
+}
+
+TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) {
+  std::string contents;
+  ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/libclient/libclient.apk",
+                                      "resources.arsc", &contents));
+
+  std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+  ASSERT_NE(nullptr, loaded_arsc);
+
+  const auto& packages = loaded_arsc->GetPackages();
+  ASSERT_EQ(1u, packages.size());
+
+  EXPECT_FALSE(packages[0]->IsDynamic());
+  EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName());
+  EXPECT_EQ(0x7f, packages[0]->GetPackageId());
+
+  const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap();
+
+  // The library has two dependencies.
+  ASSERT_EQ(2u, dynamic_pkg_map.size());
+
+  EXPECT_EQ(std::string("com.android.lib_one"), dynamic_pkg_map[0].package_name);
+  EXPECT_EQ(0x02, dynamic_pkg_map[0].package_id);
+
+  EXPECT_EQ(std::string("com.android.lib_two"), dynamic_pkg_map[1].package_name);
+  EXPECT_EQ(0x03, dynamic_pkg_map[1].package_id);
+}
+
+TEST(LoadedArscTest, LoadAppAsSharedLibrary) {
+  std::string contents;
+  ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib.apk",
+                                      "resources.arsc", &contents));
+
+  std::unique_ptr<LoadedArsc> loaded_arsc =
+      LoadedArsc::Load(contents.data(), contents.size(), true /*load_as_shared_library*/);
+  ASSERT_NE(nullptr, loaded_arsc);
+
+  const auto& packages = loaded_arsc->GetPackages();
+  ASSERT_EQ(1u, packages.size());
+
+  EXPECT_TRUE(packages[0]->IsDynamic());
+  EXPECT_EQ(0x7f, packages[0]->GetPackageId());
+}
+
 // 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.
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
index b151f3f..ad1cd2b 100644
--- a/libs/androidfw/tests/ResTable_test.cpp
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -25,10 +25,10 @@
 
 #include "TestHelpers.h"
 #include "data/basic/R.h"
-#include "data/lib/R.h"
+#include "data/lib_one/R.h"
 
 namespace basic = com::android::basic;
-namespace lib = com::android::lib;
+namespace lib = com::android::lib_one;
 
 namespace android {
 
@@ -119,7 +119,7 @@
 
 TEST(ResTableTest, LibraryThemeIsAppliedCorrectly) {
   std::string contents;
-  ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib/lib.apk",
+  ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk",
                                       "resources.arsc", &contents));
 
   ResTable table;
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index c0011b6d..59cb18a 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -19,9 +19,13 @@
 #include "android-base/logging.h"
 
 #include "TestHelpers.h"
+#include "data/lib_one/R.h"
+#include "data/libclient/R.h"
 #include "data/styles/R.h"
 
 namespace app = com::android::app;
+namespace lib_one = com::android::lib_one;
+namespace libclient = com::android::libclient;
 
 namespace android {
 
@@ -30,10 +34,22 @@
   void SetUp() override {
     style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
     ASSERT_NE(nullptr, style_assets_);
+
+    libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk");
+    ASSERT_NE(nullptr, libclient_assets_);
+
+    lib_one_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_one/lib_one.apk");
+    ASSERT_NE(nullptr, lib_one_assets_);
+
+    lib_two_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_two/lib_two.apk");
+    ASSERT_NE(nullptr, lib_two_assets_);
   }
 
  protected:
   std::unique_ptr<ApkAssets> style_assets_;
+  std::unique_ptr<ApkAssets> libclient_assets_;
+  std::unique_ptr<ApkAssets> lib_one_assets_;
+  std::unique_ptr<ApkAssets> lib_two_assets_;
 };
 
 TEST_F(ThemeTest, EmptyTheme) {
@@ -174,6 +190,36 @@
   EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
 }
 
+TEST_F(ThemeTest, ResolveDynamicAttributesAndReferencesToSharedLibrary) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets(
+      {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
+
+  std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+  ASSERT_TRUE(theme->ApplyStyle(libclient::R::style::Theme, false /*force*/));
+
+  Res_value value;
+  uint32_t flags;
+  ApkAssetsCookie cookie;
+
+  // The attribute should be resolved to the final value.
+  cookie = theme->GetAttribute(libclient::R::attr::foo, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(700u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+  // The reference should be resolved to a TYPE_REFERENCE.
+  cookie = theme->GetAttribute(libclient::R::attr::bar, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+
+  // lib_one is assigned package ID 0x03.
+  EXPECT_EQ(3u, util::get_package_id(value.data));
+  EXPECT_EQ(util::get_type_id(lib_one::R::string::foo), util::get_type_id(value.data));
+  EXPECT_EQ(util::get_entry_id(lib_one::R::string::foo), util::get_entry_id(value.data));
+}
+
 TEST_F(ThemeTest, CopyThemeSameAssetManager) {
   AssetManager2 assetmanager;
   assetmanager.SetApkAssets({style_assets_.get()});
diff --git a/libs/androidfw/tests/data/lib/lib.apk b/libs/androidfw/tests/data/lib/lib.apk
deleted file mode 100644
index 44c27c7..0000000
--- a/libs/androidfw/tests/data/lib/lib.apk
+++ /dev/null
Binary files differ
diff --git a/libs/androidfw/tests/data/lib/AndroidManifest.xml b/libs/androidfw/tests/data/lib_one/AndroidManifest.xml
similarity index 95%
rename from libs/androidfw/tests/data/lib/AndroidManifest.xml
rename to libs/androidfw/tests/data/lib_one/AndroidManifest.xml
index 02f5d3e..860adf7 100644
--- a/libs/androidfw/tests/data/lib/AndroidManifest.xml
+++ b/libs/androidfw/tests/data/lib_one/AndroidManifest.xml
@@ -15,6 +15,6 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.lib">
+    package="com.android.lib_one">
     <application />
 </manifest>
diff --git a/libs/androidfw/tests/data/lib/R.h b/libs/androidfw/tests/data/lib_one/R.h
similarity index 80%
rename from libs/androidfw/tests/data/lib/R.h
rename to libs/androidfw/tests/data/lib_one/R.h
index bb22d22..fcaeb8d 100644
--- a/libs/androidfw/tests/data/lib/R.h
+++ b/libs/androidfw/tests/data/lib_one/R.h
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef TEST_DATA_LIB_R_H_
-#define TEST_DATA_LIB_R_H_
+#ifndef TEST_DATA_LIB_ONE_R_H_
+#define TEST_DATA_LIB_ONE_R_H_
 
 #include <cstdint>
 
 namespace com {
 namespace android {
-namespace lib {
+namespace lib_one {
 
 struct R {
   struct attr {
@@ -36,10 +36,16 @@
       Theme = 0x02020000,  // default
     };
   };
+
+  struct string {
+    enum : uint32_t {
+      foo = 0x02030000,  // default
+    };
+  };
 };
 
-}  // namespace lib
+}  // namespace lib_one
 }  // namespace android
 }  // namespace com
 
-#endif  // TEST_DATA_R_H_
+#endif  // TEST_DATA_LIB_ONE_R_H_
diff --git a/libs/androidfw/tests/data/lib/build b/libs/androidfw/tests/data/lib_one/build
similarity index 89%
copy from libs/androidfw/tests/data/lib/build
copy to libs/androidfw/tests/data/lib_one/build
index 5c3d02c..c6adf0b 100755
--- a/libs/androidfw/tests/data/lib/build
+++ b/libs/androidfw/tests/data/lib_one/build
@@ -17,4 +17,4 @@
 
 set -e
 
-aapt package -M AndroidManifest.xml -S res -F lib.apk -f --shared-lib
+aapt package -M AndroidManifest.xml -S res -F lib_one.apk -f --shared-lib
diff --git a/libs/androidfw/tests/data/lib_one/lib_one.apk b/libs/androidfw/tests/data/lib_one/lib_one.apk
new file mode 100644
index 0000000..f287554
--- /dev/null
+++ b/libs/androidfw/tests/data/lib_one/lib_one.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/lib/res/values/values.xml b/libs/androidfw/tests/data/lib_one/res/values/values.xml
similarity index 66%
copy from libs/androidfw/tests/data/lib/res/values/values.xml
copy to libs/androidfw/tests/data/lib_one/res/values/values.xml
index 51e3a40..752b7e9 100644
--- a/libs/androidfw/tests/data/lib/res/values/values.xml
+++ b/libs/androidfw/tests/data/lib_one/res/values/values.xml
@@ -15,11 +15,18 @@
 -->
 
 <resources>
+    <public type="attr" name="attr1" id="0x00010000" />
     <attr name="attr1" format="integer" />
+
+    <public type="attr" name="attr2" id="0x00010001" />
     <attr name="attr2" format="integer" />
 
+    <public type="style" name="Theme" id="0x00020000" />
     <style name="Theme">
-        <item name="com.android.lib:attr1">700</item>
-        <item name="com.android.lib:attr2">?com.android.lib:attr1</item>
+        <item name="com.android.lib_one:attr1">700</item>
+        <item name="com.android.lib_one:attr2">?com.android.lib_one:attr1</item>
     </style>
+
+    <public type="string" name="foo" id="0x00030000" />
+    <string name="foo">Foo from lib_one</string>
 </resources>
diff --git a/libs/androidfw/tests/data/lib/AndroidManifest.xml b/libs/androidfw/tests/data/lib_two/AndroidManifest.xml
similarity index 87%
copy from libs/androidfw/tests/data/lib/AndroidManifest.xml
copy to libs/androidfw/tests/data/lib_two/AndroidManifest.xml
index 02f5d3e..4b131e5 100644
--- a/libs/androidfw/tests/data/lib/AndroidManifest.xml
+++ b/libs/androidfw/tests/data/lib_two/AndroidManifest.xml
@@ -14,7 +14,6 @@
      limitations under the License.
 -->
 
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.lib">
+<manifest package="com.android.lib_two">
     <application />
 </manifest>
diff --git a/libs/androidfw/tests/data/lib/R.h b/libs/androidfw/tests/data/lib_two/R.h
similarity index 72%
copy from libs/androidfw/tests/data/lib/R.h
copy to libs/androidfw/tests/data/lib_two/R.h
index bb22d22..c04a9d3 100644
--- a/libs/androidfw/tests/data/lib/R.h
+++ b/libs/androidfw/tests/data/lib_two/R.h
@@ -14,32 +14,26 @@
  * limitations under the License.
  */
 
-#ifndef TEST_DATA_LIB_R_H_
-#define TEST_DATA_LIB_R_H_
+#ifndef TEST_DATA_LIB_TWO_R_H_
+#define TEST_DATA_LIB_TWO_R_H_
 
 #include <cstdint>
 
 namespace com {
 namespace android {
-namespace lib {
+namespace lib_two {
 
 struct R {
-  struct attr {
+  struct string {
     enum : uint32_t {
-      attr1 = 0x02010000,  // default
-      attr2 = 0x02010001,  // default
-    };
-  };
-
-  struct style {
-    enum : uint32_t {
-      Theme = 0x02020000,  // default
+      LibraryString = 0x02020000,  // default
+      foo = 0x02020001, // default
     };
   };
 };
 
-}  // namespace lib
+}  // namespace lib_two
 }  // namespace android
 }  // namespace com
 
-#endif  // TEST_DATA_R_H_
+#endif  // TEST_DATA_LIB_TWO_R_H_
diff --git a/libs/androidfw/tests/data/lib/build b/libs/androidfw/tests/data/lib_two/build
similarity index 89%
rename from libs/androidfw/tests/data/lib/build
rename to libs/androidfw/tests/data/lib_two/build
index 5c3d02c..fd75e1d 100755
--- a/libs/androidfw/tests/data/lib/build
+++ b/libs/androidfw/tests/data/lib_two/build
@@ -17,4 +17,4 @@
 
 set -e
 
-aapt package -M AndroidManifest.xml -S res -F lib.apk -f --shared-lib
+aapt package -M AndroidManifest.xml -S res -F lib_two.apk -f --shared-lib
diff --git a/libs/androidfw/tests/data/lib_two/lib_two.apk b/libs/androidfw/tests/data/lib_two/lib_two.apk
new file mode 100644
index 0000000..ad44f9c
--- /dev/null
+++ b/libs/androidfw/tests/data/lib_two/lib_two.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/lib/res/values/values.xml b/libs/androidfw/tests/data/lib_two/res/values/values.xml
similarity index 73%
rename from libs/androidfw/tests/data/lib/res/values/values.xml
rename to libs/androidfw/tests/data/lib_two/res/values/values.xml
index 51e3a40..f4eea26 100644
--- a/libs/androidfw/tests/data/lib/res/values/values.xml
+++ b/libs/androidfw/tests/data/lib_two/res/values/values.xml
@@ -15,11 +15,9 @@
 -->
 
 <resources>
-    <attr name="attr1" format="integer" />
-    <attr name="attr2" format="integer" />
+    <public type="string" name="LibraryString" id="0x00020000" />
+    <string name="LibraryString">Hi from library two</string>
 
-    <style name="Theme">
-        <item name="com.android.lib:attr1">700</item>
-        <item name="com.android.lib:attr2">?com.android.lib:attr1</item>
-    </style>
+    <public type="string" name="foo" id="0x00020001" />
+    <string name="foo">Foo from lib_two</string>
 </resources>
diff --git a/libs/androidfw/tests/data/lib/AndroidManifest.xml b/libs/androidfw/tests/data/libclient/AndroidManifest.xml
similarity index 87%
copy from libs/androidfw/tests/data/lib/AndroidManifest.xml
copy to libs/androidfw/tests/data/libclient/AndroidManifest.xml
index 02f5d3e..8436383 100644
--- a/libs/androidfw/tests/data/lib/AndroidManifest.xml
+++ b/libs/androidfw/tests/data/libclient/AndroidManifest.xml
@@ -14,7 +14,6 @@
      limitations under the License.
 -->
 
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.lib">
+<manifest package="com.android.libclient">
     <application />
 </manifest>
diff --git a/libs/androidfw/tests/data/lib/R.h b/libs/androidfw/tests/data/libclient/R.h
similarity index 75%
copy from libs/androidfw/tests/data/lib/R.h
copy to libs/androidfw/tests/data/libclient/R.h
index bb22d22..43d1f9b 100644
--- a/libs/androidfw/tests/data/lib/R.h
+++ b/libs/androidfw/tests/data/libclient/R.h
@@ -21,24 +21,31 @@
 
 namespace com {
 namespace android {
-namespace lib {
+namespace libclient {
 
 struct R {
   struct attr {
     enum : uint32_t {
-      attr1 = 0x02010000,  // default
-      attr2 = 0x02010001,  // default
+      foo = 0x7f010000,  // default
+      bar = 0x7f010001,  // default
     };
   };
 
   struct style {
     enum : uint32_t {
-      Theme = 0x02020000,  // default
+      Theme = 0x7f020000,  // default
+    };
+  };
+
+  struct string {
+    enum : uint32_t {
+      foo_one = 0x7f030000,  // default
+      foo_two = 0x7f030001,  // default
     };
   };
 };
 
-}  // namespace lib
+}  // namespace libclient
 }  // namespace android
 }  // namespace com
 
diff --git a/libs/androidfw/tests/data/lib/build b/libs/androidfw/tests/data/libclient/build
similarity index 66%
copy from libs/androidfw/tests/data/lib/build
copy to libs/androidfw/tests/data/libclient/build
index 5c3d02c..08310e3 100755
--- a/libs/androidfw/tests/data/lib/build
+++ b/libs/androidfw/tests/data/libclient/build
@@ -17,4 +17,14 @@
 
 set -e
 
-aapt package -M AndroidManifest.xml -S res -F lib.apk -f --shared-lib
+PATH_TO_FRAMEWORK_RES=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/android.jar
+PATH_TO_LIB_ONE=../lib_one/lib_one.apk
+PATH_TO_LIB_TWO=../lib_two/lib_two.apk
+
+aapt package \
+    -M AndroidManifest.xml \
+    -S res \
+    -I $PATH_TO_FRAMEWORK_RES \
+    -I $PATH_TO_LIB_ONE \
+    -I $PATH_TO_LIB_TWO \
+    -F libclient.apk -f
diff --git a/libs/androidfw/tests/data/libclient/libclient.apk b/libs/androidfw/tests/data/libclient/libclient.apk
new file mode 100644
index 0000000..1799024
--- /dev/null
+++ b/libs/androidfw/tests/data/libclient/libclient.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/libclient/res/values/values.xml b/libs/androidfw/tests/data/libclient/res/values/values.xml
new file mode 100644
index 0000000..fead7c3
--- /dev/null
+++ b/libs/androidfw/tests/data/libclient/res/values/values.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <public type="attr" name="foo" id="0x7f010000" />
+    <attr name="foo" />
+
+    <public type="attr" name="bar" id="0x7f010001" />
+    <attr name="bar" />
+
+    <public type="style" name="Theme" id="0x7f020000" />
+    <style name="Theme" parent="com.android.lib_one:style/Theme">
+      <item name="foo">?com.android.lib_one:attr/attr2</item>
+      <item name="bar">@com.android.lib_one:string/foo</item>
+    </style>
+
+    <public type="string" name="foo_one" id="0x7f030000" />
+    <string name="foo_one">@com.android.lib_one:string/foo</string>
+
+    <public type="string" name="foo_two" id="0x7f030001" />
+    <string name="foo_two">@com.android.lib_two:string/foo</string>
+</resources>