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 5 import glob 6 import logging 7 import os 8 import pipes 9 import re 10 import shutil 11 import subprocess 12 import sys 13 import tempfile 14 15 from autotest_lib.client.bin import test, utils 16 from autotest_lib.client.common_lib import error 17 from autotest_lib.client.common_lib.cros import chrome, arc_common 18 19 _ADB_KEYS_PATH = '/tmp/adb_keys' 20 _ADB_VENDOR_KEYS = 'ADB_VENDOR_KEYS' 21 _ANDROID_CONTAINER_PID_PATH = '/run/containers/android*/container.pid' 22 _ANDROID_DATA_ROOT_PATH = '/opt/google/containers/android/rootfs/android-data' 23 _ANDROID_CONTAINER_ROOT_PATH = '/opt/google/containers/android/rootfs' 24 _SCREENSHOT_DIR_PATH = '/var/log/arc-screenshots' 25 _SCREENSHOT_BASENAME = 'arc-screenshot' 26 _MAX_SCREENSHOT_NUM = 10 27 _SDCARD_PID_PATH = '/run/arc/sdcard.pid' 28 _ANDROID_ADB_KEYS_PATH = '/data/misc/adb/adb_keys' 29 _PROCESS_CHECK_INTERVAL_SECONDS = 1 30 _WAIT_FOR_ADB_READY = 60 31 _WAIT_FOR_ANDROID_PROCESS_SECONDS = 60 32 _WAIT_FOR_DATA_MOUNTED_SECONDS = 60 33 _VAR_LOGCAT_PATH = '/var/log/logcat' 34 _PLAY_STORE_PKG = 'com.android.vending' 35 _SETTINGS_PKG = 'com.android.settings' 36 37 38 def setup_adb_host(): 39 """Setup ADB host keys. 40 41 This sets up the files and environment variables that wait_for_adb_ready() 42 needs""" 43 if _ADB_VENDOR_KEYS in os.environ: 44 return 45 if not os.path.exists(_ADB_KEYS_PATH): 46 os.mkdir(_ADB_KEYS_PATH) 47 # adb expects $HOME to be writable. 48 os.environ['HOME'] = _ADB_KEYS_PATH 49 50 # Generate and save keys for adb if needed 51 key_path = os.path.join(_ADB_KEYS_PATH, 'test_key') 52 if not os.path.exists(key_path): 53 utils.system('adb keygen ' + pipes.quote(key_path)) 54 os.environ[_ADB_VENDOR_KEYS] = key_path 55 56 57 def adb_connect(): 58 """Attempt to connect ADB to the Android container. 59 60 Returns true if successful. Do not call this function directly. Call 61 wait_for_adb_ready() instead.""" 62 if not is_android_booted(): 63 return False 64 if utils.system('adb connect localhost:22', ignore_status=True) != 0: 65 return False 66 return is_adb_connected() 67 68 69 def is_adb_connected(): 70 """Return true if adb is connected to the container.""" 71 output = utils.system_output('adb get-state', ignore_status=True) 72 logging.debug('adb get-state: %s', output) 73 return output.strip() == 'device' 74 75 76 def is_partial_boot_enabled(): 77 """Return true if partial boot is enabled. 78 79 When partial boot is enabled, Android is started at login screen without 80 any persistent state (e.g. /data is not mounted). 81 """ 82 return _android_shell('getprop ro.boot.partial_boot') == '1' 83 84 85 def _is_android_data_mounted(): 86 """Return true if Android's /data is mounted with partial boot enabled.""" 87 return _android_shell('getprop ro.data_mounted') == '1' 88 89 90 def get_zygote_type(): 91 """Return zygote service type.""" 92 return _android_shell('getprop ro.zygote') 93 94 95 def get_sdk_version(): 96 """Return the SDK level version for Android.""" 97 return _android_shell('getprop ro.build.version.sdk') 98 99 100 def get_product(): 101 """Return the product string used for the Android build.""" 102 return _android_shell('getprop ro.build.product') 103 104 105 def _wait_for_data_mounted(timeout=_WAIT_FOR_DATA_MOUNTED_SECONDS): 106 utils.poll_for_condition( 107 condition=_is_android_data_mounted, 108 desc='Wait for /data mounted', 109 timeout=timeout, 110 sleep_interval=_PROCESS_CHECK_INTERVAL_SECONDS) 111 112 113 def wait_for_adb_ready(timeout=_WAIT_FOR_ADB_READY): 114 """Wait for the ADB client to connect to the ARC container. 115 116 @param timeout: Timeout in seconds. 117 """ 118 # When partial boot is enabled, although adbd is started at login screen, 119 # we still need /data to be mounted to set up key-based authentication. 120 # /data should be mounted once the user has logged in. 121 if is_partial_boot_enabled(): 122 _wait_for_data_mounted() 123 124 setup_adb_host() 125 if is_adb_connected(): 126 return 127 128 # Push keys for adb. 129 pubkey_path = os.environ[_ADB_VENDOR_KEYS] + '.pub' 130 with open(pubkey_path, 'r') as f: 131 _write_android_file(_ANDROID_ADB_KEYS_PATH, f.read()) 132 _android_shell('restorecon ' + pipes.quote(_ANDROID_ADB_KEYS_PATH)) 133 134 # This starts adbd. 135 _android_shell('setprop sys.usb.config mtp,adb') 136 137 # Kill existing adb server to ensure that a full reconnect is performed. 138 utils.system('adb kill-server', ignore_status=True) 139 140 exception = error.TestFail('adb is not ready in %d seconds.' % timeout) 141 utils.poll_for_condition(adb_connect, 142 exception, 143 timeout) 144 145 146 def grant_permissions(package, permissions): 147 """Grants permissions to a package. 148 149 @param package: Package name. 150 @param permissions: A list of permissions. 151 152 """ 153 for permission in permissions: 154 adb_shell('pm grant %s android.permission.%s' % ( 155 pipes.quote(package), pipes.quote(permission))) 156 157 158 def adb_cmd(cmd, **kwargs): 159 """Executed cmd using adb. Must wait for adb ready. 160 161 @param cmd: Command to run. 162 """ 163 wait_for_adb_ready() 164 return utils.system_output('adb %s' % cmd, **kwargs) 165 166 167 def adb_shell(cmd, **kwargs): 168 """Executed shell command with adb. 169 170 @param cmd: Command to run. 171 """ 172 output = adb_cmd('shell %s' % pipes.quote(cmd), **kwargs) 173 # Some android commands include a trailing CRLF in their output. 174 if kwargs.pop('strip_trailing_whitespace', True): 175 output = output.rstrip() 176 return output 177 178 179 def adb_install(apk): 180 """Install an apk into container. You must connect first. 181 182 @param apk: Package to install. 183 """ 184 return adb_cmd('install -r %s' % apk, timeout=60*5) 185 186 187 def adb_uninstall(apk): 188 """Remove an apk from container. You must connect first. 189 190 @param apk: Package to uninstall. 191 """ 192 return adb_cmd('uninstall %s' % apk) 193 194 195 def adb_reboot(): 196 """Reboots the container. You must connect first.""" 197 adb_root() 198 return adb_cmd('reboot', ignore_status=True) 199 200 201 def adb_root(): 202 """Restart adbd with root permission.""" 203 adb_cmd('root') 204 205 206 def get_container_root(): 207 """Returns path to Android container root directory.""" 208 return _ANDROID_CONTAINER_ROOT_PATH 209 210 211 def get_container_pid_path(): 212 """Returns the container's PID file path. 213 214 Raises: 215 TestError if no PID file is found, or more than one files are found. 216 """ 217 # Find the PID file rather than the android-XXXXXX/ directory to ignore 218 # stale and empty android-XXXXXX/ directories when they exist. 219 arc_container_pid_files = glob.glob(_ANDROID_CONTAINER_PID_PATH) 220 221 if len(arc_container_pid_files) == 0: 222 raise error.TestError('Android container.pid not available') 223 224 if len(arc_container_pid_files) > 1: 225 raise error.TestError('Multiple Android container.pid files found: %r. ' 226 'Reboot your DUT to recover.' % ( 227 arc_container_pid_files)) 228 229 return arc_container_pid_files[0] 230 231 232 def get_android_data_root(): 233 """Returns path to Chrome OS directory that bind-mounts Android's /data.""" 234 return _ANDROID_DATA_ROOT_PATH 235 236 237 def get_job_pid(job_name): 238 """Returns the PID of an upstart job.""" 239 status = utils.system_output('status %s' % job_name) 240 match = re.match(r'^%s start/running, process (\d+)$' % job_name, 241 status) 242 if not match: 243 raise error.TestError('Unexpected status: "%s"' % status) 244 return match.group(1) 245 246 247 def get_container_pid(): 248 """Returns the PID of the container.""" 249 return utils.read_one_line(get_container_pid_path()) 250 251 252 def get_sdcard_pid(): 253 """Returns the PID of the sdcard container.""" 254 return utils.read_one_line(_SDCARD_PID_PATH) 255 256 257 def get_removable_media_pid(): 258 """Returns the PID of the arc-removable-media FUSE daemon.""" 259 job_pid = get_job_pid('arc-removable-media') 260 # |job_pid| is the minijail process, obtain the PID of the process running 261 # inside the mount namespace. 262 # FUSE process is the only process running as chronos in the process group. 263 return utils.system_output('pgrep -u chronos -g %s' % job_pid) 264 265 266 def get_obb_mounter_pid(): 267 """Returns the PID of the OBB mounter.""" 268 return utils.system_output('pgrep -f -u root ^/usr/bin/arc-obb-mounter') 269 270 271 def is_android_booted(): 272 """Return whether Android has completed booting.""" 273 # We used to check sys.boot_completed system property to detect Android has 274 # booted in Android M, but in Android N it is set long before 275 # BOOT_COMPLETED intent is broadcast. So we read event logs instead. 276 log = _android_shell( 277 'logcat -d -b events *:S arc_system_event', ignore_status=True) 278 return 'ArcAppLauncher:started' in log 279 280 281 def is_android_process_running(process_name): 282 """Return whether Android has completed booting. 283 284 @param process_name: Process name. 285 """ 286 output = adb_shell('pgrep -c -f %s' % pipes.quote(process_name)) 287 return int(output) > 0 288 289 290 def check_android_file_exists(filename): 291 """Checks whether the given file exists in the Android filesystem 292 293 @param filename: File to check. 294 """ 295 return adb_shell('test -e {} && echo FileExists'.format( 296 pipes.quote(filename))).find("FileExists") >= 0 297 298 299 def read_android_file(filename): 300 """Reads a file in Android filesystem. 301 302 @param filename: File to read. 303 """ 304 with tempfile.NamedTemporaryFile() as tmpfile: 305 adb_cmd('pull %s %s' % (pipes.quote(filename), 306 pipes.quote(tmpfile.name))) 307 with open(tmpfile.name) as f: 308 return f.read() 309 310 return None 311 312 313 def write_android_file(filename, data): 314 """Writes to a file in Android filesystem. 315 316 @param filename: File to write. 317 @param data: Data to write. 318 """ 319 with tempfile.NamedTemporaryFile() as tmpfile: 320 tmpfile.write(data) 321 tmpfile.flush() 322 323 adb_cmd('push %s %s' % (pipes.quote(tmpfile.name), 324 pipes.quote(filename))) 325 326 327 def _write_android_file(filename, data): 328 """Writes to a file in Android filesystem. 329 330 This is an internal function used to bootstrap adb. 331 Tests should use write_android_file instead. 332 """ 333 android_cmd = 'cat > %s' % pipes.quote(filename) 334 cros_cmd = 'android-sh -c %s' % pipes.quote(android_cmd) 335 utils.run(cros_cmd, stdin=data) 336 337 338 def remove_android_file(filename): 339 """Removes a file in Android filesystem. 340 341 @param filename: File to remove. 342 """ 343 adb_shell('rm -f %s' % pipes.quote(filename)) 344 345 346 def wait_for_android_boot(timeout=None): 347 """Sleep until Android has completed booting or timeout occurs. 348 349 @param timeout: Timeout in seconds. 350 """ 351 arc_common.wait_for_android_boot(timeout) 352 353 354 def wait_for_android_process(process_name, 355 timeout=_WAIT_FOR_ANDROID_PROCESS_SECONDS): 356 """Sleep until an Android process is running or timeout occurs. 357 358 @param process_name: Process name. 359 @param timeout: Timeout in seconds. 360 """ 361 condition = lambda: is_android_process_running(process_name) 362 utils.poll_for_condition(condition=condition, 363 desc='%s is running' % process_name, 364 timeout=timeout, 365 sleep_interval=_PROCESS_CHECK_INTERVAL_SECONDS) 366 367 368 def _android_shell(cmd, **kwargs): 369 """Execute cmd instead the Android container. 370 371 This function is strictly for internal use only, as commands do not run in 372 a fully consistent Android environment. Prefer adb_shell instead. 373 """ 374 return utils.system_output('android-sh -c {}'.format(pipes.quote(cmd)), 375 **kwargs) 376 377 378 def is_android_container_alive(): 379 """Check if android container is alive.""" 380 try: 381 container_pid = get_container_pid() 382 except Exception, e: 383 logging.error('is_android_container_alive failed: %r', e) 384 return False 385 return utils.pid_is_alive(int(container_pid)) 386 387 388 def _is_in_installed_packages_list(package, option=None): 389 """Check if a package is in the list returned by pm list packages. 390 391 adb must be ready. 392 393 @param package: Package in request. 394 @param option: An option for the command adb shell pm list packages. 395 Valid values include '-s', '-3', '-d', and '-e'. 396 """ 397 command = 'pm list packages' 398 if option: 399 command += ' ' + option 400 packages = adb_shell(command).splitlines() 401 package_entry = 'package:' + package 402 return package_entry in packages 403 404 405 def is_package_installed(package): 406 """Check if a package is installed. adb must be ready. 407 408 @param package: Package in request. 409 """ 410 return _is_in_installed_packages_list(package) 411 412 413 def is_package_disabled(package): 414 """Check if an installed package is disabled. adb must be ready. 415 416 @param package: Package in request. 417 """ 418 return _is_in_installed_packages_list(package, '-d') 419 420 421 def _before_iteration_hook(obj): 422 """Executed by parent class before every iteration. 423 424 This function resets the run_once_finished flag before every iteration 425 so we can detect failure on every single iteration. 426 427 Args: 428 obj: the test itself 429 """ 430 obj.run_once_finished = False 431 432 433 def _after_iteration_hook(obj): 434 """Executed by parent class after every iteration. 435 436 The parent class will handle exceptions and failures in the run and will 437 always call this hook afterwards. Take a screenshot if the run has not 438 been marked as finished (i.e. there was a failure/exception). 439 440 Args: 441 obj: the test itself 442 """ 443 if not obj.run_once_finished: 444 if is_adb_connected(): 445 logging.debug('Recent activities dump:\n%s', 446 adb_shell('dumpsys activity recents')) 447 if not os.path.exists(_SCREENSHOT_DIR_PATH): 448 os.mkdir(_SCREENSHOT_DIR_PATH, 0755) 449 obj.num_screenshots += 1 450 if obj.num_screenshots <= _MAX_SCREENSHOT_NUM: 451 logging.warning('Iteration %d failed, taking a screenshot.', 452 obj.iteration) 453 from cros.graphics.gbm import crtcScreenshot 454 try: 455 image = crtcScreenshot() 456 image.save('{}/{}_iter{}.png'.format(_SCREENSHOT_DIR_PATH, 457 _SCREENSHOT_BASENAME, 458 obj.iteration)) 459 except Exception as e: 460 logging.warning('Unable to capture screenshot. %s', e) 461 else: 462 logging.warning('Too many failures, no screenshot taken') 463 464 465 def send_keycode(keycode): 466 """Sends the given keycode to the container 467 468 @param keycode: keycode to send. 469 """ 470 adb_shell('input keyevent {}'.format(keycode)) 471 472 473 def get_android_sdk_version(): 474 """Returns the Android SDK version. 475 476 This function can be called before Android container boots. 477 """ 478 with open('/etc/lsb-release') as f: 479 values = dict(line.split('=', 1) for line in f.read().splitlines()) 480 try: 481 return int(values['CHROMEOS_ARC_ANDROID_SDK_VERSION']) 482 except (KeyError, ValueError): 483 raise error.TestError('Could not determine Android SDK version') 484 485 486 def set_device_mode(device_mode, use_fake_sensor_with_lifetime_secs=0): 487 """Sets the device in either Clamshell or Tablet mode. 488 489 "inject_powerd_input_event" might fail if the DUT does not support Tablet 490 mode, and it will raise an |error.CmdError| exception. To prevent that, use 491 the |use_fake_sensor_with_lifetime_secs| parameter. 492 493 @param device_mode: string with either 'clamshell' or 'tablet' 494 @param use_fake_sensor_with_lifetime_secs: if > 0, it will create the 495 input device with the given lifetime in seconds 496 @raise ValueError: if passed invalid parameters 497 @raise error.CmdError: if inject_powerd_input_event fails 498 """ 499 valid_value = ('tablet', 'clamshell') 500 if device_mode not in valid_value: 501 raise ValueError('Invalid device_mode parameter: %s' % device_mode) 502 503 value = 1 if device_mode == 'tablet' else 0 504 505 args = ['--code=tablet', '--value=%d' % value] 506 507 if use_fake_sensor_with_lifetime_secs > 0: 508 args.extend(['--create_dev', '--dev_lifetime=%d' % 509 use_fake_sensor_with_lifetime_secs]) 510 utils.run('inject_powerd_input_event', args=args) 511 512 513 class ArcTest(test.test): 514 """ Base class of ARC Test. 515 516 This class could be used as super class of an ARC test for saving 517 redundant codes for container bringup, autotest-dep package(s) including 518 uiautomator setup if required, and apks install/remove during 519 arc_setup/arc_teardown, respectively. By default arc_setup() is called in 520 initialize() after Android have been brought up. It could also be overridden 521 to perform non-default tasks. For example, a simple ArcHelloWorldTest can be 522 just implemented with print 'HelloWorld' in its run_once() and no other 523 functions are required. We could expect ArcHelloWorldTest would bring up 524 browser and wait for container up, then print 'Hello World', and shutdown 525 browser after. As a precaution, if you overwrite initialize(), arc_setup(), 526 or cleanup() function(s) in ARC test, remember to call the corresponding 527 function(s) in this base class as well. 528 529 """ 530 version = 1 531 _PKG_UIAUTOMATOR = 'uiautomator' 532 _FULL_PKG_NAME_UIAUTOMATOR = 'com.github.uiautomator' 533 534 def __init__(self, *args, **kwargs): 535 """Initialize flag setting.""" 536 super(ArcTest, self).__init__(*args, **kwargs) 537 self.initialized = False 538 # Set the flag run_once_finished to detect if a test is executed 539 # successfully without any exception thrown. Otherwise, generate 540 # a screenshot in /var/log for debugging. 541 self.run_once_finished = False 542 self.logcat_proc = None 543 self.dep_package = None 544 self.apks = None 545 self.full_pkg_names = [] 546 self.uiautomator = False 547 self._should_reenable_play_store = False 548 self._chrome = None 549 if os.path.exists(_SCREENSHOT_DIR_PATH): 550 shutil.rmtree(_SCREENSHOT_DIR_PATH) 551 self.register_before_iteration_hook(_before_iteration_hook) 552 self.register_after_iteration_hook(_after_iteration_hook) 553 # Keep track of the number of debug screenshots taken and keep the 554 # total number sane to avoid issues. 555 self.num_screenshots = 0 556 557 def initialize(self, extension_path=None, username=None, password=None, 558 arc_mode=arc_common.ARC_MODE_ENABLED, **chrome_kargs): 559 """Log in to a test account.""" 560 extension_paths = [extension_path] if extension_path else [] 561 self._chrome = chrome.Chrome(extension_paths=extension_paths, 562 username=username, 563 password=password, 564 arc_mode=arc_mode, 565 **chrome_kargs) 566 if extension_path: 567 self._extension = self._chrome.get_extension(extension_path) 568 else: 569 self._extension = None 570 # With ARC enabled, Chrome will wait until container to boot up 571 # before returning here, see chrome.py. 572 self.initialized = True 573 try: 574 if is_android_container_alive(): 575 self.arc_setup() 576 else: 577 logging.error('Container is alive?') 578 except Exception as err: 579 raise error.TestFail(err) 580 581 def after_run_once(self): 582 """Executed after run_once() only if there were no errors. 583 584 This function marks the run as finished with a flag. If there was a 585 failure the flag won't be set and the failure can then be detected by 586 testing the run_once_finished flag. 587 """ 588 logging.info('After run_once') 589 self.run_once_finished = True 590 591 def cleanup(self): 592 """Log out of Chrome.""" 593 if not self.initialized: 594 logging.info('Skipping ARC cleanup: not initialized') 595 return 596 logging.info('Starting ARC cleanup') 597 try: 598 if is_android_container_alive(): 599 self.arc_teardown() 600 except Exception as err: 601 raise error.TestFail(err) 602 finally: 603 try: 604 self._stop_logcat() 605 finally: 606 if self._chrome is not None: 607 self._chrome.close() 608 609 def _install_apks(self, dep_package, apks, full_pkg_names): 610 """"Install apks fetched from the specified package folder. 611 612 @param dep_package: A dependent package directory 613 @param apks: List of apk names to be installed 614 @param full_pkg_names: List of packages to be uninstalled at teardown 615 """ 616 apk_path = os.path.join(self.autodir, 'deps', dep_package) 617 if apks: 618 for apk in apks: 619 logging.info('Installing %s', apk) 620 adb_install('%s/%s' % (apk_path, apk)) 621 # Verify if package(s) are installed correctly 622 if not full_pkg_names: 623 raise error.TestError('Package names of apks expected') 624 for pkg in full_pkg_names: 625 logging.info('Check if %s is installed', pkg) 626 if not is_package_installed(pkg): 627 raise error.TestError('Package %s not found' % pkg) 628 # Make sure full_pkg_names contains installed packages only 629 # so arc_teardown() knows what packages to uninstall. 630 self.full_pkg_names.append(pkg) 631 632 def _count_nested_array_level(self, array): 633 """Count the level of a nested array.""" 634 if isinstance(array, list): 635 return 1 + self._count_nested_array_level(array[0]) 636 return 0 637 638 def _fix_nested_array_level(self, var_name, expected_level, array): 639 """Enclose array one level deeper if needed.""" 640 level = self._count_nested_array_level(array) 641 if level == expected_level: 642 return array 643 if level == expected_level - 1: 644 return [array] 645 646 logging.error("Variable %s nested level is not fixable: " 647 "Expecting %d, seeing %d", 648 var_name, expected_level, level) 649 raise error.TestError('Format error with variable %s' % var_name) 650 651 def arc_setup(self, dep_packages=None, apks=None, full_pkg_names=None, 652 uiautomator=False, block_outbound=False, 653 disable_play_store=False): 654 """ARC test setup: Setup dependencies and install apks. 655 656 This function disables package verification and enables non-market 657 APK installation. Then, it installs specified APK(s) and uiautomator 658 package and path if required in a test. 659 660 @param dep_packages: Array of package names of autotest_deps APK 661 packages. 662 @param apks: Array of APK name arrays to be installed in dep_package. 663 @param full_pkg_names: Array of full package name arrays to be removed 664 in teardown. 665 @param uiautomator: uiautomator python package is required or not. 666 @param block_outbound: block outbound network traffic during a test. 667 @param disable_play_store: Set this to True if you want to prevent 668 GMS Core from updating. 669 """ 670 if not self.initialized: 671 logging.info('Skipping ARC setup: not initialized') 672 return 673 logging.info('Starting ARC setup') 674 675 # Sample parameters for multi-deps setup after fixup (if needed): 676 # dep_packages: ['Dep1-apk', 'Dep2-apk'] 677 # apks: [['com.dep1.arch1.apk', 'com.dep2.arch2.apk'], ['com.dep2.apk'] 678 # full_pkg_nmes: [['com.dep1.app'], ['com.dep2.app']] 679 # TODO(crbug/777787): once the parameters of all callers of arc_setup 680 # are refactored, we can delete the safety net here. 681 if dep_packages: 682 dep_packages = self._fix_nested_array_level( 683 'dep_packages', 1, dep_packages) 684 apks = self._fix_nested_array_level('apks', 2, apks) 685 full_pkg_names = self._fix_nested_array_level( 686 'full_pkg_names', 2, full_pkg_names) 687 if (len(dep_packages) != len(apks) or 688 len(apks) != len(full_pkg_names)): 689 logging.info('dep_packages length is %d', len(dep_packages)) 690 logging.info('apks length is %d', len(apks)) 691 logging.info('full_pkg_names length is %d', len(full_pkg_names)) 692 raise error.TestFail( 693 'dep_packages/apks/full_pkg_names format error') 694 695 self.dep_packages = dep_packages 696 self.apks = apks 697 self.uiautomator = uiautomator or disable_play_store 698 # Setup dependent packages if required 699 packages = [] 700 if dep_packages: 701 packages = dep_packages[:] 702 if self.uiautomator: 703 packages.append(self._PKG_UIAUTOMATOR) 704 if packages: 705 logging.info('Setting up dependent package(s) %s', packages) 706 self.job.setup_dep(packages) 707 708 # TODO(b/29341443): Run logcat on non ArcTest test cases too. 709 with open(_VAR_LOGCAT_PATH, 'w') as f: 710 self.logcat_proc = subprocess.Popen( 711 ['android-sh', '-c', 'logcat -v threadtime'], 712 stdout=f, 713 stderr=subprocess.STDOUT, 714 close_fds=True) 715 716 wait_for_adb_ready() 717 718 # Setting verifier_verify_adb_installs to zero suppresses a dialog box 719 # that can appear asking for the user to consent to the install. 720 adb_shell('settings put global verifier_verify_adb_installs 0') 721 722 # Install apks based on dep_packages/apks/full_pkg_names tuples 723 if dep_packages: 724 for i in xrange(len(dep_packages)): 725 self._install_apks(dep_packages[i], apks[i], full_pkg_names[i]) 726 727 if self.uiautomator: 728 path = os.path.join(self.autodir, 'deps', self._PKG_UIAUTOMATOR) 729 sys.path.append(path) 730 self._add_ui_object_not_found_handler() 731 if disable_play_store and not is_package_disabled(_PLAY_STORE_PKG): 732 self._disable_play_store() 733 if not is_package_disabled(_PLAY_STORE_PKG): 734 raise error.TestFail('Failed to disable Google Play Store.') 735 self._should_reenable_play_store = True 736 if block_outbound: 737 self.block_outbound() 738 739 def _stop_logcat(self): 740 """Stop the adb logcat process gracefully.""" 741 if not self.logcat_proc: 742 return 743 # Running `adb kill-server` should have killed `adb logcat` 744 # process, but just in case also send termination signal. 745 self.logcat_proc.terminate() 746 747 class TimeoutException(Exception): 748 """Termination timeout timed out.""" 749 750 try: 751 utils.poll_for_condition( 752 condition=lambda: self.logcat_proc.poll() is not None, 753 exception=TimeoutException, 754 timeout=10, 755 sleep_interval=0.1, 756 desc='Waiting for adb logcat to terminate') 757 except TimeoutException: 758 logging.info('Killing adb logcat due to timeout') 759 self.logcat_proc.kill() 760 self.logcat_proc.wait() 761 762 def arc_teardown(self): 763 """ARC test teardown. 764 765 This function removes all installed packages in arc_setup stage 766 first. Then, it restores package verification and disables non-market 767 APK installation. 768 769 """ 770 if self.full_pkg_names: 771 for pkg in self.full_pkg_names: 772 logging.info('Uninstalling %s', pkg) 773 if not is_package_installed(pkg): 774 raise error.TestError('Package %s was not installed' % pkg) 775 adb_uninstall(pkg) 776 if self.uiautomator: 777 logging.info('Uninstalling %s', self._FULL_PKG_NAME_UIAUTOMATOR) 778 adb_uninstall(self._FULL_PKG_NAME_UIAUTOMATOR) 779 if self._should_reenable_play_store: 780 adb_shell('pm enable ' + _PLAY_STORE_PKG) 781 adb_shell('settings put secure install_non_market_apps 0') 782 adb_shell('settings put global package_verifier_enable 1') 783 adb_shell('settings put secure package_verifier_user_consent 0') 784 785 remove_android_file(_ANDROID_ADB_KEYS_PATH) 786 utils.system_output('adb kill-server') 787 788 def block_outbound(self): 789 """ Blocks the connection from the container to outer network. 790 791 The iptables settings accept only 100.115.92.2 port 5555 (adb) and 792 all local connections, e.g. uiautomator. 793 """ 794 logging.info('Blocking outbound connection') 795 _android_shell('iptables -I OUTPUT -j REJECT') 796 _android_shell('iptables -I OUTPUT -p tcp -s 100.115.92.2 --sport 5555 ' 797 '-j ACCEPT') 798 _android_shell('iptables -I OUTPUT -p tcp -d localhost -j ACCEPT') 799 800 def unblock_outbound(self): 801 """ Unblocks the connection from the container to outer network. 802 803 The iptables settings are not permanent which means they reset on 804 each instance invocation. But we can still use this function to 805 unblock the outbound connections during the test if needed. 806 """ 807 logging.info('Unblocking outbound connection') 808 _android_shell('iptables -D OUTPUT -p tcp -d localhost -j ACCEPT') 809 _android_shell('iptables -D OUTPUT -p tcp -s 100.115.92.2 --sport 5555 ' 810 '-j ACCEPT') 811 _android_shell('iptables -D OUTPUT -j REJECT') 812 813 def _add_ui_object_not_found_handler(self): 814 """Logs the device dump upon uiautomator.UiObjectNotFoundException.""" 815 from uiautomator import device as d 816 d.handlers.on(lambda d: logging.debug('Device window dump:\n%s', 817 d.dump())) 818 819 def _disable_play_store(self): 820 """Disables the Google Play Store app.""" 821 if is_package_disabled(_PLAY_STORE_PKG): 822 return 823 from uiautomator import device as d 824 adb_shell('am force-stop ' + _PLAY_STORE_PKG) 825 adb_shell('am start -W ' + _SETTINGS_PKG) 826 d(text='Apps', packageName=_SETTINGS_PKG).click.wait() 827 adb_shell('input text Store') 828 d(text='Google Play Store', packageName=_SETTINGS_PKG).click.wait() 829 d(textMatches='(?i)DISABLE').click.wait() 830 d(textMatches='(?i)DISABLE APP').click.wait() 831 ok_button = d(textMatches='(?i)OK') 832 if ok_button.exists: 833 ok_button.click.wait() 834 d(description='Close', packageName=_SETTINGS_PKG).click.wait() 835