Search in sources :

Example 16 with ResolvedBag

use of org.robolectric.res.android.CppAssetManager2.ResolvedBag 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 17 with ResolvedBag

use of org.robolectric.res.android.CppAssetManager2.ResolvedBag in project robolectric by robolectric.

the class ShadowArscAssetManager10 method nativeGetResourceArray.

// static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
// jintArray out_data) {
@Implementation(minSdk = P)
protected static int nativeGetResourceArray(long ptr, @ArrayRes int resid, @NonNull int[] out_data) {
    CppAssetManager2 assetmanager = AssetManagerFromLong(ptr);
    ResolvedBag bag = assetmanager.GetBag(resid);
    if (bag == null) {
        return -1;
    }
    int out_data_length = out_data.length;
    if ((int) (bag.entry_count) > out_data_length * STYLE_NUM_ENTRIES) {
        throw new IllegalArgumentException("Input array is not large enough");
    }
    int[] buffer = // reinterpret_cast<int*>(env.GetPrimitiveArrayCritical(out_data, null));
    out_data;
    if (buffer == null) {
        return -1;
    }
    int[] cursor = buffer;
    for (int i = 0; i < bag.entry_count; i++) {
        ResolvedBag.Entry entry = bag.entries[i];
        final Ref<Res_value> value = new Ref<>(entry.value);
        final Ref<ResTable_config> selected_config = new Ref<>(new ResTable_config());
        selected_config.get().density = 0;
        final Ref<Integer> flags = new Ref<>(bag.type_spec_flags);
        final Ref<Integer> ref = new Ref<>(0);
        ApkAssetsCookie cookie = assetmanager.ResolveReference(entry.cookie, value, selected_config, flags, ref);
        if (cookie.intValue() == kInvalidCookie) {
            // env.ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT);
            return -1;
        }
        // Deal with the special @null value -- it turns back to TYPE_NULL.
        if (value.get().dataType == Res_value.TYPE_REFERENCE && value.get().data == 0) {
            value.set(Res_value.NULL_VALUE);
        }
        int offset = i * STYLE_NUM_ENTRIES;
        cursor[offset + STYLE_TYPE] = (int) (value.get().dataType);
        cursor[offset + STYLE_DATA] = (int) (value.get().data);
        cursor[offset + STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
        cursor[offset + STYLE_RESOURCE_ID] = (int) (ref.get());
        cursor[offset + STYLE_CHANGING_CONFIGURATIONS] = (int) (flags.get());
        cursor[offset + STYLE_DENSITY] = (int) (selected_config.get().density);
    // cursor += STYLE_NUM_ENTRIES;
    }
    // env.ReleasePrimitiveArrayCritical(out_data, buffer, 0);
    return (int) (bag.entry_count);
}
Also used : CppAssetManager2(org.robolectric.res.android.CppAssetManager2) ResolvedBag(org.robolectric.res.android.CppAssetManager2.ResolvedBag) Res_value(org.robolectric.res.android.ResourceTypes.Res_value) ResTable_config(org.robolectric.res.android.ResTable_config) Ref(org.robolectric.res.android.Ref) ApkAssetsCookie(org.robolectric.res.android.ApkAssetsCookie) Implementation(org.robolectric.annotation.Implementation)

Example 18 with ResolvedBag

use of org.robolectric.res.android.CppAssetManager2.ResolvedBag in project robolectric by robolectric.

the class ShadowArscAssetManager10 method nativeGetStyleAttributes.

// static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid)
// {
@Implementation(minSdk = P)
@Nullable
@AttrRes
protected static int[] nativeGetStyleAttributes(long ptr, @StyleRes int resid) {
    CppAssetManager2 assetmanager = AssetManagerFromLong(ptr);
    ResolvedBag bag = assetmanager.GetBag(resid);
    if (bag == null) {
        return null;
    }
    int[] array = new int[bag.entry_count];
    for (int i = 0; i < bag.entry_count; i++) {
        int attr_resid = bag.entries[i].key;
        // env.SetIntArrayRegion(array, i, 1, &attr_resid);
        array[i] = attr_resid;
    }
    return array;
}
Also used : CppAssetManager2(org.robolectric.res.android.CppAssetManager2) ResolvedBag(org.robolectric.res.android.CppAssetManager2.ResolvedBag) AttrRes(android.annotation.AttrRes) Implementation(org.robolectric.annotation.Implementation) Nullable(android.annotation.Nullable)

Example 19 with ResolvedBag

use of org.robolectric.res.android.CppAssetManager2.ResolvedBag in project robolectric by robolectric.

the class ShadowArscAssetManager10 method nativeGetResourceStringArrayInfo.

// static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
// jint resid) {
@Implementation(minSdk = P)
@Nullable
protected static int[] nativeGetResourceStringArrayInfo(long ptr, @ArrayRes int resid) {
    CppAssetManager2 assetmanager = AssetManagerFromLong(ptr);
    ResolvedBag bag = assetmanager.GetBag(resid);
    if (bag == null) {
        return null;
    }
    int[] array = new int[bag.entry_count * 2];
    // if (array == null) {
    // return null;
    // }
    // reinterpret_cast<int*>(env.GetPrimitiveArrayCritical(array, null));
    int[] buffer = array;
    for (int i = 0; i < bag.entry_count; i++) {
        ResolvedBag.Entry entry = bag.entries[i];
        final Ref<Res_value> value = new Ref<>(entry.value);
        final Ref<ResTable_config> selected_config = new Ref<>(null);
        final Ref<Integer> flags = new Ref<>(0);
        final Ref<Integer> ref = new Ref<>(0);
        ApkAssetsCookie cookie = assetmanager.ResolveReference(entry.cookie, value, selected_config, flags, ref);
        if (cookie.intValue() == kInvalidCookie) {
            // env.ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
            return null;
        }
        int string_index = -1;
        if (value.get().dataType == Res_value.TYPE_STRING) {
            string_index = (int) (value.get().data);
        }
        buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie);
        buffer[(i * 2) + 1] = string_index;
    }
    // env.ReleasePrimitiveArrayCritical(array, buffer, 0);
    return array;
}
Also used : CppAssetManager2(org.robolectric.res.android.CppAssetManager2) ResolvedBag(org.robolectric.res.android.CppAssetManager2.ResolvedBag) Res_value(org.robolectric.res.android.ResourceTypes.Res_value) ResTable_config(org.robolectric.res.android.ResTable_config) Ref(org.robolectric.res.android.Ref) ApkAssetsCookie(org.robolectric.res.android.ApkAssetsCookie) Implementation(org.robolectric.annotation.Implementation) Nullable(android.annotation.Nullable)

Aggregations

ResolvedBag (org.robolectric.res.android.CppAssetManager2.ResolvedBag)18 Res_value (org.robolectric.res.android.ResourceTypes.Res_value)15 Implementation (org.robolectric.annotation.Implementation)14 CppAssetManager2 (org.robolectric.res.android.CppAssetManager2)14 ApkAssetsCookie (org.robolectric.res.android.ApkAssetsCookie)10 Ref (org.robolectric.res.android.Ref)10 ResTable_config (org.robolectric.res.android.ResTable_config)10 Nullable (android.annotation.Nullable)8 Entry (org.robolectric.res.android.CppAssetManager2.ResolvedBag.Entry)5 AttrRes (android.annotation.AttrRes)2 CppApkAssets (org.robolectric.res.android.CppApkAssets)2 ResStringPool (org.robolectric.res.android.ResStringPool)2 DynamicPackageEntry (org.robolectric.res.android.LoadedArsc.DynamicPackageEntry)1 ResTable_map (org.robolectric.res.android.ResourceTypes.ResTable_map)1 ResTable_map_entry (org.robolectric.res.android.ResourceTypes.ResTable_map_entry)1