From 31815ca9ea990f16b3600d61fd570c1805be1c82 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sat, 11 Jun 2022 06:36:13 +0200 Subject: [PATCH 1/7] fix: resource patcher --- .../kotlin/app/revanced/patcher/Patcher.kt | 176 ++++++++++++------ .../app/revanced/patcher/PatcherOptions.kt | 1 + .../app/revanced/patcher/PatcherResult.kt | 13 ++ .../revanced/patcher/data/PackageMetadata.kt | 13 ++ .../app/revanced/patcher/data/PatcherData.kt | 8 +- .../extensions/AnnotationExtensions.kt | 4 +- .../patch/annotations/PatchAnnotation.kt | 2 +- .../app/revanced/patcher/util/dex/DexFile.kt | 10 + 8 files changed, 165 insertions(+), 62 deletions(-) create mode 100644 src/main/kotlin/app/revanced/patcher/PatcherResult.kt create mode 100644 src/main/kotlin/app/revanced/patcher/data/PackageMetadata.kt create mode 100644 src/main/kotlin/app/revanced/patcher/util/dex/DexFile.kt diff --git a/src/main/kotlin/app/revanced/patcher/Patcher.kt b/src/main/kotlin/app/revanced/patcher/Patcher.kt index 2fb0a11..36f7652 100644 --- a/src/main/kotlin/app/revanced/patcher/Patcher.kt +++ b/src/main/kotlin/app/revanced/patcher/Patcher.kt @@ -1,5 +1,6 @@ package app.revanced.patcher +import app.revanced.patcher.data.PackageMetadata import app.revanced.patcher.data.PatcherData import app.revanced.patcher.data.base.Data import app.revanced.patcher.data.implementation.findIndexed @@ -16,11 +17,13 @@ import app.revanced.patcher.signature.implementation.method.resolver.MethodSigna import app.revanced.patcher.util.ListBackedSet import brut.androlib.Androlib import brut.androlib.meta.UsesFramework +import brut.androlib.options.BuildOptions import brut.androlib.res.AndrolibResources import brut.androlib.res.data.ResPackage import brut.androlib.res.decoder.AXmlResourceParser import brut.androlib.res.decoder.ResAttrDecoder import brut.androlib.res.decoder.XmlPullStreamDecoder +import brut.androlib.res.xml.ResXmlPatcher import brut.directory.ExtFile import lanchon.multidexlib2.BasicDexFileNamer import lanchon.multidexlib2.DexIO @@ -40,32 +43,36 @@ val NAMER = BasicDexFileNamer() class Patcher( private val options: PatcherOptions ) { - val packageVersion: String - val packageName: String + val data: PatcherData - private lateinit var usesFramework: UsesFramework - private val patcherData: PatcherData private val opcodes: Opcodes init { - val extFileInput = ExtFile(options.inputFile) + val extInputFile = ExtFile(options.inputFile) val outDir = File(options.resourceCacheDirectory) - if (outDir.exists()) outDir.deleteRecursively() - outDir.mkdir() + outDir.mkdirs() - // load the resource table from the input file val androlib = Androlib() - val resourceTable = androlib.getResTable(extFileInput, true) + val resourceTable = androlib.getResTable(extInputFile, true) + + val packageMetadata = PackageMetadata() if (options.patchResources) { - // 1. decode resources to cache directory - androlib.decodeManifestWithResources(extFileInput, outDir, resourceTable) - androlib.decodeResourcesFull(extFileInput, outDir, resourceTable) + // decode resources to cache directory + androlib.decodeManifestWithResources(extInputFile, outDir, resourceTable) + androlib.decodeResourcesFull(extInputFile, outDir, resourceTable) + + // read additional metadata from the resource table + packageMetadata.metaInfo.usesFramework = UsesFramework().let { usesFramework -> + usesFramework.ids = resourceTable.listFramePackages().map { it.id }.sorted() + + usesFramework + } + packageMetadata.metaInfo.doNotCompress = buildList { + androlib.recordUncompressedFiles(extInputFile, this) + } - // 2. read framework ids from the resource table - usesFramework = UsesFramework() - usesFramework.ids = resourceTable.listFramePackages().map { it.id }.sorted() } else { // create decoder for the resource table val decoder = ResAttrDecoder() @@ -80,19 +87,27 @@ class Patcher( XmlPullStreamDecoder( axmlParser, AndrolibResources().resXmlSerializer ).decodeManifest( - extFileInput.directory.getFileInput("AndroidManifest.xml"), nullOutputStream + extInputFile.directory.getFileInput("AndroidManifest.xml"), nullOutputStream ) } - // set package information - packageVersion = resourceTable.versionInfo.versionName - packageName = resourceTable.currentResPackage.name - // read dex files - val dexFile = MultiDexIO.readDexFile(true, options.inputFile, NAMER, null, null) - opcodes = dexFile.opcodes + packageMetadata.packageName = resourceTable.currentResPackage.name + packageMetadata.packageVersion = resourceTable.versionInfo.versionName + packageMetadata.metaInfo.versionInfo = resourceTable.versionInfo + packageMetadata.metaInfo.sdkInfo = resourceTable.sdkInfo - // save to patcher data - patcherData = PatcherData(dexFile.classes.toMutableList(), options.resourceCacheDirectory) + // read dex files + val dexFile = MultiDexIO.readDexFile(true, options.inputFile, NAMER, null, null).let { dexFile -> + // get the opcodes + opcodes = dexFile.opcodes + + dexFile + } + + // finally create patcher data + data = PatcherData( + dexFile.classes.toMutableList(), options.resourceCacheDirectory, packageMetadata + ) } /** @@ -102,23 +117,25 @@ class Patcher( * @param throwOnDuplicates If this is set to true, the patcher will throw an exception if a duplicate class has been found. */ fun addFiles( - files: Iterable, allowedOverwrites: Iterable = emptyList(), throwOnDuplicates: Boolean = false + files: List, allowedOverwrites: Iterable = emptyList(), throwOnDuplicates: Boolean = false ) { for (file in files) { - val dexFile = MultiDexIO.readDexFile(true, file, NAMER, null, null) - for (classDef in dexFile.classes) { - val e = patcherData.bytecodeData.classes.internalClasses.findIndexed { it.type == classDef.type } - if (e != null) { - if (throwOnDuplicates) { - throw Exception("Class ${classDef.type} has already been added to the patcher.") + MultiDexIO.readDexFile(true, file, NAMER, null, null).let { dexFile -> + for (classDef in dexFile.classes) { + val e = + data.bytecodeData.classes.internalClasses.findIndexed { internalClass -> internalClass.type == classDef.type } + if (e != null) { + if (throwOnDuplicates) { + throw Exception("Class ${classDef.type} has already been added to the patcher.") + } + val (_, idx) = e + if (allowedOverwrites.contains(classDef.type)) { + data.bytecodeData.classes.internalClasses[idx] = classDef + } + continue } - val (_, idx) = e - if (allowedOverwrites.contains(classDef.type)) { - patcherData.bytecodeData.classes.internalClasses[idx] = classDef - } - continue + data.bytecodeData.classes.internalClasses.add(classDef) } - patcherData.bytecodeData.classes.internalClasses.add(classDef) } } } @@ -126,11 +143,58 @@ class Patcher( /** * Save the patched dex file. */ - fun save(): Map { + fun save(): PatcherResult { + val packageMetadata = data.packageMetadata + val metaInfo = packageMetadata.metaInfo + + if (options.patchResources) { + val cacheDirectory = ExtFile(options.resourceCacheDirectory) + + val androlibResources = AndrolibResources().let { resources -> + resources.buildOptions = BuildOptions().let { options -> + // TODO: options.useAapt2 = true + // TODO: options.aaptPath = "" + options.isFramework = metaInfo.isFrameworkApk + options.resourcesAreCompressed = metaInfo.compressionType + options.doNotCompress = metaInfo.doNotCompress + + options + } + + resources.setSdkInfo(metaInfo.sdkInfo) + resources.setVersionInfo(metaInfo.versionInfo) + resources.setSharedLibrary(metaInfo.sharedLibrary) + resources.setSparseResources(metaInfo.sparseResources) + + resources + } + + val manifestFile = cacheDirectory.resolve("AndroidManifest.xml") + + ResXmlPatcher.fixingPublicAttrsInProviderAttributes(manifestFile) + + cacheDirectory.resolve("aapt_temp_file").let { temporalFile -> + val resDirectory = cacheDirectory.resolve("res") + val includedFiles = + metaInfo.usesFramework.ids.map { id -> androlibResources.getFrameworkApk(id, metaInfo.usesFramework.tag) } + .toTypedArray() + + androlibResources.aaptPackage( + temporalFile, manifestFile, resDirectory, null, + null, includedFiles + ) + + // write packaged resources to cache directory + // TODO: consider returning a list of the files instead of extracting them to the cache directory, + // less disk but more ram usage + ExtFile(temporalFile).directory.copyToDir(cacheDirectory.resolve("build/")) + } + } + val newDexFile = object : DexFile { override fun getClasses(): Set { - patcherData.bytecodeData.classes.applyProxies() - return ListBackedSet(patcherData.bytecodeData.classes.internalClasses) + data.bytecodeData.classes.applyProxies() + return ListBackedSet(data.bytecodeData.classes.internalClasses) } override fun getOpcodes(): Opcodes { @@ -138,21 +202,19 @@ class Patcher( } } - // build modified resources - if (options.patchResources) { - val extDir = ExtFile(options.resourceCacheDirectory) - - // TODO: figure out why a new instance of Androlib is necessary here - Androlib().buildResources(extDir, usesFramework) - } - // write dex modified files - val output = mutableMapOf() + val dexFiles = mutableMapOf() MultiDexIO.writeDexFile( true, -1, // core count - output, NAMER, newDexFile, DexIO.DEFAULT_MAX_DEX_POOL_SIZE, null + dexFiles, NAMER, newDexFile, DexIO.DEFAULT_MAX_DEX_POOL_SIZE, null + ) + + return PatcherResult( + dexFiles.map { + app.revanced.patcher.util.dex.DexFile(it.key, it.value) + }, + metaInfo.doNotCompress.toList() ) - return output } /** @@ -160,7 +222,7 @@ class Patcher( * @param patches [Patch]es The patches to add. */ fun addPatches(patches: Iterable>>) { - patcherData.patches.addAll(patches) + data.patches.addAll(patches) } /** @@ -197,12 +259,12 @@ class Patcher( // TODO: find a solution for this val data = if (isResourcePatch) { - patcherData.resourceData + data.resourceData } else { MethodSignatureResolver( - patcherData.bytecodeData.classes.internalClasses, (patchInstance as BytecodePatch).signatures - ).resolve(patcherData) - patcherData.bytecodeData + data.bytecodeData.classes.internalClasses, (patchInstance as BytecodePatch).signatures + ).resolve(data) + data.bytecodeData } return try { @@ -225,7 +287,7 @@ class Patcher( val appliedPatches = mutableListOf() return buildMap { - for (patch in patcherData.patches) { + for (patch in data.patches) { val result = applyPatch(patch, appliedPatches) val name = patch.patchName diff --git a/src/main/kotlin/app/revanced/patcher/PatcherOptions.kt b/src/main/kotlin/app/revanced/patcher/PatcherOptions.kt index 038826d..2e36a28 100644 --- a/src/main/kotlin/app/revanced/patcher/PatcherOptions.kt +++ b/src/main/kotlin/app/revanced/patcher/PatcherOptions.kt @@ -3,6 +3,7 @@ package app.revanced.patcher import java.io.File /** + * Options for a patcher. * @param inputFile The input file (usually an apk file). * @param resourceCacheDirectory Directory to cache resources. * @param patchResources Weather to use the resource patcher. Resources will still need to be decoded. diff --git a/src/main/kotlin/app/revanced/patcher/PatcherResult.kt b/src/main/kotlin/app/revanced/patcher/PatcherResult.kt new file mode 100644 index 0000000..beb304c --- /dev/null +++ b/src/main/kotlin/app/revanced/patcher/PatcherResult.kt @@ -0,0 +1,13 @@ +package app.revanced.patcher + +import app.revanced.patcher.util.dex.DexFile + +/** + * The result of a patcher. + * @param dexFiles The patched dex files. + * @param doNotCompress List of relative paths to files to exclude from compressing. + */ +data class PatcherResult( + val dexFiles: List, + val doNotCompress: List? = null +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patcher/data/PackageMetadata.kt b/src/main/kotlin/app/revanced/patcher/data/PackageMetadata.kt new file mode 100644 index 0000000..eaa1601 --- /dev/null +++ b/src/main/kotlin/app/revanced/patcher/data/PackageMetadata.kt @@ -0,0 +1,13 @@ +package app.revanced.patcher.data + +import brut.androlib.meta.MetaInfo + +/** + * Metadata about a package. + */ +class PackageMetadata { + lateinit var packageName: String + lateinit var packageVersion: String + + internal val metaInfo: MetaInfo = MetaInfo() +} diff --git a/src/main/kotlin/app/revanced/patcher/data/PatcherData.kt b/src/main/kotlin/app/revanced/patcher/data/PatcherData.kt index 2462d42..fec6f1d 100644 --- a/src/main/kotlin/app/revanced/patcher/data/PatcherData.kt +++ b/src/main/kotlin/app/revanced/patcher/data/PatcherData.kt @@ -7,10 +7,12 @@ import app.revanced.patcher.patch.base.Patch import org.jf.dexlib2.iface.ClassDef import java.io.File -internal data class PatcherData( - val internalClasses: MutableList, - val resourceCacheDirectory: String +data class PatcherData( + internal val internalClasses: MutableList, + internal val resourceCacheDirectory: String, + val packageMetadata: PackageMetadata ) { + internal val patches = mutableListOf>>() internal val bytecodeData = BytecodeData(internalClasses) diff --git a/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt b/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt index ce3a0d1..2d09716 100644 --- a/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt +++ b/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt @@ -41,13 +41,15 @@ object PatchExtensions { val Class>.patchName: String get() = recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName val Class>.version get() = recursiveAnnotation(Version::class)?.version + val Class>.excludeByDefault get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.Patch::class)!!.excludeByDefault val Class>.description get() = recursiveAnnotation(Description::class)?.description val Class>.dependencies get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.Dependencies::class)?.dependencies val Class>.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages } object MethodSignatureExtensions { - val MethodSignature.name: String get() = javaClass.recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName + val MethodSignature.name: String + get() = javaClass.recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName val MethodSignature.version get() = javaClass.recursiveAnnotation(Version::class)?.version ?: "0.0.1" val MethodSignature.description get() = javaClass.recursiveAnnotation(Description::class)?.description val MethodSignature.compatiblePackages get() = javaClass.recursiveAnnotation(Compatibility::class)?.compatiblePackages diff --git a/src/main/kotlin/app/revanced/patcher/patch/annotations/PatchAnnotation.kt b/src/main/kotlin/app/revanced/patcher/patch/annotations/PatchAnnotation.kt index d2e9f99..c302d28 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/annotations/PatchAnnotation.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/annotations/PatchAnnotation.kt @@ -10,7 +10,7 @@ import kotlin.reflect.KClass @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) @MustBeDocumented -annotation class Patch +annotation class Patch(val excludeByDefault: Boolean = false) /** * Annotation for dependencies of [Patch]es . diff --git a/src/main/kotlin/app/revanced/patcher/util/dex/DexFile.kt b/src/main/kotlin/app/revanced/patcher/util/dex/DexFile.kt new file mode 100644 index 0000000..1ddd2a9 --- /dev/null +++ b/src/main/kotlin/app/revanced/patcher/util/dex/DexFile.kt @@ -0,0 +1,10 @@ +package app.revanced.patcher.util.dex + +import org.jf.dexlib2.writer.io.MemoryDataStore + +/** + * Wrapper for dex files. + * @param name The original name of the dex file + * @param memoryDataStore The data store for the dex file. + */ +data class DexFile(val name: String, val memoryDataStore: MemoryDataStore) \ No newline at end of file From 26fca60b533bebff90f397ff060762065340cfa5 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sat, 11 Jun 2022 16:24:49 +0200 Subject: [PATCH 2/7] refactor: use `also` instead of `let` where possible --- src/main/kotlin/app/revanced/patcher/Patcher.kt | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/app/revanced/patcher/Patcher.kt b/src/main/kotlin/app/revanced/patcher/Patcher.kt index 36f7652..da9ac87 100644 --- a/src/main/kotlin/app/revanced/patcher/Patcher.kt +++ b/src/main/kotlin/app/revanced/patcher/Patcher.kt @@ -64,10 +64,8 @@ class Patcher( androlib.decodeResourcesFull(extInputFile, outDir, resourceTable) // read additional metadata from the resource table - packageMetadata.metaInfo.usesFramework = UsesFramework().let { usesFramework -> + packageMetadata.metaInfo.usesFramework = UsesFramework().also { usesFramework -> usesFramework.ids = resourceTable.listFramePackages().map { it.id }.sorted() - - usesFramework } packageMetadata.metaInfo.doNotCompress = buildList { androlib.recordUncompressedFiles(extInputFile, this) @@ -97,11 +95,9 @@ class Patcher( packageMetadata.metaInfo.sdkInfo = resourceTable.sdkInfo // read dex files - val dexFile = MultiDexIO.readDexFile(true, options.inputFile, NAMER, null, null).let { dexFile -> + val dexFile = MultiDexIO.readDexFile(true, options.inputFile, NAMER, null, null).also { dexFile -> // get the opcodes opcodes = dexFile.opcodes - - dexFile } // finally create patcher data @@ -150,23 +146,19 @@ class Patcher( if (options.patchResources) { val cacheDirectory = ExtFile(options.resourceCacheDirectory) - val androlibResources = AndrolibResources().let { resources -> - resources.buildOptions = BuildOptions().let { options -> + val androlibResources = AndrolibResources().also { resources -> + resources.buildOptions = BuildOptions().also { options -> // TODO: options.useAapt2 = true // TODO: options.aaptPath = "" options.isFramework = metaInfo.isFrameworkApk options.resourcesAreCompressed = metaInfo.compressionType options.doNotCompress = metaInfo.doNotCompress - - options } resources.setSdkInfo(metaInfo.sdkInfo) resources.setVersionInfo(metaInfo.versionInfo) resources.setSharedLibrary(metaInfo.sharedLibrary) resources.setSparseResources(metaInfo.sparseResources) - - resources } val manifestFile = cacheDirectory.resolve("AndroidManifest.xml") From 22267883b153124d383f1ecad2a51fe4cb128688 Mon Sep 17 00:00:00 2001 From: Lucaskyy Date: Sat, 11 Jun 2022 16:56:05 +0200 Subject: [PATCH 3/7] refactor: get rid of all useless let blocks --- .../kotlin/app/revanced/patcher/Patcher.kt | 66 ++++++++----------- .../resolver/MethodSignatureResolver.kt | 18 +++-- 2 files changed, 36 insertions(+), 48 deletions(-) diff --git a/src/main/kotlin/app/revanced/patcher/Patcher.kt b/src/main/kotlin/app/revanced/patcher/Patcher.kt index 36f7652..1150560 100644 --- a/src/main/kotlin/app/revanced/patcher/Patcher.kt +++ b/src/main/kotlin/app/revanced/patcher/Patcher.kt @@ -64,11 +64,10 @@ class Patcher( androlib.decodeResourcesFull(extInputFile, outDir, resourceTable) // read additional metadata from the resource table - packageMetadata.metaInfo.usesFramework = UsesFramework().let { usesFramework -> - usesFramework.ids = resourceTable.listFramePackages().map { it.id }.sorted() - - usesFramework + packageMetadata.metaInfo.usesFramework = UsesFramework().also { framework -> + framework.ids = resourceTable.listFramePackages().map { it.id }.sorted() } + packageMetadata.metaInfo.doNotCompress = buildList { androlib.recordUncompressedFiles(extInputFile, this) } @@ -97,12 +96,9 @@ class Patcher( packageMetadata.metaInfo.sdkInfo = resourceTable.sdkInfo // read dex files - val dexFile = MultiDexIO.readDexFile(true, options.inputFile, NAMER, null, null).let { dexFile -> - // get the opcodes - opcodes = dexFile.opcodes - - dexFile - } + val dexFile = MultiDexIO.readDexFile(true, options.inputFile, NAMER, null, null) + // get the opcodes + opcodes = dexFile.opcodes // finally create patcher data data = PatcherData( @@ -120,22 +116,19 @@ class Patcher( files: List, allowedOverwrites: Iterable = emptyList(), throwOnDuplicates: Boolean = false ) { for (file in files) { - MultiDexIO.readDexFile(true, file, NAMER, null, null).let { dexFile -> - for (classDef in dexFile.classes) { - val e = - data.bytecodeData.classes.internalClasses.findIndexed { internalClass -> internalClass.type == classDef.type } - if (e != null) { - if (throwOnDuplicates) { - throw Exception("Class ${classDef.type} has already been added to the patcher.") - } - val (_, idx) = e - if (allowedOverwrites.contains(classDef.type)) { - data.bytecodeData.classes.internalClasses[idx] = classDef - } - continue + for (classDef in MultiDexIO.readDexFile(true, file, NAMER, null, null).classes) { + val e = data.bytecodeData.classes.internalClasses.findIndexed { it.type == classDef.type } + if (e != null) { + if (throwOnDuplicates) { + throw Exception("Class ${classDef.type} has already been added to the patcher.") } - data.bytecodeData.classes.internalClasses.add(classDef) + val (_, idx) = e + if (allowedOverwrites.contains(classDef.type)) { + data.bytecodeData.classes.internalClasses[idx] = classDef + } + continue } + data.bytecodeData.classes.internalClasses.add(classDef) } } } @@ -150,44 +143,41 @@ class Patcher( if (options.patchResources) { val cacheDirectory = ExtFile(options.resourceCacheDirectory) - val androlibResources = AndrolibResources().let { resources -> - resources.buildOptions = BuildOptions().let { options -> + val androlibResources = AndrolibResources().also { resources -> + resources.buildOptions = BuildOptions().also { options -> // TODO: options.useAapt2 = true // TODO: options.aaptPath = "" options.isFramework = metaInfo.isFrameworkApk options.resourcesAreCompressed = metaInfo.compressionType options.doNotCompress = metaInfo.doNotCompress - - options } resources.setSdkInfo(metaInfo.sdkInfo) resources.setVersionInfo(metaInfo.versionInfo) resources.setSharedLibrary(metaInfo.sharedLibrary) resources.setSparseResources(metaInfo.sparseResources) - - resources } val manifestFile = cacheDirectory.resolve("AndroidManifest.xml") ResXmlPatcher.fixingPublicAttrsInProviderAttributes(manifestFile) - cacheDirectory.resolve("aapt_temp_file").let { temporalFile -> + with(cacheDirectory.resolve("aapt_temp_file")) { val resDirectory = cacheDirectory.resolve("res") - val includedFiles = - metaInfo.usesFramework.ids.map { id -> androlibResources.getFrameworkApk(id, metaInfo.usesFramework.tag) } - .toTypedArray() + val includedFiles = metaInfo.usesFramework.ids.map { id -> + androlibResources.getFrameworkApk( + id, + metaInfo.usesFramework.tag + ) + }.toTypedArray() androlibResources.aaptPackage( - temporalFile, manifestFile, resDirectory, null, + this, manifestFile, resDirectory, null, null, includedFiles ) // write packaged resources to cache directory - // TODO: consider returning a list of the files instead of extracting them to the cache directory, - // less disk but more ram usage - ExtFile(temporalFile).directory.copyToDir(cacheDirectory.resolve("build/")) + ExtFile(this).directory.copyToDir(cacheDirectory.resolve("build/")) } } diff --git a/src/main/kotlin/app/revanced/patcher/signature/implementation/method/resolver/MethodSignatureResolver.kt b/src/main/kotlin/app/revanced/patcher/signature/implementation/method/resolver/MethodSignatureResolver.kt index 75255a4..5c1f804 100644 --- a/src/main/kotlin/app/revanced/patcher/signature/implementation/method/resolver/MethodSignatureResolver.kt +++ b/src/main/kotlin/app/revanced/patcher/signature/implementation/method/resolver/MethodSignatureResolver.kt @@ -76,19 +76,17 @@ internal class MethodSignatureResolver( signature.strings?.let { strings -> method.implementation ?: return null - method.implementation!!.instructions.let { instructions -> - val stringsList = strings.toMutableList() + val stringsList = strings.toMutableList() - for (instruction in instructions) { - if (instruction.opcode != Opcode.CONST_STRING) continue + for (instruction in method.implementation!!.instructions) { + if (instruction.opcode != Opcode.CONST_STRING) continue - val string = ((instruction as Instruction21c).reference as StringReference).string - val i = stringsList.indexOfFirst { it == string } - if (i != -1) stringsList.removeAt(i) - } - - if (stringsList.isNotEmpty()) return null + val string = ((instruction as Instruction21c).reference as StringReference).string + val i = stringsList.indexOfFirst { it == string } + if (i != -1) stringsList.removeAt(i) } + + if (stringsList.isNotEmpty()) return null } return if (signature.opcodes == null) { From 2dcbd8d079fe9f2613e18cb5d515a70af4b38ce6 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sat, 11 Jun 2022 19:14:19 +0200 Subject: [PATCH 4/7] refactor: use `include` annotation parameter instead of `excludeByDefault` --- .../app/revanced/patcher/extensions/AnnotationExtensions.kt | 2 +- .../app/revanced/patcher/patch/annotations/PatchAnnotation.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt b/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt index 2d09716..abad2b9 100644 --- a/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt +++ b/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt @@ -41,7 +41,7 @@ object PatchExtensions { val Class>.patchName: String get() = recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName val Class>.version get() = recursiveAnnotation(Version::class)?.version - val Class>.excludeByDefault get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.Patch::class)!!.excludeByDefault + val Class>.include get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.Patch::class)!!.include val Class>.description get() = recursiveAnnotation(Description::class)?.description val Class>.dependencies get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.Dependencies::class)?.dependencies val Class>.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages diff --git a/src/main/kotlin/app/revanced/patcher/patch/annotations/PatchAnnotation.kt b/src/main/kotlin/app/revanced/patcher/patch/annotations/PatchAnnotation.kt index c302d28..901f616 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/annotations/PatchAnnotation.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/annotations/PatchAnnotation.kt @@ -6,11 +6,12 @@ import kotlin.reflect.KClass /** * Annotation to mark a Class as a patch. + * @param include If false, the patch should be treated as optional by default. */ @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) @MustBeDocumented -annotation class Patch(val excludeByDefault: Boolean = false) +annotation class Patch(val include: Boolean = true) /** * Annotation for dependencies of [Patch]es . From afcba5c212f2892077d4640e2cf02cc3d459e4e2 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sat, 11 Jun 2022 19:45:11 +0200 Subject: [PATCH 5/7] refactor: simplify code by removing the `with` block --- .../kotlin/app/revanced/patcher/Patcher.kt | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/app/revanced/patcher/Patcher.kt b/src/main/kotlin/app/revanced/patcher/Patcher.kt index 1150560..aa4c100 100644 --- a/src/main/kotlin/app/revanced/patcher/Patcher.kt +++ b/src/main/kotlin/app/revanced/patcher/Patcher.kt @@ -33,6 +33,7 @@ import org.jf.dexlib2.iface.ClassDef import org.jf.dexlib2.iface.DexFile import org.jf.dexlib2.writer.io.MemoryDataStore import java.io.File +import java.nio.file.Files val NAMER = BasicDexFileNamer() @@ -162,23 +163,27 @@ class Patcher( ResXmlPatcher.fixingPublicAttrsInProviderAttributes(manifestFile) - with(cacheDirectory.resolve("aapt_temp_file")) { - val resDirectory = cacheDirectory.resolve("res") - val includedFiles = metaInfo.usesFramework.ids.map { id -> - androlibResources.getFrameworkApk( - id, - metaInfo.usesFramework.tag - ) - }.toTypedArray() + val aaptFile = cacheDirectory.resolve("aapt_temp_file") - androlibResources.aaptPackage( - this, manifestFile, resDirectory, null, - null, includedFiles + // delete if it exists + Files.deleteIfExists(aaptFile.toPath()) + + val resDirectory = cacheDirectory.resolve("res") + val includedFiles = metaInfo.usesFramework.ids.map { id -> + androlibResources.getFrameworkApk( + id, + metaInfo.usesFramework.tag ) + }.toTypedArray() + + androlibResources.aaptPackage( + aaptFile, manifestFile, resDirectory, null, + null, includedFiles + ) + + // write packaged resources to cache directory + ExtFile(aaptFile).directory.copyToDir(cacheDirectory.resolve("build/")) - // write packaged resources to cache directory - ExtFile(this).directory.copyToDir(cacheDirectory.resolve("build/")) - } } val newDexFile = object : DexFile { From 8eb4a8f87ae7679a272f3224273a37a31d4bb121 Mon Sep 17 00:00:00 2001 From: Lucaskyy Date: Sat, 11 Jun 2022 20:08:00 +0200 Subject: [PATCH 6/7] feat: allow custom aapt path to be specified --- src/main/kotlin/app/revanced/patcher/Patcher.kt | 16 ++++++---------- .../app/revanced/patcher/PatcherOptions.kt | 5 +++-- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/app/revanced/patcher/Patcher.kt b/src/main/kotlin/app/revanced/patcher/Patcher.kt index aa4c100..4653a99 100644 --- a/src/main/kotlin/app/revanced/patcher/Patcher.kt +++ b/src/main/kotlin/app/revanced/patcher/Patcher.kt @@ -41,11 +41,8 @@ val NAMER = BasicDexFileNamer() * The ReVanced Patcher. * @param options The options for the patcher. */ -class Patcher( - private val options: PatcherOptions -) { +class Patcher(private val options: PatcherOptions) { val data: PatcherData - private val opcodes: Opcodes init { @@ -145,12 +142,11 @@ class Patcher( val cacheDirectory = ExtFile(options.resourceCacheDirectory) val androlibResources = AndrolibResources().also { resources -> - resources.buildOptions = BuildOptions().also { options -> - // TODO: options.useAapt2 = true - // TODO: options.aaptPath = "" - options.isFramework = metaInfo.isFrameworkApk - options.resourcesAreCompressed = metaInfo.compressionType - options.doNotCompress = metaInfo.doNotCompress + resources.buildOptions = BuildOptions().also { buildOptions -> + buildOptions.aaptPath = options.aaptPath + buildOptions.isFramework = metaInfo.isFrameworkApk + buildOptions.resourcesAreCompressed = metaInfo.compressionType + buildOptions.doNotCompress = metaInfo.doNotCompress } resources.setSdkInfo(metaInfo.sdkInfo) diff --git a/src/main/kotlin/app/revanced/patcher/PatcherOptions.kt b/src/main/kotlin/app/revanced/patcher/PatcherOptions.kt index 2e36a28..a371037 100644 --- a/src/main/kotlin/app/revanced/patcher/PatcherOptions.kt +++ b/src/main/kotlin/app/revanced/patcher/PatcherOptions.kt @@ -7,10 +7,11 @@ import java.io.File * @param inputFile The input file (usually an apk file). * @param resourceCacheDirectory Directory to cache resources. * @param patchResources Weather to use the resource patcher. Resources will still need to be decoded. + * @param aaptPath Optional path to a custom aapt binary. */ data class PatcherOptions( internal val inputFile: File, - // TODO: maybe a file system in memory is better. Could cause high memory usage. internal val resourceCacheDirectory: String, - internal val patchResources: Boolean = false + internal val patchResources: Boolean = false, + internal val aaptPath: String = "" ) From 566ecefa2bd4cde5ebfb2b22dc56cd8bf9f396bd Mon Sep 17 00:00:00 2001 From: Lucaskyy Date: Sat, 11 Jun 2022 20:39:29 +0200 Subject: [PATCH 7/7] fix: update apktool to fork --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 52e52b1..038574c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -27,7 +27,7 @@ dependencies { implementation(kotlin("stdlib")) api("xpp3:xpp3:1.1.4c") - api("org.apktool:apktool-lib:2.6.1") + api("org.apktool:apktool-lib:2.6.2-SNAPSHOT") api("app.revanced:multidexlib2:2.5.2.r2") api("org.smali:smali:2.5.2")