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
5import datetime
6import errno
7import functools
8import logging
9import os
10import re
11import signal
12import stat
13import sys
14import time
15
16import common
17
18from autotest_lib.client.bin import utils as client_utils
19from autotest_lib.client.common_lib import android_utils
20from autotest_lib.client.common_lib import error
21from autotest_lib.client.common_lib import global_config
22from autotest_lib.client.common_lib.cros import dev_server
23from autotest_lib.client.common_lib.cros import retry
24from autotest_lib.server import afe_utils
25from autotest_lib.server import autoserv_parser
26from autotest_lib.server import constants as server_constants
27from autotest_lib.server import utils
28from autotest_lib.server.cros import provision
29from autotest_lib.server.cros.dynamic_suite import tools
30from autotest_lib.server.cros.dynamic_suite import constants
31from autotest_lib.server.hosts import abstract_ssh
32from autotest_lib.server.hosts import adb_label
33from autotest_lib.server.hosts import base_label
34from autotest_lib.server.hosts import teststation_host
35
36
37CONFIG = global_config.global_config
38
39ADB_CMD = 'adb'
40FASTBOOT_CMD = 'fastboot'
41SHELL_CMD = 'shell'
42# Some devices have no serial, then `adb serial` has output such as:
43# (no serial number)  device
44# ??????????          device
45DEVICE_NO_SERIAL_MSG = '(no serial number)'
46DEVICE_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
52DEVICE_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)')
57CMD_OUTPUT_PREFIX = 'ADB_CMD_OUTPUT'
58CMD_OUTPUT_REGEX = ('(?P<OUTPUT>[\s\S]*)%s:(?P<EXIT_CODE>\d{1,3})' %
59                    CMD_OUTPUT_PREFIX)
60RELEASE_FILE = 'ro.build.version.release'
61BOARD_FILE = 'ro.product.device'
62SDK_FILE = 'ro.build.version.sdk'
63LOGCAT_FILE_FMT = 'logcat_%s.log'
64TMP_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
67FILE_INFO_REGEX = '^(?P<TYPE>[dl-])(?P<PERMS>[rwx-]{9})'
68FILE_SYMLINK_REGEX = '^.*-> (?P<SYMLINK>.+)'
69# List of the perm stats indexed by the order they are listed in the example
70# supplied above.
71FILE_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.
76DEFAULT_WAIT_DOWN_TIME_SECONDS = 10
77# Default maximum number of seconds to wait for a device to be up.
78DEFAULT_WAIT_UP_TIME_SECONDS = 300
79# Maximum number of seconds to wait for a device to be up after it's wiped.
80WAIT_UP_AFTER_WIPE_TIME_SECONDS = 1200
81
82# Default timeout for retrying adb/fastboot command.
83DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS = 10
84
85OS_TYPE_ANDROID = 'android'
86OS_TYPE_BRILLO = 'brillo'
87
88# Regex to parse build name to get the detailed build information.
89BUILD_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
93DEVSERVER_URL_REGEX = '.*/%s/*' % BUILD_REGEX
94
95ANDROID_IMAGE_FILE_FMT = '%(build_target)s-img-%(build_id)s.zip'
96
97BRILLO_VENDOR_PARTITIONS_FILE_FMT = (
98        '%(build_target)s-vendor_partitions-%(build_id)s.zip')
99AUTOTEST_SERVER_PACKAGE_FILE_FMT = (
100        '%(build_target)s-autotest_server_package-%(build_id)s.tar.bz2')
101ADB_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.
108BRILLO_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.
114DEFAULT_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.
121PROPERTY_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.
125APK_INSTALL_TIMEOUT_MIN = 5
126
127# The amount of time to wait for package verification to be turned off.
128DISABLE_PACKAGE_VERIFICATION_TIMEOUT_MIN = 1
129
130# Directory where (non-Brillo) Android stores tombstone crash logs.
131ANDROID_TOMBSTONE_CRASH_LOG_DIR = '/data/tombstones'
132# Directory where Brillo stores crash logs for native (non-Java) crashes.
133BRILLO_NATIVE_CRASH_LOG_DIR = '/data/misc/crash_reporter/crash'
134
135# A specific string value to return when a timeout has occurred.
136TIMEOUT_MSG = 'TIMEOUT_OCCURRED'
137
138class AndroidInstallError(error.InstallError):
139    """Generic error for Android installation related exceptions."""
140
141
142class 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