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