diff --git a/adb/adb.go b/adb/adb.go index acd5676..be02f06 100644 --- a/adb/adb.go +++ b/adb/adb.go @@ -11,17 +11,19 @@ import ( "os/exec" "strings" + saveSlice "github.com/botherder/go-savetime/slice" "github.com/mvt-project/androidqf/log" ) type ADB struct { ExePath string + Serial string } var Client *ADB // New returns a new ADB instance. -func New() (*ADB, error) { +func New(serial string) (*ADB, error) { adb := ADB{} err := adb.findExe() if err != nil { @@ -32,15 +34,75 @@ func New() (*ADB, error) { log.Debug("Killing existing ADB server if running") adb.KillServer() + + // Managing devices + devices, err := adb.Devices() + if err != nil { + return nil, err + } + + serial = strings.TrimSpace(serial) + if len(devices) == 0 { + return nil, fmt.Errorf("no devices connected to adb") + } + if serial != "" { + // Check that the serial match one of the devices + // Can be replace with the go package slices in 1.21 + if !saveSlice.ContainsNoCase(devices, serial) { + // Serial is not an existing device + return nil, fmt.Errorf("serial %s not found in the device list", serial) + } + adb.Serial = serial + } else { + // Problem if multiple devices + if len(devices) > 1 { + return nil, fmt.Errorf("multiple devices connected, please provide a serial number") + } + adb.Serial = "" + } + return &adb, nil } +// List existing devices +func (a *ADB) Devices() ([]string, error) { + var devices []string + out, err := exec.Command(a.ExePath, "devices").Output() + if err != nil { + return devices, fmt.Errorf("failed to use the adb executable: %v", + err) + } + + lines := strings.Split(string(out), "\n") + for _, s := range lines[1:] { + dev := strings.Split(s, "\t") + if len(dev) == 2 { + devices = append(devices, strings.TrimSpace(dev[0])) + } + } + + return devices, nil +} + +// Run a command to the given phone using exec +// Returns string and/or error +func (a *ADB) Exec(args ...string) ([]byte, error) { + if a.Serial == "" { + return exec.Command(a.ExePath, args...).Output() + } else { + var params []string + params = append(params, "-s", a.Serial) + params = append(params, args...) + return exec.Command(a.ExePath, params...).Output() + } +} + // GetState returns the output of `adb get-state`. // It is used to check whether a device is connected. If it is not, adb // will exit with status 1. func (a *ADB) GetState() (string, error) { log.Debug("Starting get-state") - out, err := exec.Command(a.ExePath, "get-state").Output() + out, err := a.Exec("get-state") if err != nil { log.Debug("get-state failed") return "", err @@ -53,7 +115,7 @@ func (a *ADB) GetState() (string, error) { // Shell executes a shell command through adb. func (a *ADB) Shell(cmd ...string) (string, error) { fullCmd := append([]string{"shell"}, cmd...) - out, err := exec.Command(a.ExePath, fullCmd...).Output() + out, err := a.Exec(fullCmd...) if err != nil { if out == nil { return "", err @@ -67,7 +129,7 @@ func (a *ADB) Shell(cmd ...string) (string, error) { // Pull downloads a file from the device to a local path. func (a *ADB) Pull(remotePath, localPath string) (string, error) { - out, err := exec.Command(a.ExePath, "pull", remotePath, localPath).Output() + out, err := a.Exec("pull", remotePath, localPath) if err != nil { return string(out), err } @@ -77,7 +139,7 @@ func (a *ADB) Pull(remotePath, localPath string) (string, error) { // Push a file on the phone func (a *ADB) Push(localPath, remotePath string) (string, error) { - out, err := exec.Command(a.ExePath, "push", localPath, remotePath).Output() + out, err := a.Exec("push", localPath, remotePath) if err != nil { return string(out), err } diff --git a/main.go b/main.go index 54fa107..b11891e 100644 --- a/main.go +++ b/main.go @@ -44,7 +44,8 @@ func main() { var list_modules bool var fast bool var module string - var output_folder string + var output_folder string + var serial string // Command line options flag.BoolVar(&verbose, "verbose", false, "Verbose mode") @@ -55,8 +56,10 @@ func main() { flag.BoolVar(&list_modules, "l", false, "List modules and exit") flag.StringVar(&module, "module", "", "Only execute a specific module") flag.StringVar(&module, "m", "", "Only execute a specific module") - flag.StringVar(&output_folder, "output", "", "Output folder") - flag.StringVar(&output_folder, "o", "", "Output folder") + flag.StringVar(&output_folder, "output", "", "Output folder") + flag.StringVar(&output_folder, "o", "", "Output folder") + flag.StringVar(&serial, "serial", "", "Phone serial number") + flag.StringVar(&serial, "s", "", "Phone serial number") flag.BoolVar(&version_flag, "version", false, "Show version") flag.Parse() @@ -79,9 +82,9 @@ func main() { } log.Debug("Starting androidqf") - adb.Client, err = adb.New() + adb.Client, err = adb.New(serial) if err != nil { - log.Fatal("Impossible to initialize adb") + log.Fatal("Impossible to initialize adb: ", err) } // Initialization