From edcf9f1b0c524d8b7150ffcef387f248e32f0972 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 13 Dec 2021 00:16:53 -0800 Subject: [PATCH] Introduce RootServices to the app --- app/build.gradle.kts | 3 +- .../topjohnwu/magisk/arch/BaseMainActivity.kt | 13 ++--- .../java/com/topjohnwu/magisk/core/App.kt | 17 +++++-- .../magisk/core/utils/IODispatcherExecutor.kt | 20 ++++---- .../magisk/core/utils/RootRegistry.kt | 47 +++++++++++++++++++ .../topjohnwu/magisk/core/utils/ShellInit.kt | 43 +++++++---------- 6 files changed, 97 insertions(+), 46 deletions(-) create mode 100644 app/src/main/java/com/topjohnwu/magisk/core/utils/RootRegistry.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4a15365bd..0b4b193be 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -94,9 +94,10 @@ dependencies { implementation("io.noties.markwon:image:${vMarkwon}") implementation("com.caverock:androidsvg:1.4") - val vLibsu = "3.2.0" + val vLibsu = "3.2.1" implementation("com.github.topjohnwu.libsu:core:${vLibsu}") implementation("com.github.topjohnwu.libsu:io:${vLibsu}") + implementation("com.github.topjohnwu.libsu:service:${vLibsu}") val vRetrofit = "2.9.0" implementation("com.squareup.retrofit2:retrofit:${vRetrofit}") diff --git a/app/src/main/java/com/topjohnwu/magisk/arch/BaseMainActivity.kt b/app/src/main/java/com/topjohnwu/magisk/arch/BaseMainActivity.kt index 16ddf444a..617abed44 100644 --- a/app/src/main/java/com/topjohnwu/magisk/arch/BaseMainActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/arch/BaseMainActivity.kt @@ -9,11 +9,9 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.databinding.ViewDataBinding import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.core.Config -import com.topjohnwu.magisk.core.Const -import com.topjohnwu.magisk.core.JobService -import com.topjohnwu.magisk.core.isRunningAsStub +import com.topjohnwu.magisk.core.* import com.topjohnwu.magisk.core.tasks.HideAPK +import com.topjohnwu.magisk.core.utils.RootRegistry import com.topjohnwu.magisk.di.ServiceLocator import com.topjohnwu.magisk.ui.theme.Theme import com.topjohnwu.magisk.view.MagiskDialog @@ -21,6 +19,7 @@ import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.magisk.view.Shortcuts import com.topjohnwu.superuser.Shell import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit abstract class BaseMainActivity : BaseUIActivity() { @@ -48,13 +47,15 @@ abstract class BaseMainActivity } if (doPreload) { - // Pre-initialize root shell Shell.getShell(null) { - if (isRunningAsStub && !Shell.rootAccess()) { + if (isRunningAsStub && !it.isRoot) { showInvalidStateMessage() return@getShell } preLoad() + if (it.isRoot) { + RootRegistry.Connection.await(2, TimeUnit.SECONDS) + } runOnUiThread { doPreload = false if (isRunningAsStub) { diff --git a/app/src/main/java/com/topjohnwu/magisk/core/App.kt b/app/src/main/java/com/topjohnwu/magisk/core/App.kt index 3853f231c..801522c4b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/App.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/App.kt @@ -7,13 +7,15 @@ import android.content.Context import android.content.res.Configuration import android.os.Bundle import com.topjohnwu.magisk.DynAPK -import com.topjohnwu.magisk.core.utils.AppShellInit -import com.topjohnwu.magisk.core.utils.BusyBoxInit import com.topjohnwu.magisk.core.utils.IODispatcherExecutor +import com.topjohnwu.magisk.core.utils.RootRegistry +import com.topjohnwu.magisk.core.utils.ShellInit import com.topjohnwu.magisk.core.utils.updateConfig import com.topjohnwu.magisk.di.ServiceLocator import com.topjohnwu.magisk.ktx.unwrap import com.topjohnwu.superuser.Shell +import com.topjohnwu.superuser.internal.UiThreadHandler +import com.topjohnwu.superuser.ipc.RootService import timber.log.Timber import kotlin.system.exitProcess @@ -26,7 +28,7 @@ open class App() : Application() { init { Shell.setDefaultBuilder(Shell.Builder.create() .setFlags(Shell.FLAG_MOUNT_MASTER) - .setInitializers(BusyBoxInit::class.java, AppShellInit::class.java) + .setInitializers(ShellInit::class.java) .setTimeout(2)) Shell.EXECUTOR = IODispatcherExecutor() @@ -57,6 +59,15 @@ open class App() : Application() { app.registerActivityLifecycleCallbacks(ForegroundTracker) } + override fun onCreate() { + super.onCreate() + RootRegistry.bindTask = RootService.createBindTask( + intent(), + UiThreadHandler.executor, + RootRegistry.Connection + ) + } + // This is required as some platforms expect ContextImpl override fun getBaseContext(): Context { return super.getBaseContext().unwrap() diff --git a/app/src/main/java/com/topjohnwu/magisk/core/utils/IODispatcherExecutor.kt b/app/src/main/java/com/topjohnwu/magisk/core/utils/IODispatcherExecutor.kt index c12f037d3..938a519bc 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/utils/IODispatcherExecutor.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/utils/IODispatcherExecutor.kt @@ -1,13 +1,19 @@ package com.topjohnwu.magisk.core.utils import kotlinx.coroutines.* -import java.util.concurrent.* +import java.util.concurrent.AbstractExecutorService +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit class IODispatcherExecutor : AbstractExecutorService() { - private val job = SupervisorJob().apply { invokeOnCompletion { future.run() } } + private val job = SupervisorJob() private val scope = CoroutineScope(job + Dispatchers.IO) - private val future = FutureTask(Callable { true }) + private val latch = CountDownLatch(1) + + init { + job.invokeOnCompletion { latch.countDown() } + } override fun execute(command: Runnable) { scope.launch { @@ -26,11 +32,5 @@ class IODispatcherExecutor : AbstractExecutorService() { override fun isTerminated() = job.isCancelled && job.isCompleted - override fun awaitTermination(timeout: Long, unit: TimeUnit): Boolean { - return try { - future.get(timeout, unit) - } catch (e: TimeoutException) { - false - } - } + override fun awaitTermination(timeout: Long, unit: TimeUnit) = latch.await(timeout, unit) } diff --git a/app/src/main/java/com/topjohnwu/magisk/core/utils/RootRegistry.kt b/app/src/main/java/com/topjohnwu/magisk/core/utils/RootRegistry.kt new file mode 100644 index 000000000..77853e5b6 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/core/utils/RootRegistry.kt @@ -0,0 +1,47 @@ +package com.topjohnwu.magisk.core.utils + +import android.content.ComponentName +import android.content.Intent +import android.content.ServiceConnection +import android.os.Binder +import android.os.IBinder +import com.topjohnwu.superuser.ipc.RootService +import timber.log.Timber +import java.util.concurrent.CountDownLatch +import kotlin.system.exitProcess + +class RootRegistry : RootService() { + + init { + // Always log full stack trace with Timber + Timber.plant(Timber.DebugTree()) + Thread.setDefaultUncaughtExceptionHandler { _, e -> + Timber.e(e) + exitProcess(1) + } + } + + override fun onBind(intent: Intent): IBinder { + // TODO: PLACEHOLDER + return Binder() + } + + // TODO: PLACEHOLDER + object Connection : CountDownLatch(1), ServiceConnection { + override fun onServiceConnected(name: ComponentName, service: IBinder) { + Timber.d("onServiceConnected") + countDown() + } + override fun onNullBinding(name: ComponentName) { + Timber.d("onServiceConnected") + countDown() + } + override fun onServiceDisconnected(name: ComponentName) { + bind(Intent().setComponent(name), this) + } + } + + companion object { + var bindTask: Runnable? = null + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/core/utils/ShellInit.kt b/app/src/main/java/com/topjohnwu/magisk/core/utils/ShellInit.kt index dc4a8d59f..8ab385f06 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/utils/ShellInit.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/utils/ShellInit.kt @@ -3,7 +3,10 @@ package com.topjohnwu.magisk.core.utils import android.content.Context import com.topjohnwu.magisk.DynAPK import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.core.* +import com.topjohnwu.magisk.core.Config +import com.topjohnwu.magisk.core.Const +import com.topjohnwu.magisk.core.Info +import com.topjohnwu.magisk.core.isRunningAsStub import com.topjohnwu.magisk.ktx.cachedFile import com.topjohnwu.magisk.ktx.deviceProtectedContext import com.topjohnwu.magisk.ktx.rawResource @@ -13,18 +16,12 @@ import com.topjohnwu.superuser.ShellUtils import java.io.File import java.util.jar.JarFile -abstract class BaseShellInit : Shell.Initializer() { - final override fun onInit(context: Context, shell: Shell): Boolean { - return init(context.wrap(), shell) - } - - abstract fun init(context: Context, shell: Shell): Boolean -} - - -class BusyBoxInit : BaseShellInit() { - - override fun init(context: Context, shell: Shell): Boolean { +class ShellInit : Shell.Initializer() { + override fun onInit(context: Context, shell: Shell): Boolean { + if (shell.isRoot) { + RootRegistry.bindTask?.run() + RootRegistry.bindTask = null + } shell.newJob().apply { add("export ASH_STANDALONE=1") @@ -63,20 +60,7 @@ class BusyBoxInit : BaseShellInit() { // Directly execute the file add("exec $localBB sh") } - }.exec() - return true - } -} -class AppShellInit : BaseShellInit() { - - override fun init(context: Context, shell: Shell): Boolean { - - fun fastCmd(cmd: String) = ShellUtils.fastCmd(shell, cmd) - fun getVar(name: String) = fastCmd("echo \$$name") - fun getBool(name: String) = getVar(name).toBoolean() - - shell.newJob().apply { add(context.rawResource(R.raw.manager)) if (shell.isRoot) { add(context.assets.open("util_functions.sh")) @@ -84,6 +68,10 @@ class AppShellInit : BaseShellInit() { add("app_init") }.exec() + fun fastCmd(cmd: String) = ShellUtils.fastCmd(shell, cmd) + fun getVar(name: String) = fastCmd("echo \$$name") + fun getBool(name: String) = getVar(name).toBoolean() + Const.MAGISKTMP = getVar("MAGISKTMP") Info.isSAR = getBool("SYSTEM_ROOT") Info.ramdisk = getBool("RAMDISKEXIST") @@ -95,6 +83,9 @@ class AppShellInit : BaseShellInit() { Config.keepVerity = getBool("KEEPVERITY") Config.keepEnc = getBool("KEEPFORCEENCRYPT") + // Pre-fetch env + Info.env + return true } }