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