Home | History | Annotate | Download | only in android
      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 {
     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
     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.
     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.
     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 //
    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 
    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