org.robolectric.res.android.DynamicRefTable 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;
// transliterated from https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/include/androidfw/ResourceTypes.h
import static org.robolectric.res.android.Errors.NO_ERROR;
import static org.robolectric.res.android.Errors.UNKNOWN_ERROR;
import static org.robolectric.res.android.ResTable.APP_PACKAGE_ID;
import static org.robolectric.res.android.ResTable.Res_GETPACKAGE;
import static org.robolectric.res.android.ResTable.SYS_PACKAGE_ID;
import static org.robolectric.res.android.Util.ALOGW;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import org.robolectric.res.android.ResourceTypes.Res_value;
/**
* Holds the shared library ID table. Shared libraries are assigned package IDs at
* build time, but they may be loaded in a different order, so we need to maintain
* a mapping of build-time package ID to run-time assigned package ID.
*
* Dynamic references are not currently supported in overlays. Only the base package
* may have dynamic references.
*/
public class DynamicRefTable
{
DynamicRefTable(byte packageId, boolean appAsLib) {
this.mAssignedPackageId = packageId;
this.mAppAsLib = appAsLib;
mLookupTable[APP_PACKAGE_ID] = APP_PACKAGE_ID;
mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID;
}
// // Loads an unmapped reference table from the package.
// Errors load(final ResTable_lib_header header) {
// return null;
// }
// Adds mappings from the other DynamicRefTable
int addMappings(final DynamicRefTable other) {
if (mAssignedPackageId != other.mAssignedPackageId) {
return UNKNOWN_ERROR;
}
// final int entryCount = other.mEntries.size();
// for (size_t i = 0; i < entryCount; i++) {
// ssize_t index = mEntries.indexOfKey(other.mEntries.keyAt(i));
// if (index < 0) {
// mEntries.add(other.mEntries.keyAt(i), other.mEntries[i]);
// } else {
// if (other.mEntries[i] != mEntries[index]) {
// return UNKNOWN_ERROR;
// }
// }
// }
for (Entry otherEntry : other.mEntries.entrySet()) {
String key = otherEntry.getKey();
Byte curValue = mEntries.get(key);
if (curValue == null) {
mEntries.put(key, otherEntry.getValue());
} else {
if (!Objects.equals(otherEntry.getValue(), curValue)) {
return UNKNOWN_ERROR;
}
}
}
// Merge the lookup table. No entry can conflict
// (value of 0 means not set).
for (int i = 0; i < 256; i++) {
if (mLookupTable[i] != other.mLookupTable[i]) {
if (mLookupTable[i] == 0) {
mLookupTable[i] = other.mLookupTable[i];
} else if (other.mLookupTable[i] != 0) {
return UNKNOWN_ERROR;
}
}
}
return NO_ERROR;
}
// Creates a mapping from build-time package ID to run-time package ID for
// the given package.
int addMapping(final String packageName, byte packageId) {
Byte index = mEntries.get(packageName);
if (index == null) {
return UNKNOWN_ERROR;
}
mLookupTable[index] = packageId;
return NO_ERROR;
}
void addAlias(int stagedId, int finalizedId) {
mAliasId.put(stagedId, finalizedId);
}
// // Performs the actual conversion of build-time resource ID to run-time
// // resource ID.
int lookupResourceId(Ref resId) {
int res = resId.get();
int packageId = Res_GETPACKAGE(res) + 1;
Integer aliasId = mAliasId.get(res);
if (aliasId != null) {
// Rewrite the resource id to its alias resource id. Since the alias resource id is a
// compile-time id, it still needs to be resolved further.
res = aliasId;
}
if (packageId == SYS_PACKAGE_ID || (packageId == APP_PACKAGE_ID && !mAppAsLib)) {
// No lookup needs to be done, app and framework package IDs are absolute.
resId.set(res);
return NO_ERROR;
}
if (packageId == 0 || (packageId == APP_PACKAGE_ID && mAppAsLib)) {
// The package ID is 0x00. That means that a shared library is accessing
// its own local resource.
// Or if app resource is loaded as shared library, the resource which has
// app package Id is local resources.
// so we fix up those resources with the calling package ID.
resId.set((0xFFFFFF & resId.get()) | (((int) mAssignedPackageId) << 24));
return NO_ERROR;
}
// Do a proper lookup.
int translatedId = mLookupTable[packageId];
if (translatedId == 0) {
ALOGW("DynamicRefTable(0x%02x): No mapping for build-time package ID 0x%02x.",
mAssignedPackageId, packageId);
for (int i = 0; i < 256; i++) {
if (mLookupTable[i] != 0) {
ALOGW("e[0x%02x] . 0x%02x", i, mLookupTable[i]);
}
}
return UNKNOWN_ERROR;
}
resId.set((res & 0x00ffffff) | (((int) translatedId) << 24));
return NO_ERROR;
}
//
int lookupResourceValue(Ref value) {
byte resolvedType = DataType.REFERENCE.code();
Res_value inValue = value.get();
switch (DataType.fromCode(inValue.dataType)) {
case ATTRIBUTE:
resolvedType = DataType.ATTRIBUTE.code();
// fallthrough
case REFERENCE:
if (!mAppAsLib) {
return NO_ERROR;
}
// If the package is loaded as shared library, the resource reference
// also need to be fixed.
break;
case DYNAMIC_ATTRIBUTE:
resolvedType = DataType.ATTRIBUTE.code();
// fallthrough
case DYNAMIC_REFERENCE:
break;
default:
return NO_ERROR;
}
final Ref resIdRef = new Ref<>(inValue.data);
int err = lookupResourceId(resIdRef);
value.set(inValue.withData(resIdRef.get()));
if (err != NO_ERROR) {
return err;
}
value.set(new Res_value(resolvedType, resIdRef.get()));
return NO_ERROR;
}
public Map entries() {
return mEntries;
}
//
// final KeyedVector& entries() final {
// return mEntries;
//}
//
// private:
final byte mAssignedPackageId;
final byte[] mLookupTable = new byte[256];
final Map mEntries = new HashMap<>();
boolean mAppAsLib;
final Map mAliasId = new HashMap<>();
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy