Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 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_PATH = '/var/run/containers/android_*' |
| 22 | _SCREENSHOT_DIR_PATH = '/var/log/arc-screenshots' |
| 23 | _SCREENSHOT_BASENAME = 'arc-screenshot' |
| 24 | _MAX_SCREENSHOT_NUM = 10 |
| 25 | _SDCARD_PID_PATH = '/var/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 | _VAR_LOGCAT_PATH = '/var/log/logcat' |
| 31 | |
| 32 | |
| 33 | def setup_adb_host(): |
| 34 | """Setup ADB host keys. |
| 35 | |
| 36 | This sets up the files and environment variables that wait_for_adb_ready() needs""" |
| 37 | if _ADB_VENDOR_KEYS in os.environ: |
| 38 | return |
| 39 | if not os.path.exists(_ADB_KEYS_PATH): |
| 40 | os.mkdir(_ADB_KEYS_PATH) |
| 41 | # adb expects $HOME to be writable. |
| 42 | os.environ['HOME'] = _ADB_KEYS_PATH |
| 43 | |
| 44 | # Generate and save keys for adb if needed |
| 45 | key_path = os.path.join(_ADB_KEYS_PATH, 'test_key') |
| 46 | if not os.path.exists(key_path): |
| 47 | utils.system('adb keygen ' + pipes.quote(key_path)) |
| 48 | os.environ[_ADB_VENDOR_KEYS] = key_path |
| 49 | |
| 50 | |
| 51 | def adb_connect(): |
| 52 | """Attempt to connect ADB to the Android container. |
| 53 | |
| 54 | Returns true if successful. Do not call this function directly. Call |
| 55 | wait_for_adb_ready() instead.""" |
| 56 | if not is_android_booted(): |
| 57 | return False |
Ricky Zhou | 5c2745d | 2016-09-19 20:44:39 -0700 | [diff] [blame] | 58 | if utils.system('adb connect localhost:22', ignore_status=True) != 0: |
| 59 | return False |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 60 | return is_adb_connected() |
| 61 | |
| 62 | |
| 63 | def is_adb_connected(): |
| 64 | """Return true if adb is connected to the container.""" |
Ricky Zhou | 5c2745d | 2016-09-19 20:44:39 -0700 | [diff] [blame] | 65 | output = utils.system_output('adb get-state', ignore_status=True) |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 66 | logging.debug('adb get-state: %s', output) |
| 67 | return output.strip() == 'device' |
| 68 | |
| 69 | |
| 70 | def wait_for_adb_ready(timeout=_WAIT_FOR_ADB_READY): |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 71 | """Wait for the ADB client to connect to the ARC container. |
| 72 | |
| 73 | @param timeout: Timeout in seconds. |
| 74 | """ |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 75 | setup_adb_host() |
| 76 | if is_adb_connected(): |
| 77 | return |
| 78 | |
| 79 | # Push keys for adb. |
| 80 | pubkey_path = os.environ[_ADB_VENDOR_KEYS] + '.pub' |
| 81 | with open(pubkey_path, 'r') as f: |
| 82 | _write_android_file(_ANDROID_ADB_KEYS_PATH, f.read()) |
| 83 | _android_shell('restorecon ' + pipes.quote(_ANDROID_ADB_KEYS_PATH)) |
| 84 | |
| 85 | # This starts adbd. |
| 86 | _android_shell('setprop sys.usb.config mtp,adb') |
| 87 | |
| 88 | # Kill existing adb server to ensure that a full reconnect is performed. |
Ricky Zhou | 5c2745d | 2016-09-19 20:44:39 -0700 | [diff] [blame] | 89 | utils.system('adb kill-server', ignore_status=True) |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 90 | |
| 91 | exception = error.TestFail('adb is not ready in %d seconds.' % timeout) |
| 92 | utils.poll_for_condition(adb_connect, |
| 93 | exception, |
| 94 | timeout) |
| 95 | |
| 96 | |
Cheng-Yi Chiang | 21cf23a | 2016-09-10 01:31:31 +0800 | [diff] [blame] | 97 | def grant_permissions(package, permissions): |
| 98 | """Grants permissions to a package. |
| 99 | |
| 100 | @param package: Package name. |
| 101 | @param permissions: A list of permissions. |
| 102 | |
| 103 | """ |
| 104 | for permission in permissions: |
| 105 | adb_shell('pm grant %s android.permission.%s' % ( |
| 106 | pipes.quote(package), pipes.quote(permission))) |
| 107 | |
| 108 | |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 109 | def adb_cmd(cmd, **kwargs): |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 110 | """Executed cmd using adb. Must wait for adb ready. |
| 111 | |
| 112 | @param cmd: Command to run. |
| 113 | """ |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 114 | wait_for_adb_ready() |
| 115 | return utils.system_output('adb %s' % cmd, **kwargs) |
| 116 | |
| 117 | |
| 118 | def adb_shell(cmd, **kwargs): |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 119 | """Executed shell command with adb. |
| 120 | |
| 121 | @param cmd: Command to run. |
| 122 | """ |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 123 | output = adb_cmd('shell %s' % pipes.quote(cmd), **kwargs) |
| 124 | # Some android commands include a trailing CRLF in their output. |
| 125 | if kwargs.pop('strip_trailing_whitespace', True): |
| 126 | output = output.rstrip() |
| 127 | return output |
| 128 | |
| 129 | |
| 130 | def adb_install(apk): |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 131 | """Install an apk into container. You must connect first. |
| 132 | |
| 133 | @param apk: Package to install. |
| 134 | """ |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 135 | return adb_cmd('install -r %s' % apk) |
| 136 | |
| 137 | |
| 138 | def adb_uninstall(apk): |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 139 | """Remove an apk from container. You must connect first. |
| 140 | |
| 141 | @param apk: Package to uninstall. |
| 142 | """ |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 143 | return adb_cmd('uninstall %s' % apk) |
| 144 | |
| 145 | |
| 146 | def adb_reboot(): |
| 147 | """Reboots the container. You must connect first.""" |
| 148 | adb_root() |
| 149 | return adb_cmd('reboot', ignore_status=True) |
| 150 | |
| 151 | |
| 152 | def adb_root(): |
| 153 | """Restart adbd with root permission.""" |
| 154 | adb_cmd('root') |
| 155 | |
| 156 | |
| 157 | def get_container_root(): |
| 158 | """Returns path to Android container root directory. |
| 159 | |
| 160 | Raises: |
| 161 | TestError if no container root directory is found, or |
| 162 | more than one container root directories are found. |
| 163 | """ |
| 164 | arc_container_roots = glob.glob(_ANDROID_CONTAINER_PATH) |
| 165 | if len(arc_container_roots) != 1: |
| 166 | raise error.TestError( |
| 167 | 'Android container not available: %r' % arc_container_roots) |
| 168 | return arc_container_roots[0] |
| 169 | |
| 170 | |
| 171 | def get_job_pid(job_name): |
| 172 | """Returns the PID of an upstart job.""" |
| 173 | status = utils.system_output('status %s' % job_name) |
| 174 | match = re.match(r'^%s start/running, process (\d+)$' % job_name, |
| 175 | status) |
| 176 | if not match: |
| 177 | raise error.TestError('Unexpected status: "%s"' % status) |
| 178 | return match.group(1) |
| 179 | |
| 180 | |
| 181 | def get_container_pid(): |
| 182 | """Returns the PID of the container.""" |
| 183 | container_root = get_container_root() |
| 184 | pid_path = os.path.join(container_root, 'container.pid') |
| 185 | return utils.read_one_line(pid_path) |
| 186 | |
| 187 | |
| 188 | def get_sdcard_pid(): |
| 189 | """Returns the PID of the sdcard container.""" |
| 190 | return utils.read_one_line(_SDCARD_PID_PATH) |
| 191 | |
| 192 | |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 193 | def get_removable_media_pid(): |
| 194 | """Returns the PID of the arc-removable-media FUSE daemon.""" |
| 195 | job_pid = get_job_pid('arc-removable-media') |
| 196 | # |job_pid| is the minijail process, obtain the PID of the process running |
| 197 | # inside the mount namespace. |
| 198 | # FUSE process is the only process running as chronos in the process group. |
| 199 | return utils.system_output('pgrep -u chronos -g %s' % job_pid) |
| 200 | |
| 201 | |
| 202 | def get_obb_mounter_pid(): |
| 203 | """Returns the PID of the OBB mounter.""" |
| 204 | return utils.system_output('pgrep -f -u root ^/usr/bin/arc-obb-mounter') |
| 205 | |
| 206 | |
| 207 | def is_android_booted(): |
| 208 | """Return whether Android has completed booting.""" |
| 209 | output = _android_shell('getprop sys.boot_completed', |
| 210 | ignore_status=True) |
| 211 | return output.strip() == '1' |
| 212 | |
| 213 | |
| 214 | def is_android_process_running(process_name): |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 215 | """Return whether Android has completed booting. |
| 216 | |
| 217 | @param process_name: Process name. |
| 218 | """ |
Shuhei Takahashi | c412741 | 2017-02-01 15:40:09 +0900 | [diff] [blame^] | 219 | output = adb_shell('ps | grep %s' % pipes.quote(' %s$' % process_name)) |
| 220 | return bool(output) |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 221 | |
| 222 | |
Chen-Hao Chang@google.com | 1d6f3b4 | 2016-09-26 15:17:57 +0800 | [diff] [blame] | 223 | def check_android_file_exists(filename): |
| 224 | """Checks whether the given file exists in the Android filesystem |
| 225 | |
| 226 | @param filename: File to check. |
| 227 | """ |
| 228 | return adb_shell('test -e {} && echo FileExists'.format( |
| 229 | pipes.quote(filename))).find("FileExists") >= 0 |
| 230 | |
| 231 | |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 232 | def read_android_file(filename): |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 233 | """Reads a file in Android filesystem. |
| 234 | |
| 235 | @param filename: File to read. |
| 236 | """ |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 237 | with tempfile.NamedTemporaryFile() as tmpfile: |
| 238 | adb_cmd('pull %s %s' % (pipes.quote(filename), pipes.quote(tmpfile.name))) |
| 239 | with open(tmpfile.name) as f: |
| 240 | return f.read() |
| 241 | |
| 242 | return None |
| 243 | |
| 244 | |
| 245 | def write_android_file(filename, data): |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 246 | """Writes to a file in Android filesystem. |
| 247 | |
| 248 | @param filename: File to write. |
| 249 | @param data: Data to write. |
| 250 | """ |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 251 | with tempfile.NamedTemporaryFile() as tmpfile: |
| 252 | tmpfile.write(data) |
| 253 | tmpfile.flush() |
| 254 | |
| 255 | adb_cmd('push %s %s' % (pipes.quote(tmpfile.name), pipes.quote(filename))) |
| 256 | |
| 257 | |
| 258 | def _write_android_file(filename, data): |
| 259 | """Writes to a file in Android filesystem. |
| 260 | |
| 261 | This is an internal function used to bootstrap adb. |
| 262 | Tests should use write_android_file instead. |
| 263 | """ |
| 264 | android_cmd = 'cat > %s' % pipes.quote(filename) |
| 265 | cros_cmd = 'android-sh -c %s' % pipes.quote(android_cmd) |
| 266 | utils.run(cros_cmd, stdin=data) |
| 267 | |
| 268 | |
| 269 | def remove_android_file(filename): |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 270 | """Removes a file in Android filesystem. |
| 271 | |
| 272 | @param filename: File to remove. |
| 273 | """ |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 274 | adb_shell('rm -f %s' % pipes.quote(filename)) |
| 275 | |
| 276 | |
| 277 | def wait_for_android_boot(timeout=None): |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 278 | """Sleep until Android has completed booting or timeout occurs. |
| 279 | |
| 280 | @param timeout: Timeout in seconds. |
| 281 | """ |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 282 | arc_common.wait_for_android_boot(timeout) |
| 283 | |
| 284 | |
| 285 | def wait_for_android_process(process_name, |
| 286 | timeout=_WAIT_FOR_ANDROID_PROCESS_SECONDS): |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 287 | """Sleep until an Android process is running or timeout occurs. |
| 288 | |
| 289 | @param process_name: Process name. |
| 290 | @param timeout: Timeout in seconds. |
| 291 | """ |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 292 | condition = lambda: is_android_process_running(process_name) |
| 293 | utils.poll_for_condition(condition=condition, |
| 294 | desc='%s is running' % process_name, |
| 295 | timeout=timeout, |
| 296 | sleep_interval=_PROCESS_CHECK_INTERVAL_SECONDS) |
| 297 | |
| 298 | |
| 299 | def _android_shell(cmd, **kwargs): |
| 300 | """Execute cmd instead the Android container. |
| 301 | |
| 302 | This function is strictly for internal use only, as commands do not run in a |
| 303 | fully consistent Android environment. Prefer adb_shell instead. |
| 304 | """ |
| 305 | return utils.system_output('android-sh -c {}'.format(pipes.quote(cmd)), |
| 306 | **kwargs) |
| 307 | |
| 308 | |
| 309 | def is_android_container_alive(): |
| 310 | """Check if android container is alive.""" |
| 311 | try: |
| 312 | container_pid = get_container_pid() |
| 313 | except Exception: |
| 314 | return False |
| 315 | return utils.pid_is_alive(int(container_pid)) |
| 316 | |
| 317 | |
| 318 | def is_package_installed(package): |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 319 | """Check if a package is installed. adb must be ready. |
| 320 | |
| 321 | @param package: Package in request. |
| 322 | """ |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 323 | packages = adb_shell('pm list packages').splitlines() |
| 324 | package_entry = 'package:{}'.format(package) |
| 325 | return package_entry in packages |
| 326 | |
| 327 | |
| 328 | def _before_iteration_hook(obj): |
| 329 | """Executed by parent class before every iteration. |
| 330 | |
| 331 | This function resets the run_once_finished flag before every iteration |
| 332 | so we can detect failure on every single iteration. |
| 333 | |
| 334 | Args: |
| 335 | obj: the test itself |
| 336 | """ |
| 337 | obj.run_once_finished = False |
| 338 | |
| 339 | |
| 340 | def _after_iteration_hook(obj): |
| 341 | """Executed by parent class after every iteration. |
| 342 | |
| 343 | The parent class will handle exceptions and failures in the run and will |
| 344 | always call this hook afterwards. Take a screenshot if the run has not |
| 345 | been marked as finished (i.e. there was a failure/exception). |
| 346 | |
| 347 | Args: |
| 348 | obj: the test itself |
| 349 | """ |
| 350 | if not obj.run_once_finished: |
| 351 | if not os.path.exists(_SCREENSHOT_DIR_PATH): |
| 352 | os.mkdir(_SCREENSHOT_DIR_PATH, 0755) |
| 353 | obj.num_screenshots += 1 |
| 354 | if obj.num_screenshots <= _MAX_SCREENSHOT_NUM: |
| 355 | logging.warning('Iteration %d failed, taking a screenshot.', |
| 356 | obj.iteration) |
| 357 | from cros.graphics.drm import crtcScreenshot |
| 358 | image = crtcScreenshot() |
| 359 | image.save('{}/{}_iter{}.png'.format(_SCREENSHOT_DIR_PATH, |
| 360 | _SCREENSHOT_BASENAME, |
| 361 | obj.iteration)) |
| 362 | else: |
| 363 | logging.warning('Too many failures, no screenshot taken') |
| 364 | |
| 365 | |
Chen-Hao Chang@google.com | 1d6f3b4 | 2016-09-26 15:17:57 +0800 | [diff] [blame] | 366 | def send_keycode(keycode): |
| 367 | """Sends the given keycode to the container |
| 368 | |
| 369 | @param keycode: keycode to send. |
| 370 | """ |
| 371 | adb_shell('input keyevent {}'.format(keycode)) |
| 372 | |
| 373 | |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 374 | class ArcTest(test.test): |
| 375 | """ Base class of ARC Test. |
| 376 | |
| 377 | This class could be used as super class of an ARC test for saving |
| 378 | redundant codes for container bringup, autotest-dep package(s) including |
| 379 | uiautomator setup if required, and apks install/remove during |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 380 | arc_setup/arc_teardown, respectively. By default arc_setup() is called in |
| 381 | initialize() after Android have been brought up. It could also be overridden |
| 382 | to perform non-default tasks. For example, a simple ArcHelloWorldTest can be |
| 383 | just implemented with print 'HelloWorld' in its run_once() and no other |
| 384 | functions are required. We could expect ArcHelloWorldTest would bring up |
| 385 | browser and wait for container up, then print 'Hello World', and shutdown |
| 386 | browser after. As a precaution, if you overwrite initialize(), arc_setup(), |
| 387 | or cleanup() function(s) in ARC test, remember to call the corresponding |
| 388 | function(s) in this base class as well. |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 389 | |
| 390 | """ |
| 391 | version = 1 |
| 392 | _PKG_UIAUTOMATOR = 'uiautomator' |
| 393 | _FULL_PKG_NAME_UIAUTOMATOR = 'com.github.uiautomator' |
| 394 | |
| 395 | def __init__(self, *args, **kwargs): |
| 396 | """Initialize flag setting.""" |
| 397 | super(ArcTest, self).__init__(*args, **kwargs) |
| 398 | self.initialized = False |
| 399 | # Set the flag run_once_finished to detect if a test is executed |
| 400 | # successfully without any exception thrown. Otherwise, generate |
| 401 | # a screenshot in /var/log for debugging. |
| 402 | self.run_once_finished = False |
| 403 | self.logcat_proc = None |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 404 | self.dep_package = None |
| 405 | self.apks = None |
| 406 | self.full_pkg_names = [] |
| 407 | self.uiautomator = False |
| 408 | self.email_id = None |
| 409 | self.password = None |
Chen-Hao Chang@google.com | 1d6f3b4 | 2016-09-26 15:17:57 +0800 | [diff] [blame] | 410 | self._chrome = None |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 411 | if os.path.exists(_SCREENSHOT_DIR_PATH): |
| 412 | shutil.rmtree(_SCREENSHOT_DIR_PATH) |
| 413 | self.register_before_iteration_hook(_before_iteration_hook) |
| 414 | self.register_after_iteration_hook(_after_iteration_hook) |
| 415 | # Keep track of the number of debug screenshots taken and keep the |
| 416 | # total number sane to avoid issues. |
| 417 | self.num_screenshots = 0 |
| 418 | |
| 419 | def initialize(self, extension_path=None, |
| 420 | arc_mode=arc_common.ARC_MODE_ENABLED, **chrome_kargs): |
| 421 | """Log in to a test account.""" |
| 422 | extension_paths = [extension_path] if extension_path else [] |
| 423 | self._chrome = chrome.Chrome(extension_paths=extension_paths, |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 424 | arc_mode=arc_mode, |
| 425 | **chrome_kargs) |
| 426 | if extension_path: |
| 427 | self._extension = self._chrome.get_extension(extension_path) |
| 428 | else: |
| 429 | self._extension = None |
| 430 | # With ARC enabled, Chrome will wait until container to boot up |
| 431 | # before returning here, see chrome.py. |
| 432 | self.initialized = True |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 433 | try: |
| 434 | if is_android_container_alive(): |
| 435 | self.arc_setup() |
| 436 | else: |
| 437 | logging.error('Container is alive?') |
| 438 | except Exception as err: |
| 439 | self.cleanup() |
| 440 | raise error.TestFail(err) |
| 441 | |
| 442 | def after_run_once(self): |
| 443 | """Executed after run_once() only if there were no errors. |
| 444 | |
| 445 | This function marks the run as finished with a flag. If there was a |
| 446 | failure the flag won't be set and the failure can then be detected by |
| 447 | testing the run_once_finished flag. |
| 448 | """ |
| 449 | logging.info('After run_once') |
| 450 | self.run_once_finished = True |
| 451 | |
| 452 | def cleanup(self): |
| 453 | """Log out of Chrome.""" |
| 454 | if not self.initialized: |
| 455 | logging.info('Skipping ARC cleanup: not initialized') |
| 456 | return |
| 457 | logging.info('Starting ARC cleanup') |
| 458 | try: |
| 459 | if is_android_container_alive(): |
| 460 | self.arc_teardown() |
| 461 | except Exception as err: |
| 462 | raise error.TestFail(err) |
| 463 | finally: |
| 464 | try: |
| 465 | self._stop_logcat() |
| 466 | finally: |
Chen-Hao Chang@google.com | 1d6f3b4 | 2016-09-26 15:17:57 +0800 | [diff] [blame] | 467 | if self._chrome is not None: |
| 468 | self._chrome.close() |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 469 | |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 470 | def arc_setup(self, dep_package=None, apks=None, full_pkg_names=[], |
| 471 | uiautomator=False, email_id=None, password=None): |
| 472 | """ARC test setup: Setup dependencies and install apks. |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 473 | |
| 474 | This function disables package verification and enables non-market |
| 475 | APK installation. Then, it installs specified APK(s) and uiautomator |
| 476 | package and path if required in a test. |
| 477 | |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 478 | @param dep_package: Package name of autotest_deps APK package. |
| 479 | @param apks: Array of APK names to be installed in dep_package. |
| 480 | @param full_pkg_names: Array of full package names to be removed |
| 481 | in teardown. |
| 482 | @param uiautomator: uiautomator python package is required or not. |
| 483 | |
| 484 | @param email_id: email id to be attached to the android. Only used |
| 485 | when account_util is set to true. |
| 486 | @param password: password related to the email_id. |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 487 | """ |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 488 | if not self.initialized: |
| 489 | logging.info('Skipping ARC setup: not initialized') |
| 490 | return |
| 491 | logging.info('Starting ARC setup') |
| 492 | self.dep_package = dep_package |
| 493 | self.apks = apks |
| 494 | self.uiautomator = uiautomator |
| 495 | self.email_id = email_id |
| 496 | self.password = password |
| 497 | # Setup dependent packages if required |
| 498 | packages = [] |
| 499 | if dep_package: |
| 500 | packages.append(dep_package) |
| 501 | if self.uiautomator: |
| 502 | packages.append(self._PKG_UIAUTOMATOR) |
| 503 | if packages: |
| 504 | logging.info('Setting up dependent package(s) %s', packages) |
| 505 | self.job.setup_dep(packages) |
| 506 | |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 507 | # TODO(b/29341443): Run logcat on non ArcTest test cases too. |
| 508 | with open(_VAR_LOGCAT_PATH, 'w') as f: |
| 509 | self.logcat_proc = subprocess.Popen( |
| 510 | ['android-sh', '-c', 'logcat -v threadtime'], |
| 511 | stdout=f, |
| 512 | stderr=subprocess.STDOUT, |
| 513 | close_fds=True) |
| 514 | |
| 515 | wait_for_adb_ready() |
| 516 | |
| 517 | # package_verifier_user_consent == -1 means to reject Google's |
| 518 | # verification on the server side through Play Store. This suppress a |
| 519 | # consent dialog from the system. |
| 520 | adb_shell('settings put secure package_verifier_user_consent -1') |
| 521 | # TODO(30310952): Remove the workaround below to a Phonesky bug. |
| 522 | adb_shell('am broadcast -a com.google.gservices.intent.action.GSERVICES_OVERRIDE ' |
| 523 | '-e finsky.platform_anti_malware_enabled false') |
| 524 | adb_shell('settings put global package_verifier_enable 0') |
| 525 | adb_shell('settings put secure install_non_market_apps 1') |
| 526 | |
| 527 | if self.dep_package: |
| 528 | apk_path = os.path.join(self.autodir, 'deps', self.dep_package) |
| 529 | if self.apks: |
| 530 | for apk in self.apks: |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 531 | logging.info('Installing %s', apk) |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 532 | adb_install('%s/%s' % (apk_path, apk)) |
| 533 | # Verify if package(s) are installed correctly |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 534 | if not full_pkg_names: |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 535 | raise error.TestError('Package names of apks expected') |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 536 | for pkg in full_pkg_names: |
| 537 | logging.info('Check if %s is installed', pkg) |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 538 | if not is_package_installed(pkg): |
| 539 | raise error.TestError('Package %s not found' % pkg) |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 540 | # Make sure full_pkg_names contains installed packages only |
| 541 | # so arc_teardown() knows what packages to uninstall. |
| 542 | self.full_pkg_names.append(pkg) |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 543 | |
| 544 | if self.uiautomator: |
| 545 | path = os.path.join(self.autodir, 'deps', self._PKG_UIAUTOMATOR) |
| 546 | sys.path.append(path) |
| 547 | |
| 548 | def _stop_logcat(self): |
| 549 | """Stop the adb logcat process gracefully.""" |
| 550 | if not self.logcat_proc: |
| 551 | return |
| 552 | # Running `adb kill-server` should have killed `adb logcat` |
| 553 | # process, but just in case also send termination signal. |
| 554 | self.logcat_proc.terminate() |
| 555 | |
| 556 | class TimeoutException(Exception): |
| 557 | """Termination timeout timed out.""" |
| 558 | |
| 559 | try: |
| 560 | utils.poll_for_condition( |
| 561 | condition=lambda: self.logcat_proc.poll() is not None, |
| 562 | exception=TimeoutException, |
| 563 | timeout=10, |
| 564 | sleep_interval=0.1, |
| 565 | desc='Waiting for adb logcat to terminate') |
| 566 | except TimeoutException: |
| 567 | logging.info('Killing adb logcat due to timeout') |
| 568 | self.logcat_proc.kill() |
| 569 | self.logcat_proc.wait() |
| 570 | |
| 571 | def arc_teardown(self): |
| 572 | """ARC test teardown. |
| 573 | |
| 574 | This function removes all installed packages in arc_setup stage |
| 575 | first. Then, it restores package verification and disables non-market |
| 576 | APK installation. |
| 577 | |
| 578 | """ |
| 579 | if self.full_pkg_names: |
| 580 | for pkg in self.full_pkg_names: |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 581 | logging.info('Uninstalling %s', pkg) |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 582 | if not is_package_installed(pkg): |
| 583 | raise error.TestError('Package %s was not installed' % pkg) |
| 584 | adb_uninstall(pkg) |
| 585 | if self.uiautomator: |
Cheng-Yu Lee | a854fbf | 2016-09-09 22:25:14 +0800 | [diff] [blame] | 586 | logging.info('Uninstalling %s', self._FULL_PKG_NAME_UIAUTOMATOR) |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 587 | adb_uninstall(self._FULL_PKG_NAME_UIAUTOMATOR) |
| 588 | adb_shell('settings put secure install_non_market_apps 0') |
| 589 | adb_shell('settings put global package_verifier_enable 1') |
| 590 | # TODO(30310952): Remove the workaround below to a Phonesky bug. |
| 591 | adb_shell('am broadcast -a com.google.gservices.intent.action.GSERVICES_OVERRIDE ' |
| 592 | '--esn finsky.platform_anti_malware_enabled') |
| 593 | adb_shell('settings put secure package_verifier_user_consent 0') |
| 594 | |
| 595 | remove_android_file(_ANDROID_ADB_KEYS_PATH) |
| 596 | utils.system_output('adb kill-server') |
| 597 | |
| 598 | def block_outbound(self): |
| 599 | """ Blocks the connection from the container to outer network. |
| 600 | |
Abhishek Bhardwaj | 754b7fb | 2017-01-05 16:02:48 -0800 | [diff] [blame] | 601 | The iptables settings accept only 100.115.92.2 port 5555 (adb) and |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 602 | localhost port 9008 (uiautomator) |
| 603 | """ |
| 604 | logging.info('Blocking outbound connection') |
| 605 | _android_shell('iptables -I OUTPUT -j REJECT') |
Abhishek Bhardwaj | 754b7fb | 2017-01-05 16:02:48 -0800 | [diff] [blame] | 606 | _android_shell('iptables -I OUTPUT -p tcp -s 100.115.92.2 --sport 5555 -j ACCEPT') |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 607 | _android_shell('iptables -I OUTPUT -p tcp -d localhost --dport 9008 -j ACCEPT') |
| 608 | _android_shell('iptables -I OUTPUT -p tcp -s localhost --sport 9008 -j ACCEPT') |
| 609 | |
| 610 | |
| 611 | def unblock_outbound(self): |
| 612 | """ Unblocks the connection from the container to outer network. |
| 613 | |
| 614 | The iptables settings are not permanent which means they reset on |
| 615 | each instance invocation. But we can still use this function to |
| 616 | unblock the outbound connections during the test if needed. |
| 617 | """ |
| 618 | logging.info('Unblocking outbound connection') |
| 619 | _android_shell('iptables -D OUTPUT -p tcp -s localhost --sport 9008 -j ACCEPT') |
| 620 | _android_shell('iptables -D OUTPUT -p tcp -d localhost --dport 9008 -j ACCEPT') |
Abhishek Bhardwaj | 754b7fb | 2017-01-05 16:02:48 -0800 | [diff] [blame] | 621 | _android_shell('iptables -D OUTPUT -p tcp -s 100.115.92.2 --sport 5555 -j ACCEPT') |
Victor Hsieh | 2eba892 | 2016-08-17 12:54:14 -0700 | [diff] [blame] | 622 | _android_shell('iptables -D OUTPUT -j REJECT') |