-Optimize file permission requests, simplify backup and restore logic, and directly process user-selected files

- This will fix an issue where some devices were unable to back up their app lists
This commit is contained in:
ShirkNeko
2025-03-21 03:03:27 +08:00
parent aefd2b57cf
commit ba928150d5
2 changed files with 31 additions and 54 deletions

View File

@@ -3,10 +3,9 @@
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:name=".KernelSUApplication"
@@ -19,6 +18,7 @@
android:networkSecurityConfig="@xml/network_security_config"
android:supportsRtl="true"
android:theme="@style/Theme.KernelSU"
android:requestLegacyExternalStorage="true"
tools:targetApi="34">
<activity
android:name=".ui.MainActivity"

View File

@@ -18,7 +18,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import shirkneko.zako.sukisu.R
import java.io.BufferedReader
import java.io.File
import java.io.IOException
import java.io.InputStreamReader
import java.text.SimpleDateFormat
@@ -45,30 +44,25 @@ object ModuleModify {
try {
val busyboxPath = "/data/adb/ksu/bin/busybox"
val moduleDir = "/data/adb/modules"
val tempFile = File(context.cacheDir, "backup_${System.currentTimeMillis()}.tar.gz")
val tempPath = tempFile.absolutePath
// 直接将tar输出重定向到用户选择的文件
val command = """
cd "$moduleDir" &&
$busyboxPath tar -czvf "$tempPath" ./*
$busyboxPath tar -cz ./* > /proc/self/fd/1
""".trimIndent()
val process = Runtime.getRuntime().exec(arrayOf("su", "-c", command))
process.waitFor()
// 直接将tar输出写入到用户选择的文件
context.contentResolver.openOutputStream(uri)?.use { output ->
process.inputStream.copyTo(output)
}
val error = BufferedReader(InputStreamReader(process.errorStream)).readText()
if (process.exitValue() != 0) {
throw IOException(context.getString(R.string.command_execution_failed, error))
}
context.contentResolver.openOutputStream(uri)?.use { output ->
tempFile.inputStream().use { input ->
input.copyTo(output)
}
}
tempFile.delete()
withContext(Dispatchers.Main) {
snackBarHost.showSnackbar(
context.getString(R.string.backup_success),
@@ -96,23 +90,15 @@ object ModuleModify {
try {
val busyboxPath = "/data/adb/ksu/bin/busybox"
val moduleDir = "/data/adb/modules"
val tempFile = File(context.cacheDir, "temp_restore.tar.gz").apply {
if (exists()) delete()
}
// 直接从用户选择的文件读取并解压
val process = Runtime.getRuntime().exec(arrayOf("su", "-c", "$busyboxPath tar -xz -C $moduleDir"))
context.contentResolver.openInputStream(uri)?.use { input ->
tempFile.outputStream().use { output ->
input.copyTo(output)
}
input.copyTo(process.outputStream)
}
process.outputStream.close()
val command = """
cd "$moduleDir" &&
rm -rf * &&
$busyboxPath tar -xzvf "${tempFile.absolutePath}"
""".trimIndent()
val process = Runtime.getRuntime().exec(arrayOf("su", "-c", command))
process.waitFor()
val error = BufferedReader(InputStreamReader(process.errorStream)).readText()
@@ -120,8 +106,6 @@ object ModuleModify {
throw IOException(context.getString(R.string.command_execution_failed, error))
}
tempFile.delete()
withContext(Dispatchers.Main) {
val snackbarResult = snackBarHost.showSnackbar(
message = context.getString(R.string.restore_success),
@@ -166,25 +150,19 @@ object ModuleModify {
withContext(Dispatchers.IO) {
try {
val allowlistPath = "/data/adb/ksu/.allowlist"
val tempFile = File(context.cacheDir, "allowlist_backup_${System.currentTimeMillis()}")
val command = "cp $allowlistPath ${tempFile.absolutePath}"
val process = Runtime.getRuntime().exec(arrayOf("su", "-c", command))
process.waitFor()
// 直接复制文件到用户选择的位置
val process = Runtime.getRuntime().exec(arrayOf("su", "-c", "cat $allowlistPath"))
context.contentResolver.openOutputStream(uri)?.use { output ->
process.inputStream.copyTo(output)
}
val error = BufferedReader(InputStreamReader(process.errorStream)).readText()
if (process.exitValue() != 0) {
throw IOException(context.getString(R.string.command_execution_failed, error))
}
context.contentResolver.openOutputStream(uri)?.use { output ->
tempFile.inputStream().use { input ->
input.copyTo(output)
}
}
tempFile.delete()
withContext(Dispatchers.Main) {
snackBarHost.showSnackbar(
context.getString(R.string.allowlist_backup_success),
@@ -211,18 +189,15 @@ object ModuleModify {
withContext(Dispatchers.IO) {
try {
val allowlistPath = "/data/adb/ksu/.allowlist"
val tempFile = File(context.cacheDir, "allowlist_restore_temp").apply {
if (exists()) delete()
}
// 直接从用户选择的文件读取并写入到目标位置
val process = Runtime.getRuntime().exec(arrayOf("su", "-c", "cat > $allowlistPath"))
context.contentResolver.openInputStream(uri)?.use { input ->
tempFile.outputStream().use { output ->
input.copyTo(output)
}
input.copyTo(process.outputStream)
}
process.outputStream.close()
val command = "cp ${tempFile.absolutePath} $allowlistPath"
val process = Runtime.getRuntime().exec(arrayOf("su", "-c", command))
process.waitFor()
val error = BufferedReader(InputStreamReader(process.errorStream)).readText()
@@ -230,8 +205,6 @@ object ModuleModify {
throw IOException(context.getString(R.string.command_execution_failed, error))
}
tempFile.delete()
withContext(Dispatchers.Main) {
snackBarHost.showSnackbar(
context.getString(R.string.allowlist_restore_success),
@@ -350,4 +323,8 @@ object ModuleModify {
type = "application/octet-stream"
}
}
private fun reboot() {
Runtime.getRuntime().exec(arrayOf("su", "-c", "reboot"))
}
}