diff --git a/src/main/kotlin/app/revanced/cli/MainCommand.kt b/src/main/kotlin/app/revanced/cli/MainCommand.kt index 659f31d..29afa52 100644 --- a/src/main/kotlin/app/revanced/cli/MainCommand.kt +++ b/src/main/kotlin/app/revanced/cli/MainCommand.kt @@ -2,7 +2,7 @@ package app.revanced.cli import app.revanced.patch.PatchLoader import app.revanced.patch.Patches -import picocli.CommandLine +import app.revanced.utils.adb.Adb import picocli.CommandLine.* import java.io.File @@ -10,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() - @Parameters( paramLabel = "INCLUDE", description = ["Which patches to include. If none is specified, all compatible patches will be included"] ) internal var includedPatches = arrayOf() - @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() + + @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 @@ -44,7 +46,6 @@ internal object MainCommand : Runnable { @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 { @@ -61,17 +62,23 @@ internal object MainCommand : Runnable { cacheDirectory, patchResources ) + Patcher.start(patcher) - if (!wipe) return - File(cacheDirectory).deleteRecursively() + if (clean) { + File(cacheDirectory).deleteRecursively() + } + + val outputFile = File(outputPath) deploy?.let { Adb( - File(outputPath), + outputFile, patcher.packageName, deploy!! ).deploy() } + + if (clean) outputFile.delete() } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/utils/adb/Adb.kt b/src/main/kotlin/app/revanced/utils/adb/Adb.kt index a93a3cc..c03db28 100644 --- a/src/main/kotlin/app/revanced/utils/adb/Adb.kt +++ b/src/main/kotlin/app/revanced/utils/adb/Adb.kt @@ -17,40 +17,49 @@ internal class Adb( device = JadbConnection().devices.find { it.serial == deviceName } ?: throw IllegalArgumentException("No such device with name $deviceName") - if (device.run("su -h") == 0) + 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_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) + device.run("${Constants.COMMAND_CREATE_DIR} ${Constants.PATH_REVANCED}") // push patched file device.copy(Constants.PATH_INIT_PUSH, apk) - // move patched file to revanced path - device.run(Constants.COMMAND_MOVE_BASE) + // install apk + device.run(Constants.COMMAND_INSTALL_APK.replacePlaceholder()) - // 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)) + // 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() - - // unmount it, after it closes - device.run(Constants.COMMAND_UNMOUNT.replace(Constants.PLACEHOLDER, packageName)) } private fun log() { @@ -61,16 +70,16 @@ internal class Adb( ProcessBuilder.Redirect.PIPE } - val process = device.buildCommand(Constants.COMMAND_LOGCAT.replace(Constants.PLACEHOLDER, packageName)) + val process = device.buildCommand(Constants.COMMAND_LOGCAT.replacePlaceholder()) .redirectOutput(pipe) .redirectError(pipe) .useExecutor(executor) .start() - Thread.sleep(250) // give the app some time to start up. + Thread.sleep(500) // give the app some time to start up. while (true) { try { - while (device.run(Constants.COMMAND_PID_OF + packageName) == 0) { + while (device.run("${Constants.COMMAND_PID_OF} $packageName") == 0) { Thread.sleep(1000) } break diff --git a/src/main/kotlin/app/revanced/utils/adb/Commands.kt b/src/main/kotlin/app/revanced/utils/adb/Commands.kt index af23c27..1b3af07 100644 --- a/src/main/kotlin/app/revanced/utils/adb/Commands.kt +++ b/src/main/kotlin/app/revanced/utils/adb/Commands.kt @@ -6,6 +6,10 @@ 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 val cmd = args.removeFirst() @@ -13,13 +17,13 @@ internal fun JadbDevice.buildCommand(command: String, su: Boolean = true): Shell } internal fun JadbDevice.run(command: String, su: Boolean = true): Int { - return this.buildCommand(command).start().waitFor() + 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, su: Boolean = true) { +internal fun JadbDevice.createFile(targetFile: String, content: String) { push(content.byteInputStream(), System.currentTimeMillis(), 644, RemoteFile(targetFile)) } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/utils/adb/Constants.kt b/src/main/kotlin/app/revanced/utils/adb/Constants.kt index 39b74c8..32f7bf1 100644 --- a/src/main/kotlin/app/revanced/utils/adb/Constants.kt +++ b/src/main/kotlin/app/revanced/utils/adb/Constants.kt @@ -1,34 +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" - 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')" + // 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_RUN_APP = "monkey -p $PLACEHOLDER 1" - internal const val COMMAND_KILL_APP = "kill \$($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 = """ - base_path="$PATH_DATA/base.apk" + #!/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' } - 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() } \ No newline at end of file