int parsePackage()

in resources/src/main/java/org/robolectric/res/android/ResTable.java [721:1046]


  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 group;
//      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 group;
//      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;

      // Find all packages that reference this package
//      int N = mPackageGroups.size();
//      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;
//    while (((const uint8_t*)chunk) <= (endPos-sizeof(ResChunk_header)) &&
//      ((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 /*sizeof(int)*/)
          || 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;
  }