ksud: backup stock image and use it when restore (#1619)
This commit is contained in:
@@ -12,9 +12,21 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material.icons.filled.Save
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.key.Key
|
||||
@@ -37,9 +49,17 @@ import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.util.installBoot
|
||||
import me.weishu.kernelsu.ui.util.installModule
|
||||
import me.weishu.kernelsu.ui.util.reboot
|
||||
import me.weishu.kernelsu.ui.util.restoreBoot
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
enum class FlashingStatus {
|
||||
FLASHING,
|
||||
SUCCESS,
|
||||
FAILED
|
||||
}
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
@@ -57,19 +77,24 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val scrollState = rememberScrollState()
|
||||
var flashing by rememberSaveable {
|
||||
mutableStateOf(FlashingStatus.FLASHING)
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (text.isNotEmpty()) {
|
||||
return@LaunchedEffect
|
||||
}
|
||||
withContext(Dispatchers.IO) {
|
||||
flashIt(flashIt, onFinish = { showReboot ->
|
||||
flashIt(flashIt, onFinish = { showReboot, code ->
|
||||
if (code != 0) {
|
||||
text += "Error: exit code = $code.\nPlease save and check the log.\n"
|
||||
}
|
||||
if (showReboot) {
|
||||
for (i in 0..2) {
|
||||
text += "\n"
|
||||
}
|
||||
text += "\n\n\n"
|
||||
showFloatAction = true
|
||||
}
|
||||
flashing = if (code == 0) FlashingStatus.SUCCESS else FlashingStatus.FAILED
|
||||
}, onStdout = {
|
||||
text += "$it\n"
|
||||
logContent.append(it).append("\n")
|
||||
@@ -82,6 +107,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(
|
||||
flashing,
|
||||
onBack = {
|
||||
navigator.popBackStack()
|
||||
},
|
||||
@@ -146,10 +172,12 @@ sealed class FlashIt : Parcelable {
|
||||
FlashIt()
|
||||
|
||||
data class FlashModule(val uri: Uri) : FlashIt()
|
||||
|
||||
data object FlashRestore : FlashIt()
|
||||
}
|
||||
|
||||
fun flashIt(
|
||||
flashIt: FlashIt, onFinish: (Boolean) -> Unit,
|
||||
flashIt: FlashIt, onFinish: (Boolean, Int) -> Unit,
|
||||
onStdout: (String) -> Unit,
|
||||
onStderr: (String) -> Unit
|
||||
) {
|
||||
@@ -164,14 +192,26 @@ fun flashIt(
|
||||
)
|
||||
|
||||
is FlashIt.FlashModule -> installModule(flashIt.uri, onFinish, onStdout, onStderr)
|
||||
|
||||
FlashIt.FlashRestore -> restoreBoot(onFinish, onStdout, onStderr)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(onBack: () -> Unit = {}, onSave: () -> Unit = {}) {
|
||||
private fun TopBar(status: FlashingStatus, onBack: () -> Unit = {}, onSave: () -> Unit = {}) {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.install)) },
|
||||
title = {
|
||||
Text(
|
||||
stringResource(
|
||||
when (status) {
|
||||
FlashingStatus.FLASHING -> R.string.flashing
|
||||
FlashingStatus.SUCCESS -> R.string.flash_success
|
||||
FlashingStatus.FAILED -> R.string.flash_failed
|
||||
}
|
||||
)
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
|
||||
@@ -123,11 +123,19 @@ fun InstallScreen(navigator: DestinationsNavigator) {
|
||||
installMethod = method
|
||||
}
|
||||
|
||||
Row(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
(lkmSelection as? LkmSelection.LkmUri)?.let {
|
||||
Text(
|
||||
stringResource(
|
||||
id = R.string.selected_lkm,
|
||||
it.uri.lastPathSegment ?: "(file)"
|
||||
)
|
||||
)
|
||||
}
|
||||
Button(modifier = Modifier.fillMaxWidth(),
|
||||
enabled = installMethod != null,
|
||||
onClick = {
|
||||
|
||||
@@ -63,6 +63,7 @@ import me.weishu.kernelsu.ui.component.rememberConfirmDialog
|
||||
import me.weishu.kernelsu.ui.component.rememberCustomDialog
|
||||
import me.weishu.kernelsu.ui.component.rememberLoadingDialog
|
||||
import me.weishu.kernelsu.ui.screen.destinations.AppProfileTemplateScreenDestination
|
||||
import me.weishu.kernelsu.ui.screen.destinations.FlashScreenDestination
|
||||
import me.weishu.kernelsu.ui.util.getBugreportFile
|
||||
import me.weishu.kernelsu.ui.util.shrinkModules
|
||||
|
||||
@@ -214,7 +215,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
|
||||
val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
|
||||
if (lkmMode) {
|
||||
UninstallItem {
|
||||
UninstallItem(navigator) {
|
||||
loadingDialog.withLoading(it)
|
||||
}
|
||||
}
|
||||
@@ -237,7 +238,10 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UninstallItem(withLoading: suspend (suspend () -> Unit) -> Unit) {
|
||||
fun UninstallItem(
|
||||
navigator: DestinationsNavigator,
|
||||
withLoading: suspend (suspend () -> Unit) -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val uninstallConfirmDialog = rememberConfirmDialog()
|
||||
@@ -255,7 +259,9 @@ fun UninstallItem(withLoading: suspend (suspend () -> Unit) -> Unit) {
|
||||
when (uninstallType) {
|
||||
UninstallType.TEMPORARY -> showTodo()
|
||||
UninstallType.PERMANENT -> showTodo()
|
||||
UninstallType.RESTORE_STOCK_IMAGE -> showTodo()
|
||||
UninstallType.RESTORE_STOCK_IMAGE -> navigator.navigate(
|
||||
FlashScreenDestination(FlashIt.FlashRestore)
|
||||
)
|
||||
UninstallType.NONE -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,10 @@ fun uninstallModule(id: String): Boolean {
|
||||
}
|
||||
|
||||
fun installModule(
|
||||
uri: Uri, onFinish: (Boolean) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
uri: Uri,
|
||||
onFinish: (Boolean, Int) -> Unit,
|
||||
onStdout: (String) -> Unit,
|
||||
onStderr: (String) -> Unit
|
||||
): Boolean {
|
||||
val resolver = ksuApp.contentResolver
|
||||
with(resolver.openInputStream(uri)) {
|
||||
@@ -137,11 +140,38 @@ fun installModule(
|
||||
|
||||
file.delete()
|
||||
|
||||
onFinish(result.isSuccess)
|
||||
onFinish(result.isSuccess, result.code)
|
||||
return result.isSuccess
|
||||
}
|
||||
}
|
||||
|
||||
fun restoreBoot(
|
||||
onFinish: (Boolean, Int) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): Boolean {
|
||||
val shell = createRootShell()
|
||||
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so")
|
||||
|
||||
val stdoutCallback: CallbackList<String?> = object : CallbackList<String?>() {
|
||||
override fun onAddElement(s: String?) {
|
||||
onStdout(s ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
val stderrCallback: CallbackList<String?> = object : CallbackList<String?>() {
|
||||
override fun onAddElement(s: String?) {
|
||||
onStderr(s ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
val result =
|
||||
shell.newJob().add("${getKsuDaemonPath()} boot-restore -f --magiskboot $magiskboot")
|
||||
.to(stdoutCallback, stderrCallback)
|
||||
.exec()
|
||||
|
||||
onFinish(result.isSuccess, result.code)
|
||||
return result.isSuccess
|
||||
}
|
||||
|
||||
suspend fun shrinkModules(): Boolean = withContext(Dispatchers.IO) {
|
||||
execKsud("module shrink", true)
|
||||
}
|
||||
@@ -157,7 +187,7 @@ fun installBoot(
|
||||
bootUri: Uri?,
|
||||
lkm: LkmSelection,
|
||||
ota: Boolean,
|
||||
onFinish: (Boolean) -> Unit,
|
||||
onFinish: (Boolean, Int) -> Unit,
|
||||
onStdout: (String) -> Unit,
|
||||
onStderr: (String) -> Unit,
|
||||
): Boolean {
|
||||
@@ -238,7 +268,7 @@ fun installBoot(
|
||||
lkmFile?.delete()
|
||||
|
||||
// if boot uri is empty, it is direct install, when success, we should show reboot button
|
||||
onFinish(bootUri == null && result.isSuccess)
|
||||
onFinish(bootUri == null && result.isSuccess, result.code)
|
||||
return result.isSuccess
|
||||
}
|
||||
|
||||
|
||||
@@ -124,4 +124,8 @@
|
||||
<string name="settings_uninstall_temporary_message">临时卸载 KernelSU,下次重启后恢复</string>
|
||||
<string name="settings_uninstall_permanent_message">完全并永久移除 KernelSU 和所有模块</string>
|
||||
<string name="settings_restore_stock_image_message">恢复原厂镜像,一般在 OTA 前使用;如需卸载请使用“永久卸载”</string>
|
||||
<string name="flashing">刷写中</string>
|
||||
<string name="flash_success">刷写完成</string>
|
||||
<string name="flash_failed">刷写失败</string>
|
||||
<string name="selected_lkm">选择的 LKM :%s</string>
|
||||
</resources>
|
||||
@@ -126,4 +126,8 @@
|
||||
<string name="settings_uninstall_temporary_message">Temporarily uninstall KernelSU, restore to original state after next reboot.</string>
|
||||
<string name="settings_uninstall_permanent_message">Uninstalling KernelSU(Root and all modules) completely and permanently.</string>
|
||||
<string name="settings_restore_stock_image_message">Restore the stock factory image (if a backup exists), usually used before OTA; if you need to uninstall KernelSU, please use \"Permanent Uninstall\".</string>
|
||||
<string name="flashing">Flashing</string>
|
||||
<string name="flash_success">Flash success</string>
|
||||
<string name="flash_failed">Flash failed</string>
|
||||
<string name="selected_lkm">Selected lkm: %s</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user