| // 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" |
| "strings" |
| |
| "android.googlesource.com/platform/tools/gpu/maker" |
| ) |
| |
| // ErrADBNotFound is returned when the ADB executable is not found. |
| var ErrADBNotFound = errors.New("ADB command not found on PATH") |
| |
| // ErrDeviceUnauthorized is returned by ADB commands when the device has not |
| // authorized ADB debugging. Check the confirmation dialog on the device. |
| var ErrDeviceUnauthorized = errors.New("Device unauthorized") |
| |
| // 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") + maker.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 { |
| if p, err = filepath.Abs(p); err == nil { |
| adb = p |
| } |
| } |
| } |
| |
| // Cmd represents a command that can be run on an Android device. |
| type Cmd struct { |
| // Path is the path of the command to run on the device. |
| // |
| // If the string is empty, the command is treated as a ADB command for Device. |
| Path string |
| |
| // Args holds the command line arguments to pass to the command. |
| Args []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) |
| } |
| if c.Path != "" { |
| args = append(args, "shell", c.Path) |
| } |
| args = append(args, 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 |
| |
| stdout := &bytes.Buffer{} |
| if clone.Stdout != nil { |
| clone.Stdout = io.MultiWriter(clone.Stdout, stdout) |
| } else { |
| clone.Stdout = stdout |
| } |
| |
| stderr := &bytes.Buffer{} |
| if clone.Stdout != nil { |
| clone.Stderr = io.MultiWriter(clone.Stdout, stderr) |
| } else { |
| clone.Stderr = stderr |
| } |
| err := clone.Run() |
| if err != nil && strings.Contains(stderr.String(), "error: device unauthorized.") { |
| err = ErrDeviceUnauthorized |
| } |
| return stdout.String(), err |
| } |