def Install(self, apk_path, forward_lock=False, reinstall=False, sd_card=False, timeout=60 * 2, retries=_DEFAULT_RETRIES): """Install an apk on the device. Args: apk_path: Host path to the APK file. forward_lock: (optional) If set forward-locks the app. reinstall: (optional) If set reinstalls the app, keeping its data. sd_card: (optional) If set installs on the SD card. timeout: (optional) Timeout per try in seconds. retries: (optional) Number of retries to attempt. """ _VerifyLocalFileExists(apk_path) cmd = ['install'] if forward_lock: cmd.append('-l') if reinstall: cmd.append('-r') if sd_card: cmd.append('-s') cmd.append(apk_path) output = self._DeviceAdbCmd(cmd, timeout, retries) if 'Success' not in output: raise device_errors.CommandFailedError(cmd, output)
def StartActivity(self, intent_obj, blocking=False, trace_file_name=None, force_stop=False, timeout=None, retries=None): """Start package's activity on the device. Args: intent_obj: An Intent object to send. blocking: A boolean indicating whether we should wait for the activity to finish launching. trace_file_name: If present, a string that both indicates that we want to profile the activity and contains the path to which the trace should be saved. force_stop: A boolean indicating whether we should stop the activity before starting it. timeout: timeout in seconds retries: number of retries Raises: CommandFailedError if the activity could not be started. CommandTimeoutError on timeout. DeviceUnreachableError on missing device. """ cmd = ['am', 'start'] if blocking: cmd.append('-W') if trace_file_name: cmd.extend(['--start-profiler', trace_file_name]) if force_stop: cmd.append('-S') cmd.extend(intent_obj.am_args) for line in self.RunShellCommand(cmd, check_return=True): if line.startswith('Error:'): raise device_errors.CommandFailedError(line, str(self))
def ReadFile(self, device_path, as_root=False, timeout=None, retries=None): """Reads the contents of a file from the device. Args: device_path: A string containing the absolute path of the file to read from the device. as_root: A boolean indicating whether the read should be executed with root privileges. timeout: timeout in seconds retries: number of retries Returns: The contents of the file at |device_path| as a list of lines. Raises: CommandFailedError if the file can't be read. CommandTimeoutError on timeout. DeviceUnreachableError on missing device. """ # TODO(jbudorick) Evaluate whether we want to return a list of lines after # the implementation switch, and if file not found should raise exception. if as_root: if not self.old_interface.CanAccessProtectedFileContents(): raise device_errors.CommandFailedError( 'Cannot read from %s with root privileges.' % device_path) return self.old_interface.GetProtectedFileContents(device_path) else: return self.old_interface.GetFileContents(device_path)
def KillAll(self, process_name, signum=9, as_root=False, blocking=False, timeout=None, retries=None): """Kill all processes with the given name on the device. Args: process_name: A string containing the name of the process to kill. signum: An integer containing the signal number to send to kill. Defaults to 9 (SIGKILL). as_root: A boolean indicating whether the kill should be executed with root privileges. blocking: A boolean indicating whether we should wait until all processes with the given |process_name| are dead. timeout: timeout in seconds retries: number of retries Raises: CommandFailedError if no process was killed. CommandTimeoutError on timeout. DeviceUnreachableError on missing device. """ pids = self._GetPidsImpl(process_name) if not pids: raise device_errors.CommandFailedError( 'No process "%s"' % process_name, str(self)) cmd = ['kill', '-%d' % signum] + pids.values() self.RunShellCommand(cmd, as_root=as_root, check_return=True) if blocking: wait_period = 0.1 while self._GetPidsImpl(process_name): time.sleep(wait_period) return len(pids)
def SetCharging(self, enabled, timeout=None, retries=None): """Enables or disables charging on the device. Args: enabled: A boolean indicating whether charging should be enabled or disabled. timeout: timeout in seconds retries: number of retries Raises: device_errors.CommandFailedError: If method of disabling charging cannot be determined. """ if 'charging_config' not in self._cache: for c in _CONTROL_CHARGING_COMMANDS: if self._device.FileExists(c['witness_file']): self._cache['charging_config'] = c break else: raise device_errors.CommandFailedError( 'Unable to find charging commands.') if enabled: command = self._cache['charging_config']['enable_command'] else: command = self._cache['charging_config']['disable_command'] def set_and_verify_charging(): self._device.RunShellCommand(command, check_return=True) return self.GetCharging() == enabled timeout_retry.WaitFor(set_and_verify_charging, wait_period=1)
def SetCharging(self, enabled, timeout=None, retries=None): """Enables or disables charging on the device. Args: enabled: A boolean indicating whether charging should be enabled or disabled. timeout: timeout in seconds retries: number of retries Raises: device_errors.CommandFailedError: If method of disabling charging cannot be determined. """ self._DiscoverDeviceProfile() if not self._cache['profile']['enable_command']: raise device_errors.CommandFailedError( 'Unable to find charging commands.') if enabled: command = self._cache['profile']['enable_command'] else: command = self._cache['profile']['disable_command'] def verify_charging(): return self.GetCharging() == enabled self._device.RunShellCommand(command, check_return=True, as_root=True, large_output=True) timeout_retry.WaitFor(verify_charging, wait_period=1)
def WriteFile(self, device_path, contents, as_root=False, timeout=None, retries=None): """Writes |contents| to a file on the device. Args: device_path: A string containing the absolute path to the file to write on the device. contents: A string containing the data to write to the device. as_root: A boolean indicating whether the write should be executed with root privileges. timeout: timeout in seconds retries: number of retries Raises: CommandFailedError if the file could not be written on the device. CommandTimeoutError on timeout. DeviceUnreachableError on missing device. """ if as_root: if not self.old_interface.CanAccessProtectedFileContents(): raise device_errors.CommandFailedError( 'Cannot write to %s with root privileges.' % device_path) self.old_interface.SetProtectedFileContents(device_path, contents) else: self.old_interface.SetFileContents(device_path, contents)
def SetProp(self, property_name, value, check=False, timeout=None, retries=None): """Sets a property on the device. Args: property_name: A string containing the name of the property to set on the device. value: A string containing the value to set to the property on the device. check: A boolean indicating whether to check that the property was successfully set on the device. timeout: timeout in seconds retries: number of retries Raises: CommandFailedError if check is true and the property was not correctly set on the device (e.g. because it is not rooted). CommandTimeoutError on timeout. """ assert isinstance(property_name, basestring), ( "property_name is not a string: %r" % property_name) assert isinstance(value, basestring), "value is not a string: %r" % value self.RunShellCommand(['setprop', property_name, value], check_return=True) if property_name in self._cache: del self._cache[property_name] # TODO(perezju) remove the option and make the check mandatory, but using a # single shell script to both set- and getprop. if check and value != self.GetProp(property_name): raise device_errors.CommandFailedError( 'Unable to set property %r on the device to %r' % (property_name, value), str(self))
def _RunShellCommandImpl(self, cmd, check_return=False, root=False, timeout=None): """Implementation of RunShellCommand. This is split from RunShellCommand to allow other DeviceUtils methods to call RunShellCommand without spawning a new timeout thread. TODO(jbudorick) Remove the timeout parameter once this is no longer implemented via AndroidCommands. Args: cmd: Same as for |RunShellCommand|. check_return: Same as for |RunShellCommand|. timeout: Same as for |IsOnline|. Raises: Same as for |RunShellCommand|. Returns: Same as for |RunShellCommand|. """ if isinstance(cmd, list): cmd = ' '.join(cmd) if root and not self.HasRoot(): cmd = 'su -c %s' % cmd if check_return: code, output = self.old_interface.GetShellCommandStatusAndOutput( cmd, timeout_time=timeout) if int(code) != 0: raise device_errors.CommandFailedError( cmd, 'Nonzero exit code (%d)' % code) else: output = self.old_interface.RunShellCommand(cmd, timeout_time=timeout) return output
def GetExternalStoragePath(self, timeout=None, retries=None): """Get the device's path to its SD card. Args: timeout: timeout in seconds retries: number of retries Returns: The device's path to its SD card. Raises: CommandFailedError if the external storage path could not be determined. CommandTimeoutError on timeout. DeviceUnreachableError on missing device. """ if 'external_storage' in self._cache: return self._cache['external_storage'] value = self.RunShellCommand('echo $EXTERNAL_STORAGE', single_line=True, check_return=True) if not value: raise device_errors.CommandFailedError( '$EXTERNAL_STORAGE is not set', str(self)) self._cache['external_storage'] = value return value
def _RunAdbCmd(cls, arg_list, timeout=None, retries=None, check_error=True): cmd = ['adb'] + arg_list exit_code, output = cmd_helper.GetCmdStatusAndOutput(cmd) if exit_code != 0: raise device_errors.CommandFailedError( cmd, 'returned non-zero exit code %s, output: %s' % (exit_code, output)) # This catches some errors, including when the device drops offline; # unfortunately adb is very inconsistent with error reporting so many # command failures present differently. if check_error and output[:len('error:')] == 'error:': raise device_errors.CommandFailedError(arg_list, output) return output
def KillAll(self, process_name, signum=9, as_root=False, blocking=False, timeout=None, retries=None): """Kill all processes with the given name on the device. Args: process_name: A string containing the name of the process to kill. signum: An integer containing the signal number to send to kill. Defaults to 9 (SIGKILL). as_root: A boolean indicating whether the kill should be executed with root privileges. blocking: A boolean indicating whether we should wait until all processes with the given |process_name| are dead. timeout: timeout in seconds retries: number of retries Raises: CommandFailedError if no process was killed. CommandTimeoutError on timeout. DeviceUnreachableError on missing device. """ pids = self.old_interface.ExtractPid(process_name) if len(pids) == 0: raise device_errors.CommandFailedError('No process "%s"' % process_name, device=str(self)) if blocking: total_killed = self.old_interface.KillAllBlocking( process_name, signum=signum, with_su=as_root, timeout_sec=timeout) else: total_killed = self.old_interface.KillAll(process_name, signum=signum, with_su=as_root) if total_killed == 0: raise device_errors.CommandFailedError('Failed to kill "%s"' % process_name, device=str(self))
def GetPowerData(self, timeout=None, retries=None): """Get power data for device. Args: timeout: timeout in seconds retries: number of retries Returns: Dict of power data, keyed on package names. { package_name: { 'uid': uid, 'data': [1,2,3] }, } """ if 'uids' not in self._cache: self._cache['uids'] = {} dumpsys_output = self._device.RunShellCommand( ['dumpsys', 'batterystats', '-c'], check_return=True, large_output=True) csvreader = csv.reader(dumpsys_output) pwi_entries = collections.defaultdict(list) for entry in csvreader: if entry[_DUMP_VERSION_INDEX] not in ['8', '9']: # Wrong dumpsys version. raise device_errors.DeviceVersionError( 'Dumpsys version must be 8 or 9. %s found.' % entry[_DUMP_VERSION_INDEX]) if _ROW_TYPE_INDEX < len( entry) and entry[_ROW_TYPE_INDEX] == 'uid': current_package = entry[_PACKAGE_NAME_INDEX] if (self._cache['uids'].get(current_package) and self._cache['uids'].get(current_package) != entry[_PACKAGE_UID_INDEX]): raise device_errors.CommandFailedError( 'Package %s found multiple times with differnt UIDs %s and %s' % (current_package, self._cache['uids'][current_package], entry[_PACKAGE_UID_INDEX])) self._cache['uids'][current_package] = entry[ _PACKAGE_UID_INDEX] elif (_PWI_POWER_CONSUMPTION_INDEX < len(entry) and entry[_ROW_TYPE_INDEX] == 'pwi' and entry[_PWI_AGGREGATION_INDEX] == 'l'): pwi_entries[entry[_PWI_UID_INDEX]].append( float(entry[_PWI_POWER_CONSUMPTION_INDEX])) return { p: { 'uid': uid, 'data': pwi_entries[uid] } for p, uid in self._cache['uids'].iteritems() }
def Install(self, apk_path, reinstall=False, timeout=None, retries=None): """Install an APK. Noop if an identical APK is already installed. Args: apk_path: A string containing the path to the APK to install. reinstall: A boolean indicating if we should keep any existing app data. timeout: timeout in seconds retries: number of retries Raises: CommandFailedError if the installation fails. CommandTimeoutError if the installation times out. DeviceUnreachableError on missing device. """ package_name = apk_helper.GetPackageName(apk_path) device_path = self.old_interface.GetApplicationPath(package_name) if device_path is not None: files_changed = self.old_interface.GetFilesChanged( apk_path, device_path, ignore_filenames=True) if len(files_changed) > 0: should_install = True if not reinstall: out = self.old_interface.Uninstall(package_name) for line in out.splitlines(): if 'Failure' in line: raise device_errors.CommandFailedError( line.strip(), device=str(self)) else: should_install = False else: should_install = True if should_install: try: out = self.old_interface.Install(apk_path, reinstall=reinstall) for line in out.splitlines(): if 'Failure' in line: raise device_errors.CommandFailedError( line.strip(), device=str(self)) except AssertionError as e: raise device_errors.CommandFailedError( str(e), device=str(self)), None, sys.exc_info()[2]
def Root(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES): """Restarts the adbd daemon with root permissions, if possible. Args: timeout: (optional) Timeout per try in seconds. retries: (optional) Number of retries to attempt. """ output = self._DeviceAdbCmd(['root'], timeout, retries) if 'cannot' in output: raise device_errors.CommandFailedError(['root'], output)
def FinishProvisioning(device, options): if options.min_battery_level is not None: try: battery = battery_utils.BatteryUtils(device) battery.ChargeDeviceToLevel(options.min_battery_level) except device_errors.CommandFailedError: logging.exception('Unable to charge device to specified level.') if options.max_battery_temp is not None: try: battery = battery_utils.BatteryUtils(device) battery.LetBatteryCoolToTemperature(options.max_battery_temp) except device_errors.CommandFailedError: logging.exception( 'Unable to let battery cool to specified temperature.') def _set_and_verify_date(): if (device.build_version_sdk >= constants.ANDROID_SDK_VERSION_CODES.MARSHMALLOW): date_format = '%m%d%H%M%Y.%S' set_date_command = ['date'] else: date_format = '%Y%m%d.%H%M%S' set_date_command = ['date', '-s'] strgmtime = time.strftime(date_format, time.gmtime()) set_date_command.append(strgmtime) device.RunShellCommand(set_date_command, as_root=True, check_return=True) device_time = device.RunShellCommand(['date', '+"%Y%m%d.%H%M%S"'], as_root=True, single_line=True).replace( '"', '') device_time = datetime.datetime.strptime(device_time, "%Y%m%d.%H%M%S") correct_time = datetime.datetime.strptime(strgmtime, date_format) tdelta = (correct_time - device_time).seconds if tdelta <= 1: logging.info('Date/time successfully set on %s', device) return True else: return False # Sometimes the date is not set correctly on the devices. Retry on failure. if not timeout_retry.WaitFor( _set_and_verify_date, wait_period=1, max_tries=2): raise device_errors.CommandFailedError('Failed to set date & time.', device_serial=str(device)) props = device.RunShellCommand('getprop', check_return=True) for prop in props: logging.info(' %s' % prop) if options.auto_reconnect: _PushAndLaunchAdbReboot(device, options.target)
def Install(self, apk_path, reinstall=False, timeout=None, retries=None): """Install an APK. Noop if an identical APK is already installed. Args: apk_path: A string containing the path to the APK to install. reinstall: A boolean indicating if we should keep any existing app data. timeout: Same as for |IsOnline|. retries: Same as for |IsOnline|. Raises: CommandFailedError if the installation fails. CommandTimeoutError if the installation times out. """ package_name = apk_helper.GetPackageName(apk_path) device_path = self.old_interface.GetApplicationPath(package_name) if device_path is not None: files_changed = self.old_interface.GetFilesChanged( apk_path, device_path, ignore_filenames=True) if len(files_changed) > 0: should_install = True if not reinstall: out = self.old_interface.Uninstall(package_name) for line in out.splitlines(): if 'Failure' in line: raise device_errors.CommandFailedError( ['adb', 'uninstall', package_name], line.strip()) else: should_install = False else: should_install = True if should_install: try: out = self.old_interface.Install(apk_path, reinstall=reinstall) for line in out.splitlines(): if 'Failure' in line: raise device_errors.CommandFailedError( ['adb', 'install', apk_path], line.strip()) except AssertionError as e: raise device_errors.CommandFailedError( ['adb', 'install', apk_path], str(e))
def EnableRoot(self, timeout=None, retries=None): """ Restarts adbd with root privileges. Args: timeout: Same as for |IsOnline|. retries: Same as for |IsOnline|. Raises: CommandFailedError if root could not be enabled. """ if not self.old_interface.EnableAdbRoot(): raise device_errors.CommandFailedError('adb root', 'Could not enable root.')
def _GetExternalStoragePathImpl(self): if 'external_storage' in self._cache: return self._cache['external_storage'] value = self._RunShellCommandImpl('echo $EXTERNAL_STORAGE', single_line=True, check_return=True) if not value: raise device_errors.CommandFailedError( '$EXTERNAL_STORAGE is not set', str(self)) self._cache['external_storage'] = value return value
def EnableRoot(self, timeout=None, retries=None): """Restarts adbd with root privileges. Args: timeout: timeout in seconds retries: number of retries Raises: CommandFailedError if root could not be enabled. CommandTimeoutError on timeout. """ if not self.old_interface.EnableAdbRoot(): raise device_errors.CommandFailedError('Could not enable root.', device=str(self))
def GetExternalStoragePath(self, timeout=None, retries=None): """ Get the device's path to its SD card. Args: timeout: Same as for |IsOnline|. retries: Same as for |IsOnline|. Returns: The device's path to its SD card. """ try: return self.old_interface.GetExternalStorage() except AssertionError as e: raise device_errors.CommandFailedError(str(e))
def _RunShellCommandImpl(self, cmd, check_return=False, cwd=None, env=None, as_root=False, single_line=False, timeout=None): def env_quote(key, value): if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): raise KeyError('Invalid shell variable name %r' % key) # using double quotes here to allow interpolation of shell variables return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) if not isinstance(cmd, basestring): cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) if as_root and not self._HasRootImpl(): cmd = 'su -c %s' % cmd if env: env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) cmd = '%s %s' % (env, cmd) if cwd: cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) if timeout is None: timeout = self._default_timeout try: # TODO(perezju) still need to make sure that we call a version of # adb.Shell without a timeout-and-retries wrapper. output = self.adb.Shell(cmd, expect_rc=0, timeout=timeout, retries=0) except device_errors.AdbShellCommandFailedError as e: if check_return: raise else: output = e.output output = output.splitlines() if single_line: if len(output) != 1: msg = 'exactly one line of output expected, but got: %s' raise device_errors.CommandFailedError(msg % output) return output[0] else: return output
def EnableRoot(self, timeout=None, retries=None): """Restarts adbd with root privileges. Args: timeout: timeout in seconds retries: number of retries Raises: CommandFailedError if root could not be enabled. CommandTimeoutError on timeout. """ if self.IsUserBuild(): raise device_errors.CommandFailedError( 'Cannot enable root in user builds.', str(self)) if 'needs_su' in self._cache: del self._cache['needs_su'] self.adb.Root() self.adb.WaitForDevice()
def build_version_sdk(self): """Returns the build version sdk of the system as a number (e.g. 19). For version code numbers see: http://developer.android.com/reference/android/os/Build.VERSION_CODES.html For named constants see: pylib.constants.ANDROID_SDK_VERSION_CODES Raises: CommandFailedError if the build version sdk is not a number. """ value = self.GetProp('ro.build.version.sdk', cache=True) try: return int(value) except ValueError: raise device_errors.CommandFailedError( 'Invalid build version sdk: %r' % value)
def GetExternalStoragePath(self, timeout=None, retries=None): """Get the device's path to its SD card. Args: timeout: timeout in seconds retries: number of retries Returns: The device's path to its SD card. Raises: CommandFailedError if the external storage path could not be determined. CommandTimeoutError on timeout. DeviceUnreachableError on missing device. """ try: return self.old_interface.GetExternalStorage() except AssertionError as e: raise device_errors.CommandFailedError( str(e), device=str(self)), None, sys.exc_info()[2]
def GetApplicationPath(self, package, timeout=None, retries=None): """Get the path of the installed apk on the device for the given package. Args: package: Name of the package. Returns: Path to the apk on the device if it exists, None otherwise. """ output = self.RunShellCommand(['pm', 'path', package], single_line=True, check_return=True) if not output: return None if not output.startswith('package:'): raise device_errors.CommandFailedError( 'pm path returned: %r' % output, str(self)) return output[len('package:'):]
def DisableBatteryUpdates(self, timeout=None, retries=None): """ Resets battery data and makes device appear like it is not charging so that it will collect power data since last charge. Args: timeout: timeout in seconds retries: number of retries Raises: device_errors.CommandFailedError: When resetting batterystats fails to reset power values. device_errors.DeviceVersionError: If device is not L or higher. """ def battery_updates_disabled(): return self.GetCharging() is False if (self._device.build_version_sdk < constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP): raise device_errors.DeviceVersionError( 'Device must be L or higher.') self._device.RunShellCommand(['dumpsys', 'battery', 'set', 'usb', '1'], check_return=True) self._device.RunShellCommand(['dumpsys', 'battery', 'set', 'ac', '1'], check_return=True) self._device.RunShellCommand(['dumpsys', 'batterystats', '--reset'], check_return=True) battery_data = self._device.RunShellCommand( ['dumpsys', 'batterystats', '--charged', '--checkin'], check_return=True, large_output=True) ROW_TYPE_INDEX = 3 PWI_POWER_INDEX = 5 for line in battery_data: l = line.split(',') if (len(l) > PWI_POWER_INDEX and l[ROW_TYPE_INDEX] == 'pwi' and l[PWI_POWER_INDEX] != 0): raise device_errors.CommandFailedError( 'Non-zero pmi value found after reset.') self._device.RunShellCommand(['dumpsys', 'battery', 'set', 'ac', '0'], check_return=True) self._device.RunShellCommand(['dumpsys', 'battery', 'set', 'usb', '0'], check_return=True) timeout_retry.WaitFor(battery_updates_disabled, wait_period=1)
def RestartServer(): """Restarts the adb server. Raises: CommandFailedError if we fail to kill or restart the server. """ def adb_killed(): return not adb_wrapper.AdbWrapper.IsServerOnline() def adb_started(): return adb_wrapper.AdbWrapper.IsServerOnline() adb_wrapper.AdbWrapper.KillServer() if not timeout_retry.WaitFor(adb_killed, wait_period=1, max_tries=5): # TODO(perezju): raise an exception after fixng http://crbug.com/442319 logging.warning('Failed to kill adb server') adb_wrapper.AdbWrapper.StartServer() if not timeout_retry.WaitFor(adb_started, wait_period=1, max_tries=5): raise device_errors.CommandFailedError('Failed to start adb server')
def PullFile(self, device_path, host_path, timeout=None, retries=None): """Pull a file from the device. Args: device_path: A string containing the absolute path of the file to pull from the device. host_path: A string containing the absolute path of the destination on the host. timeout: timeout in seconds retries: number of retries Raises: CommandFailedError on failure. CommandTimeoutError on timeout. """ try: self.old_interface.PullFileFromDevice(device_path, host_path) except AssertionError as e: raise device_errors.CommandFailedError( str(e), device=str(self)), None, sys.exc_info()[2]
def StartActivity(self, intent, blocking=False, trace_file_name=None, force_stop=False, timeout=None, retries=None): """Start package's activity on the device. Args: intent: An Intent to send. blocking: A boolean indicating whether we should wait for the activity to finish launching. trace_file_name: If present, a string that both indicates that we want to profile the activity and contains the path to which the trace should be saved. force_stop: A boolean indicating whether we should stop the activity before starting it. timeout: timeout in seconds retries: number of retries Raises: CommandFailedError if the activity could not be started. CommandTimeoutError on timeout. DeviceUnreachableError on missing device. """ single_category = (intent.category[0] if isinstance( intent.category, list) else intent.category) output = self.old_interface.StartActivity( intent.package, intent.activity, wait_for_completion=blocking, action=intent.action, category=single_category, data=intent.data, extras=intent.extras, trace_file_name=trace_file_name, force_stop=force_stop, flags=intent.flags) for l in output: if l.startswith('Error:'): raise device_errors.CommandFailedError(l, str(self))