Search in sources :

Example 1 with ResTable_type

use of org.robolectric.res.android.ResourceTypes.ResTable_type 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_type

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

the class CppAssetManager2 method RebuildFilterList.

// Triggers the re-construction of lists of types that match the set configuration.
// This should always be called when mutating the AssetManager's configuration or ApkAssets set.
void RebuildFilterList() {
    for (PackageGroup group : package_groups_) {
        for (ConfiguredPackage impl : group.packages_) {
            // // Destroy it.
            // impl.filtered_configs_.~ByteBucketArray();
            // 
            // // Re-create it.
            // new (impl.filtered_configs_) ByteBucketArray<FilteredConfigGroup>();
            impl.filtered_configs_ = new ByteBucketArray<FilteredConfigGroup>(new FilteredConfigGroup()) {

                @Override
                FilteredConfigGroup newInstance() {
                    return new FilteredConfigGroup();
                }
            };
            // Create the filters here.
            impl.loaded_package_.ForEachTypeSpec((TypeSpec spec, byte type_index) -> {
                FilteredConfigGroup configGroup = impl.filtered_configs_.editItemAt(type_index);
                // for (auto iter = spec->types; iter != iter_end; ++iter) {
                for (ResTable_type iter : spec.types) {
                    ResTable_config this_config = ResTable_config.fromDtoH(iter.config);
                    if (this_config.match(configuration_)) {
                        configGroup.configurations.add(this_config);
                        configGroup.types.add(iter);
                    }
                }
            });
        }
    }
}
Also used : ResTable_type(org.robolectric.res.android.ResourceTypes.ResTable_type) TypeSpec(org.robolectric.res.android.LoadedArsc.TypeSpec)

Example 3 with ResTable_type

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

the class ResTable method findEntry.

int findEntry(PackageGroup group, int typeIndex, String name, Ref<Integer> outTypeSpecFlags) {
    List<Type> typeList = getOrDefault(group.types, typeIndex, Collections.emptyList());
    for (Type type : typeList) {
        int ei = type._package_.keyStrings.indexOfString(name);
        if (ei < 0) {
            continue;
        }
        for (ResTable_type resTableType : type.configs) {
            int entryIndex = resTableType.findEntryByResName(ei);
            if (entryIndex >= 0) {
                int resId = Res_MAKEID(group.id - 1, typeIndex, entryIndex);
                if (outTypeSpecFlags != null) {
                    Entry result = new Entry();
                    if (getEntry(group, typeIndex, entryIndex, null, result) != NO_ERROR) {
                        ALOGW("Failed to find spec flags for 0x%08x", resId);
                        return 0;
                    }
                    outTypeSpecFlags.set(result.specFlags);
                }
                return resId;
            }
        }
    }
    return 0;
}
Also used : ResTable_sparseTypeEntry(org.robolectric.res.android.ResourceTypes.ResTable_sparseTypeEntry) ResTable_type(org.robolectric.res.android.ResourceTypes.ResTable_type)

Example 4 with ResTable_type

use of org.robolectric.res.android.ResourceTypes.ResTable_type 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 5 with ResTable_type

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

the class ResTable method parsePackage.

int parsePackage(ResTable_package pkg, Header header, boolean appAsLib, boolean isSystemAsset) {
    int base = pkg.myOffset();
    int err = validate_chunk(pkg.header, ResTable_package.SIZEOF - 4, /*sizeof(pkg.typeIdOffset)*/
    header.dataEnd, "ResTable_package");
    if (err != NO_ERROR) {
        return (mError = err);
    }
    final int pkgSize = dtohl(pkg.header.size);
    if (dtohl(pkg.typeStrings) >= pkgSize) {
        ALOGW("ResTable_package type strings at 0x%x are past chunk size 0x%x.", dtohl(pkg.typeStrings), pkgSize);
        return (mError = BAD_TYPE);
    }
    if ((dtohl(pkg.typeStrings) & 0x3) != 0) {
        ALOGW("ResTable_package type strings at 0x%x is not on an integer boundary.", dtohl(pkg.typeStrings));
        return (mError = BAD_TYPE);
    }
    if (dtohl(pkg.keyStrings) >= pkgSize) {
        ALOGW("ResTable_package key strings at 0x%x are past chunk size 0x%x.", dtohl(pkg.keyStrings), pkgSize);
        return (mError = BAD_TYPE);
    }
    if ((dtohl(pkg.keyStrings) & 0x3) != 0) {
        ALOGW("ResTable_package key strings at 0x%x is not on an integer boundary.", dtohl(pkg.keyStrings));
        return (mError = BAD_TYPE);
    }
    int id = dtohl(pkg.id);
    final Map<Byte, IdmapEntries> idmapEntries = new HashMap<>();
    if (header.resourceIDMap != NULL) {
    // byte targetPackageId = 0;
    // int err = parseIdmap(header.resourceIDMap, header.resourceIDMapSize, &targetPackageId, &idmapEntries);
    // if (err != NO_ERROR) {
    // ALOGW("Overlay is broken");
    // return (mError=err);
    // }
    // id = targetPackageId;
    }
    boolean isDynamic = false;
    if (id >= 256) {
        // LOG_ALWAYS_FATAL("Package id out of range");
        throw new IllegalStateException("Package id out of range");
    // return NO_ERROR;
    } else if (id == 0 || (id == 0x7f && appAsLib) || isSystemAsset) {
        // This is a library or a system asset, so assign an ID
        id = mNextPackageId++;
        isDynamic = true;
    }
    PackageGroup group = null;
    Package _package = new Package(this, header, pkg);
    if (_package == NULL) {
        return (mError = NO_MEMORY);
    }
    // err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
    // header->dataEnd-(base+dtohl(pkg->typeStrings)));
    err = _package.typeStrings.setTo(pkg.myBuf(), base + dtohl(pkg.typeStrings), header.dataEnd - (base + dtohl(pkg.typeStrings)), false);
    if (err != NO_ERROR) {
        // delete _package;
        return (mError = err);
    }
    // err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
    // header->dataEnd-(base+dtohl(pkg->keyStrings)));
    err = _package.keyStrings.setTo(pkg.myBuf(), base + dtohl(pkg.keyStrings), header.dataEnd - (base + dtohl(pkg.keyStrings)), false);
    if (err != NO_ERROR) {
        // delete _package;
        return (mError = err);
    }
    int idx = mPackageMap[id];
    if (idx == 0) {
        idx = mPackageGroups.size() + 1;
        // char[] tmpName = new char[pkg.name.length /*sizeof(pkg.name)/sizeof(pkg.name[0])*/];
        // strcpy16_dtoh(tmpName, pkg.name, sizeof(pkg.name)/sizeof(pkg.name[0]));
        group = new PackageGroup(this, new String(pkg.name), id, appAsLib, isSystemAsset, isDynamic);
        if (group == NULL) {
            // delete _package;
            return (mError = NO_MEMORY);
        }
        mPackageGroups.put(group.id, group);
        // if (err < NO_ERROR) {
        // return (mError=err);
        // }
        mPackageMap[id] = (byte) idx;
        // for (int i = 0; i < N; i++) {
        for (PackageGroup packageGroup : mPackageGroups.values()) {
            packageGroup.dynamicRefTable.addMapping(group.name, (byte) group.id);
        }
    } else {
        group = mPackageGroups.get(idx - 1);
        if (group == NULL) {
            return (mError = UNKNOWN_ERROR);
        }
    }
    group.packages.add(_package);
    // if (err < NO_ERROR) {
    // return (mError=err);
    // }
    // Iterate through all chunks.
    ResChunk_header chunk = new ResChunk_header(pkg.myBuf(), pkg.myOffset() + dtohs(pkg.header.headerSize));
    // const uint8_t* endPos = ((const uint8_t*)pkg) + dtohs(pkg->header.size);
    final int endPos = (pkg.myOffset()) + pkg.header.size;
    // ((const uint8_t*)chunk) <= (endPos-dtohl(chunk->size))) {
    while (chunk != null && (chunk.myOffset()) <= (endPos - ResChunk_header.SIZEOF) && (chunk.myOffset()) <= (endPos - dtohl(chunk.size))) {
        if (kDebugTableNoisy) {
            ALOGV("PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%s\n", dtohs(chunk.type), dtohs(chunk.headerSize), dtohl(chunk.size), ((chunk.myOffset()) - (header.header.myOffset())));
        }
        final int csize = dtohl(chunk.size);
        final short ctype = dtohs(chunk.type);
        if (ctype == RES_TABLE_TYPE_SPEC_TYPE) {
            final ResTable_typeSpec typeSpec = new ResTable_typeSpec(chunk.myBuf(), chunk.myOffset());
            err = validate_chunk(typeSpec.header, ResTable_typeSpec.SIZEOF, endPos, "ResTable_typeSpec");
            if (err != NO_ERROR) {
                return (mError = err);
            }
            final int typeSpecSize = dtohl(typeSpec.header.size);
            final int newEntryCount = dtohl(typeSpec.entryCount);
            if (kDebugLoadTableNoisy) {
                ALOGI("TypeSpec off %s: type=0x%x, headerSize=0x%x, size=%s\n", (base - chunk.myOffset()), dtohs(typeSpec.header.type), dtohs(typeSpec.header.headerSize), typeSpecSize);
            }
            // look for block overrun or int overflow when multiplying by 4
            if ((dtohl(typeSpec.entryCount) > (Integer.MAX_VALUE / 4) || dtohs(typeSpec.header.headerSize) + (4 * /*sizeof(int)*/
            newEntryCount) > typeSpecSize)) {
                ALOGW("ResTable_typeSpec entry index to %s extends beyond chunk end %s.", (dtohs(typeSpec.header.headerSize) + (4 * /*sizeof(int)*/
                newEntryCount)), typeSpecSize);
                return (mError = BAD_TYPE);
            }
            if (typeSpec.id == 0) {
                ALOGW("ResTable_type has an id of 0.");
                return (mError = BAD_TYPE);
            }
            if (newEntryCount > 0) {
                boolean addToType = true;
                byte typeIndex = (byte) (typeSpec.id - 1);
                IdmapEntries idmapEntry = idmapEntries.get(typeSpec.id);
                if (idmapEntry != null) {
                    typeIndex = (byte) (idmapEntry.targetTypeId() - 1);
                } else if (header.resourceIDMap != NULL) {
                    // This is an overlay, but the types in this overlay are not
                    // overlaying anything according to the idmap. We can skip these
                    // as they will otherwise conflict with the other resources in the package
                    // without a mapping.
                    addToType = false;
                }
                if (addToType) {
                    List<Type> typeList = computeIfAbsent(group.types, (int) typeIndex, k -> new ArrayList<>());
                    if (!typeList.isEmpty()) {
                        final Type existingType = typeList.get(0);
                        if (existingType.entryCount != newEntryCount && idmapEntry == null) {
                            ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", (int) newEntryCount, (int) existingType.entryCount);
                        // We should normally abort here, but some legacy apps declare
                        // resources in the 'android' package (old bug in AAPT).
                        }
                    }
                    Type t = new Type(header, _package, newEntryCount);
                    t.typeSpec = typeSpec;
                    t.typeSpecFlags = typeSpec.getSpecFlags();
                    if (idmapEntry != null) {
                        t.idmapEntries = idmapEntry;
                    }
                    typeList.add(t);
                    group.largestTypeId = max(group.largestTypeId, typeSpec.id);
                }
            } else {
                ALOGV("Skipping empty ResTable_typeSpec for type %d", typeSpec.id);
            }
        } else if (ctype == RES_TABLE_TYPE_TYPE) {
            ResTable_type type = new ResTable_type(chunk.myBuf(), chunk.myOffset());
            err = validate_chunk(type.header, ResTable_type.SIZEOF_WITHOUT_CONFIG + /*-sizeof(ResTable_config)*/
            4, endPos, "ResTable_type");
            if (err != NO_ERROR) {
                return (mError = err);
            }
            final int typeSize = dtohl(type.header.size);
            final int newEntryCount = dtohl(type.entryCount);
            if (kDebugLoadTableNoisy) {
                System.out.println(String.format("Type off 0x%x: type=0x%x, headerSize=0x%x, size=%d\n", base - chunk.myOffset(), dtohs(type.header.type), dtohs(type.header.headerSize), typeSize));
            }
            if (dtohs(type.header.headerSize) + (4 * /*sizeof(int)*/
            newEntryCount) > typeSize) {
                ALOGW("ResTable_type entry index to %s extends beyond chunk end 0x%x.", (dtohs(type.header.headerSize) + (4 * /*sizeof(int)*/
                newEntryCount)), typeSize);
                return (mError = BAD_TYPE);
            }
            if (newEntryCount != 0 && dtohl(type.entriesStart) > (typeSize - ResTable_entry.SIZEOF)) {
                ALOGW("ResTable_type entriesStart at 0x%x extends beyond chunk end 0x%x.", dtohl(type.entriesStart), typeSize);
                return (mError = BAD_TYPE);
            }
            if (type.id == 0) {
                ALOGW("ResTable_type has an id of 0.");
                return (mError = BAD_TYPE);
            }
            if (newEntryCount > 0) {
                boolean addToType = true;
                byte typeIndex = (byte) (type.id - 1);
                IdmapEntries idmapEntry = idmapEntries.get(type.id);
                if (idmapEntry != null) {
                    typeIndex = (byte) (idmapEntry.targetTypeId() - 1);
                } else if (header.resourceIDMap != NULL) {
                    // This is an overlay, but the types in this overlay are not
                    // overlaying anything according to the idmap. We can skip these
                    // as they will otherwise conflict with the other resources in the package
                    // without a mapping.
                    addToType = false;
                }
                if (addToType) {
                    List<Type> typeList = getOrDefault(group.types, (int) typeIndex, Collections.emptyList());
                    if (typeList.isEmpty()) {
                        ALOGE("No TypeSpec for type %d", type.id);
                        return (mError = BAD_TYPE);
                    }
                    Type t = typeList.get(typeList.size() - 1);
                    if (newEntryCount != t.entryCount) {
                        ALOGE("ResTable_type entry count inconsistent: given %d, previously %d", (int) newEntryCount, (int) t.entryCount);
                        return (mError = BAD_TYPE);
                    }
                    if (t._package_ != _package) {
                        ALOGE("No TypeSpec for type %d", type.id);
                        return (mError = BAD_TYPE);
                    }
                    t.configs.add(type);
                    if (kDebugTableGetEntry) {
                        ResTable_config thisConfig = ResTable_config.fromDtoH(type.config);
                        ALOGI("Adding config to type %d: %s\n", type.id, thisConfig.toString());
                    }
                }
            } else {
                ALOGV("Skipping empty ResTable_type for type %d", type.id);
            }
        } else if (ctype == RES_TABLE_LIBRARY_TYPE) {
            if (group.dynamicRefTable.entries().isEmpty()) {
                throw new UnsupportedOperationException("libraries not supported yet");
            // const ResTable_lib_header* lib = (const ResTable_lib_header*) chunk;
            // status_t err = validate_chunk(&lib->header, sizeof(*lib),
            // endPos, "ResTable_lib_header");
            // if (err != NO_ERROR) {
            // return (mError=err);
            // }
            // 
            // err = group->dynamicRefTable.load(lib);
            // if (err != NO_ERROR) {
            // return (mError=err);
            // }
            // 
            // // Fill in the reference table with the entries we already know about.
            // size_t N = mPackageGroups.size();
            // for (size_t i = 0; i < N; i++) {
            // group.dynamicRefTable.addMapping(mPackageGroups[i].name, mPackageGroups[i].id);
            // }
            } else {
                ALOGW("Found multiple library tables, ignoring...");
            }
        } else {
            err = validate_chunk(chunk, ResChunk_header.SIZEOF, endPos, "ResTable_package:unknown");
            if (err != NO_ERROR) {
                return (mError = err);
            }
        }
        chunk = chunk.myOffset() + csize < endPos ? new ResChunk_header(chunk.myBuf(), chunk.myOffset() + csize) : null;
    }
    return NO_ERROR;
}
Also used : HashMap(java.util.HashMap) ResChunk_header(org.robolectric.res.android.ResourceTypes.ResChunk_header) ArrayList(java.util.ArrayList) List(java.util.List) ResTable_typeSpec(org.robolectric.res.android.ResourceTypes.ResTable_typeSpec) ResTable_type(org.robolectric.res.android.ResourceTypes.ResTable_type)

Aggregations

ResTable_type (org.robolectric.res.android.ResourceTypes.ResTable_type)5 TypeSpec (org.robolectric.res.android.LoadedArsc.TypeSpec)2 ResTable_entry (org.robolectric.res.android.ResourceTypes.ResTable_entry)2 ResTable_sparseTypeEntry (org.robolectric.res.android.ResourceTypes.ResTable_sparseTypeEntry)2 ByteBuffer (java.nio.ByteBuffer)1 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 List (java.util.List)1 LoadedPackage (org.robolectric.res.android.LoadedArsc.LoadedPackage)1 ResChunk_header (org.robolectric.res.android.ResourceTypes.ResChunk_header)1 ResTable_typeSpec (org.robolectric.res.android.ResourceTypes.ResTable_typeSpec)1