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