From 8a891d86abdf35b6703785fb3368b39510e91357 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Mon, 1 Jul 2019 09:48:23 -0700 Subject: [PATCH] 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 --- .../idmap2/tests/BinaryStreamVisitorTests.cpp | 4 - cmds/idmap2/tests/Idmap2BinaryTests.cpp | 49 ++- cmds/idmap2/tests/IdmapTests.cpp | 4 +- cmds/idmap2/tests/RawPrintVisitorTests.cpp | 2 +- core/jni/android_util_AssetManager.cpp | 12 +- libs/androidfw/Android.bp | 5 +- libs/androidfw/ApkAssets.cpp | 10 +- libs/androidfw/AssetManager2.cpp | 361 +++++++++++------- libs/androidfw/Idmap.cpp | 265 ++++++++----- libs/androidfw/LoadedArsc.cpp | 49 +-- libs/androidfw/ResourceTypes.cpp | 45 ++- libs/androidfw/TEST_MAPPING | 3 + libs/androidfw/include/androidfw/ApkAssets.h | 12 +- .../include/androidfw/AssetManager2.h | 100 +++-- libs/androidfw/include/androidfw/Idmap.h | 158 +++++++- libs/androidfw/include/androidfw/LoadedArsc.h | 28 +- .../include/androidfw/ResourceTypes.h | 94 ++++- libs/androidfw/tests/ApkAssets_test.cpp | 33 -- libs/androidfw/tests/AssetManager2_test.cpp | 4 +- libs/androidfw/tests/Idmap_test.cpp | 261 +++++++++---- libs/androidfw/tests/LoadedArsc_test.cpp | 72 +--- .../tests/data/loader/AndroidManifest.xml | 20 + libs/androidfw/tests/data/loader/build | 27 ++ .../tests/data/loader/res/values/public.xml | 19 + .../tests/data/loader/res/values/values.xml | 19 + .../tests/data/loader/resources.arsc | Bin 620 -> 652 bytes .../tests/data/overlay/AndroidManifest.xml | 8 +- libs/androidfw/tests/data/overlay/R.h | 38 ++ libs/androidfw/tests/data/overlay/build | 11 +- libs/androidfw/tests/data/overlay/overlay.apk | Bin 5211 -> 2988 bytes .../tests/data/overlay/overlay.idmap | Bin 0 -> 1077 bytes .../data/overlay/res/layout/hello_view.xml | 20 + .../tests/data/overlay/res/values/values.xml | 14 +- .../tests/data/overlay/res/xml/overlays.xml | 47 +++ libs/androidfw/tests/data/overlayable/R.h | 37 ++ libs/androidfw/tests/data/overlayable/build | 5 +- .../tests/data/overlayable/overlayable.apk | Bin 3443 -> 6306 bytes .../overlayable/res/layout/hello_view.xml | 20 + .../overlayable/res/values/overlayable.xml | 54 ++- .../data/overlayable/res/values/public.xml | 17 + .../data/overlayable/res/values/values.xml | 11 + libs/androidfw/tests/data/system/R.h | 3 +- .../tests/data/system/res/values/public.xml | 20 + .../tests/data/system/res/values/values.xml | 20 + libs/androidfw/tests/data/system/system.apk | Bin 1624 -> 1976 bytes tools/aapt/Command.cpp | 4 +- .../aapt2/format/binary/XmlFlattener_test.cpp | 6 +- 47 files changed, 1375 insertions(+), 616 deletions(-) create mode 100644 libs/androidfw/tests/data/loader/AndroidManifest.xml create mode 100755 libs/androidfw/tests/data/loader/build create mode 100644 libs/androidfw/tests/data/loader/res/values/public.xml create mode 100644 libs/androidfw/tests/data/loader/res/values/values.xml create mode 100644 libs/androidfw/tests/data/overlay/R.h create mode 100644 libs/androidfw/tests/data/overlay/overlay.idmap create mode 100644 libs/androidfw/tests/data/overlay/res/layout/hello_view.xml create mode 100644 libs/androidfw/tests/data/overlay/res/xml/overlays.xml create mode 100644 libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml create mode 100644 libs/androidfw/tests/data/system/res/values/public.xml create mode 100644 libs/androidfw/tests/data/system/res/values/values.xml diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp index 3a01e8fca70b2..db4778c8ee095 100644 --- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp +++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp @@ -88,8 +88,4 @@ TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) { ASSERT_EQ(overlay_entries1[2].target_id, overlay_entries2[2].target_id); } -TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) { - // TODO(135943783): Removed temporarily until libandroidfw idmap loading is fixed. -} - } // namespace android::idmap2 diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp index b1685b7f13127..b535f30de1f50 100644 --- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp +++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp @@ -280,7 +280,54 @@ TEST_F(Idmap2BinaryTests, Scan) { } TEST_F(Idmap2BinaryTests, Lookup) { - // TODO(135943783): Removed temporarily until libandroidfw idmap loading is fixed. + SKIP_TEST_IF_CANT_EXEC_IDMAP2; + + // clang-format off + auto result = ExecuteBinary({"idmap2", + "create", + "--target-apk-path", GetTargetApkPath(), + "--overlay-apk-path", GetOverlayApkPath(), + "--idmap-path", GetIdmapPath()}); + // clang-format on + ASSERT_THAT(result, NotNull()); + ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; + + // clang-format off + result = ExecuteBinary({"idmap2", + "lookup", + "--idmap-path", GetIdmapPath(), + "--config", "", + "--resid", "0x7f02000c"}); // string/str1 + // clang-format on + ASSERT_THAT(result, NotNull()); + ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; + ASSERT_NE(result->stdout.find("overlay-1"), std::string::npos); + ASSERT_EQ(result->stdout.find("overlay-1-sv"), std::string::npos); + + // clang-format off + result = ExecuteBinary({"idmap2", + "lookup", + "--idmap-path", GetIdmapPath(), + "--config", "", + "--resid", "test.target:string/str1"}); + // clang-format on + ASSERT_THAT(result, NotNull()); + ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; + ASSERT_NE(result->stdout.find("overlay-1"), std::string::npos); + ASSERT_EQ(result->stdout.find("overlay-1-sv"), std::string::npos); + + // clang-format off + result = ExecuteBinary({"idmap2", + "lookup", + "--idmap-path", GetIdmapPath(), + "--config", "sv", + "--resid", "test.target:string/str1"}); + // clang-format on + ASSERT_THAT(result, NotNull()); + ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; + ASSERT_NE(result->stdout.find("overlay-1-sv"), std::string::npos); + + unlink(GetIdmapPath().c_str()); } TEST_F(Idmap2BinaryTests, InvalidCommandLineOptions) { diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index 30b1372005dbe..cd816ddea8142 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -248,7 +248,7 @@ TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) { OverlayManifestInfo info{}; info.target_package = "test.target"; info.target_name = "TestResources"; - info.resource_mapping = 0x7f030002; // xml/overlays_different_packages + info.resource_mapping = 0x7f030001; // xml/overlays_different_packages auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", info, PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ false); @@ -272,7 +272,7 @@ TEST(IdmapTests, CreateIdmapDataInlineResources) { OverlayManifestInfo info{}; info.target_package = "test.target"; info.target_name = "TestResources"; - info.resource_mapping = 0x7f030000; // xml/overlays_inline + info.resource_mapping = 0x7f030002; // xml/overlays_inline auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", info, PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ false); diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp index 24f9845df87b4..d387880cb7715 100644 --- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp @@ -49,7 +49,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos); ASSERT_NE(stream.str().find("00000004: 00000002 version\n"), std::string::npos); ASSERT_NE(stream.str().find("00000008: 76a20829 target crc\n"), std::string::npos); - ASSERT_NE(stream.str().find("0000000c: e3c188b6 overlay crc\n"), std::string::npos); + ASSERT_NE(stream.str().find("0000000c: c054fb26 overlay crc\n"), std::string::npos); ASSERT_NE(stream.str().find("00000210: 7f target package id\n"), std::string::npos); ASSERT_NE(stream.str().find("00000211: 7f overlay package id\n"), std::string::npos); ASSERT_NE(stream.str().find("00000212: 00000004 target entry count\n"), std::string::npos); diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index c7b36d0f8fc94..3c0971b9ec1a2 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -750,9 +750,11 @@ static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint } // May be nullptr. - const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie); + std::shared_ptr dynamic_ref_table = + assetmanager->GetDynamicRefTableForCookie(cookie); - std::unique_ptr xml_tree = util::make_unique(dynamic_ref_table); + std::unique_ptr xml_tree = util::make_unique( + std::move(dynamic_ref_table)); status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); asset.reset(); @@ -785,9 +787,11 @@ static jlong NativeOpenXmlAssetFd(JNIEnv* env, jobject /*clazz*/, jlong ptr, int ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); // May be nullptr. - const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie); + std::shared_ptr dynamic_ref_table = + assetmanager->GetDynamicRefTableForCookie(cookie); - std::unique_ptr xml_tree = util::make_unique(dynamic_ref_table); + std::unique_ptr xml_tree = util::make_unique( + std::move(dynamic_ref_table)); status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); asset.reset(); diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index a34a6c0b37248..4f52a8800a748 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -167,8 +167,9 @@ cc_test { }, }, 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 b309621435b5d..16dbbf61351ad 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -45,7 +45,7 @@ ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle, 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 ApkAssets::Load(const std::string& path, bool system, @@ -75,7 +75,7 @@ std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap 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 ApkAssets::LoadFromFd(unique_fd fd, @@ -165,12 +165,14 @@ std::unique_ptr ApkAssets::LoadImpl( // 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(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::ForEachFile(const std::string& root_path, 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 e914f37bcac44..ca4143f3e2151 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 @@ struct FindEntryResult { // 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 @@ void AssetManager2::BuildDynamicRefTable() { 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 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& package : loaded_arsc->GetPackages()) { // Get the package ID or assign one if a shared library. @@ -103,9 +109,37 @@ void AssetManager2::BuildDynamicRefTable() { if (idx == 0xff) { package_ids_[package_id] = idx = static_cast(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( + 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(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 @@ void AssetManager2::BuildDynamicRefTable() { // 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(entry.package_id)); } + + apk_assets_package_ids.insert(std::make_pair(apk_assets->GetPath(), package_id)); } } @@ -127,8 +163,8 @@ void AssetManager2::BuildDynamicRefTable() { 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 @@ void AssetManager2::DumpToLog() const { (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 @@ const DynamicRefTable* AssetManager2::GetDynamicRefTableForPackage(uint32_t pack 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 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 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { } } -std::set AssetManager2::GetResourceConfigurations(bool exclude_system, - bool exclude_mipmap) const { - ATRACE_NAME("AssetManager::GetResourceConfigurations"); - std::set configurations; +std::set AssetManager2::GetNonSystemOverlayPaths() const { + std::set 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 AssetManager2::GetResourceConfigurations(bool exclude_system, + bool exclude_mipmap) const { + ATRACE_NAME("AssetManager::GetResourceConfigurations"); + const auto non_system_overlays = + (exclude_system) ? GetNonSystemOverlayPaths() : std::set(); + + std::set 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 @@ std::set AssetManager2::GetResourceLocales(bool exclude_system, bool merge_equivalent_languages) const { ATRACE_NAME("AssetManager::GetResourceLocales"); std::set locales; + const auto non_system_overlays = + (exclude_system) ? GetNonSystemOverlayPaths() : std::set(); + 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 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri 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 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri 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 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri 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 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri } 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 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri 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_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 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri 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 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri // 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 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri // 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 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri } 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 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri 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 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri 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 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri // 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 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri } 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 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri 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 @@ std::string AssetManager2::GetLastResourceResolution() const { 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& steps = last_resolution.steps; + uint32_t resid = last_resolution_.resid; + std::vector& 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 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons 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 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, 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 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, } const Res_value* device_value = reinterpret_cast( - reinterpret_cast(entry.entry) + dtohs(entry.entry->size)); + reinterpret_cast(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 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& // 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(entry.entry); + const ResTable_map_entry* map = reinterpret_cast(table_entry); const ResTable_map* map_entry = reinterpret_cast(reinterpret_cast(map) + map->size); const ResTable_map* const map_entry_end = map_entry + dtohl(map->count); @@ -1134,7 +1215,7 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, } 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 @@ uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const 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 7c1ee5cd7cfa5..2b69c923597f9 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(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(malloc(malloc_size)); + memset(table_entry, 0, malloc_size); + table_entry->size = htods(sizeof(ResTable_entry)); + + auto table_value = reinterpret_cast(reinterpret_cast(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 @@ static bool IsValidIdmapHeader(const StringPiece& data) { 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(header_->overlay_path), arraysize(header_->overlay_path)); overlay_apk_path_.assign(reinterpret_cast(header_->overlay_path), length); + + length = strnlen(reinterpret_cast(header_->target_path), + arraysize(header_->target_path)); + target_apk_path_.assign(reinterpret_cast(header_->target_path), length); } std::unique_ptr LoadedIdmap::Load(const StringPiece& idmap_data) { @@ -121,70 +209,67 @@ std::unique_ptr LoadedIdmap::Load(const StringPiece& idmap_da return {}; } - const Idmap_header* header = reinterpret_cast(idmap_data.data()); - - // Can't use make_unique because LoadedImpl constructor is private. - std::unique_ptr loaded_idmap = std::unique_ptr(new LoadedIdmap(header)); - + auto header = reinterpret_cast(idmap_data.data()); const uint8_t* data_ptr = reinterpret_cast(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(data_ptr); + data_ptr += sizeof(*data_header); + data_size -= sizeof(*data_header); - // Validate the type IDs. - const IdmapEntry_header* entry_header = reinterpret_cast(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(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(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(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(data_ptr); + if (data_size / sizeof(Idmap_target_entry) < + static_cast(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 {}; } + + // 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; + + // Make sure there is enough space for the overlay entries declared in the header. + const auto overlay_entries = reinterpret_cast(data_ptr); + if (data_size / sizeof(Idmap_overlay_entry) < + static_cast(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 {}; + } + + // 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(); + 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 loaded_idmap = std::unique_ptr( + new LoadedIdmap(header, data_header, target_entries, overlay_entries, + idmap_string_pool.release())); + return std::move(loaded_idmap); } -uint8_t LoadedIdmap::TargetPackageId() const { - return static_cast(dtohs(header_->target_package_id)); -} - -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; - } - return nullptr; -} - } // namespace android diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 882dc0d71759b..c8962416d0822 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -51,9 +51,8 @@ namespace { // 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 @@ class TypeSpecPtrBuilder { 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 @@ class TypeSpecPtrBuilder { DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder); const ResTable_typeSpec* header_; - const IdmapEntry_header* idmap_header_; std::vector types_; }; @@ -400,7 +397,6 @@ const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { } std::unique_ptr LoadedPackage::Load(const Chunk& chunk, - const LoadedIdmap* loaded_idmap, bool system, bool load_as_shared_library, bool for_loader) { @@ -426,12 +422,6 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, 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 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, 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& builder_ptr = type_builder_map[type_spec->id - 1]; if (builder_ptr == nullptr) { - builder_ptr = util::make_unique(type_spec, idmap_entry_header); + builder_ptr = util::make_unique(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 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, 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 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, return false; } + if (loaded_idmap != nullptr) { + global_string_pool_ = util::make_unique(loaded_idmap); + } + const size_t package_count = dtohl(header->packageCount); size_t packages_seen = 0; @@ -720,9 +699,9 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, 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(), - child_chunk.size()); + if (global_string_pool_->getError() == NO_INIT) { + status_t err = global_string_pool_->setTo(child_chunk.header(), + child_chunk.size()); if (err != NO_ERROR) { LOG(ERROR) << "RES_STRING_POOL_TYPE corrupt."; return false; @@ -741,11 +720,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, packages_seen++; std::unique_ptr 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 3fe2c5b0e21a0..4d7e5dfea4f7c 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -1363,11 +1363,10 @@ int32_t ResXMLParser::getAttributeData(size_t idx) const (((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 @@ uint32_t ResXMLParser::getSourceResourceId() const static volatile int32_t gCount = 0; -ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable) +ResXMLTree::ResXMLTree(std::shared_ptr dynamicRefTable) : ResXMLParser(*this) - , mDynamicRefTable((dynamicRefTable != nullptr) ? dynamicRefTable->clone() - : std::unique_ptr(nullptr)) + , mDynamicRefTable(std::move(dynamicRefTable)) , mError(NO_INIT), mOwnedData(NULL) { if (kDebugResXMLTree) { @@ -1627,7 +1625,7 @@ ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable) ResXMLTree::ResXMLTree() : ResXMLParser(*this) - , mDynamicRefTable(std::unique_ptr(nullptr)) + , mDynamicRefTable(nullptr) , mError(NO_INIT), mOwnedData(NULL) { if (kDebugResXMLTree) { @@ -4789,7 +4787,7 @@ void ResTable::setParameters(const ResTable_config* params) 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 @@ DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib) mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID; } -std::unique_ptr DynamicRefTable::clone() const { - std::unique_ptr clone = std::unique_ptr( - 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 @@ status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const { 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 a58b47fcff9dc..d1a6a5c182995 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 625b68207d83b..20472872263e6 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 @@ class ApkAssets { 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 @@ class ApkAssets { ZipArchivePtr zip_handle_; const std::string path_; time_t last_mod_time_; - bool for_loader; + bool for_loader_; std::unique_ptr resources_asset_; std::unique_ptr idmap_asset_; std::unique_ptr loaded_arsc_; + std::unique_ptr loaded_idmap_; }; } // namespace android diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index c7348b1806483..20e40234b418b 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -122,7 +122,7 @@ class AssetManager2 { // 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 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 @@ class AssetManager2 { 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 GetBagResIdStack(uint32_t resid); @@ -264,7 +266,7 @@ class AssetManager2 { void ForEachPackage(const std::function 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 @@ class AssetManager2 { 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 configurations; + std::vector 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 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 packages_; + + // The cookies associated with each package in the group. They share the same order as + // packages_. + std::vector cookies_; + + // Runtime Resource Overlays that overlay resources in this package group. + std::vector overlays_; + + // A library reference table that contains build-package ID to runtime-package ID mappings. + std::shared_ptr dynamic_ref_table = std::make_shared(); + }; + // 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 @@ class AssetManager2 { 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 @@ class AssetManager2 { // 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 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& child_resids); @@ -318,38 +372,6 @@ class AssetManager2 { // have a longer lifetime. std::vector apk_assets_; - // A collection of configurations and their associated ResTable_type that match the current - // AssetManager configuration. - struct FilteredConfigGroup { - std::vector configurations; - std::vector 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 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 packages_; - - // The cookies associated with each package in the group. They share the same order as - // packages_. - std::vector 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 @@ class AssetManager2 { }; // 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 fd02e6f63b744..ab4c9c2048423 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -20,20 +20,122 @@ #include #include #include +#include #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(&data_); + } + + inline bool IsResourceId() const { + return std::get_if(&data_); + } + + inline uint32_t GetResourceId() const { + return *std::get_if(&data_); + } + + inline bool IsTableEntry() const { + return std::get_if(&data_); + } + + inline const ResTable_entry_handle& GetTableEntry() const { + return *std::get_if(&data_); + } + + private: + std::variant 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 @@ class LoadedIdmap { // Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed. static std::unique_ptr 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 string_pool_; + std::string overlay_apk_path_; - std::unordered_map 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 1a56876b96868..ba1beaa7827c0 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -51,10 +51,6 @@ struct TypeSpec { // 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 @@ class LoadedPackage { return iterator(this, resource_ids_.size() + 1, 0); } - static std::unique_ptr Load(const Chunk& chunk, - const LoadedIdmap* loaded_idmap, bool system, + static std::unique_ptr Load(const Chunk& chunk, bool system, bool load_as_shared_library, bool load_as_custom_loader); @@ -183,11 +178,6 @@ class LoadedPackage { 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 @@ class LoadedPackage { 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 @@ class LoadedPackage { 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 @@ class LoadedArsc { // Returns the string pool where all string resource values // (Res_value::dataType == Res_value::TYPE_STRING) are indexed. inline const ResStringPool* GetStringPool() const { - return &global_string_pool_; + 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 @@ class LoadedArsc { 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 LoadData(std::unique_ptr& loaded_arsc, const char* data, @@ -333,7 +315,7 @@ class LoadedArsc { bool load_as_shared_library = false, bool for_loader = false); - ResStringPool global_string_pool_; + std::unique_ptr global_string_pool_ = util::make_unique(); std::vector> packages_; bool system_ = false; }; diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 2efa65a009e18..b20e6579fda7c 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -492,7 +492,7 @@ class ResStringPool 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 @@ public: 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 @@ public: * 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 dynamicRefTable); ResXMLTree(); ~ResXMLTree(); @@ -827,7 +827,7 @@ private: status_t validateNode(const ResXMLTree_node* node) const; - std::unique_ptr mDynamicRefTable; + std::shared_ptr mDynamicRefTable; status_t mError; void* mOwnedData; @@ -1584,6 +1584,50 @@ struct ResTable_map 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(entry)); + } + + inline static ResTable_entry_handle unmanaged(const ResTable_entry* entry) { + return ResTable_entry_handle(std::shared_ptr(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 entry) + : entry_(std::move(entry)) { } + + std::shared_ptr 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 @@ struct ResTable_overlayable_policy_header 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 @@ struct alignas(uint32_t) Idmap_header { 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 @@ class DynamicRefTable 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 @@ public: void addMapping(uint8_t buildPackageId, uint8_t runtimePackageId); - // Creates a new clone of the reference table - std::unique_ptr 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& entries() const { diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index e2b9f0040989a..0f2ee6fb968e2 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -79,39 +79,6 @@ TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { 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 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 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 15910241518d2..b3190be8caf17 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -718,15 +718,17 @@ TEST_F(AssetManager2Test, GetOverlayablesToString) { 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 10b83a75304d5..b679672ab34e2 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 system_assets_; + std::unique_ptr overlay_assets_; + std::unique_ptr 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(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 fd57a92c216b8..82dd33523c754 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -144,7 +144,7 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { "resources.arsc", &contents)); std::unique_ptr 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 @@ TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) { 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*)::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 entry_header; -}; - -TEST(LoadedArscTest, LoadOverlay) { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc", - &contents)); - - MockLoadedIdmap loaded_idmap; - - std::unique_ptr 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 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 @@ TEST(LoadedArscTest, GetOverlayableMap) { 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 @@ TEST(LoadedArscTest, LoadCustomLoader) { std::unique_ptr asset = ApkAssets::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc"); - MockLoadedIdmap loaded_idmap; const StringPiece data( reinterpret_cast(asset->getBuffer(true /*wordAligned*/)), asset->getLength()); @@ -404,13 +350,13 @@ TEST(LoadedArscTest, LoadCustomLoader) { 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 0000000000000..4c0bb47f59bf9 --- /dev/null +++ b/libs/androidfw/tests/data/loader/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/libs/androidfw/tests/data/loader/build b/libs/androidfw/tests/data/loader/build new file mode 100755 index 0000000000000..457ec51ffe692 --- /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 0000000000000..3293229104ce3 --- /dev/null +++ b/libs/androidfw/tests/data/loader/res/values/public.xml @@ -0,0 +1,19 @@ + + + + + + \ 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 0000000000000..0653536508f80 --- /dev/null +++ b/libs/androidfw/tests/data/loader/res/values/values.xml @@ -0,0 +1,19 @@ + + + + + loader + diff --git a/libs/androidfw/tests/data/loader/resources.arsc b/libs/androidfw/tests/data/loader/resources.arsc index 2c881f2cdfe5f52bc87446d5a7f4844e60a0bc27..2bdb288dbcab6ecbd6730a1865fe44159930cf59 100644 GIT binary patch literal 652 zcmdr~I|>3Z6r4l|7A|-IiyS}}_XatF#aawDBoo_;)oVKcF?o6{rhb>BY?9ia6Ysn=dm8S~a`)61w zk61ogv~;;hEN*~(39`W+2lmpkvkm>v`LFJaqC18@`jcP9P*u=+3YT)=3ueP9UE}ta WzfimEYd!<_3`lYeexSq{2|oaAz(TtK literal 620 zcmd^*KMTSz5XE1klnxFZ92`2t-9`NXc5oML1y^Z{24f2SGxXd0PE&Dr^uf#TT?lvN z6lSmr2p~?df02iXm--dn$8wp~kLqYjQ}nj(TRWVbHTTL6S=ShMJ-7-`I!JuK3mi%` zsA0(ela{4You_2kaZXO9@LG+5jOdtN& - - + package="com.android.test.overlay"> + diff --git a/libs/androidfw/tests/data/overlay/R.h b/libs/androidfw/tests/data/overlay/R.h new file mode 100644 index 0000000000000..f3dbed29d7ee6 --- /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 + +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 716b1bd9db647..99dfd63051a7a 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 d37874dcbb40d218aacd3fbb778ef0674f291b8e..c594b8e67f28bf3c5e71401ddfef8a28de0f0df6 100644 GIT binary patch literal 2988 zcmds3dr*_d7T+Y0%Ofgakbp=XU#WP7FB%(PJgibcilitCVv!8QKpPTGAV8Hzl^_nO z2!aNrUIAZRM*%HZk<#+C3{j>6K~X6!$kd_*D~VdL>DfSlGWMU=zj`t!=e6ISv%j<7 ze#`Zt7$G1Ngp&V9LWAcnoFgH`0N+gTt`mhz#F62?JW=E>zBJk?E=rhl^!i51I(n*Z zkMwRtp{4T%?z+;;gglg2Ty8_JuYK3IikFqMz-;UG?Zfu-TvJ*?`TU2JJN=xa-z}J( z@@2D|RI6)QCGArQPjTfb;p~eyNQ+CfSH9=9?B}<*Br^x8T{TQ4=e9!})w$bxUvi|R zudS`%=hf{j^(>jUjY1o2_t>9Pdg1U}4NGdP+pakhYaC~mrw44KIQdU2DR!+=Gn$F= z@9zh-g$;Ra3tT8DDGDj{jo94bR85vr4)5rDax!h<-Rh|2XP^GR?7ca!@`Lh*VwN^$ zduSuVP6T>JR5V)sE_WR%x>R}E`12Z0@h2W0UDQ>#2G3VkeU-a?3H^bqvohG_ZEc%F zC2d9ZrjbtB!Kdx5;g4LK$clgy^&W9s%Qs$qd@WnIGE-Oog}Cu^(`y%R=PQEz7@Pg< zT2XE`YgxRc^WOY~r>7rGJ4)>ySQ9r)<7GTs^lDCQV#th_*1Oi)Bxm=h@6Ya#s%%^M zYdQAm7U7FL!;mun7cD66#@ojvSNE3AGD^z3kb{#!Mj z-DPYsG)Nk3Pwq5g?#Sfy&biC<>~(qh?t34+<7vP5>a(AsCD-?U$p3iTx_;)ymfXGT zv+ij2Z6-D?4nD@9&FVTnqjAd>d2`eX(fyvF%-O+CHyZ=yU)LC?)-`9I^__0Np~z?0 zG}AMaxa*LmPB_-kcpHmRpWQ&E`k>y?TkS$g2l z`7=FPy7$u z>VE%au2!(}tz38C-8CO0B2d1D+D%$Ik~rP1`Gzo5#$qa$zn{`X_$dV{^FHfmsqr`1 zds27a8aiAzuTnMNLDN|1b(zumgqt>xA&;$NH@#qNzqr8cQhZ)top-Icpw)X>O<0K0 z@pxHI{rVBpV>u?S`{iRTWFUD!ck%h@C)HoL z9H}^XTWexB&2kpYCiLD>;;SD}hN^*sLWzO?#O7h$DS3%;<%{C2@qP5?o1Q2<1hmzg zWl{G9#LABR0?|p?V+N(araq`SD=4SzJWrLyTEbG~m7OP!u@#GKZzd@g%F7CQs!SG> zrOGQSB-CuhEZe1rc^LUEPIZ9){TVn-!Jtl5BPrOD=$04V}vvO>y&n13j$sZ=uY?< z+6p~{v&?5@y`9n+AwaiHWx;nlBOYgvi|7Z*-T;^%L+wT~I);}R#Xtnx7)=R8MMwZP1;qjGLeNI@DM7;bag?Bz zwFVs|&WHB~&gTrg8rTDv3v6l{#f#f1j1=*u$jofwpoI+-Y1lv}CS%783%q0MvI%wh zgvy>!rLY#Vg?^nF8Us8e@)rGi48A~!xdZ=GvKY@Wo{I!{Sm29;p$;1}jiE}`1D-h$kDPwuC4`YCH8+>mr_vrc{HwDj)*NuCQF_^p;!5k-C%a>po#^Ac# z$D@B=v4BVC0^@z}4h%KvvBtH5s<*L%?J>OcHDDX}1dqoxgM_eA61L&nr?<&q8+wYz z;O8+Ja5ebeB(DE~`EhL+C$0|(mk0!&*W5@)V}}3N+)t-YF!kzVcod;Ku(rQEp{GtW v^^!0#jRxv{-C;g41B`vm_h@1U6)YTPnDlkx`WWMeK-Y{A70NtfhOIvUyzAee literal 5211 zcmds*e`rT;{a3LpHl_I*;Di9?pZwJqr$x5Ctkj9bMmgS#E}>N zJ{kY!E4^<%nSJ2PpZ=`%=hrrV=fx}2w=J%^@8!y+qunQdJJRy2uePKQOq+Uo$pcI8 zc{bJd-NyG@H@*I~=2L}p$?<*P`r$wJZC&;IA0J4!4X=OerG1Z{_}9T32M_)8(-$T# zKK)cve9ymkE_pw3eekcFcKqmK_G0z;haWEQZg}>CMXy&(9(d#7g}=MI|IDexmak<0 zb~t~0;_dAxdLR5`@cX}b@$enn7M*He|4hf7zdkcGx+vS^3M*#J>>m8noDb;wFU&(B znwMxgn3stqzkF}!Z09^*LM(Ol_YIahy87eorT&hXyDM7br?0}zbBm)f`mPt;{Br4s zS+fS1$iyRkLtUlb_ASoE=2tw)USDBdhwF12UEH<1qU&@eKJ_rh-GJ+I{rr!+wXEw$ z?YM1^|3Bq?KY=X0H8T4Dem=i(w{!n7$<^|U)y1EUI#>X4$UqY`Lo2L=HLw8&U^{#d z_QF9p3U9$__yb&kKf?rEhnwKfVKv+fb+8Z;@O5a00<3~gD8gnKhCQ$!UV~%sHk^fb z;S%`Y*;V)$Zo%{zzb>eSdUzNzunbz@Iam$dP=as6E_fA2;U{n$egk9hM;M2X;2L}a zQ!sOybF*O{EP%z(2+N@ro`<#23q!C2cEdh61V4q7a0br7dvF=9!VQ>$YR=JIsE0T- zLNm0%YS;inFbpFw3P<52oQ3l+4ihj5x1g$$E2+!dBP?d*KiqgLhyIF2H5D3O8U1s%OwY)I%H^ zp&8m>HEe((7={rTg`;p1&cb;ZhY6U3TTn$v&xSg92r}?Ew8AP_2PN1JyJ0^Zf#Yx* z&cP+P0@vUs#O|PfxE~fk0-B%&R>E2+!dBP?d*KiqgLhyIF2H5D3O8U1s;lWA>LCt| z&XPL#RUXsj(IFW9to!&i74Y_t$HM!dL*2BB!fpXcqD^IGI%6|M>2RMgGVx& zC-0UF9?9U53LdH8kqREE;E@U*so;?c9;x7w3LdH8kq#c|;E@g<>EMwL9_iqb4j$>? zkq#c|;E@R)nc$HL9+}{g2_Bi?kqI7|;E@R)nc$HP9@*fL4IbIxkqsW%;E@d;+2D~4 z9@*fL3m&=PkqaKV;E@X+x!{ot9=YI=3m&=Pkq;jE;E@j=`QVWc9{J#r4<7m8kq;jE z$YXl>8uWKjTKj86Yk!q!?XN|x{gtA%zgD#NSESbddePcnF4W zp}O@eX`#CHD~X}H^((2Ny7eo`A-ZIT*4D4&hw9d^WQgk4ujGj8)~{rV=#nN{TfdSh zs$0L3Dymz*k}RrQzmhJhTfdSpqD#hTZT(8lsBZmA)~Ig%O5Uh${YvJDE~%rn^()Dv zy7epRqq_Ag38cF9D=DP9^(#pvx@3{o*01D|>ejDhlIqs4`@5_;e#K{&&v~z>JBYUXoL64<{aOq^`vR=6qLN2m Oeajmks-W>0`>)(y~{hy5LzvKHiIIOn^ + + \ 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 8e4417e457d18..ba018ec19e9fb 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. --> - - test2-overlay - - 10 - 11 - - - @null + Overlay One + Overlay Two + @string/internal + @string/overlay2 + Internal + 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 0000000000000..9eca2faa76f45 --- /dev/null +++ b/libs/androidfw/tests/data/overlay/res/xml/overlays.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/androidfw/tests/data/overlayable/R.h b/libs/androidfw/tests/data/overlayable/R.h index e46e264da3180..35125a62ff4b1 100644 --- a/libs/androidfw/tests/data/overlayable/R.h +++ b/libs/androidfw/tests/data/overlayable/R.h @@ -31,6 +31,43 @@ struct R { 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 98fdc51011607..0aa97d639c300 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 047e6afde86b5c420bbb705634e4f047ded966cb..9dc9c15f68a9e716aa1b5423d28b9d0fb8fa9221 100644 GIT binary patch literal 6306 zcmeHLX;4#F6ux;tfPhU<6jbb_wu)O(M6tMnJB30-Mnz$e&#EB@VSX%*Q zreIr@4z26d+8OuS)`dc?R6s#RlvcoH6hyEN($;?WC6OcpKOFf}%E^3l?>Tol=bdwM z?t51jU}a5+9e!lpE&(y2e(3wDV%3}?_Jb&uB97H)JyH^sxjCM}jGrXqwwn3c+tvU4 z>?tu(j;#X*+>nLEN3)uhQkPn3a(uL0QO`YbZxgpxCk8(rn%P+Y=wa!vYo>g6EN$jQ ztCD-ZO>=KlsGZV!-G^>O9Tyc{+j{fnBVDEIT)rB=z zFOb*N9e93hK}%)P%zdlwtu1;I(Igsm@%Wvv$gAz%0pX`^jarp3WZt!!+{xalLoV2* zit9hyePvnMhOmkjcUgd~-A5Zv7te>&ovjFwbs30(2!xu|j8#Uh<`T7I<5{JWi%e3m z$ut}pS>Fep^OdB_*9vzPI*$yINf*pLvLk#}?iTlT!m_g?r;i-ayzP%!{w{BB8$aik zZNt_>B;@>(U8VUE-*Aik?#v=TP5gv}v=$_9+U`-jIXruCPWG}RQlHF%q4V6<9BY|h zQQr7P)tvgJOP?jy?|%CB=-`a%q5jHTZpG2j>`r0D{a@PSq5@BapC~`ta%O5|W#PA% z4qQK;6n8uI<1y_&R5+g7mt6MglIgX2Q_^D{OtU_GhPg7xR}>ooY$@sP?uRxa7up& zhZMtYdYADji`*@$no(TqObEprdY5sD>KInz5vA6|FvO7`p(!pPZbXJoAATt>ATnbg zZC_wGFa-z#q5v(h5y%G)0%w47pbij=gE*~}OJdasQB<^&B~DJk9h`KILLRRrefyfp zlxz~KA`*$IaIPYWwG`nQEJQ&H6$_oF3xgps*SV12-h9?VMT*6`P?%u8E;L@vTJ9d2 z%#ncuyG%hs7^xr~oC2)}+9bk)S7Kx}5RD#-`8|!KG{Hc7 z4K3}{eA|pxNEri7Fm}*N05@O+FbKGX6N+K+}mId~AR=lx5F zEk9EWpUQbZ(;f7_=13x+y?Q$WVHefM6Z+xCK3bahZvoVX834`w2!QHY18f2EfPKIT z-~vzq)B<9$iql4N9a&Et9Zfh-b9THr>t)VPFlQ&4v)<0@ffI1ApB z*xM&Wr9>+6pVANqhZs&3r;tZRbDWZhL>;9TjA2NjqNRW^jD+~2rD1jiK)$a60TW7Q zW3h34x?wSz*CHBQDL`ZB3&?=wz)D~pkOk}p4g+?`9!fa-aRBLgY7gaUuF`QjgYpgr zo=&8^$iU;}f#>xtLu?GZejdZ;Je?2l``H@C^({l!^6T|_8DekX_4_bHuZLLah5jdsvyHnk%jyiwL& zcF>s6a|^oZ-}R+#YKNt6qpbUM3;C1PQa6p2rEa6F`*brz3_aB6XkI=y%~MO=Mp<|2 zrf(F`ZAEjyn!#%boEzNfnHGZjt3h>6MD>p0`<__SOFhp6=I=C<$yoffBpxwO0D7sX zd%yuo#9|jZ`(NZ(lH33Q delta 669 zcmZ2v_*rU#VSVWYPp(4_0&Jg}?qIS?U|IKojY}$f{mi~kTicc@O=aGvI$x-L zL%-Xq$-i%{oc-$MA@#E-x9^#AuqDIKJzD#h^D^G6hndyZRNP$1SzmXi^6dMa+-4sH zb02(qFKug{=)bL?>q70x##>YNZJd_(`)GxRVQ>($Rx`WURGkw6@+z+;rgmoU==>iU za@sX&d#IW4_pJ}I7M(v*o4@(vxh=<*D}Qj7CTi;`1|^%9GUlNo>_JYYGXP6kE>8HNc! zniZrO1b~7D5Ehse0+OmgY!1ZUlO35wCr@G)p1gxuVX^>=1Sd!dlLF%(uFYO7e2jqs zAQPbgWH87eBq0U`m?V&9V<=`QVJKqAWXNMk2dk4|XaLeE4w=lzCOz4M%@61bCME#} z6|g#pn#lp|B7%%eAq+c^1T!W-|9m_sVSn-Qb~C6LZ 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 0000000000000..268118a91bff0 --- /dev/null +++ b/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml @@ -0,0 +1,20 @@ + + + \ 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 fcdbe94466c15..b3e8f7d8e84ba 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 @@ --> - - - + + + + + - - - - + + + + + + - - - - - + + + + + + - - - - - - + + + + + + + + + + + + + + + \ 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 5676d7cc64c98..042a311b2b88a 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 @@ + + + + + + + + + + + + + + + + + \ 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 a86b31282bc9c..235772d35fd0d 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 @@ Overlayable Two Overlayable Three Overlayable Four + Overlayable Five + Overlayable Six + Overlayable Seven + Overlayable Eight + Overlayable Nine + Overlayable Ten + Overlayable Eleven + + 0 + false + diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h index 3741074847849..c0160c0f78a93 100644 --- a/libs/androidfw/tests/data/system/R.h +++ b/libs/androidfw/tests/data/system/R.h @@ -43,7 +43,8 @@ struct R { 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 0000000000000..077874d0b0fe6 --- /dev/null +++ b/libs/androidfw/tests/data/system/res/values/public.xml @@ -0,0 +1,20 @@ + + + + + + + \ 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 0000000000000..0629c1d13795d --- /dev/null +++ b/libs/androidfw/tests/data/system/res/values/values.xml @@ -0,0 +1,20 @@ + + + + + yes + no + \ 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 9045d6c4de2122c5a95a6f42e517de14f9ba9a40..1f7e0073336639c6a5db159e6f97d4132b86d389 100644 GIT binary patch delta 409 zcmcb?vx9%a2d-bczHy7NGBAj+PW&Zh2VyZYfUpdM0gz?_VlV&-s=(Ml8mNqci779i zftk57wHU-vV7$Sy*^ZHq(X<0(02F}C$pErhU;;o|2Z_xCWJ`g>Cf73QtFkc^Gn6nC zF=R62F{A@ULAncoG#e0u0nlWV$p@LlS@nTjlgU?^jJT0pv6+o|A0sallK?{kP#R>V z$mBpqt;r_L8bS_?Od$+MV8TFp$z(%j=bZmU0H9(9B9#3X0IK5vh8W0V4h9BhkUkI} m6jmVq9frRdlUe|Gjk^Z delta 201 zcmdnNe}iYj2QJ&`hktKjW?MCbKddZFXkf%Q#tu g?G%#pCjVgbVLHMx*^S+r=^M-BW_EoxTUL+(0N+|46#xJL diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 05375b0cb8717..21386b88ce2c1 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -787,7 +787,9 @@ int doDump(Bundle* bundle) // The dynamicRefTable can be null if there are no resources for this asset cookie. // This fine. - const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie); + auto noop_destructor = [](const DynamicRefTable* /*ref_table */) { }; + auto dynamicRefTable = std::shared_ptr( + res.getDynamicRefTableForCookie(assetsCookie), noop_destructor); Asset* asset = NULL; diff --git a/tools/aapt2/format/binary/XmlFlattener_test.cpp b/tools/aapt2/format/binary/XmlFlattener_test.cpp index 1dac493359e45..c24488b16153e 100644 --- a/tools/aapt2/format/binary/XmlFlattener_test.cpp +++ b/tools/aapt2/format/binary/XmlFlattener_test.cpp @@ -226,10 +226,10 @@ TEST_F(XmlFlattenerTest, FlattenNonStandardPackageId) { ASSERT_TRUE(linker.Consume(context_.get(), doc.get())); // The tree needs a custom DynamicRefTable since it is not using a standard app ID (0x7f). - android::DynamicRefTable dynamic_ref_table; - dynamic_ref_table.addMapping(0x80, 0x80); + auto dynamic_ref_table = std::make_shared(); + dynamic_ref_table->addMapping(0x80, 0x80); - android::ResXMLTree tree(&dynamic_ref_table); + auto tree = android::ResXMLTree(std::move(dynamic_ref_table)); ASSERT_TRUE(Flatten(doc.get(), &tree)); while (tree.next() != android::ResXMLTree::START_TAG) {