Skip to content

Commit b49e770

Browse files
authored
refactor: clean up external pull parser and introduce brut.j.xml (#3709)
* refactor: clean up external pull parser and introduce brut.j.xml We have no need for an XML pull parser in the project, it was only used for testing, which is now done with XPath. The external xpp3 library from org.ogce is obsolete and has the issue of including javax.xml.namespace.QName which conflicts with the JRE implementation that exists for a very long time now. This makes direct usages of QName produce very obscure NPEs that took me hours to figure out. This patch will allow further optimization that is WIP. The external library was replaced by the basic xmlpull API. The MXSerializer has been cleaned and the features used by apktool have been integrated into the custom implementation, now part of a separate module called brut.j.xml. Writing has been optimized by buffering write operations, inspired by KXmlSerializer used by Android itself. A class XmlPullUtils also written that allows copying from a XmlPullParser into a XmlSerializer with or without an EventHandler. We use it for AndroidManifestPullStreamDecoder (with EventHandler, to allow omitting the uses-sdk tag), and for ResXmlPullStreamDecoder (direct copy, without EventHandler). saveDocument in ResXmlPatcher was tweaked to output proper output - a new line after declaration and a new line after root element's end tag. TL;DR mostly behind the scene refactor, no end user changes.
1 parent 7033f4e commit b49e770

18 files changed

+783
-735
lines changed

brut.apktool/apktool-cli/proguard-rules.pro

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66
static ** valueOf(java.lang.String);
77
}
88

9-
# https://github.com/iBotPeaches/Apktool/issues/3602#issuecomment-2117317880
10-
-dontwarn org.xmlpull.mxp1**
11-
129
# https://github.com/iBotPeaches/Apktool/pull/3670#issuecomment-2296326878
1310
-dontwarn com.google.j2objc.annotations.Weak
1411
-dontwarn com.google.j2objc.annotations.RetainedWith

brut.apktool/apktool-lib/build.gradle.kts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ tasks {
2525
}
2626

2727
dependencies {
28-
api(project(":brut.j.dir"))
29-
api(project(":brut.j.util"))
3028
api(project(":brut.j.common"))
29+
api(project(":brut.j.util"))
30+
api(project(":brut.j.dir"))
31+
api(project(":brut.j.xml"))
3132

3233
implementation(libs.baksmali)
3334
implementation(libs.smali)
34-
implementation(libs.xmlpull)
3535
implementation(libs.guava)
3636
implementation(libs.commons.lang3)
3737
implementation(libs.commons.io)

brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResourcesDecoder.java

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,12 @@
2121
import brut.androlib.exceptions.AndrolibException;
2222
import brut.androlib.res.data.*;
2323
import brut.androlib.res.decoder.*;
24-
import brut.androlib.res.util.ExtMXSerializer;
25-
import brut.androlib.res.util.ExtXmlSerializer;
2624
import brut.androlib.res.xml.ResValuesXmlSerializable;
2725
import brut.androlib.res.xml.ResXmlPatcher;
2826
import brut.directory.Directory;
2927
import brut.directory.DirectoryException;
3028
import brut.directory.FileDirectory;
29+
import brut.xmlpull.MXSerializer;
3130
import org.apache.commons.io.IOUtils;
3231
import org.xmlpull.v1.XmlSerializer;
3332

@@ -75,7 +74,8 @@ public void decodeManifest(File outDir) throws AndrolibException {
7574
}
7675

7776
AXmlResourceParser axmlParser = new AndroidManifestResourceParser(mResTable);
78-
ResStreamDecoder fileDecoder = new AndroidManifestPullStreamDecoder(axmlParser, getResXmlSerializer());
77+
XmlSerializer xmlSerializer = newXmlSerializer();
78+
ResStreamDecoder fileDecoder = new AndroidManifestPullStreamDecoder(axmlParser, xmlSerializer);
7979

8080
Directory inApk, out;
8181
InputStream inputStream = null;
@@ -157,7 +157,8 @@ public void decodeResources(File outDir) throws AndrolibException {
157157
decoders.setDecoder("9patch", new Res9patchStreamDecoder());
158158

159159
AXmlResourceParser axmlParser = new AXmlResourceParser(mResTable);
160-
decoders.setDecoder("xml", new ResXmlPullStreamDecoder(axmlParser, getResXmlSerializer()));
160+
XmlSerializer xmlSerializer = newXmlSerializer();
161+
decoders.setDecoder("xml", new ResXmlPullStreamDecoder(axmlParser, xmlSerializer));
161162

162163
ResFileDecoder fileDecoder = new ResFileDecoder(decoders);
163164
Directory in, out, outRes;
@@ -170,7 +171,6 @@ public void decodeResources(File outDir) throws AndrolibException {
170171
throw new AndrolibException(ex);
171172
}
172173

173-
ExtMXSerializer xmlSerializer = getResXmlSerializer();
174174
for (ResPackage pkg : mResTable.listMainPackages()) {
175175

176176
LOGGER.info("Decoding file-resources...");
@@ -191,20 +191,24 @@ public void decodeResources(File outDir) throws AndrolibException {
191191
}
192192
}
193193

194-
private ExtMXSerializer getResXmlSerializer() {
195-
ExtMXSerializer serial = new ExtMXSerializer();
196-
serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_INDENTATION, " ");
197-
serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_LINE_SEPARATOR, System.getProperty("line.separator"));
198-
serial.setProperty(ExtXmlSerializer.PROPERTY_DEFAULT_ENCODING, "utf-8");
199-
serial.setDisabledAttrEscape(true);
200-
return serial;
194+
private XmlSerializer newXmlSerializer() throws AndrolibException {
195+
try {
196+
XmlSerializer serial = new MXSerializer();
197+
serial.setFeature(MXSerializer.FEATURE_ATTR_VALUE_NO_ESCAPE, true);
198+
serial.setProperty(MXSerializer.PROPERTY_DEFAULT_ENCODING, "utf-8");
199+
serial.setProperty(MXSerializer.PROPERTY_INDENTATION, " ");
200+
serial.setProperty(MXSerializer.PROPERTY_LINE_SEPARATOR, System.getProperty("line.separator"));
201+
return serial;
202+
} catch (IllegalArgumentException | IllegalStateException ex) {
203+
throw new AndrolibException(ex);
204+
}
201205
}
202206

203207
private void generateValuesFile(ResValuesFile valuesFile, Directory out,
204-
ExtXmlSerializer serial) throws AndrolibException {
208+
XmlSerializer serial) throws AndrolibException {
205209
try {
206210
OutputStream outStream = out.getFileOutput(valuesFile.getPath());
207-
serial.setOutput((outStream), null);
211+
serial.setOutput(outStream, null);
208212
serial.startDocument(null, null);
209213
serial.startTag(null, "resources");
210214

@@ -216,7 +220,6 @@ private void generateValuesFile(ResValuesFile valuesFile, Directory out,
216220
}
217221

218222
serial.endTag(null, "resources");
219-
serial.newLine();
220223
serial.endDocument();
221224
serial.flush();
222225
outStream.close();

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -611,12 +611,12 @@ public void setProperty(String name, Object value) throws XmlPullParserException
611611
}
612612

613613
@Override
614-
public boolean getFeature(String feature) {
614+
public boolean getFeature(String name) {
615615
return false;
616616
}
617617

618618
@Override
619-
public void setFeature(String name, boolean value) throws XmlPullParserException {
619+
public void setFeature(String name, boolean state) throws XmlPullParserException {
620620
throw new XmlPullParserException(E_NOT_SUPPORTED);
621621
}
622622

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

Lines changed: 92 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -20,129 +20,119 @@
2020
import brut.androlib.exceptions.AXmlDecodingException;
2121
import brut.androlib.exceptions.RawXmlEncounteredException;
2222
import brut.androlib.res.data.ResTable;
23-
import brut.androlib.res.util.ExtXmlSerializer;
23+
import brut.xmlpull.XmlPullUtils;
2424
import org.xmlpull.v1.XmlPullParser;
2525
import org.xmlpull.v1.XmlPullParserException;
26-
import org.xmlpull.v1.wrapper.XmlPullParserWrapper;
27-
import org.xmlpull.v1.wrapper.XmlPullWrapperFactory;
28-
import org.xmlpull.v1.wrapper.XmlSerializerWrapper;
29-
import org.xmlpull.v1.wrapper.classic.StaticXmlSerializerWrapper;
26+
import org.xmlpull.v1.XmlSerializer;
3027

3128
import java.io.*;
3229

3330
public class AndroidManifestPullStreamDecoder implements ResStreamDecoder {
34-
public AndroidManifestPullStreamDecoder(AXmlResourceParser parser,
35-
ExtXmlSerializer serializer) {
36-
this.mParser = parser;
37-
this.mSerial = serializer;
31+
private final AXmlResourceParser mParser;
32+
private final XmlSerializer mSerial;
33+
34+
public AndroidManifestPullStreamDecoder(AXmlResourceParser parser, XmlSerializer serial) {
35+
mParser = parser;
36+
mSerial = serial;
3837
}
3938

4039
@Override
41-
public void decode(InputStream in, OutputStream out)
42-
throws AndrolibException {
40+
public void decode(InputStream in, OutputStream out) throws AndrolibException {
4341
try {
44-
XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance();
45-
XmlPullParserWrapper par = factory.newPullParserWrapper(mParser);
46-
final ResTable resTable = mParser.getResTable();
47-
48-
XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial, factory) {
49-
final boolean hideSdkInfo = !resTable.getAnalysisMode();
50-
51-
@Override
52-
public void event(XmlPullParser pp)
53-
throws XmlPullParserException, IOException {
54-
int type = pp.getEventType();
55-
56-
if (type == XmlPullParser.START_TAG) {
57-
if ("manifest".equals(pp.getName())) {
58-
try {
59-
parseManifest(pp);
60-
} catch (AndrolibException ignored) {}
61-
} else if ("uses-sdk".equals(pp.getName())) {
62-
try {
63-
parseUsesSdk(pp);
64-
} catch (AndrolibException ignored) {}
65-
if (hideSdkInfo) {
66-
return;
67-
}
68-
}
69-
} else if (type == XmlPullParser.END_TAG
70-
&& "uses-sdk".equals(pp.getName())) {
71-
if (hideSdkInfo) {
72-
return;
73-
}
74-
}
42+
mParser.setInput(in, null);
43+
mSerial.setOutput(out, null);
44+
XmlPullUtils.copy(mParser, mSerial, new EventHandler(mParser.getResTable()));
45+
} catch (XmlPullParserException ex) {
46+
throw new AXmlDecodingException("Could not decode XML", ex);
47+
} catch (IOException ex) {
48+
throw new RawXmlEncounteredException("Could not decode XML", ex);
49+
}
50+
}
7551

76-
super.event(pp);
52+
private static class EventHandler implements XmlPullUtils.EventHandler {
53+
private final ResTable mResTable;
54+
private final boolean mHideSdkInfo;
55+
56+
public EventHandler(ResTable resTable) {
57+
mResTable = resTable;
58+
mHideSdkInfo = !resTable.getAnalysisMode();
59+
}
60+
61+
@Override
62+
public boolean onEvent(XmlPullParser in, XmlSerializer out) throws XmlPullParserException {
63+
int type = in.getEventType();
64+
65+
if (type == XmlPullParser.START_TAG) {
66+
String name = in.getName();
67+
68+
if (name.equals("manifest")) {
69+
parseManifest(in);
70+
} else if (name.equals("uses-sdk")) {
71+
parseUsesSdk(in);
72+
73+
if (mHideSdkInfo) {
74+
return true;
75+
}
7776
}
77+
} else if (type == XmlPullParser.END_TAG) {
78+
String name = in.getName();
7879

79-
private void parseManifest(XmlPullParser pp)
80-
throws AndrolibException {
81-
for (int i = 0; i < pp.getAttributeCount(); i++) {
82-
String ns = pp.getAttributeNamespace(i);
83-
String name = pp.getAttributeName(i);
84-
String value = pp.getAttributeValue(i);
85-
86-
if (value.isEmpty()) {
87-
continue;
88-
}
89-
90-
if (ns.isEmpty()) {
91-
if (name.equals("package")) {
92-
resTable.setPackageRenamed(value);
93-
}
94-
} else if (ns.equals(AXmlResourceParser.ANDROID_RES_NS)) {
95-
switch (name) {
96-
case "versionCode":
97-
resTable.setVersionCode(value);
98-
break;
99-
case "versionName":
100-
resTable.setVersionName(value);
101-
break;
102-
}
103-
}
80+
if (name.equals("uses-sdk")) {
81+
if (mHideSdkInfo) {
82+
return true;
10483
}
10584
}
85+
}
10686

107-
private void parseUsesSdk(XmlPullParser pp)
108-
throws AndrolibException {
109-
for (int i = 0; i < pp.getAttributeCount(); i++) {
110-
String ns = pp.getAttributeNamespace(i);
111-
String name = pp.getAttributeName(i);
112-
String value = pp.getAttributeValue(i);
113-
114-
if (value.isEmpty()) {
115-
continue;
116-
}
117-
118-
if (ns.equals(AXmlResourceParser.ANDROID_RES_NS)) {
119-
switch (name) {
120-
case "minSdkVersion":
121-
case "targetSdkVersion":
122-
case "maxSdkVersion":
123-
case "compileSdkVersion":
124-
resTable.addSdkInfo(name, value);
125-
break;
126-
}
127-
}
87+
return false;
88+
}
89+
90+
private void parseManifest(XmlPullParser in) {
91+
for (int i = 0; i < in.getAttributeCount(); i++) {
92+
String ns = in.getAttributeNamespace(i);
93+
String name = in.getAttributeName(i);
94+
String value = in.getAttributeValue(i);
95+
96+
if (value.isEmpty()) {
97+
continue;
98+
}
99+
if (ns.isEmpty()) {
100+
if (name.equals("package")) {
101+
mResTable.setPackageRenamed(value);
102+
}
103+
} else if (ns.equals(AXmlResourceParser.ANDROID_RES_NS)) {
104+
switch (name) {
105+
case "versionCode":
106+
mResTable.setVersionCode(value);
107+
break;
108+
case "versionName":
109+
mResTable.setVersionName(value);
110+
break;
128111
}
129112
}
130-
};
113+
}
114+
}
131115

132-
par.setInput(in, null);
133-
ser.setOutput(out, null);
116+
private void parseUsesSdk(XmlPullParser in) {
117+
for (int i = 0; i < in.getAttributeCount(); i++) {
118+
String ns = in.getAttributeNamespace(i);
119+
String name = in.getAttributeName(i);
120+
String value = in.getAttributeValue(i);
134121

135-
while (par.nextToken() != XmlPullParser.END_DOCUMENT) {
136-
ser.event(par);
122+
if (value.isEmpty()) {
123+
continue;
124+
}
125+
if (ns.equals(AXmlResourceParser.ANDROID_RES_NS)) {
126+
switch (name) {
127+
case "minSdkVersion":
128+
case "targetSdkVersion":
129+
case "maxSdkVersion":
130+
case "compileSdkVersion":
131+
mResTable.addSdkInfo(name, value);
132+
break;
133+
}
134+
}
137135
}
138-
ser.flush();
139-
} catch (XmlPullParserException ex) {
140-
throw new AXmlDecodingException("Could not decode XML", ex);
141-
} catch (IOException ex) {
142-
throw new RawXmlEncounteredException("Could not decode XML", ex);
143136
}
144137
}
145-
146-
private final AXmlResourceParser mParser;
147-
private final ExtXmlSerializer mSerial;
148138
}

0 commit comments

Comments
 (0)