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