Introduce RootServices to the app

This commit is contained in:
topjohnwu 2021-12-13 00:16:53 -08:00
parent de3747d65e
commit edcf9f1b0c
6 changed files with 97 additions and 46 deletions

View File

@ -94,9 +94,10 @@ dependencies {
implementation("io.noties.markwon:image:${vMarkwon}") implementation("io.noties.markwon:image:${vMarkwon}")
implementation("com.caverock:androidsvg:1.4") 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:core:${vLibsu}")
implementation("com.github.topjohnwu.libsu:io:${vLibsu}") implementation("com.github.topjohnwu.libsu:io:${vLibsu}")
implementation("com.github.topjohnwu.libsu:service:${vLibsu}")
val vRetrofit = "2.9.0" val vRetrofit = "2.9.0"
implementation("com.squareup.retrofit2:retrofit:${vRetrofit}") implementation("com.squareup.retrofit2:retrofit:${vRetrofit}")

View File

@ -9,11 +9,9 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.databinding.ViewDataBinding import androidx.databinding.ViewDataBinding
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.*
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.JobService
import com.topjohnwu.magisk.core.isRunningAsStub
import com.topjohnwu.magisk.core.tasks.HideAPK import com.topjohnwu.magisk.core.tasks.HideAPK
import com.topjohnwu.magisk.core.utils.RootRegistry
import com.topjohnwu.magisk.di.ServiceLocator import com.topjohnwu.magisk.di.ServiceLocator
import com.topjohnwu.magisk.ui.theme.Theme import com.topjohnwu.magisk.ui.theme.Theme
import com.topjohnwu.magisk.view.MagiskDialog 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.magisk.view.Shortcuts
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
abstract class BaseMainActivity<VM : BaseViewModel, Binding : ViewDataBinding> abstract class BaseMainActivity<VM : BaseViewModel, Binding : ViewDataBinding>
: BaseUIActivity<VM, Binding>() { : BaseUIActivity<VM, Binding>() {
@ -48,13 +47,15 @@ abstract class BaseMainActivity<VM : BaseViewModel, Binding : ViewDataBinding>
} }
if (doPreload) { if (doPreload) {
// Pre-initialize root shell
Shell.getShell(null) { Shell.getShell(null) {
if (isRunningAsStub && !Shell.rootAccess()) { if (isRunningAsStub && !it.isRoot) {
showInvalidStateMessage() showInvalidStateMessage()
return@getShell return@getShell
} }
preLoad() preLoad()
if (it.isRoot) {
RootRegistry.Connection.await(2, TimeUnit.SECONDS)
}
runOnUiThread { runOnUiThread {
doPreload = false doPreload = false
if (isRunningAsStub) { if (isRunningAsStub) {

View File

@ -7,13 +7,15 @@ import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import com.topjohnwu.magisk.DynAPK 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.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.core.utils.updateConfig
import com.topjohnwu.magisk.di.ServiceLocator import com.topjohnwu.magisk.di.ServiceLocator
import com.topjohnwu.magisk.ktx.unwrap import com.topjohnwu.magisk.ktx.unwrap
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.internal.UiThreadHandler
import com.topjohnwu.superuser.ipc.RootService
import timber.log.Timber import timber.log.Timber
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -26,7 +28,7 @@ open class App() : Application() {
init { init {
Shell.setDefaultBuilder(Shell.Builder.create() Shell.setDefaultBuilder(Shell.Builder.create()
.setFlags(Shell.FLAG_MOUNT_MASTER) .setFlags(Shell.FLAG_MOUNT_MASTER)
.setInitializers(BusyBoxInit::class.java, AppShellInit::class.java) .setInitializers(ShellInit::class.java)
.setTimeout(2)) .setTimeout(2))
Shell.EXECUTOR = IODispatcherExecutor() Shell.EXECUTOR = IODispatcherExecutor()
@ -57,6 +59,15 @@ open class App() : Application() {
app.registerActivityLifecycleCallbacks(ForegroundTracker) app.registerActivityLifecycleCallbacks(ForegroundTracker)
} }
override fun onCreate() {
super.onCreate()
RootRegistry.bindTask = RootService.createBindTask(
intent<RootRegistry>(),
UiThreadHandler.executor,
RootRegistry.Connection
)
}
// This is required as some platforms expect ContextImpl // This is required as some platforms expect ContextImpl
override fun getBaseContext(): Context { override fun getBaseContext(): Context {
return super.getBaseContext().unwrap() return super.getBaseContext().unwrap()

View File

@ -1,13 +1,19 @@
package com.topjohnwu.magisk.core.utils package com.topjohnwu.magisk.core.utils
import kotlinx.coroutines.* 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() { class IODispatcherExecutor : AbstractExecutorService() {
private val job = SupervisorJob().apply { invokeOnCompletion { future.run() } } private val job = SupervisorJob()
private val scope = CoroutineScope(job + Dispatchers.IO) 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) { override fun execute(command: Runnable) {
scope.launch { scope.launch {
@ -26,11 +32,5 @@ class IODispatcherExecutor : AbstractExecutorService() {
override fun isTerminated() = job.isCancelled && job.isCompleted override fun isTerminated() = job.isCancelled && job.isCompleted
override fun awaitTermination(timeout: Long, unit: TimeUnit): Boolean { override fun awaitTermination(timeout: Long, unit: TimeUnit) = latch.await(timeout, unit)
return try {
future.get(timeout, unit)
} catch (e: TimeoutException) {
false
}
}
} }

View File

@ -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
}
}

View File

@ -3,7 +3,10 @@ package com.topjohnwu.magisk.core.utils
import android.content.Context import android.content.Context
import com.topjohnwu.magisk.DynAPK import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.R 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.cachedFile
import com.topjohnwu.magisk.ktx.deviceProtectedContext import com.topjohnwu.magisk.ktx.deviceProtectedContext
import com.topjohnwu.magisk.ktx.rawResource import com.topjohnwu.magisk.ktx.rawResource
@ -13,18 +16,12 @@ import com.topjohnwu.superuser.ShellUtils
import java.io.File import java.io.File
import java.util.jar.JarFile import java.util.jar.JarFile
abstract class BaseShellInit : Shell.Initializer() { class ShellInit : Shell.Initializer() {
final override fun onInit(context: Context, shell: Shell): Boolean { override fun onInit(context: Context, shell: Shell): Boolean {
return init(context.wrap(), shell) if (shell.isRoot) {
} RootRegistry.bindTask?.run()
RootRegistry.bindTask = null
abstract fun init(context: Context, shell: Shell): Boolean }
}
class BusyBoxInit : BaseShellInit() {
override fun init(context: Context, shell: Shell): Boolean {
shell.newJob().apply { shell.newJob().apply {
add("export ASH_STANDALONE=1") add("export ASH_STANDALONE=1")
@ -63,20 +60,7 @@ class BusyBoxInit : BaseShellInit() {
// Directly execute the file // Directly execute the file
add("exec $localBB sh") 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)) add(context.rawResource(R.raw.manager))
if (shell.isRoot) { if (shell.isRoot) {
add(context.assets.open("util_functions.sh")) add(context.assets.open("util_functions.sh"))
@ -84,6 +68,10 @@ class AppShellInit : BaseShellInit() {
add("app_init") add("app_init")
}.exec() }.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") Const.MAGISKTMP = getVar("MAGISKTMP")
Info.isSAR = getBool("SYSTEM_ROOT") Info.isSAR = getBool("SYSTEM_ROOT")
Info.ramdisk = getBool("RAMDISKEXIST") Info.ramdisk = getBool("RAMDISKEXIST")
@ -95,6 +83,9 @@ class AppShellInit : BaseShellInit() {
Config.keepVerity = getBool("KEEPVERITY") Config.keepVerity = getBool("KEEPVERITY")
Config.keepEnc = getBool("KEEPFORCEENCRYPT") Config.keepEnc = getBool("KEEPFORCEENCRYPT")
// Pre-fetch env
Info.env
return true return true
} }
} }