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