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