Shizuku: Dex2Oat usually needs root to run

This commit is contained in:
WeiguangTWK 2026-04-02 20:52:56 +08:00
parent fced5612fb
commit 798d98c089
5 changed files with 32 additions and 5 deletions

View File

@ -42,6 +42,15 @@ object ShizukuApi {
var isBinderAvailable = false var isBinderAvailable = false
var isPermissionGranted by mutableStateOf(false) var isPermissionGranted by mutableStateOf(false)
fun getBackendUidOrNull(): Int? {
if (!isBinderAvailable || !isPermissionGranted) return null
return runCatching { Shizuku.getUid() }
.getOrNull()
?.takeIf { it >= 0 }
}
fun isRootBackend(): Boolean = getBackendUidOrNull() == 0
fun init() { fun init() {
Shizuku.addBinderReceivedListenerSticky { Shizuku.addBinderReceivedListenerSticky {
isBinderAvailable = true isBinderAvailable = true

View File

@ -114,11 +114,16 @@ fun AppManageBody(
is ProcessingState.Idle -> Unit is ProcessingState.Idle -> Unit
is ProcessingState.Processing -> LoadingDialog() is ProcessingState.Processing -> LoadingDialog()
is ProcessingState.Done -> { is ProcessingState.Done -> {
val it = viewModel.optimizeState as ProcessingState.Done val it = viewModel.optimizeState as ProcessingState.Done<AppManageViewModel.OptimizeResult>
val optimizeSucceed = stringResource(R.string.manage_optimize_successfully) val optimizeSucceed = stringResource(R.string.manage_optimize_successfully)
val optimizeFailed = stringResource(R.string.manage_optimize_failed) val optimizeFailed = stringResource(R.string.manage_optimize_failed)
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
snackbarHost.showSnackbar(if (it.result) optimizeSucceed else optimizeFailed) val message = if (it.result.success) {
optimizeSucceed
} else {
it.result.messageRes?.let { resId -> lspApp.getString(resId) } ?: optimizeFailed
}
snackbarHost.showSnackbar(message)
viewModel.dispatch(AppManageViewModel.ViewAction.ClearOptimizeResult) viewModel.dispatch(AppManageViewModel.ViewAction.ClearOptimizeResult)
} }
} }

View File

@ -17,6 +17,7 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.lsposed.npatch.Patcher import org.lsposed.npatch.Patcher
import org.lsposed.npatch.R
import org.lsposed.npatch.lspApp import org.lsposed.npatch.lspApp
import org.lsposed.npatch.share.Constants import org.lsposed.npatch.share.Constants
import org.lsposed.npatch.share.PatchConfig import org.lsposed.npatch.share.PatchConfig
@ -45,6 +46,11 @@ class AppManageViewModel : ViewModel() {
object Refresh : ViewAction() object Refresh : ViewAction()
} }
data class OptimizeResult(
val success: Boolean,
val messageRes: Int? = null
)
// 手動管理狀態,避免實時響應系統廣播導致列表跳動 // 手動管理狀態,避免實時響應系統廣播導致列表跳動
var appList: List<Pair<AppInfo, PatchConfig>> by mutableStateOf(emptyList()) var appList: List<Pair<AppInfo, PatchConfig>> by mutableStateOf(emptyList())
private set private set
@ -55,7 +61,7 @@ class AppManageViewModel : ViewModel() {
var updateLoaderState: ProcessingState<Result<Unit>> by mutableStateOf(ProcessingState.Idle) var updateLoaderState: ProcessingState<Result<Unit>> by mutableStateOf(ProcessingState.Idle)
private set private set
var optimizeState: ProcessingState<Boolean> by mutableStateOf(ProcessingState.Idle) var optimizeState: ProcessingState<OptimizeResult> by mutableStateOf(ProcessingState.Idle)
private set private set
private val logger = object : Logger() { private val logger = object : Logger() {
@ -195,7 +201,12 @@ class AppManageViewModel : ViewModel() {
Log.i(TAG, "Perform optimize for ${appInfo.app.packageName}") Log.i(TAG, "Perform optimize for ${appInfo.app.packageName}")
optimizeState = ProcessingState.Processing optimizeState = ProcessingState.Processing
val result = withContext(Dispatchers.IO) { val result = withContext(Dispatchers.IO) {
ShizukuApi.performDexOptMode(appInfo.app.packageName) if (!ShizukuApi.isRootBackend()) {
Log.w(TAG, "Optimize refused: Shizuku backend is not root, uid=${ShizukuApi.getBackendUidOrNull()}")
OptimizeResult(success = false, messageRes = R.string.manage_optimize_permission_denied)
} else {
OptimizeResult(success = ShizukuApi.performDexOptMode(appInfo.app.packageName))
}
} }
optimizeState = ProcessingState.Done(result) optimizeState = ProcessingState.Done(result)
} }

View File

@ -41,6 +41,7 @@
<string name="manage_optimize">优化</string> <string name="manage_optimize">优化</string>
<string name="manage_optimize_successfully">优化成功</string> <string name="manage_optimize_successfully">优化成功</string>
<string name="manage_optimize_failed">优化失败</string> <string name="manage_optimize_failed">优化失败</string>
<string name="manage_optimize_permission_denied">权限不足:优化功能要求 Shizuku 运行在 Root 特权下</string>
<string name="manage_uninstall_successfully">卸载成功</string> <string name="manage_uninstall_successfully">卸载成功</string>
<string name="manage_no_modules">尚无模块</string> <string name="manage_no_modules">尚无模块</string>
<string name="manage_module_settings">模块设置</string> <string name="manage_module_settings">模块设置</string>

View File

@ -42,6 +42,7 @@
<string name="manage_optimize">Optimize</string> <string name="manage_optimize">Optimize</string>
<string name="manage_optimize_successfully">Optimize successfully</string> <string name="manage_optimize_successfully">Optimize successfully</string>
<string name="manage_optimize_failed">Optimize failed</string> <string name="manage_optimize_failed">Optimize failed</string>
<string name="manage_optimize_permission_denied">Insufficient privilege: optimize requires Shizuku running as root</string>
<string name="manage_uninstall_successfully">Uninstall successfully</string> <string name="manage_uninstall_successfully">Uninstall successfully</string>
<string name="manage_no_modules">No modules yet</string> <string name="manage_no_modules">No modules yet</string>
<string name="manage_module_settings">Module settings</string> <string name="manage_module_settings">Module settings</string>