in resources/src/main/java/org/robolectric/res/android/LoadedArsc.java [544:785]
static LoadedPackage Load(Chunk chunk,
LoadedIdmap loaded_idmap,
boolean system, boolean load_as_shared_library) {
// ATRACE_NAME("LoadedPackage::Load");
LoadedPackage loaded_package = new LoadedPackage();
// typeIdOffset was added at some point, but we still must recognize apps built before this
// was added.
// constexpr int kMinPackageSize =
// sizeof(ResTable_package) - sizeof(ResTable_package.typeIdOffset);
final int kMinPackageSize = ResTable_package.SIZEOF - 4;
// ResTable_package header = chunk.header<ResTable_package, kMinPackageSize>();
ResTable_package header = chunk.asResTable_package(kMinPackageSize);
if (header == null) {
logError("RES_TABLE_PACKAGE_TYPE too small.");
return emptyBraces();
}
loaded_package.system_ = system;
loaded_package.package_id_ = dtohl(header.id);
if (loaded_package.package_id_ == 0 ||
(loaded_package.package_id_ == kAppPackageId && load_as_shared_library)) {
// Package ID of 0 means this is a shared library.
loaded_package.dynamic_ = true;
}
if (loaded_idmap != null) {
// 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 (header.header.headerSize >= ResTable_package.SIZEOF) {
int type_id_offset = dtohl(header.typeIdOffset);
// if (type_id_offset > std.numeric_limits<uint8_t>.max()) {
if (type_id_offset > 255) {
logError("RES_TABLE_PACKAGE_TYPE type ID offset too large.");
return emptyBraces();
}
loaded_package.type_id_offset_ = type_id_offset;
}
loaded_package.package_name_ = Util
.ReadUtf16StringFromDevice(header.name, header.name.length);
// A map of TypeSpec builders, each associated with an type index.
// We use these to accumulate the set of Types available for a TypeSpec, and later build a single,
// contiguous block of memory that holds all the Types together with the TypeSpec.
Map<Integer, TypeSpecPtrBuilder> type_builder_map = new HashMap<>();
Chunk.Iterator iter = new Iterator(chunk.data_ptr(), chunk.data_size());
while (iter.HasNext()) {
Chunk child_chunk = iter.Next();
switch (child_chunk.type()) {
case RES_STRING_POOL_TYPE: {
// uintptr_t pool_address =
// reinterpret_cast<uintptr_t>(child_chunk.header<ResChunk_header>());
// uintptr_t header_address = reinterpret_cast<uintptr_t>(header);
int pool_address =
child_chunk.myOffset();
int header_address = header.myOffset();
if (pool_address == header_address + dtohl(header.typeStrings)) {
// This string pool is the type string pool.
int err = loaded_package.type_string_pool_.setTo(
child_chunk.myBuf(), child_chunk.myOffset(), child_chunk.size(), false);
if (err != NO_ERROR) {
logError("RES_STRING_POOL_TYPE for types corrupt.");
return emptyBraces();
}
} else if (pool_address == header_address + dtohl(header.keyStrings)) {
// This string pool is the key string pool.
int err = loaded_package.key_string_pool_.setTo(
child_chunk.myBuf(), child_chunk.myOffset(), child_chunk.size(), false);
if (err != NO_ERROR) {
logError("RES_STRING_POOL_TYPE for keys corrupt.");
return emptyBraces();
}
} else {
logWarning("Too many RES_STRING_POOL_TYPEs found in RES_TABLE_PACKAGE_TYPE.");
}
} break;
case RES_TABLE_TYPE_SPEC_TYPE: {
ResTable_typeSpec type_spec = new ResTable_typeSpec(child_chunk.myBuf(),
child_chunk.myOffset());
if (type_spec == null) {
logError("RES_TABLE_TYPE_SPEC_TYPE too small.");
return emptyBraces();
}
if (type_spec.id == 0) {
logError("RES_TABLE_TYPE_SPEC_TYPE has invalid ID 0.");
return emptyBraces();
}
// if (loaded_package.type_id_offset_ + static_cast<int>(type_spec.id) >
// std.numeric_limits<uint8_t>.max()) {
if (loaded_package.type_id_offset_ + type_spec.id > 255) {
logError("RES_TABLE_TYPE_SPEC_TYPE has out of range ID.");
return emptyBraces();
}
// The data portion of this chunk contains entry_count 32bit entries,
// each one representing a set of flags.
// Here we only validate that the chunk is well formed.
int entry_count = dtohl(type_spec.entryCount);
// There can only be 2^16 entries in a type, because that is the ID
// space for entries (EEEE) in the resource ID 0xPPTTEEEE.
// if (entry_count > std.numeric_limits<short>.max()) {
if (entry_count > 0xffff) {
logError("RES_TABLE_TYPE_SPEC_TYPE has too many entries (" + entry_count + ").");
return emptyBraces();
}
if (entry_count * 4 /*sizeof(int)*/ > chunk.data_size()) {
logError("RES_TABLE_TYPE_SPEC_TYPE too small to hold entries.");
return emptyBraces();
}
// If this is an overlay, associate the mapping of this type to the target type
// from the IDMAP.
IdmapEntry_header idmap_entry_header = null;
if (loaded_idmap != null) {
idmap_entry_header = loaded_idmap.GetEntryMapForType(type_spec.id);
}
TypeSpecPtrBuilder builder_ptr = type_builder_map.get(type_spec.id - 1);
if (builder_ptr == null) {
// builder_ptr = util.make_unique<TypeSpecPtrBuilder>(type_spec, idmap_entry_header);
builder_ptr = new TypeSpecPtrBuilder(type_spec, idmap_entry_header);
type_builder_map.put(type_spec.id - 1, builder_ptr);
} else {
logWarning(String.format("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x",
type_spec.id));
}
} break;
case RES_TABLE_TYPE_TYPE: {
// ResTable_type type = child_chunk.header<ResTable_type, kResTableTypeMinSize>();
ResTable_type type = child_chunk.asResTable_type(kResTableTypeMinSize);
if (type == null) {
logError("RES_TABLE_TYPE_TYPE too small.");
return emptyBraces();
}
if (!VerifyResTableType(type)) {
return emptyBraces();
}
// Type chunks must be preceded by their TypeSpec chunks.
TypeSpecPtrBuilder builder_ptr = type_builder_map.get(type.id - 1);
if (builder_ptr != null) {
builder_ptr.AddType(type);
} else {
logError(String.format(
"RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.",
type.id));
return emptyBraces();
}
} break;
case RES_TABLE_LIBRARY_TYPE: {
ResTable_lib_header lib = child_chunk.asResTable_lib_header();
if (lib == null) {
logError("RES_TABLE_LIBRARY_TYPE too small.");
return emptyBraces();
}
if (child_chunk.data_size() / ResTable_lib_entry.SIZEOF < dtohl(lib.count)) {
logError("RES_TABLE_LIBRARY_TYPE too small to hold entries.");
return emptyBraces();
}
// loaded_package.dynamic_package_map_.reserve(dtohl(lib.count));
// ResTable_lib_entry entry_begin =
// reinterpret_cast<ResTable_lib_entry*>(child_chunk.data_ptr());
ResTable_lib_entry entry_begin =
child_chunk.asResTable_lib_entry();
// ResTable_lib_entry entry_end = entry_begin + dtohl(lib.count);
// for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) {
for (ResTable_lib_entry entry_iter = entry_begin;
entry_iter.myOffset() != entry_begin.myOffset() + dtohl(lib.count);
entry_iter = new ResTable_lib_entry(entry_iter.myBuf(), entry_iter.myOffset() + ResTable_lib_entry.SIZEOF)) {
String package_name =
Util.ReadUtf16StringFromDevice(entry_iter.packageName,
entry_iter.packageName.length);
if (dtohl(entry_iter.packageId) >= 255) {
logError(String.format(
"Package ID %02x in RES_TABLE_LIBRARY_TYPE too large for package '%s'.",
dtohl(entry_iter.packageId), package_name));
return emptyBraces();
}
// loaded_package.dynamic_package_map_.emplace_back(std.move(package_name),
// dtohl(entry_iter.packageId));
loaded_package.dynamic_package_map_.add(new DynamicPackageEntry(package_name,
dtohl(entry_iter.packageId)));
}
} break;
default:
logWarning(String.format("Unknown chunk type '%02x'.", chunk.type()));
break;
}
}
if (iter.HadError()) {
logError(iter.GetLastError());
if (iter.HadFatalError()) {
return emptyBraces();
}
}
// Flatten and construct the TypeSpecs.
for (Entry<Integer, TypeSpecPtrBuilder> entry : type_builder_map.entrySet()) {
byte type_idx = (byte) entry.getKey().byteValue();
TypeSpec type_spec_ptr = entry.getValue().Build();
if (type_spec_ptr == null) {
logError("Too many type configurations, overflow detected.");
return emptyBraces();
}
// We only add the type to the package if there is no IDMAP, or if the type is
// overlaying something.
if (loaded_idmap == null || type_spec_ptr.idmap_entries != null) {
// If this is an overlay, insert it at the target type ID.
if (type_spec_ptr.idmap_entries != null) {
type_idx = (byte) (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_.put((int) type_idx, type_spec_ptr);
}
}
// return std.move(loaded_package);
return loaded_package;
}