Home | History | Annotate | Download | only in cros
      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