perf: decode manifest only when not using resource patcher

Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
oSumAtrIX 2022-05-24 01:28:31 +02:00
parent 1a49d9439f
commit 4f60bea81e
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
2 changed files with 40 additions and 21 deletions

View File

@ -24,6 +24,7 @@ repositories {
dependencies { dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.6.21") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.6.21")
api("xpp3:xpp3:1.1.4c")
api("org.apktool:apktool-lib:2.6.1") api("org.apktool:apktool-lib:2.6.1")
api("app.revanced:multidexlib2:2.5.2.r2") api("app.revanced:multidexlib2:2.5.2.r2")
api("org.smali:smali:2.5.2") api("org.smali:smali:2.5.2")

View File

@ -14,6 +14,11 @@ import app.revanced.patcher.signature.implementation.method.resolver.MethodSigna
import app.revanced.patcher.util.ListBackedSet import app.revanced.patcher.util.ListBackedSet
import brut.androlib.Androlib import brut.androlib.Androlib
import brut.androlib.meta.UsesFramework 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 brut.directory.ExtFile
import lanchon.multidexlib2.BasicDexFileNamer import lanchon.multidexlib2.BasicDexFileNamer
import lanchon.multidexlib2.DexIO import lanchon.multidexlib2.DexIO
@ -23,6 +28,7 @@ import org.jf.dexlib2.iface.ClassDef
import org.jf.dexlib2.iface.DexFile import org.jf.dexlib2.iface.DexFile
import org.jf.dexlib2.writer.io.MemoryDataStore import org.jf.dexlib2.writer.io.MemoryDataStore
import java.io.File import java.io.File
import java.io.OutputStream
val NAMER = BasicDexFileNamer() val NAMER = BasicDexFileNamer()
@ -35,13 +41,12 @@ val NAMER = BasicDexFileNamer()
class Patcher( class Patcher(
inputFile: File, inputFile: File,
// TODO: maybe a file system in memory is better. Could cause high memory usage. // TODO: maybe a file system in memory is better. Could cause high memory usage.
private val resourceCacheDirectory: String, private val resourceCacheDirectory: String, private val patchResources: Boolean = false
private val patchResources: Boolean = false
) { ) {
val packageVersion: String val packageVersion: String
val packageName: String val packageName: String
private val usesFramework: UsesFramework private lateinit var usesFramework: UsesFramework
private val patcherData: PatcherData private val patcherData: PatcherData
private val opcodes: Opcodes private val opcodes: Opcodes
private var signaturesResolved = false private var signaturesResolved = false
@ -54,9 +59,11 @@ class Patcher(
if (outDir.exists()) outDir.deleteRecursively() if (outDir.exists()) outDir.deleteRecursively()
outDir.mkdir() outDir.mkdir()
// load the resource table from the input file
val androlib = Androlib() val androlib = Androlib()
val resourceTable = androlib.getResTable(extFileInput, true) val resourceTable = androlib.getResTable(extFileInput, true)
if (patchResources) {
// 1. decode resources to cache directory // 1. decode resources to cache directory
androlib.decodeManifestWithResources(extFileInput, outDir, resourceTable) androlib.decodeManifestWithResources(extFileInput, outDir, resourceTable)
androlib.decodeResourcesFull(extFileInput, outDir, resourceTable) androlib.decodeResourcesFull(extFileInput, outDir, resourceTable)
@ -64,11 +71,27 @@ class Patcher(
// 2. read framework ids from the resource table // 2. read framework ids from the resource table
usesFramework = UsesFramework() usesFramework = UsesFramework()
usesFramework.ids = resourceTable.listFramePackages().map { it.id }.sorted() usesFramework.ids = resourceTable.listFramePackages().map { it.id }.sorted()
} else {
// create decoder for the resource table
val decoder = ResAttrDecoder()
decoder.currentPackage = ResPackage(resourceTable, 0, null)
// 2. read package info // create xml parser with the decoder
packageName = resourceTable.packageOriginal 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()
)
}
// set package information
packageVersion = resourceTable.versionInfo.versionName packageVersion = resourceTable.versionInfo.versionName
packageName = resourceTable.currentResPackage.name
// read dex files // read dex files
val dexFile = MultiDexIO.readDexFile(true, inputFile, NAMER, null, null) val dexFile = MultiDexIO.readDexFile(true, inputFile, NAMER, null, null)
opcodes = dexFile.opcodes 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. * @param throwOnDuplicates If this is set to true, the patcher will throw an exception if a duplicate class has been found.
*/ */
fun addFiles( fun addFiles(
files: Iterable<File>, files: Iterable<File>, allowedOverwrites: Iterable<String> = emptyList(), throwOnDuplicates: Boolean = false
allowedOverwrites: Iterable<String> = emptyList(),
throwOnDuplicates: Boolean = false
) { ) {
for (file in files) { for (file in files) {
val dexFile = MultiDexIO.readDexFile(true, file, NAMER, null, null) val dexFile = MultiDexIO.readDexFile(true, file, NAMER, null, null)
@ -134,9 +155,7 @@ class Patcher(
val output = mutableMapOf<String, MemoryDataStore>() val output = mutableMapOf<String, MemoryDataStore>()
MultiDexIO.writeDexFile( MultiDexIO.writeDexFile(
true, -1, // core count true, -1, // core count
output, NAMER, newDexFile, output, NAMER, newDexFile, DexIO.DEFAULT_MAX_DEX_POOL_SIZE, null
DexIO.DEFAULT_MAX_DEX_POOL_SIZE,
null
) )
return output 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. * If the [Patch] failed to apply, an Exception will always be returned to the wrapping Result object.
*/ */
fun applyPatches( fun applyPatches(
stopOnError: Boolean = false, stopOnError: Boolean = false, callback: (String) -> Unit = {}
callback: (String) -> Unit = {}
): Map<String, Result<PatchResultSuccess>> { ): Map<String, Result<PatchResultSuccess>> {
if (!signaturesResolved) { if (!signaturesResolved) {
resolveSignatures() resolveSignatures()