@@ -4,12 +4,12 @@ import android.annotation.SuppressLint
44import android.content.ComponentName
55import android.content.Context
66import android.content.ServiceConnection
7+ import android.content.pm.PackageInstaller
78import android.content.pm.PackageManager
89import android.os.Binder
910import android.os.IBinder
1011import android.os.Parcel
1112import android.os.ParcelFileDescriptor
12- import android.util.Log
1313import app.revanced.manager.R
1414import kotlinx.coroutines.Dispatchers
1515import kotlinx.coroutines.suspendCancellableCoroutine
@@ -26,6 +26,37 @@ class ShizukuInstaller(
2626) {
2727 fun isAvailable (): Boolean = Shizuku .pingBinder()
2828
29+ fun mapStatus (output : String ): Int = when {
30+ output.contains(" INSTALL_FAILED_UPDATE_INCOMPATIBLE" ) ||
31+ output.contains(" INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES" ) ||
32+ output.contains(" INSTALL_FAILED_VERSION_DOWNGRADE" ) ->
33+ PackageInstaller .STATUS_FAILURE_CONFLICT
34+
35+ output.contains(" INSTALL_FAILED_INSUFFICIENT_STORAGE" ) ->
36+ PackageInstaller .STATUS_FAILURE_STORAGE
37+
38+ output.contains(" INSTALL_FAILED_INVALID_APK" ) ||
39+ output.contains(" INSTALL_PARSE_FAILED_NOT_APK" ) ||
40+ output.contains(" INSTALL_FAILED_INVALID_URI" ) ->
41+ PackageInstaller .STATUS_FAILURE_INVALID
42+
43+ output.contains(" INSTALL_FAILED_INCOMPATIBLE_ABI" ) ||
44+ output.contains(" INSTALL_FAILED_OLDER_SDK" ) ->
45+ PackageInstaller .STATUS_FAILURE_INCOMPATIBLE
46+
47+ output.contains(" INSTALL_FAILED_ABORTED" ) ->
48+ PackageInstaller .STATUS_FAILURE_ABORTED
49+
50+ output.contains(" INSTALL_FAILED_VERIFICATION_TIMEOUT" ) ->
51+ PackageInstaller .STATUS_FAILURE_TIMEOUT
52+
53+ output.contains(" INSTALL_FAILED_VERIFICATION_FAILURE" ) ||
54+ output.contains(" INSTALL_FAILED_REJECTED_BY_BUILDER" ) ->
55+ PackageInstaller .STATUS_FAILURE_BLOCKED
56+
57+ else -> PackageInstaller .STATUS_FAILURE
58+ }
59+
2960 fun hasPermission (): Boolean {
3061 if (! isAvailable()) return false
3162 return if (Shizuku .getVersion() >= 11 ) {
@@ -38,7 +69,8 @@ class ShizukuInstaller(
3869 private fun mapErrorMessage (output : String , exitCode : Int ): String {
3970 return when {
4071 output.contains(" INSTALL_FAILED_UPDATE_INCOMPATIBLE" ) ||
41- output.contains(" INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES" ) ->
72+ output.contains(" INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES" ) ||
73+ output.contains(" INSTALL_FAILED_VERSION_DOWNGRADE" ) ->
4274 context.getString(R .string.installation_conflict_description)
4375
4476 output.contains(" INSTALL_FAILED_INSUFFICIENT_STORAGE" ) ->
@@ -166,10 +198,49 @@ class ShizukuInstaller(
166198 }
167199 }
168200
201+ suspend fun bootstrapAdb (publicKey : String ) = withContext(Dispatchers .IO ) {
202+ val serviceArgs = Shizuku .UserServiceArgs (ComponentName (context.packageName, UserService ::class .java.name))
203+ .daemon(false )
204+ .processNameSuffix(" shizuku_adb_bootstrap" )
205+
206+ suspendCancellableCoroutine { continuation ->
207+ val connection = object : ServiceConnection {
208+ private val resumed = AtomicBoolean (false )
209+ override fun onServiceConnected (name : ComponentName ? , service : IBinder ? ) {
210+ if (service == null || resumed.get() || ! continuation.isActive) return
211+ thread {
212+ val data = Parcel .obtain()
213+ val reply = Parcel .obtain()
214+ try {
215+ data.writeInt(UserService .TRANSACTION_BOOTSTRAP_ADB )
216+ data.writeString(context.packageName)
217+ data.writeString(publicKey)
218+ service.transact(IBinder .FIRST_CALL_TRANSACTION , data, reply, 0 )
219+ val result = reply.readInt()
220+ if (resumed.compareAndSet(false , true )) {
221+ if (result == 0 ) continuation.resume(Unit )
222+ else continuation.resumeWithException(Exception (" Bootstrap failed ($result )" ))
223+ }
224+ } catch (e: Exception ) {
225+ if (resumed.compareAndSet(false , true )) continuation.resumeWithException(e)
226+ } finally {
227+ data.recycle()
228+ reply.recycle()
229+ Shizuku .unbindUserService(serviceArgs, this , true )
230+ }
231+ }
232+ }
233+ override fun onServiceDisconnected (name : ComponentName ? ) {}
234+ }
235+ Shizuku .bindUserService(serviceArgs, connection)
236+ }
237+ }
238+
169239 class UserService : Binder () {
170240 companion object {
171241 const val TRANSACTION_INSTALL = 1
172242 const val TRANSACTION_UNINSTALL = 2
243+ const val TRANSACTION_BOOTSTRAP_ADB = 3
173244 }
174245
175246 override fun onTransact (code : Int , data : Parcel , reply : Parcel ? , flags : Int ): Boolean {
@@ -178,6 +249,7 @@ class ShizukuInstaller(
178249 return when (actionCode) {
179250 TRANSACTION_INSTALL -> handleInstall(data, reply)
180251 TRANSACTION_UNINSTALL -> handleUninstall(data, reply)
252+ TRANSACTION_BOOTSTRAP_ADB -> handleBootstrapAdb(data, reply)
181253 else -> false
182254 }
183255 }
@@ -220,5 +292,27 @@ class ShizukuInstaller(
220292 }
221293 return true
222294 }
295+
296+ private fun handleBootstrapAdb (data : Parcel , reply : Parcel ? ): Boolean {
297+ val packageName = data.readString() ? : return false
298+ val publicKey = data.readString() ? : return false
299+ try {
300+ // Grant WRITE_SECURE_SETTINGS to the app
301+ ProcessBuilder (" pm" , " grant" , packageName, " android.permission.WRITE_SECURE_SETTINGS" )
302+ .redirectErrorStream(true ).start().waitFor()
303+ // Append the RSA public key to ADB trusted keys
304+ ProcessBuilder (" sh" , " -c" , " echo \" $publicKey \" >> /data/misc/adb/adb_keys" )
305+ .redirectErrorStream(true ).start().waitFor()
306+ // Enable ADB TCP on port 5555
307+ ProcessBuilder (" setprop" , " service.adb.tcp.port" , " 5555" )
308+ .redirectErrorStream(true ).start().waitFor()
309+ ProcessBuilder (" stop" , " adbd" ).redirectErrorStream(true ).start().waitFor()
310+ ProcessBuilder (" start" , " adbd" ).redirectErrorStream(true ).start().waitFor()
311+ reply?.writeInt(0 )
312+ } catch (e: Exception ) {
313+ reply?.writeInt(- 1 )
314+ }
315+ return true
316+ }
223317 }
224318}
0 commit comments