1# /usr/bin/env python3.4 2# 3# Copyright (C) 2018 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); you may not 6# use this file except in compliance with the License. You may obtain a copy of 7# 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, WITHOUT 13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14# License for the specific language governing permissions and limitations under 15# the License 16 17import json 18import logging 19import math 20import os 21import re 22import subprocess 23import time 24import xlsxwriter 25 26from acts.controllers.ap_lib import hostapd_config 27from acts.controllers.ap_lib import hostapd_constants 28from acts.controllers.ap_lib import hostapd_security 29from acts.test_utils.bt.bt_constants import \ 30 bluetooth_profile_connection_state_changed 31from acts.test_utils.bt.bt_constants import bt_default_timeout 32from acts.test_utils.bt.bt_constants import bt_profile_constants 33from acts.test_utils.bt.bt_constants import bt_profile_states 34from acts.test_utils.bt.bt_constants import bt_scan_mode_types 35from acts.test_utils.bt.bt_gatt_utils import GattTestUtilsError 36from acts.test_utils.bt.bt_gatt_utils import orchestrate_gatt_connection 37from acts.test_utils.bt.bt_test_utils import disable_bluetooth 38from acts.test_utils.bt.bt_test_utils import enable_bluetooth 39from acts.test_utils.bt.bt_test_utils import is_a2dp_src_device_connected 40from acts.test_utils.bt.bt_test_utils import is_a2dp_snk_device_connected 41from acts.test_utils.bt.bt_test_utils import is_hfp_client_device_connected 42from acts.test_utils.bt.bt_test_utils import is_map_mce_device_connected 43from acts.test_utils.bt.bt_test_utils import is_map_mse_device_connected 44from acts.test_utils.car.car_telecom_utils import wait_for_active 45from acts.test_utils.car.car_telecom_utils import wait_for_dialing 46from acts.test_utils.car.car_telecom_utils import wait_for_not_in_call 47from acts.test_utils.car.car_telecom_utils import wait_for_ringing 48from acts.test_utils.tel.tel_test_utils import get_phone_number 49from acts.test_utils.tel.tel_test_utils import hangup_call 50from acts.test_utils.tel.tel_test_utils import initiate_call 51from acts.test_utils.tel.tel_test_utils import run_multithread_func 52from acts.test_utils.tel.tel_test_utils import setup_droid_properties 53from acts.test_utils.tel.tel_test_utils import wait_and_answer_call 54from acts.test_utils.wifi.wifi_test_utils import reset_wifi 55from acts.test_utils.wifi.wifi_test_utils import wifi_connect 56from acts.test_utils.wifi.wifi_test_utils import wifi_test_device_init 57from acts.test_utils.wifi.wifi_test_utils import wifi_toggle_state 58from acts.utils import exe_cmd, create_dir 59 60THROUGHPUT_THRESHOLD = 100 61AP_START_TIME = 10 62DISCOVERY_TIME = 10 63BLUETOOTH_WAIT_TIME = 2 64 65 66def a2dp_dumpsys_parser(file_path): 67 """Convenience function to parse a2dp dumpsys logs. 68 69 Args: 70 file_path: Path of dumpsys logs. 71 72 Returns: 73 True if parsing is successful, False otherwise. 74 """ 75 a2dp_dumpsys_info = [] 76 with open(file_path) as dumpsys_file: 77 for line in dumpsys_file: 78 if "A2DP State:" in line: 79 a2dp_dumpsys_info.append(line) 80 elif "Counts (max dropped)" not in line and len( 81 a2dp_dumpsys_info) > 0: 82 a2dp_dumpsys_info.append(line) 83 elif "Counts (max dropped)" in line: 84 a2dp_dumpsys_info = ''.join(a2dp_dumpsys_info) 85 logging.info(a2dp_dumpsys_info) 86 return True 87 logging.error("failed to get A2DP state") 88 return False 89 90 91def connect_ble(pri_ad, sec_ad): 92 """Connect BLE device from DUT. 93 94 Args: 95 pri_ad: An android device object. 96 sec_ad: An android device object. 97 98 Returns: 99 True if successful, otherwise False. 100 """ 101 adv_instances = [] 102 gatt_server_list = [] 103 bluetooth_gatt_list = [] 104 pri_ad.droid.bluetoothEnableBLE() 105 gatt_server_cb = sec_ad.droid.gattServerCreateGattServerCallback() 106 gatt_server = sec_ad.droid.gattServerOpenGattServer(gatt_server_cb) 107 gatt_server_list.append(gatt_server) 108 try: 109 bluetooth_gatt, gatt_callback, adv_callback = ( 110 orchestrate_gatt_connection(pri_ad, sec_ad)) 111 bluetooth_gatt_list.append(bluetooth_gatt) 112 except GattTestUtilsError as err: 113 logging.error(err) 114 return False 115 adv_instances.append(adv_callback) 116 connected_devices = sec_ad.droid.gattServerGetConnectedDevices(gatt_server) 117 logging.debug("Connected device = {}".format(connected_devices)) 118 return True 119 120 121def collect_bluetooth_manager_dumpsys_logs(pri_ad): 122 """Collect "adb shell dumpsys bluetooth_manager" logs. 123 124 Args: 125 pri_ad : An android device. 126 127 Returns: 128 True if dumpsys is successful, False otherwise. 129 """ 130 out_file = "{}_{}".format(pri_ad.serial, "bluetooth_dumpsys.txt") 131 dumpsys_path = ''.join((pri_ad.log_path, "/BluetoothDumpsys")) 132 create_dir(dumpsys_path) 133 cmd = ''.join("adb -s {} shell dumpsys bluetooth_manager > {}/{}".format( 134 pri_ad.serial, dumpsys_path, out_file)) 135 exe_cmd(cmd) 136 file_path = "{}/{}".format(dumpsys_path, out_file) 137 if not a2dp_dumpsys_parser(file_path): 138 logging.error("Could not parse dumpsys logs") 139 return False 140 return True 141 142 143def configure_and_start_ap(ap, network): 144 """Configure hostapd parameters and starts access point. 145 146 Args: 147 ap: An access point object. 148 network: A dictionary with wifi network details. 149 """ 150 hostapd_sec = hostapd_security.Security( 151 security_mode=network["security"], password=network["password"]) 152 153 config = hostapd_config.HostapdConfig( 154 n_capabilities=[hostapd_constants.N_CAPABILITY_HT40_MINUS], 155 mode=hostapd_constants.MODE_11N_PURE, 156 channel=network["channel"], 157 ssid=network["SSID"], 158 security=hostapd_sec) 159 ap.start_ap(config) 160 time.sleep(AP_START_TIME) 161 162 163def connect_dev_to_headset(pri_droid, dev_to_connect, profiles_set): 164 supported_profiles = bt_profile_constants.values() 165 for profile in profiles_set: 166 if profile not in supported_profiles: 167 pri_droid.log.info("Profile {} is not supported list {}".format( 168 profile, supported_profiles)) 169 return False 170 171 paired = False 172 for paired_device in pri_droid.droid.bluetoothGetBondedDevices(): 173 if paired_device['address'] == \ 174 dev_to_connect: 175 paired = True 176 break 177 178 if not paired: 179 pri_droid.log.info("{} not paired to {}".format( 180 pri_droid.droid.getBuildSerial(), dev_to_connect)) 181 return False 182 183 pri_droid.droid.bluetoothConnectBonded(dev_to_connect) 184 185 end_time = time.time() + 10 186 profile_connected = set() 187 sec_addr = dev_to_connect 188 logging.info("Profiles to be connected {}".format(profiles_set)) 189 190 while (time.time() < end_time and 191 not profile_connected.issuperset(profiles_set)): 192 if (bt_profile_constants['headset_client'] not in profile_connected and 193 bt_profile_constants['headset_client'] in profiles_set): 194 if is_hfp_client_device_connected(pri_droid, sec_addr): 195 profile_connected.add(bt_profile_constants['headset_client']) 196 if (bt_profile_constants['headset'] not in profile_connected and 197 bt_profile_constants['headset'] in profiles_set): 198 profile_connected.add(bt_profile_constants['headset']) 199 if (bt_profile_constants['a2dp'] not in profile_connected and 200 bt_profile_constants['a2dp'] in profiles_set): 201 if is_a2dp_src_device_connected(pri_droid, sec_addr): 202 profile_connected.add(bt_profile_constants['a2dp']) 203 if (bt_profile_constants['a2dp_sink'] not in profile_connected and 204 bt_profile_constants['a2dp_sink'] in profiles_set): 205 if is_a2dp_snk_device_connected(pri_droid, sec_addr): 206 profile_connected.add(bt_profile_constants['a2dp_sink']) 207 if (bt_profile_constants['map_mce'] not in profile_connected and 208 bt_profile_constants['map_mce'] in profiles_set): 209 if is_map_mce_device_connected(pri_droid, sec_addr): 210 profile_connected.add(bt_profile_constants['map_mce']) 211 if (bt_profile_constants['map'] not in profile_connected and 212 bt_profile_constants['map'] in profiles_set): 213 if is_map_mse_device_connected(pri_droid, sec_addr): 214 profile_connected.add(bt_profile_constants['map']) 215 time.sleep(0.1) 216 217 while not profile_connected.issuperset(profiles_set): 218 try: 219 time.sleep(10) 220 profile_event = pri_droid.ed.pop_event( 221 bluetooth_profile_connection_state_changed, 222 bt_default_timeout + 10) 223 logging.info("Got event {}".format(profile_event)) 224 except Exception: 225 logging.error("Did not get {} profiles left {}".format( 226 bluetooth_profile_connection_state_changed, profile_connected)) 227 return False 228 profile = profile_event['data']['profile'] 229 state = profile_event['data']['state'] 230 device_addr = profile_event['data']['addr'] 231 if state == bt_profile_states['connected'] and \ 232 device_addr == dev_to_connect: 233 profile_connected.add(profile) 234 logging.info("Profiles connected until now {}".format( 235 profile_connected)) 236 return True 237 238 239def device_discoverable(pri_ad, sec_ad): 240 """Verifies whether the device is discoverable or not. 241 242 Args: 243 pri_ad: An primary android device object. 244 sec_ad: An secondary android device object. 245 246 Returns: 247 True if the device found, False otherwise. 248 """ 249 pri_ad.droid.bluetoothMakeDiscoverable() 250 scan_mode = pri_ad.droid.bluetoothGetScanMode() 251 if scan_mode == bt_scan_mode_types['connectable_discoverable']: 252 logging.info("Primary device scan mode is " 253 "SCAN_MODE_CONNECTABLE_DISCOVERABLE.") 254 else: 255 logging.info("Primary device scan mode is not " 256 "SCAN_MODE_CONNECTABLE_DISCOVERABLE.") 257 return False 258 if sec_ad.droid.bluetoothStartDiscovery(): 259 time.sleep(DISCOVERY_TIME) 260 droid_name = pri_ad.droid.bluetoothGetLocalName() 261 get_discovered_devices = sec_ad.droid.bluetoothGetDiscoveredDevices() 262 find_flag = False 263 264 if get_discovered_devices: 265 for device in get_discovered_devices: 266 if 'name' in device and device['name'] == droid_name: 267 logging.info("Primary device is in the discovery " 268 "list of secondary device.") 269 find_flag = True 270 break 271 else: 272 logging.info("Secondary device get all the discovered devices " 273 "list is empty") 274 return False 275 else: 276 logging.info("Secondary device start discovery process error.") 277 return False 278 if not find_flag: 279 return False 280 return True 281 282 283def disconnect_headset_from_dev(pri_ad, sec_ad, profiles_list): 284 """ 285 Disconnect primary from secondary on a specific set of profiles 286 Args: 287 pri_ad - Primary android_device initiating disconnection 288 sec_ad - Secondary android droid (sl4a interface to keep the 289 method signature the same connect_pri_to_sec above) 290 profiles_list - List of profiles we want to disconnect from 291 292 Returns: 293 True on Success 294 False on Failure 295 """ 296 supported_profiles = bt_profile_constants.values() 297 for profile in profiles_list: 298 if profile not in supported_profiles: 299 pri_ad.log.info("Profile {} is not in supported list {}".format( 300 profile, supported_profiles)) 301 return False 302 303 pri_ad.log.info(pri_ad.droid.bluetoothGetBondedDevices()) 304 305 try: 306 pri_ad.droid.bluetoothDisconnectConnectedProfile(sec_ad, profiles_list) 307 except Exception as err: 308 pri_ad.log.error( 309 "Exception while trying to disconnect profile(s) {}: {}".format( 310 profiles_list, err)) 311 return False 312 313 profile_disconnected = set() 314 pri_ad.log.info("Disconnecting from profiles: {}".format(profiles_list)) 315 316 while not profile_disconnected.issuperset(profiles_list): 317 try: 318 profile_event = pri_ad.ed.pop_event( 319 bluetooth_profile_connection_state_changed, bt_default_timeout) 320 pri_ad.log.info("Got event {}".format(profile_event)) 321 except Exception: 322 pri_ad.log.error("Did not disconnect from Profiles") 323 return False 324 325 profile = profile_event['data']['profile'] 326 state = profile_event['data']['state'] 327 device_addr = profile_event['data']['addr'] 328 329 if state == bt_profile_states['disconnected'] and \ 330 device_addr == sec_ad: 331 profile_disconnected.add(profile) 332 pri_ad.log.info("Profiles disconnected so far {}".format( 333 profile_disconnected)) 334 335 return True 336 337 338def initiate_disconnect_from_hf(audio_receiver, pri_ad, sec_ad, duration): 339 """Initiates call and disconnect call on primary device. 340 Steps: 341 1. Initiate call from HF. 342 2. Wait for dialing state at DUT and wait for ringing at secondary device. 343 3. Accepts call from secondary device. 344 4. Wait for call active state at primary and secondary device. 345 5. Sleeps until given duration. 346 6. Disconnect call from primary device. 347 7. Wait for call is not present state. 348 349 Args: 350 pri_ad: An android device to disconnect call. 351 sec_ad: An android device accepting call. 352 duration: Duration of call in seconds. 353 354 Returns: 355 True if successful, False otherwise. 356 """ 357 audio_receiver.initiate_call_from_hf() 358 time.sleep(2) 359 flag = True 360 flag &= wait_for_dialing(logging, pri_ad) 361 flag &= wait_for_ringing(logging, sec_ad) 362 if not flag: 363 logging.error("Outgoing call did not get established") 364 return False 365 366 if not wait_and_answer_call(logging, sec_ad): 367 logging.error("Failed to answer call in second device.") 368 return False 369 if not wait_for_active(logging, pri_ad): 370 logging.error("AG not in Active state.") 371 return False 372 if not wait_for_active(logging, sec_ad): 373 logging.error("RE not in Active state.") 374 return False 375 time.sleep(duration) 376 if not hangup_call(logging, pri_ad): 377 logging.error("Failed to hangup call.") 378 return False 379 flag = True 380 flag &= wait_for_not_in_call(logging, pri_ad) 381 flag &= wait_for_not_in_call(logging, sec_ad) 382 return flag 383 384 385def initiate_disconnect_call_dut(pri_ad, sec_ad, duration, callee_number): 386 """Initiates call and disconnect call on primary device. 387 Steps: 388 1. Initiate call from DUT. 389 2. Wait for dialing state at DUT and wait for ringing at secondary device. 390 3. Accepts call from secondary device. 391 4. Wait for call active state at primary and secondary device. 392 5. Sleeps until given duration. 393 6. Disconnect call from primary device. 394 7. Wait for call is not present state. 395 396 Args: 397 pri_ad: An android device to disconnect call. 398 sec_ad: An android device accepting call. 399 duration: Duration of call in seconds. 400 callee_number: Secondary device's phone number. 401 402 Returns: 403 True if successful, False otherwise. 404 """ 405 if not initiate_call(logging, pri_ad, callee_number): 406 logging.error("Failed to initiate call") 407 return False 408 time.sleep(2) 409 410 flag = True 411 flag &= wait_for_dialing(logging, pri_ad) 412 flag &= wait_for_ringing(logging, sec_ad) 413 if not flag: 414 logging.error("Outgoing call did not get established") 415 return False 416 417 if not wait_and_answer_call(logging, sec_ad): 418 logging.error("Failed to answer call in second device.") 419 return False 420 # Wait for AG, RE to go into an Active state. 421 if not wait_for_active(logging, pri_ad): 422 logging.error("AG not in Active state.") 423 return False 424 if not wait_for_active(logging, sec_ad): 425 logging.error("RE not in Active state.") 426 return False 427 time.sleep(duration) 428 if not hangup_call(logging, pri_ad): 429 logging.error("Failed to hangup call.") 430 return False 431 flag = True 432 flag &= wait_for_not_in_call(logging, pri_ad) 433 flag &= wait_for_not_in_call(logging, sec_ad) 434 435 return flag 436 437 438def iperf_result(result, stream): 439 """Accepts the iperf result in json format and parse the output to 440 get throughput value. 441 442 Args: 443 result: contains the logs of iperf in json format. 444 stream: string to indicate uplink/downlink traffic. 445 446 Returns: 447 tx_rate: Data sent from client. 448 rx_rate: Data received from client. 449 """ 450 try: 451 with open(result, 'r') as iperf_data: 452 time.sleep(1) 453 json_data = json.load(iperf_data) 454 except ValueError: 455 with open(result, 'r') as iperf_data: 456 # Possibly a result from interrupted iperf run, skip first line 457 # and try again. 458 time.sleep(1) 459 lines = iperf_data.readlines()[1:] 460 json_data = json.loads(''.join(lines)) 461 462 if "error" in json_data: 463 logging.error(json_data["error"]) 464 return False 465 466 protocol = json_data["start"]["test_start"]["protocol"] 467 if protocol == "UDP": 468 if "intervals" in json_data.keys(): 469 interval = [ 470 interval["sum"]["bits_per_second"] / 1e6 471 for interval in json_data["intervals"] 472 ] 473 tx_rate = math.fsum(interval) / len(interval) 474 else: 475 logging.error("Unable to retrive client results") 476 return False 477 if "server_output_json" in json_data.keys(): 478 interval = [ 479 interval["sum"]["bits_per_second"] / 1e6 480 for interval in json_data["server_output_json"]["intervals"] 481 ] 482 rx_rate = math.fsum(interval) / len(interval) 483 else: 484 logging.info("unable to retrive server results") 485 return False 486 if not stream == "ul": 487 return tx_rate, rx_rate 488 return rx_rate, tx_rate 489 490 elif protocol == "TCP": 491 tx_rate = json_data['end']['sum_sent']['bits_per_second'] 492 rx_rate = json_data['end']['sum_received']['bits_per_second'] 493 return tx_rate / 1e6, rx_rate / 1e6 494 else: 495 return False 496 497 498def is_a2dp_connected(pri_ad, headset_mac_address): 499 """Convenience Function to see if the 2 devices are connected on A2DP. 500 501 Args: 502 pri_ad : An android device. 503 headset_mac_address : Mac address of headset. 504 505 Returns: 506 True:If A2DP connection exists, False otherwise. 507 """ 508 devices = pri_ad.droid.bluetoothA2dpGetConnectedDevices() 509 for device in devices: 510 logging.debug("A2dp Connected device {}".format(device["name"])) 511 if device["address"] == headset_mac_address: 512 return True 513 return False 514 515 516def media_stream_check(pri_ad, duration, headset_mac_address): 517 """Checks whether A2DP connecion is active or not for given duration of 518 time. 519 520 Args: 521 pri_ad : An android device. 522 duration : No of seconds to check if a2dp streaming is alive. 523 headset_mac_address : Headset mac address. 524 525 Returns: 526 True: If A2dp connection is active for entire duration. 527 False: If A2dp connection is not active. 528 """ 529 while time.time() < duration: 530 if not is_a2dp_connected(pri_ad, headset_mac_address): 531 logging.error("A2dp connection not active at {}".format( 532 pri_ad.droid.getBuildSerial())) 533 return False 534 time.sleep(1) 535 return True 536 537 538def multithread_func(log, tasks): 539 """Multi-thread function wrapper. 540 541 Args: 542 log: log object. 543 tasks: tasks to be executed in parallel. 544 545 Returns: 546 List of results of tasks 547 """ 548 results = run_multithread_func(log, tasks) 549 for res in results: 550 if not res: 551 return False 552 return True 553 554 555def music_play_and_check(pri_ad, headset_mac_address, music_to_play, duration): 556 """Starts playing media and checks if media plays for n seconds. 557 558 Steps: 559 1. Starts media player on android device. 560 2. Checks if music streaming is ongoing for n seconds. 561 3. Stops media player. 562 4. Collect dumpsys logs. 563 564 Args: 565 pri_ad: An android device. 566 headset_mac_address: Mac address of third party headset. 567 music_to_play: Indicates the music file to play. 568 duration: Time in secs to indicate music time to play. 569 570 Returns: 571 True if successful, False otherwise. 572 """ 573 if not start_media_play(pri_ad, music_to_play): 574 logging.error("Start media play failed.") 575 return False 576 stream_time = time.time() + duration 577 if not media_stream_check(pri_ad, stream_time, headset_mac_address): 578 logging.error("A2DP Connection check failed.") 579 pri_ad.droid.mediaPlayStop() 580 return False 581 pri_ad.droid.mediaPlayStop() 582 if not collect_bluetooth_manager_dumpsys_logs(pri_ad): 583 return False 584 return True 585 586 587def music_play_and_check_via_app(pri_ad, headset_mac_address): 588 """Starts google music player and check for A2DP connection. 589 590 Steps: 591 1. Starts Google music player on android device. 592 2. Checks for A2DP connection. 593 594 Args: 595 pri_ad: An android device. 596 headset_mac_address: Mac address of third party headset. 597 598 Returns: 599 True if successful, False otherwise. 600 """ 601 pri_ad.adb.shell("am start com.google.android.music") 602 time.sleep(3) 603 pri_ad.adb.shell("input keyevent 85") 604 605 if not is_a2dp_connected(pri_ad, headset_mac_address): 606 logging.error("A2dp connection not active at {}".format( 607 pri_ad.droid.getBuildSerial())) 608 return False 609 return True 610 611 612def get_phone_ip(ad): 613 """Get the WiFi IP address of the phone. 614 615 Args: 616 ad: the android device under test 617 618 Returns: 619 Ip address of the phone for WiFi, as a string 620 """ 621 return ad.droid.connectivityGetIPv4Addresses('wlan0')[0] 622 623 624def pair_dev_to_headset(pri_ad, dev_to_pair): 625 626 bonded_devices = pri_ad.droid.bluetoothGetBondedDevices() 627 for d in bonded_devices: 628 if d['address'] == dev_to_pair: 629 pri_ad.log.info("Successfully bonded to device".format( 630 dev_to_pair)) 631 return True 632 pri_ad.droid.bluetoothStartDiscovery() 633 time.sleep(10) 634 pri_ad.droid.bluetoothCancelDiscovery() 635 logging.info("disovered devices = {}".format( 636 pri_ad.droid.bluetoothGetDiscoveredDevices())) 637 for device in pri_ad.droid.bluetoothGetDiscoveredDevices(): 638 if device['address'] == dev_to_pair: 639 640 result = pri_ad.droid.bluetoothDiscoverAndBond(dev_to_pair) 641 pri_ad.log.info(result) 642 end_time = time.time() + bt_default_timeout 643 pri_ad.log.info("Verifying devices are bonded") 644 time.sleep(5) 645 while time.time() < end_time: 646 bonded_devices = pri_ad.droid.bluetoothGetBondedDevices() 647 bonded = False 648 for d in bonded_devices: 649 if d['address'] == dev_to_pair: 650 pri_ad.log.info("Successfully bonded to device".format( 651 dev_to_pair)) 652 return True 653 pri_ad.log.info("Failed to bond devices.") 654 return False 655 656def pair_and_connect_headset(pri_ad, headset_mac_address, profile_to_connect): 657 """Pair and connect android device with third party headset. 658 659 Args: 660 pri_ad: An android device. 661 headset_mac_address: Mac address of third party headset. 662 profile_to_connect: Profile to be connected with headset. 663 664 Returns: 665 True if pair and connect to headset successful, False otherwise. 666 """ 667 if not pair_dev_to_headset(pri_ad, headset_mac_address): 668 logging.error("Could not pair to headset.") 669 return False 670 # Wait until pairing gets over. 671 time.sleep(2) 672 if not connect_dev_to_headset(pri_ad, headset_mac_address, 673 profile_to_connect): 674 logging.error("Could not connect to headset.") 675 return False 676 return True 677 678 679def perform_classic_discovery(pri_ad): 680 """Convenience function to start and stop device discovery. 681 682 Args: 683 pri_ad: An android device. 684 685 Returns: 686 True start and stop discovery is successful, False otherwise. 687 """ 688 if not pri_ad.droid.bluetoothStartDiscovery(): 689 logging.info("Failed to start inquiry") 690 return False 691 time.sleep(DISCOVERY_TIME) 692 if not pri_ad.droid.bluetoothCancelDiscovery(): 693 logging.info("Failed to cancel inquiry") 694 return False 695 logging.info("Discovered device list {}".format( 696 pri_ad.droid.bluetoothGetDiscoveredDevices())) 697 return True 698 699 700def connect_wlan_profile(pri_ad, network): 701 """Disconnect and Connect to AP. 702 703 Args: 704 pri_ad: An android device. 705 network: Network to which AP to be connected. 706 707 Returns: 708 True if successful, False otherwise. 709 """ 710 reset_wifi(pri_ad) 711 wifi_toggle_state(pri_ad, False) 712 wifi_test_device_init(pri_ad) 713 wifi_connect(pri_ad, network) 714 if not wifi_connection_check(pri_ad, network["SSID"]): 715 logging.error("Wifi connection does not exist.") 716 return False 717 return True 718 719 720def toggle_bluetooth(pri_ad, iterations): 721 """Toggles bluetooth on/off for N iterations. 722 723 Args: 724 pri_ad: An android device object. 725 iterations: Number of iterations to run. 726 727 Returns: 728 True if successful, False otherwise. 729 """ 730 for i in range(iterations): 731 logging.info("Bluetooth Turn on/off iteration : {}".format(i)) 732 if not enable_bluetooth(pri_ad.droid, pri_ad.ed): 733 logging.error("Failed to enable bluetooth") 734 return False 735 time.sleep(BLUETOOTH_WAIT_TIME) 736 if not disable_bluetooth(pri_ad.droid): 737 logging.error("Failed to turn off bluetooth") 738 return False 739 time.sleep(BLUETOOTH_WAIT_TIME) 740 return True 741 742 743def toggle_screen_state(pri_ad, iterations): 744 """Toggles the screen state to on or off.. 745 746 Args: 747 pri_ad: Android device. 748 iterations: Number of times screen on/off should be performed. 749 750 Returns: 751 True if successful, False otherwise. 752 """ 753 for i in range(iterations): 754 if not pri_ad.ensure_screen_on(): 755 logging.error("User window cannot come up") 756 return False 757 if not pri_ad.go_to_sleep(): 758 logging.info("Screen off") 759 return True 760 761 762def setup_tel_config(pri_ad, sec_ad, sim_conf_file): 763 """Sets tel properties for primary device and secondary devices 764 765 Args: 766 pri_ad: An android device object. 767 sec_ad: An android device object. 768 sim_conf_file: Sim card map. 769 770 Returns: 771 pri_ad_num: Phone number of primary device. 772 sec_ad_num: Phone number of secondary device. 773 """ 774 setup_droid_properties(logging, pri_ad, sim_conf_file) 775 pri_ad_num = get_phone_number(logging, pri_ad) 776 setup_droid_properties(logging, sec_ad, sim_conf_file) 777 sec_ad_num = get_phone_number(logging, sec_ad) 778 return pri_ad_num, sec_ad_num 779 780 781def start_fping(pri_ad, duration): 782 """Starts fping to ping for DUT's ip address. 783 784 Steps: 785 1. Run fping command to check DUT's IP is alive or not. 786 787 Args: 788 pri_ad: An android device object. 789 duration: Duration of fping in seconds. 790 791 Returns: 792 True if successful, False otherwise. 793 """ 794 out_file_name = "{}".format("fping.txt") 795 full_out_path = os.path.join(pri_ad.log_path, out_file_name) 796 cmd = "fping {} -D -c {}".format(get_phone_ip(pri_ad), duration) 797 cmd = cmd.split() 798 with open(full_out_path, "w") as f: 799 subprocess.call(cmd, stdout=f) 800 f = open(full_out_path, "r") 801 for lines in f: 802 l = re.split("[:/=]", lines) 803 if l[len(l) - 1] != "0%": 804 logging.error("Packet drop observed") 805 return False 806 return True 807 808 809def start_media_play(pri_ad, music_file_to_play): 810 """Starts media player on device. 811 812 Args: 813 pri_ad : An android device. 814 music_file_to_play : An audio file to play. 815 816 Returns: 817 True:If media player start music, False otherwise. 818 """ 819 if not pri_ad.droid.mediaPlayOpen("file:///sdcard/Music/{}" 820 .format(music_file_to_play)): 821 logging.error("Failed to play music") 822 return False 823 824 pri_ad.droid.mediaPlaySetLooping(True) 825 logging.info("Music is now playing on device {}".format(pri_ad.serial)) 826 return True 827 828 829def throughput_pass_fail_check(throughput): 830 """Method to check if the throughput is above the defined 831 threshold. 832 833 Args: 834 throughput: Throughput value of test run. 835 836 Returns: 837 Pass if throughput is above threshold. 838 Fail if throughput is below threshold and Iperf Failed. 839 Empty if Iperf value is not present. 840 """ 841 iperf_result_list = [] 842 if len(throughput) != 0: 843 for i in range(len(throughput)): 844 if throughput[i] != 'Iperf Failed': 845 if float(throughput[i].split('Mb/s')[0]) > \ 846 THROUGHPUT_THRESHOLD: 847 iperf_result_list.append("PASS") 848 else: 849 iperf_result_list.append("FAIL") 850 elif throughput[i] == 'Iperf Failed': 851 iperf_result_list.append("FAIL") 852 return "FAIL" if "FAIL" or "Iperf Failed" in iperf_result_list \ 853 else "PASS" 854 else: 855 return " " 856 857 858def wifi_connection_check(pri_ad, ssid): 859 """Function to check existence of wifi connection. 860 861 Args: 862 pri_ad : An android device. 863 ssid : wifi ssid to check. 864 865 Returns: 866 True if wifi connection exists, False otherwise. 867 """ 868 wifi_info = pri_ad.droid.wifiGetConnectionInfo() 869 if (wifi_info["SSID"] == ssid and 870 wifi_info["supplicant_state"] == "completed"): 871 return True 872 logging.error("Wifi Connection check failed : {}".format(wifi_info)) 873 return False 874 875 876def xlsheet(pri_ad, test_result, attenuation_range=range(0, 1, 1)): 877 """Parses the output and writes in spreadsheet. 878 879 Args: 880 pri_ad: An android device. 881 test_result: final output of testrun. 882 attenuation_range: list of attenuation values. 883 """ 884 r = 0 885 c = 0 886 i = 0 887 test_result = json.loads(test_result) 888 file_name = '/'.join((pri_ad.log_path, 889 test_result["Results"][0]["Test Class"] + ".xlsx")) 890 workbook = xlsxwriter.Workbook(file_name) 891 worksheet = workbook.add_worksheet() 892 wb_format = workbook.add_format() 893 wb_format.set_text_wrap() 894 worksheet.set_column('A:A', 50) 895 worksheet.set_column('B:B', 10) 896 wb_format = workbook.add_format({'bold': True}) 897 worksheet.write(r, c, "Test_case_name", wb_format) 898 worksheet.write(r, c + 1, "Result", wb_format) 899 if len(attenuation_range) > 1: 900 for idx in attenuation_range: 901 c += 1 902 worksheet.write(r, c + 1, str(idx) + "db", wb_format) 903 else: 904 worksheet.write(r, c + 2, "Iperf_throughput", wb_format) 905 c += 1 906 907 worksheet.write(r, (c + 2), "Throughput_Result", wb_format) 908 result = [(i["Test Name"], i["Result"], (i["Extras"]), 909 throughput_pass_fail_check(i["Extras"])) 910 for i in test_result["Results"]] 911 912 for row, line in enumerate(result): 913 for col, cell in enumerate(line): 914 if isinstance(cell, list): 915 for i in range(len(cell)): 916 worksheet.write(row + 1, col, cell[i]) 917 col += 1 918 else: 919 worksheet.write(row + 1, col + i, cell) 920