diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e4002f6da..c0e4b4c8d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -86,7 +86,7 @@ dependencies { implementation("${bindingAdapter}:${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:io:${vLibsu}") implementation("com.github.topjohnwu.libsu:service:${vLibsu}") 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 ba74f1e3f..25894c78f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/arch/BaseMainActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/arch/BaseMainActivity.kt @@ -14,6 +14,7 @@ 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.utils.RootUtils import com.topjohnwu.magisk.di.ServiceLocator import com.topjohnwu.magisk.ui.theme.Theme import com.topjohnwu.magisk.utils.Utils @@ -104,6 +105,9 @@ abstract class BaseMainActivity : NavigationActivity< // Pre-fetch network services ServiceLocator.networkService + + // Wait for root service + RootUtils.Connection.await() } private fun handleRepackage(pkg: String?) { 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 31d7cde65..1f0325ee3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/App.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/App.kt @@ -8,6 +8,7 @@ import android.content.res.Configuration import android.os.Bundle import com.topjohnwu.magisk.StubApk import com.topjohnwu.magisk.core.utils.* +import com.topjohnwu.magisk.di.AppContext import com.topjohnwu.magisk.di.ServiceLocator import com.topjohnwu.magisk.ui.surequest.SuRequestActivity import com.topjohnwu.superuser.Shell @@ -22,9 +23,9 @@ open class App() : Application() { constructor(o: Any) : this() { val data = StubApk.Data(o) // 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 - data.rootService = RootRegistry::class.java + data.rootService = RootUtils::class.java Info.stub = data } @@ -38,43 +39,40 @@ open class App() : Application() { } 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 val app: Application val base: Context if (context is Application) { app = context base = context.baseContext + AppApkPath = StubApk.current(base).path } else { app = this base = context + AppApkPath = base.packageResourcePath } super.attachBaseContext(base) ServiceLocator.context = base - - refreshLocale() - AppApkPath = if (isRunningAsStub) { - StubApk.current(base).path - } else { - base.packageResourcePath - } - - base.resources.patch() app.registerActivityLifecycleCallbacks(ActivityTracker) } override fun onCreate() { super.onCreate() - RootRegistry.bindTask = RootService.bindOrTask( - intent(), + Shell.setDefaultBuilder(Shell.Builder.create() + .setFlags(Shell.FLAG_MOUNT_MASTER) + .setInitializers(ShellInit::class.java) + .setTimeout(2)) + Shell.EXECUTOR = DispatcherExecutor(Dispatchers.IO) + RootUtils.bindTask = RootService.bindOrTask( + intent(), UiThreadHandler.executor, - RootRegistry.Connection + RootUtils.Connection ) + // Pre-heat the shell ASAP + Shell.getShell(null) {} + + refreshLocale() + AppContext.resources.patch() } override fun onConfigurationChanged(newConfig: Configuration) { 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 deleted file mode 100644 index 4fae2cb3a..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/core/utils/RootRegistry.kt +++ /dev/null @@ -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 - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/core/utils/RootUtils.kt b/app/src/main/java/com/topjohnwu/magisk/core/utils/RootUtils.kt new file mode 100644 index 000000000..3773d2f90 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/core/utils/RootUtils.kt @@ -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 + } +} 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 7f5a9d284..a40c18b3e 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 @@ -19,8 +19,8 @@ import java.util.jar.JarFile class ShellInit : Shell.Initializer() { override fun onInit(context: Context, shell: Shell): Boolean { if (shell.isRoot) { - RootRegistry.bindTask?.let { shell.execTask(it) } - RootRegistry.bindTask = null + RootUtils.bindTask?.let { shell.execTask(it) } + RootUtils.bindTask = null } shell.newJob().apply { add("export ASH_STANDALONE=1")