Search in sources :

Example 1 with ResTable_typeSpec

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

ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 List (java.util.List)1 ResChunk_header (org.robolectric.res.android.ResourceTypes.ResChunk_header)1 ResTable_type (org.robolectric.res.android.ResourceTypes.ResTable_type)1 ResTable_typeSpec (org.robolectric.res.android.ResourceTypes.ResTable_typeSpec)1