From 4f60bea81e0bbe85dc6c3150238980292a1e52ab Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Tue, 24 May 2022 01:28:31 +0200 Subject: [PATCH] perf: decode manifest only when not using resource patcher Signed-off-by: oSumAtrIX --- build.gradle.kts | 1 + .../kotlin/app/revanced/patcher/Patcher.kt | 60 ++++++++++++------- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index c89f07c9..c5a36cf2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,6 +24,7 @@ repositories { dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib:1.6.21") + api("xpp3:xpp3:1.1.4c") api("org.apktool:apktool-lib:2.6.1") api("app.revanced:multidexlib2:2.5.2.r2") api("org.smali:smali:2.5.2") diff --git a/src/main/kotlin/app/revanced/patcher/Patcher.kt b/src/main/kotlin/app/revanced/patcher/Patcher.kt index f5699ecc..e2e762de 100644 --- a/src/main/kotlin/app/revanced/patcher/Patcher.kt +++ b/src/main/kotlin/app/revanced/patcher/Patcher.kt @@ -14,6 +14,11 @@ 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.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.directory.ExtFile import lanchon.multidexlib2.BasicDexFileNamer import lanchon.multidexlib2.DexIO @@ -23,6 +28,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.io.OutputStream val NAMER = BasicDexFileNamer() @@ -35,13 +41,12 @@ val NAMER = BasicDexFileNamer() class Patcher( inputFile: File, // TODO: maybe a file system in memory is better. Could cause high memory usage. - private val resourceCacheDirectory: String, - private val patchResources: Boolean = false + private val resourceCacheDirectory: String, private val patchResources: Boolean = false ) { val packageVersion: String val packageName: String - private val usesFramework: UsesFramework + private lateinit var usesFramework: UsesFramework private val patcherData: PatcherData private val opcodes: Opcodes private var signaturesResolved = false @@ -54,21 +59,39 @@ class Patcher( if (outDir.exists()) outDir.deleteRecursively() outDir.mkdir() + // load the resource table from the input file val androlib = Androlib() - val resourceTable = androlib.getResTable(extFileInput, true) - // 1. decode resources to cache directory - androlib.decodeManifestWithResources(extFileInput, outDir, resourceTable) - androlib.decodeResourcesFull(extFileInput, outDir, resourceTable) - // 2. read framework ids from the resource table - usesFramework = UsesFramework() - usesFramework.ids = resourceTable.listFramePackages().map { it.id }.sorted() + if (patchResources) { + // 1. decode resources to cache directory + androlib.decodeManifestWithResources(extFileInput, outDir, resourceTable) + androlib.decodeResourcesFull(extFileInput, outDir, resourceTable) + + // 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() + decoder.currentPackage = ResPackage(resourceTable, 0, null) + + // create xml parser with the decoder + val axmlParser = AXmlResourceParser() + axmlParser.attrDecoder = decoder + + // parse package information with the decoder and parser which will set required values in the resource table + // instead of decodeManifest another more low level solution can be created to make it faster/better + XmlPullStreamDecoder( + axmlParser, AndrolibResources().resXmlSerializer + ).decodeManifest( + extFileInput.directory.getFileInput("AndroidManifest.xml"), OutputStream.nullOutputStream() + ) + } - // 2. read package info - packageName = resourceTable.packageOriginal + // set package information packageVersion = resourceTable.versionInfo.versionName - + packageName = resourceTable.currentResPackage.name // read dex files val dexFile = MultiDexIO.readDexFile(true, inputFile, NAMER, null, null) opcodes = dexFile.opcodes @@ -84,9 +107,7 @@ 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: Iterable, allowedOverwrites: Iterable = emptyList(), throwOnDuplicates: Boolean = false ) { for (file in files) { val dexFile = MultiDexIO.readDexFile(true, file, NAMER, null, null) @@ -134,9 +155,7 @@ class Patcher( val output = mutableMapOf() MultiDexIO.writeDexFile( true, -1, // core count - output, NAMER, newDexFile, - DexIO.DEFAULT_MAX_DEX_POOL_SIZE, - null + output, NAMER, newDexFile, DexIO.DEFAULT_MAX_DEX_POOL_SIZE, null ) return output } @@ -177,8 +196,7 @@ class Patcher( * If the [Patch] failed to apply, an Exception will always be returned to the wrapping Result object. */ fun applyPatches( - stopOnError: Boolean = false, - callback: (String) -> Unit = {} + stopOnError: Boolean = false, callback: (String) -> Unit = {} ): Map> { if (!signaturesResolved) { resolveSignatures()