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/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;