diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java index fef7a463..f82ecca1 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java @@ -32,6 +32,7 @@ import brut.util.OS; import java.io.*; import java.util.*; import java.util.logging.Logger; +import java.util.regex.Pattern; import java.util.zip.CRC32; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -158,6 +159,22 @@ public class Androlib { } } + public void recordUncompressedFiles(ExtFile apkFile, Collection uncompressedFiles) throws AndrolibException { + try { + Directory unk = apkFile.getDirectory(); + Set files = unk.getFiles(true); + for (String file : files) { + if (isAPKFileNames(file) && !NO_COMPRESS_PATTERN.matcher(file).find()) { + if (unk.getCompressionLevel(file) == 0) { + uncompressedFiles.add(file); + } + } + } + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } + private boolean isAPKFileNames(String file) { for (String apkFile : APK_STANDARD_ALL_FILENAMES) { if (apkFile.equals(file) || file.startsWith(apkFile + "/")) { @@ -171,13 +188,8 @@ public class Androlib { throws AndrolibException { LOGGER.info("Copying unknown files..."); File unknownOut = new File(outDir, UNK_DIRNAME); - ZipEntry invZipFile; - - // have to use container of ZipFile to help identify compression type - // with regular looping of apkFile for easy copy try { Directory unk = apkFile.getDirectory(); - ZipFile apkZipFile = new ZipFile(apkFile.getAbsolutePath()); // loop all items in container recursively, ignoring any that are pre-defined by aapt Set files = unk.getFiles(true); @@ -186,19 +198,12 @@ public class Androlib { // copy file out of archive into special "unknown" folder unk.copyToDir(unknownOut, file); - try { - invZipFile = apkZipFile.getEntry(file); - - // lets record the name of the file, and its compression type - // so that we may re-include it the same way - if (invZipFile != null) { - mResUnknownFiles.addUnknownFileInfo(invZipFile.getName(), String.valueOf(invZipFile.getMethod())); - } - } catch (NullPointerException ignored) { } + // lets record the name of the file, and its compression type + // so that we may re-include it the same way + mResUnknownFiles.addUnknownFileInfo(file, String.valueOf(unk.getCompressionLevel(file))); } } - apkZipFile.close(); - } catch (DirectoryException | IOException ex) { + } catch (DirectoryException ex) { throw new AndrolibException(ex); } } @@ -266,6 +271,7 @@ public class Androlib { apkOptions.resourcesAreCompressed = meta.get("compressionType") == null ? false : Boolean.valueOf(meta.get("compressionType").toString()); + apkOptions.doNotCompress = (Collection) meta.get("doNotCompress"); mAndRes.setSdkInfo((Map) meta.get("sdkInfo")); mAndRes.setPackageId((Map) meta.get("packageInfo")); @@ -739,4 +745,9 @@ public class Androlib { "AndroidManifest.xml" }; private final static String[] APK_STANDARD_ALL_FILENAMES = new String[] { "classes.dex", "AndroidManifest.xml", "resources.arsc", "res", "lib", "libs", "assets", "META-INF" }; + // Taken from AOSP's frameworks/base/tools/aapt/Package.cpp + private final static Pattern NO_COMPRESS_PATTERN = Pattern.compile("\\.(" + + "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)$"); + } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java index 68f45451..b9e988d7 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java @@ -31,8 +31,6 @@ import java.io.File; import java.io.IOException; import java.util.*; import java.util.logging.Logger; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; /** * @author Ryszard Wiśniewski @@ -89,8 +87,6 @@ public class ApkDecoder { LOGGER.info("Using Apktool " + Androlib.getVersion() + " on " + mApkFile.getName()); if (hasResources()) { - setCompressionMode(); - switch (mDecodeResources) { case DECODE_RESOURCES_NONE: mAndrolib.decodeResourcesRaw(mApkFile, outDir); @@ -159,6 +155,8 @@ public class ApkDecoder { mAndrolib.decodeRawFiles(mApkFile, outDir); mAndrolib.decodeUnknownFiles(mApkFile, outDir, mResTable); + mUncompressedFiles = new ArrayList(); + mAndrolib.recordUncompressedFiles(mApkFile, mUncompressedFiles); mAndrolib.writeOriginalFiles(mApkFile, outDir); writeMetaFile(); } @@ -193,18 +191,6 @@ public class ApkDecoder { } } - public void setCompressionMode() throws AndrolibException, IOException { - // read the resources.arsc checking for STORED vs DEFLATE - // this will determine whether we compress on rebuild or not. - ZipFile zf = new ZipFile(mApkFile.getAbsolutePath()); - ZipEntry ze = zf.getEntry("resources.arsc"); - if (ze != null) { - int compression = ze.getMethod(); - mCompressResources = (compression == ZipEntry.DEFLATED); - } - zf.close(); - } - public void setTargetSdkVersion() throws AndrolibException, IOException { if (mResTable == null) { mResTable = mAndrolib.getResTable(mApkFile); @@ -320,10 +306,10 @@ public class ApkDecoder { putSdkInfo(meta); putPackageInfo(meta); putVersionInfo(meta); - putCompressionInfo(meta); putSharedLibraryInfo(meta); } putUnknownInfo(meta); + putFileCompressionInfo(meta); mAndrolib.writeMetaFile(mOutDir, meta); } @@ -391,18 +377,16 @@ public class ApkDecoder { } } - private void putCompressionInfo(Map meta) throws AndrolibException { - meta.put("compressionType", getCompressionType()); + private void putFileCompressionInfo(Map meta) throws AndrolibException { + if (!mUncompressedFiles.isEmpty()) { + meta.put("doNotCompress", mUncompressedFiles); + } } private void putSharedLibraryInfo(Map meta) throws AndrolibException { meta.put("sharedLibrary", mResTable.getSharedLibrary()); } - private boolean getCompressionType() { - return mCompressResources; - } - private final Androlib mAndrolib; private final static Logger LOGGER = Logger.getLogger(Androlib.class.getName()); @@ -417,7 +401,7 @@ public class ApkDecoder { private boolean mForceDelete = false; private boolean mKeepBrokenResources = false; private boolean mBakDeb = true; - private boolean mCompressResources = false; + private Collection mUncompressedFiles; private boolean mAnalysisMode = false; private int mApi = 15; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkOptions.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkOptions.java index 77eced97..df4774fd 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkOptions.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkOptions.java @@ -15,6 +15,8 @@ */ package brut.androlib; +import java.util.Collection; + public class ApkOptions { public boolean forceBuildAll = false; public boolean debugMode = false; @@ -23,6 +25,7 @@ public class ApkOptions { public boolean updateFiles = false; public boolean isFramework = false; public boolean resourcesAreCompressed = false; + public Collection doNotCompress; public String frameworkFolderLocation = null; public String frameworkTag = null; diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java index d8c987ce..c68e023f 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java @@ -383,7 +383,13 @@ final public class AndrolibResources { cmd.add("-x"); } - if (! apkOptions.resourcesAreCompressed) { + if (apkOptions.doNotCompress != null) { + for (String file : apkOptions.doNotCompress) { + cmd.add("-0"); + cmd.add(file); + } + } + if (!apkOptions.resourcesAreCompressed) { cmd.add("-0"); cmd.add("arsc"); } diff --git a/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java b/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java index 79e3488b..c2befcbe 100644 --- a/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java +++ b/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java @@ -26,6 +26,7 @@ import java.util.Set; public abstract class AbstractDirectory implements Directory { protected Set mFiles; + protected Set mFilesRecursive; protected Map mDirs; @Override @@ -41,14 +42,15 @@ public abstract class AbstractDirectory implements Directory { if (!recursive) { return mFiles; } - - Set files = new LinkedHashSet(mFiles); - for (Map.Entry dir : getAbstractDirs().entrySet()) { - for (String path : dir.getValue().getFiles(true)) { - files.add(dir.getKey() + separator + path); + if (mFilesRecursive == null) { + mFilesRecursive = new LinkedHashSet(mFiles); + for (Map.Entry dir : getAbstractDirs().entrySet()) { + for (String path : dir.getValue().getFiles(true)) { + mFilesRecursive.add(dir.getKey() + separator + path); + } } } - return files; + return mFilesRecursive; } @Override @@ -205,6 +207,11 @@ public abstract class AbstractDirectory implements Directory { DirUtil.copyToDir(this, out, fileName); } + public int getCompressionLevel(String fileName) + throws DirectoryException { + return -1; // Unknown + } + protected Map getAbstractDirs() { return getAbstractDirs(false); } diff --git a/brut.j.dir/src/main/java/brut/directory/Directory.java b/brut.j.dir/src/main/java/brut/directory/Directory.java index d59ff1fb..0199135d 100644 --- a/brut.j.dir/src/main/java/brut/directory/Directory.java +++ b/brut.j.dir/src/main/java/brut/directory/Directory.java @@ -47,5 +47,8 @@ public interface Directory { public void copyToDir(File out, String fileName) throws DirectoryException; + public int getCompressionLevel(String fileName) + throws DirectoryException; + public final char separator = '/'; } diff --git a/brut.j.dir/src/main/java/brut/directory/ZipRODirectory.java b/brut.j.dir/src/main/java/brut/directory/ZipRODirectory.java index 544fcbbe..bb76298a 100644 --- a/brut.j.dir/src/main/java/brut/directory/ZipRODirectory.java +++ b/brut.j.dir/src/main/java/brut/directory/ZipRODirectory.java @@ -100,6 +100,16 @@ public class ZipRODirectory extends AbstractDirectory { throw new UnsupportedOperationException(); } + @Override + public int getCompressionLevel(String fileName) + throws DirectoryException { + ZipEntry entry = mZipFile.getEntry(fileName); + if (entry == null) { + throw new PathNotExist("Entry not found: " + fileName); + } + return entry.getMethod(); + } + private void loadAll() { mFiles = new LinkedHashSet(); mDirs = new LinkedHashMap();