diff --git a/devlib/platform/__init__.py b/devlib/platform/__init__.py index beda557..025d9f4 100644 --- a/devlib/platform/__init__.py +++ b/devlib/platform/__init__.py @@ -68,7 +68,7 @@ class Platform(object): def _identify_big_core(self): for core in self.core_names: - if core.upper() in BIG_CPUS: + if core.upper() in BIG_CPUS: return core big_idx = self.core_clusters.index(max(self.core_clusters)) return self.core_names[big_idx] @@ -88,4 +88,3 @@ class Platform(object): if core != self.big_core: self.little_core = core break - diff --git a/devlib/target.py b/devlib/target.py index d808534..da3d2af 100644 --- a/devlib/target.py +++ b/devlib/target.py @@ -20,7 +20,7 @@ from devlib.utils.types import integer, boolean, bitmask, identifier, caseless_s FSTAB_ENTRY_REGEX = re.compile(r'(\S+) on (\S+) type (\S+) \((\S+)\)') -ANDROID_SCREEN_STATE_REGEX = re.compile('(?:mPowerState|mScreenOn)=([0-9]+|true|false)', +ANDROID_SCREEN_STATE_REGEX = re.compile('(?:mPowerState|mScreenOn|Display Power: state)=([0-9]+|true|false|ON|OFF)', re.IGNORECASE) ANDROID_SCREEN_RESOLUTION_REGEX = re.compile(r'mUnrestrictedScreen=\(\d+,\d+\)' r'\s+(?P\d+)x(?P\d+)') @@ -348,6 +348,12 @@ class Target(object): command = 'if [ -e \'{}\' ]; then echo 1; else echo 0; fi' return boolean(self.execute(command.format(filepath)).strip()) + def directory_exists(self, filepath): + output = self.execute('if [ -d \'{}\' ]; then echo 1; else echo 0; fi'.format(filepath)) + # output from ssh my contain part of the expression in the buffer, + # split out everything except the last word. + return boolean(output.split()[-1]) # pylint: disable=maybe-no-member + def list_file_systems(self): output = self.execute('mount') fstab = [] @@ -415,19 +421,30 @@ class Target(object): def uninstall(self, name): raise NotImplementedError() - def get_installed(self, name): - for path in self.getenv('PATH').split(self.path.pathsep): - try: - if name in self.list_directory(path): - return self.path.join(path, name) - except TargetError: - pass # directory does not exist or no executable premssions + def get_installed(self, name, search_system_binaries=True): + # Check user installed binaries first if self.file_exists(self.executables_directory): if name in self.list_directory(self.executables_directory): return self.path.join(self.executables_directory, name) + # Fall back to binaries in PATH + if search_system_binaries: + for path in self.getenv('PATH').split(self.path.pathsep): + try: + if name in self.list_directory(path): + return self.path.join(path, name) + except TargetError: + pass # directory does not exist or no executable premssions which = get_installed + def install_if_needed(self, host_path, search_system_binaries=True): + + binary_path = self.get_installed(os.path.split(host_path)[1], + search_system_binaries=search_system_binaries) + if not binary_path: + binary_path = self.install(host_path) + return binary_path + def is_installed(self, name): return bool(self.get_installed(name)) @@ -626,6 +643,21 @@ class AndroidTarget(Target): def adb_name(self): return self.conn.device + @property + def android_id(self): + """ + Get the device's ANDROID_ID. Which is + + "A 64-bit number (as a hex string) that is randomly generated when the user + first sets up the device and should remain constant for the lifetime of the + user's device." + + .. note:: This will get reset on userdata erasure. + + """ + output = self.execute('content query --uri content://settings/secure --projection value --where "name=\'android_id\'"').strip() + return output.split('value=')[-1] + @property @memoized def screen_resolution(self): @@ -637,7 +669,6 @@ class AndroidTarget(Target): else: return (0, 0) - def reset(self, fastboot=False): # pylint: disable=arguments-differ try: self.execute('reboot {}'.format(fastboot and 'fastboot' or ''), @@ -741,28 +772,35 @@ class AndroidTarget(Target): self.conn.push(source, dest, timeout=timeout) else: device_tempfile = self.path.join(self._file_transfer_cache, source.lstrip(self.path.sep)) - self.execute('mkdir -p {}'.format(self.path.dirname(device_tempfile))) + self.execute("mkdir -p '{}'".format(self.path.dirname(device_tempfile))) self.conn.push(source, device_tempfile, timeout=timeout) - self.execute('cp {} {}'.format(device_tempfile, dest), as_root=True) + self.execute("cp '{}' '{}'".format(device_tempfile, dest), as_root=True) def pull(self, source, dest, as_root=False, timeout=None): # pylint: disable=arguments-differ if not as_root: self.conn.pull(source, dest, timeout=timeout) else: device_tempfile = self.path.join(self._file_transfer_cache, source.lstrip(self.path.sep)) - self.execute('mkdir -p {}'.format(self.path.dirname(device_tempfile))) - self.execute('cp {} {}'.format(source, device_tempfile), as_root=True) + self.execute("mkdir -p '{}'".format(self.path.dirname(device_tempfile))) + self.execute("cp '{}' '{}'".format(source, device_tempfile), as_root=True) self.conn.pull(device_tempfile, dest, timeout=timeout) # Android-specific - def swipe_to_unlock(self): + def swipe_to_unlock(self, direction="horizontal"): width, height = self.screen_resolution - swipe_heigh = height * 2 // 3 - start = 100 - stop = width - start command = 'input swipe {} {} {} {}' - self.execute(command.format(start, swipe_heigh, stop, swipe_heigh)) + if direction == "horizontal": + swipe_heigh = height * 2 // 3 + start = 100 + stop = width - start + self.execute(command.format(start, swipe_heigh, stop, swipe_heigh)) + if direction == "vertical": + swipe_middle = height / 2 + swipe_heigh = height * 2 // 3 + self.execute(command.format(swipe_middle, swipe_heigh, swipe_middle, 0)) + else: + raise DeviceError("Invalid swipe direction: {}".format(self.swipe_to_unlock)) def getprop(self, prop=None): props = AndroidProperties(self.execute('getprop')) @@ -791,7 +829,7 @@ class AndroidTarget(Target): def install_apk(self, filepath, timeout=None): # pylint: disable=W0221 ext = os.path.splitext(filepath)[1].lower() if ext == '.apk': - return adb_command(self.adb_name, "install {}".format(filepath), timeout=timeout) + return adb_command(self.adb_name, "install '{}'".format(filepath), timeout=timeout) else: raise TargetError('Can\'t install {}: unsupported format.'.format(filepath)) @@ -804,7 +842,7 @@ class AndroidTarget(Target): if on_device_file != on_device_executable: self.execute('cp {} {}'.format(on_device_file, on_device_executable), as_root=self.is_rooted) self.remove(on_device_file, as_root=self.is_rooted) - self.execute('chmod 0777 {}'.format(on_device_executable), as_root=self.is_rooted) + self.execute("chmod 0777 '{}'".format(on_device_executable), as_root=self.is_rooted) self._installed_binaries[executable_name] = on_device_executable return on_device_executable @@ -817,7 +855,7 @@ class AndroidTarget(Target): self.remove(on_device_executable, as_root=self.is_rooted) def dump_logcat(self, filepath, filter=None, append=False, timeout=30): # pylint: disable=redefined-builtin - op = '>>' if append == True else '>' + op = '>>' if append else '>' filtstr = ' -s {}'.format(filter) if filter else '' command = 'logcat -d{} {} {}'.format(filtstr, op, filepath) adb_command(self.adb_name, command, timeout=timeout) @@ -842,7 +880,7 @@ class AndroidTarget(Target): self.working_directory = '/data/local/tmp/devlib-target' self._file_transfer_cache = self.path.join(self.working_directory, '.file-cache') if self.executables_directory is None: - self.executables_directory = self.path.join(self.working_directory, 'bin') + self.executables_directory = '/data/local/tmp/bin' def _ensure_executables_directory_is_writable(self): matched = [] @@ -1032,4 +1070,3 @@ def _get_part_name(section): if name is None: name = '{}/{}/{}'.format(implementer, part, variant) return name - diff --git a/devlib/utils/android.py b/devlib/utils/android.py index ad637fa..8328b22 100644 --- a/devlib/utils/android.py +++ b/devlib/utils/android.py @@ -39,6 +39,7 @@ AM_START_ERROR = re.compile(r"Error: Activity class {[\w|.|/]*} does not exist") # See: # http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels ANDROID_VERSION_MAP = { + 23: 'MARSHMALLOW', 22: 'LOLLYPOP_MR1', 21: 'LOLLYPOP', 20: 'KITKAT_WATCH', @@ -167,7 +168,7 @@ class AdbConnection(object): def push(self, source, dest, timeout=None): if timeout is None: timeout = self.timeout - command = 'push {} {}'.format(source, dest) + command = "push '{}' '{}'".format(source, dest) return adb_command(self.device, command, timeout=timeout) def pull(self, source, dest, timeout=None): @@ -175,14 +176,14 @@ class AdbConnection(object): timeout = self.timeout # Pull all files matching a wildcard expression if os.path.isdir(dest) and \ - ('*' in source or '?' in source): + ('*' in source or '?' in source): command = 'shell ls {}'.format(source) output = adb_command(self.device, command, timeout=timeout) for line in output.splitlines(): - command = 'pull {} {}'.format(line, dest) + command = "pull '{}' '{}'".format(line, dest) adb_command(self.device, command, timeout=timeout) return - command = 'pull {} {}'.format(source, dest) + command = "pull '{}' '{}'".format(source, dest) return adb_command(self.device, command, timeout=timeout) def execute(self, command, timeout=None, check_exit_code=False, as_root=False): @@ -241,7 +242,7 @@ def adb_get_device(timeout=None): return output[1].split('\t')[0] elif output_length > 3: message = '{} Android devices found; either explicitly specify ' +\ - 'the device you want, or make sure only one is connected.' + 'the device you want, or make sure only one is connected.' raise HostError(message.format(output_length - 2)) else: if timeout < time.time() - start: