add: deploy to adb

Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
oSumAtrIX 2022-05-05 01:43:35 +02:00
parent eab58aa0a2
commit de2d29c464
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
7 changed files with 170 additions and 46 deletions

View File

@ -27,6 +27,9 @@ dependencies {
implementation("app.revanced:revanced-patcher:+") implementation("app.revanced:revanced-patcher:+")
implementation(patchesDependency) implementation(patchesDependency)
implementation("info.picocli:picocli:+") implementation("info.picocli:picocli:+")
implementation("me.tongfei:progressbar:+")
implementation("com.github.li-wjohnson:jadb:master-SNAPSHOT") // using a fork instead.
implementation("org.bouncycastle:bcpkix-jdk15on:+") implementation("org.bouncycastle:bcpkix-jdk15on:+")
} }

View File

@ -2,6 +2,7 @@ package app.revanced.cli
import app.revanced.patch.PatchLoader import app.revanced.patch.PatchLoader
import app.revanced.patch.Patches import app.revanced.patch.Patches
import app.revanced.utils.adb.Adb
import picocli.CommandLine import picocli.CommandLine
import picocli.CommandLine.* import picocli.CommandLine.*
import java.io.File import java.io.File
@ -10,15 +11,15 @@ import java.io.File
name = "ReVanced-CLI", version = ["1.0.0"], mixinStandardHelpOptions = true name = "ReVanced-CLI", version = ["1.0.0"], mixinStandardHelpOptions = true
) )
internal object MainCommand : Runnable { internal object MainCommand : Runnable {
@Option(names = ["-p", "--patches"], description = ["One or more bundles of patches"])
internal var patchBundles = arrayOf<File>()
@Parameters( @Parameters(
paramLabel = "INCLUDE", paramLabel = "INCLUDE",
description = ["Which patches to include. If none is specified, all compatible patches will be included"] description = ["Which patches to include. If none is specified, all compatible patches will be included"]
) )
internal var includedPatches = arrayOf<String>() internal var includedPatches = arrayOf<String>()
@Option(names = ["-p", "--patches"], description = ["One or more bundles of patches"])
internal var patchBundles = arrayOf<File>()
@Option(names = ["-c", "--cache"], description = ["Output resource cache directory"], required = true) @Option(names = ["-c", "--cache"], description = ["Output resource cache directory"], required = true)
internal lateinit var cacheDirectory: String internal lateinit var cacheDirectory: String
@ -41,6 +42,10 @@ internal object MainCommand : Runnable {
@Option(names = ["-o", "--out"], description = ["Output file path"], required = true) @Option(names = ["-o", "--out"], description = ["Output file path"], required = true)
internal lateinit var outputPath: String internal lateinit var outputPath: String
@Option(names = ["-d", "--deploy-on"], description = ["If specified, deploy to adb device with given name"])
internal var deploy: String? = null
override fun run() { override fun run() {
if (listOnly) { if (listOnly) {
patchBundles.forEach { patchBundles.forEach {
@ -52,10 +57,23 @@ internal object MainCommand : Runnable {
return return
} }
Patcher.run() val patcher = app.revanced.patcher.Patcher(
inputFile,
cacheDirectory,
patchResources
)
Patcher.start(patcher)
if (!wipe) return if (!wipe) return
File(cacheDirectory).deleteRecursively() File(cacheDirectory).deleteRecursively()
deploy?.let {
Adb(
File(outputPath),
patcher.packageName,
deploy!!
).deploy()
}
} }
} }

View File

@ -10,13 +10,7 @@ import java.io.File
internal class Patcher { internal class Patcher {
internal companion object { internal companion object {
internal fun run() { internal fun start(patcher: app.revanced.patcher.Patcher) {
val patcher = app.revanced.patcher.Patcher(
MainCommand.inputFile,
MainCommand.cacheDirectory,
MainCommand.patchResources
)
// merge files like necessary integrations // merge files like necessary integrations
patcher.addFiles(MainCommand.mergeFiles) patcher.addFiles(MainCommand.mergeFiles)
// add patches, but filter incompatible or excluded patches // add patches, but filter incompatible or excluded patches

View File

@ -1,35 +0,0 @@
package app.revanced.utils
// TODO: make this a class with PACKAGE_NAME as argument, then use that everywhere.
// make sure to remove the "const" from all the vals, they won't compile obviously.
internal object Scripts {
private const val PACKAGE_NAME = "com.google.android.apps.youtube.music"
private const val DATA_PATH = "/data/adb/ReVanced"
internal const val APK_PATH = "/sdcard/base.apk"
internal const val SCRIPT_PATH = "/sdcard/mount.sh"
internal val MOUNT_SCRIPT =
"""
base_path="$DATA_PATH/base.apk"
stock_path=${'$'}{ pm path $PACKAGE_NAME | grep base | sed 's/package://g' }
umount -l ${'$'}stock_path
rm ${'$'}base_path
mv "$APK_PATH" ${'$'}base_path
chmod 644 ${'$'}base_path
chown system:system ${'$'}base_path
chcon u:object_r:apk_data_file:s0 ${'$'}base_path
mount -o bind ${'$'}base_path ${'$'}stock_path
""".trimIndent()
internal const val PIDOF_APP_COMMAND = "pidof -s $PACKAGE_NAME"
private const val PIDOF_APP = "\$($PIDOF_APP_COMMAND)"
internal const val CREATE_DIR_COMMAND = "su -c \"mkdir -p $DATA_PATH/\""
internal const val MV_MOUNT_COMMAND = "su -c \"mv /sdcard/mount.sh $DATA_PATH/\""
internal const val CHMOD_MOUNT_COMMAND = "su -c \"chmod +x $DATA_PATH/mount.sh\""
internal const val START_MOUNT_COMMAND = "su -c $DATA_PATH/mount.sh"
internal const val UNMOUNT_COMMAND =
"su -c \"umount -l $(pm path $PACKAGE_NAME | grep base | sed 's/package://g')\""
internal const val LOGCAT_COMMAND = "su -c \"logcat -c && logcat --pid=$PIDOF_APP\""
internal const val STOP_APP_COMMAND = "su -c \"kill $PIDOF_APP\""
internal const val START_APP_COMMAND = "monkey -p $PACKAGE_NAME 1"
}

View File

@ -0,0 +1,85 @@
package app.revanced.utils.adb
import se.vidstige.jadb.JadbConnection
import se.vidstige.jadb.JadbDevice
import java.io.File
import java.util.concurrent.Executors
internal class Adb(
private val apk: File,
private val packageName: String,
deviceName: String,
private val logging: Boolean = true
) {
private val device: JadbDevice
init {
device = JadbConnection().devices.find { it.serial == deviceName }
?: throw IllegalArgumentException("No such device with name $deviceName")
if (device.run("su -h") == 0)
throw IllegalArgumentException("Root required on $deviceName. Deploying failed.")
}
internal fun deploy() {
// create revanced path
device.run(Constants.COMMAND_CREATE_DIR + Constants.PATH_DATA)
// create mount script
device.createFile(
Constants.PATH_INIT_PUSH,
Constants.CONTENT_MOUNT_SCRIPT.replace(Constants.PLACEHOLDER, packageName)
)
// move the mount script to the revanced path
device.run(Constants.COMMAND_MOVE_MOUNT)
// make the mount script executable
device.run(Constants.COMMAND_CHMOD_MOUNT + Constants.NAME_MOUNT_SCRIPT)
// push patched file
device.copy(Constants.PATH_INIT_PUSH, apk)
// move patched file to revanced path
device.run(Constants.COMMAND_MOVE_BASE)
// kill, mount & run app
device.run(Constants.COMMAND_KILL_APP.replace(Constants.PLACEHOLDER, packageName))
device.run(Constants.COMMAND_MOUNT)
device.run(Constants.COMMAND_RUN_APP.replace(Constants.PLACEHOLDER, packageName))
// log the app
log()
// unmount it, after it closes
device.run(Constants.COMMAND_UNMOUNT.replace(Constants.PLACEHOLDER, packageName))
}
private fun log() {
val executor = Executors.newSingleThreadExecutor()
val pipe = if (logging) {
ProcessBuilder.Redirect.INHERIT
} else {
ProcessBuilder.Redirect.PIPE
}
val process = device.buildCommand(Constants.COMMAND_LOGCAT.replace(Constants.PLACEHOLDER, packageName))
.redirectOutput(pipe)
.redirectError(pipe)
.useExecutor(executor)
.start()
Thread.sleep(250) // give the app some time to start up.
while (true) {
try {
while (device.run(Constants.COMMAND_PID_OF + packageName) == 0) {
Thread.sleep(1000)
}
break
} catch (e: Exception) {
throw RuntimeException("An error occurred while monitoring state of app", e)
}
}
println("App closed, continuing.")
process.destroy()
executor.shutdown()
}
}

View File

@ -0,0 +1,25 @@
package app.revanced.utils.adb
import se.vidstige.jadb.JadbDevice
import se.vidstige.jadb.RemoteFile
import se.vidstige.jadb.ShellProcessBuilder
import java.io.File
internal fun JadbDevice.buildCommand(command: String, su: Boolean = true): ShellProcessBuilder {
val args = command.split(" ") as ArrayList<String>
val cmd = args.removeFirst()
return shellProcessBuilder(cmd, *args.toTypedArray())
}
internal fun JadbDevice.run(command: String, su: Boolean = true): Int {
return this.buildCommand(command).start().waitFor()
}
internal fun JadbDevice.copy(targetPath: String, file: File) {
push(file, RemoteFile(targetPath))
}
internal fun JadbDevice.createFile(targetFile: String, content: String, su: Boolean = true) {
push(content.byteInputStream(), System.currentTimeMillis(), 644, RemoteFile(targetFile))
}

View File

@ -0,0 +1,34 @@
package app.revanced.utils.adb
internal object Constants {
internal const val PLACEHOLDER = "TEMPLATE_PACKAGE_NAME"
internal const val NAME_MOUNT_SCRIPT = "mount.sh"
internal const val PATH_DATA = "/data/adb/revanced/"
internal const val PATH_INIT_PUSH = "/sdcard/revanced"
internal const val COMMAND_PID_OF = "pidof -s "
internal const val COMMAND_CREATE_DIR = "mkdir -p "
internal const val COMMAND_MOVE_BASE = "mv $PATH_INIT_PUSH $PATH_DATA/base.apk"
internal const val COMMAND_MOVE_MOUNT = "mv $PATH_INIT_PUSH $PATH_DATA/$NAME_MOUNT_SCRIPT"
internal const val COMMAND_CHMOD_MOUNT = "chmod +x $PATH_DATA"
internal const val COMMAND_MOUNT = "./$PATH_DATA/$NAME_MOUNT_SCRIPT"
internal const val COMMAND_UNMOUNT = "umount -l $(pm path $PLACEHOLDER | grep base | sed 's/package://g')"
internal const val COMMAND_LOGCAT = "logcat -c && logcat --pid=$($COMMAND_PID_OF $PLACEHOLDER)"
internal const val COMMAND_RUN_APP = "monkey -p $PLACEHOLDER 1"
internal const val COMMAND_KILL_APP = "kill \$($COMMAND_PID_OF $PLACEHOLDER)"
internal val CONTENT_MOUNT_SCRIPT =
"""
base_path="$PATH_DATA/base.apk"
stock_path=${'$'}{ pm path $PLACEHOLDER | grep base | sed 's/package://g' }
umount -l ${'$'}stock_path
rm ${'$'}base_path
mv "$PATH_INIT_PUSH" ${'$'}base_path
chmod 644 ${'$'}base_path
chown system:system ${'$'}base_path
chcon u:object_r:apk_data_file:s0 ${'$'}base_path
mount -o bind ${'$'}base_path ${'$'}stock_path
""".trimIndent()
}