• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.res.android;
2 
3 import static java.nio.charset.StandardCharsets.UTF_8;
4 import static org.robolectric.res.android.Errors.BAD_TYPE;
5 import static org.robolectric.res.android.Errors.NO_ERROR;
6 import static org.robolectric.res.android.Util.ALOGW;
7 import static org.robolectric.res.android.Util.SIZEOF_INT;
8 import static org.robolectric.res.android.Util.SIZEOF_SHORT;
9 import static org.robolectric.res.android.Util.dtohl;
10 import static org.robolectric.res.android.Util.dtohs;
11 
12 import java.nio.ByteBuffer;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import org.robolectric.res.android.ResourceTypes.ResStringPool_header.Writer;
18 
19 // transliterated from https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/ResourceTypes.cpp
20 //   and https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/include/androidfw/ResourceTypes.h
21 public class ResourceTypes {
22   public static final String ANDROID_NS = "http://schemas.android.com/apk/res/android";
23   public static final String AUTO_NS = "http://schemas.android.com/apk/res-auto";
24 
25   static final int kIdmapMagic = 0x504D4449;
26   static final int kIdmapCurrentVersion = 0x00000001;
27 
validate_chunk(ResChunk_header chunk, int minSize, int dataLen, String name)28   static int validate_chunk(ResChunk_header chunk,
29       int minSize,
30       int dataLen,
31       String name)
32   {
33     final short headerSize = dtohs(chunk.headerSize);
34     final int size = dtohl(chunk.size);
35 
36     if (headerSize >= minSize) {
37       if (headerSize <= size) {
38         if (((headerSize|size)&0x3) == 0) {
39           if (size <= (dataLen)) {
40             return NO_ERROR;
41           }
42           ALOGW("%s data size 0x%x extends beyond resource end.",
43               name, size /*, (dataEnd-((const uint8_t*)chunk))*/);
44           return BAD_TYPE;
45         }
46         ALOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.",
47             name, (int)size, (int)headerSize);
48         return BAD_TYPE;
49       }
50       ALOGW("%s size 0x%x is smaller than header size 0x%x.",
51           name, size, headerSize);
52       return BAD_TYPE;
53     }
54     ALOGW("%s header size 0x%04x is too small.",
55         name, headerSize);
56     return BAD_TYPE;
57   }
58 
59   static class WithOffset {
60     private final ByteBuffer buf;
61     private final int offset;
62 
WithOffset(ByteBuffer buf, int offset)63     WithOffset(ByteBuffer buf, int offset) {
64       this.buf = buf;
65       this.offset = offset;
66     }
67 
myBuf()68     public final ByteBuffer myBuf() {
69       return buf;
70     }
71 
myOffset()72     public final int myOffset() {
73       return offset;
74     }
75 
76     @Override
toString()77     public String toString() {
78       return "{buf+" + offset + '}';
79     }
80   }
81 
82   /** ********************************************************************
83    *  Base Types
84    *
85    *  These are standard types that are shared between multiple specific
86    *  resource types.
87    *
88    *********************************************************************** */
89 
90   /**
91    * Header that appears at the front of every data chunk in a resource.
92    */
93   public static class ResChunk_header extends WithOffset
94   {
95     static int SIZEOF = 8;
96 
97     // Type identifier for this chunk.  The meaning of this value depends
98     // on the containing chunk.
99     final short type;
100 
101     // Size of the chunk header (in bytes).  Adding this value to
102     // the address of the chunk allows you to find its associated data
103     // (if any).
104     final short headerSize;
105 
106     // Total size of this chunk (in bytes).  This is the chunkSize plus
107     // the size of any data associated with the chunk.  Adding this value
108     // to the chunk allows you to completely skip its contents (including
109     // any child chunks).  If this value is the same as chunkSize, there is
110     // no data associated with the chunk.
111     final int size;
112 
ResChunk_header(ByteBuffer buf, int offset)113     public ResChunk_header(ByteBuffer buf, int offset) {
114       super(buf, offset);
115       this.type = buf.getShort(offset);
116       this.headerSize = buf.getShort(offset + 2);
117       this.size = buf.getInt(offset + 4);
118     }
119 
write(ByteBuffer buf, short type, Runnable header, Runnable contents)120     public static void write(ByteBuffer buf, short type, Runnable header, Runnable contents) {
121       int startPos = buf.position();
122       buf.putShort(type);
123       ShortWriter headerSize = new ShortWriter(buf);
124       IntWriter size = new IntWriter(buf);
125 
126       header.run();
127       headerSize.write((short) (buf.position() - startPos));
128 
129       contents.run();
130 
131       // pad to next int boundary
132       int len = buf.position() - startPos;
133       while ((len & 0x3) != 0) {
134         buf.put((byte) 0);
135         len++;
136       }
137       size.write(len);
138     }
139   }
140 
141   public static final int RES_NULL_TYPE               = 0x0000;
142   public static final int RES_STRING_POOL_TYPE        = 0x0001;
143   public static final int RES_TABLE_TYPE              = 0x0002;
144   public static final int RES_XML_TYPE                = 0x0003;
145 
146   // Chunk types in RES_XML_TYPE
147   public static final int RES_XML_FIRST_CHUNK_TYPE    = 0x0100;
148   public static final int RES_XML_START_NAMESPACE_TYPE= 0x0100;
149   public static final int RES_XML_END_NAMESPACE_TYPE  = 0x0101;
150   public static final int RES_XML_START_ELEMENT_TYPE  = 0x0102;
151   public static final int RES_XML_END_ELEMENT_TYPE    = 0x0103;
152   public static final int RES_XML_CDATA_TYPE          = 0x0104;
153   public static final int RES_XML_LAST_CHUNK_TYPE     = 0x017f;
154   // This contains a uint32_t array mapping strings in the string
155   // pool back to resource identifiers.  It is optional.
156   public static final int RES_XML_RESOURCE_MAP_TYPE   = 0x0180;
157 
158   // Chunk types in RES_TABLE_TYPE
159   public static final int RES_TABLE_PACKAGE_TYPE      = 0x0200;
160   public static final int RES_TABLE_TYPE_TYPE         = 0x0201;
161   public static final int RES_TABLE_TYPE_SPEC_TYPE    = 0x0202;
162   public static final int RES_TABLE_LIBRARY_TYPE      = 0x0203;
163 
164   /**
165    * Macros for building/splitting resource identifiers.
166    */
167 //#define Res_VALIDID(resid) (resid != 0)
168 //#define Res_CHECKID(resid) ((resid&0xFFFF0000) != 0)
169 //#define Res_MAKEID(package, type, entry) \
170 //(((package+1)<<24) | (((type+1)&0xFF)<<16) | (entry&0xFFFF))
171 //#define Res_GETPACKAGE(id) ((id>>24)-1)
172 //#define Res_GETTYPE(id) (((id>>16)&0xFF)-1)
173 //#define Res_GETENTRY(id) (id&0xFFFF)
174 
175 //#define Res_INTERNALID(resid) ((resid&0xFFFF0000) != 0 && (resid&0xFF0000) == 0)
Res_MAKEINTERNAL(int entry)176   private static int Res_MAKEINTERNAL(int entry) {
177     return (0x01000000 | (entry & 0xFFFF));
178   }
179 //#define Res_MAKEARRAY(entry) (0x02000000 | (entry&0xFFFF))
180 
181 //  static const size_t Res_MAXPACKAGE = 255;
182 //  static const size_t Res_MAXTYPE = 255;
183 
184   /**
185    * Representation of a value in a resource, supplying type
186    * information.
187    */
188   public static class Res_value
189   {
190     static final int SIZEOF = 8;
191 
192     // Number of bytes in this structure.
193     final short size;
194 
195     // Always set to 0.
196 //    byte res0;
197 
198     // Type of the data value.
199 //    enum {
200     // The 'data' is either 0 or 1, specifying this resource is either
201     // undefined or empty, respectively.
202     public static final int TYPE_NULL = 0x00;
203     // The 'data' holds a ResTable_ref, a reference to another resource
204     // table entry.
205     public static final int TYPE_REFERENCE = 0x01;
206     // The 'data' holds an attribute resource identifier.
207     public static final int TYPE_ATTRIBUTE = 0x02;
208     // The 'data' holds an index into the containing resource table's
209     // global value string pool.
210     public static final int TYPE_STRING = 0x03;
211     // The 'data' holds a single-precision floating point number.
212     public static final int TYPE_FLOAT = 0x04;
213     // The 'data' holds a complex number encoding a dimension value,
214     // such as "100in".
215     public static final int TYPE_DIMENSION = 0x05;
216     // The 'data' holds a complex number encoding a fraction of a
217     // container.
218     public static final int TYPE_FRACTION = 0x06;
219     // The 'data' holds a dynamic ResTable_ref, which needs to be
220     // resolved before it can be used like a TYPE_REFERENCE.
221     public static final int TYPE_DYNAMIC_REFERENCE = 0x07;
222     // The 'data' holds an attribute resource identifier, which needs to be resolved
223     // before it can be used like a TYPE_ATTRIBUTE.
224     public static final int TYPE_DYNAMIC_ATTRIBUTE = 0x08;
225 
226     // Beginning of integer flavors...
227     public static final int TYPE_FIRST_INT = 0x10;
228 
229     // The 'data' is a raw integer value of the form n..n.
230     public static final int TYPE_INT_DEC = 0x10;
231     // The 'data' is a raw integer value of the form 0xn..n.
232     public static final int TYPE_INT_HEX = 0x11;
233     // The 'data' is either 0 or 1, for input "false" or "true" respectively.
234     public static final int TYPE_INT_BOOLEAN = 0x12;
235 
236     // Beginning of color integer flavors...
237     public static final int TYPE_FIRST_COLOR_INT = 0x1c;
238 
239     // The 'data' is a raw integer value of the form #aarrggbb.
240     public static final int TYPE_INT_COLOR_ARGB8 = 0x1c;
241     // The 'data' is a raw integer value of the form #rrggbb.
242     public static final int TYPE_INT_COLOR_RGB8 = 0x1d;
243     // The 'data' is a raw integer value of the form #argb.
244     public static final int TYPE_INT_COLOR_ARGB4 = 0x1e;
245     // The 'data' is a raw integer value of the form #rgb.
246     public static final int TYPE_INT_COLOR_RGB4 = 0x1f;
247 
248     // ...end of integer flavors.
249     public static final int TYPE_LAST_COLOR_INT = 0x1f;
250 
251     // ...end of integer flavors.
252     public static final int TYPE_LAST_INT = 0x1f;
253 //  };
254 
255     public final byte dataType;
256 
257     // Structure of complex data values (TYPE_UNIT and TYPE_FRACTION)
258 //    enum {
259     // Where the unit type information is.  This gives us 16 possible
260     // types, as defined below.
261     public static final int COMPLEX_UNIT_SHIFT = 0;
262     public static final int COMPLEX_UNIT_MASK = 0xf;
263 
264     // TYPE_DIMENSION: Value is raw pixels.
265     public static final int COMPLEX_UNIT_PX = 0;
266     // TYPE_DIMENSION: Value is Device Independent Pixels.
267     public static final int COMPLEX_UNIT_DIP = 1;
268     // TYPE_DIMENSION: Value is a Scaled device independent Pixels.
269     public static final int COMPLEX_UNIT_SP = 2;
270     // TYPE_DIMENSION: Value is in points.
271     public static final int COMPLEX_UNIT_PT = 3;
272     // TYPE_DIMENSION: Value is in inches.
273     public static final int COMPLEX_UNIT_IN = 4;
274     // TYPE_DIMENSION: Value is in millimeters.
275     public static final int COMPLEX_UNIT_MM = 5;
276 
277     // TYPE_FRACTION: A basic fraction of the overall size.
278     public static final int COMPLEX_UNIT_FRACTION = 0;
279     // TYPE_FRACTION: A fraction of the parent size.
280     public static final int COMPLEX_UNIT_FRACTION_PARENT = 1;
281 
282     // Where the radix information is, telling where the decimal place
283     // appears in the mantissa.  This give us 4 possible fixed point
284     // representations as defined below.
285     public static final int COMPLEX_RADIX_SHIFT = 4;
286     public static final int COMPLEX_RADIX_MASK = 0x3;
287 
288     // The mantissa is an integral number -- i.e., 0xnnnnnn.0
289     public static final int COMPLEX_RADIX_23p0 = 0;
290     // The mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn
291     public static final int COMPLEX_RADIX_16p7 = 1;
292     // The mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn
293     public static final int COMPLEX_RADIX_8p15 = 2;
294     // The mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn
295     public static final int COMPLEX_RADIX_0p23 = 3;
296 
297     // Where the actual value is.  This gives us 23 bits of
298     // precision.  The top bit is the sign.
299     public static final int COMPLEX_MANTISSA_SHIFT = 8;
300     public static final int COMPLEX_MANTISSA_MASK = 0xffffff;
301 //  };
302 
303     // Possible data values for TYPE_NULL.
304 //    enum {
305     // The value is not defined.
306     public static final int DATA_NULL_UNDEFINED = 0;
307     // The value is explicitly defined as empty.
308     public static final int DATA_NULL_EMPTY = 1;
309 //  };
310 
311     public static final Res_value NULL_VALUE = new Res_value((byte) TYPE_NULL, DATA_NULL_UNDEFINED);
312 
313     // The data for this item, as interpreted according to dataType.
314 //    typedef uint32_t data_type;
315     public final int data;
316 
Res_value()317     public Res_value() {
318       this.size = 0;
319 //      this.res0 = 0;
320       this.dataType = 0;
321       this.data = 0;
322     }
323 
Res_value(ByteBuffer buf, int offset)324     public Res_value(ByteBuffer buf, int offset) {
325       this.size = buf.getShort(offset);
326       byte res0 = buf.get(offset + 2);
327       this.dataType = buf.get(offset + 3);
328       this.data = buf.getInt(offset + 4);
329 
330       if (res0 != 0) {
331         throw new IllegalStateException("res0 != 0 (" + res0 + ")");
332       }
333     }
334 
Res_value(Res_value other)335     public Res_value(Res_value other) {
336       this.size = other.size;
337 //      this.res0 = other.res0;
338       this.dataType = other.dataType;
339       this.data = other.data;
340     }
341 
Res_value(byte dataType, int data)342     public Res_value(byte dataType, int data) {
343       this.size = 0;
344 //      this.res0 = 0;
345       this.dataType = dataType;
346       this.data = data;
347     }
348 
write(ByteBuffer buf, int dataType, int data)349     public static void write(ByteBuffer buf, int dataType, int data) {
350       buf.putShort((short) SIZEOF); // size
351       buf.put((byte) 0); // res0
352       buf.put((byte) dataType); // dataType
353       buf.putInt(data); // data
354     }
355 
withType(byte dataType)356     public Res_value withType(byte dataType) {
357       return new Res_value(dataType, data);
358     }
359 
withData(int data)360     public Res_value withData(int data) {
361       return new Res_value(dataType, data);
362     }
363 
364 //    public void copyFrom_dtoh(Res_value other) {
365 //      this.size = other.size;
366 // //      this.res0 = other.res0;
367 //      this.dataType = other.dataType;
368 //      this.data = other.data;
369 //    }
370 
copy()371     public Res_value copy() {
372       return new Res_value(this);
373     }
374 
375     @Override
toString()376     public String toString() {
377       return "Res_value{dataType=" + dataType + ", data=" + data + '}';
378     }
379   }
380 
381 /**
382  *  This is a reference to a unique entry (a ResTable_entry structure)
383  *  in a resource table.  The value is structured as: 0xpptteeee,
384  *  where pp is the package index, tt is the type index in that
385  *  package, and eeee is the entry index in that type.  The package
386  *  and type values start at 1 for the first item, to help catch cases
387  *  where they have not been supplied.
388  */
389 public static class ResTable_ref
390     {
391       public static final int SIZEOF = 4;
392 
393       public int ident;
394 
ResTable_ref(ByteBuffer buf, int offset)395       public ResTable_ref(ByteBuffer buf, int offset) {
396         ident = buf.getInt(offset);
397       }
398 
ResTable_ref()399       public ResTable_ref() {
400         ident = 0;
401       }
402 
403       @Override
toString()404       public String toString() {
405         return "ResTable_ref{ident=" + ident + '}';
406       }
407     };
408 
409   /**
410    * Reference to a string in a string pool.
411    */
412   public static class ResStringPool_ref
413   {
414     public static final int SIZEOF = 4;
415 
416     // Index into the string pool table (uint32_t-offset from the indices
417     // immediately after ResStringPool_header) at which to find the location
418     // of the string data in the pool.
419     public final int index;
420 
ResStringPool_ref(ByteBuffer buf, int offset)421     public ResStringPool_ref(ByteBuffer buf, int offset) {
422       this.index = buf.getInt(offset);
423     }
424 
write(ByteBuffer buf, int value)425     public static void write(ByteBuffer buf, int value) {
426       buf.putInt(value);
427     }
428 
429     @Override
toString()430     public String toString() {
431       return "ResStringPool_ref{index=" + index + '}';
432     }
433   }
434 
435 /** ********************************************************************
436  *  String Pool
437  *
438  *  A set of strings that can be references by others through a
439  *  ResStringPool_ref.
440  *
441  *********************************************************************** */
442 
443 
444 /**
445    * Definition for a pool of strings.  The data of this chunk is an
446    * array of uint32_t providing indices into the pool, relative to
447    * stringsStart.  At stringsStart are all of the UTF-16 strings
448    * concatenated together; each starts with a uint16_t of the string's
449    * length and each ends with a 0x0000 terminator.  If a string is >
450    * 32767 characters, the high bit of the length is set meaning to take
451    * those 15 bits as a high word and it will be followed by another
452    * uint16_t containing the low word.
453    *
454    * If styleCount is not zero, then immediately following the array of
455    * uint32_t indices into the string table is another array of indices
456    * into a style table starting at stylesStart.  Each entry in the
457    * style table is an array of ResStringPool_span structures.
458    */
459   public static class ResStringPool_header extends WithOffset
460   {
461     public static final int SIZEOF = ResChunk_header.SIZEOF + 20;
462 
463     final ResChunk_header header;
464 
465     // Number of strings in this pool (number of uint32_t indices that follow
466     // in the data).
467     final int stringCount;
468 
469     // Number of style span arrays in the pool (number of uint32_t indices
470     // follow the string indices).
471     final int styleCount;
472 
473     // Flags.
474 //    enum {
475     // If set, the string index is sorted by the string values (based
476     // on strcmp16()).
477     public static final int SORTED_FLAG = 1<<0;
478 
479         // String pool is encoded in UTF-8
480         public static final int UTF8_FLAG = 1<<8;
481 //  };
482     final int flags;
483 
484     // Index from header of the string data.
485     final int stringsStart;
486 
487     // Index from header of the style data.
488     final int stylesStart;
489 
ResStringPool_header(ByteBuffer buf, int offset)490     public ResStringPool_header(ByteBuffer buf, int offset) {
491       super(buf, offset);
492 
493       this.header = new ResChunk_header(buf, offset);
494       this.stringCount = buf.getInt(offset + ResChunk_header.SIZEOF);
495       this.styleCount = buf.getInt(offset + ResChunk_header.SIZEOF + 4);
496       this.flags = buf.getInt(offset + ResChunk_header.SIZEOF + 8);
497       this.stringsStart = buf.getInt(offset + ResChunk_header.SIZEOF + 12);
498       this.stylesStart = buf.getInt(offset + ResChunk_header.SIZEOF + 16);
499     }
500 
getByte(int i)501     public int getByte(int i) {
502       return myBuf().get(myOffset() + i);
503     }
504 
getShort(int i)505     public int getShort(int i) {
506       return myBuf().getShort(myOffset() + i);
507     }
508 
509     public static class Writer {
510 
511       private final List<String> strings = new ArrayList<>();
512       private final List<byte[]> stringsAsBytes = new ArrayList<>();
513       private final Map<String, Integer> stringIds = new HashMap<>();
514 
515       private boolean frozen;
516 
string(String s)517       public int string(String s) {
518         if (frozen) {
519           throw new IllegalStateException("string pool is frozen!");
520         }
521 
522         if (s == null) {
523           return -1;
524         }
525 
526         Integer id = stringIds.get(s);
527         if (id == null) {
528           id = strings.size();
529           strings.add(s);
530           stringsAsBytes.add(s.getBytes(UTF_8));
531           stringIds.put(s, id);
532         }
533         return id;
534       }
535 
uniqueString(String s)536       public int uniqueString(String s) {
537         if (frozen) {
538           throw new IllegalStateException("string pool is frozen!");
539         }
540 
541         if (s == null) {
542           return -1;
543         }
544 
545         int id = strings.size();
546         strings.add(s);
547         stringsAsBytes.add(s.getBytes(UTF_8));
548         return id;
549       }
550 
write(ByteBuffer buf)551       public void write(ByteBuffer buf) {
552         freeze();
553 
554         ResChunk_header.write(buf, (short) RES_STRING_POOL_TYPE, () -> {
555           // header
556           int startPos = buf.position();
557           int stringCount = strings.size();
558 
559           // begin string pool...
560           buf.putInt(stringCount); // stringCount
561           buf.putInt(0); // styleCount
562           buf.putInt(UTF8_FLAG); // flags
563           IntWriter stringStart = new IntWriter(buf);
564           buf.putInt(0); // stylesStart
565 
566           stringStart.write(buf.position() - startPos);
567         }, () -> {
568           // contents
569           int stringOffset = /*buf.position() + */8 + 4 * stringsAsBytes.size();
570           for (int i = 0; i < stringsAsBytes.size(); i++) {
571             String string = strings.get(i);
572             byte[] bytes = stringsAsBytes.get(i);
573             buf.putInt(stringOffset);
574             stringOffset += lenLen(string.length()) + lenLen(bytes.length) + bytes.length + 1;
575           }
576 
577           for (int i = 0; i < stringsAsBytes.size(); i++) {
578             // number of chars
579             writeLen(buf, strings.get(i).length());
580 
581             // number of bytes
582             writeLen(buf, stringsAsBytes.get(i).length);
583 
584             // bytes
585             buf.put(stringsAsBytes.get(i));
586             // null terminator
587             buf.put((byte) '\0');
588           }
589         });
590       }
591 
lenLen(int length)592       private int lenLen(int length) {
593         return length > 0x7f ? 2 : 1;
594       }
595 
writeLen(ByteBuffer buf, int length)596       private void writeLen(ByteBuffer buf, int length) {
597         if (length <= 0x7f) {
598           buf.put((byte) length);
599         } else {
600           buf.put((byte) ((length >> 8) | 0x80));
601           buf.put((byte) (length & 0x7f));
602         }
603       }
604 
freeze()605       public void freeze() {
606         frozen = true;
607       }
608     }
609   }
610 
611   /**
612    * This structure defines a span of style information associated with
613    * a string in the pool.
614    */
615   public static class ResStringPool_span extends WithOffset
616   {
617     public static final int SIZEOF = ResStringPool_ref.SIZEOF + 8;
618 
619     //    enum {
620     public static final int END = 0xFFFFFFFF;
621 //  };
622 
623     // This is the name of the span -- that is, the name of the XML
624     // tag that defined it.  The special value END (0xFFFFFFFF) indicates
625     // the end of an array of spans.
626     public final ResStringPool_ref name;
627 
628     // The range of characters in the string that this span applies to.
629     final int firstChar;
630     final int lastChar;
631 
ResStringPool_span(ByteBuffer buf, int offset)632     public ResStringPool_span(ByteBuffer buf, int offset) {
633       super(buf, offset);
634 
635       name = new ResStringPool_ref(buf, offset);
636       firstChar = buf.getInt(offset + ResStringPool_ref.SIZEOF);
637       lastChar = buf.getInt(offset + ResStringPool_ref.SIZEOF + 4);
638     }
639 
isEnd()640     public boolean isEnd() {
641       return name.index == END && firstChar == END && lastChar == END;
642     }
643   };
644 
645 
646   /** ********************************************************************
647    *  XML Tree
648    *
649    *  Binary representation of an XML document.  This is designed to
650    *  express everything in an XML document, in a form that is much
651    *  easier to parse on the device.
652    *
653    *********************************************************************** */
654 
655   /**
656    * XML tree header.  This appears at the front of an XML tree,
657    * describing its content.  It is followed by a flat array of
658    * ResXMLTree_node structures; the hierarchy of the XML document
659    * is described by the occurrance of RES_XML_START_ELEMENT_TYPE
660    * and corresponding RES_XML_END_ELEMENT_TYPE nodes in the array.
661    */
662   public static class ResXMLTree_header extends WithOffset
663   {
664     public final ResChunk_header header;
665 
ResXMLTree_header(ByteBuffer buf, int offset)666     ResXMLTree_header(ByteBuffer buf, int offset) {
667       super(buf, offset);
668       header = new ResChunk_header(buf, offset);
669     }
670 
write(ByteBuffer buf, Writer resStringPoolWriter, Runnable contents)671     public static void write(ByteBuffer buf, Writer resStringPoolWriter, Runnable contents) {
672       ResChunk_header.write(buf, (short) RES_XML_TYPE, ()-> {}, () -> {
673         resStringPoolWriter.write(buf);
674         contents.run();
675       });
676     }
677   }
678 
679   /**
680    * Basic XML tree node.  A single item in the XML document.  Extended info
681    * about the node can be found after header.headerSize.
682    */
683   public static class ResXMLTree_node extends WithOffset
684   {
685     final ResChunk_header header;
686 
687     // Line number in original source file at which this element appeared.
688     final int lineNumber;
689 
690     // Optional XML comment that was associated with this element; -1 if none.
691     final ResStringPool_ref comment;
692 
ResXMLTree_node(ByteBuffer buf, int offset)693     ResXMLTree_node(ByteBuffer buf, int offset) {
694       super(buf, offset);
695 
696       this.header = new ResChunk_header(buf, offset);
697       this.lineNumber = buf.getInt(offset + ResChunk_header.SIZEOF);
698       this.comment = new ResStringPool_ref(buf, offset + 12);
699     }
700 
ResXMLTree_node(ByteBuffer buf, ResChunk_header header)701     ResXMLTree_node(ByteBuffer buf, ResChunk_header header) {
702       super(buf, header.myOffset());
703 
704       this.header = header;
705       this.lineNumber = buf.getInt(myOffset() + ResChunk_header.SIZEOF);
706       this.comment = new ResStringPool_ref(buf, myOffset() + ResChunk_header.SIZEOF + 4);
707     }
708 
write(ByteBuffer buf, int type, Runnable contents)709     public static void write(ByteBuffer buf, int type, Runnable contents) {
710       ResChunk_header.write(buf, (short) type, () -> {
711         buf.putInt(-1); // lineNumber
712         ResStringPool_ref.write(buf, -1); // comment
713       }, contents);
714     }
715   };
716 
717   /**
718    * Extended XML tree node for CDATA tags -- includes the CDATA string.
719    * Appears header.headerSize bytes after a ResXMLTree_node.
720    */
721   static class ResXMLTree_cdataExt
722   {
723     // The raw CDATA character data.
724     final ResStringPool_ref data;
725 
726     // The typed value of the character data if this is a CDATA node.
727     final Res_value typedData;
728 
ResXMLTree_cdataExt(ByteBuffer buf, int offset)729     public ResXMLTree_cdataExt(ByteBuffer buf, int offset) {
730       this.data = new ResStringPool_ref(buf, offset);
731 
732       int dataType = buf.getInt(offset + 4);
733       int data = buf.getInt(offset + 8);
734       this.typedData = new Res_value((byte) dataType, data);
735     }
736   };
737 
738   /**
739    * Extended XML tree node for namespace start/end nodes.
740    * Appears header.headerSize bytes after a ResXMLTree_node.
741    */
742   static class ResXMLTree_namespaceExt
743   {
744     // The prefix of the namespace.
745     final ResStringPool_ref prefix;
746 
747     // The URI of the namespace.
748     final ResStringPool_ref uri;
749 
ResXMLTree_namespaceExt(ByteBuffer buf, int offset)750     public ResXMLTree_namespaceExt(ByteBuffer buf, int offset) {
751       this.prefix = new ResStringPool_ref(buf, offset);
752       this.uri = new ResStringPool_ref(buf, offset + 4);
753     }
754   };
755 
756   /**
757    * Extended XML tree node for element start/end nodes.
758    * Appears header.headerSize bytes after a ResXMLTree_node.
759    */
760   public static class ResXMLTree_endElementExt
761   {
762     static final int SIZEOF = 8;
763 
764     // String of the full namespace of this element.
765     final ResStringPool_ref ns;
766 
767     // String name of this node if it is an ELEMENT; the raw
768     // character data if this is a CDATA node.
769     final ResStringPool_ref name;
770 
ResXMLTree_endElementExt(ByteBuffer buf, int offset)771     public ResXMLTree_endElementExt(ByteBuffer buf, int offset) {
772       this.ns = new ResStringPool_ref(buf, offset);
773       this.name = new ResStringPool_ref(buf, offset + ResStringPool_ref.SIZEOF);
774     }
775 
776     public static class Writer {
777       private final ByteBuffer buf;
778       private final ResStringPool_header.Writer resStringPoolWriter;
779       private final int ns;
780       private final int name;
781 
Writer(ByteBuffer buf, ResStringPool_header.Writer resStringPoolWriter, String ns, String name)782       public Writer(ByteBuffer buf, ResStringPool_header.Writer resStringPoolWriter,
783           String ns, String name) {
784         this.buf = buf;
785         this.resStringPoolWriter = resStringPoolWriter;
786         this.ns = resStringPoolWriter.string(ns);
787         this.name = resStringPoolWriter.string(name);
788       }
789 
write()790       public void write() {
791         ResStringPool_ref.write(buf, ns);
792         ResStringPool_ref.write(buf, name);
793       }
794     }
795   };
796 
797   /**
798    * Extended XML tree node for start tags -- includes attribute
799    * information.
800    * Appears header.headerSize bytes after a ResXMLTree_node.
801    */
802   public static class ResXMLTree_attrExt extends WithOffset
803   {
804     private final ByteBuffer buf;
805 
806     // String of the full namespace of this element.
807     final ResStringPool_ref ns;
808 
809     // String name of this node if it is an ELEMENT; the raw
810     // character data if this is a CDATA node.
811     final ResStringPool_ref name;
812 
813     // Byte offset from the start of this structure where the attributes start.
814     final short attributeStart;
815 
816     // Size of the ResXMLTree_attribute structures that follow.
817     final short attributeSize;
818 
819     // Number of attributes associated with an ELEMENT.  These are
820     // available as an array of ResXMLTree_attribute structures
821     // immediately following this node.
822     final short attributeCount;
823 
824     // Index (1-based) of the "id" attribute. 0 if none.
825     final short idIndex;
826 
827     // Index (1-based) of the "class" attribute. 0 if none.
828     final short classIndex;
829 
830     // Index (1-based) of the "style" attribute. 0 if none.
831     final short styleIndex;
832 
ResXMLTree_attrExt(ByteBuffer buf, int offset)833     public ResXMLTree_attrExt(ByteBuffer buf, int offset) {
834       super(buf, offset);
835       this.buf = buf;
836 
837       this.ns = new ResStringPool_ref(buf, offset);
838       this.name = new ResStringPool_ref(buf, offset + 4);
839       this.attributeStart = buf.getShort(offset + 8);
840       this.attributeSize = buf.getShort(offset + 10);
841       this.attributeCount = buf.getShort(offset + 12);
842       this.idIndex = buf.getShort(offset + 14);
843       this.classIndex = buf.getShort(offset + 16);
844       this.styleIndex = buf.getShort(offset + 18);
845     }
846 
attributeAt(int idx)847     ResXMLTree_attribute attributeAt(int idx) {
848       return new ResXMLTree_attribute(buf,
849           myOffset() + dtohs(attributeStart) + dtohs(attributeSize) * idx);
850     }
851 
852     public static class Writer {
853       private final ByteBuffer buf;
854       private final ResStringPool_header.Writer resStringPoolWriter;
855       private final int ns;
856       private final int name;
857 
858       private short idIndex;
859       private short classIndex;
860       private short styleIndex;
861 
862       private final List<Attr> attrs = new ArrayList<>();
863 
Writer(ByteBuffer buf, ResStringPool_header.Writer resStringPoolWriter, String ns, String name)864       public Writer(ByteBuffer buf, ResStringPool_header.Writer resStringPoolWriter,
865           String ns, String name) {
866         this.buf = buf;
867         this.resStringPoolWriter = resStringPoolWriter;
868         this.ns = resStringPoolWriter.string(ns);
869         this.name = resStringPoolWriter.string(name);
870       }
871 
attr(int ns, int name, int value, Res_value resValue, String fullName)872       public void attr(int ns, int name, int value, Res_value resValue, String fullName) {
873         attrs.add(new Attr(ns, name, value, resValue, fullName));
874       }
875 
write()876       public void write() {
877         int startPos = buf.position();
878         int attributeCount = attrs.size();
879 
880         ResStringPool_ref.write(buf, ns);
881         ResStringPool_ref.write(buf, name);
882         ShortWriter attributeStartWriter = new ShortWriter(buf);
883         buf.putShort((short) ResXMLTree_attribute.SIZEOF); // attributeSize
884         buf.putShort((short) attributeCount); // attributeCount
885         ShortWriter idIndexWriter = new ShortWriter(buf);
886         ShortWriter classIndexWriter = new ShortWriter(buf);
887         ShortWriter styleIndexWriter = new ShortWriter(buf);
888 
889         attributeStartWriter.write((short) (buf.position() - startPos));
890         for (int i = 0; i < attributeCount; i++) {
891           Attr attr = attrs.get(i);
892 
893           switch (attr.fullName) {
894             case ":id":
895               idIndex = (short) (i + 1);
896               break;
897             case ":style":
898               styleIndex = (short) (i + 1);
899               break;
900             case ":class":
901               classIndex = (short) (i + 1);
902               break;
903           }
904 
905           attr.write(buf);
906         }
907 
908         idIndexWriter.write(idIndex);
909         classIndexWriter.write(classIndex);
910         styleIndexWriter.write(styleIndex);
911       }
912 
913       private static class Attr {
914         final int ns;
915         final int name;
916         final int value;
917         final int resValueDataType;
918         final int resValueData;
919         final String fullName;
920 
Attr(int ns, int name, int value, Res_value resValue, String fullName)921         public Attr(int ns, int name, int value, Res_value resValue, String fullName) {
922           this.ns = ns;
923           this.name = name;
924           this.value = value;
925           this.resValueDataType = resValue.dataType;
926           this.resValueData = resValue.data;
927           this.fullName = fullName;
928         }
929 
write(ByteBuffer buf)930         public void write(ByteBuffer buf) {
931           ResXMLTree_attribute.write(buf, ns, name, value, resValueDataType, resValueData);
932         }
933       }
934     }
935   };
936 
937   static class ResXMLTree_attribute
938   {
939     public static final int SIZEOF = 12+ ResourceTypes.Res_value.SIZEOF;
940 
941     // Namespace of this attribute.
942     final ResStringPool_ref ns;
943 
944     // Name of this attribute.
945     final ResStringPool_ref name;
946 
947     // The original raw string value of this attribute.
948     final ResStringPool_ref rawValue;
949 
950     // Processesd typed value of this attribute.
951     final Res_value typedValue;
952 
ResXMLTree_attribute(ByteBuffer buf, int offset)953     public ResXMLTree_attribute(ByteBuffer buf, int offset) {
954       this.ns = new ResStringPool_ref(buf, offset);
955       this.name = new ResStringPool_ref(buf, offset + 4);
956       this.rawValue = new ResStringPool_ref(buf, offset + 8);
957       this.typedValue = new Res_value(buf, offset + 12);
958     }
959 
write(ByteBuffer buf, int ns, int name, int value, int resValueDataType, int resValueData)960     public static void write(ByteBuffer buf, int ns, int name, int value, int resValueDataType,
961         int resValueData) {
962       ResStringPool_ref.write(buf, ns);
963       ResStringPool_ref.write(buf, name);
964       ResStringPool_ref.write(buf, value);
965       ResourceTypes.Res_value.write(buf, resValueDataType, resValueData);
966     }
967   };
968 
969 /** ********************************************************************
970  *  RESOURCE TABLE
971  *
972  *********************************************************************** */
973 
974   /**
975    * Header for a resource table.  Its data contains a series of
976    * additional chunks:
977    *   * A ResStringPool_header containing all table values.  This string pool
978    *     contains all of the string values in the entire resource table (not
979    *     the names of entries or type identifiers however).
980    *   * One or more ResTable_package chunks.
981    *
982    * Specific entries within a resource table can be uniquely identified
983    * with a single integer as defined by the ResTable_ref structure.
984    */
985   static class ResTable_header extends WithOffset
986   {
987     public static final int SIZEOF = ResChunk_header.SIZEOF + 4;
988 
989     final ResChunk_header header;
990 
991     // The number of ResTable_package structures.
992     final int packageCount;
993 
ResTable_header(ByteBuffer buf, int offset)994     public ResTable_header(ByteBuffer buf, int offset) {
995       super(buf, offset);
996       this.header = new ResChunk_header(buf, offset);
997       this.packageCount = buf.getInt(offset + ResChunk_header.SIZEOF);
998     }
999   }
1000 
1001   /**
1002    * A collection of resource data types within a package.  Followed by
1003    * one or more ResTable_type and ResTable_typeSpec structures containing the
1004    * entry values for each resource type.
1005    */
1006   static class ResTable_package extends WithOffset
1007   {
1008     public static final int SIZEOF = ResChunk_header.SIZEOF + 4 + 128 + 20;
1009 
1010     final ResChunk_header header;
1011 
1012     // If this is a base package, its ID.  Package IDs start
1013     // at 1 (corresponding to the value of the package bits in a
1014     // resource identifier).  0 means this is not a base package.
1015     public final int id;
1016 
1017     // Actual name of this package, \0-terminated.
1018     public final char[] name = new char[128];
1019 
1020     // Offset to a ResStringPool_header defining the resource
1021     // type symbol table.  If zero, this package is inheriting from
1022     // another base package (overriding specific values in it).
1023     public final int typeStrings;
1024 
1025     // Last index into typeStrings that is for public use by others.
1026     public final int lastPublicType;
1027 
1028     // Offset to a ResStringPool_header defining the resource
1029     // key symbol table.  If zero, this package is inheriting from
1030     // another base package (overriding specific values in it).
1031     public final int keyStrings;
1032 
1033     // Last index into keyStrings that is for public use by others.
1034     public final int lastPublicKey;
1035 
1036     public final int typeIdOffset;
1037 
ResTable_package(ByteBuffer buf, int offset)1038     public ResTable_package(ByteBuffer buf, int offset) {
1039       super(buf, offset);
1040       header = new ResChunk_header(buf, offset);
1041       id = buf.getInt(offset + ResChunk_header.SIZEOF);
1042       for (int i = 0; i < name.length; i++) {
1043         name[i] = buf.getChar(offset + ResChunk_header.SIZEOF + 4 + i * 2);
1044       }
1045       typeStrings = buf.getInt(offset + ResChunk_header.SIZEOF + 4 + 256);
1046       lastPublicType = buf.getInt(offset + ResChunk_header.SIZEOF + 4 + 256 + 4);
1047       keyStrings = buf.getInt(offset + ResChunk_header.SIZEOF + 4 + 256 + 8);
1048       lastPublicKey = buf.getInt(offset + ResChunk_header.SIZEOF + 4 + 256 + 12);
1049       typeIdOffset = buf.getInt(offset + ResChunk_header.SIZEOF + 4 + 256 + 16);
1050     }
1051   };
1052 
1053   // The most specific locale can consist of:
1054   //
1055   // - a 3 char language code
1056   // - a 3 char region code prefixed by a 'r'
1057   // - a 4 char script code prefixed by a 's'
1058   // - a 8 char variant code prefixed by a 'v'
1059   //
1060 // each separated by a single char separator, which sums up to a total of 24
1061 // chars, (25 include the string terminator). Numbering system specificator,
1062 // if present, can add up to 14 bytes (-u-nu-xxxxxxxx), giving 39 bytes,
1063 // or 40 bytes to make it 4 bytes aligned.
1064   public static final int RESTABLE_MAX_LOCALE_LEN = 40;
1065 
1066   /**
1067    * A specification of the resources defined by a particular type.
1068    *
1069    * There should be one of these chunks for each resource type.
1070    *
1071    * This structure is followed by an array of integers providing the set of
1072    * configuration change flags (ResTable_config::CONFIG_*) that have multiple
1073    * resources for that configuration.  In addition, the high bit is set if that
1074    * resource has been made public.
1075    */
1076   static class ResTable_typeSpec extends WithOffset
1077   {
1078     public static final int SIZEOF = ResChunk_header.SIZEOF + 8;
1079 
1080     final ResChunk_header header;
1081 
1082     // The type identifier this chunk is holding.  Type IDs start
1083     // at 1 (corresponding to the value of the type bits in a
1084     // resource identifier).  0 is invalid.
1085     final byte id;
1086 
1087     // Must be 0.
1088     final byte res0;
1089     // Must be 0.
1090     final short res1;
1091 
1092     // Number of uint32_t entry configuration masks that follow.
1093     final int entryCount;
1094 
1095     //enum : uint32_t {
1096     // Additional flag indicating an entry is public.
1097     static final int SPEC_PUBLIC = 0x40000000;
1098 
1099     // Additional flag indicating an entry is overlayable at runtime.
1100     // Added in Android-P.
1101     static final int SPEC_OVERLAYABLE = 0x80000000;
1102 //    };
1103 
ResTable_typeSpec(ByteBuffer buf, int offset)1104     public ResTable_typeSpec(ByteBuffer buf, int offset) {
1105       super(buf, offset);
1106 
1107       header = new ResChunk_header(buf, offset);
1108       id = buf.get(offset + ResChunk_header.SIZEOF);
1109       res0 = buf.get(offset + ResChunk_header.SIZEOF + 1);
1110       res1 = buf.getShort(offset + ResChunk_header.SIZEOF + 2);
1111       entryCount = buf.getInt(offset + ResChunk_header.SIZEOF + 4);
1112     }
1113 
getSpecFlags()1114     public int[] getSpecFlags() {
1115       int[] ints = new int[(header.size - header.headerSize) / 4];
1116       for (int i = 0; i < ints.length; i++) {
1117         ints[i] = myBuf().getInt(myOffset() + header.headerSize + i * 4);
1118 
1119       }
1120       return ints;
1121     }
1122   };
1123 
1124   /**
1125    * A collection of resource entries for a particular resource data
1126    * type.
1127    *
1128    * If the flag FLAG_SPARSE is not set in `flags`, then this struct is
1129    * followed by an array of uint32_t defining the resource
1130    * values, corresponding to the array of type strings in the
1131    * ResTable_package::typeStrings string block. Each of these hold an
1132    * index from entriesStart; a value of NO_ENTRY means that entry is
1133    * not defined.
1134    *
1135    * If the flag FLAG_SPARSE is set in `flags`, then this struct is followed
1136    * by an array of ResTable_sparseTypeEntry defining only the entries that
1137    * have values for this type. Each entry is sorted by their entry ID such
1138    * that a binary search can be performed over the entries. The ID and offset
1139    * are encoded in a uint32_t. See ResTabe_sparseTypeEntry.
1140    *
1141    * There may be multiple of these chunks for a particular resource type,
1142    * supply different configuration variations for the resource values of
1143    * that type.
1144    *
1145    * It would be nice to have an additional ordered index of entries, so
1146    * we can do a binary search if trying to find a resource by string name.
1147    */
1148   static class ResTable_type extends WithOffset
1149   {
1150     //      public static final int SIZEOF = ResChunk_header.SIZEOF + 12 + ResTable_config.SIZ;
1151     public static final int SIZEOF_WITHOUT_CONFIG = ResChunk_header.SIZEOF + 12;
1152 
1153     final ResChunk_header header;
1154 
1155     //enum {
1156     public static final int NO_ENTRY = 0xFFFFFFFF;
1157 //    };
1158 
1159     // The type identifier this chunk is holding.  Type IDs start
1160     // at 1 (corresponding to the value of the type bits in a
1161     // resource identifier).  0 is invalid.
1162     final byte id;
1163 
1164     //      enum {
1165     // If set, the entry is sparse, and encodes both the entry ID and offset into each entry,
1166     // and a binary search is used to find the key. Only available on platforms >= O.
1167     // Mark any types that use this with a v26 qualifier to prevent runtime issues on older
1168     // platforms.
1169     public static final int FLAG_SPARSE = 0x01;
1170     //    };
1171     final byte flags;
1172 
1173     // Must be 0.
1174     final short reserved;
1175 
1176     // Number of uint32_t entry indices that follow.
1177     final int entryCount;
1178 
1179     // Offset from header where ResTable_entry data starts.
1180     final int entriesStart;
1181 
1182     // Configuration this collection of entries is designed for. This must always be last.
1183     final ResTable_config config;
1184 
ResTable_type(ByteBuffer buf, int offset)1185     ResTable_type(ByteBuffer buf, int offset) {
1186       super(buf, offset);
1187 
1188       header = new ResChunk_header(buf, offset);
1189       id = buf.get(offset + ResChunk_header.SIZEOF);
1190       flags = buf.get(offset + ResChunk_header.SIZEOF + 1);
1191       reserved = buf.getShort(offset + ResChunk_header.SIZEOF + 2);
1192       entryCount = buf.getInt(offset + ResChunk_header.SIZEOF + 4);
1193       entriesStart = buf.getInt(offset + ResChunk_header.SIZEOF + 8);
1194 
1195       buf.position(offset + ResChunk_header.SIZEOF + 12);
1196       config = ResTable_config.createConfig(buf);
1197     }
1198 
findEntryByResName(int stringId)1199     public int findEntryByResName(int stringId) {
1200       for (int i = 0; i < entryCount; i++) {
1201         if (entryNameIndex(i) == stringId) {
1202           return i;
1203         }
1204       }
1205       return -1;
1206     }
1207 
entryOffset(int entryIndex)1208     int entryOffset(int entryIndex) {
1209       ByteBuffer byteBuffer = myBuf();
1210       int offset = myOffset();
1211 
1212       // from ResTable cpp:
1213 //            const uint32_t* const eindex = reinterpret_cast<const uint32_t*>(
1214 //            reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize));
1215 //
1216 //        uint32_t thisOffset = dtohl(eindex[realEntryIndex]);
1217       return byteBuffer.getInt(offset + header.headerSize + entryIndex * 4);
1218     }
1219 
entryNameIndex(int entryIndex)1220     private int entryNameIndex(int entryIndex) {
1221       ByteBuffer byteBuffer = myBuf();
1222       int offset = myOffset();
1223 
1224       // from ResTable cpp:
1225 //            const uint32_t* const eindex = reinterpret_cast<const uint32_t*>(
1226 //            reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize));
1227 //
1228 //        uint32_t thisOffset = dtohl(eindex[realEntryIndex]);
1229       int entryOffset = byteBuffer.getInt(offset + header.headerSize + entryIndex * 4);
1230       if (entryOffset == -1) {
1231         return -1;
1232       }
1233 
1234       int STRING_POOL_REF_OFFSET = 4;
1235       return dtohl(byteBuffer.getInt(offset + entriesStart + entryOffset + STRING_POOL_REF_OFFSET));
1236     }
1237   };
1238 
1239   // The minimum size required to read any version of ResTable_type.
1240 //   constexpr size_t kResTableTypeMinSize =
1241 //   sizeof(ResTable_type) - sizeof(ResTable_config) + sizeof(ResTable_config::size);
1242   static final int kResTableTypeMinSize =
1243       ResTable_type.SIZEOF_WITHOUT_CONFIG - ResTable_config.SIZEOF + SIZEOF_INT /*sizeof(ResTable_config::size)*/;
1244 
1245   /**
1246    * An entry in a ResTable_type with the flag `FLAG_SPARSE` set.
1247    */
1248   static class ResTable_sparseTypeEntry extends WithOffset {
1249     public static final int SIZEOF = 6;
1250 
1251     // Holds the raw uint32_t encoded value. Do not read this.
1252     int entry;
1253 
1254     short idxOrOffset;
1255 //    struct {
1256       // The index of the entry.
1257 //      uint16_t idx;
1258 
1259       // The offset from ResTable_type::entriesStart, divided by 4.
1260 //      uint16_t offset;
1261 //    };
1262 
ResTable_sparseTypeEntry(ByteBuffer buf, int offset)1263     public ResTable_sparseTypeEntry(ByteBuffer buf, int offset) {
1264       super(buf, offset);
1265 
1266       entry = buf.getInt(offset);
1267       idxOrOffset = buf.getShort(offset + 4);
1268     }
1269   };
1270 
1271   /**
1272    * This is the beginning of information about an entry in the resource
1273    * table.  It holds the reference to the name of this entry, and is
1274    * immediately followed by one of:
1275    *   * A Res_value structure, if FLAG_COMPLEX is -not- set.
1276    *   * An array of ResTable_map structures, if FLAG_COMPLEX is set.
1277    *     These supply a set of name/value mappings of data.
1278    */
1279   static class ResTable_entry extends WithOffset
1280   {
1281     public static final int SIZEOF = 4 + ResStringPool_ref.SIZEOF;
1282 
1283     // Number of bytes in this structure.
1284     final short size;
1285 
1286     //enum {
1287     // If set, this is a complex entry, holding a set of name/value
1288     // mappings.  It is followed by an array of ResTable_map structures.
1289     public static final int FLAG_COMPLEX = 0x0001;
1290     // If set, this resource has been declared public, so libraries
1291     // are allowed to reference it.
1292     public static final int FLAG_PUBLIC = 0x0002;
1293     // If set, this is a weak resource and may be overriden by strong
1294     // resources of the same name/type. This is only useful during
1295     // linking with other resource tables.
1296     public static final int FLAG_WEAK = 0x0004;
1297     //    };
1298     final short flags;
1299 
1300     // Reference into ResTable_package::keyStrings identifying this entry.
1301     final ResStringPool_ref key;
1302 
ResTable_entry(ByteBuffer buf, int offset)1303     ResTable_entry(ByteBuffer buf, int offset) {
1304       super(buf, offset);
1305 
1306       size = buf.getShort(offset);
1307       flags = buf.getShort(offset + 2);
1308       key = new ResStringPool_ref(buf, offset + 4);
1309     }
1310 
getResValue()1311     public Res_value getResValue() {
1312       // something like:
1313 
1314       // final Res_value device_value = reinterpret_cast<final Res_value>(
1315       //     reinterpret_cast<final byte*>(entry) + dtohs(entry.size));
1316 
1317       return new Res_value(myBuf(), myOffset() + dtohs(size));
1318     }
1319   }
1320 
1321   /**
1322    * Extended form of a ResTable_entry for map entries, defining a parent map
1323    * resource from which to inherit values.
1324    */
1325   static class ResTable_map_entry extends ResTable_entry
1326   {
1327 
1328     /**
1329      * Indeterminate size, calculate using {@link #size} instead.
1330      */
1331     public static final Void SIZEOF = null;
1332 
1333     public static final int BASE_SIZEOF = ResTable_entry.SIZEOF + 8;
1334 
1335     // Resource identifier of the parent mapping, or 0 if there is none.
1336     // This is always treated as a TYPE_DYNAMIC_REFERENCE.
1337     ResTable_ref parent;
1338     // Number of name/value pairs that follow for FLAG_COMPLEX.
1339     int count;
1340 
ResTable_map_entry(ByteBuffer buf, int offset)1341     ResTable_map_entry(ByteBuffer buf, int offset) {
1342       super(buf, offset);
1343 
1344       parent = new ResTable_ref(buf, offset + ResTable_entry.SIZEOF);
1345       count = buf.getInt(offset + ResTable_entry.SIZEOF + ResTable_ref.SIZEOF);
1346     }
1347   };
1348 
1349   /**
1350    * A single name/value mapping that is part of a complex resource
1351    * entry.
1352    */
1353   public static class ResTable_map extends WithOffset
1354   {
1355     public static final int SIZEOF = ResTable_ref.SIZEOF + ResourceTypes.Res_value.SIZEOF;
1356 
1357     // The resource identifier defining this mapping's name.  For attribute
1358     // resources, 'name' can be one of the following special resource types
1359     // to supply meta-data about the attribute; for all other resource types
1360     // it must be an attribute resource.
1361     public final ResTable_ref name;
1362 
1363     // Special values for 'name' when defining attribute resources.
1364 //enum {
1365     // This entry holds the attribute's type code.
1366     public static final int ATTR_TYPE = Res_MAKEINTERNAL(0);
1367 
1368     // For integral attributes, this is the minimum value it can hold.
1369     public static final int ATTR_MIN = Res_MAKEINTERNAL(1);
1370 
1371     // For integral attributes, this is the maximum value it can hold.
1372     public static final int ATTR_MAX = Res_MAKEINTERNAL(2);
1373 
1374     // Localization of this resource is can be encouraged or required with
1375     // an aapt flag if this is set
1376     public static final int ATTR_L10N = Res_MAKEINTERNAL(3);
1377 
1378     // for plural support, see android.content.res.PluralRules#attrForQuantity(int)
1379     public static final int ATTR_OTHER = Res_MAKEINTERNAL(4);
1380     public static final int ATTR_ZERO = Res_MAKEINTERNAL(5);
1381     public static final int ATTR_ONE = Res_MAKEINTERNAL(6);
1382     public static final int ATTR_TWO = Res_MAKEINTERNAL(7);
1383     public static final int ATTR_FEW = Res_MAKEINTERNAL(8);
1384     public static final int ATTR_MANY = Res_MAKEINTERNAL(9);
1385 
1386 //    };
1387 
1388     // Bit mask of allowed types, for use with ATTR_TYPE.
1389 //enum {
1390     // No type has been defined for this attribute, use generic
1391     // type handling.  The low 16 bits are for types that can be
1392     // handled generically; the upper 16 require additional information
1393     // in the bag so can not be handled generically for TYPE_ANY.
1394     public static final int TYPE_ANY = 0x0000FFFF;
1395 
1396     // Attribute holds a references to another resource.
1397     public static final int TYPE_REFERENCE = 1<<0;
1398 
1399     // Attribute holds a generic string.
1400     public static final int TYPE_STRING = 1<<1;
1401 
1402     // Attribute holds an integer value.  ATTR_MIN and ATTR_MIN can
1403     // optionally specify a constrained range of possible integer values.
1404     public static final int TYPE_INTEGER = 1<<2;
1405 
1406     // Attribute holds a boolean integer.
1407     public static final int TYPE_BOOLEAN = 1<<3;
1408 
1409     // Attribute holds a color value.
1410     public static final int TYPE_COLOR = 1<<4;
1411 
1412     // Attribute holds a floating point value.
1413     public static final int TYPE_FLOAT = 1<<5;
1414 
1415     // Attribute holds a dimension value, such as "20px".
1416     public static final int TYPE_DIMENSION = 1<<6;
1417 
1418     // Attribute holds a fraction value, such as "20%".
1419     public static final int TYPE_FRACTION = 1<<7;
1420 
1421     // Attribute holds an enumeration.  The enumeration values are
1422     // supplied as additional entries in the map.
1423     public static final int TYPE_ENUM = 1<<16;
1424 
1425     // Attribute holds a bitmaks of flags.  The flag bit values are
1426     // supplied as additional entries in the map.
1427     public static final int TYPE_FLAGS = 1<<17;
1428 //    };
1429 
1430     // Enum of localization modes, for use with ATTR_L10N.
1431 //enum {
1432     public static final int L10N_NOT_REQUIRED = 0;
1433     public static final int L10N_SUGGESTED    = 1;
1434 //    };
1435 
1436     // This mapping's value.
1437     public Res_value value;
1438 
ResTable_map(ByteBuffer buf, int offset)1439     public ResTable_map(ByteBuffer buf, int offset) {
1440       super(buf, offset);
1441 
1442       name = new ResTable_ref(buf, offset);
1443       value = new Res_value(buf, offset + ResTable_ref.SIZEOF);
1444     }
1445 
ResTable_map()1446     public ResTable_map() {
1447       super(null, 0);
1448       this.name = new ResTable_ref();
1449       this.value = new Res_value();
1450     }
1451 
1452     @Override
toString()1453     public String toString() {
1454       return "ResTable_map{" + "name=" + name + ", value=" + value + '}';
1455     }
1456   };
1457 
1458   /**
1459    * A package-id to package name mapping for any shared libraries used
1460    * in this resource table. The package-id's encoded in this resource
1461    * table may be different than the id's assigned at runtime. We must
1462    * be able to translate the package-id's based on the package name.
1463    */
1464   static class ResTable_lib_header extends WithOffset
1465   {
1466     static final int SIZEOF = ResChunk_header.SIZEOF + 4;
1467 
1468     ResChunk_header header;
1469 
1470     // The number of shared libraries linked in this resource table.
1471     int count;
1472 
ResTable_lib_header(ByteBuffer buf, int offset)1473     ResTable_lib_header(ByteBuffer buf, int offset) {
1474       super(buf, offset);
1475 
1476       header = new ResChunk_header(buf, offset);
1477       count = buf.getInt(offset + ResChunk_header.SIZEOF);
1478     }
1479   };
1480 
1481   /**
1482    * A shared library package-id to package name entry.
1483    */
1484   static class ResTable_lib_entry extends WithOffset
1485   {
1486     public static final int SIZEOF = 4 + 128 * SIZEOF_SHORT;
1487 
1488     // The package-id this shared library was assigned at build time.
1489     // We use a uint32 to keep the structure aligned on a uint32 boundary.
1490     int packageId;
1491 
1492     // The package name of the shared library. \0 terminated.
1493     char[] packageName = new char[128];
1494 
ResTable_lib_entry(ByteBuffer buf, int offset)1495     ResTable_lib_entry(ByteBuffer buf, int offset) {
1496       super(buf, offset);
1497 
1498       packageId = buf.getInt(offset);
1499 
1500       for (int i = 0; i < packageName.length; i++) {
1501         packageName[i] = buf.getChar(offset + 4 + i * SIZEOF_SHORT);
1502       }
1503     }
1504   };
1505 
1506   // struct alignas(uint32_t) Idmap_header {
1507   static class Idmap_header extends WithOffset {
1508     // Always 0x504D4449 ('IDMP')
1509     int magic;
1510 
1511     int version;
1512 
1513     int target_crc32;
1514     int overlay_crc32;
1515 
1516     final byte[] target_path = new byte[256];
1517     final byte[] overlay_path = new byte[256];
1518 
1519     short target_package_id;
1520     short type_count;
1521 
Idmap_header(ByteBuffer buf, int offset)1522     Idmap_header(ByteBuffer buf, int offset) {
1523       super(buf, offset);
1524 
1525       magic = buf.getInt(offset);
1526       version = buf.getInt(offset + 4);
1527       target_crc32 = buf.getInt(offset + 8);
1528       overlay_crc32 = buf.getInt(offset + 12);
1529 
1530       buf.get(target_path, offset + 16, 256);
1531       buf.get(overlay_path, offset + 16 + 256, 256);
1532 
1533       target_package_id = buf.getShort(offset + 16 + 256 + 256);
1534       type_count = buf.getShort(offset + 16 + 256 + 256 + 2);
1535     }
1536   } // __attribute__((packed));
1537 
1538   // struct alignas(uint32_t) IdmapEntry_header {
1539   static class IdmapEntry_header extends WithOffset {
1540     static final int SIZEOF = 2 * 4;
1541 
1542     short target_type_id;
1543     short overlay_type_id;
1544     short entry_count;
1545     short entry_id_offset;
1546     int entries[];
1547 
IdmapEntry_header(ByteBuffer buf, int offset)1548     IdmapEntry_header(ByteBuffer buf, int offset) {
1549       super(buf, offset);
1550 
1551       target_type_id = buf.getShort(offset);
1552       overlay_type_id = buf.getShort(offset + 2);
1553       entry_count = buf.getShort(offset + 4);
1554       entry_id_offset = buf.getShort(offset + 6);
1555       entries = new int[entry_count];
1556       for (int i = 0; i < entries.length; i++) {
1557         entries[i] = buf.getInt(offset + 8 + i * SIZEOF_INT);
1558       }
1559     }
1560   } // __attribute__((packed));
1561 
1562 
1563   abstract private static class FutureWriter<T> {
1564     protected final ByteBuffer buf;
1565     private final int position;
1566 
FutureWriter(ByteBuffer buf, int size)1567     public FutureWriter(ByteBuffer buf, int size) {
1568       this.buf = buf;
1569       this.position = buf.position();
1570       buf.position(position + size);
1571     }
1572 
put(int position, T value)1573     abstract protected void put(int position, T value);
1574 
write(T value)1575     public void write(T value) {
1576       put(position, value);
1577     }
1578   }
1579 
1580   private static class IntWriter extends FutureWriter<Integer> {
IntWriter(ByteBuffer buf)1581     public IntWriter(ByteBuffer buf) {
1582       super(buf, 4);
1583     }
1584 
1585     @Override
put(int position, Integer value)1586     protected void put(int position, Integer value) {
1587       buf.putInt(position, value);
1588     }
1589   }
1590 
1591   private static class ShortWriter extends FutureWriter<Short> {
ShortWriter(ByteBuffer buf)1592     public ShortWriter(ByteBuffer buf) {
1593       super(buf, 2);
1594     }
1595 
1596     @Override
put(int position, Short value)1597     protected void put(int position, Short value) {
1598       buf.putShort(position, value);
1599     }
1600   }
1601 
1602   private static final Runnable NO_OP = () -> {};
1603 }
1604