ksud: backup stock image and use it when restore (#1619)

This commit is contained in:
5ec1cff
2024-04-14 00:45:06 +08:00
committed by GitHub
parent 1be266b6f6
commit 60dd52afd1
8 changed files with 243 additions and 63 deletions

View File

@@ -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

View File

@@ -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 = {

View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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>

View File

@@ -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>