Skip to content

Commit 5a1bd06

Browse files
authored
refactor: convert meta classes to pojo, rework ARSC & rework cli parsing (#3868)
* refactor: convert meta classes to pojo Wanted to make that change back when I converted Config class to POJO, finishing the job now. This will guarantee safe access to all meta classes (moved from brut.androlib.apk to brut.androlib.meta to make more sense). All non-primitive fields are guaranteed to never be null. Outputs clean apktool.yml without useless fields that don't affect the rebuild procedure. Still backwards compatible, just cleaner output, especially for JARs. Cleaned some logic in a few classes, especially related to updateApkInfo and loading of libraries/frameworks. It was a mess that was difficult to make sense of. Test issue1235 removed, it's a aapt1 duplicate of aapt2's issue2328/debuggable-false. Misc style tweaks for consistency. Tested on a ROM decompile/recompile, no regression detected. * fix accessors * fix flaw in net sec conf * refactor ResConfigFlags and isInvalid logic * git windows case issue (1) * git windows case issue (2) * style * checker, wake up * reorganize some classes and minimize reading signed data from arsc Move: * brut.util.AaptManager -> brut.androlib.AaptManager * brut.androlib.BackgroundWorker -> brut.util.BackgroundWorker * brut.androlib.meta.Yaml* -> brut.yaml Removed useless "throws" on signatures of Yaml functions, none of the Yaml functions throw any managed exceptions, it's a perfectly generic set of classes that deserves a dedicated module. Most of data in arsc is encoded as unsigned integers, so normally we'd use readUnsignedByte/Short where possible (both return int) to ensure the values are never negative. Java doesn't have any valid unsigned counterpart for readInt, so we'll have to live with it, it works for now. Java was never good for dealing with raw binary data. * remove useless semicolon * cases ordered by value * comment correction * unneeded import * use readBytes for clarity * config options larping as flags * config options larping as flags (2) * refactor: command line parsing and Config Make command line commands and options great again. Each command has its own valid options, verified properly like any good CLI application. Instead of checking hasOption(shortName/longName), we do hasOption(Option) on the option object, which maintains sanity and very easy to manage. "allOptions" collection is no longer needed.
1 parent 2b2efb1 commit 5a1bd06

File tree

138 files changed

+2983
-2900
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

138 files changed

+2983
-2900
lines changed

brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java

Lines changed: 620 additions & 545 deletions
Large diffs are not rendered by default.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ dependencies {
2828
api(project(":brut.j.util"))
2929
api(project(":brut.j.dir"))
3030
api(project(":brut.j.xml"))
31+
api(project(":brut.j.yaml"))
3132

3233
implementation(libs.baksmali)
3334
implementation(libs.smali)

brut.apktool/apktool-lib/src/main/java/android/util/TypedValue.java

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -87,37 +87,37 @@ public class TypedValue {
8787
* Identifies the start of integer values that were specified as color
8888
* constants (starting with '#').
8989
*/
90-
public static final int TYPE_FIRST_COLOR_INT = 0x1c;
90+
public static final int TYPE_FIRST_COLOR_INT = 0x1C;
9191

9292
/**
9393
* The <var>data</var> field holds a color that was originally specified as
9494
* #aarrggbb.
9595
*/
96-
public static final int TYPE_INT_COLOR_ARGB8 = 0x1c;
96+
public static final int TYPE_INT_COLOR_ARGB8 = 0x1C;
9797
/**
9898
* The <var>data</var> field holds a color that was originally specified as
9999
* #rrggbb.
100100
*/
101-
public static final int TYPE_INT_COLOR_RGB8 = 0x1d;
101+
public static final int TYPE_INT_COLOR_RGB8 = 0x1D;
102102
/**
103103
* The <var>data</var> field holds a color that was originally specified as
104104
* #argb.
105105
*/
106-
public static final int TYPE_INT_COLOR_ARGB4 = 0x1e;
106+
public static final int TYPE_INT_COLOR_ARGB4 = 0x1E;
107107
/**
108108
* The <var>data</var> field holds a color that was originally specified as
109109
* #rgb.
110110
*/
111-
public static final int TYPE_INT_COLOR_RGB4 = 0x1f;
111+
public static final int TYPE_INT_COLOR_RGB4 = 0x1F;
112112

113113
/**
114114
* Identifies the end of integer values that were specified as color
115115
* constants.
116116
*/
117-
public static final int TYPE_LAST_COLOR_INT = 0x1f;
117+
public static final int TYPE_LAST_COLOR_INT = 0x1F;
118118

119119
/** Identifies the end of plain integer values. */
120-
public static final int TYPE_LAST_INT = 0x1f;
120+
public static final int TYPE_LAST_INT = 0x1F;
121121

122122
/* ------------------------------------------------------------ */
123123

@@ -128,7 +128,7 @@ public class TypedValue {
128128
* {@link #COMPLEX_UNIT_SHIFT}). This gives us 16 possible types, as defined
129129
* below.
130130
*/
131-
public static final int COMPLEX_UNIT_MASK = 0xf;
131+
public static final int COMPLEX_UNIT_MASK = 0xF;
132132

133133
/** {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. */
134134
public static final int COMPLEX_UNIT_PX = 0;
@@ -180,7 +180,7 @@ public class TypedValue {
180180
* {@link #COMPLEX_MANTISSA_SHIFT}). This gives us 23 bits of precision; the
181181
* top bit is the sign.
182182
*/
183-
public static final int COMPLEX_MANTISSA_MASK = 0xffffff;
183+
public static final int COMPLEX_MANTISSA_MASK = 0xFFFFFF;
184184

185185
/* ------------------------------------------------------------ */
186186

@@ -205,7 +205,7 @@ public class TypedValue {
205205
* If density is equal to this value, then there is no density
206206
* associated with the resource and it should not be scaled.
207207
*/
208-
public static final int DENSITY_NONE = 0xffff;
208+
public static final int DENSITY_NONE = 0xFFFF;
209209

210210
/* ------------------------------------------------------------ */
211211

@@ -288,12 +288,10 @@ public static String coerceToString(int type, int data) {
288288
res = res.substring(2);
289289
break;
290290
case TYPE_INT_COLOR_ARGB4:// #AARRGGBB->#ARGB
291-
res = String.valueOf(vals[0]) + vals[2] +
292-
vals[4] + vals[6];
291+
res = new String(new char[] { vals[0], vals[2], vals[4], vals[6] });
293292
break;
294293
case TYPE_INT_COLOR_RGB4:// #FFRRGGBB->#RGB
295-
res = String.valueOf(vals[2]) + vals[4] +
296-
vals[6];
294+
res = new String(new char[] { vals[2], vals[4], vals[6] });
297295
break;
298296
}
299297
return "#" + res;

brut.apktool/apktool-lib/src/main/java/brut/androlib/AaptInvoker.java

Lines changed: 38 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@
1616
*/
1717
package brut.androlib;
1818

19-
import brut.androlib.apk.ApkInfo;
2019
import brut.androlib.exceptions.AndrolibException;
20+
import brut.androlib.meta.ApkInfo;
2121
import brut.common.BrutException;
22-
import brut.util.AaptManager;
2322
import brut.util.OS;
2423

2524
import java.io.*;
@@ -50,7 +49,7 @@ public void invoke(File apkFile, File manifest, File resDir, File rawDir, File a
5049
try {
5150
aaptPath = AaptManager.getAaptBinary(mConfig.getAaptVersion()).getPath();
5251
customAapt = false;
53-
} catch (BrutException ex) {
52+
} catch (AndrolibException ex) {
5453
aaptPath = AaptManager.getAaptName(mConfig.getAaptVersion());
5554
customAapt = true;
5655
LOGGER.warning(aaptPath + ": " + ex.getMessage() + " (defaulting to $PATH binary)");
@@ -120,40 +119,40 @@ private void invokeAapt2(File apkFile, File manifest, File resDir, File rawDir,
120119
cmd.add("-o");
121120
cmd.add(apkFile.getAbsolutePath());
122121

123-
if (mApkInfo.packageInfo.forcedPackageId != null) {
124-
int pkgId = Integer.parseInt(mApkInfo.packageInfo.forcedPackageId);
122+
if (mApkInfo.getPackageInfo().getForcedPackageId() != null) {
123+
int pkgId = Integer.parseInt(mApkInfo.getPackageInfo().getForcedPackageId());
125124
if (pkgId == 0) {
126125
cmd.add("--shared-lib");
127126
} else if (pkgId > 1) {
128127
cmd.add("--package-id");
129-
cmd.add(mApkInfo.packageInfo.forcedPackageId);
130-
if (pkgId < 0x7f) {
128+
cmd.add(Integer.toString(pkgId));
129+
if (pkgId < 0x7F) {
131130
cmd.add("--allow-reserved-package-id");
132131
}
133132
}
134133
}
135-
if (mApkInfo.getMinSdkVersion() != null) {
134+
if (mApkInfo.getSdkInfo().getMinSdkVersion() != null) {
136135
cmd.add("--min-sdk-version");
137-
cmd.add(mApkInfo.getMinSdkVersion());
136+
cmd.add(mApkInfo.getSdkInfo().getMinSdkVersion());
138137
}
139-
if (mApkInfo.getTargetSdkVersion() != null) {
138+
if (mApkInfo.getSdkInfo().getTargetSdkVersion() != null) {
140139
cmd.add("--target-sdk-version");
141-
cmd.add(mApkInfo.checkTargetSdkVersionBounds());
140+
cmd.add(mApkInfo.getSdkInfo().getTargetSdkVersion());
142141
}
143-
if (mApkInfo.packageInfo.renameManifestPackage != null) {
142+
if (mApkInfo.getPackageInfo().getRenameManifestPackage() != null) {
144143
cmd.add("--rename-manifest-package");
145-
cmd.add(mApkInfo.packageInfo.renameManifestPackage);
144+
cmd.add(mApkInfo.getPackageInfo().getRenameManifestPackage());
146145

147146
cmd.add("--rename-instrumentation-target-package");
148-
cmd.add(mApkInfo.packageInfo.renameManifestPackage);
147+
cmd.add(mApkInfo.getPackageInfo().getRenameManifestPackage());
149148
}
150-
if (mApkInfo.versionInfo.versionCode != null) {
149+
if (mApkInfo.getVersionInfo().getVersionCode() != null) {
151150
cmd.add("--version-code");
152-
cmd.add(mApkInfo.versionInfo.versionCode);
151+
cmd.add(mApkInfo.getVersionInfo().getVersionCode());
153152
}
154-
if (mApkInfo.versionInfo.versionName != null) {
153+
if (mApkInfo.getVersionInfo().getVersionName() != null) {
155154
cmd.add("--version-name");
156-
cmd.add(mApkInfo.versionInfo.versionName);
155+
cmd.add(mApkInfo.getVersionInfo().getVersionName());
157156
}
158157

159158
// Disable automatic changes
@@ -166,15 +165,15 @@ private void invokeAapt2(File apkFile, File manifest, File resDir, File rawDir,
166165
// #3427 - Ignore stricter parsing during aapt2
167166
cmd.add("--warn-manifest-validation");
168167

169-
if (mApkInfo.sparseResources) {
168+
if (mApkInfo.isSparseResources()) {
170169
cmd.add("--enable-sparse-encoding");
171170
}
172-
if (mApkInfo.compactEntries) {
171+
if (mApkInfo.isCompactEntries()) {
173172
cmd.add("--enable-compact-entries");
174173
}
175-
if (!mApkInfo.featureFlags.isEmpty()) {
174+
if (!mApkInfo.getFeatureFlags().isEmpty()) {
176175
List<String> featureFlags = new ArrayList<>();
177-
for (Map.Entry<String, Boolean> entry : mApkInfo.featureFlags.entrySet()) {
176+
for (Map.Entry<String, Boolean> entry : mApkInfo.getFeatureFlags().entrySet()) {
178177
featureFlags.add(entry.getKey() + "=" + entry.getValue());
179178
}
180179
cmd.add("--feature-flags");
@@ -223,60 +222,54 @@ private void invokeAapt1(File apkFile, File manifest, File resDir, File rawDir,
223222
if (mConfig.isVerbose()) { // output aapt verbose
224223
cmd.add("-v");
225224
}
226-
if (mConfig.isUpdateFiles()) {
227-
cmd.add("-u");
228-
}
229225
if (mConfig.isDebugMode()) { // inject debuggable="true" into manifest
230226
cmd.add("--debug-mode");
231227
}
232228
if (mConfig.isNoCrunch()) {
233229
cmd.add("--no-crunch");
234230
}
235-
if (mApkInfo.packageInfo.forcedPackageId != null) {
236-
int pkgId = Integer.parseInt(mApkInfo.packageInfo.forcedPackageId);
231+
if (mApkInfo.getPackageInfo().getForcedPackageId() != null) {
232+
int pkgId = Integer.parseInt(mApkInfo.getPackageInfo().getForcedPackageId());
237233
if (pkgId == 0) {
238234
cmd.add("--shared-lib");
239235
} else if (pkgId > 0) {
240236
if (!customAapt) { // custom aapt might not have this option
241237
cmd.add("--forced-package-id");
242-
cmd.add(mApkInfo.packageInfo.forcedPackageId);
238+
cmd.add(Integer.toString(pkgId));
243239
}
244-
if (pkgId < 0x7f) {
240+
if (pkgId < 0x7F) {
245241
cmd.add("-x");
246242
}
247243
}
248244
}
249-
if (mApkInfo.getMinSdkVersion() != null) {
245+
if (mApkInfo.getSdkInfo().getMinSdkVersion() != null) {
250246
cmd.add("--min-sdk-version");
251-
cmd.add(mApkInfo.getMinSdkVersion());
247+
cmd.add(mApkInfo.getSdkInfo().getMinSdkVersion());
252248
}
253-
if (mApkInfo.getTargetSdkVersion() != null) {
249+
if (mApkInfo.getSdkInfo().getTargetSdkVersion() != null) {
254250
cmd.add("--target-sdk-version");
255-
256-
// Ensure that targetSdkVersion is between minSdkVersion/maxSdkVersion if
257-
// they are specified.
258-
cmd.add(mApkInfo.checkTargetSdkVersionBounds());
251+
cmd.add(mApkInfo.getSdkInfo().getTargetSdkVersionBounded());
259252
}
260-
if (mApkInfo.getMaxSdkVersion() != null) {
253+
if (mApkInfo.getSdkInfo().getMaxSdkVersion() != null) {
261254
cmd.add("--max-sdk-version");
262-
cmd.add(mApkInfo.getMaxSdkVersion());
255+
cmd.add(mApkInfo.getSdkInfo().getMaxSdkVersion());
263256

264257
// if we have max sdk version, set --max-res-version,
265258
// so we can ignore anything over that during build.
266259
cmd.add("--max-res-version");
267-
cmd.add(mApkInfo.getMaxSdkVersion());
260+
cmd.add(mApkInfo.getSdkInfo().getMaxSdkVersion());
268261
}
269-
if (mApkInfo.packageInfo.renameManifestPackage != null) {
262+
if (mApkInfo.getPackageInfo().getRenameManifestPackage() != null) {
270263
cmd.add("--rename-manifest-package");
271-
cmd.add(mApkInfo.packageInfo.renameManifestPackage);
264+
cmd.add(mApkInfo.getPackageInfo().getRenameManifestPackage());
272265
}
273-
if (mApkInfo.versionInfo.versionCode != null) {
266+
if (mApkInfo.getVersionInfo().getVersionCode() != null) {
274267
cmd.add("--version-code");
275-
cmd.add(mApkInfo.versionInfo.versionCode);
268+
cmd.add(mApkInfo.getVersionInfo().getVersionCode());
276269
}
277-
if (mApkInfo.versionInfo.versionName != null) {
270+
if (mApkInfo.getVersionInfo().getVersionName() != null) {
278271
cmd.add("--version-name");
279-
cmd.add(mApkInfo.versionInfo.versionName);
272+
cmd.add(mApkInfo.getVersionInfo().getVersionName());
280273
}
281274
cmd.add("--no-version-vectors");
282275
cmd.add("-F");

brut.j.util/src/main/java/brut/util/AaptManager.java renamed to brut.apktool/apktool-lib/src/main/java/brut/androlib/AaptManager.java

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,13 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
package brut.util;
17+
package brut.androlib;
1818

19+
import brut.androlib.exceptions.AndrolibException;
1920
import brut.common.BrutException;
21+
import brut.util.Jar;
22+
import brut.util.OS;
23+
import brut.util.OSDetection;
2024

2125
import java.io.File;
2226
import java.util.ArrayList;
@@ -37,11 +41,12 @@ public static String getAaptName(int version) {
3741
}
3842
}
3943

40-
public static File getAaptBinary(int version) throws BrutException {
44+
public static File getAaptBinary(int version) throws AndrolibException {
4145
String aaptName = getAaptName(version);
4246

4347
if (!OSDetection.is64Bit() && OSDetection.isMacOSX()) {
44-
throw new BrutException(aaptName + " binary is not available for 32-bit platform: " + OSDetection.returnOS());
48+
throw new AndrolibException(
49+
aaptName + " binary is not available for 32-bit platform: " + OSDetection.returnOS());
4550
}
4651

4752
StringBuilder aaptPath = new StringBuilder("/prebuilt/");
@@ -52,7 +57,7 @@ public static File getAaptBinary(int version) throws BrutException {
5257
} else if (OSDetection.isWindows()) {
5358
aaptPath.append("windows");
5459
} else {
55-
throw new BrutException("Could not identify platform: " + OSDetection.returnOS());
60+
throw new AndrolibException("Could not identify platform: " + OSDetection.returnOS());
5661
}
5762
aaptPath.append("/");
5863
aaptPath.append(aaptName);
@@ -63,12 +68,17 @@ public static File getAaptBinary(int version) throws BrutException {
6368
aaptPath.append(".exe");
6469
}
6570

66-
File aaptBinary = Jar.getResourceAsFile(AaptManager.class, aaptPath.toString());
71+
File aaptBinary;
72+
try {
73+
aaptBinary = Jar.getResourceAsFile(AaptManager.class, aaptPath.toString());
74+
} catch (BrutException ex) {
75+
throw new AndrolibException(ex);
76+
}
6777
setAaptBinaryExecutable(aaptBinary);
6878
return aaptBinary;
6979
}
7080

71-
public static int getAaptVersion(File aaptBinary) throws BrutException {
81+
public static int getAaptVersion(File aaptBinary) throws AndrolibException {
7282
setAaptBinaryExecutable(aaptBinary);
7383

7484
List<String> cmd = new ArrayList<>();
@@ -77,13 +87,13 @@ public static int getAaptVersion(File aaptBinary) throws BrutException {
7787

7888
String versionStr = OS.execAndReturn(cmd.toArray(new String[0]));
7989
if (versionStr == null) {
80-
throw new BrutException("Could not execute aapt binary at location: " + aaptBinary.getPath());
90+
throw new AndrolibException("Could not execute aapt binary at location: " + aaptBinary.getPath());
8191
}
8292

8393
return getAaptVersionFromString(versionStr);
8494
}
8595

86-
public static int getAaptVersionFromString(String versionStr) throws BrutException {
96+
public static int getAaptVersionFromString(String versionStr) throws AndrolibException {
8797
if (versionStr.startsWith("Android Asset Packaging Tool (aapt) 2:")) {
8898
return 2;
8999
} else if (versionStr.startsWith("Android Asset Packaging Tool (aapt) 2.")) {
@@ -92,15 +102,15 @@ public static int getAaptVersionFromString(String versionStr) throws BrutExcepti
92102
return 1;
93103
}
94104

95-
throw new BrutException("aapt version could not be identified: " + versionStr);
105+
throw new AndrolibException("aapt version could not be identified: " + versionStr);
96106
}
97107

98-
private static void setAaptBinaryExecutable(File aaptBinary) throws BrutException {
108+
private static void setAaptBinaryExecutable(File aaptBinary) throws AndrolibException {
99109
if (!aaptBinary.isFile() || !aaptBinary.canRead()) {
100-
throw new BrutException("Could not read aapt binary: " + aaptBinary.getPath());
110+
throw new AndrolibException("Could not read aapt binary: " + aaptBinary.getPath());
101111
}
102112
if (!aaptBinary.setExecutable(true)) {
103-
throw new BrutException("Could not set aapt binary as executable: " + aaptBinary.getPath());
113+
throw new AndrolibException("Could not set aapt binary as executable: " + aaptBinary.getPath());
104114
}
105115
}
106116
}

0 commit comments

Comments
 (0)