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