155 lines
3.3 KiB
Go
155 lines
3.3 KiB
Go
package adb
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"os"
|
|
"strconv"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
const maxChunk = 64 * 1024
|
|
|
|
type Sync struct {
|
|
Conn net.Conn
|
|
}
|
|
|
|
func InitSync(conn net.Conn) *Sync {
|
|
return &Sync{
|
|
Conn: conn,
|
|
}
|
|
}
|
|
|
|
// SyncPushFile 将本地文件推送到设备
|
|
// localPath: 本地文件路径
|
|
// 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
|
|
}
|
|
defer f.Close()
|
|
fi, _ := f.Stat()
|
|
if debug {
|
|
fmt.Printf("[DEBUG] local filesize=%d\n", fi.Size())
|
|
}
|
|
s.StartSync()
|
|
// 构造 SEND payload
|
|
modeStr := strconv.Itoa(syscall.S_IFREG | mode)
|
|
sendPayload := []byte(remotePath + "," + modeStr)
|
|
|
|
// 写入 "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("[DEBUG] Wrote SEND payload len=%d path=%s mode=%s\n", len(sendPayload), remotePath, modeStr)
|
|
}
|
|
|
|
// 写入 DATA 块
|
|
var total int64
|
|
buf := make([]byte, maxChunk)
|
|
for {
|
|
n, rerr := f.Read(buf)
|
|
if n > 0 {
|
|
hdr := make([]byte, 8)
|
|
copy(hdr[:4], []byte("DATA"))
|
|
binary.LittleEndian.PutUint32(hdr[4:], uint32(n))
|
|
if _, err := s.Conn.Write(hdr); err != nil {
|
|
return total, err
|
|
}
|
|
if _, err := s.Conn.Write(buf[:n]); err != nil {
|
|
return total, err
|
|
}
|
|
total += int64(n)
|
|
}
|
|
if rerr != nil {
|
|
if rerr == io.EOF {
|
|
break
|
|
}
|
|
return total, rerr
|
|
}
|
|
}
|
|
|
|
// 发送 DONE
|
|
done := make([]byte, 8)
|
|
copy(done[:4], []byte("DONE"))
|
|
mtime := uint32(fi.ModTime().Unix())
|
|
binary.LittleEndian.PutUint32(done[4:], mtime)
|
|
if _, err := s.Conn.Write(done); err != nil {
|
|
return total, err
|
|
}
|
|
if debug {
|
|
fmt.Println("[DEBUG] Wrote DONE, waiting response")
|
|
}
|
|
|
|
// 读取最终响应
|
|
resp, msg, err := ReadSyncStatus(s.Conn)
|
|
if err != nil {
|
|
return total, err
|
|
}
|
|
if resp != "OKAY" {
|
|
if len(msg) > 0 {
|
|
return total, fmt.Errorf("sync failed: %s", string(msg))
|
|
}
|
|
return total, fmt.Errorf("sync failed: %s", resp)
|
|
}
|
|
return total, nil
|
|
}
|
|
|
|
func (s *Sync) StartSync() error {
|
|
if err := WriteAdbCmd(s.Conn, "sync:"); err != nil {
|
|
return err
|
|
}
|
|
tok, err := readN(s.Conn, 4, 10*time.Second)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if string(tok) != "OKAY" {
|
|
_, msg, _ := ReadSyncStatus(s.Conn)
|
|
if len(msg) > 0 {
|
|
return fmt.Errorf("sync open failed: %s", string(msg))
|
|
}
|
|
return fmt.Errorf("sync open failed: %q", string(tok))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ReadSyncStatus(r io.Reader) (string, string, error) {
|
|
hdr := make([]byte, 4)
|
|
if _, err := io.ReadFull(r, hdr); err != nil {
|
|
return "", "", err
|
|
}
|
|
status := string(hdr)
|
|
switch status {
|
|
case "OKAY":
|
|
return "OKAY", "", nil
|
|
case "FAIL":
|
|
lenBuf := make([]byte, 4)
|
|
if _, err := io.ReadFull(r, lenBuf); err != nil {
|
|
return "FAIL", "", err
|
|
}
|
|
l := binary.LittleEndian.Uint32(lenBuf)
|
|
msg := make([]byte, l)
|
|
if _, err := io.ReadFull(r, msg); err != nil {
|
|
return "FAIL", "", err
|
|
}
|
|
return "FAIL", string(msg), nil
|
|
default:
|
|
// 非预期状态,直接返回原始字符串,便于上层报错
|
|
return status, "", nil
|
|
}
|
|
}
|