Browse Source

Merge github.com/mvdan/adb back into fdroidcl

Splitting adb into a separate repo wasn't a good idea, as it's not very
useful in general and it's tightly coupled with fdroidcl.
pull/10/head
Daniel Martí 10 years ago
parent
commit
bb403cd4eb
  1. 247
      adb/device.go
  2. 109
      adb/error.go
  3. 23
      adb/error_test.go
  4. 28
      adb/server.go

247
adb/device.go

@ -0,0 +1,247 @@
// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information
package adb
import (
"bufio"
"fmt"
"io"
"os/exec"
"regexp"
"strconv"
"strings"
)
type Device struct {
ID string
Usb string
Product string
Model string
Device string
ABIs []string
APILevel int
}
var deviceRegex = regexp.MustCompile(`^([^\s]+)\s+device(.*)$`)
func Devices() ([]*Device, error) {
cmd := exec.Command("adb", "devices", "-l")
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
if err := cmd.Start(); err != nil {
return nil, err
}
var devices []*Device
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
line := scanner.Text()
m := deviceRegex.FindStringSubmatch(line)
if m == nil {
continue
}
device := &Device{
ID: m[1],
}
for _, extra := range strings.Split(m[2], " ") {
sp := strings.SplitN(extra, ":", 2)
if len(sp) < 2 {
continue
}
switch sp[0] {
case "usb":
device.Usb = sp[1]
case "product":
device.Product = sp[1]
case "model":
device.Model = sp[1]
case "device":
device.Device = sp[1]
}
}
props, err := device.AdbProps()
if err != nil {
return nil, err
}
device.ABIs = getAbis(props)
if len(device.ABIs) == 0 {
return nil, fmt.Errorf("failed to get device ABIs")
}
device.APILevel, err = strconv.Atoi(props["ro.build.version.sdk"])
if device.APILevel == 0 {
return nil, fmt.Errorf("failed to get device API level")
}
devices = append(devices, device)
}
return devices, nil
}
func (d *Device) AdbCmd(args ...string) *exec.Cmd {
cmdArgs := append([]string{"-s", d.ID}, args...)
return exec.Command("adb", cmdArgs...)
}
func (d *Device) AdbShell(args ...string) *exec.Cmd {
shellArgs := append([]string{"shell"}, args...)
return d.AdbCmd(shellArgs...)
}
var propLineRegex = regexp.MustCompile(`^\[(.*)\]: \[(.*)\]$`)
func (d *Device) AdbProps() (map[string]string, error) {
cmd := d.AdbShell("getprop")
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
if err := cmd.Start(); err != nil {
return nil, err
}
props := make(map[string]string)
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
m := propLineRegex.FindStringSubmatch(scanner.Text())
if m == nil {
continue
}
key, val := m[1], m[2]
props[key] = val
}
return props, nil
}
func getFailureCode(r *regexp.Regexp, line string) string {
return r.FindStringSubmatch(line)[1]
}
func getAbis(props map[string]string) []string {
// Android 5.0 and later specify a list of ABIs
if abilist, e := props["ro.product.cpu.abilist"]; e {
return strings.Split(abilist, ",")
}
// Older Android versions specify one primary ABI and optionally
// one secondary ABI
abi, e := props["ro.product.cpu.abi"]
if !e {
return nil
}
if abi2, e := props["ro.product.cpu.abi2"]; e {
return []string{abi, abi2}
}
return []string{abi}
}
var installFailureRegex = regexp.MustCompile(`^Failure \[INSTALL_(.+)\]$`)
func withOpts(cmd string, opts []string, args ...string) []string {
v := append([]string{"install"}, opts...)
return append(v, args...)
}
func (d *Device) install(opts []string, path string) error {
cmd := d.AdbCmd(withOpts("install", opts, path)...)
stdout, err := cmd.StdoutPipe()
if err != nil {
return err
}
if err := cmd.Start(); err != nil {
return err
}
line := getResultLine(stdout)
if line == "Success" {
return nil
}
return parseError(getFailureCode(installFailureRegex, line))
}
func (d *Device) Install(path string) error {
return d.install(nil, path)
}
func (d *Device) Upgrade(path string) error {
return d.install([]string{"-r"}, path)
}
func getResultLine(out io.Reader) string {
scanner := bufio.NewScanner(out)
for scanner.Scan() {
l := scanner.Text()
if strings.HasPrefix(l, "Failure") || strings.HasPrefix(l, "Success") {
return l
}
}
return ""
}
var deleteFailureRegex = regexp.MustCompile(`^Failure \[DELETE_(.+)\]$`)
func (d *Device) Uninstall(pkg string) error {
cmd := d.AdbCmd("uninstall", pkg)
stdout, err := cmd.StdoutPipe()
if err != nil {
return err
}
if err := cmd.Start(); err != nil {
return err
}
line := getResultLine(stdout)
if line == "Success" {
return nil
}
return parseError(getFailureCode(deleteFailureRegex, line))
}
type Package struct {
ID string
VCode int
VName string
}
var (
packageRegex = regexp.MustCompile(`^ Package \[([^\s]+)\]`)
verCodeRegex = regexp.MustCompile(`^ versionCode=([0-9]+)`)
verNameRegex = regexp.MustCompile(`^ versionName=(.+)`)
)
func (d *Device) Installed() (map[string]Package, error) {
cmd := d.AdbShell("dumpsys", "package", "packages")
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
if err := cmd.Start(); err != nil {
return nil, err
}
packages := make(map[string]Package)
scanner := bufio.NewScanner(stdout)
var cur Package
first := true
for scanner.Scan() {
l := scanner.Text()
if m := packageRegex.FindStringSubmatch(l); m != nil {
if first {
first = false
} else {
packages[cur.ID] = cur
cur = Package{}
}
cur.ID = m[1]
} else if m := verCodeRegex.FindStringSubmatch(l); m != nil {
n, err := strconv.Atoi(m[1])
if err != nil {
panic(err)
}
cur.VCode = n
} else if m := verNameRegex.FindStringSubmatch(l); m != nil {
cur.VName = m[1]
}
}
if !first {
packages[cur.ID] = cur
}
return packages, nil
}

109
adb/error.go

@ -0,0 +1,109 @@
// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information
package adb
import (
"errors"
"fmt"
)
var (
// Common install and uninstall errors
ErrInternalError = errors.New("internal error")
ErrUserRestricted = errors.New("user restricted")
ErrAborted = errors.New("aborted")
// Install errors
ErrAlreadyExists = errors.New("already exists")
ErrInvalidApk = errors.New("invalid apk")
ErrInvalidURI = errors.New("invalid uri")
ErrInsufficientStorage = errors.New("insufficient storage")
ErrDuplicatePackage = errors.New("duplicate package")
ErrNoSharedUser = errors.New("no shared user")
ErrUpdateIncompatible = errors.New("update incompatible")
ErrSharedUserIncompatible = errors.New("shared user incompatible")
ErrMissingSharedLibrary = errors.New("missing shared library")
ErrReplaceCouldntDelete = errors.New("replace couldn't delete")
ErrDexopt = errors.New("dexopt")
ErrOlderSdk = errors.New("older sdk")
ErrConflictingProvider = errors.New("conflicting provider")
ErrNewerSdk = errors.New("newer sdk")
ErrTestOnly = errors.New("test only")
ErrCPUAbiIncompatible = errors.New("cpu abi incompatible")
ErrMissingFeature = errors.New("missing feature")
ErrContainerError = errors.New("combiner error")
ErrInvalidInstallLocation = errors.New("invalid install location")
ErrMediaUnavailable = errors.New("media unavailable")
ErrVerificationTimeout = errors.New("verification timeout")
ErrVerificationFailure = errors.New("verification failure")
ErrPackageChanged = errors.New("package changed")
ErrUIDChanged = errors.New("uid changed")
ErrVersionDowngrade = errors.New("version downgrade")
ErrNotApk = errors.New("not apk")
ErrBadManifest = errors.New("bad manifest")
ErrUnexpectedException = errors.New("unexpected exception")
ErrNoCertificates = errors.New("no certificates")
ErrInconsistentCertificates = errors.New("inconsistent certificates")
ErrCertificateEncoding = errors.New("certificate encoding")
ErrBadPackageName = errors.New("bad package name")
ErrBadSharedUserID = errors.New("bad shared user id")
ErrManifestMalformed = errors.New("manifest malformed")
ErrManifestEmpty = errors.New("manifest empty")
ErrDuplicatePermission = errors.New("duplicate permission")
ErrNoMatchingAbis = errors.New("no matching abis")
// Uninstall errors
ErrDevicePolicyManager = errors.New("device policy manager")
ErrOwnerBlocked = errors.New("owner blocked")
)
var errorVals = map[string]error{
"FAILED_ALREADY_EXISTS": ErrAlreadyExists,
"FAILED_INVALID_APK": ErrInvalidApk,
"FAILED_INVALID_URI": ErrInvalidURI,
"FAILED_INSUFFICIENT_STORAGE": ErrInsufficientStorage,
"FAILED_DUPLICATE_PACKAGE": ErrDuplicatePackage,
"FAILED_NO_SHARED_USER": ErrNoSharedUser,
"FAILED_UPDATE_INCOMPATIBLE": ErrUpdateIncompatible,
"FAILED_SHARED_USER_INCOMPATIBLE": ErrSharedUserIncompatible,
"FAILED_MISSING_SHARED_LIBRARY": ErrMissingSharedLibrary,
"FAILED_REPLACE_COULDNT_DELETE": ErrReplaceCouldntDelete,
"FAILED_DEXOPT": ErrDexopt,
"FAILED_OLDER_SDK": ErrOlderSdk,
"FAILED_CONFLICTING_PROVIDER": ErrConflictingProvider,
"FAILED_NEWER_SDK": ErrNewerSdk,
"FAILED_TEST_ONLY": ErrTestOnly,
"FAILED_CPU_ABI_INCOMPATIBLE": ErrCPUAbiIncompatible,
"FAILED_MISSING_FEATURE": ErrMissingFeature,
"FAILED_CONTAINER_ERROR": ErrContainerError,
"FAILED_INVALID_INSTALL_LOCATION": ErrInvalidInstallLocation,
"FAILED_MEDIA_UNAVAILABLE": ErrMediaUnavailable,
"FAILED_VERIFICATION_TIMEOUT": ErrVerificationTimeout,
"FAILED_VERIFICATION_FAILURE": ErrVerificationFailure,
"FAILED_PACKAGE_CHANGED": ErrPackageChanged,
"FAILED_UID_CHANGED": ErrUIDChanged,
"FAILED_VERSION_DOWNGRADE": ErrVersionDowngrade,
"PARSE_FAILED_NOT_APK": ErrNotApk,
"PARSE_FAILED_BAD_MANIFEST": ErrBadManifest,
"PARSE_FAILED_UNEXPECTED_EXCEPTION": ErrUnexpectedException,
"PARSE_FAILED_NO_CERTIFICATES": ErrNoCertificates,
"PARSE_FAILED_INCONSISTENT_CERTIFICATES": ErrInconsistentCertificates,
"PARSE_FAILED_CERTIFICATE_ENCODING": ErrCertificateEncoding,
"PARSE_FAILED_BAD_PACKAGE_NAME": ErrBadPackageName,
"PARSE_FAILED_BAD_SHARED_USER_ID": ErrBadSharedUserID,
"PARSE_FAILED_MANIFEST_MALFORMED": ErrManifestMalformed,
"PARSE_FAILED_MANIFEST_EMPTY": ErrManifestEmpty,
"FAILED_INTERNAL_ERROR": ErrInternalError,
"FAILED_USER_RESTRICTED": ErrUserRestricted,
"FAILED_DUPLICATE_PERMISSION": ErrDuplicatePermission,
"FAILED_NO_MATCHING_ABIS": ErrNoMatchingAbis,
"FAILED_ABORTED": ErrAborted,
}
func parseError(s string) error {
if err, e := errorVals[s]; e {
return err
}
return fmt.Errorf("unknown error: %s", s)
}

23
adb/error_test.go

@ -0,0 +1,23 @@
// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information
package adb
import "testing"
func TestParseError(t *testing.T) {
tests := []struct {
in string
want error
}{
{"FAILED_DEXOPT", ErrDexopt},
{"PARSE_FAILED_NOT_APK", ErrNotApk},
{"FAILED_ABORTED", ErrAborted},
}
for _, c := range tests {
got := parseError(c.in)
if got != c.want {
t.Fatalf("Parse error in %s - wanted %v, got %v", c.in, c.want, got)
}
}
}

28
adb/server.go

@ -0,0 +1,28 @@
// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information
package adb
import (
"fmt"
"net"
"os/exec"
)
const (
host = "127.0.0.1"
port = 5037
)
func IsServerRunning() bool {
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
if err != nil {
return false
}
conn.Close()
return true
}
func StartServer() error {
return exec.Command("adb", "start-server").Run()
}
Loading…
Cancel
Save