Skip to content

Commit

Permalink
refactor: ExtDataInput rework, source layout and formatting (iBotPeac…
Browse files Browse the repository at this point in the history
…hes#3738)

* refactor: ExtDataInput rework, source layout and formatting

Refactor ExtDataInput classes: ExtDataInput is now the extended interface,
ExtDataInputStream is an easy-to-use FilterInputStream implementing ExtDataInput
with static creator methods for big-endian and little-endian wrappers.

Refactor AaptManager class: unify aapt-related verifications to one class.

Replace Apache Commons' deprecated CountingInputStream with Google Guava's
equivalent with the same name. Apache's BoundedInputStream is an overkill
for our use case and its constructors are deprecated as well.

Normalize source layout to have a common and somewhat more standard order:
Static fields first, instance fields after, methods last.

Fix some formatting, like empty spaces or extra spaces and exception messages.

Renamed ResXmlPatcher to ResXmlUtils, as it has more purposes than just patching.

Renamed DirUtil to DirUtils, to match other utility classes naming convention.

Moved "properties/apktool.properties" to jar's root, to match smali/baksmali.

Moved Android Framework to "prebuilt", as it is just a prebuilt, looks out of
place among .class files.

@SuppressWarnings removed from Duo as there are quite a few unsafe assignments
of raw Duo[] instances to parameterized Duo<> variables in the project, this is
just Java being the primitive boilerplate it is, no point in fighting it.

No end-user changes.
Tested against a full ROM decompile/recompile, no issues found.

* small tweak

* last refinement

* missed a stream
  • Loading branch information
IgorEisberg authored Dec 11, 2024
1 parent 858c071 commit 542b66c
Show file tree
Hide file tree
Showing 120 changed files with 1,473 additions and 1,501 deletions.
55 changes: 17 additions & 38 deletions brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@
* Main entry point of the apktool.
*/
public class Main {
private enum Verbosity { NORMAL, VERBOSE, QUIET }

private static final Options normalOptions = new Options();
private static final Options decodeOptions = new Options();
private static final Options buildOptions = new Options();
private static final Options frameOptions = new Options();
private static final Options allOptions = new Options();
private static final Options emptyOptions = new Options();
private static final Options emptyFrameworkOptions = new Options();
private static final Options listFrameworkOptions = new Options();

private static boolean advanceMode = false;

public static void main(String[] args) throws BrutException {

// headless
Expand Down Expand Up @@ -239,7 +252,7 @@ private static void cmdDecode(CommandLine cli, Config config) throws AndrolibExc
System.exit(1);
} catch (CantFindFrameworkResException ex) {
System.err
.println("Can't find framework resources for package of id: "
.println("Could not find framework resources for package of id: "
+ ex.getPkgId()
+ ". You must install proper "
+ "framework files, see project website for more info.");
Expand Down Expand Up @@ -271,15 +284,8 @@ private static void cmdBuild(CommandLine cli, Config config) throws AndrolibExce
}

try {
String aaptPath = cli.getOptionValue("a");
int aaptVersion = AaptManager.getAaptVersion(aaptPath);
if (aaptVersion < AaptManager.AAPT_VERSION_MIN && aaptVersion > AaptManager.AAPT_VERSION_MAX) {
System.err.println("AAPT version " + aaptVersion + " is not supported");
System.exit(1);
}

config.aaptPath = aaptPath;
config.aaptVersion = aaptVersion;
config.aaptBinary = new File(cli.getOptionValue("a"));
config.aaptVersion = AaptManager.getAaptVersion(config.aaptBinary);
} catch (BrutException ex) {
System.err.println(ex.getMessage());
System.exit(1);
Expand Down Expand Up @@ -310,7 +316,7 @@ private static void cmdBuild(CommandLine cli, Config config) throws AndrolibExce
}

if (config.netSecConf && config.aaptVersion == 1) {
System.err.println("-n / --net-sec-conf is not supported with legacy AAPT.");
System.err.println("-n / --net-sec-conf is not supported with legacy aapt.");
System.exit(1);
}

Expand Down Expand Up @@ -724,31 +730,4 @@ private static boolean isAdvanceMode() {
private static void setAdvanceMode() {
Main.advanceMode = true;
}

private enum Verbosity {
NORMAL, VERBOSE, QUIET
}

private static boolean advanceMode = false;

private final static Options normalOptions;
private final static Options decodeOptions;
private final static Options buildOptions;
private final static Options frameOptions;
private final static Options allOptions;
private final static Options emptyOptions;
private final static Options emptyFrameworkOptions;
private final static Options listFrameworkOptions;

static {
//normal and advance usage output
normalOptions = new Options();
buildOptions = new Options();
decodeOptions = new Options();
frameOptions = new Options();
allOptions = new Options();
emptyOptions = new Options();
emptyFrameworkOptions = new Options();
listFrameworkOptions = new Options();
}
}
5 changes: 2 additions & 3 deletions brut.apktool/apktool-lib/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ val apktoolVersion: String by rootProject.extra

tasks {
processResources {
from("src/main/resources/properties") {
include("**/*.properties")
into("properties")
from("src/main/resources") {
include("apktool.properties")
expand("version" to apktoolVersion, "gitrev" to gitRevision)
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
public interface XmlResourceParser extends XmlPullParser, AttributeSet {
/**
* Close this interface to the resource. Calls on the interface are no
* longer value after this call.
* longer valid after this call.
*/
void close();
}
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ public class TypedValue {
public int type;

private static final float MANTISSA_MULT = 1.0f / (1 << TypedValue.COMPLEX_MANTISSA_SHIFT);
private static final float[] RADIX_MULTS = new float[] {
private static final float[] RADIX_MULTS = {
MANTISSA_MULT, 1.0f / (1 << 7) * MANTISSA_MULT,
1.0f / (1 << 15) * MANTISSA_MULT, 1.0f / (1 << 23) * MANTISSA_MULT };

Expand All @@ -237,9 +237,10 @@ public static float complexToFloat(int complex) {
& TypedValue.COMPLEX_RADIX_MASK];
}

private static final String[] DIMENSION_UNIT_STRS = new String[] { "px",
"dip", "sp", "pt", "in", "mm" };
private static final String[] FRACTION_UNIT_STRS = new String[] { "%", "%p" };
private static final String[] DIMENSION_UNIT_STRS = {
"px", "dip", "sp", "pt", "in", "mm"
};
private static final String[] FRACTION_UNIT_STRS = { "%", "%p" };

/**
* Perform type conversion as per coerceToString on an explicitly
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,44 +27,40 @@
import java.util.logging.Logger;

public class AaptInvoker {
private static final Logger LOGGER = Logger.getLogger(AaptInvoker.class.getName());

private final Config mConfig;
private final ApkInfo mApkInfo;

private final static Logger LOGGER = Logger.getLogger(AaptInvoker.class.getName());

public AaptInvoker(Config config, ApkInfo apkInfo) {
mConfig = config;
mApkInfo = apkInfo;
}

private File getAaptBinaryFile() throws AndrolibException {
try {
switch (mConfig.aaptVersion) {
case 2:
return AaptManager.getAapt2();
default:
return AaptManager.getAapt1();
}
} catch (BrutException ex) {
throw new AndrolibException(ex);
}
}

public void invoke(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include)
throws AndrolibException {
File aaptBinary = mConfig.aaptBinary;

String aaptPath = mConfig.aaptPath;
boolean customAapt = !aaptPath.isEmpty();
List<String> cmd = new ArrayList<>();
String aaptPath;
boolean customAapt;

try {
String aaptCommand = AaptManager.getAaptExecutionCommand(aaptPath, getAaptBinaryFile());
cmd.add(aaptCommand);
} catch (BrutException ex) {
LOGGER.warning("aapt: " + ex.getMessage() + " (defaulting to $PATH binary)");
cmd.add(AaptManager.getAaptBinaryName(mConfig.aaptVersion));
if (mConfig.aaptBinary != null) {
aaptPath = mConfig.aaptBinary.getPath();
customAapt = true;
} else {
try {
aaptPath = AaptManager.getAaptBinary(mConfig.aaptVersion).getPath();
customAapt = false;
} catch (BrutException ex) {
aaptPath = AaptManager.getAaptName(mConfig.aaptVersion);
customAapt = true;
LOGGER.warning(aaptPath + ": " + ex.getMessage() + " (defaulting to $PATH binary)");
}
}

cmd.add(aaptPath);

switch (mConfig.aaptVersion) {
case 2:
invokeAapt2(apkFile, manifest, resDir, rawDir, assetDir, include, cmd, customAapt);
Expand All @@ -77,7 +73,6 @@ public void invoke(File apkFile, File manifest, File resDir, File rawDir, File a

private void invokeAapt2(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include,
List<String> cmd, boolean customAapt) throws AndrolibException {

List<String> compileCommand = new ArrayList<>(cmd);
File resourcesZip = null;

Expand All @@ -87,7 +82,6 @@ private void invokeAapt2(File apkFile, File manifest, File resDir, File rawDir,
}

if (resDir != null && !resourcesZip.exists()) {

// Compile the files into flat arsc files
cmd.add("compile");

Expand Down Expand Up @@ -241,7 +235,6 @@ private void invokeAapt2(File apkFile, File manifest, File resDir, File rawDir,

private void invokeAapt1(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include,
List<String> cmd, boolean customAapt) throws AndrolibException {

cmd.add("p");

if (mConfig.verbose) { // output aapt verbose
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import brut.androlib.apk.UsesFramework;
import brut.androlib.res.Framework;
import brut.androlib.res.data.ResConfigFlags;
import brut.androlib.res.xml.ResXmlPatcher;
import brut.androlib.res.xml.ResXmlUtils;
import brut.androlib.src.SmaliBuilder;
import brut.common.BrutException;
import brut.common.InvalidUnknownFileException;
Expand All @@ -47,14 +47,15 @@
import java.util.zip.ZipOutputStream;

public class ApkBuilder {
private final static Logger LOGGER = Logger.getLogger(ApkBuilder.class.getName());
private static final Logger LOGGER = Logger.getLogger(ApkBuilder.class.getName());

private final ExtFile mApkDir;
private final Config mConfig;
private final AtomicReference<AndrolibException> mBuildError;

private ApkInfo mApkInfo;
private int mMinSdkVersion = 0;
private int mMinSdkVersion;
private BackgroundWorker mWorker;
private final AtomicReference<AndrolibException> mBuildError = new AtomicReference<>(null);

public ApkBuilder(ExtFile apkDir) {
this(apkDir, Config.getDefaultConfig());
Expand All @@ -63,6 +64,7 @@ public ApkBuilder(ExtFile apkDir) {
public ApkBuilder(ExtFile apkDir, Config config) {
mApkDir = apkDir;
mConfig = config;
mBuildError = new AtomicReference<>(null);
}

public void build(File outApk) throws AndrolibException {
Expand Down Expand Up @@ -122,19 +124,19 @@ public void build(File outApk) throws AndrolibException {

LOGGER.info("Building apk file...");

try (ZipOutputStream outStream = new ZipOutputStream(Files.newOutputStream(outApk.toPath()))) {
try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(outApk.toPath()))) {
// zip aapt output files
try {
ZipUtils.zipDir(outDir, outStream, mApkInfo.doNotCompress);
ZipUtils.zipDir(outDir, out, mApkInfo.doNotCompress);
} catch (IOException ex) {
throw new AndrolibException(ex);
}

// zip remaining standard files
importRawFiles(outStream);
importRawFiles(out);

// zip unknown files
importUnknownFiles(outStream);
importUnknownFiles(out);
} catch (IOException ex) {
throw new AndrolibException(ex);
}
Expand Down Expand Up @@ -242,10 +244,10 @@ private void buildSourcesSmaliJob(File outDir, String dirName, String fileName)
//noinspection ResultOfMethodCallIgnored
dex.delete();

int apiLevel = mConfig.apiLevel > 0 ? mConfig.apiLevel : mMinSdkVersion;

LOGGER.info("Smaling " + dirName + " folder into " + fileName + "...");
SmaliBuilder.build(smaliDir, dex, apiLevel);
int apiLevel = mConfig.apiLevel > 0 ? mConfig.apiLevel : mMinSdkVersion;
SmaliBuilder builder = new SmaliBuilder(smaliDir, apiLevel);
builder.build(dex);
}

private void backupManifestFile(File manifest, File manifestOrig) throws AndrolibException {
Expand All @@ -265,7 +267,7 @@ private void backupManifestFile(File manifest, File manifestOrig) throws Androli

try {
FileUtils.copyFile(manifest, manifestOrig);
ResXmlPatcher.fixingPublicAttrsInProviderAttributes(manifest);
ResXmlUtils.fixingPublicAttrsInProviderAttributes(manifest);
} catch (IOException ex) {
throw new AndrolibException(ex);
}
Expand Down Expand Up @@ -327,10 +329,10 @@ private void buildResourcesFull(File outDir, File manifest) throws AndrolibExcep
try {
if (mConfig.debugMode) {
if (mConfig.aaptVersion == 2) {
LOGGER.info("Using aapt2 - setting 'debuggable' attribute to 'true' in AndroidManifest.xml");
ResXmlPatcher.setApplicationDebugTagTrue(manifest);
LOGGER.info("Setting 'debuggable' attribute to 'true' in AndroidManifest.xml");
ResXmlUtils.setApplicationDebugTagTrue(manifest);
} else {
ResXmlPatcher.removeApplicationDebugTag(manifest);
ResXmlUtils.removeApplicationDebugTag(manifest);
}
}

Expand All @@ -343,8 +345,8 @@ private void buildResourcesFull(File outDir, File manifest) throws AndrolibExcep
}

File netSecConfOrig = new File(mApkDir, "res/xml/network_security_config.xml");
ResXmlPatcher.modNetworkSecurityConfig(netSecConfOrig);
ResXmlPatcher.setNetworkSecurityConfig(manifest);
ResXmlUtils.modNetworkSecurityConfig(netSecConfOrig);
ResXmlUtils.setNetworkSecurityConfig(manifest);
LOGGER.info("Added permissive network security config in manifest");
}
} catch (IOException | ParserConfigurationException | TransformerException | SAXException ex) {
Expand All @@ -366,7 +368,7 @@ private void buildResourcesFull(File outDir, File manifest) throws AndrolibExcep
ninePatch = null;
}

LOGGER.info("Building resources with " + AaptManager.getAaptBinaryName(mConfig.aaptVersion) + "...");
LOGGER.info("Building resources with " + AaptManager.getAaptName(mConfig.aaptVersion) + "...");

try {
AaptInvoker invoker = new AaptInvoker(mConfig, mApkInfo);
Expand Down Expand Up @@ -406,7 +408,7 @@ private void buildManifest(File outDir, File manifest) throws AndrolibException
ninePatch = null;
}

LOGGER.info("Building AndroidManifest.xml with " + AaptManager.getAaptBinaryName(mConfig.aaptVersion) + "...");
LOGGER.info("Building AndroidManifest.xml with " + AaptManager.getAaptName(mConfig.aaptVersion) + "...");

try {
AaptInvoker invoker = new AaptInvoker(mConfig, mApkInfo);
Expand Down Expand Up @@ -460,7 +462,7 @@ private void copyOriginalFiles(File outDir) throws AndrolibException {
}
}

private void importRawFiles(ZipOutputStream outStream) throws AndrolibException {
private void importRawFiles(ZipOutputStream out) throws AndrolibException {
for (String dirName : ApkInfo.RAW_DIRNAMES) {
File rawDir = new File(mApkDir, dirName);
if (!rawDir.isDirectory()) {
Expand All @@ -469,22 +471,22 @@ private void importRawFiles(ZipOutputStream outStream) throws AndrolibException

LOGGER.info("Importing " + dirName + "...");
try {
ZipUtils.zipDir(mApkDir, dirName, outStream, mApkInfo.doNotCompress);
ZipUtils.zipDir(mApkDir, dirName, out, mApkInfo.doNotCompress);
} catch (IOException ex) {
throw new AndrolibException(ex);
}
}
}

private void importUnknownFiles(ZipOutputStream outStream) throws AndrolibException {
private void importUnknownFiles(ZipOutputStream out) throws AndrolibException {
File unknownDir = new File(mApkDir, "unknown");
if (!unknownDir.isDirectory()) {
return;
}

LOGGER.info("Importing unknown files...");
try {
ZipUtils.zipDir(unknownDir, outStream, mApkInfo.doNotCompress);
ZipUtils.zipDir(unknownDir, out, mApkInfo.doNotCompress);
} catch (IOException ex) {
throw new AndrolibException(ex);
}
Expand Down
Loading

0 comments on commit 542b66c

Please sign in to comment.