mirror of
https://github.com/revanced/Apktool.git
synced 2025-04-29 21:54:25 +02:00

Use BrutIO where possible to improve and simplify stream handling. Ensure streams are closed when no longer needed. Some minor formatting tweaks and naming consistency. No functionality changes.
338 lines
11 KiB
Java
338 lines
11 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;
|
|
|
|
import brut.androlib.apk.ApkInfo;
|
|
import brut.androlib.exceptions.AndrolibException;
|
|
import brut.common.BrutException;
|
|
import brut.util.AaptManager;
|
|
import brut.util.OS;
|
|
|
|
import java.io.*;
|
|
import java.util.*;
|
|
import java.util.logging.Logger;
|
|
|
|
public class AaptInvoker {
|
|
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 {
|
|
|
|
String aaptPath = mConfig.aaptPath;
|
|
boolean customAapt = !aaptPath.isEmpty();
|
|
List<String> cmd = new ArrayList<>();
|
|
|
|
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));
|
|
}
|
|
|
|
switch (mConfig.aaptVersion) {
|
|
case 2:
|
|
invokeAapt2(apkFile, manifest, resDir, rawDir, assetDir, include, cmd, customAapt);
|
|
break;
|
|
default:
|
|
invokeAapt1(apkFile, manifest, resDir, rawDir, assetDir, include, cmd, customAapt);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
if (resDir != null) {
|
|
File buildDir = new File(resDir.getParent(), "build");
|
|
resourcesZip = new File(buildDir, "resources.zip");
|
|
}
|
|
|
|
if (resDir != null && !resourcesZip.exists()) {
|
|
|
|
// Compile the files into flat arsc files
|
|
cmd.add("compile");
|
|
|
|
cmd.add("--dir");
|
|
cmd.add(resDir.getAbsolutePath());
|
|
|
|
// Treats error that used to be valid in aapt1 as warnings in aapt2
|
|
cmd.add("--legacy");
|
|
|
|
File buildDir = new File(resDir.getParent(), "build");
|
|
resourcesZip = new File(buildDir, "resources.zip");
|
|
|
|
cmd.add("-o");
|
|
cmd.add(resourcesZip.getAbsolutePath());
|
|
|
|
if (mConfig.verbose) {
|
|
cmd.add("-v");
|
|
}
|
|
|
|
if (mConfig.noCrunch) {
|
|
cmd.add("--no-crunch");
|
|
}
|
|
|
|
try {
|
|
OS.exec(cmd.toArray(new String[0]));
|
|
LOGGER.fine("aapt2 compile command ran: ");
|
|
LOGGER.fine(cmd.toString());
|
|
} catch (BrutException ex) {
|
|
throw new AndrolibException(ex);
|
|
}
|
|
}
|
|
|
|
if (manifest == null) {
|
|
return;
|
|
}
|
|
|
|
// Link them into the final apk, reusing our old command after clearing for the aapt2 binary
|
|
cmd = new ArrayList<>(compileCommand);
|
|
cmd.add("link");
|
|
|
|
cmd.add("-o");
|
|
cmd.add(apkFile.getAbsolutePath());
|
|
|
|
if (mApkInfo.packageInfo.forcedPackageId != null && !mApkInfo.packageInfo.forcedPackageId.equals("1")
|
|
&& !mApkInfo.sharedLibrary) {
|
|
cmd.add("--allow-reserved-package-id");
|
|
cmd.add("--package-id");
|
|
cmd.add(mApkInfo.packageInfo.forcedPackageId);
|
|
}
|
|
|
|
if (mApkInfo.sharedLibrary) {
|
|
cmd.add("--shared-lib");
|
|
}
|
|
|
|
if (mApkInfo.getMinSdkVersion() != null) {
|
|
cmd.add("--min-sdk-version");
|
|
cmd.add(mApkInfo.getMinSdkVersion() );
|
|
}
|
|
|
|
if (mApkInfo.getTargetSdkVersion() != null) {
|
|
cmd.add("--target-sdk-version");
|
|
cmd.add(mApkInfo.checkTargetSdkVersionBounds());
|
|
}
|
|
|
|
if (mApkInfo.packageInfo.renameManifestPackage != null) {
|
|
cmd.add("--rename-manifest-package");
|
|
cmd.add(mApkInfo.packageInfo.renameManifestPackage);
|
|
|
|
cmd.add("--rename-instrumentation-target-package");
|
|
cmd.add(mApkInfo.packageInfo.renameManifestPackage);
|
|
}
|
|
|
|
if (mApkInfo.versionInfo.versionCode != null) {
|
|
cmd.add("--version-code");
|
|
cmd.add(mApkInfo.versionInfo.versionCode);
|
|
}
|
|
|
|
if (mApkInfo.versionInfo.versionName != null) {
|
|
cmd.add("--version-name");
|
|
cmd.add(mApkInfo.versionInfo.versionName);
|
|
}
|
|
|
|
// Disable automatic changes
|
|
cmd.add("--no-auto-version");
|
|
cmd.add("--no-version-vectors");
|
|
cmd.add("--no-version-transitions");
|
|
cmd.add("--no-resource-deduping");
|
|
|
|
cmd.add("--no-compile-sdk-metadata");
|
|
|
|
// #3427 - Ignore stricter parsing during aapt2
|
|
cmd.add("--warn-manifest-validation");
|
|
|
|
if (mApkInfo.sparseResources) {
|
|
cmd.add("--enable-sparse-encoding");
|
|
}
|
|
|
|
if (mApkInfo.compactEntries) {
|
|
cmd.add("--enable-compact-entries");
|
|
}
|
|
|
|
if (mApkInfo.isFrameworkApk) {
|
|
cmd.add("-x");
|
|
}
|
|
|
|
if (!mApkInfo.featureFlags.isEmpty()) {
|
|
List<String> featureFlags = new ArrayList<>();
|
|
for (Map.Entry<String, Boolean> entry : mApkInfo.featureFlags.entrySet()) {
|
|
featureFlags.add(entry.getKey() + "=" + entry.getValue());
|
|
}
|
|
cmd.add("--feature-flags");
|
|
cmd.add(String.join(",", featureFlags));
|
|
}
|
|
|
|
if (include != null) {
|
|
for (File file : include) {
|
|
cmd.add("-I");
|
|
cmd.add(file.getPath());
|
|
}
|
|
}
|
|
|
|
cmd.add("--manifest");
|
|
cmd.add(manifest.getAbsolutePath());
|
|
|
|
if (assetDir != null) {
|
|
cmd.add("-A");
|
|
cmd.add(assetDir.getAbsolutePath());
|
|
}
|
|
|
|
if (rawDir != null) {
|
|
cmd.add("-R");
|
|
cmd.add(rawDir.getAbsolutePath());
|
|
}
|
|
|
|
if (mConfig.verbose) {
|
|
cmd.add("-v");
|
|
}
|
|
|
|
if (resourcesZip != null) {
|
|
cmd.add(resourcesZip.getAbsolutePath());
|
|
}
|
|
|
|
try {
|
|
OS.exec(cmd.toArray(new String[0]));
|
|
LOGGER.fine("aapt2 link command ran: ");
|
|
LOGGER.fine(cmd.toString());
|
|
} catch (BrutException ex) {
|
|
throw new AndrolibException(ex);
|
|
}
|
|
}
|
|
|
|
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
|
|
cmd.add("-v");
|
|
}
|
|
if (mConfig.updateFiles) {
|
|
cmd.add("-u");
|
|
}
|
|
if (mConfig.debugMode) { // inject debuggable="true" into manifest
|
|
cmd.add("--debug-mode");
|
|
}
|
|
if (mConfig.noCrunch) {
|
|
cmd.add("--no-crunch");
|
|
}
|
|
// force package id so that some frameworks build with correct id
|
|
// disable if user adds own aapt (can't know if they have this feature)
|
|
if (mApkInfo.packageInfo.forcedPackageId != null && !mApkInfo.sharedLibrary && !customAapt) {
|
|
cmd.add("--forced-package-id");
|
|
cmd.add(mApkInfo.packageInfo.forcedPackageId);
|
|
}
|
|
if (mApkInfo.sharedLibrary) {
|
|
cmd.add("--shared-lib");
|
|
}
|
|
if (mApkInfo.getMinSdkVersion() != null) {
|
|
cmd.add("--min-sdk-version");
|
|
cmd.add(mApkInfo.getMinSdkVersion());
|
|
}
|
|
if (mApkInfo.getTargetSdkVersion() != null) {
|
|
cmd.add("--target-sdk-version");
|
|
|
|
// Ensure that targetSdkVersion is between minSdkVersion/maxSdkVersion if
|
|
// they are specified.
|
|
cmd.add(mApkInfo.checkTargetSdkVersionBounds());
|
|
}
|
|
if (mApkInfo.getMaxSdkVersion() != null) {
|
|
cmd.add("--max-sdk-version");
|
|
cmd.add(mApkInfo.getMaxSdkVersion());
|
|
|
|
// if we have max sdk version, set --max-res-version,
|
|
// so we can ignore anything over that during build.
|
|
cmd.add("--max-res-version");
|
|
cmd.add(mApkInfo.getMaxSdkVersion());
|
|
}
|
|
if (mApkInfo.packageInfo.renameManifestPackage != null) {
|
|
cmd.add("--rename-manifest-package");
|
|
cmd.add(mApkInfo.packageInfo.renameManifestPackage);
|
|
}
|
|
if (mApkInfo.versionInfo.versionCode != null) {
|
|
cmd.add("--version-code");
|
|
cmd.add(mApkInfo.versionInfo.versionCode);
|
|
}
|
|
if (mApkInfo.versionInfo.versionName != null) {
|
|
cmd.add("--version-name");
|
|
cmd.add(mApkInfo.versionInfo.versionName);
|
|
}
|
|
cmd.add("--no-version-vectors");
|
|
cmd.add("-F");
|
|
cmd.add(apkFile.getAbsolutePath());
|
|
|
|
if (mApkInfo.isFrameworkApk) {
|
|
cmd.add("-x");
|
|
}
|
|
|
|
if (include != null) {
|
|
for (File file : include) {
|
|
cmd.add("-I");
|
|
cmd.add(file.getPath());
|
|
}
|
|
}
|
|
if (resDir != null) {
|
|
cmd.add("-S");
|
|
cmd.add(resDir.getAbsolutePath());
|
|
}
|
|
if (manifest != null) {
|
|
cmd.add("-M");
|
|
cmd.add(manifest.getAbsolutePath());
|
|
}
|
|
if (assetDir != null) {
|
|
cmd.add("-A");
|
|
cmd.add(assetDir.getAbsolutePath());
|
|
}
|
|
if (rawDir != null) {
|
|
cmd.add(rawDir.getAbsolutePath());
|
|
}
|
|
try {
|
|
OS.exec(cmd.toArray(new String[0]));
|
|
LOGGER.fine("command ran: ");
|
|
LOGGER.fine(cmd.toString());
|
|
} catch (BrutException ex) {
|
|
throw new AndrolibException(ex);
|
|
}
|
|
}
|
|
}
|