ADB.kt 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. package com.example.modifier.utils
  2. import android.util.Log
  3. import com.example.modifier.model.AdbResult
  4. import kotlinx.coroutines.Dispatchers
  5. import kotlinx.coroutines.coroutineScope
  6. import kotlinx.coroutines.delay
  7. import kotlinx.coroutines.launch
  8. import kotlinx.coroutines.withContext
  9. class ADB {
  10. companion object {
  11. const val TAG = "ADB"
  12. val adbPath by lazy { "${getContext().applicationInfo.nativeLibraryDir}/libadb.so" }
  13. private val homeDir by lazy { getContext().filesDir }
  14. private val cacheDir by lazy { getContext().cacheDir }
  15. suspend fun connect(): Boolean {
  16. return run ls@{
  17. repeat(1000) {
  18. adb("connect", "localhost:5555")
  19. delay(5000)
  20. adb("devices")
  21. if (adb("shell", "echo", "hello").exitCode == 0) {
  22. return@ls true
  23. }
  24. delay(3000)
  25. }
  26. return@ls false
  27. }
  28. }
  29. /**
  30. * Send a raw ADB command
  31. */
  32. suspend fun adb(vararg command: String): AdbResult {
  33. runCatching {
  34. val fullCommand = command.toMutableList().also {
  35. it.add(0, adbPath)
  36. if (command.firstOrNull() == "shell") {
  37. it.add(1, "-s")
  38. it.add(2, "localhost:5555")
  39. }
  40. }
  41. Log.i(TAG, "ADB command: ${fullCommand.joinToString(" ")}".replace(adbPath, "adb"))
  42. var output = ""
  43. var error = ""
  44. val process = withContext(Dispatchers.IO) {
  45. createProcess(*fullCommand.toTypedArray())
  46. }
  47. coroutineScope {
  48. launch {
  49. process.inputStream.bufferedReader().useLines { line ->
  50. line.forEach {
  51. output += it + "\n"
  52. Log.i(TAG, "ADB out: $it")
  53. }
  54. }
  55. }
  56. launch {
  57. process.errorStream.bufferedReader().useLines { line ->
  58. line.forEach {
  59. error += it + "\n"
  60. Log.e(TAG, "ADB err: $it")
  61. }
  62. }
  63. }
  64. }
  65. withContext(Dispatchers.IO) {
  66. process.waitFor()
  67. }
  68. return AdbResult(exitCode = process.exitValue())
  69. }.onFailure {
  70. Log.wtf(TAG, "Error running ADB command: ${it.message}", it)
  71. }
  72. return AdbResult()
  73. }
  74. /**
  75. * Send a raw ADB command
  76. */
  77. suspend fun adbShell(vararg commands: String): AdbResult {
  78. Log.i(TAG, "ADB command: ${commands.joinToString("\n")}")
  79. runCatching {
  80. var output = ""
  81. var error = ""
  82. val process = withContext(Dispatchers.IO) {
  83. createProcess(adbPath, "-s", "localhost:5555", "shell")
  84. }
  85. process.outputStream.bufferedWriter().use { writer ->
  86. commands.forEach { command ->
  87. writer.write(command)
  88. writer.newLine()
  89. writer.flush()
  90. }
  91. }
  92. coroutineScope {
  93. launch {
  94. process.inputStream.bufferedReader().useLines { line ->
  95. line.forEach {
  96. output += it + "\n"
  97. Log.i(TAG, "ADB out: $it")
  98. }
  99. }
  100. }
  101. launch {
  102. process.errorStream.bufferedReader().useLines { line ->
  103. line.forEach {
  104. error += it + "\n"
  105. Log.e(TAG, "ADB err: $it")
  106. }
  107. }
  108. }
  109. }
  110. withContext(Dispatchers.IO) {
  111. process.waitFor()
  112. }
  113. Log.i(TAG, "ADB exit code: ${process.exitValue()}")
  114. return AdbResult(output = output, error = error, exitCode = process.exitValue())
  115. }.onFailure {
  116. Log.wtf(TAG, "Error running ADB command: ${it.message}", it)
  117. }
  118. return AdbResult()
  119. }
  120. fun createProcess(vararg command: String): Process {
  121. return ProcessBuilder(*command)
  122. .directory(homeDir)
  123. .apply {
  124. // redirectErrorStream(true)
  125. environment().apply {
  126. put("HOME", homeDir.path)
  127. put("TMPDIR", cacheDir.path)
  128. }
  129. }.start()
  130. }
  131. }
  132. }