682 lines
24 KiB
Python
682 lines
24 KiB
Python
# Copyright 2015 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
import glob
|
|
import logging
|
|
import os
|
|
import pipes
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
from autotest_lib.client.bin import test, utils
|
|
from autotest_lib.client.common_lib import error
|
|
from autotest_lib.client.common_lib.cros import chrome, arc_common
|
|
|
|
_ADB_KEYS_PATH = '/tmp/adb_keys'
|
|
_ADB_VENDOR_KEYS = 'ADB_VENDOR_KEYS'
|
|
_ANDROID_CONTAINER_PID_PATH = '/var/run/containers/android_*/container.pid'
|
|
_SCREENSHOT_DIR_PATH = '/var/log/arc-screenshots'
|
|
_SCREENSHOT_BASENAME = 'arc-screenshot'
|
|
_MAX_SCREENSHOT_NUM = 10
|
|
_SDCARD_PID_PATH = '/var/run/arc/sdcard.pid'
|
|
_ANDROID_ADB_KEYS_PATH = '/data/misc/adb/adb_keys'
|
|
_PROCESS_CHECK_INTERVAL_SECONDS = 1
|
|
_WAIT_FOR_ADB_READY = 60
|
|
_WAIT_FOR_ANDROID_PROCESS_SECONDS = 60
|
|
_WAIT_FOR_DATA_MOUNTED_SECONDS = 60
|
|
_VAR_LOGCAT_PATH = '/var/log/logcat'
|
|
|
|
|
|
def setup_adb_host():
|
|
"""Setup ADB host keys.
|
|
|
|
This sets up the files and environment variables that wait_for_adb_ready()
|
|
needs"""
|
|
if _ADB_VENDOR_KEYS in os.environ:
|
|
return
|
|
if not os.path.exists(_ADB_KEYS_PATH):
|
|
os.mkdir(_ADB_KEYS_PATH)
|
|
# adb expects $HOME to be writable.
|
|
os.environ['HOME'] = _ADB_KEYS_PATH
|
|
|
|
# Generate and save keys for adb if needed
|
|
key_path = os.path.join(_ADB_KEYS_PATH, 'test_key')
|
|
if not os.path.exists(key_path):
|
|
utils.system('adb keygen ' + pipes.quote(key_path))
|
|
os.environ[_ADB_VENDOR_KEYS] = key_path
|
|
|
|
|
|
def adb_connect():
|
|
"""Attempt to connect ADB to the Android container.
|
|
|
|
Returns true if successful. Do not call this function directly. Call
|
|
wait_for_adb_ready() instead."""
|
|
if not is_android_booted():
|
|
return False
|
|
if utils.system('adb connect localhost:22', ignore_status=True) != 0:
|
|
return False
|
|
return is_adb_connected()
|
|
|
|
|
|
def is_adb_connected():
|
|
"""Return true if adb is connected to the container."""
|
|
output = utils.system_output('adb get-state', ignore_status=True)
|
|
logging.debug('adb get-state: %s', output)
|
|
return output.strip() == 'device'
|
|
|
|
|
|
def is_partial_boot_enabled():
|
|
"""Return true if partial boot is enabled.
|
|
|
|
When partial boot is enabled, Android is started at login screen without
|
|
any persistent state (e.g. /data is not mounted).
|
|
"""
|
|
return _android_shell('getprop ro.boot.partial_boot') == '1'
|
|
|
|
|
|
def _is_android_data_mounted():
|
|
"""Return true if Android's /data is mounted with partial boot enabled."""
|
|
return _android_shell('getprop ro.data_mounted') == '1'
|
|
|
|
|
|
def _wait_for_data_mounted(timeout=_WAIT_FOR_DATA_MOUNTED_SECONDS):
|
|
utils.poll_for_condition(
|
|
condition=_is_android_data_mounted,
|
|
desc='Wait for /data mounted',
|
|
timeout=timeout,
|
|
sleep_interval=_PROCESS_CHECK_INTERVAL_SECONDS)
|
|
|
|
|
|
def wait_for_adb_ready(timeout=_WAIT_FOR_ADB_READY):
|
|
"""Wait for the ADB client to connect to the ARC container.
|
|
|
|
@param timeout: Timeout in seconds.
|
|
"""
|
|
# When partial boot is enabled, although adbd is started at login screen,
|
|
# we still need /data to be mounted to set up key-based authentication.
|
|
# /data should be mounted once the user has logged in.
|
|
if is_partial_boot_enabled():
|
|
_wait_for_data_mounted()
|
|
|
|
setup_adb_host()
|
|
if is_adb_connected():
|
|
return
|
|
|
|
# Push keys for adb.
|
|
pubkey_path = os.environ[_ADB_VENDOR_KEYS] + '.pub'
|
|
with open(pubkey_path, 'r') as f:
|
|
_write_android_file(_ANDROID_ADB_KEYS_PATH, f.read())
|
|
_android_shell('restorecon ' + pipes.quote(_ANDROID_ADB_KEYS_PATH))
|
|
|
|
# This starts adbd.
|
|
_android_shell('setprop sys.usb.config mtp,adb')
|
|
|
|
# Kill existing adb server to ensure that a full reconnect is performed.
|
|
utils.system('adb kill-server', ignore_status=True)
|
|
|
|
exception = error.TestFail('adb is not ready in %d seconds.' % timeout)
|
|
utils.poll_for_condition(adb_connect,
|
|
exception,
|
|
timeout)
|
|
|
|
|
|
def grant_permissions(package, permissions):
|
|
"""Grants permissions to a package.
|
|
|
|
@param package: Package name.
|
|
@param permissions: A list of permissions.
|
|
|
|
"""
|
|
for permission in permissions:
|
|
adb_shell('pm grant %s android.permission.%s' % (
|
|
pipes.quote(package), pipes.quote(permission)))
|
|
|
|
|
|
def adb_cmd(cmd, **kwargs):
|
|
"""Executed cmd using adb. Must wait for adb ready.
|
|
|
|
@param cmd: Command to run.
|
|
"""
|
|
wait_for_adb_ready()
|
|
return utils.system_output('adb %s' % cmd, **kwargs)
|
|
|
|
|
|
def adb_shell(cmd, **kwargs):
|
|
"""Executed shell command with adb.
|
|
|
|
@param cmd: Command to run.
|
|
"""
|
|
output = adb_cmd('shell %s' % pipes.quote(cmd), **kwargs)
|
|
# Some android commands include a trailing CRLF in their output.
|
|
if kwargs.pop('strip_trailing_whitespace', True):
|
|
output = output.rstrip()
|
|
return output
|
|
|
|
|
|
def adb_install(apk):
|
|
"""Install an apk into container. You must connect first.
|
|
|
|
@param apk: Package to install.
|
|
"""
|
|
return adb_cmd('install -r %s' % apk)
|
|
|
|
|
|
def adb_uninstall(apk):
|
|
"""Remove an apk from container. You must connect first.
|
|
|
|
@param apk: Package to uninstall.
|
|
"""
|
|
return adb_cmd('uninstall %s' % apk)
|
|
|
|
|
|
def adb_reboot():
|
|
"""Reboots the container. You must connect first."""
|
|
adb_root()
|
|
return adb_cmd('reboot', ignore_status=True)
|
|
|
|
|
|
def adb_root():
|
|
"""Restart adbd with root permission."""
|
|
adb_cmd('root')
|
|
|
|
|
|
def get_container_root():
|
|
"""Returns path to Android container root directory.
|
|
|
|
Raises:
|
|
TestError if no container root directory is found, or
|
|
more than one container root directories are found.
|
|
"""
|
|
# Find the PID file rather than the android_XXXXXX/ directory to ignore
|
|
# stale and empty android_XXXXXX/ directories when they exist.
|
|
# TODO(yusukes): Investigate why libcontainer sometimes fails to remove
|
|
# the directory. See b/63376749 for more details.
|
|
arc_container_pid_files = glob.glob(_ANDROID_CONTAINER_PID_PATH)
|
|
|
|
if len(arc_container_pid_files) == 0:
|
|
raise error.TestError('Android container not available')
|
|
|
|
if len(arc_container_pid_files) > 1:
|
|
raise error.TestError('Multiple Android containers found: %r. '
|
|
'Reboot your DUT to recover.' % (
|
|
arc_container_pid_files))
|
|
|
|
return os.path.dirname(arc_container_pid_files[0])
|
|
|
|
|
|
def get_job_pid(job_name):
|
|
"""Returns the PID of an upstart job."""
|
|
status = utils.system_output('status %s' % job_name)
|
|
match = re.match(r'^%s start/running, process (\d+)$' % job_name,
|
|
status)
|
|
if not match:
|
|
raise error.TestError('Unexpected status: "%s"' % status)
|
|
return match.group(1)
|
|
|
|
|
|
def get_container_pid():
|
|
"""Returns the PID of the container."""
|
|
container_root = get_container_root()
|
|
pid_path = os.path.join(container_root, 'container.pid')
|
|
return utils.read_one_line(pid_path)
|
|
|
|
|
|
def get_sdcard_pid():
|
|
"""Returns the PID of the sdcard container."""
|
|
return utils.read_one_line(_SDCARD_PID_PATH)
|
|
|
|
|
|
def get_removable_media_pid():
|
|
"""Returns the PID of the arc-removable-media FUSE daemon."""
|
|
job_pid = get_job_pid('arc-removable-media')
|
|
# |job_pid| is the minijail process, obtain the PID of the process running
|
|
# inside the mount namespace.
|
|
# FUSE process is the only process running as chronos in the process group.
|
|
return utils.system_output('pgrep -u chronos -g %s' % job_pid)
|
|
|
|
|
|
def get_obb_mounter_pid():
|
|
"""Returns the PID of the OBB mounter."""
|
|
return utils.system_output('pgrep -f -u root ^/usr/bin/arc-obb-mounter')
|
|
|
|
|
|
def is_android_booted():
|
|
"""Return whether Android has completed booting."""
|
|
# We used to check sys.boot_completed system property to detect Android has
|
|
# booted in Android M, but in Android N it is set long before BOOT_COMPLETED
|
|
# intent is broadcast. So we read event logs instead.
|
|
log = _android_shell(
|
|
'logcat -d -b events *:S arc_system_event', ignore_status=True)
|
|
return 'ArcAppLauncher:started' in log
|
|
|
|
|
|
def is_android_process_running(process_name):
|
|
"""Return whether Android has completed booting.
|
|
|
|
@param process_name: Process name.
|
|
"""
|
|
output = adb_shell('ps | grep %s' % pipes.quote(' %s$' % process_name))
|
|
return bool(output)
|
|
|
|
|
|
def check_android_file_exists(filename):
|
|
"""Checks whether the given file exists in the Android filesystem
|
|
|
|
@param filename: File to check.
|
|
"""
|
|
return adb_shell('test -e {} && echo FileExists'.format(
|
|
pipes.quote(filename))).find("FileExists") >= 0
|
|
|
|
|
|
def read_android_file(filename):
|
|
"""Reads a file in Android filesystem.
|
|
|
|
@param filename: File to read.
|
|
"""
|
|
with tempfile.NamedTemporaryFile() as tmpfile:
|
|
adb_cmd('pull %s %s' % (pipes.quote(filename),
|
|
pipes.quote(tmpfile.name)))
|
|
with open(tmpfile.name) as f:
|
|
return f.read()
|
|
|
|
return None
|
|
|
|
|
|
def write_android_file(filename, data):
|
|
"""Writes to a file in Android filesystem.
|
|
|
|
@param filename: File to write.
|
|
@param data: Data to write.
|
|
"""
|
|
with tempfile.NamedTemporaryFile() as tmpfile:
|
|
tmpfile.write(data)
|
|
tmpfile.flush()
|
|
|
|
adb_cmd('push %s %s' % (pipes.quote(tmpfile.name),
|
|
pipes.quote(filename)))
|
|
|
|
|
|
def _write_android_file(filename, data):
|
|
"""Writes to a file in Android filesystem.
|
|
|
|
This is an internal function used to bootstrap adb.
|
|
Tests should use write_android_file instead.
|
|
"""
|
|
android_cmd = 'cat > %s' % pipes.quote(filename)
|
|
cros_cmd = 'android-sh -c %s' % pipes.quote(android_cmd)
|
|
utils.run(cros_cmd, stdin=data)
|
|
|
|
|
|
def remove_android_file(filename):
|
|
"""Removes a file in Android filesystem.
|
|
|
|
@param filename: File to remove.
|
|
"""
|
|
adb_shell('rm -f %s' % pipes.quote(filename))
|
|
|
|
|
|
def wait_for_android_boot(timeout=None):
|
|
"""Sleep until Android has completed booting or timeout occurs.
|
|
|
|
@param timeout: Timeout in seconds.
|
|
"""
|
|
arc_common.wait_for_android_boot(timeout)
|
|
|
|
|
|
def wait_for_android_process(process_name,
|
|
timeout=_WAIT_FOR_ANDROID_PROCESS_SECONDS):
|
|
"""Sleep until an Android process is running or timeout occurs.
|
|
|
|
@param process_name: Process name.
|
|
@param timeout: Timeout in seconds.
|
|
"""
|
|
condition = lambda: is_android_process_running(process_name)
|
|
utils.poll_for_condition(condition=condition,
|
|
desc='%s is running' % process_name,
|
|
timeout=timeout,
|
|
sleep_interval=_PROCESS_CHECK_INTERVAL_SECONDS)
|
|
|
|
|
|
def _android_shell(cmd, **kwargs):
|
|
"""Execute cmd instead the Android container.
|
|
|
|
This function is strictly for internal use only, as commands do not run in a
|
|
fully consistent Android environment. Prefer adb_shell instead.
|
|
"""
|
|
return utils.system_output('android-sh -c {}'.format(pipes.quote(cmd)),
|
|
**kwargs)
|
|
|
|
|
|
def is_android_container_alive():
|
|
"""Check if android container is alive."""
|
|
try:
|
|
container_pid = get_container_pid()
|
|
except Exception, e:
|
|
logging.error('is_android_container_alive failed: %r', e)
|
|
return False
|
|
return utils.pid_is_alive(int(container_pid))
|
|
|
|
|
|
def is_package_installed(package):
|
|
"""Check if a package is installed. adb must be ready.
|
|
|
|
@param package: Package in request.
|
|
"""
|
|
packages = adb_shell('pm list packages').splitlines()
|
|
package_entry = 'package:{}'.format(package)
|
|
return package_entry in packages
|
|
|
|
|
|
def _before_iteration_hook(obj):
|
|
"""Executed by parent class before every iteration.
|
|
|
|
This function resets the run_once_finished flag before every iteration
|
|
so we can detect failure on every single iteration.
|
|
|
|
Args:
|
|
obj: the test itself
|
|
"""
|
|
obj.run_once_finished = False
|
|
|
|
|
|
def _after_iteration_hook(obj):
|
|
"""Executed by parent class after every iteration.
|
|
|
|
The parent class will handle exceptions and failures in the run and will
|
|
always call this hook afterwards. Take a screenshot if the run has not
|
|
been marked as finished (i.e. there was a failure/exception).
|
|
|
|
Args:
|
|
obj: the test itself
|
|
"""
|
|
if not obj.run_once_finished:
|
|
if not os.path.exists(_SCREENSHOT_DIR_PATH):
|
|
os.mkdir(_SCREENSHOT_DIR_PATH, 0755)
|
|
obj.num_screenshots += 1
|
|
if obj.num_screenshots <= _MAX_SCREENSHOT_NUM:
|
|
logging.warning('Iteration %d failed, taking a screenshot.',
|
|
obj.iteration)
|
|
from cros.graphics.gbm import crtcScreenshot
|
|
try:
|
|
image = crtcScreenshot()
|
|
image.save('{}/{}_iter{}.png'.format(_SCREENSHOT_DIR_PATH,
|
|
_SCREENSHOT_BASENAME,
|
|
obj.iteration))
|
|
except Exception:
|
|
e = sys.exc_info()[0]
|
|
logging.warning('Unable to capture screenshot. %s' % e)
|
|
else:
|
|
logging.warning('Too many failures, no screenshot taken')
|
|
|
|
|
|
def send_keycode(keycode):
|
|
"""Sends the given keycode to the container
|
|
|
|
@param keycode: keycode to send.
|
|
"""
|
|
adb_shell('input keyevent {}'.format(keycode))
|
|
|
|
|
|
def get_android_sdk_version():
|
|
"""Returns the Android SDK version.
|
|
|
|
This function can be called before Android container boots.
|
|
"""
|
|
with open('/etc/lsb-release') as f:
|
|
values = dict(line.split('=', 1) for line in f.read().splitlines())
|
|
try:
|
|
return int(values['CHROMEOS_ARC_ANDROID_SDK_VERSION'])
|
|
except (KeyError, ValueError):
|
|
raise error.TestError('Could not determine Android SDK version')
|
|
|
|
|
|
class ArcTest(test.test):
|
|
""" Base class of ARC Test.
|
|
|
|
This class could be used as super class of an ARC test for saving
|
|
redundant codes for container bringup, autotest-dep package(s) including
|
|
uiautomator setup if required, and apks install/remove during
|
|
arc_setup/arc_teardown, respectively. By default arc_setup() is called in
|
|
initialize() after Android have been brought up. It could also be overridden
|
|
to perform non-default tasks. For example, a simple ArcHelloWorldTest can be
|
|
just implemented with print 'HelloWorld' in its run_once() and no other
|
|
functions are required. We could expect ArcHelloWorldTest would bring up
|
|
browser and wait for container up, then print 'Hello World', and shutdown
|
|
browser after. As a precaution, if you overwrite initialize(), arc_setup(),
|
|
or cleanup() function(s) in ARC test, remember to call the corresponding
|
|
function(s) in this base class as well.
|
|
|
|
"""
|
|
version = 1
|
|
_PKG_UIAUTOMATOR = 'uiautomator'
|
|
_FULL_PKG_NAME_UIAUTOMATOR = 'com.github.uiautomator'
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
"""Initialize flag setting."""
|
|
super(ArcTest, self).__init__(*args, **kwargs)
|
|
self.initialized = False
|
|
# Set the flag run_once_finished to detect if a test is executed
|
|
# successfully without any exception thrown. Otherwise, generate
|
|
# a screenshot in /var/log for debugging.
|
|
self.run_once_finished = False
|
|
self.logcat_proc = None
|
|
self.dep_package = None
|
|
self.apks = None
|
|
self.full_pkg_names = []
|
|
self.uiautomator = False
|
|
self.email_id = None
|
|
self.password = None
|
|
self._chrome = None
|
|
if os.path.exists(_SCREENSHOT_DIR_PATH):
|
|
shutil.rmtree(_SCREENSHOT_DIR_PATH)
|
|
self.register_before_iteration_hook(_before_iteration_hook)
|
|
self.register_after_iteration_hook(_after_iteration_hook)
|
|
# Keep track of the number of debug screenshots taken and keep the
|
|
# total number sane to avoid issues.
|
|
self.num_screenshots = 0
|
|
|
|
def initialize(self, extension_path=None,
|
|
arc_mode=arc_common.ARC_MODE_ENABLED, **chrome_kargs):
|
|
"""Log in to a test account."""
|
|
extension_paths = [extension_path] if extension_path else []
|
|
self._chrome = chrome.Chrome(extension_paths=extension_paths,
|
|
arc_mode=arc_mode,
|
|
**chrome_kargs)
|
|
if extension_path:
|
|
self._extension = self._chrome.get_extension(extension_path)
|
|
else:
|
|
self._extension = None
|
|
# With ARC enabled, Chrome will wait until container to boot up
|
|
# before returning here, see chrome.py.
|
|
self.initialized = True
|
|
try:
|
|
if is_android_container_alive():
|
|
self.arc_setup()
|
|
else:
|
|
logging.error('Container is alive?')
|
|
except Exception as err:
|
|
self.cleanup()
|
|
raise error.TestFail(err)
|
|
|
|
def after_run_once(self):
|
|
"""Executed after run_once() only if there were no errors.
|
|
|
|
This function marks the run as finished with a flag. If there was a
|
|
failure the flag won't be set and the failure can then be detected by
|
|
testing the run_once_finished flag.
|
|
"""
|
|
logging.info('After run_once')
|
|
self.run_once_finished = True
|
|
|
|
def cleanup(self):
|
|
"""Log out of Chrome."""
|
|
if not self.initialized:
|
|
logging.info('Skipping ARC cleanup: not initialized')
|
|
return
|
|
logging.info('Starting ARC cleanup')
|
|
try:
|
|
if is_android_container_alive():
|
|
self.arc_teardown()
|
|
except Exception as err:
|
|
raise error.TestFail(err)
|
|
finally:
|
|
try:
|
|
self._stop_logcat()
|
|
finally:
|
|
if self._chrome is not None:
|
|
self._chrome.close()
|
|
|
|
def arc_setup(self, dep_package=None, apks=None, full_pkg_names=None,
|
|
uiautomator=False, email_id=None, password=None,
|
|
block_outbound=False):
|
|
"""ARC test setup: Setup dependencies and install apks.
|
|
|
|
This function disables package verification and enables non-market
|
|
APK installation. Then, it installs specified APK(s) and uiautomator
|
|
package and path if required in a test.
|
|
|
|
@param dep_package: Package name of autotest_deps APK package.
|
|
@param apks: Array of APK names to be installed in dep_package.
|
|
@param full_pkg_names: Array of full package names to be removed
|
|
in teardown.
|
|
@param uiautomator: uiautomator python package is required or not.
|
|
|
|
@param email_id: email id to be attached to the android. Only used
|
|
when account_util is set to true.
|
|
@param password: password related to the email_id.
|
|
@param block_outbound: block outbound network traffic during a test.
|
|
"""
|
|
if not self.initialized:
|
|
logging.info('Skipping ARC setup: not initialized')
|
|
return
|
|
logging.info('Starting ARC setup')
|
|
self.dep_package = dep_package
|
|
self.apks = apks
|
|
self.uiautomator = uiautomator
|
|
self.email_id = email_id
|
|
self.password = password
|
|
# Setup dependent packages if required
|
|
packages = []
|
|
if dep_package:
|
|
packages.append(dep_package)
|
|
if self.uiautomator:
|
|
packages.append(self._PKG_UIAUTOMATOR)
|
|
if packages:
|
|
logging.info('Setting up dependent package(s) %s', packages)
|
|
self.job.setup_dep(packages)
|
|
|
|
# TODO(b/29341443): Run logcat on non ArcTest test cases too.
|
|
with open(_VAR_LOGCAT_PATH, 'w') as f:
|
|
self.logcat_proc = subprocess.Popen(
|
|
['android-sh', '-c', 'logcat -v threadtime'],
|
|
stdout=f,
|
|
stderr=subprocess.STDOUT,
|
|
close_fds=True)
|
|
|
|
wait_for_adb_ready()
|
|
|
|
# package_verifier_user_consent == -1 means to reject Google's
|
|
# verification on the server side through Play Store. This suppress a
|
|
# consent dialog from the system.
|
|
adb_shell('settings put secure package_verifier_user_consent -1')
|
|
adb_shell('settings put global package_verifier_enable 0')
|
|
adb_shell('settings put secure install_non_market_apps 1')
|
|
|
|
if self.dep_package:
|
|
apk_path = os.path.join(self.autodir, 'deps', self.dep_package)
|
|
if self.apks:
|
|
for apk in self.apks:
|
|
logging.info('Installing %s', apk)
|
|
adb_install('%s/%s' % (apk_path, apk))
|
|
# Verify if package(s) are installed correctly
|
|
if not full_pkg_names:
|
|
raise error.TestError('Package names of apks expected')
|
|
for pkg in full_pkg_names:
|
|
logging.info('Check if %s is installed', pkg)
|
|
if not is_package_installed(pkg):
|
|
raise error.TestError('Package %s not found' % pkg)
|
|
# Make sure full_pkg_names contains installed packages only
|
|
# so arc_teardown() knows what packages to uninstall.
|
|
self.full_pkg_names.append(pkg)
|
|
|
|
if self.uiautomator:
|
|
path = os.path.join(self.autodir, 'deps', self._PKG_UIAUTOMATOR)
|
|
sys.path.append(path)
|
|
if block_outbound:
|
|
self.block_outbound()
|
|
|
|
def _stop_logcat(self):
|
|
"""Stop the adb logcat process gracefully."""
|
|
if not self.logcat_proc:
|
|
return
|
|
# Running `adb kill-server` should have killed `adb logcat`
|
|
# process, but just in case also send termination signal.
|
|
self.logcat_proc.terminate()
|
|
|
|
class TimeoutException(Exception):
|
|
"""Termination timeout timed out."""
|
|
|
|
try:
|
|
utils.poll_for_condition(
|
|
condition=lambda: self.logcat_proc.poll() is not None,
|
|
exception=TimeoutException,
|
|
timeout=10,
|
|
sleep_interval=0.1,
|
|
desc='Waiting for adb logcat to terminate')
|
|
except TimeoutException:
|
|
logging.info('Killing adb logcat due to timeout')
|
|
self.logcat_proc.kill()
|
|
self.logcat_proc.wait()
|
|
|
|
def arc_teardown(self):
|
|
"""ARC test teardown.
|
|
|
|
This function removes all installed packages in arc_setup stage
|
|
first. Then, it restores package verification and disables non-market
|
|
APK installation.
|
|
|
|
"""
|
|
if self.full_pkg_names:
|
|
for pkg in self.full_pkg_names:
|
|
logging.info('Uninstalling %s', pkg)
|
|
if not is_package_installed(pkg):
|
|
raise error.TestError('Package %s was not installed' % pkg)
|
|
adb_uninstall(pkg)
|
|
if self.uiautomator:
|
|
logging.info('Uninstalling %s', self._FULL_PKG_NAME_UIAUTOMATOR)
|
|
adb_uninstall(self._FULL_PKG_NAME_UIAUTOMATOR)
|
|
adb_shell('settings put secure install_non_market_apps 0')
|
|
adb_shell('settings put global package_verifier_enable 1')
|
|
adb_shell('settings put secure package_verifier_user_consent 0')
|
|
|
|
remove_android_file(_ANDROID_ADB_KEYS_PATH)
|
|
utils.system_output('adb kill-server')
|
|
|
|
def block_outbound(self):
|
|
""" Blocks the connection from the container to outer network.
|
|
|
|
The iptables settings accept only 100.115.92.2 port 5555 (adb) and
|
|
all local connections, e.g. uiautomator.
|
|
"""
|
|
logging.info('Blocking outbound connection')
|
|
_android_shell('iptables -I OUTPUT -j REJECT')
|
|
_android_shell('iptables -I OUTPUT -p tcp -s 100.115.92.2 --sport 5555 '
|
|
'-j ACCEPT')
|
|
_android_shell('iptables -I OUTPUT -p tcp -d localhost -j ACCEPT')
|
|
|
|
def unblock_outbound(self):
|
|
""" Unblocks the connection from the container to outer network.
|
|
|
|
The iptables settings are not permanent which means they reset on
|
|
each instance invocation. But we can still use this function to
|
|
unblock the outbound connections during the test if needed.
|
|
"""
|
|
logging.info('Unblocking outbound connection')
|
|
_android_shell('iptables -D OUTPUT -p tcp -d localhost -j ACCEPT')
|
|
_android_shell('iptables -D OUTPUT -p tcp -s 100.115.92.2 --sport 5555 '
|
|
'-j ACCEPT')
|
|
_android_shell('iptables -D OUTPUT -j REJECT')
|