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