Android 车载中控屏远程调试:用 FRP 把 ADB 从客户现场接回研发环境

6 阅读3分钟

Android 车载中控屏远程调试:用 FRP 把 ADB 从客户现场接回研发环境

工程车辆上的 Android 中控屏通常需要连接串口、CAN、USB、网络和各种外围设备,很多问题只有整车安装后才会暴露。公司环境难以完整复现现场条件,客户又很难协助执行 shell、导日志或复现调试动作,因此需要让研发能够直接远程获得 ADB + Shell + Logcat + 屏幕控制 能力。

整体方案

局域网内可以直接开启 ADB TCP:

adb tcpip 5555

但现场设备通常位于 Wi-Fi、4G 或运营商 NAT 后,没有公网 IP。ADB over TCP/IP 只解决了 ADB 网络化,没有解决公网访问问题。

最终链路:

研发电脑 → 公网 frps → Android frpc → 127.0.0.1:5555 → adbd

核心就是把现场设备的 5555 端口通过 FRP 映射到研发侧,再继续使用 Android 原有调试工具:

adb connect
adb shell
adb logcat
scrcpy

FRP:

github.com/fatedier/fr…

FRP 服务端

公网服务器运行 frps

docker-compose.yml

version: '3'

services:
  frps:
    image: snowdreamtech/frps
    container_name: frps
    restart: always
    network_mode: host
    volumes:
      - ./frps.toml:/etc/frp/frps.toml

frps.toml

bindPort = 7000
auth.token = "tkoen"

webServer.addr = "0.0.0.0"
webServer.port = 7500
webServer.user = "user"
webServer.password = "password"

7000 用于 frpc 回连,7500 用于查看 FRP 状态。服务器安全组和防火墙需要放行对应端口,实际部署应更换示例中的 Token、用户名和密码。

Android 独立远程调试 App

没有把 FRP 集成进业务 App,而是单独做一个无界面的系统调试 App,只负责:

  • 监听开机广播
  • 开启 ADB TCP
  • 启动 frpc
  • 设备重启后自动恢复远程调试链路

这样可以让远程调试能力与业务 App 解耦,避免业务版本更新影响调试链路。

将 FRP 封装成 Android AAR

Android 端不重新实现 FRP,而是复用 FRP 的 Go 实现,并通过 gomobile 封装成 AAR。

Go 侧提供一个简单入口:

package frp

import (
	"context"
	"fmt"
	"github.com/fatedier/frp/client"
	"github.com/fatedier/frp/pkg/config"
	"github.com/fatedier/frp/pkg/config/v1/validation"
	"github.com/fatedier/frp/pkg/util/log"
	"github.com/fatedier/frp/server"
	"os"
)

func RunClient(cfgFile string) {
	cfg, proxyCfgs, visitorCfgs, isLegacyFormat, err := config.LoadClientConfig(cfgFile, true)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	if isLegacyFormat {
		fmt.Printf("WARNING: ini format is deprecated and the support will be removed in the future, please use yaml/json/toml format instead!\n")
	}

	warning, err := validation.ValidateAllClientConfig(cfg, proxyCfgs, visitorCfgs)
	if warning != nil {
		fmt.Printf("WARNING: %v\n", warning)
	}
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	log.InitLogger(cfg.Log.To, cfg.Log.Level, int(cfg.Log.MaxDays), cfg.Log.DisablePrintColor)

	svr, err := client.NewService(client.ServiceOptions{
		Common:         cfg,
		ProxyCfgs:      proxyCfgs,
		VisitorCfgs:    visitorCfgs,
		ConfigFilePath: cfgFile,
	})
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	svr.Run(context.Background())
}

func RunServer(cfgFile string) {
	cfg, isLegacyFormat, err := config.LoadServerConfig(cfgFile, true)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	if isLegacyFormat {
		fmt.Printf("WARNING: ini format is deprecated and the support will be removed in the future, please use yaml/json/toml format instead!\n")
	}

	warning, err := validation.ValidateServerConfig(cfg)
	if warning != nil {
		fmt.Printf("WARNING: %v\n", warning)
	}
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	log.InitLogger(cfg.Log.To, cfg.Log.Level, int(cfg.Log.MaxDays), cfg.Log.DisablePrintColor)

	svr, err := server.NewService(cfg)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	log.Infof("frps started successfully")
	svr.Run(context.Background())
}

Android 端只需要调用 RunClient,不需要关心 FRP 内部实现。

安装 gomobile

go install golang.org/x/mobile/cmd/gomobile@latest
gomobile init
go get golang.org/x/mobile/bind

生成 AAR:

gomobile bind -androidapi 21 .

生成:

frp.aar
frp-sources.jar

frp.aar 放入 Android 项目的 libs

implementation(files("libs/frp.aar"))

Android 侧准备好 FRP 配置文件后调用:

Frp.runClient(configFilePath)

开机自动开启 ADB TCP

FRP 映射之前,需要先让 adbd 监听 TCP 5555

setprop service.adb.tcp.port 5555
stop adbd
start adbd

独立 App 收到开机广播后执行这一步,再启动 frpc

Android 开机
→ 开启 adbd:5555
→ 启动 frpc
→ frpc 连接公网 frps
→ 映射 5555
→ 研发侧 adb connect

这样设备掉电或重启后可以自动恢复链路,不需要现场人员重新输入命令。

研发侧连接

假设现场设备的 5555 被映射到研发侧可访问的 10000

adb connect 127.0.0.1:10000

确认设备:

adb devices

进入 Shell:

adb shell

查看日志:

adb logcat

安装 APK:

adb install -r app.apk

查看并操作设备界面可以使用 scrcpy

github.com/genymobile/…

scrcpy

最终研发侧可以直接完成:

ADB → Shell → Logcat → APK 安装 → 界面查看 → 远程操作

最终链路

Android 中控屏
├── adbd :5555
├── 独立调试 App
│   ├── BootReceiver
│   ├── 开启 ADB TCP
│   └── 启动 FRP AAR
│
└── frpc
     ↓
Internet
     ↓
frps 公网服务器
     ↓
研发电脑
├── adb connect
├── adb shell
├── adb logcat
└── scrcpy

这套方案没有重新实现远程调试协议,只通过 FRP 打通 ADB TCP 链路,再复用 Android 原有的 ADB 和 scrcpy 工具链。