Pairing devices over Wi-Fi in Android
Android使用Wi-Fi配对的原理和实现
原理
Android Studio中插件实现的原理
- 源码
重点函数说明
2.1 检查adb mdns的支持状况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36override fun checkMdnsSupport(): ListenableFuture<MdnsSupportState> {
// TODO: Investigate updating (then using) ddmlib instead of spawning an adb client command, so that
// we don't have to rely on parsing command line output
LOG.info("Checking if mDNS is supported (`adb mdns check` command)")
val futureResult = adbService.executeCommand(listOf("mdns", "check"))
return futureResult.transform(taskExecutor) { result ->
when {
result.errorCode != 0 -> {
LOG.warn("`adb mdns check` returned a non-zero error code (${result.errorCode})")
val isUnknownCommand = result.stderr.any { line -> line.contains(Regex("unknown.*command")) }
if (isUnknownCommand)
MdnsSupportState.AdbVersionTooLow
else
MdnsSupportState.AdbInvocationError
}
result.stdout.isEmpty() -> {
LOG.warn("`adb mdns check` returned an empty output (why?)")
MdnsSupportState.AdbInvocationError
}
// See https://android-review.googlesource.com/c/platform/system/core/+/1274009/5/adb/client/transport_mdns.cpp#553
result.stdout.any { it.contains("mdns daemon version") } -> {
MdnsSupportState.Supported
}
else -> {
MdnsSupportState.NotSupported
}
}
}.catching(taskExecutor, Throwable::class.java) { t ->
LOG.warn("Error executing `adb mdns check`", t)
MdnsSupportState.AdbInvocationError
}.transform(taskExecutor) { supportState ->
// This `tansform` is just for logging purposes
LOG.info("Checking if mDNS is supportState result: ${supportState}")
supportState
}
}2.2 adb mdns services列出支持mdns的设备
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41override fun scanMdnsServices(): ListenableFuture<List<MdnsService>> {
// TODO: Investigate updating (then using) ddmlib instead of spawning an adb client command, so that
// we don't have to rely on parsing command line output
val futureResult = adbService.executeCommand(listOf("mdns", "services"))
return futureResult.transform(taskExecutor) { result ->
// Output example:
// List of discovered mdns services
// adb-939AX05XBZ-vWgJpq _adb-tls-connect._tcp. 192.168.1.86:39149
// adb-939AX05XBZ-vWgJpq _adb-tls-pairing._tcp. 192.168.1.86:37313
// Regular expression
// adb-<everything-until-space><spaces>__adb-tls-pairing._tcp.<spaces><everything-until-colon>:<port>
val lineRegex = Regex("([^\\t]+)\\t*_adb-tls-pairing._tcp.\\t*([^:]+):([0-9]+)")
if (result.errorCode != 0) {
throw AdbCommandException("Error discovering services", result.errorCode, result.stderr)
}
if (result.stdout.isEmpty()) {
throw AdbCommandException("Empty output from \"adb mdns services\" command", -1, result.stderr)
}
return@transform result.stdout
.drop(1)
.mapNotNull { line ->
val matchResult = lineRegex.find(line)
matchResult?.let {
try {
val serviceName = it.groupValues[1]
val ipAddress = InetAddress.getByName(it.groupValues[2])
val port = it.groupValues[3].toInt()
val serviceType = if (serviceName.startsWith(studioServiceNamePrefix)) ServiceType.QrCode else ServiceType.PairingCode
MdnsService(serviceName, serviceType, ipAddress, port)
}
catch (ignored: Exception) {
LOG.warn("mDNS service entry ignored due do invalid characters: ${line}")
null
}
}
}
}
}2.3 adb pair ipAddr:port password,用来配对PC和Android手机
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42override fun pairMdnsService(mdnsService: MdnsService, password: String): ListenableFuture<PairingResult> {
LOG.info("Start mDNS pairing: ${mdnsService}")
val deviceAddress = "${mdnsService.ipAddress.hostAddress}:${mdnsService.port}"
// TODO: Update this when password can be passed as an argument
val passwordInput = password + LineSeparator.getSystemLineSeparator().separatorString
// TODO: Investigate updating (then using) ddmlib instead of spawning an adb client command, so that
// we don't have to rely on parsing command line output
val futureResult = adbService.executeCommand(listOf("pair", deviceAddress), passwordInput)
return futureResult.transform(taskExecutor) { result ->
LOG.info("mDNS pairing exited with code ${result.errorCode}")
result.stdout.take(5).forEachIndexed { index, line ->
LOG.info(" stdout line #$index: $line") }
if (result.errorCode != 0) {
throw AdbCommandException("Error pairing device", result.errorCode, result.stderr)
}
if (result.stdout.isEmpty()) {
throw AdbCommandException("Empty output from \"adb pair\" command", -1, result.stderr)
}
// Output example:
// Enter pairing code: Successfully paired to 192.168.1.86:41915 [guid=adb-939AX05XBZ-vWgJpq]
// Regular expression
// <Prefix><everything-until-colon>:<port>[guid=<everything-until-close-bracket>]
val lineRegex = Regex("Successfully paired to ([^:]*):([0-9]*) \\[guid=([^\\]]*)\\]")
val matchResult = lineRegex.find(result.stdout[0])
matchResult?.let {
try {
val ipAddress = InetAddress.getByName(it.groupValues[1])
val port = it.groupValues[2].toInt()
val serviceGuid = it.groupValues[3]
PairingResult(ipAddress, port, serviceGuid)
}
catch (e: Exception) {
throw InvalidDataException("Pairing result is invalid", e)
}
} ?: throw InvalidDataException("Pairing result is invalid")
}
}2.4 生成二维码的内容格式
1
2
3
4
5
6/**
* Format is "WIFI:T:ADB;S:service;P:password;;" (without the quotes)
*/
private fun createPairingString(service: String, password: String): String {
return "WIFI:T:ADB;S:${service};P:${password};;"
}
实现
用nodejs实现的命令行工具
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.