Decoupled clients from each other.

This commit is contained in:
Zach Klippenstein 2015-07-11 14:32:04 -07:00
parent 31c075d150
commit 0817d29438
13 changed files with 132 additions and 135 deletions

16
client_config.go Normal file
View file

@ -0,0 +1,16 @@
package goadb
var (
defaultDialer Dialer = NewDialer("", 0)
)
type ClientConfig struct {
Dialer Dialer
}
func (c ClientConfig) sanitized() ClientConfig {
if c.Dialer == nil {
c.Dialer = defaultDialer
}
return c
}

View file

@ -15,13 +15,10 @@ var port = flag.Int("p", adb.AdbPort, "")
func main() { func main() {
flag.Parse() flag.Parse()
client, err := adb.NewHostClientPort(*port) client := adb.NewHostClient(adb.ClientConfig{})
if err != nil {
log.Fatal(err)
}
fmt.Println("Starting server…") fmt.Println("Starting server…")
client.StartServer() adb.StartServer()
serverVersion, err := client.GetServerVersion() serverVersion, err := client.GetServerVersion()
if err != nil { if err != nil {
@ -38,23 +35,24 @@ func main() {
fmt.Printf("\t%+v\n", *device) fmt.Printf("\t%+v\n", *device)
} }
PrintDeviceInfoAndError(client.GetAnyDevice()) PrintDeviceInfoAndError(adb.AnyDevice())
PrintDeviceInfoAndError(client.GetLocalDevice()) PrintDeviceInfoAndError(adb.AnyLocalDevice())
PrintDeviceInfoAndError(client.GetUsbDevice()) PrintDeviceInfoAndError(adb.AnyUsbDevice())
serials, err := client.ListDeviceSerials() serials, err := client.ListDeviceSerials()
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
for _, serial := range serials { for _, serial := range serials {
PrintDeviceInfoAndError(client.GetDeviceWithSerial(serial)) PrintDeviceInfoAndError(adb.DeviceWithSerial(serial))
} }
//fmt.Println("Killing server…") //fmt.Println("Killing server…")
//client.KillServer() //client.KillServer()
} }
func PrintDeviceInfoAndError(device *adb.DeviceClient) { func PrintDeviceInfoAndError(descriptor adb.DeviceDescriptor) {
device := adb.NewDeviceClient(adb.ClientConfig{}, descriptor)
if err := PrintDeviceInfo(device); err != nil { if err := PrintDeviceInfo(device); err != nil {
log.Println(err) log.Println(err)
} }

View file

@ -49,7 +49,7 @@ func readLine() string {
} }
func doCommand(cmd string) error { func doCommand(cmd string) error {
conn, err := wire.NewDialer("", *port).Dial() conn, err := goadb.NewDialer("", *port).Dial()
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View file

@ -10,8 +10,15 @@ import (
// DeviceClient communicates with a specific Android device. // DeviceClient communicates with a specific Android device.
type DeviceClient struct { type DeviceClient struct {
dialer wire.Dialer config ClientConfig
descriptor *DeviceDescriptor descriptor DeviceDescriptor
}
func NewDeviceClient(config ClientConfig, descriptor DeviceDescriptor) *DeviceClient {
return &DeviceClient{
config: config.sanitized(),
descriptor: descriptor,
}
} }
func (c *DeviceClient) String() string { func (c *DeviceClient) String() string {
@ -134,7 +141,7 @@ func (c *DeviceClient) OpenRead(path string) (io.ReadCloser, error) {
// getAttribute returns the first message returned by the server by running // getAttribute returns the first message returned by the server by running
// <host-prefix>:<attr>, where host-prefix is determined from the DeviceDescriptor. // <host-prefix>:<attr>, where host-prefix is determined from the DeviceDescriptor.
func (c *DeviceClient) getAttribute(attr string) (string, error) { func (c *DeviceClient) getAttribute(attr string) (string, error) {
resp, err := wire.RoundTripSingleResponse(c.dialer, resp, err := roundTripSingleResponse(c.config.Dialer,
fmt.Sprintf("%s:%s", c.descriptor.getHostPrefix(), attr)) fmt.Sprintf("%s:%s", c.descriptor.getHostPrefix(), attr))
if err != nil { if err != nil {
return "", err return "", err
@ -145,9 +152,9 @@ func (c *DeviceClient) getAttribute(attr string) (string, error) {
// dialDevice switches the connection to communicate directly with the device // dialDevice switches the connection to communicate directly with the device
// by requesting the transport defined by the DeviceDescriptor. // by requesting the transport defined by the DeviceDescriptor.
func (c *DeviceClient) dialDevice() (*wire.Conn, error) { func (c *DeviceClient) dialDevice() (*wire.Conn, error) {
conn, err := c.dialer.Dial() conn, err := c.config.Dialer.Dial()
if err != nil { if err != nil {
return nil, fmt.Errorf("error dialing adb server (%s): %+v", c.dialer, err) return nil, fmt.Errorf("error dialing adb server (%s): %+v", c.config.Dialer, err)
} }
req := fmt.Sprintf("host:%s", c.descriptor.getTransportDescriptor()) req := fmt.Sprintf("host:%s", c.descriptor.getTransportDescriptor())
@ -197,7 +204,7 @@ func prepareCommandLine(cmd string, args ...string) (string, error) {
} }
} }
// Prepend the comand to the args array. // Prepend the command to the args array.
if len(args) > 0 { if len(args) > 0 {
cmd = fmt.Sprintf("%s %s", cmd, strings.Join(args, " ")) cmd = fmt.Sprintf("%s %s", cmd, strings.Join(args, " "))
} }

View file

@ -12,7 +12,12 @@ func TestGetAttribute(t *testing.T) {
Status: wire.StatusSuccess, Status: wire.StatusSuccess,
Messages: []string{"value"}, Messages: []string{"value"},
} }
client := &DeviceClient{s, deviceWithSerial("serial")} client := NewDeviceClient(
ClientConfig{
Dialer: s,
},
DeviceWithSerial("serial"),
)
v, err := client.getAttribute("attr") v, err := client.getAttribute("attr")
assert.Equal(t, "host-serial:serial:attr", s.Requests[0]) assert.Equal(t, "host-serial:serial:attr", s.Requests[0])
@ -25,7 +30,12 @@ func TestRunCommandNoArgs(t *testing.T) {
Status: wire.StatusSuccess, Status: wire.StatusSuccess,
Messages: []string{"output"}, Messages: []string{"output"},
} }
client := &DeviceClient{s, anyDevice()} client := NewDeviceClient(
ClientConfig{
Dialer: s,
},
AnyDevice(),
)
v, err := client.RunCommand("cmd") v, err := client.RunCommand("cmd")
assert.Equal(t, "host:transport-any", s.Requests[0]) assert.Equal(t, "host:transport-any", s.Requests[0])

View file

@ -23,33 +23,33 @@ type DeviceDescriptor struct {
serial string serial string
} }
func anyDevice() *DeviceDescriptor { func AnyDevice() DeviceDescriptor {
return &DeviceDescriptor{descriptorType: DeviceAny} return DeviceDescriptor{descriptorType: DeviceAny}
} }
func anyUsbDevice() *DeviceDescriptor { func AnyUsbDevice() DeviceDescriptor {
return &DeviceDescriptor{descriptorType: DeviceUsb} return DeviceDescriptor{descriptorType: DeviceUsb}
} }
func anyLocalDevice() *DeviceDescriptor { func AnyLocalDevice() DeviceDescriptor {
return &DeviceDescriptor{descriptorType: DeviceLocal} return DeviceDescriptor{descriptorType: DeviceLocal}
} }
func deviceWithSerial(serial string) *DeviceDescriptor { func DeviceWithSerial(serial string) DeviceDescriptor {
return &DeviceDescriptor{ return DeviceDescriptor{
descriptorType: DeviceSerial, descriptorType: DeviceSerial,
serial: serial, serial: serial,
} }
} }
func (d *DeviceDescriptor) String() string { func (d DeviceDescriptor) String() string {
if d.descriptorType == DeviceSerial { if d.descriptorType == DeviceSerial {
return fmt.Sprintf("%s[%s]", d.descriptorType, d.serial) return fmt.Sprintf("%s[%s]", d.descriptorType, d.serial)
} }
return d.descriptorType.String() return d.descriptorType.String()
} }
func (d *DeviceDescriptor) getHostPrefix() string { func (d DeviceDescriptor) getHostPrefix() string {
switch d.descriptorType { switch d.descriptorType {
case DeviceAny: case DeviceAny:
return "host" return "host"
@ -64,7 +64,7 @@ func (d *DeviceDescriptor) getHostPrefix() string {
} }
} }
func (d *DeviceDescriptor) getTransportDescriptor() string { func (d DeviceDescriptor) getTransportDescriptor() string {
switch d.descriptorType { switch d.descriptorType {
case DeviceAny: case DeviceAny:
return "transport-any" return "transport-any"

View file

@ -1,17 +1,38 @@
package wire package goadb
import ( import (
"errors"
"fmt" "fmt"
"net" "net"
"runtime" "runtime"
"github.com/zach-klippenstein/goadb/wire"
)
const (
// Default port the adb server listens on.
AdbPort = 5037
) )
/* /*
Dialer knows how to create connections to an adb server. Dialer knows how to create connections to an adb server.
*/ */
type Dialer interface { type Dialer interface {
Dial() (*Conn, error) Dial() (*wire.Conn, error)
}
/*
NewDialer creates a new Dialer.
If host is "" or port is 0, "localhost:5037" is used.
*/
func NewDialer(host string, port int) Dialer {
if host == "" {
host = "localhost"
}
if port == 0 {
port = AdbPort
}
return &netDialer{host, port}
} }
type netDialer struct { type netDialer struct {
@ -19,35 +40,24 @@ type netDialer struct {
Port int Port int
} }
func NewDialer(host string, port int) Dialer {
return &netDialer{host, port}
}
func (d *netDialer) String() string { func (d *netDialer) String() string {
return fmt.Sprintf("netDialer(%s:%d)", d.Host, d.Port) return fmt.Sprintf("netDialer(%s:%d)", d.Host, d.Port)
} }
// Dial connects to the adb server on the host and port set on the netDialer. // Dial connects to the adb server on the host and port set on the netDialer.
// The zero-value will connect to the default, localhost:5037. // The zero-value will connect to the default, localhost:5037.
func (d *netDialer) Dial() (*Conn, error) { func (d *netDialer) Dial() (*wire.Conn, error) {
host := d.Host host := d.Host
if host == "" {
return nil, errors.New("Must specify adb hostname (cannot be empty).")
}
port := d.Port port := d.Port
if port == 0 {
return nil, errors.New("Must specify port (cannot be 0).")
}
netConn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", host, port)) netConn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
if err != nil { if err != nil {
return nil, err return nil, err
} }
conn := &Conn{ conn := &wire.Conn{
Scanner: NewScanner(netConn), Scanner: wire.NewScanner(netConn),
Sender: NewSender(netConn), Sender: wire.NewSender(netConn),
} }
// Prevent leaking the network connection, not sure if TCPConn does this itself. // Prevent leaking the network connection, not sure if TCPConn does this itself.
@ -58,7 +68,8 @@ func (d *netDialer) Dial() (*Conn, error) {
return conn, nil return conn, nil
} }
func RoundTripSingleResponse(d Dialer, req string) ([]byte, error) { // TODO(zach): Make this unexported.
func roundTripSingleResponse(d Dialer, req string) ([]byte, error) {
conn, err := d.Dial() conn, err := d.Dial()
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -2,18 +2,11 @@
package goadb package goadb
import ( import (
"errors"
"os/exec"
"strconv" "strconv"
"github.com/zach-klippenstein/goadb/wire" "github.com/zach-klippenstein/goadb/wire"
) )
const (
// Default port the adb server listens on.
AdbPort = 5037
)
/* /*
HostClient communicates with host services on the adb server. HostClient communicates with host services on the adb server.
@ -27,27 +20,31 @@ See list of services at https://android.googlesource.com/platform/system/core/+/
*/ */
// TODO(z): Finish implementing host services. // TODO(z): Finish implementing host services.
type HostClient struct { type HostClient struct {
dialer wire.Dialer config ClientConfig
} }
func NewHostClient() (*HostClient, error) { // func NewHostClient() (*HostClient, error) {
return NewHostClientPort(AdbPort) // return NewHostClientPort(AdbPort)
} // }
func NewHostClientPort(port int) (*HostClient, error) { // func NewHostClientPort(port int) (*HostClient, error) {
return NewHostClientDialer(wire.NewDialer("localhost", port)) // return NewHostClientDialer(wire.NewDialer("localhost", port))
} // }
func NewHostClientDialer(d wire.Dialer) (*HostClient, error) { // func NewHostClientDialer(d wire.Dialer) (*HostClient, error) {
if d == nil { // if d == nil {
return nil, errors.New("dialer cannot be nil.") // return nil, errors.New("dialer cannot be nil.")
} // }
return &HostClient{d}, nil // return &HostClient{d}, nil
// }
func NewHostClient(config ClientConfig) *HostClient {
return &HostClient{config.sanitized()}
} }
// GetServerVersion asks the ADB server for its internal version number. // GetServerVersion asks the ADB server for its internal version number.
func (c *HostClient) GetServerVersion() (int, error) { func (c *HostClient) GetServerVersion() (int, error) {
resp, err := wire.RoundTripSingleResponse(c.dialer, "host:version") resp, err := roundTripSingleResponse(c.config.Dialer, "host:version")
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -63,7 +60,7 @@ Corresponds to the command:
adb kill-server adb kill-server
*/ */
func (c *HostClient) KillServer() error { func (c *HostClient) KillServer() error {
conn, err := c.dialer.Dial() conn, err := c.config.Dialer.Dial()
if err != nil { if err != nil {
return err return err
} }
@ -76,17 +73,6 @@ func (c *HostClient) KillServer() error {
return nil return nil
} }
/*
StartServer ensures there is a server running.
Currently implemented by just running
adb start-server
*/
func (c *HostClient) StartServer() error {
cmd := exec.Command("adb", "start-server")
return cmd.Run()
}
/* /*
ListDeviceSerials returns the serial numbers of all attached devices. ListDeviceSerials returns the serial numbers of all attached devices.
@ -94,7 +80,7 @@ Corresponds to the command:
adb devices adb devices
*/ */
func (c *HostClient) ListDeviceSerials() ([]string, error) { func (c *HostClient) ListDeviceSerials() ([]string, error) {
resp, err := wire.RoundTripSingleResponse(c.dialer, "host:devices") resp, err := roundTripSingleResponse(c.config.Dialer, "host:devices")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -118,41 +104,10 @@ Corresponds to the command:
adb devices -l adb devices -l
*/ */
func (c *HostClient) ListDevices() ([]*DeviceInfo, error) { func (c *HostClient) ListDevices() ([]*DeviceInfo, error) {
resp, err := wire.RoundTripSingleResponse(c.dialer, "host:devices-l") resp, err := roundTripSingleResponse(c.config.Dialer, "host:devices-l")
if err != nil { if err != nil {
return nil, err return nil, err
} }
return parseDeviceList(string(resp), parseDeviceLong) return parseDeviceList(string(resp), parseDeviceLong)
} }
func (c *HostClient) GetDevice(d *DeviceInfo) *DeviceClient {
return c.GetDeviceWithSerial(d.Serial)
}
// GetDeviceWithSerial returns a client for the device with the specified serial number.
// Will return a client even if there is no matching device connected.
func (c *HostClient) GetDeviceWithSerial(serial string) *DeviceClient {
return c.getDevice(deviceWithSerial(serial))
}
// GetAnyDevice returns a client for any one connected device.
func (c *HostClient) GetAnyDevice() *DeviceClient {
return c.getDevice(anyDevice())
}
// GetUsbDevice returns a client for the USB device.
// Will return a client even if there is no device connected.
func (c *HostClient) GetUsbDevice() *DeviceClient {
return c.getDevice(anyUsbDevice())
}
// GetLocalDevice returns a client for the local device.
// Will return a client even if there is no device connected.
func (c *HostClient) GetLocalDevice() *DeviceClient {
return c.getDevice(anyLocalDevice())
}
func (c *HostClient) getDevice(descriptor *DeviceDescriptor) *DeviceClient {
return &DeviceClient{c.dialer, descriptor}
}

View file

@ -14,8 +14,9 @@ func TestGetServerVersion(t *testing.T) {
Status: wire.StatusSuccess, Status: wire.StatusSuccess,
Messages: []string{"000a"}, Messages: []string{"000a"},
} }
client, err := NewHostClientDialer(s) client := NewHostClient(ClientConfig{
assert.NoError(t, err) Dialer: s,
})
v, err := client.GetServerVersion() v, err := client.GetServerVersion()
assert.Equal(t, "host:version", s.Requests[0]) assert.Equal(t, "host:version", s.Requests[0])

View file

@ -1,15 +0,0 @@
package goadb
import "github.com/zach-klippenstein/goadb/wire"
type nilSafeDialer struct {
wire.Dialer
}
func (d nilSafeDialer) Dial() (*wire.Conn, error) {
if d.Dialer == nil {
d.Dialer = wire.NewDialer("", AdbPort)
}
return d.Dialer.Dial()
}

14
server_controller.go Normal file
View file

@ -0,0 +1,14 @@
package goadb
import "os/exec"
/*
StartServer ensures there is a server running.
Currently implemented by just running
adb start-server
*/
func StartServer() error {
cmd := exec.Command("adb", "start-server")
return cmd.Run()
}

View file

@ -4,14 +4,14 @@ import (
"fmt" "fmt"
) )
type AdbError struct { type AdbServerError struct {
Request string Request string
ServerMsg string ServerMsg string
} }
var _ error = &AdbError{} var _ error = &AdbServerError{}
func (e *AdbError) Error() string { func (e *AdbServerError) Error() string {
if e.Request == "" { if e.Request == "" {
return fmt.Sprintf("server error: %s", e.ServerMsg) return fmt.Sprintf("server error: %s", e.ServerMsg)
} else { } else {

View file

@ -20,7 +20,7 @@ func ReadStatusFailureAsError(s Scanner, req string) error {
return fmt.Errorf("server returned error for %s, but couldn't read the error message: %+v", err) return fmt.Errorf("server returned error for %s, but couldn't read the error message: %+v", err)
} }
return &AdbError{ return &AdbServerError{
Request: req, Request: req,
ServerMsg: string(msg), ServerMsg: string(msg),
} }