From ca83d818fea416dc7a23a99d3a06fb3ee6279155 Mon Sep 17 00:00:00 2001 From: Tai Groot Date: Sun, 31 Jul 2022 02:50:14 -0700 Subject: [PATCH] Adding code from twitch stream https://www.twitch.tv/videos/1548041732 --- adb.go | 104 +++++++++++++++++++++++++++++++++++++++++++++++++--- adb_test.go | 43 ++++++++++++++++++++++ errors.go | 1 + shell.go | 1 + 4 files changed, 143 insertions(+), 6 deletions(-) create mode 100644 adb_test.go diff --git a/adb.go b/adb.go index 923678b..8c117b4 100644 --- a/adb.go +++ b/adb.go @@ -2,8 +2,11 @@ package adb import ( "context" + "errors" "net" "os" + "strconv" + "strings" ) type Serial string @@ -20,15 +23,18 @@ const ( // Device contains the information necessary to connect to and // communicate with a device type Device struct { - SerialNo Serial - ConnType Connection - IP net.IPAddr - FileHandle string // TODO change this to a discrete type + IsAuthorized bool + SerialNo Serial + ConnType Connection + IP net.IPAddr + Port uint + FileHandle string // TODO change this to a discrete type } // Provides a connection string for Connect() type ConnOptions struct { Address net.IPAddr + Port uint SerialNo Serial } @@ -37,14 +43,37 @@ type ConnOptions struct { // 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. func Connect(ctx context.Context, opts ConnOptions) (Device, error) { + if opts.Port == 0 { + opts.Port = 5555 + } 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. // // This function is helpful when connecting to a device found from the Devices call // 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 } @@ -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 // if not already connected before proceeding. 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. @@ -88,6 +145,10 @@ func (d Device) Push(ctx context.Context, src, dest string) error { 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 } @@ -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 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 } @@ -104,6 +178,15 @@ func (d Device) Pull(ctx context.Context, src, dest string) error { // Once the device reboots, you must manually reconnect. // Returns an error if the device cannot be contacted 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 } @@ -115,5 +198,14 @@ func (d Device) Reboot(ctx context.Context) error { // Once adb is relaunched as root, it will stay root until rebooted. // returns true if the device successfully relaunched as root 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 } diff --git a/adb_test.go b/adb_test.go new file mode 100644 index 0000000..cfdca2b --- /dev/null +++ b/adb_test.go @@ -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) + } + }) + } +} diff --git a/errors.go b/errors.go index 977d163..5c26ec2 100644 --- a/errors.go +++ b/errors.go @@ -10,5 +10,6 @@ var ( ErrStdoutEmpty = errors.New("stdout expected to contain data but was empty") ErrNotInstalled = errors.New("adb is not installed or not in PATH") 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") ) diff --git a/shell.go b/shell.go index 0068321..265388b 100644 --- a/shell.go +++ b/shell.go @@ -12,6 +12,7 @@ import ( // Instead of using Shell, please consider submitting a PR with the functionality // you require. func (d Device) Shell(ctx context.Context, command string) (stdout string, stderr string, ErrCode int, err error) { + return "", "", 1, nil }