Search in sources :

Example 1 with ResTable_entry

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

the class CppAssetManager2 method FindEntry.

// Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple
// Res_value, or a complex map/bag type. If successful, it is available in `out_entry`.
// Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with
// the ApkAssets in which the entry was found.
// 
// `density_override` overrides the density of the current configuration when doing a search.
// 
// When `stop_at_first_match` is true, the first match found is selected and the search
// terminates. This is useful for methods that just look up the name of a resource and don't
// care about the value. In this case, the value of `FindEntryResult::type_flags` is incomplete
// and should not be used.
// 
// NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly
// bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds.
// ApkAssetsCookie FindEntry(int resid, short density_override, boolean stop_at_first_match,
// LoadedArscEntry* out_entry, ResTable_config out_selected_config,
// int* out_flags);
private ApkAssetsCookie FindEntry(int resid, short density_override, boolean stop_at_first_match, final Ref<FindEntryResult> out_entry) {
    ATRACE_CALL();
    // Might use this if density_override != 0.
    ResTable_config density_override_config;
    // Select our configuration or generate a density override configuration.
    ResTable_config desired_config = configuration_;
    if (density_override != 0 && density_override != configuration_.density) {
        density_override_config = configuration_;
        density_override_config.density = density_override;
        desired_config = density_override_config;
    }
    if (!is_valid_resid(resid)) {
        System.err.println(String.format("Invalid ID 0x%08x.", resid));
        return K_INVALID_COOKIE;
    }
    final int package_id = get_package_id(resid);
    final int type_idx = (byte) (get_type_id(resid) - 1);
    final int entry_idx = get_entry_id(resid);
    final byte package_idx = package_ids_[package_id];
    if (package_idx == (byte) 0xff) {
        System.err.println(String.format("No package ID %02x found for ID 0x%08x.", package_id, resid));
        return K_INVALID_COOKIE;
    }
    final PackageGroup package_group = package_groups_.get(package_idx);
    final int package_count = package_group.packages_.size();
    ApkAssetsCookie best_cookie = K_INVALID_COOKIE;
    LoadedPackage best_package = null;
    ResTable_type best_type = null;
    ResTable_config best_config = null;
    ResTable_config best_config_copy;
    int best_offset = 0;
    int type_flags = 0;
    // If desired_config is the same as the set configuration, then we can use our filtered list
    // and we don't need to match the configurations, since they already matched.
    boolean use_fast_path = desired_config == configuration_;
    for (int pi = 0; pi < package_count; pi++) {
        ConfiguredPackage loaded_package_impl = package_group.packages_.get(pi);
        LoadedPackage loaded_package = loaded_package_impl.loaded_package_;
        ApkAssetsCookie cookie = package_group.cookies_.get(pi);
        // If the type IDs are offset in this package, we need to take that into account when searching
        // for a type.
        TypeSpec type_spec = loaded_package.GetTypeSpecByTypeIndex(type_idx);
        if (Util.UNLIKELY(type_spec == null)) {
            continue;
        }
        int local_entry_idx = entry_idx;
        // If there is an IDMAP supplied with this package, translate the entry ID.
        if (type_spec.idmap_entries != null) {
            if (!LoadedIdmap.Lookup(type_spec.idmap_entries, local_entry_idx, new Ref<>(local_entry_idx))) {
                // There is no mapping, so the resource is not meant to be in this overlay package.
                continue;
            }
        }
        type_flags |= type_spec.GetFlagsForEntryIndex(local_entry_idx);
        // If the package is an overlay, then even configurations that are the same MUST be chosen.
        boolean package_is_overlay = loaded_package.IsOverlay();
        FilteredConfigGroup filtered_group = loaded_package_impl.filtered_configs_.get(type_idx);
        if (use_fast_path) {
            List<ResTable_config> candidate_configs = filtered_group.configurations;
            int type_count = candidate_configs.size();
            for (int i = 0; i < type_count; i++) {
                ResTable_config this_config = candidate_configs.get(i);
                // configurations that do NOT match have been filtered-out.
                if ((best_config == null || this_config.isBetterThan(best_config, desired_config)) || (package_is_overlay && this_config.compare(best_config) == 0)) {
                    // The configuration matches and is better than the previous selection.
                    // Find the entry value if it exists for this configuration.
                    ResTable_type type_chunk = filtered_group.types.get(i);
                    int offset = LoadedPackage.GetEntryOffset(type_chunk, local_entry_idx);
                    if (offset == ResTable_type.NO_ENTRY) {
                        continue;
                    }
                    best_cookie = cookie;
                    best_package = loaded_package;
                    best_type = type_chunk;
                    best_config = this_config;
                    best_offset = offset;
                }
            }
        } else {
            // for (auto iter = type_spec.types; iter != iter_end; ++iter) {
            for (ResTable_type type : type_spec.types) {
                ResTable_config this_config = ResTable_config.fromDtoH(type.config);
                if (this_config.match(desired_config)) {
                    if ((best_config == null || this_config.isBetterThan(best_config, desired_config)) || (package_is_overlay && this_config.compare(best_config) == 0)) {
                        // The configuration matches and is better than the previous selection.
                        // Find the entry value if it exists for this configuration.
                        int offset = LoadedPackage.GetEntryOffset(type, local_entry_idx);
                        if (offset == ResTable_type.NO_ENTRY) {
                            continue;
                        }
                        best_cookie = cookie;
                        best_package = loaded_package;
                        best_type = type;
                        best_config_copy = this_config;
                        best_config = best_config_copy;
                        best_offset = offset;
                    }
                }
            }
        }
    }
    if (Util.UNLIKELY(best_cookie.intValue() == kInvalidCookie)) {
        return K_INVALID_COOKIE;
    }
    ResTable_entry best_entry = LoadedPackage.GetEntryFromOffset(best_type, best_offset);
    if (Util.UNLIKELY(best_entry == null)) {
        return K_INVALID_COOKIE;
    }
    FindEntryResult out_entry_ = new FindEntryResult();
    out_entry_.entry = best_entry;
    out_entry_.config = best_config;
    out_entry_.type_flags = type_flags;
    out_entry_.type_string_ref = new StringPoolRef(best_package.GetTypeStringPool(), best_type.id - 1);
    out_entry_.entry_string_ref = new StringPoolRef(best_package.GetKeyStringPool(), best_entry.key.index);
    out_entry_.dynamic_ref_table = package_group.dynamic_ref_table;
    out_entry.set(out_entry_);
    return best_cookie;
}
Also used : ResTable_entry(org.robolectric.res.android.ResourceTypes.ResTable_entry) LoadedPackage(org.robolectric.res.android.LoadedArsc.LoadedPackage) ResTable_type(org.robolectric.res.android.ResourceTypes.ResTable_type) TypeSpec(org.robolectric.res.android.LoadedArsc.TypeSpec)

Example 2 with ResTable_entry

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

the class ResTable method getEntry.

private int getEntry(final PackageGroup packageGroup, int typeIndex, int entryIndex, final ResTable_config config, Entry outEntry) {
    final List<Type> typeList = getOrDefault(packageGroup.types, typeIndex, Collections.emptyList());
    if (typeList.isEmpty()) {
        ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex);
        return BAD_TYPE;
    }
    ResTable_type bestType = null;
    int bestOffset = ResTable_type.NO_ENTRY;
    Package bestPackage = null;
    int specFlags = 0;
    byte actualTypeIndex = (byte) typeIndex;
    ResTable_config bestConfig = null;
    // memset(&bestConfig, 0, sizeof(bestConfig));
    // Iterate over the Types of each package.
    final int typeCount = typeList.size();
    for (int i = 0; i < typeCount; i++) {
        final Type typeSpec = typeList.get(i);
        int realEntryIndex = entryIndex;
        int realTypeIndex = typeIndex;
        boolean currentTypeIsOverlay = false;
        // ID to package resource ID.
        if (typeSpec.idmapEntries.hasEntries()) {
            final Ref<Short> overlayEntryIndex = new Ref<>((short) 0);
            if (typeSpec.idmapEntries.lookup(entryIndex, overlayEntryIndex) != NO_ERROR) {
                // No such mapping exists
                continue;
            }
            realEntryIndex = overlayEntryIndex.get();
            realTypeIndex = typeSpec.idmapEntries.overlayTypeId() - 1;
            currentTypeIsOverlay = true;
        }
        // entryCount do not need to match.
        if (((int) realEntryIndex) >= typeSpec.entryCount) {
            ALOGW("For resource 0x%08x, entry index(%d) is beyond type entryCount(%d)", Res_MAKEID(packageGroup.id - 1, typeIndex, entryIndex), entryIndex, ((int) typeSpec.entryCount));
            // resources in the 'android' package (old bug in AAPT).
            continue;
        }
        // Aggregate all the flags for each package that defines this entry.
        if (typeSpec.typeSpecFlags != null) {
            specFlags |= dtohl(typeSpec.typeSpecFlags[realEntryIndex]);
        } else {
            specFlags = -1;
        }
        List<ResTable_type> candidateConfigs = typeSpec.configs;
        // List<ResTable_type> filteredConfigs;
        // if (isTruthy(config) && Objects.equals(mParams, config)) {
        // // Grab the lock first so we can safely get the current filtered list.
        // synchronized (mFilteredConfigLock) {
        // // This configuration is equal to the one we have previously cached for,
        // // so use the filtered configs.
        // 
        // final TypeCacheEntry cacheEntry = packageGroup.typeCacheEntries.get(typeIndex);
        // if (i < cacheEntry.filteredConfigs.size()) {
        // if (isTruthy(cacheEntry.filteredConfigs.get(i))) {
        // // Grab a reference to the shared_ptr so it doesn't get destroyed while
        // // going through this list.
        // filteredConfigs = cacheEntry.filteredConfigs.get(i);
        // 
        // // Use this filtered list.
        // candidateConfigs = filteredConfigs;
        // }
        // }
        // }
        // }
        final int numConfigs = candidateConfigs.size();
        for (int c = 0; c < numConfigs; c++) {
            final ResTable_type thisType = candidateConfigs.get(c);
            if (thisType == NULL) {
                continue;
            }
            final ResTable_config thisConfig;
            // thisConfig.copyFromDtoH(thisType.config);
            thisConfig = ResTable_config.fromDtoH(thisType.config);
            // Check to make sure this one is valid for the current parameters.
            if (config != NULL && !thisConfig.match(config)) {
                continue;
            }
            // const uint32_t* const eindex = reinterpret_cast<const uint32_t*>(
            // reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize));
            final int eindex = thisType.myOffset() + dtohs(thisType.header.headerSize);
            int thisOffset;
            // Check if there is the desired entry in this type.
            if (isTruthy(thisType.flags & ResTable_type.FLAG_SPARSE)) {
                // This is encoded as a sparse map, so perform a binary search.
                final ByteBuffer buf = thisType.myBuf();
                ResTable_sparseTypeEntry sparseIndices = new ResTable_sparseTypeEntry(buf, eindex);
                ResTable_sparseTypeEntry result = lower_bound(sparseIndices, new ResTable_sparseTypeEntry(buf, sparseIndices.myOffset() + dtohl(thisType.entryCount)), new ResTable_sparseTypeEntry(buf, realEntryIndex), (a, b) -> dtohs(a.idxOrOffset) < dtohs(b.idxOrOffset));
                // || dtohs(result.idx) != realEntryIndex) {
                if (result.myOffset() == sparseIndices.myOffset() + dtohl(thisType.entryCount) || dtohs(result.idxOrOffset) != realEntryIndex) {
                    // No entry found.
                    continue;
                }
                // Extract the offset from the entry. Each offset must be a multiple of 4
                // so we store it as the real offset divided by 4.
                // thisOffset = dtohs(result->offset) * 4u;
                thisOffset = dtohs(result.idxOrOffset) * 4;
            } else {
                if (realEntryIndex >= dtohl(thisType.entryCount)) {
                    // Entry does not exist.
                    continue;
                }
                // thisOffset = dtohl(eindex[realEntryIndex]);
                thisOffset = thisType.entryOffset(realEntryIndex);
            }
            if (thisOffset == ResTable_type.NO_ENTRY) {
                // There is no entry for this index and configuration.
                continue;
            }
            if (bestType != NULL) {
                // about to those we least care about.
                if (!thisConfig.isBetterThan(bestConfig, config)) {
                    if (!currentTypeIsOverlay || thisConfig.compare(bestConfig) != 0) {
                        continue;
                    }
                }
            }
            bestType = thisType;
            bestOffset = thisOffset;
            bestConfig = thisConfig;
            bestPackage = typeSpec._package_;
            actualTypeIndex = (byte) realTypeIndex;
            // If no config was specified, any type will do, so skip
            if (config == NULL) {
                break;
            }
        }
    }
    if (bestType == NULL) {
        return BAD_INDEX;
    }
    bestOffset += dtohl(bestType.entriesStart);
    // if (bestOffset > (dtohl(bestType->header.size)-sizeof(ResTable_entry))) {
    if (bestOffset > (dtohl(bestType.header.size) - ResTable_entry.SIZEOF)) {
        ALOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x", bestOffset, dtohl(bestType.header.size));
        return BAD_TYPE;
    }
    if ((bestOffset & 0x3) != 0) {
        ALOGW("ResTable_entry at 0x%x is not on an integer boundary", bestOffset);
        return BAD_TYPE;
    }
    // const ResTable_entry* const entry = reinterpret_cast<const ResTable_entry*>(
    // reinterpret_cast<const uint8_t*>(bestType) + bestOffset);
    final ResTable_entry entry = new ResTable_entry(bestType.myBuf(), bestType.myOffset() + bestOffset);
    if (dtohs(entry.size) < ResTable_entry.SIZEOF) {
        ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry.size));
        return BAD_TYPE;
    }
    if (outEntry != null) {
        outEntry.entry = entry;
        outEntry.config = bestConfig;
        outEntry.type = bestType;
        outEntry.specFlags = specFlags;
        outEntry._package_ = bestPackage;
        outEntry.typeStr = new StringPoolRef(bestPackage.typeStrings, actualTypeIndex - bestPackage.typeIdOffset);
        outEntry.keyStr = new StringPoolRef(bestPackage.keyStrings, dtohl(entry.key.index));
    }
    return NO_ERROR;
}
Also used : ByteBuffer(java.nio.ByteBuffer) ResTable_entry(org.robolectric.res.android.ResourceTypes.ResTable_entry) ResTable_sparseTypeEntry(org.robolectric.res.android.ResourceTypes.ResTable_sparseTypeEntry) ResTable_type(org.robolectric.res.android.ResourceTypes.ResTable_type)

Example 3 with ResTable_entry

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

the class LoadedArsc method VerifyResTableEntry.

static boolean VerifyResTableEntry(ResTable_type type, int entry_offset) {
    // Check that the offset is aligned.
    if (isTruthy(entry_offset & 0x03)) {
        logError("Entry at offset " + entry_offset + " is not 4-byte aligned.");
        return false;
    }
    // if (entry_offset > std.numeric_limits<int>.max() - dtohl(type.entriesStart)) {
    if (entry_offset > Integer.MAX_VALUE - dtohl(type.entriesStart)) {
        // Overflow in offset.
        logError("Entry at offset " + entry_offset + " is too large.");
        return false;
    }
    int chunk_size = dtohl(type.header.size);
    entry_offset += dtohl(type.entriesStart);
    if (entry_offset > chunk_size - ResTable_entry.SIZEOF) {
        logError("Entry at offset " + entry_offset + " is too large. No room for ResTable_entry.");
        return false;
    }
    // ResTable_entry* entry = reinterpret_cast<ResTable_entry*>(
    // reinterpret_cast<uint8_t*>(type) + entry_offset);
    ResTable_entry entry = new ResTable_entry(type.myBuf(), type.myOffset() + entry_offset);
    int entry_size = dtohs(entry.size);
    // if (entry_size < sizeof(*entry)) {
    if (entry_size < ResTable_entry.SIZEOF) {
        logError("ResTable_entry size " + entry_size + " at offset " + entry_offset + " is too small.");
        return false;
    }
    if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) {
        logError("ResTable_entry size " + entry_size + " at offset " + entry_offset + " is too large.");
        return false;
    }
    if (entry_size < ResTable_map_entry.BASE_SIZEOF) {
        // There needs to be room for one Res_value struct.
        if (entry_offset + entry_size > chunk_size - Res_value.SIZEOF) {
            logError("No room for Res_value after ResTable_entry at offset " + entry_offset + " for type " + (int) type.id + ".");
            return false;
        }
        // Res_value value =
        // reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(entry) + entry_size);
        Res_value value = new Res_value(entry.myBuf(), entry.myOffset() + ResTable_entry.SIZEOF);
        int value_size = dtohs(value.size);
        if (value_size < Res_value.SIZEOF) {
            logError("Res_value at offset " + entry_offset + " is too small.");
            return false;
        }
        if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) {
            logError("Res_value size " + value_size + " at offset " + entry_offset + " is too large.");
            return false;
        }
    } else {
        ResTable_map_entry map = new ResTable_map_entry(entry.myBuf(), entry.myOffset());
        int map_entry_count = dtohl(map.count);
        int map_entries_start = entry_offset + entry_size;
        if (isTruthy(map_entries_start & 0x03)) {
            logError("Map entries at offset " + entry_offset + " start at unaligned offset.");
            return false;
        }
        // Each entry is sizeof(ResTable_map) big.
        if (map_entry_count > ((chunk_size - map_entries_start) / ResTable_map.SIZEOF)) {
            logError("Too many map entries in ResTable_map_entry at offset " + entry_offset + ".");
            return false;
        }
    }
    return true;
}
Also used : Res_value(org.robolectric.res.android.ResourceTypes.Res_value) ResTable_map_entry(org.robolectric.res.android.ResourceTypes.ResTable_map_entry) ResTable_entry(org.robolectric.res.android.ResourceTypes.ResTable_entry)

Aggregations

ResTable_entry (org.robolectric.res.android.ResourceTypes.ResTable_entry)3 ResTable_type (org.robolectric.res.android.ResourceTypes.ResTable_type)2 ByteBuffer (java.nio.ByteBuffer)1 LoadedPackage (org.robolectric.res.android.LoadedArsc.LoadedPackage)1 TypeSpec (org.robolectric.res.android.LoadedArsc.TypeSpec)1 ResTable_map_entry (org.robolectric.res.android.ResourceTypes.ResTable_map_entry)1 ResTable_sparseTypeEntry (org.robolectric.res.android.ResourceTypes.ResTable_sparseTypeEntry)1 Res_value (org.robolectric.res.android.ResourceTypes.Res_value)1