blob: 9d1cde6098ebdd537a75c69c245165da059c286c [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
import (
"errors"
"fmt"
"strings"
)
// DeviceState represents the last queried state of an Android device.
type DeviceState int
// binary: DeviceState#Offline = offline
// binary: DeviceState#Online = device
// binary: DeviceState#Unauthorized = unauthorized
const (
Offline DeviceState = iota
Online
Unauthorized
)
// ErrDeviceNotRooted is returned by Device.Root when the device is running a
// production build as is not 'rooted'.
var ErrDeviceNotRooted = errors.New("Device is not rooted")
// Device represents an attached Android device.
type Device struct {
Serial string
State DeviceState
abi string
}
// Command returns a new Cmd that will run the command with the specified name
// and arguments on this device.
func (d *Device) Command(path string, args ...string) *Cmd {
return &Cmd{
Path: path,
Args: args,
Device: d,
}
}
// Root restarts adb as root. If the device is running a production build then
// Root will return ErrDeviceNotRooted.
func (d *Device) Root() error {
cmd := Cmd{Args: []string{"root"}}
res, err := cmd.Call()
switch strings.TrimSpace(res) {
case "adbd cannot run as root in production builds":
return ErrDeviceNotRooted
case "restarting adbd as root", "adbd is already running as root":
return err
default:
if err == nil {
return errors.New(res)
} else {
return err
}
}
}
// SELinuxEnforcing returns true if the device is currently in a
// SELinux enforcing mode, or false if the device is currently in a SELinux
// permissive mode.
func (d *Device) SELinuxEnforcing() (bool, error) {
res, err := d.Command("getenforce").Call()
return strings.Contains(strings.ToLower(res), "enforcing"), err
}
// SetSELinuxEnforcing changes the SELinux-enforcing mode.
func (d *Device) SetSELinuxEnforcing(enforce bool) error {
if enforce {
return d.Command("setenforce", "1").Run()
} else {
return d.Command("setenforce", "0").Run()
}
}
// StartActivity launches the specified action.
func (d *Device) StartActivity(a Action) error {
return d.Command("am", "start",
"-S", // Force-stop the target app before starting the activity
"-W", // Wait for the activity to start
"-a", a.Name,
"-n", a.Package.Name+"/"+a.Activity).Run()
}
// String returns a string representing the device.
func (d *Device) Abi() string {
if d.abi == "" {
res, err := d.Command("getprop", "ro.product.cpu.abi").Call()
if err == nil {
d.abi = strings.TrimSpace(res)
}
}
return d.abi
}
// String returns a string representing the device.
func (d *Device) String() string {
return fmt.Sprintf("Device<%s>", d.Serial)
}
// Devices returns the list of serial numbers of all the attached Android
// devices.
func Devices() ([]*Device, error) {
if adb == "" {
return nil, ErrADBNotFound
}
cmd := Cmd{Args: []string{"devices"}}
if out, err := cmd.Call(); err == nil {
return parseDevices(out)
} else {
return nil, err
}
}
func parseDevices(out string) ([]*Device, error) {
a := strings.SplitAfter(out, "List of devices attached")
if len(a) != 2 {
return nil, errors.New("Device list not returned")
}
lines := strings.Split(a[1], "\n")
devices := make([]*Device, 0, len(lines))
for _, line := range lines {
fields := strings.Fields(line)
switch len(fields) {
case 0:
continue
case 2:
state := DeviceState(0)
if err := state.Parse(fields[1]); err != nil {
return nil, err
}
device := &Device{
Serial: fields[0],
State: state,
}
devices = append(devices, device)
default:
return nil, errors.New("Could not parse device list")
}
}
return devices, nil
}