refactor: clean up style and redundancy (#3232)

* refactor: clean up redundancy

* refactor: no need for both setApkFile and setApkFileName
This commit is contained in:
Igor Eisberg 2023-07-30 01:56:27 +03:00 committed by GitHub
parent ce180dce87
commit 33ca2929c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 349 additions and 446 deletions

View File

@ -29,8 +29,7 @@ import brut.util.AaptManager;
import brut.util.OSDetection; import brut.util.OSDetection;
import org.apache.commons.cli.*; import org.apache.commons.cli.*;
import java.io.File; import java.io.*;
import java.io.IOException;
import java.util.logging.*; import java.util.logging.*;
/** /**

View File

@ -22,13 +22,8 @@ import brut.common.BrutException;
import brut.util.AaptManager; import brut.util.AaptManager;
import brut.util.OS; import brut.util.OS;
import java.io.BufferedWriter; import java.io.*;
import java.io.File; import java.util.*;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.logging.Logger; import java.util.logging.Logger;
public class AaptInvoker { public class AaptInvoker {
@ -81,8 +76,7 @@ public class AaptInvoker {
} }
private void invokeAapt2(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include, private void invokeAapt2(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include,
List<String> cmd, boolean customAapt) List<String> cmd, boolean customAapt) throws AndrolibException {
throws AndrolibException {
List<String> compileCommand = new ArrayList<>(cmd); List<String> compileCommand = new ArrayList<>(cmd);
File resourcesZip = null; File resourcesZip = null;
@ -249,8 +243,7 @@ public class AaptInvoker {
} }
private void invokeAapt1(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include, private void invokeAapt1(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include,
List<String> cmd, boolean customAapt) List<String> cmd, boolean customAapt) throws AndrolibException {
throws AndrolibException {
cmd.add("p"); cmd.add("p");
@ -365,7 +358,7 @@ public class AaptInvoker {
} }
public void invokeAapt(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include) public void invokeAapt(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include)
throws AndrolibException { throws AndrolibException {
String aaptPath = mConfig.aaptPath; String aaptPath = mConfig.aaptPath;
boolean customAapt = !aaptPath.isEmpty(); boolean customAapt = !aaptPath.isEmpty();

View File

@ -52,9 +52,9 @@ public class ApkBuilder {
private final static Logger LOGGER = Logger.getLogger(ApkBuilder.class.getName()); private final static Logger LOGGER = Logger.getLogger(ApkBuilder.class.getName());
private final Config mConfig; private final Config mConfig;
private final ExtFile mApkDir;
private ApkInfo mApkInfo; private ApkInfo mApkInfo;
private int mMinSdkVersion = 0; private int mMinSdkVersion = 0;
private final ExtFile mApkDir;
private final static String APK_DIRNAME = "build/apk"; private final static String APK_DIRNAME = "build/apk";
private final static String UNK_DIRNAME = "unknown"; private final static String UNK_DIRNAME = "unknown";
@ -87,7 +87,7 @@ public class ApkBuilder {
} }
if (outFile == null) { if (outFile == null) {
String outFileName = mApkInfo.getApkFileName(); String outFileName = mApkInfo.apkFileName;
outFile = new File(mApkDir, "dist" + File.separator + (outFileName == null ? "out.apk" : outFileName)); outFile = new File(mApkDir, "dist" + File.separator + (outFileName == null ? "out.apk" : outFileName));
} }
@ -96,17 +96,17 @@ public class ApkBuilder {
File manifest = new File(mApkDir, "AndroidManifest.xml"); File manifest = new File(mApkDir, "AndroidManifest.xml");
File manifestOriginal = new File(mApkDir, "AndroidManifest.xml.orig"); File manifestOriginal = new File(mApkDir, "AndroidManifest.xml.orig");
buildSources(mApkDir); buildSources();
buildNonDefaultSources(mApkDir); buildNonDefaultSources();
buildManifestFile(mApkDir, manifest, manifestOriginal); buildManifestFile(manifest, manifestOriginal);
buildResources(mApkDir, mApkInfo.usesFramework); buildResources();
buildLibs(mApkDir); buildLibs();
buildCopyOriginalFiles(mApkDir); buildCopyOriginalFiles();
buildApk(mApkDir, outFile); buildApk(outFile);
// we must go after the Apk is built, and copy the files in via Zip // we must go after the Apk is built, and copy the files in via Zip
// this is because Aapt won't add files it doesn't know (ex unknown files) // this is because Aapt won't add files it doesn't know (ex unknown files)
buildUnknownFiles(mApkDir, outFile, mApkInfo); buildUnknownFiles(outFile);
// we copied the AndroidManifest.xml to AndroidManifest.xml.orig so we can edit it // we copied the AndroidManifest.xml to AndroidManifest.xml.orig so we can edit it
// lets restore the unedited one, to not change the original // lets restore the unedited one, to not change the original
@ -122,11 +122,9 @@ public class ApkBuilder {
LOGGER.info("Built apk into: " + outFile.getPath()); LOGGER.info("Built apk into: " + outFile.getPath());
} }
private void buildManifestFile(File appDir, File manifest, File manifestOriginal) private void buildManifestFile(File manifest, File manifestOriginal) throws AndrolibException {
throws AndrolibException {
// If we decoded in "raw", we cannot patch AndroidManifest // If we decoded in "raw", we cannot patch AndroidManifest
if (new File(appDir, "resources.arsc").exists()) { if (new File(mApkDir, "resources.arsc").exists()) {
return; return;
} }
if (manifest.isFile() && manifest.exists()) { if (manifest.isFile() && manifest.exists()) {
@ -143,36 +141,34 @@ public class ApkBuilder {
} }
} }
private void buildSources(File appDir) private void buildSources() throws AndrolibException {
throws AndrolibException { if (!buildSourcesRaw("classes.dex") && !buildSourcesSmali("smali", "classes.dex")) {
if (!buildSourcesRaw(appDir, "classes.dex") && !buildSourcesSmali(appDir, "smali", "classes.dex")) {
LOGGER.warning("Could not find sources"); LOGGER.warning("Could not find sources");
} }
} }
private void buildNonDefaultSources(ExtFile appDir) private void buildNonDefaultSources() throws AndrolibException {
throws AndrolibException {
try { try {
// loop through any smali_ directories for multi-dex apks // loop through any smali_ directories for multi-dex apks
Map<String, Directory> dirs = appDir.getDirectory().getDirs(); Map<String, Directory> dirs = mApkDir.getDirectory().getDirs();
for (Map.Entry<String, Directory> directory : dirs.entrySet()) { for (Map.Entry<String, Directory> directory : dirs.entrySet()) {
String name = directory.getKey(); String name = directory.getKey();
if (name.startsWith("smali_")) { if (name.startsWith("smali_")) {
String filename = name.substring(name.indexOf("_") + 1) + ".dex"; String filename = name.substring(name.indexOf("_") + 1) + ".dex";
if (!buildSourcesRaw(appDir, filename) && !buildSourcesSmali(appDir, name, filename)) { if (!buildSourcesRaw(filename) && !buildSourcesSmali(name, filename)) {
LOGGER.warning("Could not find sources"); LOGGER.warning("Could not find sources");
} }
} }
} }
// loop through any classes#.dex files for multi-dex apks // loop through any classes#.dex files for multi-dex apks
File[] dexFiles = appDir.listFiles(); File[] dexFiles = mApkDir.listFiles();
if (dexFiles != null) { if (dexFiles != null) {
for (File dex : dexFiles) { for (File dex : dexFiles) {
// skip classes.dex because we have handled it in buildSources() // skip classes.dex because we have handled it in buildSources()
if (dex.getName().endsWith(".dex") && ! dex.getName().equalsIgnoreCase("classes.dex")) { if (dex.getName().endsWith(".dex") && !dex.getName().equalsIgnoreCase("classes.dex")) {
buildSourcesRaw(appDir, dex.getName()); buildSourcesRaw(dex.getName());
} }
} }
} }
@ -181,15 +177,14 @@ public class ApkBuilder {
} }
} }
private boolean buildSourcesRaw(File appDir, String filename) private boolean buildSourcesRaw(String filename) throws AndrolibException {
throws AndrolibException { File working = new File(mApkDir, filename);
File working = new File(appDir, filename);
if (!working.exists()) { if (!working.exists()) {
return false; return false;
} }
File stored = new File(appDir, APK_DIRNAME + "/" + filename); File stored = new File(mApkDir, APK_DIRNAME + "/" + filename);
if (mConfig.forceBuildAll || isModified(working, stored)) { if (mConfig.forceBuildAll || isModified(working, stored)) {
LOGGER.info("Copying " + appDir.toString() + " " + filename + " file..."); LOGGER.info("Copying " + mApkDir.toString() + " " + filename + " file...");
try { try {
BrutIO.copyAndClose(Files.newInputStream(working.toPath()), Files.newOutputStream(stored.toPath())); BrutIO.copyAndClose(Files.newInputStream(working.toPath()), Files.newOutputStream(stored.toPath()));
return true; return true;
@ -200,14 +195,13 @@ public class ApkBuilder {
return true; return true;
} }
private boolean buildSourcesSmali(File appDir, String folder, String filename) private boolean buildSourcesSmali(String folder, String filename) throws AndrolibException {
throws AndrolibException { ExtFile smaliDir = new ExtFile(mApkDir, folder);
ExtFile smaliDir = new ExtFile(appDir, folder);
if (!smaliDir.exists()) { if (!smaliDir.exists()) {
return false; return false;
} }
File dex = new File(appDir, APK_DIRNAME + "/" + filename); File dex = new File(mApkDir, APK_DIRNAME + "/" + filename);
if (! mConfig.forceBuildAll) { if (!mConfig.forceBuildAll) {
LOGGER.info("Checking whether sources has changed..."); LOGGER.info("Checking whether sources has changed...");
} }
if (mConfig.forceBuildAll || isModified(smaliDir, dex)) { if (mConfig.forceBuildAll || isModified(smaliDir, dex)) {
@ -219,28 +213,25 @@ public class ApkBuilder {
return true; return true;
} }
private void buildResources(ExtFile appDir, UsesFramework usesFramework) private void buildResources() throws BrutException {
throws BrutException { if (!buildResourcesRaw() && !buildResourcesFull() && !buildManifest()) {
if (!buildResourcesRaw(appDir) && !buildResourcesFull(appDir, usesFramework)
&& !buildManifest(appDir, usesFramework)) {
LOGGER.warning("Could not find resources"); LOGGER.warning("Could not find resources");
} }
} }
private boolean buildResourcesRaw(ExtFile appDir) private boolean buildResourcesRaw() throws AndrolibException {
throws AndrolibException {
try { try {
if (!new File(appDir, "resources.arsc").exists()) { if (!new File(mApkDir, "resources.arsc").exists()) {
return false; return false;
} }
File apkDir = new File(appDir, APK_DIRNAME); File apkDir = new File(mApkDir, APK_DIRNAME);
if (! mConfig.forceBuildAll) { if (!mConfig.forceBuildAll) {
LOGGER.info("Checking whether resources has changed..."); LOGGER.info("Checking whether resources has changed...");
} }
if (mConfig.forceBuildAll || isModified(newFiles(APK_RESOURCES_FILENAMES, appDir), if (mConfig.forceBuildAll || isModified(newFiles(APK_RESOURCES_FILENAMES, mApkDir),
newFiles(APK_RESOURCES_FILENAMES, apkDir))) { newFiles(APK_RESOURCES_FILENAMES, apkDir))) {
LOGGER.info("Copying raw resources..."); LOGGER.info("Copying raw resources...");
appDir.getDirectory().copyToDir(apkDir, APK_RESOURCES_FILENAMES); mApkDir.getDirectory().copyToDir(apkDir, APK_RESOURCES_FILENAMES);
} }
return true; return true;
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
@ -248,46 +239,45 @@ public class ApkBuilder {
} }
} }
private boolean buildResourcesFull(File appDir, UsesFramework usesFramework) private boolean buildResourcesFull() throws AndrolibException {
throws AndrolibException {
try { try {
if (!new File(appDir, "res").exists()) { if (!new File(mApkDir, "res").exists()) {
return false; return false;
} }
if (! mConfig.forceBuildAll) { if (!mConfig.forceBuildAll) {
LOGGER.info("Checking whether resources has changed..."); LOGGER.info("Checking whether resources has changed...");
} }
File apkDir = new File(appDir, APK_DIRNAME); File apkDir = new File(mApkDir, APK_DIRNAME);
File resourceFile = new File(apkDir.getParent(), "resources.zip"); File resourceFile = new File(apkDir.getParent(), "resources.zip");
if (mConfig.forceBuildAll || isModified(newFiles(APP_RESOURCES_FILENAMES, appDir), if (mConfig.forceBuildAll || isModified(newFiles(APP_RESOURCES_FILENAMES, mApkDir),
newFiles(APK_RESOURCES_FILENAMES, apkDir)) || (mConfig.isAapt2() && !isFile(resourceFile))) { newFiles(APK_RESOURCES_FILENAMES, apkDir)) || (mConfig.isAapt2() && !isFile(resourceFile))) {
LOGGER.info("Building resources..."); LOGGER.info("Building resources...");
if (mConfig.debugMode) { if (mConfig.debugMode) {
if (mConfig.isAapt2()) { if (mConfig.isAapt2()) {
LOGGER.info("Using aapt2 - setting 'debuggable' attribute to 'true' in AndroidManifest.xml"); LOGGER.info("Using aapt2 - setting 'debuggable' attribute to 'true' in AndroidManifest.xml");
ResXmlPatcher.setApplicationDebugTagTrue(new File(appDir, "AndroidManifest.xml")); ResXmlPatcher.setApplicationDebugTagTrue(new File(mApkDir, "AndroidManifest.xml"));
} else { } else {
ResXmlPatcher.removeApplicationDebugTag(new File(appDir, "AndroidManifest.xml")); ResXmlPatcher.removeApplicationDebugTag(new File(mApkDir, "AndroidManifest.xml"));
} }
} }
if (mConfig.netSecConf) { if (mConfig.netSecConf) {
ApkInfo meta = ApkInfo.load(new ExtFile(appDir)); ApkInfo meta = ApkInfo.load(new ExtFile(mApkDir));
if (meta.getSdkInfo() != null && meta.getSdkInfo().get("targetSdkVersion") != null) { if (meta.getSdkInfo() != null && meta.getSdkInfo().get("targetSdkVersion") != null) {
if (Integer.parseInt(meta.getSdkInfo().get("targetSdkVersion")) < ResConfigFlags.SDK_NOUGAT) { if (Integer.parseInt(meta.getSdkInfo().get("targetSdkVersion")) < ResConfigFlags.SDK_NOUGAT) {
LOGGER.warning("Target SDK version is lower than 24! Network Security Configuration might be ignored!"); LOGGER.warning("Target SDK version is lower than 24! Network Security Configuration might be ignored!");
} }
} }
File netSecConfOrig = new File(appDir, "res/xml/network_security_config.xml"); File netSecConfOrig = new File(mApkDir, "res/xml/network_security_config.xml");
if (netSecConfOrig.exists()) { if (netSecConfOrig.exists()) {
LOGGER.info("Replacing existing network_security_config.xml!"); LOGGER.info("Replacing existing network_security_config.xml!");
//noinspection ResultOfMethodCallIgnored //noinspection ResultOfMethodCallIgnored
netSecConfOrig.delete(); netSecConfOrig.delete();
} }
ResXmlPatcher.modNetworkSecurityConfig(netSecConfOrig); ResXmlPatcher.modNetworkSecurityConfig(netSecConfOrig);
ResXmlPatcher.setNetworkSecurityConfig(new File(appDir, "AndroidManifest.xml")); ResXmlPatcher.setNetworkSecurityConfig(new File(mApkDir, "AndroidManifest.xml"));
LOGGER.info("Added permissive network security config in manifest"); LOGGER.info("Added permissive network security config in manifest");
} }
@ -297,14 +287,13 @@ public class ApkBuilder {
//noinspection ResultOfMethodCallIgnored //noinspection ResultOfMethodCallIgnored
resourceFile.delete(); resourceFile.delete();
File ninePatch = new File(appDir, "9patch"); File ninePatch = new File(mApkDir, "9patch");
if (!ninePatch.exists()) { if (!ninePatch.exists()) {
ninePatch = null; ninePatch = null;
} }
AaptInvoker invoker = new AaptInvoker(mConfig, mApkInfo); AaptInvoker invoker = new AaptInvoker(mConfig, mApkInfo);
invoker.invokeAapt(apkFile, new File(appDir, invoker.invokeAapt(apkFile, new File(mApkDir, "AndroidManifest.xml"),
"AndroidManifest.xml"), new File(appDir, "res"), new File(mApkDir, "res"), ninePatch, null, getIncludeFiles());
ninePatch, null, parseUsesFramework(usesFramework));
ExtFile tmpExtFile = new ExtFile(apkFile); ExtFile tmpExtFile = new ExtFile(apkFile);
Directory tmpDir = tmpExtFile.getDirectory(); Directory tmpDir = tmpExtFile.getDirectory();
@ -332,31 +321,29 @@ public class ApkBuilder {
} }
} }
private boolean buildManifestRaw(ExtFile appDir) private boolean buildManifestRaw() throws AndrolibException {
throws AndrolibException {
try { try {
File apkDir = new File(appDir, APK_DIRNAME); File apkDir = new File(mApkDir, APK_DIRNAME);
LOGGER.info("Copying raw AndroidManifest.xml..."); LOGGER.info("Copying raw AndroidManifest.xml...");
appDir.getDirectory().copyToDir(apkDir, APK_MANIFEST_FILENAMES); mApkDir.getDirectory().copyToDir(apkDir, APK_MANIFEST_FILENAMES);
return true; return true;
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
} }
private boolean buildManifest(ExtFile appDir, UsesFramework usesFramework) private boolean buildManifest() throws BrutException {
throws BrutException {
try { try {
if (!new File(appDir, "AndroidManifest.xml").exists()) { if (!new File(mApkDir, "AndroidManifest.xml").exists()) {
return false; return false;
} }
if (! mConfig.forceBuildAll) { if (!mConfig.forceBuildAll) {
LOGGER.info("Checking whether resources has changed..."); LOGGER.info("Checking whether resources has changed...");
} }
File apkDir = new File(appDir, APK_DIRNAME); File apkDir = new File(mApkDir, APK_DIRNAME);
if (mConfig.forceBuildAll || isModified(newFiles(APK_MANIFEST_FILENAMES, appDir), if (mConfig.forceBuildAll || isModified(newFiles(APK_MANIFEST_FILENAMES, mApkDir),
newFiles(APK_MANIFEST_FILENAMES, apkDir))) { newFiles(APK_MANIFEST_FILENAMES, apkDir))) {
LOGGER.info("Building AndroidManifest.xml..."); LOGGER.info("Building AndroidManifest.xml...");
@ -364,15 +351,14 @@ public class ApkBuilder {
//noinspection ResultOfMethodCallIgnored //noinspection ResultOfMethodCallIgnored
apkFile.delete(); apkFile.delete();
File ninePatch = new File(appDir, "9patch"); File ninePatch = new File(mApkDir, "9patch");
if (!ninePatch.exists()) { if (!ninePatch.exists()) {
ninePatch = null; ninePatch = null;
} }
AaptInvoker invoker = new AaptInvoker(mConfig, mApkInfo); AaptInvoker invoker = new AaptInvoker(mConfig, mApkInfo);
invoker.invokeAapt(apkFile, new File(appDir, invoker.invokeAapt(apkFile, new File(mApkDir, "AndroidManifest.xml"),
"AndroidManifest.xml"), null, ninePatch, null, null, ninePatch, null, getIncludeFiles());
parseUsesFramework(usesFramework));
Directory tmpDir = new ExtFile(apkFile).getDirectory(); Directory tmpDir = new ExtFile(apkFile).getDirectory();
tmpDir.copyToDir(apkDir, APK_MANIFEST_FILENAMES); tmpDir.copyToDir(apkDir, APK_MANIFEST_FILENAMES);
@ -385,25 +371,25 @@ public class ApkBuilder {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} catch (AndrolibException ex) { } catch (AndrolibException ex) {
LOGGER.warning("Parse AndroidManifest.xml failed, treat it as raw file."); LOGGER.warning("Parse AndroidManifest.xml failed, treat it as raw file.");
return buildManifestRaw(appDir); return buildManifestRaw();
} }
} }
private void buildLibs(File appDir) throws AndrolibException { private void buildLibs() throws AndrolibException {
buildLibrary(appDir, "lib"); buildLibrary("lib");
buildLibrary(appDir, "libs"); buildLibrary("libs");
buildLibrary(appDir, "kotlin"); buildLibrary("kotlin");
buildLibrary(appDir, "META-INF/services"); buildLibrary("META-INF/services");
} }
private void buildLibrary(File appDir, String folder) throws AndrolibException { private void buildLibrary(String folder) throws AndrolibException {
File working = new File(appDir, folder); File working = new File(mApkDir, folder);
if (! working.exists()) { if (!working.exists()) {
return; return;
} }
File stored = new File(appDir, APK_DIRNAME + "/" + folder); File stored = new File(mApkDir, APK_DIRNAME + "/" + folder);
if (mConfig.forceBuildAll || isModified(working, stored)) { if (mConfig.forceBuildAll || isModified(working, stored)) {
LOGGER.info("Copying libs... (/" + folder + ")"); LOGGER.info("Copying libs... (/" + folder + ")");
try { try {
@ -415,25 +401,24 @@ public class ApkBuilder {
} }
} }
private void buildCopyOriginalFiles(File appDir) private void buildCopyOriginalFiles() throws AndrolibException {
throws AndrolibException {
if (mConfig.copyOriginalFiles) { if (mConfig.copyOriginalFiles) {
File originalDir = new File(appDir, "original"); File originalDir = new File(mApkDir, "original");
if (originalDir.exists()) { if (originalDir.exists()) {
try { try {
LOGGER.info("Copy original files..."); LOGGER.info("Copy original files...");
Directory in = (new ExtFile(originalDir)).getDirectory(); Directory in = (new ExtFile(originalDir)).getDirectory();
if (in.containsFile("AndroidManifest.xml")) { if (in.containsFile("AndroidManifest.xml")) {
LOGGER.info("Copy AndroidManifest.xml..."); LOGGER.info("Copy AndroidManifest.xml...");
in.copyToDir(new File(appDir, APK_DIRNAME), "AndroidManifest.xml"); in.copyToDir(new File(mApkDir, APK_DIRNAME), "AndroidManifest.xml");
} }
if (in.containsFile("stamp-cert-sha256")) { if (in.containsFile("stamp-cert-sha256")) {
LOGGER.info("Copy stamp-cert-sha256..."); LOGGER.info("Copy stamp-cert-sha256...");
in.copyToDir(new File(appDir, APK_DIRNAME), "stamp-cert-sha256"); in.copyToDir(new File(mApkDir, APK_DIRNAME), "stamp-cert-sha256");
} }
if (in.containsDir("META-INF")) { if (in.containsDir("META-INF")) {
LOGGER.info("Copy META-INF..."); LOGGER.info("Copy META-INF...");
in.copyToDir(new File(appDir, APK_DIRNAME), "META-INF"); in.copyToDir(new File(mApkDir, APK_DIRNAME), "META-INF");
} }
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
@ -442,12 +427,11 @@ public class ApkBuilder {
} }
} }
private void buildUnknownFiles(File appDir, File outFile, ApkInfo meta) private void buildUnknownFiles(File outFile) throws AndrolibException {
throws AndrolibException { if (mApkInfo.unknownFiles != null) {
if (meta.unknownFiles != null) {
LOGGER.info("Copying unknown files/dir..."); LOGGER.info("Copying unknown files/dir...");
Map<String, String> files = meta.unknownFiles; Map<String, String> files = mApkInfo.unknownFiles;
File tempFile = new File(outFile.getParent(), outFile.getName() + ".apktool_temp"); File tempFile = new File(outFile.getParent(), outFile.getName() + ".apktool_temp");
boolean renamed = outFile.renameTo(tempFile); boolean renamed = outFile.renameTo(tempFile);
if (!renamed) { if (!renamed) {
@ -455,11 +439,11 @@ public class ApkBuilder {
} }
try ( try (
ZipFile inputFile = new ZipFile(tempFile); ZipFile inputFile = new ZipFile(tempFile);
ZipOutputStream actualOutput = new ZipOutputStream(Files.newOutputStream(outFile.toPath())) ZipOutputStream actualOutput = new ZipOutputStream(Files.newOutputStream(outFile.toPath()))
) { ) {
copyExistingFiles(inputFile, actualOutput); copyExistingFiles(inputFile, actualOutput);
copyUnknownFiles(appDir, actualOutput, files); copyUnknownFiles(actualOutput, files);
} catch (IOException | BrutException ex) { } catch (IOException | BrutException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
@ -481,7 +465,7 @@ public class ApkBuilder {
outputFile.putNextEntry(entry); outputFile.putNextEntry(entry);
// No need to create directory entries in the final apk // No need to create directory entries in the final apk
if (! entry.isDirectory()) { if (!entry.isDirectory()) {
BrutIO.copy(inputFile, outputFile, entry); BrutIO.copy(inputFile, outputFile, entry);
} }
@ -489,9 +473,9 @@ public class ApkBuilder {
} }
} }
private void copyUnknownFiles(File appDir, ZipOutputStream outputFile, Map<String, String> files) private void copyUnknownFiles(ZipOutputStream outputFile, Map<String, String> files)
throws BrutException, IOException { throws BrutException, IOException {
File unknownFileDir = new File(appDir, UNK_DIRNAME); File unknownFileDir = new File(mApkDir, UNK_DIRNAME);
// loop through unknown files // loop through unknown files
for (Map.Entry<String,String> unknownFileInfo : files.entrySet()) { for (Map.Entry<String,String> unknownFileInfo : files.entrySet()) {
@ -529,7 +513,7 @@ public class ApkBuilder {
} }
} }
private void buildApk(File appDir, File outApk) throws AndrolibException { private void buildApk(File outApk) throws AndrolibException {
LOGGER.info("Building apk file..."); LOGGER.info("Building apk file...");
if (outApk.exists()) { if (outApk.exists()) {
//noinspection ResultOfMethodCallIgnored //noinspection ResultOfMethodCallIgnored
@ -541,16 +525,14 @@ public class ApkBuilder {
outDir.mkdirs(); outDir.mkdirs();
} }
} }
File assetDir = new File(appDir, "assets"); File assetDir = new File(mApkDir, "assets");
if (!assetDir.exists()) { if (!assetDir.exists()) {
assetDir = null; assetDir = null;
} }
zipPackage(outApk, new File(appDir, APK_DIRNAME), assetDir); zipPackage(outApk, new File(mApkDir, APK_DIRNAME), assetDir);
} }
private void zipPackage(File apkFile, File rawDir, File assetDir) private void zipPackage(File apkFile, File rawDir, File assetDir) throws AndrolibException {
throws AndrolibException {
try { try {
ZipUtils.zipFolders(rawDir, apkFile, assetDir, mApkInfo.doNotCompress); ZipUtils.zipFolders(rawDir, apkFile, assetDir, mApkInfo.doNotCompress);
} catch (IOException | BrutException ex) { } catch (IOException | BrutException ex) {
@ -558,8 +540,8 @@ public class ApkBuilder {
} }
} }
private File[] parseUsesFramework(UsesFramework usesFramework) private File[] getIncludeFiles() throws AndrolibException {
throws AndrolibException { UsesFramework usesFramework = mApkInfo.usesFramework;
if (usesFramework == null) { if (usesFramework == null) {
return null; return null;
} }
@ -580,7 +562,7 @@ public class ApkBuilder {
} }
private boolean isModified(File working, File stored) { private boolean isModified(File working, File stored) {
return ! stored.exists() || BrutIO.recursiveModifiedTime(working) > BrutIO .recursiveModifiedTime(stored); return !stored.exists() || BrutIO.recursiveModifiedTime(working) > BrutIO .recursiveModifiedTime(stored);
} }
private boolean isFile(File working) { private boolean isFile(File working) {
@ -604,17 +586,15 @@ public class ApkBuilder {
return files; return files;
} }
public boolean detectWhetherAppIsFramework(File appDir) public boolean detectWhetherAppIsFramework() throws AndrolibException {
throws AndrolibException { File publicXml = new File(mApkDir, "res/values/public.xml");
File publicXml = new File(appDir, "res/values/public.xml"); if (!publicXml.exists()) {
if (! publicXml.exists()) {
return false; return false;
} }
Iterator<String> it; Iterator<String> it;
try { try {
it = IOUtils.lineIterator(new FileReader(new File(appDir, it = IOUtils.lineIterator(new FileReader(new File(mApkDir, "res/values/public.xml")));
"res/values/public.xml")));
} catch (FileNotFoundException ex) { } catch (FileNotFoundException ex) {
throw new AndrolibException( throw new AndrolibException(
"Could not detect whether app is framework one", ex); "Could not detect whether app is framework one", ex);

View File

@ -32,8 +32,7 @@ import brut.util.OS;
import com.android.tools.smali.dexlib2.iface.DexFile; import com.android.tools.smali.dexlib2.iface.DexFile;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import java.io.File; import java.io.*;
import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -44,6 +43,7 @@ public class ApkDecoder {
private final Config mConfig; private final Config mConfig;
private final ExtFile mApkFile; private final ExtFile mApkFile;
protected final ResUnknownFiles mResUnknownFiles; protected final ResUnknownFiles mResUnknownFiles;
private ApkInfo mApkInfo;
private int mMinSdkVersion = 0; private int mMinSdkVersion = 0;
private final static String SMALI_DIRNAME = "smali"; private final static String SMALI_DIRNAME = "smali";
@ -59,24 +59,24 @@ public class ApkDecoder {
"jpg|jpeg|png|gif|wav|mp2|mp3|ogg|aac|mpg|mpeg|mid|midi|smf|jet|rtttl|imy|xmf|mp4|" + "jpg|jpeg|png|gif|wav|mp2|mp3|ogg|aac|mpg|mpeg|mid|midi|smf|jet|rtttl|imy|xmf|mp4|" +
"m4a|m4v|3gp|3gpp|3g2|3gpp2|amr|awb|wma|wmv|webm|webp|mkv)$"); "m4a|m4v|3gp|3gpp|3g2|3gpp2|amr|awb|wma|wmv|webm|webp|mkv)$");
public ApkDecoder(File apkFile) {
this(Config.getDefaultConfig(), new ExtFile(apkFile));
}
public ApkDecoder(ExtFile apkFile) { public ApkDecoder(ExtFile apkFile) {
this(Config.getDefaultConfig(), apkFile); this(Config.getDefaultConfig(), apkFile);
} }
public ApkDecoder(Config config, ExtFile apkFile) {
mConfig = config;
mResUnknownFiles = new ResUnknownFiles();
mApkFile = apkFile;
}
public ApkDecoder(File apkFile) {
this(new ExtFile(apkFile));
}
public ApkDecoder(Config config, File apkFile) { public ApkDecoder(Config config, File apkFile) {
this(config, new ExtFile(apkFile)); this(config, new ExtFile(apkFile));
} }
public ApkDecoder(Config config, ExtFile apkFile) {
mConfig = config;
mApkFile = apkFile;
mResUnknownFiles = new ResUnknownFiles();
}
public ApkInfo decode(File outDir) throws AndrolibException, IOException, DirectoryException { public ApkInfo decode(File outDir) throws AndrolibException, IOException, DirectoryException {
try { try {
if (!mConfig.forceDelete && outDir.exists()) { if (!mConfig.forceDelete && outDir.exists()) {
@ -96,8 +96,11 @@ public class ApkDecoder {
LOGGER.info("Using Apktool " + ApktoolProperties.getVersion() + " on " + mApkFile.getName()); LOGGER.info("Using Apktool " + ApktoolProperties.getVersion() + " on " + mApkFile.getName());
ResourcesDecoder resourcesDecoder = new ResourcesDecoder(mConfig, mApkFile); mApkInfo = new ApkInfo(mApkFile);
if (hasResources()) {
ResourcesDecoder resourcesDecoder = new ResourcesDecoder(mConfig, mApkFile, mApkInfo);
if (mApkInfo.hasResources()) {
switch (mConfig.decodeResources) { switch (mConfig.decodeResources) {
case Config.DECODE_RESOURCES_NONE: case Config.DECODE_RESOURCES_NONE:
copyResourcesRaw(outDir); copyResourcesRaw(outDir);
@ -108,7 +111,7 @@ public class ApkDecoder {
} }
} }
if (hasManifest()) { if (mApkInfo.hasManifest()) {
if (mConfig.decodeResources == Config.DECODE_RESOURCES_FULL || if (mConfig.decodeResources == Config.DECODE_RESOURCES_FULL ||
mConfig.forceDecodeManifest == Config.FORCE_DECODE_MANIFEST_FULL) { mConfig.forceDecodeManifest == Config.FORCE_DECODE_MANIFEST_FULL) {
resourcesDecoder.decodeManifest(outDir); resourcesDecoder.decodeManifest(outDir);
@ -119,7 +122,7 @@ public class ApkDecoder {
} }
resourcesDecoder.updateApkInfo(outDir); resourcesDecoder.updateApkInfo(outDir);
if (hasSources()) { if (mApkInfo.hasSources()) {
switch (mConfig.decodeSources) { switch (mConfig.decodeSources) {
case Config.DECODE_SOURCES_NONE: case Config.DECODE_SOURCES_NONE:
copySourcesRaw(outDir, "classes.dex"); copySourcesRaw(outDir, "classes.dex");
@ -131,12 +134,12 @@ public class ApkDecoder {
} }
} }
if (hasMultipleSources()) { if (mApkInfo.hasMultipleSources()) {
// foreach unknown dex file in root, lets disassemble it // foreach unknown dex file in root, lets disassemble it
Set<String> files = mApkFile.getDirectory().getFiles(true); Set<String> files = mApkFile.getDirectory().getFiles(true);
for (String file : files) { for (String file : files) {
if (file.endsWith(".dex")) { if (file.endsWith(".dex")) {
if (! file.equalsIgnoreCase("classes.dex")) { if (!file.equalsIgnoreCase("classes.dex")) {
switch(mConfig.decodeSources) { switch(mConfig.decodeSources) {
case Config.DECODE_SOURCES_NONE: case Config.DECODE_SOURCES_NONE:
copySourcesRaw(outDir, file); copySourcesRaw(outDir, file);
@ -158,19 +161,17 @@ public class ApkDecoder {
} }
// In case we have no resources. We should store the minSdk we pulled from the source opcode api level // In case we have no resources. We should store the minSdk we pulled from the source opcode api level
ApkInfo apkInfo = resourcesDecoder.getApkInfo(); if (!mApkInfo.hasResources() && mMinSdkVersion > 0) {
if (!hasResources() && mMinSdkVersion > 0) { mApkInfo.setSdkInfoField("minSdkVersion", Integer.toString(mMinSdkVersion));
apkInfo.setSdkInfoField("minSdkVersion", Integer.toString(mMinSdkVersion));
} }
copyRawFiles(outDir); copyRawFiles(outDir);
copyUnknownFiles(apkInfo, outDir); copyUnknownFiles(outDir);
List<String> mUncompressedFiles = new ArrayList<>(); recordUncompressedFiles(resourcesDecoder.getResFileMapping());
recordUncompressedFiles(apkInfo, resourcesDecoder.getResFileMapping(), mUncompressedFiles);
copyOriginalFiles(outDir); copyOriginalFiles(outDir);
writeApkInfo(apkInfo, outDir); writeApkInfo(outDir);
return apkInfo; return mApkInfo;
} finally { } finally {
try { try {
mApkFile.close(); mApkFile.close();
@ -178,53 +179,11 @@ public class ApkDecoder {
} }
} }
private boolean hasManifest() throws AndrolibException { private void writeApkInfo(File outDir) throws AndrolibException {
try { mApkInfo.save(new File(outDir, "apktool.yml"));
return mApkFile.getDirectory().containsFile("AndroidManifest.xml");
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
} }
private boolean hasResources() throws AndrolibException { private void copyManifestRaw(File outDir) throws AndrolibException {
try {
return mApkFile.getDirectory().containsFile("resources.arsc");
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}
private boolean hasSources() throws AndrolibException {
try {
return mApkFile.getDirectory().containsFile("classes.dex");
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}
private boolean hasMultipleSources() throws AndrolibException {
try {
Set<String> files = mApkFile.getDirectory().getFiles(false);
for (String file : files) {
if (file.endsWith(".dex")) {
if (! file.equalsIgnoreCase("classes.dex")) {
return true;
}
}
}
return false;
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}
private void writeApkInfo(ApkInfo apkInfo, File outDir) throws AndrolibException {
apkInfo.save(new File(outDir, "apktool.yml"));
}
private void copyManifestRaw(File outDir)
throws AndrolibException {
try { try {
LOGGER.info("Copying raw manifest..."); LOGGER.info("Copying raw manifest...");
mApkFile.getDirectory().copyToDir(outDir, APK_MANIFEST_FILENAMES); mApkFile.getDirectory().copyToDir(outDir, APK_MANIFEST_FILENAMES);
@ -233,8 +192,7 @@ public class ApkDecoder {
} }
} }
private void copyResourcesRaw(File outDir) private void copyResourcesRaw(File outDir) throws AndrolibException {
throws AndrolibException {
try { try {
LOGGER.info("Copying raw resources..."); LOGGER.info("Copying raw resources...");
mApkFile.getDirectory().copyToDir(outDir, APK_RESOURCES_FILENAMES); mApkFile.getDirectory().copyToDir(outDir, APK_RESOURCES_FILENAMES);
@ -243,8 +201,7 @@ public class ApkDecoder {
} }
} }
private void copySourcesRaw(File outDir, String filename) private void copySourcesRaw(File outDir, String filename) throws AndrolibException {
throws AndrolibException {
try { try {
LOGGER.info("Copying raw " + filename + " file..."); LOGGER.info("Copying raw " + filename + " file...");
mApkFile.getDirectory().copyToDir(outDir, filename); mApkFile.getDirectory().copyToDir(outDir, filename);
@ -253,8 +210,7 @@ public class ApkDecoder {
} }
} }
private void decodeSourcesSmali(File outDir, String filename) private void decodeSourcesSmali(File outDir, String filename) throws AndrolibException {
throws AndrolibException {
try { try {
File smaliDir; File smaliDir;
if (filename.equalsIgnoreCase("classes.dex")) { if (filename.equalsIgnoreCase("classes.dex")) {
@ -277,8 +233,7 @@ public class ApkDecoder {
} }
} }
private void copyRawFiles(File outDir) private void copyRawFiles(File outDir) throws AndrolibException {
throws AndrolibException {
LOGGER.info("Copying assets and libs..."); LOGGER.info("Copying assets and libs...");
try { try {
Directory in = mApkFile.getDirectory(); Directory in = mApkFile.getDirectory();
@ -311,8 +266,7 @@ public class ApkDecoder {
return false; return false;
} }
private void copyUnknownFiles(ApkInfo apkInfo, File outDir) private void copyUnknownFiles(File outDir) throws AndrolibException {
throws AndrolibException {
LOGGER.info("Copying unknown files..."); LOGGER.info("Copying unknown files...");
File unknownOut = new File(outDir, UNK_DIRNAME); File unknownOut = new File(outDir, UNK_DIRNAME);
try { try {
@ -331,14 +285,13 @@ public class ApkDecoder {
} }
} }
// update apk info // update apk info
apkInfo.unknownFiles = mResUnknownFiles.getUnknownFiles(); mApkInfo.unknownFiles = mResUnknownFiles.getUnknownFiles();
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
} }
private void copyOriginalFiles(File outDir) private void copyOriginalFiles(File outDir) throws AndrolibException {
throws AndrolibException {
LOGGER.info("Copying original files..."); LOGGER.info("Copying original files...");
File originalDir = new File(outDir, "original"); File originalDir = new File(outDir, "original");
if (!originalDir.exists()) { if (!originalDir.exists()) {
@ -370,11 +323,9 @@ public class ApkDecoder {
} }
} }
private void recordUncompressedFiles(ApkInfo apkInfo, private void recordUncompressedFiles(Map<String, String> resFileMapping) throws AndrolibException {
Map<String, String> resFileMapping,
List<String> uncompressedFilesOrExts)
throws AndrolibException {
try { try {
List<String> uncompressedFilesOrExts = new ArrayList<>();
Directory unk = mApkFile.getDirectory(); Directory unk = mApkFile.getDirectory();
Set<String> files = unk.getFiles(true); Set<String> files = unk.getFiles(true);
@ -398,9 +349,8 @@ public class ApkDecoder {
} }
// update apk info // update apk info
if (!uncompressedFilesOrExts.isEmpty()) { if (!uncompressedFilesOrExts.isEmpty()) {
apkInfo.doNotCompress = uncompressedFilesOrExts; mApkInfo.doNotCompress = uncompressedFilesOrExts;
} }
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }

View File

@ -20,18 +20,17 @@ import brut.androlib.ApktoolProperties;
import brut.androlib.exceptions.AndrolibException; import brut.androlib.exceptions.AndrolibException;
import brut.androlib.res.data.ResConfigFlags; import brut.androlib.res.data.ResConfigFlags;
import brut.directory.DirectoryException; import brut.directory.DirectoryException;
import brut.directory.ExtFile;
import brut.directory.FileDirectory; import brut.directory.FileDirectory;
import java.io.*; import java.io.*;
import java.util.ArrayList; import java.util.*;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class ApkInfo implements YamlSerializable { public class ApkInfo implements YamlSerializable {
public String version; private transient ExtFile mApkFile;
private String apkFileName; public String version;
public String apkFileName;
public boolean isFrameworkApk; public boolean isFrameworkApk;
public UsesFramework usesFramework; public UsesFramework usesFramework;
private Map<String, String> sdkInfo = new LinkedHashMap<>(); private Map<String, String> sdkInfo = new LinkedHashMap<>();
@ -47,7 +46,78 @@ public class ApkInfo implements YamlSerializable {
public boolean compressionType; public boolean compressionType;
public ApkInfo() { public ApkInfo() {
this(null);
}
public ApkInfo(ExtFile apkFile) {
this.version = ApktoolProperties.getVersion(); this.version = ApktoolProperties.getVersion();
if (apkFile != null) {
setApkFile(apkFile);
}
}
public ExtFile getApkFile() {
return mApkFile;
}
public void setApkFile(ExtFile apkFile) {
mApkFile = apkFile;
if (this.apkFileName == null) {
this.apkFileName = apkFile.getName();
}
}
public boolean hasManifest() throws AndrolibException {
if (mApkFile == null) {
return false;
}
try {
return mApkFile.getDirectory().containsFile("AndroidManifest.xml");
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}
public boolean hasResources() throws AndrolibException {
if (mApkFile == null) {
return false;
}
try {
return mApkFile.getDirectory().containsFile("resources.arsc");
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}
public boolean hasSources() throws AndrolibException {
if (mApkFile == null) {
return false;
}
try {
return mApkFile.getDirectory().containsFile("classes.dex");
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}
public boolean hasMultipleSources() throws AndrolibException {
if (mApkFile == null) {
return false;
}
try {
Set<String> files = mApkFile.getDirectory().getFiles(false);
for (String file : files) {
if (file.endsWith(".dex")) {
if (!file.equalsIgnoreCase("classes.dex")) {
return true;
}
}
}
return false;
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
} }
public String checkTargetSdkVersionBounds() { public String checkTargetSdkVersionBounds() {
@ -61,14 +131,6 @@ public class ApkInfo implements YamlSerializable {
return Integer.toString(target); return Integer.toString(target);
} }
public String getApkFileName() {
return apkFileName;
}
public void setApkFileName(String apkFileName) {
this.apkFileName = apkFileName;
}
public Map<String, String> getSdkInfo() { public Map<String, String> getSdkInfo() {
return sdkInfo; return sdkInfo;
} }
@ -134,9 +196,7 @@ public class ApkInfo implements YamlSerializable {
} }
public void save(File file) throws AndrolibException { public void save(File file) throws AndrolibException {
try ( try (YamlWriter writer = new YamlWriter(new FileOutputStream(file))) {
YamlWriter writer = new YamlWriter(new FileOutputStream(file));
) {
write(writer); write(writer);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
throw new AndrolibException("File not found"); throw new AndrolibException("File not found");
@ -153,10 +213,10 @@ public class ApkInfo implements YamlSerializable {
} }
public static ApkInfo load(File appDir) throws AndrolibException { public static ApkInfo load(File appDir) throws AndrolibException {
try( try (InputStream in = new FileDirectory(appDir).getFileInput("apktool.yml")) {
InputStream in = new FileDirectory(appDir).getFileInput("apktool.yml"); ApkInfo apkInfo = ApkInfo.load(in);
) { apkInfo.setApkFile(new ExtFile(appDir));
return ApkInfo.load(in); return apkInfo;
} catch (DirectoryException | IOException ex) { } catch (DirectoryException | IOException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }

View File

@ -49,8 +49,7 @@ public class Framework {
installFramework(frameFile, config.frameworkTag); installFramework(frameFile, config.frameworkTag);
} }
public void installFramework(File frameFile, String tag) public void installFramework(File frameFile, String tag) throws AndrolibException {
throws AndrolibException {
InputStream in = null; InputStream in = null;
ZipOutputStream out = null; ZipOutputStream out = null;
try { try {
@ -198,8 +197,7 @@ public class Framework {
return dir; return dir;
} }
public File getFrameworkApk(int id, String frameTag) public File getFrameworkApk(int id, String frameTag) throws AndrolibException {
throws AndrolibException {
File dir = getFrameworkDirectory(); File dir = getFrameworkDirectory();
File apk; File apk;
@ -216,8 +214,10 @@ public class Framework {
} }
if (id == 1) { if (id == 1) {
try (InputStream in = getAndroidFrameworkResourcesAsStream(); try (
OutputStream out = Files.newOutputStream(apk.toPath())) { InputStream in = getAndroidFrameworkResourcesAsStream();
OutputStream out = Files.newOutputStream(apk.toPath())
) {
IOUtils.copy(in, out); IOUtils.copy(in, out);
return apk; return apk;
} catch (IOException ex) { } catch (IOException ex) {

View File

@ -31,10 +31,7 @@ import brut.directory.ExtFile;
import brut.directory.FileDirectory; import brut.directory.FileDirectory;
import org.xmlpull.v1.XmlSerializer; import org.xmlpull.v1.XmlSerializer;
import java.io.File; import java.io.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*; import java.util.*;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -43,93 +40,51 @@ public class ResourcesDecoder {
private final Config mConfig; private final Config mConfig;
private final ExtFile mApkFile; private final ExtFile mApkFile;
private final ResTable mResTable;
private final ApkInfo mApkInfo; private final ApkInfo mApkInfo;
private final ResTable mResTable;
private final Map<String, String> mResFileMapping = new HashMap<>(); private final Map<String, String> mResFileMapping = new HashMap<>();
private final static String[] IGNORED_PACKAGES = new String[] { private final static String[] IGNORED_PACKAGES = new String[] {
"android", "com.htc", "com.lge", "com.lge.internal", "yi", "flyme", "air.com.adobe.appentry", "android", "com.htc", "com.lge", "com.lge.internal", "yi", "flyme", "air.com.adobe.appentry",
"FFFFFFFFFFFFFFFFFFFFFF" }; "FFFFFFFFFFFFFFFFFFFFFF" };
public ResourcesDecoder(Config config, ExtFile apkFile) { public ResourcesDecoder(Config config, ExtFile apkFile, ApkInfo apkInfo) {
mConfig = config; mConfig = config;
mApkFile = apkFile; mApkFile = apkFile;
mApkInfo = new ApkInfo(); mApkInfo = apkInfo;
mApkInfo.setApkFileName(apkFile.getName());
mResTable = new ResTable(mConfig, mApkInfo); mResTable = new ResTable(mConfig, mApkInfo);
} }
public boolean hasManifest() throws AndrolibException {
try {
return mApkFile.getDirectory().containsFile("AndroidManifest.xml");
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}
public boolean hasResources() throws AndrolibException {
try {
return mApkFile.getDirectory().containsFile("resources.arsc");
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}
public ResTable getResTable() throws AndrolibException { public ResTable getResTable() throws AndrolibException {
if (! (hasManifest() || hasResources())) { if (!mApkInfo.hasManifest() && !mApkInfo.hasResources()) {
throw new AndrolibException( throw new AndrolibException(
"Apk doesn't contain either AndroidManifest.xml file or resources.arsc file"); "Apk doesn't contain either AndroidManifest.xml file or resources.arsc file");
} }
return mResTable; return mResTable;
} }
public ApkInfo getApkInfo() {
return mApkInfo;
}
public Map<String, String> getResFileMapping() { public Map<String, String> getResFileMapping() {
return mResFileMapping; return mResFileMapping;
} }
public void loadMainPkg() throws AndrolibException {
mResTable.loadMainPkg(mApkFile);
}
public void decodeManifest(File outDir) throws AndrolibException { public void decodeManifest(File outDir) throws AndrolibException {
if (hasManifest()) { if (!mApkInfo.hasManifest()) {
decodeManifest(getResTable(), mApkFile, outDir); return;
if (hasResources()) {
if (!mConfig.analysisMode) {
// Remove versionName / versionCode (aapt API 16)
//
// check for a mismatch between resources.arsc package and the package listed in AndroidManifest
// also remove the android::versionCode / versionName from manifest for rebuild
// this is a required change to prevent aapt warning about conflicting versions
// it will be passed as a parameter to aapt like "--min-sdk-version" via apktool.yml
adjustPackageManifest(getResTable(), outDir.getAbsolutePath() + File.separator + "AndroidManifest.xml");
ResXmlPatcher.removeManifestVersions(new File(
outDir.getAbsolutePath() + File.separator + "AndroidManifest.xml"));
// update apk info
mApkInfo.packageInfo.forcedPackageId = String.valueOf(mResTable.getPackageId());
}
}
} }
}
public void updateApkInfo(File outDir) throws AndrolibException { AXmlResourceParser axmlParser = new AndroidManifestResourceParser(mResTable);
mResTable.initApkInfo(mApkInfo, outDir);
}
private void decodeManifest(ResTable resTable, ExtFile apkFile, File outDir)
throws AndrolibException {
AXmlResourceParser axmlParser = new AndroidManifestResourceParser(resTable);
XmlPullStreamDecoder fileDecoder = new XmlPullStreamDecoder(axmlParser, getResXmlSerializer()); XmlPullStreamDecoder fileDecoder = new XmlPullStreamDecoder(axmlParser, getResXmlSerializer());
Directory inApk, out; Directory inApk, out;
try { try {
inApk = apkFile.getDirectory(); inApk = mApkFile.getDirectory();
out = new FileDirectory(outDir); out = new FileDirectory(outDir);
if (hasResources()) { if (mApkInfo.hasResources()) {
LOGGER.info("Decoding AndroidManifest.xml with resources..."); LOGGER.info("Decoding AndroidManifest.xml with resources...");
} else { } else {
LOGGER.info("Decoding AndroidManifest.xml with only framework resources..."); LOGGER.info("Decoding AndroidManifest.xml with only framework resources...");
@ -141,18 +96,38 @@ public class ResourcesDecoder {
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
if (mApkInfo.hasResources()) {
if (!mConfig.analysisMode) {
// Remove versionName / versionCode (aapt API 16)
//
// check for a mismatch between resources.arsc package and the package listed in AndroidManifest
// also remove the android::versionCode / versionName from manifest for rebuild
// this is a required change to prevent aapt warning about conflicting versions
// it will be passed as a parameter to aapt like "--min-sdk-version" via apktool.yml
adjustPackageManifest(outDir.getAbsolutePath() + File.separator + "AndroidManifest.xml");
ResXmlPatcher.removeManifestVersions(new File(
outDir.getAbsolutePath() + File.separator + "AndroidManifest.xml"));
// update apk info
mApkInfo.packageInfo.forcedPackageId = String.valueOf(mResTable.getPackageId());
}
}
} }
private void adjustPackageManifest(ResTable resTable, String filePath) public void updateApkInfo(File outDir) throws AndrolibException {
throws AndrolibException { mResTable.initApkInfo(mApkInfo, outDir);
}
private void adjustPackageManifest(String filePath) throws AndrolibException {
// compare resources.arsc package name to the one present in AndroidManifest // compare resources.arsc package name to the one present in AndroidManifest
ResPackage resPackage = resTable.getCurrentResPackage(); ResPackage resPackage = mResTable.getCurrentResPackage();
String pkgOriginal = resPackage.getName(); String pkgOriginal = resPackage.getName();
String pkgRenamed = resTable.getPackageRenamed(); String pkgRenamed = mResTable.getPackageRenamed();
resTable.setPackageId(resPackage.getId()); mResTable.setPackageId(resPackage.getId());
resTable.setPackageOriginal(pkgOriginal); mResTable.setPackageOriginal(pkgOriginal);
// 1) Check if pkgOriginal is null (empty resources.arsc) // 1) Check if pkgOriginal is null (empty resources.arsc)
// 2) Check if pkgRenamed is null // 2) Check if pkgRenamed is null
@ -167,35 +142,18 @@ public class ResourcesDecoder {
} }
} }
private ExtMXSerializer getResXmlSerializer() { public void decodeResources(File outDir) throws AndrolibException {
ExtMXSerializer serial = new ExtMXSerializer(); if (!mApkInfo.hasResources()) {
serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_INDENTATION, " "); return;
serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_LINE_SEPARATOR, System.getProperty("line.separator"));
serial.setProperty(ExtXmlSerializer.PROPERTY_DEFAULT_ENCODING, "utf-8");
serial.setDisabledAttrEscape(true);
return serial;
}
public void loadMainPkg() throws AndrolibException {
mResTable.loadMainPkg(mApkFile);
}
public ResTable decodeResources(File outDir) throws AndrolibException {
if (hasResources()) {
loadMainPkg();
decodeResources(getResTable(), mApkFile, outDir);
} }
return mResTable;
}
private void decodeResources(ResTable resTable, ExtFile apkFile, File outDir) mResTable.loadMainPkg(mApkFile);
throws AndrolibException {
ResStreamDecoderContainer decoders = new ResStreamDecoderContainer(); ResStreamDecoderContainer decoders = new ResStreamDecoderContainer();
decoders.setDecoder("raw", new ResRawStreamDecoder()); decoders.setDecoder("raw", new ResRawStreamDecoder());
decoders.setDecoder("9patch", new Res9patchStreamDecoder()); decoders.setDecoder("9patch", new Res9patchStreamDecoder());
AXmlResourceParser axmlParser = new AXmlResourceParser(resTable); AXmlResourceParser axmlParser = new AXmlResourceParser(mResTable);
decoders.setDecoder("xml", new XmlPullStreamDecoder(axmlParser, getResXmlSerializer())); decoders.setDecoder("xml", new XmlPullStreamDecoder(axmlParser, getResXmlSerializer()));
ResFileDecoder fileDecoder = new ResFileDecoder(decoders); ResFileDecoder fileDecoder = new ResFileDecoder(decoders);
@ -203,14 +161,14 @@ public class ResourcesDecoder {
try { try {
out = new FileDirectory(outDir); out = new FileDirectory(outDir);
in = apkFile.getDirectory(); in = mApkFile.getDirectory();
out = out.createDir("res"); out = out.createDir("res");
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
ExtMXSerializer xmlSerializer = getResXmlSerializer(); ExtMXSerializer xmlSerializer = getResXmlSerializer();
for (ResPackage pkg : resTable.listMainPackages()) { for (ResPackage pkg : mResTable.listMainPackages()) {
LOGGER.info("Decoding file-resources..."); LOGGER.info("Decoding file-resources...");
for (ResResource res : pkg.listFiles()) { for (ResResource res : pkg.listFiles()) {
@ -230,6 +188,15 @@ public class ResourcesDecoder {
} }
} }
private ExtMXSerializer getResXmlSerializer() {
ExtMXSerializer serial = new ExtMXSerializer();
serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_INDENTATION, " ");
serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_LINE_SEPARATOR, System.getProperty("line.separator"));
serial.setProperty(ExtXmlSerializer.PROPERTY_DEFAULT_ENCODING, "utf-8");
serial.setDisabledAttrEscape(true);
return serial;
}
private void generateValuesFile(ResValuesFile valuesFile, Directory out, private void generateValuesFile(ResValuesFile valuesFile, Directory out,
ExtXmlSerializer serial) throws AndrolibException { ExtXmlSerializer serial) throws AndrolibException {
try { try {

View File

@ -57,6 +57,10 @@ public class ResTable {
this(Config.getDefaultConfig(), new ApkInfo()); this(Config.getDefaultConfig(), new ApkInfo());
} }
public ResTable(ExtFile apkFile) {
this(Config.getDefaultConfig(), new ApkInfo(apkFile));
}
public ResTable(Config config, ApkInfo apkInfo) { public ResTable(Config config, ApkInfo apkInfo) {
mConfig = config; mConfig = config;
mApkInfo = apkInfo; mApkInfo = apkInfo;
@ -145,8 +149,7 @@ public class ResTable {
mMainPkgLoaded = true; mMainPkgLoaded = true;
} }
private ResPackage loadFrameworkPkg(int id) private ResPackage loadFrameworkPkg(int id) throws AndrolibException {
throws AndrolibException {
Framework framework = new Framework(mConfig); Framework framework = new Framework(mConfig);
File frameworkApk = framework.getFrameworkApk(id, mConfig.frameworkTag); File frameworkApk = framework.getFrameworkApk(id, mConfig.frameworkTag);
@ -168,8 +171,7 @@ public class ResTable {
return pkg; return pkg;
} }
private ResPackage[] loadResPackagesFromApk(ExtFile apkFile, boolean keepBrokenResources) private ResPackage[] loadResPackagesFromApk(ExtFile apkFile, boolean keepBrokenResources) throws AndrolibException {
throws AndrolibException {
try { try {
Directory dir = apkFile.getDirectory(); Directory dir = apkFile.getDirectory();
try (BufferedInputStream bfi = new BufferedInputStream(dir.getFileInput("resources.arsc"))) { try (BufferedInputStream bfi = new BufferedInputStream(dir.getFileInput("resources.arsc"))) {

View File

@ -40,8 +40,7 @@ public class ResAttr extends ResBagValue implements ResValuesXmlSerializable {
} }
@Override @Override
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) throws IOException, AndrolibException {
throws IOException, AndrolibException {
String type = getTypeAsString(); String type = getTypeAsString();
serializer.startTag(null, "attr"); serializer.startTag(null, "attr");
@ -106,7 +105,7 @@ public class ResAttr extends ResBagValue implements ResValuesXmlSerializable {
throw new AndrolibException("Could not decode attr value"); throw new AndrolibException("Could not decode attr value");
} }
protected void serializeBody(XmlSerializer serializer, ResResource res) throws AndrolibException, IOException { protected void serializeBody(XmlSerializer serializer, ResResource res) throws IOException, AndrolibException {
} }
protected String getTypeAsString() { protected String getTypeAsString() {

View File

@ -19,19 +19,14 @@ package brut.androlib.res.decoder;
import android.util.TypedValue; import android.util.TypedValue;
import brut.androlib.exceptions.AndrolibException; import brut.androlib.exceptions.AndrolibException;
import brut.androlib.res.data.*; import brut.androlib.res.data.*;
import brut.androlib.res.data.arsc.*;
import brut.androlib.res.data.value.*; import brut.androlib.res.data.value.*;
import brut.androlib.res.data.arsc.ARSCData;
import brut.androlib.res.data.arsc.ARSCHeader;
import brut.androlib.res.data.arsc.EntryData;
import brut.androlib.res.data.arsc.FlagsOffset;
import brut.util.Duo; import brut.util.Duo;
import brut.util.ExtDataInput; import brut.util.ExtDataInput;
import com.google.common.io.LittleEndianDataInputStream; import com.google.common.io.LittleEndianDataInputStream;
import org.apache.commons.io.input.CountingInputStream; import org.apache.commons.io.input.CountingInputStream;
import java.io.DataInput; import java.io.*;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.*; import java.util.*;
import java.util.logging.Logger; import java.util.logging.Logger;

View File

@ -25,13 +25,11 @@ import brut.androlib.res.data.arsc.ARSCHeader;
import brut.androlib.res.data.axml.NamespaceStack; import brut.androlib.res.data.axml.NamespaceStack;
import brut.androlib.res.xml.ResXmlEncoders; import brut.androlib.res.xml.ResXmlEncoders;
import brut.util.ExtDataInput; import brut.util.ExtDataInput;
import org.apache.commons.io.input.CountingInputStream;
import com.google.common.io.LittleEndianDataInputStream; import com.google.common.io.LittleEndianDataInputStream;
import org.apache.commons.io.input.CountingInputStream;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import java.io.DataInput;
import java.io.IOException; import java.io.*;
import java.io.InputStream;
import java.io.Reader;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;

View File

@ -27,11 +27,7 @@ import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.Raster; import java.awt.image.Raster;
import java.awt.image.WritableRaster; import java.awt.image.WritableRaster;
import java.io.ByteArrayInputStream; import java.io.*;
import java.io.DataInput;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class Res9patchStreamDecoder implements ResStreamDecoder { public class Res9patchStreamDecoder implements ResStreamDecoder {
@Override @Override

View File

@ -31,8 +31,7 @@ public class ResAttrDecoder {
mResTable = resTable; mResTable = resTable;
} }
public String decode(int type, int value, String rawValue, int attrResId) public String decode(int type, int value, String rawValue, int attrResId) throws AndrolibException {
throws AndrolibException {
ResScalarValue resValue = mResTable.getCurrentResPackage().getValueFactory().factory(type, value, rawValue); ResScalarValue resValue = mResTable.getCurrentResPackage().getValueFactory().factory(type, value, rawValue);
String decoded = null; String decoded = null;
@ -47,9 +46,7 @@ public class ResAttrDecoder {
return decoded != null ? decoded : resValue.encodeAsResXmlAttr(); return decoded != null ? decoded : resValue.encodeAsResXmlAttr();
} }
public String decodeFromResourceId(int attrResId) public String decodeFromResourceId(int attrResId) throws AndrolibException {
throws AndrolibException {
if (attrResId != 0) { if (attrResId != 0) {
try { try {
ResResSpec resResSpec = mResTable.getResSpec(attrResId); ResResSpec resResSpec = mResTable.getResSpec(attrResId);

View File

@ -139,8 +139,8 @@ public class ResFileDecoder {
public void decode(Directory inDir, String inFileName, Directory outDir, public void decode(Directory inDir, String inFileName, Directory outDir,
String outFileName, String decoder) throws AndrolibException { String outFileName, String decoder) throws AndrolibException {
try ( try (
InputStream in = inDir.getFileInput(inFileName); InputStream in = inDir.getFileInput(inFileName);
OutputStream out = outDir.getFileOutput(outFileName) OutputStream out = outDir.getFileOutput(outFileName)
) { ) {
mDecoders.decode(in, out, decoder); mDecoders.decode(in, out, decoder);
} catch (DirectoryException | IOException ex) { } catch (DirectoryException | IOException ex) {
@ -160,8 +160,8 @@ public class ResFileDecoder {
public void decodeManifest(Directory inDir, String inFileName, public void decodeManifest(Directory inDir, String inFileName,
Directory outDir, String outFileName) throws AndrolibException { Directory outDir, String outFileName) throws AndrolibException {
try ( try (
InputStream in = inDir.getFileInput(inFileName); InputStream in = inDir.getFileInput(inFileName);
OutputStream out = outDir.getFileOutput(outFileName) OutputStream out = outDir.getFileOutput(outFileName)
) { ) {
((XmlPullStreamDecoder) mDecoders.getDecoder("xml")).decodeManifest(in, out); ((XmlPullStreamDecoder) mDecoders.getDecoder("xml")).decodeManifest(in, out);
} catch (DirectoryException | IOException ex) { } catch (DirectoryException | IOException ex) {

View File

@ -19,9 +19,7 @@ package brut.androlib.res.decoder;
import brut.androlib.exceptions.AndrolibException; import brut.androlib.exceptions.AndrolibException;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import java.io.IOException; import java.io.*;
import java.io.InputStream;
import java.io.OutputStream;
public class ResRawStreamDecoder implements ResStreamDecoder { public class ResRawStreamDecoder implements ResStreamDecoder {
@Override @Override

View File

@ -17,8 +17,7 @@
package brut.androlib.res.decoder; package brut.androlib.res.decoder;
import brut.androlib.exceptions.AndrolibException; import brut.androlib.exceptions.AndrolibException;
import java.io.InputStream; import java.io.*;
import java.io.OutputStream;
public interface ResStreamDecoder { public interface ResStreamDecoder {
void decode(InputStream in, OutputStream out) void decode(InputStream in, OutputStream out)

View File

@ -17,10 +17,8 @@
package brut.androlib.res.decoder; package brut.androlib.res.decoder;
import brut.androlib.exceptions.AndrolibException; import brut.androlib.exceptions.AndrolibException;
import java.io.InputStream; import java.io.*;
import java.io.OutputStream; import java.util.*;
import java.util.HashMap;
import java.util.Map;
public class ResStreamDecoderContainer { public class ResStreamDecoderContainer {
private final Map<String, ResStreamDecoder> mDecoders = new HashMap<>(); private final Map<String, ResStreamDecoder> mDecoders = new HashMap<>();

View File

@ -28,9 +28,7 @@ import org.xmlpull.v1.wrapper.XmlPullWrapperFactory;
import org.xmlpull.v1.wrapper.XmlSerializerWrapper; import org.xmlpull.v1.wrapper.XmlSerializerWrapper;
import org.xmlpull.v1.wrapper.classic.StaticXmlSerializerWrapper; import org.xmlpull.v1.wrapper.classic.StaticXmlSerializerWrapper;
import java.io.IOException; import java.io.*;
import java.io.InputStream;
import java.io.OutputStream;
public class XmlPullStreamDecoder implements ResStreamDecoder { public class XmlPullStreamDecoder implements ResStreamDecoder {
public XmlPullStreamDecoder(XmlPullParser parser, public XmlPullStreamDecoder(XmlPullParser parser,

View File

@ -18,9 +18,7 @@ package brut.androlib.res.util;
import org.xmlpull.renamed.MXSerializer; import org.xmlpull.renamed.MXSerializer;
import java.io.IOException; import java.io.*;
import java.io.OutputStream;
import java.io.Writer;
public class ExtMXSerializer extends MXSerializer implements ExtXmlSerializer { public class ExtMXSerializer extends MXSerializer implements ExtXmlSerializer {
@Override @Override

View File

@ -29,9 +29,7 @@ import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource; import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.*; import javax.xml.xpath.*;
import java.io.File; import java.io.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.logging.Logger; import java.util.logging.Logger;
public final class ResXmlPatcher { public final class ResXmlPatcher {
@ -135,7 +133,7 @@ public final class ResXmlPatcher {
* @throws ParserConfigurationException XML nodes could be written * @throws ParserConfigurationException XML nodes could be written
*/ */
public static void modNetworkSecurityConfig(File file) public static void modNetworkSecurityConfig(File file)
throws ParserConfigurationException, TransformerException, IOException, SAXException { throws ParserConfigurationException, TransformerException, IOException, SAXException {
DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance(); DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder(); DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder();

View File

@ -25,9 +25,7 @@ import com.android.tools.smali.dexlib2.Opcodes;
import com.android.tools.smali.dexlib2.writer.builder.DexBuilder; import com.android.tools.smali.dexlib2.writer.builder.DexBuilder;
import com.android.tools.smali.dexlib2.writer.io.FileDataStore; import com.android.tools.smali.dexlib2.writer.io.FileDataStore;
import java.io.File; import java.io.*;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.logging.Logger; import java.util.logging.Logger;

View File

@ -27,8 +27,7 @@ import com.android.tools.smali.dexlib2.analysis.InlineMethodResolver;
import com.android.tools.smali.dexlib2.iface.DexFile; import com.android.tools.smali.dexlib2.iface.DexFile;
import com.android.tools.smali.dexlib2.iface.MultiDexContainer; import com.android.tools.smali.dexlib2.iface.MultiDexContainer;
import java.io.File; import java.io.*;
import java.io.IOException;
public class SmaliDecoder { public class SmaliDecoder {

View File

@ -18,10 +18,7 @@ package org.xmlpull.renamed;
import org.xmlpull.v1.XmlSerializer; import org.xmlpull.v1.XmlSerializer;
import java.io.IOException; import java.io.*;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Objects; import java.util.Objects;
/** /**

View File

@ -17,6 +17,7 @@
package brut.androlib.aapt2; package brut.androlib.aapt2;
import brut.androlib.*; import brut.androlib.*;
import brut.androlib.apk.ApkInfo;
import brut.androlib.exceptions.AndrolibException; import brut.androlib.exceptions.AndrolibException;
import brut.androlib.res.ResourcesDecoder; import brut.androlib.res.ResourcesDecoder;
import brut.androlib.res.data.ResTable; import brut.androlib.res.data.ResTable;
@ -27,8 +28,6 @@ import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import java.io.File;
import static org.junit.Assert.*; import static org.junit.Assert.*;
public class NonStandardPkgIdTest extends BaseTest { public class NonStandardPkgIdTest extends BaseTest {
@ -50,16 +49,18 @@ public class NonStandardPkgIdTest extends BaseTest {
config.verbose = true; config.verbose = true;
LOGGER.info("Building pkgid8.apk..."); LOGGER.info("Building pkgid8.apk...");
File testApk = new File(sTmpDir, "pkgid8.apk"); ExtFile testApk = new ExtFile(sTmpDir, "pkgid8.apk");
new ApkBuilder(config, sTestOrigDir).build(testApk); new ApkBuilder(config, sTestOrigDir).build(testApk);
LOGGER.info("Decoding pkgid8.apk..."); LOGGER.info("Decoding pkgid8.apk...");
ResourcesDecoder resourcesDecoder = new ResourcesDecoder( ApkInfo testInfo = new ApkInfo(testApk);
Config.getDefaultConfig(), new ExtFile(testApk)); ResourcesDecoder resourcesDecoder = new ResourcesDecoder(Config.getDefaultConfig(), testApk, testInfo);
sTestNewDir.mkdirs(); sTestNewDir.mkdirs();
mResTable = resourcesDecoder.decodeResources(sTestNewDir); resourcesDecoder.decodeResources(sTestNewDir);
resourcesDecoder.decodeManifest(sTestNewDir); resourcesDecoder.decodeManifest(sTestNewDir);
mResTable = resourcesDecoder.getResTable();
} }
@AfterClass @AfterClass

View File

@ -24,7 +24,7 @@ import static org.junit.Assert.*;
public class ApkInfoReaderTest { public class ApkInfoReaderTest {
private void checkStandard(ApkInfo apkInfo) { private void checkStandard(ApkInfo apkInfo) {
assertEquals("standard.apk", apkInfo.getApkFileName()); assertEquals("standard.apk", apkInfo.apkFileName);
assertFalse(apkInfo.resourcesAreCompressed); assertFalse(apkInfo.resourcesAreCompressed);
assertEquals(1, apkInfo.doNotCompress.size()); assertEquals(1, apkInfo.doNotCompress.size());
assertEquals("resources.arsc", apkInfo.doNotCompress.iterator().next()); assertEquals("resources.arsc", apkInfo.doNotCompress.iterator().next());
@ -85,7 +85,7 @@ public class ApkInfoReaderTest {
ApkInfo apkInfo = ApkInfo.load( ApkInfo apkInfo = ApkInfo.load(
this.getClass().getResourceAsStream("/apk/unknown_files.yml")); this.getClass().getResourceAsStream("/apk/unknown_files.yml"));
assertEquals("2.0.0", apkInfo.version); assertEquals("2.0.0", apkInfo.version);
assertEquals("testapp.apk", apkInfo.getApkFileName()); assertEquals("testapp.apk", apkInfo.apkFileName);
assertFalse(apkInfo.isFrameworkApk); assertFalse(apkInfo.isFrameworkApk);
assertNotNull(apkInfo.usesFramework); assertNotNull(apkInfo.usesFramework);
assertEquals(1, apkInfo.usesFramework.ids.size()); assertEquals(1, apkInfo.usesFramework.ids.size());
@ -118,7 +118,7 @@ public class ApkInfoReaderTest {
ApkInfo apkInfo = ApkInfo.load( ApkInfo apkInfo = ApkInfo.load(
this.getClass().getResourceAsStream("/apk/list_with_indent.yml")); this.getClass().getResourceAsStream("/apk/list_with_indent.yml"));
assertEquals("2.8.0", apkInfo.version); assertEquals("2.8.0", apkInfo.version);
assertEquals("basic.apk", apkInfo.getApkFileName()); assertEquals("basic.apk", apkInfo.apkFileName);
assertFalse(apkInfo.isFrameworkApk); assertFalse(apkInfo.isFrameworkApk);
assertNotNull(apkInfo.usesFramework); assertNotNull(apkInfo.usesFramework);
assertEquals(1, apkInfo.usesFramework.ids.size()); assertEquals(1, apkInfo.usesFramework.ids.size());

View File

@ -39,9 +39,7 @@ public class ApkInfoSerializationTest {
File savedApkInfo = folder.newFile( "saved.yml" ); File savedApkInfo = folder.newFile( "saved.yml" );
control.save(savedApkInfo); control.save(savedApkInfo);
try ( try (FileInputStream fis = new FileInputStream(savedApkInfo)) {
FileInputStream fis = new FileInputStream(savedApkInfo);
) {
ApkInfo saved = ApkInfo.load(fis); ApkInfo saved = ApkInfo.load(fis);
check(saved); check(saved);
} }
@ -49,7 +47,7 @@ public class ApkInfoSerializationTest {
private void check(ApkInfo apkInfo) { private void check(ApkInfo apkInfo) {
assertEquals("2.0.0", apkInfo.version); assertEquals("2.0.0", apkInfo.version);
assertEquals("testapp.apk", apkInfo.getApkFileName()); assertEquals("testapp.apk", apkInfo.apkFileName);
assertFalse(apkInfo.isFrameworkApk); assertFalse(apkInfo.isFrameworkApk);
assertNotNull(apkInfo.usesFramework); assertNotNull(apkInfo.usesFramework);
assertEquals(1, apkInfo.usesFramework.ids.size()); assertEquals(1, apkInfo.usesFramework.ids.size());

View File

@ -29,7 +29,7 @@ public class ConsistentPropertyTest {
this.getClass().getResourceAsStream("/apk/basic.yml")); this.getClass().getResourceAsStream("/apk/basic.yml"));
assertEquals("2.8.0", apkInfo.version); assertEquals("2.8.0", apkInfo.version);
assertEquals("basic.apk", apkInfo.getApkFileName()); assertEquals("basic.apk", apkInfo.apkFileName);
assertFalse(apkInfo.isFrameworkApk); assertFalse(apkInfo.isFrameworkApk);
assertEquals(1, apkInfo.usesFramework.ids.size()); assertEquals(1, apkInfo.usesFramework.ids.size());
assertEquals("tag", apkInfo.usesFramework.tag); assertEquals("tag", apkInfo.usesFramework.tag);

View File

@ -28,7 +28,7 @@ public class DoNotCompressHieroglyphTest {
ApkInfo apkInfo = ApkInfo.load( ApkInfo apkInfo = ApkInfo.load(
this.getClass().getResourceAsStream("/apk/donotcompress_with_hieroglyph.yml")); this.getClass().getResourceAsStream("/apk/donotcompress_with_hieroglyph.yml"));
assertEquals("2.0.0", apkInfo.version); assertEquals("2.0.0", apkInfo.version);
assertEquals("testapp.apk", apkInfo.getApkFileName()); assertEquals("testapp.apk", apkInfo.apkFileName);
assertEquals(2, apkInfo.doNotCompress.size()); assertEquals(2, apkInfo.doNotCompress.size());
assertEquals("assets/AllAssetBundles/Andriod/tx_1001_冰原1", apkInfo.doNotCompress.get(0)); assertEquals("assets/AllAssetBundles/Andriod/tx_1001_冰原1", apkInfo.doNotCompress.get(0));
assertEquals("assets/AllAssetBundles/Andriod/tx_1001_冰原1.manifest", apkInfo.doNotCompress.get(1)); assertEquals("assets/AllAssetBundles/Andriod/tx_1001_冰原1.manifest", apkInfo.doNotCompress.get(1));

View File

@ -20,6 +20,7 @@ import brut.androlib.ApkDecoder;
import brut.androlib.BaseTest; import brut.androlib.BaseTest;
import brut.androlib.Config; import brut.androlib.Config;
import brut.androlib.TestUtils; import brut.androlib.TestUtils;
import brut.androlib.apk.ApkInfo;
import brut.androlib.res.ResourcesDecoder; import brut.androlib.res.ResourcesDecoder;
import brut.androlib.res.data.ResTable; import brut.androlib.res.data.ResTable;
import brut.androlib.res.data.value.ResArrayValue; import brut.androlib.res.data.value.ResArrayValue;
@ -51,11 +52,10 @@ public class DecodeArrayTest extends BaseTest {
@Test @Test
public void decodeStringArray() throws BrutException { public void decodeStringArray() throws BrutException {
String apk = "issue1994.apk"; ExtFile apkFile = new ExtFile(sTmpDir, "issue1994.apk");
//ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk)); ApkInfo apkInfo = new ApkInfo(apkFile);
ResourcesDecoder resourcesDecoder = new ResourcesDecoder( //ApkDecoder apkDecoder = new ApkDecoder(apkFile);
Config.getDefaultConfig(), ResourcesDecoder resourcesDecoder = new ResourcesDecoder(Config.getDefaultConfig(), apkFile, apkInfo);
new ExtFile(sTmpDir + File.separator + apk));
resourcesDecoder.loadMainPkg(); resourcesDecoder.loadMainPkg();
ResTable resTable = resourcesDecoder.getResTable(); ResTable resTable = resourcesDecoder.getResTable();
@ -66,10 +66,9 @@ public class DecodeArrayTest extends BaseTest {
@Test @Test
public void decodeArray() throws BrutException { public void decodeArray() throws BrutException {
String apk = "issue1994.apk"; ExtFile apkFile = new ExtFile(sTmpDir, "issue1994.apk");
ResourcesDecoder resourcesDecoder = new ResourcesDecoder( ApkInfo apkInfo = new ApkInfo(apkFile);
Config.getDefaultConfig(), ResourcesDecoder resourcesDecoder = new ResourcesDecoder(Config.getDefaultConfig(), apkFile, apkInfo);
new ExtFile(sTmpDir + File.separator + apk));
resourcesDecoder.loadMainPkg(); resourcesDecoder.loadMainPkg();
ResTable resTable = resourcesDecoder.getResTable(); ResTable resTable = resourcesDecoder.getResTable();

View File

@ -262,12 +262,9 @@ public abstract class AbstractDirectory implements Directory {
protected abstract void loadFiles(); protected abstract void loadFiles();
protected abstract void loadDirs(); protected abstract void loadDirs();
protected abstract InputStream getFileInputLocal(String name) protected abstract InputStream getFileInputLocal(String name) throws DirectoryException;
throws DirectoryException; protected abstract OutputStream getFileOutputLocal(String name) throws DirectoryException;
protected abstract OutputStream getFileOutputLocal(String name) protected abstract AbstractDirectory createDirLocal(String name) throws DirectoryException;
throws DirectoryException;
protected abstract AbstractDirectory createDirLocal(String name)
throws DirectoryException;
protected abstract void removeFileLocal(String name); protected abstract void removeFileLocal(String name);

View File

@ -48,28 +48,21 @@ public interface Directory {
void copyToDir(Directory out) throws DirectoryException; void copyToDir(Directory out) throws DirectoryException;
void copyToDir(Directory out, String[] fileNames) void copyToDir(Directory out, String[] fileNames) throws DirectoryException;
throws DirectoryException;
void copyToDir(Directory out, String fileName) void copyToDir(Directory out, String fileName) throws DirectoryException;
throws DirectoryException;
void copyToDir(File out) throws DirectoryException; void copyToDir(File out) throws DirectoryException;
void copyToDir(File out, String[] fileNames) void copyToDir(File out, String[] fileNames) throws DirectoryException;
throws DirectoryException;
void copyToDir(File out, String fileName) void copyToDir(File out, String fileName) throws DirectoryException;
throws DirectoryException;
long getSize(String fileName) long getSize(String fileName) throws DirectoryException;
throws DirectoryException;
long getCompressedSize(String fileName) long getCompressedSize(String fileName) throws DirectoryException;
throws DirectoryException;
int getCompressionLevel(String fileName) int getCompressionLevel(String fileName) throws DirectoryException;
throws DirectoryException;
void close() throws IOException; void close() throws IOException;

View File

@ -105,17 +105,13 @@ public class BrutIO {
} }
public static void copy(File inputFile, ZipOutputStream outputFile) throws IOException { public static void copy(File inputFile, ZipOutputStream outputFile) throws IOException {
try ( try (FileInputStream fis = new FileInputStream(inputFile)) {
FileInputStream fis = new FileInputStream(inputFile)
) {
IOUtils.copy(fis, outputFile); IOUtils.copy(fis, outputFile);
} }
} }
public static void copy(ZipFile inputFile, ZipOutputStream outputFile, ZipEntry entry) throws IOException { public static void copy(ZipFile inputFile, ZipOutputStream outputFile, ZipEntry entry) throws IOException {
try ( try (InputStream is = inputFile.getInputStream(entry)) {
InputStream is = inputFile.getInputStream(entry)
) {
IOUtils.copy(is, outputFile); IOUtils.copy(is, outputFile);
} }
} }