Android 手机通过 adblib 使用 ADB Wi-Fi 控制 Android 9 开发板

Android 手机通过 adblib 使用 ADB Wi-Fi 控制 Android 9 开发板

项目中需要让 Android 手机通过 Wi-Fi 直接连接 Android 9 开发板的 adbd,不经过电脑,也不在手机端安装 adb。这里使用 tananaev/adblib 建立 ADB TCP 通信。

通信链路:

rust 复制代码
Android 手机 App -> Socket -> adblib -> Wi-Fi -> Android 9 开发板:5555 -> adbd -> shell

1. 整体流程

开发板启动后在代码中打开 adbd 的 TCP 5555 端口,手机和开发板进入同一局域网,手机取得开发板 IP 后建立 TCP Socket,adblib 通过 Socket 与开发板 adbd 通信。当前开发板关闭了 ADB RSA 授权,因此双方完成 CNXN 后可以直接建立 ADB 会话。

rust 复制代码
开发板启动 -> 开启 adbd TCP 5555 -> 手机获取开发板 IP -> Socket 连接 IP:5555 -> CNXN -> ADB 连接建立 -> OPEN shell:xxx -> WRTE 返回结果

实际通信:

lua 复制代码
Android 手机                         Android 9 开发板
     |                                     |
     |-------- TCP Connect :5555 --------->|
     |                                     |
     |------------- CNXN ----------------->|
     |<------------ CNXN ------------------|
     |                                     |
     |------ OPEN shell:getprop ---------->|
     |<------------ OKAY ------------------|
     |<------------ WRTE ------------------|
     |------------- OKAY ----------------->|
     |<------------ CLSE ------------------|

2. 开发板代码开启 adbd TCP 5555

这部分运行在 Android 9 开发板。当前项目使用的是可控制系统服务的开发板环境,不是普通三方 App 权限模型。核心就是设置 service.adb.tcp.port=5555,然后重启 adbd

Root 环境可以直接执行:

arduino 复制代码
Runtime.getRuntime().exec(new String[]{"su", "-c", "setprop service.adb.tcp.port 5555; stop adbd; start adbd"});

如果希望重启开发板后仍保持 TCP 5555,可以写 persist.adb.tcp.port

arduino 复制代码
Runtime.getRuntime().exec(new String[]{"su", "-c", "setprop persist.adb.tcp.port 5555; stop adbd; start adbd"});

Android 9 的 adbd 启动时读取:

yaml 复制代码
service.adb.tcp.port
        ↓ 如果为空
persist.adb.tcp.port
        ↓
5555
        ↓
监听 TCP 5555

AOSP Android 9 的 adbd 就按这个顺序读取端口配置。 adb tcpip 5555 内部同样会把端口写入 service.adb.tcp.port

如果只需要本次开机有效,使用:

复制代码
service.adb.tcp.port

如果开发板每次启动都需要开放 5555,使用:

复制代码
persist.adb.tcp.port

检查结果:

arduino 复制代码
Process process = Runtime.getRuntime().exec(new String[]{"su", "-c", "getprop service.adb.tcp.port"});

或者直接检查监听端口:

perl 复制代码
netstat -tunlp | grep 5555

这里的关键不是启动另一个 ADB 程序,而是让开发板原本运行的 adbd 增加 TCP listener:

复制代码
adbd
 ├─ USB ADB
 └─ TCP :5555

3. 手机引入 adblib

项目加入:

arduino 复制代码
implementation 'com.tananaev:adblib:1.3'

AndroidManifest 添加网络权限:

ini 复制代码
<uses-permission android:name="android.permission.INTERNET" />

手机端不需要 adb 可执行文件,也不需要 USB Host,adblib 直接通过 Socket 与开发板 adbd 通信。

4. 手机连接开发板

手机取得开发板 IP 后,通过 Socket 连接开发板的 5555 端口,再创建 AdbCryptoAdbConnectionadblib 官方示例也是这套流程:Socket -> AdbCrypto -> AdbConnection -> connect -> open

ini 复制代码
Socket socket = new Socket("192.168.1.42", 5555);

AdbCrypto crypto = AdbCrypto.generateAdbKeyPair(new AdbBase64() {
    @Override
    public String encodeToString(byte[] data) {
        return Base64.encodeToString(data, Base64.NO_WRAP);
    }
});

AdbConnection connection = AdbConnection.create(socket, crypto);
connection.connect();

AdbStream stream = connection.open("shell:logcat");

这里虽然创建了 RSA Key,但不等于每次都会出现授权弹窗。AdbCryptoadblib ADB 连接流程的一部分。当目标 adbd 返回 AUTH TOKEN 时,库会使用它签名;如果目标直接返回 CNXN,连接则直接建立。源码中的处理顺序就是 CMD_AUTH 使用 cryptoCMD_CNXN 标记连接成功。

整体流程:

css 复制代码
开发板启动
    ↓
adbd 开启 TCP 5555
    ↓
手机连接 开发板IP:5555
    ↓
创建 AdbCrypto
    ↓
创建 AdbConnection
    ↓
connection.connect()
    ↓
CNXN / AUTH
    ↓
ADB 连接建立
    ↓
connection.open("shell:xxx")
    ↓
读取 WRTE 返回的数据

执行一个简单命令:

ini 复制代码
Socket socket = new Socket("192.168.1.42", 5555);

AdbCrypto crypto = AdbCrypto.generateAdbKeyPair(new AdbBase64() {
    @Override
    public String encodeToString(byte[] data) {
        return Base64.encodeToString(data, Base64.NO_WRAP);
    }
});

AdbConnection connection = AdbConnection.create(socket, crypto);
connection.connect();

AdbStream stream = connection.open("shell:getprop ro.product.model");

while (!stream.isClosed()) {
    byte[] data = stream.read();
    if (data != null) Log.d("ADB", new String(data, StandardCharsets.UTF_8));
}

stream.close();
connection.close();

整个手机端的核心就是:

rust 复制代码
Socket -> AdbCrypto -> AdbConnection -> connect -> open("shell:xxx") -> read

5. 执行 ADB Shell

连接建立后通过 OPEN shell:xxx 执行命令:

ini 复制代码
AdbStream stream = connection.open("shell:getprop ro.product.model");
while (!stream.isClosed()) {
    byte[] data = stream.read();
    if (data != null) Log.d("ADB", new String(data, StandardCharsets.UTF_8));
}
stream.close();
connection.close();

它对应:

复制代码
adb shell getprop ro.product.model

只是手机上没有真正运行 adb

更换 shell: 后面的命令即可执行其他操作:

ini 复制代码
AdbStream stream = connection.open("shell:getprop ro.build.version.release");
ini 复制代码
AdbStream stream = connection.open("shell:ps -A");
ini 复制代码
AdbStream stream = connection.open("shell:ip addr");
ini 复制代码
AdbStream stream = connection.open("shell:pm list packages");
ini 复制代码
AdbStream stream = connection.open("shell:am start -n com.example.demo/.MainActivity");
ini 复制代码
AdbStream stream = connection.open("shell:am force-stop com.example.demo");
相关推荐
律宏阔1 小时前
Android App 里实现开机动画替换
android
消失的旧时光-19432 小时前
Android 系统层扫盲 05:Android 开机后发生了什么?从 Bootloader 到 Launcher
android·zygote·fork·aosp·cow
hunterandroid3 小时前
Android 线上卡顿治理:从布局层级到主线程负载的全链路排查
android
恋猫de小郭3 小时前
Dart Skills CLI 1.0 :AI 时代的 Dart 交付支持
android·前端·flutter
2501_916007474 小时前
使用Apple Dashboards显示和自定iOS应用性能指标与数据可视化指南
android·ios·小程序·https·uni-app·iphone·webview
蜡台4 小时前
# 已解决|MySQL 8\.4\.11 密码正确仍报错1045 \(28000\) Access denied 终极修复方案
android·mysql·adb
JMchen1234 小时前
2026年六款主流AI编程工具深度实测:Cursor、Copilot、Claude Code等对比与选型思考
android·kotlin·copilot·ai编程·开发工具·cursor·claude code
mmsx5 小时前
osmdroid 地图实战 05|让地图动起来:三个实时能力 + 六条工程排雷清单
android
码农coding5 小时前
android12 SystemUI组件之StatusBar启动
android