summaryrefslogtreecommitdiff
path: root/libs/androidfw/LoadedArsc.cpp
diff options
context:
space:
mode:
author Eric Miao <ericymiao@google.com> 2022-09-09 15:46:14 -0700
committer Eric Miao <ericymiao@google.com> 2022-11-15 12:12:33 -0800
commit368cd19d031f5a0b219ea92531d11ccb0ee66c4d (patch)
treeea6aa74fa746c043112705fdb08438abf782717b /libs/androidfw/LoadedArsc.cpp
parentb0d0974aeba2b0498f5f576ff38012ac94b909a8 (diff)
androidfw: Add support for compact resource entries
Bug: 237583012 Given the large number of simple resources such as strings in Android resources, their ResTable_entry and Res_value can be encoded together in a compact way. This allows a significant saving in both storage and memory footprint. The basic observations for simple resources are: * ResTable_entry.size will always be sizeof(ResTable_entry) unless it's a complex entry * ResTable_entry.key is unlikely to exceed 16-bit * ResTable_entry.flags only uses 3 bits for now * Res_value.size will always be sizeof(Res_value) Given the above, we could well encode the information into a compact/compatible structure. struct compact { uint16_t key; uint16_t flags; uint32_t data; }; The layout of this structure will allow maximum backward compatibility. e.g. the flags will be at the same offset, and a `dtohs((ResTable_entry *)entry->flags) & FLAG_COMPACT` would tell if this entry is a compact one or not. For a compact entry: struct compact *entry; entry_size == sizeof(*entry) entry_key == static_cast<uint32_t>(dtohs(entry->key)) entry_flags == dtohs(entry->flags) & 0xff // low 8-bit data_type == dtohs(entry->flags) >> 8 // high 8-bit data_size == sizeof(Res_value) data_value == dtohl(entry->data) To allow minimum code change and backward compatibility, we change 'struct ResTable_entry' to 'union ResTable_entry', with an anonymous structure inside that's fully backward compatible. Thus, any existing reference such as: ResTable_entry *entry = ... if (dtohs(entry->flags) & ResTable_entry::FLAG_COMPLEX) ... would still work. However, special care needs to be taken after an entry is obtained, and when value needs to be extracted. A compact entry will not encode a complex value, and hence complex entries/values are handled the same way. Change-Id: I15d97a4f5e85fab28c075496f7f0cf6b1fcd73e3
Diffstat (limited to 'libs/androidfw/LoadedArsc.cpp')
-rw-r--r--libs/androidfw/LoadedArsc.cpp24
1 files changed, 15 insertions, 9 deletions
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 5b69cca2d747..e78f91ee3f46 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -107,8 +107,8 @@ static bool VerifyResTableType(incfs::map_ptr<ResTable_type> header) {
return true;
}
-static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry(
- incfs::verified_map_ptr<ResTable_type> type, uint32_t entry_offset) {
+static base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError>
+VerifyResTableEntry(incfs::verified_map_ptr<ResTable_type> type, uint32_t entry_offset) {
// Check that the offset is aligned.
if (UNLIKELY(entry_offset & 0x03U)) {
LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned.";
@@ -136,7 +136,7 @@ static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry(
return base::unexpected(IOError::PAGES_MISSING);
}
- const size_t entry_size = dtohs(entry->size);
+ const size_t entry_size = entry->size();
if (UNLIKELY(entry_size < sizeof(entry.value()))) {
LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
<< " is too small.";
@@ -149,6 +149,11 @@ static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry(
return base::unexpected(std::nullopt);
}
+ // If entry is compact, value is already encoded, and a compact entry
+ // cannot be a map_entry, we are done verifying
+ if (entry->is_compact())
+ return entry.verified();
+
if (entry_size < sizeof(ResTable_map_entry)) {
// There needs to be room for one Res_value struct.
if (UNLIKELY(entry_offset + entry_size > chunk_size - sizeof(Res_value))) {
@@ -192,7 +197,7 @@ static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry(
return base::unexpected(std::nullopt);
}
}
- return {};
+ return entry.verified();
}
LoadedPackage::iterator::iterator(const LoadedPackage* lp, size_t ti, size_t ei)
@@ -228,7 +233,7 @@ uint32_t LoadedPackage::iterator::operator*() const {
entryIndex_);
}
-base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntry(
+base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntry(
incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index) {
base::expected<uint32_t, NullOrIOError> entry_offset = GetEntryOffset(type_chunk, entry_index);
if (UNLIKELY(!entry_offset.has_value())) {
@@ -297,13 +302,14 @@ base::expected<uint32_t, NullOrIOError> LoadedPackage::GetEntryOffset(
return value;
}
-base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntryFromOffset(
- incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset) {
+base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError>
+LoadedPackage::GetEntryFromOffset(incfs::verified_map_ptr<ResTable_type> type_chunk,
+ uint32_t offset) {
auto valid = VerifyResTableEntry(type_chunk, offset);
if (UNLIKELY(!valid.has_value())) {
return base::unexpected(valid.error());
}
- return type_chunk.offset(offset + dtohl(type_chunk->entriesStart)).convert<ResTable_entry>();
+ return valid;
}
base::expected<std::monostate, IOError> LoadedPackage::CollectConfigurations(
@@ -400,7 +406,7 @@ base::expected<uint32_t, NullOrIOError> LoadedPackage::FindEntryByName(
return base::unexpected(IOError::PAGES_MISSING);
}
- if (dtohl(entry->key.index) == static_cast<uint32_t>(*key_idx)) {
+ if (entry->key() == static_cast<uint32_t>(*key_idx)) {
// The package ID will be overridden by the caller (due to runtime assignment of package
// IDs for shared libraries).
return make_resid(0x00, *type_idx + type_id_offset_ + 1, res_idx);