build: Bump ReVanced Patcher (#335)

This commit is contained in:
oSumAtrIX
2024-08-13 00:05:47 +04:00
committed by GitHub
parent 0a2e99c1c0
commit 54ae01cd76
10 changed files with 210 additions and 196 deletions

View File

@@ -3,7 +3,7 @@ package app.revanced.cli.command
import app.revanced.library.PackageName
import app.revanced.library.PatchUtils
import app.revanced.library.VersionMap
import app.revanced.patcher.PatchBundleLoader
import app.revanced.patcher.patch.loadPatchesFromJar
import picocli.CommandLine
import java.io.File
import java.util.logging.Logger
@@ -12,7 +12,7 @@ import java.util.logging.Logger
name = "list-versions",
description = [
"List the most common compatible versions of apps that are compatible " +
"with the patches in the supplied patch bundles.",
"with the patches in the supplied patch bundles.",
],
)
internal class ListCompatibleVersions : Runnable {
@@ -22,7 +22,7 @@ internal class ListCompatibleVersions : Runnable {
description = ["Paths to patch bundles."],
arity = "1..*",
)
private lateinit var patchBundles: Array<File>
private lateinit var patchBundles: Set<File>
@CommandLine.Option(
names = ["-f", "--filter-package-names"],
@@ -38,8 +38,6 @@ internal class ListCompatibleVersions : Runnable {
private var countUnusedPatches: Boolean = false
override fun run() {
val patches = PatchBundleLoader.Jar(*patchBundles)
fun VersionMap.buildVersionsString(): String {
if (isEmpty()) return "Any"
@@ -58,6 +56,8 @@ internal class ListCompatibleVersions : Runnable {
appendLine(versions.buildVersionsString().prependIndent("\t"))
}
val patches = loadPatchesFromJar(patchBundles)
PatchUtils.getMostCommonCompatibleVersions(
patches,
packageNames,

View File

@@ -1,12 +1,13 @@
package app.revanced.cli.command
import app.revanced.patcher.PatchBundleLoader
import app.revanced.patcher.patch.Package
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.options.PatchOption
import app.revanced.patcher.patch.loadPatchesFromJar
import picocli.CommandLine.*
import picocli.CommandLine.Help.Visibility.ALWAYS
import java.io.File
import java.util.logging.Logger
import app.revanced.patcher.patch.Option as PatchOption
@Command(
name = "list-patches",
@@ -19,7 +20,7 @@ internal object ListPatchesCommand : Runnable {
description = ["Paths to patch bundles."],
arity = "1..*",
)
private lateinit var patchBundles: Array<File>
private lateinit var patchBundles: Set<File>
@Option(
names = ["-d", "--with-descriptions"],
@@ -70,16 +71,19 @@ internal object ListPatchesCommand : Runnable {
private var packageName: String? = null
override fun run() {
fun Patch.CompatiblePackage.buildString() =
buildString {
fun Package.buildString(): String {
val (name, versions) = this
return buildString {
if (withVersions && versions != null) {
appendLine("Package name: $name")
appendLine("Compatible versions:")
append(versions!!.joinToString("\n") { version -> version }.prependIndent("\t"))
append(versions.joinToString("\n") { version -> version }.prependIndent("\t"))
} else {
append("Package name: $name")
}
}
}
fun PatchOption<*>.buildString() =
buildString {
@@ -126,10 +130,10 @@ internal object ListPatchesCommand : Runnable {
}
fun Patch<*>.filterCompatiblePackages(name: String) =
compatiblePackages?.any { it.name == name }
compatiblePackages?.any { (compatiblePackageName, _) -> compatiblePackageName == name }
?: withUniversalPatches
val patches = PatchBundleLoader.Jar(*patchBundles).withIndex().toList()
val patches = loadPatchesFromJar(patchBundles).withIndex().toList()
val filtered =
packageName?.let { patches.filter { (_, patch) -> patch.filterCompatiblePackages(it) } } ?: patches

View File

@@ -2,7 +2,7 @@ package app.revanced.cli.command
import app.revanced.library.Options
import app.revanced.library.Options.setOptions
import app.revanced.patcher.PatchBundleLoader
import app.revanced.patcher.patch.loadPatchesFromJar
import picocli.CommandLine
import picocli.CommandLine.Help.Visibility.ALWAYS
import java.io.File
@@ -19,7 +19,7 @@ internal object OptionsCommand : Runnable {
description = ["Paths to patch bundles."],
arity = "1..*",
)
private lateinit var patchBundles: Array<File>
private lateinit var patchBundles: Set<File>
@CommandLine.Option(
names = ["-p", "--path"],
@@ -44,7 +44,7 @@ internal object OptionsCommand : Runnable {
override fun run() =
try {
PatchBundleLoader.Jar(*patchBundles).let { patches ->
loadPatchesFromJar(patchBundles).let { patches ->
val exists = filePath.exists()
if (!exists || overwrite) {
if (exists && update) patches.setOptions(filePath)

View File

@@ -4,11 +4,11 @@ import app.revanced.library.ApkUtils
import app.revanced.library.ApkUtils.applyTo
import app.revanced.library.Options
import app.revanced.library.Options.setOptions
import app.revanced.library.adb.AdbManager
import app.revanced.patcher.PatchBundleLoader
import app.revanced.patcher.PatchSet
import app.revanced.library.installation.installer.*
import app.revanced.patcher.Patcher
import app.revanced.patcher.PatcherConfig
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.loadPatchesFromJar
import kotlinx.coroutines.runBlocking
import picocli.CommandLine
import picocli.CommandLine.Help.Visibility.ALWAYS
@@ -31,8 +31,6 @@ internal object PatchCommand : Runnable {
private lateinit var apk: File
private var integrations = setOf<File>()
private var patchBundles = emptySet<File>()
@CommandLine.Option(
@@ -121,16 +119,6 @@ internal object PatchCommand : Runnable {
)
private var keyStorePassword: String? = null // Empty password by default
@CommandLine.Option(
names = ["--alias"],
description = ["The alias of the keystore entry to sign the patched APK file with."],
showDefaultValue = ALWAYS,
)
private fun setKeyStoreEntryAlias(alias: String = "ReVanced Key") {
logger.warning("The --alias option is deprecated. Use --keystore-entry-alias instead.")
keyStoreEntryAlias = alias
}
@CommandLine.Option(
names = ["--keystore-entry-alias"],
description = ["The alias of the keystore entry to sign the patched APK file with."],
@@ -193,11 +181,8 @@ internal object PatchCommand : Runnable {
description = ["One or more DEX files or containers to merge into the APK."],
)
@Suppress("unused")
private fun setIntegrations(integrations: Array<File>) {
integrations.firstOrNull { !it.exists() }?.let {
throw CommandLine.ParameterException(spec.commandLine(), "Integrations file ${it.path} does not exist.")
}
this.integrations += integrations
private fun setIntegrations(integrations: Set<File>) {
logger.warning("The --merge option is not used anymore.")
}
@CommandLine.Option(
@@ -256,7 +241,7 @@ internal object PatchCommand : Runnable {
logger.info("Loading patches")
val patches = PatchBundleLoader.Jar(*patchBundles.toTypedArray())
val patches = loadPatchesFromJar(patchBundles)
// Warn if a patch can not be found in the supplied patch bundles.
if (warn) {
@@ -271,6 +256,7 @@ internal object PatchCommand : Runnable {
}
// endregion
val patcherTemporaryFilesPath = temporaryFilesPath.resolve("patcher")
val (packageName, patcherResult) = Patcher(
PatcherConfig(
@@ -292,28 +278,27 @@ internal object PatchCommand : Runnable {
}
}
// region Patch
patcher += filteredPatches
patcher.context.packageMetadata.packageName to patcher.apply {
acceptIntegrations(integrations)
acceptPatches(filteredPatches)
// Execute patches.
runBlocking {
patcher().collect { patchResult ->
val exception = patchResult.exception
?: return@collect logger.info("\"${patchResult.patch}\" succeeded")
// Execute patches.
runBlocking {
apply(false).collect { patchResult ->
patchResult.exception?.let {
StringWriter().use { writer ->
it.printStackTrace(PrintWriter(writer))
logger.severe("${patchResult.patch.name} failed:\n$writer")
}
} ?: logger.info("${patchResult.patch.name} succeeded")
StringWriter().use { writer ->
exception.printStackTrace(PrintWriter(writer))
logger.severe("\"${patchResult.patch}\" failed:\n$writer")
}
}
}.get()
// endregion
}
patcher.context.packageMetadata.packageName to patcher.get()
}
// region Save
apk.copyTo(temporaryFilesPath.resolve(apk.name), overwrite = true).apply {
patcherResult.applyTo(this)
}.let { patchedApkFile ->
@@ -340,9 +325,23 @@ internal object PatchCommand : Runnable {
// region Install
deviceSerial?.let { serial ->
AdbManager.getAdbManager(deviceSerial = serial.ifEmpty { null }, mount)
}?.install(AdbManager.Apk(outputFilePath, packageName))
deviceSerial?.let { it ->
val deviceSerial = it.ifEmpty { null }
runBlocking {
val result = if (mount) {
AdbRootInstaller(deviceSerial)
} else {
AdbInstaller(deviceSerial)
}.install(Installer.Apk(outputFilePath, packageName))
when (result) {
RootInstallerResult.FAILURE -> logger.severe("Failed to mount the patched APK file")
is AdbInstallerResult.Failure -> logger.severe(result.exception.toString())
else -> logger.info("Installed the patched APK file")
}
}
}
// endregion
@@ -358,7 +357,7 @@ internal object PatchCommand : Runnable {
* @param patches The patches to filter.
* @return The filtered patches.
*/
private fun Patcher.filterPatchSelection(patches: PatchSet): PatchSet =
private fun Patcher.filterPatchSelection(patches: Set<Patch<*>>): Set<Patch<*>> =
buildSet {
val packageName = context.packageMetadata.packageName
val packageVersion = context.packageMetadata.packageVersion
@@ -367,33 +366,36 @@ internal object PatchCommand : Runnable {
val patchName = patch.name!!
val explicitlyExcluded = excludedPatches.contains(patchName) || excludedPatchesByIndex.contains(i)
if (explicitlyExcluded) return@patch logger.info("Excluding $patchName")
if (explicitlyExcluded) return@patch logger.info("\"$patchName\" excluded manually")
// Make sure the patch is compatible with the supplied APK files package name and version.
patch.compatiblePackages?.let { packages ->
packages.singleOrNull { it.name == packageName }?.let { `package` ->
val matchesVersion =
force || `package`.versions?.let {
it.any { version -> version == packageVersion }
} ?: true
packages.singleOrNull { (name, _) -> name == packageName }?.let { (_, versions) ->
if (versions?.isEmpty() == true) {
return@patch logger.warning("\"$patchName\" incompatible with \"$packageName\"")
}
val matchesVersion = force ||
versions?.let { it.any { version -> version == packageVersion } }
?: true
if (!matchesVersion) {
return@patch logger.warning(
"$patchName is incompatible with version $packageVersion. " +
"This patch is only compatible with version " +
packages.joinToString(";") { pkg ->
pkg.versions!!.joinToString(", ")
"\"$patchName\" incompatible with $packageName $packageVersion " +
"but compatible with " +
packages.joinToString("; ") { (packageName, versions) ->
packageName + " " + versions!!.joinToString(", ")
},
)
}
} ?: return@patch logger.fine(
"$patchName is incompatible with $packageName. " +
"This patch is only compatible with " +
packages.joinToString(", ") { `package` -> `package`.name },
"\"$patchName\" incompatible with $packageName. " +
"It is only compatible with " +
packages.joinToString(", ") { (name, _) -> name },
)
return@let
} ?: logger.fine("$patchName has no constraint on packages.")
} ?: logger.fine("\"$patchName\" has no package constraints")
// If the patch is implicitly used, it will be only included if [exclusive] is false.
val implicitlyIncluded = !exclusive && patch.use
@@ -401,11 +403,11 @@ internal object PatchCommand : Runnable {
val explicitlyIncluded = includedPatches.contains(patchName) || includedPatchesByIndex.contains(i)
val included = implicitlyIncluded || explicitlyIncluded
if (!included) return@patch logger.info("$patchName excluded") // Case 1.
logger.fine("Adding $patchName")
if (!included) return@patch logger.info("\"$patchName\" excluded") // Case 1.
add(patch)
logger.fine("\"$patchName\" added")
}
}

View File

@@ -1,6 +1,9 @@
package app.revanced.cli.command.utility
import app.revanced.library.adb.AdbManager
import app.revanced.library.installation.installer.*
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import picocli.CommandLine.*
import java.io.File
import java.util.logging.Logger
@@ -32,13 +35,29 @@ internal object InstallCommand : Runnable {
private var packageName: String? = null
override fun run() {
fun install(deviceSerial: String? = null) =
try {
AdbManager.getAdbManager(deviceSerial, packageName != null).install(AdbManager.Apk(apk, packageName))
} catch (e: AdbManager.DeviceNotFoundException) {
suspend fun install(deviceSerial: String? = null) {
val result = try {
if (packageName != null) {
AdbRootInstaller(deviceSerial)
} else {
AdbInstaller(deviceSerial)
}.install(Installer.Apk(apk, packageName))
} catch (e: Exception) {
logger.severe(e.toString())
}
deviceSerials?.forEach(::install) ?: install()
when (result) {
RootInstallerResult.FAILURE ->
logger.severe("Failed to mount the APK file")
is AdbInstallerResult.Failure ->
logger.severe(result.exception.toString())
else ->
logger.info("Installed the APK file")
}
}
runBlocking {
deviceSerials?.map { async { install(it) } }?.awaitAll() ?: install()
}
}
}

View File

@@ -1,6 +1,9 @@
package app.revanced.cli.command.utility
import app.revanced.library.adb.AdbManager
import app.revanced.library.installation.installer.*
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import picocli.CommandLine.*
import picocli.CommandLine.Help.Visibility.ALWAYS
import java.util.logging.Logger
@@ -33,13 +36,28 @@ internal object UninstallCommand : Runnable {
private var unmount: Boolean = false
override fun run() {
fun uninstall(deviceSerial: String? = null) =
try {
AdbManager.getAdbManager(deviceSerial, unmount).uninstall(packageName)
} catch (e: AdbManager.DeviceNotFoundException) {
suspend fun uninstall(deviceSerial: String? = null) {
val result = try {
if (unmount) {
AdbRootInstaller(deviceSerial)
} else {
AdbInstaller(deviceSerial)
}.uninstall(packageName)
} catch (e: Exception) {
logger.severe(e.toString())
}
deviceSerials?.forEach { uninstall(it) } ?: uninstall()
when (result) {
RootInstallerResult.FAILURE ->
logger.severe("Failed to unmount the patched APK file")
is AdbInstallerResult.Failure ->
logger.severe(result.exception.toString())
else -> logger.info("Uninstalled the patched APK file")
}
}
runBlocking {
deviceSerials?.map { async { uninstall(it) } }?.awaitAll() ?: uninstall()
}
}
}