blob: 9e0ed9376034797f31111412a6126b7dccc1d1cf [file] [log] [blame]
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package adb provides an interface to the Android Debug Bridge.
package adb
import (
"bytes"
"errors"
"io"
"os"
"os/exec"
"path/filepath"
"android.googlesource.com/platform/tools/gpu/build"
)
var ErrADBNotFound = errors.New("ADB command not found on PATH")
// The path to the adb executable, or an empty string if the adb executable was
// not found.
var adb string
func init() {
// Search for ADB using ANDROID_HOME
if home := os.Getenv("ANDROID_HOME"); home != "" {
path, err := filepath.Abs(filepath.Join(home, "platform-tools", "adb") + build.HostExecutableExtension)
if err == nil {
if _, err := os.Stat(path); err == nil {
adb = path
return
}
}
}
// Fallback to searching on PATH.
if p, err := exec.LookPath("adb"); err != nil {
p, err = filepath.Abs(p)
if err == nil {
adb = p
}
}
}
func run(args ...string) (string, error) {
buf := &bytes.Buffer{}
cmd := exec.Command(adb, args...)
cmd.Stdout = buf
err := cmd.Run()
return string(buf.Bytes()), err
}
// Cmd represents a command that can be run on an Android device.
type Cmd struct {
// Path is the path of the command to run.
//
// This is the only field that must be set to a non-zero
// value. If Path is relative, it is evaluated relative
// to Dir.
Path string
// Args holds the command line arguments to pass to the command.
Args []string
// Dir specifies the working directory of the command.
// If Dir is the empty string, Run runs the command in the
// calling process's current directory.
Dir string
// The device this command should be run on. If nil, then any one of the
// attached devices will execute the command.
Device *Device
// Stdout and Stderr specify the process's standard output and error.
//
// If either is nil, Run connects the corresponding file descriptor
// to the null device (os.DevNull).
//
// If Stdout and Stderr are the same writer, at most one
// goroutine at a time will call Write.
Stdout io.Writer
Stderr io.Writer
}
// Run starts the specified command and waits for it to complete.
// The returned error is nil if the command runs, has no problems copying
// stdout and stderr, and exits with a zero exit status.
func (c *Cmd) Run() error {
args := []string{}
if c.Device != nil {
args = append(args, "-s", c.Device.Serial)
}
args = append(append(args, "shell", c.Path), c.Args...)
cmd := exec.Command(adb, args...)
cmd.Stdout = c.Stdout
cmd.Stderr = c.Stderr
return cmd.Run()
}
// Call starts the specified command and waits for it to complete, returning the
// all stdout as a string.
// The returned error is nil if the command runs, has no problems copying
// stdout and stderr, and exits with a zero exit status.
func (c *Cmd) Call() (string, error) {
clone := *c // Don't change c's Stdout
buf := &bytes.Buffer{}
if clone.Stdout != nil {
clone.Stdout = io.MultiWriter(clone.Stdout, buf)
} else {
clone.Stdout = buf
}
err := clone.Run()
return string(buf.Bytes()), err
}