diff options
| author | 2021-01-08 13:34:28 -0800 | |
|---|---|---|
| committer | 2021-02-09 20:13:50 -0800 | |
| commit | 2ed8bfa7fda3c42280e0816c6cf1af852981723b (patch) | |
| tree | 4fbe3151d2a0094f9bb97b8714a4afa9be2ed094 /libs/androidfw | |
| parent | 5c8ba79373ef7a950f6bfb87e7aa694f29ace243 (diff) | |
Add fabricated RRO generation to libidmap2
Fabricated Runtime Resource Overlays are overlays that are generated
at runtime and are stored in the data/ partition.
The system can fabricate RROs at runtime to dynamically theme the
device. Idmaps can now be created from APK RROs and fabricated RROs.
Rather than operating on ApkAssets, libidmap2 now operates on abstract
resource "containers" that supply resource values. Target resource
containers implement methods needed to query overlayable and target
overlay information. Currently only APKs can be loaded as target
resource containers. Overlay resource containers implement methods to
supply the mapping of target resource to overlay value and other
overlay information.
The format of a fabricated RRO is as follows:
0x00 - 0x04 : fabricated overlay magic (always FRRO)
0x04 - 0x08 : file format version
0x08 - 0x0c : crc of version + proto data
0x0c - EOF : proto fabricated overlay data
The magic is used to quickly detect if the file is a fabricated overlay.
The version is incremented whenever backwards incompatible changes are
made to the proto file format. Idmap must always be able to upgrade
fabricated overlays from previous versions to new versions, so all
previous versions must be checked into the tree.
Bug: 172471315
Test: libidmap2_tests && libandroidfw_tests
Change-Id: I4c9f29da278672e5695fb57d131a44c11a835180
Diffstat (limited to 'libs/androidfw')
| -rwxr-xr-x | libs/androidfw/ApkAssets.cpp | 10 | ||||
| -rw-r--r-- | libs/androidfw/AssetManager2.cpp | 80 | ||||
| -rw-r--r-- | libs/androidfw/AssetsProvider.cpp | 12 | ||||
| -rw-r--r-- | libs/androidfw/Idmap.cpp | 27 | ||||
| -rw-r--r-- | libs/androidfw/ResourceTypes.cpp | 11 | ||||
| -rw-r--r-- | libs/androidfw/include/androidfw/AssetManager2.h | 2 | ||||
| -rw-r--r-- | libs/androidfw/include/androidfw/AssetsProvider.h | 2 | ||||
| -rw-r--r-- | libs/androidfw/include/androidfw/Idmap.h | 7 | ||||
| -rw-r--r-- | libs/androidfw/include/androidfw/ResourceTypes.h | 15 | ||||
| -rw-r--r-- | libs/androidfw/tests/data/overlay/overlay.idmap | bin | 640 -> 636 bytes |
10 files changed, 102 insertions, 64 deletions
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index ca5981c0dd5c..9c743cea592a 100755 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -83,8 +83,16 @@ std::unique_ptr<ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path, return {}; } + std::unique_ptr<AssetsProvider> overlay_assets; const std::string overlay_path(loaded_idmap->OverlayApkPath()); - auto overlay_assets = ZipAssetsProvider::Create(overlay_path); + if (IsFabricatedOverlay(overlay_path)) { + // Fabricated overlays do not contain resource definitions. All of the overlay resource values + // are defined inline in the idmap. + overlay_assets = EmptyAssetsProvider::Create(); + } else { + // The overlay should be an APK. + overlay_assets = ZipAssetsProvider::Create(overlay_path); + } if (overlay_assets == nullptr) { return {}; } diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 03ab62f48870..36bde5ccf267 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -102,9 +102,8 @@ AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } -bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, - bool invalidate_caches) { - apk_assets_ = apk_assets; +bool AssetManager2::SetApkAssets(std::vector<const ApkAssets*> apk_assets, bool invalidate_caches) { + apk_assets_ = std::move(apk_assets); BuildDynamicRefTable(); RebuildFilterList(); if (invalidate_caches) { @@ -137,6 +136,36 @@ void AssetManager2::BuildDynamicRefTable() { // 0x01 is reserved for the android package. int next_package_id = 0x02; for (const ApkAssets* apk_assets : sorted_apk_assets) { + std::shared_ptr<OverlayDynamicRefTable> overlay_ref_table; + if (auto loaded_idmap = apk_assets->GetLoadedIdmap(); loaded_idmap != nullptr) { + // The target package must precede the overlay package in the apk assets paths in order + // to take effect. + auto iter = apk_assets_package_ids.find(std::string(loaded_idmap->TargetApkPath())); + if (iter == apk_assets_package_ids.end()) { + LOG(INFO) << "failed to find target package for overlay " + << loaded_idmap->OverlayApkPath(); + } else { + uint8_t target_package_id = iter->second; + + // Create a special dynamic reference table for the overlay to rewrite references to + // overlay resources as references to the target resources they overlay. + overlay_ref_table = std::make_shared<OverlayDynamicRefTable>( + loaded_idmap->GetOverlayDynamicRefTable(target_package_id)); + + // Add the overlay resource map to the target package's set of overlays. + const uint8_t target_idx = package_ids_[target_package_id]; + CHECK(target_idx != 0xff) << "overlay target '" << loaded_idmap->TargetApkPath() + << "'added to apk_assets_package_ids but does not have an" + << " assigned package group"; + + PackageGroup& target_package_group = package_groups_[target_idx]; + target_package_group.overlays_.push_back( + ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id, + overlay_ref_table.get()), + apk_assets_cookies[apk_assets]}); + } + } + const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc(); for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) { // Get the package ID or assign one if a shared library. @@ -147,50 +176,25 @@ void AssetManager2::BuildDynamicRefTable() { package_id = package->GetPackageId(); } - // Add the mapping for package ID to index if not present. uint8_t idx = package_ids_[package_id]; if (idx == 0xff) { + // Add the mapping for package ID to index if not present. package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size()); - package_groups_.push_back({}); - - 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( - std::string(loaded_idmap->TargetApkPath())); - if (target_package_iter == apk_assets_package_ids.end()) { - LOG(INFO) << "failed to find target package for overlay " - << loaded_idmap->OverlayApkPath(); - } else { - 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 rewrite references to - // overlay resources as references to the target resources they overlay. - auto overlay_table = std::make_shared<OverlayDynamicRefTable>( - loaded_idmap->GetOverlayDynamicRefTable(target_package_id)); - package_groups_.back().dynamic_ref_table = overlay_table; - - // Add the overlay resource map to the target package's set of overlays. - target_package_group.overlays_.push_back( - ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id, - overlay_table.get()), - apk_assets_cookies[apk_assets]}); - } + PackageGroup& new_group = package_groups_.emplace_back(); + + if (overlay_ref_table != nullptr) { + // If this package is from an overlay, use a dynamic reference table that can rewrite + // overlay resource ids to their corresponding target resource ids. + new_group.dynamic_ref_table = overlay_ref_table; } - DynamicRefTable* ref_table = package_groups_.back().dynamic_ref_table.get(); + DynamicRefTable* ref_table = new_group.dynamic_ref_table.get(); ref_table->mAssignedPackageId = package_id; ref_table->mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f; } - PackageGroup* package_group = &package_groups_[idx]; // Add the package and to the set of packages with the same ID. + PackageGroup* package_group = &package_groups_[idx]; package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); package_group->cookies_.push_back(apk_assets_cookies[apk_assets]); @@ -578,7 +582,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry( const PackageGroup& package_group = package_groups_[package_idx]; auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config, - stop_at_first_match, ignore_configuration); + stop_at_first_match, ignore_configuration); if (UNLIKELY(!result.has_value())) { return base::unexpected(result.error()); } diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp index 23cacf88a6db..f3c48f7f9fc8 100644 --- a/libs/androidfw/AssetsProvider.cpp +++ b/libs/androidfw/AssetsProvider.cpp @@ -84,7 +84,7 @@ const std::string& ZipAssetsProvider::PathOrDebugName::GetDebugName() const { return value_; } -ZipAssetsProvider::ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, +ZipAssetsProvider::ZipAssetsProvider(ZipArchiveHandle handle, PathOrDebugName&& path, time_t last_mod_time) : zip_handle_(handle, ::CloseArchive), name_(std::forward<PathOrDebugName>(path)), @@ -93,7 +93,7 @@ ZipAssetsProvider::ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path) { ZipArchiveHandle handle; if (int32_t result = OpenArchive(path.c_str(), &handle); result != 0) { - LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); + LOG(ERROR) << "Failed to open APK '" << path << "': " << ::ErrorCodeString(result); CloseArchive(handle); return {}; } @@ -253,6 +253,14 @@ bool ZipAssetsProvider::ForEachFile(const std::string& root_path, return result == -1; } +std::optional<uint32_t> ZipAssetsProvider::GetCrc(std::string_view path) const { + ::ZipEntry entry; + if (FindEntry(zip_handle_.get(), path, &entry) != 0) { + return {}; + } + return entry.crc32; +} + const std::string& ZipAssetsProvider::GetDebugName() const { return name_.GetDebugName(); } diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index f216f55771c2..efd1f6a25786 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -54,12 +54,6 @@ struct Idmap_header { }; struct Idmap_data_header { - uint8_t target_package_id; - uint8_t overlay_package_id; - - // Padding to ensure 4 byte alignment for target_entry_count - uint16_t p0; - uint32_t target_entry_count; uint32_t target_inline_entry_count; uint32_t overlay_entry_count; @@ -158,19 +152,19 @@ IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const { 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) << 24U); + // The resource ids encoded within the idmap are build-time resource ids so do not consider the + // package id when determining if the resource in the target package is overlaid. + target_res_id &= 0x00FFFFFFU; // Check if the target resource is mapped to an overlay resource. auto first_entry = entries_; auto end_entry = entries_ + dtohl(data_header_->target_entry_count); auto entry = std::lower_bound(first_entry, end_entry, target_res_id, - [](const Idmap_target_entry &e, const uint32_t target_id) { - return dtohl(e.target_id) < target_id; + [](const Idmap_target_entry& e, const uint32_t target_id) { + return (0x00FFFFFFU & dtohl(e.target_id)) < target_id; }); - if (entry != end_entry && dtohl(entry->target_id) == target_res_id) { + if (entry != end_entry && (0x00FFFFFFU & dtohl(entry->target_id)) == target_res_id) { uint32_t overlay_resource_id = dtohl(entry->overlay_id); // Lookup the resource without rewriting the overlay resource id back to the target resource id // being looked up. @@ -182,12 +176,13 @@ IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const { auto first_inline_entry = inline_entries_; auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count); auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id, - [](const Idmap_target_entry_inline &e, + [](const Idmap_target_entry_inline& e, const uint32_t target_id) { - return dtohl(e.target_id) < target_id; + return (0x00FFFFFFU & dtohl(e.target_id)) < target_id; }); - if (inline_entry != end_inline_entry && dtohl(inline_entry->target_id) == target_res_id) { + if (inline_entry != end_inline_entry && + (0x00FFFFFFU & dtohl(inline_entry->target_id)) == target_res_id) { return Result(inline_entry->value); } return {}; @@ -235,7 +230,7 @@ std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size } return std::string_view(data, *len); } -} +} // namespace LoadedIdmap::LoadedIdmap(std::string&& idmap_path, const Idmap_header* header, diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 223382731bc0..30500abc39c0 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -25,6 +25,7 @@ #include <string.h> #include <algorithm> +#include <fstream> #include <limits> #include <map> #include <memory> @@ -44,6 +45,7 @@ #ifdef __ANDROID__ #include <binder/TextOutput.h> + #endif #ifndef INT32_MAX @@ -233,6 +235,15 @@ void Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs fill9patchOffsets(reinterpret_cast<Res_png_9patch*>(outData)); } +bool IsFabricatedOverlay(const std::string& path) { + std::ifstream fin(path); + uint32_t magic; + if (fin.read(reinterpret_cast<char*>(&magic), sizeof(uint32_t))) { + return magic == kFabricatedOverlayMagic; + } + return false; +} + static bool assertIdmapHeader(const void* idmap, size_t size) { if (reinterpret_cast<uintptr_t>(idmap) & 0x03) { ALOGE("idmap: header is not word aligned"); diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 6fbd6aa0df7b..2255973f1039 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -101,7 +101,7 @@ class AssetManager2 { // Only pass invalidate_caches=false when it is known that the structure // change in ApkAssets is due to a safe addition of resources with completely // new resource IDs. - bool SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches = true); + bool SetApkAssets(std::vector<const ApkAssets*> apk_assets, bool invalidate_caches = true); inline const std::vector<const ApkAssets*> GetApkAssets() const { return apk_assets_; diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h index 7b06947f45aa..6f16ff453905 100644 --- a/libs/androidfw/include/androidfw/AssetsProvider.h +++ b/libs/androidfw/include/androidfw/AssetsProvider.h @@ -88,6 +88,8 @@ struct ZipAssetsProvider : public AssetsProvider { WARN_UNUSED const std::string& GetDebugName() const override; WARN_UNUSED bool IsUpToDate() const override; + WARN_UNUSED std::optional<uint32_t> GetCrc(std::string_view path) const; + ~ZipAssetsProvider() override = default; protected: std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode, diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index 0ded79309bc1..6804472b3d17 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -168,15 +168,14 @@ class LoadedIdmap { } // Returns a mapping from target resource ids to overlay values. - const IdmapResMap GetTargetResourcesMap( - uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const { + const IdmapResMap GetTargetResourcesMap(uint8_t target_assigned_package_id, + const OverlayDynamicRefTable* overlay_ref_table) const { return IdmapResMap(data_header_, target_entries_, target_inline_entries_, target_assigned_package_id, overlay_ref_table); } // Returns a dynamic reference table for a loaded overlay package. - const OverlayDynamicRefTable GetOverlayDynamicRefTable( - uint8_t target_assigned_package_id) const { + const OverlayDynamicRefTable GetOverlayDynamicRefTable(uint8_t target_assigned_package_id) const { return OverlayDynamicRefTable(data_header_, overlay_entries_, target_assigned_package_id); } diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index bfd564c258ee..168a863df2bc 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -43,8 +43,19 @@ namespace android { -constexpr const static uint32_t kIdmapMagic = 0x504D4449u; -constexpr const static uint32_t kIdmapCurrentVersion = 0x00000007u; +constexpr const uint32_t kIdmapMagic = 0x504D4449u; +constexpr const uint32_t kIdmapCurrentVersion = 0x00000008u; + +// This must never change. +constexpr const uint32_t kFabricatedOverlayMagic = 0x4f525246; // FRRO (big endian) + +// The version should only be changed when a backwards-incompatible change must be made to the +// fabricated overlay file format. Old fabricated overlays must be migrated to the new file format +// to prevent losing fabricated overlay data. +constexpr const uint32_t kFabricatedOverlayCurrentVersion = 1; + +// Returns whether or not the path represents a fabricated overlay. +bool IsFabricatedOverlay(const std::string& path); /** * In C++11, char16_t is defined as *at least* 16 bits. We do a lot of diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap Binary files differindex 723413c3cea8..88eadccb38cf 100644 --- a/libs/androidfw/tests/data/overlay/overlay.idmap +++ b/libs/androidfw/tests/data/overlay/overlay.idmap |