• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_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
38def 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
57def 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
69def 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
76def 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
85def _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
90def get_zygote_type():
91    """Return zygote service type."""
92    return _android_shell('getprop ro.zygote')
93
94
95def get_sdk_version():
96    """Return the SDK level version for Android."""
97    return _android_shell('getprop ro.build.version.sdk')
98
99
100def get_product():
101    """Return the product string used for the Android build."""
102    return _android_shell('getprop ro.build.product')
103
104
105def _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
113def 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
146def 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
158def 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
167def 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
179def 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
187def 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
195def adb_reboot():
196    """Reboots the container. You must connect first."""
197    adb_root()
198    return adb_cmd('reboot', ignore_status=True)
199
200
201def adb_root():
202    """Restart adbd with root permission."""
203    adb_cmd('root')
204
205
206def get_container_root():
207    """Returns path to Android container root directory."""
208    return _ANDROID_CONTAINER_ROOT_PATH
209
210
211def 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
232def 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
237def 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
247def get_container_pid():
248    """Returns the PID of the container."""
249    return utils.read_one_line(get_container_pid_path())
250
251
252def get_sdcard_pid():
253    """Returns the PID of the sdcard container."""
254    return utils.read_one_line(_SDCARD_PID_PATH)
255
256
257def 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
266def 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
271def 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
281def 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
290def 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
299def 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
313def 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
327def _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
338def 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
346def 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
354def 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
368def _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
378def 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
388def _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
405def 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
413def 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
421def _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
433def _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
465def 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
473def 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
486def 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
513class 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