完成基础功能
This commit is contained in:
@@ -202,35 +202,71 @@ func ReadResponse(conn net.Conn, debug bool) (string, []byte, error) {
|
||||
return st, nil, nil
|
||||
}
|
||||
|
||||
// LaunchUiautomator 连接 ADB 服务器,路由到指定设备,启动 UIAutomator2 服务
|
||||
// 启动后持续将服务日志输出到标准输出
|
||||
// addr 为 ADB 服务器地址,serial 为设备序列号
|
||||
func LaunchUiautomator(addr, serial string) {
|
||||
conn, err := DialADB(addr, 15*time.Second)
|
||||
// ConnectToDevice 建立到指定设备的 ADB 连接并完成路由
|
||||
// 返回已路由到目标设备的连接,调用者负责关闭
|
||||
// 这是一个便捷函数,封装了 DialADB + TransportTo 的常见组合
|
||||
func ConnectToDevice(addr, serial string, timeout time.Duration) (net.Conn, error) {
|
||||
conn, err := DialADB(addr, timeout)
|
||||
if err != nil {
|
||||
fmt.Println("连接失败:", err)
|
||||
return
|
||||
return nil, fmt.Errorf("连接 ADB 服务器失败: %w", err)
|
||||
}
|
||||
|
||||
// 路由到目标设备
|
||||
if err := TransportTo(conn, serial); err != nil {
|
||||
fmt.Println("设备路由失败:", err)
|
||||
return
|
||||
conn.Close()
|
||||
return nil, fmt.Errorf("设备路由失败: %w", err)
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// 启动 UIAutomator2 服务
|
||||
cmd := "shell:CLASSPATH=/data/local/tmp/u2.jar app_process / com.wetest.uia2.Main"
|
||||
if err := WriteAdbCmd(conn, cmd); err != nil {
|
||||
fmt.Println("发送命令失败:", err)
|
||||
return
|
||||
// PushFile 通过 ADB 推送本地文件到设备
|
||||
// 这是一个独立函数,不依赖 Device 实例,适合初始化阶段使用
|
||||
// addr 为 ADB 服务器地址,serial 为设备序列号
|
||||
// localPath 为本地文件路径,remotePath 为设备目标路径
|
||||
// mode 为文件权限(如 0644),debug 为 true 时输出调试信息
|
||||
func PushFile(addr, serial, localPath, remotePath string, mode int, debug bool) (int64, error) {
|
||||
conn, err := ConnectToDevice(addr, serial, 15*time.Second)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
s := InitSync(conn)
|
||||
return s.SyncPushFile(localPath, remotePath, mode, debug)
|
||||
}
|
||||
|
||||
// ---------- ADB 隧道(与 Python uiautomator2 完全一致) ----------
|
||||
|
||||
// CreateTunnel 建立到设备指定端口的 ADB 隧道
|
||||
// 等同于 Python adbutils 的 device.create_connection(Network.TCP, port)
|
||||
//
|
||||
// 工作原理:
|
||||
// 1. 连接 ADB 服务器
|
||||
// 2. 通过 transport 选择目标设备
|
||||
// 3. 发送 tcp:<port> 命令建立隧道
|
||||
// 4. 返回的连接是一条直通设备端口的原始 TCP 管道
|
||||
//
|
||||
// 返回的连接由调用者负责关闭
|
||||
func CreateTunnel(addr, serial string, port int) (net.Conn, error) {
|
||||
conn, err := DialADB(addr, 10*time.Second)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("连接 ADB 服务器失败: %w", err)
|
||||
}
|
||||
if err := TransportTo(conn, serial); err != nil {
|
||||
conn.Close()
|
||||
return nil, fmt.Errorf("设备路由失败: %w", err)
|
||||
}
|
||||
if err := WriteAdbCmd(conn, fmt.Sprintf("tcp:%d", port)); err != nil {
|
||||
conn.Close()
|
||||
return nil, fmt.Errorf("发送隧道命令失败: %w", err)
|
||||
}
|
||||
status, err := ReadStatus(conn)
|
||||
if err != nil {
|
||||
fmt.Println("读取状态失败:", err)
|
||||
return
|
||||
conn.Close()
|
||||
return nil, fmt.Errorf("读取隧道状态失败: %w", err)
|
||||
}
|
||||
fmt.Println("UIAutomator2 启动状态:", status)
|
||||
|
||||
// 持续输出 UIAutomator2 服务日志
|
||||
io.Copy(os.Stdout, conn)
|
||||
if status != "OKAY" {
|
||||
msg, _ := ReadLenFrame(conn)
|
||||
conn.Close()
|
||||
return nil, fmt.Errorf("ADB 隧道建立失败: %s %s", status, string(msg))
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
@@ -21,12 +21,6 @@ type DeviceInfo struct {
|
||||
Props map[string]string // 额外属性
|
||||
}
|
||||
|
||||
// AdbDevice 表示一个已建立连接的 ADB 设备
|
||||
type AdbDevice struct {
|
||||
Connect net.Conn // 到设备的 TCP 连接
|
||||
Serial string // 设备序列号
|
||||
}
|
||||
|
||||
// ListDevicesRaw 向 ADB 服务器请求设备列表并返回原始文本
|
||||
// addr 为 ADB 服务器地址,timeout 为超时时间
|
||||
func ListDevicesRaw(addr string, timeout time.Duration) (string, error) {
|
||||
@@ -146,33 +140,6 @@ func parseGetprop(raw []byte) map[string]string {
|
||||
return m
|
||||
}
|
||||
|
||||
// ParseDevicesMap 将设备列表文本解析为 serial → product 的映射
|
||||
// 同时返回原始行列表
|
||||
func ParseDevicesMap(payload string) (map[string]string, []string) {
|
||||
m := make(map[string]string)
|
||||
lines := strings.Split(payload, "\n")
|
||||
for _, ln := range lines {
|
||||
ln = strings.TrimSpace(ln)
|
||||
if ln == "" || strings.HasPrefix(ln, "List of devices attached") {
|
||||
continue
|
||||
}
|
||||
fields := strings.Fields(ln)
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
serial := fields[0]
|
||||
product := ""
|
||||
for _, kv := range fields[2:] {
|
||||
if strings.HasPrefix(kv, "product:") {
|
||||
product = strings.TrimPrefix(kv, "product:")
|
||||
break
|
||||
}
|
||||
}
|
||||
m[serial] = product
|
||||
}
|
||||
return m, lines
|
||||
}
|
||||
|
||||
// FindSerialByProduct 根据产品名称查找设备序列号
|
||||
// 优先从设备列表中匹配,如果列表中没有 product 字段,则回退到逐设备查询 getprop
|
||||
func FindSerialByProduct(addr, targetProduct string) (string, error) {
|
||||
@@ -180,10 +147,10 @@ func FindSerialByProduct(addr, targetProduct string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
m, _ := ParseDevicesMap(payload)
|
||||
for serial, product := range m {
|
||||
if product == targetProduct {
|
||||
return serial, nil
|
||||
devices := ParseDevicesPayload(payload)
|
||||
for _, dev := range devices {
|
||||
if dev.Product == targetProduct {
|
||||
return dev.Serial, nil
|
||||
}
|
||||
}
|
||||
// 回退:逐设备查询 getprop ro.product.model
|
||||
@@ -198,15 +165,12 @@ func FindSerialByProduct(addr, targetProduct string) (string, error) {
|
||||
continue
|
||||
}
|
||||
serial := fields[0]
|
||||
conn, err := DialADB(addr, 2*time.Second)
|
||||
conn, err := ConnectToDevice(addr, serial, 2*time.Second)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
defer conn.Close()
|
||||
if err := TransportTo(conn, serial); err != nil {
|
||||
continue
|
||||
}
|
||||
out, err := ExecShell(conn, "getprop ro.product.model")
|
||||
conn.Close()
|
||||
if err == nil {
|
||||
if strings.TrimSpace(string(out)) == targetProduct {
|
||||
return serial, nil
|
||||
@@ -222,13 +186,11 @@ func FindSerialByProduct(addr, targetProduct string) (string, error) {
|
||||
// pmArgs 为 pm install 的额外参数(如 "-r" 表示覆盖安装),默认为 "-r"
|
||||
// debug 为 true 时输出调试信息
|
||||
func InstallApkOnDevice(addr, serial string, remoteTmp string, pmArgs string, debug bool) (string, error) {
|
||||
conn, err := DialADB(addr, 15*time.Second)
|
||||
conn, err := ConnectToDevice(addr, serial, 15*time.Second)
|
||||
if err != nil {
|
||||
fmt.Println("连接失败:", err)
|
||||
return "", err
|
||||
}
|
||||
defer conn.Close()
|
||||
TransportTo(conn, serial)
|
||||
|
||||
if pmArgs == "" {
|
||||
pmArgs = "-r"
|
||||
|
||||
Reference in New Issue
Block a user