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/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, ¤t_entry, ¤t_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, ¤t_entry,
+ ¤t_config, ¤t_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();