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 62fd75bf3e..29fb181d93 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 @@ -19,24 +19,17 @@ import brut.androlib.exceptions.AndrolibException; import brut.androlib.exceptions.InFileNotFoundException; import brut.androlib.exceptions.OutDirExistsException; -import brut.androlib.exceptions.UndefinedResObjectException; import brut.androlib.meta.MetaInfo; -import brut.androlib.meta.PackageInfo; -import brut.androlib.meta.UsesFramework; -import brut.androlib.meta.VersionInfo; import brut.androlib.res.AndrolibResources; -import brut.androlib.res.data.ResPackage; import brut.androlib.res.data.ResTable; import brut.androlib.res.data.ResUnknownFiles; import brut.androlib.src.SmaliDecoder; import brut.directory.Directory; import brut.directory.ExtFile; -import brut.androlib.res.xml.ResXmlPatcher; import brut.common.BrutException; import brut.directory.DirectoryException; import brut.util.OS; import com.android.tools.smali.dexlib2.iface.DexFile; -import com.google.common.base.Strings; import org.apache.commons.io.FilenameUtils; import java.io.File; @@ -111,7 +104,7 @@ public void decode(File outDir) throws AndrolibException, IOException, Directory if (hasResources()) { switch (config.decodeResources) { case Config.DECODE_RESOURCES_NONE: - decodeResourcesRaw(mApkFile, outDir); + copyResourcesRaw(mApkFile, outDir); if (config.forceDecodeManifest == Config.FORCE_DECODE_MANIFEST_FULL) { // done after raw decoding of resources because copyToDir overwrites dest files if (hasManifest()) { @@ -135,7 +128,7 @@ public void decode(File outDir) throws AndrolibException, IOException, Directory decodeManifestFull(mApkFile, outDir, getResTable()); } else { - decodeManifestRaw(mApkFile, outDir); + copyManifestRaw(mApkFile, outDir); } } } @@ -143,7 +136,7 @@ public void decode(File outDir) throws AndrolibException, IOException, Directory if (hasSources()) { switch (config.decodeSources) { case Config.DECODE_SOURCES_NONE: - decodeSourcesRaw(mApkFile, outDir, "classes.dex"); + copySourcesRaw(mApkFile, outDir, "classes.dex"); break; case Config.DECODE_SOURCES_SMALI: case Config.DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES: @@ -160,7 +153,7 @@ public void decode(File outDir) throws AndrolibException, IOException, Directory if (! file.equalsIgnoreCase("classes.dex")) { switch(config.decodeSources) { case Config.DECODE_SOURCES_NONE: - decodeSourcesRaw(mApkFile, outDir, file); + copySourcesRaw(mApkFile, outDir, file); break; case Config.DECODE_SOURCES_SMALI: decodeSourcesSmali(mApkFile, outDir, file); @@ -169,7 +162,7 @@ public void decode(File outDir) throws AndrolibException, IOException, Directory if (file.startsWith("classes") && file.endsWith(".dex")) { decodeSourcesSmali(mApkFile, outDir, file); } else { - decodeSourcesRaw(mApkFile, outDir, file); + copySourcesRaw(mApkFile, outDir, file); } break; } @@ -178,11 +171,11 @@ public void decode(File outDir) throws AndrolibException, IOException, Directory } } - decodeRawFiles(mApkFile, outDir); - decodeUnknownFiles(mApkFile, outDir); + copyRawFiles(mApkFile, outDir); + copyUnknownFiles(mApkFile, outDir); mUncompressedFiles = new ArrayList<>(); recordUncompressedFiles(mApkFile, mUncompressedFiles); - writeOriginalFiles(mApkFile, outDir); + copyOriginalFiles(mApkFile, outDir); writeMetaFile(outDir); } finally { try { @@ -250,25 +243,25 @@ public void close() throws IOException { mAndRes.close(); } - public void writeMetaFile(File outDir) - throws AndrolibException { + private void writeMetaFile(File outDir) throws AndrolibException { MetaInfo meta = new MetaInfo(); meta.version = ApktoolProperties.getVersion(); meta.apkFileName = mApkFile.getName(); if (mResTable != null) { - meta.isFrameworkApk = mResTable.isFrameworkApk(); - putUsesFramework(meta); - putSdkInfo(outDir, meta); - putPackageInfo(meta); - putVersionInfo(outDir, meta); - putSharedLibraryInfo(meta); - putSparseResourcesInfo(meta); + mResTable.initMetaInfo(meta, outDir); + if (config.frameworkTag != null) { + meta.usesFramework.tag = config.frameworkTag; + } } else { - putMinSdkInfo(meta); + if (mMinSdkVersion > 0) { + meta.sdkInfo = getMinSdkInfo(); + } + } + meta.unknownFiles = mResUnknownFiles.getUnknownFiles(); + if (mUncompressedFiles != null && !mUncompressedFiles.isEmpty()) { + meta.doNotCompress = mUncompressedFiles; } - putUnknownInfo(meta); - putFileCompressionInfo(meta); try { meta.save(new File(outDir, "apktool.yml")); @@ -277,111 +270,13 @@ public void writeMetaFile(File outDir) } } - private void putUsesFramework(MetaInfo meta) { - Set pkgs = mResTable.listFramePackages(); - if (pkgs.isEmpty()) { - return; - } - - Integer[] ids = new Integer[pkgs.size()]; - int i = 0; - for (ResPackage pkg : pkgs) { - ids[i++] = pkg.getId(); - } - Arrays.sort(ids); - - meta.usesFramework = new UsesFramework(); - meta.usesFramework.ids = Arrays.asList(ids); - - if (config.frameworkTag != null) { - meta.usesFramework.tag = config.frameworkTag; - } - } - - private void putSdkInfo(File outDir, MetaInfo meta) { - Map info = mResTable.getSdkInfo(); - if (info.size() > 0) { - String refValue; - if (info.get("minSdkVersion") != null) { - refValue = ResXmlPatcher.pullValueFromIntegers(outDir, info.get("minSdkVersion")); - if (refValue != null) { - info.put("minSdkVersion", refValue); - } - } - if (info.get("targetSdkVersion") != null) { - refValue = ResXmlPatcher.pullValueFromIntegers(outDir, info.get("targetSdkVersion")); - if (refValue != null) { - info.put("targetSdkVersion", refValue); - } - } - if (info.get("maxSdkVersion") != null) { - refValue = ResXmlPatcher.pullValueFromIntegers(outDir, info.get("maxSdkVersion")); - if (refValue != null) { - info.put("maxSdkVersion", refValue); - } - } - meta.sdkInfo = info; - } - } - - private void putMinSdkInfo(MetaInfo meta) { - if (mMinSdkVersion > 0) { - Map sdkInfo = new LinkedHashMap<>(); - sdkInfo.put("minSdkVersion", Integer.toString(mMinSdkVersion)); - meta.sdkInfo = sdkInfo; - } - } - - private void putPackageInfo(MetaInfo meta) throws AndrolibException { - String renamed = mResTable.getPackageRenamed(); - String original = mResTable.getPackageOriginal(); - - int id = mResTable.getPackageId(); - try { - id = mResTable.getPackage(renamed).getId(); - } catch (UndefinedResObjectException ignored) {} - - if (Strings.isNullOrEmpty(original)) { - return; - } - - meta.packageInfo = new PackageInfo(); - - // only put rename-manifest-package into apktool.yml, if the change will be required - if (!renamed.equalsIgnoreCase(original)) { - meta.packageInfo.renameManifestPackage = renamed; - } - meta.packageInfo.forcedPackageId = String.valueOf(id); - } - - private void putVersionInfo(File outDir, MetaInfo meta) { - VersionInfo info = mResTable.getVersionInfo(); - String refValue = ResXmlPatcher.pullValueFromStrings(outDir, info.versionName); - if (refValue != null) { - info.versionName = refValue; - } - meta.versionInfo = info; - } - - private void putSharedLibraryInfo(MetaInfo meta) { - meta.sharedLibrary = mResTable.getSharedLibrary(); - } - - private void putSparseResourcesInfo(MetaInfo meta) { - meta.sparseResources = mResTable.getSparseResources(); - } - - private void putUnknownInfo(MetaInfo meta) { - meta.unknownFiles = mResUnknownFiles.getUnknownFiles(); - } - - private void putFileCompressionInfo(MetaInfo meta) { - if (mUncompressedFiles != null && !mUncompressedFiles.isEmpty()) { - meta.doNotCompress = mUncompressedFiles; - } + private Map getMinSdkInfo() { + Map sdkInfo = new LinkedHashMap<>(); + sdkInfo.put("minSdkVersion", Integer.toString(mMinSdkVersion)); + return sdkInfo; } - public void decodeSourcesRaw(ExtFile apkFile, File outDir, String filename) + private void copySourcesRaw(ExtFile apkFile, File outDir, String filename) throws AndrolibException { try { LOGGER.info("Copying raw " + filename + " file..."); @@ -391,7 +286,7 @@ public void decodeSourcesRaw(ExtFile apkFile, File outDir, String filename) } } - public void decodeSourcesSmali(File apkFile, File outDir, String filename) + private void decodeSourcesSmali(File apkFile, File outDir, String filename) throws AndrolibException { try { File smaliDir; @@ -415,7 +310,7 @@ public void decodeSourcesSmali(File apkFile, File outDir, String filename) } } - public void decodeManifestRaw(ExtFile apkFile, File outDir) + private void copyManifestRaw(ExtFile apkFile, File outDir) throws AndrolibException { try { LOGGER.info("Copying raw manifest..."); @@ -425,12 +320,12 @@ public void decodeManifestRaw(ExtFile apkFile, File outDir) } } - public void decodeManifestFull(ExtFile apkFile, File outDir, ResTable resTable) + private void decodeManifestFull(ExtFile apkFile, File outDir, ResTable resTable) throws AndrolibException { mAndRes.decodeManifest(resTable, apkFile, outDir); } - public void decodeResourcesRaw(ExtFile apkFile, File outDir) + private void copyResourcesRaw(ExtFile apkFile, File outDir) throws AndrolibException { try { LOGGER.info("Copying raw resources..."); @@ -440,17 +335,17 @@ public void decodeResourcesRaw(ExtFile apkFile, File outDir) } } - public void decodeResourcesFull(ExtFile apkFile, File outDir, ResTable resTable) + private void decodeResourcesFull(ExtFile apkFile, File outDir, ResTable resTable) throws AndrolibException { mAndRes.decode(resTable, apkFile, outDir); } - public void decodeManifestWithResources(ExtFile apkFile, File outDir, ResTable resTable) + private void decodeManifestWithResources(ExtFile apkFile, File outDir, ResTable resTable) throws AndrolibException { mAndRes.decodeManifestWithResources(resTable, apkFile, outDir); } - public void decodeRawFiles(ExtFile apkFile, File outDir) + private void copyRawFiles(ExtFile apkFile, File outDir) throws AndrolibException { LOGGER.info("Copying assets and libs..."); try { @@ -484,7 +379,7 @@ private boolean isAPKFileNames(String file) { return false; } - public void decodeUnknownFiles(ExtFile apkFile, File outDir) + private void copyUnknownFiles(ExtFile apkFile, File outDir) throws AndrolibException { LOGGER.info("Copying unknown files..."); File unknownOut = new File(outDir, UNK_DIRNAME); @@ -508,7 +403,7 @@ public void decodeUnknownFiles(ExtFile apkFile, File outDir) } } - public void writeOriginalFiles(ExtFile apkFile, File outDir) + private void copyOriginalFiles(ExtFile apkFile, File outDir) throws AndrolibException { LOGGER.info("Copying original files..."); File originalDir = new File(outDir, "original"); @@ -531,7 +426,7 @@ public void writeOriginalFiles(ExtFile apkFile, File outDir) if (in.containsDir("META-INF/services")) { // If the original APK contains the folder META-INF/services folder // that is used for service locators (like coroutines on android), - // copy it to the destination folder so it does not get dropped. + // copy it to the destination folder, so it does not get dropped. LOGGER.info("Copying META-INF/services directory"); in.copyToDir(outDir, "META-INF/services"); } @@ -541,7 +436,7 @@ public void writeOriginalFiles(ExtFile apkFile, File outDir) } } - public void recordUncompressedFiles(ExtFile apkFile, Collection uncompressedFilesOrExts) throws AndrolibException { + private void recordUncompressedFiles(ExtFile apkFile, Collection uncompressedFilesOrExts) throws AndrolibException { try { Directory unk = apkFile.getDirectory(); Set files = unk.getFiles(true); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java index bbb3d36258..fdb6ff1385 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java @@ -18,9 +18,16 @@ import brut.androlib.exceptions.AndrolibException; import brut.androlib.exceptions.UndefinedResObjectException; +import brut.androlib.meta.MetaInfo; +import brut.androlib.meta.PackageInfo; +import brut.androlib.meta.UsesFramework; import brut.androlib.meta.VersionInfo; import brut.androlib.res.AndrolibResources; import brut.androlib.res.data.value.ResValue; +import brut.androlib.res.xml.ResXmlPatcher; +import com.google.common.base.Strings; + +import java.io.File; import java.util.*; public class ResTable { @@ -220,4 +227,90 @@ public boolean isFrameworkApk() { } return false; } + + public void initMetaInfo(MetaInfo meta, File outDir) throws AndrolibException { + meta.isFrameworkApk = isFrameworkApk(); + if (!listFramePackages().isEmpty()) { + meta.usesFramework = getUsesFramework(); + } + if (!getSdkInfo().isEmpty()) { + initSdkInfo(outDir); + meta.sdkInfo = getSdkInfo(); + } + meta.packageInfo = getPackageInfo(); + meta.versionInfo = getVersionInfoWithName(outDir); + meta.sharedLibrary = getSharedLibrary(); + meta.sparseResources = getSparseResources(); + } + + private UsesFramework getUsesFramework() { + Set pkgs = listFramePackages(); + + Integer[] ids = new Integer[pkgs.size()]; + int i = 0; + for (ResPackage pkg : pkgs) { + ids[i++] = pkg.getId(); + } + Arrays.sort(ids); + + UsesFramework info = new UsesFramework(); + info.ids = Arrays.asList(ids); + + return info; + } + + private void initSdkInfo(File outDir) { + Map info = mSdkInfo; + String refValue; + if (info.get("minSdkVersion") != null) { + refValue = ResXmlPatcher.pullValueFromIntegers(outDir, info.get("minSdkVersion")); + if (refValue != null) { + info.put("minSdkVersion", refValue); + } + } + if (info.get("targetSdkVersion") != null) { + refValue = ResXmlPatcher.pullValueFromIntegers(outDir, info.get("targetSdkVersion")); + if (refValue != null) { + info.put("targetSdkVersion", refValue); + } + } + if (info.get("maxSdkVersion") != null) { + refValue = ResXmlPatcher.pullValueFromIntegers(outDir, info.get("maxSdkVersion")); + if (refValue != null) { + info.put("maxSdkVersion", refValue); + } + } + } + + private PackageInfo getPackageInfo() throws AndrolibException { + String renamed = getPackageRenamed(); + String original = getPackageOriginal(); + + int id = getPackageId(); + try { + id = getPackage(renamed).getId(); + } catch (UndefinedResObjectException ignored) {} + + if (Strings.isNullOrEmpty(original)) { + return null; + } + + PackageInfo info = new PackageInfo(); + + // only put rename-manifest-package into apktool.yml, if the change will be required + if (!renamed.equalsIgnoreCase(original)) { + info.renameManifestPackage = renamed; + } + info.forcedPackageId = String.valueOf(id); + return info; + } + + private VersionInfo getVersionInfoWithName(File outDir) { + VersionInfo info = getVersionInfo(); + String refValue = ResXmlPatcher.pullValueFromStrings(outDir, info.versionName); + if (refValue != null) { + info.versionName = refValue; + } + return info; + } }