Home | History | Annotate | Download | only in android
      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 
     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 
     63     WithOffset(ByteBuffer buf, int offset) {
     64       this.buf = buf;
     65       this.offset = offset;
     66     }
     67 
     68     public final ByteBuffer myBuf() {
     69       return buf;
     70     }
     71 
     72     public final int myOffset() {
     73       return offset;
     74     }
     75 
     76     @Override
     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 
    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 
    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)
    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 
    317     public Res_value() {
    318       this.size = 0;
    319 //      this.res0 = 0;
    320       this.dataType = 0;
    321       this.data = 0;
    322     }
    323 
    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 
    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 
    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 
    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 
    356     public Res_value withType(byte dataType) {
    357       return new Res_value(dataType, data);
    358     }
    359 
    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 
    371     public Res_value copy() {
    372       return new Res_value(this);
    373     }
    374 
    375     @Override
    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 
    395       public ResTable_ref(ByteBuffer buf, int offset) {
    396         ident = buf.getInt(offset);
    397       }
    398 
    399       public ResTable_ref() {
    400         ident = 0;
    401       }
    402 
    403       @Override
    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 
    421     public ResStringPool_ref(ByteBuffer buf, int offset) {
    422       this.index = buf.getInt(offset);
    423     }
    424 
    425     public static void write(ByteBuffer buf, int value) {
    426       buf.putInt(value);
    427     }
    428 
    429     @Override
    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 
    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 
    501     public int getByte(int i) {
    502       return myBuf().get(myOffset() + i);
    503     }
    504 
    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 
    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 
    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 
    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 
    592       private int lenLen(int length) {
    593         return length > 0x7f ? 2 : 1;
    594       }
    595 
    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 
    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 
    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 
    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 
    666     ResXMLTree_header(ByteBuffer buf, int offset) {
    667       super(buf, offset);
    668       header = new ResChunk_header(buf, offset);
    669     }
    670 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   1446     public ResTable_map() {
   1447       super(null, 0);
   1448       this.name = new ResTable_ref();
   1449       this.value = new Res_value();
   1450     }
   1451 
   1452     @Override
   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 
   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 
   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 
   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 
   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 
   1567     public FutureWriter(ByteBuffer buf, int size) {
   1568       this.buf = buf;
   1569       this.position = buf.position();
   1570       buf.position(position + size);
   1571     }
   1572 
   1573     abstract protected void put(int position, T value);
   1574 
   1575     public void write(T value) {
   1576       put(position, value);
   1577     }
   1578   }
   1579 
   1580   private static class IntWriter extends FutureWriter<Integer> {
   1581     public IntWriter(ByteBuffer buf) {
   1582       super(buf, 4);
   1583     }
   1584 
   1585     @Override
   1586     protected void put(int position, Integer value) {
   1587       buf.putInt(position, value);
   1588     }
   1589   }
   1590 
   1591   private static class ShortWriter extends FutureWriter<Short> {
   1592     public ShortWriter(ByteBuffer buf) {
   1593       super(buf, 2);
   1594     }
   1595 
   1596     @Override
   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