diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java index 679af932..a22072b2 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java @@ -19,10 +19,15 @@ package brut.androlib.res.decoder; import android.content.res.XmlResourceParser; import android.util.TypedValue; import brut.androlib.exceptions.AndrolibException; +import brut.androlib.exceptions.CantFindFrameworkResException; +import brut.androlib.exceptions.UndefinedResObjectException; import brut.androlib.res.data.ResID; +import brut.androlib.res.data.ResResSpec; import brut.androlib.res.data.ResTable; import brut.androlib.res.data.arsc.ARSCHeader; import brut.androlib.res.data.axml.NamespaceStack; +import brut.androlib.res.data.value.ResAttr; +import brut.androlib.res.data.value.ResScalarValue; import brut.androlib.res.xml.ResXmlEncoders; import brut.util.ExtCountingDataInput; import com.google.common.io.LittleEndianDataInputStream; @@ -44,20 +49,16 @@ import java.util.logging.Logger; public class AXmlResourceParser implements XmlResourceParser { public AXmlResourceParser(ResTable resTable) { + mResTable = resTable; resetEventInfo(); - setAttrDecoder(new ResAttrDecoder(resTable)); } public AndrolibException getFirstError() { return mFirstError; } - public ResAttrDecoder getAttrDecoder() { - return mAttrDecoder; - } - - public void setAttrDecoder(ResAttrDecoder attrDecoder) { - mAttrDecoder = attrDecoder; + public ResTable getResTable() { + return mResTable; } public void open(InputStream stream) { @@ -298,6 +299,19 @@ public class AXmlResourceParser implements XmlResourceParser { return value; } + public String decodeFromResourceId(int attrResId) throws AndrolibException { + if (attrResId != 0) { + try { + ResResSpec resResSpec = mResTable.getResSpec(attrResId); + if (resResSpec != null) { + return resResSpec.getName(); + } + } catch (UndefinedResObjectException | CantFindFrameworkResException ignored) {} + } + + return null; + } + private String getNonDefaultNamespaceUri(int offset) { String prefix = mStringBlock.getString(mNamespaces.getPrefix(offset)); if (prefix != null) { @@ -337,7 +351,7 @@ public class AXmlResourceParser implements XmlResourceParser { int resourceId = getAttributeNameResource(index); try { - resourceMapValue = mAttrDecoder.decodeFromResourceId(resourceId); + resourceMapValue = decodeFromResourceId(resourceId); } catch (AndrolibException ignored) { resourceMapValue = null; } @@ -389,48 +403,56 @@ public class AXmlResourceParser implements XmlResourceParser { int valueData = mAttributes[offset + ATTRIBUTE_IX_VALUE_DATA]; int valueRaw = mAttributes[offset + ATTRIBUTE_IX_VALUE_STRING]; - if (mAttrDecoder != null) { - try { - String stringBlockValue = valueRaw == -1 ? null : ResXmlEncoders.escapeXmlChars(mStringBlock.getString(valueRaw)); - String resourceMapValue = null; + try { + String stringBlockValue = valueRaw == -1 ? null : ResXmlEncoders.escapeXmlChars(mStringBlock.getString(valueRaw)); + String resourceMapValue = null; - // Ensure we only track down obfuscated values for reference/attribute type values. Otherwise we might - // spam lookups against resource table for invalid ids. - if (valueType == TypedValue.TYPE_REFERENCE || valueType == TypedValue.TYPE_DYNAMIC_REFERENCE || - valueType == TypedValue.TYPE_ATTRIBUTE || valueType == TypedValue.TYPE_DYNAMIC_ATTRIBUTE) { - resourceMapValue = mAttrDecoder.decodeFromResourceId(valueData); - } - String value = stringBlockValue; - - if (stringBlockValue != null && resourceMapValue != null) { - int slashPos = stringBlockValue.lastIndexOf("/"); - int colonPos = stringBlockValue.lastIndexOf(":"); - - // Handle a value with a format of "@yyy/xxx", but avoid "@yyy/zzz:xxx" - if (slashPos != -1) { - if (colonPos == -1) { - String type = stringBlockValue.substring(0, slashPos); - value = type + "/" + resourceMapValue; - } - } else if (! stringBlockValue.equals(resourceMapValue)) { - value = resourceMapValue; - } - } - - 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); + // Ensure we only track down obfuscated values for reference/attribute type values. Otherwise we might + // spam lookups against resource table for invalid ids. + if (valueType == TypedValue.TYPE_REFERENCE || valueType == TypedValue.TYPE_DYNAMIC_REFERENCE || + valueType == TypedValue.TYPE_ATTRIBUTE || valueType == TypedValue.TYPE_DYNAMIC_ATTRIBUTE) { + resourceMapValue = decodeFromResourceId(valueData); } + String value = stringBlockValue; + + if (stringBlockValue != null && resourceMapValue != null) { + int slashPos = stringBlockValue.lastIndexOf("/"); + int colonPos = stringBlockValue.lastIndexOf(":"); + + // Handle a value with a format of "@yyy/xxx", but avoid "@yyy/zzz:xxx" + if (slashPos != -1) { + if (colonPos == -1) { + String type = stringBlockValue.substring(0, slashPos); + value = type + "/" + resourceMapValue; + } + } else if (! stringBlockValue.equals(resourceMapValue)) { + value = resourceMapValue; + } + } + + // try to decode from resource table + int attrResId = getAttributeNameResource(index); + ResScalarValue resValue = mResTable.getCurrentResPackage() + .getValueFactory().factory(valueType, valueData, value); + + String decoded = null; + if (attrResId > 0) { + try { + ResAttr attr = (ResAttr) mResTable.getResSpec(attrResId).getDefaultResource().getValue(); + + decoded = attr.convertToResXmlFormat(resValue); + } catch (UndefinedResObjectException | ClassCastException ignored) {} + } + + return decoded != null ? decoded : resValue.encodeAsResXmlAttr(); + + } 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); } @@ -801,7 +823,7 @@ public class AXmlResourceParser implements XmlResourceParser { } private ExtCountingDataInput mIn; - private ResAttrDecoder mAttrDecoder; + private final ResTable mResTable; private AndrolibException mFirstError; private boolean isOperational = false; diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java deleted file mode 100644 index 7a12b637..00000000 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2010 Ryszard Wiśniewski - * Copyright (C) 2010 Connor Tumbleson - * - * 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 brut.androlib.exceptions.AndrolibException; -import brut.androlib.exceptions.CantFindFrameworkResException; -import brut.androlib.exceptions.UndefinedResObjectException; -import brut.androlib.res.data.ResID; -import brut.androlib.res.data.ResResSpec; -import brut.androlib.res.data.ResTable; -import brut.androlib.res.data.value.ResAttr; -import brut.androlib.res.data.value.ResScalarValue; - -public class ResAttrDecoder { - - public ResAttrDecoder(ResTable resTable) { - mResTable = resTable; - } - - public String decode(int type, int value, String rawValue, int attrResId) throws AndrolibException { - ResScalarValue resValue = mResTable.getCurrentResPackage().getValueFactory().factory(type, value, rawValue); - - String decoded = null; - if (attrResId > 0) { - try { - ResAttr attr = (ResAttr) mResTable.getResSpec(attrResId).getDefaultResource().getValue(); - - decoded = attr.convertToResXmlFormat(resValue); - } catch (UndefinedResObjectException | ClassCastException ignored) {} - } - - return decoded != null ? decoded : resValue.encodeAsResXmlAttr(); - } - - public String decodeFromResourceId(int attrResId) throws AndrolibException { - if (attrResId != 0) { - try { - ResResSpec resResSpec = mResTable.getResSpec(attrResId); - if (resResSpec != null) { - return resResSpec.getName(); - } - } catch (UndefinedResObjectException | CantFindFrameworkResException ignored) {} - } - - return null; - } - - public ResTable getResTable() throws AndrolibException { - if (mResTable == null) { - throw new AndrolibException("Res Table not set"); - } - return mResTable; - } - - public void setResTable(ResTable resTable) { - mResTable = resTable; - } - - private ResTable mResTable; -} diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java index 08cf8923..de7aff98 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java @@ -31,7 +31,7 @@ import org.xmlpull.v1.wrapper.classic.StaticXmlSerializerWrapper; import java.io.*; public class XmlPullStreamDecoder implements ResStreamDecoder { - public XmlPullStreamDecoder(XmlPullParser parser, + public XmlPullStreamDecoder(AXmlResourceParser parser, ExtXmlSerializer serializer) { this.mParser = parser; this.mSerial = serializer; @@ -43,7 +43,7 @@ public class XmlPullStreamDecoder implements ResStreamDecoder { try { XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance(); XmlPullParserWrapper par = factory.newPullParserWrapper(mParser); - final ResTable resTable = ((AXmlResourceParser) mParser).getAttrDecoder().getResTable(); + final ResTable resTable = mParser.getResTable(); XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial, factory) { boolean hideSdkInfo = false; @@ -149,6 +149,6 @@ public class XmlPullStreamDecoder implements ResStreamDecoder { decode(in, out); } - private final XmlPullParser mParser; + private final AXmlResourceParser mParser; private final ExtXmlSerializer mSerial; }