summaryrefslogtreecommitdiff
path: root/libs/androidfw
diff options
context:
space:
mode:
author Ryan Mitchell <rtmitchell@google.com> 2021-01-08 13:34:28 -0800
committer Ryan Mitchell <rtmitchell@google.com> 2021-02-09 20:13:50 -0800
commit2ed8bfa7fda3c42280e0816c6cf1af852981723b (patch)
tree4fbe3151d2a0094f9bb97b8714a4afa9be2ed094 /libs/androidfw
parent5c8ba79373ef7a950f6bfb87e7aa694f29ace243 (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-xlibs/androidfw/ApkAssets.cpp10
-rw-r--r--libs/androidfw/AssetManager2.cpp80
-rw-r--r--libs/androidfw/AssetsProvider.cpp12
-rw-r--r--libs/androidfw/Idmap.cpp27
-rw-r--r--libs/androidfw/ResourceTypes.cpp11
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h2
-rw-r--r--libs/androidfw/include/androidfw/AssetsProvider.h2
-rw-r--r--libs/androidfw/include/androidfw/Idmap.h7
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h15
-rw-r--r--libs/androidfw/tests/data/overlay/overlay.idmapbin640 -> 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
index 723413c3cea8..88eadccb38cf 100644
--- a/libs/androidfw/tests/data/overlay/overlay.idmap
+++ b/libs/androidfw/tests/data/overlay/overlay.idmap
Binary files differ