Karsten Tausche | 81ffea2 | 2018-06-19 15:34:01 +0200 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | |
| 3 | import re |
| 4 | import subprocess |
| 5 | import time |
| 6 | |
| 7 | |
| 8 | ADB_DEVICES_PATTERN = re.compile(r"^([a-z0-9-]+)\s+device$", flags=re.M) |
| 9 | |
| 10 | |
| 11 | class DeviceCommandError(BaseException): |
| 12 | """An error happened while sending a command to a device.""" |
| 13 | |
| 14 | def __init__(self, serial, command, error_message): |
| 15 | self.serial = serial |
| 16 | self.command = command |
| 17 | self.error_message = error_message |
| 18 | message = "Command `{}` failed on {}: {}".format( |
| 19 | command, serial, error_message |
| 20 | ) |
| 21 | super(DeviceCommandError, self).__init__(message) |
| 22 | |
| 23 | |
| 24 | def adb(*args, serial=None, raise_on_error=True): |
| 25 | """Run ADB command attached to serial. |
| 26 | |
| 27 | Example: |
| 28 | >>> process = adb('shell', 'getprop', 'ro.build.fingerprint', serial='aserialnumber') |
| 29 | >>> process.returncode |
| 30 | 0 |
| 31 | >>> process.stdout.strip() |
| 32 | 'ExampleVendor/Device/version/tags' |
| 33 | |
| 34 | :param *args: |
| 35 | List of options to ADB (including command). |
| 36 | :param str serial: |
| 37 | Identifier for ADB connection to device. |
| 38 | :param raise_on_error bool: |
| 39 | Whether to raise a DeviceCommandError exception if the return code is |
| 40 | less than 0. |
| 41 | :returns subprocess.CompletedProcess: |
| 42 | Completed process. |
| 43 | :raises DeviceCommandError: |
| 44 | If the command failed. |
| 45 | """ |
| 46 | |
| 47 | # Make sure the adb server is started to avoid the infamous "out of date" |
| 48 | # message that pollutes stdout. |
| 49 | ret = subprocess.run( |
| 50 | ["adb", "start-server"], |
| 51 | stdout=subprocess.PIPE, |
| 52 | stderr=subprocess.PIPE, |
| 53 | universal_newlines=True, |
| 54 | ) |
| 55 | if ret.returncode < 0: |
| 56 | if raise_on_error: |
| 57 | raise DeviceCommandError( |
| 58 | serial if serial else "??", str(args), ret.stderr |
| 59 | ) |
| 60 | else: |
| 61 | return None |
| 62 | |
| 63 | command = ["adb"] |
| 64 | if serial: |
| 65 | command += ["-s", serial] |
| 66 | if args: |
| 67 | command += list(args) |
| 68 | ret = subprocess.run( |
| 69 | command, |
| 70 | stdout=subprocess.PIPE, |
| 71 | stderr=subprocess.PIPE, |
| 72 | universal_newlines=True, |
| 73 | ) |
| 74 | |
| 75 | if raise_on_error and ret.returncode < 0: |
| 76 | raise DeviceCommandError( |
| 77 | serial if serial else "??", str(args), ret.stderr |
| 78 | ) |
| 79 | |
| 80 | return ret |
| 81 | |
| 82 | |
| 83 | def list_devices(): |
| 84 | """List serial numbers of devices attached to adb. |
| 85 | |
| 86 | Raises: |
| 87 | DeviceCommandError: If the underlying adb command failed. |
| 88 | """ |
| 89 | process = adb("devices") |
| 90 | return ADB_DEVICES_PATTERN.findall(process.stdout) |
| 91 | |
| 92 | |
| 93 | def unlock(dut): |
| 94 | """Wake-up the device and unlock it. |
| 95 | |
| 96 | Raises: |
| 97 | DeviceCommandError: If the underlying adb commands failed. |
| 98 | """ |
| 99 | if not dut.info["screenOn"]: |
| 100 | adb("shell", "input keyevent KEYCODE_POWER", serial=dut.serial) |
| 101 | time.sleep(1) |
| 102 | |
| 103 | # Make sure we are on the home screen. |
| 104 | adb("shell", "input keyevent KEYCODE_HOME", serial=dut.serial) |
| 105 | # The KEYCODE_MENU input is enough to unlock a "swipe up to unlock" |
| 106 | # lockscreen on Android 6, but unfortunately not Android 7. So we use a |
| 107 | # swipe up (that depends on the screen resolution) instead. |
| 108 | adb("shell", "input touchscreen swipe 930 880 930 380", serial=dut.serial) |
| 109 | time.sleep(1) |
| 110 | adb("shell", "input keyevent KEYCODE_HOME", serial=dut.serial) |