in resources/src/main/java/org/robolectric/res/android/ResTable.java [518:719]
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;
// Runtime overlay packages provide a mapping of app resource
// 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;
}
// Check that the entry idx is within range of the declared entry count (ResTable_typeSpec).
// Particular types (ResTable_type) may be encoded with sparse entries, and so their
// 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));
// We should normally abort here, but some legacy apps declare
// 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));
// if (result == sparseIndices + dtohl(thisType.entryCount)
// || 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) {
// Check if this one is less specific than the last found. If so,
// we will skip it. We check starting with things we most care
// 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;
}