想先从一个真实场景说起。去年有一批车机在客户那边出现“偶发性黑屏重启”,视频拍回来十几条,研发部开了一下午会,猜了七八个方向——电源时序、屏幕背光驱动、系统内存压力、第三方应用崩溃——但没有一个人能给出定论。原因很简单:手里没有现场那台设备的 ADB。这种场景做车载中控屏研发的朋友应该都不陌生。客户现场的 Android 车机一旦出问题,最常见的路径是售后录视频、拍照片、导日志文件回传,但这些流程到研发手里,往往已经丢掉了关键的原始现场信息。于是我们把目光放到了“把 ADB 这条调试通道直接从客户现场接回研发环境”上,用的工具是 FRP,走的是 ADB 自带的网络调试协议。整套链路折腾完之后,感觉这完全可以作为车载团队的日常远程调试基础设施来用。
这篇文章就围绕这条链路展开:FRP 在这里面到底扮演什么角色,公网侧和车机侧分别怎么配,连上之后日常调试怎么用,以及我实测过程中踩过的那些坑。
1. 客户现场的疑难杂症,为什么必须把 ADB 接回研发环境
1.1 一个典型场景:售后传回来的视频救不了急
车载中控屏的问题有个特点:大部分是偶发的、环境相关的。比如客户反映“导航用着用着屏幕闪一下”,售后到现场看的时候可能等半小时都不复现;比如“蓝牙电话听完之后多媒体没声音”,这问题可能跟某个特定手机型号、某个特定通话时间点都有关。售后能做的就是拍视频、拍照片、把用户描述整理成工单。但这些信息到研发手里,基本只能靠猜。
真正能定位问题的数据,都在系统里面。崩溃栈、ANR 日志、system_server 的异常输出、当前窗口焦点变化、内存压力曲线——这些只有通过 ADB 才能拿到。没有 ADB,就像医生看病不让量体温、不让拍片子,只能听病人描述“我大概哪里不舒服”。
我自己经历过的例子:一台车机偶发性重启,售后录了视频,能看出来是重启了,但没人知道重启前系统里发生了什么。后来我们通过 ADB 连上去,抓了几天的 logcat,才看到是某个后台服务在特定网络环境下触发了内核 panic。如果没有 ADB,这个问题可能要在客户现场换好几台整机才能蒙对方向。
1.2 ADB 在车机问题排查里到底能干什么
ADB(Android Debug Bridge)的价值不只是“敲几条命令”,它是个完整的调试通道:
- logcat:实时看系统日志、应用日志、崩溃栈,还能按缓冲区区分 main、system、crash、events。车机问题里最常见的 app crash、native crash、ANR,几乎都能从这里找到线索。
- dumpsys:查询系统服务的内部状态。比如
dumpsys window能看当前焦点窗口是什么,dumpsys meminfo能查内存占用,dumpsys battery能看电池/电源状态,dumpsys package能查应用安装情况。车机上很多“界面卡在某页”“应用退到后台后被杀”的问题,都靠它来确认现场状态。 - screencap / screenrecord:直接把车机屏幕截下来或录下来,不需要现场人员配合。
- input:远程注入触摸、按键、滑动事件。研发可以直接在远程环境里“动手操作”车机,复现操作路径。
- push / pull:往车机里推调试 APK、配置文件,或者把车机里的数据库、日志拉回来。
这些能力组合起来,基本等于把研发的工位搬到了车机面前。
1.3 只靠“现场人员协助”的常见做法为什么不够用
在没有远程 ADB 通道的时候,团队通常尝试过这些办法:
- 让售后/客户在车机上装日志抓取 App。听起来可行,但很多问题在 App 启动之前就发生了,而且用户场景多变,装个 App 等于让用户当测试员,数据质量很难保证。
- OTA 一个带 debug 的固件给客户升级。这个流程太慢,通常要法务、质量、售后好几道审批。而且问题固件可能根本装不上,或者升级本身就把现场情况给覆盖了。
- 研发人员打飞的去现场。这是最后的兜底方案,成本高、周期长,而且人到了现场不一定能复现,可能就白跑一趟。
这些办法的共同问题是:它们都是“一次性”的,而问题排查往往是迭代的——看了日志,产生新假设,需要再抓另一份数据,再调一次参数,再试一次复现路径。如果没有一个持续在线的调试通道,每次迭代都意味着几天的沟通成本。
2. 为什么选 FRP 作为这条 ADB 回传链路的管道
2.1 FRP 的原理一句话:把内网服务挂到公网端口
FRP 是个开源内网穿透工具,全称 Fast Reverse Proxy。核心思路很简单:一台有公网 IP 的服务器上跑frps,内网设备上跑frpc,frpc 主动向外连接 frps,并在 frps 上注册一个“代理”。之后任何人访问 frps 的某个端口,流量就会被 frps 原样转发给 frpc,frpc 再把流量转给内网设备上指定的本地端口。
放到我们的场景里就是:车机上的 adbd 监听 5555 端口,frpc 把车机的 5555 端口映射到公网 VPS 的 15555 端口,研发在办公室执行adb connect VPS_IP:15555,就连上了客户现场那台车机的 adbd。
关键点在于:车机不需要有公网 IP,只要能主动访问到公网 VPS 就行。绝大多数车机在客户现场都是走 Wi-Fi 或者 4G/5G 网络,都在 NAT 后面,没有入站连接条件。FRP 这种“反向代理”方式恰好解决这个问题。
2.2 和直连端口映射、商业远程软件、自研通道对比
刚开始做技术选型的时候,我们也对比过几个方案:
| 方案 | 优点 | 缺点 | 结论 |
|---|---|---|---|
| 运营商专线/公网 IP | 延迟低、稳定 | 客户现场没条件装,成本太高 | 不可行 |
| 商业远程桌面(向日葵/TeamViewer 等) | 能看屏幕、能远程操作 | 拿不到 ADB 通道、权限受限、车机需要额外装 App | 可辅助,但不能替代 ADB |
| 自研云通道+车机端 Agent | 完全可控 | 工作量大,要自己做心跳、加密、协议解析 | 重了,前期不划算 |
| FRP | 轻量、开源、只转发 TCP、配置简单 | 自己负责服务端安全 | 选定方案 |
为什么 ADB 特别适合走 FRP?因为 ADB 的网络模式本质就是 TCP。开启adb tcpip 5555之后,adbd 就在 5555 端口上监听,FRP 只管做 TCP 流量转发,完全不关心上层协议内容。ADB 的握手、认证、命令交互都是建立在 TCP 之上的,所以 FRP 对 ADB 是完全透明的,不存在协议兼容问题。
2.3 整条链路的拓扑结构
把上面这些串在一起,完整链路是这样的:
客户现场车机 adbd 监听 127.0.0.1:5555 ↑ frpc(车机上运行,主动连接公网 VPS 的 7000 端口) ↓ 公网 VPS(frps 运行) 接收 frpc 注册,开放 15555 端口 ↓ 研发 PC adb connect VPS_IP:15555 → 流量经 frps 转发 → frpc → 车机 adbd链路里每个节点职责单一:车机跑 adbd 和 frpc,VPS 只做流量转发,研发 PC 只需要标准 ADB 工具。没有复杂的协议转换,没有额外的中间层。
3. 公网侧准备:FRP 服务端和研发连接环境
3.1 VPS 选型与基础安全配置
FRP 服务端最好放在离车机现场比较近的机房,这样能降低 RTT。如果车机大部分在华东,就选华东的节点;如果全国分散,选一个中部节点可能更均衡。配置不用太高,1 核 1G 内存跑 frps 绰绰有余,但带宽建议 5Mbps 以上,因为后面截屏、拉日志都走这条链路。
VPS 操作系统我选的是 Ubuntu 22.04 LTS,Debian 系在包管理上比较顺手。拿到机器后第一件事不是装 frps,而是先改 SSH 端口、禁用密码登录、配好防火墙。别小看这一步,公网机器暴露 22 端口默认口令很容易被爆破,尤其是这种以后要承载调试链路的机器,安全底线不能松。
3.2 新版 frps.toml 配置与启动方式
FRP 在 0.52 版本之后把配置格式从 INI 改成了 TOML。网上大量教程还在用老的 INI 写法,我强烈建议新部署直接用 TOML,因为后续版本已经逐步放弃对老格式的支持。下面是我在用的 frps.toml:
# /etc/frp/frps.toml bindPort = 7000 auth.method = "token" auth.token = "这里替换成强随机字符串" webServer.addr = "0.0.0.0" webServer.port = 7500 webServer.user = "admin" webServer.password = "这里替换成另一组强密码"这里解释下几个配置项的用途:
bindPort = 7000是 frpc 回连的端口,可以改成不常见的端口,不过因为后面有 token 做认证,端口本身不是最关键的。auth.token是 frpc 连接 frps 时的凭证,建议用openssl rand -hex 32生成一串 64 位十六进制随机字符串。千万别用 admin/123456 这种。webServer是 frps 自带的面板,能看当前有哪些代理在线、流量多少。这个只建议在排查问题时打开,平时可以把端口限制为研发出口 IP。
启动方式我用 systemd 托管,保证 VPS 重启后 frps 能自动起来。配置文件放在/etc/frp/frps.toml,service 文件内容如下:
[Unit] Description=FRP Server After=network.target [Service] Type=simple ExecStart=/usr/local/bin/frps -c /etc/frp/frps.toml Restart=always RestartSec=5 [Install] WantedBy=multi-user.target之后执行:
systemctl daemon-reload systemctl enable frps systemctl start frpsVPS 防火墙(如果用的是云厂商的安全组,也是类似配置)需要放行这么几个端口:7000/tcp给 frpc 回连,15555/tcp(以及后续规划的其它 ADB 映射端口)给研发连接,7500/tcp给面板。这里建议在云控制台的安全组里直接把 ADB 映射端口来源 IP 限制成研发团队的出口 IP 段,而不是对全公网开放。
3.3 研发本机 ADB 环境准备与连接验证
研发 PC 这侧不需要装 frpc,只需要标准的 ADB 工具。Android SDK Platform-Tools 里自带 adb,macOS 上也可以用 Homebrew 装android-platform-tools,Linux 发行版仓库里一般也有adb包。
装好之后,先确认版本和连接状态:
adb version然后测试连接:
adb connect VPS_IP:15555 adb devices如果看到状态是unauthorized,说明车机端弹了 RSA 授权确认框,需要去车机上点一下允许;如果看到device,链路就通了。第一次连接成功的时候,我还记得整个调试群都热闹了一下——因为这意味着客户现场那台反复出问题的车机,现在就在我们的工位上。
4. 车机侧落地:ADB 开启、frpc 部署、断线自愈
4.1 先把车载设备的 ADB 网络调试跑起来
车机这侧的准备工作,核心是把 adbd 切到 TCP 监听模式。普通 Android 设备在“开发者选项”里能直接开“无线调试”,但车载中控屏的固件千差万别,很多没有暴露这个开关。通用的做法是先用 USB 连上一台调试电脑,执行:
adb tcpip 5555这条命令会让 adbd 在 5555 端口开始监听 TCP 连接。执行完之后,USB 线就可以拔掉了,车机变成一个“ADB over TCP”的调试目标。
需要注意:Android 11 及以上引入了无线调试配对流程(adb pair),部分新版本固件里传统的adb tcpip 5555可能仍然可用,但有些定制系统会限制必须在设置界面里手动配对。如果遇到这种情况,解决办法是连 USB 时先把ro.adb.secure相关配置调成允许调试,或者走配对流程拿到 pairing code 后再做 FRP 转发。总之先把“车机本机能够被远程 adb connect”这一步跑通,再谈 FRP。
另外,车机必须保证在客户现场能联网。如果是走 Wi-Fi,要确保 Wi-Fi 不会在息屏后掉线;很多车机有“WLAN 休眠策略”,要设置成“始终连接”。如果走 4G/5G 模组,要确认 APN 配置正确、没有停机限速。
4.2 frpc 客户端的配置与部署
frpc 是单个可执行文件,官方 Release 页面会区分linux_amd64、linux_arm64、windows_amd64等版本。车载中控屏的 CPU 架构常见的是 ARM 或者 ARM64,选型时要注意:
adb shell getprop ro.product.cpu.abi比如输出是arm64-v8a,就下载frp_x.x.x_linux_arm64.tar.gz。如果没有合适的现成版本,也可以考虑交叉编译,但一般官方 Release 已经覆盖全了。
把 frpc 推送到车机上:
adb push frpc /data/local/tmp/ adb shell chmod 755 /data/local/tmp/frpc这里有一个非常容易踩的坑:Android 的/data/local/tmp分区在很多固件上是以 noexec 方式挂载的,直接运行会报Permission denied。解决办法有几个:
- 把 frpc 放到
/data/data/<某个已有应用的包名>/目录下,应用私有目录通常允许执行; - 或者放到
/system/xbin/(需要 system 分区可写,车机工程机一般可以 remount); - 如果是已经 root 的开发机,直接把 frpc 放进
/system/xbin/是最省心的。
frpc 的 TOML 配置如下:
# /data/local/tmp/frpc.toml serverAddr = "VPS公网IP" serverPort = 7000 auth.method = "token" auth.token = "和frps.toml里一致" [[proxies]] name = "car-001-adb" type = "tcp" localIP = "127.0.0.1" localPort = 5555 remotePort = 15555注意localIP这块,有人会写成车机的 Wi-Fi IP,其实没必要,因为 frpc 就跑在车机本机上,访问127.0.0.1就够了,还能避免多网卡路由问题。
启动:
/data/local/tmp/frpc -c /data/local/tmp/frpc.toml然后回到研发 PC:
adb connect VPS_IP:15555如果adb devices里出现device,说明整条链路已经打通。
4.3 守护进程与掉线自愈
车机现场的运行环境和实验室完全不同。Wi-Fi 不稳定、4G 信号切换、系统休眠、用户熄屏,任何一个因素都可能导致 frpc 断开或者 adbd 退出。所以车机侧的 frpc 不能是一次性命令,必须有一个守护机制。
我实测下来,最简单有效的方式是写一个循环脚本:
#!/system/bin/sh while true; do /data/local/tmp/frpc -c /data/local/tmp/frpc.toml echo "frpc exited, restart in 5 seconds" sleep 5 done把它存成frpc_guard.sh,丢到车机上用nohup后台启动。这样 frpc 因为断网、被杀进程等各种原因退出后,5 秒内会自动重新拉起来。
更讲究一点的做法是注册成 Android 系统服务或者用 init 脚本管理,但那需要在固件层做定制,适合量产预埋方案。对于研发阶段的临时调试,上面的 shell 循环完全够用。
另外还要注意 adbd 本身的稳定性。有时候车机和调试电脑之间 USB 没插好,或者 adbd 崩溃,TCP 5555 端口就没人监听了。此时 frpc 还活着,但连上后adb devices可能一直显示offline。判断方法是adb shell echo ok看是否返回,如果不通,就需要去现场或者用带外方式(比如车机的另一个管理通道)重启 adbd。这也是我在实际项目里比较头疼的问题之一,后面在稳定性章节里再细说。
4.4 量产预埋还是售后现场临时部署
这里有一个策略问题:frpc 是预埋到量产固件里,还是等车出了问题再现场部署?
- 预埋到量产固件:优点是车真出问题时链路已经在,不用等现场人员配合;缺点是有安全风险——每一台车都有一个能通过 FRP 访问到 ADB 的入口,如果 token 泄露或者 VPS 被攻破,后果很严重。如果要这么做,至少要做到每一台车独立的 token 和端口、ADB 授权白名单、远程调试开关按需开启。
- 售后现场临时部署:适合小批量问题车,风险低,但响应慢,需要现场人员愿意配合做 USB 连接。
我们的做法是:先临时部署解决眼前问题,同时推动固件团队把“远程调试代理”做成一款可独立安装、可停用的预置系统应用,平时不启用,只有售后申请后才下发凭证。这个路线比较稳妥,也符合车规安全的要求。
5. 连接成功之后,研发这侧最常用的一套调试流程
5.1 一键连接脚本与多设备端口规划
当链路通了之后,研发侧需要解决“多辆车同时在线”的管理问题。我们是这样规划的:每一辆车分配一个独立的 remote_port,形成一张简单的映射表。
| 车辆编号 | remote_port | 负责人 |
|---|---|---|
| car-001 | 15555 | 张三 |
| car-002 | 15556 | 李四 |
| car-003 | 15557 | 王五 |
然后给每个人配一个一键连接脚本:
#!/bin/bash # connect_car.sh 用法: ./connect_car.sh 15555 adb connect VPS_IP:$1 adb devices这样研发不用记 IP、不用记端口,只需要知道车辆编号对应的端口。
5.2 抓 logcat 与崩溃日志
连上之后最频繁的操作就是抓日志。常用命令:
# 抓完整日志,带线程时间信息 adb logcat -v threadtime > car001_full.log # 单独抓崩溃缓冲区 adb logcat -b crash -v threadtime > car001_crash.log # 抓 main + system 缓冲区 adb logcat -b main -b system -v threadtime实测下来,空闲状态下的 logcat 输出量大约 1-5KB/s,走 5Mbps 的 VPS 完全没压力。但如果车机处于满日志输出状态(比如在做视频编解码、导航语音播报),持续抓几个小时后文件会很大。建议启动 logcat 时带上过滤器,比如只看特定进程或者特定 tag:
adb logcat -v threadtime | grep -E "system_server|AndroidRuntime|FATAL"车机问题排查中,ANR 也是个高频场景。/data/anr/目录下会有 traces 文件,可以直接拉回来:
adb pull /data/anr/ car001_anr/5.3 截屏、录屏与界面状态采集
远程调试时,看到车机屏幕上的内容是刚需。截屏命令:
adb exec-out screencap -p > screen.png注意用exec-out而不是shell screencap,前者是二进制安全传输,不会因为终端转义把 PNG 搞坏。1080p 分辨率的截屏 PNG 通常在 2-5MB 左右,5Mbps 带宽下大约要 10 秒左右,可以接受。
需要记录连续操作过程时用录屏:
adb shell screenrecord /sdcard/record.mp4 --time-limit 30 adb pull /sdcard/record.mp4另外,判断当前车机停在哪个界面上,dumpsys window很好用:
adb shell dumpsys window | grep -E "mCurrentFocus|mFocusedApp"输出里能看到当前焦点窗口是哪个应用、哪个 Activity。遇到“界面卡住不动”“应用退到后台找不到”这类问题,这一条命令基本能定位方向。
5.4 远程执行命令与文件互传
远程操作车机是复现问题路径的关键。输入事件注入:
# 模拟点击坐标 adb shell input tap 400 800 # 模拟滑动 adb shell input swipe 200 600 800 600 500 # 模拟按键 adb shell input keyevent KEYCODE_HOME adb shell input keyevent KEYCODE_BACK文件互传之间,有时候需要临时推一个测试 APK:
adb install test.apk adb push config.xml /sdcard/ adb pull /data/data/com.example/databases/db.db ./如果现场车机已经启动了某个自动复现脚本,配合adb shell am start、am force-stop这些命令,就能远程完成一轮完整的复现-抓日志-分析循环。
6. 这条链路的稳定性与安全性调优
6.1 反复掉线、连上就断的排查
实测中遇到最头疼的问题是“链路通了,但用着用着就断了”。总结下来有以下几类原因:
车机 Wi-Fi 休眠。很多车机为了省电,会在息屏或几分钟无操作后断开 Wi-Fi,frpc 随之掉线。对策是在车机设置里找到 Wi-Fi 高级选项,把“休眠时保持 Wi-Fi 连接”设为始终;如果车机系统把这个选项藏起来了,可能要改系统设置项
wifi_sleep_policy。更重要的是在 frpc 守护脚本里加一层网络检测,比如 ping 不通 VPS 就重新拉起 Wi-Fi。adbd 状态异常。TCP 模式下 adbd 偶尔会假死,表现为 frpc 在线、
adb connect能连上,但任何命令都卡住。判断方法是用adb shell echo ok做个超时探测。遇到假死,简单粗暴的方法是去现场重启,但这不符合远程调试的初衷。后来我们在工程固件里加了一个系统服务,定时检查 adbd 状态,异常就自动重启 adbd。frpc 进程被杀。Android 系统在内存压力大时会杀后台进程。frpc 这种独立可执行文件不在应用管理框架内,但可能被 LMK 或其他清理机制处理。守护脚本 + 每 5 秒重启能兜住大部分情况。
6.2 ADB RSA 授权弹窗问题
这是远程调试最容易卡住的地方。普通 USB 连接调试时,电脑的 ADB 公钥会自动下发,车机上弹窗问“是否允许 USB 调试”,点一下允许就行。但远程链路里,弹窗出现在车机上,现场没人点。
有两个解决办法:
- 预置研发机器的公钥到车机。把研发电脑上的
~/.android/adbkey.pub内容追加到车机的/data/misc/adb/adb_keys文件里,重启 adbd 后新连接就不需要弹窗确认。追加命令:
adb push adbkey.pub /data/local/tmp/ adb shell "cat /data/local/tmp/adbkey.pub >> /data/misc/adb/adb_keys" adb shell "chown system:system /data/misc/adb/adb_keys" adb shell "pkill adbd" # 或者系统服务重启 adbd- 设置
ro.adb.secure=0。这个属性关闭 ADB 认证,任何能连到端口的人都能直接获得 root 权限的 ADB 访问。仅适合工程调试机,量产固件千万别这么干。
我们的经验是:研发阶段的工程用车机可以预置好研发团队所有电脑的公钥,量产售后场景用独立的调试开关。
6.3 带宽、延迟对远程调试的影响
公网 VPS 的带宽和延迟决定了远程调试的体验上限。
- 延迟:如果车机在西北,VPS 在华东,冬天开个
adb shell命令可能等 1-2 秒才有返回,用起来很煎熬。解决办法是尽量选离车机最近的云节点,或者用一个简单的测速脚本评估多个节点再决定。 - 带宽:截屏、拉大文件时带宽不够会明显拖慢节奏。1080p 截屏 2-5MB,5Mbps 带宽约 10 秒,如果是项目高峰期几个人同时拉日志,带宽会被抢光。VPS 带宽尽量选 10Mbps 以上,成本不高但体验好很多。
- 批量执行代替逐条交互:延迟高的时候,逐条敲命令很痛苦。建议把常见操作组合成 shell 脚本,一条
adb shell跑完,减少往返次数。比如诊断信息采集:
adb shell "logcat -d -v threadtime > /sdcard/logcat_full.txt; dumpsys meminfo > /sdcard/meminfo.txt; dumpsys window >> /sdcard/window.txt" adb pull /sdcard/logcat_full.txt adb pull /sdcard/meminfo.txt adb pull /sdcard/window.txt6.4 访问控制与最小暴露原则
最后聊安全。FRP 把 ADB 暴露到公网,本质上是在攻击面上开了一个口子。一定要遵守最小暴露原则:
- 非默认端口:frps 的
bindPort和 ADB 的remotePort都别用默认值,越不显眼越好。 - 强 token:每个车机用独立的 token,万一某个现场设备丢了,可以单独吊销,不影响其它车。
- 防火墙白名单:在 VPS 安全组里把 ADB 映射端口(15555 等)限制为研发团队出口 IP 段;frps 面板端口只对特定管理 IP 开放。
- 及时关闭:调试完成、问题闭环后,关掉 frpc 进程,必要时回收 VPS 上的映射端口。别让一台已经交付客户的车长期保持 ADB 公网可达状态。
- 审计日志:frps 自带连接日志,定期检查有没有异常来源 IP 尝试连接。
最后再分享一点经验
这套链路从搭建到稳定运行,前后迭代了好几轮。最初我们也想过要不要做一个更“高大上”的远程调试平台,但后来发现 FRP+ADB 的组合已经解决了 90% 的问题。真正难的不是技术,而是让每个环节都具备无人值守的稳定性——车机侧掉线能自愈,研发侧有统一的端口规划,现场人员不需要参与技术操作。如果你也在做车载中控屏的远程调试,建议先从一台工程车跑通链路,再逐步推广;千万别一上来就在量产固件里预埋 frpc,安全和运维的压力会大得多。