|
1 | 1 | package com.appcontrolx.executor |
2 | 2 |
|
3 | | -import android.content.ComponentName |
4 | | -import android.content.Context |
5 | | -import android.content.ServiceConnection |
6 | | -import android.os.IBinder |
7 | | -import com.appcontrolx.IShellService |
8 | 3 | import rikka.shizuku.Shizuku |
| 4 | +import java.io.BufferedReader |
| 5 | +import java.io.DataOutputStream |
9 | 6 |
|
10 | | -class ShizukuExecutor(private val context: Context) : CommandExecutor { |
11 | | - |
12 | | - private var shellService: IShellService? = null |
13 | | - |
14 | | - private val serviceConnection = object : ServiceConnection { |
15 | | - override fun onServiceConnected(name: ComponentName?, service: IBinder?) { |
16 | | - shellService = IShellService.Stub.asInterface(service) |
17 | | - } |
18 | | - override fun onServiceDisconnected(name: ComponentName?) { |
19 | | - shellService = null |
20 | | - } |
21 | | - } |
22 | | - |
23 | | - fun bindService() { |
24 | | - if (!Shizuku.pingBinder()) return |
25 | | - |
26 | | - // TODO: Implement UserService binding |
27 | | - // This requires AIDL setup |
28 | | - } |
29 | | - |
30 | | - fun unbindService() { |
31 | | - shellService = null |
32 | | - } |
| 7 | +class ShizukuExecutor : CommandExecutor { |
33 | 8 |
|
34 | 9 | override fun execute(command: String): Result<String> { |
35 | | - val service = shellService |
36 | | - ?: return Result.failure(Exception("Shizuku service not bound")) |
| 10 | + if (!Shizuku.pingBinder()) { |
| 11 | + return Result.failure(Exception("Shizuku not available")) |
| 12 | + } |
37 | 13 |
|
38 | 14 | return try { |
39 | | - val output = service.exec(command) |
40 | | - if (output.startsWith("ERROR:")) { |
41 | | - Result.failure(Exception(output)) |
| 15 | + // Use Shizuku's remote process to execute shell commands |
| 16 | + val process = Shizuku.newProcess(arrayOf("sh", "-c", command), null, null) |
| 17 | + |
| 18 | + val output = process.inputStream.bufferedReader().use(BufferedReader::readText) |
| 19 | + val error = process.errorStream.bufferedReader().use(BufferedReader::readText) |
| 20 | + val exitCode = process.waitFor() |
| 21 | + |
| 22 | + if (exitCode == 0) { |
| 23 | + Result.success(output.trim()) |
42 | 24 | } else { |
43 | | - Result.success(output) |
| 25 | + val errorMsg = error.ifBlank { output }.trim() |
| 26 | + if (errorMsg.isNotBlank()) { |
| 27 | + Result.failure(Exception(errorMsg)) |
| 28 | + } else { |
| 29 | + Result.failure(Exception("Command failed with exit code $exitCode")) |
| 30 | + } |
44 | 31 | } |
45 | 32 | } catch (e: Exception) { |
46 | 33 | Result.failure(e) |
47 | 34 | } |
48 | 35 | } |
49 | 36 |
|
50 | 37 | override fun executeBatch(commands: List<String>): Result<Unit> { |
51 | | - commands.forEach { cmd -> |
| 38 | + for (cmd in commands) { |
52 | 39 | val result = execute(cmd) |
53 | | - if (result.isFailure) return Result.failure(result.exceptionOrNull()!!) |
| 40 | + // Continue even if some commands fail (best effort) |
54 | 41 | } |
55 | 42 | return Result.success(Unit) |
56 | 43 | } |
| 44 | + |
| 45 | + companion object { |
| 46 | + fun isAvailable(): Boolean { |
| 47 | + return try { |
| 48 | + Shizuku.pingBinder() |
| 49 | + } catch (e: Exception) { |
| 50 | + false |
| 51 | + } |
| 52 | + } |
| 53 | + } |
57 | 54 | } |
0 commit comments