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("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}")

View File

@ -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<VM : BaseViewModel, Binding : ViewDataBinding>
: BaseUIActivity<VM, Binding>() {
@ -48,13 +47,15 @@ abstract class BaseMainActivity<VM : BaseViewModel, Binding : ViewDataBinding>
}
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) {

View File

@ -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<RootRegistry>(),
UiThreadHandler.executor,
RootRegistry.Connection
)
}
// This is required as some platforms expect ContextImpl
override fun getBaseContext(): Context {
return super.getBaseContext().unwrap()

View File

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

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 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)
class ShellInit : Shell.Initializer() {
override fun onInit(context: Context, shell: Shell): Boolean {
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 {
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
}
}