| // 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 |
| } |