新增文件推送方法

This commit is contained in:
zyj
2025-10-30 16:45:57 +08:00
parent a03d13613c
commit 0d26525086
4 changed files with 62 additions and 304 deletions

View File

@@ -7,6 +7,7 @@ import (
"net"
"os"
"strconv"
"syscall"
"time"
)
@@ -22,52 +23,13 @@ func InitSync(conn net.Conn) *Sync {
}
}
// SyncPushTryVariants 按顺序尝试多种 SEND payload 风格,直到成功或尝试完毕。
// addr: adb server (e.g., "127.0.0.1:5037")
// serial: device serial
// SyncPushFile 将本地文件推送到设备
// localPath: 本地文件路径
// remotePath: 目标完整路径(必须包含文件名)
// mode: unix permission like 0644
// debug: 打印调试信息
func SyncPushTryVariants(addr, serial, localPath, remotePath string, mode int, debug bool) (int64, error) {
type sendOption struct {
name string
withNUL bool
modeFormat string // "hex" (0x8000|mode) or "dec" (decimal S_IFREG|mode)
}
opts := []sendOption{
{"send-with-nul-hex", true, "hex"},
{"send-without-nul-hex", false, "hex"},
{"send-without-nul-dec", false, "dec"},
}
var lastErr error
for _, opt := range opts {
if debug {
fmt.Printf("[try] option=%s\n", opt.name)
}
n, err := syncPushOne(addr, serial, localPath, remotePath, mode, opt.withNUL, opt.modeFormat, debug)
if err == nil {
if debug {
fmt.Printf("[ok] option=%s pushed=%d\n", opt.name, n)
}
return n, nil
}
lastErr = fmt.Errorf("%s: %w", opt.name, err)
if debug {
fmt.Printf("[fail] option=%s err=%v\n", opt.name, err)
}
// small pause between tries
time.Sleep(150 * time.Millisecond)
}
return 0, lastErr
}
// syncPushOne 在单个连接上按给定选项完成 sync push一次性连接
// withNUL: 是否在 SEND payload 后追加 NUL byte
// modeFormat: "hex" 表示 use 0x8000|mode in decimal string (common), "dec" 表示 decimal S_IFREG|mode
func syncPushOne(addr, serial, localPath, remotePath string, mode int, withNUL bool, modeFormat string, debug bool) (int64, error) {
// open file
// remotePath: 设备目标路径
// mode: 文件权限 (如 0644)
// debug: 是否打印调试信息
func (s *Sync) SyncPushFile(localPath, remotePath string, mode int, debug bool) (int64, error) {
// 打开文件
f, err := os.Open(localPath)
if err != nil {
return 0, err
@@ -75,130 +37,44 @@ func syncPushOne(addr, serial, localPath, remotePath string, mode int, withNUL b
defer f.Close()
fi, _ := f.Stat()
if debug {
fmt.Printf("local filesize=%d\n", fi.Size())
fmt.Printf("[DEBUG] local filesize=%d\n", fi.Size())
}
// dial adb
d := net.Dialer{Timeout: 8 * time.Second}
conn, err := d.Dial("tcp", addr)
if err != nil {
// 打开 sync
if err := WriteAdbCmd(s.Conn, "sync:"); err != nil {
return 0, err
}
defer conn.Close()
if tcp, ok := conn.(*net.TCPConn); ok {
_ = tcp.SetNoDelay(true)
}
writeAdbCmd := func(cmd string) error {
hdr := fmt.Sprintf("%04x", len(cmd))
conn.SetWriteDeadline(time.Now().Add(5 * time.Second))
_, err := conn.Write([]byte(hdr + cmd))
return err
}
readN := func(n int) ([]byte, error) {
buf := make([]byte, n)
total := 0
for total < n {
conn.SetReadDeadline(time.Now().Add(5 * time.Second))
nr, err := conn.Read(buf[total:])
if err != nil {
return nil, err
}
total += nr
}
return buf, nil
}
read4 := func() ([]byte, error) { return readN(4) }
// readResponse that returns status and optional msg (LE length)
readResponse := func() (string, []byte, error) {
stb, err := read4()
if err != nil {
return "", nil, err
}
st := string(stb)
// if OKAY quick return
if st == "OKAY" {
return st, nil, nil
}
if st == "FAIL" {
// try LE length
hdr, err := read4()
if err != nil {
return st, nil, nil
}
l := int(binary.LittleEndian.Uint32(hdr))
if l > 0 {
msg, err := readN(l)
if err != nil {
return st, nil, nil
}
return st, msg, nil
}
// fallback ascii-hex
if n, perr := strconv.ParseInt(string(hdr), 16, 32); perr == nil && n > 0 {
msg, err := readN(int(n))
if err != nil {
return st, nil, nil
}
return st, msg, nil
}
return st, nil, nil
}
return st, nil, nil
}
// transport
if err := writeAdbCmd("host:transport:" + serial); err != nil {
return 0, err
}
tok, err := read4()
tok, err := readN(s.Conn, 4, 10*time.Second)
if err != nil {
return 0, err
}
if string(tok) != "OKAY" {
return 0, fmt.Errorf("transport failed: %q", string(tok))
}
// open sync
if err := writeAdbCmd("sync:"); err != nil {
return 0, err
}
tok, err = read4()
if err != nil {
return 0, err
}
if string(tok) != "OKAY" {
_, msg, _ := readResponse()
_, msg, _ := ReadResponse(s.Conn, true)
if len(msg) > 0 {
return 0, fmt.Errorf("sync open failed: %s", string(msg))
}
return 0, fmt.Errorf("sync open failed: %q", string(tok))
}
// build SEND payload according options
var modeStr string
if modeFormat == "hex" {
// common implementations expect decimal of (S_IFREG|mode) where S_IFREG is 0100000 (octal) but using 0x8000 is fine as decimal string
modeStr = strconv.FormatInt(int64(0x8000|mode), 10)
} else {
// decimal form: simply decimal of (0x8000|mode)
modeStr = strconv.FormatInt(int64(0x8000|mode), 10)
}
// 构造 SEND payload
modeStr := strconv.Itoa(syscall.S_IFREG | mode)
sendPayload := []byte(remotePath + "," + modeStr)
if withNUL {
sendPayload = append(sendPayload, 0)
}
// write SEND
if _, err := conn.Write(append([]byte("SEND"), sendPayload...)); err != nil {
// 写入 "SEND" + 长度 + payload
hdr := make([]byte, 8)
copy(hdr[:4], []byte("SEND"))
binary.LittleEndian.PutUint32(hdr[4:], uint32(len(sendPayload)))
if _, err := s.Conn.Write(hdr); err != nil {
return 0, err
}
if _, err := s.Conn.Write(sendPayload); err != nil {
return 0, err
}
if debug {
fmt.Printf("Wrote SEND payload len=%d withNUL=%v modeFmt=%s path=%s\n", len(sendPayload), withNUL, modeFormat, remotePath)
fmt.Printf("[DEBUG] Wrote SEND payload len=%d path=%s mode=%s\n", len(sendPayload), remotePath, modeStr)
}
// write DATA blocks if file has content (if zero-length, skip DATA)
// 写入 DATA
var total int64
buf := make([]byte, maxChunk)
for {
@@ -207,10 +83,10 @@ func syncPushOne(addr, serial, localPath, remotePath string, mode int, withNUL b
hdr := make([]byte, 8)
copy(hdr[:4], []byte("DATA"))
binary.LittleEndian.PutUint32(hdr[4:], uint32(n))
if _, err := conn.Write(hdr); err != nil {
if _, err := s.Conn.Write(hdr); err != nil {
return total, err
}
if _, err := conn.Write(buf[:n]); err != nil {
if _, err := s.Conn.Write(buf[:n]); err != nil {
return total, err
}
total += int64(n)
@@ -223,19 +99,19 @@ func syncPushOne(addr, serial, localPath, remotePath string, mode int, withNUL b
}
}
// send DONE
// 发送 DONE
done := make([]byte, 8)
copy(done[:4], []byte("DONE"))
binary.LittleEndian.PutUint32(done[4:], uint32(time.Now().Unix()))
if _, err := conn.Write(done); err != nil {
if _, err := s.Conn.Write(done); err != nil {
return total, err
}
if debug {
fmt.Println("Wrote DONE, waiting response")
fmt.Println("[DEBUG] Wrote DONE, waiting response")
}
// read final
resp, msg, err := readResponse()
// 读取最终响应
resp, msg, err := ReadResponse(s.Conn, true)
if err != nil {
return total, err
}