blob: d5d27a9a8e95fde240e586dc7390fadcb6c64e5c [file] [log] [blame]
// Package adb is a simple wrapper around calling adb.
package adb
import (
"context"
"os/exec"
"regexp"
"strconv"
"strings"
"time"
"go.skia.org/infra/go/executil"
"go.skia.org/infra/go/skerr"
)
const (
commandTimeout = 5 * time.Second
)
var (
// proplines is a regex that matches the output of `adb shell getprop`. Which
// has output that looks like:
//
// [ro.product.manufacturer]: [asus]
// [ro.product.model]: [Nexus 7]
// [ro.product.name]: [razor]
proplines = regexp.MustCompile(`(?m)^\[(?P<key>.+)\]:\s*\[(?P<value>.*)\].*$`)
)
// AdbImpl handles talking to the adb process.
type AdbImpl struct{}
// New returns a new Adb.
func New() AdbImpl {
return AdbImpl{}
}
// Adb is the interface that AdbImpl provides.
type Adb interface {
// RawProperties returns the unfiltered output of running "adb shell getprop".
RawProperties(ctx context.Context) (string, error)
// RawDumpSys returns the unfiltered output of running "adb shell dumpsys <service>".
RawDumpSys(ctx context.Context, service string) (string, error)
// Reboot the device.
Reboot(ctx context.Context) error
// Uptime
Uptime(ctx context.Context) (time.Duration, error)
}
// RawProperties implements the Adb interface.
func (a AdbImpl) RawProperties(ctx context.Context) (string, error) {
ctx, cancel := context.WithTimeout(ctx, commandTimeout)
defer cancel()
cmd := executil.CommandContext(ctx, "adb", "shell", "getprop")
b, err := cmd.Output()
if err != nil {
if ee, ok := err.(*exec.ExitError); ok {
err = skerr.Wrapf(err, "adb failed with stderr: %q", ee.Stderr)
}
return "", err
}
return string(b), nil
}
// RawDumpSys implements the Adb interface.
func (a AdbImpl) RawDumpSys(ctx context.Context, service string) (string, error) {
ctx, cancel := context.WithTimeout(ctx, commandTimeout)
defer cancel()
cmd := executil.CommandContext(ctx, "adb", "shell", "dumpsys", service)
b, err := cmd.Output()
if err != nil {
if ee, ok := err.(*exec.ExitError); ok {
err = skerr.Wrapf(err, "adb failed with stderr: %q", ee.Stderr)
}
return "", err
}
return string(b), nil
}
// Reboot implements the Adb interface.
func (a AdbImpl) Reboot(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, commandTimeout)
defer cancel()
cmd := executil.CommandContext(ctx, "adb", "reboot")
_, err := cmd.Output()
if err != nil {
if ee, ok := err.(*exec.ExitError); ok {
err = skerr.Wrapf(err, "adb reboot with stderr: %q", ee.Stderr)
}
return err
}
return nil
}
// Uptime implements the Adb interface.
func (a AdbImpl) Uptime(ctx context.Context) (time.Duration, error) {
ctx, cancel := context.WithTimeout(ctx, commandTimeout)
defer cancel()
cmd := executil.CommandContext(ctx, "adb", "shell", "cat", "/proc/uptime")
b, err := cmd.Output()
if err != nil {
if ee, ok := err.(*exec.ExitError); ok {
err = skerr.Wrapf(err, "adb failed with stderr: %q", ee.Stderr)
}
return time.Duration(0), err
}
// The contents of /proc/uptime are the uptime in seconds, followed by the
// idle time of all the cores.
// https://en.wikipedia.org/wiki/Uptime#Using_/proc/uptime
uptimeAsString := string(b)
parts := strings.Split(uptimeAsString, " ")
if len(parts) != 2 {
return time.Duration(0), skerr.Fmt("Found invalid format for /proc/uptime: %q", uptimeAsString)
}
uptime, err := strconv.ParseFloat(parts[0], 64)
return time.Duration(int64(uptime) * int64(time.Second)), nil
}
// Assert that AdbImpl implements the Adb interface.
var _ Adb = AdbImpl{}