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