1414import java .io .IOException ;
1515import java .io .InputStream ;
1616import java .math .BigInteger ;
17+ import java .nio .charset .StandardCharsets ;
1718import java .util .ArrayList ;
1819import java .util .Arrays ;
1920import java .util .Collection ;
3839 */
3940public class ARSCFileParser extends AbstractResourceParser {
4041
42+ /**
43+ * If <code>true</code> any encountered resource format violation like reserved
44+ * fields which should be zero but have a value will raise an
45+ * {@link RuntimeException}.
46+ *
47+ * If <code>false</code> format violations will only be logged as errors.
48+ */
49+ public static boolean STRICT_MODE = true ;
50+
4151 protected final Logger logger = LoggerFactory .getLogger (getClass ());
4252
4353 protected final static int RES_STRING_POOL_TYPE = 0x0001 ;
@@ -1438,13 +1448,13 @@ protected static class ResTable_TypeSpec {
14381448 */
14391449 int id ; // uint8
14401450 /**
1441- * Must be 0 (solid) or 1 (sparse).
1451+ * Must be 0
14421452 */
1443- int flags ; // uint8
1453+ int res0 ; // uint8
14441454 /**
14451455 * Must be 1.
14461456 */
1447- int reserved ; // uint16
1457+ int res1 ; // uint16
14481458 /**
14491459 * Number of uint32_t entry configuration masks that follow.
14501460 */
@@ -1457,8 +1467,8 @@ public int hashCode() {
14571467 result = prime * result + entryCount ;
14581468 result = prime * result + ((header == null ) ? 0 : header .hashCode ());
14591469 result = prime * result + id ;
1460- result = prime * result + flags ;
1461- result = prime * result + reserved ;
1470+ result = prime * result + res0 ;
1471+ result = prime * result + res1 ;
14621472 return result ;
14631473 }
14641474
@@ -1480,9 +1490,9 @@ public boolean equals(Object obj) {
14801490 return false ;
14811491 if (id != other .id )
14821492 return false ;
1483- if (flags != other .flags )
1493+ if (res0 != other .res0 )
14841494 return false ;
1485- if (reserved != other .reserved )
1495+ if (res1 != other .res1 )
14861496 return false ;
14871497 return true ;
14881498 }
@@ -2137,8 +2147,7 @@ private void readResourceHeader(InputStream stream) throws IOException {
21372147 packageTable .header = nextChunkHeader ;
21382148 offset = parsePackageTable (packageTable , remainingData , offset );
21392149
2140- logger .debug (
2141- String .format ("\t Package %s id=%d name=%s" , packageCtr , packageTable .id , packageTable .name ));
2150+ logger .debug ("\t Package {} id={} name={}" , packageCtr , packageTable .id , packageTable .name );
21422151
21432152 // Record the end of the object to know then to stop looking for
21442153 // internal records
@@ -2272,7 +2281,7 @@ private void readResourceHeader(InputStream stream) throws IOException {
22722281 ResTable_Map map = new ResTable_Map ();
22732282 entryOffset = readComplexValue (map , remainingData , entryOffset );
22742283
2275- final String mapName = map .name + "" ;
2284+ final String mapName = Integer . toString ( map .name ) ;
22762285 AbstractResource value = parseValue (map .value );
22772286
22782287 // If we are dealing with an array, we put it into a special array container
@@ -2438,8 +2447,9 @@ private int readValue(Res_Value val, byte[] remainingData, int offset) throws IO
24382447 return 0 ;
24392448
24402449 val .res0 = readUInt8 (remainingData , offset );
2441- if (val .res0 != 0 )
2442- throw new RuntimeException ("File format error, res0 was not zero" );
2450+ if (val .res0 != 0 ) {
2451+ raiseFormatViolationIssue ("File format violation: res0 is not zero" , offset );
2452+ }
24432453 offset += 1 ;
24442454
24452455 val .dataType = readUInt8 (remainingData , offset );
@@ -2484,18 +2494,35 @@ else if (size == 0x10)
24842494 return entry ;
24852495 }
24862496
2497+ /**
2498+ * Parse data struct <code>ResTable_type</code> as defined in AOSP
2499+ * https://android.googlesource.com/platform/frameworks/base/+/master/libs/androidfw/include/androidfw/ResourceTypes.h
2500+ *
2501+ * Also parses subsequent config table.
2502+ *
2503+ * @param typeTable
2504+ * @param data
2505+ * @param offset
2506+ * @return
2507+ * @throws IOException
2508+ */
24872509 private int readTypeTable (ResTable_Type typeTable , byte [] data , int offset ) throws IOException {
24882510 typeTable .id = readUInt8 (data , offset );
2511+ if (typeTable .id == 0 ) {
2512+ raiseFormatViolationIssue ("File format violation in type table: id is zero" , offset );
2513+ }
24892514 offset += 1 ;
24902515
24912516 typeTable .flags = readUInt8 (data , offset );
2492- if (typeTable .flags != 0 && typeTable .flags != 1 )
2493- throw new RuntimeException ("File format error, flags is not zero or one" );
2517+ if (typeTable .flags != 0 && typeTable .flags != 1 ) {
2518+ raiseFormatViolationIssue ("File format violation in type table: flags is not zero or one" , offset );
2519+ }
24942520 offset += 1 ;
24952521
24962522 typeTable .reserved = readUInt16 (data , offset );
2497- if (typeTable .reserved != 0 )
2498- throw new RuntimeException ("File format error, reserved was not zero" );
2523+ if (typeTable .reserved != 0 ) {
2524+ raiseFormatViolationIssue ("File format violation in type table: reserved is not zero" , offset );
2525+ }
24992526 offset += 2 ;
25002527
25012528 typeTable .entryCount = readUInt32 (data , offset );
@@ -2600,19 +2627,34 @@ private int readConfigTable(ResTable_Config config, byte[] data, int offset) thr
26002627 return offset ;
26012628 }
26022629
2630+ /**
2631+ * Parse data struct <code>ResTable_typeSpec</code> as defined in AOSP
2632+ * https://android.googlesource.com/platform/frameworks/base/+/master/libs/androidfw/include/androidfw/ResourceTypes.h
2633+ *
2634+ * @param typeSpecTable
2635+ * @param data
2636+ * @param offset
2637+ * @return
2638+ * @throws IOException
2639+ */
26032640 private int readTypeSpecTable (ResTable_TypeSpec typeSpecTable , byte [] data , int offset ) throws IOException {
26042641 typeSpecTable .id = readUInt8 (data , offset );
2642+ if (typeSpecTable .id == 0 ) {
2643+ raiseFormatViolationIssue ("File format violation in type spec table: id is zero" , offset );
2644+ }
26052645 offset += 1 ;
26062646
2607- typeSpecTable .flags = readUInt8 (data , offset );
2647+ typeSpecTable .res0 = readUInt8 (data , offset );
2648+ if (typeSpecTable .res0 != 0 ) {
2649+ raiseFormatViolationIssue ("File format violation in type spec table: res0 is not zero" , offset );
2650+ }
26082651 offset += 1 ;
2609- if (typeSpecTable .flags != 0 )
2610- throw new RuntimeException ("File format violation, res0 was not zero" );
26112652
2612- typeSpecTable .reserved = readUInt16 (data , offset );
2653+ typeSpecTable .res1 = readUInt16 (data , offset );
2654+ if (typeSpecTable .res1 != 0 ) {
2655+ raiseFormatViolationIssue ("File format violation in type spec table: res1 is not zero" , offset );
2656+ }
26132657 offset += 2 ;
2614- if (typeSpecTable .reserved != 0 )
2615- throw new RuntimeException ("File format violation, res1 was not zero" );
26162658
26172659 typeSpecTable .entryCount = readUInt32 (data , offset );
26182660 offset += 4 ;
@@ -2674,7 +2716,7 @@ private String readString(byte[] remainingData, int stringIdx) throws IOExceptio
26742716 stringIdx += 2 ;
26752717 byte [] str = new byte [strLen * 2 ];
26762718 System .arraycopy (remainingData , stringIdx , str , 0 , strLen * 2 );
2677- return new String (remainingData , stringIdx , strLen * 2 , "UTF-16LE" );
2719+ return new String (remainingData , stringIdx , strLen * 2 , StandardCharsets . UTF_16LE );
26782720 }
26792721
26802722 private String readStringUTF8 (byte [] remainingData , int stringIdx ) throws IOException {
@@ -2683,7 +2725,7 @@ private String readStringUTF8(byte[] remainingData, int stringIdx) throws IOExce
26832725 // the length here is somehow weird
26842726 int strLen = readUInt8 (remainingData , stringIdx + 1 );
26852727 stringIdx += 2 ;
2686- String str = new String (remainingData , stringIdx , strLen , "UTF-8" );
2728+ String str = new String (remainingData , stringIdx , strLen , StandardCharsets . UTF_8 );
26872729 return str ;
26882730 }
26892731
@@ -2944,4 +2986,10 @@ public void addAll(ARSCFileParser otherParser) {
29442986 stringTable .putAll (otherParser .stringTable );
29452987 }
29462988
2989+ protected void raiseFormatViolationIssue (String message , int offset ) {
2990+ if (STRICT_MODE ) {
2991+ throw new RuntimeException (String .format ("%s offset=0x%x" , message , offset ));
2992+ }
2993+ logger .error ("{} offset=0x{}" , message , Integer .toHexString (offset ));
2994+ }
29472995}
0 commit comments