1#!/usr/bin/env python3.4 2# 3# Copyright 2016 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17from builtins import str 18from builtins import open 19from datetime import datetime 20 21import collections 22import logging 23import math 24import os 25import re 26import socket 27import time 28 29from acts import logger as acts_logger 30from acts import signals 31from acts import tracelogger 32from acts import utils 33from acts.controllers import adb 34from acts.controllers import fastboot 35from acts.controllers.sl4a_lib import sl4a_manager 36from acts.controllers.utils_lib.ssh import connection 37from acts.controllers.utils_lib.ssh import settings 38 39ACTS_CONTROLLER_CONFIG_NAME = "AndroidDevice" 40ACTS_CONTROLLER_REFERENCE_NAME = "android_devices" 41 42ANDROID_DEVICE_PICK_ALL_TOKEN = "*" 43# Key name for adb logcat extra params in config file. 44ANDROID_DEVICE_ADB_LOGCAT_PARAM_KEY = "adb_logcat_param" 45ANDROID_DEVICE_EMPTY_CONFIG_MSG = "Configuration is empty, abort!" 46ANDROID_DEVICE_NOT_LIST_CONFIG_MSG = "Configuration should be a list, abort!" 47CRASH_REPORT_PATHS = ("/data/tombstones/", "/data/vendor/ramdump/", 48 "/data/ramdump/", "/data/vendor/ssrdump", 49 "/data/vendor/ramdump/bluetooth") 50CRASH_REPORT_SKIPS = ("RAMDUMP_RESERVED", "RAMDUMP_STATUS", "RAMDUMP_OUTPUT", 51 "bluetooth") 52DEFAULT_QXDM_LOG_PATH = "/data/vendor/radio/diag_logs" 53BUG_REPORT_TIMEOUT = 1800 54PULL_TIMEOUT = 300 55PORT_RETRY_COUNT = 3 56IPERF_TIMEOUT = 60 57SL4A_APK_NAME = "com.googlecode.android_scripting" 58WAIT_FOR_DEVICE_TIMEOUT = 180 59ENCRYPTION_WINDOW = "CryptKeeper" 60DEFAULT_DEVICE_PASSWORD = "1111" 61RELEASE_ID_REGEXES = [re.compile(r'\w+\.\d+\.\d+'), re.compile(r'N\w+')] 62 63 64class AndroidDeviceError(signals.ControllerError): 65 pass 66 67 68class DoesNotExistError(AndroidDeviceError): 69 """Raised when something that does not exist is referenced. 70 """ 71 72 73def create(configs): 74 """Creates AndroidDevice controller objects. 75 76 Args: 77 configs: A list of dicts, each representing a configuration for an 78 Android device. 79 80 Returns: 81 A list of AndroidDevice objects. 82 """ 83 if not configs: 84 raise AndroidDeviceError(ANDROID_DEVICE_EMPTY_CONFIG_MSG) 85 elif configs == ANDROID_DEVICE_PICK_ALL_TOKEN: 86 ads = get_all_instances() 87 elif not isinstance(configs, list): 88 raise AndroidDeviceError(ANDROID_DEVICE_NOT_LIST_CONFIG_MSG) 89 elif isinstance(configs[0], str): 90 # Configs is a list of serials. 91 ads = get_instances(configs) 92 else: 93 # Configs is a list of dicts. 94 ads = get_instances_with_configs(configs) 95 96 ads[0].log.info('The primary device under test is "%s".' % ads[0].serial) 97 98 for ad in ads: 99 if not ad.is_connected(): 100 raise DoesNotExistError(("Android device %s is specified in config" 101 " but is not attached.") % ad.serial) 102 _start_services_on_ads(ads) 103 return ads 104 105 106def destroy(ads): 107 """Cleans up AndroidDevice objects. 108 109 Args: 110 ads: A list of AndroidDevice objects. 111 """ 112 for ad in ads: 113 try: 114 ad.clean_up() 115 except: 116 ad.log.exception("Failed to clean up properly.") 117 118 119def get_info(ads): 120 """Get information on a list of AndroidDevice objects. 121 122 Args: 123 ads: A list of AndroidDevice objects. 124 125 Returns: 126 A list of dict, each representing info for an AndroidDevice objects. 127 """ 128 device_info = [] 129 for ad in ads: 130 info = {"serial": ad.serial, "model": ad.model} 131 info.update(ad.build_info) 132 device_info.append(info) 133 return device_info 134 135 136def get_post_job_info(ads): 137 """Returns the tracked build id to test_run_summary.json 138 139 Args: 140 ads: A list of AndroidDevice objects. 141 142 Returns: 143 A dict consisting of {'build_id': ads[0].build_info} 144 """ 145 return 'Build Info', ads[0].build_info 146 147 148def _start_services_on_ads(ads): 149 """Starts long running services on multiple AndroidDevice objects. 150 151 If any one AndroidDevice object fails to start services, cleans up all 152 existing AndroidDevice objects and their services. 153 154 Args: 155 ads: A list of AndroidDevice objects whose services to start. 156 """ 157 running_ads = [] 158 for ad in ads: 159 running_ads.append(ad) 160 if not ad.ensure_screen_on(): 161 ad.log.error("User window cannot come up") 162 destroy(running_ads) 163 raise AndroidDeviceError("User window cannot come up") 164 if not ad.skip_sl4a and not ad.is_sl4a_installed(): 165 ad.log.error("sl4a.apk is not installed") 166 destroy(running_ads) 167 raise AndroidDeviceError("The required sl4a.apk is not installed") 168 try: 169 ad.start_services(skip_sl4a=ad.skip_sl4a) 170 except: 171 ad.log.exception("Failed to start some services, abort!") 172 destroy(running_ads) 173 raise 174 175 176def _parse_device_list(device_list_str, key): 177 """Parses a byte string representing a list of devices. The string is 178 generated by calling either adb or fastboot. 179 180 Args: 181 device_list_str: Output of adb or fastboot. 182 key: The token that signifies a device in device_list_str. 183 184 Returns: 185 A list of android device serial numbers. 186 """ 187 return re.findall(r"(\S+)\t%s" % key, device_list_str) 188 189 190def list_adb_devices(): 191 """List all android devices connected to the computer that are detected by 192 adb. 193 194 Returns: 195 A list of android device serials. Empty if there's none. 196 """ 197 out = adb.AdbProxy().devices() 198 return _parse_device_list(out, "device") 199 200 201def list_fastboot_devices(): 202 """List all android devices connected to the computer that are in in 203 fastboot mode. These are detected by fastboot. 204 205 Returns: 206 A list of android device serials. Empty if there's none. 207 """ 208 out = fastboot.FastbootProxy().devices() 209 return _parse_device_list(out, "fastboot") 210 211 212def get_instances(serials): 213 """Create AndroidDevice instances from a list of serials. 214 215 Args: 216 serials: A list of android device serials. 217 218 Returns: 219 A list of AndroidDevice objects. 220 """ 221 results = [] 222 for s in serials: 223 results.append(AndroidDevice(s)) 224 return results 225 226 227def get_instances_with_configs(configs): 228 """Create AndroidDevice instances from a list of json configs. 229 230 Each config should have the required key-value pair "serial". 231 232 Args: 233 configs: A list of dicts each representing the configuration of one 234 android device. 235 236 Returns: 237 A list of AndroidDevice objects. 238 """ 239 results = [] 240 for c in configs: 241 try: 242 serial = c.pop("serial") 243 except KeyError: 244 raise AndroidDeviceError( 245 "Required value 'serial' is missing in AndroidDevice config %s." 246 % c) 247 ssh_config = c.pop("ssh_config", None) 248 ssh_connection = None 249 if ssh_config is not None: 250 ssh_settings = settings.from_config(ssh_config) 251 ssh_connection = connection.SshConnection(ssh_settings) 252 ad = AndroidDevice(serial, ssh_connection=ssh_connection) 253 ad.load_config(c) 254 results.append(ad) 255 return results 256 257 258def get_all_instances(include_fastboot=False): 259 """Create AndroidDevice instances for all attached android devices. 260 261 Args: 262 include_fastboot: Whether to include devices in bootloader mode or not. 263 264 Returns: 265 A list of AndroidDevice objects each representing an android device 266 attached to the computer. 267 """ 268 if include_fastboot: 269 serial_list = list_adb_devices() + list_fastboot_devices() 270 return get_instances(serial_list) 271 return get_instances(list_adb_devices()) 272 273 274def filter_devices(ads, func): 275 """Finds the AndroidDevice instances from a list that match certain 276 conditions. 277 278 Args: 279 ads: A list of AndroidDevice instances. 280 func: A function that takes an AndroidDevice object and returns True 281 if the device satisfies the filter condition. 282 283 Returns: 284 A list of AndroidDevice instances that satisfy the filter condition. 285 """ 286 results = [] 287 for ad in ads: 288 if func(ad): 289 results.append(ad) 290 return results 291 292 293def get_device(ads, **kwargs): 294 """Finds a unique AndroidDevice instance from a list that has specific 295 attributes of certain values. 296 297 Example: 298 get_device(android_devices, label="foo", phone_number="1234567890") 299 get_device(android_devices, model="angler") 300 301 Args: 302 ads: A list of AndroidDevice instances. 303 kwargs: keyword arguments used to filter AndroidDevice instances. 304 305 Returns: 306 The target AndroidDevice instance. 307 308 Raises: 309 AndroidDeviceError is raised if none or more than one device is 310 matched. 311 """ 312 313 def _get_device_filter(ad): 314 for k, v in kwargs.items(): 315 if not hasattr(ad, k): 316 return False 317 elif getattr(ad, k) != v: 318 return False 319 return True 320 321 filtered = filter_devices(ads, _get_device_filter) 322 if not filtered: 323 raise AndroidDeviceError( 324 "Could not find a target device that matches condition: %s." % 325 kwargs) 326 elif len(filtered) == 1: 327 return filtered[0] 328 else: 329 serials = [ad.serial for ad in filtered] 330 raise AndroidDeviceError("More than one device matched: %s" % serials) 331 332 333def take_bug_reports(ads, test_name, begin_time): 334 """Takes bug reports on a list of android devices. 335 336 If you want to take a bug report, call this function with a list of 337 android_device objects in on_fail. But reports will be taken on all the 338 devices in the list concurrently. Bug report takes a relative long 339 time to take, so use this cautiously. 340 341 Args: 342 ads: A list of AndroidDevice instances. 343 test_name: Name of the test case that triggered this bug report. 344 begin_time: Logline format timestamp taken when the test started. 345 """ 346 347 def take_br(test_name, begin_time, ad): 348 ad.take_bug_report(test_name, begin_time) 349 350 args = [(test_name, begin_time, ad) for ad in ads] 351 utils.concurrent_exec(take_br, args) 352 353 354class AndroidDevice: 355 """Class representing an android device. 356 357 Each object of this class represents one Android device in ACTS, including 358 handles to adb, fastboot, and sl4a clients. In addition to direct adb 359 commands, this object also uses adb port forwarding to talk to the Android 360 device. 361 362 Attributes: 363 serial: A string that's the serial number of the Android device. 364 log_path: A string that is the path where all logs collected on this 365 android device should be stored. 366 log: A logger adapted from root logger with added token specific to an 367 AndroidDevice instance. 368 adb_logcat_process: A process that collects the adb logcat. 369 adb_logcat_file_path: A string that's the full path to the adb logcat 370 file collected, if any. 371 adb: An AdbProxy object used for interacting with the device via adb. 372 fastboot: A FastbootProxy object used for interacting with the device 373 via fastboot. 374 """ 375 376 def __init__(self, serial='', ssh_connection=None): 377 self.serial = serial 378 # logging.log_path only exists when this is used in an ACTS test run. 379 log_path_base = getattr(logging, 'log_path', '/tmp/logs') 380 self.log_path = os.path.join(log_path_base, 'AndroidDevice%s' % serial) 381 self.log = tracelogger.TraceLogger( 382 AndroidDeviceLoggerAdapter(logging.getLogger(), { 383 'serial': self.serial 384 })) 385 self._event_dispatchers = {} 386 self.adb_logcat_process = None 387 self.adb_logcat_file_path = None 388 self.adb = adb.AdbProxy(serial, ssh_connection=ssh_connection) 389 self.fastboot = fastboot.FastbootProxy( 390 serial, ssh_connection=ssh_connection) 391 if not self.is_bootloader: 392 self.root_adb() 393 self._ssh_connection = ssh_connection 394 self.skip_sl4a = False 395 self.crash_report = None 396 self.data_accounting = collections.defaultdict(int) 397 self._sl4a_manager = sl4a_manager.Sl4aManager(self.adb) 398 self.last_logcat_timestamp = None 399 400 def clean_up(self): 401 """Cleans up the AndroidDevice object and releases any resources it 402 claimed. 403 """ 404 self.stop_services() 405 if self._ssh_connection: 406 self._ssh_connection.close() 407 408 # TODO(angli): This function shall be refactored to accommodate all services 409 # and not have hard coded switch for SL4A when b/29157104 is done. 410 def start_services(self, skip_sl4a=False, skip_setup_wizard=True): 411 """Starts long running services on the android device. 412 413 1. Start adb logcat capture. 414 2. Start SL4A if not skipped. 415 416 Args: 417 skip_sl4a: Does not attempt to start SL4A if True. 418 skip_setup_wizard: Whether or not to skip the setup wizard. 419 """ 420 if skip_setup_wizard: 421 self.exit_setup_wizard() 422 try: 423 self.start_adb_logcat() 424 except: 425 self.log.exception("Failed to start adb logcat!") 426 raise 427 if not skip_sl4a: 428 try: 429 droid, ed = self.get_droid() 430 ed.start() 431 except: 432 self.log.exception("Failed to start sl4a!") 433 raise 434 435 def stop_services(self): 436 """Stops long running services on the android device. 437 438 Stop adb logcat and terminate sl4a sessions if exist. 439 """ 440 if self.is_adb_logcat_on: 441 self.stop_adb_logcat() 442 self.terminate_all_sessions() 443 self.stop_sl4a() 444 445 def is_connected(self): 446 out = self.adb.devices() 447 devices = _parse_device_list(out, "device") 448 return self.serial in devices 449 450 @property 451 def build_info(self): 452 """Get the build info of this Android device, including build id and 453 build type. 454 455 This is not available if the device is in bootloader mode. 456 457 Returns: 458 A dict with the build info of this Android device, or None if the 459 device is in bootloader mode. 460 """ 461 if self.is_bootloader: 462 self.log.error("Device is in fastboot mode, could not get build " 463 "info.") 464 return 465 466 build_id = self.adb.getprop("ro.build.id") 467 valid_build_id = False 468 for regex in RELEASE_ID_REGEXES: 469 if re.match(regex, build_id): 470 valid_build_id = True 471 break 472 if not valid_build_id: 473 build_id = self.adb.getprop("ro.build.version.incremental") 474 475 info = { 476 "build_id": build_id, 477 "build_type": self.adb.getprop("ro.build.type") 478 } 479 return info 480 481 @property 482 def is_bootloader(self): 483 """True if the device is in bootloader mode. 484 """ 485 return self.serial in list_fastboot_devices() 486 487 @property 488 def is_adb_root(self): 489 """True if adb is running as root for this device. 490 """ 491 try: 492 return "0" == self.adb.shell("id -u") 493 except adb.AdbError: 494 # Wait a bit and retry to work around adb flakiness for this cmd. 495 time.sleep(0.2) 496 return "0" == self.adb.shell("id -u") 497 498 @property 499 def model(self): 500 """The Android code name for the device.""" 501 # If device is in bootloader mode, get mode name from fastboot. 502 if self.is_bootloader: 503 out = self.fastboot.getvar("product").strip() 504 # "out" is never empty because of the "total time" message fastboot 505 # writes to stderr. 506 lines = out.split('\n', 1) 507 if lines: 508 tokens = lines[0].split(' ') 509 if len(tokens) > 1: 510 return tokens[1].lower() 511 return None 512 model = self.adb.getprop("ro.build.product").lower() 513 if model == "sprout": 514 return model 515 else: 516 return self.adb.getprop("ro.product.name").lower() 517 518 @property 519 def droid(self): 520 """Returns the RPC Service of the first Sl4aSession created.""" 521 if len(self._sl4a_manager.sessions) > 0: 522 session_id = sorted(self._sl4a_manager.sessions.keys())[0] 523 return self._sl4a_manager.sessions[session_id].rpc_client 524 else: 525 return None 526 527 @property 528 def ed(self): 529 """Returns the event dispatcher of the first Sl4aSession created.""" 530 if len(self._sl4a_manager.sessions) > 0: 531 session_id = sorted(self._sl4a_manager.sessions.keys())[0] 532 return self._sl4a_manager.sessions[ 533 session_id].get_event_dispatcher() 534 else: 535 return None 536 537 @property 538 def sl4a_sessions(self): 539 """Returns a dictionary of session ids to sessions.""" 540 return list(self._sl4a_manager.sessions) 541 542 @property 543 def is_adb_logcat_on(self): 544 """Whether there is an ongoing adb logcat collection. 545 """ 546 if self.adb_logcat_process: 547 try: 548 utils._assert_subprocess_running(self.adb_logcat_process) 549 return True 550 except Exception: 551 # if skip_sl4a is true, there is no sl4a session 552 # if logcat died due to device reboot and sl4a session has 553 # not restarted there is no droid. 554 if self.droid: 555 self.droid.logI('Logcat died') 556 self.log.info("Logcat to %s died", self.adb_logcat_file_path) 557 return False 558 return False 559 560 def load_config(self, config): 561 """Add attributes to the AndroidDevice object based on json config. 562 563 Args: 564 config: A dictionary representing the configs. 565 566 Raises: 567 AndroidDeviceError is raised if the config is trying to overwrite 568 an existing attribute. 569 """ 570 for k, v in config.items(): 571 # skip_sl4a value can be reset from config file 572 if hasattr(self, k) and k != "skip_sl4a": 573 raise AndroidDeviceError( 574 "Attempting to set existing attribute %s on %s" % 575 (k, self.serial)) 576 setattr(self, k, v) 577 578 def root_adb(self): 579 """Change adb to root mode for this device if allowed. 580 581 If executed on a production build, adb will not be switched to root 582 mode per security restrictions. 583 """ 584 self.adb.root() 585 self.adb.wait_for_device() 586 587 def get_droid(self, handle_event=True): 588 """Create an sl4a connection to the device. 589 590 Return the connection handler 'droid'. By default, another connection 591 on the same session is made for EventDispatcher, and the dispatcher is 592 returned to the caller as well. 593 If sl4a server is not started on the device, try to start it. 594 595 Args: 596 handle_event: True if this droid session will need to handle 597 events. 598 599 Returns: 600 droid: Android object used to communicate with sl4a on the android 601 device. 602 ed: An optional EventDispatcher to organize events for this droid. 603 604 Examples: 605 Don't need event handling: 606 >>> ad = AndroidDevice() 607 >>> droid = ad.get_droid(False) 608 609 Need event handling: 610 >>> ad = AndroidDevice() 611 >>> droid, ed = ad.get_droid() 612 """ 613 session = self._sl4a_manager.create_session() 614 droid = session.rpc_client 615 if handle_event: 616 ed = session.get_event_dispatcher() 617 return droid, ed 618 return droid 619 620 def get_package_pid(self, package_name): 621 """Gets the pid for a given package. Returns None if not running. 622 Args: 623 package_name: The name of the package. 624 Returns: 625 The first pid found under a given package name. None if no process 626 was found running the package. 627 Raises: 628 AndroidDeviceError if the output of the phone's process list was 629 in an unexpected format. 630 """ 631 for cmd in ("ps -A", "ps"): 632 try: 633 out = self.adb.shell( 634 '%s | grep "S %s"' % (cmd, package_name), 635 ignore_status=True) 636 if package_name not in out: 637 continue 638 try: 639 pid = int(out.split()[1]) 640 self.log.info('apk %s has pid %s.', package_name, pid) 641 return pid 642 except (IndexError, ValueError) as e: 643 # Possible ValueError from string to int cast. 644 # Possible IndexError from split. 645 self.log.warn('Command \"%s\" returned output line: ' 646 '\"%s\".\nError: %s', cmd, out, e) 647 except Exception as e: 648 self.log.warn( 649 'Device fails to check if %s running with \"%s\"\n' 650 'Exception %s', package_name, cmd, e) 651 self.log.debug("apk %s is not running", package_name) 652 return None 653 654 def get_dispatcher(self, droid): 655 """Return an EventDispatcher for an sl4a session 656 657 Args: 658 droid: Session to create EventDispatcher for. 659 660 Returns: 661 ed: An EventDispatcher for specified session. 662 """ 663 return self._sl4a_manager.sessions[droid.uid].get_event_dispatcher() 664 665 def _is_timestamp_in_range(self, target, log_begin_time, log_end_time): 666 low = acts_logger.logline_timestamp_comparator(log_begin_time, 667 target) <= 0 668 high = acts_logger.logline_timestamp_comparator(log_end_time, 669 target) >= 0 670 return low and high 671 672 def cat_adb_log(self, tag, begin_time): 673 """Takes an excerpt of the adb logcat log from a certain time point to 674 current time. 675 676 Args: 677 tag: An identifier of the time period, usualy the name of a test. 678 begin_time: Epoch time of the beginning of the time period. 679 """ 680 log_begin_time = acts_logger.epoch_to_log_line_timestamp(begin_time) 681 if not self.adb_logcat_file_path: 682 raise AndroidDeviceError( 683 ("Attempting to cat adb log when none has" 684 " been collected on Android device %s.") % self.serial) 685 log_end_time = acts_logger.get_log_line_timestamp() 686 self.log.debug("Extracting adb log from logcat.") 687 adb_excerpt_path = os.path.join(self.log_path, "AdbLogExcerpts") 688 utils.create_dir(adb_excerpt_path) 689 f_name = os.path.basename(self.adb_logcat_file_path) 690 out_name = f_name.replace("adblog,", "").replace(".txt", "") 691 out_name = ",{},{}.txt".format(log_begin_time, out_name) 692 tag_len = utils.MAX_FILENAME_LEN - len(out_name) 693 tag = tag[:tag_len] 694 out_name = tag + out_name 695 full_adblog_path = os.path.join(adb_excerpt_path, out_name) 696 with open(full_adblog_path, 'w', encoding='utf-8') as out: 697 in_file = self.adb_logcat_file_path 698 with open(in_file, 'r', encoding='utf-8', errors='replace') as f: 699 in_range = False 700 while True: 701 line = None 702 try: 703 line = f.readline() 704 if not line: 705 break 706 except: 707 continue 708 line_time = line[:acts_logger.log_line_timestamp_len] 709 if not acts_logger.is_valid_logline_timestamp(line_time): 710 continue 711 if self._is_timestamp_in_range(line_time, log_begin_time, 712 log_end_time): 713 in_range = True 714 if not line.endswith('\n'): 715 line += '\n' 716 out.write(line) 717 else: 718 if in_range: 719 break 720 721 def start_adb_logcat(self, cont_logcat_file=False): 722 """Starts a standing adb logcat collection in separate subprocesses and 723 save the logcat in a file. 724 725 Args: 726 cont_logcat_file: Specifies whether to continue the previous logcat 727 file. This allows for start_adb_logcat to act 728 as a restart logcat function if it is noticed 729 logcat is no longer running. 730 """ 731 if self.is_adb_logcat_on: 732 raise AndroidDeviceError(("Android device {} already has an adb " 733 "logcat thread going on. Cannot start " 734 "another one.").format(self.serial)) 735 # Disable adb log spam filter. Have to stop and clear settings first 736 # because 'start' doesn't support --clear option before Android N. 737 self.adb.shell("logpersist.stop --clear") 738 self.adb.shell("logpersist.start") 739 if cont_logcat_file: 740 if self.droid: 741 self.droid.logI('Restarting logcat') 742 self.log.info( 743 'Restarting logcat on file %s' % self.adb_logcat_file_path) 744 logcat_file_path = self.adb_logcat_file_path 745 else: 746 f_name = "adblog,{},{}.txt".format(self.model, self.serial) 747 utils.create_dir(self.log_path) 748 logcat_file_path = os.path.join(self.log_path, f_name) 749 if hasattr(self, 'adb_logcat_param'): 750 extra_params = self.adb_logcat_param 751 else: 752 extra_params = "-b all" 753 if self.last_logcat_timestamp: 754 begin_at = '-T "%s"' % self.last_logcat_timestamp 755 else: 756 begin_at = '-T 1' 757 # TODO(markdr): Pull 'adb -s %SERIAL' from the AdbProxy object. 758 cmd = "adb -s {} logcat {} -v year {} >> {}".format( 759 self.serial, begin_at, extra_params, logcat_file_path) 760 self.adb_logcat_process = utils.start_standing_subprocess(cmd) 761 self.adb_logcat_file_path = logcat_file_path 762 763 def stop_adb_logcat(self): 764 """Stops the adb logcat collection subprocess. 765 """ 766 if not self.is_adb_logcat_on: 767 raise AndroidDeviceError( 768 "Android device %s does not have an ongoing adb logcat collection." 769 % self.serial) 770 # Set the last timestamp to the current timestamp. This may cause 771 # a race condition that allows the same line to be logged twice, 772 # but it does not pose a problem for our logging purposes. 773 logcat_output = self.adb.logcat('-t 1 -v year') 774 next_line = logcat_output.find('\n') 775 self.last_logcat_timestamp = logcat_output[next_line + 1: 776 next_line + 24] 777 utils.stop_standing_subprocess(self.adb_logcat_process) 778 self.adb_logcat_process = None 779 780 def get_apk_uid(self, apk_name): 781 """Get the uid of the given apk. 782 783 Args: 784 apk_name: Name of the package, e.g., com.android.phone. 785 786 Returns: 787 Linux UID for the apk. 788 """ 789 output = self.adb.shell( 790 "dumpsys package %s | grep userId=" % apk_name, ignore_status=True) 791 result = re.search(r"userId=(\d+)", output) 792 if result: 793 return result.group(1) 794 else: 795 None 796 797 def is_apk_installed(self, package_name): 798 """Check if the given apk is already installed. 799 800 Args: 801 package_name: Name of the package, e.g., com.android.phone. 802 803 Returns: 804 True if package is installed. False otherwise. 805 """ 806 807 try: 808 return bool( 809 self.adb.shell( 810 'pm list packages | grep -w "package:%s"' % package_name)) 811 812 except Exception as err: 813 self.log.error('Could not determine if %s is installed. ' 814 'Received error:\n%s', package_name, err) 815 return False 816 817 def is_sl4a_installed(self): 818 return self.is_apk_installed(SL4A_APK_NAME) 819 820 def is_apk_running(self, package_name): 821 """Check if the given apk is running. 822 823 Args: 824 package_name: Name of the package, e.g., com.android.phone. 825 826 Returns: 827 True if package is installed. False otherwise. 828 """ 829 for cmd in ("ps -A", "ps"): 830 try: 831 out = self.adb.shell( 832 '%s | grep "S %s"' % (cmd, package_name), 833 ignore_status=True) 834 if package_name in out: 835 self.log.info("apk %s is running", package_name) 836 return True 837 except Exception as e: 838 self.log.warn("Device fails to check is %s running by %s " 839 "Exception %s", package_name, cmd, e) 840 continue 841 self.log.debug("apk %s is not running", package_name) 842 return False 843 844 def is_sl4a_running(self): 845 return self.is_apk_running(SL4A_APK_NAME) 846 847 def force_stop_apk(self, package_name): 848 """Force stop the given apk. 849 850 Args: 851 package_name: Name of the package, e.g., com.android.phone. 852 853 Returns: 854 True if package is installed. False otherwise. 855 """ 856 try: 857 self.adb.shell( 858 'am force-stop %s' % package_name, ignore_status=True) 859 except Exception as e: 860 self.log.warn("Fail to stop package %s: %s", package_name, e) 861 862 def stop_sl4a(self): 863 return self.force_stop_apk(SL4A_APK_NAME) 864 865 def start_sl4a(self): 866 self._sl4a_manager.start_sl4a_service() 867 868 def take_bug_report(self, test_name, begin_time): 869 """Takes a bug report on the device and stores it in a file. 870 871 Args: 872 test_name: Name of the test case that triggered this bug report. 873 begin_time: Epoch time when the test started. 874 """ 875 self.adb.wait_for_device(timeout=WAIT_FOR_DEVICE_TIMEOUT) 876 new_br = True 877 try: 878 stdout = self.adb.shell("bugreportz -v") 879 # This check is necessary for builds before N, where adb shell's ret 880 # code and stderr are not propagated properly. 881 if "not found" in stdout: 882 new_br = False 883 except adb.AdbError: 884 new_br = False 885 br_path = os.path.join(self.log_path, test_name) 886 utils.create_dir(br_path) 887 time_stamp = acts_logger.normalize_log_line_timestamp( 888 acts_logger.epoch_to_log_line_timestamp(begin_time)) 889 out_name = "AndroidDevice%s_%s" % ( 890 self.serial, time_stamp.replace(" ", "_").replace(":", "-")) 891 out_name = "%s.zip" % out_name if new_br else "%s.txt" % out_name 892 full_out_path = os.path.join(br_path, out_name) 893 # in case device restarted, wait for adb interface to return 894 self.wait_for_boot_completion() 895 self.log.info("Taking bugreport for %s.", test_name) 896 if new_br: 897 out = self.adb.shell("bugreportz", timeout=BUG_REPORT_TIMEOUT) 898 if not out.startswith("OK"): 899 raise AndroidDeviceError("Failed to take bugreport on %s: %s" % 900 (self.serial, out)) 901 br_out_path = out.split(':')[1].strip() 902 self.adb.pull("%s %s" % (br_out_path, full_out_path)) 903 else: 904 self.adb.bugreport( 905 " > {}".format(full_out_path), timeout=BUG_REPORT_TIMEOUT) 906 self.log.info("Bugreport for %s taken at %s.", test_name, 907 full_out_path) 908 self.adb.wait_for_device(timeout=WAIT_FOR_DEVICE_TIMEOUT) 909 910 def get_file_names(self, 911 directory, 912 begin_time=None, 913 skip_files=[], 914 match_string=None): 915 """Get files names with provided directory.""" 916 cmd = "find %s -type f" % directory 917 if begin_time: 918 current_time = utils.get_current_epoch_time() 919 seconds = int(math.ceil((current_time - begin_time) / 1000.0)) 920 cmd = "%s -mtime -%ss" % (cmd, seconds) 921 if match_string: 922 cmd = "%s -iname %s" % (cmd, match_string) 923 for skip_file in skip_files: 924 cmd = "%s ! -iname %s" % (cmd, skip_file) 925 out = self.adb.shell(cmd, ignore_status=True) 926 if not out or "No such" in out or "Permission denied" in out: 927 return [] 928 files = out.split("\n") 929 self.log.debug("Find files in directory %s: %s", directory, files) 930 return files 931 932 def pull_files(self, files, remote_path=None): 933 """Pull files from devies.""" 934 if not remote_path: 935 remote_path = self.log_path 936 for file_name in files: 937 self.adb.pull( 938 "%s %s" % (file_name, remote_path), timeout=PULL_TIMEOUT) 939 940 def check_crash_report(self, 941 test_name=None, 942 begin_time=None, 943 log_crash_report=False): 944 """check crash report on the device.""" 945 crash_reports = [] 946 for crash_path in CRASH_REPORT_PATHS: 947 crashes = self.get_file_names( 948 crash_path, 949 skip_files=CRASH_REPORT_SKIPS, 950 begin_time=begin_time) 951 if crash_path == "/data/tombstones/" and crashes: 952 tombstones = crashes[:] 953 for tombstone in tombstones: 954 if self.adb.shell( 955 'cat %s | grep "crash_dump failed to dump process"' 956 % tombstone): 957 crashes.remove(tombstone) 958 if crashes: 959 crash_reports.extend(crashes) 960 if crash_reports and log_crash_report: 961 test_name = test_name or time.strftime("%Y-%m-%d-%Y-%H-%M-%S") 962 crash_log_path = os.path.join(self.log_path, test_name, 963 "Crashes_%s" % self.serial) 964 utils.create_dir(crash_log_path) 965 self.pull_files(crash_reports, crash_log_path) 966 return crash_reports 967 968 def get_qxdm_logs(self, test_name="", begin_time=None): 969 """Get qxdm logs.""" 970 # Sleep 10 seconds for the buffered log to be written in qxdm log file 971 time.sleep(10) 972 log_path = getattr(self, "qxdm_log_path", DEFAULT_QXDM_LOG_PATH) 973 qxdm_logs = self.get_file_names( 974 log_path, begin_time=begin_time, match_string="*.qmdl") 975 if qxdm_logs: 976 qxdm_log_path = os.path.join(self.log_path, test_name, 977 "QXDM_%s" % self.serial) 978 utils.create_dir(qxdm_log_path) 979 self.log.info("Pull QXDM Log %s to %s", qxdm_logs, qxdm_log_path) 980 self.pull_files(qxdm_logs, qxdm_log_path) 981 else: 982 self.log.error("Didn't find QXDM logs in %s." % log_path) 983 if "Verizon" in self.adb.getprop("gsm.sim.operator.alpha"): 984 omadm_log_path = os.path.join(self.log_path, test_name, 985 "OMADM_%s" % self.serial) 986 utils.create_dir(omadm_log_path) 987 self.log.info("Pull OMADM Log") 988 self.adb.pull( 989 "/data/data/com.android.omadm.service/files/dm/log/ %s" % 990 omadm_log_path, 991 timeout=PULL_TIMEOUT, 992 ignore_status=True) 993 994 def start_new_session(self, max_connections=None, server_port=None): 995 """Start a new session in sl4a. 996 997 Also caches the droid in a dict with its uid being the key. 998 999 Returns: 1000 An Android object used to communicate with sl4a on the android 1001 device. 1002 1003 Raises: 1004 Sl4aException: Something is wrong with sl4a and it returned an 1005 existing uid to a new session. 1006 """ 1007 session = self._sl4a_manager.create_session( 1008 max_connections=max_connections, server_port=server_port) 1009 1010 self._sl4a_manager.sessions[session.uid] = session 1011 return session.rpc_client 1012 1013 def terminate_all_sessions(self): 1014 """Terminate all sl4a sessions on the AndroidDevice instance. 1015 1016 Terminate all sessions and clear caches. 1017 """ 1018 self._sl4a_manager.terminate_all_sessions() 1019 1020 def run_iperf_client_nb(self, 1021 server_host, 1022 extra_args="", 1023 timeout=IPERF_TIMEOUT, 1024 log_file_path=None): 1025 """Start iperf client on the device asynchronously. 1026 1027 Return status as true if iperf client start successfully. 1028 And data flow information as results. 1029 1030 Args: 1031 server_host: Address of the iperf server. 1032 extra_args: A string representing extra arguments for iperf client, 1033 e.g. "-i 1 -t 30". 1034 log_file_path: The complete file path to log the results. 1035 1036 """ 1037 cmd = "iperf3 -c {} {}".format(server_host, extra_args) 1038 if log_file_path: 1039 cmd += " --logfile {} &".format(log_file_path) 1040 self.adb.shell_nb(cmd) 1041 1042 def run_iperf_client(self, 1043 server_host, 1044 extra_args="", 1045 timeout=IPERF_TIMEOUT): 1046 """Start iperf client on the device. 1047 1048 Return status as true if iperf client start successfully. 1049 And data flow information as results. 1050 1051 Args: 1052 server_host: Address of the iperf server. 1053 extra_args: A string representing extra arguments for iperf client, 1054 e.g. "-i 1 -t 30". 1055 1056 Returns: 1057 status: true if iperf client start successfully. 1058 results: results have data flow information 1059 """ 1060 out = self.adb.shell( 1061 "iperf3 -c {} {}".format(server_host, extra_args), timeout=timeout) 1062 clean_out = out.split('\n') 1063 if "error" in clean_out[0].lower(): 1064 return False, clean_out 1065 return True, clean_out 1066 1067 def run_iperf_server(self, extra_args=""): 1068 """Start iperf server on the device 1069 1070 Return status as true if iperf server started successfully. 1071 1072 Args: 1073 extra_args: A string representing extra arguments for iperf server. 1074 1075 Returns: 1076 status: true if iperf server started successfully. 1077 results: results have output of command 1078 """ 1079 out = self.adb.shell("iperf3 -s {}".format(extra_args)) 1080 clean_out = out.split('\n') 1081 if "error" in clean_out[0].lower(): 1082 return False, clean_out 1083 return True, clean_out 1084 1085 def wait_for_boot_completion(self): 1086 """Waits for Android framework to broadcast ACTION_BOOT_COMPLETED. 1087 1088 This function times out after 15 minutes. 1089 """ 1090 timeout_start = time.time() 1091 timeout = 15 * 60 1092 1093 self.adb.wait_for_device(timeout=WAIT_FOR_DEVICE_TIMEOUT) 1094 while time.time() < timeout_start + timeout: 1095 try: 1096 completed = self.adb.getprop("sys.boot_completed") 1097 if completed == '1': 1098 return 1099 except adb.AdbError: 1100 # adb shell calls may fail during certain period of booting 1101 # process, which is normal. Ignoring these errors. 1102 pass 1103 time.sleep(5) 1104 raise AndroidDeviceError( 1105 "Device %s booting process timed out." % self.serial) 1106 1107 def reboot(self, stop_at_lock_screen=False): 1108 """Reboots the device. 1109 1110 Terminate all sl4a sessions, reboot the device, wait for device to 1111 complete booting, and restart an sl4a session if restart_sl4a is True. 1112 1113 Args: 1114 stop_at_lock_screen: whether to unlock after reboot. Set to False 1115 if want to bring the device to reboot up to password locking 1116 phase. Sl4a checking need the device unlocked after rebooting. 1117 1118 Returns: 1119 None, sl4a session and event_dispatcher. 1120 """ 1121 if self.is_bootloader: 1122 self.fastboot.reboot() 1123 return 1124 self.terminate_all_sessions() 1125 self.log.info("Rebooting") 1126 self.adb.reboot() 1127 self.wait_for_boot_completion() 1128 self.root_adb() 1129 if stop_at_lock_screen: 1130 return 1131 if not self.ensure_screen_on(): 1132 self.log.error("User window cannot come up") 1133 raise AndroidDeviceError("User window cannot come up") 1134 self.start_services(self.skip_sl4a) 1135 1136 def restart_runtime(self): 1137 """Restarts android runtime. 1138 1139 Terminate all sl4a sessions, restarts runtime, wait for framework 1140 complete restart, and restart an sl4a session if restart_sl4a is True. 1141 """ 1142 self.stop_services() 1143 self.log.info("Restarting android runtime") 1144 self.adb.shell("stop") 1145 self.adb.shell("start") 1146 self.wait_for_boot_completion() 1147 self.root_adb() 1148 if not self.ensure_screen_on(): 1149 self.log.error("User window cannot come up") 1150 raise AndroidDeviceError("User window cannot come up") 1151 self.start_services(self.skip_sl4a) 1152 1153 def search_logcat(self, matching_string, begin_time=None): 1154 """Search logcat message with given string. 1155 1156 Args: 1157 matching_string: matching_string to search. 1158 1159 Returns: 1160 A list of dictionaries with full log message, time stamp string 1161 and time object. For example: 1162 [{"log_message": "05-03 17:39:29.898 968 1001 D" 1163 "ActivityManager: Sending BOOT_COMPLETE user #0", 1164 "time_stamp": "2017-05-03 17:39:29.898", 1165 "datetime_obj": datetime object}] 1166 """ 1167 cmd_option = '-b all -v year -d' 1168 if begin_time: 1169 log_begin_time = acts_logger.epoch_to_log_line_timestamp( 1170 begin_time) 1171 cmd_option = '%s -t "%s"' % (cmd_option, log_begin_time) 1172 out = self.adb.logcat( 1173 '%s | grep "%s"' % (cmd_option, matching_string), 1174 ignore_status=True) 1175 if not out: return [] 1176 result = [] 1177 logs = re.findall(r'(\S+\s\S+)(.*%s.*)' % re.escape(matching_string), 1178 out) 1179 for log in logs: 1180 time_stamp = log[0] 1181 time_obj = datetime.strptime(time_stamp, "%Y-%m-%d %H:%M:%S.%f") 1182 result.append({ 1183 "log_message": "".join(log), 1184 "time_stamp": time_stamp, 1185 "datetime_obj": time_obj 1186 }) 1187 return result 1188 1189 def get_ipv4_address(self, interface='wlan0', timeout=5): 1190 for timer in range(0, timeout): 1191 try: 1192 ip_string = self.adb.shell('ifconfig %s|grep inet' % interface) 1193 break 1194 except adb.AdbError as e: 1195 if timer + 1 == timeout: 1196 self.log.warning( 1197 'Unable to find IP address for %s.' % interface) 1198 return None 1199 else: 1200 time.sleep(1) 1201 result = re.search('addr:(.*) Bcast', ip_string) 1202 if result != None: 1203 ip_address = result.group(1) 1204 try: 1205 socket.inet_aton(ip_address) 1206 return ip_address 1207 except socket.error: 1208 return None 1209 else: 1210 return None 1211 1212 def get_ipv4_gateway(self, timeout=5): 1213 for timer in range(0, timeout): 1214 try: 1215 gateway_string = self.adb.shell( 1216 'dumpsys wifi | grep mDhcpResults') 1217 break 1218 except adb.AdbError as e: 1219 if timer + 1 == timeout: 1220 self.log.warning('Unable to find gateway') 1221 return None 1222 else: 1223 time.sleep(1) 1224 result = re.search('Gateway (.*) DNS servers', gateway_string) 1225 if result != None: 1226 ipv4_gateway = result.group(1) 1227 try: 1228 socket.inet_aton(ipv4_gateway) 1229 return ipv4_gateway 1230 except socket.error: 1231 return None 1232 else: 1233 return None 1234 1235 def send_keycode(self, keycode): 1236 self.adb.shell("input keyevent KEYCODE_%s" % keycode) 1237 1238 def get_my_current_focus_window(self): 1239 """Get the current focus window on screen""" 1240 output = self.adb.shell( 1241 'dumpsys window windows | grep -E mCurrentFocus', 1242 ignore_status=True) 1243 if not output or "not found" in output or "Can't find" in output or ( 1244 "mCurrentFocus=null" in output): 1245 result = '' 1246 else: 1247 result = output.split(' ')[-1].strip("}") 1248 self.log.debug("Current focus window is %s", result) 1249 return result 1250 1251 def get_my_current_focus_app(self): 1252 """Get the current focus application""" 1253 output = self.adb.shell( 1254 'dumpsys window windows | grep -E mFocusedApp', ignore_status=True) 1255 if not output or "not found" in output or "Can't find" in output or ( 1256 "mFocusedApp=null" in output): 1257 result = '' 1258 else: 1259 result = output.split(' ')[-2] 1260 self.log.debug("Current focus app is %s", result) 1261 return result 1262 1263 def is_window_ready(self, window_name=None): 1264 current_window = self.get_my_current_focus_window() 1265 if window_name: 1266 return window_name in current_window 1267 return current_window and ENCRYPTION_WINDOW not in current_window 1268 1269 def wait_for_window_ready(self, 1270 window_name=None, 1271 check_interval=5, 1272 check_duration=60): 1273 elapsed_time = 0 1274 while elapsed_time < check_duration: 1275 if self.is_window_ready(window_name=window_name): 1276 return True 1277 time.sleep(check_interval) 1278 elapsed_time += check_interval 1279 self.log.info("Current focus window is %s", 1280 self.get_my_current_focus_window()) 1281 return False 1282 1283 def is_user_setup_complete(self): 1284 return "1" in self.adb.shell("settings get secure user_setup_complete") 1285 1286 def is_screen_awake(self): 1287 """Check if device screen is in sleep mode""" 1288 return "Awake" in self.adb.shell("dumpsys power | grep mWakefulness=") 1289 1290 def is_screen_emergency_dialer(self): 1291 """Check if device screen is in emergency dialer mode""" 1292 return "EmergencyDialer" in self.get_my_current_focus_window() 1293 1294 def is_screen_in_call_activity(self): 1295 """Check if device screen is in in-call activity notification""" 1296 return "InCallActivity" in self.get_my_current_focus_window() 1297 1298 def is_setupwizard_on(self): 1299 """Check if device screen is in emergency dialer mode""" 1300 return "setupwizard" in self.get_my_current_focus_app() 1301 1302 def is_screen_lock_enabled(self): 1303 """Check if screen lock is enabled""" 1304 cmd = ("sqlite3 /data/system/locksettings.db .dump"" | grep lockscreen.password_type | grep -v alternate") 1305 out = self.adb.shell(cmd, ignore_status=True) 1306 if "unable to open" in out: 1307 self.root_adb() 1308 out = self.adb.shell(cmd, ignore_status=True) 1309 if ",0,'0'" not in out and out != "": 1310 self.log.info("Screen lock is enabled") 1311 return True 1312 return False 1313 1314 def is_waiting_for_unlock_pin(self): 1315 """Check if device is waiting for unlock pin to boot up""" 1316 current_window = self.get_my_current_focus_window() 1317 current_app = self.get_my_current_focus_app() 1318 if ENCRYPTION_WINDOW in current_window: 1319 self.log.info("Device is in CrpytKeeper window") 1320 return True 1321 if "StatusBar" in current_window and ( 1322 (not current_app) or "FallbackHome" in current_app): 1323 self.log.info("Device is locked") 1324 return True 1325 return False 1326 1327 def ensure_screen_on(self): 1328 """Ensure device screen is powered on""" 1329 if self.is_screen_lock_enabled(): 1330 for _ in range(2): 1331 self.unlock_screen() 1332 time.sleep(1) 1333 if self.is_waiting_for_unlock_pin(): 1334 self.unlock_screen(password=DEFAULT_DEVICE_PASSWORD) 1335 time.sleep(1) 1336 if not self.is_waiting_for_unlock_pin( 1337 ) and self.wait_for_window_ready(): 1338 return True 1339 return False 1340 else: 1341 self.wakeup_screen() 1342 return True 1343 1344 def wakeup_screen(self): 1345 if not self.is_screen_awake(): 1346 self.log.info("Screen is not awake, wake it up") 1347 self.send_keycode("WAKEUP") 1348 1349 def go_to_sleep(self): 1350 if self.is_screen_awake(): 1351 self.send_keycode("SLEEP") 1352 1353 def send_keycode_number_pad(self, number): 1354 self.send_keycode("NUMPAD_%s" % number) 1355 1356 def unlock_screen(self, password=None): 1357 self.log.info("Unlocking with %s", password or "swipe up") 1358 # Bring device to SLEEP so that unlock process can start fresh 1359 self.send_keycode("SLEEP") 1360 time.sleep(1) 1361 self.send_keycode("WAKEUP") 1362 if ENCRYPTION_WINDOW not in self.get_my_current_focus_app(): 1363 self.send_keycode("MENU") 1364 if password: 1365 self.send_keycode("DEL") 1366 for number in password: 1367 self.send_keycode_number_pad(number) 1368 self.send_keycode("ENTER") 1369 self.send_keycode("BACK") 1370 1371 def exit_setup_wizard(self): 1372 if not self.is_user_setup_complete() or self.is_setupwizard_on(): 1373 self.adb.shell("pm disable %s" % self.get_setupwizard_package_name()) 1374 # Wait up to 5 seconds for user_setup_complete to be updated 1375 for _ in range(5): 1376 if self.is_user_setup_complete() or not self.is_setupwizard_on(): 1377 return 1378 time.sleep(1) 1379 1380 # If fail to exit setup wizard, set local.prop and reboot 1381 if not self.is_user_setup_complete() and self.is_setupwizard_on(): 1382 self.adb.shell("echo ro.test_harness=1 > /data/local.prop") 1383 self.adb.shell("chmod 644 /data/local.prop") 1384 self.reboot(stop_at_lock_screen=True) 1385 1386 def get_setupwizard_package_name(self): 1387 """Finds setupwizard package/.activity 1388 1389 Returns: 1390 packageName/.ActivityName 1391 """ 1392 package = self.adb.shell("pm list packages -f | grep setupwizard | grep com.google.android") 1393 wizard_package = re.split("=", package)[1] 1394 activity = re.search("wizard/(.*?).apk", package, re.IGNORECASE).groups()[0] 1395 self.log.info("%s/.%sActivity" % (wizard_package, activity)) 1396 return "%s/.%sActivity" % (wizard_package, activity) 1397 1398 1399class AndroidDeviceLoggerAdapter(logging.LoggerAdapter): 1400 def process(self, msg, kwargs): 1401 msg = "[AndroidDevice|%s] %s" % (self.extra["serial"], msg) 1402 return (msg, kwargs) 1403