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