Home | History | Annotate | Download | only in hosts
      1 # Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import datetime
      6 import errno
      7 import functools
      8 import logging
      9 import os
     10 import re
     11 import signal
     12 import stat
     13 import sys
     14 import time
     15 
     16 import common
     17 
     18 from autotest_lib.client.bin import utils as client_utils
     19 from autotest_lib.client.common_lib import android_utils
     20 from autotest_lib.client.common_lib import error
     21 from autotest_lib.client.common_lib import global_config
     22 from autotest_lib.client.common_lib.cros import dev_server
     23 from autotest_lib.client.common_lib.cros import retry
     24 from autotest_lib.server import autoserv_parser
     25 from autotest_lib.server import constants as server_constants
     26 from autotest_lib.server import utils
     27 from autotest_lib.server.cros import provision
     28 from autotest_lib.server.cros.dynamic_suite import tools
     29 from autotest_lib.server.cros.dynamic_suite import constants
     30 from autotest_lib.server.hosts import abstract_ssh
     31 from autotest_lib.server.hosts import adb_label
     32 from autotest_lib.server.hosts import base_label
     33 from autotest_lib.server.hosts import host_info
     34 from autotest_lib.server.hosts import teststation_host
     35 
     36 
     37 CONFIG = global_config.global_config
     38 
     39 ADB_CMD = 'adb'
     40 FASTBOOT_CMD = 'fastboot'
     41 SHELL_CMD = 'shell'
     42 # Some devices have no serial, then `adb serial` has output such as:
     43 # (no serial number)  device
     44 # ??????????          device
     45 DEVICE_NO_SERIAL_MSG = '(no serial number)'
     46 DEVICE_NO_SERIAL_TAG = '<NO_SERIAL>'
     47 # Regex to find an adb device. Examples:
     48 # 0146B5580B01801B    device
     49 # 018e0ecb20c97a62    device
     50 # 172.22.75.141:5555  device
     51 # localhost:22        device
     52 DEVICE_FINDER_REGEX = (r'^(?P<SERIAL>([\w-]+)|((tcp:)?' +
     53                        '\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}([:]5555)?)|' +
     54                        '((tcp:)?localhost([:]22)?)|' +
     55                        re.escape(DEVICE_NO_SERIAL_MSG) +
     56                        r')[ \t]+(?:device|fastboot)')
     57 CMD_OUTPUT_PREFIX = 'ADB_CMD_OUTPUT'
     58 CMD_OUTPUT_REGEX = ('(?P<OUTPUT>[\s\S]*)%s:(?P<EXIT_CODE>\d{1,3})' %
     59                     CMD_OUTPUT_PREFIX)
     60 RELEASE_FILE = 'ro.build.version.release'
     61 BOARD_FILE = 'ro.product.device'
     62 SDK_FILE = 'ro.build.version.sdk'
     63 LOGCAT_FILE_FMT = 'logcat_%s.log'
     64 TMP_DIR = '/data/local/tmp'
     65 # Regex to pull out file type, perms and symlink. Example:
     66 # lrwxrwx--- 1 6 root system 2015-09-12 19:21 blah_link -> ./blah
     67 FILE_INFO_REGEX = '^(?P<TYPE>[dl-])(?P<PERMS>[rwx-]{9})'
     68 FILE_SYMLINK_REGEX = '^.*-> (?P<SYMLINK>.+)'
     69 # List of the perm stats indexed by the order they are listed in the example
     70 # supplied above.
     71 FILE_PERMS_FLAGS = [stat.S_IRUSR, stat.S_IWUSR, stat.S_IXUSR,
     72                     stat.S_IRGRP, stat.S_IWGRP, stat.S_IXGRP,
     73                     stat.S_IROTH, stat.S_IWOTH, stat.S_IXOTH]
     74 
     75 # Default maximum number of seconds to wait for a device to be down.
     76 DEFAULT_WAIT_DOWN_TIME_SECONDS = 10
     77 # Default maximum number of seconds to wait for a device to be up.
     78 DEFAULT_WAIT_UP_TIME_SECONDS = 300
     79 # Maximum number of seconds to wait for a device to be up after it's wiped.
     80 WAIT_UP_AFTER_WIPE_TIME_SECONDS = 1200
     81 
     82 # Default timeout for retrying adb/fastboot command.
     83 DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS = 10
     84 
     85 OS_TYPE_ANDROID = 'android'
     86 OS_TYPE_BRILLO = 'brillo'
     87 
     88 # Regex to parse build name to get the detailed build information.
     89 BUILD_REGEX = ('(?P<BRANCH>([^/]+))/(?P<BUILD_TARGET>([^/]+))-'
     90                '(?P<BUILD_TYPE>([^/]+))/(?P<BUILD_ID>([^/]+))')
     91 # Regex to parse devserver url to get the detailed build information. Sample
     92 # url: http://$devserver:8080/static/branch/target/build_id
     93 DEVSERVER_URL_REGEX = '.*/%s/*' % BUILD_REGEX
     94 
     95 ANDROID_IMAGE_FILE_FMT = '%(build_target)s-img-%(build_id)s.zip'
     96 
     97 BRILLO_VENDOR_PARTITIONS_FILE_FMT = (
     98         '%(build_target)s-vendor_partitions-%(build_id)s.zip')
     99 AUTOTEST_SERVER_PACKAGE_FILE_FMT = (
    100         '%(build_target)s-autotest_server_package-%(build_id)s.tar.bz2')
    101 ADB_DEVICE_PREFIXES = ['product:', 'model:', 'device:']
    102 
    103 # Command to provision a Brillo device.
    104 # os_image_dir: The full path of the directory that contains all the Android image
    105 # files (from the image zip file).
    106 # vendor_partition_dir: The full path of the directory that contains all the
    107 # Brillo vendor partitions, and provision-device script.
    108 BRILLO_PROVISION_CMD = (
    109         'sudo ANDROID_PROVISION_OS_PARTITIONS=%(os_image_dir)s '
    110         'ANDROID_PROVISION_VENDOR_PARTITIONS=%(vendor_partition_dir)s '
    111         '%(vendor_partition_dir)s/provision-device')
    112 
    113 # Default timeout in minutes for fastboot commands.
    114 DEFAULT_FASTBOOT_RETRY_TIMEOUT_MIN = 10
    115 
    116 # Default permissions for files/dirs copied from the device.
    117 _DEFAULT_FILE_PERMS = 0o600
    118 _DEFAULT_DIR_PERMS = 0o700
    119 
    120 # Constants for getprop return value for a given property.
    121 PROPERTY_VALUE_TRUE = '1'
    122 
    123 # Timeout used for retrying installing apk. After reinstall apk failed, we try
    124 # to reboot the device and try again.
    125 APK_INSTALL_TIMEOUT_MIN = 5
    126 
    127 # The amount of time to wait for package verification to be turned off.
    128 DISABLE_PACKAGE_VERIFICATION_TIMEOUT_MIN = 1
    129 
    130 # Directory where (non-Brillo) Android stores tombstone crash logs.
    131 ANDROID_TOMBSTONE_CRASH_LOG_DIR = '/data/tombstones'
    132 # Directory where Brillo stores crash logs for native (non-Java) crashes.
    133 BRILLO_NATIVE_CRASH_LOG_DIR = '/data/misc/crash_reporter/crash'
    134 
    135 # A specific string value to return when a timeout has occurred.
    136 TIMEOUT_MSG = 'TIMEOUT_OCCURRED'
    137 
    138 class AndroidInstallError(error.InstallError):
    139     """Generic error for Android installation related exceptions."""
    140 
    141 
    142 class ADBHost(abstract_ssh.AbstractSSHHost):
    143     """This class represents a host running an ADB server."""
    144 
    145     VERSION_PREFIX = provision.ANDROID_BUILD_VERSION_PREFIX
    146     _LABEL_FUNCTIONS = []
    147     _DETECTABLE_LABELS = []
    148     label_decorator = functools.partial(utils.add_label_detector,
    149                                         _LABEL_FUNCTIONS,
    150                                         _DETECTABLE_LABELS)
    151 
    152     _parser = autoserv_parser.autoserv_parser
    153 
    154     # Minimum build id that supports server side packaging. Older builds may
    155     # not have server side package built or with Autotest code change to support
    156     # server-side packaging.
    157     MIN_VERSION_SUPPORT_SSP = CONFIG.get_config_value(
    158             'AUTOSERV', 'min_launch_control_build_id_support_ssp', type=int)
    159 
    160     @staticmethod
    161     def check_host(host, timeout=10):
    162         """
    163         Check if the given host is an adb host.
    164 
    165         If SSH connectivity can't be established, check_host will try to use
    166         user 'adb' as well. If SSH connectivity still can't be established
    167         then the original SSH user is restored.
    168 
    169         @param host: An ssh host representing a device.
    170         @param timeout: The timeout for the run command.
    171 
    172 
    173         @return: True if the host device has adb.
    174 
    175         @raises AutoservRunError: If the command failed.
    176         @raises AutoservSSHTimeout: Ssh connection has timed out.
    177         """
    178         # host object may not have user attribute if it's a LocalHost object.
    179         current_user = host.user if hasattr(host, 'user') else None
    180         try:
    181             if not (host.hostname == 'localhost' or
    182                     host.verify_ssh_user_access()):
    183                 host.user = 'adb'
    184             result = host.run(
    185                     'test -f %s' % server_constants.ANDROID_TESTER_FILEFLAG,
    186                     timeout=timeout)
    187         except (error.GenericHostRunError, error.AutoservSSHTimeout):
    188             if current_user is not None:
    189                 host.user = current_user
    190             return False
    191         return result.exit_status == 0
    192 
    193 
    194     def _initialize(self, hostname='localhost', serials=None,
    195                     adb_serial=None, fastboot_serial=None,
    196                     teststation=None, *args, **dargs):
    197         """Initialize an ADB Host.
    198 
    199         This will create an ADB Host. Hostname should always refer to the
    200         test station connected to an Android DUT. This will be the DUT
    201         to test with.  If there are multiple, serial must be specified or an
    202         exception will be raised.
    203 
    204         @param hostname: Hostname of the machine running ADB.
    205         @param serials: DEPRECATED (to be removed)
    206         @param adb_serial: An ADB device serial. If None, assume a single
    207                            device is attached (and fail otherwise).
    208         @param fastboot_serial: A fastboot device serial. If None, defaults to
    209                                 the ADB serial (or assumes a single device if
    210                                 the latter is None).
    211         @param teststation: The teststation object ADBHost should use.
    212         """
    213         # Sets up the is_client_install_supported field.
    214         super(ADBHost, self)._initialize(hostname=hostname,
    215                                          is_client_install_supported=False,
    216                                          *args, **dargs)
    217 
    218         self.tmp_dirs = []
    219         self.labels = base_label.LabelRetriever(adb_label.ADB_LABELS)
    220         adb_serial = adb_serial or self._afe_host.attributes.get('serials')
    221         fastboot_serial = (fastboot_serial or
    222                 self._afe_host.attributes.get('fastboot_serial'))
    223 
    224         self.adb_serial = adb_serial
    225         if adb_serial:
    226             adb_prefix = any(adb_serial.startswith(p)
    227                              for p in ADB_DEVICE_PREFIXES)
    228             self.fastboot_serial = (fastboot_serial or
    229                     ('tcp:%s' % adb_serial.split(':')[0] if
    230                     ':' in adb_serial and not adb_prefix else adb_serial))
    231             self._use_tcpip = ':' in adb_serial and not adb_prefix
    232         else:
    233             self.fastboot_serial = fastboot_serial or adb_serial
    234             self._use_tcpip = False
    235         self.teststation = (teststation if teststation
    236                 else teststation_host.create_teststationhost(
    237                         hostname=hostname,
    238                         user=self.user,
    239                         password=self.password,
    240                         port=self.port
    241                 ))
    242 
    243         msg ='Initializing ADB device on host: %s' % hostname
    244         if self.adb_serial:
    245             msg += ', ADB serial: %s' % self.adb_serial
    246         if self.fastboot_serial:
    247             msg += ', fastboot serial: %s' % self.fastboot_serial
    248         logging.debug(msg)
    249 
    250         self._os_type = None
    251 
    252 
    253     def _connect_over_tcpip_as_needed(self):
    254         """Connect to the ADB device over TCP/IP if so configured."""
    255         if not self._use_tcpip:
    256             return
    257         logging.debug('Connecting to device over TCP/IP')
    258         self.adb_run('connect %s' % self.adb_serial)
    259 
    260 
    261     def _restart_adbd_with_root_permissions(self):
    262         """Restarts the adb daemon with root permissions."""
    263         @retry.retry(error.GenericHostRunError, timeout_min=20/60.0,
    264                      delay_sec=1)
    265         def run_adb_root():
    266             """Run command `adb root`."""
    267             self.adb_run('root')
    268 
    269         # adb command may flake with error "device not found". Retry the root
    270         # command to reduce the chance of flake.
    271         run_adb_root()
    272         # TODO(ralphnathan): Remove this sleep once b/19749057 is resolved.
    273         time.sleep(1)
    274         self._connect_over_tcpip_as_needed()
    275         self.adb_run('wait-for-device')
    276 
    277 
    278     def _set_tcp_port(self):
    279         """Ensure the device remains in tcp/ip mode after a reboot."""
    280         if not self._use_tcpip:
    281             return
    282         port = self.adb_serial.split(':')[-1]
    283         self.run('setprop persist.adb.tcp.port %s' % port)
    284 
    285 
    286     def _reset_adbd_connection(self):
    287         """Resets adbd connection to the device after a reboot/initialization"""
    288         self._connect_over_tcpip_as_needed()
    289         self._restart_adbd_with_root_permissions()
    290         self._set_tcp_port()
    291 
    292 
    293     # pylint: disable=missing-docstring
    294     def adb_run(self, command, **kwargs):
    295         """Runs an adb command.
    296 
    297         This command will launch on the test station.
    298 
    299         Refer to _device_run method for docstring for parameters.
    300         """
    301         # Suppresses 'adb devices' from printing to the logs, which often
    302         # causes large log files.
    303         if command == "devices":
    304             kwargs['verbose'] = False
    305         return self._device_run(ADB_CMD, command, **kwargs)
    306 
    307 
    308     # pylint: disable=missing-docstring
    309     def fastboot_run(self, command, **kwargs):
    310         """Runs an fastboot command.
    311 
    312         This command will launch on the test station.
    313 
    314         Refer to _device_run method for docstring for parameters.
    315         """
    316         return self._device_run(FASTBOOT_CMD, command, **kwargs)
    317 
    318 
    319     # pylint: disable=missing-docstring
    320     @retry.retry(error.GenericHostRunError,
    321                  timeout_min=DEFAULT_FASTBOOT_RETRY_TIMEOUT_MIN)
    322     def _fastboot_run_with_retry(self, command, **kwargs):
    323         """Runs an fastboot command with retry.
    324 
    325         This command will launch on the test station.
    326 
    327         Refer to _device_run method for docstring for parameters.
    328         """
    329         return self.fastboot_run(command, **kwargs)
    330 
    331 
    332     def _log_adb_pid(self):
    333         """Log the pid of adb server.
    334 
    335         adb's server is known to have bugs and randomly restart. BY logging
    336         the server's pid it will allow us to better debug random adb failures.
    337         """
    338         adb_pid = self.teststation.run('pgrep -f "adb.*server"')
    339         logging.debug('ADB Server PID: %s', adb_pid.stdout)
    340 
    341 
    342     def _device_run(self, function, command, shell=False,
    343                     timeout=3600, ignore_status=False, ignore_timeout=False,
    344                     stdout=utils.TEE_TO_LOGS, stderr=utils.TEE_TO_LOGS,
    345                     connect_timeout=30, options='', stdin=None, verbose=True,
    346                     require_sudo=False, args=()):
    347         """Runs a command named `function` on the test station.
    348 
    349         This command will launch on the test station.
    350 
    351         @param command: Command to run.
    352         @param shell: If true the command runs in the adb shell otherwise if
    353                       False it will be passed directly to adb. For example
    354                       reboot with shell=False will call 'adb reboot'. This
    355                       option only applies to function adb.
    356         @param timeout: Time limit in seconds before attempting to
    357                         kill the running process. The run() function
    358                         will take a few seconds longer than 'timeout'
    359                         to complete if it has to kill the process.
    360         @param ignore_status: Do not raise an exception, no matter
    361                               what the exit code of the command is.
    362         @param ignore_timeout: Bool True if command timeouts should be
    363                                ignored.  Will return None on command timeout.
    364         @param stdout: Redirect stdout.
    365         @param stderr: Redirect stderr.
    366         @param connect_timeout: Connection timeout (in seconds)
    367         @param options: String with additional ssh command options
    368         @param stdin: Stdin to pass (a string) to the executed command
    369         @param require_sudo: True to require sudo to run the command. Default is
    370                              False.
    371         @param args: Sequence of strings to pass as arguments to command by
    372                      quoting them in " and escaping their contents if
    373                      necessary.
    374 
    375         @returns a CMDResult object.
    376         """
    377         if function == ADB_CMD:
    378             serial = self.adb_serial
    379         elif function == FASTBOOT_CMD:
    380             serial = self.fastboot_serial
    381         else:
    382             raise NotImplementedError('Mode %s is not supported' % function)
    383 
    384         if function != ADB_CMD and shell:
    385             raise error.CmdError('shell option is only applicable to `adb`.')
    386 
    387         client_side_cmd = 'timeout --signal=%d %d %s' % (signal.SIGKILL,
    388                                                          timeout + 1, function)
    389         cmd = '%s%s ' % ('sudo -n ' if require_sudo else '', client_side_cmd)
    390 
    391         if serial:
    392             cmd += '-s %s ' % serial
    393 
    394         if shell:
    395             cmd += '%s ' % SHELL_CMD
    396         cmd += command
    397 
    398         self._log_adb_pid()
    399 
    400         if verbose:
    401             logging.debug('Command: %s', cmd)
    402 
    403         return self.teststation.run(cmd, timeout=timeout,
    404                 ignore_status=ignore_status,
    405                 ignore_timeout=ignore_timeout, stdout_tee=stdout,
    406                 stderr_tee=stderr, options=options, stdin=stdin,
    407                 connect_timeout=connect_timeout, args=args)
    408 
    409 
    410     def _run_output_with_retry(self, cmd):
    411         """Call run_output method for the given command with retry.
    412 
    413         adb command can be flaky some time, and the command may fail or return
    414         empty string. It may take several retries until a value can be returned.
    415 
    416         @param cmd: The command to run.
    417 
    418         @return: Return value from the command after retry.
    419         """
    420         try:
    421             return client_utils.poll_for_condition(
    422                     lambda: self.run_output(cmd, ignore_status=True),
    423                     timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS,
    424                     sleep_interval=0.5,
    425                     desc='Get return value for command `%s`' % cmd)
    426         except client_utils.TimeoutError:
    427             return ''
    428 
    429 
    430     def get_device_aliases(self):
    431         """Get all aliases for this device."""
    432         product = self.get_product_name()
    433         return android_utils.AndroidAliases.get_product_aliases(product)
    434 
    435     def get_product_name(self):
    436         """Get the product name of the device, eg., shamu, bat"""
    437         return self.run_output('getprop %s' % BOARD_FILE)
    438 
    439     def get_board_name(self):
    440         """Get the name of the board, e.g., shamu, bat_land etc.
    441         """
    442         product = self.get_product_name()
    443         return android_utils.AndroidAliases.get_board_name(product)
    444 
    445 
    446     @label_decorator()
    447     def get_board(self):
    448         """Determine the correct board label for the device.
    449 
    450         @returns a string representing this device's board.
    451         """
    452         board = self.get_board_name()
    453         board_os = self.get_os_type()
    454         return constants.BOARD_PREFIX + '-'.join([board_os, board])
    455 
    456 
    457     def job_start(self):
    458         """Overload of parent which intentionally doesn't log certain files.
    459 
    460         The parent implementation attempts to log certain Linux files, such as
    461         /var/log, which do not exist on Android, thus there is no call to the
    462         parent's job_start().  The sync call is made so that logcat logs can be
    463         approximately matched to server logs.
    464         """
    465         # Try resetting the ADB daemon on the device, however if we are
    466         # creating the host to do a repair job, the device maybe inaccesible
    467         # via ADB.
    468         try:
    469             self._reset_adbd_connection()
    470         except error.GenericHostRunError as e:
    471             logging.error('Unable to reset the device adb daemon connection: '
    472                           '%s.', e)
    473 
    474         if self.is_up():
    475             self._sync_time()
    476             self._enable_native_crash_logging()
    477 
    478 
    479     def run(self, command, timeout=3600, ignore_status=False,
    480             ignore_timeout=False, stdout_tee=utils.TEE_TO_LOGS,
    481             stderr_tee=utils.TEE_TO_LOGS, connect_timeout=30, options='',
    482             stdin=None, verbose=True, args=()):
    483         """Run a command on the adb device.
    484 
    485         The command given will be ran directly on the adb device; for example
    486         'ls' will be ran as: 'abd shell ls'
    487 
    488         @param command: The command line string.
    489         @param timeout: Time limit in seconds before attempting to
    490                         kill the running process. The run() function
    491                         will take a few seconds longer than 'timeout'
    492                         to complete if it has to kill the process.
    493         @param ignore_status: Do not raise an exception, no matter
    494                               what the exit code of the command is.
    495         @param ignore_timeout: Bool True if command timeouts should be
    496                                ignored.  Will return None on command timeout.
    497         @param stdout_tee: Redirect stdout.
    498         @param stderr_tee: Redirect stderr.
    499         @param connect_timeout: Connection timeout (in seconds).
    500         @param options: String with additional ssh command options.
    501         @param stdin: Stdin to pass (a string) to the executed command
    502         @param args: Sequence of strings to pass as arguments to command by
    503                      quoting them in " and escaping their contents if
    504                      necessary.
    505 
    506         @returns A CMDResult object or None if the call timed out and
    507                  ignore_timeout is True.
    508 
    509         @raises AutoservRunError: If the command failed.
    510         @raises AutoservSSHTimeout: Ssh connection has timed out.
    511         """
    512         command = ('"%s; echo %s:\$?"' %
    513                    (utils.sh_escape(command), CMD_OUTPUT_PREFIX))
    514 
    515         def _run():
    516             """Run the command and try to parse the exit code.
    517             """
    518             result = self.adb_run(
    519                     command, shell=True, timeout=timeout,
    520                     ignore_status=ignore_status, ignore_timeout=ignore_timeout,
    521                     stdout=stdout_tee, stderr=stderr_tee,
    522                     connect_timeout=connect_timeout, options=options,
    523                     stdin=stdin, verbose=verbose, args=args)
    524             if not result:
    525                 # In case of timeouts. Set the return to a specific string
    526                 # value. That way the caller of poll_for_condition knows
    527                 # a timeout occurs and should return None. Return None here will
    528                 # lead to the command to be retried.
    529                 return TIMEOUT_MSG
    530             parse_output = re.match(CMD_OUTPUT_REGEX, result.stdout)
    531             if not parse_output and not ignore_status:
    532                 logging.error('Failed to parse the exit code for command: `%s`.'
    533                               ' result: `%s`', command, result.stdout)
    534                 return None
    535             elif parse_output:
    536                 result.stdout = parse_output.group('OUTPUT')
    537                 result.exit_status = int(parse_output.group('EXIT_CODE'))
    538                 if result.exit_status != 0 and not ignore_status:
    539                     raise error.AutoservRunError(command, result)
    540             return result
    541 
    542         result = client_utils.poll_for_condition(
    543                 lambda: _run(),
    544                 timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS,
    545                 sleep_interval=0.5,
    546                 desc='Run command `%s`' % command)
    547         return None if result == TIMEOUT_MSG else result
    548 
    549 
    550     def check_boot_to_adb_complete(self, exception_type=error.TimeoutException):
    551         """Check if the device has finished booting and accessible by adb.
    552 
    553         @param exception_type: Type of exception to raise. Default is set to
    554                 error.TimeoutException for retry.
    555 
    556         @raise exception_type: If the device has not finished booting yet, raise
    557                 an exception of type `exception_type`.
    558         """
    559         bootcomplete = self._run_output_with_retry('getprop dev.bootcomplete')
    560         if bootcomplete != PROPERTY_VALUE_TRUE:
    561             raise exception_type('dev.bootcomplete is %s.' % bootcomplete)
    562         if self.get_os_type() == OS_TYPE_ANDROID:
    563             boot_completed = self._run_output_with_retry(
    564                     'getprop sys.boot_completed')
    565             if boot_completed != PROPERTY_VALUE_TRUE:
    566                 raise exception_type('sys.boot_completed is %s.' %
    567                                      boot_completed)
    568 
    569 
    570     def wait_up(self, timeout=DEFAULT_WAIT_UP_TIME_SECONDS, command=ADB_CMD):
    571         """Wait until the remote host is up or the timeout expires.
    572 
    573         Overrides wait_down from AbstractSSHHost.
    574 
    575         @param timeout: Time limit in seconds before returning even if the host
    576                 is not up.
    577         @param command: The command used to test if a device is up, i.e.,
    578                 accessible by the given command. Default is set to `adb`.
    579 
    580         @returns True if the host was found to be up before the timeout expires,
    581                  False otherwise.
    582         """
    583         @retry.retry(error.TimeoutException, timeout_min=timeout/60.0,
    584                      delay_sec=1)
    585         def _wait_up():
    586             if not self.is_up(command=command):
    587                 raise error.TimeoutException('Device is still down.')
    588             if command == ADB_CMD:
    589                 self.check_boot_to_adb_complete()
    590             return True
    591 
    592         try:
    593             _wait_up()
    594             logging.debug('Host %s is now up, and can be accessed by %s.',
    595                           self.hostname, command)
    596             return True
    597         except error.TimeoutException:
    598             logging.debug('Host %s is still down after waiting %d seconds',
    599                           self.hostname, timeout)
    600             return False
    601 
    602 
    603     def wait_down(self, timeout=DEFAULT_WAIT_DOWN_TIME_SECONDS,
    604                   warning_timer=None, old_boot_id=None, command=ADB_CMD,
    605                   boot_id=None):
    606         """Wait till the host goes down.
    607 
    608         Return when the host is down (not accessible via the command) OR when
    609         the device's boot_id changes (if a boot_id was provided).
    610 
    611         Overrides wait_down from AbstractSSHHost.
    612 
    613         @param timeout: Time in seconds to wait for the host to go down.
    614         @param warning_timer: Time limit in seconds that will generate
    615                               a warning if the host is not down yet.
    616                               Currently ignored.
    617         @param old_boot_id: Not applicable for adb_host.
    618         @param command: `adb`, test if the device can be accessed by adb
    619                 command, or `fastboot`, test if the device can be accessed by
    620                 fastboot command. Default is set to `adb`.
    621         @param boot_id: UUID of previous boot (consider the device down when the
    622                         boot_id changes from this value). Ignored if None.
    623 
    624         @returns True if the device goes down before the timeout, False
    625                  otherwise.
    626         """
    627         @retry.retry(error.TimeoutException, timeout_min=timeout/60.0,
    628                      delay_sec=1)
    629         def _wait_down():
    630             up = self.is_up(command=command)
    631             if not up:
    632                 return True
    633             if boot_id:
    634                 try:
    635                     new_boot_id = self.get_boot_id()
    636                     if new_boot_id != boot_id:
    637                         return True
    638                 except error.GenericHostRunError:
    639                     pass
    640             raise error.TimeoutException('Device is still up.')
    641 
    642         try:
    643             _wait_down()
    644             logging.debug('Host %s is now down', self.hostname)
    645             return True
    646         except error.TimeoutException:
    647             logging.debug('Host %s is still up after waiting %d seconds',
    648                           self.hostname, timeout)
    649             return False
    650 
    651 
    652     def reboot(self):
    653         """Reboot the android device via adb.
    654 
    655         @raises AutoservRebootError if reboot failed.
    656         """
    657         # Not calling super.reboot() as we want to reboot the ADB device not
    658         # the test station we are running ADB on.
    659         boot_id = self.get_boot_id()
    660         self.adb_run('reboot', timeout=10, ignore_timeout=True)
    661         if not self.wait_down(boot_id=boot_id):
    662             raise error.AutoservRebootError(
    663                     'ADB Device %s is still up after reboot' % self.adb_serial)
    664         if not self.wait_up():
    665             raise error.AutoservRebootError(
    666                     'ADB Device %s failed to return from reboot.' %
    667                     self.adb_serial)
    668         self._reset_adbd_connection()
    669 
    670 
    671     def fastboot_reboot(self):
    672         """Do a fastboot reboot to go back to adb.
    673 
    674         @raises AutoservRebootError if reboot failed.
    675         """
    676         self.fastboot_run('reboot')
    677         if not self.wait_down(command=FASTBOOT_CMD):
    678             raise error.AutoservRebootError(
    679                     'Device %s is still in fastboot mode after reboot' %
    680                     self.fastboot_serial)
    681         if not self.wait_up():
    682             raise error.AutoservRebootError(
    683                     'Device %s failed to boot to adb after fastboot reboot.' %
    684                     self.adb_serial)
    685         self._reset_adbd_connection()
    686 
    687 
    688     def remount(self):
    689         """Remounts paritions on the device read-write.
    690 
    691         Specifically, the /system, /vendor (if present) and /oem (if present)
    692         partitions on the device are remounted read-write.
    693         """
    694         self.adb_run('remount')
    695 
    696 
    697     @staticmethod
    698     def parse_device_serials(devices_output):
    699         """Return a list of parsed serials from the output.
    700 
    701         @param devices_output: Output from either an adb or fastboot command.
    702 
    703         @returns List of device serials
    704         """
    705         devices = []
    706         for line in devices_output.splitlines():
    707             match = re.search(DEVICE_FINDER_REGEX, line)
    708             if match:
    709                 serial = match.group('SERIAL')
    710                 if serial == DEVICE_NO_SERIAL_MSG or re.match(r'^\?+$', serial):
    711                     serial = DEVICE_NO_SERIAL_TAG
    712                 logging.debug('Found Device: %s', serial)
    713                 devices.append(serial)
    714         return devices
    715 
    716 
    717     def _get_devices(self, use_adb):
    718         """Get a list of devices currently attached to the test station.
    719 
    720         @params use_adb: True to get adb accessible devices. Set to False to
    721                          get fastboot accessible devices.
    722 
    723         @returns a list of devices attached to the test station.
    724         """
    725         if use_adb:
    726             result = self.adb_run('devices').stdout
    727             if self.adb_serial and self.adb_serial not in result:
    728                 self._connect_over_tcpip_as_needed()
    729         else:
    730             result = self.fastboot_run('devices').stdout
    731             if (self.fastboot_serial and
    732                 self.fastboot_serial not in result):
    733                 # fastboot devices won't list the devices using TCP
    734                 try:
    735                     if 'product' in self.fastboot_run('getvar product',
    736                                                       timeout=2).stderr:
    737                         result += '\n%s\tfastboot' % self.fastboot_serial
    738                 # The main reason we do a general Exception catch here instead
    739                 # of setting ignore_timeout/status to True is because even when
    740                 # the fastboot process has been nuked, it still stays around and
    741                 # so bgjob wants to warn us of this and tries to read the
    742                 # /proc/<pid>/stack file which then promptly returns an
    743                 # 'Operation not permitted' error since we're running as moblab
    744                 # and we don't have permission to read those files.
    745                 except Exception:
    746                     pass
    747         return self.parse_device_serials(result)
    748 
    749 
    750     def adb_devices(self):
    751         """Get a list of devices currently attached to the test station and
    752         accessible with the adb command."""
    753         devices = self._get_devices(use_adb=True)
    754         if self.adb_serial is None and len(devices) > 1:
    755             raise error.AutoservError(
    756                     'Not given ADB serial but multiple devices detected')
    757         return devices
    758 
    759 
    760     def fastboot_devices(self):
    761         """Get a list of devices currently attached to the test station and
    762         accessible by fastboot command.
    763         """
    764         devices = self._get_devices(use_adb=False)
    765         if self.fastboot_serial is None and len(devices) > 1:
    766             raise error.AutoservError(
    767                     'Not given fastboot serial but multiple devices detected')
    768         return devices
    769 
    770 
    771     def is_up(self, timeout=0, command=ADB_CMD):
    772         """Determine if the specified adb device is up with expected mode.
    773 
    774         @param timeout: Not currently used.
    775         @param command: `adb`, the device can be accessed by adb command,
    776                 or `fastboot`, the device can be accessed by fastboot command.
    777                 Default is set to `adb`.
    778 
    779         @returns True if the device is detectable by given command, False
    780                  otherwise.
    781 
    782         """
    783         if command == ADB_CMD:
    784             devices = self.adb_devices()
    785             serial = self.adb_serial
    786             # ADB has a device state, if the device is not online, no
    787             # subsequent ADB command will complete.
    788             # DUT with single device connected may not have adb_serial set.
    789             # Therefore, skip checking if serial is in the list of adb devices
    790             # if self.adb_serial is not set.
    791             if (serial and serial not in devices) or not self.is_device_ready():
    792                 logging.debug('Waiting for device to enter the ready state.')
    793                 return False
    794         elif command == FASTBOOT_CMD:
    795             devices = self.fastboot_devices()
    796             serial = self.fastboot_serial
    797         else:
    798             raise NotImplementedError('Mode %s is not supported' % command)
    799 
    800         return bool(devices and (not serial or serial in devices))
    801 
    802 
    803     def stop_loggers(self):
    804         """Inherited stop_loggers function.
    805 
    806         Calls parent function and captures logcat, since the end of the run
    807         is logically the end/stop of the logcat log.
    808         """
    809         super(ADBHost, self).stop_loggers()
    810 
    811         # When called from atest and tools like it there will be no job.
    812         if not self.job:
    813             return
    814 
    815         # Record logcat log to a temporary file on the teststation.
    816         tmp_dir = self.teststation.get_tmp_dir()
    817         logcat_filename = LOGCAT_FILE_FMT % self.adb_serial
    818         teststation_filename = os.path.join(tmp_dir, logcat_filename)
    819         try:
    820             self.adb_run('logcat -v time -d > "%s"' % (teststation_filename),
    821                          timeout=20)
    822         except (error.GenericHostRunError, error.AutoservSSHTimeout,
    823                 error.CmdTimeoutError):
    824             return
    825         # Copy-back the log to the drone's results directory.
    826         results_logcat_filename = os.path.join(self.job.resultdir,
    827                                                logcat_filename)
    828         self.teststation.get_file(teststation_filename,
    829                                   results_logcat_filename)
    830         try:
    831             self.teststation.run('rm -rf %s' % tmp_dir)
    832         except (error.GenericHostRunError, error.AutoservSSHTimeout) as e:
    833             logging.warn('failed to remove dir %s: %s', tmp_dir, e)
    834 
    835         self._collect_crash_logs()
    836 
    837 
    838     def close(self):
    839         """Close the ADBHost object.
    840 
    841         Called as the test ends. Will return the device to USB mode and kill
    842         the ADB server.
    843         """
    844         super(ADBHost, self).close()
    845         self.teststation.close()
    846 
    847 
    848     def syslog(self, message, tag='autotest'):
    849         """Logs a message to syslog on the device.
    850 
    851         @param message String message to log into syslog
    852         @param tag String tag prefix for syslog
    853 
    854         """
    855         self.run('log -t "%s" "%s"' % (tag, message))
    856 
    857 
    858     def get_autodir(self):
    859         """Return the directory to install autotest for client side tests."""
    860         return '/data/autotest'
    861 
    862 
    863     def is_device_ready(self):
    864         """Return the if the device is ready for ADB commands."""
    865         try:
    866             # Retry to avoid possible flakes.
    867             is_ready = client_utils.poll_for_condition(
    868                 lambda: self.adb_run('get-state').stdout.strip() == 'device',
    869                 timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS, sleep_interval=1,
    870                 desc='Waiting for device state to be `device`')
    871         except client_utils.TimeoutError:
    872             is_ready = False
    873 
    874         logging.debug('Device state is %sready', '' if is_ready else 'NOT ')
    875         return is_ready
    876 
    877 
    878     def verify_connectivity(self):
    879         """Verify we can connect to the device."""
    880         if not self.is_device_ready():
    881             raise error.AutoservHostError('device state is not in the '
    882                                           '\'device\' state.')
    883 
    884 
    885     def verify_software(self):
    886         """Verify working software on an adb_host.
    887 
    888         """
    889         # Check if adb and fastboot are present.
    890         self.teststation.run('which adb')
    891         self.teststation.run('which fastboot')
    892         self.teststation.run('which unzip')
    893 
    894         # Apply checks only for Android device.
    895         if self.get_os_type() == OS_TYPE_ANDROID:
    896             # Make sure ro.boot.hardware and ro.build.product match.
    897             hardware = self._run_output_with_retry('getprop ro.boot.hardware')
    898             product = self._run_output_with_retry('getprop ro.build.product')
    899             if hardware != product:
    900                 raise error.AutoservHostError('ro.boot.hardware: %s does not '
    901                                               'match to ro.build.product: %s' %
    902                                               (hardware, product))
    903 
    904 
    905     def verify_job_repo_url(self, tag=''):
    906         """Make sure job_repo_url of this host is valid.
    907 
    908         TODO (crbug.com/532223): Actually implement this method.
    909 
    910         @param tag: The tag from the server job, in the format
    911                     <job_id>-<user>/<hostname>, or <hostless> for a server job.
    912         """
    913         return
    914 
    915 
    916     def repair(self, board=None, os=None):
    917         """Attempt to get the DUT to pass `self.verify()`.
    918 
    919         @param board: Board name of the device. For host created in testbed,
    920                       it does not have host labels and attributes. Therefore,
    921                       the board name needs to be passed in from the testbed
    922                       repair call.
    923         @param os: OS of the device. For host created in testbed, it does not
    924                    have host labels and attributes. Therefore, the OS needs to
    925                    be passed in from the testbed repair call.
    926         """
    927         if self.is_up():
    928             logging.debug('The device is up and accessible by adb. No need to '
    929                           'repair.')
    930             return
    931         # Force to do a reinstall in repair first. The reason is that it
    932         # requires manual action to put the device into fastboot mode.
    933         # If repair tries to switch the device back to adb mode, one will
    934         # have to change it back to fastboot mode manually again.
    935         logging.debug('Verifying the device is accessible via fastboot.')
    936         self.ensure_bootloader_mode()
    937         subdir_tag = self.adb_serial if board else None
    938         if not self.job.run_test(
    939                 'provision_AndroidUpdate', host=self, value=None, force=True,
    940                 repair=True, board=board, os=os, subdir_tag=subdir_tag):
    941             raise error.AutoservRepairTotalFailure(
    942                     'Unable to repair the device.')
    943 
    944 
    945     def send_file(self, source, dest, delete_dest=False,
    946                   preserve_symlinks=False):
    947         """Copy files from the drone to the device.
    948 
    949         Just a note, there is the possibility the test station is localhost
    950         which makes some of these steps redundant (e.g. creating tmp dir) but
    951         that scenario will undoubtedly be a development scenario (test station
    952         is also the moblab) and not the typical live test running scenario so
    953         the redundancy I think is harmless.
    954 
    955         @param source: The file/directory on the drone to send to the device.
    956         @param dest: The destination path on the device to copy to.
    957         @param delete_dest: A flag set to choose whether or not to delete
    958                             dest on the device if it exists.
    959         @param preserve_symlinks: Controls if symlinks on the source will be
    960                                   copied as such on the destination or
    961                                   transformed into the referenced
    962                                   file/directory.
    963         """
    964         # If we need to preserve symlinks, let's check if the source is a
    965         # symlink itself and if so, just create it on the device.
    966         if preserve_symlinks:
    967             symlink_target = None
    968             try:
    969                 symlink_target = os.readlink(source)
    970             except OSError:
    971                 # Guess it's not a symlink.
    972                 pass
    973 
    974             if symlink_target is not None:
    975                 # Once we create the symlink, let's get out of here.
    976                 self.run('ln -s %s %s' % (symlink_target, dest))
    977                 return
    978 
    979         # Stage the files on the test station.
    980         tmp_dir = self.teststation.get_tmp_dir()
    981         src_path = os.path.join(tmp_dir, os.path.basename(dest))
    982         # Now copy the file over to the test station so you can reference the
    983         # file in the push command.
    984         self.teststation.send_file(source, src_path,
    985                                    preserve_symlinks=preserve_symlinks)
    986 
    987         if delete_dest:
    988             self.run('rm -rf %s' % dest)
    989 
    990         self.adb_run('push %s %s' % (src_path, dest))
    991 
    992         # Cleanup the test station.
    993         try:
    994             self.teststation.run('rm -rf %s' % tmp_dir)
    995         except (error.GenericHostRunError, error.AutoservSSHTimeout) as e:
    996             logging.warn('failed to remove dir %s: %s', tmp_dir, e)
    997 
    998 
    999     def _get_file_info(self, dest):
   1000         """Get permission and possible symlink info about file on the device.
   1001 
   1002         These files are on the device so we only have shell commands (via adb)
   1003         to get the info we want.  We'll use 'ls' to get it all.
   1004 
   1005         @param dest: File to get info about.
   1006 
   1007         @returns a dict of the file permissions and symlink.
   1008         """
   1009         # Grab file info.
   1010         file_info = self.run_output('ls -ld %s' % dest)
   1011         symlink = None
   1012         perms = 0
   1013         match = re.match(FILE_INFO_REGEX, file_info)
   1014         if match:
   1015             # Check if it's a symlink and grab the linked dest if it is.
   1016             if match.group('TYPE') == 'l':
   1017                 symlink_match = re.match(FILE_SYMLINK_REGEX, file_info)
   1018                 if symlink_match:
   1019                     symlink = symlink_match.group('SYMLINK')
   1020 
   1021             # Set the perms.
   1022             for perm, perm_flag in zip(match.group('PERMS'), FILE_PERMS_FLAGS):
   1023                 if perm != '-':
   1024                     perms |= perm_flag
   1025 
   1026         return {'perms': perms,
   1027                 'symlink': symlink}
   1028 
   1029 
   1030     def get_file(self, source, dest, delete_dest=False, preserve_perm=True,
   1031                  preserve_symlinks=False):
   1032         """Copy files from the device to the drone.
   1033 
   1034         Just a note, there is the possibility the test station is localhost
   1035         which makes some of these steps redundant (e.g. creating tmp dir) but
   1036         that scenario will undoubtedly be a development scenario (test station
   1037         is also the moblab) and not the typical live test running scenario so
   1038         the redundancy I think is harmless.
   1039 
   1040         @param source: The file/directory on the device to copy back to the
   1041                        drone.
   1042         @param dest: The destination path on the drone to copy to.
   1043         @param delete_dest: A flag set to choose whether or not to delete
   1044                             dest on the drone if it exists.
   1045         @param preserve_perm: Tells get_file() to try to preserve the sources
   1046                               permissions on files and dirs.
   1047         @param preserve_symlinks: Try to preserve symlinks instead of
   1048                                   transforming them into files/dirs on copy.
   1049         """
   1050         # Stage the files on the test station under teststation_temp_dir.
   1051         teststation_temp_dir = self.teststation.get_tmp_dir()
   1052         teststation_dest = os.path.join(teststation_temp_dir,
   1053                                         os.path.basename(source))
   1054 
   1055         source_info = {}
   1056         if preserve_symlinks or preserve_perm:
   1057             source_info = self._get_file_info(source)
   1058 
   1059         # If we want to preserve symlinks, just create it here, otherwise pull
   1060         # the file off the device.
   1061         #
   1062         # TODO(sadmac): Directories containing symlinks won't behave as
   1063         # expected.
   1064         if preserve_symlinks and source_info['symlink']:
   1065             os.symlink(source_info['symlink'], dest)
   1066         else:
   1067             self.adb_run('pull %s %s' % (source, teststation_temp_dir))
   1068 
   1069             # Copy over the file from the test station and clean up.
   1070             self.teststation.get_file(teststation_dest, dest,
   1071                                       delete_dest=delete_dest)
   1072             try:
   1073                 self.teststation.run('rm -rf %s' % teststation_temp_dir)
   1074             except (error.GenericHostRunError, error.AutoservSSHTimeout) as e:
   1075                 logging.warn('failed to remove dir %s: %s',
   1076                              teststation_temp_dir, e)
   1077 
   1078             # Source will be copied under dest if either:
   1079             #  1. Source is a directory and doesn't end with /.
   1080             #  2. Source is a file and dest is a directory.
   1081             command = '[ -d %s ]' % source
   1082             source_is_dir = self.run(command,
   1083                                      ignore_status=True).exit_status == 0
   1084             logging.debug('%s on the device %s a directory', source,
   1085                           'is' if source_is_dir else 'is not')
   1086 
   1087             if ((source_is_dir and not source.endswith(os.sep)) or
   1088                 (not source_is_dir and os.path.isdir(dest))):
   1089                 receive_path = os.path.join(dest, os.path.basename(source))
   1090             else:
   1091                 receive_path = dest
   1092 
   1093             if not os.path.exists(receive_path):
   1094                 logging.warning('Expected file %s does not exist; skipping'
   1095                                 ' permissions copy', receive_path)
   1096                 return
   1097 
   1098             # Set the permissions of the received file/dirs.
   1099             if os.path.isdir(receive_path):
   1100                 for root, _dirs, files in os.walk(receive_path):
   1101                     def process(rel_path, default_perm):
   1102                         info = self._get_file_info(os.path.join(source,
   1103                                                                 rel_path))
   1104                         if info['perms'] != 0:
   1105                             target = os.path.join(receive_path, rel_path)
   1106                             if preserve_perm:
   1107                                 os.chmod(target, info['perms'])
   1108                             else:
   1109                                 os.chmod(target, default_perm)
   1110 
   1111                     rel_root = os.path.relpath(root, receive_path)
   1112                     process(rel_root, _DEFAULT_DIR_PERMS)
   1113                     for f in files:
   1114                         process(os.path.join(rel_root, f), _DEFAULT_FILE_PERMS)
   1115             elif preserve_perm:
   1116                 os.chmod(receive_path, source_info['perms'])
   1117             else:
   1118                 os.chmod(receive_path, _DEFAULT_FILE_PERMS)
   1119 
   1120 
   1121     def get_release_version(self):
   1122         """Get the release version from the RELEASE_FILE on the device.
   1123 
   1124         @returns The release string in the RELEASE_FILE.
   1125 
   1126         """
   1127         return self.run_output('getprop %s' % RELEASE_FILE)
   1128 
   1129 
   1130     def get_tmp_dir(self, parent=''):
   1131         """Return a suitable temporary directory on the device.
   1132 
   1133         We ensure this is a subdirectory of /data/local/tmp.
   1134 
   1135         @param parent: Parent directory of the returned tmp dir.
   1136 
   1137         @returns a path to the temp directory on the host.
   1138         """
   1139         # TODO(kevcheng): Refactor the cleanup of tmp dir to be inherited
   1140         #                 from the parent.
   1141         if not parent.startswith(TMP_DIR):
   1142             parent = os.path.join(TMP_DIR, parent.lstrip(os.path.sep))
   1143         self.run('mkdir -p %s' % parent)
   1144         tmp_dir = self.run_output('mktemp -d -p %s' % parent)
   1145         self.tmp_dirs.append(tmp_dir)
   1146         return tmp_dir
   1147 
   1148 
   1149     def get_platform(self):
   1150         """Determine the correct platform label for this host.
   1151 
   1152         @returns a string representing this host's platform.
   1153         """
   1154         return 'adb'
   1155 
   1156 
   1157     def get_os_type(self):
   1158         """Get the OS type of the DUT, e.g., android or brillo.
   1159         """
   1160         if not self._os_type:
   1161             if self.run_output('getprop ro.product.brand') == 'Brillo':
   1162                 self._os_type = OS_TYPE_BRILLO
   1163             else:
   1164                 self._os_type = OS_TYPE_ANDROID
   1165 
   1166         return self._os_type
   1167 
   1168 
   1169     def _forward(self, reverse, args):
   1170         """Execute a forwarding command.
   1171 
   1172         @param reverse: Whether this is reverse forwarding (Boolean).
   1173         @param args: List of command arguments.
   1174         """
   1175         cmd = '%s %s' % ('reverse' if reverse else 'forward', ' '.join(args))
   1176         self.adb_run(cmd)
   1177 
   1178 
   1179     def add_forwarding(self, src, dst, reverse=False, rebind=True):
   1180         """Forward a port between the ADB host and device.
   1181 
   1182         Port specifications are any strings accepted as such by ADB, for
   1183         example 'tcp:8080'.
   1184 
   1185         @param src: Port specification to forward from.
   1186         @param dst: Port specification to forward to.
   1187         @param reverse: Do reverse forwarding from device to host (Boolean).
   1188         @param rebind: Allow rebinding an already bound port (Boolean).
   1189         """
   1190         args = []
   1191         if not rebind:
   1192             args.append('--no-rebind')
   1193         args += [src, dst]
   1194         self._forward(reverse, args)
   1195 
   1196 
   1197     def remove_forwarding(self, src=None, reverse=False):
   1198         """Removes forwarding on port.
   1199 
   1200         @param src: Port specification, or None to remove all forwarding.
   1201         @param reverse: Whether this is reverse forwarding (Boolean).
   1202         """
   1203         args = []
   1204         if src is None:
   1205             args.append('--remove-all')
   1206         else:
   1207             args += ['--remove', src]
   1208         self._forward(reverse, args)
   1209 
   1210 
   1211     def create_ssh_tunnel(self, port, local_port):
   1212         """
   1213         Forwards a port securely through a tunnel process from the server
   1214         to the DUT for RPC server connection.
   1215         Add a 'ADB forward' rule to forward the RPC packets from the AdbHost
   1216         to the DUT.
   1217 
   1218         @param port: remote port on the DUT.
   1219         @param local_port: local forwarding port.
   1220 
   1221         @return: the tunnel process.
   1222         """
   1223         self.add_forwarding('tcp:%s' % port, 'tcp:%s' % port)
   1224         return super(ADBHost, self).create_ssh_tunnel(port, local_port)
   1225 
   1226 
   1227     def disconnect_ssh_tunnel(self, tunnel_proc, port):
   1228         """
   1229         Disconnects a previously forwarded port from the server to the DUT for
   1230         RPC server connection.
   1231         Remove the previously added 'ADB forward' rule to forward the RPC
   1232         packets from the AdbHost to the DUT.
   1233 
   1234         @param tunnel_proc: the original tunnel process returned from
   1235                             |create_ssh_tunnel|.
   1236         @param port: remote port on the DUT.
   1237 
   1238         """
   1239         self.remove_forwarding('tcp:%s' % port)
   1240         super(ADBHost, self).disconnect_ssh_tunnel(tunnel_proc, port)
   1241 
   1242 
   1243     def ensure_bootloader_mode(self):
   1244         """Ensure the device is in bootloader mode.
   1245 
   1246         @raise: error.AutoservError if the device failed to reboot into
   1247                 bootloader mode.
   1248         """
   1249         if self.is_up(command=FASTBOOT_CMD):
   1250             return
   1251         self.adb_run('reboot bootloader')
   1252         if not self.wait_up(command=FASTBOOT_CMD):
   1253             raise error.AutoservError(
   1254                     'Device %s failed to reboot into bootloader mode.' %
   1255                     self.fastboot_serial)
   1256 
   1257 
   1258     def ensure_adb_mode(self, timeout=DEFAULT_WAIT_UP_TIME_SECONDS):
   1259         """Ensure the device is up and can be accessed by adb command.
   1260 
   1261         @param timeout: Time limit in seconds before returning even if the host
   1262                         is not up.
   1263 
   1264         @raise: error.AutoservError if the device failed to reboot into
   1265                 adb mode.
   1266         """
   1267         if self.is_up():
   1268             return
   1269         # Ignore timeout error to allow `fastboot reboot` to fail quietly and
   1270         # check if the device is in adb mode.
   1271         self.fastboot_run('reboot', timeout=timeout, ignore_timeout=True)
   1272         if not self.wait_up(timeout=timeout):
   1273             raise error.AutoservError(
   1274                     'Device %s failed to reboot into adb mode.' %
   1275                     self.adb_serial)
   1276         self._reset_adbd_connection()
   1277 
   1278 
   1279     @classmethod
   1280     def get_build_info_from_build_url(cls, build_url):
   1281         """Get the Android build information from the build url.
   1282 
   1283         @param build_url: The url to use for downloading Android artifacts.
   1284                 pattern: http://$devserver:###/static/branch/target/build_id
   1285 
   1286         @return: A dictionary of build information, including keys:
   1287                  build_target, branch, target, build_id.
   1288         @raise AndroidInstallError: If failed to parse build_url.
   1289         """
   1290         if not build_url:
   1291             raise AndroidInstallError('Need build_url to download image files.')
   1292 
   1293         try:
   1294             match = re.match(DEVSERVER_URL_REGEX, build_url)
   1295             return {'build_target': match.group('BUILD_TARGET'),
   1296                     'branch': match.group('BRANCH'),
   1297                     'target': ('%s-%s' % (match.group('BUILD_TARGET'),
   1298                                           match.group('BUILD_TYPE'))),
   1299                     'build_id': match.group('BUILD_ID')}
   1300         except (AttributeError, IndexError, ValueError) as e:
   1301             raise AndroidInstallError(
   1302                     'Failed to parse build url: %s\nError: %s' % (build_url, e))
   1303 
   1304 
   1305     @retry.retry(error.GenericHostRunError, timeout_min=10)
   1306     def download_file(self, build_url, file, dest_dir, unzip=False,
   1307                       unzip_dest=None):
   1308         """Download the given file from the build url.
   1309 
   1310         @param build_url: The url to use for downloading Android artifacts.
   1311                 pattern: http://$devserver:###/static/branch/target/build_id
   1312         @param file: Name of the file to be downloaded, e.g., boot.img.
   1313         @param dest_dir: Destination folder for the file to be downloaded to.
   1314         @param unzip: If True, unzip the downloaded file.
   1315         @param unzip_dest: Location to unzip the downloaded file to. If not
   1316                            provided, dest_dir is used.
   1317         """
   1318         # Append the file name to the url if build_url is linked to the folder
   1319         # containing the file.
   1320         if not build_url.endswith('/%s' % file):
   1321             src_url = os.path.join(build_url, file)
   1322         else:
   1323             src_url = build_url
   1324         dest_file = os.path.join(dest_dir, file)
   1325         try:
   1326             self.teststation.run('wget -q -O "%s" "%s"' % (dest_file, src_url))
   1327             if unzip:
   1328                 unzip_dest = unzip_dest or dest_dir
   1329                 self.teststation.run('unzip "%s/%s" -x -d "%s"' %
   1330                                      (dest_dir, file, unzip_dest))
   1331         except:
   1332             # Delete the destination file if download failed.
   1333             self.teststation.run('rm -f "%s"' % dest_file)
   1334             raise
   1335 
   1336 
   1337     def stage_android_image_files(self, build_url):
   1338         """Download required image files from the given build_url to a local
   1339         directory in the machine runs fastboot command.
   1340 
   1341         @param build_url: The url to use for downloading Android artifacts.
   1342                 pattern: http://$devserver:###/static/branch/target/build_id
   1343 
   1344         @return: Path to the directory contains image files.
   1345         """
   1346         build_info = self.get_build_info_from_build_url(build_url)
   1347 
   1348         zipped_image_file = ANDROID_IMAGE_FILE_FMT % build_info
   1349         image_dir = self.teststation.get_tmp_dir()
   1350 
   1351         try:
   1352             self.download_file(build_url, zipped_image_file, image_dir,
   1353                                unzip=True)
   1354             images = android_utils.AndroidImageFiles.get_standalone_images(
   1355                     build_info['build_target'])
   1356             for image_file in images:
   1357                 self.download_file(build_url, image_file, image_dir)
   1358 
   1359             return image_dir
   1360         except:
   1361             self.teststation.run('rm -rf %s' % image_dir)
   1362             raise
   1363 
   1364 
   1365     def stage_brillo_image_files(self, build_url):
   1366         """Download required brillo image files from the given build_url to a
   1367         local directory in the machine runs fastboot command.
   1368 
   1369         @param build_url: The url to use for downloading Android artifacts.
   1370                 pattern: http://$devserver:###/static/branch/target/build_id
   1371 
   1372         @return: Path to the directory contains image files.
   1373         """
   1374         build_info = self.get_build_info_from_build_url(build_url)
   1375 
   1376         zipped_image_file = ANDROID_IMAGE_FILE_FMT % build_info
   1377         vendor_partitions_file = BRILLO_VENDOR_PARTITIONS_FILE_FMT % build_info
   1378         image_dir = self.teststation.get_tmp_dir()
   1379 
   1380         try:
   1381             self.download_file(build_url, zipped_image_file, image_dir,
   1382                                unzip=True)
   1383             self.download_file(build_url, vendor_partitions_file, image_dir,
   1384                                unzip=True,
   1385                                unzip_dest=os.path.join(image_dir, 'vendor'))
   1386             return image_dir
   1387         except:
   1388             self.teststation.run('rm -rf %s' % image_dir)
   1389             raise
   1390 
   1391 
   1392     def stage_build_for_install(self, build_name, os_type=None):
   1393         """Stage a build on a devserver and return the build_url and devserver.
   1394 
   1395         @param build_name: a name like git-master/shamu-userdebug/2040953
   1396 
   1397         @returns a tuple with an update URL like:
   1398             http://172.22.50.122:8080/git-master/shamu-userdebug/2040953
   1399             and the devserver instance.
   1400         """
   1401         os_type = os_type or self.get_os_type()
   1402         logging.info('Staging build for installation: %s', build_name)
   1403         devserver = dev_server.AndroidBuildServer.resolve(build_name,
   1404                                                           self.hostname)
   1405         build_name = devserver.translate(build_name)
   1406         branch, target, build_id = utils.parse_launch_control_build(build_name)
   1407         devserver.trigger_download(target, build_id, branch,
   1408                                    os=os_type, synchronous=False)
   1409         return '%s/static/%s' % (devserver.url(), build_name), devserver
   1410 
   1411 
   1412     def install_android(self, build_url, build_local_path=None, wipe=True,
   1413                         flash_all=False, disable_package_verification=True,
   1414                         skip_setup_wizard=True):
   1415         """Install the Android DUT.
   1416 
   1417         Following are the steps used here to provision an android device:
   1418         1. If build_local_path is not set, download the image zip file, e.g.,
   1419            shamu-img-2284311.zip, unzip it.
   1420         2. Run fastboot to install following artifacts:
   1421            bootloader, radio, boot, system, vendor(only if exists)
   1422 
   1423         Repair is not supported for Android devices yet.
   1424 
   1425         @param build_url: The url to use for downloading Android artifacts.
   1426                 pattern: http://$devserver:###/static/$build
   1427         @param build_local_path: The path to a local folder that contains the
   1428                 image files needed to provision the device. Note that the folder
   1429                 is in the machine running adb command, rather than the drone.
   1430         @param wipe: If true, userdata will be wiped before flashing.
   1431         @param flash_all: If True, all img files found in img_path will be
   1432                 flashed. Otherwise, only boot and system are flashed.
   1433 
   1434         @raises AndroidInstallError if any error occurs.
   1435         """
   1436         # If the build is not staged in local server yet, clean up the temp
   1437         # folder used to store image files after the provision is completed.
   1438         delete_build_folder = bool(not build_local_path)
   1439 
   1440         try:
   1441             # Download image files needed for provision to a local directory.
   1442             if not build_local_path:
   1443                 build_local_path = self.stage_android_image_files(build_url)
   1444 
   1445             # Device needs to be in bootloader mode for flashing.
   1446             self.ensure_bootloader_mode()
   1447 
   1448             if wipe:
   1449                 self._fastboot_run_with_retry('-w')
   1450 
   1451             # Get all *.img file in the build_local_path.
   1452             list_file_cmd = 'ls -d %s' % os.path.join(build_local_path, '*.img')
   1453             image_files = self.teststation.run(
   1454                     list_file_cmd).stdout.strip().split()
   1455             images = dict([(os.path.basename(f), f) for f in image_files])
   1456             build_info = self.get_build_info_from_build_url(build_url)
   1457             board = build_info['build_target']
   1458             all_images = (
   1459                     android_utils.AndroidImageFiles.get_standalone_images(board)
   1460                     + android_utils.AndroidImageFiles.get_zipped_images(board))
   1461 
   1462             # Sort images to be flashed, bootloader needs to be the first one.
   1463             bootloader = android_utils.AndroidImageFiles.BOOTLOADER
   1464             sorted_images = sorted(
   1465                     images.items(),
   1466                     key=lambda pair: 0 if pair[0] == bootloader else 1)
   1467             for image, image_file in sorted_images:
   1468                 if image not in all_images:
   1469                     continue
   1470                 logging.info('Flashing %s...', image_file)
   1471                 self._fastboot_run_with_retry('-S 256M flash %s %s' %
   1472                                               (image[:-4], image_file))
   1473                 if image == android_utils.AndroidImageFiles.BOOTLOADER:
   1474                     self.fastboot_run('reboot-bootloader')
   1475                     self.wait_up(command=FASTBOOT_CMD)
   1476         except Exception as e:
   1477             logging.error('Install Android build failed with error: %s', e)
   1478             # Re-raise the exception with type of AndroidInstallError.
   1479             raise AndroidInstallError, sys.exc_info()[1], sys.exc_info()[2]
   1480         finally:
   1481             if delete_build_folder:
   1482                 self.teststation.run('rm -rf %s' % build_local_path)
   1483             timeout = (WAIT_UP_AFTER_WIPE_TIME_SECONDS if wipe else
   1484                        DEFAULT_WAIT_UP_TIME_SECONDS)
   1485             self.ensure_adb_mode(timeout=timeout)
   1486             if disable_package_verification:
   1487                 # TODO: Use a whitelist of devices to do this for rather than
   1488                 # doing it by default.
   1489                 self.disable_package_verification()
   1490             if skip_setup_wizard:
   1491                 try:
   1492                     self.skip_setup_wizard()
   1493                 except error.GenericHostRunError:
   1494                     logging.error('Could not skip setup wizard.')
   1495         logging.info('Successfully installed Android build staged at %s.',
   1496                      build_url)
   1497 
   1498 
   1499     def install_brillo(self, build_url, build_local_path=None):
   1500         """Install the Brillo DUT.
   1501 
   1502         Following are the steps used here to provision an android device:
   1503         1. If build_local_path is not set, download the image zip file, e.g.,
   1504            dragonboard-img-123456.zip, unzip it. And download the vendor
   1505            partition zip file, e.g., dragonboard-vendor_partitions-123456.zip,
   1506            unzip it to vendor folder.
   1507         2. Run provision_device script to install OS images and vendor
   1508            partitions.
   1509 
   1510         @param build_url: The url to use for downloading Android artifacts.
   1511                 pattern: http://$devserver:###/static/$build
   1512         @param build_local_path: The path to a local folder that contains the
   1513                 image files needed to provision the device. Note that the folder
   1514                 is in the machine running adb command, rather than the drone.
   1515 
   1516         @raises AndroidInstallError if any error occurs.
   1517         """
   1518         # If the build is not staged in local server yet, clean up the temp
   1519         # folder used to store image files after the provision is completed.
   1520         delete_build_folder = bool(not build_local_path)
   1521 
   1522         try:
   1523             # Download image files needed for provision to a local directory.
   1524             if not build_local_path:
   1525                 build_local_path = self.stage_brillo_image_files(build_url)
   1526 
   1527             # Device needs to be in bootloader mode for flashing.
   1528             self.ensure_bootloader_mode()
   1529 
   1530             # Run provision_device command to install image files and vendor
   1531             # partitions.
   1532             vendor_partition_dir = os.path.join(build_local_path, 'vendor')
   1533             cmd = (BRILLO_PROVISION_CMD %
   1534                    {'os_image_dir': build_local_path,
   1535                     'vendor_partition_dir': vendor_partition_dir})
   1536             if self.fastboot_serial:
   1537                 cmd += ' -s %s ' % self.fastboot_serial
   1538             self.teststation.run(cmd)
   1539         except Exception as e:
   1540             logging.error('Install Brillo build failed with error: %s', e)
   1541             # Re-raise the exception with type of AndroidInstallError.
   1542             raise AndroidInstallError, sys.exc_info()[1], sys.exc_info()[2]
   1543         finally:
   1544             if delete_build_folder:
   1545                 self.teststation.run('rm -rf %s' % build_local_path)
   1546             self.ensure_adb_mode()
   1547         logging.info('Successfully installed Android build staged at %s.',
   1548                      build_url)
   1549 
   1550 
   1551     @property
   1552     def job_repo_url_attribute(self):
   1553         """Get the host attribute name for job_repo_url, which should append the
   1554         adb serial.
   1555         """
   1556         return '%s_%s' % (constants.JOB_REPO_URL, self.adb_serial)
   1557 
   1558 
   1559     def machine_install(self, build_url=None, build_local_path=None, wipe=True,
   1560                         flash_all=False, os_type=None):
   1561         """Install the DUT.
   1562 
   1563         @param build_url: The url to use for downloading Android artifacts.
   1564                 pattern: http://$devserver:###/static/$build. If build_url is
   1565                 set to None, the code may try _parser.options.image to do the
   1566                 installation. If none of them is set, machine_install will fail.
   1567         @param build_local_path: The path to a local directory that contains the
   1568                 image files needed to provision the device.
   1569         @param wipe: If true, userdata will be wiped before flashing.
   1570         @param flash_all: If True, all img files found in img_path will be
   1571                 flashed. Otherwise, only boot and system are flashed.
   1572 
   1573         @returns A tuple of (image_name, host_attributes).
   1574                 image_name is the name of image installed, e.g.,
   1575                 git_mnc-release/shamu-userdebug/1234
   1576                 host_attributes is a dictionary of (attribute, value), which
   1577                 can be saved to afe_host_attributes table in database. This
   1578                 method returns a dictionary with a single entry of
   1579                 `job_repo_url_[adb_serial]`: devserver_url, where devserver_url
   1580                 is a url to the build staged on devserver.
   1581         """
   1582         os_type = os_type or self.get_os_type()
   1583         if not build_url and self._parser.options.image:
   1584             build_url, _ = self.stage_build_for_install(
   1585                     self._parser.options.image, os_type=os_type)
   1586         if os_type == OS_TYPE_ANDROID:
   1587             self.install_android(
   1588                     build_url=build_url, build_local_path=build_local_path,
   1589                     wipe=wipe, flash_all=flash_all)
   1590         elif os_type == OS_TYPE_BRILLO:
   1591             self.install_brillo(
   1592                     build_url=build_url, build_local_path=build_local_path)
   1593         else:
   1594             raise error.InstallError(
   1595                     'Installation of os type %s is not supported.' %
   1596                     self.get_os_type())
   1597         return (build_url.split('static/')[-1],
   1598                 {self.job_repo_url_attribute: build_url})
   1599 
   1600 
   1601     def list_files_glob(self, path_glob):
   1602         """Get a list of files on the device given glob pattern path.
   1603 
   1604         @param path_glob: The path glob that we want to return the list of
   1605                 files that match the glob.  Relative paths will not work as
   1606                 expected.  Supply an absolute path to get the list of files
   1607                 you're hoping for.
   1608 
   1609         @returns List of files that match the path_glob.
   1610         """
   1611         # This is just in case path_glob has no path separator.
   1612         base_path = os.path.dirname(path_glob) or '.'
   1613         result = self.run('find %s -path \'%s\' -print' %
   1614                           (base_path, path_glob), ignore_status=True)
   1615         if result.exit_status != 0:
   1616             return []
   1617         return result.stdout.splitlines()
   1618 
   1619 
   1620     @retry.retry(error.GenericHostRunError,
   1621                  timeout_min=DISABLE_PACKAGE_VERIFICATION_TIMEOUT_MIN)
   1622     def disable_package_verification(self):
   1623         """Disables package verification on an android device.
   1624 
   1625         Disables the package verificatoin manager allowing any package to be
   1626         installed without checking
   1627         """
   1628         logging.info('Disabling package verification on %s.', self.adb_serial)
   1629         self.check_boot_to_adb_complete()
   1630         self.run('am broadcast -a '
   1631                  'com.google.gservices.intent.action.GSERVICES_OVERRIDE -e '
   1632                  'global:package_verifier_enable 0')
   1633 
   1634 
   1635     @retry.retry(error.GenericHostRunError, timeout_min=APK_INSTALL_TIMEOUT_MIN)
   1636     def install_apk(self, apk, force_reinstall=True):
   1637         """Install the specified apk.
   1638 
   1639         This will install the apk and override it if it's already installed and
   1640         will also allow for downgraded apks.
   1641 
   1642         @param apk: The path to apk file.
   1643         @param force_reinstall: True to reinstall the apk even if it's already
   1644                 installed. Default is set to True.
   1645 
   1646         @returns a CMDResult object.
   1647         """
   1648         try:
   1649             client_utils.poll_for_condition(
   1650                     lambda: self.run('pm list packages',
   1651                                      ignore_status=True).exit_status == 0,
   1652                     timeout=120)
   1653             client_utils.poll_for_condition(
   1654                     lambda: self.run('service list | grep mount',
   1655                                      ignore_status=True).exit_status == 0,
   1656                     timeout=120)
   1657             return self.adb_run('install %s -d %s' %
   1658                                 ('-r' if force_reinstall else '', apk))
   1659         except error.GenericHostRunError:
   1660             self.reboot()
   1661             raise
   1662 
   1663 
   1664     def uninstall_package(self, package):
   1665         """Remove the specified package.
   1666 
   1667         @param package: Android package name.
   1668 
   1669         @raises GenericHostRunError: uninstall failed
   1670         """
   1671         result = self.adb_run('uninstall %s' % package)
   1672 
   1673         if self.is_apk_installed(package):
   1674             raise error.GenericHostRunError('Uninstall of "%s" failed.'
   1675                                             % package, result)
   1676 
   1677     def save_info(self, results_dir, include_build_info=True):
   1678         """Save info about this device.
   1679 
   1680         @param results_dir: The local directory to store the info in.
   1681         @param include_build_info: If true this will include the build info
   1682                                    artifact.
   1683         """
   1684         if include_build_info:
   1685             teststation_temp_dir = self.teststation.get_tmp_dir()
   1686 
   1687             try:
   1688                 info = self.host_info_store.get()
   1689             except host_info.StoreError:
   1690                 logging.warning(
   1691                     'Device %s could not get repo url for build info.',
   1692                     self.adb_serial)
   1693                 return
   1694 
   1695             job_repo_url = info.attributes.get(self.job_repo_url_attribute, '')
   1696             if not job_repo_url:
   1697                 logging.warning(
   1698                     'Device %s could not get repo url for build info.',
   1699                     self.adb_serial)
   1700                 return
   1701 
   1702             build_info = ADBHost.get_build_info_from_build_url(job_repo_url)
   1703 
   1704             target = build_info['target']
   1705             branch = build_info['branch']
   1706             build_id = build_info['build_id']
   1707 
   1708             devserver_url = dev_server.AndroidBuildServer.get_server_url(
   1709                     job_repo_url)
   1710             ds = dev_server.AndroidBuildServer(devserver_url)
   1711 
   1712             ds.trigger_download(target, build_id, branch, files='BUILD_INFO',
   1713                                 synchronous=True)
   1714 
   1715             pull_base_url = ds.get_pull_url(target, build_id, branch)
   1716 
   1717             source_path = os.path.join(teststation_temp_dir, 'BUILD_INFO')
   1718 
   1719             self.download_file(pull_base_url, 'BUILD_INFO',
   1720                                teststation_temp_dir)
   1721 
   1722             destination_path = os.path.join(
   1723                     results_dir, 'BUILD_INFO-%s' % self.adb_serial)
   1724             self.teststation.get_file(source_path, destination_path)
   1725 
   1726 
   1727 
   1728     @retry.retry(error.GenericHostRunError, timeout_min=0.2)
   1729     def _confirm_apk_installed(self, package_name):
   1730         """Confirm if apk is already installed with the given name.
   1731 
   1732         `pm list packages` command is not reliable some time. The retry helps to
   1733         reduce the chance of false negative.
   1734 
   1735         @param package_name: Name of the package, e.g., com.android.phone.
   1736 
   1737         @raise AutoservRunError: If the package is not found or pm list command
   1738                 failed for any reason.
   1739         """
   1740         name = 'package:%s' % package_name
   1741         self.adb_run('shell pm list packages | grep -w "%s"' % name)
   1742 
   1743 
   1744     def is_apk_installed(self, package_name):
   1745         """Check if apk is already installed with the given name.
   1746 
   1747         @param package_name: Name of the package, e.g., com.android.phone.
   1748 
   1749         @return: True if package is installed. False otherwise.
   1750         """
   1751         try:
   1752             self._confirm_apk_installed(package_name)
   1753             return True
   1754         except:
   1755             return False
   1756 
   1757     @retry.retry(error.GenericHostRunError, timeout_min=1)
   1758     def skip_setup_wizard(self):
   1759         """Skip the setup wizard.
   1760 
   1761         Skip the starting setup wizard that normally shows up on android.
   1762         """
   1763         logging.info('Skipping setup wizard on %s.', self.adb_serial)
   1764         self.check_boot_to_adb_complete()
   1765         result = self.run('am start -n com.google.android.setupwizard/'
   1766                           '.SetupWizardExitActivity')
   1767 
   1768         if result.exit_status != 0:
   1769             if result.stdout.startswith('ADB_CMD_OUTPUT:255'):
   1770                 # If the result returns ADB_CMD_OUTPUT:255, then run the above
   1771                 # as root.
   1772                 logging.debug('Need root access to bypass setup wizard.')
   1773                 self._restart_adbd_with_root_permissions()
   1774                 result = self.run('am start -n com.google.android.setupwizard/'
   1775                                   '.SetupWizardExitActivity')
   1776 
   1777             if result.stdout == 'ADB_CMD_OUTPUT:0':
   1778                 # If the result returns ADB_CMD_OUTPUT:0, Error type 3, then the
   1779                 # setup wizard does not exist, so we do not have to bypass it.
   1780                 if result.stderr and not \
   1781                         result.stderr.startswith('Error type 3\n'):
   1782                     logging.error('Unrecoverable skip setup wizard failure:'
   1783                                   ' %s', result.stderr)
   1784                     raise error.TestError()
   1785                 logging.debug('Skip setup wizard received harmless error: '
   1786                               'No setup to bypass.')
   1787 
   1788         logging.debug('Bypass setup wizard was successful.')
   1789 
   1790 
   1791     def get_attributes_to_clear_before_provision(self):
   1792         """Get a list of attributes to be cleared before machine_install starts.
   1793         """
   1794         return [self.job_repo_url_attribute]
   1795 
   1796 
   1797     def get_labels(self):
   1798         """Return a list of the labels gathered from the devices connected.
   1799 
   1800         @return: A list of strings that denote the labels from all the devices
   1801                  connected.
   1802         """
   1803         return self.labels.get_labels(self)
   1804 
   1805 
   1806     def update_labels(self):
   1807         """Update the labels for this testbed."""
   1808         self.labels.update_labels(self)
   1809 
   1810 
   1811     def stage_server_side_package(self, image=None):
   1812         """Stage autotest server-side package on devserver.
   1813 
   1814         @param image: A build name, e.g., git_mnc_dev/shamu-eng/123
   1815 
   1816         @return: A url to the autotest server-side package.
   1817 
   1818         @raise: error.AutoservError if fail to locate the build to test with, or
   1819                 fail to stage server-side package.
   1820         """
   1821         # If enable_drone_in_restricted_subnet is False, do not set hostname
   1822         # in devserver.resolve call, so a devserver in non-restricted subnet
   1823         # is picked to stage autotest server package for drone to download.
   1824         hostname = self.hostname
   1825         if not utils.ENABLE_DRONE_IN_RESTRICTED_SUBNET:
   1826             hostname = None
   1827         if image:
   1828             ds = dev_server.AndroidBuildServer.resolve(image, hostname)
   1829         else:
   1830             info = self.host_info_store.get()
   1831             job_repo_url = info.attributes.get(self.job_repo_url_attribute)
   1832             if job_repo_url is not None:
   1833                 devserver_url, image = (
   1834                         tools.get_devserver_build_from_package_url(
   1835                                 job_repo_url, True))
   1836                 # If enable_drone_in_restricted_subnet is True, use the
   1837                 # existing devserver. Otherwise, resolve a new one in
   1838                 # non-restricted subnet.
   1839                 if utils.ENABLE_DRONE_IN_RESTRICTED_SUBNET:
   1840                     ds = dev_server.AndroidBuildServer(devserver_url)
   1841                 else:
   1842                     ds = dev_server.AndroidBuildServer.resolve(image)
   1843             elif info.build is not None:
   1844                 ds = dev_server.AndroidBuildServer.resolve(info.build, hostname)
   1845             else:
   1846                 raise error.AutoservError(
   1847                         'Failed to stage server-side package. The host has '
   1848                         'no job_report_url attribute or version label.')
   1849 
   1850         branch, target, build_id = utils.parse_launch_control_build(image)
   1851         build_target, _ = utils.parse_launch_control_target(target)
   1852 
   1853         # For any build older than MIN_VERSION_SUPPORT_SSP, server side
   1854         # packaging is not supported.
   1855         try:
   1856             # Some build ids may have special character before the actual
   1857             # number, skip such characters.
   1858             actual_build_id = build_id
   1859             if build_id.startswith('P'):
   1860                 actual_build_id = build_id[1:]
   1861             if int(actual_build_id) < self.MIN_VERSION_SUPPORT_SSP:
   1862                 raise error.AutoservError(
   1863                         'Build %s is older than %s. Server side packaging is '
   1864                         'disabled.' % (image, self.MIN_VERSION_SUPPORT_SSP))
   1865         except ValueError:
   1866             raise error.AutoservError(
   1867                     'Failed to compare build id in %s with the minimum '
   1868                     'version that supports server side packaging. Server '
   1869                     'side packaging is disabled.' % image)
   1870 
   1871         ds.stage_artifacts(target, build_id, branch,
   1872                            artifacts=['autotest_server_package'])
   1873         autotest_server_package_name = (AUTOTEST_SERVER_PACKAGE_FILE_FMT %
   1874                                         {'build_target': build_target,
   1875                                          'build_id': build_id})
   1876         return '%s/static/%s/%s' % (ds.url(), image,
   1877                                     autotest_server_package_name)
   1878 
   1879 
   1880     def _sync_time(self):
   1881         """Approximate synchronization of time between host and ADB device.
   1882 
   1883         This sets the ADB/Android device's clock to approximately the same
   1884         time as the Autotest host for the purposes of comparing Android system
   1885         logs such as logcat to logs from the Autotest host system.
   1886         """
   1887         command = 'date '
   1888         sdk_version = int(self.run('getprop %s' % SDK_FILE).stdout)
   1889         if sdk_version < 23:
   1890             # Android L and earlier use this format: date -s (format).
   1891             command += ('-s %s' %
   1892                         datetime.datetime.now().strftime('%Y%m%d.%H%M%S'))
   1893         else:
   1894             # Android M and later use this format: date -u (format).
   1895             command += ('-u %s' %
   1896                         datetime.datetime.utcnow().strftime('%m%d%H%M%Y.%S'))
   1897         self.run(command, timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS,
   1898                  ignore_timeout=True)
   1899 
   1900 
   1901     def _enable_native_crash_logging(self):
   1902         """Enable native (non-Java) crash logging.
   1903         """
   1904         if self.get_os_type() == OS_TYPE_ANDROID:
   1905             self._enable_android_native_crash_logging()
   1906 
   1907 
   1908     def _enable_brillo_native_crash_logging(self):
   1909         """Enables native crash logging for a Brillo DUT.
   1910         """
   1911         try:
   1912             self.run('touch /data/misc/metrics/enabled',
   1913                      timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS,
   1914                      ignore_timeout=True)
   1915             # If running, crash_sender will delete crash files every hour.
   1916             self.run('stop crash_sender',
   1917                      timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS,
   1918                      ignore_timeout=True)
   1919         except error.GenericHostRunError as e:
   1920             logging.warn(e)
   1921             logging.warn('Failed to enable Brillo native crash logging.')
   1922 
   1923 
   1924     def _enable_android_native_crash_logging(self):
   1925         """Enables native crash logging for an Android DUT.
   1926         """
   1927         # debuggerd should be enabled by default on Android.
   1928         result = self.run('pgrep debuggerd',
   1929                           timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS,
   1930                           ignore_timeout=True, ignore_status=True)
   1931         if not result or result.exit_status != 0:
   1932             logging.debug('Unable to confirm that debuggerd is running.')
   1933 
   1934 
   1935     def _collect_crash_logs(self):
   1936         """Copies crash log files from the DUT to the drone.
   1937         """
   1938         if self.get_os_type() == OS_TYPE_BRILLO:
   1939             self._collect_crash_logs_dut(BRILLO_NATIVE_CRASH_LOG_DIR)
   1940         elif self.get_os_type() == OS_TYPE_ANDROID:
   1941             self._collect_crash_logs_dut(ANDROID_TOMBSTONE_CRASH_LOG_DIR)
   1942 
   1943 
   1944     def _collect_crash_logs_dut(self, log_directory):
   1945         """Copies native crash logs from the Android/Brillo DUT to the drone.
   1946 
   1947         @param log_directory: absolute path of the directory on the DUT where
   1948                 log files are stored.
   1949         """
   1950         files = None
   1951         try:
   1952             result = self.run('find %s -maxdepth 1 -type f' % log_directory,
   1953                               timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS)
   1954             files = result.stdout.strip().split()
   1955         except (error.GenericHostRunError, error.AutoservSSHTimeout,
   1956                 error.CmdTimeoutError):
   1957             logging.debug('Unable to call find %s, unable to find crash logs',
   1958                           log_directory)
   1959         if not files:
   1960             logging.debug('There are no crash logs on the DUT.')
   1961             return
   1962 
   1963         crash_dir = os.path.join(self.job.resultdir, 'crash')
   1964         try:
   1965             os.mkdir(crash_dir)
   1966         except OSError as e:
   1967             if e.errno != errno.EEXIST:
   1968                 raise e
   1969 
   1970         for f in files:
   1971             logging.debug('DUT native crash file produced: %s', f)
   1972             dest = os.path.join(crash_dir, os.path.basename(f))
   1973             # We've had cases where the crash file on the DUT has permissions
   1974             # "000". Let's override permissions to make them sane for the user
   1975             # collecting the crashes.
   1976             self.get_file(source=f, dest=dest, preserve_perm=False)
   1977