All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pxb.android.arsc.ArscParser Maven / Gradle / Ivy

Go to download

The axml components for reading binary Android XML files forked from a not further maintained repository created by Bob Pan

There is a newer version: 2.1.3
Show newest version
/*
 * Copyright (c) 2009-2013 Panxiaobo
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package pxb.android.arsc;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import pxb.android.ResConst;
import pxb.android.StringItems;

/**
 * 
 * Read the resources.arsc inside an Android apk.
 * 
 * Usage:
 * 
 * 
 * byte[] oldArscFile= ... ; //
 * List<Pkg> pkgs = new ArscParser(oldArscFile).parse(); // read the file
 * modify(pkgs); // do what you want here
 * byte[] newArscFile = new ArscWriter(pkgs).toByteArray(); // build a new file
 * 
* * The format of arsc is described here (gingerbread) *
    *
  • frameworks/base/libs/utils/ResourceTypes.cpp
  • *
  • frameworks/base/include/utils/ResourceTypes.h
  • *
* and the cmd line aapt d resources abc.apk is also good for debug * (available in android sdk) * *

* Todos: *

    * TODO add support to read styled strings *
* *

* Thanks to the the following projects *

    *
  • android4me https://code.google.com/p/android4me/
  • *
  • Apktool https://code.google.com/p/android-apktool
  • *
  • Android http://source.android.com/
  • *
* * @author bob * */ public class ArscParser implements ResConst { private static final Logger logger = LoggerFactory.getLogger(ArscParser.class); /* pkg */class Chunk { public final int headSize; public final int location; public final int size; public final int type; public Chunk() { location = in.position(); type = in.getShort() & 0xFFFF; headSize = in.getShort() & 0xFFFF; size = in.getInt(); D("[%08x]type: %04x, headsize: %04x, size:%08x", location, type, headSize, size); } } private static void D(String fmt, Object... args) { if (logger.isTraceEnabled()) { logger.trace(String.format(fmt, args)); } } /** * If set, this resource has been declared public, so libraries are allowed * to reference it. */ static final int ENGRY_FLAG_PUBLIC = 0x0002; /** * If set, this is a complex entry, holding a set of name/value mappings. It * is followed by an array of ResTable_map structures. */ final static short ENTRY_FLAG_COMPLEX = 0x0001; public static final int TYPE_STRING = 0x03; private int fileSize = -1; private ByteBuffer in; private String[] keyNamesX; private Pkg pkg; private List pkgs = new ArrayList(); private String[] strings; private String[] typeNamesX; public ArscParser(byte[] b) { this.in = ByteBuffer.wrap(b).order(ByteOrder.LITTLE_ENDIAN); } public List parse() throws IOException { if (fileSize < 0) { Chunk head = new Chunk(); if (head.type != RES_TABLE_TYPE) { throw new RuntimeException(); } fileSize = head.size; in.getInt();// packagecount } while (in.hasRemaining()) { Chunk chunk = new Chunk(); switch (chunk.type) { case RES_STRING_POOL_TYPE: strings = StringItems.read(in); if (logger.isTraceEnabled()) { for (int i = 0; i < strings.length; i++) { D("STR [%08x] %s", i, strings[i]); } } break; case RES_TABLE_PACKAGE_TYPE: readPackage(in); } in.position(chunk.location + chunk.size); } return pkgs; } // private void readConfigFlags() { // int size = in.getInt(); // if (size < 28) { // throw new RuntimeException(); // } // short mcc = in.getShort(); // short mnc = in.getShort(); // // char[] language = new char[] { (char) in.get(), (char) in.get() }; // char[] country = new char[] { (char) in.get(), (char) in.get() }; // // byte orientation = in.get(); // byte touchscreen = in.get(); // short density = in.getShort(); // // byte keyboard = in.get(); // byte navigation = in.get(); // byte inputFlags = in.get(); // byte inputPad0 = in.get(); // // short screenWidth = in.getShort(); // short screenHeight = in.getShort(); // // short sdkVersion = in.getShort(); // short minorVersion = in.getShort(); // // byte screenLayout = 0; // byte uiMode = 0; // short smallestScreenWidthDp = 0; // if (size >= 32) { // screenLayout = in.get(); // uiMode = in.get(); // smallestScreenWidthDp = in.getShort(); // } // // short screenWidthDp = 0; // short screenHeightDp = 0; // // if (size >= 36) { // screenWidthDp = in.getShort(); // screenHeightDp = in.getShort(); // } // // short layoutDirection = 0; // if (size >= 38 && sdkVersion >= 17) { // layoutDirection = in.getShort(); // } // // } private void readEntry(Config config, ResSpec spec) { D("[%08x]read ResTable_entry", in.position()); int size = in.getShort(); D("ResTable_entry %d", size); int flags = in.getShort(); // ENTRY_FLAG_PUBLIC int keyStr = in.getInt(); spec.updateName(keyNamesX[keyStr]); ResEntry resEntry = new ResEntry(flags, spec); if (0 != (flags & ENTRY_FLAG_COMPLEX)) { int parent = in.getInt(); int count = in.getInt(); BagValue bag = new BagValue(parent); for (int i = 0; i < count; i++) { Map.Entry entry = new AbstractMap.SimpleEntry(in.getInt(), readValue()); bag.map.add(entry); } resEntry.value = bag; } else { resEntry.value = readValue(); } config.resources.put(spec.id, resEntry); } private void readPackage(ByteBuffer in) throws IOException { int pid = in.getInt() % 0xFF; String name; { int nextPisition = in.position() + 128 * 2; StringBuilder sb = new StringBuilder(32); for (int i = 0; i < 128; i++) { int s = in.getShort(); if (s == 0) { break; } else { sb.append((char) s); } } name = sb.toString(); in.position(nextPisition); } pkg = new Pkg(pid, name); pkgs.add(pkg); int typeStringOff = in.getInt(); int typeNameCount = in.getInt(); int keyStringOff = in.getInt(); int specNameCount = in.getInt(); { Chunk chunk = new Chunk(); if (chunk.type != RES_STRING_POOL_TYPE) { throw new RuntimeException(); } typeNamesX = StringItems.read(in); in.position(chunk.location + chunk.size); } { Chunk chunk = new Chunk(); if (chunk.type != RES_STRING_POOL_TYPE) { throw new RuntimeException(); } keyNamesX = StringItems.read(in); if (logger.isTraceEnabled()) { for (int i = 0; i < keyNamesX.length; i++) { D("STR [%08x] %s", i, keyNamesX[i]); } } in.position(chunk.location + chunk.size); } out: while (in.hasRemaining()) { Chunk chunk = new Chunk(); switch (chunk.type) { case RES_TABLE_TYPE_SPEC_TYPE: { D("[%08x]read spec", in.position() - 8); int tid = in.get() & 0xFF; in.get(); // res0 in.getShort();// res1 int entryCount = in.getInt(); Type t = pkg.getType(tid, typeNamesX[tid - 1], entryCount); for (int i = 0; i < entryCount; i++) { t.getSpec(i).flags = in.getInt(); } } break; case RES_TABLE_TYPE_TYPE: { D("[%08x]read config", in.position() - 8); int tid = in.get() & 0xFF; in.get(); // res0 in.getShort();// res1 int entryCount = in.getInt(); Type t = pkg.getType(tid, typeNamesX[tid - 1], entryCount); int entriesStart = in.getInt(); D("[%08x]read config id", in.position()); int p = in.position(); int size = in.getInt(); // readConfigFlags(); byte[] data = new byte[size]; in.position(p); in.get(data); Config config = new Config(data, entryCount); in.position(chunk.location + chunk.headSize); D("[%08x]read config entry offset", in.position()); int[] entrys = new int[entryCount]; for (int i = 0; i < entryCount; i++) { entrys[i] = in.getInt(); } D("[%08x]read config entrys", in.position()); for (int i = 0; i < entrys.length; i++) { if (entrys[i] != -1) { in.position(chunk.location + entriesStart + entrys[i]); ResSpec spec = t.getSpec(i); readEntry(config, spec); } } t.addConfig(config); } break; default: break out; } in.position(chunk.location + chunk.size); } } private Object readValue() { int size1 = in.getShort();// 8 int zero = in.get();// 0 int type = in.get() & 0xFF; // TypedValue.* int data = in.getInt(); String raw = null; if (type == TYPE_STRING) { raw = strings[data]; } return new Value(type, data, raw); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy