Allow for RRO internal referencing
This change allows RROs to reference their own internal resources as
expected.
Overlays are loaded as shared libraries so they can have their own
resource id space that does not conflict with the resource id space of
the target or other overlays.
References to overlay resources that override target resources now
appear as references to the target resources.
Overlay values that are inlined into the xml file specified using
android:overlayResources are now able to be used at runtime.
See go/rro-references for more information.
Bug: 135943783
Test: idmap2_tests
Test: libandroidfw_tests
Change-Id: Ie349c56d7fd3f7d94b7d595ed6d01dc6b59b6178
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index a34a6c0..4f52a88 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -167,8 +167,9 @@
},
},
data: [
- "tests/data/**/*.apk",
- "tests/data/**/*.arsc",
+ "tests/data/**/*.apk",
+ "tests/data/**/*.arsc",
+ "tests/data/**/*.idmap",
],
test_suites: ["device-tests"],
}
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index b309621..16dbbf6 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -45,7 +45,7 @@
time_t last_mod_time,
bool for_loader)
: zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time),
- for_loader(for_loader) {
+ for_loader_(for_loader) {
}
std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system,
@@ -75,7 +75,7 @@
return {};
}
return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(), std::move(idmap_asset),
- std::move(loaded_idmap), system, false /*load_as_shared_library*/);
+ std::move(loaded_idmap), system, true /*load_as_shared_library*/);
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd,
@@ -165,12 +165,14 @@
// Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid.
loaded_apk->idmap_asset_ = std::move(idmap_asset);
+ loaded_apk->loaded_idmap_ = std::move(loaded_idmap);
const StringPiece data(
reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
loaded_apk->resources_asset_->getLength());
loaded_apk->loaded_arsc_ =
- LoadedArsc::Load(data, loaded_idmap.get(), system, load_as_shared_library, for_loader);
+ LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(), system, load_as_shared_library,
+ for_loader);
if (loaded_apk->loaded_arsc_ == nullptr) {
LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
return {};
@@ -319,7 +321,7 @@
bool ApkAssets::IsUpToDate() const {
// Loaders are invalidated by the app, not the system, so assume up to date
- if (for_loader) {
+ if (for_loader_) {
return true;
}
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index e914f37..ca4143f 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -25,6 +25,7 @@
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
+#include "androidfw/ResourceUtils.h"
#include "androidfw/Util.h"
#include "utils/ByteOrder.h"
#include "utils/Trace.h"
@@ -35,15 +36,13 @@
#endif
#endif
-#include "androidfw/ResourceUtils.h"
-
namespace android {
struct FindEntryResult {
// A pointer to the resource table entry for this resource.
// If the size of the entry is > sizeof(ResTable_entry), it can be cast to
// a ResTable_map_entry and processed as a bag/map.
- const ResTable_entry* entry;
+ ResTable_entry_handle entry;
// The configuration for which the resulting entry was defined. This is already swapped to host
// endianness.
@@ -55,6 +54,9 @@
// The dynamic package ID map for the package from which this resource came from.
const DynamicRefTable* dynamic_ref_table;
+ // The package name of the resource.
+ const std::string* package_name;
+
// 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;
@@ -83,11 +85,15 @@
package_groups_.clear();
package_ids_.fill(0xff);
+ // A mapping from apk assets path to the runtime package id of its first loaded package.
+ std::unordered_map<std::string, uint8_t> apk_assets_package_ids;
+
// 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 LoadedArsc* loaded_arsc = apk_assets_[i]->GetLoadedArsc();
+ const ApkAssets* apk_assets = apk_assets_[i];
+ const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc();
for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) {
// Get the package ID or assign one if a shared library.
@@ -103,9 +109,37 @@
if (idx == 0xff) {
package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size());
package_groups_.push_back({});
- DynamicRefTable& ref_table = package_groups_.back().dynamic_ref_table;
- ref_table.mAssignedPackageId = package_id;
- ref_table.mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
+
+ if (apk_assets->IsOverlay()) {
+ // The target package must precede the overlay package in the apk assets paths in order
+ // to take effect.
+ const auto& loaded_idmap = apk_assets->GetLoadedIdmap();
+ auto target_package_iter = apk_assets_package_ids.find(loaded_idmap->TargetApkPath());
+ if (target_package_iter != apk_assets_package_ids.end()) {
+ const uint8_t target_package_id = target_package_iter->second;
+ const uint8_t target_idx = package_ids_[target_package_id];
+ CHECK(target_idx != 0xff) << "overlay added to apk_assets_package_ids but does not"
+ << " have an assigned package group";
+
+ PackageGroup& target_package_group = package_groups_[target_idx];
+
+ // Create a special dynamic reference table for the overlay to rewite references to
+ // overlay resources as references to the target resources they overlay.
+ auto overlay_table = std::make_shared<OverlayDynamicRefTable>(
+ loaded_idmap->GetOverlayDynamicRefTable(target_package_id));
+ package_groups_.back().dynamic_ref_table = overlay_table;
+
+ // Add the overlay resource map to the target package's set of overlays.
+ target_package_group.overlays_.push_back(
+ ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id,
+ overlay_table.get()),
+ static_cast<ApkAssetsCookie>(i)});
+ }
+ }
+
+ DynamicRefTable* ref_table = package_groups_.back().dynamic_ref_table.get();
+ ref_table->mAssignedPackageId = package_id;
+ ref_table->mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
}
PackageGroup* package_group = &package_groups_[idx];
@@ -116,9 +150,11 @@
// 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_group->dynamic_ref_table->mEntries.replaceValueFor(
package_name, static_cast<uint8_t>(entry.package_id));
}
+
+ apk_assets_package_ids.insert(std::make_pair(apk_assets->GetPath(), package_id));
}
}
@@ -127,8 +163,8 @@
for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) {
const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName();
for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) {
- iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()),
- iter->dynamic_ref_table.mAssignedPackageId);
+ iter2->dynamic_ref_table->addMapping(String16(package_name.c_str(), package_name.size()),
+ iter->dynamic_ref_table->mAssignedPackageId);
}
}
}
@@ -161,13 +197,13 @@
(loaded_package->IsDynamic() ? " dynamic" : ""));
}
LOG(INFO) << base::StringPrintf("PG (%02x): ",
- package_group.dynamic_ref_table.mAssignedPackageId)
+ package_group.dynamic_ref_table->mAssignedPackageId)
<< list;
for (size_t i = 0; i < 256; i++) {
- if (package_group.dynamic_ref_table.mLookupTable[i] != 0) {
+ if (package_group.dynamic_ref_table->mLookupTable[i] != 0) {
LOG(INFO) << base::StringPrintf(" e[0x%02x] -> 0x%02x", (uint8_t) i,
- package_group.dynamic_ref_table.mLookupTable[i]);
+ package_group.dynamic_ref_table->mLookupTable[i]);
}
}
}
@@ -189,14 +225,15 @@
if (idx == 0xff) {
return nullptr;
}
- return &package_groups_[idx].dynamic_ref_table;
+ return package_groups_[idx].dynamic_ref_table.get();
}
-const DynamicRefTable* AssetManager2::GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const {
+std::shared_ptr<const DynamicRefTable> AssetManager2::GetDynamicRefTableForCookie(
+ ApkAssetsCookie cookie) const {
for (const PackageGroup& package_group : package_groups_) {
for (const ApkAssetsCookie& package_cookie : package_group.cookies_) {
if (package_cookie == cookie) {
- return &package_group.dynamic_ref_table;
+ return package_group.dynamic_ref_table;
}
}
}
@@ -290,21 +327,45 @@
}
}
-std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
- bool exclude_mipmap) const {
- ATRACE_NAME("AssetManager::GetResourceConfigurations");
- std::set<ResTable_config> configurations;
+std::set<std::string> AssetManager2::GetNonSystemOverlayPaths() const {
+ std::set<std::string> non_system_overlays;
for (const PackageGroup& package_group : package_groups_) {
bool found_system_package = false;
for (const ConfiguredPackage& package : package_group.packages_) {
- if (exclude_system && package.loaded_package_->IsSystem()) {
+ if (package.loaded_package_->IsSystem()) {
found_system_package = true;
+ break;
+ }
+ }
+
+ if (!found_system_package) {
+ for (const ConfiguredOverlay& overlay : package_group.overlays_) {
+ non_system_overlays.insert(apk_assets_[overlay.cookie]->GetPath());
+ }
+ }
+ }
+
+ return non_system_overlays;
+}
+
+std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
+ bool exclude_mipmap) const {
+ ATRACE_NAME("AssetManager::GetResourceConfigurations");
+ const auto non_system_overlays =
+ (exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>();
+
+ std::set<ResTable_config> configurations;
+ for (const PackageGroup& package_group : package_groups_) {
+ for (size_t i = 0; i < package_group.packages_.size(); i++) {
+ const ConfiguredPackage& package = package_group.packages_[i];
+ if (exclude_system && package.loaded_package_->IsSystem()) {
continue;
}
- if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) {
- // Overlays must appear after the target package to take effect. Any overlay found in the
- // same package as a system package is able to overlay system resources.
+ auto apk_assets = apk_assets_[package_group.cookies_[i]];
+ if (exclude_system && apk_assets->IsOverlay()
+ && non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) {
+ // Exclude overlays that target system resources.
continue;
}
@@ -318,17 +379,20 @@
bool merge_equivalent_languages) const {
ATRACE_NAME("AssetManager::GetResourceLocales");
std::set<std::string> locales;
+ const auto non_system_overlays =
+ (exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>();
+
for (const PackageGroup& package_group : package_groups_) {
- bool found_system_package = false;
- for (const ConfiguredPackage& package : package_group.packages_) {
+ for (size_t i = 0; i < package_group.packages_.size(); i++) {
+ const ConfiguredPackage& package = package_group.packages_[i];
if (exclude_system && package.loaded_package_->IsSystem()) {
- found_system_package = true;
continue;
}
- if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) {
- // Overlays must appear after the target package to take effect. Any overlay found in the
- // same package as a system package is able to overlay system resources.
+ auto apk_assets = apk_assets_[package_group.cookies_[i]];
+ if (exclude_system && apk_assets->IsOverlay()
+ && non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) {
+ // Exclude overlays that target system resources.
continue;
}
@@ -424,6 +488,12 @@
bool /*stop_at_first_match*/,
bool ignore_configuration,
FindEntryResult* out_entry) const {
+ if (resource_resolution_logging_enabled_) {
+ // Clear the last logged resource resolution.
+ ResetResourceResolution();
+ last_resolution_.resid = resid;
+ }
+
// Might use this if density_override != 0.
ResTable_config density_override_config;
@@ -435,6 +505,7 @@
desired_config = &density_override_config;
}
+ // Retrieve the package group from the package id of the resource id.
if (!is_valid_resid(resid)) {
LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
return kInvalidCookie;
@@ -443,8 +514,7 @@
const uint32_t package_id = get_package_id(resid);
const uint8_t type_idx = get_type_id(resid) - 1;
const uint16_t entry_idx = get_entry_id(resid);
-
- const uint8_t package_idx = package_ids_[package_id];
+ uint8_t package_idx = package_ids_[package_id];
if (package_idx == 0xff) {
ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.",
package_id, resid);
@@ -452,8 +522,71 @@
}
const PackageGroup& package_group = package_groups_[package_idx];
- const size_t package_count = package_group.packages_.size();
+ ApkAssetsCookie cookie = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
+ false /* stop_at_first_match */,
+ ignore_configuration, out_entry);
+ if (UNLIKELY(cookie == kInvalidCookie)) {
+ return kInvalidCookie;
+ }
+ if (!apk_assets_[cookie]->IsLoader()) {
+ for (const auto& id_map : package_group.overlays_) {
+ auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
+ if (!overlay_entry) {
+ // No id map entry exists for this target resource.
+ continue;
+ }
+
+ if (overlay_entry.IsTableEntry()) {
+ // The target resource is overlaid by an inline value not represented by a resource.
+ out_entry->entry = overlay_entry.GetTableEntry();
+ out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
+ cookie = id_map.cookie;
+ continue;
+ }
+
+ FindEntryResult overlay_result;
+ ApkAssetsCookie overlay_cookie = FindEntry(overlay_entry.GetResourceId(), density_override,
+ false /* stop_at_first_match */,
+ ignore_configuration, &overlay_result);
+ if (UNLIKELY(overlay_cookie == kInvalidCookie)) {
+ continue;
+ }
+
+ if (!overlay_result.config.isBetterThan(out_entry->config, desired_config)
+ && overlay_result.config.compare(out_entry->config) != 0) {
+ // The configuration of the entry for the overlay must be equal to or better than the target
+ // configuration to be chosen as the better value.
+ continue;
+ }
+
+ cookie = overlay_cookie;
+ out_entry->entry = std::move(overlay_result.entry);
+ out_entry->config = overlay_result.config;
+ out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
+ if (resource_resolution_logging_enabled_) {
+ last_resolution_.steps.push_back(
+ Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(),
+ &package_group.packages_[0].loaded_package_->GetPackageName()});
+ }
+ }
+ }
+
+ if (resource_resolution_logging_enabled_) {
+ last_resolution_.cookie = cookie;
+ last_resolution_.type_string_ref = out_entry->type_string_ref;
+ last_resolution_.entry_string_ref = out_entry->entry_string_ref;
+ }
+
+ return cookie;
+}
+
+ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_group,
+ uint8_t type_idx, uint16_t entry_idx,
+ const ResTable_config& desired_config,
+ bool /*stop_at_first_match*/,
+ bool ignore_configuration,
+ FindEntryResult* out_entry) const {
ApkAssetsCookie best_cookie = kInvalidCookie;
const LoadedPackage* best_package = nullptr;
const ResTable_type* best_type = nullptr;
@@ -462,13 +595,14 @@
uint32_t best_offset = 0u;
uint32_t type_flags = 0u;
- Resolution::Step::Type resolution_type;
+ Resolution::Step::Type resolution_type = Resolution::Step::Type::NO_ENTRY;
std::vector<Resolution::Step> resolution_steps;
// If desired_config is the same as the set configuration, then we can use our filtered list
// and we don't need to match the configurations, since they already matched.
- const bool use_fast_path = !ignore_configuration && desired_config == &configuration_;
+ const bool use_fast_path = !ignore_configuration && &desired_config == &configuration_;
+ const size_t package_count = package_group.packages_.size();
for (size_t pi = 0; pi < package_count; pi++) {
const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_;
@@ -481,24 +615,10 @@
continue;
}
- uint16_t local_entry_idx = entry_idx;
-
- // If there is an IDMAP supplied with this package, translate the entry ID.
- if (type_spec->idmap_entries != nullptr) {
- if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) {
- // There is no mapping, so the resource is not meant to be in this overlay package.
- continue;
- }
- }
-
- type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx);
-
-
// If the package is an overlay or custom loader,
// then even configurations that are the same MUST be chosen.
- const bool package_is_overlay = loaded_package->IsOverlay();
const bool package_is_loader = loaded_package->IsCustomLoader();
- const bool should_overlay = package_is_overlay || package_is_loader;
+ type_flags |= type_spec->GetFlagsForEntryIndex(entry_idx);
if (use_fast_path) {
const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
@@ -511,25 +631,15 @@
// configurations that do NOT match have been filtered-out.
if (best_config == nullptr) {
resolution_type = Resolution::Step::Type::INITIAL;
- } else if (this_config.isBetterThan(*best_config, desired_config)) {
- if (package_is_loader) {
- resolution_type = Resolution::Step::Type::BETTER_MATCH_LOADER;
- } else {
- resolution_type = Resolution::Step::Type::BETTER_MATCH;
- }
- } else if (should_overlay && this_config.compare(*best_config) == 0) {
- if (package_is_loader) {
- resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
- } else if (package_is_overlay) {
- resolution_type = Resolution::Step::Type::OVERLAID;
- }
+ } else if (this_config.isBetterThan(*best_config, &desired_config)) {
+ resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER
+ : Resolution::Step::Type::BETTER_MATCH;
+ } else if (package_is_loader && this_config.compare(*best_config) == 0) {
+ resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
} else {
if (resource_resolution_logging_enabled_) {
- if (package_is_loader) {
- resolution_type = Resolution::Step::Type::SKIPPED_LOADER;
- } else {
- resolution_type = Resolution::Step::Type::SKIPPED;
- }
+ resolution_type = (package_is_loader) ? Resolution::Step::Type::SKIPPED_LOADER
+ : Resolution::Step::Type::SKIPPED;
resolution_steps.push_back(Resolution::Step{resolution_type,
this_config.toString(),
&loaded_package->GetPackageName()});
@@ -540,7 +650,7 @@
// The configuration matches and is better than the previous selection.
// Find the entry value if it exists for this configuration.
const ResTable_type* type = filtered_group.types[i];
- const uint32_t offset = LoadedPackage::GetEntryOffset(type, local_entry_idx);
+ const uint32_t offset = LoadedPackage::GetEntryOffset(type, entry_idx);
if (offset == ResTable_type::NO_ENTRY) {
if (resource_resolution_logging_enabled_) {
if (package_is_loader) {
@@ -548,7 +658,7 @@
} else {
resolution_type = Resolution::Step::Type::NO_ENTRY;
}
- resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::NO_ENTRY,
+ resolution_steps.push_back(Resolution::Step{resolution_type,
this_config.toString(),
&loaded_package->GetPackageName()});
}
@@ -562,9 +672,9 @@
best_offset = offset;
if (resource_resolution_logging_enabled_) {
- resolution_steps.push_back(Resolution::Step{resolution_type,
- this_config.toString(),
- &loaded_package->GetPackageName()});
+ last_resolution_.steps.push_back(Resolution::Step{resolution_type,
+ this_config.toString(),
+ &loaded_package->GetPackageName()});
}
}
} else {
@@ -579,24 +689,17 @@
if (!ignore_configuration) {
this_config.copyFromDtoH((*iter)->config);
- if (!this_config.match(*desired_config)) {
+ if (!this_config.match(desired_config)) {
continue;
}
if (best_config == nullptr) {
resolution_type = Resolution::Step::Type::INITIAL;
- } else if (this_config.isBetterThan(*best_config, desired_config)) {
- if (package_is_loader) {
- resolution_type = Resolution::Step::Type::BETTER_MATCH_LOADER;
- } else {
- resolution_type = Resolution::Step::Type::BETTER_MATCH;
- }
- } else if (should_overlay && this_config.compare(*best_config) == 0) {
- if (package_is_overlay) {
- resolution_type = Resolution::Step::Type::OVERLAID;
- } else if (package_is_loader) {
- resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
- }
+ } else if (this_config.isBetterThan(*best_config, &desired_config)) {
+ resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER
+ : Resolution::Step::Type::BETTER_MATCH;
+ } else if (package_is_loader && this_config.compare(*best_config) == 0) {
+ resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
} else {
continue;
}
@@ -604,7 +707,7 @@
// The configuration matches and is better than the previous selection.
// Find the entry value if it exists for this configuration.
- const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx);
+ const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, entry_idx);
if (offset == ResTable_type::NO_ENTRY) {
continue;
}
@@ -622,9 +725,9 @@
}
if (resource_resolution_logging_enabled_) {
- resolution_steps.push_back(Resolution::Step{resolution_type,
- this_config.toString(),
- &loaded_package->GetPackageName()});
+ last_resolution_.steps.push_back(Resolution::Step{resolution_type,
+ this_config.toString(),
+ &loaded_package->GetPackageName()});
}
}
}
@@ -639,38 +742,30 @@
return kInvalidCookie;
}
- out_entry->entry = best_entry;
+ out_entry->entry = ResTable_entry_handle::unmanaged(best_entry);
out_entry->config = *best_config;
out_entry->type_flags = type_flags;
+ out_entry->package_name = &best_package->GetPackageName();
out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
out_entry->entry_string_ref =
- StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
- out_entry->dynamic_ref_table = &package_group.dynamic_ref_table;
-
- if (resource_resolution_logging_enabled_) {
- last_resolution.resid = resid;
- last_resolution.cookie = best_cookie;
- last_resolution.steps = resolution_steps;
-
- // Cache only the type/entry refs since that's all that's needed to build name
- last_resolution.type_string_ref =
- StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
- last_resolution.entry_string_ref =
- StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
- }
+ StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
+ out_entry->dynamic_ref_table = package_group.dynamic_ref_table.get();
return best_cookie;
}
+void AssetManager2::ResetResourceResolution() const {
+ last_resolution_.cookie = kInvalidCookie;
+ last_resolution_.resid = 0;
+ last_resolution_.steps.clear();
+ last_resolution_.type_string_ref = StringPoolRef();
+ last_resolution_.entry_string_ref = StringPoolRef();
+}
+
void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) {
resource_resolution_logging_enabled_ = enabled;
-
if (!enabled) {
- last_resolution.cookie = kInvalidCookie;
- last_resolution.resid = 0;
- last_resolution.steps.clear();
- last_resolution.type_string_ref = StringPoolRef();
- last_resolution.entry_string_ref = StringPoolRef();
+ ResetResourceResolution();
}
}
@@ -680,24 +775,24 @@
return std::string();
}
- auto cookie = last_resolution.cookie;
+ auto cookie = last_resolution_.cookie;
if (cookie == kInvalidCookie) {
LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path.";
return std::string();
}
- uint32_t resid = last_resolution.resid;
- std::vector<Resolution::Step>& steps = last_resolution.steps;
+ uint32_t resid = last_resolution_.resid;
+ std::vector<Resolution::Step>& steps = last_resolution_.steps;
ResourceName resource_name;
std::string resource_name_string;
const LoadedPackage* package =
- apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
+ apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
if (package != nullptr) {
- ToResourceName(last_resolution.type_string_ref,
- last_resolution.entry_string_ref,
+ ToResourceName(last_resolution_.type_string_ref,
+ last_resolution_.entry_string_ref,
package->GetPackageName(),
&resource_name);
resource_name_string = ToFormattedResourceString(&resource_name);
@@ -762,25 +857,9 @@
return false;
}
- const uint8_t package_idx = package_ids_[get_package_id(resid)];
- if (package_idx == 0xff) {
- LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.",
- get_package_id(resid), resid);
- return false;
- }
-
- const PackageGroup& package_group = package_groups_[package_idx];
- auto cookie_iter = std::find(package_group.cookies_.begin(),
- package_group.cookies_.end(), cookie);
- if (cookie_iter == package_group.cookies_.end()) {
- return false;
- }
-
- long package_pos = std::distance(package_group.cookies_.begin(), cookie_iter);
- const LoadedPackage* package = package_group.packages_[package_pos].loaded_package_;
return ToResourceName(entry.type_string_ref,
entry.entry_string_ref,
- package->GetPackageName(),
+ *entry.package_name,
out_name);
}
@@ -807,7 +886,8 @@
return kInvalidCookie;
}
- if (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) {
+ const ResTable_entry* table_entry = *entry.entry;
+ if (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) {
if (!may_be_bag) {
LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
return kInvalidCookie;
@@ -822,7 +902,7 @@
}
const Res_value* device_value = reinterpret_cast<const Res_value*>(
- reinterpret_cast<const uint8_t*>(entry.entry) + dtohs(entry.entry->size));
+ reinterpret_cast<const uint8_t*>(table_entry) + dtohs(table_entry->size));
out_value->copyFrom_dtoh(*device_value);
// Convert the package ID to the runtime assigned package ID.
@@ -903,13 +983,14 @@
// Check that the size of the entry header is at least as big as
// the desired ResTable_map_entry. Also verify that the entry
// was intended to be a map.
- if (dtohs(entry.entry->size) < sizeof(ResTable_map_entry) ||
- (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) {
+ const ResTable_entry* table_entry = *entry.entry;
+ if (dtohs(table_entry->size) < sizeof(ResTable_map_entry) ||
+ (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) {
// Not a bag, nothing to do.
return nullptr;
}
- const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry.entry);
+ const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(table_entry);
const ResTable_map* map_entry =
reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(map) + map->size);
const ResTable_map* const map_entry_end = map_entry + dtohl(map->count);
@@ -1134,7 +1215,7 @@
}
if (resid != 0u) {
- return fix_package_id(resid, package_group.dynamic_ref_table.mAssignedPackageId);
+ return fix_package_id(resid, package_group.dynamic_ref_table->mAssignedPackageId);
}
}
}
@@ -1191,7 +1272,7 @@
for (auto& package_group : package_groups_) {
for (auto& package2 : package_group.packages_) {
if (package2.loaded_package_ == package) {
- return package_group.dynamic_ref_table.mAssignedPackageId;
+ return package_group.dynamic_ref_table->mAssignedPackageId;
}
}
}
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index 7c1ee5c..2b69c92 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -20,6 +20,8 @@
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/Util.h"
#include "utils/ByteOrder.h"
#include "utils/Trace.h"
@@ -29,40 +31,124 @@
#endif
#endif
-#include "androidfw/ResourceTypes.h"
-
using ::android::base::StringPrintf;
namespace android {
-constexpr static inline bool is_valid_package_id(uint16_t id) {
- return id != 0 && id <= 255;
+static bool compare_target_entries(const Idmap_target_entry &e1, const uint32_t target_id) {
+ return dtohl(e1.target_id) < target_id;
}
-constexpr static inline bool is_valid_type_id(uint16_t id) {
- // Type IDs and package IDs have the same constraints in the IDMAP.
- return is_valid_package_id(id);
+static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
+ return dtohl(e1.overlay_id) < overlay_id;
}
-bool LoadedIdmap::Lookup(const IdmapEntry_header* header, uint16_t input_entry_id,
- uint16_t* output_entry_id) {
- if (input_entry_id < dtohs(header->entry_id_offset)) {
- // After applying the offset, the entry is not present.
- return false;
+OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
+ : data_header_(loaded_idmap->data_header_),
+ idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
+
+OverlayStringPool::~OverlayStringPool() {
+ uninit();
+}
+
+const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const {
+ const size_t offset = dtohl(data_header_->string_pool_index_offset);
+ if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) {
+ return idmap_string_pool_->stringAt(idx - offset, outLen);
}
- input_entry_id -= dtohs(header->entry_id_offset);
- if (input_entry_id >= dtohs(header->entry_count)) {
- // The entry is not present.
- return false;
+ return ResStringPool::stringAt(idx, outLen);
+}
+
+const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const {
+ const size_t offset = dtohl(data_header_->string_pool_index_offset);
+ if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) {
+ return idmap_string_pool_->string8At(idx - offset, outLen);
}
- uint32_t result = dtohl(header->entries[input_entry_id]);
- if (result == 0xffffffffu) {
- return false;
+ return ResStringPool::string8At(idx, outLen);
+}
+
+OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
+ const Idmap_overlay_entry* entries,
+ uint8_t target_assigned_package_id)
+ : data_header_(data_header),
+ entries_(entries),
+ target_assigned_package_id_(target_assigned_package_id) { };
+
+status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
+ const Idmap_overlay_entry* first_entry = entries_;
+ const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
+ auto entry = std::lower_bound(first_entry, end_entry, *resId, compare_overlay_entries);
+
+ if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
+ // A mapping for the target resource id could not be found.
+ return DynamicRefTable::lookupResourceId(resId);
}
- *output_entry_id = static_cast<uint16_t>(result);
- return true;
+
+ *resId = (0x00FFFFFFU & dtohl(entry->target_id))
+ | (((uint32_t) target_assigned_package_id_) << 24);
+ return NO_ERROR;
+}
+
+status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const {
+ return DynamicRefTable::lookupResourceId(resId);
+}
+
+IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
+ const Idmap_target_entry* entries,
+ uint8_t target_assigned_package_id,
+ const OverlayDynamicRefTable* overlay_ref_table)
+ : data_header_(data_header),
+ entries_(entries),
+ target_assigned_package_id_(target_assigned_package_id),
+ overlay_ref_table_(overlay_ref_table) { };
+
+IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
+ if ((target_res_id >> 24) != target_assigned_package_id_) {
+ // The resource id must have the same package id as the target package.
+ return {};
+ }
+
+ // The resource ids encoded within the idmap are build-time resource ids.
+ target_res_id = (0x00FFFFFFU & target_res_id)
+ | (((uint32_t) data_header_->target_package_id) << 24);
+
+ const Idmap_target_entry* first_entry = entries_;
+ const Idmap_target_entry* end_entry = entries_ + dtohl(data_header_->target_entry_count);
+ auto entry = std::lower_bound(first_entry, end_entry, target_res_id, compare_target_entries);
+
+ if (entry == end_entry || dtohl(entry->target_id) != target_res_id) {
+ // A mapping for the target resource id could not be found.
+ return {};
+ }
+
+ // A reference should be treated as an alias of the resource. Instead of returning the table
+ // entry, return the alias resource id to look up. The alias resource might not reside within the
+ // overlay package, so the resource id must be fixed with the dynamic reference table of the
+ // overlay before returning.
+ if (entry->type == Res_value::TYPE_REFERENCE
+ || entry->type == Res_value::TYPE_DYNAMIC_REFERENCE) {
+ uint32_t overlay_resource_id = dtohl(entry->value);
+
+ // Lookup the resource without rewriting the overlay resource id back to the target resource id
+ // being looked up.
+ overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
+ return Result(overlay_resource_id);
+ }
+
+ // Copy the type and value into the ResTable_entry structure needed by asset manager.
+ uint16_t malloc_size = sizeof(ResTable_entry) + sizeof(Res_value);
+ auto table_entry = reinterpret_cast<ResTable_entry*>(malloc(malloc_size));
+ memset(table_entry, 0, malloc_size);
+ table_entry->size = htods(sizeof(ResTable_entry));
+
+ auto table_value = reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(table_entry)
+ + sizeof(ResTable_entry));
+ table_value->dataType = entry->type;
+ table_value->data = entry->value;
+
+ return Result(ResTable_entry_handle::managed(table_entry));
}
static bool is_word_aligned(const void* data) {
@@ -95,24 +181,26 @@
return false;
}
- if (!is_valid_package_id(dtohs(header->target_package_id))) {
- LOG(ERROR) << StringPrintf("Target package ID in Idmap is invalid: 0x%02x",
- dtohs(header->target_package_id));
- return false;
- }
-
- if (dtohs(header->type_count) > 255) {
- LOG(ERROR) << StringPrintf("Idmap has too many type mappings (was %d, max 255)",
- (int)dtohs(header->type_count));
- return false;
- }
return true;
}
-LoadedIdmap::LoadedIdmap(const Idmap_header* header) : header_(header) {
+LoadedIdmap::LoadedIdmap(const Idmap_header* header,
+ const Idmap_data_header* data_header,
+ const Idmap_target_entry* target_entries,
+ const Idmap_overlay_entry* overlay_entries,
+ ResStringPool* string_pool) : header_(header),
+ data_header_(data_header),
+ target_entries_(target_entries),
+ overlay_entries_(overlay_entries),
+ string_pool_(string_pool) {
+
size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path),
arraysize(header_->overlay_path));
overlay_apk_path_.assign(reinterpret_cast<const char*>(header_->overlay_path), length);
+
+ length = strnlen(reinterpret_cast<const char*>(header_->target_path),
+ arraysize(header_->target_path));
+ target_apk_path_.assign(reinterpret_cast<const char*>(header_->target_path), length);
}
std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_data) {
@@ -121,70 +209,67 @@
return {};
}
- const Idmap_header* header = reinterpret_cast<const Idmap_header*>(idmap_data.data());
-
- // Can't use make_unique because LoadedImpl constructor is private.
- std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(new LoadedIdmap(header));
-
+ auto header = reinterpret_cast<const Idmap_header*>(idmap_data.data());
const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + sizeof(*header);
size_t data_size = idmap_data.size() - sizeof(*header);
- size_t type_maps_encountered = 0u;
- while (data_size >= sizeof(IdmapEntry_header)) {
- if (!is_word_aligned(data_ptr)) {
- LOG(ERROR) << "Type mapping in Idmap is not word aligned";
- return {};
- }
+ // Currently idmap2 can only generate one data block.
+ auto data_header = reinterpret_cast<const Idmap_data_header*>(data_ptr);
+ data_ptr += sizeof(*data_header);
+ data_size -= sizeof(*data_header);
- // Validate the type IDs.
- const IdmapEntry_header* entry_header = reinterpret_cast<const IdmapEntry_header*>(data_ptr);
- if (!is_valid_type_id(dtohs(entry_header->target_type_id)) || !is_valid_type_id(dtohs(entry_header->overlay_type_id))) {
- LOG(ERROR) << StringPrintf("Invalid type map (0x%02x -> 0x%02x)",
- dtohs(entry_header->target_type_id),
- dtohs(entry_header->overlay_type_id));
- return {};
- }
-
- // Make sure there is enough space for the entries declared in the header.
- if ((data_size - sizeof(*entry_header)) / sizeof(uint32_t) <
- static_cast<size_t>(dtohs(entry_header->entry_count))) {
- LOG(ERROR) << StringPrintf("Idmap too small for the number of entries (%d)",
- (int)dtohs(entry_header->entry_count));
- return {};
- }
-
- // Only add a non-empty overlay.
- if (dtohs(entry_header->entry_count != 0)) {
- loaded_idmap->type_map_[static_cast<uint8_t>(dtohs(entry_header->overlay_type_id))] =
- entry_header;
- }
-
- const size_t entry_size_bytes =
- sizeof(*entry_header) + (dtohs(entry_header->entry_count) * sizeof(uint32_t));
- data_ptr += entry_size_bytes;
- data_size -= entry_size_bytes;
- type_maps_encountered++;
- }
-
- // Verify that we parsed all the type maps.
- if (type_maps_encountered != static_cast<size_t>(dtohs(header->type_count))) {
- LOG(ERROR) << "Parsed " << type_maps_encountered << " type maps but expected "
- << (int)dtohs(header->type_count);
+ // Make sure there is enough space for the target entries declared in the header.
+ const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr);
+ if (data_size / sizeof(Idmap_target_entry) <
+ static_cast<size_t>(dtohl(data_header->target_entry_count))) {
+ LOG(ERROR) << StringPrintf("Idmap too small for the number of target entries (%d)",
+ (int)dtohl(data_header->target_entry_count));
return {};
}
- return std::move(loaded_idmap);
-}
-uint8_t LoadedIdmap::TargetPackageId() const {
- return static_cast<uint8_t>(dtohs(header_->target_package_id));
-}
+ // Advance the data pointer past the target entries.
+ const size_t target_entry_size_bytes =
+ (dtohl(data_header->target_entry_count) * sizeof(Idmap_target_entry));
+ data_ptr += target_entry_size_bytes;
+ data_size -= target_entry_size_bytes;
-const IdmapEntry_header* LoadedIdmap::GetEntryMapForType(uint8_t type_id) const {
- auto iter = type_map_.find(type_id);
- if (iter != type_map_.end()) {
- return iter->second;
+ // Make sure there is enough space for the overlay entries declared in the header.
+ const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr);
+ if (data_size / sizeof(Idmap_overlay_entry) <
+ static_cast<size_t>(dtohl(data_header->overlay_entry_count))) {
+ LOG(ERROR) << StringPrintf("Idmap too small for the number of overlay entries (%d)",
+ (int)dtohl(data_header->overlay_entry_count));
+ return {};
}
- return nullptr;
+
+ // Advance the data pointer past the target entries.
+ const size_t overlay_entry_size_bytes =
+ (dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry));
+ data_ptr += overlay_entry_size_bytes;
+ data_size -= overlay_entry_size_bytes;
+
+ // Read the idmap string pool that holds the value of inline string entries.
+ if (data_size < dtohl(data_header->string_pool_length)) {
+ LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)",
+ (int)dtohl(data_header->string_pool_length));
+ return {};
+ }
+
+ auto idmap_string_pool = util::make_unique<ResStringPool>();
+ if (dtohl(data_header->string_pool_length) > 0) {
+ status_t err = idmap_string_pool->setTo(data_ptr, dtohl(data_header->string_pool_length));
+ if (err != NO_ERROR) {
+ LOG(ERROR) << "idmap string pool corrupt.";
+ return {};
+ }
+ }
+
+ // Can't use make_unique because LoadedImpl constructor is private.
+ std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(
+ new LoadedIdmap(header, data_header, target_entries, overlay_entries,
+ idmap_string_pool.release()));
+
+ return std::move(loaded_idmap);
}
} // namespace android
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 882dc0d..c896241 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -51,9 +51,8 @@
// the Type structs.
class TypeSpecPtrBuilder {
public:
- explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header,
- const IdmapEntry_header* idmap_header)
- : header_(header), idmap_header_(idmap_header) {
+ explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header)
+ : header_(header) {
}
void AddType(const ResTable_type* type) {
@@ -70,7 +69,6 @@
TypeSpec* type_spec =
(TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType)));
type_spec->type_spec = header_;
- type_spec->idmap_entries = idmap_header_;
type_spec->type_count = types_.size();
memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType));
return TypeSpecPtr(type_spec);
@@ -80,7 +78,6 @@
DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder);
const ResTable_typeSpec* header_;
- const IdmapEntry_header* idmap_header_;
std::vector<const ResTable_type*> types_;
};
@@ -400,7 +397,6 @@
}
std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
- const LoadedIdmap* loaded_idmap,
bool system,
bool load_as_shared_library,
bool for_loader) {
@@ -426,12 +422,6 @@
loaded_package->dynamic_ = true;
}
- if (loaded_idmap != nullptr) {
- // This is an overlay and so it needs to pretend to be the target package.
- loaded_package->package_id_ = loaded_idmap->TargetPackageId();
- loaded_package->overlay_ = true;
- }
-
if (for_loader) {
loaded_package->custom_loader_ = true;
}
@@ -517,16 +507,9 @@
return {};
}
- // If this is an overlay, associate the mapping of this type to the target type
- // from the IDMAP.
- const IdmapEntry_header* idmap_entry_header = nullptr;
- if (loaded_idmap != nullptr) {
- idmap_entry_header = loaded_idmap->GetEntryMapForType(type_spec->id);
- }
-
std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type_spec->id - 1];
if (builder_ptr == nullptr) {
- builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec, idmap_entry_header);
+ builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec);
loaded_package->resource_ids_.set(type_spec->id, entry_count);
} else {
LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x",
@@ -687,15 +670,7 @@
return {};
}
- // We only add the type to the package if there is no IDMAP, or if the type is
- // overlaying something.
- if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) {
- // If this is an overlay, insert it at the target type ID.
- if (type_spec_ptr->idmap_entries != nullptr) {
- type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1;
- }
- loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr);
- }
+ loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr);
}
return std::move(loaded_package);
@@ -709,6 +684,10 @@
return false;
}
+ if (loaded_idmap != nullptr) {
+ global_string_pool_ = util::make_unique<OverlayStringPool>(loaded_idmap);
+ }
+
const size_t package_count = dtohl(header->packageCount);
size_t packages_seen = 0;
@@ -720,9 +699,9 @@
switch (child_chunk.type()) {
case RES_STRING_POOL_TYPE:
// Only use the first string pool. Ignore others.
- if (global_string_pool_.getError() == NO_INIT) {
- status_t err = global_string_pool_.setTo(child_chunk.header<ResStringPool_header>(),
- child_chunk.size());
+ if (global_string_pool_->getError() == NO_INIT) {
+ status_t err = global_string_pool_->setTo(child_chunk.header<ResStringPool_header>(),
+ child_chunk.size());
if (err != NO_ERROR) {
LOG(ERROR) << "RES_STRING_POOL_TYPE corrupt.";
return false;
@@ -741,11 +720,7 @@
packages_seen++;
std::unique_ptr<const LoadedPackage> loaded_package =
- LoadedPackage::Load(child_chunk,
- loaded_idmap,
- system_,
- load_as_shared_library,
- for_loader);
+ LoadedPackage::Load(child_chunk, system_, load_as_shared_library, for_loader);
if (!loaded_package) {
return false;
}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 3fe2c5b..4d7e5df 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1363,11 +1363,10 @@
(((const uint8_t*)tag)
+ dtohs(tag->attributeStart)
+ (dtohs(tag->attributeSize)*idx));
- if (attr->typedValue.dataType != Res_value::TYPE_DYNAMIC_REFERENCE ||
- mTree.mDynamicRefTable == NULL) {
+ if (mTree.mDynamicRefTable == NULL ||
+ !mTree.mDynamicRefTable->requiresLookup(&attr->typedValue)) {
return dtohl(attr->typedValue.data);
}
-
uint32_t data = dtohl(attr->typedValue.data);
if (mTree.mDynamicRefTable->lookupResourceId(&data) == NO_ERROR) {
return data;
@@ -1613,10 +1612,9 @@
static volatile int32_t gCount = 0;
-ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable)
+ResXMLTree::ResXMLTree(std::shared_ptr<const DynamicRefTable> dynamicRefTable)
: ResXMLParser(*this)
- , mDynamicRefTable((dynamicRefTable != nullptr) ? dynamicRefTable->clone()
- : std::unique_ptr<DynamicRefTable>(nullptr))
+ , mDynamicRefTable(std::move(dynamicRefTable))
, mError(NO_INIT), mOwnedData(NULL)
{
if (kDebugResXMLTree) {
@@ -1627,7 +1625,7 @@
ResXMLTree::ResXMLTree()
: ResXMLParser(*this)
- , mDynamicRefTable(std::unique_ptr<DynamicRefTable>(nullptr))
+ , mDynamicRefTable(nullptr)
, mError(NO_INIT), mOwnedData(NULL)
{
if (kDebugResXMLTree) {
@@ -4789,7 +4787,7 @@
packageGroup->clearBagCache();
// Find which configurations match the set of parameters. This allows for a much
- // faster lookup in getEntry() if the set of values is narrowed down.
+ // faster lookup in Lookup() if the set of values is narrowed down.
for (size_t t = 0; t < packageGroup->types.size(); t++) {
if (packageGroup->types[t].isEmpty()) {
continue;
@@ -6897,13 +6895,6 @@
mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID;
}
-std::unique_ptr<DynamicRefTable> DynamicRefTable::clone() const {
- std::unique_ptr<DynamicRefTable> clone = std::unique_ptr<DynamicRefTable>(
- new DynamicRefTable(mAssignedPackageId, mAppAsLib));
- clone->addMappings(*this);
- return clone;
-}
-
status_t DynamicRefTable::load(const ResTable_lib_header* const header)
{
const uint32_t entryCount = dtohl(header->count);
@@ -7020,21 +7011,29 @@
return NO_ERROR;
}
+bool DynamicRefTable::requiresLookup(const Res_value* value) const {
+ // Only resolve non-dynamic references and attributes if the package is loaded as a
+ // library or if a shared library is attempting to retrieve its own resource
+ if ((value->dataType == Res_value::TYPE_REFERENCE ||
+ value->dataType == Res_value::TYPE_ATTRIBUTE) &&
+ (mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) {
+ return true;
+ }
+ return value->dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE ||
+ value->dataType == Res_value::TYPE_DYNAMIC_REFERENCE;
+}
+
status_t DynamicRefTable::lookupResourceValue(Res_value* value) const {
+ if (!requiresLookup(value)) {
+ return NO_ERROR;
+ }
+
uint8_t resolvedType = Res_value::TYPE_REFERENCE;
switch (value->dataType) {
case Res_value::TYPE_ATTRIBUTE:
resolvedType = Res_value::TYPE_ATTRIBUTE;
FALLTHROUGH_INTENDED;
case Res_value::TYPE_REFERENCE:
- // Only resolve non-dynamic references and attributes if the package is loaded as a
- // library or if a shared library is attempting to retrieve its own resource
- if (!(mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) {
- return NO_ERROR;
- }
-
- // If the package is loaded as shared library, the resource reference
- // also need to be fixed.
break;
case Res_value::TYPE_DYNAMIC_ATTRIBUTE:
resolvedType = Res_value::TYPE_ATTRIBUTE;
diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING
index a58b47f..d1a6a5c 100644
--- a/libs/androidfw/TEST_MAPPING
+++ b/libs/androidfw/TEST_MAPPING
@@ -3,6 +3,9 @@
{
"name": "libandroidfw_tests",
"host": true
+ },
+ {
+ "name": "FrameworksResourceLoaderTests"
}
]
}
\ No newline at end of file
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 625b6820..2047287 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -24,6 +24,7 @@
#include "android-base/unique_fd.h"
#include "androidfw/Asset.h"
+#include "androidfw/Idmap.h"
#include "androidfw/LoadedArsc.h"
#include "androidfw/misc.h"
@@ -95,10 +96,18 @@
return loaded_arsc_.get();
}
+ inline const LoadedIdmap* GetLoadedIdmap() const {
+ return loaded_idmap_.get();
+ }
+
inline bool IsOverlay() const {
return idmap_asset_.get() != nullptr;
}
+ inline bool IsLoader() const {
+ return for_loader_;
+ }
+
bool IsUpToDate() const;
// Creates an Asset from any file on the file system.
@@ -127,10 +136,11 @@
ZipArchivePtr zip_handle_;
const std::string path_;
time_t last_mod_time_;
- bool for_loader;
+ bool for_loader_;
std::unique_ptr<Asset> resources_asset_;
std::unique_ptr<Asset> idmap_asset_;
std::unique_ptr<const LoadedArsc> loaded_arsc_;
+ std::unique_ptr<const LoadedIdmap> loaded_idmap_;
};
} // namespace android
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index c7348b1..20e4023 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -122,7 +122,7 @@
// Returns the DynamicRefTable for the ApkAssets represented by the cookie.
// This may be nullptr if the APK represented by `cookie` has no resource table.
- const DynamicRefTable* GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const;
+ std::shared_ptr<const DynamicRefTable> GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const;
// Returns a string representation of the overlayable API of a package.
bool GetOverlayablesToString(const android::StringPiece& package_name,
@@ -236,12 +236,14 @@
ResTable_config* in_out_selected_config, uint32_t* in_out_flags,
uint32_t* out_last_reference) const;
- // Enables or disables resource resolution logging. Clears stored steps when
- // disabled.
+ // Resets the resource resolution structures in preparation for the next resource retrieval.
+ void ResetResourceResolution() const;
+
+ // Enables or disables resource resolution logging. Clears stored steps when disabled.
void SetResourceResolutionLoggingEnabled(bool enabled);
- // Returns formatted log of last resource resolution path, or empty if no
- // resource has been resolved yet.
+ // Returns formatted log of last resource resolution path, or empty if no resource has been
+ // resolved yet.
std::string GetLastResourceResolution() const;
const std::vector<uint32_t> GetBagResIdStack(uint32_t resid);
@@ -264,7 +266,7 @@
void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func) const {
for (const PackageGroup& package_group : package_groups_) {
if (!func(package_group.packages_.front().loaded_package_->GetPackageName(),
- package_group.dynamic_ref_table.mAssignedPackageId)) {
+ package_group.dynamic_ref_table->mAssignedPackageId)) {
return;
}
}
@@ -275,6 +277,50 @@
private:
DISALLOW_COPY_AND_ASSIGN(AssetManager2);
+ // A collection of configurations and their associated ResTable_type that match the current
+ // AssetManager configuration.
+ struct FilteredConfigGroup {
+ std::vector<ResTable_config> configurations;
+ std::vector<const ResTable_type*> types;
+ };
+
+ // Represents an single package.
+ struct ConfiguredPackage {
+ // A pointer to the immutable, loaded package info.
+ const LoadedPackage* loaded_package_;
+
+ // A mutable AssetManager-specific list of configurations that match the AssetManager's
+ // current configuration. This is used as an optimization to avoid checking every single
+ // candidate configuration when looking up resources.
+ ByteBucketArray<FilteredConfigGroup> filtered_configs_;
+ };
+
+ // Represents a Runtime Resource Overlay that overlays resources in the logical package.
+ struct ConfiguredOverlay {
+ // The set of package groups that overlay this package group.
+ IdmapResMap overlay_res_maps_;
+
+ // The cookie of the overlay assets.
+ ApkAssetsCookie cookie;
+ };
+
+ // Represents a logical package, which can be made up of many individual packages. Each package
+ // in a PackageGroup shares the same package name and package ID.
+ struct PackageGroup {
+ // The set of packages that make-up this group.
+ std::vector<ConfiguredPackage> packages_;
+
+ // The cookies associated with each package in the group. They share the same order as
+ // packages_.
+ std::vector<ApkAssetsCookie> cookies_;
+
+ // Runtime Resource Overlays that overlay resources in this package group.
+ std::vector<ConfiguredOverlay> overlays_;
+
+ // A library reference table that contains build-package ID to runtime-package ID mappings.
+ std::shared_ptr<DynamicRefTable> dynamic_ref_table = std::make_shared<DynamicRefTable>();
+ };
+
// Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple
// Res_value, or a complex map/bag type. If successful, it is available in `out_entry`.
// Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with
@@ -295,6 +341,11 @@
ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
bool ignore_configuration, FindEntryResult* out_entry) const;
+ ApkAssetsCookie FindEntryInternal(const PackageGroup& package_group, uint8_t type_idx,
+ uint16_t entry_idx, const ResTable_config& desired_config,
+ bool /*stop_at_first_match*/,
+ bool ignore_configuration, FindEntryResult* out_entry) const;
+
// Assigns package IDs to all shared library ApkAssets.
// Should be called whenever the ApkAssets are changed.
void BuildDynamicRefTable();
@@ -307,6 +358,9 @@
// This should always be called when mutating the AssetManager's configuration or ApkAssets set.
void RebuildFilterList(bool filter_incompatible_configs = true);
+ // Retrieves the APK paths of overlays that overlay non-system packages.
+ std::set<std::string> GetNonSystemOverlayPaths() const;
+
// AssetManager2::GetBag(resid) wraps this function to track which resource ids have already
// been seen while traversing bag parents.
const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids);
@@ -318,38 +372,6 @@
// have a longer lifetime.
std::vector<const ApkAssets*> apk_assets_;
- // A collection of configurations and their associated ResTable_type that match the current
- // AssetManager configuration.
- struct FilteredConfigGroup {
- std::vector<ResTable_config> configurations;
- std::vector<const ResTable_type*> types;
- };
-
- // Represents an single package.
- struct ConfiguredPackage {
- // A pointer to the immutable, loaded package info.
- const LoadedPackage* loaded_package_;
-
- // A mutable AssetManager-specific list of configurations that match the AssetManager's
- // current configuration. This is used as an optimization to avoid checking every single
- // candidate configuration when looking up resources.
- ByteBucketArray<FilteredConfigGroup> filtered_configs_;
- };
-
- // Represents a logical package, which can be made up of many individual packages. Each package
- // in a PackageGroup shares the same package name and package ID.
- struct PackageGroup {
- // The set of packages that make-up this group.
- std::vector<ConfiguredPackage> packages_;
-
- // The cookies associated with each package in the group. They share the same order as
- // packages_.
- std::vector<ApkAssetsCookie> cookies_;
-
- // A library reference table that contains build-package ID to runtime-package ID mappings.
- DynamicRefTable dynamic_ref_table;
- };
-
// 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
@@ -418,7 +440,7 @@
};
// Record of the last resolved resource's resolution path.
- mutable Resolution last_resolution;
+ mutable Resolution last_resolution_;
};
class Theme {
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index fd02e6f..ab4c9c2 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -20,20 +20,122 @@
#include <memory>
#include <string>
#include <unordered_map>
+#include <variant>
#include "android-base/macros.h"
-
#include "androidfw/StringPiece.h"
+#include "androidfw/ResourceTypes.h"
+#include "utils/ByteOrder.h"
namespace android {
-struct Idmap_header;
-struct IdmapEntry_header;
+class LoadedIdmap;
+class IdmapResMap;
+
+// A string pool for overlay apk assets. The string pool holds the strings of the overlay resources
+// table and additionally allows for loading strings from the idmap string pool. The idmap string
+// pool strings are offset after the end of the overlay resource table string pool entries so
+// queries for strings defined inline in the idmap do not conflict with queries for overlay
+// resource table strings.
+class OverlayStringPool : public ResStringPool {
+ public:
+ virtual ~OverlayStringPool();
+ virtual const char16_t* stringAt(size_t idx, size_t* outLen) const;
+ virtual const char* string8At(size_t idx, size_t* outLen) const;
+
+ explicit OverlayStringPool(const LoadedIdmap* loaded_idmap);
+ private:
+ const Idmap_data_header* data_header_;
+ const ResStringPool* idmap_string_pool_;
+};
+
+// A dynamic reference table for loaded overlay packages that rewrites the resource id of overlay
+// resources to the resource id of corresponding target resources.
+class OverlayDynamicRefTable : public DynamicRefTable {
+ public:
+ virtual ~OverlayDynamicRefTable() = default;
+ virtual status_t lookupResourceId(uint32_t* resId) const;
+
+ private:
+ explicit OverlayDynamicRefTable(const Idmap_data_header* data_header,
+ const Idmap_overlay_entry* entries,
+ uint8_t target_assigned_package_id);
+
+ // Rewrites a compile-time overlay resource id to the runtime resource id of corresponding target
+ // resource.
+ virtual status_t lookupResourceIdNoRewrite(uint32_t* resId) const;
+
+ const Idmap_data_header* data_header_;
+ const Idmap_overlay_entry* entries_;
+ const int8_t target_assigned_package_id_;
+
+ friend LoadedIdmap;
+ friend IdmapResMap;
+};
+
+// A mapping of target resource ids to a values or resource ids that should overlay the target.
+class IdmapResMap {
+ public:
+ // Represents the result of a idmap lookup. The result can be one of three possibillities:
+ // 1) The result is a resource id which represents the overlay resource that should act as an
+ // alias of the target resource.
+ // 2) The result is a table entry which overlays the type and value of the target resource.
+ // 3) The result is neither and the target resource is not overlaid.
+ class Result {
+ public:
+ Result() : data_(nullptr) {};
+ explicit Result(uint32_t value) : data_(value) {};
+ explicit Result(ResTable_entry_handle&& value) : data_(value) { };
+
+ // Returns `true` if the resource is overlaid.
+ inline explicit operator bool() const {
+ return !std::get_if<nullptr_t>(&data_);
+ }
+
+ inline bool IsResourceId() const {
+ return std::get_if<uint32_t>(&data_);
+ }
+
+ inline uint32_t GetResourceId() const {
+ return *std::get_if<uint32_t>(&data_);
+ }
+
+ inline bool IsTableEntry() const {
+ return std::get_if<ResTable_entry_handle>(&data_);
+ }
+
+ inline const ResTable_entry_handle& GetTableEntry() const {
+ return *std::get_if<ResTable_entry_handle>(&data_);
+ }
+
+ private:
+ std::variant<uint32_t, nullptr_t, ResTable_entry_handle> data_;
+ };
+
+ // Looks up the value that overlays the target resource id.
+ Result Lookup(uint32_t target_res_id) const;
+
+ inline const OverlayDynamicRefTable* GetOverlayDynamicRefTable() const {
+ return overlay_ref_table_;
+ }
+
+ private:
+ explicit IdmapResMap(const Idmap_data_header* data_header,
+ const Idmap_target_entry* entries,
+ uint8_t target_assigned_package_id,
+ const OverlayDynamicRefTable* overlay_ref_table);
+
+ const Idmap_data_header* data_header_;
+ const Idmap_target_entry* entries_;
+ const uint8_t target_assigned_package_id_;
+ const OverlayDynamicRefTable* overlay_ref_table_;
+
+ friend LoadedIdmap;
+};
// Represents a loaded/parsed IDMAP for a Runtime Resource Overlay (RRO).
-// An RRO and its target APK have different resource IDs assigned to their resources. Overlaying
-// a resource is done by resource name. An IDMAP is a generated mapping between the resource IDs
-// of the RRO and the target APK for each resource with the same name.
+// An RRO and its target APK have different resource IDs assigned to their resources.
+// An IDMAP is a generated mapping between the resource IDs of the RRO and the target APK.
// A LoadedIdmap can be set alongside the overlay's LoadedArsc to allow the overlay ApkAssets to
// masquerade as the target ApkAssets resources.
class LoadedIdmap {
@@ -41,34 +143,52 @@
// Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed.
static std::unique_ptr<const LoadedIdmap> Load(const StringPiece& idmap_data);
- // Performs a lookup of the expected entry ID for the given IDMAP entry header.
- // Returns true if the mapping exists and fills `output_entry_id` with the result.
- static bool Lookup(const IdmapEntry_header* header, uint16_t input_entry_id,
- uint16_t* output_entry_id);
-
- // Returns the package ID for which this overlay should apply.
- uint8_t TargetPackageId() const;
-
// Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated.
inline const std::string& OverlayApkPath() const {
return overlay_apk_path_;
}
- // Returns the mapping of target entry ID to overlay entry ID for the given target type.
- const IdmapEntry_header* GetEntryMapForType(uint8_t type_id) const;
+ // Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated.
+ inline const std::string& TargetApkPath() const {
+ return target_apk_path_;
+ }
+
+ // Returns a mapping from target resource ids to overlay values.
+ inline const IdmapResMap GetTargetResourcesMap(
+ uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const {
+ return IdmapResMap(data_header_, target_entries_, target_assigned_package_id,
+ overlay_ref_table);
+ }
+
+ // Returns a dynamic reference table for a loaded overlay package.
+ inline const OverlayDynamicRefTable GetOverlayDynamicRefTable(
+ uint8_t target_assigned_package_id) const {
+ return OverlayDynamicRefTable(data_header_, overlay_entries_, target_assigned_package_id);
+ }
protected:
// Exposed as protected so that tests can subclass and mock this class out.
LoadedIdmap() = default;
- const Idmap_header* header_ = nullptr;
+ const Idmap_header* header_;
+ const Idmap_data_header* data_header_;
+ const Idmap_target_entry* target_entries_;
+ const Idmap_overlay_entry* overlay_entries_;
+ const std::unique_ptr<ResStringPool> string_pool_;
+
std::string overlay_apk_path_;
- std::unordered_map<uint8_t, const IdmapEntry_header*> type_map_;
+ std::string target_apk_path_;
private:
DISALLOW_COPY_AND_ASSIGN(LoadedIdmap);
- explicit LoadedIdmap(const Idmap_header* header);
+ explicit LoadedIdmap(const Idmap_header* header,
+ const Idmap_data_header* data_header,
+ const Idmap_target_entry* target_entries,
+ const Idmap_overlay_entry* overlay_entries,
+ ResStringPool* string_pool);
+
+ friend OverlayStringPool;
};
} // namespace android
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 1a56876..ba1beaa 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -51,10 +51,6 @@
// and under which configurations it varies.
const ResTable_typeSpec* type_spec;
- // Pointer to the mmapped data where the IDMAP mappings for this type
- // exist. May be nullptr if no IDMAP exists.
- const IdmapEntry_header* idmap_entries;
-
// The number of types that follow this struct.
// There is a type for each configuration that entries are defined for.
size_t type_count;
@@ -135,8 +131,7 @@
return iterator(this, resource_ids_.size() + 1, 0);
}
- static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk,
- const LoadedIdmap* loaded_idmap, bool system,
+ static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk, bool system,
bool load_as_shared_library,
bool load_as_custom_loader);
@@ -183,11 +178,6 @@
return system_;
}
- // Returns true if this package is from an overlay ApkAssets.
- inline bool IsOverlay() const {
- return overlay_;
- }
-
// Returns true if this package is a custom loader and should behave like an overlay
inline bool IsCustomLoader() const {
return custom_loader_;
@@ -222,9 +212,6 @@
const TypeSpecPtr& ptr = type_specs_[i];
if (ptr != nullptr) {
uint8_t type_id = ptr->type_spec->id;
- if (ptr->idmap_entries != nullptr) {
- type_id = ptr->idmap_entries->target_type_id;
- }
f(ptr.get(), type_id - 1);
}
}
@@ -265,7 +252,6 @@
int type_id_offset_ = 0;
bool dynamic_ = false;
bool system_ = false;
- bool overlay_ = false;
bool custom_loader_ = false;
bool defines_overlayable_ = false;
@@ -298,7 +284,7 @@
// Returns the string pool where all string resource values
// (Res_value::dataType == Res_value::TYPE_STRING) are indexed.
inline const ResStringPool* GetStringPool() const {
- return &global_string_pool_;
+ return global_string_pool_.get();
}
// Gets a pointer to the package with the specified package ID, or nullptr if no such package
@@ -319,12 +305,8 @@
DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
LoadedArsc() = default;
- bool LoadTable(
- const Chunk& chunk,
- const LoadedIdmap* loaded_idmap,
- bool load_as_shared_library,
- bool for_loader
- );
+ bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library,
+ bool for_loader);
static std::unique_ptr<const LoadedArsc> LoadData(std::unique_ptr<LoadedArsc>& loaded_arsc,
const char* data,
@@ -333,7 +315,7 @@
bool load_as_shared_library = false,
bool for_loader = false);
- ResStringPool global_string_pool_;
+ std::unique_ptr<ResStringPool> global_string_pool_ = util::make_unique<ResStringPool>();
std::vector<std::unique_ptr<const LoadedPackage>> packages_;
bool system_ = false;
};
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 2efa65a..b20e657 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -492,7 +492,7 @@
public:
ResStringPool();
ResStringPool(const void* data, size_t size, bool copyData=false);
- ~ResStringPool();
+ virtual ~ResStringPool();
void setToEmpty();
status_t setTo(const void* data, size_t size, bool copyData=false);
@@ -506,10 +506,10 @@
inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const {
return stringAt(ref.index, outLen);
}
- const char16_t* stringAt(size_t idx, size_t* outLen) const;
+ virtual const char16_t* stringAt(size_t idx, size_t* outLen) const;
// Note: returns null if the string pool is not UTF8.
- const char* string8At(size_t idx, size_t* outLen) const;
+ virtual const char* string8At(size_t idx, size_t* outLen) const;
// Return string whether the pool is UTF8 or UTF16. Does not allow you
// to distinguish null.
@@ -812,7 +812,7 @@
* The tree stores a clone of the specified DynamicRefTable, so any changes to the original
* DynamicRefTable will not affect this tree after instantiation.
**/
- explicit ResXMLTree(const DynamicRefTable* dynamicRefTable);
+ explicit ResXMLTree(std::shared_ptr<const DynamicRefTable> dynamicRefTable);
ResXMLTree();
~ResXMLTree();
@@ -827,7 +827,7 @@
status_t validateNode(const ResXMLTree_node* node) const;
- std::unique_ptr<const DynamicRefTable> mDynamicRefTable;
+ std::shared_ptr<const DynamicRefTable> mDynamicRefTable;
status_t mError;
void* mOwnedData;
@@ -1584,6 +1584,50 @@
Res_value value;
};
+
+// A ResTable_entry variant that either holds an unmanaged pointer to a constant ResTable_entry or
+// holds a ResTable_entry which is tied to the lifetime of the handle.
+class ResTable_entry_handle {
+ public:
+ ResTable_entry_handle() = default;
+
+ ResTable_entry_handle(const ResTable_entry_handle& handle) {
+ entry_ = handle.entry_;
+ }
+
+ ResTable_entry_handle(ResTable_entry_handle&& handle) noexcept {
+ entry_ = handle.entry_;
+ }
+
+ inline static ResTable_entry_handle managed(ResTable_entry* entry) {
+ return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry));
+ }
+
+ inline static ResTable_entry_handle unmanaged(const ResTable_entry* entry) {
+ return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, [](auto /*p */){}));
+ }
+
+ inline ResTable_entry_handle& operator=(const ResTable_entry_handle& handle) noexcept {
+ entry_ = handle.entry_;
+ return *this;
+ }
+
+ inline ResTable_entry_handle& operator=(ResTable_entry_handle&& handle) noexcept {
+ entry_ = handle.entry_;
+ return *this;
+ }
+
+ inline const ResTable_entry* operator*() & {
+ return entry_.get();
+ }
+
+ private:
+ explicit ResTable_entry_handle(std::shared_ptr<const ResTable_entry> entry)
+ : entry_(std::move(entry)) { }
+
+ std::shared_ptr<const ResTable_entry> entry_;
+};
+
/**
* A package-id to package name mapping for any shared libraries used
* in this resource table. The package-id's encoded in this resource
@@ -1668,7 +1712,8 @@
uint32_t entry_count;
};
-struct alignas(uint32_t) Idmap_header {
+#pragma pack(push, 1)
+struct Idmap_header {
// Always 0x504D4449 ('IDMP')
uint32_t magic;
@@ -1679,18 +1724,28 @@
uint8_t target_path[256];
uint8_t overlay_path[256];
+};
- uint16_t target_package_id;
- uint16_t type_count;
-} __attribute__((packed));
+struct Idmap_data_header {
+ uint8_t target_package_id;
+ uint8_t overlay_package_id;
+ uint32_t target_entry_count;
+ uint32_t overlay_entry_count;
+ uint32_t string_pool_index_offset;
+ uint32_t string_pool_length;
+};
-struct alignas(uint32_t) IdmapEntry_header {
- uint16_t target_type_id;
- uint16_t overlay_type_id;
- uint16_t entry_count;
- uint16_t entry_id_offset;
- uint32_t entries[0];
-} __attribute__((packed));
+struct Idmap_target_entry {
+ uint32_t target_id;
+ uint8_t type;
+ uint32_t value;
+};
+
+struct Idmap_overlay_entry {
+ uint32_t overlay_id;
+ uint32_t target_id;
+};
+#pragma pack(pop)
class AssetManager2;
@@ -1708,6 +1763,7 @@
public:
DynamicRefTable();
DynamicRefTable(uint8_t packageId, bool appAsLib);
+ virtual ~DynamicRefTable() = default;
// Loads an unmapped reference table from the package.
status_t load(const ResTable_lib_header* const header);
@@ -1721,12 +1777,12 @@
void addMapping(uint8_t buildPackageId, uint8_t runtimePackageId);
- // Creates a new clone of the reference table
- std::unique_ptr<DynamicRefTable> clone() const;
+ // Returns whether or not the value must be looked up.
+ bool requiresLookup(const Res_value* value) const;
// Performs the actual conversion of build-time resource ID to run-time
// resource ID.
- status_t lookupResourceId(uint32_t* resId) const;
+ virtual status_t lookupResourceId(uint32_t* resId) const;
status_t lookupResourceValue(Res_value* value) const;
inline const KeyedVector<String16, uint8_t>& entries() const {
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
index e2b9f00..0f2ee6f 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -79,39 +79,6 @@
EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic());
}
-TEST(ApkAssetsTest, LoadApkWithIdmap) {
- std::string contents;
- ResTable target_table;
- const std::string target_path = GetTestDataPath() + "/basic/basic.apk";
- ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents));
- ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/),
- Eq(NO_ERROR));
-
- ResTable overlay_table;
- const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk";
- ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents));
- ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/),
- Eq(NO_ERROR));
-
- util::unique_cptr<void> idmap_data;
- void* temp_data;
- size_t idmap_len;
-
- ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(),
- overlay_path.c_str(), &temp_data, &idmap_len),
- Eq(NO_ERROR));
- idmap_data.reset(temp_data);
-
- TemporaryFile tf;
- ASSERT_TRUE(base::WriteFully(tf.fd, idmap_data.get(), idmap_len));
- close(tf.fd);
-
- // Open something so that the destructor of TemporaryFile closes a valid fd.
- tf.fd = open("/dev/null", O_WRONLY);
-
- ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull());
-}
-
TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) {
std::unique_ptr<const ApkAssets> loaded_apk =
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 1591024..b3190be 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -718,15 +718,17 @@
const auto map = assetmanager.GetOverlayableMapForPackage(0x7f);
ASSERT_NE(nullptr, map);
- ASSERT_EQ(2, map->size());
+ ASSERT_EQ(3, map->size());
ASSERT_EQ(map->at("OverlayableResources1"), "overlay://theme");
ASSERT_EQ(map->at("OverlayableResources2"), "overlay://com.android.overlayable");
+ ASSERT_EQ(map->at("OverlayableResources3"), "");
std::string api;
ASSERT_TRUE(assetmanager.GetOverlayablesToString("com.android.overlayable", &api));
ASSERT_EQ(api.find("not_overlayable"), std::string::npos);
ASSERT_NE(api.find("resource='com.android.overlayable:string/overlayable2' overlayable='OverlayableResources1' actor='overlay://theme' policy='0x0000000a'\n"),
std::string::npos);
+
}
} // namespace android
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 10b83a7..b679672 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -14,114 +14,231 @@
* limitations under the License.
*/
+#include "android-base/file.h"
+#include "androidfw/ApkAssets.h"
+#include "androidfw/AssetManager2.h"
#include "androidfw/ResourceTypes.h"
#include "utils/String16.h"
#include "utils/String8.h"
#include "TestHelpers.h"
-#include "data/basic/R.h"
+#include "data/overlay/R.h"
+#include "data/overlayable/R.h"
+#include "data/system/R.h"
-using ::com::android::basic::R;
+namespace overlay = com::android::overlay;
+namespace overlayable = com::android::overlayable;
namespace android {
+namespace {
+
class IdmapTest : public ::testing::Test {
protected:
void SetUp() override {
- std::string contents;
- ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc",
- &contents));
- ASSERT_EQ(NO_ERROR, target_table_.add(contents.data(), contents.size(), 0, true));
+ // Move to the test data directory so the idmap can locate the overlay APK.
+ std::string original_path = base::GetExecutableDirectory();
+ chdir(GetTestDataPath().c_str());
- ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk",
- "resources.arsc", &overlay_data_));
- ResTable overlay_table;
- ASSERT_EQ(NO_ERROR, overlay_table.add(overlay_data_.data(), overlay_data_.size()));
+ system_assets_ = ApkAssets::Load("system/system.apk");
+ ASSERT_NE(nullptr, system_assets_);
- char target_name[256] = "com.android.basic";
- ASSERT_EQ(NO_ERROR, overlay_table.createIdmap(target_table_, 0, 0, target_name, target_name,
- &data_, &data_size_));
+ overlay_assets_ = ApkAssets::LoadOverlay("overlay/overlay.idmap");
+ ASSERT_NE(nullptr, overlay_assets_);
+
+ overlayable_assets_ = ApkAssets::Load("overlayable/overlayable.apk");
+ ASSERT_NE(nullptr, overlayable_assets_);
+ chdir(original_path.c_str());
}
- void TearDown() override {
- ::free(data_);
- }
-
- ResTable target_table_;
- std::string overlay_data_;
- void* data_ = nullptr;
- size_t data_size_ = 0;
+ protected:
+ std::unique_ptr<const ApkAssets> system_assets_;
+ std::unique_ptr<const ApkAssets> overlay_assets_;
+ std::unique_ptr<const ApkAssets> overlayable_assets_;
};
-TEST_F(IdmapTest, CanLoadIdmap) {
- ASSERT_EQ(NO_ERROR,
- target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
+std::string GetStringFromApkAssets(const AssetManager2& asset_manager, const Res_value& value,
+ ApkAssetsCookie cookie) {
+ auto assets = asset_manager.GetApkAssets();
+ const ResStringPool* string_pool = assets[cookie]->GetLoadedArsc()->GetStringPool();
+ return GetStringFromPool(string_pool, value.data);
+}
+
}
TEST_F(IdmapTest, OverlayOverridesResourceValue) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
Res_value val;
- ssize_t block = target_table_.getResource(R::string::test2, &val, false);
- ASSERT_GE(block, 0);
- ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
- const ResStringPool* pool = target_table_.getTableStringBlock(block);
- ASSERT_TRUE(pool != NULL);
- ASSERT_LT(val.data, pool->size());
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable5,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
+ ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Overlay One");
+}
- size_t str_len;
- const char16_t* target_str16 = pool->stringAt(val.data, &str_len);
- ASSERT_TRUE(target_str16 != NULL);
- ASSERT_EQ(String16("test2"), String16(target_str16, str_len));
+TEST_F(IdmapTest, OverlayOverridesResourceValueUsingDifferentPackage) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable10,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 0U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
+ ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "yes");
+}
- ASSERT_EQ(NO_ERROR,
- target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
+TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInternalResource) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable8,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE);
+ ASSERT_EQ(val.data, (overlay::R::string::internal & 0x00ffffff) | (0x02 << 24));
+}
- ssize_t new_block = target_table_.getResource(R::string::test2, &val, false);
- ASSERT_GE(new_block, 0);
- ASSERT_NE(block, new_block);
- ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
- pool = target_table_.getTableStringBlock(new_block);
- ASSERT_TRUE(pool != NULL);
- ASSERT_LT(val.data, pool->size());
+TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineInteger) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::integer::config_integer,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_INT_DEC);
+ ASSERT_EQ(val.data, 42);
+}
- target_str16 = pool->stringAt(val.data, &str_len);
- ASSERT_TRUE(target_str16 != NULL);
- ASSERT_EQ(String16("test2-overlay"), String16(target_str16, str_len));
+TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineString) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
+ ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Hardcoded string");
+}
+
+TEST_F(IdmapTest, OverlayOverridesResourceValueUsingOverlayingResource) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable9,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE);
+ ASSERT_EQ(val.data, overlayable::R::string::overlayable7);
+}
+
+TEST_F(IdmapTest, OverlayOverridesXmlParser) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::layout::hello_view,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
+ ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "res/layout/hello_view.xml");
+
+ auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", cookie,
+ Asset::ACCESS_RANDOM);
+ auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(cookie);
+ auto xml_tree = util::make_unique<ResXMLTree>(std::move(dynamic_ref_table));
+ status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), false);
+ ASSERT_EQ(err, NO_ERROR);
+
+ while (xml_tree->next() != ResXMLParser::START_TAG) { }
+
+ // The resource id of @id/hello_view should be rewritten to the resource id/hello_view within the
+ // target.
+ ASSERT_EQ(xml_tree->getAttributeNameResID(0), 0x010100d0 /* android:attr/id */);
+ ASSERT_EQ(xml_tree->getAttributeDataType(0), Res_value::TYPE_REFERENCE);
+ ASSERT_EQ(xml_tree->getAttributeData(0), overlayable::R::id::hello_view);
+
+ // The resource id of @android:string/yes should not be rewritten even though it overlays
+ // string/overlayable10 in the target.
+ ASSERT_EQ(xml_tree->getAttributeNameResID(1), 0x0101014f /* android:attr/text */);
+ ASSERT_EQ(xml_tree->getAttributeDataType(1), Res_value::TYPE_REFERENCE);
+ ASSERT_EQ(xml_tree->getAttributeData(1), 0x01040013 /* android:string/yes */);
+
+ // The resource id of the attribute within the overlay should be rewritten to the resource id of
+ // the attribute in the target.
+ ASSERT_EQ(xml_tree->getAttributeNameResID(2), overlayable::R::attr::max_lines);
+ ASSERT_EQ(xml_tree->getAttributeDataType(2), Res_value::TYPE_INT_DEC);
+ ASSERT_EQ(xml_tree->getAttributeData(2), 4);
}
TEST_F(IdmapTest, OverlaidResourceHasSameName) {
- ASSERT_EQ(NO_ERROR,
- target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
- ResTable::resource_name res_name;
- ASSERT_TRUE(target_table_.getResourceName(R::array::integerArray1, true, &res_name));
-
- ASSERT_TRUE(res_name.package != NULL);
- ASSERT_TRUE(res_name.type != NULL);
- ASSERT_TRUE(res_name.name8 != NULL);
-
- EXPECT_EQ(String16("com.android.basic"), String16(res_name.package, res_name.packageLen));
- EXPECT_EQ(String16("array"), String16(res_name.type, res_name.typeLen));
- EXPECT_EQ(String8("integerArray1"), String8(res_name.name8, res_name.nameLen));
+ AssetManager2::ResourceName name;
+ ASSERT_TRUE(asset_manager.GetResourceName(overlayable::R::string::overlayable9, &name));
+ ASSERT_EQ(std::string(name.package), "com.android.overlayable");
+ ASSERT_EQ(String16(name.type16), u"string");
+ ASSERT_EQ(std::string(name.entry), "overlayable9");
}
-constexpr const uint32_t kNonOverlaidResourceId = 0x7fff0000u;
+TEST_F(IdmapTest, OverlayLoaderInterop) {
+ std::string contents;
+ auto loader_assets = ApkAssets::LoadArsc(GetTestDataPath() + "/loader/resources.arsc",
+ /* for_loader */ true);
-TEST_F(IdmapTest, OverlayDoesNotIncludeNonOverlaidResources) {
- // First check that the resource we're trying to not include when overlaid is present when
- // the overlay is loaded as a standalone APK.
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(overlay_data_.data(), overlay_data_.size(), 0, true));
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(),
+ overlay_assets_.get()});
Res_value val;
- ssize_t block = table.getResource(kNonOverlaidResourceId, &val, false /*mayBeBag*/);
- ASSERT_GE(block, 0);
-
- // Now add the overlay and verify that the unoverlaid resource is gone.
- ASSERT_EQ(NO_ERROR,
- target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
- block = target_table_.getResource(kNonOverlaidResourceId, &val, false /*mayBeBag*/);
- ASSERT_LT(block, 0);
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ std::cout << asset_manager.GetLastResourceResolution();
+ ASSERT_EQ(cookie, 1U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
+ ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "loader");
}
} // namespace
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index fd57a92..82dd335 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -144,7 +144,7 @@
"resources.arsc", &contents));
std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/,
+ LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, false /*system*/,
true /*load_as_shared_library*/);
ASSERT_THAT(loaded_arsc, NotNull());
@@ -222,67 +222,13 @@
ASSERT_THAT(type_spec->types[0], NotNull());
}
-class MockLoadedIdmap : public LoadedIdmap {
- public:
- MockLoadedIdmap() : LoadedIdmap() {
- local_header_.magic = kIdmapMagic;
- local_header_.version = kIdmapCurrentVersion;
- local_header_.target_package_id = 0x08;
- local_header_.type_count = 1;
- header_ = &local_header_;
-
- entry_header = util::unique_cptr<IdmapEntry_header>(
- (IdmapEntry_header*)::malloc(sizeof(IdmapEntry_header) + sizeof(uint32_t)));
- entry_header->target_type_id = 0x03;
- entry_header->overlay_type_id = 0x02;
- entry_header->entry_id_offset = 1;
- entry_header->entry_count = 1;
- entry_header->entries[0] = 0x00000000u;
- type_map_[entry_header->overlay_type_id] = entry_header.get();
- }
-
- private:
- Idmap_header local_header_;
- util::unique_cptr<IdmapEntry_header> entry_header;
-};
-
-TEST(LoadedArscTest, LoadOverlay) {
- std::string contents;
- ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc",
- &contents));
-
- MockLoadedIdmap loaded_idmap;
-
- std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(contents), &loaded_idmap);
- ASSERT_THAT(loaded_arsc, NotNull());
-
- const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u);
- ASSERT_THAT(package, NotNull());
-
- const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1);
- ASSERT_THAT(type_spec, NotNull());
- ASSERT_THAT(type_spec->type_count, Ge(1u));
- ASSERT_THAT(type_spec->types[0], NotNull());
-
- // The entry being overlaid doesn't exist at the original entry index.
- ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull());
-
- // Since this is an overlay, the actual entry ID must be mapped.
- ASSERT_THAT(type_spec->idmap_entries, NotNull());
- uint16_t target_entry_id = 0u;
- ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id));
- ASSERT_THAT(target_entry_id, Eq(0x0u));
- ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull());
-}
-
TEST(LoadedArscTest, LoadOverlayable) {
std::string contents;
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk",
"resources.arsc", &contents));
std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/,
+ LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, false /*system*/,
false /*load_as_shared_library*/);
ASSERT_THAT(loaded_arsc, NotNull());
@@ -383,9 +329,10 @@
ASSERT_EQ(std::string("com.android.overlayable"), packages[0]->GetPackageName());
const auto map = packages[0]->GetOverlayableMap();
- ASSERT_EQ(2, map.size());
+ ASSERT_EQ(3, map.size());
ASSERT_EQ(map.at("OverlayableResources1"), "overlay://theme");
ASSERT_EQ(map.at("OverlayableResources2"), "overlay://com.android.overlayable");
+ ASSERT_EQ(map.at("OverlayableResources3"), "");
}
TEST(LoadedArscTest, LoadCustomLoader) {
@@ -394,7 +341,6 @@
std::unique_ptr<Asset>
asset = ApkAssets::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
- MockLoadedIdmap loaded_idmap;
const StringPiece data(
reinterpret_cast<const char*>(asset->getBuffer(true /*wordAligned*/)),
asset->getLength());
@@ -404,13 +350,13 @@
ASSERT_THAT(loaded_arsc, NotNull());
const LoadedPackage* package =
- loaded_arsc->GetPackageById(get_package_id(android::R::string::cancel));
+ loaded_arsc->GetPackageById(get_package_id(overlayable::R::string::overlayable11));
ASSERT_THAT(package, NotNull());
- EXPECT_THAT(package->GetPackageName(), StrEq("android"));
- EXPECT_THAT(package->GetPackageId(), Eq(0x01));
+ EXPECT_THAT(package->GetPackageName(), StrEq("com.android.loader"));
+ EXPECT_THAT(package->GetPackageId(), Eq(0x7f));
- const uint8_t type_index = get_type_id(android::R::string::cancel) - 1;
- const uint16_t entry_index = get_entry_id(android::R::string::cancel);
+ const uint8_t type_index = get_type_id(overlayable::R::string::overlayable11) - 1;
+ const uint16_t entry_index = get_entry_id(overlayable::R::string::overlayable11);
const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
ASSERT_THAT(type_spec, NotNull());
diff --git a/libs/androidfw/tests/data/loader/AndroidManifest.xml b/libs/androidfw/tests/data/loader/AndroidManifest.xml
new file mode 100644
index 0000000..4c0bb47
--- /dev/null
+++ b/libs/androidfw/tests/data/loader/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest package="com.android.loader">
+ <application>
+ </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/loader/build b/libs/androidfw/tests/data/loader/build
new file mode 100755
index 0000000..457ec51
--- /dev/null
+++ b/libs/androidfw/tests/data/loader/build
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -e
+
+FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk
+
+rm resources.arsc
+aapt2 compile --dir res -o compiled.flata
+aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o loader.apk compiled.flata
+unzip loader.apk resources.arsc
+rm loader.apk
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/loader/res/values/public.xml b/libs/androidfw/tests/data/loader/res/values/public.xml
new file mode 100644
index 0000000..3293229
--- /dev/null
+++ b/libs/androidfw/tests/data/loader/res/values/public.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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="string" name="overlayable11" id="0x7f01000b" />
+</resources>
\ No newline at end of file
diff --git a/libs/androidfw/tests/data/loader/res/values/values.xml b/libs/androidfw/tests/data/loader/res/values/values.xml
new file mode 100644
index 0000000..0653536
--- /dev/null
+++ b/libs/androidfw/tests/data/loader/res/values/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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>
+ <string name="overlayable11">loader</string>
+</resources>
diff --git a/libs/androidfw/tests/data/loader/resources.arsc b/libs/androidfw/tests/data/loader/resources.arsc
index 2c881f2..2bdb288 100644
--- a/libs/androidfw/tests/data/loader/resources.arsc
+++ b/libs/androidfw/tests/data/loader/resources.arsc
Binary files differ
diff --git a/libs/androidfw/tests/data/overlay/AndroidManifest.xml b/libs/androidfw/tests/data/overlay/AndroidManifest.xml
index a56ac18..28a1148 100644
--- a/libs/androidfw/tests/data/overlay/AndroidManifest.xml
+++ b/libs/androidfw/tests/data/overlay/AndroidManifest.xml
@@ -15,7 +15,9 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.test.basic">
- <application>
- </application>
+ package="com.android.test.overlay">
+ <overlay
+ android:targetPackage="com.android.test.basic"
+ android:targetName="OverlayableResources3"
+ android:resourcesMap="@xml/overlays"/>
</manifest>
diff --git a/libs/androidfw/tests/data/overlay/R.h b/libs/androidfw/tests/data/overlay/R.h
new file mode 100644
index 0000000..f3dbed2
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/R.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TESTS_DATA_OVERLAY_R_H_
+#define TESTS_DATA_OVERLAY_R_H_
+
+#include <cstdint>
+
+namespace com {
+namespace android {
+namespace overlay {
+
+struct R {
+ struct string {
+ enum : uint32_t {
+ internal = 0x7f040000,
+ };
+ };
+};
+
+} // namespace overlay
+} // namespace android
+} // namespace com
+
+#endif /* TESTS_DATA_OVERLAY_R_H_ */
diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build
index 716b1bd..99dfd63 100755
--- a/libs/androidfw/tests/data/overlay/build
+++ b/libs/androidfw/tests/data/overlay/build
@@ -17,6 +17,15 @@
set -e
+FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk
+
aapt2 compile --dir res -o compiled.flata
-aapt2 link --manifest AndroidManifest.xml -o overlay.apk compiled.flata
+aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o overlay.apk compiled.flata \
+ --no-auto-version
rm compiled.flata
+
+# Navigate back a directory so the idmap can find the overlays in the test data directory when being
+# loaded during testing.
+cd ../
+idmap2 create --target-apk-path overlayable/overlayable.apk \
+ --overlay-apk-path overlay/overlay.apk --idmap-path overlay/overlay.idmap
diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk
index d37874d..c594b8e 100644
--- a/libs/androidfw/tests/data/overlay/overlay.apk
+++ b/libs/androidfw/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap
new file mode 100644
index 0000000..27cf792f
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/overlay.idmap
Binary files differ
diff --git a/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml b/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml
new file mode 100644
index 0000000..54dc6c0
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/hello_view"
+ android:text="@android:string/yes"
+ app:max_lines="4"/>
\ No newline at end of file
diff --git a/libs/androidfw/tests/data/overlay/res/values/values.xml b/libs/androidfw/tests/data/overlay/res/values/values.xml
index 8e4417e..ba018ec 100644
--- a/libs/androidfw/tests/data/overlay/res/values/values.xml
+++ b/libs/androidfw/tests/data/overlay/res/values/values.xml
@@ -13,13 +13,11 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<resources>
- <string name="test2">test2-overlay</string>
- <integer-array name="integerArray1">
- <item>10</item>
- <item>11</item>
- </integer-array>
- <public type="animator" name="unoverlaid" id="0x7fff0000" />
- <item type="animator" name="unoverlaid">@null</item>
+ <string name="overlay1">Overlay One</string>
+ <string name="overlay2">Overlay Two</string>
+ <string name="overlay3">@string/internal</string>
+ <string name="overlay4">@string/overlay2</string>
+ <string name="internal">Internal</string>
+ <attr name="max_lines" format="integer" />
</resources>
diff --git a/libs/androidfw/tests/data/overlay/res/xml/overlays.xml b/libs/androidfw/tests/data/overlay/res/xml/overlays.xml
new file mode 100644
index 0000000..9eca2fa
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/res/xml/overlays.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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 language governing permissions and
+ limitations under the License.
+-->
+<overlay>
+ <!-- Overlays string/overlayable5 with the string "Overlay One". -->
+ <item target="string/overlayable5" value="@string/overlay1"/>
+
+ <!-- Overlays string/overlayable6 and string/overlayable7 with the string "Overlay Two". -->
+ <item target="string/overlayable7" value="@string/overlay2" />
+ <item target="string/overlayable6" value="@string/overlay2" />
+
+ <!-- Overlays string/overlayable8 with a reference to @string/internal. -->
+ <item target="string/overlayable8" value="@string/overlay3" />
+
+ <!-- Overlays string/overlayable9 with a reference to @string/overlay2. The reference to
+ @string/overlay2 should be rewritten to @string/overlayable7 in the target. -->
+ <item target="string/overlayable9" value="@string/overlay4" />
+
+ <!-- Overlays string/overlayable10 with the string "yes". -->
+ <item target="string/overlayable10" value="@android:string/yes" />
+
+ <!-- Overlays string/overlayable11 with the string "Hardcoded string". -->
+ <item target="string/overlayable11" value="Hardcoded string" />
+
+ <!-- Overlays string/overlayable10 with the string "yes". -->
+ <item target="integer/config_int" value="42" />
+
+ <!-- @attr/max_lines and @id/hello_view should be rewritten to @attr/max_lines and
+ @id/hello_view in the target. -->
+ <item target="layout/hello_view" value="@layout/hello_view" />
+ <item target="attr/max_lines" value="@attr/max_lines" />
+ <item target="id/hello_view" value="@id/hello_view" />
+</overlay>
+
+
diff --git a/libs/androidfw/tests/data/overlayable/R.h b/libs/androidfw/tests/data/overlayable/R.h
index e46e264d..35125a6 100644
--- a/libs/androidfw/tests/data/overlayable/R.h
+++ b/libs/androidfw/tests/data/overlayable/R.h
@@ -31,6 +31,43 @@
overlayable2 = 0x7f010002,
overlayable3 = 0x7f010003,
overlayable4 = 0x7f010004,
+ overlayable5 = 0x7f010005,
+ overlayable6 = 0x7f010006,
+ overlayable7 = 0x7f010007,
+ overlayable8 = 0x7f010008,
+ overlayable9 = 0x7f010009,
+ overlayable10 = 0x7f01000a,
+ overlayable11 = 0x7f01000b,
+ };
+ };
+
+ struct attr {
+ enum : uint32_t {
+ max_lines = 0x7f020000,
+ };
+ };
+
+ struct boolean {
+ enum : uint32_t {
+ config_bool = 0x7f030000,
+ };
+ };
+
+ struct id {
+ enum : uint32_t {
+ hello_view = 0x7f040000,
+ };
+ };
+
+ struct integer {
+ enum : uint32_t {
+ config_integer = 0x7f050000,
+ };
+ };
+
+ struct layout {
+ enum : uint32_t {
+ hello_view = 0x7f060000,
};
};
};
diff --git a/libs/androidfw/tests/data/overlayable/build b/libs/androidfw/tests/data/overlayable/build
index 98fdc51..0aa97d6 100755
--- a/libs/androidfw/tests/data/overlayable/build
+++ b/libs/androidfw/tests/data/overlayable/build
@@ -17,6 +17,9 @@
set -e
+FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk
+
aapt2 compile --dir res -o compiled.flata
-aapt2 link --manifest AndroidManifest.xml -o overlayable.apk compiled.flata
+aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o overlayable.apk compiled.flata \
+ --no-auto-version
rm compiled.flata
diff --git a/libs/androidfw/tests/data/overlayable/overlayable.apk b/libs/androidfw/tests/data/overlayable/overlayable.apk
index 047e6af..9dc9c15 100644
--- a/libs/androidfw/tests/data/overlayable/overlayable.apk
+++ b/libs/androidfw/tests/data/overlayable/overlayable.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml b/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml
new file mode 100644
index 0000000..268118a
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/hello_view"
+ android:text="None"
+ app:max_lines="0"/>
\ No newline at end of file
diff --git a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
index fcdbe94..b3e8f7d 100644
--- a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
+++ b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
@@ -15,27 +15,41 @@
-->
<resources>
-<overlayable name="OverlayableResources1" actor="overlay://theme">
- <!-- Any overlay can overlay the value of @string/overlayable1 -->
- <item type="string" name="overlayable1" />
+ <overlayable name="OverlayableResources1" actor="overlay://theme">
+ <!-- Any overlay on the product or system partition can overlay the value of
+ @string/overlayable2 -->
+ <policy type="product|system">
+ <item type="string" name="overlayable2" />
+ </policy>
- <!-- Any overlay on the product or system partition can overlay the value of
- @string/overlayable2 -->
- <policy type="product|system">
- <item type="string" name="overlayable2" />
- </policy>
+ <!-- Any overlay can overlay the value of @string/overlayable4 -->
+ <policy type="public">
+ <item type="string" name="overlayable1" />
+ <item type="string" name="overlayable4" />
+ </policy>
+ </overlayable>
- <!-- Any overlay can overlay the value of @string/overlayable4 -->
- <policy type="public">
- <item type="string" name="overlayable4" />
- </policy>
-</overlayable>
+ <overlayable name="OverlayableResources2" actor="overlay://com.android.overlayable">
+ <!-- Any overlay on the vendor or product partition can overlay the value of
+ @string/overlayable3 -->
+ <policy type="vendor|product">
+ <item type="string" name="overlayable3" />
+ </policy>
+ </overlayable>
-<overlayable name="OverlayableResources2" actor="overlay://com.android.overlayable">
- <!-- Any overlay on the vendor or product partition can overlay the value of
- @string/overlayable3 -->
- <policy type="vendor|product">
- <item type="string" name="overlayable3" />
- </policy>
-</overlayable>
+ <overlayable name="OverlayableResources3">
+ <policy type="public">
+ <item type="string" name="overlayable5" />
+ <item type="string" name="overlayable6" />
+ <item type="string" name="overlayable7" />
+ <item type="string" name="overlayable8" />
+ <item type="string" name="overlayable9" />
+ <item type="string" name="overlayable10" />
+ <item type="string" name="overlayable11" />
+ <item type="integer" name="config_int" />
+ <item type="id" name="hello_view" />
+ <item type="attr" name="max_lines" />
+ <item type="layout" name="hello_view" />
+ </policy>
+ </overlayable>
</resources>
\ No newline at end of file
diff --git a/libs/androidfw/tests/data/overlayable/res/values/public.xml b/libs/androidfw/tests/data/overlayable/res/values/public.xml
index 5676d7c..042a311 100644
--- a/libs/androidfw/tests/data/overlayable/res/values/public.xml
+++ b/libs/androidfw/tests/data/overlayable/res/values/public.xml
@@ -20,4 +20,21 @@
<public type="string" name="overlayable2" id="0x7f010002" />
<public type="string" name="overlayable3" id="0x7f010003" />
<public type="string" name="overlayable4" id="0x7f010004" />
+ <public type="string" name="overlayable5" id="0x7f010005" />
+ <public type="string" name="overlayable6" id="0x7f010006" />
+ <public type="string" name="overlayable7" id="0x7f010007" />
+ <public type="string" name="overlayable8" id="0x7f010008" />
+ <public type="string" name="overlayable9" id="0x7f010009" />
+ <public type="string" name="overlayable10" id="0x7f01000a" />
+ <public type="string" name="overlayable11" id="0x7f01000b" />
+
+ <public type="attr" name="max_lines" id="0x7f020000" />
+
+ <public type="bool" name="config_bool" id="0x7f030000" />
+
+ <public type="id" name="hello_view" id="0x7f040000" />
+
+ <public type="integer" name="config_int" id="0x7f050000" />
+
+ <public type="layout" name="hello_view" id="0x7f060000" />
</resources>
\ No newline at end of file
diff --git a/libs/androidfw/tests/data/overlayable/res/values/values.xml b/libs/androidfw/tests/data/overlayable/res/values/values.xml
index a86b312..235772d 100644
--- a/libs/androidfw/tests/data/overlayable/res/values/values.xml
+++ b/libs/androidfw/tests/data/overlayable/res/values/values.xml
@@ -20,4 +20,15 @@
<string name="overlayable2">Overlayable Two</string>
<string name="overlayable3">Overlayable Three</string>
<string name="overlayable4">Overlayable Four</string>
+ <string name="overlayable5">Overlayable Five</string>
+ <string name="overlayable6">Overlayable Six</string>
+ <string name="overlayable7">Overlayable Seven</string>
+ <string name="overlayable8">Overlayable Eight</string>
+ <string name="overlayable9">Overlayable Nine</string>
+ <string name="overlayable10">Overlayable Ten</string>
+ <string name="overlayable11">Overlayable Eleven</string>
+
+ <integer name="config_int">0</integer>
+ <bool name="config_bool">false</bool>
+ <attr name="max_lines" format="integer" />
</resources>
diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h
index 3741074..c0160c0 100644
--- a/libs/androidfw/tests/data/system/R.h
+++ b/libs/androidfw/tests/data/system/R.h
@@ -43,7 +43,8 @@
struct string {
enum : uint32_t {
- cancel = 0x01040000,
+ no = 0x01040009,
+ yes = 0x01040013,
};
};
};
diff --git a/libs/androidfw/tests/data/system/res/values/public.xml b/libs/androidfw/tests/data/system/res/values/public.xml
new file mode 100644
index 0000000..077874d
--- /dev/null
+++ b/libs/androidfw/tests/data/system/res/values/public.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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="string" name="no" id="0x01040009" />
+ <public type="string" name="yes" id="0x01040013" />
+</resources>
\ No newline at end of file
diff --git a/libs/androidfw/tests/data/system/res/values/values.xml b/libs/androidfw/tests/data/system/res/values/values.xml
new file mode 100644
index 0000000..0629c1d1
--- /dev/null
+++ b/libs/androidfw/tests/data/system/res/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <string name="yes">yes</string>
+ <string name="no">no</string>
+</resources>
\ No newline at end of file
diff --git a/libs/androidfw/tests/data/system/system.apk b/libs/androidfw/tests/data/system/system.apk
index 9045d6c..1f7e007 100644
--- a/libs/androidfw/tests/data/system/system.apk
+++ b/libs/androidfw/tests/data/system/system.apk
Binary files differ