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