修改注释及代码重构
This commit is contained in:
137
adb/device.go
137
adb/device.go
@@ -10,23 +10,25 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// DeviceInfo 包含从 adb server 列表和设备端 getprop 收集到的信息
|
||||
// DeviceInfo 包含从 ADB 服务器设备列表和设备端 getprop 收集到的设备信息
|
||||
type DeviceInfo struct {
|
||||
Serial string
|
||||
State string
|
||||
Product string
|
||||
Model string
|
||||
Device string
|
||||
TransportID string
|
||||
Props map[string]string
|
||||
Serial string // 设备序列号
|
||||
State string // 设备状态(device/offline/unauthorized)
|
||||
Product string // 产品名称
|
||||
Model string // 设备型号
|
||||
Device string // 设备代号
|
||||
TransportID string // ADB 传输 ID
|
||||
Props map[string]string // 额外属性
|
||||
}
|
||||
|
||||
// AdbDevice 表示一个已建立连接的 ADB 设备
|
||||
type AdbDevice struct {
|
||||
Connect net.Conn
|
||||
Serial string
|
||||
Connect net.Conn // 到设备的 TCP 连接
|
||||
Serial string // 设备序列号
|
||||
}
|
||||
|
||||
// ListDevicesRaw: 请求 host:devices 并返回原始 payload
|
||||
// ListDevicesRaw 向 ADB 服务器请求设备列表并返回原始文本
|
||||
// addr 为 ADB 服务器地址,timeout 为超时时间
|
||||
func ListDevicesRaw(addr string, timeout time.Duration) (string, error) {
|
||||
conn, err := DialADB(addr, timeout)
|
||||
if err != nil {
|
||||
@@ -43,17 +45,17 @@ func ListDevicesRaw(addr string, timeout time.Duration) (string, error) {
|
||||
}
|
||||
if status == "FAIL" {
|
||||
msg, _ := ReadLenFrame(conn)
|
||||
return "", fmt.Errorf("adb FAIL: %s", string(msg))
|
||||
return "", fmt.Errorf("ADB 返回失败: %s", string(msg))
|
||||
}
|
||||
if status != "OKAY" {
|
||||
return "", fmt.Errorf("unexpected status: %s", status)
|
||||
return "", fmt.Errorf("意外的状态码: %s", status)
|
||||
}
|
||||
|
||||
var parts []string
|
||||
for {
|
||||
data, err := ReadLenFrame(conn)
|
||||
if err != nil {
|
||||
// treat short read timeout as finish
|
||||
// 超时视为读取结束
|
||||
if ne, ok := err.(net.Error); ok && ne.Timeout() {
|
||||
break
|
||||
}
|
||||
@@ -71,7 +73,8 @@ func ListDevicesRaw(addr string, timeout time.Duration) (string, error) {
|
||||
return strings.Join(parts, ""), nil
|
||||
}
|
||||
|
||||
// ParseDevicesPayload: 解析 host:devices 返回的 payload,提取可能的 product/model/device/transport_id
|
||||
// ParseDevicesPayload 解析 host:devices-l 命令返回的文本
|
||||
// 提取设备的序列号、状态、product/model/device/transport_id 等信息
|
||||
func ParseDevicesPayload(payload string) []DeviceInfo {
|
||||
out := []DeviceInfo{}
|
||||
lines := strings.Split(payload, "\n")
|
||||
@@ -80,7 +83,7 @@ func ParseDevicesPayload(payload string) []DeviceInfo {
|
||||
if ln == "" || strings.HasPrefix(ln, "List of devices attached") {
|
||||
continue
|
||||
}
|
||||
// adb -l 格式通常:serial <state> key:val key:val ...
|
||||
// adb -l 格式:serial <state> key:val key:val ...
|
||||
fields := strings.Fields(ln)
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
@@ -90,7 +93,7 @@ func ParseDevicesPayload(payload string) []DeviceInfo {
|
||||
State: fields[1],
|
||||
Props: map[string]string{},
|
||||
}
|
||||
// parse remaining key:val pairs
|
||||
// 解析剩余的 key:val 键值对
|
||||
for _, kv := range fields[2:] {
|
||||
if strings.Contains(kv, ":") {
|
||||
parts := strings.SplitN(kv, ":", 2)
|
||||
@@ -106,7 +109,7 @@ func ParseDevicesPayload(payload string) []DeviceInfo {
|
||||
case "transport_id":
|
||||
dev.TransportID = v
|
||||
default:
|
||||
// store any extra short fields into Props under prefixed key
|
||||
// 将未知字段存入 Props,添加 "short." 前缀
|
||||
dev.Props["short."+k] = v
|
||||
}
|
||||
}
|
||||
@@ -116,26 +119,24 @@ func ParseDevicesPayload(payload string) []DeviceInfo {
|
||||
return out
|
||||
}
|
||||
|
||||
// parseGetprop parses getprop output "key]: [value" lines into map
|
||||
// parseGetprop 解析 getprop 命令的输出
|
||||
// 输出格式为 "[key]: [value]",解析为 map
|
||||
func parseGetprop(raw []byte) map[string]string {
|
||||
m := map[string]string{}
|
||||
sc := bufio.NewScanner(strings.NewReader(string(raw)))
|
||||
for sc.Scan() {
|
||||
line := strings.TrimSpace(sc.Text())
|
||||
// getprop lines are like: [ro.build.version.release]: [10]
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
// find first ']:' separator
|
||||
// safe parse: extract between first '[' and first ']: [' pattern
|
||||
// simpler: split by "]: [" into two parts after trimming surrounding brackets
|
||||
// getprop 输出格式:[ro.build.version.release]: [10]
|
||||
parts := strings.SplitN(line, "]: [", 2)
|
||||
if len(parts) == 2 {
|
||||
k := strings.TrimPrefix(parts[0], "[")
|
||||
v := strings.TrimSuffix(parts[1], "]")
|
||||
m[k] = v
|
||||
} else {
|
||||
// fallback: try split by ": "
|
||||
// 回退:尝试按 ": " 分割
|
||||
kv := strings.SplitN(line, ": ", 2)
|
||||
if len(kv) == 2 {
|
||||
m[strings.Trim(kv[0], "[]")] = strings.Trim(kv[1], "[]")
|
||||
@@ -145,6 +146,8 @@ 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")
|
||||
@@ -170,7 +173,8 @@ func ParseDevicesMap(payload string) (map[string]string, []string) {
|
||||
return m, lines
|
||||
}
|
||||
|
||||
// Find device serial by product value and return serial (first match)
|
||||
// FindSerialByProduct 根据产品名称查找设备序列号
|
||||
// 优先从设备列表中匹配,如果列表中没有 product 字段,则回退到逐设备查询 getprop
|
||||
func FindSerialByProduct(addr, targetProduct string) (string, error) {
|
||||
payload, err := ListDevicesRaw(addr, 3*time.Second)
|
||||
if err != nil {
|
||||
@@ -182,9 +186,8 @@ func FindSerialByProduct(addr, targetProduct string) (string, error) {
|
||||
return serial, nil
|
||||
}
|
||||
}
|
||||
// fallback: if no product fields in devices-l, try per-device getprop
|
||||
payloadBasic, _ := ListDevicesRaw(addr, 3*time.Second) // reuse; could be host:devices if preferred
|
||||
lines := strings.Split(payloadBasic, "\n")
|
||||
// 回退:逐设备查询 getprop ro.product.model
|
||||
lines := strings.Split(payload, "\n")
|
||||
for _, ln := range lines {
|
||||
ln = strings.TrimSpace(ln)
|
||||
if ln == "" || strings.HasPrefix(ln, "List of devices attached") {
|
||||
@@ -195,12 +198,10 @@ func FindSerialByProduct(addr, targetProduct string) (string, error) {
|
||||
continue
|
||||
}
|
||||
serial := fields[0]
|
||||
// query getprop ro.product.model for this serial
|
||||
conn, err := DialADB(addr, 2*time.Second)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// ensure close
|
||||
defer conn.Close()
|
||||
if err := TransportTo(conn, serial); err != nil {
|
||||
continue
|
||||
@@ -212,91 +213,49 @@ func FindSerialByProduct(addr, targetProduct string) (string, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("no device with product=%s found", targetProduct)
|
||||
return "", fmt.Errorf("未找到产品名为 %s 的设备", targetProduct)
|
||||
}
|
||||
|
||||
// 安装APK到设备
|
||||
// InstallApkOnDevice 安装 APK 到设备
|
||||
// addr 为 ADB 服务器地址,serial 为设备序列号
|
||||
// remoteTmp 为 APK 在设备上的临时路径
|
||||
// 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)
|
||||
if err != nil {
|
||||
fmt.Println("dial error:", err)
|
||||
fmt.Println("连接失败:", err)
|
||||
return "", err
|
||||
}
|
||||
defer conn.Close()
|
||||
TransportTo(conn, serial)
|
||||
// 1. 准备远端临时路径
|
||||
|
||||
if pmArgs == "" {
|
||||
pmArgs = "-r"
|
||||
}
|
||||
|
||||
// 执行 shell: pm install ...
|
||||
// 使用 exec shell(非 interactive)通过 "shell:<cmd>"
|
||||
// 执行 pm install 命令
|
||||
installCmd := "pm install " + pmArgs + " " + remoteTmp
|
||||
if debug {
|
||||
fmt.Printf("[DEBUG] running shell command: %s\n", installCmd)
|
||||
fmt.Printf("[调试] 执行命令: %s\n", installCmd)
|
||||
}
|
||||
outBuf, err := ExecShell(conn, installCmd)
|
||||
|
||||
if err != nil {
|
||||
log.Println("[ERROR] ", err)
|
||||
log.Println("[错误]", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
outStr := string(outBuf)
|
||||
if debug {
|
||||
fmt.Printf("[DEBUG] pm install output:\n%s\n", outStr)
|
||||
fmt.Printf("[调试] pm install 输出:\n%s\n", outStr)
|
||||
}
|
||||
|
||||
// 安装完成后删除临时文件
|
||||
ExecShell(conn, "rm -f "+remoteTmp)
|
||||
// 5. 根据 pm 输出判断成功(pm install 成功通常包含 "Success")
|
||||
if containsSuccess(outStr) {
|
||||
|
||||
// 根据输出判断是否安装成功
|
||||
if strings.Contains(strings.ToLower(outStr), "success") {
|
||||
return outStr, nil
|
||||
}
|
||||
return outStr, fmt.Errorf("install failed: %s", outStr)
|
||||
return outStr, fmt.Errorf("安装失败: %s", outStr)
|
||||
}
|
||||
|
||||
func containsSuccess(s string) bool {
|
||||
// 简单判断:忽略大小写包含 "success"
|
||||
return (len(s) > 0) && (stringContainsFold(s, "success"))
|
||||
}
|
||||
|
||||
func stringContainsFold(s, sub string) bool {
|
||||
// 不依赖 strings 包的 ToLower 性能差别,这里直接用标准方法
|
||||
return (len(s) >= len(sub)) && (IndexFold(s, sub) >= 0)
|
||||
}
|
||||
|
||||
func IndexFold(s, sub string) int {
|
||||
// 直接使用 strings 包实现(为了清晰,这里直接调用)
|
||||
return indexFoldUsingStrings(s, sub)
|
||||
}
|
||||
|
||||
func indexFoldUsingStrings(s, sub string) int {
|
||||
// 实际工程里直接用 strings.Contains(strings.ToLower(s), strings.ToLower(sub))
|
||||
// 但为了最小示例,这里直接实现:
|
||||
// 换成标准库实现:
|
||||
//
|
||||
// 注意:下面两行才是简洁实现
|
||||
//
|
||||
// import "strings"
|
||||
// return strings.Index(strings.ToLower(s), strings.ToLower(sub))
|
||||
//
|
||||
// 这里我们直接调用:
|
||||
return stringsIndexFold(s, sub)
|
||||
}
|
||||
|
||||
func stringsIndexFold(s, sub string) int {
|
||||
// 调用标准库
|
||||
// 把此函数简单实现为:
|
||||
// strings.Index(strings.ToLower(s), strings.ToLower(sub))
|
||||
// 以便示例完整可运行
|
||||
// 这里需要引入 strings 包
|
||||
// 为了保持示例简洁,我在文件顶部添加下面两行导入:
|
||||
//
|
||||
// "strings"
|
||||
//
|
||||
// 然后直接实现:
|
||||
return stringsIndex(stringsToLower(s), stringsToLower(sub))
|
||||
}
|
||||
|
||||
func stringsToLower(s string) string { return strings.ToLower(s) }
|
||||
func stringsIndex(a, b string) int { return strings.Index(a, b) }
|
||||
|
||||
Reference in New Issue
Block a user