├── .gitignore ├── .idea ├── compiler.xml ├── gradle.xml ├── kotlinc.xml ├── misc.xml ├── modules.xml ├── uiDesigner.xml └── vcs.xml ├── README.md ├── build.gradle ├── gradle └── wrapper │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── libs ├── kxml2-2.3.0.jar ├── public.xml └── xmlpull_1_1_3_4c.jar ├── settings.gradle ├── src └── main │ └── java │ └── cn │ └── zaratustra │ └── axmlparser │ ├── RunMain.java │ ├── core │ ├── AXMLParser.java │ ├── ValuePool.java │ └── XMLParser.java │ ├── model │ ├── AXMLHeader.java │ ├── Chunk.java │ ├── EndNamespaceChunk.java │ ├── EndTagChunk.java │ ├── ResourceChunk.java │ ├── StartNamespaceChunk.java │ ├── StartTagChunk.java │ ├── StringChunk.java │ └── TextChunk.java │ └── utils │ ├── StringUtils.java │ └── TypedValue.java └── test-data ├── AndroidManifest-normal.xml ├── AndroidManifest.xml ├── AndroidManifest_out.xml ├── abc.apk ├── app-debug.apk └── app-release-normal.apk /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | test-data -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # axml-parser 2 | A tools to parser Android AndroidManifest.xml axml to xml or parser xml to axml 3 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | version '1.0-SNAPSHOT' 2 | 3 | apply plugin: 'java' 4 | 5 | sourceCompatibility = 1.8 6 | 7 | repositories { 8 | flatDir { 9 | dirs 'libs' 10 | } 11 | mavenCentral() 12 | } 13 | 14 | 15 | 16 | dependencies { 17 | testCompile group: 'junit', name: 'junit', version: '4.12' 18 | compile fileTree(dir: 'libs', include: ['*.jar']) 19 | } 20 | 21 | jar { 22 | manifest { 23 | attributes 'Main-Class': 'cn.zaratustra.axmlparser.RunMain' 24 | } 25 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 11 13:57:48 CST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /libs/kxml2-2.3.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaratustraN/axml-parser/fb1501185346b3c8e73baec222055d62452c5fb6/libs/kxml2-2.3.0.jar -------------------------------------------------------------------------------- /libs/xmlpull_1_1_3_4c.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaratustraN/axml-parser/fb1501185346b3c8e73baec222055d62452c5fb6/libs/xmlpull_1_1_3_4c.jar -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'axml-parser' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/cn/zaratustra/axmlparser/RunMain.java: -------------------------------------------------------------------------------- 1 | package cn.zaratustra.axmlparser; 2 | 3 | import cn.zaratustra.axmlparser.core.AXMLParser; 4 | import cn.zaratustra.axmlparser.core.XMLParser; 5 | import cn.zaratustra.axmlparser.utils.StringUtils; 6 | 7 | /** 8 | * Created by zaratustra on 2017/12/11. 9 | */ 10 | public class RunMain { 11 | 12 | public static void main(String[] args) { 13 | String[] args1 = new String[]{ 14 | "", "a2x", "./test-data/AndroidManifest-normal.xml", "./test-data/AndroidManifest_out.xml" 15 | }; 16 | String[] args2 = new String[]{ 17 | "", "x2a", "./test-data/AndroidManifest_out.xml", "./test-data/AndroidManifest.xml" 18 | }; 19 | 20 | Run(args1); 21 | Run(args2); 22 | } 23 | 24 | private static void Run(String[] args) { 25 | if (args.length < 4) { 26 | System.out.println("Please enter correct args"); 27 | System.out.println("Ex: java -jar xxx.jar [a2x|x2a] [inputFile] [outputFile]"); 28 | return; 29 | } 30 | 31 | String operation = args[1]; 32 | String inputFile = args[2]; 33 | String outputFile = args[3]; 34 | 35 | boolean isAXML2XML = false; 36 | 37 | if ("a2x".equals(operation)) { 38 | isAXML2XML = true; 39 | } else if ("x2a".equals(operation)) { 40 | isAXML2XML = false; 41 | } else { 42 | System.out.println("Please enter operation a2x or x2a"); 43 | } 44 | 45 | if (StringUtils.isEmpty(inputFile)) { 46 | System.out.println("Please enter input file"); 47 | return; 48 | } 49 | 50 | if (StringUtils.isEmpty(outputFile)) { 51 | System.out.println("Please enter output file"); 52 | return; 53 | } 54 | 55 | if (isAXML2XML) { 56 | AXMLParser axmlParser = new AXMLParser(); 57 | axmlParser.parseToXML(inputFile, outputFile); 58 | } else { 59 | XMLParser xmlParser = new XMLParser(); 60 | xmlParser.parseAXML(inputFile, outputFile); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/cn/zaratustra/axmlparser/core/AXMLParser.java: -------------------------------------------------------------------------------- 1 | package cn.zaratustra.axmlparser.core; 2 | 3 | import cn.zaratustra.axmlparser.model.*; 4 | import com.sun.org.apache.xml.internal.serialize.OutputFormat; 5 | import com.sun.org.apache.xml.internal.serialize.XMLSerializer; 6 | import org.w3c.dom.Document; 7 | import org.w3c.dom.Text; 8 | import org.xml.sax.InputSource; 9 | import org.xml.sax.SAXException; 10 | 11 | import javax.xml.parsers.DocumentBuilder; 12 | import javax.xml.parsers.DocumentBuilderFactory; 13 | import javax.xml.parsers.ParserConfigurationException; 14 | import java.io.*; 15 | import java.nio.ByteBuffer; 16 | import java.nio.ByteOrder; 17 | import java.util.ArrayList; 18 | 19 | 20 | /** 21 | * Created by zaratustra on 2017/12/12. 22 | */ 23 | public class AXMLParser { 24 | 25 | private ArrayList mChunkList; 26 | 27 | public AXMLParser() { 28 | mChunkList = new ArrayList<>(); 29 | } 30 | 31 | public void parseToXML(String inputFile, String outputFile) { 32 | try { 33 | byte[] byteData = readFileData(inputFile); 34 | 35 | ByteBuffer byteBuffer = ByteBuffer.wrap(byteData); 36 | //解析头部 37 | AXMLHeader axmlHeader = new AXMLHeader(byteData); 38 | byteBuffer.position(axmlHeader.getHeaderLength()); 39 | 40 | ValuePool valuePool = new ValuePool(); 41 | //读取Chunk 42 | while (byteBuffer.position() < byteData.length) { 43 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 44 | int chunkType = byteBuffer.getInt(); 45 | byteBuffer.position(byteBuffer.position() - 4); 46 | Chunk chunk = null; 47 | switch (chunkType) { 48 | case StringChunk.CHUNK_TYPE: 49 | chunk = new StringChunk(); 50 | break; 51 | case ResourceChunk.CHUNK_TYPE: 52 | chunk = new ResourceChunk(); 53 | break; 54 | case StartNamespaceChunk.CHUNK_TYPE: 55 | chunk = new StartNamespaceChunk(); 56 | break; 57 | case StartTagChunk.CHUNK_TYPE: 58 | chunk = new StartTagChunk(); 59 | break; 60 | case EndTagChunk.CHUNK_TYPE: 61 | chunk = new EndTagChunk(); 62 | break; 63 | case EndNamespaceChunk.CHUNK_TYPE: 64 | chunk = new EndNamespaceChunk(); 65 | break; 66 | case TextChunk.CHUNK_TYPE: 67 | chunk = new TextChunk(); 68 | break; 69 | default: 70 | byteBuffer.position(byteData.length); 71 | break; 72 | } 73 | if (chunk != null) { 74 | chunk.parseFromAXML(valuePool, byteData, byteBuffer.position()); 75 | byteBuffer.position(byteBuffer.position() + chunk.getChunkSize()); 76 | System.out.println(chunk.toString()); 77 | mChunkList.add(chunk); 78 | } 79 | } 80 | 81 | StringBuilder stringBuilder = new StringBuilder(); 82 | 83 | for (int i = 0; i < mChunkList.size(); i++) { 84 | Chunk chunk = mChunkList.get(i); 85 | if (chunk instanceof EndNamespaceChunk || chunk instanceof StartNamespaceChunk 86 | || chunk instanceof StartTagChunk || chunk instanceof EndTagChunk || chunk instanceof TextChunk) { 87 | stringBuilder.append(chunk.genXML()); 88 | } 89 | } 90 | writeToFile(formatXML(stringBuilder.toString()), outputFile); 91 | 92 | } catch (Exception e) { 93 | e.printStackTrace(); 94 | return; 95 | } 96 | } 97 | 98 | private String formatXML(String in) { 99 | return in; 100 | // try { 101 | // DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 102 | // 103 | // DocumentBuilder db = dbf.newDocumentBuilder(); 104 | // InputSource is = new InputSource(new StringReader(in)); 105 | // Document document = db.parse(is); 106 | // OutputFormat format = new OutputFormat(document); 107 | // 108 | // format.setIndenting(true); 109 | // format.setStandalone(true); 110 | // format.setLineWidth(30); 111 | // format.setAllowJavaNames(); 112 | // format.setEncoding("utf-8"); 113 | // format.setOmitComments(true); 114 | // format.setOmitDocumentType(true); 115 | // format.setOmitXMLDeclaration(false); 116 | // 117 | // Writer out = new StringWriter(); 118 | // XMLSerializer serializer = new XMLSerializer(out, format); 119 | // serializer.serialize(document); 120 | // return out.toString(); 121 | // } catch (Exception e) { 122 | // return in; 123 | // } 124 | } 125 | 126 | private void writeToFile(String s, String outputFile) throws IOException { 127 | FileOutputStream fileOutputStream = new FileOutputStream(new File(outputFile)); 128 | ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(s.getBytes()); 129 | 130 | byte[] buffer = new byte[1024]; 131 | int length; 132 | while ((length = byteArrayInputStream.read(buffer)) != -1) { 133 | fileOutputStream.write(buffer, 0, length); 134 | } 135 | } 136 | 137 | private byte[] readFileData(String inputFile) throws IOException { 138 | FileInputStream fileInputStream = new FileInputStream(new File(inputFile)); 139 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 140 | byte[] buffer = new byte[1024]; 141 | int length; 142 | while ((length = fileInputStream.read(buffer)) != -1) { 143 | byteArrayOutputStream.write(buffer, 0, length); 144 | } 145 | return byteArrayOutputStream.toByteArray(); 146 | } 147 | 148 | 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/cn/zaratustra/axmlparser/core/ValuePool.java: -------------------------------------------------------------------------------- 1 | package cn.zaratustra.axmlparser.core; 2 | 3 | import cn.zaratustra.axmlparser.utils.StringUtils; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Collections; 7 | import java.util.HashMap; 8 | import java.util.Stack; 9 | 10 | /** 11 | * Created by zaratustra on 2017/12/12. 12 | */ 13 | public class ValuePool { 14 | private ArrayList mStringPools; 15 | private ArrayList mResourceIds; 16 | private HashMap mUriToNameSpaceMaps; 17 | private HashMap mNamespaceToUriMaps; 18 | 19 | private Stack mIntegers; 20 | private boolean isUTF8 = false; 21 | 22 | public boolean isUTF8() { 23 | return isUTF8; 24 | } 25 | 26 | public void setUTF8(boolean UTF8) { 27 | isUTF8 = UTF8; 28 | } 29 | 30 | // public String[] mStringCache = {"theme", 31 | // "label", 32 | // "icon", 33 | // "name", 34 | // "value", 35 | // "minSdkVersion", 36 | // "versionCode", 37 | // "versionName", 38 | // "targetSdkVersion", 39 | // "allowBackup", 40 | // "supportsRtl", 41 | // "roundIcon", 42 | // "1.0", 43 | // "26.1.0", 44 | // "27.0.0-SNAPSHOT", 45 | // "action", 46 | // "activity", 47 | // "android", 48 | // "android.arch.lifecycle.VERSION", 49 | // "android.intent.action.MAIN", 50 | // "android.intent.category.LAUNCHER", 51 | // "android.permission.INTERNET", 52 | // "android.permission.READ_EXTERNAL_STORAGE", 53 | // "android.permission.WRITE_EXTERNAL_STORAGE", 54 | // "android.support.VERSION", 55 | // "application", 56 | // "category", 57 | // "cn.zarathustra.net", 58 | // "cn.zarathustra.net.MainActivity", 59 | // "http://schemas.android.com/apk/res/android", 60 | // "intent-filter", 61 | // "manifest", 62 | // "meta-data", 63 | // "package", 64 | // "uses-permission", 65 | // "uses-sdk" 66 | // }; 67 | public String[] mStringCache = {"versionCode", 68 | "versionName", 69 | "minSdkVersion", 70 | "targetSdkVersion", 71 | "name", 72 | "allowBackup", 73 | "icon", 74 | "label", 75 | "largeHeap", 76 | "supportsRtl", 77 | "theme", 78 | "screenOrientation", 79 | "windowSoftInputMode", 80 | "configChanges", 81 | "launchMode", 82 | "exported", 83 | "hardwareAccelerated", 84 | "scheme", 85 | "enabled", 86 | "permission", 87 | "resource", 88 | "value", 89 | "process", 90 | "noHistory", 91 | "priority", 92 | "persistent", 93 | "authorities", 94 | "android", 95 | "http://schemas.android.com/apk/res/android", 96 | "", 97 | "package", 98 | "platformBuildVersionCode", 99 | "platformBuildVersionName", 100 | "manifest", 101 | "com.flamingo.gpgame", 102 | "2.4.1", 103 | "25", 104 | "7.1.1", 105 | "uses-sdk", 106 | "uses-permission", 107 | "android.permission.WRITE_EXTERNAL_STORAGE", 108 | "android.permission.READ_EXTERNAL_STORAGE", 109 | "android.permission.MOUNT_UNMOUNT_FILESYSTEMS", 110 | "android.permission.ACCESS_NETWORK_STATE", 111 | "android.permission.READ_PHONE_STATE", 112 | "android.permission.ACCESS_WIFI_STATE", 113 | "android.permission.BLUETOOTH", 114 | "android.permission.INTERNET", 115 | "android.permission.ACCESS_FINE_LOCATION", 116 | "android.permission.RECEIVE_SMS", 117 | "android.permission.READ_SMS", 118 | "android.permission.READ_LOGS", 119 | "android.permission.RECEIVE_BOOT_COMPLETED", 120 | "android.permission.RESTART_PACKAGES", 121 | "android.permission.BROADCAST_STICKY", 122 | "android.permission.WRITE_SETTINGS", 123 | "android.permission.RECEIVE_USER_PRESENT", 124 | "android.permission.WAKE_LOCK", 125 | "android.permission.KILL_BACKGROUND_PROCESSES", 126 | "android.permission.GET_TASKS", 127 | "android.permission.VIBRATE", 128 | "android.permission.ACCESS_COARSE_LOCATION", 129 | "android.permission.CAMERA", 130 | "android.webkit.permission.PLUGIN", 131 | "android.permission.CHANGE_NETWORK_STATE", 132 | "android.hardware.camera.autofocus", 133 | "uses-feature", 134 | "android.hardware.camera", 135 | "android.permission.CHANGE_WIFI_STATE", 136 | "android.permission.RECORD_AUDIO", 137 | "com.google.android.gms.permission.ACTIVITY_RECOGNITION", 138 | "android.permission.READ_CALENDAR", 139 | "android.permission.WRITE_CALENDAR", 140 | "com.android.launcher.permission.INSTALL_SHORTCUT", 141 | "android.permission.BATTERY_STATS", 142 | "application", 143 | "com.flamingo.gpgame.config.GPGameApplication", 144 | "activity", 145 | "com.flamingo.gpgame.view.activity.SplashActivity", 146 | "intent-filter", 147 | "action", 148 | "android.intent.action.MAIN", 149 | "category", 150 | "android.intent.category.LAUNCHER", 151 | "com.flamingo.gpgame.view.activity.GPMainActivity", 152 | "com.flamingo.gpgame.view.activity.GPDialogActivity", 153 | "receiver", 154 | "com.flamingo.gpgame.service.GPDownloadNotifyManager$NotificationClickReceiver", 155 | "com.flamingo.gpgame.module.gpgroup.view.activity.GPDialogReportActivity", 156 | "com.flamingo.gpgame.view.dialog.DownloadErrorSpaceDialog", 157 | "com.flamingo.gpgame.view.dialog.NoInstallTaskActivity", 158 | "com.flamingo.gpgame.view.dialog.InstallErrorSpaceDialog", 159 | "com.flamingo.gpgame.view.activity.GPUpdateDialogActivity", 160 | "com.flamingo.gpgame.view.activity.GPTestActivity", 161 | "com.flamingo.gpgame.view.activity.GPTestDownloadActivity", 162 | "com.flamingo.gpgame.view.activity.HotOrNewCrackGameListActivity", 163 | "com.flamingo.gpgame.view.activity.ModuleGameListActivity", 164 | "com.flamingo.gpgame.view.activity.TagGameListActivity", 165 | "com.flamingo.gpgame.view.activity.AGameGiftListActivity", 166 | "com.flamingo.gpgame.view.activity.NotificationDetailActivity", 167 | "com.flamingo.gpgame.view.activity.GiftDetailActivity", 168 | "com.flamingo.gpgame.view.activity.MyGiftActivity", 169 | "com.flamingo.gpgame.view.activity.MyBillActivity", 170 | "com.flamingo.gpgame.view.activity.MyLevelActivity", 171 | "com.flamingo.gpgame.module.my.honey.view.activity.MyHoneyActivity", 172 | "com.flamingo.gpgame.view.activity.TaEventActivity", 173 | "com.flamingo.gpgame.view.activity.TaVideoActivity", 174 | "com.flamingo.gpgame.view.activity.CardRecordsActivity", 175 | "com.flamingo.gpgame.view.activity.MedalListActivity", 176 | "com.flamingo.gpgame.view.activity.MyVoucherActivity", 177 | "com.flamingo.gpgame.view.activity.VoucherDetailActivity", 178 | "com.flamingo.gpgame.view.activity.NotificationActivity", 179 | "com.flamingo.gpgame.view.activity.MoreGiftListActivity", 180 | "com.flamingo.gpgame.view.activity.MoreHotGamesWithGiftActivity", 181 | "com.flamingo.gpgame.view.activity.GiftCenterActivity", 182 | "com.flamingo.gpgame.view.activity.DownloadMainActivity", 183 | "com.flamingo.gpgame.module.account.view.activity.LoginActivity", 184 | "com.flamingo.gpgame.module.account.view.activity.PhoneNumRegister_ForgetPassword_Activity", 185 | "com.flamingo.gpgame.module.account.view.activity.PhoneRegisterSetPassword_ResetPasswordByMobile_Activity", 186 | "com.flamingo.gpgame.module.account.view.activity.GuopanNameRegister_ResetPasswordByOldPassword_Activity", 187 | "com.flamingo.gpgame.module.account.view.activity.GPPhoneBindActivity", 188 | "com.flamingo.gpgame.module.account.view.activity.MyInfoActivity", 189 | "com.flamingo.gpgame.module.account.view.activity.SetUserInfoActivity", 190 | "com.flamingo.gpgame.view.activity.MyHomeActivity", 191 | "com.flamingo.gpgame.view.activity.GPMyActivity", 192 | "com.flamingo.gpgame.view.activity.MyPostActivity", 193 | "com.flamingo.gpgame.view.activity.MyFavouriteActivity", 194 | "com.flamingo.gpgame.view.activity.MyDraftActivity", 195 | "com.flamingo.gpgame.view.activity.FollowOrFanActivity", 196 | "com.flamingo.gpgame.view.activity.PostZanListActivity", 197 | "com.flamingo.gpgame.view.activity.TaHomeActivity", 198 | "com.flamingo.gpgame.module.account.view.activity.MyDetailSetSignatureActivity", 199 | "com.flamingo.gpgame.view.activity.PicChooseActivity", 200 | "com.flamingo.gpgame.view.activity.GPPicChooseDialogActivity", 201 | "com.flamingo.gpgame.module.detail.DetailActivity", 202 | "com.flamingo.gpgame.view.activity.GuideWindowActivity", 203 | "com.flamingo.gpgame.module.open.view.activity.OpenTestServerActivity", 204 | "com.flamingo.gpgame.module.subject.view.activity.SubjectListActivity", 205 | "com.flamingo.gpgame.view.activity.PicClipActivity", 206 | "com.flamingo.gpgame.view.activity.GPPicChooseMiddleEmptyActivity", 207 | "com.flamingo.gpgame.module.account.view.activity.LoginEmptyActivity", 208 | "com.flamingo.gpgame.module.market.view.activity.GPGameBuyEmptyActivity", 209 | "com.flamingo.gpgame.view.activity.SimpleWebViewActivity", 210 | "com.flamingo.gpgame.view.activity.SignUpWebViewActivity", 211 | "com.flamingo.gpgame.view.activity.ActionActivity", 212 | "com.flamingo.gpgame.view.activity.GameAndWebActivity", 213 | "com.flamingo.gpgame.view.activity.GPSearchMainActivity", 214 | "com.flamingo.gpgame.view.activity.CommentListActivity", 215 | "com.flamingo.gpgame.view.activity.GPSettingActivity", 216 | "com.flamingo.gpgame.view.activity.GPFeedbackSuggestActivity", 217 | "com.flamingo.gpgame.view.activity.GPAboutUsActivity", 218 | "com.flamingo.gpgame.view.activity.LargeViewActivity", 219 | "com.flamingo.gpgame.view.activity.GPMainViewRecommendActivity", 220 | "com.flamingo.gpgame.view.activity.MyHeadImageActivity", 221 | "com.flamingo.gpgame.module.my.message.view.MyMessageActivity", 222 | "com.flamingo.gpgame.module.my.message.view.ChatListActivity", 223 | "com.flamingo.gpgame.module.my.message.view.BlockListActivity", 224 | "com.flamingo.gpgame.module.my.games.view.MyGamesListActivity", 225 | "com.flamingo.gpgame.module.gpgroup.view.activity.GroupDetailActivity", 226 | "com.flamingo.gpgame.module.gpgroup.view.activity.GroupPostReportActivity", 227 | "com.flamingo.gpgame.module.gpgroup.view.activity.PostDetailActivity", 228 | "com.flamingo.gpgame.module.gpgroup.view.activity.GroupListActivity", 229 | "com.flamingo.gpgame.module.gpgroup.view.activity.MyGroupListActivity", 230 | "com.flamingo.gpgame.module.gpgroup.view.activity.PostCommentDetailActivity", 231 | "com.flamingo.gpgame.module.market.view.activity.VoucherMarketActivity", 232 | "com.flamingo.gpgame.module.market.view.activity.VIPMarketActivity", 233 | "com.flamingo.gpgame.module.market.view.activity.VIPMarketWebActivity", 234 | "com.flamingo.gpgame.module.market.view.activity.HoneyMarketActivity", 235 | "com.flamingo.gpgame.module.market.view.activity.HoneyExchangeRecordsActivity", 236 | "com.flamingo.gpgame.module.market.view.activity.HoneyMarketAllActivity", 237 | "com.flamingo.gpgame.module.market.view.activity.GoodsDetailActivity", 238 | "com.flamingo.gpgame.module.market.view.activity.ModuleVoucherListActivity", 239 | "com.flamingo.gpgame.module.market.view.activity.MyVoucherOrderActivity", 240 | "com.flamingo.gpgame.module.market.view.activity.MyVoucherOrderDetailActivity", 241 | "com.flamingo.gpgame.module.gpgroup.view.activity.GroupActivityActivity", 242 | "com.flamingo.gpgame.module.gpgroup.view.activity.GroupVideoActivity", 243 | "com.flamingo.gpgame.module.gpgroup.view.activity.GroupAllGameVideoActivity", 244 | "com.flamingo.gpgame.module.gpgroup.view.activity.GroupUserVideoAndHotPostActivity", 245 | "com.flamingo.gpgame.module.gpgroup.view.activity.TrashPostActivity", 246 | "com.flamingo.gpgame.module.gpgroup.view.activity.PostPublishActivity", 247 | "com.flamingo.gpgame.view.activity.PlayVideoActivity", 248 | "com.flamingo.gpgame.view.activity.GPGameConnectActivity", 249 | "android.intent.action.VIEW", 250 | "android.intent.category.DEFAULT", 251 | "android.intent.category.BROWSABLE", 252 | "data", 253 | "gpgame", 254 | "com.flamingo.gpgame.view.activity.GPH5GameActivity", 255 | "com.flamingo.gpgame.module.rank.view.activity.GPGameRankActivity", 256 | "com.flamingo.gpgame.utils.permission.PermissionRequestActivity", 257 | "com.flamingo.gpgame.utils.permission.PermissionSettingActivity", 258 | "com.flamingo.gpgame.utils.permission.PermissionDialogActivity", 259 | "com.flamingo.gpgame.module.gpgroup.view.activity.GroupRecommendActivity", 260 | "com.flamingo.gpgame.module.game.view.CrackGameActivity", 261 | "com.flamingo.gpgame.module.game.view.RankGameListActivity", 262 | "com.flamingo.gpgame.module.my.honey.view.activity.BindSettingActivity", 263 | "com.flamingo.gpgame.module.task.view.activity.TaskExperienceActivity", 264 | "com.flamingo.gpgame.module.task.view.activity.TaskJuniorMissionActivity", 265 | "com.flamingo.gpgame.module.task.view.activity.TaskGameTrialActivity", 266 | "com.flamingo.gpgame.module.task.view.activity.TaskMakeMoneyActivity", 267 | "com.flamingo.gpgame.module.task.view.activity.TaskMyTaskActivity", 268 | "com.flamingo.gpgame.module.bind.BindWeChatActivity", 269 | "com.flamingo.gpgame.receiver.WifiDownloadReceiver", 270 | "com.flamingo.gpgame.receiver.NetworkChangeReceiver", 271 | "android.intent.action.BOOT_COMPLETED", 272 | "android.net.conn.CONNECTIVITY_CHANGE", 273 | "com.flamingo.gpgame.receiver.PackageInstallReceiver", 274 | "android.intent.action.PACKAGE_ADDED", 275 | "android.intent.action.PACKAGE_REPLACED", 276 | "android.intent.action.PACKAGE_REMOVED", 277 | "service", 278 | "com.flamingo.gpgame.service.GPGameAccessibilityService", 279 | "android.permission.BIND_ACCESSIBILITY_SERVICE", 280 | "android.accessibilityservice.AccessibilityService", 281 | "meta-data", 282 | "android.accessibilityservice", 283 | "com.flamingo.gpgame.service.GPDownloadService", 284 | "com.flamingo.gpgame.engine.image.glide.GPGlideModule", 285 | "GlideModule", 286 | "com.alipay.sdk.app.H5PayActivity", 287 | "com.alipay.sdk.auth.AuthActivity", 288 | "com.flamingo.gpgame.module.pay.ui.GPMainActivity", 289 | "com.yintong.secure.activity.BaseActivity", 290 | "com.yintong.secure.service.PayService", 291 | "com.payeco.android.plugin.PayecoPluginLoadingActivity", 292 | "com.heepay.plugin.activity.WeChatNotityActivity", 293 | "com.payeco.android.plugin.PayecoCameraActivity", 294 | "com.payeco.android.plugin.PayecoVedioActivity", 295 | "com.payeco.android.plugin.vedio", 296 | "com.baidu.location.f", 297 | ":remote", 298 | "com.baidu.location.service_v2.2", 299 | "com.baidu.lbsapi.API_KEY", 300 | "DhNxS47YIr3CNdocGoAcy1ts", 301 | "com.ipaynow.wechatpay.plugin.inner_plugin.wechat_plugin.activity.WeChatNotifyActivity", 302 | "com.flamingo.gpgame.wxapi.WXEntryActivity", 303 | "com.flamingo.gpgame.utils.share.sina.SinaShareCallBackActivity", 304 | "com.tencent.tauth.AuthActivity", 305 | "tencent1105114001", 306 | "com.tencent.connect.common.AssistActivity", 307 | "com.flamingo.gpgame.utils.share.qq.QQShareCallBackActivity", 308 | "com.flamingo.gpgame.utils.share.system.SystemShareCallBackActivity", 309 | "org.egret.egretruntimelauncher.webview.WebViewActivity", 310 | "org.egret.egretruntimelauncher.GamePlayActivity", 311 | "com.flamingo.gpgame.module.account.view.activity.GPSetAccountActivity", 312 | "com.flamingo.gpgame.module.account.view.activity.GPRealNameVerifiedActivity", 313 | "com.flamingo.gpgame.module.account.view.activity.GPRealNameVerifiedDetailActivity", 314 | "com.flamingo.gpgame.module.account.view.activity.GPIdVerifiedActivity", 315 | "com.flamingo.gpgame.module.task.view.activity.TaskChargeActivity", 316 | "com.flamingo.gpgame.module.subject.view.activity.SubjectGiftDetailActivity", 317 | "com.flamingo.gpgame.module.subject.view.activity.SubjectGameDetailActivity", 318 | "com.flamingo.gpgame.module.reservation.view.activity.AllReservationGameList", 319 | "com.flamingo.gpgame.module.reservation.view.activity.MyReservationGameList", 320 | "com.devbrackets.android.exomedia.receiver.MediaControlsReceiver", 321 | "android.intent.action.MEDIA_BUTTON", 322 | "com.sina.weibo.sdk.web.WeiboSdkWebActivity", 323 | "com.sina.weibo.sdk.share.WbShareTransActivity", 324 | "com.sina.weibo.sdk.action.ACTION_SDK_REQ_ACTIVITY", 325 | "com.tencent.android.tpush.XGPushActivity", 326 | "android.intent.action", 327 | "com.tencent.android.tpush.XGPushReceiver", 328 | ":xg_service_v3", 329 | "com.tencent.android.tpush.action.SDK", 330 | "com.tencent.android.tpush.action.INTERNAL_PUSH_MESSAGE", 331 | "android.intent.action.USER_PRESENT", 332 | "android.bluetooth.adapter.action.STATE_CHANGED", 333 | "android.intent.action.ACTION_POWER_CONNECTED", 334 | "android.intent.action.ACTION_POWER_DISCONNECTED", 335 | "android.intent.action.MEDIA_UNMOUNTED", 336 | "android.intent.action.MEDIA_REMOVED", 337 | "android.intent.action.MEDIA_CHECKING", 338 | "android.intent.action.MEDIA_EJECT", 339 | "file", 340 | "com.tencent.android.tpush.rpc.XGRemoteService", 341 | "com.flamingo.gpgame.PUSH_ACTION", 342 | "com.tencent.android.tpush.service.XGPushServiceV3", 343 | "com.tencent.android.tpush.service.XGDaemonService", 344 | "provider", 345 | "com.tencent.android.tpush.XGPushProvider", 346 | "com.flamingo.gpgame.AUTH_XGPUSH", 347 | "com.tencent.android.tpush.SettingsContentProvider", 348 | "com.flamingo.gpgame.TPUSH_PROVIDER", 349 | "XG_V2_ACCESS_ID", 350 | "XG_V2_ACCESS_KEY", 351 | "AS1GX77R9W9X", 352 | "com.tencent.mid.api.MidProvider", 353 | "com.flamingo.gpgame.TENCENT.MID.V3" 354 | }; 355 | 356 | public ValuePool() { 357 | mStringPools = new ArrayList<>(); 358 | // for (String str : mStringCache) { 359 | // mStringPools.add(str); 360 | // } 361 | 362 | mResourceIds = new ArrayList<>(); 363 | mUriToNameSpaceMaps = new HashMap<>(); 364 | mNamespaceToUriMaps = new HashMap<>(); 365 | mIntegers = new Stack<>(); 366 | } 367 | 368 | public void putInteger(Integer integer) { 369 | mIntegers.push(integer); 370 | } 371 | 372 | public Integer pullInteger() { 373 | return mIntegers.pop(); 374 | } 375 | 376 | public int getStringIndex(String string) { 377 | return mStringPools.indexOf(string); 378 | } 379 | 380 | public void addString(String string) { 381 | if (!mStringPools.contains(string)) { 382 | mStringPools.add(string); 383 | } 384 | } 385 | 386 | public String getString(int index) { 387 | if (index < 0 || index >= mStringPools.size()) { 388 | if (index == -1) { 389 | return ""; 390 | } 391 | return "out of string bounds: " + index; 392 | } 393 | return mStringPools.get(index); 394 | } 395 | 396 | public int getStringSize() { 397 | return mStringPools.size(); 398 | } 399 | 400 | public void addResourceId(int id) { 401 | mResourceIds.add(id); 402 | } 403 | 404 | public int getResourceId(int index) { 405 | return mResourceIds.get(index); 406 | } 407 | 408 | public int getResourceIdSize() { 409 | return mResourceIds.size(); 410 | } 411 | 412 | 413 | public ArrayList getResourceIds() { 414 | return mResourceIds; 415 | } 416 | 417 | public void putUriToNamespace(String uri, String nameSpace) { 418 | mUriToNameSpaceMaps.put(uri, nameSpace); 419 | } 420 | 421 | public void putNamespaceToUri(String uri, String nameSpace) { 422 | mNamespaceToUriMaps.put(nameSpace, uri); 423 | } 424 | 425 | public int getUri(String namespace) { 426 | return checkAndAddString(mNamespaceToUriMaps.get(namespace)); 427 | } 428 | 429 | public String getNamespace(String uri) { 430 | return mUriToNameSpaceMaps.get(uri); 431 | } 432 | 433 | public HashMap getUriToNameSpaceMaps() { 434 | return mUriToNameSpaceMaps; 435 | } 436 | 437 | public int checkAndAddString(String string) { 438 | if (string == null) { 439 | return -1; 440 | } 441 | int index = mStringPools.indexOf(string); 442 | if (index >= 0) { 443 | return index; 444 | } else { 445 | mStringPools.add(string); 446 | return mStringPools.size() - 1; 447 | } 448 | } 449 | 450 | } 451 | -------------------------------------------------------------------------------- /src/main/java/cn/zaratustra/axmlparser/core/XMLParser.java: -------------------------------------------------------------------------------- 1 | package cn.zaratustra.axmlparser.core; 2 | 3 | import cn.zaratustra.axmlparser.model.*; 4 | import cn.zaratustra.axmlparser.utils.TypedValue; 5 | import org.w3c.dom.Document; 6 | import org.w3c.dom.NamedNodeMap; 7 | import org.w3c.dom.Node; 8 | import org.w3c.dom.NodeList; 9 | import org.xml.sax.InputSource; 10 | import org.xmlpull.v1.XmlPullParser; 11 | import org.xmlpull.v1.XmlPullParserException; 12 | import org.xmlpull.v1.XmlPullParserFactory; 13 | 14 | import javax.xml.parsers.DocumentBuilder; 15 | import javax.xml.parsers.DocumentBuilderFactory; 16 | import java.io.*; 17 | import java.nio.ByteBuffer; 18 | import java.util.ArrayList; 19 | import java.util.HashMap; 20 | 21 | import static cn.zaratustra.axmlparser.utils.TypedValue.TYPE_INT_DEC; 22 | import static cn.zaratustra.axmlparser.utils.TypedValue.TYPE_STRING; 23 | 24 | /** 25 | * Created by zaratustra on 2017/12/12. 26 | */ 27 | public class XMLParser { 28 | 29 | private ArrayList mChunkList; 30 | private HashMap mPublicNameToResId; 31 | public static final String PUBLIC_XML = "./libs/public.xml"; 32 | 33 | public XMLParser() { 34 | mChunkList = new ArrayList<>(); 35 | mPublicNameToResId = new HashMap<>(); 36 | try { 37 | initPublicNameToResId(); 38 | } catch (Exception e) { 39 | e.printStackTrace(); 40 | } 41 | } 42 | 43 | private void initPublicNameToResId() throws Exception { 44 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 45 | 46 | DocumentBuilder db = dbf.newDocumentBuilder(); 47 | InputSource is = new InputSource(new InputStreamReader(new FileInputStream(PUBLIC_XML))); 48 | Document document = db.parse(is); 49 | NodeList nodeList = document.getElementsByTagName("public"); 50 | for (int i = 0; i < nodeList.getLength(); i++) { 51 | try { 52 | Node node = nodeList.item(i); 53 | NamedNodeMap attribute = node.getAttributes(); 54 | if (attribute.getNamedItem("type").getNodeValue().equals("attr")) { 55 | mPublicNameToResId.put(attribute.getNamedItem("name").getNodeValue(), 56 | Integer.decode(attribute.getNamedItem("id").getNodeValue())); 57 | } 58 | } catch (Exception e) { 59 | e.printStackTrace(); 60 | } 61 | } 62 | } 63 | 64 | public void parseAXML(String inputFile, String outputFile) { 65 | try { 66 | ValuePool valuePool = new ValuePool(); 67 | initValuePullString(inputFile, valuePool); 68 | XmlPullParserFactory pullParserFactory = XmlPullParserFactory.newInstance(); 69 | XmlPullParser pullParser = pullParserFactory.newPullParser(); 70 | pullParser.setInput(new FileInputStream(new File(inputFile)), "utf-8"); 71 | int event = pullParser.getEventType(); 72 | 73 | int lineIndex = 2; 74 | valuePool.putUriToNamespace("http://schemas.android.com/apk/res/android", "android"); 75 | valuePool.putNamespaceToUri("http://schemas.android.com/apk/res/android", "android"); 76 | StartNamespaceChunk startNamespaceChunk = new StartNamespaceChunk(); 77 | startNamespaceChunk.setChunkType(StartNamespaceChunk.CHUNK_TYPE); 78 | startNamespaceChunk.setLineNumber(lineIndex++); 79 | valuePool.putInteger(startNamespaceChunk.getLineNumber()); 80 | startNamespaceChunk.setPrefix(valuePool.checkAndAddString("android")); 81 | startNamespaceChunk.setUri(valuePool.checkAndAddString("http://schemas.android.com/apk/res/android")); 82 | startNamespaceChunk.setValuePool(valuePool); 83 | startNamespaceChunk.setChunkSize(4 * 6); 84 | 85 | mChunkList.add(startNamespaceChunk); 86 | 87 | while (event != XmlPullParser.END_DOCUMENT) { 88 | switch (event) { 89 | case XmlPullParser.START_TAG: 90 | lineIndex = parseStartTagChunk(pullParser, lineIndex, valuePool); 91 | break; 92 | case XmlPullParser.END_TAG: 93 | lineIndex = parseEndTagChunk(pullParser, lineIndex, valuePool); 94 | break; 95 | } 96 | try { 97 | event = pullParser.next(); 98 | } catch (Exception e) { 99 | e.printStackTrace(); 100 | } 101 | } 102 | 103 | addStringChunkAndResourceChunk(valuePool); 104 | 105 | EndNamespaceChunk endNamespaceChunk = new EndNamespaceChunk(); 106 | endNamespaceChunk.setChunkType(EndNamespaceChunk.CHUNK_TYPE); 107 | 108 | if (valuePool.isUTF8()) { 109 | endNamespaceChunk.setLineNumber(valuePool.pullInteger()); 110 | } else { 111 | endNamespaceChunk.setLineNumber(lineIndex++); 112 | } 113 | 114 | endNamespaceChunk.setPrefix(valuePool.checkAndAddString("android")); 115 | endNamespaceChunk.setUri(valuePool.checkAndAddString("http://schemas.android.com/apk/res/android")); 116 | endNamespaceChunk.setValuePool(valuePool); 117 | endNamespaceChunk.setChunkSize(4 * 6); 118 | mChunkList.add(endNamespaceChunk); 119 | 120 | AXMLHeader axmlHeader = new AXMLHeader(); 121 | axmlHeader.setMagicNumber(AXMLHeader.MAGIC_NUMBER); 122 | axmlHeader.setHeaderLength(8); 123 | int totalSize = 0; 124 | totalSize += 8; 125 | for (int i = 0; i < mChunkList.size(); i++) { 126 | totalSize += mChunkList.get(i).getChunkSize(); 127 | } 128 | axmlHeader.setFileSize(totalSize); 129 | 130 | System.out.println(axmlHeader.toString()); 131 | 132 | ByteBuffer byteBuffer = ByteBuffer.allocate(axmlHeader.getFileSize()); 133 | byteBuffer.put(axmlHeader.toBytes()); 134 | 135 | for (int i = 0; i < mChunkList.size(); i++) { 136 | byteBuffer.put(mChunkList.get(i).toByte()); 137 | System.out.println(mChunkList.get(i).toString()); 138 | } 139 | 140 | writeToFile(byteBuffer.array(), outputFile); 141 | } catch (XmlPullParserException e) { 142 | e.printStackTrace(); 143 | } catch (FileNotFoundException e) { 144 | e.printStackTrace(); 145 | } catch (IOException e) { 146 | e.printStackTrace(); 147 | } catch (Exception e) { 148 | e.printStackTrace(); 149 | } 150 | } 151 | 152 | private void initValuePullString(String inputFile, ValuePool valuePool) throws Exception { 153 | 154 | valuePool.setUTF8(readFile(new File(inputFile), "utf-8").contains("standalone=\"no\"")); 155 | 156 | XmlPullParserFactory pullParserFactory = XmlPullParserFactory.newInstance(); 157 | XmlPullParser pullParser = pullParserFactory.newPullParser(); 158 | pullParser.setInput(new FileInputStream(new File(inputFile)), "utf-8"); 159 | int event = pullParser.getEventType(); 160 | while (event != XmlPullParser.END_DOCUMENT) { 161 | switch (event) { 162 | case XmlPullParser.START_TAG: 163 | int attributeCount = pullParser.getAttributeCount(); 164 | for (int i = 0; i < attributeCount; i++) { 165 | if (pullParser.getAttributeName(i).equals("xmlns:android")) { 166 | continue; 167 | } 168 | String namespaceAndName = pullParser.getAttributeName(i); 169 | String[] splitStr = namespaceAndName.split(":"); 170 | if (splitStr.length >= 2) { 171 | valuePool.checkAndAddString(splitStr[1]); 172 | if (splitStr[0].equals("android") && !valuePool.getResourceIds().contains(mPublicNameToResId.get(splitStr[1]))) { 173 | valuePool.addResourceId(mPublicNameToResId.get(splitStr[1])); 174 | } 175 | } 176 | } 177 | break; 178 | case XmlPullParser.END_TAG: 179 | break; 180 | } 181 | try { 182 | event = pullParser.next(); 183 | } catch (Exception e) { 184 | e.printStackTrace(); 185 | } 186 | } 187 | } 188 | 189 | private void addStringChunkAndResourceChunk(ValuePool valuePool) { 190 | ResourceChunk resourceChunk = new ResourceChunk(); 191 | resourceChunk.setValuePool(valuePool); 192 | resourceChunk.setChunkType(ResourceChunk.CHUNK_TYPE); 193 | resourceChunk.setChunkSize(valuePool.getResourceIdSize() * 4 + 2 * 4); 194 | mChunkList.add(0, resourceChunk); 195 | 196 | StringChunk stringChunk = new StringChunk(); 197 | stringChunk.setUnknown(valuePool.isUTF8() ? 256 : 0x00000000); 198 | stringChunk.setValuePool(valuePool); 199 | stringChunk.setChunkType(StringChunk.CHUNK_TYPE); 200 | stringChunk.setStringCount(valuePool.getStringSize()); 201 | stringChunk.setStyleCount(0); 202 | stringChunk.setStringPoolOffset(stringChunk.getStringCount() * 4 + 7 * 4); 203 | stringChunk.setStylePoolOffset(0); 204 | stringChunk.setStyleOffsets(new int[]{0}); 205 | 206 | int[] stringOffsets = new int[valuePool.getStringSize()]; 207 | int index = 0; 208 | for (int i = 0; i < valuePool.getStringSize(); i++) { 209 | stringOffsets[i] = index; 210 | if (valuePool.isUTF8()) { 211 | index += (valuePool.getString(i).length() + 3); 212 | } else { 213 | index += (valuePool.getString(i).length() * 2 + 4); 214 | } 215 | } 216 | stringChunk.setStringOffsets(stringOffsets); 217 | if (valuePool.isUTF8()) { 218 | int size = index + stringChunk.getStringCount() * 4 + 7 * 4; 219 | if (size % 2 != 0) { 220 | size += 1; 221 | } 222 | stringChunk.setChunkSize(size); 223 | } else { 224 | int size = index + stringChunk.getStringCount() * 4 + 7 * 4; 225 | if (size % 4 != 0) { 226 | size += 2; 227 | } 228 | stringChunk.setChunkSize(size); 229 | } 230 | mChunkList.add(0, stringChunk); 231 | } 232 | 233 | private int parseEndTagChunk(XmlPullParser pullParser, int lineIndex, ValuePool valuePool) { 234 | int chunkType = EndTagChunk.CHUNK_TYPE; 235 | int nameSpaceUri = valuePool.getUri(pullParser.getNamespace()); 236 | int name = valuePool.checkAndAddString(pullParser.getName()); 237 | int chunkSize = 6 * 4; 238 | 239 | EndTagChunk endTagChunk = new EndTagChunk(); 240 | endTagChunk.setChunkType(EndTagChunk.CHUNK_TYPE); 241 | endTagChunk.setValuePool(valuePool); 242 | endTagChunk.setChunkType(chunkType); 243 | 244 | if (valuePool.isUTF8()) { 245 | endTagChunk.setLineNumber(valuePool.pullInteger()); 246 | } else { 247 | int lineNumber = lineIndex++; 248 | endTagChunk.setLineNumber(lineNumber); 249 | } 250 | endTagChunk.setName(name); 251 | endTagChunk.setNamespaceUri(nameSpaceUri); 252 | endTagChunk.setChunkSize(chunkSize); 253 | mChunkList.add(endTagChunk); 254 | return lineIndex; 255 | } 256 | 257 | private int parseStartTagChunk(XmlPullParser pullParser, int lineIndex, ValuePool valuePool) { 258 | int chunkType = StartTagChunk.CHUNK_TYPE; 259 | int tagName = valuePool.checkAndAddString(pullParser.getName()); 260 | int nameSpaceUri = valuePool.getUri(pullParser.getNamespace()); 261 | int lineNumber = lineIndex; 262 | int flags = 0x140014; 263 | int attributeCount = pullParser.getAttributeCount(); 264 | int classAttribute = 0; 265 | StartTagChunk.Attribute[] attributes = new StartTagChunk.Attribute[attributeCount]; 266 | for (int i = 0; i < attributes.length; i++) { 267 | if (pullParser.getAttributeName(i).equals("xmlns:android")) { 268 | attributes[i] = null; 269 | attributeCount--; 270 | continue; 271 | } 272 | attributes[i] = new StartTagChunk.Attribute(); 273 | 274 | int name; 275 | int namespace = -1; 276 | 277 | String namespaceAndName = pullParser.getAttributeName(i); 278 | String[] splitStr = namespaceAndName.split(":"); 279 | if (splitStr.length >= 2) { 280 | namespace = valuePool.getUri(splitStr[0]); 281 | name = valuePool.checkAndAddString(splitStr[1]); 282 | if (splitStr[0].equals("android") && !valuePool.getResourceIds().contains(mPublicNameToResId.get(splitStr[1]))) { 283 | valuePool.addResourceId(mPublicNameToResId.get(splitStr[1])); 284 | } 285 | } else { 286 | name = valuePool.checkAndAddString(namespaceAndName); 287 | } 288 | 289 | attributes[i].setNamespaceUri(namespace); 290 | attributes[i].setName(name); 291 | String valueString = pullParser.getAttributeValue(i); 292 | try { 293 | if ("versionName".equals(valuePool.getString(attributes[i].getName()))) { 294 | attributes[i].setType(TYPE_STRING); 295 | attributes[i].setValueString(valuePool.checkAndAddString(valueString)); 296 | attributes[i].setData(valuePool.getStringIndex(valueString)); 297 | } else if ("platformBuildVersionCode".equals(valuePool.getString(attributes[i].getName()))) { 298 | attributes[i].setType(TYPE_INT_DEC); 299 | attributes[i].setValueString(valuePool.checkAndAddString(valueString)); 300 | attributes[i].setData(Integer.parseInt(valueString)); 301 | } else { 302 | TypedValue.initAttribute(attributes[i], valueString); 303 | } 304 | } catch (Exception e) { 305 | if (e.getMessage().equals("nothing get in attribute")) { 306 | attributes[i].setType(TYPE_STRING); 307 | attributes[i].setValueString(valuePool.checkAndAddString(valueString)); 308 | attributes[i].setData(valuePool.getStringIndex(valueString)); 309 | } else { 310 | e.printStackTrace(); 311 | } 312 | } 313 | } 314 | int chunkSize = 9 * 4 + attributeCount * 5 * 4; 315 | StartTagChunk startTagChunk = new StartTagChunk(); 316 | startTagChunk.setValuePool(valuePool); 317 | startTagChunk.setChunkType(chunkType); 318 | startTagChunk.setChunkSize(chunkSize); 319 | startTagChunk.setName(tagName); 320 | startTagChunk.setNamespaceUri(nameSpaceUri); 321 | startTagChunk.setLineNumber(lineNumber); 322 | valuePool.putInteger(lineNumber); 323 | startTagChunk.setFlags(flags); 324 | startTagChunk.setAttributeCount(attributeCount); 325 | startTagChunk.setClassAttribute(classAttribute); 326 | startTagChunk.setAttributes(attributes); 327 | mChunkList.add(startTagChunk); 328 | lineIndex += (attributeCount); 329 | return lineIndex; 330 | } 331 | 332 | private void writeToFile(byte[] bytes, String outputFile) throws IOException { 333 | FileOutputStream fileOutputStream = new FileOutputStream(new File(outputFile)); 334 | ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); 335 | 336 | byte[] buffer = new byte[1024]; 337 | int length; 338 | while ((length = byteArrayInputStream.read(buffer)) != -1) { 339 | fileOutputStream.write(buffer, 0, length); 340 | } 341 | } 342 | 343 | public static String readFile(File file, String charset) { 344 | if (file == null || !file.exists()) { 345 | return ""; 346 | } 347 | 348 | StringWriter sw = new StringWriter(); 349 | InputStreamReader isr = null; 350 | try { 351 | isr = new InputStreamReader(new FileInputStream(file), charset == null ? "utf-8" : charset); 352 | char[] buf = new char[1024]; 353 | int len; 354 | while ((len = isr.read(buf)) != -1) { 355 | sw.write(buf, 0, len); 356 | } 357 | 358 | return sw.toString(); 359 | 360 | } catch (Exception err) { 361 | err.printStackTrace(); 362 | } finally { 363 | try { 364 | isr.close(); 365 | } catch (IOException e) { 366 | e.printStackTrace(); 367 | } 368 | try { 369 | sw.close(); 370 | } catch (IOException e) { 371 | e.printStackTrace(); 372 | } 373 | } 374 | 375 | return ""; 376 | } 377 | 378 | } 379 | -------------------------------------------------------------------------------- /src/main/java/cn/zaratustra/axmlparser/model/AXMLHeader.java: -------------------------------------------------------------------------------- 1 | package cn.zaratustra.axmlparser.model; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.ByteOrder; 5 | 6 | /** 7 | * Created by zaratustra on 2017/12/11. 8 | */ 9 | public class AXMLHeader { 10 | 11 | public static final int MAGIC_NUMBER = 0x03000800; 12 | private int mHeaderLength; 13 | private int mMagicNumber; 14 | private int mFileSize; 15 | 16 | public AXMLHeader(byte[] data) { 17 | ByteBuffer byteBuffer = ByteBuffer.wrap(data); 18 | byteBuffer.order(ByteOrder.BIG_ENDIAN); 19 | mMagicNumber = byteBuffer.getInt(); 20 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 21 | mFileSize = byteBuffer.getInt(); 22 | mHeaderLength = byteBuffer.position(); 23 | System.out.println("Get file size: " + mFileSize + ", " + Integer.toHexString(mMagicNumber) + ", " + mHeaderLength); 24 | } 25 | 26 | 27 | public AXMLHeader() { 28 | 29 | } 30 | 31 | public byte[] toBytes() { 32 | ByteBuffer byteBuffer = ByteBuffer.allocate(8); 33 | byteBuffer.order(ByteOrder.BIG_ENDIAN); 34 | byteBuffer.putInt(mMagicNumber); 35 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 36 | byteBuffer.putInt(mFileSize); 37 | // byteBuffer.putInt(mHeaderLength); 38 | return byteBuffer.array(); 39 | } 40 | 41 | public int getHeaderLength() { 42 | return mHeaderLength; 43 | } 44 | 45 | public void setHeaderLength(int headerLength) { 46 | mHeaderLength = headerLength; 47 | } 48 | 49 | public int getMagicNumber() { 50 | return mMagicNumber; 51 | } 52 | 53 | public void setMagicNumber(int magicNumber) { 54 | mMagicNumber = magicNumber; 55 | } 56 | 57 | public int getFileSize() { 58 | return mFileSize; 59 | } 60 | 61 | public void setFileSize(int fileSize) { 62 | mFileSize = fileSize; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return "file size: " + mFileSize + ", " + Integer.toHexString(mMagicNumber) + ", " + mHeaderLength; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/cn/zaratustra/axmlparser/model/Chunk.java: -------------------------------------------------------------------------------- 1 | package cn.zaratustra.axmlparser.model; 2 | 3 | import cn.zaratustra.axmlparser.core.ValuePool; 4 | 5 | /** 6 | * Created by zaratustra on 2017/12/11. 7 | */ 8 | public abstract class Chunk { 9 | 10 | protected int mChunkType; 11 | protected int mChunkSize; 12 | protected int mUnknown = -1; 13 | 14 | protected ValuePool mValuePool; 15 | 16 | public Chunk() { 17 | } 18 | 19 | public int getChunkSize() { 20 | return mChunkSize; 21 | } 22 | 23 | public void setChunkSize(int chunkSize) { 24 | mChunkSize = chunkSize; 25 | } 26 | 27 | public void setChunkType(int chunkType) { 28 | mChunkType = chunkType; 29 | } 30 | 31 | public abstract void parseFromAXML(ValuePool valuePool, byte[] data, int offset); 32 | 33 | public abstract String genXML(); 34 | 35 | public abstract int getLineNumber(); 36 | 37 | public void setValuePool(ValuePool valuePool) { 38 | mValuePool = valuePool; 39 | } 40 | 41 | public abstract byte[] toByte(); 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/cn/zaratustra/axmlparser/model/EndNamespaceChunk.java: -------------------------------------------------------------------------------- 1 | package cn.zaratustra.axmlparser.model; 2 | 3 | import cn.zaratustra.axmlparser.core.ValuePool; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.nio.ByteOrder; 7 | 8 | /** 9 | * Created by zaratustra on 2017/12/12. 10 | */ 11 | public class EndNamespaceChunk extends Chunk { 12 | 13 | public static final int CHUNK_TYPE = 0x00100101; 14 | 15 | private int mLineNumber; 16 | private int mPrefix; 17 | private int mUri; 18 | 19 | @Override 20 | public void parseFromAXML(ValuePool valuePool, byte[] data, int offset) { 21 | mValuePool = valuePool; 22 | ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, data.length - offset); 23 | mChunkType = byteBuffer.getInt(); 24 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 25 | mChunkSize = byteBuffer.getInt(); 26 | 27 | mLineNumber = byteBuffer.getInt(); 28 | mUnknown = byteBuffer.getInt(); 29 | mPrefix = byteBuffer.getInt(); 30 | mUri = byteBuffer.getInt(); 31 | } 32 | 33 | @Override 34 | public String genXML() { 35 | return ""; 36 | } 37 | 38 | public String toString() { 39 | if (mValuePool == null) { 40 | return ""; 41 | } 42 | return String.format("ChunkSize: %s\nLineNumber: %s\nUnknown: %s\nPrefix: %s\nUri: %s\n", mChunkSize, mLineNumber, 43 | mUnknown, mValuePool.getString(mPrefix), mValuePool.getString(mUri)); 44 | } 45 | 46 | @Override 47 | public int getLineNumber() { 48 | return mLineNumber; 49 | } 50 | 51 | @Override 52 | public byte[] toByte() { 53 | ByteBuffer byteBuffer = ByteBuffer.allocate(mChunkSize); 54 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 55 | byteBuffer.putInt(mChunkType); 56 | byteBuffer.putInt(mChunkSize); 57 | byteBuffer.putInt(mLineNumber); 58 | byteBuffer.putInt(mUnknown); 59 | byteBuffer.putInt(mPrefix); 60 | byteBuffer.putInt(mUri); 61 | return byteBuffer.array(); 62 | } 63 | 64 | public void setLineNumber(int lineNumber) { 65 | mLineNumber = lineNumber; 66 | } 67 | 68 | public int getUnknown() { 69 | return mUnknown; 70 | } 71 | 72 | public void setUnknown(int unknown) { 73 | mUnknown = unknown; 74 | } 75 | 76 | public int getPrefix() { 77 | return mPrefix; 78 | } 79 | 80 | public void setPrefix(int prefix) { 81 | mPrefix = prefix; 82 | } 83 | 84 | public int getUri() { 85 | return mUri; 86 | } 87 | 88 | public void setUri(int uri) { 89 | mUri = uri; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/cn/zaratustra/axmlparser/model/EndTagChunk.java: -------------------------------------------------------------------------------- 1 | package cn.zaratustra.axmlparser.model; 2 | 3 | import cn.zaratustra.axmlparser.core.ValuePool; 4 | import cn.zaratustra.axmlparser.utils.StringUtils; 5 | 6 | import java.nio.ByteBuffer; 7 | import java.nio.ByteOrder; 8 | 9 | /** 10 | * Created by zaratustra on 2017/12/12. 11 | */ 12 | public class EndTagChunk extends Chunk { 13 | 14 | public static final int CHUNK_TYPE = 0x00100103; 15 | 16 | private int mLineNumber; 17 | private int mNamespaceUri; 18 | private int mName; 19 | 20 | @Override 21 | public void parseFromAXML(ValuePool valuePool, byte[] data, int offset) { 22 | mValuePool = valuePool; 23 | ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, data.length - offset); 24 | mChunkType = byteBuffer.getInt(); 25 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 26 | mChunkSize = byteBuffer.getInt(); 27 | 28 | mLineNumber = byteBuffer.getInt(); 29 | mUnknown = byteBuffer.getInt(); 30 | mNamespaceUri = byteBuffer.getInt(); 31 | mName = byteBuffer.getInt(); 32 | } 33 | 34 | @Override 35 | public String genXML() { 36 | StringBuilder stringBuilder = new StringBuilder(); 37 | stringBuilder.append("").append("\n"); 43 | return stringBuilder.toString(); 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return String.format("ChunkSize:%s\nLineNumber:%s\nUnknown:%s\nNamespaceUri:%s\nName:%s\n", 49 | mChunkSize, mLineNumber, mUnknown, mValuePool.getString(mNamespaceUri), mValuePool.getString(mName)); 50 | } 51 | 52 | @Override 53 | public int getLineNumber() { 54 | return mLineNumber; 55 | } 56 | 57 | @Override 58 | public byte[] toByte() { 59 | ByteBuffer byteBuffer = ByteBuffer.allocate(mChunkSize); 60 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 61 | byteBuffer.putInt(mChunkType); 62 | byteBuffer.putInt(mChunkSize); 63 | byteBuffer.putInt(mLineNumber); 64 | byteBuffer.putInt(mUnknown); 65 | byteBuffer.putInt(mNamespaceUri); 66 | byteBuffer.putInt(mName); 67 | 68 | return byteBuffer.array(); 69 | } 70 | 71 | public void setLineNumber(int lineNumber) { 72 | mLineNumber = lineNumber; 73 | } 74 | 75 | public int getUnKnown() { 76 | return mUnknown; 77 | } 78 | 79 | public void setUnKnown(int unKnown) { 80 | mUnknown = unKnown; 81 | } 82 | 83 | public int getNamespaceUri() { 84 | return mNamespaceUri; 85 | } 86 | 87 | public void setNamespaceUri(int namespaceUri) { 88 | mNamespaceUri = namespaceUri; 89 | } 90 | 91 | public int getName() { 92 | return mName; 93 | } 94 | 95 | public void setName(int name) { 96 | mName = name; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/cn/zaratustra/axmlparser/model/ResourceChunk.java: -------------------------------------------------------------------------------- 1 | package cn.zaratustra.axmlparser.model; 2 | 3 | import cn.zaratustra.axmlparser.core.ValuePool; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.nio.ByteOrder; 7 | 8 | /** 9 | * Created by zaratustra on 2017/12/11. 10 | */ 11 | public class ResourceChunk extends Chunk { 12 | public final static int CHUNK_TYPE = 0x00080180; 13 | 14 | @Override 15 | public void parseFromAXML(ValuePool valuePool, byte[] data, int offset) { 16 | mValuePool = valuePool; 17 | ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, data.length - offset); 18 | 19 | mChunkType = byteBuffer.getInt(); 20 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 21 | mChunkSize = byteBuffer.getInt(); 22 | 23 | while (byteBuffer.position() - offset < mChunkSize) { 24 | int resId = byteBuffer.getInt(); 25 | mValuePool.addResourceId(resId); 26 | } 27 | } 28 | 29 | @Override 30 | public String genXML() { 31 | return null; 32 | } 33 | 34 | @Override 35 | public int getLineNumber() { 36 | return 0; 37 | } 38 | 39 | @Override 40 | public byte[] toByte() { 41 | ByteBuffer byteBuffer = ByteBuffer.allocate(mChunkSize); 42 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 43 | byteBuffer.putInt(mChunkType); 44 | byteBuffer.putInt(mChunkSize); 45 | // mValuePool.getResourceIds().clear(); 46 | // mValuePool.getResourceIds().add(Integer.decode("0x101021b")); 47 | // mValuePool.getResourceIds().add(Integer.decode("0x101021c")); 48 | // mValuePool.getResourceIds().add(Integer.decode("0x101020c")); 49 | // mValuePool.getResourceIds().add(Integer.decode("0x1010270")); 50 | // mValuePool.getResourceIds().add(Integer.decode("0x1010003")); 51 | // mValuePool.getResourceIds().add(Integer.decode("0x1010024")); 52 | // mValuePool.getResourceIds().add(Integer.decode("0x1010280")); 53 | // mValuePool.getResourceIds().add(Integer.decode("0x1010002")); 54 | // mValuePool.getResourceIds().add(Integer.decode("0x1010001")); 55 | // mValuePool.getResourceIds().add(Integer.decode("0x101052c")); 56 | // mValuePool.getResourceIds().add(Integer.decode("0x10103af")); 57 | // mValuePool.getResourceIds().add(Integer.decode("0x1010000")); 58 | 59 | 60 | // mValuePool.getResourceIds().clear(); 61 | // mValuePool.getResourceIds().add(Integer.decode("0x1010000")); 62 | // mValuePool.getResourceIds().add(Integer.decode("0x1010001")); 63 | // mValuePool.getResourceIds().add(Integer.decode("0x1010002")); 64 | // mValuePool.getResourceIds().add(Integer.decode("0x1010003")); 65 | // mValuePool.getResourceIds().add(Integer.decode("0x1010024")); 66 | // mValuePool.getResourceIds().add(Integer.decode("0x101020c")); 67 | // mValuePool.getResourceIds().add(Integer.decode("0x101021b")); 68 | // mValuePool.getResourceIds().add(Integer.decode("0x101021c")); 69 | // mValuePool.getResourceIds().add(Integer.decode("0x1010270")); 70 | // mValuePool.getResourceIds().add(Integer.decode("0x1010280")); 71 | // mValuePool.getResourceIds().add(Integer.decode("0x10103af")); 72 | // mValuePool.getResourceIds().add(Integer.decode("0x101052c")); 73 | for (int i = 0; i < mValuePool.getResourceIdSize(); i++) { 74 | byteBuffer.putInt(mValuePool.getResourceId(i)); 75 | } 76 | return byteBuffer.array(); 77 | } 78 | 79 | @Override 80 | public String toString() { 81 | StringBuilder stringBuilder = new StringBuilder(); 82 | for (Integer integer : mValuePool.getResourceIds()) { 83 | stringBuilder.append("ResId: 0x" + Integer.toHexString(integer)).append("\n"); 84 | } 85 | return String.format("ChunkType:0x%s\nChunkSize:%d\n", Integer.toHexString(mChunkType), mChunkSize) + stringBuilder.toString(); 86 | } 87 | 88 | public int getChunkSize() { 89 | return mChunkSize; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/cn/zaratustra/axmlparser/model/StartNamespaceChunk.java: -------------------------------------------------------------------------------- 1 | package cn.zaratustra.axmlparser.model; 2 | 3 | import cn.zaratustra.axmlparser.core.ValuePool; 4 | 5 | import java.nio.Buffer; 6 | import java.nio.ByteBuffer; 7 | import java.nio.ByteOrder; 8 | 9 | /** 10 | * Created by zaratustra on 2017/12/11. 11 | */ 12 | public class StartNamespaceChunk extends Chunk { 13 | 14 | public static final int CHUNK_TYPE = 0x00100100; 15 | 16 | private int mLineNumber; 17 | private int mPrefix; 18 | private int mUri; 19 | 20 | public StartNamespaceChunk() { 21 | mUnknown = -1; 22 | } 23 | 24 | @Override 25 | public void parseFromAXML(ValuePool valuePool, byte[] data, int offset) { 26 | mValuePool = valuePool; 27 | ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, data.length - offset); 28 | mChunkType = byteBuffer.getInt(); 29 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 30 | mChunkSize = byteBuffer.getInt(); 31 | 32 | mLineNumber = byteBuffer.getInt(); 33 | mUnknown = byteBuffer.getInt(); 34 | mPrefix = byteBuffer.getInt(); 35 | mUri = byteBuffer.getInt(); 36 | 37 | mValuePool.putUriToNamespace(mValuePool.getString(mUri), mValuePool.getString(mPrefix)); 38 | } 39 | 40 | @Override 41 | public String genXML() { 42 | return "\n"; 43 | } 44 | 45 | public String toString() { 46 | if (mValuePool == null) { 47 | return ""; 48 | } 49 | return String.format("ChunkSize: %s\nLineNumber: %s\nUnknown: %s\nPrefix: %s\nUri: %s\n", mChunkSize, mLineNumber, 50 | mUnknown, mValuePool.getString(mPrefix), mValuePool.getString(mUri)); 51 | } 52 | 53 | @Override 54 | public int getLineNumber() { 55 | return mLineNumber; 56 | } 57 | 58 | @Override 59 | public byte[] toByte() { 60 | ByteBuffer byteBuffer = ByteBuffer.allocate(mChunkSize); 61 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 62 | byteBuffer.putInt(mChunkType); 63 | byteBuffer.putInt(mChunkSize); 64 | byteBuffer.putInt(mLineNumber); 65 | byteBuffer.putInt(mUnknown); 66 | byteBuffer.putInt(mPrefix); 67 | byteBuffer.putInt(mUri); 68 | return byteBuffer.array(); 69 | } 70 | 71 | public void setLineNumber(int lineNumber) { 72 | mLineNumber = lineNumber; 73 | } 74 | 75 | public int getUnknown() { 76 | return mUnknown; 77 | } 78 | 79 | public void setUnknown(int unknown) { 80 | mUnknown = unknown; 81 | } 82 | 83 | public int getPrefix() { 84 | return mPrefix; 85 | } 86 | 87 | public void setPrefix(int prefix) { 88 | mPrefix = prefix; 89 | } 90 | 91 | public int getUri() { 92 | return mUri; 93 | } 94 | 95 | public void setUri(int uri) { 96 | mUri = uri; 97 | } 98 | 99 | @Override 100 | public void setChunkSize(int chunkSize) { 101 | super.setChunkSize(chunkSize); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/cn/zaratustra/axmlparser/model/StartTagChunk.java: -------------------------------------------------------------------------------- 1 | package cn.zaratustra.axmlparser.model; 2 | 3 | import cn.zaratustra.axmlparser.core.ValuePool; 4 | import cn.zaratustra.axmlparser.utils.StringUtils; 5 | import cn.zaratustra.axmlparser.utils.TypedValue; 6 | 7 | import java.nio.ByteBuffer; 8 | import java.nio.ByteOrder; 9 | import java.util.ArrayList; 10 | import java.util.Set; 11 | 12 | /** 13 | * Created by zaratustra on 2017/12/12. 14 | */ 15 | public class StartTagChunk extends Chunk { 16 | 17 | public static final int CHUNK_TYPE = 0x00100102; 18 | 19 | private int mLineNumber; 20 | private int mNamespaceUri; 21 | private int mName; 22 | private int mFlags; 23 | private int mAttributeCount; 24 | private int mClassAttribute; 25 | private Attribute[] mAttributes; 26 | 27 | public static class Attribute { 28 | int mNamespaceUri; 29 | int mName; 30 | int mValueString; 31 | int mType; 32 | int mData; 33 | 34 | public int getNamespaceUri() { 35 | return mNamespaceUri; 36 | } 37 | 38 | public void setNamespaceUri(int namespaceUri) { 39 | mNamespaceUri = namespaceUri; 40 | } 41 | 42 | public int getName() { 43 | return mName; 44 | } 45 | 46 | public void setName(int name) { 47 | mName = name; 48 | } 49 | 50 | public int getValueString() { 51 | return mValueString; 52 | } 53 | 54 | public void setValueString(int valueString) { 55 | mValueString = valueString; 56 | } 57 | 58 | public int getType() { 59 | return mType; 60 | } 61 | 62 | public void setType(int type) { 63 | mType = type; 64 | } 65 | 66 | public int getData() { 67 | return mData; 68 | } 69 | 70 | public void setData(int data) { 71 | mData = data; 72 | } 73 | } 74 | 75 | 76 | @Override 77 | public String genXML() { 78 | StringBuilder stringBuilder = new StringBuilder(); 79 | stringBuilder.append("<"); 80 | if (!StringUtils.isEmpty(mValuePool.getString(mNamespaceUri))) { 81 | stringBuilder.append(mValuePool.getNamespace(mValuePool.getString(mNamespaceUri))); 82 | stringBuilder.append(":"); 83 | } 84 | 85 | stringBuilder.append(mValuePool.getString(mName)).append(" "); 86 | if (mValuePool.getString(mName).equals("manifest")) { 87 | Set keySet = mValuePool.getUriToNameSpaceMaps().keySet(); 88 | for (String set : keySet) { 89 | stringBuilder.append("xmlns:").append(mValuePool.getNamespace(set)).append("=\"").append(set).append("\"").append(" "); 90 | } 91 | } 92 | 93 | for (int i = 0; i < mAttributes.length; i++) { 94 | Attribute attribute = mAttributes[i]; 95 | if (!StringUtils.isEmpty(mValuePool.getString(attribute.mNamespaceUri))) { 96 | stringBuilder.append(mValuePool.getNamespace(mValuePool.getString(attribute.mNamespaceUri))); 97 | stringBuilder.append(":"); 98 | } 99 | stringBuilder.append(mValuePool.getString(attribute.mName)).append("="); 100 | String data = TypedValue.coerceToString(attribute.mType, attribute.mData); 101 | if (StringUtils.isEmpty(data)) { 102 | stringBuilder.append("\"").append(mValuePool.getString(attribute.mValueString)).append("\""); 103 | } else { 104 | stringBuilder.append("\"").append(data).append("\""); 105 | } 106 | stringBuilder.append(" "); 107 | } 108 | stringBuilder.append(">\n"); 109 | 110 | return stringBuilder.toString(); 111 | } 112 | 113 | @Override 114 | public String toString() { 115 | StringBuilder stringBuilder = new StringBuilder(); 116 | for (int i = 0; i < mAttributeCount; i++) { 117 | if (i < mAttributes.length) { 118 | stringBuilder.append("Attribute.Namespace:").append(mValuePool.getNamespace(mValuePool.getString(mAttributes[i].mNamespaceUri))).append("\n"); 119 | stringBuilder.append("Attribute.Name:").append(mValuePool.getString(mAttributes[i].mName)).append("\n"); 120 | stringBuilder.append("Attribute.ValueString:").append(mValuePool.getString(mAttributes[i].mValueString)).append("\n"); 121 | stringBuilder.append("Attribute.Data:").append(TypedValue.coerceToString(mAttributes[i].mType, mAttributes[i].mData)).append("\n"); 122 | stringBuilder.append("Attribute.Type Data:").append(mAttributes[i].mType + ", " + mAttributes[i].mData).append("\n").append("\n").append("\n"); 123 | } 124 | } 125 | 126 | return String.format("ChunkSize:%s\nLineNumber:%s\nUnknown:%s\nNamespaceUri:%s\nName:%s\nFlags:0x%s\nAttribute Count:%s\nClass Attribute:%s\n", 127 | mChunkSize, mLineNumber, mUnknown, mValuePool.getString(mNamespaceUri), mValuePool.getString(mName), Integer.toHexString(mFlags), mAttributeCount, 128 | mClassAttribute) + stringBuilder.toString(); 129 | } 130 | 131 | 132 | public void setLineNumber(int lineNumber) { 133 | mLineNumber = lineNumber; 134 | } 135 | 136 | public int getUnKnown() { 137 | return mUnknown; 138 | } 139 | 140 | public void setUnKnown(int unKnown) { 141 | mUnknown = unKnown; 142 | } 143 | 144 | public int getNamespaceUri() { 145 | return mNamespaceUri; 146 | } 147 | 148 | public void setNamespaceUri(int namespaceUri) { 149 | mNamespaceUri = namespaceUri; 150 | } 151 | 152 | public int getName() { 153 | return mName; 154 | } 155 | 156 | public void setName(int name) { 157 | mName = name; 158 | } 159 | 160 | public int getFlags() { 161 | return mFlags; 162 | } 163 | 164 | public void setFlags(int flags) { 165 | mFlags = flags; 166 | } 167 | 168 | public int getAttributeCount() { 169 | return mAttributeCount; 170 | } 171 | 172 | public void setAttributeCount(int attributeCount) { 173 | mAttributeCount = attributeCount; 174 | } 175 | 176 | public int getClassAttribute() { 177 | return mClassAttribute; 178 | } 179 | 180 | public void setClassAttribute(int classAttribute) { 181 | mClassAttribute = classAttribute; 182 | } 183 | 184 | public Attribute[] getAttributes() { 185 | return mAttributes; 186 | } 187 | 188 | public void setAttributes(Attribute[] attributes) { 189 | ArrayList list = new ArrayList<>(); 190 | for (Attribute attribute : attributes) { 191 | if (attribute != null) { 192 | list.add(attribute); 193 | } 194 | } 195 | mAttributes = list.toArray(new Attribute[list.size()]); 196 | } 197 | 198 | @Override 199 | public int getLineNumber() { 200 | return mLineNumber; 201 | } 202 | 203 | @Override 204 | public byte[] toByte() { 205 | ByteBuffer byteBuffer = ByteBuffer.allocate(mChunkSize); 206 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 207 | byteBuffer.putInt(mChunkType); 208 | byteBuffer.putInt(mChunkSize); 209 | byteBuffer.putInt(mLineNumber); 210 | byteBuffer.putInt(mUnknown); 211 | byteBuffer.putInt(mNamespaceUri); 212 | byteBuffer.putInt(mName); 213 | byteBuffer.putInt(mFlags); 214 | byteBuffer.putInt(mAttributeCount); 215 | byteBuffer.putInt(mClassAttribute); 216 | for (int i = 0; i < mAttributes.length; i++) { 217 | byteBuffer.putInt(mAttributes[i].mNamespaceUri); 218 | byteBuffer.putInt(mAttributes[i].mName); 219 | byteBuffer.putInt(mAttributes[i].mValueString); 220 | int type = ((mAttributes[i].mType) << 24); 221 | byteBuffer.putInt(type); 222 | byteBuffer.position(byteBuffer.position() - 4); 223 | byteBuffer.putShort((short) 0x08); 224 | byteBuffer.position(byteBuffer.position() + 2); 225 | byteBuffer.putInt(mAttributes[i].mData); 226 | } 227 | return byteBuffer.array(); 228 | } 229 | 230 | @Override 231 | public void parseFromAXML(ValuePool valuePool, byte[] data, int offset) { 232 | mValuePool = valuePool; 233 | 234 | ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, data.length - offset); 235 | mChunkType = byteBuffer.getInt(); 236 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 237 | mChunkSize = byteBuffer.getInt(); 238 | 239 | mLineNumber = byteBuffer.getInt(); 240 | mUnknown = byteBuffer.getInt(); 241 | mNamespaceUri = byteBuffer.getInt(); 242 | mName = byteBuffer.getInt(); 243 | mFlags = byteBuffer.getInt(); 244 | mAttributeCount = byteBuffer.getInt(); 245 | mClassAttribute = byteBuffer.getInt(); 246 | 247 | mAttributes = new Attribute[mAttributeCount]; 248 | 249 | for (int i = 0; i < mAttributeCount; i++) { 250 | mAttributes[i] = new Attribute(); 251 | mAttributes[i].mNamespaceUri = byteBuffer.getInt(); 252 | mAttributes[i].mName = byteBuffer.getInt(); 253 | mAttributes[i].mValueString = byteBuffer.getInt(); 254 | int type = byteBuffer.getInt(); 255 | mAttributes[i].mType = type >> 24; 256 | mAttributes[i].mData = byteBuffer.getInt(); 257 | } 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /src/main/java/cn/zaratustra/axmlparser/model/StringChunk.java: -------------------------------------------------------------------------------- 1 | package cn.zaratustra.axmlparser.model; 2 | 3 | import cn.zaratustra.axmlparser.core.ValuePool; 4 | 5 | import java.io.UnsupportedEncodingException; 6 | import java.nio.ByteBuffer; 7 | import java.nio.ByteOrder; 8 | import java.nio.charset.Charset; 9 | import java.util.Arrays; 10 | 11 | /** 12 | * Created by zaratustra on 2017/12/11. 13 | */ 14 | public class StringChunk extends Chunk { 15 | 16 | public final static int CHUNK_TYPE = 0x001C0001; 17 | private final int UTF8_FLAG = 1 << 8; 18 | 19 | private int mChunkSize; 20 | private int mStringCount; 21 | private int mStyleCount; 22 | private int mStringPoolOffset; 23 | private int mStylePoolOffset; 24 | private int mStringOffsets[]; 25 | private int mStyleOffsets[]; 26 | 27 | public StringChunk() { 28 | // mUnknown = 0x00000000; 29 | // mUnknown = UTF8_FLAG; 30 | } 31 | 32 | @Override 33 | public void parseFromAXML(ValuePool valuePool, byte[] data, int offset) { 34 | mValuePool = valuePool; 35 | 36 | ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, data.length - offset); 37 | mChunkType = byteBuffer.getInt(); 38 | 39 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 40 | mChunkSize = byteBuffer.getInt(); 41 | mStringCount = byteBuffer.getInt(); 42 | mStyleCount = byteBuffer.getInt(); 43 | mUnknown = byteBuffer.getInt(); 44 | mStringPoolOffset = byteBuffer.getInt(); 45 | mStylePoolOffset = byteBuffer.getInt(); 46 | mStringOffsets = new int[mStringCount]; 47 | mStyleOffsets = new int[mStyleCount]; 48 | 49 | for (int i = 0; i < mStringCount; i++) { 50 | mStringOffsets[i] = byteBuffer.getInt(); 51 | } 52 | 53 | for (int i = 0; i < mStyleCount; i++) { 54 | mStyleOffsets[i] = byteBuffer.getInt(); 55 | } 56 | 57 | for (int i = 0; i < mStringCount; i++) { 58 | byteBuffer.position(mStringPoolOffset + offset + mStringOffsets[i]); 59 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 60 | 61 | int strLength; 62 | if (mUnknown == UTF8_FLAG) { 63 | valuePool.setUTF8(true); 64 | strLength = byteBuffer.get(); 65 | byteBuffer.get(); 66 | } else { 67 | valuePool.setUTF8(false); 68 | strLength = byteBuffer.getShort() * 2; 69 | } 70 | 71 | byte[] strByte = new byte[strLength]; 72 | byteBuffer.get(strByte, 0, strLength); 73 | valuePool.addString(filterStringNull(strByte)); 74 | } 75 | } 76 | 77 | @Override 78 | public byte[] toByte() { 79 | ByteBuffer byteBuffer = ByteBuffer.allocate(mChunkSize); 80 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 81 | byteBuffer.putInt(mChunkType); 82 | byteBuffer.putInt(mChunkSize); 83 | byteBuffer.putInt(mStringCount); 84 | byteBuffer.putInt(mStyleCount); 85 | byteBuffer.putInt(mUnknown); 86 | byteBuffer.putInt(mStringPoolOffset); 87 | byteBuffer.putInt(mStylePoolOffset); 88 | for (int i = 0; i < mStringOffsets.length; i++) { 89 | byteBuffer.putInt(mStringOffsets[i]); 90 | } 91 | for (int i = 0; i < mValuePool.getStringSize(); i++) { 92 | String value = mValuePool.getString(i); 93 | 94 | if (mValuePool.isUTF8()) { 95 | byteBuffer.put((byte) value.length()); 96 | byteBuffer.put((byte) value.length()); 97 | byte[] bytes = value.getBytes(Charset.forName("utf-8")); 98 | byteBuffer.put(bytes, 0, bytes.length); 99 | byteBuffer.put(new byte[]{0x00}); 100 | if (i == mValuePool.getStringSize() - 1 && byteBuffer.position() < mChunkSize) { 101 | byteBuffer.put(new byte[0x00]); 102 | } 103 | } else { 104 | byteBuffer.putShort((short) value.length()); 105 | byte[] bytes = addZero(value.getBytes(Charset.forName("utf-8"))); 106 | byteBuffer.put(bytes, 0, bytes.length); 107 | byteBuffer.putChar('\0'); 108 | if (i == mValuePool.getStringSize() - 1 && byteBuffer.position() < mChunkSize) { 109 | byteBuffer.putChar('\0'); 110 | } 111 | } 112 | } 113 | 114 | return byteBuffer.array(); 115 | } 116 | 117 | @Override 118 | public String genXML() { 119 | return null; 120 | } 121 | 122 | @Override 123 | public int getLineNumber() { 124 | return 0; 125 | } 126 | 127 | private String filterStringNull(byte[] strByte) { 128 | int noZeroIndex = 0; 129 | int endIndex = 0; 130 | while (endIndex < strByte.length) { 131 | if (strByte[endIndex] != 0 && strByte[noZeroIndex] == 0) { 132 | swap(strByte, noZeroIndex, endIndex); 133 | endIndex++; 134 | noZeroIndex++; 135 | } else { 136 | if (strByte[noZeroIndex] != 0) { 137 | noZeroIndex++; 138 | endIndex = noZeroIndex; 139 | } 140 | if (endIndex < strByte.length && 141 | strByte[endIndex] == 0) { 142 | endIndex++; 143 | } 144 | } 145 | } 146 | try { 147 | return new String(Arrays.copyOf(strByte, noZeroIndex), "utf-8"); 148 | } catch (UnsupportedEncodingException e) { 149 | e.printStackTrace(); 150 | return new String(Arrays.copyOf(strByte, noZeroIndex)); 151 | } 152 | } 153 | 154 | private byte[] addZero(byte[] strByte) { 155 | byte[] targetByte = new byte[strByte.length * 2]; 156 | for (int i = 0; i < strByte.length; i++) { 157 | targetByte[2 * i] = strByte[i]; 158 | targetByte[2 * i + 1] = '\0'; 159 | } 160 | return targetByte; 161 | } 162 | 163 | private void swap(byte[] data, int a, int b) { 164 | byte temp = data[a]; 165 | data[a] = data[b]; 166 | data[b] = temp; 167 | } 168 | 169 | @Override 170 | public String toString() { 171 | StringBuilder stringBuilder = new StringBuilder(""); 172 | for (int i = 0; i < mValuePool.getStringSize(); i++) { 173 | stringBuilder.append("\"" + mValuePool.getString(i) + "\",").append("\n"); 174 | } 175 | return String.format("ChunkSize:%s\nStringCount:%s\nStyleCount:%s\nUnknown:%s\nStringPoolOffset:%s\nStylePoolOffset:%s\n", 176 | mChunkSize, mStringCount, mStyleCount, mUnknown, mStringPoolOffset, mStylePoolOffset) + stringBuilder.toString(); 177 | } 178 | 179 | public int getChunkSize() { 180 | return mChunkSize; 181 | } 182 | 183 | @Override 184 | public void setChunkSize(int chunkSize) { 185 | mChunkSize = chunkSize; 186 | } 187 | 188 | public int getStringCount() { 189 | return mStringCount; 190 | } 191 | 192 | public void setStringCount(int stringCount) { 193 | mStringCount = stringCount; 194 | } 195 | 196 | public int getStyleCount() { 197 | return mStyleCount; 198 | } 199 | 200 | public void setStyleCount(int styleCount) { 201 | mStyleCount = styleCount; 202 | } 203 | 204 | public int getUnknown() { 205 | return mUnknown; 206 | } 207 | 208 | public void setUnknown(int unknown) { 209 | mUnknown = unknown; 210 | } 211 | 212 | public int getStringPoolOffset() { 213 | return mStringPoolOffset; 214 | } 215 | 216 | public void setStringPoolOffset(int stringPoolOffset) { 217 | mStringPoolOffset = stringPoolOffset; 218 | } 219 | 220 | public int getStylePoolOffset() { 221 | return mStylePoolOffset; 222 | } 223 | 224 | public void setStylePoolOffset(int stylePoolOffset) { 225 | mStylePoolOffset = stylePoolOffset; 226 | } 227 | 228 | public int[] getStringOffsets() { 229 | return mStringOffsets; 230 | } 231 | 232 | public void setStringOffsets(int[] stringOffsets) { 233 | mStringOffsets = stringOffsets; 234 | } 235 | 236 | public int[] getStyleOffsets() { 237 | return mStyleOffsets; 238 | } 239 | 240 | public void setStyleOffsets(int[] styleOffsets) { 241 | mStyleOffsets = styleOffsets; 242 | } 243 | } -------------------------------------------------------------------------------- /src/main/java/cn/zaratustra/axmlparser/model/TextChunk.java: -------------------------------------------------------------------------------- 1 | package cn.zaratustra.axmlparser.model; 2 | 3 | import cn.zaratustra.axmlparser.core.ValuePool; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.nio.ByteOrder; 7 | 8 | /** 9 | * Created by zaratustra on 2017/12/13. 10 | */ 11 | public class TextChunk extends Chunk { 12 | 13 | public static final int CHUNK_TYPE = 0x00100104; 14 | 15 | private int mLineNumber; 16 | private int mName; 17 | private int mUnknown2; 18 | private int mUnknown3; 19 | 20 | 21 | @Override 22 | public void parseFromAXML(ValuePool valuePool, byte[] data, int offset) { 23 | mValuePool = valuePool; 24 | ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, data.length - offset); 25 | byteBuffer.order(ByteOrder.BIG_ENDIAN); 26 | mChunkType = byteBuffer.getInt(); 27 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 28 | mChunkSize = byteBuffer.getInt(); 29 | mLineNumber = byteBuffer.getInt(); 30 | mUnknown = byteBuffer.getInt(); 31 | mName = byteBuffer.getInt(); 32 | mUnknown2 = byteBuffer.getInt(); 33 | mUnknown3 = byteBuffer.getInt(); 34 | } 35 | 36 | @Override 37 | public String genXML() { 38 | return mValuePool.getString(mName); 39 | } 40 | 41 | @Override 42 | public int getLineNumber() { 43 | return mLineNumber; 44 | } 45 | 46 | @Override 47 | public byte[] toByte() { 48 | ByteBuffer byteBuffer = ByteBuffer.allocate(mChunkSize); 49 | byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 50 | byteBuffer.putInt(mChunkType); 51 | byteBuffer.putInt(mChunkSize); 52 | byteBuffer.putInt(mLineNumber); 53 | byteBuffer.putInt(mUnknown); 54 | byteBuffer.putInt(mName); 55 | byteBuffer.putInt(mUnknown2); 56 | byteBuffer.putInt(mUnknown3); 57 | return byteBuffer.array(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/cn/zaratustra/axmlparser/utils/StringUtils.java: -------------------------------------------------------------------------------- 1 | package cn.zaratustra.axmlparser.utils; 2 | 3 | /** 4 | * Created by zaratustra on 2017/12/12. 5 | */ 6 | public class StringUtils { 7 | public static boolean isEmpty(String str) { 8 | return (str == null || str.length() == 0); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/cn/zaratustra/axmlparser/utils/TypedValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2017 Ryszard Wiśniewski 3 | * Copyright (C) 2017 Connor Tumbleson 4 | *

5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | *

9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | *

11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.zaratustra.axmlparser.utils; 18 | 19 | import cn.zaratustra.axmlparser.model.StartTagChunk; 20 | 21 | import java.util.Scanner; 22 | 23 | /** 24 | * Container for a dynamically typed data value. Primarily used with 25 | * {@link android.content.res.Resources} for holding resource values. 26 | */ 27 | public class TypedValue { 28 | /** 29 | * The value contains no data. 30 | */ 31 | public static final int TYPE_NULL = 0x00; 32 | 33 | /** 34 | * The data field holds a resource identifier. 35 | */ 36 | public static final int TYPE_REFERENCE = 0x01; 37 | /** 38 | * The data field holds an attribute resource identifier 39 | * (referencing an attribute in the current theme style, not a resource 40 | * entry). 41 | */ 42 | public static final int TYPE_ATTRIBUTE = 0x02; 43 | /** 44 | * The string field holds string data. In addition, if 45 | * data is non-zero then it is the string block index of the 46 | * string and assetCookie is the set of assets the string came 47 | * from. 48 | */ 49 | public static final int TYPE_STRING = 0x03; 50 | /** 51 | * The data field holds an IEEE 754 floating point number. 52 | */ 53 | public static final int TYPE_FLOAT = 0x04; 54 | /** 55 | * The data field holds a complex number encoding a dimension 56 | * value. 57 | */ 58 | public static final int TYPE_DIMENSION = 0x05; 59 | /** 60 | * The data field holds a complex number encoding a fraction of a 61 | * container. 62 | */ 63 | public static final int TYPE_FRACTION = 0x06; 64 | /** 65 | * The data holds a dynamic res table reference, which needs to be 66 | * resolved before it can be used like TYPE_REFERENCE 67 | */ 68 | public static final int TYPE_DYNAMIC_REFERENCE = 0x07; 69 | /** 70 | * The data an attribute resource identifier, which needs to be resolved 71 | * before it can be used like a TYPE_ATTRIBUTE. 72 | */ 73 | public static final int TYPE_DYNAMIC_ATTRIBUTE = 0x08; 74 | /** 75 | * Identifies the start of plain integer values. Any type value from this to 76 | * {@link #TYPE_LAST_INT} means the data field holds a generic 77 | * integer value. 78 | */ 79 | public static final int TYPE_FIRST_INT = 0x10; 80 | 81 | /** 82 | * The data field holds a number that was originally specified in 83 | * decimal. 84 | */ 85 | public static final int TYPE_INT_DEC = 0x10; 86 | /** 87 | * The data field holds a number that was originally specified in 88 | * hexadecimal (0xn). 89 | */ 90 | public static final int TYPE_INT_HEX = 0x11; 91 | /** 92 | * The data field holds 0 or 1 that was originally specified as 93 | * "false" or "true". 94 | */ 95 | public static final int TYPE_INT_BOOLEAN = 0x12; 96 | 97 | /** 98 | * Identifies the start of integer values that were specified as color 99 | * constants (starting with '#'). 100 | */ 101 | public static final int TYPE_FIRST_COLOR_INT = 0x1c; 102 | 103 | /** 104 | * The data field holds a color that was originally specified as 105 | * #aarrggbb. 106 | */ 107 | public static final int TYPE_INT_COLOR_ARGB8 = 0x1c; 108 | /** 109 | * The data field holds a color that was originally specified as 110 | * #rrggbb. 111 | */ 112 | public static final int TYPE_INT_COLOR_RGB8 = 0x1d; 113 | /** 114 | * The data field holds a color that was originally specified as 115 | * #argb. 116 | */ 117 | public static final int TYPE_INT_COLOR_ARGB4 = 0x1e; 118 | /** 119 | * The data field holds a color that was originally specified as 120 | * #rgb. 121 | */ 122 | public static final int TYPE_INT_COLOR_RGB4 = 0x1f; 123 | 124 | /** 125 | * Identifies the end of integer values that were specified as color 126 | * constants. 127 | */ 128 | public static final int TYPE_LAST_COLOR_INT = 0x1f; 129 | 130 | /** 131 | * Identifies the end of plain integer values. 132 | */ 133 | public static final int TYPE_LAST_INT = 0x1f; 134 | 135 | /* ------------------------------------------------------------ */ 136 | 137 | /** 138 | * Complex data: bit location of unit information. 139 | */ 140 | public static final int COMPLEX_UNIT_SHIFT = 0; 141 | /** 142 | * Complex data: mask to extract unit information (after shifting by 143 | * {@link #COMPLEX_UNIT_SHIFT}). This gives us 16 possible types, as defined 144 | * below. 145 | */ 146 | public static final int COMPLEX_UNIT_MASK = 0xf; 147 | 148 | /** 149 | * {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. 150 | */ 151 | public static final int COMPLEX_UNIT_PX = 0; 152 | /** 153 | * {@link #TYPE_DIMENSION} complex unit: Value is Device Independent Pixels. 154 | */ 155 | public static final int COMPLEX_UNIT_DIP = 1; 156 | /** 157 | * {@link #TYPE_DIMENSION} complex unit: Value is a scaled pixel. 158 | */ 159 | public static final int COMPLEX_UNIT_SP = 2; 160 | /** 161 | * {@link #TYPE_DIMENSION} complex unit: Value is in points. 162 | */ 163 | public static final int COMPLEX_UNIT_PT = 3; 164 | /** 165 | * {@link #TYPE_DIMENSION} complex unit: Value is in inches. 166 | */ 167 | public static final int COMPLEX_UNIT_IN = 4; 168 | /** 169 | * {@link #TYPE_DIMENSION} complex unit: Value is in millimeters. 170 | */ 171 | public static final int COMPLEX_UNIT_MM = 5; 172 | 173 | /** 174 | * {@link #TYPE_FRACTION} complex unit: A basic fraction of the overall size. 175 | */ 176 | public static final int COMPLEX_UNIT_FRACTION = 0; 177 | /** 178 | * {@link #TYPE_FRACTION} complex unit: A fraction of the parent size. 179 | */ 180 | public static final int COMPLEX_UNIT_FRACTION_PARENT = 1; 181 | 182 | /** 183 | * Complex data: where the radix information is, telling where the decimal 184 | * place appears in the mantissa. 185 | */ 186 | public static final int COMPLEX_RADIX_SHIFT = 4; 187 | /** 188 | * Complex data: mask to extract radix information (after shifting by 189 | * {@link #COMPLEX_RADIX_SHIFT}). This give us 4 possible fixed point 190 | * representations as defined below. 191 | */ 192 | public static final int COMPLEX_RADIX_MASK = 0x3; 193 | 194 | /** 195 | * Complex data: the mantissa is an integral number -- i.e., 0xnnnnnn.0 196 | */ 197 | public static final int COMPLEX_RADIX_23p0 = 0; 198 | /** 199 | * Complex data: the mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn 200 | */ 201 | public static final int COMPLEX_RADIX_16p7 = 1; 202 | /** 203 | * Complex data: the mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn 204 | */ 205 | public static final int COMPLEX_RADIX_8p15 = 2; 206 | /** 207 | * Complex data: the mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn 208 | */ 209 | public static final int COMPLEX_RADIX_0p23 = 3; 210 | 211 | /** 212 | * Complex data: bit location of mantissa information. 213 | */ 214 | public static final int COMPLEX_MANTISSA_SHIFT = 8; 215 | /** 216 | * Complex data: mask to extract mantissa information (after shifting by 217 | * {@link #COMPLEX_MANTISSA_SHIFT}). This gives us 23 bits of precision; the 218 | * top bit is the sign. 219 | */ 220 | public static final int COMPLEX_MANTISSA_MASK = 0xffffff; 221 | 222 | /* ------------------------------------------------------------ */ 223 | 224 | /** 225 | * {@link #TYPE_NULL} data indicating the value was not specified. 226 | */ 227 | public static final int DATA_NULL_UNDEFINED = 0; 228 | /** 229 | * {@link #TYPE_NULL} data indicating the value was explicitly set to null. 230 | */ 231 | public static final int DATA_NULL_EMPTY = 1; 232 | 233 | /* ------------------------------------------------------------ */ 234 | 235 | /** 236 | * If {@link #density} is equal to this value, then the density should be 237 | * treated as the system's default density value: 238 | * {@link DisplayMetrics#DENSITY_DEFAULT}. 239 | */ 240 | public static final int DENSITY_DEFAULT = 0; 241 | 242 | /** 243 | * If {@link #density} is equal to this value, then there is no density 244 | * associated with the resource and it should not be scaled. 245 | */ 246 | public static final int DENSITY_NONE = 0xffff; 247 | 248 | /* ------------------------------------------------------------ */ 249 | 250 | /** 251 | * The type held by this value, as defined by the constants here. This tells 252 | * you how to interpret the other fields in the object. 253 | */ 254 | public int type; 255 | 256 | private static final float MANTISSA_MULT = 1.0f / (1 << TypedValue.COMPLEX_MANTISSA_SHIFT); 257 | private static final float[] RADIX_MULTS = new float[]{ 258 | 1.0f * MANTISSA_MULT, 1.0f / (1 << 7) * MANTISSA_MULT, 259 | 1.0f / (1 << 15) * MANTISSA_MULT, 1.0f / (1 << 23) * MANTISSA_MULT}; 260 | 261 | /** 262 | * Retrieve the base value from a complex data integer. This uses the 263 | * {@link #COMPLEX_MANTISSA_MASK} and {@link #COMPLEX_RADIX_MASK} fields of 264 | * the data to compute a floating point representation of the number they 265 | * describe. The units are ignored. 266 | * 267 | * @param complex A complex data value. 268 | * @return A floating point value corresponding to the complex data. 269 | */ 270 | public static float complexToFloat(int complex) { 271 | return (complex & (TypedValue.COMPLEX_MANTISSA_MASK << TypedValue.COMPLEX_MANTISSA_SHIFT)) 272 | * RADIX_MULTS[(complex >> TypedValue.COMPLEX_RADIX_SHIFT) 273 | & TypedValue.COMPLEX_RADIX_MASK]; 274 | } 275 | 276 | private static final String[] DIMENSION_UNIT_STRS = new String[]{"px", 277 | "dip", "sp", "pt", "in", "mm"}; 278 | private static final String[] FRACTION_UNIT_STRS = new String[]{"%", "%p"}; 279 | 280 | /** 281 | * Perform type conversion as per {@link #coerceToString()} on an explicitly 282 | * supplied type and data. 283 | * 284 | * @param type The data type identifier. 285 | * @param data The data value. 286 | * @return String The coerced string value. If the value is null or the type 287 | * is not known, null is returned. 288 | */ 289 | public static final String coerceToString(int type, int data) { 290 | switch (type) { 291 | case TYPE_NULL: 292 | return null; 293 | case TYPE_REFERENCE: 294 | return "@" + data; 295 | case TYPE_ATTRIBUTE: 296 | return "?" + data; 297 | case TYPE_FLOAT: 298 | return Float.toString(Float.intBitsToFloat(data)); 299 | case TYPE_DIMENSION: 300 | return Float.toString(complexToFloat(data)) 301 | + DIMENSION_UNIT_STRS[(data >> COMPLEX_UNIT_SHIFT) 302 | & COMPLEX_UNIT_MASK]; 303 | case TYPE_FRACTION: 304 | return Float.toString(complexToFloat(data) * 100) 305 | + FRACTION_UNIT_STRS[(data >> COMPLEX_UNIT_SHIFT) 306 | & COMPLEX_UNIT_MASK]; 307 | case TYPE_INT_HEX: 308 | return String.format("0x%08X", data); 309 | case TYPE_INT_BOOLEAN: 310 | return data != 0 ? "true" : "false"; 311 | } 312 | 313 | if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) { 314 | String res = String.format("%08x", data); 315 | char[] vals = res.toCharArray(); 316 | switch (type) { 317 | default: 318 | case TYPE_INT_COLOR_ARGB8:// #AaRrGgBb 319 | break; 320 | case TYPE_INT_COLOR_RGB8:// #FFRrGgBb->#RrGgBb 321 | res = res.substring(2); 322 | break; 323 | case TYPE_INT_COLOR_ARGB4:// #AARRGGBB->#ARGB 324 | res = new StringBuffer().append(vals[0]).append(vals[2]) 325 | .append(vals[4]).append(vals[6]).toString(); 326 | break; 327 | case TYPE_INT_COLOR_RGB4:// #FFRRGGBB->#RGB 328 | res = new StringBuffer().append(vals[2]).append(vals[4]) 329 | .append(vals[6]).toString(); 330 | break; 331 | } 332 | return "#" + res; 333 | } else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) { 334 | String res; 335 | switch (type) { 336 | default: 337 | case TYPE_INT_DEC: 338 | res = Integer.toString(data); 339 | break; 340 | } 341 | return res; 342 | } 343 | 344 | return null; 345 | } 346 | 347 | public static void initAttribute(StartTagChunk.Attribute attribute, String valueString) throws Exception { 348 | if (valueString.startsWith("@")) { 349 | attribute.setType(TYPE_REFERENCE); 350 | attribute.setData(Integer.valueOf(valueString.substring(1))); 351 | attribute.setValueString(-1); 352 | } else if (valueString.startsWith("?")) { 353 | attribute.setType(TYPE_ATTRIBUTE); 354 | attribute.setData(Integer.valueOf(valueString.substring(1))); 355 | attribute.setValueString(-1); 356 | } else if (isDimension(valueString)) { 357 | throw new Exception("Do not support dimension value in AndroidManifest"); 358 | } else if (isRadix(valueString)) { 359 | throw new Exception("Do not support radix value in AndroidManifest"); 360 | } else if (valueString.startsWith("#")) { 361 | throw new Exception("Do not support color value in AndroidManifest"); 362 | } else if (valueString.startsWith("0x")) { 363 | attribute.setType(TYPE_INT_HEX); 364 | attribute.setData(Integer.decode(valueString)); 365 | attribute.setValueString(-1); 366 | } else if (valueString.equals("true") || valueString.equals("false")) { 367 | attribute.setType(TYPE_INT_BOOLEAN); 368 | attribute.setData(valueString.equals("true") ? -1 : 0); 369 | attribute.setValueString(-1); 370 | } else if (isInteger(valueString)) { 371 | attribute.setType(16); 372 | attribute.setData(Integer.valueOf(valueString)); 373 | attribute.setValueString(-1); 374 | } else if (isFloat(valueString)) { 375 | attribute.setType(TYPE_FLOAT); 376 | attribute.setData(Float.floatToIntBits(Float.valueOf(valueString))); 377 | attribute.setValueString(-1); 378 | } else { 379 | attribute.setType(TYPE_NULL); 380 | throw new Exception("nothing get in attribute"); 381 | } 382 | } 383 | 384 | private static boolean isRadix(String valueString) { 385 | for (int i = 0; i < FRACTION_UNIT_STRS.length; i++) { 386 | if (valueString.endsWith(FRACTION_UNIT_STRS[i])) { 387 | return true; 388 | } 389 | } 390 | return false; 391 | } 392 | 393 | private static boolean isDimension(String valueString) { 394 | for (int i = 0; i < DIMENSION_UNIT_STRS.length; i++) { 395 | if (valueString.endsWith(DIMENSION_UNIT_STRS[i])) { 396 | try { 397 | Integer.parseInt(valueString.substring(0, valueString.length() - DIMENSION_UNIT_STRS[i].length())); 398 | return true; 399 | } catch (Exception e) { 400 | continue; 401 | } 402 | } 403 | } 404 | return false; 405 | } 406 | 407 | public static boolean isInteger(String s) { 408 | try { 409 | Integer.parseInt(s); 410 | } catch (NumberFormatException e) { 411 | return false; 412 | } catch (NullPointerException e) { 413 | return false; 414 | } 415 | return true; 416 | } 417 | 418 | public static boolean isFloat(String s) { 419 | try { 420 | Float.parseFloat(s); 421 | } catch (NumberFormatException e) { 422 | return false; 423 | } catch (NullPointerException e) { 424 | return false; 425 | } 426 | return true; 427 | } 428 | 429 | } 430 | -------------------------------------------------------------------------------- /test-data/AndroidManifest-normal.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaratustraN/axml-parser/fb1501185346b3c8e73baec222055d62452c5fb6/test-data/AndroidManifest-normal.xml -------------------------------------------------------------------------------- /test-data/AndroidManifest.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaratustraN/axml-parser/fb1501185346b3c8e73baec222055d62452c5fb6/test-data/AndroidManifest.xml -------------------------------------------------------------------------------- /test-data/abc.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaratustraN/axml-parser/fb1501185346b3c8e73baec222055d62452c5fb6/test-data/abc.apk -------------------------------------------------------------------------------- /test-data/app-debug.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaratustraN/axml-parser/fb1501185346b3c8e73baec222055d62452c5fb6/test-data/app-debug.apk -------------------------------------------------------------------------------- /test-data/app-release-normal.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaratustraN/axml-parser/fb1501185346b3c8e73baec222055d62452c5fb6/test-data/app-release-normal.apk --------------------------------------------------------------------------------