1 package org.robolectric.res.android; 2 3 // transliterated from https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/include/androidfw/ResourceTypes.h 4 5 import static org.robolectric.res.android.Errors.NO_ERROR; 6 import static org.robolectric.res.android.Errors.UNKNOWN_ERROR; 7 import static org.robolectric.res.android.ResTable.APP_PACKAGE_ID; 8 import static org.robolectric.res.android.ResTable.Res_GETPACKAGE; 9 import static org.robolectric.res.android.ResTable.SYS_PACKAGE_ID; 10 import static org.robolectric.res.android.Util.ALOGW; 11 12 import java.util.HashMap; 13 import java.util.Map; 14 import java.util.Map.Entry; 15 import java.util.Objects; 16 import org.robolectric.res.android.ResourceTypes.Res_value; 17 18 /** 19 * Holds the shared library ID table. Shared libraries are assigned package IDs at 20 * build time, but they may be loaded in a different order, so we need to maintain 21 * a mapping of build-time package ID to run-time assigned package ID. 22 * 23 * Dynamic references are not currently supported in overlays. Only the base package 24 * may have dynamic references. 25 */ 26 public class DynamicRefTable 27 { DynamicRefTable(byte packageId, boolean appAsLib)28 DynamicRefTable(byte packageId, boolean appAsLib) { 29 this.mAssignedPackageId = packageId; 30 this.mAppAsLib = appAsLib; 31 32 mLookupTable[APP_PACKAGE_ID] = APP_PACKAGE_ID; 33 mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID; 34 } 35 36 // // Loads an unmapped reference table from the package. 37 // Errors load(final ResTable_lib_header header) { 38 // return null; 39 // } 40 41 // Adds mappings from the other DynamicRefTable addMappings(final DynamicRefTable other)42 int addMappings(final DynamicRefTable other) { 43 if (mAssignedPackageId != other.mAssignedPackageId) { 44 return UNKNOWN_ERROR; 45 } 46 47 // final int entryCount = other.mEntries.size(); 48 // for (size_t i = 0; i < entryCount; i++) { 49 // ssize_t index = mEntries.indexOfKey(other.mEntries.keyAt(i)); 50 // if (index < 0) { 51 // mEntries.add(other.mEntries.keyAt(i), other.mEntries[i]); 52 // } else { 53 // if (other.mEntries[i] != mEntries[index]) { 54 // return UNKNOWN_ERROR; 55 // } 56 // } 57 // } 58 for (Entry<String, Byte> otherEntry : other.mEntries.entrySet()) { 59 String key = otherEntry.getKey(); 60 Byte curValue = mEntries.get(key); 61 if (curValue == null) { 62 mEntries.put(key, otherEntry.getValue()); 63 } else { 64 if (!Objects.equals(otherEntry.getValue(), curValue)) { 65 return UNKNOWN_ERROR; 66 } 67 } 68 } 69 70 // Merge the lookup table. No entry can conflict 71 // (value of 0 means not set). 72 for (int i = 0; i < 256; i++) { 73 if (mLookupTable[i] != other.mLookupTable[i]) { 74 if (mLookupTable[i] == 0) { 75 mLookupTable[i] = other.mLookupTable[i]; 76 } else if (other.mLookupTable[i] != 0) { 77 return UNKNOWN_ERROR; 78 } 79 } 80 } 81 return NO_ERROR; 82 } 83 84 // Creates a mapping from build-time package ID to run-time package ID for 85 // the given package. addMapping(final String packageName, byte packageId)86 int addMapping(final String packageName, byte packageId) { 87 Byte index = mEntries.get(packageName); 88 if (index == null) { 89 return UNKNOWN_ERROR; 90 } 91 mLookupTable[index] = packageId; 92 return NO_ERROR; 93 } 94 95 // // Performs the actual conversion of build-time resource ID to run-time 96 // // resource ID. lookupResourceId(Ref<Integer> resId)97 int lookupResourceId(Ref<Integer> resId) { 98 int res = resId.get(); 99 int packageId = Res_GETPACKAGE(res) + 1; 100 101 if (packageId == APP_PACKAGE_ID && !mAppAsLib) { 102 // No lookup needs to be done, app package IDs are absolute. 103 return NO_ERROR; 104 } 105 106 if (packageId == 0 || (packageId == APP_PACKAGE_ID && mAppAsLib)) { 107 // The package ID is 0x00. That means that a shared library is accessing 108 // its own local resource. 109 // Or if app resource is loaded as shared library, the resource which has 110 // app package Id is local resources. 111 // so we fix up those resources with the calling package ID. 112 resId.set((0xFFFFFF & (resId.get())) | (((int) mAssignedPackageId) << 24)); 113 return NO_ERROR; 114 } 115 116 // Do a proper lookup. 117 int translatedId = mLookupTable[packageId]; 118 if (translatedId == 0) { 119 ALOGW("DynamicRefTable(0x%02x): No mapping for build-time package ID 0x%02x.", 120 mAssignedPackageId, packageId); 121 for (int i = 0; i < 256; i++) { 122 if (mLookupTable[i] != 0) { 123 ALOGW("e[0x%02x] . 0x%02x", i, mLookupTable[i]); 124 } 125 } 126 return UNKNOWN_ERROR; 127 } 128 129 resId.set((res & 0x00ffffff) | (((int) translatedId) << 24)); 130 return NO_ERROR; 131 } 132 // lookupResourceValue(Ref<Res_value> value)133 int lookupResourceValue(Ref<Res_value> value) { 134 byte resolvedType = DataType.REFERENCE.code(); 135 Res_value inValue = value.get(); 136 switch (DataType.fromCode(inValue.dataType)) { 137 case ATTRIBUTE: 138 resolvedType = DataType.ATTRIBUTE.code(); 139 // fallthrough 140 case REFERENCE: 141 if (!mAppAsLib) { 142 return NO_ERROR; 143 } 144 145 // If the package is loaded as shared library, the resource reference 146 // also need to be fixed. 147 break; 148 case DYNAMIC_ATTRIBUTE: 149 resolvedType = DataType.ATTRIBUTE.code(); 150 // fallthrough 151 case DYNAMIC_REFERENCE: 152 break; 153 default: 154 return NO_ERROR; 155 } 156 157 final Ref<Integer> resIdRef = new Ref<>(inValue.data); 158 int err = lookupResourceId(resIdRef); 159 value.set(inValue.withData(resIdRef.get())); 160 if (err != NO_ERROR) { 161 return err; 162 } 163 164 value.set(new Res_value(resolvedType, resIdRef.get())); 165 return NO_ERROR; 166 } 167 entries()168 public Map<String, Byte> entries() { 169 return mEntries; 170 } 171 172 // 173 // final KeyedVector<String16, uint8_t>& entries() final { 174 // return mEntries; 175 //} 176 // 177 // private: 178 final byte mAssignedPackageId; 179 final byte[] mLookupTable = new byte[256]; 180 final Map<String, Byte> mEntries = new HashMap<>(); 181 boolean mAppAsLib; 182 }; 183