blob: b50cb1d0bcb427319dd2b6088820123648ce38ae [file] [log] [blame]
# Copyright 2015 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import logging
import re
import subprocess
from .tools import TimeoutCommunicate
_log = logging.getLogger(__name__)
class ConnectionLostException(Exception):
pass
class ADB(object):
"""Wrapper around the adb command line tool."""
KEY_POWER = "26"
KEY_UNLOCK = "82"
def __init__(self, adb_device):
"""
:param str adb_device: USB serial number of the device to use.
"""
self.adb_cmd = ["adb", "-s", adb_device]
def WaitForDevice(self, timeout=1):
self.Execute(["wait-for-device"], timeout=timeout)
def UninstallPackage(self, package_name):
"""Uninstalls package on device.
:type package_name: str
"""
self.ExecuteShell(["pm", "uninstall", package_name], check_success=True,
timeout=60)
def InstallAPK(self, apk):
"""Install local apk file onto device
:param str apk: path to apk file
"""
self.Execute(["install", "-r", "-d", apk], check_success=True, timeout=120)
def GetPackageVersion(self, package_name):
"""Returns the version name of an installed package or None
:type package_name: str
:returns Optional[str]: version name of package if it is installed
"""
info = self.ExecuteShell(["pm", "dump", package_name], timeout=20,
retries=2)
version_regex = re.compile("versionName=(.+)")
match = version_regex.search(info)
if match:
return match.group(1).strip()
return None
def PowerOn(self):
if not self.IsAwake():
self.SendKey(self.KEY_POWER)
self.SendKey(self.KEY_UNLOCK)
self.ExecuteShell(["svc", "power", "stayon", "true"], retries=2)
def PowerOff(self):
self.ExecuteShell(["svc", "power", "stayon", "false"], retries=2)
if self.IsAwake():
self.SendKey(self.KEY_POWER)
def IsAwake(self):
power_dump = self.ExecuteShell(["dumpsys", "power"], timeout=20, retries=2)
return "mWakefulness=Awake" in power_dump
def GetProp(self, prop_name):
"""Return 'getprop' property from device.
Returns None if the property does not exist.
"""
prop = self.ExecuteShell(["getprop", prop_name], retries=2).strip()
if len(prop):
return prop
return None
def PutSetting(self, namespace, key, value):
self.ExecuteShell(["settings", "put", namespace, key, value], retries=2)
def GetSetting(self, namespace, key):
stdout = self.ExecuteShell(["settings", "get", namespace, key], retries=2)
return stdout.strip()
def SendKey(self, key_name):
self.ExecuteShell(["input", "keyevent", key_name], timeout=10, retries=2)
def PushFile(self, local_file, remote_file):
self.Execute(["push", local_file, remote_file], timeout=240)
def StartActivity(self, action=None, component=None, uri=None, category=None,
wait=True, force_stop=False):
"""Start an activity via the adb activity manager.
The parameters are directly passed to the am start command to specify the
intent to start.
"""
cmd = ["am", "start"]
for flag, value in [("-W", wait), ("-S", force_stop)]:
if value:
cmd += [flag]
for flag, value in [("-a", action), ("-n", component),
("-d", uri), ("-c", category)]:
if value:
cmd += [flag, value]
self.ExecuteShell(cmd, check_error=True, timeout=20, retries=2)
def Execute(self, sub_command, check_success=False, check_error=False,
timeout=False, retries=0):
"""Execute an adb command.
Many ADB commands do not return an error code when failing, so we have
the option to look for a 'success' message, which is usually printed
after commands.
:type sub_command: List[str]
:param bool check_success: Check for 'success' message, raises Exception if
it was not printed.
"""
try:
command = self.adb_cmd + sub_command
_log.info("Executing %s", " ".join(command))
process = subprocess.Popen(command, shell=False, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
stdout = TimeoutCommunicate(process, timeout)
error_message = None
if stdout is False:
error_message = "'%s' timed out." % (" ".join(command),)
elif process.returncode > 0:
error_message = "'%s' returned with %d" % (" ".join(command),
process.returncode)
elif ((check_success and not "Success" in stdout) or
(check_error and "Error" in stdout) or
(process.returncode > 0)):
error_message = "'%s' failed" % (" ".join(command))
if error_message:
if stdout:
_log.error(error_message + "\n" + stdout)
raise Exception(error_message)
return stdout
except KeyboardInterrupt:
raise
except:
if retries > 0:
_log.exception("Retrying command")
try:
self.WaitForDevice(30)
except:
raise ConnectionLostException()
return self.Execute(sub_command, check_success, check_error, timeout,
retries - 1)
raise
def ExecuteShell(self, shell_command, check_success=False, check_error=False,
timeout=False, retries=0):
"""Wrapper to execute an adb shell command.
:type shell_command: List[str]
:param bool check_success: Check for 'success' message, raises Exception if
it was not printed.
"""
return self.Execute(["shell"] + shell_command, check_success, check_error,
timeout, retries)