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 sdk_api_level(self): 563 if self._sdk_api_level is not None: 564 return self._sdk_api_level 565 if self.is_bootloader: 566 self.log.error( 567 'Device is in fastboot mode. Cannot get build info.') 568 return 569 self._sdk_api_level = int( 570 self.adb.shell('getprop ro.build.version.sdk')) 571 return self._sdk_api_level 572 573 @property 574 def is_bootloader(self): 575 """True if the device is in bootloader mode. 576 """ 577 return self.serial in list_fastboot_devices() 578 579 @property 580 def is_adb_root(self): 581 """True if adb is running as root for this device. 582 """ 583 try: 584 return "0" == self.adb.shell("id -u") 585 except AdbError: 586 # Wait a bit and retry to work around adb flakiness for this cmd. 587 time.sleep(0.2) 588 return "0" == self.adb.shell("id -u") 589 590 @property 591 def model(self): 592 """The Android code name for the device.""" 593 # If device is in bootloader mode, get mode name from fastboot. 594 if self.is_bootloader: 595 out = self.fastboot.getvar("product").strip() 596 # "out" is never empty because of the "total time" message fastboot 597 # writes to stderr. 598 lines = out.split('\n', 1) 599 if lines: 600 tokens = lines[0].split(' ') 601 if len(tokens) > 1: 602 return tokens[1].lower() 603 return None 604 model = self.adb.getprop("ro.build.product").lower() 605 if model == "sprout": 606 return model 607 else: 608 return self.adb.getprop("ro.product.name").lower() 609 610 @property 611 def flavor(self): 612 """Returns the specific flavor of Android build the device is using.""" 613 return self.adb.getprop("ro.build.flavor").lower() 614 615 @property 616 def droid(self): 617 """Returns the RPC Service of the first Sl4aSession created.""" 618 if len(self._sl4a_manager.sessions) > 0: 619 session_id = sorted(self._sl4a_manager.sessions.keys())[0] 620 return self._sl4a_manager.sessions[session_id].rpc_client 621 else: 622 return None 623 624 @property 625 def ed(self): 626 """Returns the event dispatcher of the first Sl4aSession created.""" 627 if len(self._sl4a_manager.sessions) > 0: 628 session_id = sorted(self._sl4a_manager.sessions.keys())[0] 629 return self._sl4a_manager.sessions[ 630 session_id].get_event_dispatcher() 631 else: 632 return None 633 634 @property 635 def sl4a_sessions(self): 636 """Returns a dictionary of session ids to sessions.""" 637 return list(self._sl4a_manager.sessions) 638 639 @property 640 def is_adb_logcat_on(self): 641 """Whether there is an ongoing adb logcat collection. 642 """ 643 if self.adb_logcat_process: 644 if self.adb_logcat_process.is_running(): 645 return True 646 else: 647 # if skip_sl4a is true, there is no sl4a session 648 # if logcat died due to device reboot and sl4a session has 649 # not restarted there is no droid. 650 if self.droid: 651 self.droid.logI('Logcat died') 652 self.log.info("Logcat to %s died", self.log_path) 653 return False 654 return False 655 656 @property 657 def device_log_path(self): 658 """Returns the directory for all Android device logs for the current 659 test context and serial. 660 """ 661 return context.get_current_context().get_full_output_path(self.serial) 662 663 def update_sdk_api_level(self): 664 self._sdk_api_level = None 665 self.sdk_api_level() 666 667 def load_config(self, config): 668 """Add attributes to the AndroidDevice object based on json config. 669 670 Args: 671 config: A dictionary representing the configs. 672 673 Raises: 674 AndroidDeviceError is raised if the config is trying to overwrite 675 an existing attribute. 676 """ 677 for k, v in config.items(): 678 # skip_sl4a value can be reset from config file 679 if hasattr(self, k) and k != "skip_sl4a": 680 raise errors.AndroidDeviceError( 681 "Attempting to set existing attribute %s on %s" % 682 (k, self.serial), 683 serial=self.serial) 684 setattr(self, k, v) 685 686 def root_adb(self): 687 """Change adb to root mode for this device if allowed. 688 689 If executed on a production build, adb will not be switched to root 690 mode per security restrictions. 691 """ 692 if self.is_adb_root: 693 return 694 695 for attempt in range(ADB_ROOT_RETRY_COUNT): 696 try: 697 self.log.debug('Enabling ADB root mode: attempt %d.' % attempt) 698 self.adb.root() 699 except AdbError: 700 if attempt == ADB_ROOT_RETRY_COUNT: 701 raise 702 time.sleep(ADB_ROOT_RETRY_INTERVAL) 703 self.adb.wait_for_device() 704 705 def get_droid(self, handle_event=True): 706 """Create an sl4a connection to the device. 707 708 Return the connection handler 'droid'. By default, another connection 709 on the same session is made for EventDispatcher, and the dispatcher is 710 returned to the caller as well. 711 If sl4a server is not started on the device, try to start it. 712 713 Args: 714 handle_event: True if this droid session will need to handle 715 events. 716 717 Returns: 718 droid: Android object used to communicate with sl4a on the android 719 device. 720 ed: An optional EventDispatcher to organize events for this droid. 721 722 Examples: 723 Don't need event handling: 724 >>> ad = AndroidDevice() 725 >>> droid = ad.get_droid(False) 726 727 Need event handling: 728 >>> ad = AndroidDevice() 729 >>> droid, ed = ad.get_droid() 730 """ 731 self.log.debug( 732 "Creating RPC client_port={}, forwarded_port={}, server_port={}". 733 format(self.client_port, self.forwarded_port, self.server_port)) 734 session = self._sl4a_manager.create_session( 735 client_port=self.client_port, 736 forwarded_port=self.forwarded_port, 737 server_port=self.server_port) 738 droid = session.rpc_client 739 if handle_event: 740 ed = session.get_event_dispatcher() 741 return droid, ed 742 return droid 743 744 def get_package_pid(self, package_name): 745 """Gets the pid for a given package. Returns None if not running. 746 Args: 747 package_name: The name of the package. 748 Returns: 749 The first pid found under a given package name. None if no process 750 was found running the package. 751 Raises: 752 AndroidDeviceError if the output of the phone's process list was 753 in an unexpected format. 754 """ 755 for cmd in ("ps -A", "ps"): 756 try: 757 out = self.adb.shell('%s | grep "S %s"' % (cmd, package_name), 758 ignore_status=True) 759 if package_name not in out: 760 continue 761 try: 762 pid = int(out.split()[1]) 763 self.log.info('apk %s has pid %s.', package_name, pid) 764 return pid 765 except (IndexError, ValueError) as e: 766 # Possible ValueError from string to int cast. 767 # Possible IndexError from split. 768 self.log.warning( 769 'Command \"%s\" returned output line: ' 770 '\"%s\".\nError: %s', cmd, out, e) 771 except Exception as e: 772 self.log.warning( 773 'Device fails to check if %s running with \"%s\"\n' 774 'Exception %s', package_name, cmd, e) 775 self.log.debug("apk %s is not running", package_name) 776 return None 777 778 def get_dispatcher(self, droid): 779 """Return an EventDispatcher for an sl4a session 780 781 Args: 782 droid: Session to create EventDispatcher for. 783 784 Returns: 785 ed: An EventDispatcher for specified session. 786 """ 787 return self._sl4a_manager.sessions[droid.uid].get_event_dispatcher() 788 789 def _is_timestamp_in_range(self, target, log_begin_time, log_end_time): 790 low = acts_logger.logline_timestamp_comparator(log_begin_time, 791 target) <= 0 792 high = acts_logger.logline_timestamp_comparator(log_end_time, 793 target) >= 0 794 return low and high 795 796 def cat_adb_log(self, 797 tag, 798 begin_time, 799 end_time=None, 800 dest_path="AdbLogExcerpts"): 801 """Takes an excerpt of the adb logcat log from a certain time point to 802 current time. 803 804 Args: 805 tag: An identifier of the time period, usually the name of a test. 806 begin_time: Epoch time of the beginning of the time period. 807 end_time: Epoch time of the ending of the time period, default None 808 dest_path: Destination path of the excerpt file. 809 """ 810 log_begin_time = acts_logger.epoch_to_log_line_timestamp(begin_time) 811 if end_time is None: 812 log_end_time = acts_logger.get_log_line_timestamp() 813 else: 814 log_end_time = acts_logger.epoch_to_log_line_timestamp(end_time) 815 self.log.debug("Extracting adb log from logcat.") 816 logcat_path = os.path.join(self.device_log_path, 817 'adblog_%s_debug.txt' % self.serial) 818 if not os.path.exists(logcat_path): 819 self.log.warning("Logcat file %s does not exist." % logcat_path) 820 return 821 adb_excerpt_dir = os.path.join(self.log_path, dest_path) 822 os.makedirs(adb_excerpt_dir, exist_ok=True) 823 out_name = '%s,%s.txt' % (acts_logger.normalize_log_line_timestamp( 824 log_begin_time), self.serial) 825 tag_len = utils.MAX_FILENAME_LEN - len(out_name) 826 out_name = '%s,%s' % (tag[:tag_len], out_name) 827 adb_excerpt_path = os.path.join(adb_excerpt_dir, out_name) 828 with open(adb_excerpt_path, 'w', encoding='utf-8') as out: 829 in_file = logcat_path 830 with open(in_file, 'r', encoding='utf-8', errors='replace') as f: 831 while True: 832 line = None 833 try: 834 line = f.readline() 835 if not line: 836 break 837 except: 838 continue 839 line_time = line[:acts_logger.log_line_timestamp_len] 840 if not acts_logger.is_valid_logline_timestamp(line_time): 841 continue 842 if self._is_timestamp_in_range(line_time, log_begin_time, 843 log_end_time): 844 if not line.endswith('\n'): 845 line += '\n' 846 out.write(line) 847 return adb_excerpt_path 848 849 def search_logcat(self, 850 matching_string, 851 begin_time=None, 852 end_time=None, 853 logcat_path=None): 854 """Search logcat message with given string. 855 856 Args: 857 matching_string: matching_string to search. 858 begin_time: only the lines with time stamps later than begin_time 859 will be searched. 860 end_time: only the lines with time stamps earlier than end_time 861 will be searched. 862 logcat_path: the path of a specific file in which the search should 863 be performed. If None the path will be the default device log 864 path. 865 866 Returns: 867 A list of dictionaries with full log message, time stamp string, 868 time object and message ID. For example: 869 [{"log_message": "05-03 17:39:29.898 968 1001 D" 870 "ActivityManager: Sending BOOT_COMPLETE user #0", 871 "time_stamp": "2017-05-03 17:39:29.898", 872 "datetime_obj": datetime object, 873 "message_id": None}] 874 875 [{"log_message": "08-12 14:26:42.611043 2360 2510 D RILJ : " 876 "[0853]< DEACTIVATE_DATA_CALL [PHONE0]", 877 "time_stamp": "2020-08-12 14:26:42.611043", 878 "datetime_obj": datetime object}, 879 "message_id": "0853"}] 880 """ 881 if not logcat_path: 882 logcat_path = os.path.join(self.device_log_path, 883 'adblog_%s_debug.txt' % self.serial) 884 if not os.path.exists(logcat_path): 885 self.log.warning("Logcat file %s does not exist." % logcat_path) 886 return 887 output = job.run("grep '%s' %s" % (matching_string, logcat_path), 888 ignore_status=True) 889 if not output.stdout or output.exit_status != 0: 890 return [] 891 if begin_time: 892 if not isinstance(begin_time, datetime): 893 log_begin_time = acts_logger.epoch_to_log_line_timestamp( 894 begin_time) 895 begin_time = datetime.strptime(log_begin_time, 896 "%Y-%m-%d %H:%M:%S.%f") 897 if end_time: 898 if not isinstance(end_time, datetime): 899 log_end_time = acts_logger.epoch_to_log_line_timestamp( 900 end_time) 901 end_time = datetime.strptime(log_end_time, 902 "%Y-%m-%d %H:%M:%S.%f") 903 result = [] 904 logs = re.findall(r'(\S+\s\S+)(.*)', output.stdout) 905 for log in logs: 906 time_stamp = log[0] 907 time_obj = datetime.strptime(time_stamp, "%Y-%m-%d %H:%M:%S.%f") 908 909 if begin_time and time_obj < begin_time: 910 continue 911 912 if end_time and time_obj > end_time: 913 continue 914 915 res = re.findall(r'.*\[(\d+)\]', log[1]) 916 try: 917 message_id = res[0] 918 except: 919 message_id = None 920 921 result.append({ 922 "log_message": "".join(log), 923 "time_stamp": time_stamp, 924 "datetime_obj": time_obj, 925 "message_id": message_id 926 }) 927 return result 928 929 def start_adb_logcat(self): 930 """Starts a standing adb logcat collection in separate subprocesses and 931 save the logcat in a file. 932 """ 933 if self.is_adb_logcat_on: 934 self.log.warning( 935 'Android device %s already has a running adb logcat thread. ' % 936 self.serial) 937 return 938 # Disable adb log spam filter. Have to stop and clear settings first 939 # because 'start' doesn't support --clear option before Android N. 940 self.adb.shell("logpersist.stop --clear", ignore_status=True) 941 self.adb.shell("logpersist.start", ignore_status=True) 942 if hasattr(self, 'adb_logcat_param'): 943 extra_params = self.adb_logcat_param 944 else: 945 extra_params = "-b all" 946 947 self.adb_logcat_process = logcat.create_logcat_keepalive_process( 948 self.serial, self.log_dir, extra_params) 949 self.adb_logcat_process.start() 950 951 def stop_adb_logcat(self): 952 """Stops the adb logcat collection subprocess. 953 """ 954 if not self.is_adb_logcat_on: 955 self.log.warning( 956 'Android device %s does not have an ongoing adb logcat ' % 957 self.serial) 958 return 959 # Set the last timestamp to the current timestamp. This may cause 960 # a race condition that allows the same line to be logged twice, 961 # but it does not pose a problem for our logging purposes. 962 self.adb_logcat_process.stop() 963 self.adb_logcat_process = None 964 965 def get_apk_uid(self, apk_name): 966 """Get the uid of the given apk. 967 968 Args: 969 apk_name: Name of the package, e.g., com.android.phone. 970 971 Returns: 972 Linux UID for the apk. 973 """ 974 output = self.adb.shell("dumpsys package %s | grep userId=" % apk_name, 975 ignore_status=True) 976 result = re.search(r"userId=(\d+)", output) 977 if result: 978 return result.group(1) 979 else: 980 None 981 982 @record_api_usage 983 def get_apk_version(self, package_name): 984 """Get the version of the given apk. 985 986 Args: 987 package_name: Name of the package, e.g., com.android.phone. 988 989 Returns: 990 Version of the given apk. 991 """ 992 try: 993 output = self.adb.shell("dumpsys package %s | grep versionName" % 994 package_name) 995 pattern = re.compile(r"versionName=(.+)", re.I) 996 result = pattern.findall(output) 997 if result: 998 return result[0] 999 except Exception as e: 1000 self.log.warning("Fail to get the version of package %s: %s", 1001 package_name, e) 1002 self.log.debug("apk %s is not found", package_name) 1003 return None 1004 1005 def is_apk_installed(self, package_name): 1006 """Check if the given apk is already installed. 1007 1008 Args: 1009 package_name: Name of the package, e.g., com.android.phone. 1010 1011 Returns: 1012 True if package is installed. False otherwise. 1013 """ 1014 1015 try: 1016 return bool( 1017 self.adb.shell( 1018 '(pm list packages | grep -w "package:%s") || true' % 1019 package_name)) 1020 1021 except Exception as err: 1022 self.log.error( 1023 'Could not determine if %s is installed. ' 1024 'Received error:\n%s', package_name, err) 1025 return False 1026 1027 def is_sl4a_installed(self): 1028 return self.is_apk_installed(SL4A_APK_NAME) 1029 1030 def is_apk_running(self, package_name): 1031 """Check if the given apk is running. 1032 1033 Args: 1034 package_name: Name of the package, e.g., com.android.phone. 1035 1036 Returns: 1037 True if package is installed. False otherwise. 1038 """ 1039 for cmd in ("ps -A", "ps"): 1040 try: 1041 out = self.adb.shell('%s | grep "S %s"' % (cmd, package_name), 1042 ignore_status=True) 1043 if package_name in out: 1044 self.log.info("apk %s is running", package_name) 1045 return True 1046 except Exception as e: 1047 self.log.warning( 1048 "Device fails to check is %s running by %s " 1049 "Exception %s", package_name, cmd, e) 1050 continue 1051 self.log.debug("apk %s is not running", package_name) 1052 return False 1053 1054 def is_sl4a_running(self): 1055 return self.is_apk_running(SL4A_APK_NAME) 1056 1057 def force_stop_apk(self, package_name): 1058 """Force stop the given apk. 1059 1060 Args: 1061 package_name: Name of the package, e.g., com.android.phone. 1062 1063 Returns: 1064 True if package is installed. False otherwise. 1065 """ 1066 try: 1067 self.adb.shell('am force-stop %s' % package_name, 1068 ignore_status=True) 1069 except Exception as e: 1070 self.log.warning("Fail to stop package %s: %s", package_name, e) 1071 1072 def take_bug_report(self, test_name=None, begin_time=None): 1073 """Takes a bug report on the device and stores it in a file. 1074 1075 Args: 1076 test_name: Name of the test case that triggered this bug report. 1077 begin_time: Epoch time when the test started. If none is specified, 1078 the current time will be used. 1079 """ 1080 self.adb.wait_for_device(timeout=WAIT_FOR_DEVICE_TIMEOUT) 1081 new_br = True 1082 try: 1083 stdout = self.adb.shell("bugreportz -v") 1084 # This check is necessary for builds before N, where adb shell's ret 1085 # code and stderr are not propagated properly. 1086 if "not found" in stdout: 1087 new_br = False 1088 except AdbError: 1089 new_br = False 1090 br_path = self.device_log_path 1091 os.makedirs(br_path, exist_ok=True) 1092 epoch = begin_time if begin_time else utils.get_current_epoch_time() 1093 time_stamp = acts_logger.normalize_log_line_timestamp( 1094 acts_logger.epoch_to_log_line_timestamp(epoch)) 1095 out_name = "AndroidDevice%s_%s" % (self.serial, time_stamp) 1096 out_name = "%s.zip" % out_name if new_br else "%s.txt" % out_name 1097 full_out_path = os.path.join(br_path, out_name) 1098 # in case device restarted, wait for adb interface to return 1099 self.wait_for_boot_completion() 1100 if test_name: 1101 self.log.info("Taking bugreport for %s.", test_name) 1102 else: 1103 self.log.info("Taking bugreport.") 1104 if new_br: 1105 out = self.adb.shell("bugreportz", timeout=BUG_REPORT_TIMEOUT) 1106 if not out.startswith("OK"): 1107 raise errors.AndroidDeviceError( 1108 'Failed to take bugreport on %s: %s' % (self.serial, out), 1109 serial=self.serial) 1110 br_out_path = out.split(':')[1].strip().split()[0] 1111 self.adb.pull("%s %s" % (br_out_path, full_out_path)) 1112 else: 1113 self.adb.bugreport(" > {}".format(full_out_path), 1114 timeout=BUG_REPORT_TIMEOUT) 1115 if test_name: 1116 self.log.info("Bugreport for %s taken at %s.", test_name, 1117 full_out_path) 1118 else: 1119 self.log.info("Bugreport taken at %s.", test_name, full_out_path) 1120 self.adb.wait_for_device(timeout=WAIT_FOR_DEVICE_TIMEOUT) 1121 1122 def get_file_names(self, 1123 directory, 1124 begin_time=None, 1125 skip_files=[], 1126 match_string=None): 1127 """Get files names with provided directory.""" 1128 cmd = "find %s -type f" % directory 1129 if begin_time: 1130 current_time = utils.get_current_epoch_time() 1131 seconds = int(math.ceil((current_time - begin_time) / 1000.0)) 1132 cmd = "%s -mtime -%ss" % (cmd, seconds) 1133 if match_string: 1134 cmd = "%s -iname %s" % (cmd, match_string) 1135 for skip_file in skip_files: 1136 cmd = "%s ! -iname %s" % (cmd, skip_file) 1137 out = self.adb.shell(cmd, ignore_status=True) 1138 if not out or "No such" in out or "Permission denied" in out or \ 1139 "Not a directory" in out: 1140 return [] 1141 files = out.split("\n") 1142 self.log.debug("Find files in directory %s: %s", directory, files) 1143 return files 1144 1145 @property 1146 def external_storage_path(self): 1147 """ 1148 The $EXTERNAL_STORAGE path on the device. Most commonly set to '/sdcard' 1149 """ 1150 return self.adb.shell('echo $EXTERNAL_STORAGE') 1151 1152 def file_exists(self, file_path): 1153 """Returns whether a file exists on a device. 1154 1155 Args: 1156 file_path: The path of the file to check for. 1157 """ 1158 cmd = '(test -f %s && echo yes) || echo no' % file_path 1159 result = self.adb.shell(cmd) 1160 if result == 'yes': 1161 return True 1162 elif result == 'no': 1163 return False 1164 raise ValueError('Couldn\'t determine if %s exists. ' 1165 'Expected yes/no, got %s' % (file_path, result[cmd])) 1166 1167 def pull_files(self, device_paths, host_path=None): 1168 """Pull files from devices. 1169 1170 Args: 1171 device_paths: List of paths on the device to pull from. 1172 host_path: Destination path 1173 """ 1174 if isinstance(device_paths, str): 1175 device_paths = [device_paths] 1176 if not host_path: 1177 host_path = self.log_path 1178 for device_path in device_paths: 1179 self.log.info('Pull from device: %s -> %s' % 1180 (device_path, host_path)) 1181 self.adb.pull("%s %s" % (device_path, host_path), 1182 timeout=PULL_TIMEOUT) 1183 1184 def check_crash_report(self, 1185 test_name=None, 1186 begin_time=None, 1187 log_crash_report=False): 1188 """check crash report on the device.""" 1189 crash_reports = [] 1190 for crash_path in CRASH_REPORT_PATHS: 1191 try: 1192 cmd = 'cd %s' % crash_path 1193 self.adb.shell(cmd) 1194 except Exception as e: 1195 self.log.debug("received exception %s", e) 1196 continue 1197 crashes = self.get_file_names(crash_path, 1198 skip_files=CRASH_REPORT_SKIPS, 1199 begin_time=begin_time) 1200 if crash_path == "/data/tombstones/" and crashes: 1201 tombstones = crashes[:] 1202 for tombstone in tombstones: 1203 if self.adb.shell( 1204 'cat %s | grep "crash_dump failed to dump process"' 1205 % tombstone): 1206 crashes.remove(tombstone) 1207 if crashes: 1208 crash_reports.extend(crashes) 1209 if crash_reports and log_crash_report: 1210 crash_log_path = os.path.join(self.device_log_path, 1211 "Crashes_%s" % self.serial) 1212 os.makedirs(crash_log_path, exist_ok=True) 1213 self.pull_files(crash_reports, crash_log_path) 1214 return crash_reports 1215 1216 def get_qxdm_logs(self, test_name="", begin_time=None): 1217 """Get qxdm logs.""" 1218 # Sleep 10 seconds for the buffered log to be written in qxdm log file 1219 time.sleep(10) 1220 log_path = getattr(self, "qxdm_log_path", DEFAULT_QXDM_LOG_PATH) 1221 qxdm_logs = self.get_file_names(log_path, 1222 begin_time=begin_time, 1223 match_string="*.qmdl") 1224 if qxdm_logs: 1225 qxdm_log_path = os.path.join(self.device_log_path, 1226 "QXDM_%s" % self.serial) 1227 os.makedirs(qxdm_log_path, exist_ok=True) 1228 1229 self.log.info("Pull QXDM Log %s to %s", qxdm_logs, qxdm_log_path) 1230 self.pull_files(qxdm_logs, qxdm_log_path) 1231 1232 self.adb.pull("/firmware/image/qdsp6m.qdb %s" % qxdm_log_path, 1233 timeout=PULL_TIMEOUT, 1234 ignore_status=True) 1235 # Zip Folder 1236 utils.zip_directory('%s.zip' % qxdm_log_path, qxdm_log_path) 1237 shutil.rmtree(qxdm_log_path) 1238 else: 1239 self.log.error("Didn't find QXDM logs in %s." % log_path) 1240 if "Verizon" in self.adb.getprop("gsm.sim.operator.alpha"): 1241 omadm_log_path = os.path.join(self.device_log_path, 1242 "OMADM_%s" % self.serial) 1243 os.makedirs(omadm_log_path, exist_ok=True) 1244 self.log.info("Pull OMADM Log") 1245 self.adb.pull( 1246 "/data/data/com.android.omadm.service/files/dm/log/ %s" % 1247 omadm_log_path, 1248 timeout=PULL_TIMEOUT, 1249 ignore_status=True) 1250 1251 def get_sdm_logs(self, test_name="", begin_time=None): 1252 """Get sdm logs.""" 1253 # Sleep 10 seconds for the buffered log to be written in sdm log file 1254 time.sleep(10) 1255 log_paths = [ 1256 ALWAYS_ON_LOG_PATH, 1257 getattr(self, "sdm_log_path", DEFAULT_SDM_LOG_PATH) 1258 ] 1259 sdm_logs = [] 1260 for path in log_paths: 1261 sdm_logs += self.get_file_names(path, 1262 begin_time=begin_time, 1263 match_string="*.sdm*") 1264 if sdm_logs: 1265 sdm_log_path = os.path.join(self.device_log_path, 1266 "SDM_%s" % self.serial) 1267 os.makedirs(sdm_log_path, exist_ok=True) 1268 self.log.info("Pull SDM Log %s to %s", sdm_logs, sdm_log_path) 1269 self.pull_files(sdm_logs, sdm_log_path) 1270 else: 1271 self.log.error("Didn't find SDM logs in %s." % log_path) 1272 if "Verizon" in self.adb.getprop("gsm.sim.operator.alpha"): 1273 omadm_log_path = os.path.join(self.device_log_path, 1274 "OMADM_%s" % self.serial) 1275 os.makedirs(omadm_log_path, exist_ok=True) 1276 self.log.info("Pull OMADM Log") 1277 self.adb.pull( 1278 "/data/data/com.android.omadm.service/files/dm/log/ %s" % 1279 omadm_log_path, 1280 timeout=PULL_TIMEOUT, 1281 ignore_status=True) 1282 1283 def start_new_session(self, max_connections=None, server_port=None): 1284 """Start a new session in sl4a. 1285 1286 Also caches the droid in a dict with its uid being the key. 1287 1288 Returns: 1289 An Android object used to communicate with sl4a on the android 1290 device. 1291 1292 Raises: 1293 Sl4aException: Something is wrong with sl4a and it returned an 1294 existing uid to a new session. 1295 """ 1296 session = self._sl4a_manager.create_session( 1297 max_connections=max_connections, server_port=server_port) 1298 1299 self._sl4a_manager.sessions[session.uid] = session 1300 return session.rpc_client 1301 1302 def terminate_all_sessions(self): 1303 """Terminate all sl4a sessions on the AndroidDevice instance. 1304 1305 Terminate all sessions and clear caches. 1306 """ 1307 self._sl4a_manager.terminate_all_sessions() 1308 1309 def run_iperf_client_nb(self, 1310 server_host, 1311 extra_args="", 1312 timeout=IPERF_TIMEOUT, 1313 log_file_path=None): 1314 """Start iperf client on the device asynchronously. 1315 1316 Return status as true if iperf client start successfully. 1317 And data flow information as results. 1318 1319 Args: 1320 server_host: Address of the iperf server. 1321 extra_args: A string representing extra arguments for iperf client, 1322 e.g. "-i 1 -t 30". 1323 log_file_path: The complete file path to log the results. 1324 1325 """ 1326 cmd = "iperf3 -c {} {}".format(server_host, extra_args) 1327 if log_file_path: 1328 cmd += " --logfile {} &".format(log_file_path) 1329 self.adb.shell_nb(cmd) 1330 1331 def run_iperf_client(self, 1332 server_host, 1333 extra_args="", 1334 timeout=IPERF_TIMEOUT): 1335 """Start iperf client on the device. 1336 1337 Return status as true if iperf client start successfully. 1338 And data flow information as results. 1339 1340 Args: 1341 server_host: Address of the iperf server. 1342 extra_args: A string representing extra arguments for iperf client, 1343 e.g. "-i 1 -t 30". 1344 1345 Returns: 1346 status: true if iperf client start successfully. 1347 results: results have data flow information 1348 """ 1349 out = self.adb.shell("iperf3 -c {} {}".format(server_host, extra_args), 1350 timeout=timeout) 1351 clean_out = out.split('\n') 1352 if "error" in clean_out[0].lower(): 1353 return False, clean_out 1354 return True, clean_out 1355 1356 def run_iperf_server(self, extra_args=""): 1357 """Start iperf server on the device 1358 1359 Return status as true if iperf server started successfully. 1360 1361 Args: 1362 extra_args: A string representing extra arguments for iperf server. 1363 1364 Returns: 1365 status: true if iperf server started successfully. 1366 results: results have output of command 1367 """ 1368 out = self.adb.shell("iperf3 -s {}".format(extra_args)) 1369 clean_out = out.split('\n') 1370 if "error" in clean_out[0].lower(): 1371 return False, clean_out 1372 return True, clean_out 1373 1374 def wait_for_boot_completion(self, timeout=900.0): 1375 """Waits for Android framework to broadcast ACTION_BOOT_COMPLETED. 1376 1377 Args: 1378 timeout: Seconds to wait for the device to boot. Default value is 1379 15 minutes. 1380 """ 1381 timeout_start = time.time() 1382 1383 self.log.debug("ADB waiting for device") 1384 self.adb.wait_for_device(timeout=timeout) 1385 self.log.debug("Waiting for sys.boot_completed") 1386 while time.time() < timeout_start + timeout: 1387 try: 1388 completed = self.adb.getprop("sys.boot_completed") 1389 if completed == '1': 1390 self.log.debug("devie has rebooted") 1391 return 1392 except AdbError: 1393 # adb shell calls may fail during certain period of booting 1394 # process, which is normal. Ignoring these errors. 1395 pass 1396 time.sleep(5) 1397 raise errors.AndroidDeviceError( 1398 'Device %s booting process timed out.' % self.serial, 1399 serial=self.serial) 1400 1401 def reboot(self, 1402 stop_at_lock_screen=False, 1403 timeout=180, 1404 wait_after_reboot_complete=1): 1405 """Reboots the device. 1406 1407 Terminate all sl4a sessions, reboot the device, wait for device to 1408 complete booting, and restart an sl4a session if restart_sl4a is True. 1409 1410 Args: 1411 stop_at_lock_screen: whether to unlock after reboot. Set to False 1412 if want to bring the device to reboot up to password locking 1413 phase. Sl4a checking need the device unlocked after rebooting. 1414 timeout: time in seconds to wait for the device to complete 1415 rebooting. 1416 wait_after_reboot_complete: time in seconds to wait after the boot 1417 completion. 1418 """ 1419 if self.is_bootloader: 1420 self.fastboot.reboot() 1421 return 1422 self.stop_services() 1423 self.log.info("Rebooting") 1424 self.adb.reboot() 1425 1426 timeout_start = time.time() 1427 # b/111791239: Newer versions of android sometimes return early after 1428 # `adb reboot` is called. This means subsequent calls may make it to 1429 # the device before the reboot goes through, return false positives for 1430 # getprops such as sys.boot_completed. 1431 while time.time() < timeout_start + timeout: 1432 try: 1433 self.adb.get_state() 1434 time.sleep(.1) 1435 except AdbError: 1436 # get_state will raise an error if the device is not found. We 1437 # want the device to be missing to prove the device has kicked 1438 # off the reboot. 1439 break 1440 self.wait_for_boot_completion(timeout=(timeout - time.time() + 1441 timeout_start)) 1442 1443 self.log.debug('Wait for a while after boot completion.') 1444 time.sleep(wait_after_reboot_complete) 1445 self.root_adb() 1446 skip_sl4a = self.skip_sl4a 1447 self.skip_sl4a = self.skip_sl4a or stop_at_lock_screen 1448 self.start_services() 1449 self.skip_sl4a = skip_sl4a 1450 1451 def restart_runtime(self): 1452 """Restarts android runtime. 1453 1454 Terminate all sl4a sessions, restarts runtime, wait for framework 1455 complete restart, and restart an sl4a session if restart_sl4a is True. 1456 """ 1457 self.stop_services() 1458 self.log.info("Restarting android runtime") 1459 self.adb.shell("stop") 1460 # Reset the boot completed flag before we restart the framework 1461 # to correctly detect when the framework has fully come up. 1462 self.adb.shell("setprop sys.boot_completed 0") 1463 self.adb.shell("start") 1464 self.wait_for_boot_completion() 1465 self.root_adb() 1466 1467 self.start_services() 1468 1469 def get_ipv4_address(self, interface='wlan0', timeout=5): 1470 for timer in range(0, timeout): 1471 try: 1472 ip_string = self.adb.shell('ifconfig %s|grep inet' % interface) 1473 break 1474 except adb.AdbError as e: 1475 if timer + 1 == timeout: 1476 self.log.warning('Unable to find IP address for %s.' % 1477 interface) 1478 return None 1479 else: 1480 time.sleep(1) 1481 result = re.search('addr:(.*) Bcast', ip_string) 1482 if result != None: 1483 ip_address = result.group(1) 1484 try: 1485 socket.inet_aton(ip_address) 1486 return ip_address 1487 except socket.error: 1488 return None 1489 else: 1490 return None 1491 1492 def get_ipv4_gateway(self, timeout=5): 1493 for timer in range(0, timeout): 1494 try: 1495 gateway_string = self.adb.shell( 1496 'dumpsys wifi | grep mDhcpResults') 1497 break 1498 except adb.AdbError as e: 1499 if timer + 1 == timeout: 1500 self.log.warning('Unable to find gateway') 1501 return None 1502 else: 1503 time.sleep(1) 1504 result = re.search('Gateway (.*) DNS servers', gateway_string) 1505 if result != None: 1506 ipv4_gateway = result.group(1) 1507 try: 1508 socket.inet_aton(ipv4_gateway) 1509 return ipv4_gateway 1510 except socket.error: 1511 return None 1512 else: 1513 return None 1514 1515 @record_api_usage 1516 def send_keycode(self, keycode): 1517 self.adb.shell("input keyevent KEYCODE_%s" % keycode) 1518 1519 @record_api_usage 1520 def get_my_current_focus_window(self): 1521 """Get the current focus window on screen""" 1522 output = self.adb.shell( 1523 'dumpsys window displays | grep -E mCurrentFocus | grep -v null', 1524 ignore_status=True) 1525 if not output or "not found" in output or "Can't find" in output: 1526 result = '' 1527 else: 1528 result = output.split(' ')[-1].strip("}") 1529 self.log.debug("Current focus window is %s", result) 1530 return result 1531 1532 @record_api_usage 1533 def get_my_current_focus_app(self): 1534 """Get the current focus application""" 1535 dumpsys_cmd = [ 1536 'dumpsys window | grep -E mFocusedApp', 1537 'dumpsys window displays | grep -E mFocusedApp' 1538 ] 1539 for cmd in dumpsys_cmd: 1540 output = self.adb.shell(cmd, ignore_status=True) 1541 if not output or "not found" in output or "Can't find" in output or ( 1542 "mFocusedApp=null" in output): 1543 result = '' 1544 else: 1545 result = output.split(' ')[-2] 1546 break 1547 self.log.debug("Current focus app is %s", result) 1548 return result 1549 1550 @record_api_usage 1551 def is_window_ready(self, window_name=None): 1552 current_window = self.get_my_current_focus_window() 1553 if window_name: 1554 return window_name in current_window 1555 return current_window and ENCRYPTION_WINDOW not in current_window 1556 1557 @record_api_usage 1558 def wait_for_window_ready(self, 1559 window_name=None, 1560 check_interval=5, 1561 check_duration=60): 1562 elapsed_time = 0 1563 while elapsed_time < check_duration: 1564 if self.is_window_ready(window_name=window_name): 1565 return True 1566 time.sleep(check_interval) 1567 elapsed_time += check_interval 1568 self.log.info("Current focus window is %s", 1569 self.get_my_current_focus_window()) 1570 return False 1571 1572 @record_api_usage 1573 def is_user_setup_complete(self): 1574 return "1" in self.adb.shell("settings get secure user_setup_complete") 1575 1576 @record_api_usage 1577 def is_screen_awake(self): 1578 """Check if device screen is in sleep mode""" 1579 return "Awake" in self.adb.shell("dumpsys power | grep mWakefulness=") 1580 1581 @record_api_usage 1582 def is_screen_emergency_dialer(self): 1583 """Check if device screen is in emergency dialer mode""" 1584 return "EmergencyDialer" in self.get_my_current_focus_window() 1585 1586 @record_api_usage 1587 def is_screen_in_call_activity(self): 1588 """Check if device screen is in in-call activity notification""" 1589 return "InCallActivity" in self.get_my_current_focus_window() 1590 1591 @record_api_usage 1592 def is_setupwizard_on(self): 1593 """Check if device screen is in emergency dialer mode""" 1594 return "setupwizard" in self.get_my_current_focus_app() 1595 1596 @record_api_usage 1597 def is_screen_lock_enabled(self): 1598 """Check if screen lock is enabled""" 1599 cmd = ("dumpsys window policy | grep showing=") 1600 out = self.adb.shell(cmd, ignore_status=True) 1601 return "true" in out 1602 1603 @record_api_usage 1604 def is_waiting_for_unlock_pin(self): 1605 """Check if device is waiting for unlock pin to boot up""" 1606 current_window = self.get_my_current_focus_window() 1607 current_app = self.get_my_current_focus_app() 1608 if ENCRYPTION_WINDOW in current_window: 1609 self.log.info("Device is in CrpytKeeper window") 1610 return True 1611 if "StatusBar" in current_window and ( 1612 (not current_app) or "FallbackHome" in current_app): 1613 self.log.info("Device is locked") 1614 return True 1615 return False 1616 1617 @record_api_usage 1618 def ensure_screen_on(self): 1619 """Ensure device screen is powered on""" 1620 if self.is_screen_lock_enabled(): 1621 for _ in range(2): 1622 self.unlock_screen() 1623 time.sleep(1) 1624 if self.is_waiting_for_unlock_pin(): 1625 self.unlock_screen(password=DEFAULT_DEVICE_PASSWORD) 1626 time.sleep(1) 1627 if not self.is_waiting_for_unlock_pin( 1628 ) and self.wait_for_window_ready(): 1629 return True 1630 return False 1631 else: 1632 self.wakeup_screen() 1633 return True 1634 1635 @record_api_usage 1636 def wakeup_screen(self): 1637 if not self.is_screen_awake(): 1638 self.log.info("Screen is not awake, wake it up") 1639 self.send_keycode("WAKEUP") 1640 1641 @record_api_usage 1642 def go_to_sleep(self): 1643 if self.is_screen_awake(): 1644 self.send_keycode("SLEEP") 1645 1646 @record_api_usage 1647 def send_keycode_number_pad(self, number): 1648 self.send_keycode("NUMPAD_%s" % number) 1649 1650 @record_api_usage 1651 def unlock_screen(self, password=None): 1652 self.log.info("Unlocking with %s", password or "swipe up") 1653 # Bring device to SLEEP so that unlock process can start fresh 1654 self.send_keycode("SLEEP") 1655 time.sleep(1) 1656 self.send_keycode("WAKEUP") 1657 if ENCRYPTION_WINDOW not in self.get_my_current_focus_app(): 1658 self.send_keycode("MENU") 1659 if password: 1660 self.send_keycode("DEL") 1661 for number in password: 1662 self.send_keycode_number_pad(number) 1663 self.send_keycode("ENTER") 1664 self.send_keycode("BACK") 1665 1666 @record_api_usage 1667 def screenshot(self, name=""): 1668 """Take a screenshot on the device. 1669 1670 Args: 1671 name: additional information of screenshot on the file name. 1672 """ 1673 if name: 1674 file_name = "%s_%s" % (DEFAULT_SCREENSHOT_PATH, name) 1675 file_name = "%s_%s.png" % (file_name, utils.get_current_epoch_time()) 1676 self.ensure_screen_on() 1677 self.log.info("Log screenshot to %s", file_name) 1678 try: 1679 self.adb.shell("screencap -p %s" % file_name) 1680 except: 1681 self.log.error("Fail to log screenshot to %s", file_name) 1682 1683 @record_api_usage 1684 def exit_setup_wizard(self): 1685 # Handling Android TV's setupwizard is ignored for now. 1686 if 'feature:android.hardware.type.television' in self.adb.shell( 1687 'pm list features'): 1688 return 1689 if not self.is_user_setup_complete() or self.is_setupwizard_on(): 1690 # b/116709539 need this to prevent reboot after skip setup wizard 1691 self.adb.shell("am start -a com.android.setupwizard.EXIT", 1692 ignore_status=True) 1693 self.adb.shell("pm disable %s" % 1694 self.get_setupwizard_package_name(), 1695 ignore_status=True) 1696 # Wait up to 5 seconds for user_setup_complete to be updated 1697 end_time = time.time() + 5 1698 while time.time() < end_time: 1699 if self.is_user_setup_complete() or not self.is_setupwizard_on(): 1700 return 1701 1702 # If fail to exit setup wizard, set local.prop and reboot 1703 if not self.is_user_setup_complete() and self.is_setupwizard_on(): 1704 self.adb.shell("echo ro.test_harness=1 > /data/local.prop") 1705 self.adb.shell("chmod 644 /data/local.prop") 1706 self.reboot(stop_at_lock_screen=True) 1707 1708 @record_api_usage 1709 def get_setupwizard_package_name(self): 1710 """Finds setupwizard package/.activity 1711 1712 Bypass setupwizard or setupwraith depending on device. 1713 1714 Returns: 1715 packageName/.ActivityName 1716 """ 1717 packages_to_skip = "'setupwizard|setupwraith'" 1718 android_package_name = "com.google.android" 1719 package = self.adb.shell( 1720 "pm list packages -f | grep -E {} | grep {}".format( 1721 packages_to_skip, android_package_name)) 1722 wizard_package = package.split('=')[1] 1723 activity = package.split('=')[0].split('/')[-2] 1724 self.log.info("%s/.%sActivity" % (wizard_package, activity)) 1725 return "%s/.%sActivity" % (wizard_package, activity) 1726 1727 @record_api_usage 1728 def push_system_file(self, src_file_path, dst_file_path, push_timeout=300): 1729 """Pushes a file onto the read-only file system. 1730 1731 For speed, the device is left in root mode after this call, and leaves 1732 verity disabled. To re-enable verity, call ensure_verity_enabled(). 1733 1734 Args: 1735 src_file_path: The path to the system app to install. 1736 dst_file_path: The destination of the file. 1737 push_timeout: How long to wait for the push to finish. 1738 Returns: 1739 Whether or not the install was successful. 1740 """ 1741 self.adb.ensure_root() 1742 try: 1743 self.ensure_verity_disabled() 1744 self.adb.remount() 1745 out = self.adb.push('%s %s' % (src_file_path, dst_file_path), 1746 timeout=push_timeout) 1747 if 'error' in out: 1748 self.log.error('Unable to push system file %s to %s due to %s', 1749 src_file_path, dst_file_path, out) 1750 return False 1751 return True 1752 except Exception as e: 1753 self.log.error('Unable to push system file %s to %s due to %s', 1754 src_file_path, dst_file_path, e) 1755 return False 1756 1757 @record_api_usage 1758 def ensure_verity_enabled(self): 1759 """Ensures that verity is enabled. 1760 1761 If verity is not enabled, this call will reboot the phone. Note that 1762 this only works on debuggable builds. 1763 """ 1764 user = self.adb.get_user_id() 1765 # The below properties will only exist if verity has been enabled. 1766 system_verity = self.adb.getprop('partition.system.verified') 1767 vendor_verity = self.adb.getprop('partition.vendor.verified') 1768 if not system_verity or not vendor_verity: 1769 self.adb.ensure_root() 1770 self.adb.enable_verity() 1771 self.reboot() 1772 self.adb.ensure_user(user) 1773 1774 @record_api_usage 1775 def ensure_verity_disabled(self): 1776 """Ensures that verity is disabled. 1777 1778 If verity is enabled, this call will reboot the phone. 1779 """ 1780 user = self.adb.get_user_id() 1781 # The below properties will only exist if verity has been enabled. 1782 system_verity = self.adb.getprop('partition.system.verified') 1783 vendor_verity = self.adb.getprop('partition.vendor.verified') 1784 if system_verity or vendor_verity: 1785 self.adb.ensure_root() 1786 self.adb.disable_verity() 1787 self.reboot() 1788 self.adb.ensure_user(user) 1789 1790 1791class AndroidDeviceLoggerAdapter(logging.LoggerAdapter): 1792 def process(self, msg, kwargs): 1793 msg = "[AndroidDevice|%s] %s" % (self.extra["serial"], msg) 1794 return (msg, kwargs) 1795