Ensure RootService is launched

This commit is contained in:
topjohnwu 2022-03-23 18:44:05 -07:00
parent e67d0678f9
commit f2c15c7701
6 changed files with 102 additions and 77 deletions

View File

@ -86,7 +86,7 @@ dependencies {
implementation("${bindingAdapter}:${vBAdapt}") implementation("${bindingAdapter}:${vBAdapt}")
implementation("${bindingAdapter}-recyclerview:${vBAdapt}") implementation("${bindingAdapter}-recyclerview:${vBAdapt}")
val vLibsu = "4.0.2" val vLibsu = "4.0.3"
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}") implementation("com.github.topjohnwu.libsu:service:${vLibsu}")

View File

@ -14,6 +14,7 @@ import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.JobService import com.topjohnwu.magisk.core.JobService
import com.topjohnwu.magisk.core.isRunningAsStub 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.RootUtils
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.utils.Utils import com.topjohnwu.magisk.utils.Utils
@ -104,6 +105,9 @@ abstract class BaseMainActivity<Binding : ViewDataBinding> : NavigationActivity<
// Pre-fetch network services // Pre-fetch network services
ServiceLocator.networkService ServiceLocator.networkService
// Wait for root service
RootUtils.Connection.await()
} }
private fun handleRepackage(pkg: String?) { private fun handleRepackage(pkg: String?) {

View File

@ -8,6 +8,7 @@ import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import com.topjohnwu.magisk.StubApk import com.topjohnwu.magisk.StubApk
import com.topjohnwu.magisk.core.utils.* import com.topjohnwu.magisk.core.utils.*
import com.topjohnwu.magisk.di.AppContext
import com.topjohnwu.magisk.di.ServiceLocator import com.topjohnwu.magisk.di.ServiceLocator
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
@ -22,9 +23,9 @@ open class App() : Application() {
constructor(o: Any) : this() { constructor(o: Any) : this() {
val data = StubApk.Data(o) val data = StubApk.Data(o)
// Add the root service name mapping // Add the root service name mapping
data.classToComponent[RootRegistry::class.java.name] = data.rootService.name data.classToComponent[RootUtils::class.java.name] = data.rootService.name
// Send back the actual root service class // Send back the actual root service class
data.rootService = RootRegistry::class.java data.rootService = RootUtils::class.java
Info.stub = data Info.stub = data
} }
@ -38,43 +39,40 @@ open class App() : Application() {
} }
override fun attachBaseContext(context: Context) { override fun attachBaseContext(context: Context) {
Shell.setDefaultBuilder(Shell.Builder.create()
.setFlags(Shell.FLAG_MOUNT_MASTER)
.setInitializers(ShellInit::class.java)
.setTimeout(2))
Shell.EXECUTOR = DispatcherExecutor(Dispatchers.IO)
// Get the actual ContextImpl // Get the actual ContextImpl
val app: Application val app: Application
val base: Context val base: Context
if (context is Application) { if (context is Application) {
app = context app = context
base = context.baseContext base = context.baseContext
AppApkPath = StubApk.current(base).path
} else { } else {
app = this app = this
base = context base = context
AppApkPath = base.packageResourcePath
} }
super.attachBaseContext(base) super.attachBaseContext(base)
ServiceLocator.context = base ServiceLocator.context = base
refreshLocale()
AppApkPath = if (isRunningAsStub) {
StubApk.current(base).path
} else {
base.packageResourcePath
}
base.resources.patch()
app.registerActivityLifecycleCallbacks(ActivityTracker) app.registerActivityLifecycleCallbacks(ActivityTracker)
} }
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
RootRegistry.bindTask = RootService.bindOrTask( Shell.setDefaultBuilder(Shell.Builder.create()
intent<RootRegistry>(), .setFlags(Shell.FLAG_MOUNT_MASTER)
.setInitializers(ShellInit::class.java)
.setTimeout(2))
Shell.EXECUTOR = DispatcherExecutor(Dispatchers.IO)
RootUtils.bindTask = RootService.bindOrTask(
intent<RootUtils>(),
UiThreadHandler.executor, UiThreadHandler.executor,
RootRegistry.Connection RootUtils.Connection
) )
// Pre-heat the shell ASAP
Shell.getShell(null) {}
refreshLocale()
AppContext.resources.patch()
} }
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {

View File

@ -1,54 +0,0 @@
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.Shell
import com.topjohnwu.superuser.ipc.RootService
import timber.log.Timber
import java.util.concurrent.CountDownLatch
import kotlin.system.exitProcess
class RootRegistry(stub: Any?) : RootService() {
constructor() : this(null) {
// Always log full stack trace with Timber
Timber.plant(Timber.DebugTree())
Thread.setDefaultUncaughtExceptionHandler { _, e ->
Timber.e(e)
exitProcess(1)
}
}
private val className: String? = stub?.javaClass?.name
override fun onBind(intent: Intent): IBinder {
// TODO: PLACEHOLDER
return Binder()
}
override fun getComponentName(): ComponentName {
return ComponentName(packageName, className ?: javaClass.name)
}
// 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: Shell.Task? = null
}
}

View File

@ -0,0 +1,77 @@
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.Shell
import com.topjohnwu.superuser.ShellUtils
import com.topjohnwu.superuser.ipc.RootService
import timber.log.Timber
import java.util.concurrent.locks.AbstractQueuedSynchronizer
class RootUtils(stub: Any?) : RootService() {
private val className: String = stub?.javaClass?.name ?: javaClass.name
constructor() : this(null) {
Timber.plant(Timber.DebugTree())
}
override fun getComponentName(): ComponentName {
return ComponentName(packageName, className)
}
override fun onBind(intent: Intent): IBinder {
return Binder()
}
object Connection : AbstractQueuedSynchronizer(), ServiceConnection {
init {
state = 1
}
override fun onServiceConnected(name: ComponentName, service: IBinder) {
Timber.d("onServiceConnected")
obj = IRootUtils.Stub.asInterface(service)
releaseShared(1)
}
override fun onServiceDisconnected(name: ComponentName) {
state = 1
obj = null
bind(Intent().setComponent(name), this)
}
override fun tryAcquireShared(acquires: Int) = if (state == 0) 1 else -1
override fun tryReleaseShared(releases: Int): Boolean {
// Decrement count; signal when transition to zero
while (true) {
val c = state
if (c == 0)
return false
val n = c - 1
if (compareAndSetState(c, n))
return n == 0
}
}
fun await() {
// We cannot await on the main thread
if (!ShellUtils.onMainThread())
acquireSharedInterruptibly(1)
}
}
companion object {
var bindTask: Shell.Task? = null
var obj: IRootUtils? = null
get() {
Connection.await()
return field
}
private set
}
}

View File

@ -19,8 +19,8 @@ import java.util.jar.JarFile
class ShellInit : Shell.Initializer() { class ShellInit : Shell.Initializer() {
override fun onInit(context: Context, shell: Shell): Boolean { override fun onInit(context: Context, shell: Shell): Boolean {
if (shell.isRoot) { if (shell.isRoot) {
RootRegistry.bindTask?.let { shell.execTask(it) } RootUtils.bindTask?.let { shell.execTask(it) }
RootRegistry.bindTask = null RootUtils.bindTask = null
} }
shell.newJob().apply { shell.newJob().apply {
add("export ASH_STANDALONE=1") add("export ASH_STANDALONE=1")