Home | History | Annotate | Download | only in android
      1 package org.robolectric.res.android;
      2 
      3 import static com.google.common.primitives.UnsignedBytes.max;
      4 import static org.robolectric.res.android.Errors.BAD_INDEX;
      5 import static org.robolectric.res.android.Errors.BAD_TYPE;
      6 import static org.robolectric.res.android.Errors.BAD_VALUE;
      7 import static org.robolectric.res.android.Errors.NO_ERROR;
      8 import static org.robolectric.res.android.Errors.NO_MEMORY;
      9 import static org.robolectric.res.android.Errors.UNKNOWN_ERROR;
     10 import static org.robolectric.res.android.ResourceTypes.RES_STRING_POOL_TYPE;
     11 import static org.robolectric.res.android.ResourceTypes.RES_TABLE_LIBRARY_TYPE;
     12 import static org.robolectric.res.android.ResourceTypes.RES_TABLE_PACKAGE_TYPE;
     13 import static org.robolectric.res.android.ResourceTypes.RES_TABLE_TYPE;
     14 import static org.robolectric.res.android.ResourceTypes.RES_TABLE_TYPE_SPEC_TYPE;
     15 import static org.robolectric.res.android.ResourceTypes.RES_TABLE_TYPE_TYPE;
     16 import static org.robolectric.res.android.ResourceTypes.validate_chunk;
     17 import static org.robolectric.res.android.Util.ALOGD;
     18 import static org.robolectric.res.android.Util.ALOGE;
     19 import static org.robolectric.res.android.Util.ALOGI;
     20 import static org.robolectric.res.android.Util.ALOGV;
     21 import static org.robolectric.res.android.Util.ALOGW;
     22 import static org.robolectric.res.android.Util.LOG_FATAL_IF;
     23 import static org.robolectric.res.android.Util.dtohl;
     24 import static org.robolectric.res.android.Util.dtohs;
     25 import static org.robolectric.res.android.Util.htodl;
     26 import static org.robolectric.res.android.Util.htods;
     27 import static org.robolectric.res.android.Util.isTruthy;
     28 
     29 import java.nio.ByteBuffer;
     30 import java.nio.ByteOrder;
     31 import java.util.ArrayList;
     32 import java.util.Collections;
     33 import java.util.HashMap;
     34 import java.util.List;
     35 import java.util.Map;
     36 import java.util.Objects;
     37 import java.util.concurrent.Semaphore;
     38 import org.robolectric.res.android.ResourceTypes.ResChunk_header;
     39 import org.robolectric.res.android.ResourceTypes.ResTable_entry;
     40 import org.robolectric.res.android.ResourceTypes.ResTable_header;
     41 import org.robolectric.res.android.ResourceTypes.ResTable_map;
     42 import org.robolectric.res.android.ResourceTypes.ResTable_map_entry;
     43 import org.robolectric.res.android.ResourceTypes.ResTable_package;
     44 import org.robolectric.res.android.ResourceTypes.ResTable_sparseTypeEntry;
     45 import org.robolectric.res.android.ResourceTypes.ResTable_type;
     46 import org.robolectric.res.android.ResourceTypes.ResTable_typeSpec;
     47 import org.robolectric.res.android.ResourceTypes.Res_value;
     48 
     49 // transliterated from https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/ResourceTypes.cpp
     50 //   and https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/include/androidfw/ResourceTypes.h
     51 @SuppressWarnings("NewApi")
     52 public class ResTable {
     53 
     54   private static final int IDMAP_MAGIC             = 0x504D4449;
     55   private static final int IDMAP_CURRENT_VERSION   = 0x00000001;
     56 
     57   static final int APP_PACKAGE_ID      = 0x7f;
     58   static final int SYS_PACKAGE_ID      = 0x01;
     59 
     60   static final boolean kDebugStringPoolNoisy = false;
     61   static final boolean kDebugXMLNoisy = false;
     62   static final boolean kDebugTableNoisy = false;
     63   static final boolean kDebugTableGetEntry = false;
     64   static final boolean kDebugTableSuperNoisy = false;
     65   static final boolean kDebugLoadTableNoisy = false;
     66   static final boolean kDebugLoadTableSuperNoisy = false;
     67   static final boolean kDebugTableTheme = false;
     68   static final boolean kDebugResXMLTree = false;
     69   static final boolean kDebugLibNoisy = false;
     70 
     71   private static final Object NULL = null;
     72   public static final bag_set SENTINEL_BAG_SET = new bag_set(1);
     73 
     74   final Semaphore mLock = new Semaphore(1);
     75 
     76   // Mutex that controls access to the list of pre-filtered configurations
     77   // to check when looking up entries.
     78   // When iterating over a bag, the mLock mutex is locked. While mLock is locked,
     79   // we do resource lookups.
     80   // Mutex is not reentrant, so we must use a different lock than mLock.
     81   final Object               mFilteredConfigLock = new Object();
     82 
     83   // type defined in Errors
     84   int mError;
     85 
     86   ResTable_config mParams;
     87 
     88   // Array of all resource tables.
     89   final List<Header>             mHeaders = new ArrayList<>();
     90 
     91   // Array of packages in all resource tables.
     92   final Map<Integer, PackageGroup> mPackageGroups = new HashMap<>();
     93 
     94   // Mapping from resource package IDs to indices into the internal
     95   // package array.
     96   final byte[]                     mPackageMap = new byte[256];
     97 
     98   byte                     mNextPackageId;
     99 
    100   static boolean Res_CHECKID(int resid) { return ((resid&0xFFFF0000) != 0);}
    101   static int Res_GETPACKAGE(int id) {
    102     return ((id>>24)-1);
    103   }
    104   public static int Res_GETTYPE(int id) {
    105     return (((id>>16)&0xFF)-1);
    106   }
    107   static int Res_GETENTRY(int id) {
    108     return (id&0xFFFF);
    109   }
    110   static int Res_MAKEARRAY(int entry) { return (0x02000000 | (entry&0xFFFF)); }
    111   static boolean Res_INTERNALID(int resid) { return ((resid&0xFFFF0000) != 0 && (resid&0xFF0000) == 0); }
    112 
    113   int getResourcePackageIndex(int resID)
    114   {
    115     return Res_GETPACKAGE(resID) + 1;
    116     //return mPackageMap[Res_GETPACKAGE(resID)+1]-1;
    117   }
    118 
    119   int getResourcePackageIndexFromPackage(byte packageID) {
    120     return ((int)mPackageMap[packageID])-1;
    121   }
    122 
    123   //  Errors add(final Object data, int size, final int cookie, boolean copyData) {
    124 //    return addInternal(data, size, NULL, 0, false, cookie, copyData);
    125 //  }
    126 //
    127 //  Errors add(final Object data, int size, final Object idmapData, int idmapDataSize,
    128 //        final int cookie, boolean copyData, boolean appAsLib) {
    129 //    return addInternal(data, size, idmapData, idmapDataSize, appAsLib, cookie, copyData);
    130 //  }
    131 //
    132 //  Errors add(Asset asset, final int cookie, boolean copyData) {
    133 //    final Object data = asset.getBuffer(true);
    134 //    if (data == NULL) {
    135 //      ALOGW("Unable to get buffer of resource asset file");
    136 //      return UNKNOWN_ERROR;
    137 //    }
    138 //
    139 //    return addInternal(data, static_cast<int>(asset.getLength()), NULL, false, 0, cookie,
    140 //        copyData);
    141 //  }
    142 
    143 //  status_t add(Asset* asset, Asset* idmapAsset, const int32_t cookie=-1, bool copyData=false,
    144 //      bool appAsLib=false, bool isSystemAsset=false);
    145   int add(
    146       Asset asset, Asset idmapAsset, final int cookie, boolean copyData,
    147       boolean appAsLib, boolean isSystemAsset) {
    148     final byte[] data = asset.getBuffer(true);
    149     if (data == NULL) {
    150       ALOGW("Unable to get buffer of resource asset file");
    151       return UNKNOWN_ERROR;
    152     }
    153 
    154     int idmapSize = 0;
    155     Object idmapData = NULL;
    156     if (idmapAsset != NULL) {
    157       idmapData = idmapAsset.getBuffer(true);
    158       if (idmapData == NULL) {
    159         ALOGW("Unable to get buffer of idmap asset file");
    160         return UNKNOWN_ERROR;
    161       }
    162       idmapSize = (int) idmapAsset.getLength();
    163     }
    164 
    165     return addInternal(data, (int) asset.getLength(),
    166         idmapData, idmapSize, appAsLib, cookie, copyData, isSystemAsset);
    167   }
    168 
    169   int add(ResTable src, boolean isSystemAsset)
    170   {
    171     mError = src.mError;
    172 
    173     for (int i=0; i < src.mHeaders.size(); i++) {
    174       mHeaders.add(src.mHeaders.get(i));
    175     }
    176 
    177     for (PackageGroup srcPg : src.mPackageGroups.values()) {
    178       PackageGroup pg = new PackageGroup(this, srcPg.name, srcPg.id,
    179           false /* appAsLib */, isSystemAsset || srcPg.isSystemAsset, srcPg.isDynamic);
    180       for (int j=0; j<srcPg.packages.size(); j++) {
    181         pg.packages.add(srcPg.packages.get(j));
    182       }
    183 
    184       for (Integer typeId : srcPg.types.keySet()) {
    185         List<Type> typeList = computeIfAbsent(pg.types, typeId, key -> new ArrayList<>());
    186         typeList.addAll(srcPg.types.get(typeId));
    187       }
    188       pg.dynamicRefTable.addMappings(srcPg.dynamicRefTable);
    189       pg.largestTypeId = max(pg.largestTypeId, srcPg.largestTypeId);
    190       mPackageGroups.put(pg.id, pg);
    191     }
    192 
    193 //    memcpy(mPackageMap, src->mPackageMap, sizeof(mPackageMap));
    194     System.arraycopy(src.mPackageMap, 0, mPackageMap, 0, mPackageMap.length);
    195 
    196     return mError;
    197   }
    198 
    199   int addEmpty(final int cookie) {
    200     Header header = new Header(this);
    201     header.index = mHeaders.size();
    202     header.cookie = cookie;
    203     header.values.setToEmpty();
    204     header.ownedData = new byte[ResTable_header.SIZEOF];
    205 
    206     ByteBuffer buf = ByteBuffer.wrap(header.ownedData).order(ByteOrder.LITTLE_ENDIAN);
    207     ResChunk_header.write(buf, (short) RES_TABLE_TYPE, () -> {}, () -> {});
    208 
    209     ResTable_header resHeader = new ResTable_header(buf, 0);
    210 //    resHeader.header.type = RES_TABLE_TYPE;
    211 //    resHeader.header.headerSize = sizeof(ResTable_header);
    212 //    resHeader.header.size = sizeof(ResTable_header);
    213 
    214     header.header = resHeader;
    215     mHeaders.add(header);
    216     return (mError=NO_ERROR);
    217   }
    218 
    219 //  status_t addInternal(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
    220 //      bool appAsLib, const int32_t cookie, bool copyData, bool isSystemAsset=false);
    221   int addInternal(byte[] data, int dataSize, final Object idmapData, int idmapDataSize,
    222       boolean appAsLib, final int cookie, boolean copyData, boolean isSystemAsset)
    223   {
    224     if (!isTruthy(data)) {
    225       return NO_ERROR;
    226     }
    227 
    228     if (dataSize < ResTable_header.SIZEOF) {
    229       ALOGE("Invalid data. Size(%d) is smaller than a ResTable_header(%d).",
    230           (int) dataSize, (int) ResTable_header.SIZEOF);
    231       return UNKNOWN_ERROR;
    232     }
    233 
    234     Header header = new Header(this);
    235     header.index = mHeaders.size();
    236     header.cookie = cookie;
    237     if (idmapData != NULL) {
    238       header.resourceIDMap = new int[idmapDataSize / 4];
    239       if (header.resourceIDMap == NULL) {
    240 //        delete header;
    241         return (mError = NO_MEMORY);
    242       }
    243 //      memcpy(header.resourceIDMap, idmapData, idmapDataSize);
    244 //      header.resourceIDMapSize = idmapDataSize;
    245     }
    246     mHeaders.add(header);
    247 
    248     final boolean notDeviceEndian = htods((short) 0xf0) != 0xf0;
    249 
    250     if (kDebugLoadTableNoisy) {
    251       ALOGV("Adding resources to ResTable: data=%s, size=0x%x, cookie=%d, copy=%d " +
    252           "idmap=%s\n", data, dataSize, cookie, copyData, idmapData);
    253     }
    254 
    255     if (copyData || notDeviceEndian) {
    256       header.ownedData = data; // malloc(dataSize);
    257       if (header.ownedData == NULL) {
    258         return (mError=NO_MEMORY);
    259       }
    260 //      memcpy(header.ownedData, data, dataSize);
    261       data = header.ownedData;
    262     }
    263 
    264     ByteBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
    265 //    header->header = (const ResTable_header*)data;
    266     header.header = new ResTable_header(buf, 0);
    267     header.size = dtohl(header.header.header.size);
    268     if (kDebugLoadTableSuperNoisy) {
    269       ALOGI("Got size 0x%x, again size 0x%x, raw size 0x%x\n", header.size,
    270           dtohl(header.header.header.size), header.header.header.size);
    271     }
    272     if (kDebugLoadTableNoisy) {
    273       ALOGV("Loading ResTable @%s:\n", header.header);
    274     }
    275     if (dtohs(header.header.header.headerSize) > header.size
    276         || header.size > dataSize) {
    277       ALOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n",
    278           (int)dtohs(header.header.header.headerSize),
    279           (int)header.size, (int)dataSize);
    280       return (mError=BAD_TYPE);
    281     }
    282     if (((dtohs(header.header.header.headerSize)|header.size)&0x3) != 0) {
    283       ALOGW("Bad resource table: header size 0x%x or total size 0x%x is not on an integer boundary\n",
    284           (int)dtohs(header.header.header.headerSize),
    285           (int)header.size);
    286       return (mError=BAD_TYPE);
    287     }
    288 //    header->dataEnd = ((const uint8_t*)header->header) + header->size;
    289     header.dataEnd = header.size;
    290 
    291     // Iterate through all chunks.
    292     int curPackage = 0;
    293 
    294 //    const ResChunk_header* chunk =
    295 //      (const ResChunk_header*)(((const uint8_t*)header->header)
    296 //    + dtohs(header->header->header.headerSize));
    297     ResChunk_header chunk =
    298       new ResChunk_header(buf, dtohs(header.header.header.headerSize));
    299     while (chunk != null && (chunk.myOffset()) <= (header.dataEnd -ResChunk_header.SIZEOF) &&
    300       (chunk.myOffset()) <= (header.dataEnd -dtohl(chunk.size))) {
    301     int err = validate_chunk(chunk, ResChunk_header.SIZEOF, header.dataEnd, "ResTable");
    302     if (err != NO_ERROR) {
    303       return (mError=err);
    304     }
    305     if (kDebugTableNoisy) {
    306       ALOGV("Chunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%s\n",
    307           dtohs(chunk.type), dtohs(chunk.headerSize), dtohl(chunk.size),
    308           (Object)((chunk.myOffset()) - (header.header.myOffset())));
    309     }
    310     final int csize = dtohl(chunk.size);
    311     final int ctype = dtohs(chunk.type);
    312     if (ctype == RES_STRING_POOL_TYPE) {
    313       if (header.values.getError() != NO_ERROR) {
    314         // Only use the first string chunk; ignore any others that
    315         // may appear.
    316         err = header.values.setTo(chunk.myBuf(), chunk.myOffset(), csize, false);
    317         if (err != NO_ERROR) {
    318           return (mError=err);
    319         }
    320       } else {
    321         ALOGW("Multiple string chunks found in resource table.");
    322       }
    323     } else if (ctype == RES_TABLE_PACKAGE_TYPE) {
    324       if (curPackage >= dtohl(header.header.packageCount)) {
    325         ALOGW("More package chunks were found than the %d declared in the header.",
    326             dtohl(header.header.packageCount));
    327         return (mError=BAD_TYPE);
    328       }
    329 
    330       if (parsePackage(
    331           new ResTable_package(chunk.myBuf(), chunk.myOffset()), header, appAsLib, isSystemAsset) != NO_ERROR) {
    332         return mError;
    333       }
    334       curPackage++;
    335     } else {
    336       ALOGW("Unknown chunk type 0x%x in table at 0x%x.\n",
    337           ctype,
    338           (chunk.myOffset()) - (header.header.myOffset()));
    339     }
    340     chunk = chunk.myOffset() + csize < header.dataEnd
    341         ? new ResChunk_header(chunk.myBuf(), chunk.myOffset() + csize)
    342         : null;
    343   }
    344 
    345     if (curPackage < dtohl(header.header.packageCount)) {
    346       ALOGW("Fewer package chunks (%d) were found than the %d declared in the header.",
    347           (int)curPackage, dtohl(header.header.packageCount));
    348       return (mError=BAD_TYPE);
    349     }
    350     mError = header.values.getError();
    351     if (mError != NO_ERROR) {
    352       ALOGW("No string values found in resource table!");
    353     }
    354 
    355     if (kDebugTableNoisy) {
    356       ALOGV("Returning from add with mError=%d\n", mError);
    357     }
    358     return mError;
    359   }
    360 
    361   public final int getResource(int resID, Ref<Res_value> outValue, boolean mayBeBag, int density,
    362       final Ref<Integer> outSpecFlags, Ref<ResTable_config> outConfig)
    363   {
    364     if (mError != NO_ERROR) {
    365       return mError;
    366     }
    367     final int p = getResourcePackageIndex(resID);
    368     final int t = Res_GETTYPE(resID);
    369     final int e = Res_GETENTRY(resID);
    370     if (p < 0) {
    371       if (Res_GETPACKAGE(resID)+1 == 0) {
    372         ALOGW("No package identifier when getting value for resource number 0x%08x", resID);
    373       } else {
    374         ALOGW("No known package when getting value for resource number 0x%08x", resID);
    375       }
    376       return BAD_INDEX;
    377     }
    378 
    379     if (t < 0) {
    380       ALOGW("No type identifier when getting value for resource number 0x%08x", resID);
    381       return BAD_INDEX;
    382     }
    383     final PackageGroup grp = mPackageGroups.get(p);
    384     if (grp == NULL) {
    385       ALOGW("Bad identifier when getting value for resource number 0x%08x", resID);
    386       return BAD_INDEX;
    387     }
    388     // Allow overriding density
    389     ResTable_config desiredConfig = mParams;
    390     if (density > 0) {
    391       desiredConfig.density = density;
    392     }
    393     Entry entry = new Entry();
    394     int err = getEntry(grp, t, e, desiredConfig, entry);
    395     if (err != NO_ERROR) {
    396       // Only log the failure when we're not running on the host as
    397       // part of a tool. The caller will do its own logging.
    398       return err;
    399     }
    400 
    401     if ((entry.entry.flags & ResTable_entry.FLAG_COMPLEX) != 0) {
    402       if (!mayBeBag) {
    403         ALOGW("Requesting resource 0x%08x failed because it is complex\n", resID);
    404       }
    405       return BAD_VALUE;
    406     }
    407 
    408 //    const Res_value* value = reinterpret_cast<const Res_value*>(
    409 //      reinterpret_cast<const uint8_t*>(entry.entry) + entry.entry->size);
    410     Res_value value = new Res_value(entry.entry.myBuf(), entry.entry.myOffset() + entry.entry.size);
    411 
    412 //    outValue.size = dtohs(value.size);
    413 //    outValue.res0 = value.res0;
    414 //    outValue.dataType = value.dataType;
    415 //    outValue.data = dtohl(value.data);
    416     outValue.set(value);
    417 
    418     // The reference may be pointing to a resource in a shared library. These
    419     // references have build-time generated package IDs. These ids may not match
    420     // the actual package IDs of the corresponding packages in this ResTable.
    421     // We need to fix the package ID based on a mapping.
    422     if (grp.dynamicRefTable.lookupResourceValue(outValue) != NO_ERROR) {
    423       ALOGW("Failed to resolve referenced package: 0x%08x", outValue.get().data);
    424       return BAD_VALUE;
    425     }
    426 
    427 //    if (kDebugTableNoisy) {
    428 //      size_t len;
    429 //      printf("Found value: pkg=0x%x, type=%d, str=%s, int=%d\n",
    430 //          entry.package.header.index,
    431 //          outValue.dataType,
    432 //          outValue.dataType == Res_value::TYPE_STRING ?
    433 //              String8(entry.package.header.values.stringAt(outValue.data, &len)).string() :
    434 //      "",
    435 //          outValue.data);
    436 //    }
    437 
    438     if (outSpecFlags != null) {
    439         outSpecFlags.set(entry.specFlags);
    440     }
    441     if (outConfig != null) {
    442         outConfig.set(entry.config);
    443     }
    444     return entry._package_.header.index;
    445   }
    446 
    447   public final int resolveReference(Ref<Res_value> value, int blockIndex,
    448       final Ref<Integer> outLastRef) {
    449     return resolveReference(value, blockIndex, outLastRef, null, null);
    450   }
    451 
    452   public final int resolveReference(Ref<Res_value> value, int blockIndex,
    453       final Ref<Integer> outLastRef, Ref<Integer> inoutTypeSpecFlags) {
    454     return resolveReference(value, blockIndex, outLastRef, inoutTypeSpecFlags, null);
    455   }
    456 
    457   public final int resolveReference(Ref<Res_value> value, int blockIndex,
    458       final Ref<Integer> outLastRef, Ref<Integer> inoutTypeSpecFlags,
    459       final Ref<ResTable_config> outConfig)
    460   {
    461     int count=0;
    462     while (blockIndex >= 0 && value.get().dataType == DataType.REFERENCE.code()
    463         && value.get().data != 0 && count < 20) {
    464       if (outLastRef != null) {
    465         outLastRef.set(value.get().data);
    466       }
    467       final Ref<Integer> newFlags = new Ref<>(0);
    468       final int newIndex = getResource(value.get().data, value, true, 0,
    469           newFlags, outConfig);
    470       if (newIndex == BAD_INDEX) {
    471         return BAD_INDEX;
    472       }
    473       if (kDebugTableTheme) {
    474         ALOGI("Resolving reference 0x%x: newIndex=%d, type=0x%x, data=0x%x\n",
    475             value.get().data, (int)newIndex, (int)value.get().dataType, value.get().data);
    476       }
    477       //printf("Getting reference 0x%08x: newIndex=%d\n", value.data, newIndex);
    478       if (inoutTypeSpecFlags != null) {
    479         inoutTypeSpecFlags.set(inoutTypeSpecFlags.get() | newFlags.get());
    480       }
    481       if (newIndex < 0) {
    482         // This can fail if the resource being referenced is a style...
    483         // in this case, just return the reference, and expect the
    484         // caller to deal with.
    485         return blockIndex;
    486       }
    487       blockIndex = newIndex;
    488       count++;
    489     }
    490     return blockIndex;
    491   }
    492 
    493   private interface Compare {
    494     boolean compare(ResTable_sparseTypeEntry a, ResTable_sparseTypeEntry b);
    495   }
    496 
    497   ResTable_sparseTypeEntry lower_bound(ResTable_sparseTypeEntry first, ResTable_sparseTypeEntry last,
    498                                        ResTable_sparseTypeEntry value,
    499                                        Compare comparator) {
    500     int count = (last.myOffset() - first.myOffset()) / ResTable_sparseTypeEntry.SIZEOF;
    501     int itOffset;
    502     int step;
    503     while (count > 0) {
    504       itOffset = first.myOffset();
    505       step = count / 2;
    506       itOffset += step * ResTable_sparseTypeEntry.SIZEOF;
    507       if (comparator.compare(new ResTable_sparseTypeEntry(first.myBuf(), itOffset), value)) {
    508         itOffset += ResTable_sparseTypeEntry.SIZEOF;
    509         first = new ResTable_sparseTypeEntry(first.myBuf(), itOffset);
    510       } else {
    511         count = step;
    512       }
    513     }
    514     return first;
    515   }
    516 
    517 
    518   private int getEntry(
    519       final PackageGroup packageGroup, int typeIndex, int entryIndex,
    520       final ResTable_config config,
    521       Entry outEntry)
    522   {
    523     final List<Type> typeList = getOrDefault(packageGroup.types, typeIndex, Collections.emptyList());
    524     if (typeList.isEmpty()) {
    525       ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex);
    526       return BAD_TYPE;
    527     }
    528 
    529     ResTable_type bestType = null;
    530     int bestOffset = ResTable_type.NO_ENTRY;
    531     Package bestPackage = null;
    532     int specFlags = 0;
    533     byte actualTypeIndex = (byte) typeIndex;
    534     ResTable_config bestConfig = null;
    535 //    memset(&bestConfig, 0, sizeof(bestConfig));
    536 
    537     // Iterate over the Types of each package.
    538     final int typeCount = typeList.size();
    539     for (int i = 0; i < typeCount; i++) {
    540       final Type typeSpec = typeList.get(i);
    541 
    542       int realEntryIndex = entryIndex;
    543       int realTypeIndex = typeIndex;
    544       boolean currentTypeIsOverlay = false;
    545 
    546       // Runtime overlay packages provide a mapping of app resource
    547       // ID to package resource ID.
    548       if (typeSpec.idmapEntries.hasEntries()) {
    549         final Ref<Short> overlayEntryIndex = new Ref<>((short) 0);
    550         if (typeSpec.idmapEntries.lookup(entryIndex, overlayEntryIndex) != NO_ERROR) {
    551           // No such mapping exists
    552           continue;
    553         }
    554         realEntryIndex = overlayEntryIndex.get();
    555         realTypeIndex = typeSpec.idmapEntries.overlayTypeId() - 1;
    556         currentTypeIsOverlay = true;
    557       }
    558 
    559       // Check that the entry idx is within range of the declared entry count (ResTable_typeSpec).
    560       // Particular types (ResTable_type) may be encoded with sparse entries, and so their
    561       // entryCount do not need to match.
    562       if (((int) realEntryIndex) >= typeSpec.entryCount) {
    563         ALOGW("For resource 0x%08x, entry index(%d) is beyond type entryCount(%d)",
    564             Res_MAKEID(packageGroup.id - 1, typeIndex, entryIndex),
    565             entryIndex, ((int) typeSpec.entryCount));
    566         // We should normally abort here, but some legacy apps declare
    567         // resources in the 'android' package (old bug in AAPT).
    568         continue;
    569       }
    570 
    571       // Aggregate all the flags for each package that defines this entry.
    572       if (typeSpec.typeSpecFlags != null) {
    573         specFlags |= dtohl(typeSpec.typeSpecFlags[realEntryIndex]);
    574       } else {
    575         specFlags = -1;
    576       }
    577 
    578       List<ResTable_type> candidateConfigs = typeSpec.configs;
    579 
    580 //      List<ResTable_type> filteredConfigs;
    581 //      if (isTruthy(config) && Objects.equals(mParams, config)) {
    582 //        // Grab the lock first so we can safely get the current filtered list.
    583 //        synchronized (mFilteredConfigLock) {
    584 //          // This configuration is equal to the one we have previously cached for,
    585 //          // so use the filtered configs.
    586 //
    587 //          final TypeCacheEntry cacheEntry = packageGroup.typeCacheEntries.get(typeIndex);
    588 //          if (i < cacheEntry.filteredConfigs.size()) {
    589 //            if (isTruthy(cacheEntry.filteredConfigs.get(i))) {
    590 //              // Grab a reference to the shared_ptr so it doesn't get destroyed while
    591 //              // going through this list.
    592 //              filteredConfigs = cacheEntry.filteredConfigs.get(i);
    593 //
    594 //              // Use this filtered list.
    595 //              candidateConfigs = filteredConfigs;
    596 //            }
    597 //          }
    598 //        }
    599 //      }
    600 
    601       final int numConfigs = candidateConfigs.size();
    602       for (int c = 0; c < numConfigs; c++) {
    603         final ResTable_type thisType = candidateConfigs.get(c);
    604         if (thisType == NULL) {
    605           continue;
    606         }
    607 
    608         final ResTable_config thisConfig;
    609 //        thisConfig.copyFromDtoH(thisType.config);
    610         thisConfig = ResTable_config.fromDtoH(thisType.config);
    611 
    612         // Check to make sure this one is valid for the current parameters.
    613         if (config != NULL && !thisConfig.match(config)) {
    614           continue;
    615         }
    616 
    617         // const uint32_t* const eindex = reinterpret_cast<const uint32_t*>(
    618         // reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize));
    619 
    620         final int eindex = thisType.myOffset() + dtohs(thisType.header.headerSize);
    621 
    622         int thisOffset;
    623 
    624         // Check if there is the desired entry in this type.
    625         if (isTruthy(thisType.flags & ResTable_type.FLAG_SPARSE)) {
    626           // This is encoded as a sparse map, so perform a binary search.
    627           final ByteBuffer buf = thisType.myBuf();
    628           ResTable_sparseTypeEntry sparseIndices = new ResTable_sparseTypeEntry(buf, eindex);
    629           ResTable_sparseTypeEntry result = lower_bound(
    630               sparseIndices,
    631               new ResTable_sparseTypeEntry(buf, sparseIndices.myOffset() + dtohl(thisType.entryCount)),
    632               new ResTable_sparseTypeEntry(buf, realEntryIndex),
    633               (a, b) -> dtohs(a.idxOrOffset) < dtohs(b.idxOrOffset));
    634 //          if (result == sparseIndices + dtohl(thisType.entryCount)
    635 //              || dtohs(result.idx) != realEntryIndex) {
    636           if (result.myOffset() == sparseIndices.myOffset() + dtohl(thisType.entryCount)
    637               || dtohs(result.idxOrOffset) != realEntryIndex) {
    638             // No entry found.
    639             continue;
    640           }
    641           // Extract the offset from the entry. Each offset must be a multiple of 4
    642           // so we store it as the real offset divided by 4.
    643 //          thisOffset = dtohs(result->offset) * 4u;
    644           thisOffset = dtohs(result.idxOrOffset) * 4;
    645         } else {
    646           if (realEntryIndex >= dtohl(thisType.entryCount)) {
    647             // Entry does not exist.
    648             continue;
    649           }
    650 //          thisOffset = dtohl(eindex[realEntryIndex]);
    651           thisOffset = thisType.entryOffset(realEntryIndex);
    652         }
    653 
    654         if (thisOffset == ResTable_type.NO_ENTRY) {
    655           // There is no entry for this index and configuration.
    656           continue;
    657         }
    658 
    659         if (bestType != NULL) {
    660           // Check if this one is less specific than the last found.  If so,
    661           // we will skip it.  We check starting with things we most care
    662           // about to those we least care about.
    663           if (!thisConfig.isBetterThan(bestConfig, config)) {
    664             if (!currentTypeIsOverlay || thisConfig.compare(bestConfig) != 0) {
    665               continue;
    666             }
    667           }
    668         }
    669 
    670         bestType = thisType;
    671         bestOffset = thisOffset;
    672         bestConfig = thisConfig;
    673         bestPackage = typeSpec._package_;
    674         actualTypeIndex = (byte) realTypeIndex;
    675 
    676         // If no config was specified, any type will do, so skip
    677         if (config == NULL) {
    678           break;
    679         }
    680       }
    681     }
    682 
    683     if (bestType == NULL) {
    684       return BAD_INDEX;
    685     }
    686 
    687     bestOffset += dtohl(bestType.entriesStart);
    688 
    689 //    if (bestOffset > (dtohl(bestType->header.size)-sizeof(ResTable_entry))) {
    690     if (bestOffset > (dtohl(bestType.header.size)- ResTable_entry.SIZEOF)) {
    691       ALOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x",
    692           bestOffset, dtohl(bestType.header.size));
    693       return BAD_TYPE;
    694     }
    695     if ((bestOffset & 0x3) != 0) {
    696       ALOGW("ResTable_entry at 0x%x is not on an integer boundary", bestOffset);
    697       return BAD_TYPE;
    698     }
    699 
    700 //    const ResTable_entry* const entry = reinterpret_cast<const ResTable_entry*>(
    701 //      reinterpret_cast<const uint8_t*>(bestType) + bestOffset);
    702     final ResTable_entry entry = new ResTable_entry(bestType.myBuf(),
    703         bestType.myOffset() + bestOffset);
    704     if (dtohs(entry.size) < ResTable_entry.SIZEOF) {
    705       ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry.size));
    706       return BAD_TYPE;
    707     }
    708 
    709     if (outEntry != null) {
    710       outEntry.entry = entry;
    711       outEntry.config = bestConfig;
    712       outEntry.type = bestType;
    713       outEntry.specFlags = specFlags;
    714       outEntry._package_ = bestPackage;
    715       outEntry.typeStr = new StringPoolRef(bestPackage.typeStrings, actualTypeIndex - bestPackage.typeIdOffset);
    716       outEntry.keyStr = new StringPoolRef(bestPackage.keyStrings, dtohl(entry.key.index));
    717     }
    718     return NO_ERROR;
    719   }
    720 
    721   int parsePackage(ResTable_package pkg,
    722                                 Header header, boolean appAsLib, boolean isSystemAsset)
    723   {
    724     int base = pkg.myOffset();
    725     int err = validate_chunk(pkg.header, ResTable_package.SIZEOF - 4 /*sizeof(pkg.typeIdOffset)*/,
    726       header.dataEnd, "ResTable_package");
    727     if (err != NO_ERROR) {
    728       return (mError=err);
    729     }
    730 
    731     final int pkgSize = dtohl(pkg.header.size);
    732 
    733     if (dtohl(pkg.typeStrings) >= pkgSize) {
    734       ALOGW("ResTable_package type strings at 0x%x are past chunk size 0x%x.",
    735           dtohl(pkg.typeStrings), pkgSize);
    736       return (mError=BAD_TYPE);
    737     }
    738     if ((dtohl(pkg.typeStrings)&0x3) != 0) {
    739       ALOGW("ResTable_package type strings at 0x%x is not on an integer boundary.",
    740           dtohl(pkg.typeStrings));
    741       return (mError=BAD_TYPE);
    742     }
    743     if (dtohl(pkg.keyStrings) >= pkgSize) {
    744       ALOGW("ResTable_package key strings at 0x%x are past chunk size 0x%x.",
    745           dtohl(pkg.keyStrings), pkgSize);
    746       return (mError=BAD_TYPE);
    747     }
    748     if ((dtohl(pkg.keyStrings)&0x3) != 0) {
    749       ALOGW("ResTable_package key strings at 0x%x is not on an integer boundary.",
    750           dtohl(pkg.keyStrings));
    751       return (mError=BAD_TYPE);
    752     }
    753 
    754     int id = dtohl(pkg.id);
    755     final Map<Byte, IdmapEntries> idmapEntries = new HashMap<>();
    756 
    757     if (header.resourceIDMap != NULL) {
    758 //      byte targetPackageId = 0;
    759 //      int err = parseIdmap(header.resourceIDMap, header.resourceIDMapSize, &targetPackageId, &idmapEntries);
    760 //      if (err != NO_ERROR) {
    761 //        ALOGW("Overlay is broken");
    762 //        return (mError=err);
    763 //      }
    764 //      id = targetPackageId;
    765     }
    766 
    767     boolean isDynamic = false;
    768     if (id >= 256) {
    769 //      LOG_ALWAYS_FATAL("Package id out of range");
    770       throw new IllegalStateException("Package id out of range");
    771 //      return NO_ERROR;
    772     } else if (id == 0 || (id == 0x7f && appAsLib) || isSystemAsset) {
    773       // This is a library or a system asset, so assign an ID
    774       id = mNextPackageId++;
    775       isDynamic = true;
    776     }
    777 
    778     PackageGroup group = null;
    779     Package _package = new Package(this, header, pkg);
    780     if (_package == NULL) {
    781     return (mError=NO_MEMORY);
    782   }
    783 
    784 //    err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
    785 //      header->dataEnd-(base+dtohl(pkg->typeStrings)));
    786     err = _package.typeStrings.setTo(pkg.myBuf(), base+dtohl(pkg.typeStrings),
    787       header.dataEnd -(base+dtohl(pkg.typeStrings)), false);
    788     if (err != NO_ERROR) {
    789 //      delete group;
    790 //      delete _package;
    791       return (mError=err);
    792     }
    793 
    794 //    err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
    795 //      header->dataEnd-(base+dtohl(pkg->keyStrings)));
    796     err = _package.keyStrings.setTo(pkg.myBuf(), base+dtohl(pkg.keyStrings),
    797       header.dataEnd -(base+dtohl(pkg.keyStrings)), false);
    798     if (err != NO_ERROR) {
    799 //      delete group;
    800 //      delete _package;
    801       return (mError=err);
    802     }
    803 
    804     int idx = mPackageMap[id];
    805     if (idx == 0) {
    806       idx = mPackageGroups.size() + 1;
    807 
    808 //      char[] tmpName = new char[pkg.name.length /*sizeof(pkg.name)/sizeof(pkg.name[0])*/];
    809 //      strcpy16_dtoh(tmpName, pkg.name, sizeof(pkg.name)/sizeof(pkg.name[0]));
    810       group = new PackageGroup(this, new String(pkg.name), id, appAsLib, isSystemAsset, isDynamic);
    811       if (group == NULL) {
    812 //        delete _package;
    813         return (mError=NO_MEMORY);
    814       }
    815 
    816       mPackageGroups.put(group.id, group);
    817 //      if (err < NO_ERROR) {
    818 //        return (mError=err);
    819 //      }
    820 
    821       mPackageMap[id] = (byte) idx;
    822 
    823       // Find all packages that reference this package
    824 //      int N = mPackageGroups.size();
    825 //      for (int i = 0; i < N; i++) {
    826       for (PackageGroup packageGroup : mPackageGroups.values()) {
    827         packageGroup.dynamicRefTable.addMapping(
    828             group.name, (byte) group.id);
    829       }
    830     } else {
    831       group = mPackageGroups.get(idx - 1);
    832       if (group == NULL) {
    833         return (mError=UNKNOWN_ERROR);
    834       }
    835     }
    836 
    837     group.packages.add(_package);
    838 //    if (err < NO_ERROR) {
    839 //      return (mError=err);
    840 //    }
    841 
    842     // Iterate through all chunks.
    843     ResChunk_header chunk =
    844       new ResChunk_header(pkg.myBuf(), pkg.myOffset() + dtohs(pkg.header.headerSize));
    845 //      const uint8_t* endPos = ((const uint8_t*)pkg) + dtohs(pkg->header.size);
    846     final int endPos = (pkg.myOffset()) + pkg.header.size;
    847 //    while (((const uint8_t*)chunk) <= (endPos-sizeof(ResChunk_header)) &&
    848 //      ((const uint8_t*)chunk) <= (endPos-dtohl(chunk->size))) {
    849     while (chunk != null && (chunk.myOffset()) <= (endPos-ResChunk_header.SIZEOF) &&
    850       (chunk.myOffset()) <= (endPos-dtohl(chunk.size))) {
    851     if (kDebugTableNoisy) {
    852       ALOGV("PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%s\n",
    853           dtohs(chunk.type), dtohs(chunk.headerSize), dtohl(chunk.size),
    854           ((chunk.myOffset()) - (header.header.myOffset())));
    855     }
    856         final int csize = dtohl(chunk.size);
    857         final short ctype = dtohs(chunk.type);
    858     if (ctype == RES_TABLE_TYPE_SPEC_TYPE) {
    859             final ResTable_typeSpec typeSpec = new ResTable_typeSpec(chunk.myBuf(), chunk.myOffset());
    860       err = validate_chunk(typeSpec.header, ResTable_typeSpec.SIZEOF,
    861       endPos, "ResTable_typeSpec");
    862       if (err != NO_ERROR) {
    863         return (mError=err);
    864       }
    865 
    866             final int typeSpecSize = dtohl(typeSpec.header.size);
    867             final int newEntryCount = dtohl(typeSpec.entryCount);
    868 
    869       if (kDebugLoadTableNoisy) {
    870         ALOGI("TypeSpec off %s: type=0x%x, headerSize=0x%x, size=%s\n",
    871             (base-chunk.myOffset()),
    872         dtohs(typeSpec.header.type),
    873             dtohs(typeSpec.header.headerSize),
    874             typeSpecSize);
    875       }
    876       // look for block overrun or int overflow when multiplying by 4
    877       if ((dtohl(typeSpec.entryCount) > (Integer.MAX_VALUE/4 /*sizeof(int)*/)
    878           || dtohs(typeSpec.header.headerSize)+(4 /*sizeof(int)*/*newEntryCount)
    879           > typeSpecSize)) {
    880         ALOGW("ResTable_typeSpec entry index to %s extends beyond chunk end %s.",
    881             (dtohs(typeSpec.header.headerSize) + (4 /*sizeof(int)*/*newEntryCount)),
    882             typeSpecSize);
    883         return (mError=BAD_TYPE);
    884       }
    885 
    886       if (typeSpec.id == 0) {
    887         ALOGW("ResTable_type has an id of 0.");
    888         return (mError=BAD_TYPE);
    889       }
    890 
    891       if (newEntryCount > 0) {
    892         boolean addToType = true;
    893         byte typeIndex = (byte) (typeSpec.id - 1);
    894         IdmapEntries idmapEntry = idmapEntries.get(typeSpec.id);
    895         if (idmapEntry != null) {
    896           typeIndex = (byte) (idmapEntry.targetTypeId() - 1);
    897         } else if (header.resourceIDMap != NULL) {
    898           // This is an overlay, but the types in this overlay are not
    899           // overlaying anything according to the idmap. We can skip these
    900           // as they will otherwise conflict with the other resources in the package
    901           // without a mapping.
    902           addToType = false;
    903         }
    904 
    905         if (addToType) {
    906           List<Type> typeList = computeIfAbsent(group.types, (int) typeIndex, k -> new ArrayList<>());
    907           if (!typeList.isEmpty()) {
    908             final Type existingType = typeList.get(0);
    909             if (existingType.entryCount != newEntryCount && idmapEntry == null) {
    910               ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d",
    911                   (int) newEntryCount, (int) existingType.entryCount);
    912               // We should normally abort here, but some legacy apps declare
    913               // resources in the 'android' package (old bug in AAPT).
    914             }
    915           }
    916 
    917           Type t = new Type(header, _package, newEntryCount);
    918           t.typeSpec = typeSpec;
    919           t.typeSpecFlags = typeSpec.getSpecFlags();
    920           if (idmapEntry != null) {
    921             t.idmapEntries = idmapEntry;
    922           }
    923           typeList.add(t);
    924           group.largestTypeId = max(group.largestTypeId, typeSpec.id);
    925         }
    926       } else {
    927         ALOGV("Skipping empty ResTable_typeSpec for type %d", typeSpec.id);
    928       }
    929 
    930     } else if (ctype == RES_TABLE_TYPE_TYPE) {
    931             ResTable_type type = new ResTable_type(chunk.myBuf(), chunk.myOffset());
    932       err = validate_chunk(type.header, ResTable_type.SIZEOF_WITHOUT_CONFIG/*-sizeof(ResTable_config)*/+4,
    933           endPos, "ResTable_type");
    934       if (err != NO_ERROR) {
    935         return (mError=err);
    936       }
    937 
    938             final int typeSize = dtohl(type.header.size);
    939             final int newEntryCount = dtohl(type.entryCount);
    940 
    941       if (kDebugLoadTableNoisy) {
    942         System.out.println(String.format("Type off 0x%x: type=0x%x, headerSize=0x%x, size=%d\n",
    943             base-chunk.myOffset(),
    944         dtohs(type.header.type),
    945             dtohs(type.header.headerSize),
    946             typeSize));
    947       }
    948       if (dtohs(type.header.headerSize)+(4/*sizeof(int)*/*newEntryCount) > typeSize) {
    949         ALOGW("ResTable_type entry index to %s extends beyond chunk end 0x%x.",
    950             (dtohs(type.header.headerSize) + (4/*sizeof(int)*/*newEntryCount)),
    951             typeSize);
    952         return (mError=BAD_TYPE);
    953       }
    954 
    955       if (newEntryCount != 0
    956           && dtohl(type.entriesStart) > (typeSize- ResTable_entry.SIZEOF)) {
    957         ALOGW("ResTable_type entriesStart at 0x%x extends beyond chunk end 0x%x.",
    958             dtohl(type.entriesStart), typeSize);
    959         return (mError=BAD_TYPE);
    960       }
    961 
    962       if (type.id == 0) {
    963         ALOGW("ResTable_type has an id of 0.");
    964         return (mError=BAD_TYPE);
    965       }
    966 
    967       if (newEntryCount > 0) {
    968         boolean addToType = true;
    969         byte typeIndex = (byte) (type.id - 1);
    970         IdmapEntries idmapEntry = idmapEntries.get(type.id);
    971         if (idmapEntry != null) {
    972           typeIndex = (byte) (idmapEntry.targetTypeId() - 1);
    973         } else if (header.resourceIDMap != NULL) {
    974           // This is an overlay, but the types in this overlay are not
    975           // overlaying anything according to the idmap. We can skip these
    976           // as they will otherwise conflict with the other resources in the package
    977           // without a mapping.
    978           addToType = false;
    979         }
    980 
    981         if (addToType) {
    982           List<Type> typeList = getOrDefault(group.types, (int) typeIndex, Collections.emptyList());
    983           if (typeList.isEmpty()) {
    984             ALOGE("No TypeSpec for type %d", type.id);
    985             return (mError = BAD_TYPE);
    986           }
    987 
    988           Type t = typeList.get(typeList.size() - 1);
    989           if (newEntryCount != t.entryCount) {
    990             ALOGE("ResTable_type entry count inconsistent: given %d, previously %d",
    991                 (int) newEntryCount, (int) t.entryCount);
    992             return (mError = BAD_TYPE);
    993           }
    994 
    995           if (t._package_ != _package) {
    996             ALOGE("No TypeSpec for type %d", type.id);
    997             return (mError = BAD_TYPE);
    998           }
    999 
   1000           t.configs.add(type);
   1001 
   1002           if (kDebugTableGetEntry) {
   1003             ResTable_config thisConfig = ResTable_config.fromDtoH(type.config);
   1004             ALOGI("Adding config to type %d: %s\n", type.id,
   1005                 thisConfig.toString());
   1006           }
   1007         }
   1008       } else {
   1009         ALOGV("Skipping empty ResTable_type for type %d", type.id);
   1010       }
   1011 
   1012     } else if (ctype == RES_TABLE_LIBRARY_TYPE) {
   1013       if (group.dynamicRefTable.entries().isEmpty()) {
   1014         throw new UnsupportedOperationException("libraries not supported yet");
   1015 //       const ResTable_lib_header* lib = (const ResTable_lib_header*) chunk;
   1016 //       status_t err = validate_chunk(&lib->header, sizeof(*lib),
   1017 //       endPos, "ResTable_lib_header");
   1018 //       if (err != NO_ERROR) {
   1019 //         return (mError=err);
   1020 //       }
   1021 //
   1022 //       err = group->dynamicRefTable.load(lib);
   1023 //       if (err != NO_ERROR) {
   1024 //          return (mError=err);
   1025 //        }
   1026 //
   1027 //        // Fill in the reference table with the entries we already know about.
   1028 //        size_t N = mPackageGroups.size();
   1029 //        for (size_t i = 0; i < N; i++) {
   1030 //          group.dynamicRefTable.addMapping(mPackageGroups[i].name, mPackageGroups[i].id);
   1031 //        }
   1032       } else {
   1033         ALOGW("Found multiple library tables, ignoring...");
   1034       }
   1035     } else {
   1036       err = validate_chunk(chunk, ResChunk_header.SIZEOF,
   1037           endPos, "ResTable_package:unknown");
   1038       if (err != NO_ERROR) {
   1039         return (mError=err);
   1040       }
   1041     }
   1042       chunk = chunk.myOffset() + csize < endPos ? new ResChunk_header(chunk.myBuf(), chunk.myOffset() + csize) : null;
   1043   }
   1044 
   1045     return NO_ERROR;
   1046   }
   1047 
   1048   public int getTableCookie(int index) {
   1049     return mHeaders.get(index).cookie;
   1050   }
   1051 
   1052   void setParameters(ResTable_config params)
   1053   {
   1054 //    AutoMutex _lock(mLock);
   1055 //    AutoMutex _lock2(mFilteredConfigLock);
   1056     synchronized (mLock) {
   1057       synchronized (mFilteredConfigLock) {
   1058         if (kDebugTableGetEntry) {
   1059           ALOGI("Setting parameters: %s\n", params.toString());
   1060         }
   1061         mParams = params;
   1062         for (PackageGroup packageGroup : mPackageGroups.values()) {
   1063           if (kDebugTableNoisy) {
   1064             ALOGI("CLEARING BAGS FOR GROUP 0x%x!", packageGroup.id);
   1065           }
   1066           packageGroup.clearBagCache();
   1067 
   1068           // Find which configurations match the set of parameters. This allows for a much
   1069           // faster lookup in getEntry() if the set of values is narrowed down.
   1070           //for (int t = 0; t < packageGroup.types.size(); t++) {
   1071             //if (packageGroup.types.get(t).isEmpty()) {
   1072             //   continue;
   1073             // }
   1074             //
   1075             // List<Type> typeList = packageGroup.types.get(t);
   1076         for (List<Type> typeList : packageGroup.types.values()) {
   1077           if (typeList.isEmpty()) {
   1078                continue;
   1079             }
   1080 
   1081           // Retrieve the cache entry for this type.
   1082             //TypeCacheEntry cacheEntry = packageGroup.typeCacheEntries.editItemAt(t);
   1083 
   1084             for (int ts = 0; ts < typeList.size(); ts++) {
   1085               Type type = typeList.get(ts);
   1086 
   1087 //              std::shared_ptr<Vector<const ResTable_type*>> newFilteredConfigs =
   1088 //                  std::make_shared<Vector<const ResTable_type*>>();
   1089               List<ResTable_type> newFilteredConfigs = new ArrayList<>();
   1090 
   1091               for (int ti = 0; ti < type.configs.size(); ti++) {
   1092                 ResTable_config config = ResTable_config.fromDtoH(type.configs.get(ti).config);
   1093 
   1094                 if (config.match(mParams)) {
   1095                   newFilteredConfigs.add(type.configs.get(ti));
   1096                 }
   1097               }
   1098 
   1099               if (kDebugTableNoisy) {
   1100                 ALOGD("Updating pkg=0x%x type=0x%x with 0x%x filtered configs",
   1101                     packageGroup.id, ts, newFilteredConfigs.size());
   1102               }
   1103 
   1104               // todo: implement cache
   1105 //              cacheEntry.filteredConfigs.add(newFilteredConfigs);
   1106             }
   1107           }
   1108         }
   1109       }
   1110     }
   1111   }
   1112 
   1113   ResTable_config getParameters()
   1114   {
   1115 //    mLock.lock();
   1116     synchronized (mLock) {
   1117       return mParams;
   1118     }
   1119 //    mLock.unlock();
   1120   }
   1121 
   1122   private static final Map<String, Integer> sInternalNameToIdMap = new HashMap<>();
   1123   static {
   1124     sInternalNameToIdMap.put("^type", ResTable_map.ATTR_TYPE);
   1125     sInternalNameToIdMap.put("^l10n", ResTable_map.ATTR_L10N);
   1126     sInternalNameToIdMap.put("^min" , ResTable_map.ATTR_MIN);
   1127     sInternalNameToIdMap.put("^max", ResTable_map.ATTR_MAX);
   1128     sInternalNameToIdMap.put("^other", ResTable_map.ATTR_OTHER);
   1129     sInternalNameToIdMap.put("^zero", ResTable_map.ATTR_ZERO);
   1130     sInternalNameToIdMap.put("^one", ResTable_map.ATTR_ONE);
   1131     sInternalNameToIdMap.put("^two", ResTable_map.ATTR_TWO);
   1132     sInternalNameToIdMap.put("^few", ResTable_map.ATTR_FEW);
   1133     sInternalNameToIdMap.put("^many", ResTable_map.ATTR_MANY);
   1134   }
   1135 
   1136   public int identifierForName(String name, String type, String packageName) {
   1137     return identifierForName(name, type, packageName, null);
   1138   }
   1139 
   1140   public int identifierForName(String nameString, String type, String packageName,
   1141       final Ref<Integer> outTypeSpecFlags) {
   1142 //    if (kDebugTableSuperNoisy) {
   1143 //      printf("Identifier for name: error=%d\n", mError);
   1144 //    }
   1145 //    // Check for internal resource identifier as the very first thing, so
   1146 //    // that we will always find them even when there are no resources.
   1147     if (nameString.startsWith("^")) {
   1148       if (sInternalNameToIdMap.containsKey(nameString)) {
   1149         if (outTypeSpecFlags != null) {
   1150           outTypeSpecFlags.set(ResTable_typeSpec.SPEC_PUBLIC);
   1151         }
   1152         return sInternalNameToIdMap.get(nameString);
   1153       }
   1154       if (nameString.length() > 7)
   1155         if (nameString.substring(1, 6).equals("index_")) {
   1156           int index = Integer.getInteger(nameString.substring(7));
   1157           if (Res_CHECKID(index)) {
   1158             ALOGW("Array resource index: %d is too large.",
   1159                 index);
   1160             return 0;
   1161           }
   1162           if (outTypeSpecFlags != null) {
   1163             outTypeSpecFlags.set(ResTable_typeSpec.SPEC_PUBLIC);
   1164           }
   1165           return  Res_MAKEARRAY(index);
   1166         }
   1167 
   1168       return 0;
   1169     }
   1170 
   1171     if (mError != NO_ERROR) {
   1172       return 0;
   1173     }
   1174 
   1175 
   1176     // Figure out the package and type we are looking in...
   1177     // TODO(BC): The following code block was a best effort attempt to directly transliterate
   1178     // C++ code which uses pointer artihmetic. Consider replacing with simpler logic
   1179 
   1180     boolean fakePublic = false;
   1181     char[] name = nameString.toCharArray();
   1182     int packageEnd = -1;
   1183     int typeEnd = -1;
   1184     int nameEnd = name.length;
   1185     int pIndex = 0;
   1186     while (pIndex < nameEnd) {
   1187       char p = name[pIndex];
   1188       if (p == ':') packageEnd = pIndex;
   1189       else if (p == '/') typeEnd = pIndex;
   1190       pIndex++;
   1191     }
   1192     int nameIndex = 0;
   1193     if (name[nameIndex] == '@') {
   1194       nameIndex++;
   1195       if (name[nameIndex] == '*') {
   1196         fakePublic = true;
   1197         nameIndex++;
   1198     }
   1199   }
   1200     if (nameIndex >= nameEnd) {
   1201       return 0;
   1202     }
   1203     if (packageEnd != -1) {
   1204         packageName = nameString.substring(nameIndex, packageEnd);
   1205         nameIndex = packageEnd+1;
   1206     } else if (packageName == null) {
   1207       return 0;
   1208     }
   1209     if (typeEnd != -1) {
   1210       type = nameString.substring(nameIndex, typeEnd);
   1211       nameIndex = typeEnd+1;
   1212     } else if (type == null) {
   1213       return 0;
   1214     }
   1215     if (nameIndex >= nameEnd) {
   1216       return 0;
   1217     }
   1218     nameString = nameString.substring(nameIndex, nameEnd);
   1219 
   1220 //    nameLen = nameEnd-name;
   1221 //    if (kDebugTableNoisy) {
   1222 //      printf("Looking for identifier: type=%s, name=%s, package=%s\n",
   1223 //          String8(type, typeLen).string(),
   1224 //          String8(name, nameLen).string(),
   1225 //          String8(package, packageLen).string());
   1226 //    }
   1227     final String attr = "attr";
   1228     final String attrPrivate = "^attr-private";
   1229     for (PackageGroup group : mPackageGroups.values()) {
   1230       if (!Objects.equals(packageName.trim(), group.name.trim())) {
   1231         if (kDebugTableNoisy) {
   1232            System.out.println(String.format("Skipping package group: %s\n", group.name));
   1233         }
   1234         continue;
   1235       }
   1236       for (Package pkg : group.packages) {
   1237         String targetType = type;
   1238 
   1239         do {
   1240           int ti = pkg.typeStrings.indexOfString(targetType);
   1241           if (ti < 0) {
   1242             continue;
   1243           }
   1244           ti += pkg.typeIdOffset;
   1245           int identifier = findEntry(group, ti, nameString, outTypeSpecFlags);
   1246           if (identifier != 0) {
   1247             if (fakePublic && outTypeSpecFlags != null) {
   1248                         outTypeSpecFlags.set(outTypeSpecFlags.get() | ResTable_typeSpec.SPEC_PUBLIC);
   1249             }
   1250             return identifier;
   1251           }
   1252         } while (attr.compareTo(targetType) == 0
   1253             && ((targetType = attrPrivate) != null)
   1254             );
   1255       }
   1256       break;
   1257     }
   1258     return 0;
   1259   }
   1260 
   1261   int findEntry(PackageGroup group, int typeIndex, String name, Ref<Integer> outTypeSpecFlags) {
   1262     List<Type> typeList = getOrDefault(group.types, typeIndex, Collections.emptyList());
   1263     for (Type type : typeList) {
   1264       int ei = type._package_.keyStrings.indexOfString(name);
   1265       if (ei < 0) {
   1266         continue;
   1267       }
   1268       for (ResTable_type resTableType : type.configs) {
   1269         int entryIndex = resTableType.findEntryByResName(ei);
   1270         if (entryIndex >= 0) {
   1271           int resId = Res_MAKEID(group.id - 1, typeIndex, entryIndex);
   1272           if (outTypeSpecFlags != null) {
   1273             Entry result = new Entry();
   1274             if (getEntry(group, typeIndex, entryIndex, null, result) != NO_ERROR) {
   1275               ALOGW("Failed to find spec flags for 0x%08x", resId);
   1276               return 0;
   1277             }
   1278             outTypeSpecFlags.set(result.specFlags);
   1279           }
   1280           return resId;
   1281         }
   1282       }
   1283     }
   1284     return 0;
   1285   }
   1286 
   1287 //bool ResTable::expandResourceRef(const char16_t* refStr, size_t refLen,
   1288 //                                 String16* outPackage,
   1289 //                                 String16* outType,
   1290 //                                 String16* outName,
   1291 //                                 const String16* defType,
   1292 //                                 const String16* defPackage,
   1293 //                                 const char** outErrorMsg,
   1294 //                                 bool* outPublicOnly)
   1295 //{
   1296 //    const char16_t* packageEnd = NULL;
   1297 //    const char16_t* typeEnd = NULL;
   1298 //    const char16_t* p = refStr;
   1299 //    const char16_t* const end = p + refLen;
   1300 //    while (p < end) {
   1301 //        if (*p == ':') packageEnd = p;
   1302 //        else if (*p == '/') {
   1303 //            typeEnd = p;
   1304 //            break;
   1305 //        }
   1306 //        p++;
   1307 //    }
   1308 //    p = refStr;
   1309 //    if (*p == '@') p++;
   1310 //
   1311 //    if (outPublicOnly != NULL) {
   1312 //        *outPublicOnly = true;
   1313 //    }
   1314 //    if (*p == '*') {
   1315 //        p++;
   1316 //        if (outPublicOnly != NULL) {
   1317 //            *outPublicOnly = false;
   1318 //        }
   1319 //    }
   1320 //
   1321 //    if (packageEnd) {
   1322 //        *outPackage = String16(p, packageEnd-p);
   1323 //        p = packageEnd+1;
   1324 //    } else {
   1325 //        if (!defPackage) {
   1326 //            if (outErrorMsg) {
   1327 //                *outErrorMsg = "No resource package specified";
   1328 //            }
   1329 //            return false;
   1330 //        }
   1331 //        *outPackage = *defPackage;
   1332 //    }
   1333 //    if (typeEnd) {
   1334 //        *outType = String16(p, typeEnd-p);
   1335 //        p = typeEnd+1;
   1336 //    } else {
   1337 //        if (!defType) {
   1338 //            if (outErrorMsg) {
   1339 //                *outErrorMsg = "No resource type specified";
   1340 //            }
   1341 //            return false;
   1342 //        }
   1343 //        *outType = *defType;
   1344 //    }
   1345 //    *outName = String16(p, end-p);
   1346 //    if(**outPackage == 0) {
   1347 //        if(outErrorMsg) {
   1348 //            *outErrorMsg = "Resource package cannot be an empty string";
   1349 //        }
   1350 //        return false;
   1351 //    }
   1352 //    if(**outType == 0) {
   1353 //        if(outErrorMsg) {
   1354 //            *outErrorMsg = "Resource type cannot be an empty string";
   1355 //        }
   1356 //        return false;
   1357 //    }
   1358 //    if(**outName == 0) {
   1359 //        if(outErrorMsg) {
   1360 //            *outErrorMsg = "Resource id cannot be an empty string";
   1361 //        }
   1362 //        return false;
   1363 //    }
   1364 //    return true;
   1365 //}
   1366 //
   1367 //static uint32_t get_hex(char c, bool* outError)
   1368 //{
   1369 //    if (c >= '0' && c <= '9') {
   1370 //        return c - '0';
   1371 //    } else if (c >= 'a' && c <= 'f') {
   1372 //        return c - 'a' + 0xa;
   1373 //    } else if (c >= 'A' && c <= 'F') {
   1374 //        return c - 'A' + 0xa;
   1375 //    }
   1376 //    *outError = true;
   1377 //    return 0;
   1378 //}
   1379 //
   1380 //struct unit_entry
   1381 //{
   1382 //    const char* name;
   1383 //    size_t len;
   1384 //    uint8_t type;
   1385 //    uint32_t unit;
   1386 //    float scale;
   1387 //};
   1388 //
   1389 //static const unit_entry unitNames[] = {
   1390 //    { "px", strlen("px"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PX, 1.0f },
   1391 //    { "dip", strlen("dip"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f },
   1392 //    { "dp", strlen("dp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f },
   1393 //    { "sp", strlen("sp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_SP, 1.0f },
   1394 //    { "pt", strlen("pt"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PT, 1.0f },
   1395 //    { "in", strlen("in"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_IN, 1.0f },
   1396 //    { "mm", strlen("mm"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_MM, 1.0f },
   1397 //    { "%", strlen("%"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION, 1.0f/100 },
   1398 //    { "%s", strlen("%s"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100 },
   1399 //    { NULL, 0, 0, 0, 0 }
   1400 //};
   1401 //
   1402 //static bool parse_unit(const char* str, Res_value* outValue,
   1403 //                       float* outScale, const char** outEnd)
   1404 //{
   1405 //    const char* end = str;
   1406 //    while (*end != 0 && !isspace((unsigned char)*end)) {
   1407 //        end++;
   1408 //    }
   1409 //    const size_t len = end-str;
   1410 //
   1411 //    const char* realEnd = end;
   1412 //    while (*realEnd != 0 && isspace((unsigned char)*realEnd)) {
   1413 //        realEnd++;
   1414 //    }
   1415 //    if (*realEnd != 0) {
   1416 //        return false;
   1417 //    }
   1418 //
   1419 //    const unit_entry* cur = unitNames;
   1420 //    while (cur->name) {
   1421 //        if (len == cur->len && strncmp(cur->name, str, len) == 0) {
   1422 //            outValue->dataType = cur->type;
   1423 //            outValue->data = cur->unit << Res_value::COMPLEX_UNIT_SHIFT;
   1424 //            *outScale = cur->scale;
   1425 //            *outEnd = end;
   1426 //            //printf("Found unit %s for %s\n", cur->name, str);
   1427 //            return true;
   1428 //        }
   1429 //        cur++;
   1430 //    }
   1431 //
   1432 //    return false;
   1433 //}
   1434 //
   1435 //bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue)
   1436 //{
   1437 //    while (len > 0 && isspace16(*s)) {
   1438 //        s++;
   1439 //        len--;
   1440 //    }
   1441 //
   1442 //    if (len <= 0) {
   1443 //        return false;
   1444 //    }
   1445 //
   1446 //    size_t i = 0;
   1447 //    int64_t val = 0;
   1448 //    bool neg = false;
   1449 //
   1450 //    if (*s == '-') {
   1451 //        neg = true;
   1452 //        i++;
   1453 //    }
   1454 //
   1455 //    if (s[i] < '0' || s[i] > '9') {
   1456 //        return false;
   1457 //    }
   1458 //
   1459 //    static_assert(std::is_same<uint32_t, Res_value::data_type>::value,
   1460 //                  "Res_value::data_type has changed. The range checks in this "
   1461 //                  "function are no longer correct.");
   1462 //
   1463 //    // Decimal or hex?
   1464 //    bool isHex;
   1465 //    if (len > 1 && s[i] == '0' && s[i+1] == 'x') {
   1466 //        isHex = true;
   1467 //        i += 2;
   1468 //
   1469 //        if (neg) {
   1470 //            return false;
   1471 //        }
   1472 //
   1473 //        if (i == len) {
   1474 //            // Just u"0x"
   1475 //            return false;
   1476 //        }
   1477 //
   1478 //        bool error = false;
   1479 //        while (i < len && !error) {
   1480 //            val = (val*16) + get_hex(s[i], &error);
   1481 //            i++;
   1482 //
   1483 //            if (val > std::numeric_limits<uint32_t>::max()) {
   1484 //                return false;
   1485 //            }
   1486 //        }
   1487 //        if (error) {
   1488 //            return false;
   1489 //        }
   1490 //    } else {
   1491 //        isHex = false;
   1492 //        while (i < len) {
   1493 //            if (s[i] < '0' || s[i] > '9') {
   1494 //                return false;
   1495 //            }
   1496 //            val = (val*10) + s[i]-'0';
   1497 //            i++;
   1498 //
   1499 //            if ((neg && -val < std::numeric_limits<int32_t>::min()) ||
   1500 //                (!neg && val > std::numeric_limits<int32_t>::max())) {
   1501 //                return false;
   1502 //            }
   1503 //        }
   1504 //    }
   1505 //
   1506 //    if (neg) val = -val;
   1507 //
   1508 //    while (i < len && isspace16(s[i])) {
   1509 //        i++;
   1510 //    }
   1511 //
   1512 //    if (i != len) {
   1513 //        return false;
   1514 //    }
   1515 //
   1516 //    if (outValue) {
   1517 //        outValue->dataType =
   1518 //            isHex ? outValue->TYPE_INT_HEX : outValue->TYPE_INT_DEC;
   1519 //        outValue->data = static_cast<Res_value::data_type>(val);
   1520 //    }
   1521 //    return true;
   1522 //}
   1523 //
   1524 //bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue)
   1525 //{
   1526 //    return U16StringToInt(s, len, outValue);
   1527 //}
   1528 //
   1529 //bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue)
   1530 //{
   1531 //    while (len > 0 && isspace16(*s)) {
   1532 //        s++;
   1533 //        len--;
   1534 //    }
   1535 //
   1536 //    if (len <= 0) {
   1537 //        return false;
   1538 //    }
   1539 //
   1540 //    char buf[128];
   1541 //    int i=0;
   1542 //    while (len > 0 && *s != 0 && i < 126) {
   1543 //        if (*s > 255) {
   1544 //            return false;
   1545 //        }
   1546 //        buf[i++] = *s++;
   1547 //        len--;
   1548 //    }
   1549 //
   1550 //    if (len > 0) {
   1551 //        return false;
   1552 //    }
   1553 //    if ((buf[0] < '0' || buf[0] > '9') && buf[0] != '.' && buf[0] != '-' && buf[0] != '+') {
   1554 //        return false;
   1555 //    }
   1556 //
   1557 //    buf[i] = 0;
   1558 //    const char* end;
   1559 //    float f = strtof(buf, (char**)&end);
   1560 //
   1561 //    if (*end != 0 && !isspace((unsigned char)*end)) {
   1562 //        // Might be a unit...
   1563 //        float scale;
   1564 //        if (parse_unit(end, outValue, &scale, &end)) {
   1565 //            f *= scale;
   1566 //            const bool neg = f < 0;
   1567 //            if (neg) f = -f;
   1568 //            uint64_t bits = (uint64_t)(f*(1<<23)+.5f);
   1569 //            uint32_t radix;
   1570 //            uint32_t shift;
   1571 //            if ((bits&0x7fffff) == 0) {
   1572 //                // Always use 23p0 if there is no fraction, just to make
   1573 //                // things easier to read.
   1574 //                radix = Res_value::COMPLEX_RADIX_23p0;
   1575 //                shift = 23;
   1576 //            } else if ((bits&0xffffffffff800000LL) == 0) {
   1577 //                // Magnitude is zero -- can fit in 0 bits of precision.
   1578 //                radix = Res_value::COMPLEX_RADIX_0p23;
   1579 //                shift = 0;
   1580 //            } else if ((bits&0xffffffff80000000LL) == 0) {
   1581 //                // Magnitude can fit in 8 bits of precision.
   1582 //                radix = Res_value::COMPLEX_RADIX_8p15;
   1583 //                shift = 8;
   1584 //            } else if ((bits&0xffffff8000000000LL) == 0) {
   1585 //                // Magnitude can fit in 16 bits of precision.
   1586 //                radix = Res_value::COMPLEX_RADIX_16p7;
   1587 //                shift = 16;
   1588 //            } else {
   1589 //                // Magnitude needs entire range, so no fractional part.
   1590 //                radix = Res_value::COMPLEX_RADIX_23p0;
   1591 //                shift = 23;
   1592 //            }
   1593 //            int32_t mantissa = (int32_t)(
   1594 //                (bits>>shift) & Res_value::COMPLEX_MANTISSA_MASK);
   1595 //            if (neg) {
   1596 //                mantissa = (-mantissa) & Res_value::COMPLEX_MANTISSA_MASK;
   1597 //            }
   1598 //            outValue->data |=
   1599 //                (radix<<Res_value::COMPLEX_RADIX_SHIFT)
   1600 //                | (mantissa<<Res_value::COMPLEX_MANTISSA_SHIFT);
   1601 //            //printf("Input value: %f 0x%016Lx, mult: %f, radix: %d, shift: %d, final: 0x%08x\n",
   1602 //            //       f * (neg ? -1 : 1), bits, f*(1<<23),
   1603 //            //       radix, shift, outValue->data);
   1604 //            return true;
   1605 //        }
   1606 //        return false;
   1607 //    }
   1608 //
   1609 //    while (*end != 0 && isspace((unsigned char)*end)) {
   1610 //        end++;
   1611 //    }
   1612 //
   1613 //    if (*end == 0) {
   1614 //        if (outValue) {
   1615 //            outValue->dataType = outValue->TYPE_FLOAT;
   1616 //            *(float*)(&outValue->data) = f;
   1617 //            return true;
   1618 //        }
   1619 //    }
   1620 //
   1621 //    return false;
   1622 //}
   1623 //
   1624 //bool ResTable::stringToValue(Res_value* outValue, String16* outString,
   1625 //                             const char16_t* s, size_t len,
   1626 //                             bool preserveSpaces, bool coerceType,
   1627 //                             uint32_t attrID,
   1628 //                             const String16* defType,
   1629 //                             const String16* defPackage,
   1630 //                             Accessor* accessor,
   1631 //                             void* accessorCookie,
   1632 //                             uint32_t attrType,
   1633 //                             bool enforcePrivate) const
   1634 //{
   1635 //    bool localizationSetting = accessor != NULL && accessor->getLocalizationSetting();
   1636 //    const char* errorMsg = NULL;
   1637 //
   1638 //    outValue->size = sizeof(Res_value);
   1639 //    outValue->res0 = 0;
   1640 //
   1641 //    // First strip leading/trailing whitespace.  Do this before handling
   1642 //    // escapes, so they can be used to force whitespace into the string.
   1643 //    if (!preserveSpaces) {
   1644 //        while (len > 0 && isspace16(*s)) {
   1645 //            s++;
   1646 //            len--;
   1647 //        }
   1648 //        while (len > 0 && isspace16(s[len-1])) {
   1649 //            len--;
   1650 //        }
   1651 //        // If the string ends with '\', then we keep the space after it.
   1652 //        if (len > 0 && s[len-1] == '\\' && s[len] != 0) {
   1653 //            len++;
   1654 //        }
   1655 //    }
   1656 //
   1657 //    //printf("Value for: %s\n", String8(s, len).string());
   1658 //
   1659 //    uint32_t l10nReq = ResTable_map::L10N_NOT_REQUIRED;
   1660 //    uint32_t attrMin = 0x80000000, attrMax = 0x7fffffff;
   1661 //    bool fromAccessor = false;
   1662 //    if (attrID != 0 && !Res_INTERNALID(attrID)) {
   1663 //        const ssize_t p = getResourcePackageIndex(attrID);
   1664 //        const bag_entry* bag;
   1665 //        ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
   1666 //        //printf("For attr 0x%08x got bag of %d\n", attrID, cnt);
   1667 //        if (cnt >= 0) {
   1668 //            while (cnt > 0) {
   1669 //                //printf("Entry 0x%08x = 0x%08x\n", bag->map.name.ident, bag->map.value.data);
   1670 //                switch (bag->map.name.ident) {
   1671 //                case ResTable_map::ATTR_TYPE:
   1672 //                    attrType = bag->map.value.data;
   1673 //                    break;
   1674 //                case ResTable_map::ATTR_MIN:
   1675 //                    attrMin = bag->map.value.data;
   1676 //                    break;
   1677 //                case ResTable_map::ATTR_MAX:
   1678 //                    attrMax = bag->map.value.data;
   1679 //                    break;
   1680 //                case ResTable_map::ATTR_L10N:
   1681 //                    l10nReq = bag->map.value.data;
   1682 //                    break;
   1683 //                }
   1684 //                bag++;
   1685 //                cnt--;
   1686 //            }
   1687 //            unlockBag(bag);
   1688 //        } else if (accessor && accessor->getAttributeType(attrID, &attrType)) {
   1689 //            fromAccessor = true;
   1690 //            if (attrType == ResTable_map::TYPE_ENUM
   1691 //                    || attrType == ResTable_map::TYPE_FLAGS
   1692 //                    || attrType == ResTable_map::TYPE_INTEGER) {
   1693 //                accessor->getAttributeMin(attrID, &attrMin);
   1694 //                accessor->getAttributeMax(attrID, &attrMax);
   1695 //            }
   1696 //            if (localizationSetting) {
   1697 //                l10nReq = accessor->getAttributeL10N(attrID);
   1698 //            }
   1699 //        }
   1700 //    }
   1701 //
   1702 //    const bool canStringCoerce =
   1703 //        coerceType && (attrType&ResTable_map::TYPE_STRING) != 0;
   1704 //
   1705 //    if (*s == '@') {
   1706 //        outValue->dataType = outValue->TYPE_REFERENCE;
   1707 //
   1708 //        // Note: we don't check attrType here because the reference can
   1709 //        // be to any other type; we just need to count on the client making
   1710 //        // sure the referenced type is correct.
   1711 //
   1712 //        //printf("Looking up ref: %s\n", String8(s, len).string());
   1713 //
   1714 //        // It's a reference!
   1715 //        if (len == 5 && s[1]=='n' && s[2]=='u' && s[3]=='l' && s[4]=='l') {
   1716 //            // Special case @null as undefined. This will be converted by
   1717 //            // AssetManager to TYPE_NULL with data DATA_NULL_UNDEFINED.
   1718 //            outValue->data = 0;
   1719 //            return true;
   1720 //        } else if (len == 6 && s[1]=='e' && s[2]=='m' && s[3]=='p' && s[4]=='t' && s[5]=='y') {
   1721 //            // Special case @empty as explicitly defined empty value.
   1722 //            outValue->dataType = Res_value::TYPE_NULL;
   1723 //            outValue->data = Res_value::DATA_NULL_EMPTY;
   1724 //            return true;
   1725 //        } else {
   1726 //            bool createIfNotFound = false;
   1727 //            const char16_t* resourceRefName;
   1728 //            int resourceNameLen;
   1729 //            if (len > 2 && s[1] == '+') {
   1730 //                createIfNotFound = true;
   1731 //                resourceRefName = s + 2;
   1732 //                resourceNameLen = len - 2;
   1733 //            } else if (len > 2 && s[1] == '*') {
   1734 //                enforcePrivate = false;
   1735 //                resourceRefName = s + 2;
   1736 //                resourceNameLen = len - 2;
   1737 //            } else {
   1738 //                createIfNotFound = false;
   1739 //                resourceRefName = s + 1;
   1740 //                resourceNameLen = len - 1;
   1741 //            }
   1742 //            String16 package, type, name;
   1743 //            if (!expandResourceRef(resourceRefName,resourceNameLen, &package, &type, &name,
   1744 //                                   defType, defPackage, &errorMsg)) {
   1745 //                if (accessor != NULL) {
   1746 //                    accessor->reportError(accessorCookie, errorMsg);
   1747 //                }
   1748 //                return false;
   1749 //            }
   1750 //
   1751 //            uint32_t specFlags = 0;
   1752 //            uint32_t rid = identifierForName(name.string(), name.size(), type.string(),
   1753 //                    type.size(), package.string(), package.size(), &specFlags);
   1754 //            if (rid != 0) {
   1755 //                if (enforcePrivate) {
   1756 //                    if (accessor == NULL || accessor->getAssetsPackage() != package) {
   1757 //                        if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) {
   1758 //                            if (accessor != NULL) {
   1759 //                                accessor->reportError(accessorCookie, "Resource is not public.");
   1760 //                            }
   1761 //                            return false;
   1762 //                        }
   1763 //                    }
   1764 //                }
   1765 //
   1766 //                if (accessor) {
   1767 //                    rid = Res_MAKEID(
   1768 //                        accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
   1769 //                        Res_GETTYPE(rid), Res_GETENTRY(rid));
   1770 //                    if (kDebugTableNoisy) {
   1771 //                        ALOGI("Incl %s:%s/%s: 0x%08x\n",
   1772 //                                String8(package).string(), String8(type).string(),
   1773 //                                String8(name).string(), rid);
   1774 //                    }
   1775 //                }
   1776 //
   1777 //                uint32_t packageId = Res_GETPACKAGE(rid) + 1;
   1778 //                if (packageId != APP_PACKAGE_ID && packageId != SYS_PACKAGE_ID) {
   1779 //                    outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
   1780 //                }
   1781 //                outValue->data = rid;
   1782 //                return true;
   1783 //            }
   1784 //
   1785 //            if (accessor) {
   1786 //                uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name,
   1787 //                                                                       createIfNotFound);
   1788 //                if (rid != 0) {
   1789 //                    if (kDebugTableNoisy) {
   1790 //                        ALOGI("Pckg %s:%s/%s: 0x%08x\n",
   1791 //                                String8(package).string(), String8(type).string(),
   1792 //                                String8(name).string(), rid);
   1793 //                    }
   1794 //                    uint32_t packageId = Res_GETPACKAGE(rid) + 1;
   1795 //                    if (packageId == 0x00) {
   1796 //                        outValue->data = rid;
   1797 //                        outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
   1798 //                        return true;
   1799 //                    } else if (packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID) {
   1800 //                        // We accept packageId's generated as 0x01 in order to support
   1801 //                        // building the android system resources
   1802 //                        outValue->data = rid;
   1803 //                        return true;
   1804 //                    }
   1805 //                }
   1806 //            }
   1807 //        }
   1808 //
   1809 //        if (accessor != NULL) {
   1810 //            accessor->reportError(accessorCookie, "No resource found that matches the given name");
   1811 //        }
   1812 //        return false;
   1813 //    }
   1814 //
   1815 //    // if we got to here, and localization is required and it's not a reference,
   1816 //    // complain and bail.
   1817 //    if (l10nReq == ResTable_map::L10N_SUGGESTED) {
   1818 //        if (localizationSetting) {
   1819 //            if (accessor != NULL) {
   1820 //                accessor->reportError(accessorCookie, "This attribute must be localized.");
   1821 //            }
   1822 //        }
   1823 //    }
   1824 //
   1825 //    if (*s == '#') {
   1826 //        // It's a color!  Convert to an integer of the form 0xaarrggbb.
   1827 //        uint32_t color = 0;
   1828 //        bool error = false;
   1829 //        if (len == 4) {
   1830 //            outValue->dataType = outValue->TYPE_INT_COLOR_RGB4;
   1831 //            color |= 0xFF000000;
   1832 //            color |= get_hex(s[1], &error) << 20;
   1833 //            color |= get_hex(s[1], &error) << 16;
   1834 //            color |= get_hex(s[2], &error) << 12;
   1835 //            color |= get_hex(s[2], &error) << 8;
   1836 //            color |= get_hex(s[3], &error) << 4;
   1837 //            color |= get_hex(s[3], &error);
   1838 //        } else if (len == 5) {
   1839 //            outValue->dataType = outValue->TYPE_INT_COLOR_ARGB4;
   1840 //            color |= get_hex(s[1], &error) << 28;
   1841 //            color |= get_hex(s[1], &error) << 24;
   1842 //            color |= get_hex(s[2], &error) << 20;
   1843 //            color |= get_hex(s[2], &error) << 16;
   1844 //            color |= get_hex(s[3], &error) << 12;
   1845 //            color |= get_hex(s[3], &error) << 8;
   1846 //            color |= get_hex(s[4], &error) << 4;
   1847 //            color |= get_hex(s[4], &error);
   1848 //        } else if (len == 7) {
   1849 //            outValue->dataType = outValue->TYPE_INT_COLOR_RGB8;
   1850 //            color |= 0xFF000000;
   1851 //            color |= get_hex(s[1], &error) << 20;
   1852 //            color |= get_hex(s[2], &error) << 16;
   1853 //            color |= get_hex(s[3], &error) << 12;
   1854 //            color |= get_hex(s[4], &error) << 8;
   1855 //            color |= get_hex(s[5], &error) << 4;
   1856 //            color |= get_hex(s[6], &error);
   1857 //        } else if (len == 9) {
   1858 //            outValue->dataType = outValue->TYPE_INT_COLOR_ARGB8;
   1859 //            color |= get_hex(s[1], &error) << 28;
   1860 //            color |= get_hex(s[2], &error) << 24;
   1861 //            color |= get_hex(s[3], &error) << 20;
   1862 //            color |= get_hex(s[4], &error) << 16;
   1863 //            color |= get_hex(s[5], &error) << 12;
   1864 //            color |= get_hex(s[6], &error) << 8;
   1865 //            color |= get_hex(s[7], &error) << 4;
   1866 //            color |= get_hex(s[8], &error);
   1867 //        } else {
   1868 //            error = true;
   1869 //        }
   1870 //        if (!error) {
   1871 //            if ((attrType&ResTable_map::TYPE_COLOR) == 0) {
   1872 //                if (!canStringCoerce) {
   1873 //                    if (accessor != NULL) {
   1874 //                        accessor->reportError(accessorCookie,
   1875 //                                "Color types not allowed");
   1876 //                    }
   1877 //                    return false;
   1878 //                }
   1879 //            } else {
   1880 //                outValue->data = color;
   1881 //                //printf("Color input=%s, output=0x%x\n", String8(s, len).string(), color);
   1882 //                return true;
   1883 //            }
   1884 //        } else {
   1885 //            if ((attrType&ResTable_map::TYPE_COLOR) != 0) {
   1886 //                if (accessor != NULL) {
   1887 //                    accessor->reportError(accessorCookie, "Color value not valid --"
   1888 //                            " must be #rgb, #argb, #rrggbb, or #aarrggbb");
   1889 //                }
   1890 //                #if 0
   1891 //                fprintf(stderr, "%s: Color ID %s value %s is not valid\n",
   1892 //                        "Resource File", //(const char*)in->getPrintableSource(),
   1893 //                        String8(*curTag).string(),
   1894 //                        String8(s, len).string());
   1895 //                #endif
   1896 //                return false;
   1897 //            }
   1898 //        }
   1899 //    }
   1900 //
   1901 //    if (*s == '?') {
   1902 //        outValue->dataType = outValue->TYPE_ATTRIBUTE;
   1903 //
   1904 //        // Note: we don't check attrType here because the reference can
   1905 //        // be to any other type; we just need to count on the client making
   1906 //        // sure the referenced type is correct.
   1907 //
   1908 //        //printf("Looking up attr: %s\n", String8(s, len).string());
   1909 //
   1910 //        static const String16 attr16("attr");
   1911 //        String16 package, type, name;
   1912 //        if (!expandResourceRef(s+1, len-1, &package, &type, &name,
   1913 //                               &attr16, defPackage, &errorMsg)) {
   1914 //            if (accessor != NULL) {
   1915 //                accessor->reportError(accessorCookie, errorMsg);
   1916 //            }
   1917 //            return false;
   1918 //        }
   1919 //
   1920 //        //printf("Pkg: %s, Type: %s, Name: %s\n",
   1921 //        //       String8(package).string(), String8(type).string(),
   1922 //        //       String8(name).string());
   1923 //        uint32_t specFlags = 0;
   1924 //        uint32_t rid =
   1925 //            identifierForName(name.string(), name.size(),
   1926 //                              type.string(), type.size(),
   1927 //                              package.string(), package.size(), &specFlags);
   1928 //        if (rid != 0) {
   1929 //            if (enforcePrivate) {
   1930 //                if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) {
   1931 //                    if (accessor != NULL) {
   1932 //                        accessor->reportError(accessorCookie, "Attribute is not public.");
   1933 //                    }
   1934 //                    return false;
   1935 //                }
   1936 //            }
   1937 //
   1938 //            if (accessor) {
   1939 //                rid = Res_MAKEID(
   1940 //                    accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
   1941 //                    Res_GETTYPE(rid), Res_GETENTRY(rid));
   1942 //            }
   1943 //
   1944 //            uint32_t packageId = Res_GETPACKAGE(rid) + 1;
   1945 //            if (packageId != APP_PACKAGE_ID && packageId != SYS_PACKAGE_ID) {
   1946 //                outValue->dataType = Res_value::TYPE_DYNAMIC_ATTRIBUTE;
   1947 //            }
   1948 //            outValue->data = rid;
   1949 //            return true;
   1950 //        }
   1951 //
   1952 //        if (accessor) {
   1953 //            uint32_t rid = accessor->getCustomResource(package, type, name);
   1954 //            if (rid != 0) {
   1955 //                uint32_t packageId = Res_GETPACKAGE(rid) + 1;
   1956 //                if (packageId == 0x00) {
   1957 //                    outValue->data = rid;
   1958 //                    outValue->dataType = Res_value::TYPE_DYNAMIC_ATTRIBUTE;
   1959 //                    return true;
   1960 //                } else if (packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID) {
   1961 //                    // We accept packageId's generated as 0x01 in order to support
   1962 //                    // building the android system resources
   1963 //                    outValue->data = rid;
   1964 //                    return true;
   1965 //                }
   1966 //            }
   1967 //        }
   1968 //
   1969 //        if (accessor != NULL) {
   1970 //            accessor->reportError(accessorCookie, "No resource found that matches the given name");
   1971 //        }
   1972 //        return false;
   1973 //    }
   1974 //
   1975 //    if (stringToInt(s, len, outValue)) {
   1976 //        if ((attrType&ResTable_map::TYPE_INTEGER) == 0) {
   1977 //            // If this type does not allow integers, but does allow floats,
   1978 //            // fall through on this error case because the float type should
   1979 //            // be able to accept any integer value.
   1980 //            if (!canStringCoerce && (attrType&ResTable_map::TYPE_FLOAT) == 0) {
   1981 //                if (accessor != NULL) {
   1982 //                    accessor->reportError(accessorCookie, "Integer types not allowed");
   1983 //                }
   1984 //                return false;
   1985 //            }
   1986 //        } else {
   1987 //            if (((int32_t)outValue->data) < ((int32_t)attrMin)
   1988 //                    || ((int32_t)outValue->data) > ((int32_t)attrMax)) {
   1989 //                if (accessor != NULL) {
   1990 //                    accessor->reportError(accessorCookie, "Integer value out of range");
   1991 //                }
   1992 //                return false;
   1993 //            }
   1994 //            return true;
   1995 //        }
   1996 //    }
   1997 //
   1998 //    if (stringToFloat(s, len, outValue)) {
   1999 //        if (outValue->dataType == Res_value::TYPE_DIMENSION) {
   2000 //            if ((attrType&ResTable_map::TYPE_DIMENSION) != 0) {
   2001 //                return true;
   2002 //            }
   2003 //            if (!canStringCoerce) {
   2004 //                if (accessor != NULL) {
   2005 //                    accessor->reportError(accessorCookie, "Dimension types not allowed");
   2006 //                }
   2007 //                return false;
   2008 //            }
   2009 //        } else if (outValue->dataType == Res_value::TYPE_FRACTION) {
   2010 //            if ((attrType&ResTable_map::TYPE_FRACTION) != 0) {
   2011 //                return true;
   2012 //            }
   2013 //            if (!canStringCoerce) {
   2014 //                if (accessor != NULL) {
   2015 //                    accessor->reportError(accessorCookie, "Fraction types not allowed");
   2016 //                }
   2017 //                return false;
   2018 //            }
   2019 //        } else if ((attrType&ResTable_map::TYPE_FLOAT) == 0) {
   2020 //            if (!canStringCoerce) {
   2021 //                if (accessor != NULL) {
   2022 //                    accessor->reportError(accessorCookie, "Float types not allowed");
   2023 //                }
   2024 //                return false;
   2025 //            }
   2026 //        } else {
   2027 //            return true;
   2028 //        }
   2029 //    }
   2030 //
   2031 //    if (len == 4) {
   2032 //        if ((s[0] == 't' || s[0] == 'T') &&
   2033 //            (s[1] == 'r' || s[1] == 'R') &&
   2034 //            (s[2] == 'u' || s[2] == 'U') &&
   2035 //            (s[3] == 'e' || s[3] == 'E')) {
   2036 //            if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) {
   2037 //                if (!canStringCoerce) {
   2038 //                    if (accessor != NULL) {
   2039 //                        accessor->reportError(accessorCookie, "Boolean types not allowed");
   2040 //                    }
   2041 //                    return false;
   2042 //                }
   2043 //            } else {
   2044 //                outValue->dataType = outValue->TYPE_INT_BOOLEAN;
   2045 //                outValue->data = (uint32_t)-1;
   2046 //                return true;
   2047 //            }
   2048 //        }
   2049 //    }
   2050 //
   2051 //    if (len == 5) {
   2052 //        if ((s[0] == 'f' || s[0] == 'F') &&
   2053 //            (s[1] == 'a' || s[1] == 'A') &&
   2054 //            (s[2] == 'l' || s[2] == 'L') &&
   2055 //            (s[3] == 's' || s[3] == 'S') &&
   2056 //            (s[4] == 'e' || s[4] == 'E')) {
   2057 //            if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) {
   2058 //                if (!canStringCoerce) {
   2059 //                    if (accessor != NULL) {
   2060 //                        accessor->reportError(accessorCookie, "Boolean types not allowed");
   2061 //                    }
   2062 //                    return false;
   2063 //                }
   2064 //            } else {
   2065 //                outValue->dataType = outValue->TYPE_INT_BOOLEAN;
   2066 //                outValue->data = 0;
   2067 //                return true;
   2068 //            }
   2069 //        }
   2070 //    }
   2071 //
   2072 //    if ((attrType&ResTable_map::TYPE_ENUM) != 0) {
   2073 //        const ssize_t p = getResourcePackageIndex(attrID);
   2074 //        const bag_entry* bag;
   2075 //        ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
   2076 //        //printf("Got %d for enum\n", cnt);
   2077 //        if (cnt >= 0) {
   2078 //            resource_name rname;
   2079 //            while (cnt > 0) {
   2080 //                if (!Res_INTERNALID(bag->map.name.ident)) {
   2081 //                    //printf("Trying attr #%08x\n", bag->map.name.ident);
   2082 //                    if (getResourceName(bag->map.name.ident, false, &rname)) {
   2083 //                        #if 0
   2084 //                        printf("Matching %s against %s (0x%08x)\n",
   2085 //                               String8(s, len).string(),
   2086 //                               String8(rname.name, rname.nameLen).string(),
   2087 //                               bag->map.name.ident);
   2088 //                        #endif
   2089 //                        if (strzcmp16(s, len, rname.name, rname.nameLen) == 0) {
   2090 //                            outValue->dataType = bag->map.value.dataType;
   2091 //                            outValue->data = bag->map.value.data;
   2092 //                            unlockBag(bag);
   2093 //                            return true;
   2094 //                        }
   2095 //                    }
   2096 //
   2097 //                }
   2098 //                bag++;
   2099 //                cnt--;
   2100 //            }
   2101 //            unlockBag(bag);
   2102 //        }
   2103 //
   2104 //        if (fromAccessor) {
   2105 //            if (accessor->getAttributeEnum(attrID, s, len, outValue)) {
   2106 //                return true;
   2107 //            }
   2108 //        }
   2109 //    }
   2110 //
   2111 //    if ((attrType&ResTable_map::TYPE_FLAGS) != 0) {
   2112 //        const ssize_t p = getResourcePackageIndex(attrID);
   2113 //        const bag_entry* bag;
   2114 //        ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
   2115 //        //printf("Got %d for flags\n", cnt);
   2116 //        if (cnt >= 0) {
   2117 //            bool failed = false;
   2118 //            resource_name rname;
   2119 //            outValue->dataType = Res_value::TYPE_INT_HEX;
   2120 //            outValue->data = 0;
   2121 //            const char16_t* end = s + len;
   2122 //            const char16_t* pos = s;
   2123 //            while (pos < end && !failed) {
   2124 //                const char16_t* start = pos;
   2125 //                pos++;
   2126 //                while (pos < end && *pos != '|') {
   2127 //                    pos++;
   2128 //                }
   2129 //                //printf("Looking for: %s\n", String8(start, pos-start).string());
   2130 //                const bag_entry* bagi = bag;
   2131 //                ssize_t i;
   2132 //                for (i=0; i<cnt; i++, bagi++) {
   2133 //                    if (!Res_INTERNALID(bagi->map.name.ident)) {
   2134 //                        //printf("Trying attr #%08x\n", bagi->map.name.ident);
   2135 //                        if (getResourceName(bagi->map.name.ident, false, &rname)) {
   2136 //                            #if 0
   2137 //                            printf("Matching %s against %s (0x%08x)\n",
   2138 //                                   String8(start,pos-start).string(),
   2139 //                                   String8(rname.name, rname.nameLen).string(),
   2140 //                                   bagi->map.name.ident);
   2141 //                            #endif
   2142 //                            if (strzcmp16(start, pos-start, rname.name, rname.nameLen) == 0) {
   2143 //                                outValue->data |= bagi->map.value.data;
   2144 //                                break;
   2145 //                            }
   2146 //                        }
   2147 //                    }
   2148 //                }
   2149 //                if (i >= cnt) {
   2150 //                    // Didn't find this flag identifier.
   2151 //                    failed = true;
   2152 //                }
   2153 //                if (pos < end) {
   2154 //                    pos++;
   2155 //                }
   2156 //            }
   2157 //            unlockBag(bag);
   2158 //            if (!failed) {
   2159 //                //printf("Final flag value: 0x%lx\n", outValue->data);
   2160 //                return true;
   2161 //            }
   2162 //        }
   2163 //
   2164 //
   2165 //        if (fromAccessor) {
   2166 //            if (accessor->getAttributeFlags(attrID, s, len, outValue)) {
   2167 //                //printf("Final flag value: 0x%lx\n", outValue->data);
   2168 //                return true;
   2169 //            }
   2170 //        }
   2171 //    }
   2172 //
   2173 //    if ((attrType&ResTable_map::TYPE_STRING) == 0) {
   2174 //        if (accessor != NULL) {
   2175 //            accessor->reportError(accessorCookie, "String types not allowed");
   2176 //        }
   2177 //        return false;
   2178 //    }
   2179 //
   2180 //    // Generic string handling...
   2181 //    outValue->dataType = outValue->TYPE_STRING;
   2182 //    if (outString) {
   2183 //        bool failed = collectString(outString, s, len, preserveSpaces, &errorMsg);
   2184 //        if (accessor != NULL) {
   2185 //            accessor->reportError(accessorCookie, errorMsg);
   2186 //        }
   2187 //        return failed;
   2188 //    }
   2189 //
   2190 //    return true;
   2191 //}
   2192 //
   2193 //bool ResTable::collectString(String16* outString,
   2194 //                             const char16_t* s, size_t len,
   2195 //                             bool preserveSpaces,
   2196 //                             const char** outErrorMsg,
   2197 //                             bool append)
   2198 //{
   2199 //    String16 tmp;
   2200 //
   2201 //    char quoted = 0;
   2202 //    const char16_t* p = s;
   2203 //    while (p < (s+len)) {
   2204 //        while (p < (s+len)) {
   2205 //            const char16_t c = *p;
   2206 //            if (c == '\\') {
   2207 //                break;
   2208 //            }
   2209 //            if (!preserveSpaces) {
   2210 //                if (quoted == 0 && isspace16(c)
   2211 //                    && (c != ' ' || isspace16(*(p+1)))) {
   2212 //                    break;
   2213 //                }
   2214 //                if (c == '"' && (quoted == 0 || quoted == '"')) {
   2215 //                    break;
   2216 //                }
   2217 //                if (c == '\'' && (quoted == 0 || quoted == '\'')) {
   2218 //                    /*
   2219 //                     * In practice, when people write ' instead of \'
   2220 //                     * in a string, they are doing it by accident
   2221 //                     * instead of really meaning to use ' as a quoting
   2222 //                     * character.  Warn them so they don't lose it.
   2223 //                     */
   2224 //                    if (outErrorMsg) {
   2225 //                        *outErrorMsg = "Apostrophe not preceded by \\";
   2226 //                    }
   2227 //                    return false;
   2228 //                }
   2229 //            }
   2230 //            p++;
   2231 //        }
   2232 //        if (p < (s+len)) {
   2233 //            if (p > s) {
   2234 //                tmp.append(String16(s, p-s));
   2235 //            }
   2236 //            if (!preserveSpaces && (*p == '"' || *p == '\'')) {
   2237 //                if (quoted == 0) {
   2238 //                    quoted = *p;
   2239 //                } else {
   2240 //                    quoted = 0;
   2241 //                }
   2242 //                p++;
   2243 //            } else if (!preserveSpaces && isspace16(*p)) {
   2244 //                // Space outside of a quote -- consume all spaces and
   2245 //                // leave a single plain space char.
   2246 //                tmp.append(String16(" "));
   2247 //                p++;
   2248 //                while (p < (s+len) && isspace16(*p)) {
   2249 //                    p++;
   2250 //                }
   2251 //            } else if (*p == '\\') {
   2252 //                p++;
   2253 //                if (p < (s+len)) {
   2254 //                    switch (*p) {
   2255 //                    case 't':
   2256 //                        tmp.append(String16("\t"));
   2257 //                        break;
   2258 //                    case 'n':
   2259 //                        tmp.append(String16("\n"));
   2260 //                        break;
   2261 //                    case '#':
   2262 //                        tmp.append(String16("#"));
   2263 //                        break;
   2264 //                    case '@':
   2265 //                        tmp.append(String16("@"));
   2266 //                        break;
   2267 //                    case '?':
   2268 //                        tmp.append(String16("?"));
   2269 //                        break;
   2270 //                    case '"':
   2271 //                        tmp.append(String16("\""));
   2272 //                        break;
   2273 //                    case '\'':
   2274 //                        tmp.append(String16("'"));
   2275 //                        break;
   2276 //                    case '\\':
   2277 //                        tmp.append(String16("\\"));
   2278 //                        break;
   2279 //                    case 'u':
   2280 //                    {
   2281 //                        char16_t chr = 0;
   2282 //                        int i = 0;
   2283 //                        while (i < 4 && p[1] != 0) {
   2284 //                            p++;
   2285 //                            i++;
   2286 //                            int c;
   2287 //                            if (*p >= '0' && *p <= '9') {
   2288 //                                c = *p - '0';
   2289 //                            } else if (*p >= 'a' && *p <= 'f') {
   2290 //                                c = *p - 'a' + 10;
   2291 //                            } else if (*p >= 'A' && *p <= 'F') {
   2292 //                                c = *p - 'A' + 10;
   2293 //                            } else {
   2294 //                                if (outErrorMsg) {
   2295 //                                    *outErrorMsg = "Bad character in \\u unicode escape sequence";
   2296 //                                }
   2297 //                                return false;
   2298 //                            }
   2299 //                            chr = (chr<<4) | c;
   2300 //                        }
   2301 //                        tmp.append(String16(&chr, 1));
   2302 //                    } break;
   2303 //                    default:
   2304 //                        // ignore unknown escape chars.
   2305 //                        break;
   2306 //                    }
   2307 //                    p++;
   2308 //                }
   2309 //            }
   2310 //            len -= (p-s);
   2311 //            s = p;
   2312 //        }
   2313 //    }
   2314 //
   2315 //    if (tmp.size() != 0) {
   2316 //        if (len > 0) {
   2317 //            tmp.append(String16(s, len));
   2318 //        }
   2319 //        if (append) {
   2320 //            outString->append(tmp);
   2321 //        } else {
   2322 //            outString->setTo(tmp);
   2323 //        }
   2324 //    } else {
   2325 //        if (append) {
   2326 //            outString->append(String16(s, len));
   2327 //        } else {
   2328 //            outString->setTo(s, len);
   2329 //        }
   2330 //    }
   2331 //
   2332 //    return true;
   2333 //}
   2334 
   2335   public int getBasePackageCount()
   2336   {
   2337     if (mError != NO_ERROR) {
   2338       return 0;
   2339     }
   2340     return mPackageGroups.size();
   2341   }
   2342 
   2343   public String getBasePackageName(int idx)
   2344   {
   2345     if (mError != NO_ERROR) {
   2346       return null;
   2347     }
   2348     LOG_FATAL_IF(idx >= mPackageGroups.size(),
   2349         "Requested package index %d past package count %d",
   2350         (int)idx, (int)mPackageGroups.size());
   2351     return mPackageGroups.get(keyFor(idx)).name;
   2352   }
   2353 
   2354   public int getBasePackageId(int idx)
   2355   {
   2356     if (mError != NO_ERROR) {
   2357       return 0;
   2358     }
   2359     LOG_FATAL_IF(idx >= mPackageGroups.size(),
   2360         "Requested package index %d past package count %d",
   2361         (int)idx, (int)mPackageGroups.size());
   2362     return mPackageGroups.get(keyFor(idx)).id;
   2363   }
   2364 
   2365   int getLastTypeIdForPackage(int idx)
   2366   {
   2367     if (mError != NO_ERROR) {
   2368       return 0;
   2369     }
   2370     LOG_FATAL_IF(idx >= mPackageGroups.size(),
   2371         "Requested package index %d past package count %d",
   2372         (int)idx, (int)mPackageGroups.size());
   2373     PackageGroup group = mPackageGroups.get(keyFor(idx));
   2374     return group.largestTypeId;
   2375   }
   2376 
   2377   int keyFor(int idx) {
   2378     ArrayList<Integer> keys = new ArrayList<>(mPackageGroups.keySet());
   2379     Collections.sort(keys);
   2380     return keys.get(idx);
   2381   }
   2382 
   2383   public int getTableCount() {
   2384     return mHeaders.size();
   2385   }
   2386 
   2387   public ResStringPool getTableStringBlock(int index) {
   2388     return mHeaders.get(index).values;
   2389   }
   2390 
   2391   public DynamicRefTable getDynamicRefTableForCookie(int cookie) {
   2392     for (PackageGroup pg : mPackageGroups.values()) {
   2393       int M = pg.packages.size();
   2394       for (int j = 0; j < M; j++) {
   2395         if (pg.packages.get(j).header.cookie == cookie) {
   2396           return pg.dynamicRefTable;
   2397         }
   2398       }
   2399     }
   2400     return null;
   2401   }
   2402 
   2403   public boolean getResourceName(int resID, boolean allowUtf8, ResourceName outName) {
   2404     if (mError != NO_ERROR) {
   2405       return false;
   2406     }
   2407 
   2408     final int p = getResourcePackageIndex(resID);
   2409     final int t = Res_GETTYPE(resID);
   2410     final int e = Res_GETENTRY(resID);
   2411 
   2412     if (p < 0) {
   2413       if (Res_GETPACKAGE(resID)+1 == 0) {
   2414         ALOGW("No package identifier when getting name for resource number 0x%08x", resID);
   2415       }
   2416       return false;
   2417     }
   2418     if (t < 0) {
   2419       ALOGW("No type identifier when getting name for resource number 0x%08x", resID);
   2420       return false;
   2421     }
   2422 
   2423     final PackageGroup grp = mPackageGroups.get(p);
   2424     if (grp == NULL) {
   2425       ALOGW("Bad identifier when getting name for resource number 0x%08x", resID);
   2426       return false;
   2427     }
   2428 
   2429     Entry entry = new Entry();
   2430     int err = getEntry(grp, t, e, null, entry);
   2431     if (err != NO_ERROR) {
   2432       return false;
   2433     }
   2434 
   2435     outName.packageName = grp.name;
   2436     outName.type = entry.typeStr.string();
   2437     if (outName.type == null) {
   2438       return false;
   2439     }
   2440     outName.name = entry.keyStr.string();
   2441     if (outName.name == null) {
   2442       return false;
   2443     }
   2444 
   2445     return true;
   2446   }
   2447 
   2448   String getResourceName(int resId) {
   2449     ResourceName outName = new ResourceName();
   2450     if (getResourceName(resId, true, outName)) {
   2451       return outName.toString();
   2452     }
   2453     throw new IllegalArgumentException("Unknown resource id " + resId);
   2454   }
   2455 
   2456   // A group of objects describing a particular resource package.
   2457   // The first in 'package' is always the root object (from the resource
   2458   // table that defined the package); the ones after are skins on top of it.
   2459   // from ResourceTypes.cpp struct ResTable::PackageGroup
   2460   public static class PackageGroup
   2461   {
   2462     public PackageGroup(
   2463         ResTable _owner, final String _name, int _id,
   2464         boolean appAsLib, boolean _isSystemAsset, boolean _isDynamic)
   2465 //        : owner(_owner)
   2466 //        , name(_name)
   2467 //        , id(_id)
   2468 //        , largestTypeId(0)
   2469 //        , dynamicRefTable(static_cast<uint8_t>(_id), appAsLib)
   2470 //        , isSystemAsset(_isSystemAsset)
   2471     {
   2472       this.owner = _owner;
   2473       this.name = _name;
   2474       this.id = _id;
   2475       this.dynamicRefTable = new DynamicRefTable((byte) _id, appAsLib);
   2476       this.isSystemAsset = _isSystemAsset;
   2477       this.isDynamic = _isDynamic;
   2478     }
   2479 
   2480 //    ~PackageGroup() {
   2481 //      clearBagCache();
   2482 //      final int numTypes = types.size();
   2483 //      for (int i = 0; i < numTypes; i++) {
   2484 //        final List<DataType> typeList = types.get(i);
   2485 //        final int numInnerTypes = typeList.size();
   2486 //        for (int j = 0; j < numInnerTypes; j++) {
   2487 //          if (typeList.get(j)._package_.owner == owner) {
   2488 //            delete typeList[j];
   2489 //          }
   2490 //        }
   2491 //        typeList.clear();
   2492 //      }
   2493 //
   2494 //      final int N = packages.size();
   2495 //      for (int i=0; i<N; i++) {
   2496 //        ResTable_package pkg = packages[i];
   2497 //        if (pkg.owner == owner) {
   2498 //          delete pkg;
   2499 //        }
   2500 //      }
   2501 //    }
   2502 
   2503     /**
   2504      * Clear all cache related data that depends on parameters/configuration.
   2505      * This includes the bag caches and filtered types.
   2506      */
   2507     void clearBagCache() {
   2508 //      for (int i = 0; i < typeCacheEntries.size(); i++) {
   2509 //        if (kDebugTableNoisy) {
   2510 //          printf("type=0x%x\n", i);
   2511 //        }
   2512 //        final List<DataType> typeList = types.get(i);
   2513 //        if (!typeList.isEmpty()) {
   2514 //          TypeCacheEntry cacheEntry = typeCacheEntries.editItemAt(i);
   2515 //
   2516 //          // Reset the filtered configurations.
   2517 //          cacheEntry.filteredConfigs.clear();
   2518 //
   2519 //          bag_set[][] typeBags = cacheEntry.cachedBags;
   2520 //          if (kDebugTableNoisy) {
   2521 //            printf("typeBags=%s\n", typeBags);
   2522 //          }
   2523 //
   2524 //          if (isTruthy(typeBags)) {
   2525 //            final int N = typeList.get(0).entryCount;
   2526 //            if (kDebugTableNoisy) {
   2527 //              printf("type.entryCount=0x%x\n", N);
   2528 //            }
   2529 //            for (int j = 0; j < N; j++) {
   2530 //              if (typeBags[j] && typeBags[j] != (bag_set *) 0xFFFFFFFF){
   2531 //                free(typeBags[j]);
   2532 //              }
   2533 //            }
   2534 //            free(typeBags);
   2535 //            cacheEntry.cachedBags = NULL;
   2536 //          }
   2537 //        }
   2538 //      }
   2539     }
   2540 
   2541     private void printf(String message, Object... arguments) {
   2542       System.out.print(String.format(message, arguments));
   2543     }
   2544 
   2545 //    long findType16(final String type, int len) {
   2546 //      final int N = packages.size();
   2547 //      for (int i = 0; i < N; i++) {
   2548 //        sint index = packages[i].typeStrings.indexOfString(type, len);
   2549 //        if (index >= 0) {
   2550 //          return index + packages[i].typeIdOffset;
   2551 //        }
   2552 //      }
   2553 //      return -1;
   2554 //    }
   2555 
   2556     final ResTable owner;
   2557     final String name;
   2558     final int id;
   2559 
   2560     // This is mainly used to keep track of the loaded packages
   2561     // and to clean them up properly. Accessing resources happens from
   2562     // the 'types' array.
   2563     List<Package> packages = new ArrayList<>();
   2564 
   2565     public final Map<Integer, List<Type>> types = new HashMap<>();
   2566 
   2567     byte largestTypeId;
   2568 
   2569     // Cached objects dependent on the parameters/configuration of this ResTable.
   2570     // Gets cleared whenever the parameters/configuration changes.
   2571     // These are stored here in a parallel structure because the data in `types` may
   2572     // be shared by other ResTable's (framework resources are shared this way).
   2573     ByteBucketArray<TypeCacheEntry> typeCacheEntries =
   2574         new ByteBucketArray<TypeCacheEntry>(new TypeCacheEntry()) {
   2575           @Override
   2576           TypeCacheEntry newInstance() {
   2577             return new TypeCacheEntry();
   2578           }
   2579         };
   2580 
   2581     // The table mapping dynamic references to resolved references for
   2582     // this package group.
   2583     // TODO: We may be able to support dynamic references in overlays
   2584     // by having these tables in a per-package scope rather than
   2585     // per-package-group.
   2586     DynamicRefTable dynamicRefTable;
   2587 
   2588     // If the package group comes from a system asset. Used in
   2589     // determining non-system locales.
   2590     final boolean isSystemAsset;
   2591     final boolean isDynamic;
   2592   }
   2593 
   2594   // --------------------------------------------------------------------
   2595 // --------------------------------------------------------------------
   2596 // --------------------------------------------------------------------
   2597 
   2598 //  struct ResTable::Header
   2599   public static class Header
   2600   {
   2601 //    Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL),
   2602 //      resourceIDMap(NULL), resourceIDMapSize(0) { }
   2603 
   2604     public Header(ResTable owner) {
   2605       this.owner = owner;
   2606     }
   2607 
   2608 //    ~Header()
   2609 //    {
   2610 //      free(resourceIDMap);
   2611 //    }
   2612 
   2613     ResTable            owner;
   2614     byte[]                           ownedData;
   2615     ResTable_header header;
   2616     int                          size;
   2617     int                  dataEnd;
   2618     int                          index;
   2619     int                         cookie;
   2620 
   2621     ResStringPool                   values = new ResStringPool();
   2622     int[]                       resourceIDMap;
   2623     int                          resourceIDMapSize;
   2624   };
   2625 
   2626   public static class Entry {
   2627     ResTable_config config;
   2628     ResTable_entry entry;
   2629     ResTable_type type;
   2630     int specFlags;
   2631     Package _package_;
   2632 
   2633     StringPoolRef typeStr;
   2634     StringPoolRef keyStr;
   2635   }
   2636 
   2637   // struct ResTable::DataType
   2638   public static class Type {
   2639 
   2640     final Header header;
   2641     final Package _package_;
   2642     public final int entryCount;
   2643     public ResTable_typeSpec typeSpec;
   2644     public int[] typeSpecFlags;
   2645     public IdmapEntries idmapEntries = new IdmapEntries();
   2646     public List<ResTable_type> configs;
   2647 
   2648     public Type(final Header _header, final Package _package, int count)
   2649   //        : header(_header), package(_package), entryCount(count),
   2650   //  typeSpec(NULL), typeSpecFlags(NULL) { }
   2651     {
   2652       this.header = _header;
   2653       _package_ = _package;
   2654       this.entryCount = count;
   2655       this.typeSpec = null;
   2656       this.typeSpecFlags = null;
   2657       this.configs = new ArrayList<>();
   2658     }
   2659   }
   2660 
   2661 //  struct ResTable::Package
   2662   public static class Package {
   2663 //    Package(ResTable* _owner, final Header* _header, final ResTable_package* _package)
   2664 //        : owner(_owner), header(_header), package(_package), typeIdOffset(0) {
   2665 //    if (dtohs(package.header.headerSize) == sizeof(package)) {
   2666 //      // The package structure is the same size as the definition.
   2667 //      // This means it contains the typeIdOffset field.
   2668 //      typeIdOffset = package.typeIdOffset;
   2669 //    }
   2670 
   2671     public Package(ResTable owner, Header header, ResTable_package _package) {
   2672       this.owner = owner;
   2673       this.header = header;
   2674       this._package_ = _package;
   2675     }
   2676 
   2677     final ResTable owner;
   2678     final Header header;
   2679     final ResTable_package _package_;
   2680 
   2681     ResStringPool typeStrings = new ResStringPool();
   2682     ResStringPool keyStrings = new ResStringPool();
   2683 
   2684     int typeIdOffset;
   2685   };
   2686 
   2687   public static class bag_entry {
   2688     public int stringBlock;
   2689     public ResTable_map map = new ResTable_map();
   2690   }
   2691 
   2692   public void lock() {
   2693     try {
   2694       mLock.acquire();
   2695     } catch (InterruptedException e) {
   2696       throw new RuntimeException(e);
   2697     }
   2698   }
   2699 
   2700   public void unlock() {
   2701     mLock.release();
   2702   }
   2703 
   2704   public int lockBag(int resID, Ref<bag_entry[]> outBag) {
   2705     lock();
   2706 
   2707     int err = getBagLocked(resID, outBag, null);
   2708     if (err < NO_ERROR) {
   2709       //printf("*** get failed!  unlocking\n");
   2710       mLock.release();
   2711     }
   2712     return err;
   2713   }
   2714 
   2715   public int getBagLocked(int resID, Ref<bag_entry[]> outBag, Ref<Integer> outTypeSpecFlags) {
   2716     if (mError != NO_ERROR) {
   2717       return mError;
   2718     }
   2719 
   2720     final int p = getResourcePackageIndex(resID);
   2721     final int t = Res_GETTYPE(resID);
   2722     final int e = Res_GETENTRY(resID);
   2723 
   2724     if (p < 0) {
   2725       ALOGW("Invalid package identifier when getting bag for resource number 0x%08x", resID);
   2726       return BAD_INDEX;
   2727     }
   2728     if (t < 0) {
   2729       ALOGW("No type identifier when getting bag for resource number 0x%08x", resID);
   2730       return BAD_INDEX;
   2731     }
   2732 
   2733     //printf("Get bag: id=0x%08x, p=%d, t=%d\n", resID, p, t);
   2734     PackageGroup grp = mPackageGroups.get(p);
   2735     if (grp == NULL) {
   2736       ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID);
   2737       return BAD_INDEX;
   2738     }
   2739 
   2740     final List<Type> typeConfigs = getOrDefault(grp.types, t, Collections.emptyList());
   2741     if (typeConfigs.isEmpty()) {
   2742       ALOGW("Type identifier 0x%x does not exist.", t+1);
   2743       return BAD_INDEX;
   2744     }
   2745 
   2746     final int NENTRY = typeConfigs.get(0).entryCount;
   2747     if (e >= (int)NENTRY) {
   2748       ALOGW("Entry identifier 0x%x is larger than entry count 0x%x",
   2749           e, (int)typeConfigs.get(0).entryCount);
   2750       return BAD_INDEX;
   2751     }
   2752 
   2753     // First see if we've already computed this bag...
   2754     TypeCacheEntry cacheEntry = grp.typeCacheEntries.editItemAt(t);
   2755     bag_set[] typeSet = cacheEntry.cachedBags;
   2756     // todo cache
   2757 //    if (isTruthy(typeSet)) {
   2758 //      bag_set set = typeSet[e];
   2759 //      if (isTruthy(set)) {
   2760 //        if (set != (bag_set) 0xFFFFFFFF){
   2761 //        if (set != SENTINEL_BAG_SET){
   2762 //          if (outTypeSpecFlags != NULL) {
   2763 //                    outTypeSpecFlags.set(set.typeSpecFlags);
   2764 //          }
   2765 //          outBag.set((bag_entry *) (set + 1);
   2766 //          if (kDebugTableSuperNoisy) {
   2767 //            ALOGI("Found existing bag for: 0x%x\n", resID);
   2768 //          }
   2769 //          return set.numAttrs;
   2770 //        }
   2771 //        ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.",
   2772 //            resID);
   2773 //        return BAD_INDEX;
   2774 //      }
   2775 //    }
   2776 //
   2777     // Bag not found, we need to compute it!
   2778     if (!isTruthy(typeSet)) {
   2779       typeSet = new bag_set[NENTRY]; // (bag_set**)calloc(NENTRY, sizeof(bag_set*));
   2780       //cacheEntry.cachedBags = typeSet;
   2781     }
   2782 //
   2783 //    // Mark that we are currently working on this one.
   2784 //    typeSet[e] = (bag_set*)0xFFFFFFFF;
   2785 //    typeSet[e] = SENTINEL_BAG_SET;
   2786 
   2787     if (kDebugTableNoisy) {
   2788       ALOGI("Building bag: %x\n", resID);
   2789     }
   2790 
   2791     // Now collect all bag attributes
   2792     Entry entry = new Entry();
   2793     int err = getEntry(grp, t, e, mParams, entry);
   2794     if (err != NO_ERROR) {
   2795       return err;
   2796     }
   2797     final short entrySize = dtohs(entry.entry.size);
   2798 //    const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
   2799 //        ? dtohl(((const ResTable_map_entry*)entry.entry)->parent.ident) : 0;
   2800 //    const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
   2801 //        ? dtohl(((const ResTable_map_entry*)entry.entry)->count) : 0;
   2802     ResTable_map_entry mapEntry = entrySize >= ResTable_map_entry.BASE_SIZEOF ?
   2803         new ResTable_map_entry(entry.entry.myBuf(), entry.entry.myOffset()) : null;
   2804     final int parent = mapEntry != null ? dtohl(mapEntry.parent.ident) : 0;
   2805     final int count = mapEntry != null ? dtohl(mapEntry.count) : 0;
   2806 
   2807     int N = count;
   2808 
   2809     if (kDebugTableNoisy) {
   2810       ALOGI("Found map: size=%x parent=%x count=%d\n", entrySize, parent, count);
   2811 
   2812       // If this map inherits from another, we need to start
   2813       // with its parent's values.  Otherwise start out empty.
   2814       ALOGI("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", entrySize, parent);
   2815     }
   2816 
   2817     // This is what we are building.
   2818     bag_set set;
   2819 
   2820     if (isTruthy(parent)) {
   2821       final Ref<Integer> resolvedParent = new Ref<>(parent);
   2822 
   2823       // Bags encode a parent reference without using the standard
   2824       // Res_value structure. That means we must always try to
   2825       // resolve a parent reference in case it is actually a
   2826       // TYPE_DYNAMIC_REFERENCE.
   2827       err = grp.dynamicRefTable.lookupResourceId(resolvedParent);
   2828       if (err != NO_ERROR) {
   2829         ALOGE("Failed resolving bag parent id 0x%08x", parent);
   2830         return UNKNOWN_ERROR;
   2831       }
   2832 
   2833       final Ref<bag_entry[]> parentBag = new Ref<>(null);
   2834       final Ref<Integer> parentTypeSpecFlags = new Ref<>(0);
   2835       final int NP = getBagLocked(resolvedParent.get(), parentBag, parentTypeSpecFlags);
   2836       final int NT = ((NP >= 0) ? NP : 0) + N;
   2837       set = new bag_set(NT);
   2838       if (NP > 0) {
   2839         set.copyFrom(parentBag.get(), NP);
   2840         set.numAttrs = NP;
   2841         if (kDebugTableNoisy) {
   2842           ALOGI("Initialized new bag with %d inherited attributes.\n", NP);
   2843         }
   2844       } else {
   2845         if (kDebugTableNoisy) {
   2846           ALOGI("Initialized new bag with no inherited attributes.\n");
   2847         }
   2848         set.numAttrs = 0;
   2849       }
   2850       set.availAttrs = NT;
   2851       set.typeSpecFlags = parentTypeSpecFlags.get();
   2852     } else {
   2853       set = new bag_set(N);
   2854       set.numAttrs = 0;
   2855       set.availAttrs = N;
   2856       set.typeSpecFlags = 0;
   2857     }
   2858 
   2859     set.typeSpecFlags |= entry.specFlags;
   2860 
   2861     // Now merge in the new attributes...
   2862 //    int curOff = (reinterpret_cast<uintptr_t>(entry.entry) - reinterpret_cast<uintptr_t>(entry.type))
   2863 //        + dtohs(entry.entry.size);
   2864     int curOff = entry.entry.myOffset() - entry.type.myOffset() + entry.entry.size;
   2865     ResTable_map map;
   2866 //    bag_entry* entries = (bag_entry*)(set+1);
   2867     bag_entry[] entries = set.bag_entries;
   2868     int curEntry = 0;
   2869     int pos = 0;
   2870     if (kDebugTableNoisy) {
   2871       ALOGI("Starting with set %s, entries=%s, avail=0x%x\n", set, entries, set.availAttrs);
   2872     }
   2873     while (pos < count) {
   2874       if (kDebugTableNoisy) {
   2875 //        ALOGI("Now at %s\n", curOff);
   2876         ALOGI("Now at %s\n", curEntry);
   2877       }
   2878 
   2879       if (curOff > (dtohl(entry.type.header.size)- ResTable_map.SIZEOF)) {
   2880         ALOGW("ResTable_map at %d is beyond type chunk data %d",
   2881             (int)curOff, dtohl(entry.type.header.size));
   2882         return BAD_TYPE;
   2883       }
   2884 //      map = (const ResTable_map*)(((const uint8_t*)entry.type) + curOff);
   2885       map = new ResTable_map(entry.type.myBuf(), entry.type.myOffset() + curOff);
   2886       N++;
   2887 
   2888       final Ref<Integer> newName = new Ref<>(htodl(map.name.ident));
   2889       if (!Res_INTERNALID(newName.get())) {
   2890         // Attributes don't have a resource id as the name. They specify
   2891         // other data, which would be wrong to change via a lookup.
   2892         if (grp.dynamicRefTable.lookupResourceId(newName) != NO_ERROR) {
   2893           ALOGE("Failed resolving ResTable_map name at %d with ident 0x%08x",
   2894               (int) curEntry, (int) newName.get());
   2895           return UNKNOWN_ERROR;
   2896         }
   2897       }
   2898 
   2899       boolean isInside;
   2900       int oldName = 0;
   2901       while ((isInside=(curEntry < set.numAttrs))
   2902           && (oldName=entries[curEntry].map.name.ident) < newName.get()) {
   2903         if (kDebugTableNoisy) {
   2904           ALOGI("#0x%x: Keeping existing attribute: 0x%08x\n",
   2905               curEntry, entries[curEntry].map.name.ident);
   2906         }
   2907         curEntry++;
   2908       }
   2909 
   2910       if ((!isInside) || oldName != newName.get()) {
   2911         // This is a new attribute...  figure out what to do with it.
   2912         if (set.numAttrs >= set.availAttrs) {
   2913           // Need to alloc more memory...
   2914                 final int newAvail = set.availAttrs+N;
   2915 //          set = (bag_set[])realloc(set,
   2916 //              sizeof(bag_set)
   2917 //                  + sizeof(bag_entry)*newAvail);
   2918           set.resizeBagEntries(newAvail);
   2919           set.availAttrs = newAvail;
   2920 //          entries = (bag_entry*)(set+1);
   2921           entries = set.bag_entries;
   2922           if (kDebugTableNoisy) {
   2923             ALOGI("Reallocated set %s, entries=%s, avail=0x%x\n",
   2924                 set, entries, set.availAttrs);
   2925           }
   2926         }
   2927         if (isInside) {
   2928           // Going in the middle, need to make space.
   2929 //          memmove(entries+curEntry+1, entries+curEntry,
   2930 //              sizeof(bag_entry)*(set.numAttrs-curEntry));
   2931           System.arraycopy(entries, curEntry, entries, curEntry + 1, set.numAttrs - curEntry);
   2932           entries[curEntry] = null;
   2933           set.numAttrs++;
   2934         }
   2935         if (kDebugTableNoisy) {
   2936           ALOGI("#0x%x: Inserting new attribute: 0x%08x\n", curEntry, newName.get());
   2937         }
   2938       } else {
   2939         if (kDebugTableNoisy) {
   2940           ALOGI("#0x%x: Replacing existing attribute: 0x%08x\n", curEntry, oldName);
   2941         }
   2942       }
   2943 
   2944       bag_entry cur = entries[curEntry];
   2945       if (cur == null) {
   2946         cur = entries[curEntry] = new bag_entry();
   2947       }
   2948 
   2949       cur.stringBlock = entry._package_.header.index;
   2950       cur.map.name.ident = newName.get();
   2951 //      cur->map.value.copyFrom_dtoh(map->value);
   2952       cur.map.value = map.value;
   2953       final Ref<Res_value> valueRef = new Ref<>(cur.map.value);
   2954       err = grp.dynamicRefTable.lookupResourceValue(valueRef);
   2955       cur.map.value = map.value = valueRef.get();
   2956       if (err != NO_ERROR) {
   2957         ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur.map.value.data);
   2958         return UNKNOWN_ERROR;
   2959       }
   2960 
   2961       if (kDebugTableNoisy) {
   2962         ALOGI("Setting entry #0x%x %s: block=%d, name=0x%08d, type=%d, data=0x%08x\n",
   2963             curEntry, cur, cur.stringBlock, cur.map.name.ident,
   2964             cur.map.value.dataType, cur.map.value.data);
   2965       }
   2966 
   2967       // On to the next!
   2968       curEntry++;
   2969       pos++;
   2970       final int size = dtohs(map.value.size);
   2971 //      curOff += size + sizeof(*map)-sizeof(map->value);
   2972       curOff += size + ResTable_map.SIZEOF-Res_value.SIZEOF;
   2973     };
   2974 
   2975     if (curEntry > set.numAttrs) {
   2976       set.numAttrs = curEntry;
   2977     }
   2978 
   2979     // And this is it...
   2980     typeSet[e] = set;
   2981     if (isTruthy(set)) {
   2982       if (outTypeSpecFlags != NULL) {
   2983         outTypeSpecFlags.set(set.typeSpecFlags);
   2984       }
   2985       outBag.set(set.bag_entries);
   2986       if (kDebugTableNoisy) {
   2987         ALOGI("Returning 0x%x attrs\n", set.numAttrs);
   2988       }
   2989       return set.numAttrs;
   2990     }
   2991     return BAD_INDEX;
   2992   }
   2993 
   2994   public void unlockBag(Ref<bag_entry[]> bag) {
   2995     unlock();
   2996   }
   2997 
   2998   static class bag_set {
   2999     int numAttrs;    // number in array
   3000     int availAttrs;  // total space in array
   3001     int typeSpecFlags;
   3002     // Followed by 'numAttr' bag_entry structures.
   3003 
   3004     bag_entry[] bag_entries;
   3005 
   3006     public bag_set(int entryCount) {
   3007       bag_entries = new bag_entry[entryCount];
   3008     }
   3009 
   3010     public void copyFrom(bag_entry[] parentBag, int count) {
   3011       for (int i = 0; i < count; i++) {
   3012         bag_entries[i] = parentBag[i];
   3013       }
   3014     }
   3015 
   3016     public void resizeBagEntries(int newEntryCount) {
   3017       bag_entry[] newEntries = new bag_entry[newEntryCount];
   3018       System.arraycopy(bag_entries, 0, newEntries, 0, Math.min(bag_entries.length, newEntryCount));
   3019       bag_entries = newEntries;
   3020     }
   3021   };
   3022 
   3023   /**
   3024    * Configuration dependent cached data. This must be cleared when the configuration is
   3025    * changed (setParameters).
   3026    */
   3027   static class TypeCacheEntry {
   3028 //    TypeCacheEntry() : cachedBags(NULL) {}
   3029 
   3030     // Computed attribute bags for this type.
   3031 //    bag_set** cachedBags;
   3032     bag_set[] cachedBags;
   3033 
   3034     // Pre-filtered list of configurations (per asset path) that match the parameters set on this
   3035     // ResTable.
   3036     List<List<ResTable_type>> filteredConfigs;
   3037   };
   3038 
   3039 
   3040   private int Res_MAKEID(int packageId, int typeId, int entryId) {
   3041     return (((packageId+1)<<24) | (((typeId+1)&0xFF)<<16) | (entryId&0xFFFF));
   3042   }
   3043 
   3044   // struct resource_name
   3045   public static class ResourceName {
   3046     public String packageName;
   3047     public String type;
   3048     public String name;
   3049 
   3050     @Override
   3051     public String toString() {
   3052       return packageName.trim() + '@' + type + ':' + name;
   3053     }
   3054   }
   3055 
   3056   private interface Function<K, V> {
   3057     V apply(K key);
   3058   }
   3059 
   3060   static <K, V> V computeIfAbsent(Map<K, V> map, K key, Function<K, V> vFunction) {
   3061     V v = map.get(key);
   3062     if (v == null) {
   3063       v = vFunction.apply(key);
   3064       map.put(key, v);
   3065     }
   3066     return v;
   3067   }
   3068 
   3069   static <K, V> V getOrDefault(Map<K, V> map, K key, V defaultValue) {
   3070     V v;
   3071     return (((v = map.get(key)) != null) || map.containsKey(key)) ? v : defaultValue;
   3072   }
   3073 }
   3074