def adb_connect(device, timeout=None): _check_env() command = "adb connect " + device if ":" in device: port = device.split(':')[-1] logger.debug(command) output, _ = check_output(command, shell=True, timeout=timeout) logger.debug(output) #### due to a rare adb bug sometimes an extra :5555 is appended to the IP address if output.find('{}:{}'.format(port, port)) != -1: logger.debug('ADB BUG with extra port') command = "adb connect " + device.replace(':{}'.format(port), '') tries = 0 output = None while not poll_for_file(device, "/proc/cpuinfo"): logger.debug("adb connect failed, retrying now...") tries += 1 if tries > MAX_TRIES: raise DeviceError('Cannot connect to adb server on the device.') logger.debug(command) output, _ = check_output(command, shell=True, timeout=timeout) time.sleep(10) if tries and output.find('connected to') == -1: raise DeviceError('Could not connect to {}'.format(device))
def execute(self, command, timeout=None, check_exit_code=True, as_root=False, strip_colors=True): try: with self.lock: if self.connection_lost: logger.debug('Attempting to reconnect...') self.reconnect() self.connection_lost = False output = self._execute_and_wait_for_prompt( command, timeout, as_root, strip_colors) if check_exit_code: exit_code_text = self._execute_and_wait_for_prompt( 'echo $?', strip_colors=strip_colors, log=False) try: exit_code = int(exit_code_text.split()[0]) if exit_code: message = 'Got exit code {}\nfrom: {}\nOUTPUT: {}' raise DeviceError( message.format(exit_code, command, output)) except (ValueError, IndexError): logger.warning( 'Could not get exit code for "{}",\ngot: "{}"'. format(command, exit_code_text)) return output except EOF: self.connection_lost = True raise DeviceError('Connection dropped.')
def connect(self): # NOQA pylint: disable=R0912 iteration_number = 0 max_iterations = self.ready_timeout / self.delay available = False self.logger.debug('Polling for device {}...'.format(self.adb_name)) while iteration_number < max_iterations: devices = adb_list_devices() if self.adb_name: for device in devices: if device.name == self.adb_name and device.status != 'offline': available = True else: # adb_name not set if len(devices) == 1: available = True elif len(devices) > 1: raise DeviceError( 'More than one device is connected and adb_name is not set.' ) if available: break else: time.sleep(self.delay) iteration_number += 1 else: raise DeviceError('Could not boot {} ({}).'.format( self.name, self.adb_name)) while iteration_number < max_iterations: available = (1 == int('0' + adb_shell(self.adb_name, 'getprop sys.boot_completed', timeout=self.default_timeout))) if available: break else: time.sleep(self.delay) iteration_number += 1 else: raise DeviceError('Could not boot {} ({}).'.format( self.name, self.adb_name)) if self._just_rebooted: self.logger.debug('Waiting for boot to complete...') # On some devices, adb connection gets reset some time after booting. # This causes errors during execution. To prevent this, open a shell # session and wait for it to be killed. Once its killed, give adb # enough time to restart, and then the device should be ready. # TODO: This is more of a work-around rather than an actual solution. # Need to figure out what is going on the "proper" way of handling it. try: adb_shell(self.adb_name, '', timeout=20) time.sleep(5) # give adb time to re-initialize except TimeoutError: pass # timed out waiting for the session to be killed -- assume not going to be. self.logger.debug('Boot completed.') self._just_rebooted = False self._is_ready = True
def execute(self, command, timeout=default_timeout, check_exit_code=True, background=False, as_root=False, busybox=False, **kwargs): """ Execute the specified command on the device using adb. Parameters: :param command: The command to be executed. It should appear exactly as if you were typing it into a shell. :param timeout: Time, in seconds, to wait for adb to return before aborting and raising an error. Defaults to ``AndroidDevice.default_timeout``. :param check_exit_code: If ``True``, the return code of the command on the Device will be check and exception will be raised if it is not 0. Defaults to ``True``. :param background: If ``True``, will execute adb in a subprocess, and will return immediately, not waiting for adb to return. Defaults to ``False`` :param busybox: If ``True``, will use busybox to execute the command. Defaults to ``False``. Added in version 2.1.3 .. note:: The device must be rooted to be able to use busybox. :param as_root: If ``True``, will attempt to execute command in privileged mode. The device must be rooted, otherwise an error will be raised. Defaults to ``False``. Added in version 2.1.3 :returns: If ``background`` parameter is set to ``True``, the subprocess object will be returned; otherwise, the contents of STDOUT from the device will be returned. :raises: DeviceError if adb timed out or if the command returned non-zero exit code on the device, or if attempting to execute a command in privileged mode on an unrooted device. """ self._check_ready() if as_root and not self.is_rooted: raise DeviceError( 'Attempting to execute "{}" as root on unrooted device.'. format(command)) if busybox: if not self.is_rooted: DeviceError('Attempting to execute "{}" with busybox. '.format( command) + 'Busybox can only be deployed to rooted devices.') command = ' '.join([self.busybox, command]) if background: return adb_background_shell(self.adb_name, command, as_root=as_root) else: return adb_shell(self.adb_name, command, timeout, check_exit_code, as_root)
def execute(self, command, timeout=default_timeout, check_exit_code=True, background=False, as_root=False, strip_colors=True, **kwargs): """ Execute the specified command on the device using adb. Parameters: :param command: The command to be executed. It should appear exactly as if you were typing it into a shell. :param timeout: Time, in seconds, to wait for adb to return before aborting and raising an error. Defaults to ``AndroidDevice.default_timeout``. :param check_exit_code: If ``True``, the return code of the command on the Device will be check and exception will be raised if it is not 0. Defaults to ``True``. :param background: If ``True``, will execute create a new ssh shell rather than using the default session and will return it immediately. If this is ``True``, ``timeout``, ``strip_colors`` and (obvisously) ``check_exit_code`` will be ignored; also, with this, ``as_root=True`` is only valid if ``username`` for the device was set to ``root``. :param as_root: If ``True``, will attempt to execute command in privileged mode. The device must be rooted, otherwise an error will be raised. Defaults to ``False``. Added in version 2.1.3 :returns: If ``background`` parameter is set to ``True``, the subprocess object will be returned; otherwise, the contents of STDOUT from the device will be returned. """ self._check_ready() try: if background: if as_root and self.username != 'root': raise DeviceError( 'Cannot execute in background with as_root=True unless user is root.' ) return self.shell.background(command) else: # If we're already the root user, don't bother with sudo if self._is_root_user: as_root = False return self.shell.execute(command, timeout, check_exit_code, as_root, strip_colors) except CalledProcessError as e: raise DeviceError(e)
def adb_shell(device, command, timeout=None, check_exit_code=False, as_root=False): # NOQA _check_env() if as_root: command = 'echo "{}" | su'.format(escape_double_quotes(command)) device_string = '-s {}'.format(device) if device else '' full_command = 'adb {} shell "{}"'.format(device_string, escape_double_quotes(command)) logger.debug(full_command) if check_exit_code: actual_command = "adb {} shell '({}); echo $?'".format( device_string, escape_single_quotes(command)) raw_output, error = check_output(actual_command, timeout, shell=True) if raw_output: try: output, exit_code, _ = raw_output.rsplit('\n', 2) except ValueError: exit_code, _ = raw_output.rsplit('\n', 1) output = '' else: # raw_output is empty exit_code = '969696' # just because output = '' exit_code = exit_code.strip() if exit_code.isdigit(): if int(exit_code): message = 'Got exit code {}\nfrom: {}\nSTDOUT: {}\nSTDERR: {}'.format( exit_code, full_command, output, error) raise DeviceError(message) elif am_start_error.findall(output): message = 'Could not start activity; got the following:' message += '\n{}'.format(am_start_error.findall(output)[0]) raise DeviceError(message) else: # not all digits if am_start_error.findall(output): message = 'Could not start activity; got the following:' message += '\n{}'.format(am_start_error.findall(output)[0]) raise DeviceError(message) else: raise DeviceError( 'adb has returned early; did not get an exit code. Was kill-server invoked?' ) else: # do not check exit code output, _ = check_output(full_command, timeout, shell=True) return output
def boot(self, hard=False, **kwargs): if hard: self.hard_reset() else: self.reset() self.logger.debug('Waiting for device...') # Wait a fixed delay before starting polling to give the device time to # shut down, otherwise, might create the connection while it's still shutting # down resulting in subsequenct connection failing. initial_delay = 20 time.sleep(initial_delay) boot_timeout = max(self.boot_timeout - initial_delay, 10) start_time = time.time() while (time.time() - start_time) < boot_timeout: try: s = socket.create_connection((self.host, self.port), timeout=5) s.close() break except socket.timeout: pass except socket.error: time.sleep(5) else: raise DeviceError('Could not connect to {} after reboot'.format( self.host))
def ssh_get_shell(host, username, password=None, keyfile=None, port=None, timeout=10, telnet=False, original_prompt=None): _check_env() start_time = time.time() extra_login_args = {} while True: if telnet: if keyfile: raise ValueError('keyfile may not be used with a telnet connection.') conn = TelnetConnection() if original_prompt: extra_login_args['original_prompt'] = original_prompt if port is None: port = 23 else: # ssh conn = pxssh.pxssh() try: if keyfile: conn.login(host, username, ssh_key=keyfile, port=port, login_timeout=timeout, **extra_login_args) else: conn.login(host, username, password, port=port, login_timeout=timeout, **extra_login_args) break except EOF: timeout -= time.time() - start_time if timeout <= 0: message = 'Could not connect to {}; is the host name correct?' raise DeviceError(message.format(host)) time.sleep(5) conn.setwinsize(500,200) conn.sendline('') conn.prompt() conn.setecho(False) return conn
def install_apk(self, filepath, timeout=default_timeout, replace=False, allow_downgrade=False): # pylint: disable=W0221 self._check_ready() ext = os.path.splitext(filepath)[1].lower() if ext == '.apk': flags = [] if replace: flags.append('-r') # Replace existing APK if allow_downgrade: flags.append( '-d' ) # Install the APK even if a newer version is already installed if self.get_sdk_version() >= 23: flags.append('-g') # Grant all runtime permissions self.logger.debug("Replace APK = {}, ADB flags = '{}'".format( replace, ' '.join(flags))) return adb_command(self.adb_name, "install {} '{}'".format( ' '.join(flags), filepath), timeout=timeout) else: raise DeviceError( 'Can\'t install {}: unsupported format.'.format(filepath))
def connect(self): if not self._is_ready: if not self.adb_name: # pylint: disable=E0203 with open_serial_connection(timeout=self.timeout, port=self.port, baudrate=self.baudrate, init_dtr=0) as target: target.sendline('') self.logger.debug('Waiting for the Android prompt.') target.expect(self.android_prompt) self.logger.debug('Waiting for IP address...') wait_start_time = time.time() while True: target.sendline('ip addr list eth0') time.sleep(1) try: target.expect('inet ([1-9]\d*.\d+.\d+.\d+)', timeout=10) self.adb_name = target.match.group(1) + ':5555' # pylint: disable=W0201 break except pexpect.TIMEOUT: pass # We have our own timeout -- see below. if (time.time() - wait_start_time) > self.ready_timeout: raise DeviceError('Could not acquire IP address.') if self.adb_name in adb_list_devices(): adb_disconnect(self.adb_name) adb_connect(self.adb_name, timeout=self.timeout) super(Juno, self).connect() # wait for boot to complete etc. self._is_ready = True
def ssh_get_shell(host, username, password=None, keyfile=None, port=None, timeout=10, telnet=False): _check_env() if telnet: if keyfile: raise ConfigError( 'keyfile may not be used with a telnet connection.') conn = TelnetConnection() else: # ssh conn = pxssh.pxssh() # pylint: disable=redefined-variable-type try: if keyfile: conn.login(host, username, ssh_key=keyfile, port=port, login_timeout=timeout) else: conn.login(host, username, password, port=port, login_timeout=timeout) except EOF: raise DeviceError( 'Could not connect to {}; is the host name correct?'.format(host)) return conn
def set_sysfile_value(self, sysfile, value, verify=True, binary=False): """ Set the value of the specified sysfile. By default, the value will be checked afterwards. Can be overridden by setting ``verify`` parameter to ``False``. By default binary values will not be written correctly this can be changed by setting the ``binary`` parameter to ``True``. """ value = str(value) if binary: # Value is already string encoded, so need to decode before encoding in base64 try: value = str(value.decode('string_escape')) except ValueError as e: msg = 'Can not interpret value "{}" for "{}": {}' raise ValueError(msg.format(value, sysfile, e.message)) encoded_value = base64.b64encode(value) cmd = 'echo {} | {} base64 -d > \'{}\''.format(encoded_value, self.busybox, sysfile) else: cmd = 'echo {} > \'{}\''.format(value, sysfile) self.execute(cmd, check_exit_code=False, as_root=True) if verify: output = self.get_sysfile_value(sysfile, binary=binary) if output.strip() != value: # pylint: disable=E1103 message = 'Could not set the value of {} to {}'.format(sysfile, value) raise DeviceError(message) self._written_sysfiles.append((sysfile, binary))
def kick_off(self, command): """ Like execute but closes adb session and returns immediately, leaving the command running on the device (this is different from execute(background=True) which keeps adb connection open and returns a subprocess object). .. note:: This relies on busybox's nohup applet and so won't work on unrooted devices. Added in version 2.1.4 """ if not self.is_rooted: raise DeviceError( 'kick_off uses busybox\'s nohup applet and so can only be run a rooted device.' ) try: command = 'cd {} && busybox nohup {}'.format( self.working_directory, command) output = self.execute(command, timeout=1, as_root=True) except TimeoutError: pass else: raise ValueError( 'Background command exited before timeout; got "{}"'.format( output))
def initialize(self, context): if self.device.platform != 'android': raise DeviceError( 'nestats instrument only supports on Android devices.') apk = context.resolver.get(ApkFile(self)) self.collector = NetstatsCollector(self.device, apk) # pylint: disable=attribute-defined-outside-init self.collector.setup(force=self.force_reinstall)
def connect(self): if not self._is_ready: if self.config.adb_name: self.adb_name = self.config.adb_name # pylint: disable=attribute-defined-outside-init else: with open_serial_connection( timeout=self.config.serial_max_timeout, port=self.config.serial_device, baudrate=self.config.serial_baud) as target: # Get IP address and push the Gator and PMU logger. target.sendline( 'su' ) # as of Android v5.0.2, Linux does not boot into root shell target.sendline('netcfg') ipaddr_re = re.compile('eth0 +UP +(.+)/.+', re.MULTILINE) target.expect(ipaddr_re) output = target.after match = re.search('eth0 +UP +(.+)/.+', output) if not match: raise DeviceError('Could not get adb IP address.') ipaddr = match.group(1) # Connect to device using adb. target.expect(self.android_prompt) # pylint: disable=E1101 self.adb_name = ipaddr + ":5555" # pylint: disable=W0201 if self.adb_name in adb_list_devices(): adb_disconnect(self.adb_name) adb_connect(self.adb_name) self._is_ready = True self.execute("input keyevent 82", timeout=ADB_SHELL_TIMEOUT) self.execute("svc power stayon true", timeout=ADB_SHELL_TIMEOUT)
def set_cpu_max_frequency(self, cpu, frequency): """ Set's the minimum value for CPU frequency. Actual frequency will depend on the Governor used and may vary during execution. The value should be either an int or a string representing an integer. The Value must also be supported by the device. The available frequencies can be obtained by calling get_available_frequencies() or examining /sys/devices/system/cpu/cpuX/cpufreq/scaling_available_frequencies on the device. :raises: ConfigError if the frequency is not supported by the CPU. :raises: DeviceError if, for some reason, frequency could not be set. """ if isinstance(cpu, int): cpu = 'cpu{}'.format(cpu) available_frequencies = self.list_available_cpu_frequencies(cpu) try: value = int(frequency) if available_frequencies and value not in available_frequencies: raise DeviceError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu, value, available_frequencies)) sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_max_freq'.format(cpu) self.device.set_sysfile_value(sysfile, value) except ValueError: raise ValueError('value must be an integer; got: "{}"'.format(value))
def install_apk(self, filepath, timeout=3 * 3600): # pylint: disable=W0221 """ Install an APK on the gem5 device The APK is pushed to the device. Then the file and folder permissions are changed to ensure that the APK can be correctly installed. The APK is then installed on the device using 'pm'. """ self._check_ready() self.logger.info("Installing {}".format(filepath)) ext = os.path.splitext(filepath)[1].lower() if ext == '.apk': filename = os.path.basename(filepath) on_device_path = os.path.join('/data/local/tmp', filename) self.push_file(filepath, on_device_path) # We need to make sure that the folder permissions are set # correctly, else the APK does not install correctly. self.gem5_shell('chmod 775 /data/local/tmp') self.gem5_shell('chmod 774 {}'.format(on_device_path)) self.logger.debug( "Actually installing the APK: {}".format(on_device_path)) return self.gem5_shell("pm install {}".format(on_device_path)) else: raise DeviceError( 'Can\'t install {}: unsupported format.'.format(filepath))
def is_screen_on(self): """Returns ``True`` if the device screen is currently on, ``False`` otherwise.""" output = self.execute('dumpsys power') match = SCREEN_STATE_REGEX.search(output) if match: return boolean(match.group(1)) else: raise DeviceError('Could not establish screen state.')
def discover_idle_states(self): online_cpu = self.device.get_online_cpus(self.big_core)[0] self.big_idle_states = self.device.get_cpuidle_states(online_cpu) online_cpu = self.device.get_online_cpus(self.little_core)[0] self.little_idle_states = self.device.get_cpuidle_states(online_cpu) if not (len(self.big_idle_states) >= 2 and len(self.little_idle_states) >= 2): raise DeviceError('There do not appeart to be at least two idle states ' 'on at least one of the clusters.')
def adb_disconnect(device): _check_env() if ":5555" in device: command = "adb disconnect " + device logger.debug(command) retval = subprocess.call(command, stdout=open(os.devnull, 'wb'), shell=True) if retval: raise DeviceError('"{}" returned {}'.format(command, retval))
def uninstall(self, executable_name): on_device_executable = self.get_binary_path( executable_name, search_system_binaries=False) if not on_device_executable: raise DeviceError( "Could not uninstall {}, binary not found".format( on_device_executable)) self.delete_file(on_device_executable, as_root=self.is_rooted)
def _boot_using_uefi(self, target): self.logger.debug('Booting using UEFI.') self._wait_for_vemsd_mount(target) self._setup_before_reboot() self._perform_uefi_reboot(target) # Get to the UEFI menu. self.logger.debug('Waiting for UEFI default selection.') target.sendline('reboot') target.expect('The default boot selection will start in'.rstrip()) time.sleep(1) target.sendline(''.rstrip()) # If delete every time is specified, try to delete entry. if self.config.always_delete_uefi_entry: self._delete_uefi_entry(target, entry='workload_automation_MP') self.config.always_delete_uefi_entry = False # Specify argument to be passed specifying that psci is (or is not) enabled if self.config.psci_enable: psci_enable = ' psci=enable' else: psci_enable = '' # Identify the workload automation entry. selection_pattern = r'\[([0-9]*)\] ' try: target.expect(re.compile(selection_pattern + 'workload_automation_MP'), timeout=5) wl_menu_item = target.match.group(1) except pexpect.TIMEOUT: self._create_uefi_entry(target, psci_enable, entry_name='workload_automation_MP') # At this point the board should be rebooted so we need to retry to boot self._boot_using_uefi(target) else: # Did not time out. try: #Identify the boot manager menu item target.expect(re.compile(selection_pattern + 'Boot Manager')) boot_manager_menu_item = target.match.group(1) #Update FDT target.sendline(boot_manager_menu_item) target.expect(re.compile(selection_pattern + 'Update FDT path'), timeout=15) update_fdt_menu_item = target.match.group(1) target.sendline(update_fdt_menu_item) target.expect(re.compile(selection_pattern + 'NOR Flash .*'), timeout=15) bootmonfs_menu_item = target.match.group(1) target.sendline(bootmonfs_menu_item) target.expect('File path of the FDT blob:') target.sendline(self.config.dtb) #Return to main manu and boot from wl automation target.expect(re.compile(selection_pattern + 'Return to main menu'), timeout=15) return_to_main_menu_item = target.match.group(1) target.sendline(return_to_main_menu_item) target.sendline(wl_menu_item) except pexpect.TIMEOUT: raise DeviceError('Timed out')
def _check_ready(self): """ Check if the device is ready. As this is gem5, we just assume that the device is ready once we have connected to the gem5 simulation, and updated the prompt. """ if not self._is_ready: raise DeviceError('Device not ready.')
def _run_job(self): # pylint: disable=too-many-branches spec = self.current_job.spec if not spec.enabled: self.logger.info('Skipping workload %s (iteration %s)', spec, self.context.current_iteration) self.current_job.result.status = IterationResult.SKIPPED return self.logger.info('Running workload %s (iteration %s)', spec, self.context.current_iteration) if spec.flash: if not self.context.reboot_policy.can_reboot: raise ConfigError('Cannot flash as reboot_policy does not permit rebooting.') if not self.device.can('flash'): raise DeviceError('Device does not support flashing.') self._flash_device(spec.flash) elif not self.completed_jobs: # Never reboot on the very fist job of a run, as we would have done # the initial reboot if a reboot was needed. pass elif self.context.reboot_policy.reboot_on_each_spec and self.spec_changed: self.logger.debug('Rebooting on spec change.') self._reboot_device() elif self.context.reboot_policy.reboot_on_each_iteration: self.logger.debug('Rebooting on iteration.') self._reboot_device() instrumentation.disable_all() instrumentation.enable(spec.instrumentation) self.device.start() if self.spec_changed: self._send(signal.WORKLOAD_SPEC_START) self._send(signal.ITERATION_START) try: setup_ok = False with self._handle_errors('Setting up device parameters'): self.device.set_runtime_parameters(spec.runtime_parameters) setup_ok = True if setup_ok: with self._handle_errors('running {}'.format(spec.workload.name)): self.current_job.result.status = IterationResult.RUNNING self._run_workload_iteration(spec.workload) else: self.logger.info('\tSkipping the rest of the iterations for this spec.') spec.enabled = False except KeyboardInterrupt: self._send(signal.ITERATION_END) self._send(signal.WORKLOAD_SPEC_END) raise else: self._send(signal.ITERATION_END) if self.spec_will_change or not spec.enabled: self._send(signal.WORKLOAD_SPEC_END) finally: self.device.stop()
def install_apk(self, filepath, timeout=default_timeout): # pylint: disable=W0221 self._check_ready() ext = os.path.splitext(filepath)[1].lower() if ext == '.apk': return adb_command(self.adb_name, "install {}".format(filepath), timeout=timeout) else: raise DeviceError( 'Can\'t install {}: unsupported format.'.format(filepath))
def set_mode(self, mode): if self._has_booted: raise DeviceError('Attempting to set boot mode when already booted.') valid_modes = MODES.keys() if mode is None: mode = self.config.default_mode if mode not in valid_modes: message = 'Invalid mode: {}; must be in {}'.format(mode, valid_modes) raise ConfigError(message) self.config.mode = mode
def hard_reset(self): try: conn = KshellConnection(host=self.host, port=self.port) conn.login(self.username, self.password) conn.disable_port(self.psu) time.sleep(2) conn.enable_port(self.psu) conn.close() except Exception as e: raise DeviceError('Could not reset power: {}'.format(e))
def deploy_images(self, device, image_bundle=None, images=None): try: if image_bundle: self.deploy_image_bundle(device, image_bundle) if images: self.overlay_images(device, images) os.system('sync') except (IOError, OSError), e: msg = 'Could not deploy images to {}; got: {}' raise DeviceError(msg.format(device.microsd_mount_point, e))
def _reboot_device(self): with self._signal_wrap('BOOT'): for reboot_attempts in xrange(MAX_REBOOT_ATTEMPTS): if reboot_attempts: self.logger.info('\tRetrying...') with self._handle_errors('Rebooting device'): self.device.boot(**self.current_job.spec.boot_parameters) break else: raise DeviceError('Could not reboot device; max reboot attempts exceeded.') self.device.connect()
def boot(self, **kwargs): # NOQA mode = kwargs.get('os_mode', None) self._is_ready = False self._has_booted = False self.mode = mode self.logger.debug('Booting in {} mode'.format(self.mode)) with open_serial_connection(timeout=self.config.serial_max_timeout, port=self.config.serial_device, baudrate=self.config.serial_baud) as target: if self.config.boot_firmware == 'bootmon': self._boot_using_bootmon(target) elif self.config.boot_firmware == 'uefi': self._boot_using_uefi(target) else: message = 'Unexpected boot firmware: {}'.format(self.config.boot_firmware) raise ConfigError(message) try: target.sendline('') self.logger.debug('Waiting for the Android prompt.') target.expect(self.android_prompt, timeout=40) # pylint: disable=E1101 except pexpect.TIMEOUT: # Try a second time before giving up. self.logger.debug('Did not get Android prompt, retrying...') target.sendline('') target.expect(self.android_prompt, timeout=10) # pylint: disable=E1101 self.logger.debug('Waiting for OS to initialize...') started_waiting_time = time.time() time.sleep(20) # we know it's not going to to take less time than this. boot_completed, got_ip_address = False, False while True: try: if not boot_completed: target.sendline('getprop sys.boot_completed') boot_completed = target.expect(['0.*', '1.*'], timeout=10) if not got_ip_address: target.sendline('getprop dhcp.eth0.ipaddress') # regexes are processed in order, so ip regex has to # come first (as we only want to match new line if we # don't match the IP). We do a "not" make the logic # consistent with boot_completed. got_ip_address = not target.expect(['[1-9]\d*.\d+.\d+.\d+', '\n'], timeout=10) except pexpect.TIMEOUT: pass # We have our own timeout -- see below. if boot_completed and got_ip_address: break time.sleep(5) if (time.time() - started_waiting_time) > self.config.init_timeout: raise DeviceError('Timed out waiting for the device to initialize.') self._has_booted = True