1 #!/usr/bin/env python3.4 2 # 3 # Copyright 2016 - The Android Open Source Project 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 from builtins import str 18 from builtins import open 19 20 import os 21 import time 22 import traceback 23 24 from acts import logger as acts_logger 25 from acts import signals 26 from acts import utils 27 from acts.controllers import adb 28 from acts.controllers import android 29 from acts.controllers import event_dispatcher 30 from acts.controllers import fastboot 31 32 ACTS_CONTROLLER_CONFIG_NAME = "AndroidDevice" 33 ACTS_CONTROLLER_REFERENCE_NAME = "android_devices" 34 35 ANDROID_DEVICE_PICK_ALL_TOKEN = "*" 36 # Key name for adb logcat extra params in config file. 37 ANDROID_DEVICE_ADB_LOGCAT_PARAM_KEY = "adb_logcat_param" 38 ANDROID_DEVICE_EMPTY_CONFIG_MSG = "Configuration is empty, abort!" 39 ANDROID_DEVICE_NOT_LIST_CONFIG_MSG = "Configuration should be a list, abort!" 40 41 class AndroidDeviceError(signals.ControllerError): 42 pass 43 44 class DoesNotExistError(AndroidDeviceError): 45 """Raised when something that does not exist is referenced. 46 """ 47 48 def create(configs, logger): 49 if not configs: 50 raise AndroidDeviceError(ANDROID_DEVICE_EMPTY_CONFIG_MSG) 51 elif configs == ANDROID_DEVICE_PICK_ALL_TOKEN: 52 ads = get_all_instances(logger=logger) 53 elif not isinstance(configs, list): 54 raise AndroidDeviceError(ANDROID_DEVICE_NOT_LIST_CONFIG_MSG) 55 elif isinstance(configs[0], str): 56 # Configs is a list of serials. 57 ads = get_instances(configs, logger) 58 else: 59 # Configs is a list of dicts. 60 ads = get_instances_with_configs(configs, logger) 61 connected_ads = list_adb_devices() 62 63 for ad in ads: 64 if ad.serial not in connected_ads: 65 raise DoesNotExistError(("Android device %s is specified in config" 66 " but is not attached.") % ad.serial) 67 _start_services_on_ads(ads) 68 return ads 69 70 71 def destroy(ads): 72 """Cleans up AndroidDevice objects. 73 74 Args: 75 ads: A list of AndroidDevice objects. 76 """ 77 for ad in ads: 78 try: 79 ad.clean_up() 80 except: 81 ad.log.exception("Failed to clean up properly.") 82 83 def _start_services_on_ads(ads): 84 """Starts long running services on multiple AndroidDevice objects. 85 86 If any one AndroidDevice object fails to start services, cleans up all 87 existing AndroidDevice objects and their services. 88 89 Args: 90 ads: A list of AndroidDevice objects whose services to start. 91 """ 92 running_ads = [] 93 for ad in ads: 94 running_ads.append(ad) 95 try: 96 ad.start_services(skip_sl4a=getattr(ad, "skip_sl4a", False)) 97 except: 98 ad.log.exception("Failed to start some services, abort!") 99 destroy(running_ads) 100 raise 101 102 def _parse_device_list(device_list_str, key): 103 """Parses a byte string representing a list of devices. The string is 104 generated by calling either adb or fastboot. 105 106 Args: 107 device_list_str: Output of adb or fastboot. 108 key: The token that signifies a device in device_list_str. 109 110 Returns: 111 A list of android device serial numbers. 112 """ 113 clean_lines = str(device_list_str, 'utf-8').strip().split('\n') 114 results = [] 115 for line in clean_lines: 116 tokens = line.strip().split('\t') 117 if len(tokens) == 2 and tokens[1] == key: 118 results.append(tokens[0]) 119 return results 120 121 def list_adb_devices(): 122 """List all android devices connected to the computer that are detected by 123 adb. 124 125 Returns: 126 A list of android device serials. Empty if there's none. 127 """ 128 out = adb.AdbProxy().devices() 129 return _parse_device_list(out, "device") 130 131 def list_fastboot_devices(): 132 """List all android devices connected to the computer that are in in 133 fastboot mode. These are detected by fastboot. 134 135 Returns: 136 A list of android device serials. Empty if there's none. 137 """ 138 out = fastboot.FastbootProxy().devices() 139 return _parse_device_list(out, "fastboot") 140 141 def get_instances(serials, logger=None): 142 """Create AndroidDevice instances from a list of serials. 143 144 Args: 145 serials: A list of android device serials. 146 logger: A logger to be passed to each instance. 147 148 Returns: 149 A list of AndroidDevice objects. 150 """ 151 results = [] 152 for s in serials: 153 results.append(AndroidDevice(s, logger=logger)) 154 return results 155 156 def get_instances_with_configs(configs, logger=None): 157 """Create AndroidDevice instances from a list of json configs. 158 159 Each config should have the required key-value pair "serial". 160 161 Args: 162 configs: A list of dicts each representing the configuration of one 163 android device. 164 logger: A logger to be passed to each instance. 165 166 Returns: 167 A list of AndroidDevice objects. 168 """ 169 results = [] 170 for c in configs: 171 try: 172 serial = c.pop("serial") 173 except KeyError: 174 raise AndroidDeviceError(('Required value "serial" is missing in ' 175 'AndroidDevice config %s.') % c) 176 ad = AndroidDevice(serial, logger=logger) 177 ad.load_config(c) 178 results.append(ad) 179 return results 180 181 def get_all_instances(include_fastboot=False, logger=None): 182 """Create AndroidDevice instances for all attached android devices. 183 184 Args: 185 include_fastboot: Whether to include devices in bootloader mode or not. 186 logger: A logger to be passed to each instance. 187 188 Returns: 189 A list of AndroidDevice objects each representing an android device 190 attached to the computer. 191 """ 192 if include_fastboot: 193 serial_list = list_adb_devices() + list_fastboot_devices() 194 return get_instances(serial_list, logger=logger) 195 return get_instances(list_adb_devices(), logger=logger) 196 197 def filter_devices(ads, func): 198 """Finds the AndroidDevice instances from a list that match certain 199 conditions. 200 201 Args: 202 ads: A list of AndroidDevice instances. 203 func: A function that takes an AndroidDevice object and returns True 204 if the device satisfies the filter condition. 205 206 Returns: 207 A list of AndroidDevice instances that satisfy the filter condition. 208 """ 209 results = [] 210 for ad in ads: 211 if func(ad): 212 results.append(ad) 213 return results 214 215 def get_device(ads, **kwargs): 216 """Finds a unique AndroidDevice instance from a list that has specific 217 attributes of certain values. 218 219 Example: 220 get_device(android_devices, label="foo", phone_number="1234567890") 221 get_device(android_devices, model="angler") 222 223 Args: 224 ads: A list of AndroidDevice instances. 225 kwargs: keyword arguments used to filter AndroidDevice instances. 226 227 Returns: 228 The target AndroidDevice instance. 229 230 Raises: 231 AndroidDeviceError is raised if none or more than one device is 232 matched. 233 """ 234 def _get_device_filter(ad): 235 for k, v in kwargs.items(): 236 if not hasattr(ad, k): 237 return False 238 elif getattr(ad, k) != v: 239 return False 240 return True 241 filtered = filter_devices(ads, _get_device_filter) 242 if not filtered: 243 raise AndroidDeviceError(("Could not find a target device that matches" 244 " condition: %s.") % kwargs) 245 elif len(filtered) == 1: 246 return filtered[0] 247 else: 248 serials = [ad.serial for ad in filtered] 249 raise AndroidDeviceError("More than one device matched: %s" % serials) 250 251 def take_bug_reports(ads, test_name, begin_time): 252 """Takes bug reports on a list of android devices. 253 254 If you want to take a bug report, call this function with a list of 255 android_device objects in on_fail. But reports will be taken on all the 256 devices in the list concurrently. Bug report takes a relative long 257 time to take, so use this cautiously. 258 259 Args: 260 ads: A list of AndroidDevice instances. 261 test_name: Name of the test case that triggered this bug report. 262 begin_time: Logline format timestamp taken when the test started. 263 """ 264 begin_time = acts_logger.normalize_log_line_timestamp(begin_time) 265 def take_br(test_name, begin_time, ad): 266 ad.take_bug_report(test_name, begin_time) 267 args = [(test_name, begin_time, ad) for ad in ads] 268 utils.concurrent_exec(take_br, args) 269 270 class AndroidDevice: 271 """Class representing an android device. 272 273 Each object of this class represents one Android device in ACTS, including 274 handles to adb, fastboot, and sl4a clients. In addition to direct adb 275 commands, this object also uses adb port forwarding to talk to the Android 276 device. 277 278 Attributes: 279 serial: A string that's the serial number of the Androi device. 280 h_port: An integer that's the port number for adb port forwarding used 281 on the computer the Android device is connected 282 d_port: An integer that's the port number used on the Android device 283 for adb port forwarding. 284 log: A LoggerProxy object used for the class's internal logging. 285 log_path: A string that is the path where all logs collected on this 286 android device should be stored. 287 adb_logcat_process: A process that collects the adb logcat. 288 adb_logcat_file_path: A string that's the full path to the adb logcat 289 file collected, if any. 290 adb: An AdbProxy object used for interacting with the device via adb. 291 fastboot: A FastbootProxy object used for interacting with the device 292 via fastboot. 293 """ 294 295 def __init__(self, serial="", host_port=None, device_port=8080, 296 logger=None): 297 self.serial = serial 298 self.h_port = host_port 299 self.d_port = device_port 300 self.log = acts_logger.LoggerProxy(logger) 301 lp = self.log.log_path 302 self.log_path = os.path.join(lp, "AndroidDevice%s" % serial) 303 self._droid_sessions = {} 304 self._event_dispatchers = {} 305 self.adb_logcat_process = None 306 self.adb_logcat_file_path = None 307 self.adb = adb.AdbProxy(serial) 308 self.fastboot = fastboot.FastbootProxy(serial) 309 if not self.is_bootloader: 310 self.root_adb() 311 312 def clean_up(self): 313 """Cleans up the AndroidDevice object and releases any resources it 314 claimed. 315 """ 316 self.stop_services() 317 if self.h_port: 318 self.adb.forward("--remove tcp:%d" % self.h_port) 319 320 # TODO(angli): This function shall be refactored to accommodate all services 321 # and not have hard coded switch for SL4A when b/29157104 is done. 322 def start_services(self, skip_sl4a=False): 323 """Starts long running services on the android device. 324 325 1. Start adb logcat capture. 326 2. Start SL4A if not skipped. 327 328 Args: 329 skip_sl4a: Does not attempt to start SL4A if True. 330 """ 331 try: 332 self.start_adb_logcat() 333 except: 334 self.log.exception("Failed to start adb logcat!") 335 raise 336 if not skip_sl4a: 337 try: 338 self.get_droid() 339 self.ed.start() 340 except: 341 self.log.exception("Failed to start sl4a!") 342 raise 343 344 def stop_services(self): 345 """Stops long running services on the android device. 346 347 Stop adb logcat and terminate sl4a sessions if exist. 348 """ 349 if self.adb_logcat_process: 350 self.stop_adb_logcat() 351 self.terminate_all_sessions() 352 353 @property 354 def is_bootloader(self): 355 """True if the device is in bootloader mode. 356 """ 357 return self.serial in list_fastboot_devices() 358 359 @property 360 def is_adb_root(self): 361 """True if adb is running as root for this device. 362 """ 363 try: 364 return "0" == self.adb.shell("id -u").decode("utf-8").strip() 365 except adb.AdbError: 366 # Wait a bit and retry to work around adb flakiness for this cmd. 367 time.sleep(0.2) 368 return "0" == self.adb.shell("id -u").decode("utf-8").strip() 369 370 @property 371 def model(self): 372 """The Android code name for the device. 373 """ 374 # If device is in bootloader mode, get mode name from fastboot. 375 if self.is_bootloader: 376 out = self.fastboot.getvar("product").strip() 377 # "out" is never empty because of the "total time" message fastboot 378 # writes to stderr. 379 lines = out.decode("utf-8").split('\n', 1) 380 if lines: 381 tokens = lines[0].split(' ') 382 if len(tokens) > 1: 383 return tokens[1].lower() 384 return None 385 out = self.adb.shell('getprop | grep ro.build.product') 386 model = out.decode("utf-8").strip().split('[')[-1][:-1].lower() 387 if model == "sprout": 388 return model 389 else: 390 out = self.adb.shell('getprop | grep ro.product.name') 391 model = out.decode("utf-8").strip().split('[')[-1][:-1].lower() 392 return model 393 394 @property 395 def droid(self): 396 """The first sl4a session initiated on this device. None if there isn't 397 one. 398 """ 399 try: 400 session_id = sorted(self._droid_sessions)[0] 401 return self._droid_sessions[session_id][0] 402 except IndexError: 403 return None 404 405 @property 406 def ed(self): 407 """The first event_dispatcher instance created on this device. None if 408 there isn't one. 409 """ 410 try: 411 session_id = sorted(self._event_dispatchers)[0] 412 return self._event_dispatchers[session_id] 413 except IndexError: 414 return None 415 416 @property 417 def droids(self): 418 """A list of the active sl4a sessions on this device. 419 420 If multiple connections exist for the same session, only one connection 421 is listed. 422 """ 423 keys = sorted(self._droid_sessions) 424 results = [] 425 for k in keys: 426 results.append(self._droid_sessions[k][0]) 427 return results 428 429 @property 430 def eds(self): 431 """A list of the event_dispatcher objects on this device. 432 433 The indexing of the list matches that of the droids property. 434 """ 435 keys = sorted(self._event_dispatchers) 436 results = [] 437 for k in keys: 438 results.append(self._event_dispatchers[k]) 439 return results 440 441 @property 442 def is_adb_logcat_on(self): 443 """Whether there is an ongoing adb logcat collection. 444 """ 445 if self.adb_logcat_process: 446 return True 447 return False 448 449 def load_config(self, config): 450 """Add attributes to the AndroidDevice object based on json config. 451 452 Args: 453 config: A dictionary representing the configs. 454 455 Raises: 456 AndroidDeviceError is raised if the config is trying to overwrite 457 an existing attribute. 458 """ 459 for k, v in config.items(): 460 if hasattr(self, k): 461 raise AndroidDeviceError(("Attempting to set existing " 462 "attribute %s on %s") % (k, self.serial)) 463 setattr(self, k, v) 464 465 def root_adb(self): 466 """Change adb to root mode for this device if allowed. 467 468 If executed on a production build, adb will not be switched to root 469 mode per security restrictions. 470 """ 471 self.adb.root() 472 self.adb.wait_for_device() 473 474 def get_droid(self, handle_event=True): 475 """Create an sl4a connection to the device. 476 477 Return the connection handler 'droid'. By default, another connection 478 on the same session is made for EventDispatcher, and the dispatcher is 479 returned to the caller as well. 480 If sl4a server is not started on the device, try to start it. 481 482 Args: 483 handle_event: True if this droid session will need to handle 484 events. 485 486 Returns: 487 droid: Android object used to communicate with sl4a on the android 488 device. 489 ed: An optional EventDispatcher to organize events for this droid. 490 491 Examples: 492 Don't need event handling: 493 >>> ad = AndroidDevice() 494 >>> droid = ad.get_droid(False) 495 496 Need event handling: 497 >>> ad = AndroidDevice() 498 >>> droid, ed = ad.get_droid() 499 """ 500 if not self.h_port or not adb.is_port_available(self.h_port): 501 self.h_port = adb.get_available_host_port() 502 self.adb.tcp_forward(self.h_port, self.d_port) 503 try: 504 droid = self.start_new_session() 505 except: 506 self.adb.start_sl4a() 507 droid = self.start_new_session() 508 if handle_event: 509 ed = self.get_dispatcher(droid) 510 return droid, ed 511 return droid 512 513 def get_dispatcher(self, droid): 514 """Return an EventDispatcher for an sl4a session 515 516 Args: 517 droid: Session to create EventDispatcher for. 518 519 Returns: 520 ed: An EventDispatcher for specified session. 521 """ 522 ed_key = self.serial + str(droid.uid) 523 if ed_key in self._event_dispatchers: 524 if self._event_dispatchers[ed_key] is None: 525 raise AndroidDeviceError("EventDispatcher Key Empty") 526 self.log.debug("Returning existing key %s for event dispatcher!", 527 ed_key) 528 return self._event_dispatchers[ed_key] 529 event_droid = self.add_new_connection_to_session(droid.uid) 530 ed = event_dispatcher.EventDispatcher(event_droid) 531 self._event_dispatchers[ed_key] = ed 532 return ed 533 534 def _is_timestamp_in_range(self, target, begin_time, end_time): 535 low = acts_logger.logline_timestamp_comparator(begin_time, target) <= 0 536 high = acts_logger.logline_timestamp_comparator(end_time, target) >= 0 537 return low and high 538 539 def cat_adb_log(self, tag, begin_time): 540 """Takes an excerpt of the adb logcat log from a certain time point to 541 current time. 542 543 Args: 544 tag: An identifier of the time period, usualy the name of a test. 545 begin_time: Logline format timestamp of the beginning of the time 546 period. 547 """ 548 if not self.adb_logcat_file_path: 549 raise AndroidDeviceError(("Attempting to cat adb log when none has" 550 " been collected on Android device %s." 551 ) % self.serial) 552 end_time = acts_logger.get_log_line_timestamp() 553 self.log.debug("Extracting adb log from logcat.") 554 adb_excerpt_path = os.path.join(self.log_path, "AdbLogExcerpts") 555 utils.create_dir(adb_excerpt_path) 556 f_name = os.path.basename(self.adb_logcat_file_path) 557 out_name = f_name.replace("adblog,", "").replace(".txt", "") 558 out_name = ",{},{}.txt".format(begin_time, out_name) 559 tag_len = utils.MAX_FILENAME_LEN - len(out_name) 560 tag = tag[:tag_len] 561 out_name = tag + out_name 562 full_adblog_path = os.path.join(adb_excerpt_path, out_name) 563 with open(full_adblog_path, 'w', encoding='utf-8') as out: 564 in_file = self.adb_logcat_file_path 565 with open(in_file, 'r', encoding='utf-8', errors='replace') as f: 566 in_range = False 567 while True: 568 line = None 569 try: 570 line = f.readline() 571 if not line: 572 break 573 except: 574 continue 575 line_time = line[:acts_logger.log_line_timestamp_len] 576 if not acts_logger.is_valid_logline_timestamp(line_time): 577 continue 578 if self._is_timestamp_in_range(line_time, begin_time, 579 end_time): 580 in_range = True 581 if not line.endswith('\n'): 582 line += '\n' 583 out.write(line) 584 else: 585 if in_range: 586 break 587 588 def start_adb_logcat(self): 589 """Starts a standing adb logcat collection in separate subprocesses and 590 save the logcat in a file. 591 """ 592 if self.is_adb_logcat_on: 593 raise AndroidDeviceError(("Android device {} already has an adb " 594 "logcat thread going on. Cannot start " 595 "another one.").format(self.serial)) 596 # Disable adb log spam filter. 597 self.adb.shell("logpersist.start") 598 f_name = "adblog,{},{}.txt".format(self.model, self.serial) 599 utils.create_dir(self.log_path) 600 logcat_file_path = os.path.join(self.log_path, f_name) 601 try: 602 extra_params = self.adb_logcat_param 603 except AttributeError: 604 extra_params = "" 605 cmd = "adb -s {} logcat -v threadtime {} >> {}".format( 606 self.serial, extra_params, logcat_file_path) 607 self.adb_logcat_process = utils.start_standing_subprocess(cmd) 608 self.adb_logcat_file_path = logcat_file_path 609 610 def stop_adb_logcat(self): 611 """Stops the adb logcat collection subprocess. 612 """ 613 if not self.is_adb_logcat_on: 614 raise AndroidDeviceError(("Android device {} does not have an " 615 "ongoing adb logcat collection." 616 ).format(self.serial)) 617 utils.stop_standing_subprocess(self.adb_logcat_process) 618 self.adb_logcat_process = None 619 620 def take_bug_report(self, test_name, begin_time): 621 """Takes a bug report on the device and stores it in a file. 622 623 Args: 624 test_name: Name of the test case that triggered this bug report. 625 begin_time: Logline format timestamp taken when the test started. 626 """ 627 new_br = True 628 try: 629 self.adb.shell("bugreportz -v") 630 except adb.AdbError: 631 new_br = False 632 br_path = os.path.join(self.log_path, "BugReports") 633 utils.create_dir(br_path) 634 base_name = ",{},{}.txt".format(begin_time, self.serial) 635 if new_br: 636 base_name = base_name.replace(".txt", ".zip") 637 test_name_len = utils.MAX_FILENAME_LEN - len(base_name) 638 out_name = test_name[:test_name_len] + base_name 639 full_out_path = os.path.join(br_path, out_name.replace(' ', '\ ')) 640 # in case device restarted, wait for adb interface to return 641 self.wait_for_boot_completion() 642 self.log.info("Taking bugreport for %s.", test_name) 643 if new_br: 644 out = self.adb.shell("bugreportz").decode("utf-8") 645 if not out.startswith("OK"): 646 raise AndroidDeviceError("Failed to take bugreport on %s" % 647 self.serial) 648 br_out_path = out.split(':')[1].strip() 649 self.adb.pull("%s %s" % (br_out_path, full_out_path)) 650 else: 651 self.adb.bugreport(" > {}".format(full_out_path)) 652 self.log.info("Bugreport for %s taken at %s.", test_name, 653 full_out_path) 654 655 def start_new_session(self): 656 """Start a new session in sl4a. 657 658 Also caches the droid in a dict with its uid being the key. 659 660 Returns: 661 An Android object used to communicate with sl4a on the android 662 device. 663 664 Raises: 665 SL4AException: Something is wrong with sl4a and it returned an 666 existing uid to a new session. 667 """ 668 droid = android.Android(port=self.h_port) 669 if droid.uid in self._droid_sessions: 670 raise android.SL4AException(("SL4A returned an existing uid for a " 671 "new session. Abort.")) 672 self._droid_sessions[droid.uid] = [droid] 673 return droid 674 675 def add_new_connection_to_session(self, session_id): 676 """Create a new connection to an existing sl4a session. 677 678 Args: 679 session_id: UID of the sl4a session to add connection to. 680 681 Returns: 682 An Android object used to communicate with sl4a on the android 683 device. 684 685 Raises: 686 DoesNotExistError: Raised if the session it's trying to connect to 687 does not exist. 688 """ 689 if session_id not in self._droid_sessions: 690 raise DoesNotExistError("Session %d doesn't exist." % session_id) 691 droid = android.Android(cmd='continue', uid=session_id, 692 port=self.h_port) 693 return droid 694 695 def terminate_session(self, session_id): 696 """Terminate a session in sl4a. 697 698 Send terminate signal to sl4a server; stop dispatcher associated with 699 the session. Clear corresponding droids and dispatchers from cache. 700 701 Args: 702 session_id: UID of the sl4a session to terminate. 703 """ 704 if self._droid_sessions and (session_id in self._droid_sessions): 705 for droid in self._droid_sessions[session_id]: 706 droid.closeSl4aSession() 707 droid.close() 708 del self._droid_sessions[session_id] 709 ed_key = self.serial + str(session_id) 710 if ed_key in self._event_dispatchers: 711 self._event_dispatchers[ed_key].clean_up() 712 del self._event_dispatchers[ed_key] 713 714 def terminate_all_sessions(self): 715 """Terminate all sl4a sessions on the AndroidDevice instance. 716 717 Terminate all sessions and clear caches. 718 """ 719 if self._droid_sessions: 720 session_ids = list(self._droid_sessions.keys()) 721 for session_id in session_ids: 722 try: 723 self.terminate_session(session_id) 724 except: 725 msg = "Failed to terminate session %d." % session_id 726 self.log.exception(msg) 727 self.log.error(traceback.format_exc()) 728 if self.h_port: 729 self.adb.forward("--remove tcp:%d" % self.h_port) 730 self.h_port = None 731 732 def run_iperf_client(self, server_host, extra_args=""): 733 """Start iperf client on the device. 734 735 Return status as true if iperf client start successfully. 736 And data flow information as results. 737 738 Args: 739 server_host: Address of the iperf server. 740 extra_args: A string representing extra arguments for iperf client, 741 e.g. "-i 1 -t 30". 742 743 Returns: 744 status: true if iperf client start successfully. 745 results: results have data flow information 746 """ 747 out = self.adb.shell("iperf3 -c {} {}".format(server_host, extra_args)) 748 clean_out = str(out,'utf-8').strip().split('\n') 749 if "error" in clean_out[0].lower(): 750 return False, clean_out 751 return True, clean_out 752 753 @utils.timeout(15 * 60) 754 def wait_for_boot_completion(self): 755 """Waits for the Android framework to boot back up and ready to launch 756 apps. 757 758 This function times out after 15 minutes. 759 """ 760 self.adb.wait_for_device() 761 while True: 762 try: 763 out = self.adb.shell("getprop sys.boot_completed") 764 completed = out.decode('utf-8').strip() 765 if completed == '1': 766 return 767 except adb.AdbError: 768 # adb shell calls may fail during certain period of booting 769 # process, which is normal. Ignoring these errors. 770 pass 771 time.sleep(5) 772 773 def reboot(self): 774 """Reboots the device. 775 776 Terminate all sl4a sessions, reboot the device, wait for device to 777 complete booting, and restart an sl4a session. 778 779 This is a blocking method. 780 781 This is probably going to print some error messages in console. Only 782 use if there's no other option. 783 784 Example: 785 droid, ed = ad.reboot() 786 787 Returns: 788 An sl4a session with an event_dispatcher. 789 790 Raises: 791 AndroidDeviceError is raised if waiting for completion timed 792 out. 793 """ 794 if self.is_bootloader: 795 self.fastboot.reboot() 796 return 797 has_adb_log = self.is_adb_logcat_on 798 if has_adb_log: 799 self.stop_adb_logcat() 800 self.terminate_all_sessions() 801 self.adb.reboot() 802 self.wait_for_boot_completion() 803 self.root_adb() 804 droid, ed = self.get_droid() 805 ed.start() 806 if has_adb_log: 807 self.start_adb_logcat() 808 return droid, ed 809