diff --git a/device_client.go b/device_client.go
index e54eb15..cd3d3c7 100644
--- a/device_client.go
+++ b/device_client.go
@@ -13,12 +13,16 @@ import (
 type DeviceClient struct {
 	config     ClientConfig
 	descriptor DeviceDescriptor
+
+	// Used to get device info.
+	deviceListFunc func() ([]*DeviceInfo, error)
 }
 
 func NewDeviceClient(config ClientConfig, descriptor DeviceDescriptor) *DeviceClient {
 	return &DeviceClient{
-		config:     config.sanitized(),
-		descriptor: descriptor,
+		config:         config.sanitized(),
+		descriptor:     descriptor,
+		deviceListFunc: NewHostClient(config).ListDevices,
 	}
 }
 
@@ -48,6 +52,30 @@ func (c *DeviceClient) GetState() (string, error) {
 	return attr, wrapClientError(err, c, "GetState")
 }
 
+func (c *DeviceClient) GetDeviceInfo() (*DeviceInfo, error) {
+	// Adb doesn't actually provide a way to get this for an individual device,
+	// so we have to just list devices and find ourselves.
+
+	serial, err := c.GetSerial()
+	if err != nil {
+		return nil, wrapClientError(err, c, "GetDeviceInfo(GetSerial)")
+	}
+
+	devices, err := c.deviceListFunc()
+	if err != nil {
+		return nil, wrapClientError(err, c, "GetDeviceInfo(ListDevices)")
+	}
+
+	for _, deviceInfo := range devices {
+		if deviceInfo.Serial == serial {
+			return deviceInfo, nil
+		}
+	}
+
+	err = util.Errorf(util.DeviceNotFound, "device list doesn't contain serial %s", serial)
+	return nil, wrapClientError(err, c, "GetDeviceInfo")
+}
+
 /*
 RunCommand runs the specified commands on a shell on the device.
 
diff --git a/device_client_test.go b/device_client_test.go
index fa55e16..3c43eec 100644
--- a/device_client_test.go
+++ b/device_client_test.go
@@ -26,6 +26,52 @@ func TestGetAttribute(t *testing.T) {
 	assert.Equal(t, "value", v)
 }
 
+func TestGetDeviceInfo(t *testing.T) {
+	deviceLister := func() ([]*DeviceInfo, error) {
+		return []*DeviceInfo{
+			&DeviceInfo{
+				Serial:  "abc",
+				Product: "Foo",
+			},
+			&DeviceInfo{
+				Serial:  "def",
+				Product: "Bar",
+			},
+		}, nil
+	}
+
+	client := newDeviceClientWithDeviceLister("abc", deviceLister)
+	device, err := client.GetDeviceInfo()
+	assert.NoError(t, err)
+	assert.Equal(t, "Foo", device.Product)
+
+	client = newDeviceClientWithDeviceLister("def", deviceLister)
+	device, err = client.GetDeviceInfo()
+	assert.NoError(t, err)
+	assert.Equal(t, "Bar", device.Product)
+
+	client = newDeviceClientWithDeviceLister("serial", deviceLister)
+	device, err = client.GetDeviceInfo()
+	assert.True(t, util.HasErrCode(err, util.DeviceNotFound))
+	assert.EqualError(t, err.(*util.Err).Cause,
+		"DeviceNotFound: device list doesn't contain serial serial")
+	assert.Nil(t, device)
+}
+
+func newDeviceClientWithDeviceLister(serial string, deviceLister func() ([]*DeviceInfo, error)) *DeviceClient {
+	client := NewDeviceClient(
+		ClientConfig{
+			Dialer: &MockServer{
+				Status:   wire.StatusSuccess,
+				Messages: []string{serial},
+			},
+		},
+		DeviceWithSerial(serial),
+	)
+	client.deviceListFunc = deviceLister
+	return client
+}
+
 func TestRunCommandNoArgs(t *testing.T) {
 	s := &MockServer{
 		Status:   wire.StatusSuccess,