manager: support offline patch

This commit is contained in:
weishu
2024-03-18 23:16:24 +08:00
parent 9759a779cd
commit 0c11d210a9
11 changed files with 62 additions and 164 deletions

View File

@@ -137,7 +137,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
@Parcelize
sealed class FlashIt : Parcelable {
data class FlashBoot(val bootUri: Uri? = null, val koUri: Uri, val ota: Boolean) : FlashIt()
data class FlashBoot(val bootUri: Uri? = null, val ota: Boolean) : FlashIt()
data class FlashModule(val uri: Uri) : FlashIt()
}
@@ -150,7 +150,6 @@ fun flashIt(
when (flashIt) {
is FlashIt.FlashBoot -> installBoot(
flashIt.bootUri,
flashIt.koUri,
flashIt.ota,
onFinish,
onStdout,

View File

@@ -57,55 +57,17 @@ import me.weishu.kernelsu.ui.util.rootAvailable
@Destination
@Composable
fun InstallScreen(navigator: DestinationsNavigator) {
val scope = rememberCoroutineScope()
val loadingDialog = rememberLoadingDialog()
val context = LocalContext.current
var installMethod by remember {
mutableStateOf<InstallMethod?>(null)
}
val onFileDownloaded = { uri: Uri ->
installMethod?.let {
scope.launch(Dispatchers.Main) {
when (it) {
InstallMethod.DirectInstall -> {
navigator.navigate(
FlashScreenDestination(
FlashIt.FlashBoot(
null,
uri,
false
)
)
)
}
InstallMethod.DirectInstallToInactiveSlot -> {
navigator.navigate(
FlashScreenDestination(
FlashIt.FlashBoot(
null,
uri,
true
)
)
)
}
is InstallMethod.SelectFile -> {
navigator.navigate(
FlashScreenDestination(
FlashIt.FlashBoot(
it.uri,
uri,
false
)
)
)
}
}
}
val onClickInstall = {
installMethod?.let { method ->
val flashIt = FlashIt.FlashBoot(
if (method is InstallMethod.SelectFile) method.uri else null,
method is InstallMethod.DirectInstallToInactiveSlot
)
navigator.navigate(FlashScreenDestination(flashIt))
}
}
@@ -124,45 +86,11 @@ fun InstallScreen(navigator: DestinationsNavigator) {
.fillMaxWidth()
.padding(16.dp)
) {
DownloadListener(context = context) { uri ->
onFileDownloaded(uri)
loadingDialog.hide()
}
val failedMessage = stringResource(id = R.string.failed_to_fetch_lkm_url)
val downloadingMessage = stringResource(id = R.string.downloading)
Button(
modifier = Modifier.fillMaxWidth(),
enabled = installMethod != null,
onClick = {
loadingDialog.showLoading()
scope.launch(Dispatchers.IO) {
getLKMUrl().onFailure { throwable ->
loadingDialog.hide()
scope.launch(Dispatchers.Main) {
Toast.makeText(
context,
failedMessage.format(throwable.message),
Toast.LENGTH_SHORT
).show()
}
}.onSuccess { result ->
download(
context = context,
url = result.second,
fileName = result.first,
description = downloadingMessage.format(
result.first
),
onDownloaded = { uri ->
onFileDownloaded(uri)
loadingDialog.hide()
},
onDownloading = {}
)
}
}
onClickInstall()
}) {
Text(
stringResource(id = R.string.install_next),

View File

@@ -143,7 +143,6 @@ fun installModule(
fun installBoot(
bootUri: Uri?,
lkmUri: Uri,
ota: Boolean,
onFinish: (Boolean) -> Unit,
onStdout: (String) -> Unit,
@@ -151,73 +150,60 @@ fun installBoot(
): Boolean {
val resolver = ksuApp.contentResolver
with(resolver.openInputStream(lkmUri)) {
val lkmFile = File(ksuApp.cacheDir, "kernelsu.ko")
lkmFile.outputStream().use { output ->
this?.copyTo(output)
}
if (!lkmFile.exists()) {
onStdout("- kernelsu.ko not found")
onFinish(false)
return false
}
val bootFile = bootUri?.let { uri ->
with(resolver.openInputStream(uri)) {
val bootFile = File(ksuApp.cacheDir, "boot.img")
bootFile.outputStream().use { output ->
this?.copyTo(output)
}
bootFile
val bootFile = bootUri?.let { uri ->
with(resolver.openInputStream(uri)) {
val bootFile = File(ksuApp.cacheDir, "boot.img")
bootFile.outputStream().use { output ->
this?.copyTo(output)
}
bootFile
}
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so")
var cmd = "boot-patch -m ${lkmFile.absolutePath} --magiskboot ${magiskboot.absolutePath}"
cmd += if (bootFile == null) {
// no boot.img, use -f to force install
" -f"
} else {
" -b ${bootFile.absolutePath}"
}
if (ota) {
cmd += " -u"
}
// output dir
val downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
cmd += " -o $downloadsDir"
val shell = createRootShell()
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()} $cmd").to(stdoutCallback, stderrCallback)
.exec()
Log.i("KernelSU", "install boot $lkmUri result: ${result.isSuccess}")
lkmFile.delete()
bootFile?.delete()
// if boot uri is empty, it is direct install, when success, we should show reboot button
onFinish(bootUri == null && result.isSuccess)
return result.isSuccess
}
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so")
var cmd = "boot-patch --magiskboot ${magiskboot.absolutePath}"
cmd += if (bootFile == null) {
// no boot.img, use -f to force install
" -f"
} else {
" -b ${bootFile.absolutePath}"
}
if (ota) {
cmd += " -u"
}
// output dir
val downloadsDir =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
cmd += " -o $downloadsDir"
val shell = createRootShell()
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()} $cmd").to(stdoutCallback, stderrCallback)
.exec()
Log.i("KernelSU", "install boot result: ${result.isSuccess}")
bootFile?.delete()
// if boot uri is empty, it is direct install, when success, we should show reboot button
onFinish(bootUri == null && result.isSuccess)
return result.isSuccess
}
fun reboot(reason: String = "") {
@@ -250,7 +236,8 @@ fun isInitBoot(): Boolean {
return file.exists()
}
// https://source.android.com/docs/core/architecture/partitions/generic-boot
return ShellUtils.fastCmd(shell, "getprop ro.product.first_api_level").trim().toInt() >= Build.VERSION_CODES.TIRAMISU
return ShellUtils.fastCmd(shell, "getprop ro.product.first_api_level").trim()
.toInt() >= Build.VERSION_CODES.TIRAMISU
}
fun overlayFsAvailable(): Boolean {

View File

@@ -109,8 +109,6 @@
<string name="enable_web_debugging">تمكين تصحيح أخطاء WebView</string>
<string name="enable_web_debugging_summary">يمكن استخدامه لتصحيح أخطاء WebUI، يرجى تمكينه فقط عند الحاجة.</string>
<string name="install_next">التالي</string>
<string name="failed_to_fetch_lkm_url">فشل جلب رابط LKM: %1$s</string>
<string name="downloading">تحميل %1$s</string>
<string name="select_file">اختيار ملف</string>
<string name="direct_install">تثبيت مباشر (موصى به)</string>
<string name="install_inactive_slot">التثبيت على فتحة غير نشطة (بعد OTA)</string>

View File

@@ -116,6 +116,4 @@
\nN\'utilisez cette option qu\'une fois la mise à jour OTA terminée.
\nContinuer?</string>
<string name="install_next">Suivant</string>
<string name="downloading">Téléchargement de %1$s</string>
<string name="failed_to_fetch_lkm_url">Échec de la récupération de l\'URL du LKM: %1$s</string>
</resources>

View File

@@ -115,7 +115,5 @@
<string name="install_inactive_slot_warning">Seu dispositivo será FORÇADO a inicializar no slot inativo atual após uma reinicialização!
\nSó use esta opção após a conclusão do OTA.
\nDeseja continuar?</string>
<string name="downloading">Baixando %1$s</string>
<string name="install_next">Próximo</string>
<string name="failed_to_fetch_lkm_url">Falha ao buscar a URL do LKM: %1$s</string>
</resources>

View File

@@ -112,8 +112,6 @@
<string name="open">Открыть</string>
<string name="enable_web_debugging">Включить отладку WebView</string>
<string name="enable_web_debugging_summary">Используется для отладки веб-интерфейса. Пожалуйста, включайте только при необходимости.</string>
<string name="downloading">Загрузка %1$s</string>
<string name="failed_to_fetch_lkm_url">Не удалось получить LKM по адресу: %1$s</string>
<string name="direct_install">Прямая установка (Рекомендуется)</string>
<string name="install_inactive_slot">Установка в неактивный слот (После OTA)</string>
<string name="install_next">Далее</string>

View File

@@ -117,6 +117,4 @@
\nBu seçeneği yalnızca OTA tamamlandıktan sonra kullanın.
\nDevam edilsin mi?</string>
<string name="install_next">Sonraki</string>
<string name="failed_to_fetch_lkm_url">LKM URL\'si getirilemedi: %1$s</string>
<string name="downloading">%1$s indiriliyor</string>
</resources>

View File

@@ -113,7 +113,5 @@
<string name="install_inactive_slot">安装到未使用的槽位OTA 后)</string>
<string name="install_inactive_slot_warning">将在重启后强制切换到另一个槽位!\n注意只能在 OTA 更新完成后的重启之前使用。\n确认</string>
<string name="install_next">下一步</string>
<string name="failed_to_fetch_lkm_url">获取 LKM 链接失败:%1$s</string>
<string name="downloading">正在下载:%1$s</string>
<string name="select_file_tip">建议选择 %1$s 分区镜像</string>
</resources>

View File

@@ -115,6 +115,4 @@
\n請問是否繼續</string>
<string name="direct_install">直接安裝(建議)</string>
<string name="install_next">下一步</string>
<string name="failed_to_fetch_lkm_url">取得 LKM 下載鏈接失敗:%1$s</string>
<string name="downloading">正在下載:%1$s</string>
</resources>

View File

@@ -115,7 +115,5 @@
<string name="install_inactive_slot">Install to Inactive Slot (After OTA)</string>
<string name="install_inactive_slot_warning">Your device will be **FORCED** to boot to the current inactive slot after a reboot!\nOnly use this option after OTA is done.\nContinue?</string>
<string name="install_next">Next</string>
<string name="failed_to_fetch_lkm_url">Failed to fetch LKM url: %1$s</string>
<string name="downloading">Downloading %1$s</string>
<string name="select_file_tip">%1$s partition image is recommended</string>
</resources>