1# Copyright 2015 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 collections 6import glob 7import logging 8import os 9import pipes 10import shutil 11import socket 12import sys 13import tempfile 14import time 15 16from autotest_lib.client.bin import test, utils 17from autotest_lib.client.common_lib import error 18from autotest_lib.client.common_lib.cros import chrome, arc_common 19 20_ADB_KEYS_PATH = '/tmp/adb_keys' 21_ADB_VENDOR_KEYS = 'ADB_VENDOR_KEYS' 22_ANDROID_CONTAINER_PID_PATH = '/run/containers/android*/container.pid' 23_ANDROID_DATA_ROOT_PATH = '/opt/google/containers/android/rootfs/android-data' 24_ANDROID_CONTAINER_ROOT_PATH = '/opt/google/containers/android/rootfs' 25_SCREENSHOT_DIR_PATH = '/var/log/arc-screenshots' 26_SCREENSHOT_BASENAME = 'arc-screenshot' 27_MAX_SCREENSHOT_NUM = 10 28# This address should match the one present in 29# https://chromium.googlesource.com/chromiumos/overlays/chromiumos-overlay/+/master/chromeos-base/arc-sslh-init/files/sslh.conf 30_ADBD_ADDRESS = ('100.115.92.2', 5555) 31_ADBD_PID_PATH = '/run/arc/adbd.pid' 32_SDCARD_PID_PATH = '/run/arc/sdcard.pid' 33_ANDROID_ADB_KEYS_PATH = '/data/misc/adb/adb_keys' 34_PROCESS_CHECK_INTERVAL_SECONDS = 1 35_PROPERTY_CHECK_INTERVAL_SECONDS = 1 36_WAIT_FOR_ADB_READY = 60 37_WAIT_FOR_ANDROID_PROCESS_SECONDS = 60 38_PLAY_STORE_PKG = 'com.android.vending' 39_SETTINGS_PKG = 'com.android.settings' 40 41 42def setup_adb_host(): 43 """Setup ADB host keys. 44 45 This sets up the files and environment variables that wait_for_adb_ready() 46 needs""" 47 if _ADB_VENDOR_KEYS in os.environ: 48 return 49 if not os.path.exists(_ADB_KEYS_PATH): 50 os.mkdir(_ADB_KEYS_PATH) 51 # adb expects $HOME to be writable. 52 os.environ['HOME'] = _ADB_KEYS_PATH 53 54 # Generate and save keys for adb if needed 55 key_path = os.path.join(_ADB_KEYS_PATH, 'test_key') 56 if not os.path.exists(key_path): 57 utils.system('adb keygen ' + pipes.quote(key_path)) 58 os.environ[_ADB_VENDOR_KEYS] = key_path 59 60 61def restart_adbd(timeout): 62 """Restarts the adb daemon. 63 64 Follows the same logic as tast. 65 """ 66 logging.debug('restarting adbd') 67 config = 'adb' 68 _android_shell('setprop persist.sys.usb.config ' + config) 69 _android_shell('setprop sys.usb.config ' + config) 70 71 def property_check(): 72 return _android_shell('getprop sys.usb.state') == config 73 74 try: 75 utils.poll_for_condition( 76 condition=property_check, 77 desc='Wait for sys.usb.state', 78 timeout=timeout, 79 sleep_interval=_PROPERTY_CHECK_INTERVAL_SECONDS) 80 except utils.TimeoutError: 81 raise error.TestFail('Timed out waiting for sys.usb.state change') 82 83 _android_shell('setprop ctl.restart adbd') 84 85 86def restart_adb(): 87 """Restarts adb. 88 89 Follows the same logic as in tast, specifically avoiding kill-server 90 since it is unreliable (crbug.com/855325). 91 """ 92 logging.debug('killing and restarting adb server') 93 utils.system('killall --quiet --wait -KILL adb') 94 utils.system('adb start-server') 95 96 97def is_adb_connected(): 98 """Return true if adb is connected to the container.""" 99 output = utils.system_output('adb get-state', ignore_status=True) 100 logging.debug('adb get-state: %s', output) 101 return output.strip() == 'device' 102 103 104def _is_android_data_mounted(): 105 """Return true if Android's /data is mounted with partial boot enabled.""" 106 return _android_shell('getprop ro.data_mounted', ignore_status=True) == '1' 107 108 109def get_zygote_type(): 110 """Return zygote service type.""" 111 return _android_shell('getprop ro.zygote', ignore_status=True) 112 113 114def get_sdk_version(): 115 """Return the SDK level version for Android.""" 116 return _android_shell('getprop ro.build.version.sdk') 117 118 119def get_product(): 120 """Return the product string used for the Android build.""" 121 return _android_shell('getprop ro.build.product', ignore_status=True) 122 123 124def _is_tcp_port_reachable(address): 125 """Return whether a TCP port described by |address| is reachable.""" 126 try: 127 s = socket.create_connection(address) 128 s.close() 129 return True 130 except socket.error: 131 return False 132 133 134def _wait_for_data_mounted(timeout): 135 utils.poll_for_condition( 136 condition=_is_android_data_mounted, 137 desc='Wait for /data mounted', 138 timeout=timeout, 139 sleep_interval=_PROCESS_CHECK_INTERVAL_SECONDS) 140 141 142def wait_for_adb_ready(timeout=_WAIT_FOR_ADB_READY): 143 """Wait for the ADB client to connect to the ARC container. 144 145 @param timeout: Timeout in seconds. 146 """ 147 # Although adbd is started at login screen, we still need /data to be 148 # mounted to set up key-based authentication. /data should be mounted 149 # once the user has logged in. 150 151 initial_timeout = timeout 152 153 start_time = time.time() 154 _wait_for_data_mounted(timeout) 155 timeout -= (time.time() - start_time) 156 start_time = time.time() 157 arc_common.wait_for_android_boot(timeout) 158 timeout -= (time.time() - start_time) 159 160 setup_adb_host() 161 if is_adb_connected(): 162 return 163 164 # Push keys for adb. 165 pubkey_path = os.environ[_ADB_VENDOR_KEYS] + '.pub' 166 with open(pubkey_path, 'r') as f: 167 _write_android_file(_ANDROID_ADB_KEYS_PATH, f.read()) 168 _android_shell('chown shell ' + pipes.quote(_ANDROID_ADB_KEYS_PATH)) 169 _android_shell('restorecon ' + pipes.quote(_ANDROID_ADB_KEYS_PATH)) 170 171 attempt_count = 3 172 timeout = timeout / attempt_count 173 174 for i in range(attempt_count): 175 if _restart_adb_and_wait_for_ready(timeout): 176 return 177 raise error.TestFail( 178 'Failed to connect to adb in %d seconds.' % initial_timeout) 179 180 181def _restart_adb_and_wait_for_ready(timeout): 182 """Restart adb/adbd and wait adb connection is ready. 183 184 @param timeout: Timeout in seconds. 185 @return True in case adb connection was established or throw an error in 186 case persistent error occured. 187 """ 188 189 # Restart adbd and adb. 190 start_time = time.time() 191 restart_adbd(timeout) 192 timeout -= (time.time() - start_time) 193 start_time = time.time() 194 restart_adb() 195 timeout -= (time.time() - start_time) 196 197 try: 198 utils.poll_for_condition(condition=is_adb_connected, 199 timeout=timeout) 200 return True 201 except (utils.TimeoutError): 202 # The operation has failed, but let's try to clarify the failure to 203 # avoid shifting blame to adb. 204 205 # First, collect some information and log it. 206 arc_alive = is_android_container_alive() 207 arc_booted = _android_shell('getprop sys.boot_completed', 208 ignore_status=True) 209 arc_system_events = _android_shell( 210 'logcat -d -b events *:S arc_system_event', ignore_status=True) 211 adbd_pid = _android_shell('pidof -s adbd', ignore_status=True) 212 adbd_port_reachable = _is_tcp_port_reachable(_ADBD_ADDRESS) 213 adb_state = utils.system_output('adb get-state', ignore_status=True) 214 logging.debug('ARC alive: %s', arc_alive) 215 logging.debug('ARC booted: %s', arc_booted) 216 logging.debug('ARC system events: %s', arc_system_events) 217 logging.debug('adbd process: %s', adbd_pid) 218 logging.debug('adbd port reachable: %s', adbd_port_reachable) 219 logging.debug('adb state: %s', adb_state) 220 221 # Now go through the usual suspects and raise nicer errors to make the 222 # actual failure clearer. 223 if not arc_alive: 224 raise error.TestFail('ARC is not alive.') 225 if arc_booted != '1': 226 raise error.TestFail('ARC did not finish booting.') 227 return False 228 229 230def grant_permissions(package, permissions): 231 """Grants permissions to a package. 232 233 @param package: Package name. 234 @param permissions: A list of permissions. 235 236 """ 237 for permission in permissions: 238 adb_shell('pm grant %s android.permission.%s' % ( 239 pipes.quote(package), pipes.quote(permission))) 240 241 242def adb_cmd(cmd, **kwargs): 243 """Executed cmd using adb. Must wait for adb ready. 244 245 @param cmd: Command to run. 246 """ 247 # TODO(b/79122489) - Assert if cmd == 'root' 248 wait_for_adb_ready() 249 return utils.system_output('adb %s' % cmd, **kwargs) 250 251 252def adb_shell(cmd, **kwargs): 253 """Executed shell command with adb. 254 255 @param cmd: Command to run. 256 """ 257 output = adb_cmd('shell %s' % pipes.quote(cmd), **kwargs) 258 # Some android commands include a trailing CRLF in their output. 259 if kwargs.pop('strip_trailing_whitespace', True): 260 output = output.rstrip() 261 return output 262 263 264def adb_install(apk, auto_grant_permissions=True, ignore_status=False): 265 """Install an apk into container. You must connect first. 266 267 @param apk: Package to install. 268 @param auto_grant_permissions: Set to false to not automatically grant all 269 permissions. Most tests should not care. 270 @param ignore_status: Set to true to allow the install command to fail, 271 for example if you are installing multiple architectures and only need 272 one to succeed. 273 """ 274 flags = '-g' if auto_grant_permissions else '' 275 return adb_cmd('install -r -t %s %s' % (flags, apk), 276 timeout=60*5, 277 ignore_status=ignore_status) 278 279 280def adb_uninstall(apk): 281 """Remove an apk from container. You must connect first. 282 283 @param apk: Package to uninstall. 284 """ 285 return adb_cmd('uninstall %s' % apk) 286 287 288def adb_reboot(): 289 """Reboots the container and block until container pid is gone. 290 291 You must connect first. 292 """ 293 old_pid = get_container_pid() 294 logging.info('Trying to reboot PID:%s', old_pid) 295 adb_cmd('reboot', ignore_status=True) 296 # Ensure that the old container is no longer booted 297 utils.poll_for_condition( 298 lambda: not utils.pid_is_alive(int(old_pid)), timeout=10) 299 300 301# This adb_root() function is deceiving in that it works just fine on debug 302# builds of ARC (user-debug, eng). However "adb root" does not work on user 303# builds as run by the autotest machines when testing prerelease images. In fact 304# it will silently fail. You will need to find another way to do do what you 305# need to do as root. 306# 307# TODO(b/79122489) - Remove this function. 308def adb_root(): 309 """Restart adbd with root permission.""" 310 311 adb_cmd('root') 312 313 314def get_container_root(): 315 """Returns path to Android container root directory.""" 316 return _ANDROID_CONTAINER_ROOT_PATH 317 318 319def get_container_pid_path(): 320 """Returns the container's PID file path. 321 322 Raises: 323 TestError if no PID file is found, or more than one files are found. 324 """ 325 # Find the PID file rather than the android-XXXXXX/ directory to ignore 326 # stale and empty android-XXXXXX/ directories when they exist. 327 arc_container_pid_files = glob.glob(_ANDROID_CONTAINER_PID_PATH) 328 329 if len(arc_container_pid_files) == 0: 330 raise error.TestError('Android container.pid not available') 331 332 if len(arc_container_pid_files) > 1: 333 raise error.TestError( 334 'Multiple Android container.pid files found: %r. ' 335 'Reboot your DUT to recover.' % (arc_container_pid_files)) 336 337 return arc_container_pid_files[0] 338 339 340def get_android_data_root(): 341 """Returns path to Chrome OS directory that bind-mounts Android's /data.""" 342 return _ANDROID_DATA_ROOT_PATH 343 344 345def get_container_pid(): 346 """Returns the PID of the container.""" 347 return utils.read_one_line(get_container_pid_path()) 348 349 350def get_adbd_pid(): 351 """Returns the PID of the adbd proxy container.""" 352 if not os.path.exists(_ADBD_PID_PATH): 353 # The adbd proxy does not run on all boards. 354 return None 355 return utils.read_one_line(_ADBD_PID_PATH) 356 357 358def is_android_process_running(process_name): 359 """Return whether Android has completed booting. 360 361 @param process_name: Process name. 362 """ 363 output = adb_shell('pgrep -c -f %s' % pipes.quote(process_name), 364 ignore_status=True) 365 return int(output) > 0 366 367 368def check_android_file_exists(filename): 369 """Checks whether the given file exists in the Android filesystem 370 371 @param filename: File to check. 372 """ 373 return adb_shell( 374 'test -e {} && echo FileExists'.format(pipes.quote(filename)), 375 ignore_status=True).find("FileExists") >= 0 376 377 378def read_android_file(filename): 379 """Reads a file in Android filesystem. 380 381 @param filename: File to read. 382 """ 383 with tempfile.NamedTemporaryFile() as tmpfile: 384 adb_cmd('pull %s %s' % (pipes.quote(filename), 385 pipes.quote(tmpfile.name))) 386 with open(tmpfile.name) as f: 387 return f.read() 388 389 return None 390 391 392def write_android_file(filename, data): 393 """Writes to a file in Android filesystem. 394 395 @param filename: File to write. 396 @param data: Data to write. 397 """ 398 with tempfile.NamedTemporaryFile() as tmpfile: 399 tmpfile.write(data) 400 tmpfile.flush() 401 402 adb_cmd('push %s %s' % (pipes.quote(tmpfile.name), 403 pipes.quote(filename))) 404 405 406def _write_android_file(filename, data): 407 """Writes to a file in Android filesystem. 408 409 This is an internal function used to bootstrap adb. 410 Tests should use write_android_file instead. 411 """ 412 android_cmd = 'cat > %s' % pipes.quote(filename) 413 cros_cmd = 'android-sh -c %s' % pipes.quote(android_cmd) 414 utils.run(cros_cmd, stdin=data) 415 416 417def get_android_file_stats(filename): 418 """Returns an object of file stats for an Android file. 419 420 The returned object supported limited attributes, but can be easily extended 421 if needed. Note that the value are all string. 422 423 This uses _android_shell to run as root, so that it can access to all files 424 inside the container. On non-debuggable build, adb shell is not rootable. 425 """ 426 mapping = { 427 '%a': 'mode', 428 '%g': 'gid', 429 '%h': 'nlink', 430 '%u': 'uid', 431 } 432 output = _android_shell( 433 'stat -c "%s" %s' % (' '.join(mapping.keys()), pipes.quote(filename)), 434 ignore_status=True) 435 stats = output.split(' ') 436 if len(stats) != len(mapping): 437 raise error.TestError('Unexpected output from stat: %s' % output) 438 _Stats = collections.namedtuple('_Stats', mapping.values()) 439 return _Stats(*stats) 440 441 442def remove_android_file(filename): 443 """Removes a file in Android filesystem. 444 445 @param filename: File to remove. 446 """ 447 adb_shell('rm -f %s' % pipes.quote(filename)) 448 449 450def wait_for_android_boot(timeout=None): 451 """Sleep until Android has completed booting or timeout occurs. 452 453 @param timeout: Timeout in seconds. 454 """ 455 arc_common.wait_for_android_boot(timeout) 456 457 458def wait_for_android_process(process_name, 459 timeout=_WAIT_FOR_ANDROID_PROCESS_SECONDS): 460 """Sleep until an Android process is running or timeout occurs. 461 462 @param process_name: Process name. 463 @param timeout: Timeout in seconds. 464 """ 465 condition = lambda: is_android_process_running(process_name) 466 utils.poll_for_condition(condition=condition, 467 desc='%s is running' % process_name, 468 timeout=timeout, 469 sleep_interval=_PROCESS_CHECK_INTERVAL_SECONDS) 470 471 472def _android_shell(cmd, **kwargs): 473 """Execute cmd instead the Android container. 474 475 This function is strictly for internal use only, as commands do not run in 476 a fully consistent Android environment. Prefer adb_shell instead. 477 """ 478 return utils.system_output('android-sh -c {}'.format(pipes.quote(cmd)), 479 **kwargs) 480 481 482def is_android_container_alive(): 483 """Check if android container is alive.""" 484 try: 485 container_pid = get_container_pid() 486 except Exception, e: 487 logging.error('is_android_container_alive failed: %r', e) 488 return False 489 return utils.pid_is_alive(int(container_pid)) 490 491 492def _is_in_installed_packages_list(package, option=None): 493 """Check if a package is in the list returned by pm list packages. 494 495 adb must be ready. 496 497 @param package: Package in request. 498 @param option: An option for the command adb shell pm list packages. 499 Valid values include '-s', '-3', '-d', and '-e'. 500 """ 501 command = 'pm list packages' 502 if option: 503 command += ' ' + option 504 packages = adb_shell(command).splitlines() 505 package_entry = 'package:' + package 506 ret = package_entry in packages 507 508 if not ret: 509 logging.info('Could not find "%s" in %s', 510 package_entry, str(packages)) 511 return ret 512 513 514def is_package_installed(package): 515 """Check if a package is installed. adb must be ready. 516 517 @param package: Package in request. 518 """ 519 return _is_in_installed_packages_list(package) 520 521 522def is_package_disabled(package): 523 """Check if an installed package is disabled. adb must be ready. 524 525 @param package: Package in request. 526 """ 527 return _is_in_installed_packages_list(package, '-d') 528 529 530def get_package_install_path(package): 531 """Returns the apk install location of the given package.""" 532 output = adb_shell('pm path {}'.format(pipes.quote(package))) 533 return output.split(':')[1] 534 535 536def _before_iteration_hook(obj): 537 """Executed by parent class before every iteration. 538 539 This function resets the run_once_finished flag before every iteration 540 so we can detect failure on every single iteration. 541 542 Args: 543 obj: the test itself 544 """ 545 obj.run_once_finished = False 546 547 548def _after_iteration_hook(obj): 549 """Executed by parent class after every iteration. 550 551 The parent class will handle exceptions and failures in the run and will 552 always call this hook afterwards. Take a screenshot if the run has not 553 been marked as finished (i.e. there was a failure/exception). 554 555 Args: 556 obj: the test itself 557 """ 558 if not obj.run_once_finished: 559 if is_adb_connected(): 560 logging.debug('Recent activities dump:\n%s', 561 adb_shell('dumpsys activity recents', 562 ignore_status=True)) 563 if not os.path.exists(_SCREENSHOT_DIR_PATH): 564 os.mkdir(_SCREENSHOT_DIR_PATH, 0755) 565 obj.num_screenshots += 1 566 if obj.num_screenshots <= _MAX_SCREENSHOT_NUM: 567 logging.warning('Iteration %d failed, taking a screenshot.', 568 obj.iteration) 569 try: 570 utils.run('screenshot "{}/{}_iter{}.png"'.format( 571 _SCREENSHOT_DIR_PATH, _SCREENSHOT_BASENAME, obj.iteration)) 572 except Exception as e: 573 logging.warning('Unable to capture screenshot. %s', e) 574 else: 575 logging.warning('Too many failures, no screenshot taken') 576 577 578def send_keycode(keycode): 579 """Sends the given keycode to the container 580 581 @param keycode: keycode to send. 582 """ 583 adb_shell('input keyevent {}'.format(keycode)) 584 585 586def get_android_sdk_version(): 587 """Returns the Android SDK version. 588 589 This function can be called before Android container boots. 590 """ 591 with open('/etc/lsb-release') as f: 592 values = dict(line.split('=', 1) for line in f.read().splitlines()) 593 try: 594 return int(values['CHROMEOS_ARC_ANDROID_SDK_VERSION']) 595 except (KeyError, ValueError): 596 raise error.TestError('Could not determine Android SDK version') 597 598 599def set_device_mode(device_mode, use_fake_sensor_with_lifetime_secs=0): 600 """Sets the device in either Clamshell or Tablet mode. 601 602 "inject_powerd_input_event" might fail if the DUT does not support Tablet 603 mode, and it will raise an |error.CmdError| exception. To prevent that, use 604 the |use_fake_sensor_with_lifetime_secs| parameter. 605 606 @param device_mode: string with either 'clamshell' or 'tablet' 607 @param use_fake_sensor_with_lifetime_secs: if > 0, it will create the 608 input device with the given lifetime in seconds 609 @raise ValueError: if passed invalid parameters 610 @raise error.CmdError: if inject_powerd_input_event fails 611 """ 612 valid_value = ('tablet', 'clamshell') 613 if device_mode not in valid_value: 614 raise ValueError('Invalid device_mode parameter: %s' % device_mode) 615 616 value = 1 if device_mode == 'tablet' else 0 617 618 args = ['--code=tablet', '--value=%d' % value] 619 620 if use_fake_sensor_with_lifetime_secs > 0: 621 args.extend(['--create_dev', '--dev_lifetime=%d' % 622 use_fake_sensor_with_lifetime_secs]) 623 624 try: 625 utils.run('inject_powerd_input_event', args=args) 626 except error.CmdError as err: 627 # TODO: Fragile code ahead. Correct way to do it is to check 628 # if device is already in desired mode, and do nothing if so. 629 # ATM we don't have a way to check current device mode. 630 631 # Assuming that CmdError means that device does not support 632 # --code=tablet parameter, meaning that device only supports clamshell 633 # mode. 634 if device_mode == 'clamshell' and \ 635 use_fake_sensor_with_lifetime_secs == 0: 636 return 637 raise err 638 639 640def wait_for_userspace_ready(): 641 """Waits for userspace apps to be launchable. 642 643 Launches and then closes Android settings as a way to ensure all basic 644 services are ready. This goes a bit beyond waiting for boot-up to complete, 645 as being able to launch an activity requires more of the framework to have 646 started. The boot-complete signal happens fairly early, and the framework 647 system server is still starting services. By waiting for ActivityManager to 648 respond, we automatically wait on more services to be ready. 649 """ 650 output = adb_shell('am start -W -a android.settings.SETTINGS', 651 ignore_status=True) 652 if not output.endswith('Complete'): 653 logging.debug('Output was: %s', output) 654 raise error.TestError('Could not launch SETTINGS') 655 adb_shell('am force-stop com.android.settings', ignore_status=True) 656 657 658class ArcTest(test.test): 659 """ Base class of ARC Test. 660 661 This class could be used as super class of an ARC test for saving 662 redundant codes for container bringup, autotest-dep package(s) including 663 uiautomator setup if required, and apks install/remove during 664 arc_setup/arc_teardown, respectively. By default arc_setup() is called in 665 initialize() after Android have been brought up. It could also be 666 overridden to perform non-default tasks. For example, a simple 667 ArcHelloWorldTest can be just implemented with print 'HelloWorld' in its 668 run_once() and no other functions are required. We could expect 669 ArcHelloWorldTest would bring up browser and wait for container up, then 670 print 'Hello World', and shutdown browser after. As a precaution, if you 671 overwrite initialize(), arc_setup(), or cleanup() function(s) in ARC test, 672 remember to call the corresponding function(s) in this base class as well. 673 """ 674 version = 1 675 _PKG_UIAUTOMATOR = 'uiautomator' 676 _FULL_PKG_NAME_UIAUTOMATOR = 'com.github.uiautomator' 677 678 def __init__(self, *args, **kwargs): 679 """Initialize flag setting.""" 680 super(ArcTest, self).__init__(*args, **kwargs) 681 self.initialized = False 682 # Set the flag run_once_finished to detect if a test is executed 683 # successfully without any exception thrown. Otherwise, generate 684 # a screenshot in /var/log for debugging. 685 self.run_once_finished = False 686 self.logcat_proc = None 687 self.dep_package = None 688 self.apks = None 689 self.full_pkg_names = [] 690 self.uiautomator = False 691 self._should_reenable_play_store = False 692 self._chrome = None 693 if os.path.exists(_SCREENSHOT_DIR_PATH): 694 shutil.rmtree(_SCREENSHOT_DIR_PATH) 695 self.register_before_iteration_hook(_before_iteration_hook) 696 self.register_after_iteration_hook(_after_iteration_hook) 697 # Keep track of the number of debug screenshots taken and keep the 698 # total number sane to avoid issues. 699 self.num_screenshots = 0 700 701 def initialize(self, extension_path=None, username=None, password=None, 702 arc_mode=arc_common.ARC_MODE_ENABLED, **chrome_kargs): 703 """Log in to a test account.""" 704 extension_paths = [extension_path] if extension_path else [] 705 self._chrome = chrome.Chrome(extension_paths=extension_paths, 706 username=username, 707 password=password, 708 arc_mode=arc_mode, 709 **chrome_kargs) 710 if extension_path: 711 self._extension = self._chrome.get_extension(extension_path) 712 else: 713 self._extension = None 714 # With ARC enabled, Chrome will wait until container to boot up 715 # before returning here, see chrome.py. 716 self.initialized = True 717 try: 718 if is_android_container_alive(): 719 self.arc_setup() 720 else: 721 logging.error('Container is alive?') 722 except Exception as err: 723 raise error.TestFail(err) 724 725 def after_run_once(self): 726 """Executed after run_once() only if there were no errors. 727 728 This function marks the run as finished with a flag. If there was a 729 failure the flag won't be set and the failure can then be detected by 730 testing the run_once_finished flag. 731 """ 732 logging.info('After run_once') 733 self.run_once_finished = True 734 735 def cleanup(self): 736 """Log out of Chrome.""" 737 if not self.initialized: 738 logging.info('Skipping ARC cleanup: not initialized') 739 return 740 logging.info('Starting ARC cleanup') 741 try: 742 if is_android_container_alive(): 743 self.arc_teardown() 744 except Exception as err: 745 raise error.TestFail(err) 746 finally: 747 try: 748 if self.logcat_proc: 749 self.logcat_proc.close() 750 finally: 751 if self._chrome is not None: 752 self._chrome.close() 753 754 def _install_apks(self, dep_package, apks, full_pkg_names): 755 """"Install apks fetched from the specified package folder. 756 757 @param dep_package: A dependent package directory 758 @param apks: List of apk names to be installed 759 @param full_pkg_names: List of packages to be uninstalled at teardown 760 """ 761 apk_path = os.path.join(self.autodir, 'deps', dep_package) 762 if apks: 763 for apk in apks: 764 logging.info('Installing %s', apk) 765 out = adb_install('%s/%s' % (apk_path, apk), ignore_status=True) 766 logging.info('Install apk output: %s', str(out)) 767 # Verify if package(s) are installed correctly. We ignored 768 # individual install statuses above because some tests list apks for 769 # all arches and only need one installed. 770 if not full_pkg_names: 771 raise error.TestError('Package names of apks expected') 772 for pkg in full_pkg_names: 773 logging.info('Check if %s is installed', pkg) 774 if not is_package_installed(pkg): 775 raise error.TestError('Package %s not found' % pkg) 776 # Make sure full_pkg_names contains installed packages only 777 # so arc_teardown() knows what packages to uninstall. 778 self.full_pkg_names.append(pkg) 779 780 def _count_nested_array_level(self, array): 781 """Count the level of a nested array.""" 782 if isinstance(array, list): 783 return 1 + self._count_nested_array_level(array[0]) 784 return 0 785 786 def _fix_nested_array_level(self, var_name, expected_level, array): 787 """Enclose array one level deeper if needed.""" 788 level = self._count_nested_array_level(array) 789 if level == expected_level: 790 return array 791 if level == expected_level - 1: 792 return [array] 793 794 logging.error("Variable %s nested level is not fixable: " 795 "Expecting %d, seeing %d", 796 var_name, expected_level, level) 797 raise error.TestError('Format error with variable %s' % var_name) 798 799 def arc_setup(self, dep_packages=None, apks=None, full_pkg_names=None, 800 uiautomator=False, disable_play_store=False): 801 """ARC test setup: Setup dependencies and install apks. 802 803 This function disables package verification and enables non-market 804 APK installation. Then, it installs specified APK(s) and uiautomator 805 package and path if required in a test. 806 807 @param dep_packages: Array of package names of autotest_deps APK 808 packages. 809 @param apks: Array of APK name arrays to be installed in dep_package. 810 @param full_pkg_names: Array of full package name arrays to be removed 811 in teardown. 812 @param uiautomator: uiautomator python package is required or not. 813 @param disable_play_store: Set this to True if you want to prevent 814 GMS Core from updating. 815 """ 816 if not self.initialized: 817 logging.info('Skipping ARC setup: not initialized') 818 return 819 logging.info('Starting ARC setup') 820 821 # Sample parameters for multi-deps setup after fixup (if needed): 822 # dep_packages: ['Dep1-apk', 'Dep2-apk'] 823 # apks: [['com.dep1.arch1.apk', 'com.dep2.arch2.apk'], ['com.dep2.apk'] 824 # full_pkg_nmes: [['com.dep1.app'], ['com.dep2.app']] 825 # TODO(crbug/777787): once the parameters of all callers of arc_setup 826 # are refactored, we can delete the safety net here. 827 if dep_packages: 828 dep_packages = self._fix_nested_array_level( 829 'dep_packages', 1, dep_packages) 830 apks = self._fix_nested_array_level('apks', 2, apks) 831 full_pkg_names = self._fix_nested_array_level( 832 'full_pkg_names', 2, full_pkg_names) 833 if (len(dep_packages) != len(apks) or 834 len(apks) != len(full_pkg_names)): 835 logging.info('dep_packages length is %d', len(dep_packages)) 836 logging.info('apks length is %d', len(apks)) 837 logging.info('full_pkg_names length is %d', 838 len(full_pkg_names)) 839 raise error.TestFail( 840 'dep_packages/apks/full_pkg_names format error') 841 842 self.dep_packages = dep_packages 843 self.apks = apks 844 self.uiautomator = uiautomator or disable_play_store 845 # Setup dependent packages if required 846 packages = [] 847 if dep_packages: 848 packages = dep_packages[:] 849 if self.uiautomator: 850 packages.append(self._PKG_UIAUTOMATOR) 851 if packages: 852 logging.info('Setting up dependent package(s) %s', packages) 853 self.job.setup_dep(packages) 854 855 self.logcat_proc = arc_common.Logcat() 856 857 wait_for_adb_ready() 858 859 # Setting verifier_verify_adb_installs to zero suppresses a dialog box 860 # that can appear asking for the user to consent to the install. 861 adb_shell('settings put global verifier_verify_adb_installs 0') 862 863 # Install apks based on dep_packages/apks/full_pkg_names tuples 864 if dep_packages: 865 for i in xrange(len(dep_packages)): 866 self._install_apks(dep_packages[i], apks[i], full_pkg_names[i]) 867 868 if self.uiautomator: 869 path = os.path.join(self.autodir, 'deps', self._PKG_UIAUTOMATOR) 870 sys.path.append(path) 871 self._add_ui_object_not_found_handler() 872 if disable_play_store and not is_package_disabled(_PLAY_STORE_PKG): 873 self._disable_play_store() 874 if not is_package_disabled(_PLAY_STORE_PKG): 875 raise error.TestFail('Failed to disable Google Play Store.') 876 self._should_reenable_play_store = True 877 878 def arc_teardown(self): 879 """ARC test teardown. 880 881 This function removes all installed packages in arc_setup stage 882 first. Then, it restores package verification and disables non-market 883 APK installation. 884 885 """ 886 if self.full_pkg_names: 887 for pkg in self.full_pkg_names: 888 logging.info('Uninstalling %s', pkg) 889 if not is_package_installed(pkg): 890 raise error.TestError('Package %s was not installed' % pkg) 891 adb_uninstall(pkg) 892 if (self.uiautomator and 893 is_package_installed(self._FULL_PKG_NAME_UIAUTOMATOR)): 894 logging.info('Uninstalling %s', self._FULL_PKG_NAME_UIAUTOMATOR) 895 adb_uninstall(self._FULL_PKG_NAME_UIAUTOMATOR) 896 if self._should_reenable_play_store: 897 adb_shell('pm enable ' + _PLAY_STORE_PKG) 898 adb_shell('settings put secure install_non_market_apps 0') 899 adb_shell('settings put global package_verifier_enable 1') 900 adb_shell('settings put secure package_verifier_user_consent 0') 901 902 # Remove the adb keys without going through adb. This is because the 903 # 'rm' tool does not have permissions to remove the keys once they have 904 # been restorecon(8)ed. 905 utils.system_output('rm -f %s' % 906 pipes.quote(os.path.join( 907 get_android_data_root(), 908 os.path.relpath(_ANDROID_ADB_KEYS_PATH, '/')))) 909 utils.system_output('adb kill-server') 910 911 def _add_ui_object_not_found_handler(self): 912 """Logs the device dump upon uiautomator.UiObjectNotFoundException.""" 913 from uiautomator import device as d 914 d.handlers.on(lambda d: logging.debug('Device window dump:\n%s', 915 d.dump())) 916 917 def _disable_play_store(self): 918 """Disables the Google Play Store app.""" 919 if is_package_disabled(_PLAY_STORE_PKG): 920 return 921 adb_shell('am force-stop ' + _PLAY_STORE_PKG) 922 adb_shell('am start -a android.settings.APPLICATION_DETAILS_SETTINGS ' 923 '-d package:' + _PLAY_STORE_PKG) 924 925 # Note: the straightforward "pm disable <package>" command would be 926 # better, but that requires root permissions, which aren't available on 927 # a pre-release image being tested. The only other way is through the 928 # Settings UI, but which might change. 929 from uiautomator import device as d 930 d(textMatches='(?i)DISABLE', packageName=_SETTINGS_PKG).wait.exists() 931 d(textMatches='(?i)DISABLE', packageName=_SETTINGS_PKG).click.wait() 932 d(textMatches='(?i)DISABLE APP').click.wait() 933 ok_button = d(textMatches='(?i)OK') 934 if ok_button.exists: 935 ok_button.click.wait() 936 adb_shell('am force-stop ' + _SETTINGS_PKG) 937