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