in libresource/ResourceTypes.cpp [3934:4209]
ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
uint32_t* outTypeSpecFlags) const
{
if (mError != NO_ERROR) {
return mError;
}
const ssize_t p = getResourcePackageIndex(resID);
const int t = Res_GETTYPE(resID);
const int e = Res_GETENTRY(resID);
if (p < 0) {
ALOGW("Invalid package identifier when getting bag for resource number 0x%08x", resID);
return BAD_INDEX;
}
if (t < 0) {
ALOGW("No type identifier when getting bag for resource number 0x%08x", resID);
return BAD_INDEX;
}
//printf("Get bag: id=0x%08x, p=%d, t=%d\n", resID, p, t);
PackageGroup* const grp = mPackageGroups[p];
if (grp == NULL) {
ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID);
return BAD_INDEX;
}
const TypeList& typeConfigs = grp->types[t];
if (typeConfigs.isEmpty()) {
ALOGW("Type identifier 0x%x does not exist.", t+1);
return BAD_INDEX;
}
const size_t NENTRY = typeConfigs[0]->entryCount;
if (e >= (int)NENTRY) {
ALOGW("Entry identifier 0x%x is larger than entry count 0x%x",
e, (int)typeConfigs[0]->entryCount);
return BAD_INDEX;
}
// First see if we've already computed this bag...
if (grp->bags) {
bag_set** typeSet = grp->bags->get(t);
if (typeSet) {
bag_set* set = typeSet[e];
if (set) {
if (set != (bag_set*)0xFFFFFFFF) {
if (outTypeSpecFlags != NULL) {
*outTypeSpecFlags = set->typeSpecFlags;
}
*outBag = (bag_entry*)(set+1);
if (kDebugTableSuperNoisy) {
ALOGI("Found existing bag for: 0x%x\n", resID);
}
return set->numAttrs;
}
ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.",
resID);
return BAD_INDEX;
}
}
}
// Bag not found, we need to compute it!
if (!grp->bags) {
grp->bags = new ByteBucketArray<bag_set**>();
if (!grp->bags) return NO_MEMORY;
}
bag_set** typeSet = grp->bags->get(t);
if (!typeSet) {
typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*));
if (!typeSet) return NO_MEMORY;
grp->bags->set(t, typeSet);
}
// Mark that we are currently working on this one.
typeSet[e] = (bag_set*)0xFFFFFFFF;
if (kDebugTableNoisy) {
ALOGI("Building bag: %x\n", resID);
}
// Now collect all bag attributes
Entry entry;
status_t err = getEntry(grp, t, e, &mParams, &entry);
if (err != NO_ERROR) {
return err;
}
const uint16_t entrySize = dtohs(entry.entry->size);
const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
? dtohl(((const ResTable_map_entry*)entry.entry)->parent.ident) : 0;
const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
? dtohl(((const ResTable_map_entry*)entry.entry)->count) : 0;
size_t N = count;
if (kDebugTableNoisy) {
ALOGI("Found map: size=%x parent=%x count=%d\n", entrySize, parent, count);
// If this map inherits from another, we need to start
// with its parent's values. Otherwise start out empty.
ALOGI("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", entrySize, parent);
}
// This is what we are building.
bag_set* set = NULL;
if (parent) {
uint32_t resolvedParent = parent;
// Bags encode a parent reference without using the standard
// Res_value structure. That means we must always try to
// resolve a parent reference in case it is actually a
// TYPE_DYNAMIC_REFERENCE.
status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent);
if (err != NO_ERROR) {
ALOGE("Failed resolving bag parent id 0x%08x", parent);
return UNKNOWN_ERROR;
}
const bag_entry* parentBag;
uint32_t parentTypeSpecFlags = 0;
const ssize_t NP = getBagLocked(resolvedParent, &parentBag, &parentTypeSpecFlags);
const size_t NT = ((NP >= 0) ? NP : 0) + N;
set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT);
if (set == NULL) {
return NO_MEMORY;
}
if (NP > 0) {
memcpy(set+1, parentBag, NP*sizeof(bag_entry));
set->numAttrs = NP;
if (kDebugTableNoisy) {
ALOGI("Initialized new bag with %zd inherited attributes.\n", NP);
}
} else {
if (kDebugTableNoisy) {
ALOGI("Initialized new bag with no inherited attributes.\n");
}
set->numAttrs = 0;
}
set->availAttrs = NT;
set->typeSpecFlags = parentTypeSpecFlags;
} else {
set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N);
if (set == NULL) {
return NO_MEMORY;
}
set->numAttrs = 0;
set->availAttrs = N;
set->typeSpecFlags = 0;
}
set->typeSpecFlags |= entry.specFlags;
// Now merge in the new attributes...
size_t curOff = (reinterpret_cast<uintptr_t>(entry.entry) - reinterpret_cast<uintptr_t>(entry.type))
+ dtohs(entry.entry->size);
const ResTable_map* map;
bag_entry* entries = (bag_entry*)(set+1);
size_t curEntry = 0;
uint32_t pos = 0;
if (kDebugTableNoisy) {
ALOGI("Starting with set %p, entries=%p, avail=%zu\n", set, entries, set->availAttrs);
}
while (pos < count) {
if (kDebugTableNoisy) {
ALOGI("Now at %p\n", (void*)curOff);
}
if (curOff > (dtohl(entry.type->header.size)-sizeof(ResTable_map))) {
ALOGW("ResTable_map at %d is beyond type chunk data %d",
(int)curOff, dtohl(entry.type->header.size));
return BAD_TYPE;
}
map = (const ResTable_map*)(((const uint8_t*)entry.type) + curOff);
N++;
uint32_t newName = htodl(map->name.ident);
if (!Res_INTERNALID(newName)) {
// Attributes don't have a resource id as the name. They specify
// other data, which would be wrong to change via a lookup.
if (grp->dynamicRefTable.lookupResourceId(&newName) != NO_ERROR) {
ALOGE("Failed resolving ResTable_map name at %d with ident 0x%08x",
(int) curOff, (int) newName);
return UNKNOWN_ERROR;
}
}
bool isInside;
uint32_t oldName = 0;
while ((isInside=(curEntry < set->numAttrs))
&& (oldName=entries[curEntry].map.name.ident) < newName) {
if (kDebugTableNoisy) {
ALOGI("#%zu: Keeping existing attribute: 0x%08x\n",
curEntry, entries[curEntry].map.name.ident);
}
curEntry++;
}
if ((!isInside) || oldName != newName) {
// This is a new attribute... figure out what to do with it.
if (set->numAttrs >= set->availAttrs) {
// Need to alloc more memory...
const size_t newAvail = set->availAttrs+N;
set = (bag_set*)realloc(set,
sizeof(bag_set)
+ sizeof(bag_entry)*newAvail);
if (set == NULL) {
return NO_MEMORY;
}
set->availAttrs = newAvail;
entries = (bag_entry*)(set+1);
if (kDebugTableNoisy) {
ALOGI("Reallocated set %p, entries=%p, avail=%zu\n",
set, entries, set->availAttrs);
}
}
if (isInside) {
// Going in the middle, need to make space.
memmove(entries+curEntry+1, entries+curEntry,
sizeof(bag_entry)*(set->numAttrs-curEntry));
set->numAttrs++;
}
if (kDebugTableNoisy) {
ALOGI("#%zu: Inserting new attribute: 0x%08x\n", curEntry, newName);
}
} else {
if (kDebugTableNoisy) {
ALOGI("#%zu: Replacing existing attribute: 0x%08x\n", curEntry, oldName);
}
}
bag_entry* cur = entries+curEntry;
cur->stringBlock = entry.package->header->index;
cur->map.name.ident = newName;
cur->map.value.copyFrom_dtoh(map->value);
status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value);
if (err != NO_ERROR) {
ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data);
return UNKNOWN_ERROR;
}
if (kDebugTableNoisy) {
ALOGI("Setting entry #%zu %p: block=%zd, name=0x%08d, type=%d, data=0x%08x\n",
curEntry, cur, cur->stringBlock, cur->map.name.ident,
cur->map.value.dataType, cur->map.value.data);
}
// On to the next!
curEntry++;
pos++;
const size_t size = dtohs(map->value.size);
curOff += size + sizeof(*map)-sizeof(map->value);
};
if (curEntry > set->numAttrs) {
set->numAttrs = curEntry;
}
// And this is it...
typeSet[e] = set;
if (set) {
if (outTypeSpecFlags != NULL) {
*outTypeSpecFlags = set->typeSpecFlags;
}
*outBag = (bag_entry*)(set+1);
if (kDebugTableNoisy) {
ALOGI("Returning %zu attrs\n", set->numAttrs);
}
return set->numAttrs;
}
return BAD_INDEX;
}