Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ def libCompile = [
def pluginCompile = [
kotlin : "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}",
kotlinReflect: "org.jetbrains.kotlin:kotlin-reflect:${versions.kotlin}",
discoveryLib : "com.chimerapps:discovery-plugin-ui:${versions.discoveryPluginLib}",
]

def libTestCompile = [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,119 +1,21 @@
package com.chimerapps.discovery.device.adb

import com.chimerapps.discovery.utils.Platform
import com.chimerapps.discovery.utils.currentPlatform
import com.chimerapps.discovery.utils.debug
import com.chimerapps.discovery.utils.info
import com.chimerapps.discovery.utils.logger
import com.chimerapps.discovery.utils.warn
import se.vidstige.jadb.JadbConnection
import java.io.File
import java.util.ArrayList
import java.util.HashSet
import java.util.concurrent.TimeUnit
import com.chimerapps.discovery.device.debugbridge.DebugBridgeBootstrap
import com.chimerapps.discovery.device.sdb.SDBDevice

/**
* @author Nicola Verbeeck
*/
class ADBBootstrap(private val sdkPathGuesses: Collection<String>, private val adbPathProvider: (() -> String?)? = null) {

companion object {
private val log = logger<ADBBootstrap>()
private const val DEFAULT_ADB_TIMEOUT_S = 4L
private val platformExt: String by lazy { if (currentPlatform == Platform.WINDOWS) ".exe" else "" }

private fun determineExecutablePath(name: String): String? {
val foundPath = System.getenv("PATH").split(File.pathSeparator).find {
val file = File(it, name)
file.exists() && file.canExecute()
}
if (foundPath != null)
return "$foundPath${File.separator}$name"
return null
}

private fun findADB(sdkPathGuesses: Collection<String>): String? {
findAndroidSdkDirs(sdkPathGuesses).forEach {
val file = hasValidAdb(it)
if (file != null)
return file.absolutePath
}
log.info("Failed to find android sdk dir, searching path")

return determineExecutablePath("adb$platformExt")
}

private fun hasValidAdb(sdkDir: String): File? {
log.debug("Found android sdk at $sdkDir")
val adbFile = File("$sdkDir${File.separator}platform-tools${File.separator}adb$platformExt")
if (adbFile.exists() && adbFile.canExecute())
return adbFile

log.warn("Could not find adb at $adbFile -> Exist: ${adbFile.exists()}, Can execute: ${adbFile.canExecute()}")
return null
}

private fun findAndroidSdkDirs(sdkPathGuesses: Collection<String>): Collection<String> {
val list = HashSet<String>(sdkPathGuesses)
val env = System.getenv("ANDROID_HOME")
if (env != null) {
log.info("Got android sdk dir from env variable: $env")
list += env
}
return list
}

}

val pathToAdb: String?
get() = adbPathProvider?.invoke() ?: findADB(sdkPathGuesses)
private var hasBootStrap = false

fun bootStrap(): ADBInterface {
val pathToAdb = pathToAdb
if (!hasBootStrap && pathToAdb != null && File(pathToAdb).let { it.exists() && it.canExecute() }) {
try {
executeADBCommand("start-server")
hasBootStrap = true
} catch (e: Throwable) {
}
}
if (pathToAdb == null || !File(pathToAdb).let { it.exists() && it.canExecute() })
return ADBInterface(connection = null, bootstrap = this)
return ADBInterface(connection = JadbConnection(), bootstrap = this)
}

fun executeADBCommand(vararg commands: String) = executeADBCommand(DEFAULT_ADB_TIMEOUT_S, *commands)

fun executeADBCommand(timeoutInSeconds: Long, vararg commands: String): String? {
return (adbPathProvider?.invoke() ?: pathToAdb)?.let {
val builder = ProcessBuilder(it.prepend(commands))
val process = builder.start()
val success = process.waitFor(timeoutInSeconds, TimeUnit.SECONDS)
val response = if (success) {
val response = process.inputStream.bufferedReader().readText().trim()
val error = process.errorStream.bufferedReader().readText()
if (error.isNotBlank())
System.err.println(error)
response
} else
null
try {
if (!success)
process.destroy()
} catch (e: Throwable) {
e.printStackTrace()
}

response
}
}

}

private fun String.prepend(list: Array<out String>): List<String> {
val newList = ArrayList<String>(list.size + 1)
newList += this
newList += list
return newList
}
class ADBBootstrap(
sdkPathGuesses: Collection<String>,
adbPathProvider: (() -> String?)? = null,
) : DebugBridgeBootstrap(
toolExecutableName = "adb",
toolName = "android",
sdkPathEnvironmentVariable = "ANDROID_HOME",
sdkPathGuesses = sdkPathGuesses,
debugBridgePathProvider = adbPathProvider,
deviceBridgeServerPort = 5037,
deviceCreator = { device, bootstrap -> SDBDevice(device, bootstrap) },
sdkToolSubDirectory = "platform-tools",
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.chimerapps.discovery.device.adb

import com.chimerapps.discovery.device.debugbridge.DebugBridgeBasedDevice
import com.chimerapps.discovery.device.debugbridge.DebugBridgeBootstrap
import se.vidstige.jadb.JadbDevice

class ADBDevice(
device: JadbDevice,
bootstrap: DebugBridgeBootstrap,
) : DebugBridgeBasedDevice(device, bootstrap)

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.chimerapps.discovery.device.debugbridge

import com.chimerapps.discovery.device.BaseDevice
import com.chimerapps.discovery.device.DirectPreparedConnection
import com.chimerapps.discovery.device.DiscoveredSession
import com.chimerapps.discovery.device.PreparedDeviceConnection
import com.chimerapps.discovery.device.sdb.SDBDevice
import com.chimerapps.discovery.utils.freePort
import se.vidstige.jadb.JadbDevice

abstract class DebugBridgeBasedDevice(device: JadbDevice, val bootstrap: DebugBridgeBootstrap) : BaseDevice() {

val serial: String = device.serial.trim()

fun forwardTCPPort(localPort: Int, remotePort: Int) {
bootstrap.executeDebugBridgeCommand("-s", serial, "forward", "tcp:$localPort", "tcp:$remotePort")
}

fun removeTCPForward(localPort: Int) {
bootstrap.executeDebugBridgeCommand("-s", serial, "forward", "--remove", "tcp:$localPort")
}

fun executeDebugBridgeCommand(vararg args: String): String? {
val newArgs = Array(args.size + 2) { "" }
newArgs[0] = "-s"
newArgs[1] = serial
System.arraycopy(args, 0, newArgs, 2, args.size)
return bootstrap.executeDebugBridgeCommand(*newArgs)
}

fun executeDebugBridgeCommand(timeoutInSeconds: Long, vararg args: String): String? {
val newArgs = Array(args.size + 2) { "" }
newArgs[0] = "-s"
newArgs[1] = serial
System.arraycopy(args, 0, newArgs, 2, args.size)
return bootstrap.executeDebugBridgeCommand(timeoutInSeconds, *newArgs)
}

override fun getSessions(announcementPort: Int): List<DiscoveredSession> {
val freePort = freePort()
if (freePort <= 0) {
return emptyList()
}
try {
forwardTCPPort(freePort, announcementPort)
return readAnnouncement(freePort)
} finally {
removeTCPForward(freePort)
}
}

override fun prepareConnection(suggestedLocalPort: Int, remotePort: Int): PreparedDeviceConnection {
forwardTCPPort(suggestedLocalPort, remotePort)
return object : DirectPreparedConnection("127.0.0.1", suggestedLocalPort) {
override fun tearDown() {
try {
super.tearDown()
} catch (ignore: Throwable) {
}
removeTCPForward(suggestedLocalPort)
}
}
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as SDBDevice

if (serial != other.serial) return false

return true
}

override fun hashCode(): Int {
return serial.hashCode()
}

}
Loading