• 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 logging
8import os
9import re
10import signal
11import stat
12import time
13
14import common
15
16from autotest_lib.client.bin import utils as client_utils
17from autotest_lib.client.common_lib import android_utils
18from autotest_lib.client.common_lib import error
19from autotest_lib.client.common_lib import global_config
20from autotest_lib.client.common_lib.cros import retry
21from autotest_lib.server import constants as server_constants
22from autotest_lib.server import utils
23from autotest_lib.server.cros.dynamic_suite import constants
24from autotest_lib.server.hosts import abstract_ssh
25from autotest_lib.server.hosts import adb_label
26from autotest_lib.server.hosts import base_label
27from autotest_lib.server.hosts import teststation_host
28
29
30CONFIG = global_config.global_config
31
32ADB_CMD = 'adb'
33FASTBOOT_CMD = 'fastboot'
34SHELL_CMD = 'shell'
35# Some devices have no serial, then `adb serial` has output such as:
36# (no serial number)  device
37# ??????????          device
38DEVICE_NO_SERIAL_MSG = '(no serial number)'
39DEVICE_NO_SERIAL_TAG = '<NO_SERIAL>'
40# Regex to find an adb device. Examples:
41# 0146B5580B01801B    device
42# 018e0ecb20c97a62    device
43# 172.22.75.141:5555  device
44# localhost:22        device
45DEVICE_FINDER_REGEX = (r'^(?P<SERIAL>([\w-]+)|((tcp:)?' +
46                       '\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}([:]5555)?)|' +
47                       '((tcp:)?localhost([:]22)?)|' +
48                       re.escape(DEVICE_NO_SERIAL_MSG) +
49                       r')[ \t]+(?:device|fastboot)')
50CMD_OUTPUT_PREFIX = 'ADB_CMD_OUTPUT'
51CMD_OUTPUT_REGEX = ('(?P<OUTPUT>[\s\S]*)%s:(?P<EXIT_CODE>\d{1,3})' %
52                    CMD_OUTPUT_PREFIX)
53RELEASE_FILE = 'ro.build.version.release'
54BOARD_FILE = 'ro.product.device'
55SDK_FILE = 'ro.build.version.sdk'
56LOGCAT_FILE_FMT = 'logcat_%s.log'
57TMP_DIR = '/data/local/tmp'
58# Regex to pull out file type, perms and symlink. Example:
59# lrwxrwx--- 1 6 root system 2015-09-12 19:21 blah_link -> ./blah
60FILE_INFO_REGEX = '^(?P<TYPE>[dl-])(?P<PERMS>[rwx-]{9})'
61FILE_SYMLINK_REGEX = '^.*-> (?P<SYMLINK>.+)'
62# List of the perm stats indexed by the order they are listed in the example
63# supplied above.
64FILE_PERMS_FLAGS = [stat.S_IRUSR, stat.S_IWUSR, stat.S_IXUSR,
65                    stat.S_IRGRP, stat.S_IWGRP, stat.S_IXGRP,
66                    stat.S_IROTH, stat.S_IWOTH, stat.S_IXOTH]
67
68# Default maximum number of seconds to wait for a device to be down.
69DEFAULT_WAIT_DOWN_TIME_SECONDS = 10
70# Default maximum number of seconds to wait for a device to be up.
71DEFAULT_WAIT_UP_TIME_SECONDS = 300
72
73# Default timeout for retrying adb/fastboot command.
74DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS = 10
75
76OS_TYPE_ANDROID = 'android'
77OS_TYPE_BRILLO = 'brillo'
78
79ADB_DEVICE_PREFIXES = ['product:', 'model:', 'device:']
80
81# Default permissions for files/dirs copied from the device.
82_DEFAULT_FILE_PERMS = 0o600
83_DEFAULT_DIR_PERMS = 0o700
84
85# Constants for getprop return value for a given property.
86PROPERTY_VALUE_TRUE = '1'
87
88# Timeout used for retrying installing apk. After reinstall apk failed, we try
89# to reboot the device and try again.
90APK_INSTALL_TIMEOUT_MIN = 5
91
92# The amount of time to wait for package verification to be turned off.
93DISABLE_PACKAGE_VERIFICATION_TIMEOUT_MIN = 1
94
95# Directory where (non-Brillo) Android stores tombstone crash logs.
96ANDROID_TOMBSTONE_CRASH_LOG_DIR = '/data/tombstones'
97# Directory where Brillo stores crash logs for native (non-Java) crashes.
98BRILLO_NATIVE_CRASH_LOG_DIR = '/data/misc/crash_reporter/crash'
99
100# A specific string value to return when a timeout has occurred.
101TIMEOUT_MSG = 'TIMEOUT_OCCURRED'
102
103class ADBHost(abstract_ssh.AbstractSSHHost):
104    """This class represents a host running an ADB server."""
105
106    @staticmethod
107    def check_host(host, timeout=10):
108        """
109        Check if the given host is an adb host.
110
111        If SSH connectivity can't be established, check_host will try to use
112        user 'adb' as well. If SSH connectivity still can't be established
113        then the original SSH user is restored.
114
115        @param host: An ssh host representing a device.
116        @param timeout: The timeout for the run command.
117
118
119        @return: True if the host device has adb.
120
121        @raises AutoservRunError: If the command failed.
122        @raises AutoservSSHTimeout: Ssh connection has timed out.
123        """
124        # host object may not have user attribute if it's a LocalHost object.
125        current_user = host.user if hasattr(host, 'user') else None
126        try:
127            if not (host.hostname == 'localhost' or
128                    host.verify_ssh_user_access()):
129                host.user = 'adb'
130            result = host.run(
131                    'test -f %s' % server_constants.ANDROID_TESTER_FILEFLAG,
132                    timeout=timeout)
133        except (error.GenericHostRunError, error.AutoservSSHTimeout):
134            if current_user is not None:
135                host.user = current_user
136            return False
137        return result.exit_status == 0
138
139
140    def _initialize(self, hostname='localhost', serials=None,
141                    adb_serial=None, fastboot_serial=None,
142                    teststation=None, *args, **dargs):
143        """Initialize an ADB Host.
144
145        This will create an ADB Host. Hostname should always refer to the
146        test station connected to an Android DUT. This will be the DUT
147        to test with.  If there are multiple, serial must be specified or an
148        exception will be raised.
149
150        @param hostname: Hostname of the machine running ADB.
151        @param serials: DEPRECATED (to be removed)
152        @param adb_serial: An ADB device serial. If None, assume a single
153                           device is attached (and fail otherwise).
154        @param fastboot_serial: A fastboot device serial. If None, defaults to
155                                the ADB serial (or assumes a single device if
156                                the latter is None).
157        @param teststation: The teststation object ADBHost should use.
158        """
159        # Sets up the is_client_install_supported field.
160        super(ADBHost, self)._initialize(hostname=hostname,
161                                         is_client_install_supported=False,
162                                         *args, **dargs)
163
164        self.tmp_dirs = []
165        self.labels = base_label.LabelRetriever(adb_label.ADB_LABELS)
166        adb_serial = adb_serial or self._afe_host.attributes.get('serials')
167        fastboot_serial = (fastboot_serial or
168                self._afe_host.attributes.get('fastboot_serial'))
169
170        self.adb_serial = adb_serial
171        if adb_serial:
172            adb_prefix = any(adb_serial.startswith(p)
173                             for p in ADB_DEVICE_PREFIXES)
174            self.fastboot_serial = (fastboot_serial or
175                    ('tcp:%s' % adb_serial.split(':')[0] if
176                    ':' in adb_serial and not adb_prefix else adb_serial))
177            self._use_tcpip = ':' in adb_serial and not adb_prefix
178        else:
179            self.fastboot_serial = fastboot_serial or adb_serial
180            self._use_tcpip = False
181        self.teststation = (teststation if teststation
182                else teststation_host.create_teststationhost(
183                        hostname=hostname,
184                        user=self.user,
185                        password=self.password,
186                        port=self.port
187                ))
188
189        msg ='Initializing ADB device on host: %s' % hostname
190        if self.adb_serial:
191            msg += ', ADB serial: %s' % self.adb_serial
192        if self.fastboot_serial:
193            msg += ', fastboot serial: %s' % self.fastboot_serial
194        logging.debug(msg)
195
196        self._os_type = None
197
198
199    def _connect_over_tcpip_as_needed(self):
200        """Connect to the ADB device over TCP/IP if so configured."""
201        if not self._use_tcpip:
202            return
203        logging.debug('Connecting to device over TCP/IP')
204        self.adb_run('connect %s' % self.adb_serial)
205
206
207    def _restart_adbd_with_root_permissions(self):
208        """Restarts the adb daemon with root permissions."""
209        @retry.retry(error.GenericHostRunError, timeout_min=20/60.0,
210                     delay_sec=1)
211        def run_adb_root():
212            """Run command `adb root`."""
213            self.adb_run('root')
214
215        # adb command may flake with error "device not found". Retry the root
216        # command to reduce the chance of flake.
217        run_adb_root()
218        # TODO(ralphnathan): Remove this sleep once b/19749057 is resolved.
219        time.sleep(1)
220        self._connect_over_tcpip_as_needed()
221        self.adb_run('wait-for-device')
222
223
224    def _set_tcp_port(self):
225        """Ensure the device remains in tcp/ip mode after a reboot."""
226        if not self._use_tcpip:
227            return
228        port = self.adb_serial.split(':')[-1]
229        self.run('setprop persist.adb.tcp.port %s' % port)
230
231
232    def _reset_adbd_connection(self):
233        """Resets adbd connection to the device after a reboot/initialization"""
234        self._connect_over_tcpip_as_needed()
235        self._restart_adbd_with_root_permissions()
236        self._set_tcp_port()
237
238
239    # pylint: disable=missing-docstring
240    def adb_run(self, command, **kwargs):
241        """Runs an adb command.
242
243        This command will launch on the test station.
244
245        Refer to _device_run method for docstring for parameters.
246        """
247        # Suppresses 'adb devices' from printing to the logs, which often
248        # causes large log files.
249        if command == "devices":
250            kwargs['verbose'] = False
251        return self._device_run(ADB_CMD, command, **kwargs)
252
253
254    # pylint: disable=missing-docstring
255    def fastboot_run(self, command, **kwargs):
256        """Runs an fastboot command.
257
258        This command will launch on the test station.
259
260        Refer to _device_run method for docstring for parameters.
261        """
262        return self._device_run(FASTBOOT_CMD, command, **kwargs)
263
264
265    def _log_adb_pid(self):
266        """Log the pid of adb server.
267
268        adb's server is known to have bugs and randomly restart. BY logging
269        the server's pid it will allow us to better debug random adb failures.
270        """
271        adb_pid = self.teststation.run('pgrep -f "adb.*server"')
272        logging.debug('ADB Server PID: %s', adb_pid.stdout)
273
274
275    def _device_run(self, function, command, shell=False,
276                    timeout=3600, ignore_status=False, ignore_timeout=False,
277                    stdout=utils.TEE_TO_LOGS, stderr=utils.TEE_TO_LOGS,
278                    connect_timeout=30, options='', stdin=None, verbose=True,
279                    require_sudo=False, args=()):
280        """Runs a command named `function` on the test station.
281
282        This command will launch on the test station.
283
284        @param command: Command to run.
285        @param shell: If true the command runs in the adb shell otherwise if
286                      False it will be passed directly to adb. For example
287                      reboot with shell=False will call 'adb reboot'. This
288                      option only applies to function adb.
289        @param timeout: Time limit in seconds before attempting to
290                        kill the running process. The run() function
291                        will take a few seconds longer than 'timeout'
292                        to complete if it has to kill the process.
293        @param ignore_status: Do not raise an exception, no matter
294                              what the exit code of the command is.
295        @param ignore_timeout: Bool True if command timeouts should be
296                               ignored.  Will return None on command timeout.
297        @param stdout: Redirect stdout.
298        @param stderr: Redirect stderr.
299        @param connect_timeout: Connection timeout (in seconds)
300        @param options: String with additional ssh command options
301        @param stdin: Stdin to pass (a string) to the executed command
302        @param require_sudo: True to require sudo to run the command. Default is
303                             False.
304        @param args: Sequence of strings to pass as arguments to command by
305                     quoting them in " and escaping their contents if
306                     necessary.
307
308        @returns a CMDResult object.
309        """
310        if function == ADB_CMD:
311            serial = self.adb_serial
312        elif function == FASTBOOT_CMD:
313            serial = self.fastboot_serial
314        else:
315            raise NotImplementedError('Mode %s is not supported' % function)
316
317        if function != ADB_CMD and shell:
318            raise error.CmdError('shell option is only applicable to `adb`.')
319
320        client_side_cmd = 'timeout --signal=%d %d %s' % (signal.SIGKILL,
321                                                         timeout + 1, function)
322        cmd = '%s%s ' % ('sudo -n ' if require_sudo else '', client_side_cmd)
323
324        if serial:
325            cmd += '-s %s ' % serial
326
327        if shell:
328            cmd += '%s ' % SHELL_CMD
329        cmd += command
330
331        self._log_adb_pid()
332
333        if verbose:
334            logging.debug('Command: %s', cmd)
335
336        return self.teststation.run(cmd, timeout=timeout,
337                ignore_status=ignore_status,
338                ignore_timeout=ignore_timeout, stdout_tee=stdout,
339                stderr_tee=stderr, options=options, stdin=stdin,
340                connect_timeout=connect_timeout, args=args)
341
342
343    def _run_output_with_retry(self, cmd):
344        """Call run_output method for the given command with retry.
345
346        adb command can be flaky some time, and the command may fail or return
347        empty string. It may take several retries until a value can be returned.
348
349        @param cmd: The command to run.
350
351        @return: Return value from the command after retry.
352        """
353        try:
354            return client_utils.poll_for_condition(
355                    lambda: self.run_output(cmd, ignore_status=True),
356                    timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS,
357                    sleep_interval=0.5,
358                    desc='Get return value for command `%s`' % cmd)
359        except client_utils.TimeoutError:
360            return ''
361
362
363    def get_product_name(self):
364        """Get the product name of the device, eg., shamu, bat"""
365        return self.run_output('getprop %s' % BOARD_FILE)
366
367    def get_board_name(self):
368        """Get the name of the board, e.g., shamu, bat_land etc.
369        """
370        product = self.get_product_name()
371        return android_utils.AndroidAliases.get_board_name(product)
372
373
374    def get_board(self):
375        """Determine the correct board label for the device.
376
377        @returns a string representing this device's board.
378        """
379        board = self.get_board_name()
380        board_os = self.get_os_type()
381        return constants.BOARD_PREFIX + '-'.join([board_os, board])
382
383
384    def job_start(self):
385        """Overload of parent which intentionally doesn't log certain files.
386
387        The parent implementation attempts to log certain Linux files, such as
388        /var/log, which do not exist on Android, thus there is no call to the
389        parent's job_start().  The sync call is made so that logcat logs can be
390        approximately matched to server logs.
391        """
392        # Try resetting the ADB daemon on the device, however if we are
393        # creating the host to do a repair job, the device maybe inaccesible
394        # via ADB.
395        try:
396            self._reset_adbd_connection()
397        except error.GenericHostRunError as e:
398            logging.error('Unable to reset the device adb daemon connection: '
399                          '%s.', e)
400
401        if self.is_up():
402            self._sync_time()
403            self._enable_native_crash_logging()
404
405
406    def run(self, command, timeout=3600, ignore_status=False,
407            ignore_timeout=False, stdout_tee=utils.TEE_TO_LOGS,
408            stderr_tee=utils.TEE_TO_LOGS, connect_timeout=30, options='',
409            stdin=None, verbose=True, args=()):
410        """Run a command on the adb device.
411
412        The command given will be ran directly on the adb device; for example
413        'ls' will be ran as: 'abd shell ls'
414
415        @param command: The command line string.
416        @param timeout: Time limit in seconds before attempting to
417                        kill the running process. The run() function
418                        will take a few seconds longer than 'timeout'
419                        to complete if it has to kill the process.
420        @param ignore_status: Do not raise an exception, no matter
421                              what the exit code of the command is.
422        @param ignore_timeout: Bool True if command timeouts should be
423                               ignored.  Will return None on command timeout.
424        @param stdout_tee: Redirect stdout.
425        @param stderr_tee: Redirect stderr.
426        @param connect_timeout: Connection timeout (in seconds).
427        @param options: String with additional ssh command options.
428        @param stdin: Stdin to pass (a string) to the executed command
429        @param args: Sequence of strings to pass as arguments to command by
430                     quoting them in " and escaping their contents if
431                     necessary.
432
433        @returns A CMDResult object or None if the call timed out and
434                 ignore_timeout is True.
435
436        @raises AutoservRunError: If the command failed.
437        @raises AutoservSSHTimeout: Ssh connection has timed out.
438        """
439        command = ('"%s; echo %s:\$?"' %
440                   (utils.sh_escape(command), CMD_OUTPUT_PREFIX))
441
442        def _run():
443            """Run the command and try to parse the exit code.
444            """
445            result = self.adb_run(
446                    command, shell=True, timeout=timeout,
447                    ignore_status=ignore_status, ignore_timeout=ignore_timeout,
448                    stdout=stdout_tee, stderr=stderr_tee,
449                    connect_timeout=connect_timeout, options=options,
450                    stdin=stdin, verbose=verbose, args=args)
451            if not result:
452                # In case of timeouts. Set the return to a specific string
453                # value. That way the caller of poll_for_condition knows
454                # a timeout occurs and should return None. Return None here will
455                # lead to the command to be retried.
456                return TIMEOUT_MSG
457            parse_output = re.match(CMD_OUTPUT_REGEX, result.stdout)
458            if not parse_output and not ignore_status:
459                logging.error('Failed to parse the exit code for command: `%s`.'
460                              ' result: `%s`', command, result.stdout)
461                return None
462            elif parse_output:
463                result.stdout = parse_output.group('OUTPUT')
464                result.exit_status = int(parse_output.group('EXIT_CODE'))
465                if result.exit_status != 0 and not ignore_status:
466                    raise error.AutoservRunError(command, result)
467            return result
468
469        result = client_utils.poll_for_condition(
470                lambda: _run(),
471                timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS,
472                sleep_interval=0.5,
473                desc='Run command `%s`' % command)
474        return None if result == TIMEOUT_MSG else result
475
476
477    def check_boot_to_adb_complete(self, exception_type=error.TimeoutException):
478        """Check if the device has finished booting and accessible by adb.
479
480        @param exception_type: Type of exception to raise. Default is set to
481                error.TimeoutException for retry.
482
483        @raise exception_type: If the device has not finished booting yet, raise
484                an exception of type `exception_type`.
485        """
486        bootcomplete = self._run_output_with_retry('getprop dev.bootcomplete')
487        if bootcomplete != PROPERTY_VALUE_TRUE:
488            raise exception_type('dev.bootcomplete is %s.' % bootcomplete)
489        if self.get_os_type() == OS_TYPE_ANDROID:
490            boot_completed = self._run_output_with_retry(
491                    'getprop sys.boot_completed')
492            if boot_completed != PROPERTY_VALUE_TRUE:
493                raise exception_type('sys.boot_completed is %s.' %
494                                     boot_completed)
495
496
497    def wait_up(self, timeout=DEFAULT_WAIT_UP_TIME_SECONDS, command=ADB_CMD):
498        """Wait until the remote host is up or the timeout expires.
499
500        Overrides wait_down from AbstractSSHHost.
501
502        @param timeout: Time limit in seconds before returning even if the host
503                is not up.
504        @param command: The command used to test if a device is up, i.e.,
505                accessible by the given command. Default is set to `adb`.
506
507        @returns True if the host was found to be up before the timeout expires,
508                 False otherwise.
509        """
510        @retry.retry(error.TimeoutException, timeout_min=timeout/60.0,
511                     delay_sec=1)
512        def _wait_up():
513            if not self.is_up(command=command):
514                raise error.TimeoutException('Device is still down.')
515            if command == ADB_CMD:
516                self.check_boot_to_adb_complete()
517            return True
518
519        try:
520            _wait_up()
521            logging.debug('Host %s is now up, and can be accessed by %s.',
522                          self.hostname, command)
523            return True
524        except error.TimeoutException:
525            logging.debug('Host %s is still down after waiting %d seconds',
526                          self.hostname, timeout)
527            return False
528
529
530    def wait_down(self, timeout=DEFAULT_WAIT_DOWN_TIME_SECONDS,
531                  warning_timer=None, old_boot_id=None, command=ADB_CMD,
532                  boot_id=None):
533        """Wait till the host goes down.
534
535        Return when the host is down (not accessible via the command) OR when
536        the device's boot_id changes (if a boot_id was provided).
537
538        Overrides wait_down from AbstractSSHHost.
539
540        @param timeout: Time in seconds to wait for the host to go down.
541        @param warning_timer: Time limit in seconds that will generate
542                              a warning if the host is not down yet.
543                              Currently ignored.
544        @param old_boot_id: Not applicable for adb_host.
545        @param command: `adb`, test if the device can be accessed by adb
546                command, or `fastboot`, test if the device can be accessed by
547                fastboot command. Default is set to `adb`.
548        @param boot_id: UUID of previous boot (consider the device down when the
549                        boot_id changes from this value). Ignored if None.
550
551        @returns True if the device goes down before the timeout, False
552                 otherwise.
553        """
554        @retry.retry(error.TimeoutException, timeout_min=timeout/60.0,
555                     delay_sec=1)
556        def _wait_down():
557            up = self.is_up(command=command)
558            if not up:
559                return True
560            if boot_id:
561                try:
562                    new_boot_id = self.get_boot_id()
563                    if new_boot_id != boot_id:
564                        return True
565                except error.GenericHostRunError:
566                    pass
567            raise error.TimeoutException('Device is still up.')
568
569        try:
570            _wait_down()
571            logging.debug('Host %s is now down', self.hostname)
572            return True
573        except error.TimeoutException:
574            logging.debug('Host %s is still up after waiting %d seconds',
575                          self.hostname, timeout)
576            return False
577
578
579    def reboot(self):
580        """Reboot the android device via adb.
581
582        @raises AutoservRebootError if reboot failed.
583        """
584        # Not calling super.reboot() as we want to reboot the ADB device not
585        # the test station we are running ADB on.
586        boot_id = self.get_boot_id()
587        self.adb_run('reboot', timeout=10, ignore_timeout=True)
588        if not self.wait_down(boot_id=boot_id):
589            raise error.AutoservRebootError(
590                    'ADB Device %s is still up after reboot' % self.adb_serial)
591        if not self.wait_up():
592            raise error.AutoservRebootError(
593                    'ADB Device %s failed to return from reboot.' %
594                    self.adb_serial)
595        self._reset_adbd_connection()
596
597
598    def fastboot_reboot(self):
599        """Do a fastboot reboot to go back to adb.
600
601        @raises AutoservRebootError if reboot failed.
602        """
603        self.fastboot_run('reboot')
604        if not self.wait_down(command=FASTBOOT_CMD):
605            raise error.AutoservRebootError(
606                    'Device %s is still in fastboot mode after reboot' %
607                    self.fastboot_serial)
608        if not self.wait_up():
609            raise error.AutoservRebootError(
610                    'Device %s failed to boot to adb after fastboot reboot.' %
611                    self.adb_serial)
612        self._reset_adbd_connection()
613
614
615    def remount(self):
616        """Remounts paritions on the device read-write.
617
618        Specifically, the /system, /vendor (if present) and /oem (if present)
619        partitions on the device are remounted read-write.
620        """
621        self.adb_run('remount')
622
623
624    @staticmethod
625    def parse_device_serials(devices_output):
626        """Return a list of parsed serials from the output.
627
628        @param devices_output: Output from either an adb or fastboot command.
629
630        @returns List of device serials
631        """
632        devices = []
633        for line in devices_output.splitlines():
634            match = re.search(DEVICE_FINDER_REGEX, line)
635            if match:
636                serial = match.group('SERIAL')
637                if serial == DEVICE_NO_SERIAL_MSG or re.match(r'^\?+$', serial):
638                    serial = DEVICE_NO_SERIAL_TAG
639                logging.debug('Found Device: %s', serial)
640                devices.append(serial)
641        return devices
642
643
644    def _get_devices(self, use_adb):
645        """Get a list of devices currently attached to the test station.
646
647        @params use_adb: True to get adb accessible devices. Set to False to
648                         get fastboot accessible devices.
649
650        @returns a list of devices attached to the test station.
651        """
652        if use_adb:
653            result = self.adb_run('devices').stdout
654            if self.adb_serial and self.adb_serial not in result:
655                self._connect_over_tcpip_as_needed()
656        else:
657            result = self.fastboot_run('devices').stdout
658            if (self.fastboot_serial and
659                self.fastboot_serial not in result):
660                # fastboot devices won't list the devices using TCP
661                try:
662                    if 'product' in self.fastboot_run('getvar product',
663                                                      timeout=2).stderr:
664                        result += '\n%s\tfastboot' % self.fastboot_serial
665                # The main reason we do a general Exception catch here instead
666                # of setting ignore_timeout/status to True is because even when
667                # the fastboot process has been nuked, it still stays around and
668                # so bgjob wants to warn us of this and tries to read the
669                # /proc/<pid>/stack file which then promptly returns an
670                # 'Operation not permitted' error since we're running as moblab
671                # and we don't have permission to read those files.
672                except Exception:
673                    pass
674        return self.parse_device_serials(result)
675
676
677    def adb_devices(self):
678        """Get a list of devices currently attached to the test station and
679        accessible with the adb command."""
680        devices = self._get_devices(use_adb=True)
681        if self.adb_serial is None and len(devices) > 1:
682            raise error.AutoservError(
683                    'Not given ADB serial but multiple devices detected')
684        return devices
685
686
687    def fastboot_devices(self):
688        """Get a list of devices currently attached to the test station and
689        accessible by fastboot command.
690        """
691        devices = self._get_devices(use_adb=False)
692        if self.fastboot_serial is None and len(devices) > 1:
693            raise error.AutoservError(
694                    'Not given fastboot serial but multiple devices detected')
695        return devices
696
697
698    def is_up(self, timeout=0, command=ADB_CMD):
699        """Determine if the specified adb device is up with expected mode.
700
701        @param timeout: Not currently used.
702        @param command: `adb`, the device can be accessed by adb command,
703                or `fastboot`, the device can be accessed by fastboot command.
704                Default is set to `adb`.
705
706        @returns True if the device is detectable by given command, False
707                 otherwise.
708
709        """
710        if command == ADB_CMD:
711            devices = self.adb_devices()
712            serial = self.adb_serial
713            # ADB has a device state, if the device is not online, no
714            # subsequent ADB command will complete.
715            # DUT with single device connected may not have adb_serial set.
716            # Therefore, skip checking if serial is in the list of adb devices
717            # if self.adb_serial is not set.
718            if (serial and serial not in devices) or not self.is_device_ready():
719                logging.debug('Waiting for device to enter the ready state.')
720                return False
721        elif command == FASTBOOT_CMD:
722            devices = self.fastboot_devices()
723            serial = self.fastboot_serial
724        else:
725            raise NotImplementedError('Mode %s is not supported' % command)
726
727        return bool(devices and (not serial or serial in devices))
728
729
730    def stop_loggers(self):
731        """Inherited stop_loggers function.
732
733        Calls parent function and captures logcat, since the end of the run
734        is logically the end/stop of the logcat log.
735        """
736        super(ADBHost, self).stop_loggers()
737
738        # When called from atest and tools like it there will be no job.
739        if not self.job:
740            return
741
742        # Record logcat log to a temporary file on the teststation.
743        tmp_dir = self.teststation.get_tmp_dir()
744        logcat_filename = LOGCAT_FILE_FMT % self.adb_serial
745        teststation_filename = os.path.join(tmp_dir, logcat_filename)
746        try:
747            self.adb_run('logcat -v time -d > "%s"' % (teststation_filename),
748                         timeout=20)
749        except (error.GenericHostRunError, error.AutoservSSHTimeout,
750                error.CmdTimeoutError):
751            return
752        # Copy-back the log to the drone's results directory.
753        results_logcat_filename = os.path.join(self.job.resultdir,
754                                               logcat_filename)
755        self.teststation.get_file(teststation_filename,
756                                  results_logcat_filename)
757        try:
758            self.teststation.run('rm -rf %s' % tmp_dir)
759        except (error.GenericHostRunError, error.AutoservSSHTimeout) as e:
760            logging.warn('failed to remove dir %s: %s', tmp_dir, e)
761
762        self._collect_crash_logs()
763
764
765    def close(self):
766        """Close the ADBHost object.
767
768        Called as the test ends. Will return the device to USB mode and kill
769        the ADB server.
770        """
771        super(ADBHost, self).close()
772        self.teststation.close()
773
774
775    def syslog(self, message, tag='autotest'):
776        """Logs a message to syslog on the device.
777
778        @param message String message to log into syslog
779        @param tag String tag prefix for syslog
780
781        """
782        self.run('log -t "%s" "%s"' % (tag, message))
783
784
785    def get_autodir(self):
786        """Return the directory to install autotest for client side tests."""
787        return '/data/autotest'
788
789
790    def is_device_ready(self):
791        """Return the if the device is ready for ADB commands."""
792        try:
793            # Retry to avoid possible flakes.
794            is_ready = client_utils.poll_for_condition(
795                lambda: self.adb_run('get-state').stdout.strip() == 'device',
796                timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS, sleep_interval=1,
797                desc='Waiting for device state to be `device`')
798        except client_utils.TimeoutError:
799            is_ready = False
800
801        logging.debug('Device state is %sready', '' if is_ready else 'NOT ')
802        return is_ready
803
804
805    def send_file(self, source, dest, delete_dest=False,
806                  preserve_symlinks=False, excludes=None):
807        """Copy files from the drone to the device.
808
809        Just a note, there is the possibility the test station is localhost
810        which makes some of these steps redundant (e.g. creating tmp dir) but
811        that scenario will undoubtedly be a development scenario (test station
812        is also the moblab) and not the typical live test running scenario so
813        the redundancy I think is harmless.
814
815        @param source: The file/directory on the drone to send to the device.
816        @param dest: The destination path on the device to copy to.
817        @param delete_dest: A flag set to choose whether or not to delete
818                            dest on the device if it exists.
819        @param preserve_symlinks: Controls if symlinks on the source will be
820                                  copied as such on the destination or
821                                  transformed into the referenced
822                                  file/directory.
823        @param excludes: A list of file pattern that matches files not to be
824                         sent. `send_file` will fail if exclude is set, since
825                         local copy does not support --exclude, e.g., when
826                         using scp to copy file.
827        """
828        # If we need to preserve symlinks, let's check if the source is a
829        # symlink itself and if so, just create it on the device.
830        if preserve_symlinks:
831            symlink_target = None
832            try:
833                symlink_target = os.readlink(source)
834            except OSError:
835                # Guess it's not a symlink.
836                pass
837
838            if symlink_target is not None:
839                # Once we create the symlink, let's get out of here.
840                self.run('ln -s %s %s' % (symlink_target, dest))
841                return
842
843        # Stage the files on the test station.
844        tmp_dir = self.teststation.get_tmp_dir()
845        src_path = os.path.join(tmp_dir, os.path.basename(dest))
846        # Now copy the file over to the test station so you can reference the
847        # file in the push command.
848        self.teststation.send_file(
849                source, src_path, preserve_symlinks=preserve_symlinks,
850                excludes=excludes)
851
852        if delete_dest:
853            self.run('rm -rf %s' % dest)
854
855        self.adb_run('push %s %s' % (src_path, dest))
856
857        # Cleanup the test station.
858        try:
859            self.teststation.run('rm -rf %s' % tmp_dir)
860        except (error.GenericHostRunError, error.AutoservSSHTimeout) as e:
861            logging.warn('failed to remove dir %s: %s', tmp_dir, e)
862
863
864    def _get_file_info(self, dest):
865        """Get permission and possible symlink info about file on the device.
866
867        These files are on the device so we only have shell commands (via adb)
868        to get the info we want.  We'll use 'ls' to get it all.
869
870        @param dest: File to get info about.
871
872        @returns a dict of the file permissions and symlink.
873        """
874        # Grab file info.
875        file_info = self.run_output('ls -ld %s' % dest)
876        symlink = None
877        perms = 0
878        match = re.match(FILE_INFO_REGEX, file_info)
879        if match:
880            # Check if it's a symlink and grab the linked dest if it is.
881            if match.group('TYPE') == 'l':
882                symlink_match = re.match(FILE_SYMLINK_REGEX, file_info)
883                if symlink_match:
884                    symlink = symlink_match.group('SYMLINK')
885
886            # Set the perms.
887            for perm, perm_flag in zip(match.group('PERMS'), FILE_PERMS_FLAGS):
888                if perm != '-':
889                    perms |= perm_flag
890
891        return {'perms': perms,
892                'symlink': symlink}
893
894
895    def get_file(self, source, dest, delete_dest=False, preserve_perm=True,
896                 preserve_symlinks=False):
897        """Copy files from the device to the drone.
898
899        Just a note, there is the possibility the test station is localhost
900        which makes some of these steps redundant (e.g. creating tmp dir) but
901        that scenario will undoubtedly be a development scenario (test station
902        is also the moblab) and not the typical live test running scenario so
903        the redundancy I think is harmless.
904
905        @param source: The file/directory on the device to copy back to the
906                       drone.
907        @param dest: The destination path on the drone to copy to.
908        @param delete_dest: A flag set to choose whether or not to delete
909                            dest on the drone if it exists.
910        @param preserve_perm: Tells get_file() to try to preserve the sources
911                              permissions on files and dirs.
912        @param preserve_symlinks: Try to preserve symlinks instead of
913                                  transforming them into files/dirs on copy.
914        """
915        # Stage the files on the test station under teststation_temp_dir.
916        teststation_temp_dir = self.teststation.get_tmp_dir()
917        teststation_dest = os.path.join(teststation_temp_dir,
918                                        os.path.basename(source))
919
920        source_info = {}
921        if preserve_symlinks or preserve_perm:
922            source_info = self._get_file_info(source)
923
924        # If we want to preserve symlinks, just create it here, otherwise pull
925        # the file off the device.
926        #
927        # TODO(sadmac): Directories containing symlinks won't behave as
928        # expected.
929        if preserve_symlinks and source_info['symlink']:
930            os.symlink(source_info['symlink'], dest)
931        else:
932            self.adb_run('pull %s %s' % (source, teststation_temp_dir))
933
934            # Copy over the file from the test station and clean up.
935            self.teststation.get_file(teststation_dest, dest,
936                                      delete_dest=delete_dest)
937            try:
938                self.teststation.run('rm -rf %s' % teststation_temp_dir)
939            except (error.GenericHostRunError, error.AutoservSSHTimeout) as e:
940                logging.warn('failed to remove dir %s: %s',
941                             teststation_temp_dir, e)
942
943            # Source will be copied under dest if either:
944            #  1. Source is a directory and doesn't end with /.
945            #  2. Source is a file and dest is a directory.
946            command = '[ -d %s ]' % source
947            source_is_dir = self.run(command,
948                                     ignore_status=True).exit_status == 0
949            logging.debug('%s on the device %s a directory', source,
950                          'is' if source_is_dir else 'is not')
951
952            if ((source_is_dir and not source.endswith(os.sep)) or
953                (not source_is_dir and os.path.isdir(dest))):
954                receive_path = os.path.join(dest, os.path.basename(source))
955            else:
956                receive_path = dest
957
958            if not os.path.exists(receive_path):
959                logging.warning('Expected file %s does not exist; skipping'
960                                ' permissions copy', receive_path)
961                return
962
963            # Set the permissions of the received file/dirs.
964            if os.path.isdir(receive_path):
965                for root, _dirs, files in os.walk(receive_path):
966                    def process(rel_path, default_perm):
967                        info = self._get_file_info(os.path.join(source,
968                                                                rel_path))
969                        if info['perms'] != 0:
970                            target = os.path.join(receive_path, rel_path)
971                            if preserve_perm:
972                                os.chmod(target, info['perms'])
973                            else:
974                                os.chmod(target, default_perm)
975
976                    rel_root = os.path.relpath(root, receive_path)
977                    process(rel_root, _DEFAULT_DIR_PERMS)
978                    for f in files:
979                        process(os.path.join(rel_root, f), _DEFAULT_FILE_PERMS)
980            elif preserve_perm:
981                os.chmod(receive_path, source_info['perms'])
982            else:
983                os.chmod(receive_path, _DEFAULT_FILE_PERMS)
984
985
986    def get_release_version(self):
987        """Get the release version from the RELEASE_FILE on the device.
988
989        @returns The release string in the RELEASE_FILE.
990
991        """
992        return self.run_output('getprop %s' % RELEASE_FILE)
993
994
995    def get_tmp_dir(self, parent=''):
996        """Return a suitable temporary directory on the device.
997
998        We ensure this is a subdirectory of /data/local/tmp.
999
1000        @param parent: Parent directory of the returned tmp dir.
1001
1002        @returns a path to the temp directory on the host.
1003        """
1004        # TODO(kevcheng): Refactor the cleanup of tmp dir to be inherited
1005        #                 from the parent.
1006        if not parent.startswith(TMP_DIR):
1007            parent = os.path.join(TMP_DIR, parent.lstrip(os.path.sep))
1008        self.run('mkdir -p %s' % parent)
1009        tmp_dir = self.run_output('mktemp -d -p %s' % parent)
1010        self.tmp_dirs.append(tmp_dir)
1011        return tmp_dir
1012
1013
1014    def get_platform(self):
1015        """Determine the correct platform label for this host.
1016
1017        @returns a string representing this host's platform.
1018        """
1019        return 'adb'
1020
1021
1022    def get_os_type(self):
1023        """Get the OS type of the DUT, e.g., android or brillo.
1024        """
1025        if not self._os_type:
1026            if self.run_output('getprop ro.product.brand') == 'Brillo':
1027                self._os_type = OS_TYPE_BRILLO
1028            else:
1029                self._os_type = OS_TYPE_ANDROID
1030
1031        return self._os_type
1032
1033
1034    def _forward(self, reverse, args):
1035        """Execute a forwarding command.
1036
1037        @param reverse: Whether this is reverse forwarding (Boolean).
1038        @param args: List of command arguments.
1039        """
1040        cmd = '%s %s' % ('reverse' if reverse else 'forward', ' '.join(args))
1041        self.adb_run(cmd)
1042
1043
1044    def add_forwarding(self, src, dst, reverse=False, rebind=True):
1045        """Forward a port between the ADB host and device.
1046
1047        Port specifications are any strings accepted as such by ADB, for
1048        example 'tcp:8080'.
1049
1050        @param src: Port specification to forward from.
1051        @param dst: Port specification to forward to.
1052        @param reverse: Do reverse forwarding from device to host (Boolean).
1053        @param rebind: Allow rebinding an already bound port (Boolean).
1054        """
1055        args = []
1056        if not rebind:
1057            args.append('--no-rebind')
1058        args += [src, dst]
1059        self._forward(reverse, args)
1060
1061
1062    def remove_forwarding(self, src=None, reverse=False):
1063        """Removes forwarding on port.
1064
1065        @param src: Port specification, or None to remove all forwarding.
1066        @param reverse: Whether this is reverse forwarding (Boolean).
1067        """
1068        args = []
1069        if src is None:
1070            args.append('--remove-all')
1071        else:
1072            args += ['--remove', src]
1073        self._forward(reverse, args)
1074
1075
1076    def create_ssh_tunnel(self, port, local_port):
1077        """
1078        Forwards a port securely through a tunnel process from the server
1079        to the DUT for RPC server connection.
1080        Add a 'ADB forward' rule to forward the RPC packets from the AdbHost
1081        to the DUT.
1082
1083        @param port: remote port on the DUT.
1084        @param local_port: local forwarding port.
1085
1086        @return: the tunnel process.
1087        """
1088        self.add_forwarding('tcp:%s' % port, 'tcp:%s' % port)
1089        return super(ADBHost, self).create_ssh_tunnel(port, local_port)
1090
1091
1092    def disconnect_ssh_tunnel(self, tunnel_proc, port):
1093        """
1094        Disconnects a previously forwarded port from the server to the DUT for
1095        RPC server connection.
1096        Remove the previously added 'ADB forward' rule to forward the RPC
1097        packets from the AdbHost to the DUT.
1098
1099        @param tunnel_proc: the original tunnel process returned from
1100                            |create_ssh_tunnel|.
1101        @param port: remote port on the DUT.
1102
1103        """
1104        self.remove_forwarding('tcp:%s' % port)
1105        super(ADBHost, self).disconnect_ssh_tunnel(tunnel_proc, port)
1106
1107
1108    def ensure_adb_mode(self, timeout=DEFAULT_WAIT_UP_TIME_SECONDS):
1109        """Ensure the device is up and can be accessed by adb command.
1110
1111        @param timeout: Time limit in seconds before returning even if the host
1112                        is not up.
1113
1114        @raise: error.AutoservError if the device failed to reboot into
1115                adb mode.
1116        """
1117        if self.is_up():
1118            return
1119        # Ignore timeout error to allow `fastboot reboot` to fail quietly and
1120        # check if the device is in adb mode.
1121        self.fastboot_run('reboot', timeout=timeout, ignore_timeout=True)
1122        if not self.wait_up(timeout=timeout):
1123            raise error.AutoservError(
1124                    'Device %s failed to reboot into adb mode.' %
1125                    self.adb_serial)
1126        self._reset_adbd_connection()
1127
1128
1129    @retry.retry(error.GenericHostRunError, timeout_min=10)
1130    def download_file(self, build_url, file, dest_dir, unzip=False,
1131                      unzip_dest=None):
1132        """Download the given file from the build url.
1133
1134        @param build_url: The url to use for downloading Android artifacts.
1135                pattern: http://$devserver:###/static/branch/target/build_id
1136        @param file: Name of the file to be downloaded, e.g., boot.img.
1137        @param dest_dir: Destination folder for the file to be downloaded to.
1138        @param unzip: If True, unzip the downloaded file.
1139        @param unzip_dest: Location to unzip the downloaded file to. If not
1140                           provided, dest_dir is used.
1141        """
1142        # Append the file name to the url if build_url is linked to the folder
1143        # containing the file.
1144        if not build_url.endswith('/%s' % file):
1145            src_url = os.path.join(build_url, file)
1146        else:
1147            src_url = build_url
1148        dest_file = os.path.join(dest_dir, file)
1149        try:
1150            self.teststation.run('wget -q -O "%s" "%s"' % (dest_file, src_url))
1151            if unzip:
1152                unzip_dest = unzip_dest or dest_dir
1153                self.teststation.run('unzip "%s/%s" -x -d "%s"' %
1154                                     (dest_dir, file, unzip_dest))
1155        except:
1156            # Delete the destination file if download failed.
1157            self.teststation.run('rm -f "%s"' % dest_file)
1158            raise
1159
1160
1161    @property
1162    def job_repo_url_attribute(self):
1163        """Get the host attribute name for job_repo_url, which should append the
1164        adb serial.
1165        """
1166        return '%s_%s' % (constants.JOB_REPO_URL, self.adb_serial)
1167
1168
1169    def list_files_glob(self, path_glob):
1170        """Get a list of files on the device given glob pattern path.
1171
1172        @param path_glob: The path glob that we want to return the list of
1173                files that match the glob.  Relative paths will not work as
1174                expected.  Supply an absolute path to get the list of files
1175                you're hoping for.
1176
1177        @returns List of files that match the path_glob.
1178        """
1179        # This is just in case path_glob has no path separator.
1180        base_path = os.path.dirname(path_glob) or '.'
1181        result = self.run('find %s -path \'%s\' -print' %
1182                          (base_path, path_glob), ignore_status=True)
1183        if result.exit_status != 0:
1184            return []
1185        return result.stdout.splitlines()
1186
1187
1188    @retry.retry(error.GenericHostRunError,
1189                 timeout_min=DISABLE_PACKAGE_VERIFICATION_TIMEOUT_MIN)
1190    def disable_package_verification(self):
1191        """Disables package verification on an android device.
1192
1193        Disables the package verificatoin manager allowing any package to be
1194        installed without checking
1195        """
1196        logging.info('Disabling package verification on %s.', self.adb_serial)
1197        self.check_boot_to_adb_complete()
1198        self.run('am broadcast -a '
1199                 'com.google.gservices.intent.action.GSERVICES_OVERRIDE -e '
1200                 'global:package_verifier_enable 0')
1201
1202
1203    @retry.retry(error.GenericHostRunError, timeout_min=APK_INSTALL_TIMEOUT_MIN)
1204    def install_apk(self, apk, force_reinstall=True):
1205        """Install the specified apk.
1206
1207        This will install the apk and override it if it's already installed and
1208        will also allow for downgraded apks.
1209
1210        @param apk: The path to apk file.
1211        @param force_reinstall: True to reinstall the apk even if it's already
1212                installed. Default is set to True.
1213
1214        @returns a CMDResult object.
1215        """
1216        try:
1217            client_utils.poll_for_condition(
1218                    lambda: self.run('pm list packages',
1219                                     ignore_status=True).exit_status == 0,
1220                    timeout=120)
1221            client_utils.poll_for_condition(
1222                    lambda: self.run('service list | grep mount',
1223                                     ignore_status=True).exit_status == 0,
1224                    timeout=120)
1225            return self.adb_run('install %s -d %s' %
1226                                ('-r' if force_reinstall else '', apk))
1227        except error.GenericHostRunError:
1228            self.reboot()
1229            raise
1230
1231
1232    def uninstall_package(self, package):
1233        """Remove the specified package.
1234
1235        @param package: Android package name.
1236
1237        @raises GenericHostRunError: uninstall failed
1238        """
1239        result = self.adb_run('uninstall %s' % package)
1240
1241        if self.is_apk_installed(package):
1242            raise error.GenericHostRunError('Uninstall of "%s" failed.'
1243                                            % package, result)
1244
1245    @retry.retry(error.GenericHostRunError, timeout_min=0.2)
1246    def _confirm_apk_installed(self, package_name):
1247        """Confirm if apk is already installed with the given name.
1248
1249        `pm list packages` command is not reliable some time. The retry helps to
1250        reduce the chance of false negative.
1251
1252        @param package_name: Name of the package, e.g., com.android.phone.
1253
1254        @raise AutoservRunError: If the package is not found or pm list command
1255                failed for any reason.
1256        """
1257        name = 'package:%s' % package_name
1258        self.adb_run('shell pm list packages | grep -w "%s"' % name)
1259
1260
1261    def is_apk_installed(self, package_name):
1262        """Check if apk is already installed with the given name.
1263
1264        @param package_name: Name of the package, e.g., com.android.phone.
1265
1266        @return: True if package is installed. False otherwise.
1267        """
1268        try:
1269            self._confirm_apk_installed(package_name)
1270            return True
1271        except:
1272            return False
1273
1274    def get_attributes_to_clear_before_provision(self):
1275        """Get a list of attributes to be cleared before machine_install starts.
1276        """
1277        return [self.job_repo_url_attribute]
1278
1279
1280    def get_labels(self):
1281        """Return a list of the labels gathered from the devices connected.
1282
1283        @return: A list of strings that denote the labels from all the devices
1284                 connected.
1285        """
1286        return self.labels.get_labels(self)
1287
1288
1289    def _sync_time(self):
1290        """Approximate synchronization of time between host and ADB device.
1291
1292        This sets the ADB/Android device's clock to approximately the same
1293        time as the Autotest host for the purposes of comparing Android system
1294        logs such as logcat to logs from the Autotest host system.
1295        """
1296        command = 'date '
1297        sdk_version = int(self.run('getprop %s' % SDK_FILE).stdout)
1298        if sdk_version < 23:
1299            # Android L and earlier use this format: date -s (format).
1300            command += ('-s %s' %
1301                        datetime.datetime.now().strftime('%Y%m%d.%H%M%S'))
1302        else:
1303            # Android M and later use this format: date -u (format).
1304            command += ('-u %s' %
1305                        datetime.datetime.utcnow().strftime('%m%d%H%M%Y.%S'))
1306        self.run(command, timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS,
1307                 ignore_timeout=True)
1308
1309
1310    def _enable_native_crash_logging(self):
1311        """Enable native (non-Java) crash logging.
1312        """
1313        if self.get_os_type() == OS_TYPE_ANDROID:
1314            self._enable_android_native_crash_logging()
1315
1316
1317    def _enable_brillo_native_crash_logging(self):
1318        """Enables native crash logging for a Brillo DUT.
1319        """
1320        try:
1321            self.run('touch /data/misc/metrics/enabled',
1322                     timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS,
1323                     ignore_timeout=True)
1324            # If running, crash_sender will delete crash files every hour.
1325            self.run('stop crash_sender',
1326                     timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS,
1327                     ignore_timeout=True)
1328        except error.GenericHostRunError as e:
1329            logging.warn(e)
1330            logging.warn('Failed to enable Brillo native crash logging.')
1331
1332
1333    def _enable_android_native_crash_logging(self):
1334        """Enables native crash logging for an Android DUT.
1335        """
1336        # debuggerd should be enabled by default on Android.
1337        result = self.run('pgrep debuggerd',
1338                          timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS,
1339                          ignore_timeout=True, ignore_status=True)
1340        if not result or result.exit_status != 0:
1341            logging.debug('Unable to confirm that debuggerd is running.')
1342
1343
1344    def _collect_crash_logs(self):
1345        """Copies crash log files from the DUT to the drone.
1346        """
1347        if self.get_os_type() == OS_TYPE_BRILLO:
1348            self._collect_crash_logs_dut(BRILLO_NATIVE_CRASH_LOG_DIR)
1349        elif self.get_os_type() == OS_TYPE_ANDROID:
1350            self._collect_crash_logs_dut(ANDROID_TOMBSTONE_CRASH_LOG_DIR)
1351
1352
1353    def _collect_crash_logs_dut(self, log_directory):
1354        """Copies native crash logs from the Android/Brillo DUT to the drone.
1355
1356        @param log_directory: absolute path of the directory on the DUT where
1357                log files are stored.
1358        """
1359        files = None
1360        try:
1361            result = self.run('find %s -maxdepth 1 -type f' % log_directory,
1362                              timeout=DEFAULT_COMMAND_RETRY_TIMEOUT_SECONDS)
1363            files = result.stdout.strip().split()
1364        except (error.GenericHostRunError, error.AutoservSSHTimeout,
1365                error.CmdTimeoutError):
1366            logging.debug('Unable to call find %s, unable to find crash logs',
1367                          log_directory)
1368        if not files:
1369            logging.debug('There are no crash logs on the DUT.')
1370            return
1371
1372        crash_dir = os.path.join(self.job.resultdir, 'crash')
1373        try:
1374            os.mkdir(crash_dir)
1375        except OSError as e:
1376            if e.errno != errno.EEXIST:
1377                raise e
1378
1379        for f in files:
1380            logging.debug('DUT native crash file produced: %s', f)
1381            dest = os.path.join(crash_dir, os.path.basename(f))
1382            # We've had cases where the crash file on the DUT has permissions
1383            # "000". Let's override permissions to make them sane for the user
1384            # collecting the crashes.
1385            self.get_file(source=f, dest=dest, preserve_perm=False)
1386