blob: 2dc53cdda2b06b5c8712aae500d2645433533075 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.adblib
/**
* The format specification of a socket that ADB listens from when forwarding socket connections
* (see [AdbHostServices.forward].
*
* See [ADB source code](https://cs.android.com/android/platform/superproject/+/c9877a99c1ade78c9aa6ac67b8059c20a7bf5ed2:packages/modules/adb/socket_spec.cpp;l=330)
* for reference.
*
* * `tcp:<port>` (may be `tcp:0` to pick any open port)
* * `localabstract:<unix domain socket name>`
* * `localreserved:<unix domain socket name>`
* * `localfilesystem:<unix domain socket name>`
* * `dev:<character device name>
* * `jdwp:<process pid>` (remote only)
* * `vsock:<CID>:<port>` (remote only)
* * `local:path:` Unix local domain socket on `path`
* * `acceptfd:<fd>` (listen only)
*/
abstract class SocketSpec {
/**
* Returns the string to use in the underlying ADB protocol command/query
*/
abstract fun toQueryString(): String
override fun toString(): String {
return "socket-spec: " + toQueryString()
}
/**
* A TCP connection, either local or remote. If this is used for a "server" socket, [port] can
* be zero, in which case ADB server returns the actual port the server socket has been
* bound to.
*/
class Tcp(public val port: Int = 0) : SocketSpec() {
override fun toQueryString(): String {
return "tcp:$port"
}
}
/**
* A VSock connection, either local or remote.
*
* Note: This should only be used as the "remote" parameter of [AdbHostServices.forward].
*/
class VSock(public val cid: Int, public val port: Int) : SocketSpec() {
override fun toQueryString(): String {
return "vsock:$cid:$port"
}
}
/**
* A server socket, already opened as a file descriptor [fd].
*
* Note: This should only be used as the "local" parameter of [AdbHostServices.forward].
*/
class AcceptFD(public val fd: Int) : SocketSpec() {
override fun toQueryString(): String {
return "acceptfd:$fd"
}
}
class Local(public val path: String) : SocketSpec() {
override fun toQueryString(): String {
return "local:$path"
}
}
class LocalAbstract(public val name: String) : SocketSpec() {
override fun toQueryString(): String {
return "localabstract:$name"
}
}
class LocalReserved(public val name: String) : SocketSpec() {
override fun toQueryString(): String {
return "localreserved:$name"
}
}
class LocalFileSystem(public val name: String) : SocketSpec() {
override fun toQueryString(): String {
return "localfilesystem:$name"
}
}
/**
* A [SocketSpec] passed unchanged to/from the ADB server. This is useful
* to represent [SocketSpec] not currently supported by other [SocketSpec]
* classes.
*/
class PassThrough(val value: String) : SocketSpec() {
override fun toQueryString(): String {
return value
}
}
/**
* Attaches to the JDWP debugging session of the process [pid].
*
* Note: This should only be used as the "remote" parameter of [AdbHostServices.forward].
*/
class Jdwp(public val pid: Int) : SocketSpec() {
override fun toQueryString(): String {
return "jdwp:$pid"
}
}
/**
* Invokes any [service] on the remote device, for example `track-jdwp`.
*
* Note: This should only be used as the "remote" parameter of [AdbHostServices.forward].
*/
class Service(private val service: String): SocketSpec() {
override fun toQueryString(): String {
return service
}
}
companion object {
/**
* Returns a [SocketSpec] instance given a string representation of the format
* (see [toQueryString]).
*/
fun fromQueryString(value: String): SocketSpec {
val colonIndex = value.indexOf(":")
if (colonIndex < 0) {
return PassThrough(value)
}
val prefix = value.substring(0, colonIndex)
val suffix = value.substring(colonIndex + 1)
return when (prefix) {
"tcp" -> {
val port = suffix.toIntOrNull()
return if (port == null) PassThrough(value) else Tcp(port)
}
"vsock" -> {
val suffixColonIndex = suffix.indexOf(":")
if (suffixColonIndex < 0) {
return PassThrough(value)
}
val cid = suffix.substring(0, suffixColonIndex).toIntOrNull()
val port = suffix.substring(suffixColonIndex + 1).toIntOrNull()
return if (cid == null || port == null) {
PassThrough(value)
} else {
VSock(cid, port)
}
}
"acceptfd" -> {
val fd = suffix.toIntOrNull()
return if (fd == null) PassThrough(value) else AcceptFD(fd)
}
"local" -> {
return Local(suffix)
}
"localabstract" -> {
return LocalAbstract(suffix)
}
"localreserved" -> {
return LocalReserved(suffix)
}
"localfilesystem" -> {
return LocalFileSystem(suffix)
}
"jdwp" -> {
val pid = suffix.toIntOrNull()
return if (pid == null) PassThrough(value) else Jdwp(pid)
}
else -> {
PassThrough(value)
}
}
}
}
}