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, excludes=None):
    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         @param excludes: A list of file pattern that matches files not to be
    964                          sent. `send_file` will fail if exclude is set, since
    965                          local copy does not support --exclude, e.g., when
    966                          using scp to copy file.
    967         """
    968         # If we need to preserve symlinks, let's check if the source is a
    969         # symlink itself and if so, just create it on the device.
    970         if preserve_symlinks:
    971             symlink_target = None
    972             try:
    973                 symlink_target = os.readlink(source)
    974             except OSError:
    975                 # Guess it's not a symlink.
    976                 pass
    977 
    978             if symlink_target is not None:
    979                 # Once we create the symlink, let's get out of here.
    980                 self.run('ln -s %s %s' % (symlink_target, dest))
    981                 return
    982 
    983         # Stage the files on the test station.
    984         tmp_dir = self.teststation.get_tmp_dir()
    985         src_path = os.path.join(tmp_dir, os.path.basename(dest))
    986         # Now copy the file over to the test station so you can reference the
    987         # file in the push command.
    988         self.teststation.send_file(
    989                 source, src_path, preserve_symlinks=preserve_symlinks,
    990                 excludes=excludes)
    991 
    992         if delete_dest:
    993             self.run('rm -rf %s' % dest)
    994 
    995         self.adb_run('push %s %s' % (src_path, dest))
    996 
    997         # Cleanup the test station.
    998         try:
    999             self.teststation.run('rm -rf %s' % tmp_dir)
   1000         except (error.GenericHostRunError, error.AutoservSSHTimeout) as e:
   1001             logging.warn('failed to remove dir %s: %s', tmp_dir, e)
   1002 
   1003 
   1004     def _get_file_info(self, dest):
   1005         """Get permission and possible symlink info about file on the device.
   1006 
   1007         These files are on the device so we only have shell commands (via adb)
   1008         to get the info we want.  We'll use 'ls' to get it all.
   1009 
   1010         @param dest: File to get info about.
   1011 
   1012         @returns a dict of the file permissions and symlink.
   1013         """
   1014         # Grab file info.
   1015         file_info = self.run_output('ls -ld %s' % dest)
   1016         symlink = None
   1017         perms = 0
   1018         match = re.match(FILE_INFO_REGEX, file_info)
   1019         if match:
   1020             # Check if it's a symlink and grab the linked dest if it is.
   1021             if match.group('TYPE') == 'l':
   1022                 symlink_match = re.match(FILE_SYMLINK_REGEX, file_info)
   1023                 if symlink_match:
   1024                     symlink = symlink_match.group('SYMLINK')
   1025 
   1026             # Set the perms.
   1027             for perm, perm_flag in zip(match.group('PERMS'), FILE_PERMS_FLAGS):
   1028                 if perm != '-':
   1029                     perms |= perm_flag
   1030 
   1031         return {'perms': perms,
   1032                 'symlink': symlink}
   1033 
   1034 
   1035     def get_file(self, source, dest, delete_dest=False, preserve_perm=True,
   1036                  preserve_symlinks=False):
   1037         """Copy files from the device to the drone.
   1038 
   1039         Just a note, there is the possibility the test station is localhost
   1040         which makes some of these steps redundant (e.g. creating tmp dir) but
   1041         that scenario will undoubtedly be a development scenario (test station
   1042         is also the moblab) and not the typical live test running scenario so
   1043         the redundancy I think is harmless.
   1044 
   1045         @param source: The file/directory on the device to copy back to the
   1046                        drone.
   1047         @param dest: The destination path on the drone to copy to.
   1048         @param delete_dest: A flag set to choose whether or not to delete
   1049                             dest on the drone if it exists.
   1050         @param preserve_perm: Tells get_file() to try to preserve the sources
   1051                               permissions on files and dirs.
   1052         @param preserve_symlinks: Try to preserve symlinks instead of
   1053                                   transforming them into files/dirs on copy.
   1054         """
   1055         # Stage the files on the test station under teststation_temp_dir.
   1056         teststation_temp_dir = self.teststation.get_tmp_dir()
   1057         teststation_dest = os.path.join(teststation_temp_dir,
   1058                                         os.path.basename(source))
   1059 
   1060         source_info = {}
   1061         if preserve_symlinks or preserve_perm:
   1062             source_info = self._get_file_info(source)
   1063 
   1064         # If we want to preserve symlinks, just create it here, otherwise pull
   1065         # the file off the device.
   1066         #
   1067         # TODO(sadmac): Directories containing symlinks won't behave as
   1068         # expected.
   1069         if preserve_symlinks and source_info['symlink']:
   1070             os.symlink(source_info['symlink'], dest)
   1071         else:
   1072             self.adb_run('pull %s %s' % (source, teststation_temp_dir))
   1073 
   1074             # Copy over the file from the test station and clean up.
   1075             self.teststation.get_file(teststation_dest, dest,
   1076                                       delete_dest=delete_dest)
   1077             try:
   1078                 self.teststation.run('rm -rf %s' % teststation_temp_dir)
   1079             except (error.GenericHostRunError, error.AutoservSSHTimeout) as e:
   1080                 logging.warn('failed to remove dir %s: %s',
   1081                              teststation_temp_dir, e)
   1082 
   1083             # Source will be copied under dest if either:
   1084             #  1. Source is a directory and doesn't end with /.
   1085             #  2. Source is a file and dest is a directory.
   1086             command = '[ -d %s ]' % source
   1087             source_is_dir = self.run(command,
   1088                                      ignore_status=True).exit_status == 0
   1089             logging.debug('%s on the device %s a directory', source,
   1090                           'is' if source_is_dir else 'is not')
   1091 
   1092             if ((source_is_dir and not source.endswith(os.sep)) or
   1093                 (not source_is_dir and os.path.isdir(dest))):
   1094                 receive_path = os.path.join(dest, os.path.basename(source))
   1095             else:
   1096                 receive_path = dest
   1097 
   1098             if not os.path.exists(receive_path):
   1099                 logging.warning('Expected file %s does not exist; skipping'
   1100                                 ' permissions copy', receive_path)
   1101                 return
   1102 
   1103             # Set the permissions of the received file/dirs.
   1104             if os.path.isdir(receive_path):
   1105                 for root, _dirs, files in os.walk(receive_path):
   1106                     def process(rel_path, default_perm):
   1107                         info = self._get_file_info(os.path.join(source,
   1108                                                                 rel_path))
   1109                         if info['perms'] != 0:
   1110                             target = os.path.join(receive_path, rel_path)
   1111                             if preserve_perm:
   1112                                 os.chmod(target, info['perms'])
   1113                             else:
   1114                                 os.chmod(target, default_perm)
   1115 
   1116                     rel_root = os.path.relpath(root, receive_path)
   1117                     process(rel_root, _DEFAULT_DIR_PERMS)
   1118                     for f in files:
   1119                         process(os.path.join(rel_root, f), _DEFAULT_FILE_PERMS)
   1120             elif preserve_perm:
   1121                 os.chmod(receive_path, source_info['perms'])
   1122             else:
   1123                 os.chmod(receive_path, _DEFAULT_FILE_PERMS)
   1124 
   1125 
   1126     def get_release_version(self):
   1127         """Get the release version from the RELEASE_FILE on the device.
   1128 
   1129         @returns The release string in the RELEASE_FILE.
   1130 
   1131         """
   1132         return self.run_output('getprop %s' % RELEASE_FILE)
   1133 
   1134 
   1135     def get_tmp_dir(self, parent=''):
   1136         """Return a suitable temporary directory on the device.
   1137 
   1138         We ensure this is a subdirectory of /data/local/tmp.
   1139 
   1140         @param parent: Parent directory of the returned tmp dir.
   1141 
   1142         @returns a path to the temp directory on the host.
   1143         """
   1144         # TODO(kevcheng): Refactor the cleanup of tmp dir to be inherited
   1145         #                 from the parent.
   1146         if not parent.startswith(TMP_DIR):
   1147             parent = os.path.join(TMP_DIR, parent.lstrip(os.path.sep))
   1148         self.run('mkdir -p %s' % parent)
   1149         tmp_dir = self.run_output('mktemp -d -p %s' % parent)
   1150         self.tmp_dirs.append(tmp_dir)
   1151         return tmp_dir
   1152 
   1153 
   1154     def get_platform(self):
   1155         """Determine the correct platform label for this host.
   1156 
   1157         @returns a string representing this host's platform.
   1158         """
   1159         return 'adb'
   1160 
   1161 
   1162     def get_os_type(self):
   1163         """Get the OS type of the DUT, e.g., android or brillo.
   1164         """
   1165         if not self._os_type:
   1166             if self.run_output('getprop ro.product.brand') == 'Brillo':
   1167                 self._os_type = OS_TYPE_BRILLO
   1168             else:
   1169                 self._os_type = OS_TYPE_ANDROID
   1170 
   1171         return self._os_type
   1172 
   1173 
   1174     def _forward(self, reverse, args):
   1175         """Execute a forwarding command.
   1176 
   1177         @param reverse: Whether this is reverse forwarding (Boolean).
   1178         @param args: List of command arguments.
   1179         """
   1180         cmd = '%s %s' % ('reverse' if reverse else 'forward', ' '.join(args))
   1181         self.adb_run(cmd)
   1182 
   1183 
   1184     def add_forwarding(self, src, dst, reverse=False, rebind=True):
   1185         """Forward a port between the ADB host and device.
   1186 
   1187         Port specifications are any strings accepted as such by ADB, for
   1188         example 'tcp:8080'.
   1189 
   1190         @param src: Port specification to forward from.
   1191         @param dst: Port specification to forward to.
   1192         @param reverse: Do reverse forwarding from device to host (Boolean).
   1193         @param rebind: Allow rebinding an already bound port (Boolean).
   1194         """
   1195         args = []
   1196         if not rebind:
   1197             args.append('--no-rebind')
   1198         args += [src, dst]
   1199         self._forward(reverse, args)
   1200 
   1201 
   1202     def remove_forwarding(self, src=None, reverse=False):
   1203         """Removes forwarding on port.
   1204 
   1205         @param src: Port specification, or None to remove all forwarding.
   1206         @param reverse: Whether this is reverse forwarding (Boolean).
   1207         """
   1208         args = []
   1209         if src is None:
   1210             args.append('--remove-all')
   1211         else:
   1212             args += ['--remove', src]
   1213         self._forward(reverse, args)
   1214 
   1215 
   1216     def create_ssh_tunnel(self, port, local_port):
   1217         """
   1218         Forwards a port securely through a tunnel process from the server
   1219         to the DUT for RPC server connection.
   1220         Add a 'ADB forward' rule to forward the RPC packets from the AdbHost
   1221         to the DUT.
   1222 
   1223         @param port: remote port on the DUT.
   1224         @param local_port: local forwarding port.
   1225 
   1226         @return: the tunnel process.
   1227         """
   1228         self.add_forwarding('tcp:%s' % port, 'tcp:%s' % port)
   1229         return super(ADBHost, self).create_ssh_tunnel(port, local_port)
   1230 
   1231 
   1232     def disconnect_ssh_tunnel(self, tunnel_proc, port):
   1233         """
   1234         Disconnects a previously forwarded port from the server to the DUT for
   1235         RPC server connection.
   1236         Remove the previously added 'ADB forward' rule to forward the RPC
   1237         packets from the AdbHost to the DUT.
   1238 
   1239         @param tunnel_proc: the original tunnel process returned from
   1240                             |create_ssh_tunnel|.
   1241         @param port: remote port on the DUT.
   1242 
   1243         """
   1244         self.remove_forwarding('tcp:%s' % port)
   1245         super(ADBHost, self).disconnect_ssh_tunnel(tunnel_proc, port)
   1246 
   1247 
   1248     def ensure_bootloader_mode(self):
   1249         """Ensure the device is in bootloader mode.
   1250 
   1251         @raise: error.AutoservError if the device failed to reboot into
   1252                 bootloader mode.
   1253         """
   1254         if self.is_up(command=FASTBOOT_CMD):
   1255             return
   1256         self.adb_run('reboot bootloader')
   1257         if not self.wait_up(command=FASTBOOT_CMD):
   1258             raise error.AutoservError(
   1259                     'Device %s failed to reboot into bootloader mode.' %
   1260                     self.fastboot_serial)
   1261 
   1262 
   1263     def ensure_adb_mode(self, timeout=DEFAULT_WAIT_UP_TIME_SECONDS):
   1264         """Ensure the device is up and can be accessed by adb command.
   1265 
   1266         @param timeout: Time limit in seconds before returning even if the host
   1267                         is not up.
   1268 
   1269         @raise: error.AutoservError if the device failed to reboot into
   1270                 adb mode.
   1271         """
   1272         if self.is_up():
   1273             return
   1274         # Ignore timeout error to allow `fastboot reboot` to fail quietly and
   1275         # check if the device is in adb mode.
   1276         self.fastboot_run('reboot', timeout=timeout, ignore_timeout=True)
   1277         if not self.wait_up(timeout=timeout):
   1278             raise error.AutoservError(
   1279                     'Device %s failed to reboot into adb mode.' %
   1280                     self.adb_serial)
   1281         self._reset_adbd_connection()
   1282 
   1283 
   1284     @classmethod
   1285     def get_build_info_from_build_url(cls, build_url):
   1286         """Get the Android build information from the build url.
   1287 
   1288         @param build_url: The url to use for downloading Android artifacts.
   1289                 pattern: http://$devserver:###/static/branch/target/build_id
   1290 
   1291         @return: A dictionary of build information, including keys:
   1292                  build_target, branch, target, build_id.
   1293         @raise AndroidInstallError: If failed to parse build_url.
   1294         """
   1295         if not build_url:
   1296             raise AndroidInstallError('Need build_url to download image files.')
   1297 
   1298         try:
   1299             match = re.match(DEVSERVER_URL_REGEX, build_url)
   1300             return {'build_target': match.group('BUILD_TARGET'),
   1301                     'branch': match.group('BRANCH'),
   1302                     'target': ('%s-%s' % (match.group('BUILD_TARGET'),
   1303                                           match.group('BUILD_TYPE'))),
   1304                     'build_id': match.group('BUILD_ID')}
   1305         except (AttributeError, IndexError, ValueError) as e:
   1306             raise AndroidInstallError(
   1307                     'Failed to parse build url: %s\nError: %s' % (build_url, e))
   1308 
   1309 
   1310     @retry.retry(error.GenericHostRunError, timeout_min=10)
   1311     def download_file(self, build_url, file, dest_dir, unzip=False,
   1312                       unzip_dest=None):
   1313         """Download the given file from the build url.
   1314 
   1315         @param build_url: The url to use for downloading Android artifacts.
   1316                 pattern: http://$devserver:###/static/branch/target/build_id
   1317         @param file: Name of the file to be downloaded, e.g., boot.img.
   1318         @param dest_dir: Destination folder for the file to be downloaded to.
   1319         @param unzip: If True, unzip the downloaded file.
   1320         @param unzip_dest: Location to unzip the downloaded file to. If not
   1321                            provided, dest_dir is used.
   1322         """
   1323         # Append the file name to the url if build_url is linked to the folder
   1324         # containing the file.
   1325         if not build_url.endswith('/%s' % file):
   1326             src_url = os.path.join(build_url, file)
   1327         else:
   1328             src_url = build_url
   1329         dest_file = os.path.join(dest_dir, file)
   1330         try:
   1331             self.teststation.run('wget -q -O "%s" "%s"' % (dest_file, src_url))
   1332             if unzip:
   1333                 unzip_dest = unzip_dest or dest_dir
   1334                 self.teststation.run('unzip "%s/%s" -x -d "%s"' %
   1335                                      (dest_dir, file, unzip_dest))
   1336         except:
   1337             # Delete the destination file if download failed.
   1338             self.teststation.run('rm -f "%s"' % dest_file)
   1339             raise
   1340 
   1341 
   1342     def stage_android_image_files(self, build_url):
   1343         """Download required image files from the given build_url to a local
   1344         directory in the machine runs fastboot command.
   1345 
   1346         @param build_url: The url to use for downloading Android artifacts.
   1347                 pattern: http://$devserver:###/static/branch/target/build_id
   1348 
   1349         @return: Path to the directory contains image files.
   1350         """
   1351         build_info = self.get_build_info_from_build_url(build_url)
   1352 
   1353         zipped_image_file = ANDROID_IMAGE_FILE_FMT % build_info
   1354         image_dir = self.teststation.get_tmp_dir()
   1355 
   1356         try:
   1357             self.download_file(build_url, zipped_image_file, image_dir,
   1358                                unzip=True)
   1359             images = android_utils.AndroidImageFiles.get_standalone_images(
   1360                     build_info['build_target'])
   1361             for image_file in images:
   1362                 self.download_file(build_url, image_file, image_dir)
   1363 
   1364             return image_dir
   1365         except:
   1366             self.teststation.run('rm -rf %s' % image_dir)
   1367             raise
   1368 
   1369 
   1370     def stage_brillo_image_files(self, build_url):
   1371         """Download required brillo image files from the given build_url to a
   1372         local directory in the machine runs fastboot command.
   1373 
   1374         @param build_url: The url to use for downloading Android artifacts.
   1375                 pattern: http://$devserver:###/static/branch/target/build_id
   1376 
   1377         @return: Path to the directory contains image files.
   1378         """
   1379         build_info = self.get_build_info_from_build_url(build_url)
   1380 
   1381         zipped_image_file = ANDROID_IMAGE_FILE_FMT % build_info
   1382         vendor_partitions_file = BRILLO_VENDOR_PARTITIONS_FILE_FMT % build_info
   1383         image_dir = self.teststation.get_tmp_dir()
   1384 
   1385         try:
   1386             self.download_file(build_url, zipped_image_file, image_dir,
   1387                                unzip=True)
   1388             self.download_file(build_url, vendor_partitions_file, image_dir,
   1389                                unzip=True,
   1390                                unzip_dest=os.path.join(image_dir, 'vendor'))
   1391             return image_dir
   1392         except:
   1393             self.teststation.run('rm -rf %s' % image_dir)
   1394             raise
   1395 
   1396 
   1397     def stage_build_for_install(self, build_name, os_type=None):
   1398         """Stage a build on a devserver and return the build_url and devserver.
   1399 
   1400         @param build_name: a name like git-master/shamu-userdebug/2040953
   1401 
   1402         @returns a tuple with an update URL like:
   1403             http://172.22.50.122:8080/git-master/shamu-userdebug/2040953
   1404             and the devserver instance.
   1405         """
   1406         os_type = os_type or self.get_os_type()
   1407         logging.info('Staging build for installation: %s', build_name)
   1408         devserver = dev_server.AndroidBuildServer.resolve(build_name,
   1409                                                           self.hostname)
   1410         build_name = devserver.translate(build_name)
   1411         branch, target, build_id = utils.parse_launch_control_build(build_name)
   1412         devserver.trigger_download(target, build_id, branch,
   1413                                    os=os_type, synchronous=False)
   1414         return '%s/static/%s' % (devserver.url(), build_name), devserver
   1415 
   1416 
   1417     def install_android(self, build_url, build_local_path=None, wipe=True,
   1418                         flash_all=False, disable_package_verification=True,
   1419                         skip_setup_wizard=True):
   1420         """Install the Android DUT.
   1421 
   1422         Following are the steps used here to provision an android device:
   1423         1. If build_local_path is not set, download the image zip file, e.g.,
   1424            shamu-img-2284311.zip, unzip it.
   1425         2. Run fastboot to install following artifacts:
   1426            bootloader, radio, boot, system, vendor(only if exists)
   1427 
   1428         Repair is not supported for Android devices yet.
   1429 
   1430         @param build_url: The url to use for downloading Android artifacts.
   1431                 pattern: http://$devserver:###/static/$build
   1432         @param build_local_path: The path to a local folder that contains the
   1433                 image files needed to provision the device. Note that the folder
   1434                 is in the machine running adb command, rather than the drone.
   1435         @param wipe: If true, userdata will be wiped before flashing.
   1436         @param flash_all: If True, all img files found in img_path will be
   1437                 flashed. Otherwise, only boot and system are flashed.
   1438 
   1439         @raises AndroidInstallError if any error occurs.
   1440         """
   1441         # If the build is not staged in local server yet, clean up the temp
   1442         # folder used to store image files after the provision is completed.
   1443         delete_build_folder = bool(not build_local_path)
   1444 
   1445         try:
   1446             # Download image files needed for provision to a local directory.
   1447             if not build_local_path:
   1448                 build_local_path = self.stage_android_image_files(build_url)
   1449 
   1450             # Device needs to be in bootloader mode for flashing.
   1451             self.ensure_bootloader_mode()
   1452 
   1453             if wipe:
   1454                 self._fastboot_run_with_retry('-w')
   1455 
   1456             # Get all *.img file in the build_local_path.
   1457             list_file_cmd = 'ls -d %s' % os.path.join(build_local_path, '*.img')
   1458             image_files = self.teststation.run(
   1459                     list_file_cmd).stdout.strip().split()
   1460             images = dict([(os.path.basename(f), f) for f in image_files])
   1461             build_info = self.get_build_info_from_build_url(build_url)
   1462             board = build_info['build_target']
   1463             all_images = (
   1464                     android_utils.AndroidImageFiles.get_standalone_images(board)
   1465                     + android_utils.AndroidImageFiles.get_zipped_images(board))
   1466 
   1467             # Sort images to be flashed, bootloader needs to be the first one.
   1468             bootloader = android_utils.AndroidImageFiles.BOOTLOADER
   1469             sorted_images = sorted(
   1470                     images.items(),
   1471                     key=lambda pair: 0 if pair[0] == bootloader else 1)
   1472             for image, image_file in sorted_images:
   1473                 if image not in all_images:
   1474                     continue
   1475                 logging.info('Flashing %s...', image_file)
   1476                 self._fastboot_run_with_retry('-S 256M flash %s %s' %
   1477                                               (image[:-4], image_file))
   1478                 if image == android_utils.AndroidImageFiles.BOOTLOADER:
   1479                     self.fastboot_run('reboot-bootloader')
   1480                     self.wait_up(command=FASTBOOT_CMD)
   1481         except Exception as e:
   1482             logging.error('Install Android build failed with error: %s', e)
   1483             # Re-raise the exception with type of AndroidInstallError.
   1484             raise AndroidInstallError, sys.exc_info()[1], sys.exc_info()[2]
   1485         finally:
   1486             if delete_build_folder:
   1487                 self.teststation.run('rm -rf %s' % build_local_path)
   1488             timeout = (WAIT_UP_AFTER_WIPE_TIME_SECONDS if wipe else
   1489                        DEFAULT_WAIT_UP_TIME_SECONDS)
   1490             self.ensure_adb_mode(timeout=timeout)
   1491             if disable_package_verification:
   1492                 # TODO: Use a whitelist of devices to do this for rather than
   1493                 # doing it by default.
   1494                 self.disable_package_verification()
   1495             if skip_setup_wizard:
   1496                 try:
   1497                     self.skip_setup_wizard()
   1498                 except error.GenericHostRunError:
   1499                     logging.error('Could not skip setup wizard.')
   1500         logging.info('Successfully installed Android build staged at %s.',
   1501                      build_url)
   1502 
   1503 
   1504     def install_brillo(self, build_url, build_local_path=None):
   1505         """Install the Brillo DUT.
   1506 
   1507         Following are the steps used here to provision an android device:
   1508         1. If build_local_path is not set, download the image zip file, e.g.,
   1509            dragonboard-img-123456.zip, unzip it. And download the vendor
   1510            partition zip file, e.g., dragonboard-vendor_partitions-123456.zip,
   1511            unzip it to vendor folder.
   1512         2. Run provision_device script to install OS images and vendor
   1513            partitions.
   1514 
   1515         @param build_url: The url to use for downloading Android artifacts.
   1516                 pattern: http://$devserver:###/static/$build
   1517         @param build_local_path: The path to a local folder that contains the
   1518                 image files needed to provision the device. Note that the folder
   1519                 is in the machine running adb command, rather than the drone.
   1520 
   1521         @raises AndroidInstallError if any error occurs.
   1522         """
   1523         # If the build is not staged in local server yet, clean up the temp
   1524         # folder used to store image files after the provision is completed.
   1525         delete_build_folder = bool(not build_local_path)
   1526 
   1527         try:
   1528             # Download image files needed for provision to a local directory.
   1529             if not build_local_path:
   1530                 build_local_path = self.stage_brillo_image_files(build_url)
   1531 
   1532             # Device needs to be in bootloader mode for flashing.
   1533             self.ensure_bootloader_mode()
   1534 
   1535             # Run provision_device command to install image files and vendor
   1536             # partitions.
   1537             vendor_partition_dir = os.path.join(build_local_path, 'vendor')
   1538             cmd = (BRILLO_PROVISION_CMD %
   1539                    {'os_image_dir': build_local_path,
   1540                     'vendor_partition_dir': vendor_partition_dir})
   1541             if self.fastboot_serial:
   1542                 cmd += ' -s %s ' % self.fastboot_serial
   1543             self.teststation.run(cmd)
   1544         except Exception as e:
   1545             logging.error('Install Brillo build failed with error: %s', e)
   1546             # Re-raise the exception with type of AndroidInstallError.
   1547             raise AndroidInstallError, sys.exc_info()[1], sys.exc_info()[2]
   1548         finally:
   1549             if delete_build_folder:
   1550                 self.teststation.run('rm -rf %s' % build_local_path)
   1551             self.ensure_adb_mode()
   1552         logging.info('Successfully installed Android build staged at %s.',
   1553                      build_url)
   1554 
   1555 
   1556     @property
   1557     def job_repo_url_attribute(self):
   1558         """Get the host attribute name for job_repo_url, which should append the
   1559         adb serial.
   1560         """
   1561         return '%s_%s' % (constants.JOB_REPO_URL, self.adb_serial)
   1562 
   1563 
   1564     def machine_install(self, build_url=None, build_local_path=None, wipe=True,
   1565                         flash_all=False, os_type=None):
   1566         """Install the DUT.
   1567 
   1568         @param build_url: The url to use for downloading Android artifacts.
   1569                 pattern: http://$devserver:###/static/$build. If build_url is
   1570                 set to None, the code may try _parser.options.image to do the
   1571                 installation. If none of them is set, machine_install will fail.
   1572         @param build_local_path: The path to a local directory that contains the
   1573                 image files needed to provision the device.
   1574         @param wipe: If true, userdata will be wiped before flashing.
   1575         @param flash_all: If True, all img files found in img_path will be
   1576                 flashed. Otherwise, only boot and system are flashed.
   1577 
   1578         @returns A tuple of (image_name, host_attributes).
   1579                 image_name is the name of image installed, e.g.,
   1580                 git_mnc-release/shamu-userdebug/1234
   1581                 host_attributes is a dictionary of (attribute, value), which
   1582                 can be saved to afe_host_attributes table in database. This
   1583                 method returns a dictionary with a single entry of
   1584                 `job_repo_url_[adb_serial]`: devserver_url, where devserver_url
   1585                 is a url to the build staged on devserver.
   1586         """
   1587         os_type = os_type or self.get_os_type()
   1588         if not build_url and self._parser.options.image:
   1589             build_url, _ = self.stage_build_for_install(
   1590                     self._parser.options.image, os_type=os_type)
   1591         if os_type == OS_TYPE_ANDROID:
   1592             self.install_android(
   1593                     build_url=build_url, build_local_path=build_local_path,
   1594                     wipe=wipe, flash_all=flash_all)
   1595         elif os_type == OS_TYPE_BRILLO:
   1596             self.install_brillo(
   1597                     build_url=build_url, build_local_path=build_local_path)
   1598         else:
   1599             raise error.InstallError(
   1600                     'Installation of os type %s is not supported.' %
   1601                     self.get_os_type())
   1602         return (build_url.split('static/')[-1],
   1603                 {self.job_repo_url_attribute: build_url})
   1604 
   1605 
   1606     def list_files_glob(self, path_glob):
   1607         """Get a list of files on the device given glob pattern path.
   1608 
   1609         @param path_glob: The path glob that we want to return the list of
   1610                 files that match the glob.  Relative paths will not work as
   1611                 expected.  Supply an absolute path to get the list of files
   1612                 you're hoping for.
   1613 
   1614         @returns List of files that match the path_glob.
   1615         """
   1616         # This is just in case path_glob has no path separator.
   1617         base_path = os.path.dirname(path_glob) or '.'
   1618         result = self.run('find %s -path \'%s\' -print' %
   1619                           (base_path, path_glob), ignore_status=True)
   1620         if result.exit_status != 0:
   1621             return []
   1622         return result.stdout.splitlines()
   1623 
   1624 
   1625     @retry.retry(error.GenericHostRunError,
   1626                  timeout_min=DISABLE_PACKAGE_VERIFICATION_TIMEOUT_MIN)
   1627     def disable_package_verification(self):
   1628         """Disables package verification on an android device.
   1629 
   1630         Disables the package verificatoin manager allowing any package to be
   1631         installed without checking
   1632         """
   1633         logging.info('Disabling package verification on %s.', self.adb_serial)
   1634         self.check_boot_to_adb_complete()
   1635         self.run('am broadcast -a '
   1636                  'com.google.gservices.intent.action.GSERVICES_OVERRIDE -e '
   1637                  'global:package_verifier_enable 0')
   1638 
   1639 
   1640     @retry.retry(error.GenericHostRunError, timeout_min=APK_INSTALL_TIMEOUT_MIN)
   1641     def install_apk(self, apk, force_reinstall=True):
   1642         """Install the specified apk.
   1643 
   1644         This will install the apk and override it if it's already installed and
   1645         will also allow for downgraded apks.
   1646 
   1647         @param apk: The path to apk file.
   1648         @param force_reinstall: True to reinstall the apk even if it's already
   1649                 installed. Default is set to True.
   1650 
   1651         @returns a CMDResult object.
   1652         """
   1653         try:
   1654             client_utils.poll_for_condition(
   1655                     lambda: self.run('pm list packages',
   1656                                      ignore_status=True).exit_status == 0,
   1657                     timeout=120)
   1658             client_utils.poll_for_condition(
   1659                     lambda: self.run('service list | grep mount',
   1660                                      ignore_status=True).exit_status == 0,
   1661                     timeout=120)
   1662             return self.adb_run('install %s -d %s' %
   1663                                 ('-r' if force_reinstall else '', apk))
   1664         except error.GenericHostRunError:
   1665             self.reboot()
   1666             raise
   1667 
   1668 
   1669     def uninstall_package(self, package):
   1670         """Remove the specified package.
   1671 
   1672         @param package: Android package name.
   1673 
   1674         @raises GenericHostRunError: uninstall failed
   1675         """
   1676         result = self.adb_run('uninstall %s' % package)
   1677 
   1678         if self.is_apk_installed(package):
   1679             raise error.GenericHostRunError('Uninstall of "%s" failed.'
   1680                                             % package, result)
   1681 
   1682     def save_info(self, results_dir, include_build_info=True):
   1683         """Save info about this device.
   1684 
   1685         @param results_dir: The local directory to store the info in.
   1686         @param include_build_info: If true this will include the build info
   1687                                    artifact.
   1688         """
   1689         if include_build_info:
   1690             teststation_temp_dir = self.teststation.get_tmp_dir()
   1691 
   1692             try:
   1693                 info = self.host_info_store.get()
   1694             except host_info.StoreError:
   1695                 logging.warning(
   1696                     'Device %s could not get repo url for build info.',
   1697                     self.adb_serial)
   1698                 return
   1699 
   1700             job_repo_url = info.attributes.get(self.job_repo_url_attribute, '')
   1701             if not job_repo_url:
   1702                 logging.warning(
   1703                     'Device %s could not get repo url for build info.',
   1704                     self.adb_serial)
   1705                 return
   1706 
   1707             build_info = ADBHost.get_build_info_from_build_url(job_repo_url)
   1708 
   1709             target = build_info['target']
   1710             branch = build_info['branch']
   1711             build_id = build_info['build_id']
   1712 
   1713             devserver_url = dev_server.AndroidBuildServer.get_server_url(
   1714                     job_repo_url)
   1715             ds = dev_server.AndroidBuildServer(devserver_url)
   1716 
   1717             ds.trigger_download(target, build_id, branch, files='BUILD_INFO',
   1718                                 synchronous=True)
   1719 
   1720             pull_base_url = ds.get_pull_url(target, build_id, branch)
   1721 
   1722             source_path = os.path.join(teststation_temp_dir, 'BUILD_INFO')
   1723 
   1724             self.download_file(pull_base_url, 'BUILD_INFO',
   1725                                teststation_temp_dir)
   1726 
   1727             destination_path = os.path.join(
   1728                     results_dir, 'BUILD_INFO-%s' % self.adb_serial)
   1729             self.teststation.get_file(source_path, destination_path)
   1730 
   1731 
   1732 
   1733     @retry.retry(error.GenericHostRunError, timeout_min=0.2)
   1734     def _confirm_apk_installed(self, package_name):
   1735         """Confirm if apk is already installed with the given name.
   1736 
   1737         `pm list packages` command is not reliable some time. The retry helps to
   1738         reduce the chance of false negative.
   1739 
   1740         @param package_name: Name of the package, e.g., com.android.phone.
   1741 
   1742         @raise AutoservRunError: If the package is not found or pm list command
   1743                 failed for any reason.
   1744         """
   1745         name = 'package:%s' % package_name
   1746         self.adb_run('shell pm list packages | grep -w "%s"' % name)
   1747 
   1748 
   1749     def is_apk_installed(self, package_name):
   1750         """Check if apk is already installed with the given name.
   1751 
   1752         @param package_name: Name of the package, e.g., com.android.phone.
   1753 
   1754         @return: True if package is installed. False otherwise.
   1755         """
   1756         try:
   1757             self._confirm_apk_installed(package_name)
   1758             return True
   1759         except:
   1760             return False
   1761 
   1762     @retry.retry(error.GenericHostRunError, timeout_min=1)
   1763     def skip_setup_wizard(self):
   1764         """Skip the setup wizard.
   1765 
   1766         Skip the starting setup wizard that normally shows up on android.
   1767         """
   1768         logging.info('Skipping setup wizard on %s.', self.adb_serial)
   1769         self.check_boot_to_adb_complete()
   1770         result = self.run('am start -n com.google.android.setupwizard/'
   1771                           '.SetupWizardExitActivity')
   1772 
   1773         if result.exit_status != 0:
   1774             if result.stdout.startswith('ADB_CMD_OUTPUT:255'):
   1775                 # If the result returns ADB_CMD_OUTPUT:255, then run the above
   1776                 # as root.
   1777                 logging.debug('Need root access to bypass setup wizard.')
   1778                 self._restart_adbd_with_root_permissions()
   1779                 result = self.run('am start -n com.google.android.setupwizard/'
   1780                                   '.SetupWizardExitActivity')
   1781 
   1782             if result.stdout == 'ADB_CMD_OUTPUT:0':
   1783                 # If the result returns ADB_CMD_OUTPUT:0, Error type 3, then the
   1784                 # setup wizard does not exist, so we do not have to bypass it.
   1785                 if result.stderr and not \
   1786                         result.stderr.startswith('Error type 3\n'):
   1787                     logging.error('Unrecoverable skip setup wizard failure:'
   1788                                   ' %s', result.stderr)
   1789                     raise error.TestError()
   1790                 logging.debug('Skip setup wizard received harmless error: '
   1791                               'No setup to bypass.')
   1792 
   1793         logging.debug('Bypass setup wizard was successful.')
   1794 
   1795 
   1796     def get_attributes_to_clear_before_provision(self):
   1797         """Get a list of attributes to be cleared before machine_install starts.
   1798         """
   1799         return [self.job_repo_url_attribute]
   1800 
   1801 
   1802     def get_labels(self):
   1803         """Return a list of the labels gathered from the devices connected.
   1804 
   1805         @return: A list of strings that denote the labels from all the devices
   1806                  connected.
   1807         """
   1808         return self.labels.get_labels(self)
   1809 
   1810 
   1811     def update_labels(self):
   1812         """Update the labels for this testbed."""
   1813         self.labels.update_labels(self)
   1814 
   1815 
   1816     def stage_server_side_package(self, image=None):
   1817         """Stage autotest server-side package on devserver.
   1818 
   1819         @param image: A build name, e.g., git_mnc_dev/shamu-eng/123
   1820 
   1821         @return: A url to the autotest server-side package.
   1822 
   1823         @raise: error.AutoservError if fail to locate the build to test with, or
   1824                 fail to stage server-side package.
   1825         """
   1826         # If enable_drone_in_restricted_subnet is False, do not set hostname
   1827         # in devserver.resolve call, so a devserver in non-restricted subnet
   1828         # is picked to stage autotest server package for drone to download.
   1829         hostname = self.hostname
   1830         if not utils.ENABLE_DRONE_IN_RESTRICTED_SUBNET:
   1831             hostname = None
   1832         if image:
   1833             ds = dev_server.AndroidBuildServer.resolve(image, hostname)
   1834         else:
   1835             info = self.host_info_store.get()
   1836             job_repo_url = info.attributes.get(self.job_repo_url_attribute)
   1837             if job_repo_url is not None:
   1838                 devserver_url, image = (
   1839                         tools.get_devserver_build_from_package_url(
   1840                                 job_repo_url, True))
   1841                 # If enable_drone_in_restricted_subnet is True, use the
   1842                 # existing devserver. Otherwise, resolve a new one in
   1843                 # non-restricted subnet.
   1844                 if utils.ENABLE_DRONE_IN_RESTRICTED_SUBNET:
   1845                     ds = dev_server.AndroidBuildServer(devserver_url)
   1846                 else:
   1847                     ds = dev_server.AndroidBuildServer.resolve(image)
   1848             elif info.build is not None:
   1849                 ds = dev_server.AndroidBuildServer.resolve(info.build, hostname)
   1850             else:
   1851                 raise error.AutoservError(
   1852                         'Failed to stage server-side package. The host has '
   1853                         'no job_report_url attribute or version label.')
   1854 
   1855         branch, target, build_id = utils.parse_launch_control_build(image)
   1856         build_target, _ = utils.parse_launch_control_target(target)
   1857 
   1858         # For any build older than MIN_VERSION_SUPPORT_SSP, server side
   1859         # packaging is not supported.
   1860         try:
   1861             # Some build ids may have special character before the actual
   1862             # number, skip such characters.
   1863             actual_build_id = build_id
   1864             if build_id.startswith('P'):
   1865                 actual_build_id = build_id[1:]
   1866             if int(actual_build_id) < self.MIN_VERSION_SUPPORT_SSP:
   1867                 raise error.AutoservError(
   1868                         'Build %s is older than %s. Server side packaging is '
   1869                         'disabled.' % (image, self.MIN_VERSION_SUPPORT_SSP))
   1870         except ValueError:
   1871             raise error.AutoservError(
   1872                     'Failed to compare build id in %s with the minimum '
   1873                     'version that supports server side packaging. Server '
   1874                     'side packaging is disabled.' % image)
   1875 
   1876         ds.stage_artifacts(target, build_id, branch,
   1877                            artifacts=['autotest_server_package'])
   1878         autotest_server_package_name = (AUTOTEST_SERVER_PACKAGE_FILE_FMT %
   1879                                         {'build_target': build_target,
   1880                                          'build_id': build_id})
   1881         return '%s/static/%s/%s' % (ds.url(), image,
   1882                                     autotest_server_package_name)
   1883 
   1884 
   1885     def _sync_time(self):
   1886         """Approximate synchronization of time between host and ADB device.
   1887 
   1888         This sets the ADB/Android device's clock to approximately the same
   1889         time as the Autotest host for the purposes of comparing Android system
   1890         logs such as logcat to logs from the Autotest host system.
   1891         """
   1892         command = 'date '
   1893         sdk_version = int(self.run('getprop %s' % SDK_FILE).stdout)
   1894         if sdk_version < 23:
   1895             # Android L and earlier use this format: date -s (format).
   1896             command += ('-s %s' %
   1897                         datetime.datetime.now().strftime('%Y%m%d.%H%M%S'))
   1898         else:
   1899             # Android M and later use this format: date -u (format).
   1900             command += ('-u %s' %
   1901                         datetime.datetime.utcnow().strftime('%m%d%H%M%Y.%S'))
   1902         self.run(command, timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS,
   1903                  ignore_timeout=True)
   1904 
   1905 
   1906     def _enable_native_crash_logging(self):
   1907         """Enable native (non-Java) crash logging.
   1908         """
   1909         if self.get_os_type() == OS_TYPE_ANDROID:
   1910             self._enable_android_native_crash_logging()
   1911 
   1912 
   1913     def _enable_brillo_native_crash_logging(self):
   1914         """Enables native crash logging for a Brillo DUT.
   1915         """
   1916         try:
   1917             self.run('touch /data/misc/metrics/enabled',
   1918                      timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS,
   1919                      ignore_timeout=True)
   1920             # If running, crash_sender will delete crash files every hour.
   1921             self.run('stop crash_sender',
   1922                      timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS,
   1923                      ignore_timeout=True)
   1924         except error.GenericHostRunError as e:
   1925             logging.warn(e)
   1926             logging.warn('Failed to enable Brillo native crash logging.')
   1927 
   1928 
   1929     def _enable_android_native_crash_logging(self):
   1930         """Enables native crash logging for an Android DUT.
   1931         """
   1932         # debuggerd should be enabled by default on Android.
   1933         result = self.run('pgrep debuggerd',
   1934                           timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS,
   1935                           ignore_timeout=True, ignore_status=True)
   1936         if not result or result.exit_status != 0:
   1937             logging.debug('Unable to confirm that debuggerd is running.')
   1938 
   1939 
   1940     def _collect_crash_logs(self):
   1941         """Copies crash log files from the DUT to the drone.
   1942         """
   1943         if self.get_os_type() == OS_TYPE_BRILLO:
   1944             self._collect_crash_logs_dut(BRILLO_NATIVE_CRASH_LOG_DIR)
   1945         elif self.get_os_type() == OS_TYPE_ANDROID:
   1946             self._collect_crash_logs_dut(ANDROID_TOMBSTONE_CRASH_LOG_DIR)
   1947 
   1948 
   1949     def _collect_crash_logs_dut(self, log_directory):
   1950         """Copies native crash logs from the Android/Brillo DUT to the drone.
   1951 
   1952         @param log_directory: absolute path of the directory on the DUT where
   1953                 log files are stored.
   1954         """
   1955         files = None
   1956         try:
   1957             result = self.run('find %s -maxdepth 1 -type f' % log_directory,
   1958                               timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS)
   1959             files = result.stdout.strip().split()
   1960         except (error.GenericHostRunError, error.AutoservSSHTimeout,
   1961                 error.CmdTimeoutError):
   1962             logging.debug('Unable to call find %s, unable to find crash logs',
   1963                           log_directory)
   1964         if not files:
   1965             logging.debug('There are no crash logs on the DUT.')
   1966             return
   1967 
   1968         crash_dir = os.path.join(self.job.resultdir, 'crash')
   1969         try:
   1970             os.mkdir(crash_dir)
   1971         except OSError as e:
   1972             if e.errno != errno.EEXIST:
   1973                 raise e
   1974 
   1975         for f in files:
   1976             logging.debug('DUT native crash file produced: %s', f)
   1977             dest = os.path.join(crash_dir, os.path.basename(f))
   1978             # We've had cases where the crash file on the DUT has permissions
   1979             # "000". Let's override permissions to make them sane for the user
   1980             # collecting the crashes.
   1981             self.get_file(source=f, dest=dest, preserve_perm=False)
   1982