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