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