不知道你有没有经历过这种场景:工位上堆着十几台测试机,每台都拿USB线连着电脑,Android Studio里的日志铺满屏幕,突然某台设备变成offline,你还得在一堆线里找到对应那根,拔了重插,然后等它重新授权、重新跑case。另一类场景是设备在测试柜或者车机台架上,人根本够不到USB口,想看一眼crash日志,只能喊人拆机器。这些都指向同一个需求——Android设备能不能省略“插线敲命令”的步骤,开着机就能通过Wi-Fi连接ADB,也就是标题里说的Wi-Fi ADB模式。
Android 13进入Wi-Fi ADB,说简单也简单,一条adb tcpip 5555就能让已连接设备转到TCP监听;但说麻烦也麻烦,因为Android 11之后官方主推的“无线调试”走的是动态端口加配对码机制,和传统固定端口思路完全不同,而“自动进入”更是牵扯到系统属性、adbd启动流程、root权限甚至自定义ROM。这篇文章我会从底层原理讲到实操方案,覆盖Magisk模块、AOSP源码修改、无root环境下的替代做法,最后把踩过的坑列成速查表。无论你是自动化测试工程师、嵌入式开发,还是平时自己折腾手机,都能找到能直接落地的一条路。
1. 先搞清楚原理:adb TCP 监听和 Android 13 无线调试是什么关系
很多人一开始混淆“Wi-Fi ADB”和“无线调试”这两个词,觉得它们是一回事。实际在Android源码里,这俩经常是两条路径,理解清楚之后,你自己选方案就不会纠结了。
1.1 传统 adb tcpip 的本质:让 adbd 监听一个固定 TCP 端口
传统方式的核心是adb tcpip 5555这条命令。它的背后逻辑不复杂:设备上的adbd守护进程原本只监听USB,执行adb tcpip之后,adbd会在目标端口(默认5555)上开一个TCP Socket,电脑端再通过adb connect <设备IP>:<端口>连上来。整个过程相当于在ADB上额外开了一个TCP传输通道,和USB通道并存。
在Android 13上,如果你已经用USB连接设备,并且开启了“USB调试”,那么执行:
adb tcpip 5555 adb connect 192.168.1.100:5555仍然是有效的,前提是电脑和设备处于同一局域网。问题在于,这种方式只在当前开机状态下有效,一旦设备重启,端口监听就没了,需要重新插线再敲一次命令。而且从Android 11开始,部分厂商在设置里把“网络调试”相关入口挪走或隐藏,导致不少开发者以为这条路被彻底堵死了,其实底层机制还在,只是受限于权限和产品策略。
1.2 Android 11 之后的“无线调试”和 Android 13 的安全策略变化
Android 11加入的“无线调试(Wireless debugging)”是另一套机制。它不再要求你插USB,而是在开发者选项里点开“无线调试”,系统会动态分配一个本地端口,并显示一个6位配对码,电脑端用adb pair配合配对码完成首次认证。这个过程和adb tcpip有本质区别:
- 端口是随机动态的,每次开启都可能变。
- 必须经历“配对”流程,安全性更高,但自动化麻烦。
- 配对关系在设备重启后通常保留,但Google在后续版本里对这个“保留”做了不少限制,我在第5节会细说。
Android 13延续了Android 11的框架,但安全策略更严格。比如开发者选项里“无线调试”的入口要求系统UI授权,普通应用无法直接篡改开关;又比如persist.adb.tcp.port这类系统属性,在用户态和shell权限下的可写性在不同厂商ROM上表现不一。换句话说,Android 13不是把老方法删掉了,而是用“安全默认值”把门关得更紧,想要实现真正的“自动进入Wi-Fi ADB”,必须找到能撬动adbd配置的那把钥匙。
1.3 自动进入 Wi-Fi ADB 的三个可控层
根据你的设备权限,自动开启Wi-Fi ADB有三个层级的落点:
- 第一层:系统属性。adbd启动时会读取
persist.adb.tcp.port或service.adb.tcp.port,如果值大于0,就会监听对应TCP端口。这是最直接的控制点,只要能在开机时把属性写进去并重启adbd,就达到了“自动进入”的目的。 - 第二层:init脚本/system服务。Android的init进程会根据
init.rc中的触发器设置属性、启动服务。如果你能修改或注入启动脚本,就能在每次开机时自动写入端口属性并重启adbd,这比依赖第三方应用更稳定。 - 第三层:framework/adbd源码。在AOSP编译层面,你可以直接修改adbd的默认行为,比如固定监听5555端口、预置授权公钥等。这是工厂设备、车机系统最常用的做法。
搞明白这三个层面,再回头看各种“教程”就能举一反三了。那些所谓“一键开启Wi-Fi ADB”、“支持重启自动恢复”的工具,底层无非就是在抢这几个控制点。
2. 方案选型:不同权限级别下,怎么让设备开机就进入 Wi-Fi ADB
知道原理之后,最尴尬的问题来了——我的设备到底能不能直接自动进Wi-Fi ADB?这取决于你能拿到多少权限。我按实际操作成本从低到高排一遍。
2.1 方案对比总览
| 方案 | 适用对象 | 是否需要root | 重启后自动生效 | 实施难度 |
|---|---|---|---|---|
| 手动 adb tcpip | 一次性调试 | 不需要 | 否 | 极低 |
| Magisk模块注入属性 | root后的个人手机 | 需要 | 是 | 低 |
| 第三方工具配合root | root后的个人手机 | 需要 | 部分支持 | 中 |
| AOSP源码定制 | 车机/盒子/工厂设备 | 编译时决定 | 是 | 高 |
| 无root配合无线调试 | 普通零售机 | 不需要 | 看ROM | 中低 |
很多开发者一开始只想要最省事的方式,但在不root的设备上想“每次开机自动开启Wi-Fi ADB”,本质上绕不开Android的安全模型。如果设备是自己的测试机,我强烈建议你走Magisk路线,既能保留官方无线调试的配对机制,又能让固定端口稳定监听5555,电脑端连接脚本还能统一管理。
2.2 有root的设备:Magisk 模块最省心
如果你的测试机已经刷好Magisk,方案会非常清晰。Magisk模块可以在开机早期把属性写进系统,还能利用service.sh在adbd启动后执行“补充操作”。我在第3节会给出完整模块代码,那段脚本我自己在Pixel和几台国产机上验证过,逻辑不复杂,核心就是setprop persist.adb.tcp.port 5555加setprop ctl.restart adbd。
要注意,有些ROM即使有root权限,直接setprop persist.adb.tcp.port 5555也不一定能持久生效,因为厂商在build.prop或系统服务里可能重置了persist属性。遇到这种情况,需要借助Magisk的resetprop命令,强制覆盖只读属性,或者直接在post-fs-data.sh阶段写入,这个我在踩坑部分专门讲。
2.3 定制ROM/盒子设备:AOSP 源码里直接写死
如果你手上是开发板、车机SoM、Android电视盒子,或者在给内部设备定制系统,那就没必要等开机再折腾了,直接在AOSP源码里改默认行为。关键是在设备树配置文件里加一行:
PRODUCT_PROPERTY_OVERRIDES += persist.adb.tcp.port=5555这一行写进device/<vendor>/<board>/device.mk或system.prop,编译后设备每次开机,adbd都会默认监听TCP 5555。手机和平板产品通常不会这么干,但对于固定安装、不需要USB口的嵌入式设备,这是标准的调试配置。
2.4 无root设备:能做什么、不能做什么
先说结论:无root的Android 13零售设备,在“每次开机后自动进入Wi-Fi ADB”这件事上,几乎不存在完美的自动化方案,除非你愿意每次依赖官方无线调试的配对流程,或者提前完成一次配对并接受重启后偶发失效。
可以做的优化是:先用USB连接执行一次adb tcpip 5555,然后保持“USB调试”和“无线调试”开关都打开;部分设备在软重启后仍能保留5555监听,但硬重启大概率丢失。如果想要“无感”,只能借助Shizuku这类授权服务,或者用Tasker在开机电量广播后尝试通过shell调用cmd命令,不过受限于权限,效果很不稳定。我自己的经验是:无root设备别折腾自动化,老老实实插线执行adb tcpip,把精力放在连接脚本上,效率反而高。
3. 实战一:写一个 Magisk 模块,让 Android 13 每次开机自动监听 5555
这个方案我用了很久,从Android 11到Android 13的Pixel、Redmi、一加,基本都能跑通。Magisk模块的核心价值在于:它把“写入属性”和“重启adbd”这两件事放在开机正确时机执行,比你在Termux里跑脚本靠谱得多。
3.1 先搭好模块目录结构
Magisk模块本质是一个目录,包含module.prop和若干脚本文件,打包成zip刷入即可。目录结构如下:
wifidab-autostart/ ├── module.prop ├── service.sh └── system.propmodule.prop是模块元信息,内容可以这样写:
id=wifidab_autostart name=WiFi ADB Autostart version=1.0 versionCode=1 author=yourname description=Enable adb over WiFi on boot for Android 13我一般在开发阶段会加一个customize.sh用来打印日志,实际上刷入阶段不需要额外操作,因此这个模块可以精简到只剩三个文件。
3.2 service.sh 里的核心逻辑:setprop 与重启 adbd
service.sh是Magisk在Android启动后期、sys.boot_completed置1后执行的一个脚本。这里有几个细节需要留意:
- 直接用
setprop persist.adb.tcp.port 5555可以设置持久属性,但adbd收到属性变化时,并不会自动去监听新端口,所以必须手动重启adbd。 - 重启adbd在Magisk环境下最稳妥的命令是
setprop ctl.restart adbd,它等价于stop adbd && start adbd,但不会打断你正在跑的测试。 - 有些Android 13设备在重启adbd时会同时重置
sys.usb.config相关属性,导致USB调试暂时不可用,这个无伤大雅,几秒后会自动恢复。
脚本内容:
#!/system/bin/sh MODDIR=${0%/*} # 强制设置TCP端口 setprop persist.adb.tcp.port 5555 setprop service.adb.tcp.port 5555 # 等待adbd就绪再重启 until [ "$(getprop sys.boot_completed)" = "1" ]; do sleep 1 done # 重启adbd让TCP监听生效 setprop ctl.restart adbd实际使用中,service.sh在开机后期执行,此时sys.boot_completed通常已经是1,所以那个循环更多是保险起见。如果你希望更早生效,可以把脚本挪到post-fs-data.sh里,但那时adbd还没启动,ctl.restart adbd可能报“not found”,所以我更推荐service.sh。
3.3 配合 system.prop 双保险
为了减少对service.sh执行时机的依赖,我还在模块里放了一个system.prop。Magisk会把模块里的system.prop内容合并到系统的属性存储中,相当于在启动阶段就预置属性。
persist.adb.tcp.port=5555如果设备允许直接写入persist属性,那么在开机阶段adbd启动时就会读到这个端口,service.sh里那句setprop就变成“重复确认”,逻辑更简单。但如果设备对persist属性有保护,system.prop也能提供一次兜底。
我把两种方式都写上,是因为测试中确实遇到过某款设备只靠system.prop不生效,但加上service.sh强制重启adbd后立刻生效。双保险看着冗余,实际省了很多排查时间。
3.4 安装后的验证步骤
模块创建好之后打包成zip,Magisk里“从本地安装”刷入,重启。开机后先确认模块是否激活:
adb shell su -c "magisk --list"然后查看端口监听是否起来:
adb shell su -c "cat /proc/net/tcp | grep 5555"或者直接看adbd日志:
adb shell su -c "logcat -d -s adbd"最快速的验证方式是电脑端执行:
adb connect 192.168.1.100:5555 adb devices如果能看到192.168.1.100:5555 device而不是offline,说明整个链路已经打通。之后你再也不用碰USB线,Android Studio里换用Wi-Fi连接即可。
4. 实战二:Android 13 AOSP 定制系统里从源码层实现自动 Wi-Fi ADB
如果你不是给自己手机装模块,而是在做设备整机方案,那源码层定制才是最优雅的路径。我参与过的几类项目——安卓电视盒子、车机、工业平板——基本都是这个套路:把ADB TCP监听、授权公钥、默认USB配置全部在编译阶段定死,设备出厂不需要任何人工干预就能通过局域网连接调试。
4.1 先找到设备配置目录和关键文件
AOSP源码里,每个设备的配置散落在device/<vendor>/<board>/下。通常需要关注三个文件:
device.mk:定义产品属性和预置模块。system.prop:厂商自定义系统属性。BoardConfig.mk:编译目标架构等,基本不用动。
以常见的高通平台为例:
device/qcom/common/system.prop你可以在自己的产品目录里新建或修改system.prop,加入:
# Enable adbd TCP listening on 5555 persist.adb.tcp.port=5555如果编译系统的Android 13版本比较新,某些平台已经迁移到product.prop或vendor.prop,核心逻辑不变,属性归属不同而已。搜一下adb.tcp.port在平台目录里的引用位置,跟着放就行。
4.2 修改 init.rc 和 adbd 启动属性
system.prop在早期启动阶段就会被init加载,adbd这个服务定义在system/core/rootdir/init.rc。默认情况下,adbd的service定义下面有一段属性触发逻辑,大意是:当persist.adb.tcp.port被设置为有效端口时,adbd会在TCP层监听。
如果设备用的还是老式USB配置流程,可以在init.rc里追加一个trigger:
on property:persist.adb.tcp.port=* setprop service.adb.tcp.port ${persist.adb.tcp.port}不过我实测下来,Android 13的adbd源码已经能自动读取persist属性,这个trigger更多用于兼容旧路径。真正容易遗漏的是:如果厂商在init.rc里有setprop persist.adb.tcp.port -1这种重置逻辑,你的值会被覆盖。排查时优先grep -rn "adb.tcp.port" device/ vendor/,看有没有冲突。
4.3 处理 adb 授权:预置 adb_keys,免弹窗
只开端口还不够。Android 13系统ro.adb.secure默认值为1,这意味着即便TCP 5555通了,电脑端连上来也会被要求授权,首次连接时设备上必须手动点击“允许USB调试”。在无人值守的设备上这就是灾难,所以必须预置授权公钥。
做法是把你电脑的~/.android/adbkey.pub内容,复制到系统镜像的/adb_keys文件里,然后在device.mk中声明:
PRODUCT_COPY_FILES += \ device/xxx/keys/adb_keys:adb_keys这样系统首次启动就会把该公钥放进/data/misc/adb/adb_keys,凡是私钥匹配的电脑,连接时自动通过RSA认证,不再弹窗。对测试团队来说,可以把团队统一使用的adbkey.pub预置进去,省掉每台设备手动授权的琐事。
4.4 编译烧录后的验证流程
烧录定制系统后,第一次开机的验证流程我建议按下列顺序走:
- 先用串口或USB adb确认系统正常启动。
- 执行
adb shell getprop persist.adb.tcp.port,看值是否为5555。 - 执行
adb shell netstat -tlnp | grep 5555,确认adbd在监听。 - 电脑端和盒子连同一局域网,
adb connect+adb devices确认状态为device。
如果第2步值不对,说明system.prop没编进去,回设备树配置里查归属路径;如果第3步没有监听,可能是adbd被SEAndroid限制,或者你手动改的init.rc语法错误导致属性没生效。整个流程走通后,这台设备以后插电开机就能网线或Wi-Fi直接ADB连接,整个过程没有任何物理接触。
5. 踩坑记录:Android 13 上容易翻车的 6 个细节
我在多个Android 13项目里折腾过Wi-Fi ADB,有些问题解决起来快,有些能卡一整天。下面这些坑,基本属于“官方文档不写、论坛里问不到最后只能自己读源码”的类型。
5.1 端口写对了,但 adbd 没起来
症状:设备上getprop能看到5555,但电脑adb connect连不上,netstat里没有5555监听。
原因:adbd在启动时读取service.adb.tcp.port,如果你只设置了persist.adb.tcp.port,而系统里又没有trigger把它同步到service.adb.tcp.port,adbd根本不会去监听。执行adb tcpip命令之所以有效,是因为它会直接改service.adb.tcp.port并且内部触发adbd重启。
解法:在Magisk模块或init脚本里,同时设置两个属性,或直接用setprop service.adb.tcp.port 5555再重启adbd。在源码级方案里,init.rc的trigger就是为了解决这个同步问题。
5.2 连接上了马上断开:offline 与 RSA 授权的坑
症状:adb connect返回connected,但adb devices显示offline,几秒后消失。
原因:多半是RSA公钥没通过授权。Android 13在无线调试模式下,如果/data/misc/adb/adb_keys里没有对应公钥,就会拒绝建立ADB会话。注意,offline不一定是网络瞬断,很多时候是握手失败。
解法:把电脑端~/.android/adbkey.pub的内容追加到设备的/data/misc/adb/adb_keys里,然后重启adbd。优先用标准ADB特性实现自动认证,不要为了解决弹窗问题去关闭ro.adb.secure,除非设备运行在完全隔离的测试网络中。
5.3 “无线调试”开关和 tcpip 模式不是一回事
这是最容易引起误解的地方。Android 13开发者选项里的“无线调试”开关,控制的是系统使用动态端口加配对码的新机制。而persist.adb.tcp.port=5555开启的是传统固定端口机制。两者可以共存,但你如果只开了官方“无线调试”开关,立刻执行adb connect 192.168.1.100:5555大概率失败,因为adbd并没有监听5555,而是监听动态端口。
有需要时,可以在无线调试界面查看“IP地址和端口”,用那个动态端口连接:
adb connect 192.168.1.100:43867但动态端口开了就换,自动化脚本不友好,所以我更倾向固定端口加预置公钥的组合。
5.4 设置 persist 属性为什么失败
有root也不代表你能随便写系统属性。Android 13对部分属性的写权限分得很细:
- 普通权限shell:可以直接写
service.*和部分persist.*,但ro.*不可写。 - root:可以通过
resetprop强制覆盖,包括只读属性。 - 用户态app:没有shell权限时,字符属性由
system_server代写,失败率很高。
Magisk的resetprop是解决这种问题的最强工具:
adb shell su -c "resetprop persist.adb.tcp.port 5555" adb shell su -c "stop adbd && start adbd"另外提醒一点,如果你用settings put global adb_wifi_enabled 1这种方式,想靠设置数据库打开无线调试,Android 13上通常是无效的,因为开关状态由SystemUI持有,数据库值只做展示同步。
5.5 多设备同网段下的端口冲突
给测试设备批量开启5555端口后,如果多台接入同一路由器,连接时端口全是一样的,冲突不发生在端口,而在于你要区分设备的IP。我自己的习惯是给每台固定静态IP或者用DHCP保留绑定,然后写一个批量连接脚本:
for ip in 192.168.1.101 192.168.1.102 192.168.1.103; do adb connect $ip:5555 done adb devices -l如果设备A和电脑建立了5555连接,设备B也监听5555,电脑端会把它们识别为两个不同序列号的设备,不会因为同端口而串线,但你在adb -s指定设备时要写清楚IP加端口,否则容易误操作。
5.6 安全策略:关掉 ro.adb.secure 前想清楚
有些开发者为了让自动化流程更顺畅,会直接在AOSP里设置ro.adb.secure=0,彻底关闭ADB授权。
我个人的建议是不要这么做,尤其Android 13对数据目录加固之后,ADB这个入口一旦变成完全无鉴权,等于在设备上开了一个不设防的远程管理端口,局域网里任何一台电脑都能执行任意shell命令。哪怕只是测试环境,也可能被同事的脚本误连、被其他设备干扰。
更稳妥的做法是保留ro.adb.secure=1,预置团队统一的adk公钥,这样既无人值守自动化,又不破坏ADB的传输层安全。如果真的需要临时全开,也要限定在完全隔离的专用测试网段,职责边界划清楚,后续不会有灾难性事故。
这套组合拳打完,Android 13设备的Wi-Fi ADB自动开启基本就不再是玄学了:个人测试机,Magisk模块加双属性写入;整机类产品,AOSP源码编译时固定端口加预置公钥;没root的零售机,别为难自己,老老实实插线执行一次adb tcpip,然后靠连接脚本减少重复劳动。我第一次把这些逻辑梳理通,是在一台反复重启后就要拆机插USB的车机上,改成persist.adb.tcp.port=5555出厂预置之后,整个调试团队都松了一大口气。希望这篇文章也能让你少走几条弯路。