Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit 368cd19

Browse files
author
Eric Miao
committed
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
1 parent b0d0974 commit 368cd19

16 files changed

+407
-241
lines changed

libs/androidfw/AssetManager2.cpp

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -43,28 +43,19 @@ namespace {
4343

4444
using EntryValue = std::variant<Res_value, incfs::verified_map_ptr<ResTable_map_entry>>;
4545

46+
/* NOTE: table_entry has been verified in LoadedPackage::GetEntryFromOffset(),
47+
* and so access to ->value() and ->map_entry() are safe here
48+
*/
4649
base::expected<EntryValue, IOError> GetEntryValue(
4750
incfs::verified_map_ptr<ResTable_entry> table_entry) {
48-
const uint16_t entry_size = dtohs(table_entry->size);
51+
const uint16_t entry_size = table_entry->size();
4952

5053
// Check if the entry represents a bag value.
51-
if (entry_size >= sizeof(ResTable_map_entry) &&
52-
(dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX)) {
53-
const auto map_entry = table_entry.convert<ResTable_map_entry>();
54-
if (!map_entry) {
55-
return base::unexpected(IOError::PAGES_MISSING);
56-
}
57-
return map_entry.verified();
54+
if (entry_size >= sizeof(ResTable_map_entry) && table_entry->is_complex()) {
55+
return table_entry.convert<ResTable_map_entry>().verified();
5856
}
5957

60-
// The entry represents a non-bag value.
61-
const auto entry_value = table_entry.offset(entry_size).convert<Res_value>();
62-
if (!entry_value) {
63-
return base::unexpected(IOError::PAGES_MISSING);
64-
}
65-
Res_value value;
66-
value.copyFrom_dtoh(entry_value.value());
67-
return value;
58+
return table_entry->value();
6859
}
6960

7061
} // namespace
@@ -814,17 +805,12 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
814805
return base::unexpected(std::nullopt);
815806
}
816807

817-
auto best_entry_result = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
818-
if (!best_entry_result.has_value()) {
819-
return base::unexpected(best_entry_result.error());
820-
}
821-
822-
const incfs::map_ptr<ResTable_entry> best_entry = *best_entry_result;
823-
if (!best_entry) {
824-
return base::unexpected(IOError::PAGES_MISSING);
808+
auto best_entry_verified = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
809+
if (!best_entry_verified.has_value()) {
810+
return base::unexpected(best_entry_verified.error());
825811
}
826812

827-
const auto entry = GetEntryValue(best_entry.verified());
813+
const auto entry = GetEntryValue(*best_entry_verified);
828814
if (!entry.has_value()) {
829815
return base::unexpected(entry.error());
830816
}
@@ -837,7 +823,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
837823
.package_name = &best_package->GetPackageName(),
838824
.type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1),
839825
.entry_string_ref = StringPoolRef(best_package->GetKeyStringPool(),
840-
best_entry->key.index),
826+
(*best_entry_verified)->key()),
841827
.dynamic_ref_table = package_group.dynamic_ref_table.get(),
842828
};
843829
}

libs/androidfw/LoadedArsc.cpp

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ static bool VerifyResTableType(incfs::map_ptr<ResTable_type> header) {
107107
return true;
108108
}
109109

110-
static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry(
111-
incfs::verified_map_ptr<ResTable_type> type, uint32_t entry_offset) {
110+
static base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError>
111+
VerifyResTableEntry(incfs::verified_map_ptr<ResTable_type> type, uint32_t entry_offset) {
112112
// Check that the offset is aligned.
113113
if (UNLIKELY(entry_offset & 0x03U)) {
114114
LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned.";
@@ -136,7 +136,7 @@ static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry(
136136
return base::unexpected(IOError::PAGES_MISSING);
137137
}
138138

139-
const size_t entry_size = dtohs(entry->size);
139+
const size_t entry_size = entry->size();
140140
if (UNLIKELY(entry_size < sizeof(entry.value()))) {
141141
LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
142142
<< " is too small.";
@@ -149,6 +149,11 @@ static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry(
149149
return base::unexpected(std::nullopt);
150150
}
151151

152+
// If entry is compact, value is already encoded, and a compact entry
153+
// cannot be a map_entry, we are done verifying
154+
if (entry->is_compact())
155+
return entry.verified();
156+
152157
if (entry_size < sizeof(ResTable_map_entry)) {
153158
// There needs to be room for one Res_value struct.
154159
if (UNLIKELY(entry_offset + entry_size > chunk_size - sizeof(Res_value))) {
@@ -192,7 +197,7 @@ static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry(
192197
return base::unexpected(std::nullopt);
193198
}
194199
}
195-
return {};
200+
return entry.verified();
196201
}
197202

198203
LoadedPackage::iterator::iterator(const LoadedPackage* lp, size_t ti, size_t ei)
@@ -228,7 +233,7 @@ uint32_t LoadedPackage::iterator::operator*() const {
228233
entryIndex_);
229234
}
230235

231-
base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntry(
236+
base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntry(
232237
incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index) {
233238
base::expected<uint32_t, NullOrIOError> entry_offset = GetEntryOffset(type_chunk, entry_index);
234239
if (UNLIKELY(!entry_offset.has_value())) {
@@ -297,13 +302,14 @@ base::expected<uint32_t, NullOrIOError> LoadedPackage::GetEntryOffset(
297302
return value;
298303
}
299304

300-
base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntryFromOffset(
301-
incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset) {
305+
base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError>
306+
LoadedPackage::GetEntryFromOffset(incfs::verified_map_ptr<ResTable_type> type_chunk,
307+
uint32_t offset) {
302308
auto valid = VerifyResTableEntry(type_chunk, offset);
303309
if (UNLIKELY(!valid.has_value())) {
304310
return base::unexpected(valid.error());
305311
}
306-
return type_chunk.offset(offset + dtohl(type_chunk->entriesStart)).convert<ResTable_entry>();
312+
return valid;
307313
}
308314

309315
base::expected<std::monostate, IOError> LoadedPackage::CollectConfigurations(
@@ -400,7 +406,7 @@ base::expected<uint32_t, NullOrIOError> LoadedPackage::FindEntryByName(
400406
return base::unexpected(IOError::PAGES_MISSING);
401407
}
402408

403-
if (dtohl(entry->key.index) == static_cast<uint32_t>(*key_idx)) {
409+
if (entry->key() == static_cast<uint32_t>(*key_idx)) {
404410
// The package ID will be overridden by the caller (due to runtime assignment of package
405411
// IDs for shared libraries).
406412
return make_resid(0x00, *type_idx + type_id_offset_ + 1, res_idx);

libs/androidfw/ResourceTypes.cpp

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4415,20 +4415,14 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag
44154415
return err;
44164416
}
44174417

4418-
if ((dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) != 0) {
4418+
if (entry.entry->map_entry()) {
44194419
if (!mayBeBag) {
44204420
ALOGW("Requesting resource 0x%08x failed because it is complex\n", resID);
44214421
}
44224422
return BAD_VALUE;
44234423
}
44244424

4425-
const Res_value* value = reinterpret_cast<const Res_value*>(
4426-
reinterpret_cast<const uint8_t*>(entry.entry) + entry.entry->size);
4427-
4428-
outValue->size = dtohs(value->size);
4429-
outValue->res0 = value->res0;
4430-
outValue->dataType = value->dataType;
4431-
outValue->data = dtohl(value->data);
4425+
*outValue = entry.entry->value();
44324426

44334427
// The reference may be pointing to a resource in a shared library. These
44344428
// references have build-time generated package IDs. These ids may not match
@@ -4619,11 +4613,10 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
46194613
return err;
46204614
}
46214615

4622-
const uint16_t entrySize = dtohs(entry.entry->size);
4623-
const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
4624-
? dtohl(((const ResTable_map_entry*)entry.entry)->parent.ident) : 0;
4625-
const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
4626-
? dtohl(((const ResTable_map_entry*)entry.entry)->count) : 0;
4616+
const uint16_t entrySize = entry.entry->size();
4617+
const ResTable_map_entry* map_entry = entry.entry->map_entry();
4618+
const uint32_t parent = map_entry ? dtohl(map_entry->parent.ident) : 0;
4619+
const uint32_t count = map_entry ? dtohl(map_entry->count) : 0;
46274620

46284621
size_t N = count;
46294622

@@ -4687,7 +4680,7 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
46874680

46884681
// Now merge in the new attributes...
46894682
size_t curOff = (reinterpret_cast<uintptr_t>(entry.entry) - reinterpret_cast<uintptr_t>(entry.type))
4690-
+ dtohs(entry.entry->size);
4683+
+ entrySize;
46914684
const ResTable_map* map;
46924685
bag_entry* entries = (bag_entry*)(set+1);
46934686
size_t curEntry = 0;
@@ -5065,7 +5058,7 @@ uint32_t ResTable::findEntry(const PackageGroup* group, ssize_t typeIndex, const
50655058
continue;
50665059
}
50675060

5068-
if (dtohl(entry->key.index) == (size_t) *ei) {
5061+
if (entry->key() == (size_t) *ei) {
50695062
uint32_t resId = Res_MAKEID(group->id - 1, typeIndex, iter.index());
50705063
if (outTypeSpecFlags) {
50715064
Entry result;
@@ -6579,8 +6572,8 @@ status_t ResTable::getEntry(
65796572

65806573
const ResTable_entry* const entry = reinterpret_cast<const ResTable_entry*>(
65816574
reinterpret_cast<const uint8_t*>(bestType) + bestOffset);
6582-
if (dtohs(entry->size) < sizeof(*entry)) {
6583-
ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size));
6575+
if (entry->size() < sizeof(*entry)) {
6576+
ALOGW("ResTable_entry size 0x%zx is too small", entry->size());
65846577
return BAD_TYPE;
65856578
}
65866579

@@ -6591,7 +6584,7 @@ status_t ResTable::getEntry(
65916584
outEntry->specFlags = specFlags;
65926585
outEntry->package = bestPackage;
65936586
outEntry->typeStr = StringPoolRef(&bestPackage->typeStrings, actualTypeIndex - bestPackage->typeIdOffset);
6594-
outEntry->keyStr = StringPoolRef(&bestPackage->keyStrings, dtohl(entry->key.index));
6587+
outEntry->keyStr = StringPoolRef(&bestPackage->keyStrings, entry->key());
65956588
}
65966589
return NO_ERROR;
65976590
}
@@ -7662,7 +7655,7 @@ void ResTable::print(bool inclValues) const
76627655
continue;
76637656
}
76647657

7665-
uintptr_t esize = dtohs(ent->size);
7658+
uintptr_t esize = ent->size();
76667659
if ((esize&0x3) != 0) {
76677660
printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize);
76687661
continue;
@@ -7674,30 +7667,27 @@ void ResTable::print(bool inclValues) const
76747667
}
76757668

76767669
const Res_value* valuePtr = NULL;
7677-
const ResTable_map_entry* bagPtr = NULL;
7670+
const ResTable_map_entry* bagPtr = ent->map_entry();
76787671
Res_value value;
7679-
if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
7672+
if (bagPtr) {
76807673
printf("<bag>");
7681-
bagPtr = (const ResTable_map_entry*)ent;
76827674
} else {
7683-
valuePtr = (const Res_value*)
7684-
(((const uint8_t*)ent) + esize);
7685-
value.copyFrom_dtoh(*valuePtr);
7675+
value = ent->value();
76867676
printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
76877677
(int)value.dataType, (int)value.data,
76887678
(int)value.size, (int)value.res0);
76897679
}
76907680

7691-
if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
7681+
if (ent->flags() & ResTable_entry::FLAG_PUBLIC) {
76927682
printf(" (PUBLIC)");
76937683
}
76947684
printf("\n");
76957685

76967686
if (inclValues) {
7697-
if (valuePtr != NULL) {
7687+
if (bagPtr == NULL) {
76987688
printf(" ");
76997689
print_value(typeConfigs->package, value);
7700-
} else if (bagPtr != NULL) {
7690+
} else {
77017691
const int N = dtohl(bagPtr->count);
77027692
const uint8_t* baseMapPtr = (const uint8_t*)ent;
77037693
size_t mapOffset = esize;

libs/androidfw/TypeWrappers.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,11 @@ const ResTable_entry* TypeVariant::iterator::operator*() const {
9191
if (reinterpret_cast<uintptr_t>(entry) > containerEnd - sizeof(*entry)) {
9292
ALOGE("Entry offset at index %u points outside the Type's boundaries", mIndex);
9393
return NULL;
94-
} else if (reinterpret_cast<uintptr_t>(entry) + dtohs(entry->size) > containerEnd) {
94+
} else if (reinterpret_cast<uintptr_t>(entry) + entry->size() > containerEnd) {
9595
ALOGE("Entry at index %u extends beyond Type's boundaries", mIndex);
9696
return NULL;
97-
} else if (dtohs(entry->size) < sizeof(*entry)) {
98-
ALOGE("Entry at index %u is too small (%u)", mIndex, dtohs(entry->size));
97+
} else if (entry->size() < sizeof(*entry)) {
98+
ALOGE("Entry at index %u is too small (%zu)", mIndex, entry->size());
9999
return NULL;
100100
}
101101
return entry;

libs/androidfw/include/androidfw/LoadedArsc.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,14 @@ class LoadedPackage {
166166
base::expected<uint32_t, NullOrIOError> FindEntryByName(const std::u16string& type_name,
167167
const std::u16string& entry_name) const;
168168

169-
static base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntry(
170-
incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index);
169+
static base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError>
170+
GetEntry(incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index);
171171

172172
static base::expected<uint32_t, NullOrIOError> GetEntryOffset(
173173
incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index);
174174

175-
static base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntryFromOffset(
176-
incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset);
175+
static base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError>
176+
GetEntryFromOffset(incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset);
177177

178178
// Returns the string pool where type names are stored.
179179
const ResStringPool* GetTypeStringPool() const {

0 commit comments

Comments
 (0)