Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions patcher/api/jvm/patcher.api
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,20 @@ public final class app/revanced/com/android/tools/smali/dexlib2/mutable/MutableM
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/MethodParameter;)Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethodParameter;
}

public abstract class app/revanced/patcher/Apk {
public synthetic fun <init> (Ljava/io/File;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getFile ()Ljava/io/File;
}

public final class app/revanced/patcher/Apk$Single : app/revanced/patcher/Apk {
public fun <init> (Ljava/io/File;)V
}

public final class app/revanced/patcher/Apk$Split : app/revanced/patcher/Apk {
public fun <init> (Ljava/io/File;Ljava/util/Map;)V
public final fun getSplitApkFiles ()Ljava/util/Map;
}

public final class app/revanced/patcher/ClassDefComposing {
public static final field INSTANCE Lapp/revanced/patcher/ClassDefComposing;
public final fun composingFirstMethod ([Ljava/lang/String;Lkotlin/jvm/functions/Function3;)Lkotlin/properties/ReadOnlyProperty;
Expand Down Expand Up @@ -781,6 +795,7 @@ public final class app/revanced/patcher/MutablePredicateList : java/util/List, k
public final class app/revanced/patcher/PatchesResult {
public final fun getDexFiles ()Ljava/util/Set;
public final fun getResources ()Lapp/revanced/patcher/PatchesResult$PatchedResources;
public final fun getSplitResources ()Ljava/util/Map;
}

public final class app/revanced/patcher/PatchesResult$PatchedDexFile {
Expand All @@ -797,7 +812,9 @@ public final class app/revanced/patcher/PatchesResult$PatchedResources {

public final class app/revanced/patcher/PatchingKt {
public static final fun apply (Ljava/util/Set;Lapp/revanced/patcher/patch/BytecodePatchContext;Lapp/revanced/patcher/patch/ResourcePatchContext;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/PatchesResult;
public static final fun patcher (Lapp/revanced/patcher/Apk;Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lkotlin/jvm/functions/Function2;)Lkotlin/jvm/functions/Function1;
public static final fun patcher (Ljava/io/File;Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lkotlin/jvm/functions/Function2;)Lkotlin/jvm/functions/Function1;
public static synthetic fun patcher$default (Lapp/revanced/patcher/Apk;Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlin/jvm/functions/Function1;
public static synthetic fun patcher$default (Ljava/io/File;Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlin/jvm/functions/Function1;
}

Expand Down Expand Up @@ -1209,12 +1226,16 @@ public class app/revanced/patcher/patch/ResourcePatchBuilder : app/revanced/patc

public final class app/revanced/patcher/patch/ResourcePatchContext : app/revanced/patcher/patch/PatchContext {
public final fun delete (Ljava/lang/String;)Z
public final fun deleteSplit (Ljava/lang/String;Ljava/lang/String;)V
public final fun document (Ljava/io/InputStream;)Lapp/revanced/patcher/util/Document;
public final fun document (Ljava/lang/String;)Lapp/revanced/patcher/util/Document;
public fun get ()Lapp/revanced/patcher/PatchesResult$PatchedResources;
public synthetic fun get ()Ljava/lang/Object;
public final fun get (Ljava/lang/String;Z)Ljava/io/File;
public static synthetic fun get$default (Lapp/revanced/patcher/patch/ResourcePatchContext;Ljava/lang/String;ZILjava/lang/Object;)Ljava/io/File;
public final fun split (Ljava/lang/String;Ljava/lang/String;Z)Ljava/io/File;
public static synthetic fun split$default (Lapp/revanced/patcher/patch/ResourcePatchContext;Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)Ljava/io/File;
public final fun splitDocument (Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/patcher/util/Document;
}

public final class app/revanced/patcher/util/Document : java/io/Closeable, org/w3c/dom/Document {
Expand Down
28 changes: 28 additions & 0 deletions patcher/src/commonMain/kotlin/app/revanced/patcher/Apk.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package app.revanced.patcher

import java.io.File

/**
* An APK to patch.
*
* @param file The base APK file. This is the source of truth for the manifest, package metadata, and primary dex files.
*/
sealed class Apk(val file: File) {
/**
* A single monolithic APK.
*
* @param file The APK file.
*/
class Single(file: File) : Apk(file)

/**
* A split APK bundle.
*
* @param baseApk The base APK file.
* @param splitApkFiles The split APK files, keyed by split name.
*/
class Split(
baseApk: File,
val splitApkFiles: Map<String, File>,
) : Apk(baseApk)
}
22 changes: 18 additions & 4 deletions patcher/src/commonMain/kotlin/app/revanced/patcher/Patching.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ fun patcher(
aaptBinaryPath: File? = null,
frameworkFileDirectory: String? = null,
getPatches: (packageName: String, versionName: String) -> Set<Patch>,
) = patcher(Apk.Single(apkFile), temporaryFilesPath, aaptBinaryPath, frameworkFileDirectory, getPatches)

fun patcher(
apk: Apk,
temporaryFilesPath: File = File("revanced-patcher-temporary-files"),
aaptBinaryPath: File? = null,
frameworkFileDirectory: String? = null,
getPatches: (packageName: String, versionName: String) -> Set<Patch>,
): (emit: (PatchResult) -> Unit) -> PatchesResult {
val logger = Logger.getLogger("Patcher")

Expand All @@ -25,12 +33,14 @@ fun patcher(
}

val apkFilesPath = temporaryFilesPath.kmpResolve("apk").also { it.mkdirs() }
val splitApkFilesPath = temporaryFilesPath.kmpResolve("splits").also { it.mkdirs() }
val patchedFilesPath = temporaryFilesPath.kmpResolve("patched").also { it.mkdirs() }

val resourcePatchContext =
ResourcePatchContext(
apkFile,
apk,
apkFilesPath,
splitApkFilesPath,
patchedFilesPath,
aaptBinaryPath,
frameworkFileDirectory,
Expand All @@ -45,7 +55,7 @@ fun patcher(
// After initializing the resource context, to keep memory usage time low.
val bytecodePatchContext =
BytecodePatchContext(
apkFile,
apk,
patchedFilesPath,
)

Expand Down Expand Up @@ -121,18 +131,22 @@ fun Set<Patch>.apply(
)
}

return PatchesResult(bytecodePatchContext.get(), resourcePatchContext.get())
val (baseResources, splitResources) = resourcePatchContext.getCompiledResources()

return PatchesResult(bytecodePatchContext.get(), baseResources, splitResources)
}

/**
* The result of applying patches.
*
* @param dexFiles The patched dex files.
* @param resources The patched resources.
* @param resources The patched base APK resources.
* @param splitResources The patched resources for each split APK, keyed by split name.
*/
class PatchesResult internal constructor(
val dexFiles: Set<PatchedDexFile>,
val resources: PatchedResources?,
val splitResources: Map<String, PatchedResources> = emptyMap(),
) {
/**
* A dex file.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableClassDef.Comp
import app.revanced.java.io.kmpDeleteRecursively
import app.revanced.java.io.kmpInputStream
import app.revanced.java.io.kmpResolve
import app.revanced.patcher.Apk
import app.revanced.patcher.PatchesResult
import app.revanced.patcher.extensions.instructionsOrNull
import app.revanced.patcher.extensions.string
Expand All @@ -27,12 +28,12 @@ import kotlin.reflect.jvm.jvmName
/**
* A context for patches containing the current state of the bytecode.
*
* @param apkFile The apk [File] to patch.
* @param apk The [Apk] to patch.
* @param patchedFilesPath The path to the temporary apk files directory.
*/
@Suppress("MemberVisibilityCanBePrivate")
class BytecodePatchContext internal constructor(
internal val apkFile: File,
internal val apk: Apk,
internal val patchedFilesPath: File,
) : PatchContext<Set<PatchesResult.PatchedDexFile>> {
private val logger = Logger.getLogger(this::class.jvmName)
Expand All @@ -56,15 +57,7 @@ class BytecodePatchContext internal constructor(
// private val _methodsWithString = methodsByString.values.flatten().toMutableSet()
// val methodsWithString: Set<Method> = _methodsWithString

constructor() : this(
MultiDexIO.readDexFile(
true,
apkFile,
BasicDexFileNamer(),
null,
null,
),
)
constructor() : this(readDexFiles(apk))

internal val opcodes = dexFile.opcodes

Expand Down Expand Up @@ -287,3 +280,38 @@ class BytecodePatchContext internal constructor(
return patchedDexFileResults
}
}

private fun readDexFiles(apk: Apk): DexFile {
val baseDex = MultiDexIO.readDexFile(
true,
apk.file,
BasicDexFileNamer(),
null,
null,
)

if (apk !is Apk.Split) return baseDex

val allClasses = baseDex.classes.toMutableSet()

for ((_, splitFile) in apk.splitApkFiles) {
val splitDex = try {
MultiDexIO.readDexFile(
true,
splitFile,
BasicDexFileNamer(),
null,
null,
)
} catch (_: lanchon.multidexlib2.EmptyMultiDexContainerException) {
// Config splits (ABI, density, language) contain no dex files.
continue
}
allClasses += splitDex.classes
}

return object : DexFile {
override fun getClasses() = allClasses
override fun getOpcodes() = baseDex.opcodes
}
}
Loading