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