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