in resources/src/main/java/org/robolectric/res/android/ResTable.java [2715:2992]
public int getBagLocked(int resID, Ref<bag_entry[]> outBag, Ref<Integer> outTypeSpecFlags) {
if (mError != NO_ERROR) {
return mError;
}
final int p = getResourcePackageIndex(resID);
final int t = Res_GETTYPE(resID);
final 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 grp = mPackageGroups.get(p);
if (grp == NULL) {
ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID);
return BAD_INDEX;
}
final List<Type> typeConfigs = getOrDefault(grp.types, t, Collections.emptyList());
if (typeConfigs.isEmpty()) {
ALOGW("Type identifier 0x%x does not exist.", t+1);
return BAD_INDEX;
}
final int NENTRY = typeConfigs.get(0).entryCount;
if (e >= (int)NENTRY) {
ALOGW("Entry identifier 0x%x is larger than entry count 0x%x",
e, (int)typeConfigs.get(0).entryCount);
return BAD_INDEX;
}
// First see if we've already computed this bag...
TypeCacheEntry cacheEntry = grp.typeCacheEntries.editItemAt(t);
bag_set[] typeSet = cacheEntry.cachedBags;
// todo cache
// if (isTruthy(typeSet)) {
// bag_set set = typeSet[e];
// if (isTruthy(set)) {
// if (set != (bag_set) 0xFFFFFFFF){
// if (set != SENTINEL_BAG_SET){
// if (outTypeSpecFlags != NULL) {
// outTypeSpecFlags.set(set.typeSpecFlags);
// }
// outBag.set((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 (!isTruthy(typeSet)) {
typeSet = new bag_set[NENTRY]; // (bag_set**)calloc(NENTRY, sizeof(bag_set*));
//cacheEntry.cachedBags = typeSet;
}
//
// // Mark that we are currently working on this one.
// typeSet[e] = (bag_set*)0xFFFFFFFF;
// typeSet[e] = SENTINEL_BAG_SET;
if (kDebugTableNoisy) {
ALOGI("Building bag: %x\n", resID);
}
// Now collect all bag attributes
Entry entry = new Entry();
int err = getEntry(grp, t, e, mParams, entry);
if (err != NO_ERROR) {
return err;
}
final short 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;
ResTable_map_entry mapEntry = entrySize >= ResTable_map_entry.BASE_SIZEOF ?
new ResTable_map_entry(entry.entry.myBuf(), entry.entry.myOffset()) : null;
final int parent = mapEntry != null ? dtohl(mapEntry.parent.ident) : 0;
final int count = mapEntry != null ? dtohl(mapEntry.count) : 0;
int 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;
if (isTruthy(parent)) {
final Ref<Integer> resolvedParent = new Ref<>(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.
err = grp.dynamicRefTable.lookupResourceId(resolvedParent);
if (err != NO_ERROR) {
ALOGE("Failed resolving bag parent id 0x%08x", parent);
return UNKNOWN_ERROR;
}
final Ref<bag_entry[]> parentBag = new Ref<>(null);
final Ref<Integer> parentTypeSpecFlags = new Ref<>(0);
final int NP = getBagLocked(resolvedParent.get(), parentBag, parentTypeSpecFlags);
final int NT = ((NP >= 0) ? NP : 0) + N;
set = new bag_set(NT);
if (NP > 0) {
set.copyFrom(parentBag.get(), NP);
set.numAttrs = NP;
if (kDebugTableNoisy) {
ALOGI("Initialized new bag with %d 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.get();
} else {
set = new bag_set(N);
set.numAttrs = 0;
set.availAttrs = N;
set.typeSpecFlags = 0;
}
set.typeSpecFlags |= entry.specFlags;
// Now merge in the new attributes...
// int curOff = (reinterpret_cast<uintptr_t>(entry.entry) - reinterpret_cast<uintptr_t>(entry.type))
// + dtohs(entry.entry.size);
int curOff = entry.entry.myOffset() - entry.type.myOffset() + entry.entry.size;
ResTable_map map;
// bag_entry* entries = (bag_entry*)(set+1);
bag_entry[] entries = set.bag_entries;
int curEntry = 0;
int pos = 0;
if (kDebugTableNoisy) {
ALOGI("Starting with set %s, entries=%s, avail=0x%x\n", set, entries, set.availAttrs);
}
while (pos < count) {
if (kDebugTableNoisy) {
// ALOGI("Now at %s\n", curOff);
ALOGI("Now at %s\n", curEntry);
}
if (curOff > (dtohl(entry.type.header.size)- ResTable_map.SIZEOF)) {
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);
map = new ResTable_map(entry.type.myBuf(), entry.type.myOffset() + curOff);
N++;
final Ref<Integer> newName = new Ref<>(htodl(map.name.ident));
if (!Res_INTERNALID(newName.get())) {
// 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) curEntry, (int) newName.get());
return UNKNOWN_ERROR;
}
}
boolean isInside;
int oldName = 0;
while ((isInside=(curEntry < set.numAttrs))
&& (oldName=entries[curEntry].map.name.ident) < newName.get()) {
if (kDebugTableNoisy) {
ALOGI("#0x%x: Keeping existing attribute: 0x%08x\n",
curEntry, entries[curEntry].map.name.ident);
}
curEntry++;
}
if ((!isInside) || oldName != newName.get()) {
// This is a new attribute... figure out what to do with it.
if (set.numAttrs >= set.availAttrs) {
// Need to alloc more memory...
final int newAvail = set.availAttrs+N;
// set = (bag_set[])realloc(set,
// sizeof(bag_set)
// + sizeof(bag_entry)*newAvail);
set.resizeBagEntries(newAvail);
set.availAttrs = newAvail;
// entries = (bag_entry*)(set+1);
entries = set.bag_entries;
if (kDebugTableNoisy) {
ALOGI("Reallocated set %s, entries=%s, avail=0x%x\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));
System.arraycopy(entries, curEntry, entries, curEntry + 1, set.numAttrs - curEntry);
entries[curEntry] = null;
set.numAttrs++;
}
if (kDebugTableNoisy) {
ALOGI("#0x%x: Inserting new attribute: 0x%08x\n", curEntry, newName.get());
}
} else {
if (kDebugTableNoisy) {
ALOGI("#0x%x: Replacing existing attribute: 0x%08x\n", curEntry, oldName);
}
}
bag_entry cur = entries[curEntry];
if (cur == null) {
cur = entries[curEntry] = new bag_entry();
}
cur.stringBlock = entry._package_.header.index;
cur.map.name.ident = newName.get();
// cur->map.value.copyFrom_dtoh(map->value);
cur.map.value = map.value;
final Ref<Res_value> valueRef = new Ref<>(cur.map.value);
err = grp.dynamicRefTable.lookupResourceValue(valueRef);
cur.map.value = map.value = valueRef.get();
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 #0x%x %s: block=%d, 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++;
final int size = dtohs(map.value.size);
// curOff += size + sizeof(*map)-sizeof(map->value);
curOff += size + ResTable_map.SIZEOF-Res_value.SIZEOF;
};
if (curEntry > set.numAttrs) {
set.numAttrs = curEntry;
}
// And this is it...
typeSet[e] = set;
if (isTruthy(set)) {
if (outTypeSpecFlags != NULL) {
outTypeSpecFlags.set(set.typeSpecFlags);
}
outBag.set(set.bag_entries);
if (kDebugTableNoisy) {
ALOGI("Returning 0x%x attrs\n", set.numAttrs);
}
return set.numAttrs;
}
return BAD_INDEX;
}