Search in sources :

Example 1 with ResTable_map

use of org.robolectric.res.android.ResourceTypes.ResTable_map in project robolectric by robolectric.

the class CppAssetManager2 method GetBag.

// Retrieves the best matching bag/map resource with ID `resid`.
// This method will resolve all parent references for this bag and merge keys with the child.
// To iterate over the keys, use the following idiom:
// 
// final ResolvedBag* bag = asset_manager.GetBag(id);
// if (bag != null) {
// for (auto iter = begin(bag); iter != end(bag); ++iter) {
// ...
// }
// }
ResolvedBag GetBag(int resid, List<Integer> child_resids) {
    // ATRACE_NAME("AssetManager::GetBag");
    ResolvedBag cached_iter = cached_bags_.get(resid);
    if (cached_iter != null) {
        return cached_iter;
    }
    final Ref<FindEntryResult> entryRef = new Ref<>(null);
    ApkAssetsCookie cookie = FindEntry(resid, (short) 0, /* density_override */
    false, /* stop_at_first_match */
    entryRef);
    if (cookie.intValue() == kInvalidCookie) {
        return null;
    }
    FindEntryResult entry = entryRef.get();
    // was intended to be a map.
    if (dtohs(entry.entry.size) < ResTable_map_entry.BASE_SIZEOF || (dtohs(entry.entry.flags) & ResourceTypes.ResTable_entry.FLAG_COMPLEX) == 0) {
        // Not a bag, nothing to do.
        return null;
    }
    // final ResTable_map_entry map = reinterpret_cast<final ResTable_map_entry*>(entry.entry);
    // final ResTable_map map_entry =
    // reinterpret_cast<final ResTable_map*>(reinterpret_cast<final byte*>(map) + map.size);
    // final ResTable_map map_entry_end = map_entry + dtohl(map.count);
    final ResTable_map_entry map = new ResTable_map_entry(entry.entry.myBuf(), entry.entry.myOffset());
    int curOffset = map.myOffset() + map.size;
    // = new ResTable_map(map.myBuf(), curOffset);
    ResTable_map map_entry = null;
    final int map_entry_end = curOffset + dtohl(map.count) * ResTable_map.SIZEOF;
    if (curOffset < map_entry_end) {
        map_entry = new ResTable_map(map.myBuf(), curOffset);
    }
    // Keep track of ids that have already been seen to prevent infinite loops caused by circular
    // dependencies between bags
    child_resids.add(resid);
    final Ref<Integer> parent_resid = new Ref<>(dtohl(map.parent.ident));
    if (parent_resid.get() == 0 || child_resids.contains(parent_resid.get())) {
        // There is no parent or that a circular dependency exist, meaning there is nothing to
        // inherit and we can do a simple copy of the entries in the map.
        final int entry_count = (map_entry_end - curOffset) / ResTable_map.SIZEOF;
        // util.unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
        // malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag.Entry))))};
        ResolvedBag new_bag = new ResolvedBag();
        ResolvedBag.Entry[] new_entry = new_bag.entries = new Entry[entry_count];
        int i = 0;
        while (curOffset < map_entry_end) {
            map_entry = new ResTable_map(map_entry.myBuf(), curOffset);
            final Ref<Integer> new_key = new Ref<>(dtohl(map_entry.name.ident));
            if (!is_internal_resid(new_key.get())) {
                // other data, which would be wrong to change via a lookup.
                if (entry.dynamic_ref_table.lookupResourceId(new_key) != NO_ERROR) {
                    System.err.println(String.format("Failed to resolve key 0x%08x in bag 0x%08x.", new_key.get(), resid));
                    return null;
                }
            }
            Entry new_entry_ = new_entry[i] = new Entry();
            new_entry_.cookie = cookie;
            new_entry_.key = new_key.get();
            new_entry_.key_pool = null;
            new_entry_.type_pool = null;
            new_entry_.style = resid;
            new_entry_.value = map_entry.value.copy();
            final Ref<Res_value> valueRef = new Ref<>(new_entry_.value);
            int err = entry.dynamic_ref_table.lookupResourceValue(valueRef);
            new_entry_.value = valueRef.get();
            if (err != NO_ERROR) {
                System.err.println(String.format("Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry_.value.dataType, new_entry_.value.data, new_key.get()));
                return null;
            }
            // ++new_entry;
            ++i;
            final int size = dtohs(map_entry.value.size);
            // curOffset += size + sizeof(*map)-sizeof(map->value);
            curOffset += size + ResTable_map.SIZEOF - Res_value.SIZEOF;
        }
        new_bag.type_spec_flags = entry.type_flags;
        new_bag.entry_count = entry_count;
        ResolvedBag result = new_bag;
        cached_bags_.put(resid, new_bag);
        return result;
    }
    // In case the parent is a dynamic reference, resolve it.
    entry.dynamic_ref_table.lookupResourceId(parent_resid);
    // Get the parent and do a merge of the keys.
    final ResolvedBag parent_bag = GetBag(parent_resid.get(), child_resids);
    if (parent_bag == null) {
        // Failed to get the parent that should exist.
        System.err.println(String.format("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid.get(), resid));
        return null;
    }
    // Create the max possible entries we can make. Once we construct the bag,
    // we will realloc to fit to size.
    final int max_count = parent_bag.entry_count + dtohl(map.count);
    // util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
    // malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry))))};
    ResolvedBag new_bag = new ResolvedBag();
    new_bag.entries = new Entry[max_count];
    final ResolvedBag.Entry[] new_entry = new_bag.entries;
    int newEntryIndex = 0;
    // const ResolvedBag::Entry* parent_entry = parent_bag->entries;
    int parentEntryIndex = 0;
    // final ResolvedBag.Entry parent_entry_end = parent_entry + parent_bag.entry_count;
    final int parentEntryCount = parent_bag.entry_count;
    // The keys are expected to be in sorted order. Merge the two bags.
    while (map_entry != null && curOffset != map_entry_end && parentEntryIndex != parentEntryCount) {
        map_entry = new ResTable_map(map_entry.myBuf(), curOffset);
        final Ref<Integer> child_keyRef = new Ref<>(dtohl(map_entry.name.ident));
        if (!is_internal_resid(child_keyRef.get())) {
            if (entry.dynamic_ref_table.lookupResourceId(child_keyRef) != NO_ERROR) {
                System.err.println(String.format("Failed to resolve key 0x%08x in bag 0x%08x.", child_keyRef.get(), resid));
                return null;
            }
        }
        int child_key = child_keyRef.get();
        Entry parent_entry = parent_bag.entries[parentEntryIndex];
        if (parent_entry == null) {
            parent_entry = new Entry();
        }
        if (child_key <= parent_entry.key) {
            // Use the child key if it comes before the parent
            // or is equal to the parent (overrides).
            Entry new_entry_ = new_entry[newEntryIndex] = new Entry();
            new_entry_.cookie = cookie;
            new_entry_.key = child_key;
            new_entry_.key_pool = null;
            new_entry_.type_pool = null;
            new_entry_.value = map_entry.value.copy();
            new_entry_.style = resid;
            final Ref<Res_value> valueRef = new Ref<>(new_entry_.value);
            int err = entry.dynamic_ref_table.lookupResourceValue(valueRef);
            new_entry_.value = valueRef.get();
            if (err != NO_ERROR) {
                System.err.println(String.format("Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry_.value.dataType, new_entry_.value.data, child_key));
                return null;
            }
            // ++map_entry;
            curOffset += map_entry.value.size + ResTable_map.SIZEOF - Res_value.SIZEOF;
        } else {
            // Take the parent entry as-is.
            // memcpy(new_entry, parent_entry, sizeof(*new_entry));
            new_entry[newEntryIndex] = parent_entry.copy();
        }
        if (child_key >= parent_entry.key) {
            // Move to the next parent entry if we used it or it was overridden.
            // ++parent_entry;
            ++parentEntryIndex;
        // parent_entry = parent_bag.entries[parentEntryIndex];
        }
        // Increment to the next entry to fill.
        // ++new_entry;
        ++newEntryIndex;
    }
    // Finish the child entries if they exist.
    while (map_entry != null && curOffset != map_entry_end) {
        map_entry = new ResTable_map(map_entry.myBuf(), curOffset);
        final Ref<Integer> new_key = new Ref<>(map_entry.name.ident);
        if (!is_internal_resid(new_key.get())) {
            if (entry.dynamic_ref_table.lookupResourceId(new_key) != NO_ERROR) {
                System.err.println(String.format("Failed to resolve key 0x%08x in bag 0x%08x.", new_key.get(), resid));
                return null;
            }
        }
        Entry new_entry_ = new_entry[newEntryIndex] = new Entry();
        new_entry_.cookie = cookie;
        new_entry_.key = new_key.get();
        new_entry_.key_pool = null;
        new_entry_.type_pool = null;
        new_entry_.value = map_entry.value.copy();
        new_entry_.style = resid;
        final Ref<Res_value> valueRef = new Ref<>(new_entry_.value);
        int err = entry.dynamic_ref_table.lookupResourceValue(valueRef);
        new_entry_.value = valueRef.get();
        if (err != NO_ERROR) {
            System.err.println(String.format("Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry_.value.dataType, new_entry_.value.data, new_key.get()));
            return null;
        }
        // ++map_entry;
        curOffset += map_entry.value.size + ResTable_map.SIZEOF - Res_value.SIZEOF;
        // ++new_entry;
        ++newEntryIndex;
    }
    // Finish the parent entries if they exist.
    while (parentEntryIndex != parent_bag.entry_count) {
        // Take the rest of the parent entries as-is.
        // final int num_entries_to_copy = parent_entry_end - parent_entry;
        // final int num_entries_to_copy = parent_bag.entry_count - parentEntryIndex;
        // memcpy(new_entry, parent_entry, num_entries_to_copy * sizeof(*new_entry));
        Entry parentEntry = parent_bag.entries[parentEntryIndex];
        new_entry[newEntryIndex] = parentEntry == null ? new Entry() : parentEntry.copy();
        // new_entry += num_entries_to_copy;
        ++newEntryIndex;
        ++parentEntryIndex;
    }
    // Resize the resulting array to fit.
    // final int actual_count = new_entry - new_bag.entries;
    final int actual_count = newEntryIndex;
    if (actual_count != max_count) {
        // new_bag.reset(reinterpret_cast<ResolvedBag*>(realloc(
        // new_bag.release(), sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry)))));
        Entry[] resizedEntries = new Entry[actual_count];
        System.arraycopy(new_bag.entries, 0, resizedEntries, 0, actual_count);
        new_bag.entries = resizedEntries;
    }
    // Combine flags from the parent and our own bag.
    new_bag.type_spec_flags = entry.type_flags | parent_bag.type_spec_flags;
    new_bag.entry_count = actual_count;
    ResolvedBag result2 = new_bag;
    // cached_bags_[resid] = std::move(new_bag);
    cached_bags_.put(resid, new_bag);
    return result2;
}
Also used : ResTable_map(org.robolectric.res.android.ResourceTypes.ResTable_map) Res_value(org.robolectric.res.android.ResourceTypes.Res_value) ResTable_map_entry(org.robolectric.res.android.ResourceTypes.ResTable_map_entry) DynamicPackageEntry(org.robolectric.res.android.LoadedArsc.DynamicPackageEntry) Entry(org.robolectric.res.android.CppAssetManager2.ResolvedBag.Entry)

Example 2 with ResTable_map

use of org.robolectric.res.android.ResourceTypes.ResTable_map in project robolectric by robolectric.

the class ResTable method getBagLocked.

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;
    // Bag not found, we need to compute it!
    if (!isTruthy(typeSet)) {
        // (bag_set**)calloc(NENTRY, sizeof(bag_set*));
        typeSet = new bag_set[NENTRY];
    // cacheEntry.cachedBags = typeSet;
    }
    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())) {
            // 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;
}
Also used : ResTable_map(org.robolectric.res.android.ResourceTypes.ResTable_map) Res_value(org.robolectric.res.android.ResourceTypes.Res_value) ResTable_map_entry(org.robolectric.res.android.ResourceTypes.ResTable_map_entry) ResTable_sparseTypeEntry(org.robolectric.res.android.ResourceTypes.ResTable_sparseTypeEntry)

Aggregations

ResTable_map (org.robolectric.res.android.ResourceTypes.ResTable_map)2 ResTable_map_entry (org.robolectric.res.android.ResourceTypes.ResTable_map_entry)2 Res_value (org.robolectric.res.android.ResourceTypes.Res_value)2 Entry (org.robolectric.res.android.CppAssetManager2.ResolvedBag.Entry)1 DynamicPackageEntry (org.robolectric.res.android.LoadedArsc.DynamicPackageEntry)1 ResTable_sparseTypeEntry (org.robolectric.res.android.ResourceTypes.ResTable_sparseTypeEntry)1