org.robolectric.res.android.ResTable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of resources Show documentation
Show all versions of resources Show documentation
An alternative Android testing framework.
package org.robolectric.res.android;
import static com.google.common.primitives.UnsignedBytes.max;
import static org.robolectric.res.android.Errors.BAD_INDEX;
import static org.robolectric.res.android.Errors.BAD_TYPE;
import static org.robolectric.res.android.Errors.BAD_VALUE;
import static org.robolectric.res.android.Errors.NO_ERROR;
import static org.robolectric.res.android.Errors.NO_MEMORY;
import static org.robolectric.res.android.Errors.UNKNOWN_ERROR;
import static org.robolectric.res.android.ResourceTypes.RES_STRING_POOL_TYPE;
import static org.robolectric.res.android.ResourceTypes.RES_TABLE_LIBRARY_TYPE;
import static org.robolectric.res.android.ResourceTypes.RES_TABLE_PACKAGE_TYPE;
import static org.robolectric.res.android.ResourceTypes.RES_TABLE_TYPE;
import static org.robolectric.res.android.ResourceTypes.RES_TABLE_TYPE_SPEC_TYPE;
import static org.robolectric.res.android.ResourceTypes.RES_TABLE_TYPE_TYPE;
import static org.robolectric.res.android.ResourceTypes.validate_chunk;
import static org.robolectric.res.android.Util.ALOGD;
import static org.robolectric.res.android.Util.ALOGE;
import static org.robolectric.res.android.Util.ALOGI;
import static org.robolectric.res.android.Util.ALOGV;
import static org.robolectric.res.android.Util.ALOGW;
import static org.robolectric.res.android.Util.LOG_FATAL_IF;
import static org.robolectric.res.android.Util.dtohl;
import static org.robolectric.res.android.Util.dtohs;
import static org.robolectric.res.android.Util.htodl;
import static org.robolectric.res.android.Util.htods;
import static org.robolectric.res.android.Util.isTruthy;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Semaphore;
import org.robolectric.res.android.ResourceTypes.ResChunk_header;
import org.robolectric.res.android.ResourceTypes.ResTable_entry;
import org.robolectric.res.android.ResourceTypes.ResTable_header;
import org.robolectric.res.android.ResourceTypes.ResTable_map;
import org.robolectric.res.android.ResourceTypes.ResTable_map_entry;
import org.robolectric.res.android.ResourceTypes.ResTable_package;
import org.robolectric.res.android.ResourceTypes.ResTable_sparseTypeEntry;
import org.robolectric.res.android.ResourceTypes.ResTable_type;
import org.robolectric.res.android.ResourceTypes.ResTable_typeSpec;
import org.robolectric.res.android.ResourceTypes.Res_value;
// transliterated from https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/ResourceTypes.cpp
// and https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/include/androidfw/ResourceTypes.h
@SuppressWarnings("NewApi")
public class ResTable {
private static final int IDMAP_MAGIC = 0x504D4449;
private static final int IDMAP_CURRENT_VERSION = 0x00000001;
static final int APP_PACKAGE_ID = 0x7f;
static final int SYS_PACKAGE_ID = 0x01;
static final boolean kDebugStringPoolNoisy = false;
static final boolean kDebugXMLNoisy = false;
static final boolean kDebugTableNoisy = false;
static final boolean kDebugTableGetEntry = false;
static final boolean kDebugTableSuperNoisy = false;
static final boolean kDebugLoadTableNoisy = false;
static final boolean kDebugLoadTableSuperNoisy = false;
static final boolean kDebugTableTheme = false;
static final boolean kDebugResXMLTree = false;
static final boolean kDebugLibNoisy = false;
private static final Object NULL = null;
public static final bag_set SENTINEL_BAG_SET = new bag_set(1);
final Semaphore mLock = new Semaphore(1);
// Mutex that controls access to the list of pre-filtered configurations
// to check when looking up entries.
// When iterating over a bag, the mLock mutex is locked. While mLock is locked,
// we do resource lookups.
// Mutex is not reentrant, so we must use a different lock than mLock.
final Object mFilteredConfigLock = new Object();
// type defined in Errors
int mError;
ResTable_config mParams;
// Array of all resource tables.
final List mHeaders = new ArrayList<>();
// Array of packages in all resource tables.
final Map mPackageGroups = new HashMap<>();
// Mapping from resource package IDs to indices into the internal
// package array.
final byte[] mPackageMap = new byte[256];
byte mNextPackageId;
static boolean Res_CHECKID(int resid) { return ((resid&0xFFFF0000) != 0);}
static int Res_GETPACKAGE(int id) {
return ((id>>24)-1);
}
public static int Res_GETTYPE(int id) {
return (((id>>16)&0xFF)-1);
}
static int Res_GETENTRY(int id) {
return (id&0xFFFF);
}
static int Res_MAKEARRAY(int entry) { return (0x02000000 | (entry&0xFFFF)); }
static boolean Res_INTERNALID(int resid) { return ((resid&0xFFFF0000) != 0 && (resid&0xFF0000) == 0); }
int getResourcePackageIndex(int resID)
{
return Res_GETPACKAGE(resID) + 1;
//return mPackageMap[Res_GETPACKAGE(resID)+1]-1;
}
int getResourcePackageIndexFromPackage(byte packageID) {
return ((int)mPackageMap[packageID])-1;
}
// Errors add(final Object data, int size, final int cookie, boolean copyData) {
// return addInternal(data, size, NULL, 0, false, cookie, copyData);
// }
//
// Errors add(final Object data, int size, final Object idmapData, int idmapDataSize,
// final int cookie, boolean copyData, boolean appAsLib) {
// return addInternal(data, size, idmapData, idmapDataSize, appAsLib, cookie, copyData);
// }
//
// Errors add(Asset asset, final int cookie, boolean copyData) {
// final Object data = asset.getBuffer(true);
// if (data == NULL) {
// ALOGW("Unable to get buffer of resource asset file");
// return UNKNOWN_ERROR;
// }
//
// return addInternal(data, static_cast(asset.getLength()), NULL, false, 0, cookie,
// copyData);
// }
// status_t add(Asset* asset, Asset* idmapAsset, const int32_t cookie=-1, bool copyData=false,
// bool appAsLib=false, bool isSystemAsset=false);
int add(
Asset asset, Asset idmapAsset, final int cookie, boolean copyData,
boolean appAsLib, boolean isSystemAsset) {
final byte[] data = asset.getBuffer(true);
if (data == NULL) {
ALOGW("Unable to get buffer of resource asset file");
return UNKNOWN_ERROR;
}
int idmapSize = 0;
Object idmapData = NULL;
if (idmapAsset != NULL) {
idmapData = idmapAsset.getBuffer(true);
if (idmapData == NULL) {
ALOGW("Unable to get buffer of idmap asset file");
return UNKNOWN_ERROR;
}
idmapSize = (int) idmapAsset.getLength();
}
return addInternal(data, (int) asset.getLength(),
idmapData, idmapSize, appAsLib, cookie, copyData, isSystemAsset);
}
int add(ResTable src, boolean isSystemAsset)
{
mError = src.mError;
for (int i=0; i < src.mHeaders.size(); i++) {
mHeaders.add(src.mHeaders.get(i));
}
for (PackageGroup srcPg : src.mPackageGroups.values()) {
PackageGroup pg = new PackageGroup(this, srcPg.name, srcPg.id,
false /* appAsLib */, isSystemAsset || srcPg.isSystemAsset, srcPg.isDynamic);
for (int j=0; j typeList = computeIfAbsent(pg.types, typeId, key -> new ArrayList<>());
typeList.addAll(srcPg.types.get(typeId));
}
pg.dynamicRefTable.addMappings(srcPg.dynamicRefTable);
pg.largestTypeId = max(pg.largestTypeId, srcPg.largestTypeId);
mPackageGroups.put(pg.id, pg);
}
// memcpy(mPackageMap, src->mPackageMap, sizeof(mPackageMap));
System.arraycopy(src.mPackageMap, 0, mPackageMap, 0, mPackageMap.length);
return mError;
}
int addEmpty(final int cookie) {
Header header = new Header(this);
header.index = mHeaders.size();
header.cookie = cookie;
header.values.setToEmpty();
header.ownedData = new byte[ResTable_header.SIZEOF];
ByteBuffer buf = ByteBuffer.wrap(header.ownedData).order(ByteOrder.LITTLE_ENDIAN);
ResChunk_header.write(buf, (short) RES_TABLE_TYPE, () -> {}, () -> {});
ResTable_header resHeader = new ResTable_header(buf, 0);
// resHeader.header.type = RES_TABLE_TYPE;
// resHeader.header.headerSize = sizeof(ResTable_header);
// resHeader.header.size = sizeof(ResTable_header);
header.header = resHeader;
mHeaders.add(header);
return (mError=NO_ERROR);
}
// status_t addInternal(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
// bool appAsLib, const int32_t cookie, bool copyData, bool isSystemAsset=false);
int addInternal(byte[] data, int dataSize, final Object idmapData, int idmapDataSize,
boolean appAsLib, final int cookie, boolean copyData, boolean isSystemAsset)
{
if (!isTruthy(data)) {
return NO_ERROR;
}
if (dataSize < ResTable_header.SIZEOF) {
ALOGE("Invalid data. Size(%d) is smaller than a ResTable_header(%d).",
(int) dataSize, (int) ResTable_header.SIZEOF);
return UNKNOWN_ERROR;
}
Header header = new Header(this);
header.index = mHeaders.size();
header.cookie = cookie;
if (idmapData != NULL) {
header.resourceIDMap = new int[idmapDataSize / 4];
if (header.resourceIDMap == NULL) {
// delete header;
return (mError = NO_MEMORY);
}
// memcpy(header.resourceIDMap, idmapData, idmapDataSize);
// header.resourceIDMapSize = idmapDataSize;
}
mHeaders.add(header);
final boolean notDeviceEndian = htods((short) 0xf0) != 0xf0;
if (kDebugLoadTableNoisy) {
ALOGV("Adding resources to ResTable: data=%s, size=0x%x, cookie=%d, copy=%d " +
"idmap=%s\n", data, dataSize, cookie, copyData, idmapData);
}
if (copyData || notDeviceEndian) {
header.ownedData = data; // malloc(dataSize);
if (header.ownedData == NULL) {
return (mError=NO_MEMORY);
}
// memcpy(header.ownedData, data, dataSize);
data = header.ownedData;
}
ByteBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
// header->header = (const ResTable_header*)data;
header.header = new ResTable_header(buf, 0);
header.size = dtohl(header.header.header.size);
if (kDebugLoadTableSuperNoisy) {
ALOGI("Got size 0x%x, again size 0x%x, raw size 0x%x\n", header.size,
dtohl(header.header.header.size), header.header.header.size);
}
if (kDebugLoadTableNoisy) {
ALOGV("Loading ResTable @%s:\n", header.header);
}
if (dtohs(header.header.header.headerSize) > header.size
|| header.size > dataSize) {
ALOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n",
(int)dtohs(header.header.header.headerSize),
(int)header.size, (int)dataSize);
return (mError=BAD_TYPE);
}
if (((dtohs(header.header.header.headerSize)|header.size)&0x3) != 0) {
ALOGW("Bad resource table: header size 0x%x or total size 0x%x is not on an integer boundary\n",
(int)dtohs(header.header.header.headerSize),
(int)header.size);
return (mError=BAD_TYPE);
}
// header->dataEnd = ((const uint8_t*)header->header) + header->size;
header.dataEnd = header.size;
// Iterate through all chunks.
int curPackage = 0;
// const ResChunk_header* chunk =
// (const ResChunk_header*)(((const uint8_t*)header->header)
// + dtohs(header->header->header.headerSize));
ResChunk_header chunk =
new ResChunk_header(buf, dtohs(header.header.header.headerSize));
while (chunk != null && (chunk.myOffset()) <= (header.dataEnd -ResChunk_header.SIZEOF) &&
(chunk.myOffset()) <= (header.dataEnd -dtohl(chunk.size))) {
int err = validate_chunk(chunk, ResChunk_header.SIZEOF, header.dataEnd, "ResTable");
if (err != NO_ERROR) {
return (mError=err);
}
if (kDebugTableNoisy) {
ALOGV("Chunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%s\n",
dtohs(chunk.type), dtohs(chunk.headerSize), dtohl(chunk.size),
(Object)((chunk.myOffset()) - (header.header.myOffset())));
}
final int csize = dtohl(chunk.size);
final int ctype = dtohs(chunk.type);
if (ctype == RES_STRING_POOL_TYPE) {
if (header.values.getError() != NO_ERROR) {
// Only use the first string chunk; ignore any others that
// may appear.
err = header.values.setTo(chunk.myBuf(), chunk.myOffset(), csize, false);
if (err != NO_ERROR) {
return (mError=err);
}
} else {
ALOGW("Multiple string chunks found in resource table.");
}
} else if (ctype == RES_TABLE_PACKAGE_TYPE) {
if (curPackage >= dtohl(header.header.packageCount)) {
ALOGW("More package chunks were found than the %d declared in the header.",
dtohl(header.header.packageCount));
return (mError=BAD_TYPE);
}
if (parsePackage(
new ResTable_package(chunk.myBuf(), chunk.myOffset()), header, appAsLib, isSystemAsset) != NO_ERROR) {
return mError;
}
curPackage++;
} else {
ALOGW("Unknown chunk type 0x%x in table at 0x%x.\n",
ctype,
(chunk.myOffset()) - (header.header.myOffset()));
}
chunk = chunk.myOffset() + csize < header.dataEnd
? new ResChunk_header(chunk.myBuf(), chunk.myOffset() + csize)
: null;
}
if (curPackage < dtohl(header.header.packageCount)) {
ALOGW("Fewer package chunks (%d) were found than the %d declared in the header.",
(int)curPackage, dtohl(header.header.packageCount));
return (mError=BAD_TYPE);
}
mError = header.values.getError();
if (mError != NO_ERROR) {
ALOGW("No string values found in resource table!");
}
if (kDebugTableNoisy) {
ALOGV("Returning from add with mError=%d\n", mError);
}
return mError;
}
public final int getResource(int resID, Ref outValue, boolean mayBeBag, int density,
final Ref outSpecFlags, Ref outConfig)
{
if (mError != NO_ERROR) {
return mError;
}
final int p = getResourcePackageIndex(resID);
final int t = Res_GETTYPE(resID);
final int e = Res_GETENTRY(resID);
if (p < 0) {
if (Res_GETPACKAGE(resID)+1 == 0) {
ALOGW("No package identifier when getting value for resource number 0x%08x", resID);
} else {
ALOGW("No known package when getting value for resource number 0x%08x", resID);
}
return BAD_INDEX;
}
if (t < 0) {
ALOGW("No type identifier when getting value for resource number 0x%08x", resID);
return BAD_INDEX;
}
final PackageGroup grp = mPackageGroups.get(p);
if (grp == NULL) {
ALOGW("Bad identifier when getting value for resource number 0x%08x", resID);
return BAD_INDEX;
}
// Allow overriding density
ResTable_config desiredConfig = mParams;
if (density > 0) {
desiredConfig.density = density;
}
Entry entry = new Entry();
int err = getEntry(grp, t, e, desiredConfig, entry);
if (err != NO_ERROR) {
// Only log the failure when we're not running on the host as
// part of a tool. The caller will do its own logging.
return err;
}
if ((entry.entry.flags & ResTable_entry.FLAG_COMPLEX) != 0) {
if (!mayBeBag) {
ALOGW("Requesting resource 0x%08x failed because it is complex\n", resID);
}
return BAD_VALUE;
}
// const Res_value* value = reinterpret_cast(
// reinterpret_cast(entry.entry) + entry.entry->size);
Res_value value = new Res_value(entry.entry.myBuf(), entry.entry.myOffset() + entry.entry.size);
// outValue.size = dtohs(value.size);
// outValue.res0 = value.res0;
// outValue.dataType = value.dataType;
// outValue.data = dtohl(value.data);
outValue.set(value);
// The reference may be pointing to a resource in a shared library. These
// references have build-time generated package IDs. These ids may not match
// the actual package IDs of the corresponding packages in this ResTable.
// We need to fix the package ID based on a mapping.
if (grp.dynamicRefTable.lookupResourceValue(outValue) != NO_ERROR) {
ALOGW("Failed to resolve referenced package: 0x%08x", outValue.get().data);
return BAD_VALUE;
}
// if (kDebugTableNoisy) {
// size_t len;
// printf("Found value: pkg=0x%x, type=%d, str=%s, int=%d\n",
// entry.package.header.index,
// outValue.dataType,
// outValue.dataType == Res_value::TYPE_STRING ?
// String8(entry.package.header.values.stringAt(outValue.data, &len)).string() :
// "",
// outValue.data);
// }
if (outSpecFlags != null) {
outSpecFlags.set(entry.specFlags);
}
if (outConfig != null) {
outConfig.set(entry.config);
}
return entry._package_.header.index;
}
public final int resolveReference(Ref value, int blockIndex,
final Ref outLastRef) {
return resolveReference(value, blockIndex, outLastRef, null, null);
}
public final int resolveReference(Ref value, int blockIndex,
final Ref outLastRef, Ref inoutTypeSpecFlags) {
return resolveReference(value, blockIndex, outLastRef, inoutTypeSpecFlags, null);
}
public final int resolveReference(Ref value, int blockIndex,
final Ref outLastRef, Ref inoutTypeSpecFlags,
final Ref outConfig)
{
int count=0;
while (blockIndex >= 0 && value.get().dataType == DataType.REFERENCE.code()
&& value.get().data != 0 && count < 20) {
if (outLastRef != null) {
outLastRef.set(value.get().data);
}
final Ref newFlags = new Ref<>(0);
final int newIndex = getResource(value.get().data, value, true, 0,
newFlags, outConfig);
if (newIndex == BAD_INDEX) {
return BAD_INDEX;
}
if (kDebugTableTheme) {
ALOGI("Resolving reference 0x%x: newIndex=%d, type=0x%x, data=0x%x\n",
value.get().data, (int)newIndex, (int)value.get().dataType, value.get().data);
}
//printf("Getting reference 0x%08x: newIndex=%d\n", value.data, newIndex);
if (inoutTypeSpecFlags != null) {
inoutTypeSpecFlags.set(inoutTypeSpecFlags.get() | newFlags.get());
}
if (newIndex < 0) {
// This can fail if the resource being referenced is a style...
// in this case, just return the reference, and expect the
// caller to deal with.
return blockIndex;
}
blockIndex = newIndex;
count++;
}
return blockIndex;
}
private interface Compare {
boolean compare(ResTable_sparseTypeEntry a, ResTable_sparseTypeEntry b);
}
ResTable_sparseTypeEntry lower_bound(ResTable_sparseTypeEntry first, ResTable_sparseTypeEntry last,
ResTable_sparseTypeEntry value,
Compare comparator) {
int count = (last.myOffset() - first.myOffset()) / ResTable_sparseTypeEntry.SIZEOF;
int itOffset;
int step;
while (count > 0) {
itOffset = first.myOffset();
step = count / 2;
itOffset += step * ResTable_sparseTypeEntry.SIZEOF;
if (comparator.compare(new ResTable_sparseTypeEntry(first.myBuf(), itOffset), value)) {
itOffset += ResTable_sparseTypeEntry.SIZEOF;
first = new ResTable_sparseTypeEntry(first.myBuf(), itOffset);
} else {
count = step;
}
}
return first;
}
private int getEntry(
final PackageGroup packageGroup, int typeIndex, int entryIndex,
final ResTable_config config,
Entry outEntry)
{
final List 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 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 candidateConfigs = typeSpec.configs;
// List 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(
// reinterpret_cast(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(
// reinterpret_cast(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;
}
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 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 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 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;
}
public int getTableCookie(int index) {
return mHeaders.get(index).cookie;
}
void setParameters(ResTable_config params)
{
// AutoMutex _lock(mLock);
// AutoMutex _lock2(mFilteredConfigLock);
synchronized (mLock) {
synchronized (mFilteredConfigLock) {
if (kDebugTableGetEntry) {
ALOGI("Setting parameters: %s\n", params.toString());
}
mParams = params;
for (PackageGroup packageGroup : mPackageGroups.values()) {
if (kDebugTableNoisy) {
ALOGI("CLEARING BAGS FOR GROUP 0x%x!", packageGroup.id);
}
packageGroup.clearBagCache();
// Find which configurations match the set of parameters. This allows for a much
// faster lookup in getEntry() if the set of values is narrowed down.
//for (int t = 0; t < packageGroup.types.size(); t++) {
//if (packageGroup.types.get(t).isEmpty()) {
// continue;
// }
//
// List typeList = packageGroup.types.get(t);
for (List typeList : packageGroup.types.values()) {
if (typeList.isEmpty()) {
continue;
}
// Retrieve the cache entry for this type.
//TypeCacheEntry cacheEntry = packageGroup.typeCacheEntries.editItemAt(t);
for (int ts = 0; ts < typeList.size(); ts++) {
Type type = typeList.get(ts);
// std::shared_ptr> newFilteredConfigs =
// std::make_shared>();
List newFilteredConfigs = new ArrayList<>();
for (int ti = 0; ti < type.configs.size(); ti++) {
ResTable_config config = ResTable_config.fromDtoH(type.configs.get(ti).config);
if (config.match(mParams)) {
newFilteredConfigs.add(type.configs.get(ti));
}
}
if (kDebugTableNoisy) {
ALOGD("Updating pkg=0x%x type=0x%x with 0x%x filtered configs",
packageGroup.id, ts, newFilteredConfigs.size());
}
// todo: implement cache
// cacheEntry.filteredConfigs.add(newFilteredConfigs);
}
}
}
}
}
}
ResTable_config getParameters()
{
// mLock.lock();
synchronized (mLock) {
return mParams;
}
// mLock.unlock();
}
private static final Map sInternalNameToIdMap = new HashMap<>();
static {
sInternalNameToIdMap.put("^type", ResTable_map.ATTR_TYPE);
sInternalNameToIdMap.put("^l10n", ResTable_map.ATTR_L10N);
sInternalNameToIdMap.put("^min" , ResTable_map.ATTR_MIN);
sInternalNameToIdMap.put("^max", ResTable_map.ATTR_MAX);
sInternalNameToIdMap.put("^other", ResTable_map.ATTR_OTHER);
sInternalNameToIdMap.put("^zero", ResTable_map.ATTR_ZERO);
sInternalNameToIdMap.put("^one", ResTable_map.ATTR_ONE);
sInternalNameToIdMap.put("^two", ResTable_map.ATTR_TWO);
sInternalNameToIdMap.put("^few", ResTable_map.ATTR_FEW);
sInternalNameToIdMap.put("^many", ResTable_map.ATTR_MANY);
}
public int identifierForName(String name, String type, String packageName) {
return identifierForName(name, type, packageName, null);
}
public int identifierForName(String nameString, String type, String packageName,
final Ref outTypeSpecFlags) {
// if (kDebugTableSuperNoisy) {
// printf("Identifier for name: error=%d\n", mError);
// }
// // Check for internal resource identifier as the very first thing, so
// // that we will always find them even when there are no resources.
if (nameString.startsWith("^")) {
if (sInternalNameToIdMap.containsKey(nameString)) {
if (outTypeSpecFlags != null) {
outTypeSpecFlags.set(ResTable_typeSpec.SPEC_PUBLIC);
}
return sInternalNameToIdMap.get(nameString);
}
if (nameString.length() > 7)
if (nameString.substring(1, 6).equals("index_")) {
int index = Integer.getInteger(nameString.substring(7));
if (Res_CHECKID(index)) {
ALOGW("Array resource index: %d is too large.",
index);
return 0;
}
if (outTypeSpecFlags != null) {
outTypeSpecFlags.set(ResTable_typeSpec.SPEC_PUBLIC);
}
return Res_MAKEARRAY(index);
}
return 0;
}
if (mError != NO_ERROR) {
return 0;
}
// Figure out the package and type we are looking in...
// TODO(BC): The following code block was a best effort attempt to directly transliterate
// C++ code which uses pointer artihmetic. Consider replacing with simpler logic
boolean fakePublic = false;
char[] name = nameString.toCharArray();
int packageEnd = -1;
int typeEnd = -1;
int nameEnd = name.length;
int pIndex = 0;
while (pIndex < nameEnd) {
char p = name[pIndex];
if (p == ':') packageEnd = pIndex;
else if (p == '/') typeEnd = pIndex;
pIndex++;
}
int nameIndex = 0;
if (name[nameIndex] == '@') {
nameIndex++;
if (name[nameIndex] == '*') {
fakePublic = true;
nameIndex++;
}
}
if (nameIndex >= nameEnd) {
return 0;
}
if (packageEnd != -1) {
packageName = nameString.substring(nameIndex, packageEnd);
nameIndex = packageEnd+1;
} else if (packageName == null) {
return 0;
}
if (typeEnd != -1) {
type = nameString.substring(nameIndex, typeEnd);
nameIndex = typeEnd+1;
} else if (type == null) {
return 0;
}
if (nameIndex >= nameEnd) {
return 0;
}
nameString = nameString.substring(nameIndex, nameEnd);
// nameLen = nameEnd-name;
// if (kDebugTableNoisy) {
// printf("Looking for identifier: type=%s, name=%s, package=%s\n",
// String8(type, typeLen).string(),
// String8(name, nameLen).string(),
// String8(package, packageLen).string());
// }
final String attr = "attr";
final String attrPrivate = "^attr-private";
for (PackageGroup group : mPackageGroups.values()) {
if (!Objects.equals(packageName.trim(), group.name.trim())) {
if (kDebugTableNoisy) {
System.out.println(String.format("Skipping package group: %s\n", group.name));
}
continue;
}
for (Package pkg : group.packages) {
String targetType = type;
do {
int ti = pkg.typeStrings.indexOfString(targetType);
if (ti < 0) {
continue;
}
ti += pkg.typeIdOffset;
int identifier = findEntry(group, ti, nameString, outTypeSpecFlags);
if (identifier != 0) {
if (fakePublic && outTypeSpecFlags != null) {
outTypeSpecFlags.set(outTypeSpecFlags.get() | ResTable_typeSpec.SPEC_PUBLIC);
}
return identifier;
}
} while (attr.compareTo(targetType) == 0
&& ((targetType = attrPrivate) != null)
);
}
break;
}
return 0;
}
int findEntry(PackageGroup group, int typeIndex, String name, Ref outTypeSpecFlags) {
List 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;
}
//bool ResTable::expandResourceRef(const char16_t* refStr, size_t refLen,
// String16* outPackage,
// String16* outType,
// String16* outName,
// const String16* defType,
// const String16* defPackage,
// const char** outErrorMsg,
// bool* outPublicOnly)
//{
// const char16_t* packageEnd = NULL;
// const char16_t* typeEnd = NULL;
// const char16_t* p = refStr;
// const char16_t* const end = p + refLen;
// while (p < end) {
// if (*p == ':') packageEnd = p;
// else if (*p == '/') {
// typeEnd = p;
// break;
// }
// p++;
// }
// p = refStr;
// if (*p == '@') p++;
//
// if (outPublicOnly != NULL) {
// *outPublicOnly = true;
// }
// if (*p == '*') {
// p++;
// if (outPublicOnly != NULL) {
// *outPublicOnly = false;
// }
// }
//
// if (packageEnd) {
// *outPackage = String16(p, packageEnd-p);
// p = packageEnd+1;
// } else {
// if (!defPackage) {
// if (outErrorMsg) {
// *outErrorMsg = "No resource package specified";
// }
// return false;
// }
// *outPackage = *defPackage;
// }
// if (typeEnd) {
// *outType = String16(p, typeEnd-p);
// p = typeEnd+1;
// } else {
// if (!defType) {
// if (outErrorMsg) {
// *outErrorMsg = "No resource type specified";
// }
// return false;
// }
// *outType = *defType;
// }
// *outName = String16(p, end-p);
// if(**outPackage == 0) {
// if(outErrorMsg) {
// *outErrorMsg = "Resource package cannot be an empty string";
// }
// return false;
// }
// if(**outType == 0) {
// if(outErrorMsg) {
// *outErrorMsg = "Resource type cannot be an empty string";
// }
// return false;
// }
// if(**outName == 0) {
// if(outErrorMsg) {
// *outErrorMsg = "Resource id cannot be an empty string";
// }
// return false;
// }
// return true;
//}
//
//static uint32_t get_hex(char c, bool* outError)
//{
// if (c >= '0' && c <= '9') {
// return c - '0';
// } else if (c >= 'a' && c <= 'f') {
// return c - 'a' + 0xa;
// } else if (c >= 'A' && c <= 'F') {
// return c - 'A' + 0xa;
// }
// *outError = true;
// return 0;
//}
//
//struct unit_entry
//{
// const char* name;
// size_t len;
// uint8_t type;
// uint32_t unit;
// float scale;
//};
//
//static const unit_entry unitNames[] = {
// { "px", strlen("px"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PX, 1.0f },
// { "dip", strlen("dip"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f },
// { "dp", strlen("dp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f },
// { "sp", strlen("sp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_SP, 1.0f },
// { "pt", strlen("pt"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PT, 1.0f },
// { "in", strlen("in"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_IN, 1.0f },
// { "mm", strlen("mm"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_MM, 1.0f },
// { "%", strlen("%"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION, 1.0f/100 },
// { "%s", strlen("%s"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100 },
// { NULL, 0, 0, 0, 0 }
//};
//
//static bool parse_unit(const char* str, Res_value* outValue,
// float* outScale, const char** outEnd)
//{
// const char* end = str;
// while (*end != 0 && !isspace((unsigned char)*end)) {
// end++;
// }
// const size_t len = end-str;
//
// const char* realEnd = end;
// while (*realEnd != 0 && isspace((unsigned char)*realEnd)) {
// realEnd++;
// }
// if (*realEnd != 0) {
// return false;
// }
//
// const unit_entry* cur = unitNames;
// while (cur->name) {
// if (len == cur->len && strncmp(cur->name, str, len) == 0) {
// outValue->dataType = cur->type;
// outValue->data = cur->unit << Res_value::COMPLEX_UNIT_SHIFT;
// *outScale = cur->scale;
// *outEnd = end;
// //printf("Found unit %s for %s\n", cur->name, str);
// return true;
// }
// cur++;
// }
//
// return false;
//}
//
//bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue)
//{
// while (len > 0 && isspace16(*s)) {
// s++;
// len--;
// }
//
// if (len <= 0) {
// return false;
// }
//
// size_t i = 0;
// int64_t val = 0;
// bool neg = false;
//
// if (*s == '-') {
// neg = true;
// i++;
// }
//
// if (s[i] < '0' || s[i] > '9') {
// return false;
// }
//
// static_assert(std::is_same::value,
// "Res_value::data_type has changed. The range checks in this "
// "function are no longer correct.");
//
// // Decimal or hex?
// bool isHex;
// if (len > 1 && s[i] == '0' && s[i+1] == 'x') {
// isHex = true;
// i += 2;
//
// if (neg) {
// return false;
// }
//
// if (i == len) {
// // Just u"0x"
// return false;
// }
//
// bool error = false;
// while (i < len && !error) {
// val = (val*16) + get_hex(s[i], &error);
// i++;
//
// if (val > std::numeric_limits::max()) {
// return false;
// }
// }
// if (error) {
// return false;
// }
// } else {
// isHex = false;
// while (i < len) {
// if (s[i] < '0' || s[i] > '9') {
// return false;
// }
// val = (val*10) + s[i]-'0';
// i++;
//
// if ((neg && -val < std::numeric_limits::min()) ||
// (!neg && val > std::numeric_limits::max())) {
// return false;
// }
// }
// }
//
// if (neg) val = -val;
//
// while (i < len && isspace16(s[i])) {
// i++;
// }
//
// if (i != len) {
// return false;
// }
//
// if (outValue) {
// outValue->dataType =
// isHex ? outValue->TYPE_INT_HEX : outValue->TYPE_INT_DEC;
// outValue->data = static_cast(val);
// }
// return true;
//}
//
//bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue)
//{
// return U16StringToInt(s, len, outValue);
//}
//
//bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue)
//{
// while (len > 0 && isspace16(*s)) {
// s++;
// len--;
// }
//
// if (len <= 0) {
// return false;
// }
//
// char buf[128];
// int i=0;
// while (len > 0 && *s != 0 && i < 126) {
// if (*s > 255) {
// return false;
// }
// buf[i++] = *s++;
// len--;
// }
//
// if (len > 0) {
// return false;
// }
// if ((buf[0] < '0' || buf[0] > '9') && buf[0] != '.' && buf[0] != '-' && buf[0] != '+') {
// return false;
// }
//
// buf[i] = 0;
// const char* end;
// float f = strtof(buf, (char**)&end);
//
// if (*end != 0 && !isspace((unsigned char)*end)) {
// // Might be a unit...
// float scale;
// if (parse_unit(end, outValue, &scale, &end)) {
// f *= scale;
// const bool neg = f < 0;
// if (neg) f = -f;
// uint64_t bits = (uint64_t)(f*(1<<23)+.5f);
// uint32_t radix;
// uint32_t shift;
// if ((bits&0x7fffff) == 0) {
// // Always use 23p0 if there is no fraction, just to make
// // things easier to read.
// radix = Res_value::COMPLEX_RADIX_23p0;
// shift = 23;
// } else if ((bits&0xffffffffff800000LL) == 0) {
// // Magnitude is zero -- can fit in 0 bits of precision.
// radix = Res_value::COMPLEX_RADIX_0p23;
// shift = 0;
// } else if ((bits&0xffffffff80000000LL) == 0) {
// // Magnitude can fit in 8 bits of precision.
// radix = Res_value::COMPLEX_RADIX_8p15;
// shift = 8;
// } else if ((bits&0xffffff8000000000LL) == 0) {
// // Magnitude can fit in 16 bits of precision.
// radix = Res_value::COMPLEX_RADIX_16p7;
// shift = 16;
// } else {
// // Magnitude needs entire range, so no fractional part.
// radix = Res_value::COMPLEX_RADIX_23p0;
// shift = 23;
// }
// int32_t mantissa = (int32_t)(
// (bits>>shift) & Res_value::COMPLEX_MANTISSA_MASK);
// if (neg) {
// mantissa = (-mantissa) & Res_value::COMPLEX_MANTISSA_MASK;
// }
// outValue->data |=
// (radix<data);
// return true;
// }
// return false;
// }
//
// while (*end != 0 && isspace((unsigned char)*end)) {
// end++;
// }
//
// if (*end == 0) {
// if (outValue) {
// outValue->dataType = outValue->TYPE_FLOAT;
// *(float*)(&outValue->data) = f;
// return true;
// }
// }
//
// return false;
//}
//
//bool ResTable::stringToValue(Res_value* outValue, String16* outString,
// const char16_t* s, size_t len,
// bool preserveSpaces, bool coerceType,
// uint32_t attrID,
// const String16* defType,
// const String16* defPackage,
// Accessor* accessor,
// void* accessorCookie,
// uint32_t attrType,
// bool enforcePrivate) const
//{
// bool localizationSetting = accessor != NULL && accessor->getLocalizationSetting();
// const char* errorMsg = NULL;
//
// outValue->size = sizeof(Res_value);
// outValue->res0 = 0;
//
// // First strip leading/trailing whitespace. Do this before handling
// // escapes, so they can be used to force whitespace into the string.
// if (!preserveSpaces) {
// while (len > 0 && isspace16(*s)) {
// s++;
// len--;
// }
// while (len > 0 && isspace16(s[len-1])) {
// len--;
// }
// // If the string ends with '\', then we keep the space after it.
// if (len > 0 && s[len-1] == '\\' && s[len] != 0) {
// len++;
// }
// }
//
// //printf("Value for: %s\n", String8(s, len).string());
//
// uint32_t l10nReq = ResTable_map::L10N_NOT_REQUIRED;
// uint32_t attrMin = 0x80000000, attrMax = 0x7fffffff;
// bool fromAccessor = false;
// if (attrID != 0 && !Res_INTERNALID(attrID)) {
// const ssize_t p = getResourcePackageIndex(attrID);
// const bag_entry* bag;
// ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
// //printf("For attr 0x%08x got bag of %d\n", attrID, cnt);
// if (cnt >= 0) {
// while (cnt > 0) {
// //printf("Entry 0x%08x = 0x%08x\n", bag->map.name.ident, bag->map.value.data);
// switch (bag->map.name.ident) {
// case ResTable_map::ATTR_TYPE:
// attrType = bag->map.value.data;
// break;
// case ResTable_map::ATTR_MIN:
// attrMin = bag->map.value.data;
// break;
// case ResTable_map::ATTR_MAX:
// attrMax = bag->map.value.data;
// break;
// case ResTable_map::ATTR_L10N:
// l10nReq = bag->map.value.data;
// break;
// }
// bag++;
// cnt--;
// }
// unlockBag(bag);
// } else if (accessor && accessor->getAttributeType(attrID, &attrType)) {
// fromAccessor = true;
// if (attrType == ResTable_map::TYPE_ENUM
// || attrType == ResTable_map::TYPE_FLAGS
// || attrType == ResTable_map::TYPE_INTEGER) {
// accessor->getAttributeMin(attrID, &attrMin);
// accessor->getAttributeMax(attrID, &attrMax);
// }
// if (localizationSetting) {
// l10nReq = accessor->getAttributeL10N(attrID);
// }
// }
// }
//
// const bool canStringCoerce =
// coerceType && (attrType&ResTable_map::TYPE_STRING) != 0;
//
// if (*s == '@') {
// outValue->dataType = outValue->TYPE_REFERENCE;
//
// // Note: we don't check attrType here because the reference can
// // be to any other type; we just need to count on the client making
// // sure the referenced type is correct.
//
// //printf("Looking up ref: %s\n", String8(s, len).string());
//
// // It's a reference!
// if (len == 5 && s[1]=='n' && s[2]=='u' && s[3]=='l' && s[4]=='l') {
// // Special case @null as undefined. This will be converted by
// // AssetManager to TYPE_NULL with data DATA_NULL_UNDEFINED.
// outValue->data = 0;
// return true;
// } else if (len == 6 && s[1]=='e' && s[2]=='m' && s[3]=='p' && s[4]=='t' && s[5]=='y') {
// // Special case @empty as explicitly defined empty value.
// outValue->dataType = Res_value::TYPE_NULL;
// outValue->data = Res_value::DATA_NULL_EMPTY;
// return true;
// } else {
// bool createIfNotFound = false;
// const char16_t* resourceRefName;
// int resourceNameLen;
// if (len > 2 && s[1] == '+') {
// createIfNotFound = true;
// resourceRefName = s + 2;
// resourceNameLen = len - 2;
// } else if (len > 2 && s[1] == '*') {
// enforcePrivate = false;
// resourceRefName = s + 2;
// resourceNameLen = len - 2;
// } else {
// createIfNotFound = false;
// resourceRefName = s + 1;
// resourceNameLen = len - 1;
// }
// String16 package, type, name;
// if (!expandResourceRef(resourceRefName,resourceNameLen, &package, &type, &name,
// defType, defPackage, &errorMsg)) {
// if (accessor != NULL) {
// accessor->reportError(accessorCookie, errorMsg);
// }
// return false;
// }
//
// uint32_t specFlags = 0;
// uint32_t rid = identifierForName(name.string(), name.size(), type.string(),
// type.size(), package.string(), package.size(), &specFlags);
// if (rid != 0) {
// if (enforcePrivate) {
// if (accessor == NULL || accessor->getAssetsPackage() != package) {
// if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) {
// if (accessor != NULL) {
// accessor->reportError(accessorCookie, "Resource is not public.");
// }
// return false;
// }
// }
// }
//
// if (accessor) {
// rid = Res_MAKEID(
// accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
// Res_GETTYPE(rid), Res_GETENTRY(rid));
// if (kDebugTableNoisy) {
// ALOGI("Incl %s:%s/%s: 0x%08x\n",
// String8(package).string(), String8(type).string(),
// String8(name).string(), rid);
// }
// }
//
// uint32_t packageId = Res_GETPACKAGE(rid) + 1;
// if (packageId != APP_PACKAGE_ID && packageId != SYS_PACKAGE_ID) {
// outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
// }
// outValue->data = rid;
// return true;
// }
//
// if (accessor) {
// uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name,
// createIfNotFound);
// if (rid != 0) {
// if (kDebugTableNoisy) {
// ALOGI("Pckg %s:%s/%s: 0x%08x\n",
// String8(package).string(), String8(type).string(),
// String8(name).string(), rid);
// }
// uint32_t packageId = Res_GETPACKAGE(rid) + 1;
// if (packageId == 0x00) {
// outValue->data = rid;
// outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
// return true;
// } else if (packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID) {
// // We accept packageId's generated as 0x01 in order to support
// // building the android system resources
// outValue->data = rid;
// return true;
// }
// }
// }
// }
//
// if (accessor != NULL) {
// accessor->reportError(accessorCookie, "No resource found that matches the given name");
// }
// return false;
// }
//
// // if we got to here, and localization is required and it's not a reference,
// // complain and bail.
// if (l10nReq == ResTable_map::L10N_SUGGESTED) {
// if (localizationSetting) {
// if (accessor != NULL) {
// accessor->reportError(accessorCookie, "This attribute must be localized.");
// }
// }
// }
//
// if (*s == '#') {
// // It's a color! Convert to an integer of the form 0xaarrggbb.
// uint32_t color = 0;
// bool error = false;
// if (len == 4) {
// outValue->dataType = outValue->TYPE_INT_COLOR_RGB4;
// color |= 0xFF000000;
// color |= get_hex(s[1], &error) << 20;
// color |= get_hex(s[1], &error) << 16;
// color |= get_hex(s[2], &error) << 12;
// color |= get_hex(s[2], &error) << 8;
// color |= get_hex(s[3], &error) << 4;
// color |= get_hex(s[3], &error);
// } else if (len == 5) {
// outValue->dataType = outValue->TYPE_INT_COLOR_ARGB4;
// color |= get_hex(s[1], &error) << 28;
// color |= get_hex(s[1], &error) << 24;
// color |= get_hex(s[2], &error) << 20;
// color |= get_hex(s[2], &error) << 16;
// color |= get_hex(s[3], &error) << 12;
// color |= get_hex(s[3], &error) << 8;
// color |= get_hex(s[4], &error) << 4;
// color |= get_hex(s[4], &error);
// } else if (len == 7) {
// outValue->dataType = outValue->TYPE_INT_COLOR_RGB8;
// color |= 0xFF000000;
// color |= get_hex(s[1], &error) << 20;
// color |= get_hex(s[2], &error) << 16;
// color |= get_hex(s[3], &error) << 12;
// color |= get_hex(s[4], &error) << 8;
// color |= get_hex(s[5], &error) << 4;
// color |= get_hex(s[6], &error);
// } else if (len == 9) {
// outValue->dataType = outValue->TYPE_INT_COLOR_ARGB8;
// color |= get_hex(s[1], &error) << 28;
// color |= get_hex(s[2], &error) << 24;
// color |= get_hex(s[3], &error) << 20;
// color |= get_hex(s[4], &error) << 16;
// color |= get_hex(s[5], &error) << 12;
// color |= get_hex(s[6], &error) << 8;
// color |= get_hex(s[7], &error) << 4;
// color |= get_hex(s[8], &error);
// } else {
// error = true;
// }
// if (!error) {
// if ((attrType&ResTable_map::TYPE_COLOR) == 0) {
// if (!canStringCoerce) {
// if (accessor != NULL) {
// accessor->reportError(accessorCookie,
// "Color types not allowed");
// }
// return false;
// }
// } else {
// outValue->data = color;
// //printf("Color input=%s, output=0x%x\n", String8(s, len).string(), color);
// return true;
// }
// } else {
// if ((attrType&ResTable_map::TYPE_COLOR) != 0) {
// if (accessor != NULL) {
// accessor->reportError(accessorCookie, "Color value not valid --"
// " must be #rgb, #argb, #rrggbb, or #aarrggbb");
// }
// #if 0
// fprintf(stderr, "%s: Color ID %s value %s is not valid\n",
// "Resource File", //(const char*)in->getPrintableSource(),
// String8(*curTag).string(),
// String8(s, len).string());
// #endif
// return false;
// }
// }
// }
//
// if (*s == '?') {
// outValue->dataType = outValue->TYPE_ATTRIBUTE;
//
// // Note: we don't check attrType here because the reference can
// // be to any other type; we just need to count on the client making
// // sure the referenced type is correct.
//
// //printf("Looking up attr: %s\n", String8(s, len).string());
//
// static const String16 attr16("attr");
// String16 package, type, name;
// if (!expandResourceRef(s+1, len-1, &package, &type, &name,
// &attr16, defPackage, &errorMsg)) {
// if (accessor != NULL) {
// accessor->reportError(accessorCookie, errorMsg);
// }
// return false;
// }
//
// //printf("Pkg: %s, Type: %s, Name: %s\n",
// // String8(package).string(), String8(type).string(),
// // String8(name).string());
// uint32_t specFlags = 0;
// uint32_t rid =
// identifierForName(name.string(), name.size(),
// type.string(), type.size(),
// package.string(), package.size(), &specFlags);
// if (rid != 0) {
// if (enforcePrivate) {
// if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) {
// if (accessor != NULL) {
// accessor->reportError(accessorCookie, "Attribute is not public.");
// }
// return false;
// }
// }
//
// if (accessor) {
// rid = Res_MAKEID(
// accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
// Res_GETTYPE(rid), Res_GETENTRY(rid));
// }
//
// uint32_t packageId = Res_GETPACKAGE(rid) + 1;
// if (packageId != APP_PACKAGE_ID && packageId != SYS_PACKAGE_ID) {
// outValue->dataType = Res_value::TYPE_DYNAMIC_ATTRIBUTE;
// }
// outValue->data = rid;
// return true;
// }
//
// if (accessor) {
// uint32_t rid = accessor->getCustomResource(package, type, name);
// if (rid != 0) {
// uint32_t packageId = Res_GETPACKAGE(rid) + 1;
// if (packageId == 0x00) {
// outValue->data = rid;
// outValue->dataType = Res_value::TYPE_DYNAMIC_ATTRIBUTE;
// return true;
// } else if (packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID) {
// // We accept packageId's generated as 0x01 in order to support
// // building the android system resources
// outValue->data = rid;
// return true;
// }
// }
// }
//
// if (accessor != NULL) {
// accessor->reportError(accessorCookie, "No resource found that matches the given name");
// }
// return false;
// }
//
// if (stringToInt(s, len, outValue)) {
// if ((attrType&ResTable_map::TYPE_INTEGER) == 0) {
// // If this type does not allow integers, but does allow floats,
// // fall through on this error case because the float type should
// // be able to accept any integer value.
// if (!canStringCoerce && (attrType&ResTable_map::TYPE_FLOAT) == 0) {
// if (accessor != NULL) {
// accessor->reportError(accessorCookie, "Integer types not allowed");
// }
// return false;
// }
// } else {
// if (((int32_t)outValue->data) < ((int32_t)attrMin)
// || ((int32_t)outValue->data) > ((int32_t)attrMax)) {
// if (accessor != NULL) {
// accessor->reportError(accessorCookie, "Integer value out of range");
// }
// return false;
// }
// return true;
// }
// }
//
// if (stringToFloat(s, len, outValue)) {
// if (outValue->dataType == Res_value::TYPE_DIMENSION) {
// if ((attrType&ResTable_map::TYPE_DIMENSION) != 0) {
// return true;
// }
// if (!canStringCoerce) {
// if (accessor != NULL) {
// accessor->reportError(accessorCookie, "Dimension types not allowed");
// }
// return false;
// }
// } else if (outValue->dataType == Res_value::TYPE_FRACTION) {
// if ((attrType&ResTable_map::TYPE_FRACTION) != 0) {
// return true;
// }
// if (!canStringCoerce) {
// if (accessor != NULL) {
// accessor->reportError(accessorCookie, "Fraction types not allowed");
// }
// return false;
// }
// } else if ((attrType&ResTable_map::TYPE_FLOAT) == 0) {
// if (!canStringCoerce) {
// if (accessor != NULL) {
// accessor->reportError(accessorCookie, "Float types not allowed");
// }
// return false;
// }
// } else {
// return true;
// }
// }
//
// if (len == 4) {
// if ((s[0] == 't' || s[0] == 'T') &&
// (s[1] == 'r' || s[1] == 'R') &&
// (s[2] == 'u' || s[2] == 'U') &&
// (s[3] == 'e' || s[3] == 'E')) {
// if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) {
// if (!canStringCoerce) {
// if (accessor != NULL) {
// accessor->reportError(accessorCookie, "Boolean types not allowed");
// }
// return false;
// }
// } else {
// outValue->dataType = outValue->TYPE_INT_BOOLEAN;
// outValue->data = (uint32_t)-1;
// return true;
// }
// }
// }
//
// if (len == 5) {
// if ((s[0] == 'f' || s[0] == 'F') &&
// (s[1] == 'a' || s[1] == 'A') &&
// (s[2] == 'l' || s[2] == 'L') &&
// (s[3] == 's' || s[3] == 'S') &&
// (s[4] == 'e' || s[4] == 'E')) {
// if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) {
// if (!canStringCoerce) {
// if (accessor != NULL) {
// accessor->reportError(accessorCookie, "Boolean types not allowed");
// }
// return false;
// }
// } else {
// outValue->dataType = outValue->TYPE_INT_BOOLEAN;
// outValue->data = 0;
// return true;
// }
// }
// }
//
// if ((attrType&ResTable_map::TYPE_ENUM) != 0) {
// const ssize_t p = getResourcePackageIndex(attrID);
// const bag_entry* bag;
// ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
// //printf("Got %d for enum\n", cnt);
// if (cnt >= 0) {
// resource_name rname;
// while (cnt > 0) {
// if (!Res_INTERNALID(bag->map.name.ident)) {
// //printf("Trying attr #%08x\n", bag->map.name.ident);
// if (getResourceName(bag->map.name.ident, false, &rname)) {
// #if 0
// printf("Matching %s against %s (0x%08x)\n",
// String8(s, len).string(),
// String8(rname.name, rname.nameLen).string(),
// bag->map.name.ident);
// #endif
// if (strzcmp16(s, len, rname.name, rname.nameLen) == 0) {
// outValue->dataType = bag->map.value.dataType;
// outValue->data = bag->map.value.data;
// unlockBag(bag);
// return true;
// }
// }
//
// }
// bag++;
// cnt--;
// }
// unlockBag(bag);
// }
//
// if (fromAccessor) {
// if (accessor->getAttributeEnum(attrID, s, len, outValue)) {
// return true;
// }
// }
// }
//
// if ((attrType&ResTable_map::TYPE_FLAGS) != 0) {
// const ssize_t p = getResourcePackageIndex(attrID);
// const bag_entry* bag;
// ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
// //printf("Got %d for flags\n", cnt);
// if (cnt >= 0) {
// bool failed = false;
// resource_name rname;
// outValue->dataType = Res_value::TYPE_INT_HEX;
// outValue->data = 0;
// const char16_t* end = s + len;
// const char16_t* pos = s;
// while (pos < end && !failed) {
// const char16_t* start = pos;
// pos++;
// while (pos < end && *pos != '|') {
// pos++;
// }
// //printf("Looking for: %s\n", String8(start, pos-start).string());
// const bag_entry* bagi = bag;
// ssize_t i;
// for (i=0; imap.name.ident)) {
// //printf("Trying attr #%08x\n", bagi->map.name.ident);
// if (getResourceName(bagi->map.name.ident, false, &rname)) {
// #if 0
// printf("Matching %s against %s (0x%08x)\n",
// String8(start,pos-start).string(),
// String8(rname.name, rname.nameLen).string(),
// bagi->map.name.ident);
// #endif
// if (strzcmp16(start, pos-start, rname.name, rname.nameLen) == 0) {
// outValue->data |= bagi->map.value.data;
// break;
// }
// }
// }
// }
// if (i >= cnt) {
// // Didn't find this flag identifier.
// failed = true;
// }
// if (pos < end) {
// pos++;
// }
// }
// unlockBag(bag);
// if (!failed) {
// //printf("Final flag value: 0x%lx\n", outValue->data);
// return true;
// }
// }
//
//
// if (fromAccessor) {
// if (accessor->getAttributeFlags(attrID, s, len, outValue)) {
// //printf("Final flag value: 0x%lx\n", outValue->data);
// return true;
// }
// }
// }
//
// if ((attrType&ResTable_map::TYPE_STRING) == 0) {
// if (accessor != NULL) {
// accessor->reportError(accessorCookie, "String types not allowed");
// }
// return false;
// }
//
// // Generic string handling...
// outValue->dataType = outValue->TYPE_STRING;
// if (outString) {
// bool failed = collectString(outString, s, len, preserveSpaces, &errorMsg);
// if (accessor != NULL) {
// accessor->reportError(accessorCookie, errorMsg);
// }
// return failed;
// }
//
// return true;
//}
//
//bool ResTable::collectString(String16* outString,
// const char16_t* s, size_t len,
// bool preserveSpaces,
// const char** outErrorMsg,
// bool append)
//{
// String16 tmp;
//
// char quoted = 0;
// const char16_t* p = s;
// while (p < (s+len)) {
// while (p < (s+len)) {
// const char16_t c = *p;
// if (c == '\\') {
// break;
// }
// if (!preserveSpaces) {
// if (quoted == 0 && isspace16(c)
// && (c != ' ' || isspace16(*(p+1)))) {
// break;
// }
// if (c == '"' && (quoted == 0 || quoted == '"')) {
// break;
// }
// if (c == '\'' && (quoted == 0 || quoted == '\'')) {
// /*
// * In practice, when people write ' instead of \'
// * in a string, they are doing it by accident
// * instead of really meaning to use ' as a quoting
// * character. Warn them so they don't lose it.
// */
// if (outErrorMsg) {
// *outErrorMsg = "Apostrophe not preceded by \\";
// }
// return false;
// }
// }
// p++;
// }
// if (p < (s+len)) {
// if (p > s) {
// tmp.append(String16(s, p-s));
// }
// if (!preserveSpaces && (*p == '"' || *p == '\'')) {
// if (quoted == 0) {
// quoted = *p;
// } else {
// quoted = 0;
// }
// p++;
// } else if (!preserveSpaces && isspace16(*p)) {
// // Space outside of a quote -- consume all spaces and
// // leave a single plain space char.
// tmp.append(String16(" "));
// p++;
// while (p < (s+len) && isspace16(*p)) {
// p++;
// }
// } else if (*p == '\\') {
// p++;
// if (p < (s+len)) {
// switch (*p) {
// case 't':
// tmp.append(String16("\t"));
// break;
// case 'n':
// tmp.append(String16("\n"));
// break;
// case '#':
// tmp.append(String16("#"));
// break;
// case '@':
// tmp.append(String16("@"));
// break;
// case '?':
// tmp.append(String16("?"));
// break;
// case '"':
// tmp.append(String16("\""));
// break;
// case '\'':
// tmp.append(String16("'"));
// break;
// case '\\':
// tmp.append(String16("\\"));
// break;
// case 'u':
// {
// char16_t chr = 0;
// int i = 0;
// while (i < 4 && p[1] != 0) {
// p++;
// i++;
// int c;
// if (*p >= '0' && *p <= '9') {
// c = *p - '0';
// } else if (*p >= 'a' && *p <= 'f') {
// c = *p - 'a' + 10;
// } else if (*p >= 'A' && *p <= 'F') {
// c = *p - 'A' + 10;
// } else {
// if (outErrorMsg) {
// *outErrorMsg = "Bad character in \\u unicode escape sequence";
// }
// return false;
// }
// chr = (chr<<4) | c;
// }
// tmp.append(String16(&chr, 1));
// } break;
// default:
// // ignore unknown escape chars.
// break;
// }
// p++;
// }
// }
// len -= (p-s);
// s = p;
// }
// }
//
// if (tmp.size() != 0) {
// if (len > 0) {
// tmp.append(String16(s, len));
// }
// if (append) {
// outString->append(tmp);
// } else {
// outString->setTo(tmp);
// }
// } else {
// if (append) {
// outString->append(String16(s, len));
// } else {
// outString->setTo(s, len);
// }
// }
//
// return true;
//}
public int getBasePackageCount()
{
if (mError != NO_ERROR) {
return 0;
}
return mPackageGroups.size();
}
public String getBasePackageName(int idx)
{
if (mError != NO_ERROR) {
return null;
}
LOG_FATAL_IF(idx >= mPackageGroups.size(),
"Requested package index %d past package count %d",
(int)idx, (int)mPackageGroups.size());
return mPackageGroups.get(keyFor(idx)).name;
}
public int getBasePackageId(int idx)
{
if (mError != NO_ERROR) {
return 0;
}
LOG_FATAL_IF(idx >= mPackageGroups.size(),
"Requested package index %d past package count %d",
(int)idx, (int)mPackageGroups.size());
return mPackageGroups.get(keyFor(idx)).id;
}
int getLastTypeIdForPackage(int idx)
{
if (mError != NO_ERROR) {
return 0;
}
LOG_FATAL_IF(idx >= mPackageGroups.size(),
"Requested package index %d past package count %d",
(int)idx, (int)mPackageGroups.size());
PackageGroup group = mPackageGroups.get(keyFor(idx));
return group.largestTypeId;
}
int keyFor(int idx) {
ArrayList keys = new ArrayList<>(mPackageGroups.keySet());
Collections.sort(keys);
return keys.get(idx);
}
public int getTableCount() {
return mHeaders.size();
}
public ResStringPool getTableStringBlock(int index) {
return mHeaders.get(index).values;
}
public DynamicRefTable getDynamicRefTableForCookie(int cookie) {
for (PackageGroup pg : mPackageGroups.values()) {
int M = pg.packages.size();
for (int j = 0; j < M; j++) {
if (pg.packages.get(j).header.cookie == cookie) {
return pg.dynamicRefTable;
}
}
}
return null;
}
public boolean getResourceName(int resID, boolean allowUtf8, ResourceName outName) {
if (mError != NO_ERROR) {
return false;
}
final int p = getResourcePackageIndex(resID);
final int t = Res_GETTYPE(resID);
final int e = Res_GETENTRY(resID);
if (p < 0) {
if (Res_GETPACKAGE(resID)+1 == 0) {
ALOGW("No package identifier when getting name for resource number 0x%08x", resID);
}
return false;
}
if (t < 0) {
ALOGW("No type identifier when getting name for resource number 0x%08x", resID);
return false;
}
final PackageGroup grp = mPackageGroups.get(p);
if (grp == NULL) {
ALOGW("Bad identifier when getting name for resource number 0x%08x", resID);
return false;
}
Entry entry = new Entry();
int err = getEntry(grp, t, e, null, entry);
if (err != NO_ERROR) {
return false;
}
outName.packageName = grp.name;
outName.type = entry.typeStr.string();
if (outName.type == null) {
return false;
}
outName.name = entry.keyStr.string();
if (outName.name == null) {
return false;
}
return true;
}
String getResourceName(int resId) {
ResourceName outName = new ResourceName();
if (getResourceName(resId, true, outName)) {
return outName.toString();
}
throw new IllegalArgumentException("Unknown resource id " + resId);
}
// A group of objects describing a particular resource package.
// The first in 'package' is always the root object (from the resource
// table that defined the package); the ones after are skins on top of it.
// from ResourceTypes.cpp struct ResTable::PackageGroup
public static class PackageGroup
{
public PackageGroup(
ResTable _owner, final String _name, int _id,
boolean appAsLib, boolean _isSystemAsset, boolean _isDynamic)
// : owner(_owner)
// , name(_name)
// , id(_id)
// , largestTypeId(0)
// , dynamicRefTable(static_cast(_id), appAsLib)
// , isSystemAsset(_isSystemAsset)
{
this.owner = _owner;
this.name = _name;
this.id = _id;
this.dynamicRefTable = new DynamicRefTable((byte) _id, appAsLib);
this.isSystemAsset = _isSystemAsset;
this.isDynamic = _isDynamic;
}
// ~PackageGroup() {
// clearBagCache();
// final int numTypes = types.size();
// for (int i = 0; i < numTypes; i++) {
// final List typeList = types.get(i);
// final int numInnerTypes = typeList.size();
// for (int j = 0; j < numInnerTypes; j++) {
// if (typeList.get(j)._package_.owner == owner) {
// delete typeList[j];
// }
// }
// typeList.clear();
// }
//
// final int N = packages.size();
// for (int i=0; i typeList = types.get(i);
// if (!typeList.isEmpty()) {
// TypeCacheEntry cacheEntry = typeCacheEntries.editItemAt(i);
//
// // Reset the filtered configurations.
// cacheEntry.filteredConfigs.clear();
//
// bag_set[][] typeBags = cacheEntry.cachedBags;
// if (kDebugTableNoisy) {
// printf("typeBags=%s\n", typeBags);
// }
//
// if (isTruthy(typeBags)) {
// final int N = typeList.get(0).entryCount;
// if (kDebugTableNoisy) {
// printf("type.entryCount=0x%x\n", N);
// }
// for (int j = 0; j < N; j++) {
// if (typeBags[j] && typeBags[j] != (bag_set *) 0xFFFFFFFF){
// free(typeBags[j]);
// }
// }
// free(typeBags);
// cacheEntry.cachedBags = NULL;
// }
// }
// }
}
private void printf(String message, Object... arguments) {
System.out.print(String.format(message, arguments));
}
// long findType16(final String type, int len) {
// final int N = packages.size();
// for (int i = 0; i < N; i++) {
// sint index = packages[i].typeStrings.indexOfString(type, len);
// if (index >= 0) {
// return index + packages[i].typeIdOffset;
// }
// }
// return -1;
// }
final ResTable owner;
final String name;
final int id;
// This is mainly used to keep track of the loaded packages
// and to clean them up properly. Accessing resources happens from
// the 'types' array.
List packages = new ArrayList<>();
public final Map> types = new HashMap<>();
byte largestTypeId;
// Cached objects dependent on the parameters/configuration of this ResTable.
// Gets cleared whenever the parameters/configuration changes.
// These are stored here in a parallel structure because the data in `types` may
// be shared by other ResTable's (framework resources are shared this way).
ByteBucketArray typeCacheEntries =
new ByteBucketArray(new TypeCacheEntry()) {
@Override
TypeCacheEntry newInstance() {
return new TypeCacheEntry();
}
};
// The table mapping dynamic references to resolved references for
// this package group.
// TODO: We may be able to support dynamic references in overlays
// by having these tables in a per-package scope rather than
// per-package-group.
DynamicRefTable dynamicRefTable;
// If the package group comes from a system asset. Used in
// determining non-system locales.
final boolean isSystemAsset;
final boolean isDynamic;
}
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// struct ResTable::Header
public static class Header
{
// Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL),
// resourceIDMap(NULL), resourceIDMapSize(0) { }
public Header(ResTable owner) {
this.owner = owner;
}
// ~Header()
// {
// free(resourceIDMap);
// }
ResTable owner;
byte[] ownedData;
ResTable_header header;
int size;
int dataEnd;
int index;
int cookie;
ResStringPool values = new ResStringPool();
int[] resourceIDMap;
int resourceIDMapSize;
};
public static class Entry {
ResTable_config config;
ResTable_entry entry;
ResTable_type type;
int specFlags;
Package _package_;
StringPoolRef typeStr;
StringPoolRef keyStr;
}
// struct ResTable::DataType
public static class Type {
final Header header;
final Package _package_;
public final int entryCount;
public ResTable_typeSpec typeSpec;
public int[] typeSpecFlags;
public IdmapEntries idmapEntries = new IdmapEntries();
public List configs;
public Type(final Header _header, final Package _package, int count)
// : header(_header), package(_package), entryCount(count),
// typeSpec(NULL), typeSpecFlags(NULL) { }
{
this.header = _header;
_package_ = _package;
this.entryCount = count;
this.typeSpec = null;
this.typeSpecFlags = null;
this.configs = new ArrayList<>();
}
}
// struct ResTable::Package
public static class Package {
// Package(ResTable* _owner, final Header* _header, final ResTable_package* _package)
// : owner(_owner), header(_header), package(_package), typeIdOffset(0) {
// if (dtohs(package.header.headerSize) == sizeof(package)) {
// // The package structure is the same size as the definition.
// // This means it contains the typeIdOffset field.
// typeIdOffset = package.typeIdOffset;
// }
public Package(ResTable owner, Header header, ResTable_package _package) {
this.owner = owner;
this.header = header;
this._package_ = _package;
}
final ResTable owner;
final Header header;
final ResTable_package _package_;
ResStringPool typeStrings = new ResStringPool();
ResStringPool keyStrings = new ResStringPool();
int typeIdOffset;
};
public static class bag_entry {
public int stringBlock;
public ResTable_map map = new ResTable_map();
}
public void lock() {
try {
mLock.acquire();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public void unlock() {
mLock.release();
}
public int lockBag(int resID, Ref outBag) {
lock();
int err = getBagLocked(resID, outBag, null);
if (err < NO_ERROR) {
//printf("*** get failed! unlocking\n");
mLock.release();
}
return err;
}
public int getBagLocked(int resID, Ref outBag, Ref outTypeSpecFlags) {
if (mError != NO_ERROR) {
return mError;
}
final int p = getResourcePackageIndex(resID);
final int t = Res_GETTYPE(resID);
final int e = Res_GETENTRY(resID);
if (p < 0) {
ALOGW("Invalid package identifier when getting bag for resource number 0x%08x", resID);
return BAD_INDEX;
}
if (t < 0) {
ALOGW("No type identifier when getting bag for resource number 0x%08x", resID);
return BAD_INDEX;
}
//printf("Get bag: id=0x%08x, p=%d, t=%d\n", resID, p, t);
PackageGroup grp = mPackageGroups.get(p);
if (grp == NULL) {
ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID);
return BAD_INDEX;
}
final List typeConfigs = getOrDefault(grp.types, t, Collections.emptyList());
if (typeConfigs.isEmpty()) {
ALOGW("Type identifier 0x%x does not exist.", t+1);
return BAD_INDEX;
}
final int NENTRY = typeConfigs.get(0).entryCount;
if (e >= (int)NENTRY) {
ALOGW("Entry identifier 0x%x is larger than entry count 0x%x",
e, (int)typeConfigs.get(0).entryCount);
return BAD_INDEX;
}
// First see if we've already computed this bag...
TypeCacheEntry cacheEntry = grp.typeCacheEntries.editItemAt(t);
bag_set[] typeSet = cacheEntry.cachedBags;
// todo cache
// if (isTruthy(typeSet)) {
// bag_set set = typeSet[e];
// if (isTruthy(set)) {
// if (set != (bag_set) 0xFFFFFFFF){
// if (set != SENTINEL_BAG_SET){
// if (outTypeSpecFlags != NULL) {
// outTypeSpecFlags.set(set.typeSpecFlags);
// }
// outBag.set((bag_entry *) (set + 1);
// if (kDebugTableSuperNoisy) {
// ALOGI("Found existing bag for: 0x%x\n", resID);
// }
// return set.numAttrs;
// }
// ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.",
// resID);
// return BAD_INDEX;
// }
// }
//
// Bag not found, we need to compute it!
if (!isTruthy(typeSet)) {
typeSet = new bag_set[NENTRY]; // (bag_set**)calloc(NENTRY, sizeof(bag_set*));
//cacheEntry.cachedBags = typeSet;
}
//
// // Mark that we are currently working on this one.
// typeSet[e] = (bag_set*)0xFFFFFFFF;
// typeSet[e] = SENTINEL_BAG_SET;
if (kDebugTableNoisy) {
ALOGI("Building bag: %x\n", resID);
}
// Now collect all bag attributes
Entry entry = new Entry();
int err = getEntry(grp, t, e, mParams, entry);
if (err != NO_ERROR) {
return err;
}
final short entrySize = dtohs(entry.entry.size);
// const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
// ? dtohl(((const ResTable_map_entry*)entry.entry)->parent.ident) : 0;
// const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
// ? dtohl(((const ResTable_map_entry*)entry.entry)->count) : 0;
ResTable_map_entry mapEntry = entrySize >= ResTable_map_entry.BASE_SIZEOF ?
new ResTable_map_entry(entry.entry.myBuf(), entry.entry.myOffset()) : null;
final int parent = mapEntry != null ? dtohl(mapEntry.parent.ident) : 0;
final int count = mapEntry != null ? dtohl(mapEntry.count) : 0;
int N = count;
if (kDebugTableNoisy) {
ALOGI("Found map: size=%x parent=%x count=%d\n", entrySize, parent, count);
// If this map inherits from another, we need to start
// with its parent's values. Otherwise start out empty.
ALOGI("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", entrySize, parent);
}
// This is what we are building.
bag_set set;
if (isTruthy(parent)) {
final Ref resolvedParent = new Ref<>(parent);
// Bags encode a parent reference without using the standard
// Res_value structure. That means we must always try to
// resolve a parent reference in case it is actually a
// TYPE_DYNAMIC_REFERENCE.
err = grp.dynamicRefTable.lookupResourceId(resolvedParent);
if (err != NO_ERROR) {
ALOGE("Failed resolving bag parent id 0x%08x", parent);
return UNKNOWN_ERROR;
}
final Ref parentBag = new Ref<>(null);
final Ref parentTypeSpecFlags = new Ref<>(0);
final int NP = getBagLocked(resolvedParent.get(), parentBag, parentTypeSpecFlags);
final int NT = ((NP >= 0) ? NP : 0) + N;
set = new bag_set(NT);
if (NP > 0) {
set.copyFrom(parentBag.get(), NP);
set.numAttrs = NP;
if (kDebugTableNoisy) {
ALOGI("Initialized new bag with %d inherited attributes.\n", NP);
}
} else {
if (kDebugTableNoisy) {
ALOGI("Initialized new bag with no inherited attributes.\n");
}
set.numAttrs = 0;
}
set.availAttrs = NT;
set.typeSpecFlags = parentTypeSpecFlags.get();
} else {
set = new bag_set(N);
set.numAttrs = 0;
set.availAttrs = N;
set.typeSpecFlags = 0;
}
set.typeSpecFlags |= entry.specFlags;
// Now merge in the new attributes...
// int curOff = (reinterpret_cast(entry.entry) - reinterpret_cast(entry.type))
// + dtohs(entry.entry.size);
int curOff = entry.entry.myOffset() - entry.type.myOffset() + entry.entry.size;
ResTable_map map;
// bag_entry* entries = (bag_entry*)(set+1);
bag_entry[] entries = set.bag_entries;
int curEntry = 0;
int pos = 0;
if (kDebugTableNoisy) {
ALOGI("Starting with set %s, entries=%s, avail=0x%x\n", set, entries, set.availAttrs);
}
while (pos < count) {
if (kDebugTableNoisy) {
// ALOGI("Now at %s\n", curOff);
ALOGI("Now at %s\n", curEntry);
}
if (curOff > (dtohl(entry.type.header.size)- ResTable_map.SIZEOF)) {
ALOGW("ResTable_map at %d is beyond type chunk data %d",
(int)curOff, dtohl(entry.type.header.size));
return BAD_TYPE;
}
// map = (const ResTable_map*)(((const uint8_t*)entry.type) + curOff);
map = new ResTable_map(entry.type.myBuf(), entry.type.myOffset() + curOff);
N++;
final Ref newName = new Ref<>(htodl(map.name.ident));
if (!Res_INTERNALID(newName.get())) {
// Attributes don't have a resource id as the name. They specify
// other data, which would be wrong to change via a lookup.
if (grp.dynamicRefTable.lookupResourceId(newName) != NO_ERROR) {
ALOGE("Failed resolving ResTable_map name at %d with ident 0x%08x",
(int) curEntry, (int) newName.get());
return UNKNOWN_ERROR;
}
}
boolean isInside;
int oldName = 0;
while ((isInside=(curEntry < set.numAttrs))
&& (oldName=entries[curEntry].map.name.ident) < newName.get()) {
if (kDebugTableNoisy) {
ALOGI("#0x%x: Keeping existing attribute: 0x%08x\n",
curEntry, entries[curEntry].map.name.ident);
}
curEntry++;
}
if ((!isInside) || oldName != newName.get()) {
// This is a new attribute... figure out what to do with it.
if (set.numAttrs >= set.availAttrs) {
// Need to alloc more memory...
final int newAvail = set.availAttrs+N;
// set = (bag_set[])realloc(set,
// sizeof(bag_set)
// + sizeof(bag_entry)*newAvail);
set.resizeBagEntries(newAvail);
set.availAttrs = newAvail;
// entries = (bag_entry*)(set+1);
entries = set.bag_entries;
if (kDebugTableNoisy) {
ALOGI("Reallocated set %s, entries=%s, avail=0x%x\n",
set, entries, set.availAttrs);
}
}
if (isInside) {
// Going in the middle, need to make space.
// memmove(entries+curEntry+1, entries+curEntry,
// sizeof(bag_entry)*(set.numAttrs-curEntry));
System.arraycopy(entries, curEntry, entries, curEntry + 1, set.numAttrs - curEntry);
entries[curEntry] = null;
set.numAttrs++;
}
if (kDebugTableNoisy) {
ALOGI("#0x%x: Inserting new attribute: 0x%08x\n", curEntry, newName.get());
}
} else {
if (kDebugTableNoisy) {
ALOGI("#0x%x: Replacing existing attribute: 0x%08x\n", curEntry, oldName);
}
}
bag_entry cur = entries[curEntry];
if (cur == null) {
cur = entries[curEntry] = new bag_entry();
}
cur.stringBlock = entry._package_.header.index;
cur.map.name.ident = newName.get();
// cur->map.value.copyFrom_dtoh(map->value);
cur.map.value = map.value;
final Ref valueRef = new Ref<>(cur.map.value);
err = grp.dynamicRefTable.lookupResourceValue(valueRef);
cur.map.value = map.value = valueRef.get();
if (err != NO_ERROR) {
ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur.map.value.data);
return UNKNOWN_ERROR;
}
if (kDebugTableNoisy) {
ALOGI("Setting entry #0x%x %s: block=%d, name=0x%08d, type=%d, data=0x%08x\n",
curEntry, cur, cur.stringBlock, cur.map.name.ident,
cur.map.value.dataType, cur.map.value.data);
}
// On to the next!
curEntry++;
pos++;
final int size = dtohs(map.value.size);
// curOff += size + sizeof(*map)-sizeof(map->value);
curOff += size + ResTable_map.SIZEOF-Res_value.SIZEOF;
};
if (curEntry > set.numAttrs) {
set.numAttrs = curEntry;
}
// And this is it...
typeSet[e] = set;
if (isTruthy(set)) {
if (outTypeSpecFlags != NULL) {
outTypeSpecFlags.set(set.typeSpecFlags);
}
outBag.set(set.bag_entries);
if (kDebugTableNoisy) {
ALOGI("Returning 0x%x attrs\n", set.numAttrs);
}
return set.numAttrs;
}
return BAD_INDEX;
}
public void unlockBag(Ref bag) {
unlock();
}
static class bag_set {
int numAttrs; // number in array
int availAttrs; // total space in array
int typeSpecFlags;
// Followed by 'numAttr' bag_entry structures.
bag_entry[] bag_entries;
public bag_set(int entryCount) {
bag_entries = new bag_entry[entryCount];
}
public void copyFrom(bag_entry[] parentBag, int count) {
for (int i = 0; i < count; i++) {
bag_entries[i] = parentBag[i];
}
}
public void resizeBagEntries(int newEntryCount) {
bag_entry[] newEntries = new bag_entry[newEntryCount];
System.arraycopy(bag_entries, 0, newEntries, 0, Math.min(bag_entries.length, newEntryCount));
bag_entries = newEntries;
}
};
/**
* Configuration dependent cached data. This must be cleared when the configuration is
* changed (setParameters).
*/
static class TypeCacheEntry {
// TypeCacheEntry() : cachedBags(NULL) {}
// Computed attribute bags for this type.
// bag_set** cachedBags;
bag_set[] cachedBags;
// Pre-filtered list of configurations (per asset path) that match the parameters set on this
// ResTable.
List> filteredConfigs;
};
private int Res_MAKEID(int packageId, int typeId, int entryId) {
return (((packageId+1)<<24) | (((typeId+1)&0xFF)<<16) | (entryId&0xFFFF));
}
// struct resource_name
public static class ResourceName {
public String packageName;
public String type;
public String name;
@Override
public String toString() {
return packageName.trim() + '@' + type + ':' + name;
}
}
private interface Function {
V apply(K key);
}
static V computeIfAbsent(Map map, K key, Function vFunction) {
V v = map.get(key);
if (v == null) {
v = vFunction.apply(key);
map.put(key, v);
}
return v;
}
static V getOrDefault(Map map, K key, V defaultValue) {
V v;
return (((v = map.get(key)) != null) || map.containsKey(key)) ? v : defaultValue;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy