Revert "manager: Optimized import, optimized all libsu shell calls, and fixed WebUI memory leaks #369
This reverts commit c3533861f2.
This commit is contained in:
@@ -12,8 +12,6 @@ import android.os.Bundle
|
|||||||
import coil.Coil
|
import coil.Coil
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import com.dergoogler.mmrl.platform.Platform
|
import com.dergoogler.mmrl.platform.Platform
|
||||||
import com.sukisu.ultra.ui.util.createRootShellBuilder
|
|
||||||
import com.topjohnwu.superuser.Shell
|
|
||||||
import me.zhanghai.android.appiconloader.coil.AppIconFetcher
|
import me.zhanghai.android.appiconloader.coil.AppIconFetcher
|
||||||
import me.zhanghai.android.appiconloader.coil.AppIconKeyer
|
import me.zhanghai.android.appiconloader.coil.AppIconKeyer
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -88,8 +86,6 @@ class KernelSUApplication : Application() {
|
|||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
ksuApp = this
|
ksuApp = this
|
||||||
Shell.setDefaultBuilder(createRootShellBuilder(true))
|
|
||||||
Shell.enableVerboseLogging = BuildConfig.DEBUG
|
|
||||||
|
|
||||||
// 注册Activity生命周期回调
|
// 注册Activity生命周期回调
|
||||||
registerActivityLifecycleCallbacks(activityLifecycleCallbacks)
|
registerActivityLifecycleCallbacks(activityLifecycleCallbacks)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.sukisu.ultra.ui.screen
|
package com.sukisu.ultra.ui.screen
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@@ -18,27 +17,25 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.content.edit
|
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.ramcosta.composedestinations.annotation.Destination
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||||
import com.sukisu.ultra.R
|
|
||||||
import com.sukisu.ultra.ui.component.*
|
|
||||||
import com.sukisu.ultra.ui.theme.getCardColors
|
|
||||||
import com.sukisu.ultra.ui.theme.getCardElevation
|
|
||||||
import com.sukisu.ultra.ui.util.loadKpmModule
|
|
||||||
import com.sukisu.ultra.ui.util.unloadKpmModule
|
|
||||||
import com.sukisu.ultra.ui.viewmodel.KpmViewModel
|
|
||||||
import com.topjohnwu.superuser.Shell
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import com.sukisu.ultra.ui.component.*
|
||||||
|
import com.sukisu.ultra.ui.theme.*
|
||||||
|
import com.sukisu.ultra.ui.viewmodel.KpmViewModel
|
||||||
|
import com.sukisu.ultra.ui.util.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import com.sukisu.ultra.R
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.net.URLEncoder
|
import java.net.*
|
||||||
|
import android.app.Activity
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* KPM 管理界面
|
* KPM 管理界面
|
||||||
@@ -86,8 +83,9 @@ fun KpmScreen(
|
|||||||
LaunchedEffect(tempFileForInstall) {
|
LaunchedEffect(tempFileForInstall) {
|
||||||
tempFileForInstall?.let { tempFile ->
|
tempFileForInstall?.let { tempFile ->
|
||||||
try {
|
try {
|
||||||
|
val shell = getRootShell()
|
||||||
val command = "strings ${tempFile.absolutePath} | grep 'name='"
|
val command = "strings ${tempFile.absolutePath} | grep 'name='"
|
||||||
val result = Shell.cmd(command).to(ArrayList(), null).exec()
|
val result = shell.newJob().add(command).to(ArrayList(), null).exec()
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
for (line in result.out) {
|
for (line in result.out) {
|
||||||
if (line.startsWith("name=")) {
|
if (line.startsWith("name=")) {
|
||||||
@@ -426,8 +424,9 @@ private suspend fun handleModuleInstall(
|
|||||||
) {
|
) {
|
||||||
var moduleId: String? = null
|
var moduleId: String? = null
|
||||||
try {
|
try {
|
||||||
|
val shell = getRootShell()
|
||||||
val command = "strings ${tempFile.absolutePath} | grep 'name='"
|
val command = "strings ${tempFile.absolutePath} | grep 'name='"
|
||||||
val result = Shell.cmd(command).to(ArrayList(), null).exec()
|
val result = shell.newJob().add(command).to(ArrayList(), null).exec()
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
for (line in result.out) {
|
for (line in result.out) {
|
||||||
if (line.startsWith("name=")) {
|
if (line.startsWith("name=")) {
|
||||||
@@ -454,8 +453,9 @@ private suspend fun handleModuleInstall(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (isEmbed) {
|
if (isEmbed) {
|
||||||
Shell.cmd("mkdir -p /data/adb/kpm").exec()
|
val shell = getRootShell()
|
||||||
Shell.cmd("cp ${tempFile.absolutePath} $targetPath").exec()
|
shell.newJob().add("mkdir -p /data/adb/kpm").exec()
|
||||||
|
shell.newJob().add("cp ${tempFile.absolutePath} $targetPath").exec()
|
||||||
}
|
}
|
||||||
|
|
||||||
val loadResult = loadKpmModule(tempFile.absolutePath)
|
val loadResult = loadKpmModule(tempFile.absolutePath)
|
||||||
@@ -499,7 +499,8 @@ private suspend fun handleModuleUninstall(
|
|||||||
val moduleFilePath = "/data/adb/kpm/$moduleFileName"
|
val moduleFilePath = "/data/adb/kpm/$moduleFileName"
|
||||||
|
|
||||||
val fileExists = try {
|
val fileExists = try {
|
||||||
val result = Shell.cmd("ls /data/adb/kpm/$moduleFileName").exec()
|
val shell = getRootShell()
|
||||||
|
val result = shell.newJob().add("ls /data/adb/kpm/$moduleFileName").exec()
|
||||||
result.isSuccess
|
result.isSuccess
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("KsuCli", "Failed to check module file existence: ${e.message}", e)
|
Log.e("KsuCli", "Failed to check module file existence: ${e.message}", e)
|
||||||
@@ -530,7 +531,8 @@ private suspend fun handleModuleUninstall(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fileExists) {
|
if (fileExists) {
|
||||||
Shell.cmd("rm $moduleFilePath").exec()
|
val shell = getRootShell()
|
||||||
|
shell.newJob().add("rm $moduleFilePath").exec()
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.fetchModuleList()
|
viewModel.fetchModuleList()
|
||||||
@@ -701,8 +703,9 @@ private fun KpmModuleItem(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkStringsCommand(tempFile: File): Int {
|
private fun checkStringsCommand(tempFile: File): Int {
|
||||||
|
val shell = getRootShell()
|
||||||
val command = "strings ${tempFile.absolutePath} | grep -E 'name=|version=|license=|author='"
|
val command = "strings ${tempFile.absolutePath} | grep -E 'name=|version=|license=|author='"
|
||||||
val result = Shell.cmd(command).to(ArrayList(), null).exec()
|
val result = shell.newJob().add(command).to(ArrayList(), null).exec()
|
||||||
|
|
||||||
if (!result.isSuccess) {
|
if (!result.isSuccess) {
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -10,15 +10,15 @@ import android.os.SystemClock
|
|||||||
import android.provider.OpenableColumns
|
import android.provider.OpenableColumns
|
||||||
import android.system.Os
|
import android.system.Os
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.sukisu.ultra.Natives
|
|
||||||
import com.sukisu.ultra.ksuApp
|
|
||||||
import com.topjohnwu.superuser.CallbackList
|
import com.topjohnwu.superuser.CallbackList
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import com.topjohnwu.superuser.ShellUtils
|
import com.topjohnwu.superuser.ShellUtils
|
||||||
import com.topjohnwu.superuser.io.SuFile
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import com.sukisu.ultra.BuildConfig
|
||||||
|
import com.sukisu.ultra.Natives
|
||||||
|
import com.sukisu.ultra.ksuApp
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@@ -29,8 +29,19 @@ import java.io.File
|
|||||||
*/
|
*/
|
||||||
private const val TAG = "KsuCli"
|
private const val TAG = "KsuCli"
|
||||||
|
|
||||||
private val ksuDaemonPath by lazy {
|
private fun getKsuDaemonPath(): String {
|
||||||
"${ksuApp.applicationInfo.nativeLibraryDir}${File.separator}libzakozako.so"
|
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libzakozako.so"
|
||||||
|
}
|
||||||
|
|
||||||
|
object KsuCli {
|
||||||
|
val SHELL: Shell = createRootShell()
|
||||||
|
val GLOBAL_MNT_SHELL: Shell = createRootShell(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRootShell(globalMnt: Boolean = false): Shell {
|
||||||
|
return if (globalMnt) KsuCli.GLOBAL_MNT_SHELL else {
|
||||||
|
KsuCli.SHELL
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <T> withNewRootShell(
|
inline fun <T> withNewRootShell(
|
||||||
@@ -52,39 +63,37 @@ fun Uri.getFileName(context: Context): String? {
|
|||||||
return fileName
|
return fileName
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createRootShellBuilder(globalMnt: Boolean = false): Shell.Builder {
|
|
||||||
return Shell.Builder.create().run {
|
|
||||||
val cmd = buildString {
|
|
||||||
append("$ksuDaemonPath debug su")
|
|
||||||
if (globalMnt) append(" -g")
|
|
||||||
append(" || ")
|
|
||||||
append("su")
|
|
||||||
if (globalMnt) append(" --mount-master")
|
|
||||||
append(" || ")
|
|
||||||
append("sh")
|
|
||||||
}
|
|
||||||
setCommands("sh", "-c", cmd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createRootShell(globalMnt: Boolean = false): Shell {
|
fun createRootShell(globalMnt: Boolean = false): Shell {
|
||||||
return runCatching {
|
Shell.enableVerboseLogging = BuildConfig.DEBUG
|
||||||
createRootShellBuilder(globalMnt).build()
|
val builder = Shell.Builder.create()
|
||||||
}.getOrElse { e ->
|
return try {
|
||||||
Log.w(TAG, "su failed: ", e)
|
if (globalMnt) {
|
||||||
Shell.Builder.create().apply {
|
builder.build(getKsuDaemonPath(), "debug", "su", "-g")
|
||||||
if (globalMnt) setFlags(Shell.FLAG_MOUNT_MASTER)
|
} else {
|
||||||
}.build()
|
builder.build(getKsuDaemonPath(), "debug", "su")
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Log.w(TAG, "ksu failed: ", e)
|
||||||
|
try {
|
||||||
|
if (globalMnt) {
|
||||||
|
builder.build("su", "-mm")
|
||||||
|
} else {
|
||||||
|
builder.build("su")
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Log.e(TAG, "su failed: ", e)
|
||||||
|
builder.build("sh")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun execKsud(args: String, newShell: Boolean = false): Boolean {
|
fun execKsud(args: String, newShell: Boolean = false): Boolean {
|
||||||
return if (newShell) {
|
return if (newShell) {
|
||||||
withNewRootShell {
|
withNewRootShell {
|
||||||
ShellUtils.fastCmdResult(this, "$ksuDaemonPath $args")
|
ShellUtils.fastCmdResult(this, "${getKsuDaemonPath()} $args")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ShellUtils.fastCmdResult("$ksuDaemonPath $args")
|
ShellUtils.fastCmdResult(getRootShell(), "${getKsuDaemonPath()} $args")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,15 +105,19 @@ fun install() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun listModules(): String {
|
fun listModules(): String {
|
||||||
|
val shell = getRootShell()
|
||||||
|
|
||||||
val out =
|
val out =
|
||||||
Shell.cmd("$ksuDaemonPath module list").to(ArrayList(), null).exec().out
|
shell.newJob().add("${getKsuDaemonPath()} module list").to(ArrayList(), null).exec().out
|
||||||
return out.joinToString("\n").ifBlank { "[]" }
|
return out.joinToString("\n").ifBlank { "[]" }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getModuleCount(): Int {
|
fun getModuleCount(): Int {
|
||||||
return runCatching {
|
val result = listModules()
|
||||||
JSONArray(listModules()).length()
|
runCatching {
|
||||||
}.getOrDefault(0)
|
val array = JSONArray(result)
|
||||||
|
return array.length()
|
||||||
|
}.getOrElse { return 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSuperuserCount(): Int {
|
fun getSuperuserCount(): Int {
|
||||||
@@ -172,7 +185,7 @@ fun flashModule(
|
|||||||
this?.copyTo(output)
|
this?.copyTo(output)
|
||||||
}
|
}
|
||||||
val cmd = "module install ${file.absolutePath}"
|
val cmd = "module install ${file.absolutePath}"
|
||||||
val result = flashWithIO("$ksuDaemonPath $cmd", onStdout, onStderr)
|
val result = flashWithIO("${getKsuDaemonPath()} $cmd", onStdout, onStderr)
|
||||||
Log.i("KernelSU", "install module $uri result: $result")
|
Log.i("KernelSU", "install module $uri result: $result")
|
||||||
|
|
||||||
file.delete()
|
file.delete()
|
||||||
@@ -199,7 +212,7 @@ fun runModuleAction(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val result = shell.newJob().add("$ksuDaemonPath module action $moduleId")
|
val result = shell.newJob().add("${getKsuDaemonPath()} module action $moduleId")
|
||||||
.to(stdoutCallback, stderrCallback).exec()
|
.to(stdoutCallback, stderrCallback).exec()
|
||||||
Log.i("KernelSU", "Module runAction result: $result")
|
Log.i("KernelSU", "Module runAction result: $result")
|
||||||
|
|
||||||
@@ -211,7 +224,7 @@ fun restoreBoot(
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libzakoboot.so")
|
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libzakoboot.so")
|
||||||
val result = flashWithIO(
|
val result = flashWithIO(
|
||||||
"$ksuDaemonPath boot-restore -f --magiskboot $magiskboot",
|
"${getKsuDaemonPath()} boot-restore -f --magiskboot $magiskboot",
|
||||||
onStdout,
|
onStdout,
|
||||||
onStderr
|
onStderr
|
||||||
)
|
)
|
||||||
@@ -224,7 +237,7 @@ fun uninstallPermanently(
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libzakoboot.so")
|
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libzakoboot.so")
|
||||||
val result =
|
val result =
|
||||||
flashWithIO("$ksuDaemonPath uninstall --magiskboot $magiskboot", onStdout, onStderr)
|
flashWithIO("${getKsuDaemonPath()} uninstall --magiskboot $magiskboot", onStdout, onStderr)
|
||||||
onFinish(result.isSuccess, result.code)
|
onFinish(result.isSuccess, result.code)
|
||||||
return result.isSuccess
|
return result.isSuccess
|
||||||
}
|
}
|
||||||
@@ -299,7 +312,7 @@ fun installBoot(
|
|||||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
||||||
cmd += " -o $downloadsDir"
|
cmd += " -o $downloadsDir"
|
||||||
|
|
||||||
val result = flashWithIO("$ksuDaemonPath $cmd", onStdout, onStderr)
|
val result = flashWithIO("${getKsuDaemonPath()} $cmd", onStdout, onStderr)
|
||||||
Log.i("KernelSU", "install boot result: ${result.isSuccess}")
|
Log.i("KernelSU", "install boot result: ${result.isSuccess}")
|
||||||
|
|
||||||
bootFile?.delete()
|
bootFile?.delete()
|
||||||
@@ -311,17 +324,22 @@ fun installBoot(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun reboot(reason: String = "") {
|
fun reboot(reason: String = "") {
|
||||||
|
val shell = getRootShell()
|
||||||
if (reason == "recovery") {
|
if (reason == "recovery") {
|
||||||
// KEYCODE_POWER = 26, hide incorrect "Factory data reset" message
|
// KEYCODE_POWER = 26, hide incorrect "Factory data reset" message
|
||||||
ShellUtils.fastCmdResult("/system/bin/input keyevent 26")
|
ShellUtils.fastCmd(shell, "/system/bin/input keyevent 26")
|
||||||
}
|
}
|
||||||
ShellUtils.fastCmdResult("/system/bin/svc power reboot $reason || /system/bin/reboot $reason")
|
ShellUtils.fastCmd(shell, "/system/bin/svc power reboot $reason || /system/bin/reboot $reason")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun rootAvailable() = Shell.isAppGrantedRoot() == true
|
fun rootAvailable(): Boolean {
|
||||||
|
val shell = getRootShell()
|
||||||
|
return shell.isRoot
|
||||||
|
}
|
||||||
|
|
||||||
fun isAbDevice(): Boolean {
|
fun isAbDevice(): Boolean {
|
||||||
return ShellUtils.fastCmd("getprop ro.build.ab_update").trim().toBoolean()
|
val shell = getRootShell()
|
||||||
|
return ShellUtils.fastCmd(shell, "getprop ro.build.ab_update").trim().toBoolean()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isInitBoot(): Boolean {
|
fun isInitBoot(): Boolean {
|
||||||
@@ -329,77 +347,91 @@ fun isInitBoot(): Boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getCurrentKmi(): String = withContext(Dispatchers.IO) {
|
suspend fun getCurrentKmi(): String = withContext(Dispatchers.IO) {
|
||||||
|
val shell = getRootShell()
|
||||||
val cmd = "boot-info current-kmi"
|
val cmd = "boot-info current-kmi"
|
||||||
ShellUtils.fastCmd("$ksuDaemonPath $cmd")
|
ShellUtils.fastCmd(shell, "${getKsuDaemonPath()} $cmd")
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getSupportedKmis(): List<String> = withContext(Dispatchers.IO) {
|
suspend fun getSupportedKmis(): List<String> = withContext(Dispatchers.IO) {
|
||||||
|
val shell = getRootShell()
|
||||||
val cmd = "boot-info supported-kmi"
|
val cmd = "boot-info supported-kmi"
|
||||||
val out = Shell.cmd("$ksuDaemonPath $cmd").to(ArrayList(), null).exec().out
|
val out = shell.newJob().add("${getKsuDaemonPath()} $cmd").to(ArrayList(), null).exec().out
|
||||||
out.filter { it.isNotBlank() }.map { it.trim() }
|
out.filter { it.isNotBlank() }.map { it.trim() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasMagisk(): Boolean {
|
fun hasMagisk(): Boolean {
|
||||||
val result = ShellUtils.fastCmdResult("which magisk")
|
val shell = getRootShell(true)
|
||||||
Log.i(TAG, "has magisk: $result")
|
val result = shell.newJob().add("which magisk").exec()
|
||||||
return result
|
Log.i(TAG, "has magisk: ${result.isSuccess}")
|
||||||
|
return result.isSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isSepolicyValid(rules: String?): Boolean {
|
fun isSepolicyValid(rules: String?): Boolean {
|
||||||
if (rules == null) {
|
if (rules == null) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
val shell = getRootShell()
|
||||||
val result =
|
val result =
|
||||||
Shell.cmd("$ksuDaemonPath sepolicy check '$rules'").to(ArrayList(), null)
|
shell.newJob().add("${getKsuDaemonPath()} sepolicy check '$rules'").to(ArrayList(), null)
|
||||||
.exec()
|
.exec()
|
||||||
return result.isSuccess
|
return result.isSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSepolicy(pkg: String): String {
|
fun getSepolicy(pkg: String): String {
|
||||||
|
val shell = getRootShell()
|
||||||
val result =
|
val result =
|
||||||
Shell.cmd("$ksuDaemonPath profile get-sepolicy $pkg").to(ArrayList(), null)
|
shell.newJob().add("${getKsuDaemonPath()} profile get-sepolicy $pkg").to(ArrayList(), null)
|
||||||
.exec()
|
.exec()
|
||||||
Log.i(TAG, "code: ${result.code}, out: ${result.out}, err: ${result.err}")
|
Log.i(TAG, "code: ${result.code}, out: ${result.out}, err: ${result.err}")
|
||||||
return result.out.joinToString("\n")
|
return result.out.joinToString("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSepolicy(pkg: String, rules: String): Boolean {
|
fun setSepolicy(pkg: String, rules: String): Boolean {
|
||||||
val result = Shell.cmd("$ksuDaemonPath profile set-sepolicy $pkg '$rules'")
|
val shell = getRootShell()
|
||||||
|
val result = shell.newJob().add("${getKsuDaemonPath()} profile set-sepolicy $pkg '$rules'")
|
||||||
.to(ArrayList(), null).exec()
|
.to(ArrayList(), null).exec()
|
||||||
Log.i(TAG, "set sepolicy result: ${result.code}")
|
Log.i(TAG, "set sepolicy result: ${result.code}")
|
||||||
return result.isSuccess
|
return result.isSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
fun listAppProfileTemplates(): List<String> {
|
fun listAppProfileTemplates(): List<String> {
|
||||||
return Shell.cmd("$ksuDaemonPath profile list-templates").to(ArrayList(), null)
|
val shell = getRootShell()
|
||||||
|
return shell.newJob().add("${getKsuDaemonPath()} profile list-templates").to(ArrayList(), null)
|
||||||
.exec().out
|
.exec().out
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAppProfileTemplate(id: String): String {
|
fun getAppProfileTemplate(id: String): String {
|
||||||
return Shell.cmd("$ksuDaemonPath profile get-template '${id}'")
|
val shell = getRootShell()
|
||||||
|
return shell.newJob().add("${getKsuDaemonPath()} profile get-template '${id}'")
|
||||||
.to(ArrayList(), null).exec().out.joinToString("\n")
|
.to(ArrayList(), null).exec().out.joinToString("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setAppProfileTemplate(id: String, template: String): Boolean {
|
fun setAppProfileTemplate(id: String, template: String): Boolean {
|
||||||
|
val shell = getRootShell()
|
||||||
val escapedTemplate = template.replace("\"", "\\\"")
|
val escapedTemplate = template.replace("\"", "\\\"")
|
||||||
val cmd = """$ksuDaemonPath profile set-template "$id" "$escapedTemplate'""""
|
val cmd = """${getKsuDaemonPath()} profile set-template "$id" "$escapedTemplate'""""
|
||||||
return Shell.cmd(cmd)
|
return shell.newJob().add(cmd)
|
||||||
.to(ArrayList(), null).exec().isSuccess
|
.to(ArrayList(), null).exec().isSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteAppProfileTemplate(id: String): Boolean {
|
fun deleteAppProfileTemplate(id: String): Boolean {
|
||||||
return Shell.cmd("$ksuDaemonPath profile delete-template '${id}'")
|
val shell = getRootShell()
|
||||||
|
return shell.newJob().add("${getKsuDaemonPath()} profile delete-template '${id}'")
|
||||||
.to(ArrayList(), null).exec().isSuccess
|
.to(ArrayList(), null).exec().isSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
fun forceStopApp(packageName: String) {
|
fun forceStopApp(packageName: String) {
|
||||||
val result = Shell.cmd("am force-stop $packageName").exec()
|
val shell = getRootShell()
|
||||||
|
val result = shell.newJob().add("am force-stop $packageName").exec()
|
||||||
Log.i(TAG, "force stop $packageName result: $result")
|
Log.i(TAG, "force stop $packageName result: $result")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun launchApp(packageName: String) {
|
fun launchApp(packageName: String) {
|
||||||
|
|
||||||
|
val shell = getRootShell()
|
||||||
val result =
|
val result =
|
||||||
Shell.cmd("cmd package resolve-activity --brief $packageName | tail -n 1 | xargs cmd activity start-activity -n")
|
shell.newJob()
|
||||||
|
.add("cmd package resolve-activity --brief $packageName | tail -n 1 | xargs cmd activity start-activity -n")
|
||||||
.exec()
|
.exec()
|
||||||
Log.i(TAG, "launch $packageName result: $result")
|
Log.i(TAG, "launch $packageName result: $result")
|
||||||
}
|
}
|
||||||
@@ -409,90 +441,131 @@ fun restartApp(packageName: String) {
|
|||||||
launchApp(packageName)
|
launchApp(packageName)
|
||||||
}
|
}
|
||||||
|
|
||||||
val suSFSDaemonPath by lazy {
|
fun getSuSFSDaemonPath(): String {
|
||||||
"${ksuApp.applicationInfo.nativeLibraryDir}${File.separator}libzakozakozako.so"
|
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libzakozakozako.so"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSuSFS(): String {
|
fun getSuSFS(): String {
|
||||||
return ShellUtils.fastCmd("$suSFSDaemonPath support")
|
val shell = getRootShell()
|
||||||
|
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} support")
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSuSFSVersion(): String {
|
fun getSuSFSVersion(): String {
|
||||||
return ShellUtils.fastCmd("$suSFSDaemonPath version")
|
val shell = getRootShell()
|
||||||
|
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} version")
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSuSFSVariant(): String {
|
fun getSuSFSVariant(): String {
|
||||||
return ShellUtils.fastCmd("$suSFSDaemonPath variant")
|
val shell = getRootShell()
|
||||||
|
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} variant")
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSuSFSFeatures(): String {
|
fun getSuSFSFeatures(): String {
|
||||||
return ShellUtils.fastCmd("$suSFSDaemonPath features")
|
val shell = getRootShell()
|
||||||
|
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} features")
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun susfsSUS_SU_0(): String {
|
fun susfsSUS_SU_0(): String {
|
||||||
return ShellUtils.fastCmd("$suSFSDaemonPath sus_su 0")
|
val shell = getRootShell()
|
||||||
|
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} sus_su 0")
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun susfsSUS_SU_2(): String {
|
fun susfsSUS_SU_2(): String {
|
||||||
return ShellUtils.fastCmd("$suSFSDaemonPath sus_su 2")
|
val shell = getRootShell()
|
||||||
|
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} sus_su 2")
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun susfsSUS_SU_Mode(): String {
|
fun susfsSUS_SU_Mode(): String {
|
||||||
return ShellUtils.fastCmd("$suSFSDaemonPath sus_su mode")
|
val shell = getRootShell()
|
||||||
|
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} sus_su mode")
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
val kpmmgrPath by lazy {
|
fun getKpmmgrPath(): String {
|
||||||
"${ksuApp.applicationInfo.nativeLibraryDir}${File.separator}libkpmmgr.so"
|
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libkpmmgr.so"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun loadKpmModule(path: String, args: String? = null): String {
|
fun loadKpmModule(path: String, args: String? = null): String {
|
||||||
return ShellUtils.fastCmd("$kpmmgrPath load $path ${args ?: ""}")
|
val shell = getRootShell()
|
||||||
|
val cmd = "${getKpmmgrPath()} load $path ${args ?: ""}"
|
||||||
|
return ShellUtils.fastCmd(shell, cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unloadKpmModule(name: String): String {
|
fun unloadKpmModule(name: String): String {
|
||||||
return ShellUtils.fastCmd("$kpmmgrPath unload $name")
|
val shell = getRootShell()
|
||||||
|
val cmd = "${getKpmmgrPath()} unload $name"
|
||||||
|
return ShellUtils.fastCmd(shell, cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getKpmModuleCount(): Int {
|
fun getKpmModuleCount(): Int {
|
||||||
val result = ShellUtils.fastCmd("$kpmmgrPath num")
|
val shell = getRootShell()
|
||||||
|
val cmd = "${getKpmmgrPath()} num"
|
||||||
|
val result = ShellUtils.fastCmd(shell, cmd)
|
||||||
return result.trim().toIntOrNull() ?: 0
|
return result.trim().toIntOrNull() ?: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun runCmd(cmd: String): String {
|
fun runCmd(shell: Shell, cmd: String): String {
|
||||||
return Shell.cmd(cmd)
|
return shell.newJob()
|
||||||
|
.add(cmd)
|
||||||
.to(mutableListOf<String>(), null)
|
.to(mutableListOf<String>(), null)
|
||||||
.exec().out
|
.exec().out
|
||||||
.joinToString("\n")
|
.joinToString("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun listKpmModules(): String {
|
fun listKpmModules(): String {
|
||||||
return runCmd("$kpmmgrPath list").trim()
|
val shell = getRootShell()
|
||||||
|
val cmd = "${getKpmmgrPath()} list"
|
||||||
|
return try {
|
||||||
|
runCmd(shell, cmd).trim()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to list KPM modules", e)
|
||||||
|
""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getKpmModuleInfo(name: String): String {
|
fun getKpmModuleInfo(name: String): String {
|
||||||
return runCmd("$kpmmgrPath info $name").trim()
|
val shell = getRootShell()
|
||||||
|
val cmd = "${getKpmmgrPath()} info $name"
|
||||||
|
return try {
|
||||||
|
runCmd(shell, cmd).trim()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to get KPM module info: $name", e)
|
||||||
|
""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun controlKpmModule(name: String, args: String? = null): Int {
|
fun controlKpmModule(name: String, args: String? = null): Int {
|
||||||
val result = runCmd("""$kpmmgrPath control $name "${args ?: ""}"""")
|
val shell = getRootShell()
|
||||||
|
val cmd = """${getKpmmgrPath()} control $name "${args ?: ""}""""
|
||||||
|
val result = runCmd(shell, cmd)
|
||||||
return result.trim().toIntOrNull() ?: -1
|
return result.trim().toIntOrNull() ?: -1
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getKpmVersion(): String {
|
fun getKpmVersion(): String {
|
||||||
return ShellUtils.fastCmd("$kpmmgrPath version").trim()
|
val shell = getRootShell()
|
||||||
|
val cmd = "${getKpmmgrPath()} version"
|
||||||
|
val result = ShellUtils.fastCmd(shell, cmd)
|
||||||
|
return result.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getZygiskImplement(): String {
|
fun getZygiskImplement(): String {
|
||||||
|
val shell = getRootShell()
|
||||||
val zygiskPath = "/data/adb/modules/zygisksu"
|
val zygiskPath = "/data/adb/modules/zygisksu"
|
||||||
val rezygiskPath = "/data/adb/modules/rezygisk"
|
val rezygiskPath = "/data/adb/modules/rezygisk"
|
||||||
val result = when {
|
val result = if (ShellUtils.fastCmdResult(shell, "test -f $zygiskPath/module.prop && test ! -f $zygiskPath/disable")) {
|
||||||
SuFile(zygiskPath, "module.prop").exists() && !SuFile(zygiskPath, "disable").exists() ->
|
ShellUtils.fastCmd(shell, "grep '^name=' $zygiskPath/module.prop | cut -d'=' -f2")
|
||||||
ShellUtils.fastCmd("grep '^name=' $zygiskPath/module.prop | cut -d'=' -f2")
|
} else if (ShellUtils.fastCmdResult(shell, "test -f $rezygiskPath/module.prop && test ! -f $rezygiskPath/disable")) {
|
||||||
SuFile(rezygiskPath, "module.prop").exists() && !SuFile(rezygiskPath, "disable").exists() ->
|
ShellUtils.fastCmd(shell, "grep '^name=' $rezygiskPath/module.prop | cut -d'=' -f2")
|
||||||
ShellUtils.fastCmd("grep '^name=' $rezygiskPath/module.prop | cut -d'=' -f2")
|
} else {
|
||||||
else -> "None"
|
"None"
|
||||||
}.trim()
|
}
|
||||||
Log.i(TAG, "Zygisk implement: $result")
|
Log.i(TAG, "Zygisk implement: $result")
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,9 @@ package com.sukisu.ultra.ui.util
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.system.Os
|
import android.system.Os
|
||||||
|
import com.topjohnwu.superuser.ShellUtils
|
||||||
import com.sukisu.ultra.Natives
|
import com.sukisu.ultra.Natives
|
||||||
import com.sukisu.ultra.ui.screen.getManagerVersion
|
import com.sukisu.ultra.ui.screen.getManagerVersion
|
||||||
import com.topjohnwu.superuser.Shell
|
|
||||||
import com.topjohnwu.superuser.ShellUtils
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileWriter
|
import java.io.FileWriter
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
@@ -39,28 +38,30 @@ fun getBugreportFile(context: Context): File {
|
|||||||
val bootConfig = File(bugreportDir, "boot_config.txt")
|
val bootConfig = File(bugreportDir, "boot_config.txt")
|
||||||
val kernelConfig = File(bugreportDir, "defconfig.gz")
|
val kernelConfig = File(bugreportDir, "defconfig.gz")
|
||||||
|
|
||||||
Shell.cmd("dmesg > ${dmesgFile.absolutePath}").exec()
|
val shell = getRootShell(true)
|
||||||
Shell.cmd("logcat -d > ${logcatFile.absolutePath}").exec()
|
|
||||||
Shell.cmd("tar -czf ${tombstonesFile.absolutePath} -C /data/tombstones .").exec()
|
|
||||||
Shell.cmd("tar -czf ${dropboxFile.absolutePath} -C /data/system/dropbox .").exec()
|
|
||||||
Shell.cmd("tar -czf ${pstoreFile.absolutePath} -C /sys/fs/pstore .").exec()
|
|
||||||
Shell.cmd("tar -czf ${diagFile.absolutePath} -C /data/vendor/diag . --exclude=./minidump.gz").exec()
|
|
||||||
Shell.cmd("tar -czf ${oplusFile.absolutePath} -C /mnt/oplus/op2/media/log/boot_log/ .").exec()
|
|
||||||
Shell.cmd("tar -czf ${bootlogFile.absolutePath} -C /data/adb/ksu/log .").exec()
|
|
||||||
|
|
||||||
Shell.cmd("cat /proc/1/mountinfo > ${mountsFile.absolutePath}").exec()
|
shell.newJob().add("dmesg > ${dmesgFile.absolutePath}").exec()
|
||||||
Shell.cmd("cat /proc/filesystems > ${fileSystemsFile.absolutePath}").exec()
|
shell.newJob().add("logcat -d > ${logcatFile.absolutePath}").exec()
|
||||||
Shell.cmd("busybox tree /data/adb > ${adbFileTree.absolutePath}").exec()
|
shell.newJob().add("tar -czf ${tombstonesFile.absolutePath} -C /data/tombstones .").exec()
|
||||||
Shell.cmd("ls -alRZ /data/adb > ${adbFileDetails.absolutePath}").exec()
|
shell.newJob().add("tar -czf ${dropboxFile.absolutePath} -C /data/system/dropbox .").exec()
|
||||||
Shell.cmd("du -sh /data/adb/ksu/* > ${ksuFileSize.absolutePath}").exec()
|
shell.newJob().add("tar -czf ${pstoreFile.absolutePath} -C /sys/fs/pstore .").exec()
|
||||||
Shell.cmd("cp /data/system/packages.list ${appListFile.absolutePath}").exec()
|
shell.newJob().add("tar -czf ${diagFile.absolutePath} -C /data/vendor/diag . --exclude=./minidump.gz").exec()
|
||||||
Shell.cmd("getprop > ${propFile.absolutePath}").exec()
|
shell.newJob().add("tar -czf ${oplusFile.absolutePath} -C /mnt/oplus/op2/media/log/boot_log/ .").exec()
|
||||||
Shell.cmd("cp /data/adb/ksu/.allowlist ${allowListFile.absolutePath}").exec()
|
shell.newJob().add("tar -czf ${bootlogFile.absolutePath} -C /data/adb/ksu/log .").exec()
|
||||||
Shell.cmd("cp /proc/modules ${procModules.absolutePath}").exec()
|
|
||||||
Shell.cmd("cp /proc/bootconfig ${bootConfig.absolutePath}").exec()
|
|
||||||
Shell.cmd("cp /proc/config.gz ${kernelConfig.absolutePath}").exec()
|
|
||||||
|
|
||||||
val selinux = ShellUtils.fastCmd("getenforce")
|
shell.newJob().add("cat /proc/1/mountinfo > ${mountsFile.absolutePath}").exec()
|
||||||
|
shell.newJob().add("cat /proc/filesystems > ${fileSystemsFile.absolutePath}").exec()
|
||||||
|
shell.newJob().add("busybox tree /data/adb > ${adbFileTree.absolutePath}").exec()
|
||||||
|
shell.newJob().add("ls -alRZ /data/adb > ${adbFileDetails.absolutePath}").exec()
|
||||||
|
shell.newJob().add("du -sh /data/adb/ksu/* > ${ksuFileSize.absolutePath}").exec()
|
||||||
|
shell.newJob().add("cp /data/system/packages.list ${appListFile.absolutePath}").exec()
|
||||||
|
shell.newJob().add("getprop > ${propFile.absolutePath}").exec()
|
||||||
|
shell.newJob().add("cp /data/adb/ksu/.allowlist ${allowListFile.absolutePath}").exec()
|
||||||
|
shell.newJob().add("cp /proc/modules ${procModules.absolutePath}").exec()
|
||||||
|
shell.newJob().add("cp /proc/bootconfig ${bootConfig.absolutePath}").exec()
|
||||||
|
shell.newJob().add("cp /proc/config.gz ${kernelConfig.absolutePath}").exec()
|
||||||
|
|
||||||
|
val selinux = ShellUtils.fastCmd(shell, "getenforce")
|
||||||
|
|
||||||
// basic information
|
// basic information
|
||||||
val buildInfo = File(bugreportDir, "basic.txt")
|
val buildInfo = File(bugreportDir, "basic.txt")
|
||||||
@@ -101,9 +102,9 @@ fun getBugreportFile(context: Context): File {
|
|||||||
|
|
||||||
val targetFile = File(context.cacheDir, "KernelSU_bugreport_${current}.tar.gz")
|
val targetFile = File(context.cacheDir, "KernelSU_bugreport_${current}.tar.gz")
|
||||||
|
|
||||||
Shell.cmd("tar czf ${targetFile.absolutePath} -C ${bugreportDir.absolutePath} .").exec()
|
shell.newJob().add("tar czf ${targetFile.absolutePath} -C ${bugreportDir.absolutePath} .").exec()
|
||||||
Shell.cmd("rm -rf ${bugreportDir.absolutePath}").exec()
|
shell.newJob().add("rm -rf ${bugreportDir.absolutePath}").exec()
|
||||||
Shell.cmd("chmod 0644 ${targetFile.absolutePath}").exec()
|
shell.newJob().add("chmod 0644 ${targetFile.absolutePath}").exec()
|
||||||
|
|
||||||
return targetFile
|
return targetFile
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.content.Context
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.sukisu.ultra.Natives
|
import com.sukisu.ultra.Natives
|
||||||
import com.topjohnwu.superuser.Shell
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
|
|
||||||
@@ -125,17 +124,18 @@ object ModuleVerificationManager {
|
|||||||
// 为指定模块创建验证标志文件
|
// 为指定模块创建验证标志文件
|
||||||
fun createVerificationFlag(moduleId: String): Boolean {
|
fun createVerificationFlag(moduleId: String): Boolean {
|
||||||
return try {
|
return try {
|
||||||
|
val shell = getRootShell()
|
||||||
val flagFilePath = "$VERIFICATION_FLAGS_DIR/$moduleId"
|
val flagFilePath = "$VERIFICATION_FLAGS_DIR/$moduleId"
|
||||||
|
|
||||||
// 确保目录存在
|
// 确保目录存在
|
||||||
val createDirCommand = "mkdir -p '$VERIFICATION_FLAGS_DIR'"
|
val createDirCommand = "mkdir -p '$VERIFICATION_FLAGS_DIR'"
|
||||||
Shell.cmd(createDirCommand).exec()
|
shell.newJob().add(createDirCommand).exec()
|
||||||
|
|
||||||
// 创建验证标志文件,写入验证时间戳
|
// 创建验证标志文件,写入验证时间戳
|
||||||
val timestamp = System.currentTimeMillis()
|
val timestamp = System.currentTimeMillis()
|
||||||
val command = "echo '$timestamp' > '$flagFilePath'"
|
val command = "echo '$timestamp' > '$flagFilePath'"
|
||||||
|
|
||||||
val result = Shell.cmd(command).exec()
|
val result = shell.newJob().add(command).exec()
|
||||||
|
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
Log.d(TAG, "验证标志文件创建成功: $flagFilePath")
|
Log.d(TAG, "验证标志文件创建成功: $flagFilePath")
|
||||||
@@ -152,10 +152,11 @@ object ModuleVerificationManager {
|
|||||||
|
|
||||||
fun removeVerificationFlag(moduleId: String): Boolean {
|
fun removeVerificationFlag(moduleId: String): Boolean {
|
||||||
return try {
|
return try {
|
||||||
|
val shell = getRootShell()
|
||||||
val flagFilePath = "$VERIFICATION_FLAGS_DIR/$moduleId"
|
val flagFilePath = "$VERIFICATION_FLAGS_DIR/$moduleId"
|
||||||
|
|
||||||
val command = "rm -f '$flagFilePath'"
|
val command = "rm -f '$flagFilePath'"
|
||||||
val result = Shell.cmd(command).exec()
|
val result = shell.newJob().add(command).exec()
|
||||||
|
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
Log.d(TAG, "验证标志文件移除成功: $flagFilePath")
|
Log.d(TAG, "验证标志文件移除成功: $flagFilePath")
|
||||||
@@ -172,10 +173,11 @@ object ModuleVerificationManager {
|
|||||||
|
|
||||||
fun getVerificationTimestamp(moduleId: String): Long {
|
fun getVerificationTimestamp(moduleId: String): Long {
|
||||||
return try {
|
return try {
|
||||||
|
val shell = getRootShell()
|
||||||
val flagFilePath = "$VERIFICATION_FLAGS_DIR/$moduleId"
|
val flagFilePath = "$VERIFICATION_FLAGS_DIR/$moduleId"
|
||||||
|
|
||||||
val command = "cat '$flagFilePath' 2>/dev/null || echo '0'"
|
val command = "cat '$flagFilePath' 2>/dev/null || echo '0'"
|
||||||
val result = Shell.cmd(command).to(ArrayList(), null).exec()
|
val result = shell.newJob().add(command).to(ArrayList(), null).exec()
|
||||||
|
|
||||||
if (result.isSuccess && result.out.isNotEmpty()) {
|
if (result.isSuccess && result.out.isNotEmpty()) {
|
||||||
val timestampStr = result.out.firstOrNull()?.trim() ?: "0"
|
val timestampStr = result.out.firstOrNull()?.trim() ?: "0"
|
||||||
@@ -193,11 +195,12 @@ object ModuleVerificationManager {
|
|||||||
if (moduleIds.isEmpty()) return emptyMap()
|
if (moduleIds.isEmpty()) return emptyMap()
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
|
val shell = getRootShell()
|
||||||
val result = mutableMapOf<String, Boolean>()
|
val result = mutableMapOf<String, Boolean>()
|
||||||
|
|
||||||
// 确保目录存在
|
// 确保目录存在
|
||||||
val createDirCommand = "mkdir -p '$VERIFICATION_FLAGS_DIR'"
|
val createDirCommand = "mkdir -p '$VERIFICATION_FLAGS_DIR'"
|
||||||
Shell.cmd(createDirCommand).exec()
|
shell.newJob().add(createDirCommand).exec()
|
||||||
|
|
||||||
// 批量检查所有模块的验证标志文件
|
// 批量检查所有模块的验证标志文件
|
||||||
val commands = moduleIds.map { moduleId ->
|
val commands = moduleIds.map { moduleId ->
|
||||||
@@ -205,7 +208,7 @@ object ModuleVerificationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val command = commands.joinToString(" && ")
|
val command = commands.joinToString(" && ")
|
||||||
val shellResult = Shell.cmd(command).to(ArrayList(), null).exec()
|
val shellResult = shell.newJob().add(command).to(ArrayList(), null).exec()
|
||||||
|
|
||||||
if (shellResult.isSuccess) {
|
if (shellResult.isSuccess) {
|
||||||
shellResult.out.forEach { line ->
|
shellResult.out.forEach { line ->
|
||||||
|
|||||||
@@ -7,17 +7,22 @@ import android.content.pm.ApplicationInfo
|
|||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.content.edit
|
|
||||||
import com.dergoogler.mmrl.platform.Platform.Companion.context
|
import com.dergoogler.mmrl.platform.Platform.Companion.context
|
||||||
import com.sukisu.ultra.Natives
|
import com.sukisu.ultra.Natives
|
||||||
import com.sukisu.ultra.R
|
import com.sukisu.ultra.R
|
||||||
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
|
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.Dispatchers
|
||||||
import org.json.JSONObject
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.awaitAll
|
||||||
|
import org.json.JSONObject
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@@ -425,10 +430,12 @@ object SuSFSManager {
|
|||||||
async(Dispatchers.IO) {
|
async(Dispatchers.IO) {
|
||||||
val dataPath = "$MEDIA_DATA_PATH/${appInfo.packageName}"
|
val dataPath = "$MEDIA_DATA_PATH/${appInfo.packageName}"
|
||||||
val exists = try {
|
val exists = try {
|
||||||
|
val shell = getRootShell()
|
||||||
val outputList = mutableListOf<String>()
|
val outputList = mutableListOf<String>()
|
||||||
val errorList = mutableListOf<String>()
|
val errorList = mutableListOf<String>()
|
||||||
|
|
||||||
val result = Shell.cmd("[ -d \"$dataPath\" ] && echo 'exists' || echo 'not_exists'")
|
val result = shell.newJob()
|
||||||
|
.add("[ -d \"$dataPath\" ] && echo 'exists' || echo 'not_exists'")
|
||||||
.to(outputList, errorList)
|
.to(outputList, errorList)
|
||||||
.exec()
|
.exec()
|
||||||
|
|
||||||
|
|||||||
@@ -7,26 +7,26 @@ import androidx.compose.runtime.derivedStateOf
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.core.content.edit
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.dergoogler.mmrl.platform.model.ModuleConfig
|
import com.dergoogler.mmrl.platform.model.ModuleConfig
|
||||||
import com.dergoogler.mmrl.platform.model.ModuleConfig.Companion.asModuleConfig
|
import com.dergoogler.mmrl.platform.model.ModuleConfig.Companion.asModuleConfig
|
||||||
import com.sukisu.ultra.ui.util.HanziToPinyin
|
|
||||||
import com.sukisu.ultra.ui.util.ModuleVerificationManager
|
|
||||||
import com.sukisu.ultra.ui.util.listModules
|
|
||||||
import com.topjohnwu.superuser.Shell
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import com.sukisu.ultra.ui.util.HanziToPinyin
|
||||||
|
import com.sukisu.ultra.ui.util.listModules
|
||||||
|
import com.sukisu.ultra.ui.util.getRootShell
|
||||||
|
import com.sukisu.ultra.ui.util.ModuleVerificationManager
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.text.Collator
|
import java.text.Collator
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import java.util.*
|
import java.util.Locale
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.math.log10
|
import kotlin.math.log10
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
import androidx.core.content.edit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ShirkNeko
|
* @author ShirkNeko
|
||||||
@@ -452,8 +452,9 @@ class ModuleSizeCache(context: Context) {
|
|||||||
*/
|
*/
|
||||||
private fun calculateModuleFolderSize(dirId: String): Long {
|
private fun calculateModuleFolderSize(dirId: String): Long {
|
||||||
return try {
|
return try {
|
||||||
|
val shell = getRootShell()
|
||||||
val command = "du -sb /data/adb/modules/$dirId"
|
val command = "du -sb /data/adb/modules/$dirId"
|
||||||
val result = Shell.cmd(command).to(ArrayList(), null).exec()
|
val result = shell.newJob().add(command).to(ArrayList(), null).exec()
|
||||||
|
|
||||||
if (result.isSuccess && result.out.isNotEmpty()) {
|
if (result.isSuccess && result.out.isNotEmpty()) {
|
||||||
val sizeStr = result.out.firstOrNull()?.split("\t")?.firstOrNull()
|
val sizeStr = result.out.firstOrNull()?.split("\t")?.firstOrNull()
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
package io.sukisu.ultra;
|
package io.sukisu.ultra;
|
||||||
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import com.sukisu.ultra.ui.util.KsuCli;
|
||||||
|
|
||||||
public class UltraShellHelper {
|
public class UltraShellHelper {
|
||||||
public static String runCmd(String cmds) {
|
public static String runCmd(String cmds) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for(String str : Shell.cmd(cmds)
|
for(String str : KsuCli.INSTANCE.getGLOBAL_MNT_SHELL()
|
||||||
|
.newJob()
|
||||||
|
.add(cmds)
|
||||||
.to(new ArrayList<>(), null)
|
.to(new ArrayList<>(), null)
|
||||||
.exec()
|
.exec()
|
||||||
.getOut()) {
|
.getOut()) {
|
||||||
@@ -16,6 +18,11 @@ public class UltraShellHelper {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isPathExists(String path) {
|
||||||
|
String result = runCmd("test -f '" + path + "' && echo 'exists'");
|
||||||
|
return result.contains("exists");
|
||||||
|
}
|
||||||
|
|
||||||
public static void CopyFileTo(String path, String target) {
|
public static void CopyFileTo(String path, String target) {
|
||||||
runCmd("cp -f '" + path + "' '" + target + "' 2>&1");
|
runCmd("cp -f '" + path + "' '" + target + "' 2>&1");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package io.sukisu.ultra;
|
package io.sukisu.ultra;
|
||||||
|
|
||||||
import com.topjohnwu.superuser.io.SuFile;
|
|
||||||
|
|
||||||
import static com.sukisu.ultra.ui.util.KsuCliKt.getKpmmgrPath;
|
import static com.sukisu.ultra.ui.util.KsuCliKt.getKpmmgrPath;
|
||||||
import static com.sukisu.ultra.ui.util.KsuCliKt.getSuSFSDaemonPath;
|
import static com.sukisu.ultra.ui.util.KsuCliKt.getSuSFSDaemonPath;
|
||||||
|
|
||||||
@@ -13,17 +11,15 @@ public class UltraToolInstall {
|
|||||||
@SuppressLint("SetWorldReadable")
|
@SuppressLint("SetWorldReadable")
|
||||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
public static void tryToInstall() {
|
public static void tryToInstall() {
|
||||||
SuFile KpmmgrFile = new SuFile(OUTSIDE_KPMMGR_PATH);
|
String kpmmgrPath = getKpmmgrPath();
|
||||||
if (KpmmgrFile.exists()) {
|
if (UltraShellHelper.isPathExists(OUTSIDE_KPMMGR_PATH)) {
|
||||||
UltraShellHelper.CopyFileTo(getKpmmgrPath(), OUTSIDE_KPMMGR_PATH);
|
UltraShellHelper.CopyFileTo(kpmmgrPath, OUTSIDE_KPMMGR_PATH);
|
||||||
KpmmgrFile.setReadable(true, false);
|
UltraShellHelper.runCmd("chmod a+rx " + OUTSIDE_KPMMGR_PATH);
|
||||||
KpmmgrFile.setExecutable(true, false);
|
|
||||||
}
|
}
|
||||||
SuFile SuSFSDaemonFile = new SuFile(OUTSIDE_SUSFSD_PATH);
|
String SuSFSDaemonPath = getSuSFSDaemonPath();
|
||||||
if (SuSFSDaemonFile.exists()) {
|
if (UltraShellHelper.isPathExists(OUTSIDE_SUSFSD_PATH)) {
|
||||||
UltraShellHelper.CopyFileTo(getSuSFSDaemonPath(), OUTSIDE_SUSFSD_PATH);
|
UltraShellHelper.CopyFileTo(SuSFSDaemonPath, OUTSIDE_SUSFSD_PATH);
|
||||||
SuSFSDaemonFile.setReadable(true, false);
|
UltraShellHelper.runCmd("chmod a+rx " + OUTSIDE_SUSFSD_PATH);
|
||||||
SuSFSDaemonFile.setExecutable(true, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user