6.7.0 - Alpha12 - 修复 Shizuku 用户服务进程未能正常结束导致进程堆积的问题 (issue #474)
This commit is contained in:
@@ -3,11 +3,13 @@ package org.autojs.autojs.core.shizuku
|
||||
import android.app.ActivityManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.os.Process
|
||||
import android.os.RemoteException
|
||||
import android.util.Log
|
||||
import androidx.annotation.Keep
|
||||
import org.autojs.autojs.runtime.api.AbstractShell
|
||||
import org.autojs.autojs.runtime.api.ProcessShell
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
class UserService : IUserService.Stub {
|
||||
|
||||
@@ -49,8 +51,22 @@ class UserService : IUserService.Stub {
|
||||
*/
|
||||
override fun destroy() {
|
||||
Log.i("UserService", "destroy")
|
||||
|
||||
// Ensure the user service process terminates when Shizuku server requests destroy.
|
||||
// zh-CN: 确保 Shizuku server 请求 destroy 时, user service 进程能够真正退出.
|
||||
runCatching {
|
||||
Process.killProcess(Process.myPid())
|
||||
}
|
||||
|
||||
// Fallback to exit the process if killProcess doesn't stop it immediately.
|
||||
// zh-CN: 如果 killProcess 未能立刻终止, 则使用 exitProcess 作为兜底退出.
|
||||
runCatching {
|
||||
exitProcess(0)
|
||||
}
|
||||
}
|
||||
|
||||
// Exit method defined by user.
|
||||
// zh-CN: 用户定义的退出方法.
|
||||
override fun exit() {
|
||||
destroy()
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.DeadObjectException
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import org.autojs.autojs.AbstractAutoJs.Companion.isInrt
|
||||
@@ -17,6 +18,7 @@ import org.autojs.autojs.util.App.SHIZUKU
|
||||
import org.autojs.autojs.util.ViewUtils
|
||||
import org.autojs.autojs6.R
|
||||
import rikka.shizuku.Shizuku
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import org.autojs.autojs.runtime.api.AbstractShell.Result as ShellResult
|
||||
@@ -37,18 +39,27 @@ object WrappedShizuku {
|
||||
}
|
||||
private var mHasBinder = false
|
||||
|
||||
private val mServiceWaiters = CopyOnWriteArrayList<CountDownLatch>()
|
||||
|
||||
private val mUserServiceConnection: ServiceConnection = object : ServiceConnection {
|
||||
override fun onServiceConnected(componentName: ComponentName, binder: IBinder?) {
|
||||
Log.d(TAG, "onServiceConnected: ${componentName.className}")
|
||||
if (binder?.pingBinder() == true) {
|
||||
service = IUserService.Stub.asInterface(binder)
|
||||
|
||||
// Wake all waiters when binder is ready.
|
||||
// zh-CN: 当 binder 就绪时, 唤醒所有等待者.
|
||||
mServiceWaiters.forEach { it.countDown() }
|
||||
mServiceWaiters.clear()
|
||||
} else {
|
||||
Log.w(TAG, "invalid binder for $componentName received")
|
||||
service = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(componentName: ComponentName) {
|
||||
Log.d(TAG, "onServiceDisconnected: ${componentName.className}")
|
||||
service = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +98,9 @@ object WrappedShizuku {
|
||||
}
|
||||
|
||||
internal fun bindUserServiceIfNeeded() {
|
||||
if (service?.asBinder()?.pingBinder() == true) {
|
||||
return
|
||||
}
|
||||
if (hasPermission()) {
|
||||
bindUserService()
|
||||
}
|
||||
@@ -148,17 +162,41 @@ object WrappedShizuku {
|
||||
|
||||
@ScriptInterface
|
||||
fun execCommand(context: Context, cmd: String): ShellResult {
|
||||
return execCommandWithAutoReconnect(context, cmd, allowRetry = true)
|
||||
}
|
||||
|
||||
private fun execCommandWithAutoReconnect(context: Context, cmd: String, allowRetry: Boolean): ShellResult {
|
||||
if (service == null && hasPermission()) {
|
||||
onCreate()
|
||||
bindUserServiceIfNeeded()
|
||||
initializeShizukuServiceAndWait(5000L)
|
||||
}
|
||||
|
||||
val service = service ?: when {
|
||||
!hasPermission() -> R.string.error_no_permission_to_access_shizuku
|
||||
!isRunning() -> R.string.error_shizuku_service_may_be_not_running
|
||||
else -> R.string.error_unable_to_use_shizuku_service
|
||||
}.let { throw IllegalStateException(context.getString(it)) }
|
||||
|
||||
return try {
|
||||
ShellResult.fromJson(service.execCommand(cmd.replace(Regex("^\\s*adb\\s+shell\\s+", RegexOption.IGNORE_CASE), "")))
|
||||
ShellResult.fromJson(
|
||||
service.execCommand(
|
||||
cmd.replace(Regex("^\\s*adb\\s+shell\\s+", RegexOption.IGNORE_CASE), "")
|
||||
)
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
// Reconnect and retry once when binder is dead.
|
||||
// zh-CN: 当 binder 已死亡时, 自动重连并重试一次.
|
||||
if (allowRetry && e is DeadObjectException) {
|
||||
this.service = null
|
||||
runCatching {
|
||||
onCreate()
|
||||
bindUserServiceIfNeeded()
|
||||
initializeShizukuServiceAndWait(5000L)
|
||||
}
|
||||
return execCommandWithAutoReconnect(context, cmd, allowRetry = false)
|
||||
}
|
||||
|
||||
ShellResult().apply {
|
||||
code = 1
|
||||
error = e.message ?: when {
|
||||
@@ -191,17 +229,25 @@ object WrappedShizuku {
|
||||
}
|
||||
|
||||
private fun initializeShizukuServiceAndWait(@Suppress("SameParameterValue") timeout: Long) {
|
||||
onCreate()
|
||||
bindUserServiceIfNeeded()
|
||||
if (service?.asBinder()?.pingBinder() == true) {
|
||||
return
|
||||
}
|
||||
|
||||
val latch = CountDownLatch(1)
|
||||
val tmpConnection = object : ServiceConnection {
|
||||
override fun onServiceConnected(name: ComponentName, binder: IBinder) = latch.countDown()
|
||||
override fun onServiceDisconnected(name: ComponentName) = Unit
|
||||
mServiceWaiters.add(latch)
|
||||
|
||||
// Re-check after registering waiter to avoid missing a fast onServiceConnected().
|
||||
// zh-CN: 注册等待者后再次检查, 避免 onServiceConnected() 很快到来导致错过唤醒.
|
||||
if (service?.asBinder()?.pingBinder() == true) {
|
||||
mServiceWaiters.remove(latch)
|
||||
return
|
||||
}
|
||||
|
||||
runCatching {
|
||||
latch.await(timeout, TimeUnit.MILLISECONDS)
|
||||
}.also {
|
||||
mServiceWaiters.remove(latch)
|
||||
}
|
||||
Shizuku.bindUserService(mUserServiceArgs, tmpConnection)
|
||||
latch.await(timeout, TimeUnit.MILLISECONDS)
|
||||
Shizuku.unbindUserService(mUserServiceArgs, tmpConnection, true)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -155,7 +155,13 @@ class MainActivity : BaseActivity(), DelegateHost, HostActivity {
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
WrappedShizuku.bindUserServiceIfNeeded()
|
||||
// @Hint by SuperMonster003 on Dec 24, 2025.
|
||||
// ! Avoid binding Shizuku user service on app start.
|
||||
// ! It may spawn root user-service processes repeatedly during IDE "Run" (force-stop + relaunch).
|
||||
// ! zh-CN:
|
||||
// ! 避免在应用启动时绑定 Shizuku user service.
|
||||
// ! IDE "Run" (force-stop + relaunch) 期间可能反复拉起 root user-service 进程.
|
||||
// # WrappedShizuku.bindUserServiceIfNeeded()
|
||||
}
|
||||
|
||||
private fun recreateIfNeeded() {
|
||||
|
||||
Reference in New Issue
Block a user