2020-11-10 20:35:36 +00:00
|
|
|
package main
|
2020-01-24 23:02:09 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2020-08-18 10:53:24 +00:00
|
|
|
"io/ioutil"
|
2020-01-24 23:02:09 +00:00
|
|
|
"log"
|
2020-04-09 02:54:56 +00:00
|
|
|
"os"
|
2020-08-18 10:53:24 +00:00
|
|
|
"path"
|
2020-04-09 02:54:56 +00:00
|
|
|
"sort"
|
|
|
|
"strings"
|
2020-01-24 23:02:09 +00:00
|
|
|
"sync"
|
2020-09-08 22:54:10 +00:00
|
|
|
"time"
|
2020-01-24 23:02:09 +00:00
|
|
|
|
|
|
|
adb "github.com/zach-klippenstein/goadb"
|
|
|
|
)
|
|
|
|
|
|
|
|
// DefaultPort is the default port the ADB server listens on.
|
|
|
|
const DefaultPort = 5037
|
|
|
|
|
2020-11-10 20:35:36 +00:00
|
|
|
// adbConn represents a connection with an ADB server.
|
|
|
|
type adbConn struct {
|
2020-01-24 23:02:09 +00:00
|
|
|
bridge *adb.Adb
|
|
|
|
device *adb.Device
|
2020-08-18 10:53:24 +00:00
|
|
|
dir string
|
2020-01-24 23:02:09 +00:00
|
|
|
|
2020-08-18 10:53:24 +00:00
|
|
|
sync.Mutex
|
2020-01-24 23:02:09 +00:00
|
|
|
}
|
|
|
|
|
2020-12-09 01:52:44 +00:00
|
|
|
// connectBridge establishes a connection with an ADB server. When host and
|
|
|
|
// port are unspecified they are replaced with localhost and the default port.
|
|
|
|
func connectBridge(host string, port int, pathToADB string) (bridge *adbConn, err error) {
|
2020-10-03 00:30:24 +00:00
|
|
|
adbBridge, err := adb.NewWithConfig(adb.ServerConfig{
|
2020-01-24 23:02:09 +00:00
|
|
|
PathToAdb: pathToADB,
|
|
|
|
Host: host,
|
|
|
|
Port: port,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2020-10-03 00:30:24 +00:00
|
|
|
adbBridge.StartServer() // Ignore error
|
2020-01-24 23:02:09 +00:00
|
|
|
|
2020-10-03 00:30:24 +00:00
|
|
|
_, err = adbBridge.ServerVersion()
|
2020-01-24 23:02:09 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to get server version: %s", err)
|
|
|
|
}
|
|
|
|
|
2020-11-10 20:35:36 +00:00
|
|
|
return &adbConn{bridge: adbBridge}, nil
|
2020-01-24 23:02:09 +00:00
|
|
|
}
|
|
|
|
|
2020-12-09 01:52:44 +00:00
|
|
|
func (a *adbConn) connectDevice(host string, port int) (err error) {
|
|
|
|
return a.bridge.Connect(host, port)
|
|
|
|
}
|
|
|
|
|
2020-11-10 20:35:36 +00:00
|
|
|
func (a *adbConn) listDevices() (devices []*adb.DeviceInfo, err error) {
|
2020-01-24 23:02:09 +00:00
|
|
|
a.Lock()
|
|
|
|
defer a.Unlock()
|
|
|
|
|
|
|
|
deviceInfoList, err := a.bridge.ListDevices()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, deviceInfo := range deviceInfoList {
|
|
|
|
devices = append(devices, deviceInfo)
|
|
|
|
}
|
|
|
|
|
|
|
|
return devices, nil
|
|
|
|
}
|
|
|
|
|
2020-11-10 20:35:36 +00:00
|
|
|
func (a *adbConn) setDevice(deviceInfo *adb.DeviceInfo) (err error) {
|
2020-01-24 23:02:09 +00:00
|
|
|
a.Lock()
|
|
|
|
defer a.Unlock()
|
|
|
|
|
|
|
|
device := a.bridge.Device(adb.DeviceWithSerial(deviceInfo.Serial))
|
|
|
|
if device == nil {
|
|
|
|
return fmt.Errorf("failed to get device with serial %s", deviceInfo.Serial)
|
|
|
|
}
|
|
|
|
|
|
|
|
a.device = device
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-11-10 20:35:36 +00:00
|
|
|
func (a *adbConn) upload(fileName string, data []byte, mtime time.Time) (err error) {
|
2020-08-18 10:53:24 +00:00
|
|
|
a.Lock()
|
|
|
|
defer a.Unlock()
|
|
|
|
|
2020-09-08 22:54:10 +00:00
|
|
|
writer, err := a.device.OpenWrite(path.Join(a.dir, fileName), 0655, mtime)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to open remote file: %s", err)
|
|
|
|
}
|
|
|
|
defer writer.Close()
|
|
|
|
|
|
|
|
n, err := writer.Write(data)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
} else if n != len(data) {
|
|
|
|
return errors.New("failed to write file")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-11-10 20:35:36 +00:00
|
|
|
func (a *adbConn) download(fileName string) (data []byte, err error) {
|
2020-09-08 22:54:10 +00:00
|
|
|
a.Lock()
|
|
|
|
defer a.Unlock()
|
|
|
|
|
|
|
|
reader, err := a.device.OpenRead(path.Join(a.dir, fileName))
|
2020-08-18 10:53:24 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-09-08 22:54:10 +00:00
|
|
|
defer reader.Close()
|
2020-08-18 10:53:24 +00:00
|
|
|
|
2020-10-03 00:30:24 +00:00
|
|
|
data, err = ioutil.ReadAll(reader)
|
2020-08-18 10:53:24 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
2020-11-10 20:35:36 +00:00
|
|
|
func (a *adbConn) directoryEntries(dir string) (entries []*adb.DirEntry, err error) {
|
2020-01-24 23:02:09 +00:00
|
|
|
a.Lock()
|
|
|
|
defer a.Unlock()
|
|
|
|
|
|
|
|
if a.device == nil {
|
|
|
|
return nil, errors.New("no device")
|
|
|
|
}
|
|
|
|
|
2020-08-18 10:53:24 +00:00
|
|
|
a.dir = dir
|
|
|
|
|
|
|
|
listEntries, err := a.device.ListDirEntries(dir)
|
2020-01-24 23:02:09 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to get dirents: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for listEntries.Next() {
|
2020-04-09 02:54:56 +00:00
|
|
|
entry := listEntries.Entry()
|
|
|
|
if entry.Name == "." {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
entries = append(entries, entry)
|
2020-01-24 23:02:09 +00:00
|
|
|
}
|
|
|
|
if listEntries.Err() != nil {
|
|
|
|
return nil, fmt.Errorf("failed to list files: %s", err)
|
|
|
|
}
|
|
|
|
|
2020-04-09 02:54:56 +00:00
|
|
|
sort.Slice(entries, func(i, j int) bool {
|
|
|
|
if entries[i].Name == ".." {
|
|
|
|
return true
|
|
|
|
} else if (entries[i].Mode&os.ModeDir != 0) != (entries[j].Mode&os.ModeDir != 0) {
|
|
|
|
return entries[i].Mode&os.ModeDir != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.ToLower(entries[i].Name) < strings.ToLower(entries[j].Name)
|
|
|
|
})
|
|
|
|
|
2020-01-24 23:02:09 +00:00
|
|
|
return entries, nil
|
|
|
|
}
|
2020-10-03 00:30:24 +00:00
|
|
|
|
2020-11-12 18:57:44 +00:00
|
|
|
func (a *adbConn) copy(old string, new string) (err error) {
|
|
|
|
a.Lock()
|
|
|
|
defer a.Unlock()
|
|
|
|
|
|
|
|
if a.device == nil {
|
|
|
|
return errors.New("no device")
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = a.device.RunCommand("cp", "-R", old, new)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to copy file: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-11-10 20:35:36 +00:00
|
|
|
func (a *adbConn) move(old string, new string) (err error) {
|
2020-10-03 00:30:24 +00:00
|
|
|
a.Lock()
|
|
|
|
defer a.Unlock()
|
|
|
|
|
|
|
|
if a.device == nil {
|
|
|
|
return errors.New("no device")
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = a.device.RunCommand("mv", old, new)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to move file: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2020-11-10 20:35:36 +00:00
|
|
|
|
2020-11-10 21:04:45 +00:00
|
|
|
func (a *adbConn) delete(path string) (err error) {
|
|
|
|
a.Lock()
|
|
|
|
defer a.Unlock()
|
|
|
|
|
|
|
|
if a.device == nil {
|
|
|
|
return errors.New("no device")
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = a.device.RunCommand("rm", "-rf", path)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to delete file or directory: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-11-10 20:35:36 +00:00
|
|
|
func (a *adbConn) newDirectory(path string) (err error) {
|
|
|
|
a.Lock()
|
|
|
|
defer a.Unlock()
|
|
|
|
|
|
|
|
if a.device == nil {
|
|
|
|
return errors.New("no device")
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = a.device.RunCommand("mkdir", path)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to create directory: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|