Apktool/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java

995 lines
32 KiB
Java

/*
* Copyright (C) 2010 Ryszard Wiśniewski <brut.alll@gmail.com>
* Copyright (C) 2010 Connor Tumbleson <connor.tumbleson@gmail.com>
*
* 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
*
* https://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 brut.androlib.res.decoder;
import android.content.res.XmlResourceParser;
import android.util.TypedValue;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResID;
import brut.androlib.res.xml.ResXmlEncoders;
import brut.util.ExtDataInput;
import com.google.common.io.LittleEndianDataInputStream;
import org.xmlpull.v1.XmlPullParserException;
import java.io.DataInput;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Binary xml files parser.
*
* Parser has only two states: (1) Operational state, which parser
* obtains after first successful call to next() and retains until
* open(), close(), or failed call to next(). (2) Closed state, which
* parser obtains after open(), close(), or failed call to next(). In
* this state methods return invalid values or throw exceptions.
*
* TODO: * check all methods in closed state
*/
public class AXmlResourceParser implements XmlResourceParser {
public AXmlResourceParser() {
resetEventInfo();
}
public AXmlResourceParser(InputStream stream) {
this();
open(stream);
}
public AndrolibException getFirstError() {
return mFirstError;
}
public ResAttrDecoder getAttrDecoder() {
return mAttrDecoder;
}
public void setAttrDecoder(ResAttrDecoder attrDecoder) {
mAttrDecoder = attrDecoder;
}
public void open(InputStream stream) {
close();
if (stream != null) {
// We need to explicitly cast to DataInput as otherwise the constructor is ambiguous.
// We choose DataInput instead of InputStream as ExtDataInput wraps an InputStream in
// a DataInputStream which is big-endian and ignores the little-endian behavior.
m_reader = new ExtDataInput((DataInput) new LittleEndianDataInputStream(stream));
}
}
@Override
public void close() {
if (!m_operational) {
return;
}
m_operational = false;
m_reader = null;
m_strings = null;
m_resourceIDs = null;
m_namespaces.reset();
resetEventInfo();
}
// ///////////////////////////////// iteration
@Override
public int next() throws XmlPullParserException, IOException {
if (m_reader == null) {
throw new XmlPullParserException("Parser is not opened.", this, null);
}
try {
doNext();
return m_event;
} catch (IOException e) {
close();
throw e;
}
}
@Override
public int nextToken() throws XmlPullParserException, IOException {
return next();
}
@Override
public int nextTag() throws XmlPullParserException, IOException {
int eventType = next();
if (eventType == TEXT && isWhitespace()) {
eventType = next();
}
if (eventType != START_TAG && eventType != END_TAG) {
throw new XmlPullParserException("Expected start or end tag.", this, null);
}
return eventType;
}
@Override
public String nextText() throws XmlPullParserException, IOException {
if (getEventType() != START_TAG) {
throw new XmlPullParserException("Parser must be on START_TAG to read next text.", this, null);
}
int eventType = next();
if (eventType == TEXT) {
String result = getText();
eventType = next();
if (eventType != END_TAG) {
throw new XmlPullParserException("Event TEXT must be immediately followed by END_TAG.", this, null);
}
return result;
} else if (eventType == END_TAG) {
return "";
} else {
throw new XmlPullParserException("Parser must be on START_TAG or TEXT to read text.", this, null);
}
}
@Override
public void require(int type, String namespace, String name)
throws XmlPullParserException, IOException {
if (type != getEventType() || (namespace != null && !namespace.equals(getNamespace()))
|| (name != null && !name.equals(getName()))) {
throw new XmlPullParserException(TYPES[type] + " is expected.", this, null);
}
}
@Override
public int getDepth() {
return m_namespaces.getDepth() - 1;
}
@Override
public int getEventType() throws XmlPullParserException {
return m_event;
}
@Override
public int getLineNumber() {
return m_lineNumber;
}
@Override
public String getName() {
if (m_name == -1 || (m_event != START_TAG && m_event != END_TAG)) {
return null;
}
return m_strings.getString(m_name);
}
@Override
public String getText() {
if (m_name == -1 || m_event != TEXT) {
return null;
}
return m_strings.getString(m_name);
}
@Override
public char[] getTextCharacters(int[] holderForStartAndLength) {
String text = getText();
if (text == null) {
return null;
}
holderForStartAndLength[0] = 0;
holderForStartAndLength[1] = text.length();
char[] chars = new char[text.length()];
text.getChars(0, text.length(), chars, 0);
return chars;
}
@Override
public String getNamespace() {
return m_strings.getString(m_namespaceUri);
}
@Override
public String getPrefix() {
int prefix = m_namespaces.findPrefix(m_namespaceUri);
return m_strings.getString(prefix);
}
@Override
public String getPositionDescription() {
return "XML line #" + getLineNumber();
}
@Override
public int getNamespaceCount(int depth) {
return m_namespaces.getAccumulatedCount(depth);
}
@Override
public String getNamespacePrefix(int pos) {
int prefix = m_namespaces.getPrefix(pos);
return m_strings.getString(prefix);
}
@Override
public String getNamespaceUri(int pos) {
int uri = m_namespaces.getUri(pos);
return m_strings.getString(uri);
}
// ///////////////////////////////// attributes
@Override
public String getClassAttribute() {
if (m_classAttribute == -1) {
return null;
}
int offset = getAttributeOffset(m_classAttribute);
int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING];
return m_strings.getString(value);
}
@Override
public String getIdAttribute() {
if (m_idAttribute == -1) {
return null;
}
int offset = getAttributeOffset(m_idAttribute);
int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING];
return m_strings.getString(value);
}
@Override
public int getIdAttributeResourceValue(int defaultValue) {
if (m_idAttribute == -1) {
return defaultValue;
}
int offset = getAttributeOffset(m_idAttribute);
int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE];
if (valueType != TypedValue.TYPE_REFERENCE) {
return defaultValue;
}
return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA];
}
@Override
public int getStyleAttribute() {
if (m_styleAttribute == -1) {
return 0;
}
int offset = getAttributeOffset(m_styleAttribute);
return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA];
}
@Override
public int getAttributeCount() {
if (m_event != START_TAG) {
return -1;
}
return m_attributes.length / ATTRIBUTE_LENGTH;
}
@Override
public String getAttributeNamespace(int index) {
int offset = getAttributeOffset(index);
int namespace = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI];
if (namespace == -1) {
return "";
}
// Minifiers like removing the namespace, so we will default to default namespace
// unless the pkgId of the resource is private. We will grab the non-standard one.
String value = m_strings.getString(namespace);
if (value.length() == 0) {
ResID resourceId = new ResID(getAttributeNameResource(index));
if (resourceId.package_ == PRIVATE_PKG_ID) {
value = getNonDefaultNamespaceUri(offset);
} else {
value = android_ns;
}
}
return value;
}
private String getNonDefaultNamespaceUri(int offset) {
String prefix = m_strings.getString(m_namespaces.getPrefix(offset));
if (prefix != null) {
return m_strings.getString(m_namespaces.getUri(offset));
}
// If we are here. There is some clever obfuscation going on. Our reference points to the namespace are gone.
// Normally we could take the index * attributeCount to get an offset.
// That would point to the URI in the StringBlock table, but that is empty.
// We have the namespaces that can't be touched in the opening tag.
// Though no known way to correlate them at this time.
// So return the res-auto namespace.
return "http://schemas.android.com/apk/res-auto";
}
@Override
public String getAttributePrefix(int index) {
int offset = getAttributeOffset(index);
int uri = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI];
int prefix = m_namespaces.findPrefix(uri);
if (prefix == -1) {
return "";
}
return m_strings.getString(prefix);
}
@Override
public String getAttributeName(int index) {
int offset = getAttributeOffset(index);
int name = m_attributes[offset + ATTRIBUTE_IX_NAME];
if (name == -1) {
return "";
}
String value = m_strings.getString(name);
String namespace = getAttributeNamespace(index);
// If attribute name is lacking or a private namespace emerges,
// retrieve the exact attribute name by its id.
if (value == null || value.length() == 0) {
try {
value = mAttrDecoder.decodeManifestAttr(getAttributeNameResource(index));
if (value == null) {
value = "";
}
} catch (AndrolibException e) {
value = "";
}
} else if (! namespace.equals(android_ns)) {
try {
String obfuscatedName = mAttrDecoder.decodeManifestAttr(getAttributeNameResource(index));
if (! (obfuscatedName == null || obfuscatedName.equals(value))) {
value = obfuscatedName;
}
} catch (AndrolibException ignored) {}
}
return value;
}
@Override
public int getAttributeNameResource(int index) {
int offset = getAttributeOffset(index);
int name = m_attributes[offset + ATTRIBUTE_IX_NAME];
if (m_resourceIDs == null || name < 0 || name >= m_resourceIDs.length) {
return 0;
}
return m_resourceIDs[name];
}
@Override
public int getAttributeValueType(int index) {
int offset = getAttributeOffset(index);
return m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE];
}
@Override
public int getAttributeValueData(int index) {
int offset = getAttributeOffset(index);
return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA];
}
@Override
public String getAttributeValue(int index) {
int offset = getAttributeOffset(index);
int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE];
int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA];
int valueRaw = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING];
if (mAttrDecoder != null) {
try {
String value = valueRaw == -1 ? null : ResXmlEncoders.escapeXmlChars(m_strings.getString(valueRaw));
String obfuscatedValue = mAttrDecoder.decodeManifestAttr(valueData);
if (! (value == null || obfuscatedValue == null)) {
int slashPos = value.lastIndexOf("/");
if (slashPos != -1) {
// Handle a value with a format of "@yyy/xxx"
String dir = value.substring(0, slashPos);
value = dir + "/"+ obfuscatedValue;
} else if (! value.equals(obfuscatedValue)) {
value = obfuscatedValue;
}
}
return mAttrDecoder.decode(
valueType,
valueData,
value,
getAttributeNameResource(index)
);
} catch (AndrolibException ex) {
setFirstError(ex);
LOGGER.log(Level.WARNING, String.format("Could not decode attr value, using undecoded value "
+ "instead: ns=%s, name=%s, value=0x%08x",
getAttributePrefix(index),
getAttributeName(index),
valueData), ex);
}
}
return TypedValue.coerceToString(valueType, valueData);
}
@Override
public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
return getAttributeIntValue(index, defaultValue ? 1 : 0) != 0;
}
@Override
public float getAttributeFloatValue(int index, float defaultValue) {
int offset = getAttributeOffset(index);
int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE];
if (valueType == TypedValue.TYPE_FLOAT) {
int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA];
return Float.intBitsToFloat(valueData);
}
return defaultValue;
}
@Override
public int getAttributeIntValue(int index, int defaultValue) {
int offset = getAttributeOffset(index);
int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE];
if (valueType >= TypedValue.TYPE_FIRST_INT && valueType <= TypedValue.TYPE_LAST_INT) {
return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA];
}
return defaultValue;
}
@Override
public int getAttributeUnsignedIntValue(int index, int defaultValue) {
return getAttributeIntValue(index, defaultValue);
}
@Override
public int getAttributeResourceValue(int index, int defaultValue) {
int offset = getAttributeOffset(index);
int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE];
if (valueType == TypedValue.TYPE_REFERENCE) {
return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA];
}
return defaultValue;
}
@Override
public String getAttributeValue(String namespace, String attribute) {
int index = findAttribute(namespace, attribute);
if (index == -1) {
return "";
}
return getAttributeValue(index);
}
@Override
public boolean getAttributeBooleanValue(String namespace, String attribute, boolean defaultValue) {
int index = findAttribute(namespace, attribute);
if (index == -1) {
return defaultValue;
}
return getAttributeBooleanValue(index, defaultValue);
}
@Override
public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) {
int index = findAttribute(namespace, attribute);
if (index == -1) {
return defaultValue;
}
return getAttributeFloatValue(index, defaultValue);
}
@Override
public int getAttributeIntValue(String namespace, String attribute, int defaultValue) {
int index = findAttribute(namespace, attribute);
if (index == -1) {
return defaultValue;
}
return getAttributeIntValue(index, defaultValue);
}
@Override
public int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue) {
int index = findAttribute(namespace, attribute);
if (index == -1) {
return defaultValue;
}
return getAttributeUnsignedIntValue(index, defaultValue);
}
@Override
public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
int index = findAttribute(namespace, attribute);
if (index == -1) {
return defaultValue;
}
return getAttributeResourceValue(index, defaultValue);
}
@Override
public int getAttributeListValue(int index, String[] options, int defaultValue) {
// TODO implement
return 0;
}
@Override
public int getAttributeListValue(String namespace, String attribute, String[] options, int defaultValue) {
// TODO implement
return 0;
}
@Override
public String getAttributeType(int index) {
return "CDATA";
}
@Override
public boolean isAttributeDefault(int index) {
return false;
}
// ///////////////////////////////// dummies
@Override
public void setInput(InputStream stream, String inputEncoding) {
open(stream);
}
@Override
public void setInput(Reader reader) throws XmlPullParserException {
throw new XmlPullParserException(E_NOT_SUPPORTED);
}
@Override
public String getInputEncoding() {
return null;
}
@Override
public int getColumnNumber() {
return -1;
}
@Override
public boolean isEmptyElementTag() {
return false;
}
@Override
public boolean isWhitespace() {
return false;
}
@Override
public void defineEntityReplacementText(String entityName, String replacementText)
throws XmlPullParserException {
throw new XmlPullParserException(E_NOT_SUPPORTED);
}
@Override
public String getNamespace(String prefix) {
throw new RuntimeException(E_NOT_SUPPORTED);
}
@Override
public Object getProperty(String name) {
return null;
}
@Override
public void setProperty(String name, Object value)
throws XmlPullParserException {
throw new XmlPullParserException(E_NOT_SUPPORTED);
}
@Override
public boolean getFeature(String feature) {
return false;
}
@Override
public void setFeature(String name, boolean value)
throws XmlPullParserException {
throw new XmlPullParserException(E_NOT_SUPPORTED);
}
// /////////////////////////////////////////// implementation
/**
* Namespace stack, holds prefix+uri pairs, as well as depth information.
* All information is stored in one int[] array. Array consists of depth
* frames: Data=DepthFrame*; DepthFrame=Count+[Prefix+Uri]*+Count;
* Count='count of Prefix+Uri pairs'; Yes, count is stored twice, to enable
* bottom-up traversal. increaseDepth adds depth frame, decreaseDepth
* removes it. push/pop operations operate only in current depth frame.
* decreaseDepth removes any remaining (not pop'ed) namespace pairs. findXXX
* methods search all depth frames starting from the last namespace pair of
* current depth frame. All functions that operate with int, use -1 as
* 'invalid value'.
*
* !! functions expect 'prefix'+'uri' pairs, not 'uri'+'prefix' !!
*
*/
private static final class NamespaceStack {
public NamespaceStack() {
m_data = new int[32];
}
public void reset() {
m_dataLength = 0;
m_depth = 0;
}
public int getCurrentCount() {
if (m_dataLength == 0) {
return 0;
}
int offset = m_dataLength - 1;
return m_data[offset];
}
public int getAccumulatedCount(int depth) {
if (m_dataLength == 0 || depth < 0) {
return 0;
}
if (depth > m_depth) {
depth = m_depth;
}
int accumulatedCount = 0;
int offset = 0;
for (; depth != 0; --depth) {
int count = m_data[offset];
accumulatedCount += count;
offset += (2 + count * 2);
}
return accumulatedCount;
}
public void push(int prefix, int uri) {
if (m_depth == 0) {
increaseDepth();
}
ensureDataCapacity(2);
int offset = m_dataLength - 1;
int count = m_data[offset];
m_data[offset - 1 - count * 2] = count + 1;
m_data[offset] = prefix;
m_data[offset + 1] = uri;
m_data[offset + 2] = count + 1;
m_dataLength += 2;
}
public boolean pop() {
if (m_dataLength == 0) {
return false;
}
int offset = m_dataLength - 1;
int count = m_data[offset];
if (count == 0) {
return false;
}
count -= 1;
offset -= 2;
m_data[offset] = count;
offset -= (1 + count * 2);
m_data[offset] = count;
m_dataLength -= 2;
return true;
}
public int getPrefix(int index) {
return get(index, true);
}
public int getUri(int index) {
return get(index, false);
}
public int findPrefix(int uri) {
return find(uri, false);
}
public int getDepth() {
return m_depth;
}
public void increaseDepth() {
ensureDataCapacity(2);
int offset = m_dataLength;
m_data[offset] = 0;
m_data[offset + 1] = 0;
m_dataLength += 2;
m_depth += 1;
}
public void decreaseDepth() {
if (m_dataLength == 0) {
return;
}
int offset = m_dataLength - 1;
int count = m_data[offset];
if ((offset - 1 - count * 2) == 0) {
return;
}
m_dataLength -= 2 + count * 2;
m_depth -= 1;
}
private void ensureDataCapacity(int capacity) {
int available = (m_data.length - m_dataLength);
if (available > capacity) {
return;
}
int newLength = (m_data.length + available) * 2;
int[] newData = new int[newLength];
System.arraycopy(m_data, 0, newData, 0, m_dataLength);
m_data = newData;
}
private int find(int prefixOrUri, boolean prefix) {
if (m_dataLength == 0) {
return -1;
}
int offset = m_dataLength - 1;
for (int i = m_depth; i != 0; --i) {
int count = m_data[offset];
offset -= 2;
for (; count != 0; --count) {
if (prefix) {
if (m_data[offset] == prefixOrUri) {
return m_data[offset + 1];
}
} else {
if (m_data[offset + 1] == prefixOrUri) {
return m_data[offset];
}
}
offset -= 2;
}
}
return -1;
}
private int get(int index, boolean prefix) {
if (m_dataLength == 0 || index < 0) {
return -1;
}
int offset = 0;
for (int i = m_depth; i != 0; --i) {
int count = m_data[offset];
if (index >= count) {
index -= count;
offset += (2 + count * 2);
continue;
}
offset += (1 + index * 2);
if (!prefix) {
offset += 1;
}
return m_data[offset];
}
return -1;
}
private int[] m_data;
private int m_dataLength;
private int m_depth;
}
private int getAttributeOffset(int index) {
if (m_event != START_TAG) {
throw new IndexOutOfBoundsException("Current event is not START_TAG.");
}
int offset = index * ATTRIBUTE_LENGTH;
if (offset >= m_attributes.length) {
throw new IndexOutOfBoundsException("Invalid attribute index (" + index + ").");
}
return offset;
}
private int findAttribute(String namespace, String attribute) {
if (m_strings == null || attribute == null) {
return -1;
}
int name = m_strings.find(attribute);
if (name == -1) {
return -1;
}
int uri = (namespace != null) ? m_strings.find(namespace) : -1;
for (int o = 0; o != m_attributes.length; o += ATTRIBUTE_LENGTH) {
if (name == m_attributes[o + ATTRIBUTE_IX_NAME]
&& (uri == -1 || uri == m_attributes[o + ATTRIBUTE_IX_NAMESPACE_URI])) {
return o / ATTRIBUTE_LENGTH;
}
}
return -1;
}
private void resetEventInfo() {
m_event = -1;
m_lineNumber = -1;
m_name = -1;
m_namespaceUri = -1;
m_attributes = null;
m_idAttribute = -1;
m_classAttribute = -1;
m_styleAttribute = -1;
}
private void doNext() throws IOException {
// Delayed initialization.
if (m_strings == null) {
m_reader.skipCheckInt(CHUNK_AXML_FILE, CHUNK_AXML_FILE_BROKEN);
/*
* chunkSize
*/
m_reader.skipInt();
m_strings = StringBlock.read(m_reader);
m_namespaces.increaseDepth();
m_operational = true;
}
if (m_event == END_DOCUMENT) {
return;
}
int event = m_event;
resetEventInfo();
while (true) {
if (m_decreaseDepth) {
m_decreaseDepth = false;
m_namespaces.decreaseDepth();
}
// Fake END_DOCUMENT event.
if (event == END_TAG && m_namespaces.getDepth() == 1 && m_namespaces.getCurrentCount() == 0) {
m_event = END_DOCUMENT;
break;
}
int chunkType;
if (event == START_DOCUMENT) {
// Fake event, see CHUNK_XML_START_TAG handler.
chunkType = CHUNK_XML_START_TAG;
} else {
chunkType = m_reader.readInt();
}
if (chunkType == CHUNK_RESOURCEIDS) {
int chunkSize = m_reader.readInt();
if (chunkSize < 8 || (chunkSize % 4) != 0) {
throw new IOException("Invalid resource ids size (" + chunkSize + ").");
}
m_resourceIDs = m_reader.readIntArray(chunkSize / 4 - 2);
continue;
}
if (chunkType < CHUNK_XML_FIRST || chunkType > CHUNK_XML_LAST) {
throw new IOException("Invalid chunk type (" + chunkType + ").");
}
// Fake START_DOCUMENT event.
if (chunkType == CHUNK_XML_START_TAG && event == -1) {
m_event = START_DOCUMENT;
break;
}
// Common header.
/* chunkSize */m_reader.skipInt();
int lineNumber = m_reader.readInt();
/* 0xFFFFFFFF */m_reader.skipInt();
if (chunkType == CHUNK_XML_START_NAMESPACE || chunkType == CHUNK_XML_END_NAMESPACE) {
if (chunkType == CHUNK_XML_START_NAMESPACE) {
int prefix = m_reader.readInt();
int uri = m_reader.readInt();
m_namespaces.push(prefix, uri);
} else {
/* prefix */m_reader.skipInt();
/* uri */m_reader.skipInt();
m_namespaces.pop();
}
continue;
}
m_lineNumber = lineNumber;
if (chunkType == CHUNK_XML_START_TAG) {
m_namespaceUri = m_reader.readInt();
m_name = m_reader.readInt();
/* flags? */m_reader.skipInt();
int attributeCount = m_reader.readInt();
m_idAttribute = (attributeCount >>> 16) - 1;
attributeCount &= 0xFFFF;
m_classAttribute = m_reader.readInt();
m_styleAttribute = (m_classAttribute >>> 16) - 1;
m_classAttribute = (m_classAttribute & 0xFFFF) - 1;
m_attributes = m_reader.readIntArray(attributeCount * ATTRIBUTE_LENGTH);
for (int i = ATTRIBUTE_IX_VALUE_TYPE; i < m_attributes.length; ) {
m_attributes[i] = (m_attributes[i] >>> 24);
i += ATTRIBUTE_LENGTH;
}
m_namespaces.increaseDepth();
m_event = START_TAG;
break;
}
if (chunkType == CHUNK_XML_END_TAG) {
m_namespaceUri = m_reader.readInt();
m_name = m_reader.readInt();
m_event = END_TAG;
m_decreaseDepth = true;
break;
}
if (chunkType == CHUNK_XML_TEXT) {
m_name = m_reader.readInt();
/* ? */m_reader.skipInt();
/* ? */m_reader.skipInt();
m_event = TEXT;
break;
}
}
}
private void setFirstError(AndrolibException error) {
if (mFirstError == null) {
mFirstError = error;
}
}
// ///////////////////////////////// data
/*
* All values are essentially indices, e.g. m_name is an index of name in
* m_strings.
*/
private ExtDataInput m_reader;
private ResAttrDecoder mAttrDecoder;
private AndrolibException mFirstError;
private boolean m_operational = false;
private StringBlock m_strings;
private int[] m_resourceIDs;
private final NamespaceStack m_namespaces = new NamespaceStack();
private final String android_ns = "http://schemas.android.com/apk/res/android";
private boolean m_decreaseDepth;
private int m_event;
private int m_lineNumber;
private int m_name;
private int m_namespaceUri;
private int[] m_attributes;
private int m_idAttribute;
private int m_classAttribute;
private int m_styleAttribute;
private final static Logger LOGGER = Logger.getLogger(AXmlResourceParser.class.getName());
private static final String E_NOT_SUPPORTED = "Method is not supported.";
private static final int ATTRIBUTE_IX_NAMESPACE_URI = 0,
ATTRIBUTE_IX_NAME = 1, ATTRIBUTE_IX_VALUE_STRING = 2,
ATTRIBUTE_IX_VALUE_TYPE = 3, ATTRIBUTE_IX_VALUE_DATA = 4,
ATTRIBUTE_LENGTH = 5;
private static final int CHUNK_AXML_FILE = 0x00080003, CHUNK_AXML_FILE_BROKEN = 0x00080001,
CHUNK_RESOURCEIDS = 0x00080180, CHUNK_XML_FIRST = 0x00100100,
CHUNK_XML_START_NAMESPACE = 0x00100100,
CHUNK_XML_END_NAMESPACE = 0x00100101,
CHUNK_XML_START_TAG = 0x00100102, CHUNK_XML_END_TAG = 0x00100103,
CHUNK_XML_TEXT = 0x00100104, CHUNK_XML_LAST = 0x00100104;
private static final int PRIVATE_PKG_ID = 0x7F;
}