This commit is contained in:
2022-07-31 02:50:14 -07:00
parent e5068d7b3f
commit ca83d818fe
4 changed files with 143 additions and 6 deletions

96
adb.go
View File

@@ -2,8 +2,11 @@ package adb
import ( import (
"context" "context"
"errors"
"net" "net"
"os" "os"
"strconv"
"strings"
) )
type Serial string type Serial string
@@ -20,15 +23,18 @@ const (
// Device contains the information necessary to connect to and // Device contains the information necessary to connect to and
// communicate with a device // communicate with a device
type Device struct { type Device struct {
IsAuthorized bool
SerialNo Serial SerialNo Serial
ConnType Connection ConnType Connection
IP net.IPAddr IP net.IPAddr
Port uint
FileHandle string // TODO change this to a discrete type FileHandle string // TODO change this to a discrete type
} }
// Provides a connection string for Connect() // Provides a connection string for Connect()
type ConnOptions struct { type ConnOptions struct {
Address net.IPAddr Address net.IPAddr
Port uint
SerialNo Serial SerialNo Serial
} }
@@ -37,14 +43,37 @@ type ConnOptions struct {
// This will return a Device struct, which can be used to call other methods. // This will return a Device struct, which can be used to call other methods.
// If the connection fails or cannot complete on time, Connect will return an error. // If the connection fails or cannot complete on time, Connect will return an error.
func Connect(ctx context.Context, opts ConnOptions) (Device, error) { func Connect(ctx context.Context, opts ConnOptions) (Device, error) {
if opts.Port == 0 {
opts.Port = 5555
}
return Device{}, nil return Device{}, nil
} }
func (d Device) ConnString() string {
if d.Port == 0 {
d.Port = 5555
}
return d.IP.String() + ":" + strconv.Itoa(int(d.Port))
}
// Connect to a previously discovered device. // Connect to a previously discovered device.
// //
// This function is helpful when connecting to a device found from the Devices call // This function is helpful when connecting to a device found from the Devices call
// or when reconnecting to a previously connected device. // or when reconnecting to a previously connected device.
func (d Device) Connect(ctx context.Context) (Device, error) { func (d Device) Reconnect(ctx context.Context) (Device, error) {
if d.ConnType == USB {
return d, ErrConnUSB
}
cmd := []string{"connect", d.ConnString()}
stdout, stderr, errcode, err := execute(ctx, cmd)
if err != nil {
return d, err
}
if errcode != 0 {
return d, ErrUnspecified
}
_, _ = stdout, stderr
// TODO capture and store serial number into d before returning
return d, nil return d, nil
} }
@@ -54,7 +83,35 @@ func (d Device) Connect(ctx context.Context) (Device, error) {
// It is recommended to call IsConnected() against the device you're interested in using and connect // It is recommended to call IsConnected() against the device you're interested in using and connect
// if not already connected before proceeding. // if not already connected before proceeding.
func Devices(ctx context.Context) ([]Device, error) { func Devices(ctx context.Context) ([]Device, error) {
return []Device{}, nil cmd := []string{"devices"}
stdout, _, errcode, err := execute(ctx, cmd)
devs := []Device{}
if err != nil {
return devs, err
}
if errcode != 0 {
return devs, ErrUnspecified
}
return parseDevices(stdout)
}
func parseDevices(stdout string) ([]Device, error) {
devs := []Device{}
lines := strings.Split(stdout, "\n")
for _, line := range lines {
words := strings.Fields(line)
if len(words) != 2 {
continue
}
d := Device{
SerialNo: Serial(words[0]),
IsAuthorized: words[1] == "device",
}
devs = append(devs, d)
}
return devs, nil
} }
// Disconnect from a device. // Disconnect from a device.
@@ -88,6 +145,10 @@ func (d Device) Push(ctx context.Context, src, dest string) error {
if err != nil { if err != nil {
return err return err
} }
if errcode != 0 {
return ErrUnspecified
}
_, _ = stdout, stderr
// TODO check the return strings of the output to determine if the file copy succeeded // TODO check the return strings of the output to determine if the file copy succeeded
return nil return nil
} }
@@ -96,6 +157,19 @@ func (d Device) Push(ctx context.Context, src, dest string) error {
// //
// Returns an error if src does not exist, or if dest already exists or cannot be created // Returns an error if src does not exist, or if dest already exists or cannot be created
func (d Device) Pull(ctx context.Context, src, dest string) error { func (d Device) Pull(ctx context.Context, src, dest string) error {
_, err := os.Stat(src)
if !errors.Is(err, os.ErrNotExist) {
return err
}
stdout, stderr, errcode, err := execute(ctx, []string{"pull", src, dest})
if err != nil {
return err
}
if errcode != 0 {
return ErrUnspecified
}
_, _ = stdout, stderr
// TODO check the return strings of the output to determine if the file copy succeeded
return nil return nil
} }
@@ -104,6 +178,15 @@ func (d Device) Pull(ctx context.Context, src, dest string) error {
// Once the device reboots, you must manually reconnect. // Once the device reboots, you must manually reconnect.
// Returns an error if the device cannot be contacted // Returns an error if the device cannot be contacted
func (d Device) Reboot(ctx context.Context) error { func (d Device) Reboot(ctx context.Context) error {
stdout, stderr, errcode, err := execute(ctx, []string{"reboot"})
if err != nil {
return err
}
if errcode != 0 {
return ErrUnspecified
}
_, _ = stdout, stderr
// TODO check the return strings of the output to determine if the file copy succeeded
return nil return nil
} }
@@ -115,5 +198,14 @@ func (d Device) Reboot(ctx context.Context) error {
// Once adb is relaunched as root, it will stay root until rebooted. // Once adb is relaunched as root, it will stay root until rebooted.
// returns true if the device successfully relaunched as root // returns true if the device successfully relaunched as root
func (d Device) Root(ctx context.Context) (success bool, err error) { func (d Device) Root(ctx context.Context) (success bool, err error) {
stdout, stderr, errcode, err := execute(ctx, []string{"root"})
if err != nil {
return false, err
}
if errcode != 0 {
return false, ErrUnspecified
}
_, _ = stdout, stderr
// TODO check the return strings of the output to determine if the file copy succeeded
return true, nil return true, nil
} }

43
adb_test.go Normal file
View File

@@ -0,0 +1,43 @@
package adb
import (
"reflect"
"testing"
)
func Test_parseDevices(t *testing.T) {
type args struct {
stdout string
}
tests := []struct {
name string
args args
want []Device
wantErr bool
}{
{
name: "2 auth 1 unauth", args: args{stdout: `List of devices attached
19291FDEE0023W device
9B061FFBA00BC9 device
HT75R0202681 unauthorized`},
wantErr: false,
want: []Device{
{IsAuthorized: true, SerialNo: "19291FDEE0023W"},
{IsAuthorized: true, SerialNo: "9B061FFBA00BC9"},
{IsAuthorized: false, SerialNo: "HT75R0202681"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseDevices(tt.args.stdout)
if (err != nil) != tt.wantErr {
t.Errorf("parseDevices() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("parseDevices() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -10,5 +10,6 @@ var (
ErrStdoutEmpty = errors.New("stdout expected to contain data but was empty") ErrStdoutEmpty = errors.New("stdout expected to contain data but was empty")
ErrNotInstalled = errors.New("adb is not installed or not in PATH") ErrNotInstalled = errors.New("adb is not installed or not in PATH")
ErrCoordinatesNotFound = errors.New("coordinates for an input event are missing") ErrCoordinatesNotFound = errors.New("coordinates for an input event are missing")
ErrConnUSB = errors.New("cannot call connect to device using USB")
ErrUnspecified = errors.New("an unknown error has occurred, please open an issue on GitHub") ErrUnspecified = errors.New("an unknown error has occurred, please open an issue on GitHub")
) )

View File

@@ -12,6 +12,7 @@ import (
// Instead of using Shell, please consider submitting a PR with the functionality // Instead of using Shell, please consider submitting a PR with the functionality
// you require. // you require.
func (d Device) Shell(ctx context.Context, command string) (stdout string, stderr string, ErrCode int, err error) { func (d Device) Shell(ctx context.Context, command string) (stdout string, stderr string, ErrCode int, err error) {
return "", "", 1, nil return "", "", 1, nil
} }