@@ -28,6 +28,7 @@ import java.io.IOException
2828import java.security.MessageDigest
2929import java.security.cert.CertificateFactory
3030import java.security.cert.X509Certificate
31+ import java.util.concurrent.TimeUnit
3132import java.util.zip.ZipFile
3233import kotlinx.coroutines.CoroutineScope
3334import kotlinx.coroutines.Dispatchers
@@ -40,6 +41,14 @@ import kotlinx.coroutines.withTimeout
4041private const val TAG = " APatchCli"
4142private const val SHELL_TIMEOUT_MS = 10_000L
4243
44+ data class ApdExecResult (
45+ val success : Boolean ,
46+ val commandLabel : String ,
47+ val exitCode : Int? = null ,
48+ val output : String = " " ,
49+ val errorMessage : String? = null ,
50+ )
51+
4352private fun getKPatchPath (): String {
4453 return apApp.applicationInfo.nativeLibraryDir + File .separator + " libkpatch.so"
4554}
@@ -191,12 +200,82 @@ fun rootShellForResult(vararg cmds: String): Shell.Result {
191200}
192201
193202fun execApd (args : String , newShell : Boolean = false): Boolean {
194- return if (newShell) {
195- withNewRootShell {
196- ShellUtils .fastCmdResult(this , " ${APApplication .APD_PATH } $args " )
203+ return try {
204+ if (newShell) {
205+ withNewRootShell {
206+ ShellUtils .fastCmdResult(this , " ${APApplication .APD_PATH } $args " )
207+ }
208+ } else {
209+ ShellUtils .fastCmdResult(getRootShell(), " ${APApplication .APD_PATH } $args " )
197210 }
198- } else {
199- ShellUtils .fastCmdResult(getRootShell(), " ${APApplication .APD_PATH } $args " )
211+ } catch (t: Throwable ) {
212+ Log .e(TAG , " execApd failed: args='$args ', newShell=$newShell " , t)
213+ false
214+ }
215+ }
216+
217+ private fun configureRootProcessEnv (builder : ProcessBuilder ) {
218+ val basePath = System .getenv(" PATH" ).orEmpty()
219+ builder.environment().apply {
220+ this [" PATH" ] = " $basePath :/system_ext/bin:/vendor/bin:${APApplication .APATCH_FOLDER } bin"
221+ this [" BUSYBOX" ] = " ${APApplication .APATCH_FOLDER } bin/busybox"
222+ }
223+ }
224+
225+ fun execApdBootFallback (vararg args : String , timeoutMs : Long = SHELL_TIMEOUT_MS ): ApdExecResult {
226+ val effectiveSuperKey = APApplication .superKey.ifBlank { " su" }
227+ val command = mutableListOf (
228+ APApplication .SUPERCMD ,
229+ " su" ,
230+ " -Z" ,
231+ APApplication .MAGISK_SCONTEXT ,
232+ " exec" ,
233+ APApplication .APD_PATH ,
234+ " -s" ,
235+ effectiveSuperKey,
236+ ).apply {
237+ addAll(args)
238+ }
239+ val commandLabel =
240+ " ${File (APApplication .SUPERCMD ).name} su -Z ${APApplication .MAGISK_SCONTEXT } exec ${APApplication .APD_PATH } -s <superkey> ${args.joinToString(" " )} "
241+
242+ return try {
243+ val builder = ProcessBuilder (command).redirectErrorStream(true )
244+ configureRootProcessEnv(builder)
245+
246+ val process = builder.start()
247+ val finished = process.waitFor(timeoutMs, TimeUnit .MILLISECONDS )
248+ if (! finished) {
249+ process.destroy()
250+ if (! process.waitFor(500 , TimeUnit .MILLISECONDS )) {
251+ process.destroyForcibly()
252+ }
253+ val output = runCatching {
254+ process.inputStream.bufferedReader().use { it.readText().trim() }
255+ }.getOrDefault(" " )
256+ ApdExecResult (
257+ success = false ,
258+ commandLabel = commandLabel,
259+ output = output,
260+ errorMessage = " timed out after ${timeoutMs} ms" ,
261+ )
262+ } else {
263+ val exitCode = process.exitValue()
264+ val output = process.inputStream.bufferedReader().use { it.readText().trim() }
265+ ApdExecResult (
266+ success = exitCode == 0 ,
267+ commandLabel = commandLabel,
268+ exitCode = exitCode,
269+ output = output,
270+ errorMessage = if (exitCode == 0 ) null else " exit code $exitCode " ,
271+ )
272+ }
273+ } catch (t: Throwable ) {
274+ ApdExecResult (
275+ success = false ,
276+ commandLabel = commandLabel,
277+ errorMessage = t.message ? : t.javaClass.simpleName,
278+ )
200279 }
201280}
202281
0 commit comments