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