oSumAtrIX 8f166d5125
Merge branch 'upstream'
# Conflicts:
#	brut.apktool/apktool-lib/src/main/java/brut/androlib/AaptInvoker.java
#	brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java
#	brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResourcesDecoder.java
#	brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/MissingDiv9PatchTest.java
#	brut.j.util/src/main/java/brut/util/BrutIO.java
#	brut.j.util/src/main/java/brut/util/OSDetection.java
#	build.gradle.kts
2024-12-17 03:43:46 +01:00

336 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 static final Logger LOGGER = Logger.getLogger(AaptInvoker.class.getName());
private final Config mConfig;
private final ApkInfo mApkInfo;
public AaptInvoker(Config config, ApkInfo apkInfo) {
mConfig = config;
mApkInfo = apkInfo;
}
public void invoke(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include)
throws AndrolibException {
File aaptBinary = mConfig.aaptBinary;
List<String> cmd = new ArrayList<>();
String aaptPath;
boolean customAapt;
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);
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");
//noinspection ResultOfMethodCallIgnored
buildDir.mkdir();
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");
// TODO: Add this back, once AAPT2 from platform-tools 34.0.4 is stable
// 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);
}
}
}