mirror of
https://github.com/revanced/revanced-cli.git
synced 2024-11-03 18:33:55 +01:00
Merge pull request #6 from ReVancedTeam/adb-deploy
add: deploy to `adb`
This commit is contained in:
commit
f4a7860bb3
@ -27,6 +27,9 @@ dependencies {
|
||||
implementation("app.revanced:revanced-patcher:+")
|
||||
implementation(patchesDependency)
|
||||
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:+")
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package app.revanced.cli
|
||||
|
||||
import app.revanced.patch.PatchLoader
|
||||
import app.revanced.patch.Patches
|
||||
import app.revanced.utils.adb.Adb
|
||||
import picocli.CommandLine.*
|
||||
import java.io.File
|
||||
|
||||
@ -9,24 +10,26 @@ import java.io.File
|
||||
name = "ReVanced-CLI", version = ["1.0.0"], mixinStandardHelpOptions = true
|
||||
)
|
||||
internal object MainCommand : Runnable {
|
||||
@Option(names = ["-p", "--patches"], description = ["One or more bundles of patches"])
|
||||
internal var patchBundles = arrayOf<File>()
|
||||
|
||||
@Parameters(
|
||||
paramLabel = "INCLUDE",
|
||||
description = ["Which patches to include. If none is specified, all compatible patches will be included"]
|
||||
)
|
||||
internal var includedPatches = arrayOf<String>()
|
||||
|
||||
@Option(names = ["-c", "--cache"], description = ["Output resource cache directory"], required = true)
|
||||
@Option(names = ["-p", "--patches"], description = ["One or more bundles of patches"])
|
||||
internal var patchBundles = arrayOf<File>()
|
||||
|
||||
@Option(names = ["-t", "--temp-dir"], description = ["Temporal resource cache directory"], required = true)
|
||||
internal lateinit var cacheDirectory: String
|
||||
|
||||
@Option(names = ["-r", "--resource-patcher"], description = ["Enable patching resources"])
|
||||
internal var patchResources: Boolean = false
|
||||
|
||||
@Option(names = ["-w", "--wipe-after"], description = ["Wipe the temporal directory before exiting the patcher"])
|
||||
internal var wipe: Boolean = false
|
||||
|
||||
@Option(
|
||||
names = ["-c", "--clean"],
|
||||
description = ["Clean the temporal resource cache directory. This will be done anyways when running the patcher"]
|
||||
)
|
||||
internal var clean: Boolean = false
|
||||
|
||||
@Option(names = ["-l", "--list"], description = ["List patches only"])
|
||||
internal var listOnly: Boolean = false
|
||||
@ -40,6 +43,9 @@ internal object MainCommand : Runnable {
|
||||
@Option(names = ["-o", "--out"], description = ["Output file path"], required = true)
|
||||
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() {
|
||||
if (listOnly) {
|
||||
patchBundles.forEach {
|
||||
@ -51,9 +57,28 @@ internal object MainCommand : Runnable {
|
||||
return
|
||||
}
|
||||
|
||||
Patcher.run()
|
||||
val patcher = app.revanced.patcher.Patcher(
|
||||
inputFile,
|
||||
cacheDirectory,
|
||||
patchResources
|
||||
)
|
||||
|
||||
if (!wipe) return
|
||||
Patcher.start(patcher)
|
||||
|
||||
if (clean) {
|
||||
File(cacheDirectory).deleteRecursively()
|
||||
}
|
||||
|
||||
val outputFile = File(outputPath)
|
||||
|
||||
deploy?.let {
|
||||
Adb(
|
||||
outputFile,
|
||||
patcher.packageName,
|
||||
deploy!!
|
||||
).deploy()
|
||||
}
|
||||
|
||||
if (clean) outputFile.delete()
|
||||
}
|
||||
}
|
@ -10,13 +10,7 @@ import java.io.File
|
||||
|
||||
internal class Patcher {
|
||||
internal companion object {
|
||||
internal fun run() {
|
||||
val patcher = app.revanced.patcher.Patcher(
|
||||
MainCommand.inputFile,
|
||||
MainCommand.cacheDirectory,
|
||||
MainCommand.patchResources
|
||||
)
|
||||
|
||||
internal fun start(patcher: app.revanced.patcher.Patcher) {
|
||||
// merge files like necessary integrations
|
||||
patcher.addFiles(MainCommand.mergeFiles)
|
||||
// add patches, but filter incompatible or excluded patches
|
||||
|
@ -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"
|
||||
}
|
94
src/main/kotlin/app/revanced/utils/adb/Adb.kt
Normal file
94
src/main/kotlin/app/revanced/utils/adb/Adb.kt
Normal file
@ -0,0 +1,94 @@
|
||||
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", false) != 0)
|
||||
throw IllegalArgumentException("Root required on $deviceName. Deploying failed.")
|
||||
}
|
||||
|
||||
private fun String.replacePlaceholder(): String {
|
||||
return this.replace(Constants.PLACEHOLDER, packageName)
|
||||
}
|
||||
|
||||
internal fun deploy() {
|
||||
// create revanced path
|
||||
device.run("${Constants.COMMAND_CREATE_DIR} ${Constants.PATH_REVANCED}")
|
||||
|
||||
// push patched file
|
||||
device.copy(Constants.PATH_INIT_PUSH, apk)
|
||||
// install apk
|
||||
device.run(Constants.COMMAND_INSTALL_APK.replacePlaceholder())
|
||||
|
||||
// push mount script
|
||||
device.createFile(
|
||||
Constants.PATH_INIT_PUSH,
|
||||
Constants.CONTENT_MOUNT_SCRIPT.replacePlaceholder()
|
||||
)
|
||||
// install mount script
|
||||
device.run(Constants.COMMAND_INSTALL_MOUNT.replacePlaceholder())
|
||||
|
||||
// push umount script
|
||||
device.createFile(
|
||||
Constants.PATH_INIT_PUSH,
|
||||
Constants.CONTENT_UMOUNT_SCRIPT.replacePlaceholder()
|
||||
)
|
||||
// install mount script
|
||||
device.run(Constants.COMMAND_INSTALL_UMOUNT.replacePlaceholder())
|
||||
|
||||
// unmount the apk for sanity
|
||||
device.run(Constants.PATH_UMOUNT.replacePlaceholder())
|
||||
// mount the apk
|
||||
device.run(Constants.PATH_MOUNT.replacePlaceholder())
|
||||
|
||||
// relaunch app
|
||||
device.run(Constants.COMMAND_RESTART.replacePlaceholder())
|
||||
|
||||
// log the app
|
||||
log()
|
||||
}
|
||||
|
||||
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.replacePlaceholder())
|
||||
.redirectOutput(pipe)
|
||||
.redirectError(pipe)
|
||||
.useExecutor(executor)
|
||||
.start()
|
||||
|
||||
Thread.sleep(500) // 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()
|
||||
}
|
||||
}
|
29
src/main/kotlin/app/revanced/utils/adb/Commands.kt
Normal file
29
src/main/kotlin/app/revanced/utils/adb/Commands.kt
Normal file
@ -0,0 +1,29 @@
|
||||
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 {
|
||||
if (su) {
|
||||
return shellProcessBuilder("su -c \'$command\'")
|
||||
}
|
||||
|
||||
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, su).start().waitFor()
|
||||
}
|
||||
|
||||
internal fun JadbDevice.copy(targetPath: String, file: File) {
|
||||
push(file, RemoteFile(targetPath))
|
||||
}
|
||||
|
||||
internal fun JadbDevice.createFile(targetFile: String, content: String) {
|
||||
push(content.byteInputStream(), System.currentTimeMillis(), 644, RemoteFile(targetFile))
|
||||
}
|
57
src/main/kotlin/app/revanced/utils/adb/Constants.kt
Normal file
57
src/main/kotlin/app/revanced/utils/adb/Constants.kt
Normal file
@ -0,0 +1,57 @@
|
||||
package app.revanced.utils.adb
|
||||
|
||||
internal object Constants {
|
||||
// template placeholder to replace a string in commands
|
||||
internal const val PLACEHOLDER = "TEMPLATE_PACKAGE_NAME"
|
||||
|
||||
// utility commands
|
||||
private const val COMMAND_CHMOD_MOUNT = "chmod +x"
|
||||
internal const val COMMAND_PID_OF = "pidof -s"
|
||||
internal const val COMMAND_CREATE_DIR = "mkdir -p"
|
||||
internal const val COMMAND_LOGCAT = "logcat -c && logcat --pid=$($COMMAND_PID_OF $PLACEHOLDER)"
|
||||
internal const val COMMAND_RESTART = "monkey -p $PLACEHOLDER 1 && kill ${'$'}($COMMAND_PID_OF $PLACEHOLDER)"
|
||||
|
||||
// default mount file name
|
||||
private const val NAME_MOUNT_SCRIPT = "mount_$PLACEHOLDER.sh"
|
||||
|
||||
// initial directory to push files to via adb push
|
||||
internal const val PATH_INIT_PUSH = "/sdcard/revanced.delete"
|
||||
|
||||
// revanced path
|
||||
internal const val PATH_REVANCED = "/data/adb/revanced/"
|
||||
|
||||
// revanced apk path
|
||||
private const val PATH_REVANCED_APP = "$PATH_REVANCED$PLACEHOLDER.apk"
|
||||
|
||||
// (un)mount script paths
|
||||
internal const val PATH_MOUNT = "/data/adb/service.d/$NAME_MOUNT_SCRIPT"
|
||||
internal const val PATH_UMOUNT = "/data/adb/post-fs-data.d/un$NAME_MOUNT_SCRIPT"
|
||||
|
||||
// move to revanced apk path & set permissions
|
||||
internal const val COMMAND_INSTALL_APK =
|
||||
"base_path=\"$PATH_REVANCED_APP\" && 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"
|
||||
|
||||
// install mount script & set permissions
|
||||
internal const val COMMAND_INSTALL_MOUNT = "mv $PATH_INIT_PUSH $PATH_MOUNT && $COMMAND_CHMOD_MOUNT $PATH_MOUNT"
|
||||
|
||||
// install umount script & set permissions
|
||||
internal const val COMMAND_INSTALL_UMOUNT = "mv $PATH_INIT_PUSH $PATH_UMOUNT && $COMMAND_CHMOD_MOUNT $PATH_UMOUNT"
|
||||
|
||||
// unmount script
|
||||
internal val CONTENT_UMOUNT_SCRIPT =
|
||||
"""
|
||||
#!/system/bin/sh
|
||||
while read line; do echo ${'$'}{line} | grep $PLACEHOLDER | awk '{print ${'$'}2}' | xargs umount -l; done< /proc/mounts
|
||||
""".trimIndent()
|
||||
|
||||
// mount script
|
||||
internal val CONTENT_MOUNT_SCRIPT =
|
||||
"""
|
||||
#!/system/bin/sh
|
||||
while [ "${'$'}(getprop sys.boot_completed | tr -d '\r')" != "1" ]; do sleep 1; done
|
||||
|
||||
base_path="$PATH_REVANCED_APP"
|
||||
stock_path=${'$'}{ pm path $PLACEHOLDER | grep base | sed 's/package://g' }
|
||||
mount -o bind ${'$'}base_path ${'$'}stock_path
|
||||
""".trimIndent()
|
||||
}
|
Loading…
Reference in New Issue
Block a user