1#!/usr/bin/env python3 2# 3# Copyright (C) 2019 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"""Stream music through connected device from phone across different 17attenuations.""" 18 19import json 20import math 21import time 22import acts.controllers.iperf_client as ipc 23import acts.controllers.iperf_server as ipf 24import acts_contrib.test_utils.bt.bt_test_utils as btutils 25from acts import asserts 26from acts_contrib.test_utils.bt.A2dpBaseTest import A2dpBaseTest 27from acts_contrib.test_utils.bt.loggers import bluetooth_metric_logger as log 28from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wpeutils 29from acts_contrib.test_utils.wifi import wifi_power_test_utils as wputils 30from acts_contrib.test_utils.wifi import wifi_test_utils as wutils 31from acts_contrib.test_utils.power.PowerBaseTest import ObjNew 32 33MAX_ATTENUATION = 95 34TEMP_FILE = '/sdcard/Download/tmp.log' 35IPERF_CLIENT_ERROR = 'the client has unexpectedly closed the connection' 36 37 38def setup_ap_connection(dut, network, ap, bandwidth=20): 39 """Setup AP and connect DUT to it. 40 41 Args: 42 dut: the android device to connect and run traffic 43 network: the network config for the AP to be setup 44 ap: access point object 45 bandwidth: bandwidth of the WiFi network to be setup 46 Returns: 47 self.brconfigs: dict for bridge interface configs 48 """ 49 wutils.wifi_toggle_state(dut, True) 50 brconfigs = wputils.ap_setup(ap, network, bandwidth=bandwidth) 51 wutils.wifi_connect(dut, network, num_of_tries=3) 52 return brconfigs 53 54 55def start_iperf_client(traffic_pair_obj, duration): 56 """Setup iperf traffic for TCP downlink. 57 Args: 58 traffic_pair_obj: obj to contain info on traffic pair 59 duration: duration of iperf traffic to run 60 """ 61 # Construct the iperf command based on the test params 62 iperf_cmd = 'iperf3 -c {} -i 1 -t {} -p {} -J -R > {}'.format( 63 traffic_pair_obj.server_address, duration, 64 traffic_pair_obj.iperf_server.port, TEMP_FILE) 65 # Start IPERF client 66 traffic_pair_obj.dut.adb.shell_nb(iperf_cmd) 67 68 69def unpack_custom_file(file): 70 """Unpack the json file to . 71 72 Args: 73 file: custom json file. 74 """ 75 with open(file, 'r') as f: 76 params = json.load(f) 77 return params 78 79 80def get_iperf_results(iperf_server_obj): 81 """Get the iperf results and process. 82 83 Args: 84 iperf_server_obj: the IperfServer object 85 Returns: 86 throughput: the average throughput during tests. 87 """ 88 # Get IPERF results and add this to the plot title 89 iperf_file = iperf_server_obj.log_files[-1] 90 try: 91 iperf_result = ipf.IPerfResult(iperf_file) 92 # Compute the throughput in Mbit/s 93 if iperf_result.error == IPERF_CLIENT_ERROR: 94 rates = [] 95 for item in iperf_result.result['intervals']: 96 rates.append(item['sum']['bits_per_second'] / 8 / 1024 / 1024) 97 throughput = ((math.fsum(rates) / len(rates))) * 8 * (1.024**2) 98 else: 99 throughput = (math.fsum(iperf_result.instantaneous_rates) / len( 100 iperf_result.instantaneous_rates)) * 8 * (1.024**2) 101 except (ValueError, TypeError): 102 throughput = 0 103 return throughput 104 105 106class BtInterferenceBaseTest(A2dpBaseTest): 107 def __init__(self, configs): 108 super().__init__(configs) 109 self.bt_logger = log.BluetoothMetricLogger.for_test_case() 110 self.start_time = time.time() 111 req_params = [ 112 'attenuation_vector', 'wifi_networks', 'codecs', 'custom_files', 113 'audio_params' 114 ] 115 self.unpack_userparams(req_params) 116 for file in self.custom_files: 117 if 'static_interference' in file: 118 self.static_wifi_interference = unpack_custom_file(file) 119 elif 'dynamic_interference' in file: 120 self.dynamic_wifi_interference = unpack_custom_file(file) 121 122 def setup_class(self): 123 super().setup_class() 124 # Build object to store all necessary information for each pair of wifi 125 # interference setup: phone, ap, network, channel, iperf server port/ip 126 # object and bridge interface configs 127 if len(self.android_devices) < 5 or len(self.attenuators) < 4: 128 self.log.error('Need a 4 channel attenuator and 5 android phones' 129 'please update the config file') 130 self.wifi_int_pairs = [] 131 for i in range(len(self.attenuators) - 1): 132 tmp_dict = { 133 'dut': self.android_devices[i + 1], 134 'ap': self.access_points[i], 135 'network': self.wifi_networks[i], 136 'channel': self.wifi_networks[i]['channel'], 137 'iperf_server': self.iperf_servers[i], 138 'attenuator': self.attenuators[i + 1], 139 'ether_int': self.packet_senders[i], 140 'iperf_client': 141 ipc.IPerfClientOverAdb(self.android_devices[i + 1]) 142 } 143 tmp_obj = ObjNew(**tmp_dict) 144 self.wifi_int_pairs.append(tmp_obj) 145 ##Setup connection between WiFi APs and Phones and get DHCP address 146 # for the interface 147 for obj in self.wifi_int_pairs: 148 brconfigs = setup_ap_connection(obj.dut, obj.network, obj.ap) 149 iperf_server_address = wputils.wait_for_dhcp( 150 obj.ether_int.interface) 151 setattr(obj, 'server_address', iperf_server_address) 152 setattr(obj, 'brconfigs', brconfigs) 153 obj.attenuator.set_atten(MAX_ATTENUATION) 154 # Enable BQR on master and slave Android device 155 btutils.enable_bqr(self.dut) 156 btutils.enable_bqr(self.bt_device_controller) 157 158 def teardown_class(self): 159 super().teardown_class() 160 for obj in self.wifi_int_pairs: 161 obj.ap.bridge.teardown(obj.brconfigs) 162 self.log.info('Stop IPERF server at port {}'.format( 163 obj.iperf_server.port)) 164 obj.iperf_server.stop() 165 self.log.info('Stop IPERF process on {}'.format(obj.dut.serial)) 166 obj.dut.adb.shell('pkill -9 iperf3') 167 #only for glinux machine 168 # wputils.bring_down_interface(obj.ether_int.interface) 169 obj.attenuator.set_atten(MAX_ATTENUATION) 170 obj.ap.close() 171 172 def teardown_test(self): 173 174 super().teardown_test() 175 176 for obj in self.wifi_int_pairs: 177 obj.attenuator.set_atten(MAX_ATTENUATION) 178 179 def play_and_record_audio(self, duration, queue): 180 """Play and record audio for a set duration. 181 182 Args: 183 duration: duration in seconds for music playing 184 que: multiprocess que to store the return value of this function 185 Returns: 186 audio_captured: captured audio file path 187 """ 188 189 self.log.info('Play and record audio for {} second'.format(duration)) 190 self.media.play() 191 self.audio_device.start() 192 time.sleep(duration) 193 audio_captured = self.audio_device.stop() 194 self.media.stop() 195 self.log.info('Audio play and record stopped') 196 asserts.assert_true(audio_captured, 'Audio not recorded') 197 queue.put(audio_captured) 198 199 def locate_interference_pair_by_channel(self, interference_channels): 200 """Function to find which attenautor to set based on channel info 201 Args: 202 interference_channels: list of interference channels 203 Return: 204 interference_pair_indices: list of indices for interference pair 205 in self.wifi_int_pairs 206 """ 207 interference_pair_indices = [] 208 for chan in interference_channels: 209 for i in range(len(self.wifi_int_pairs)): 210 if self.wifi_int_pairs[i].channel == chan: 211 interference_pair_indices.append(i) 212 return interference_pair_indices 213 214 def get_interference_rssi(self): 215 """Function to read wifi interference RSSI level.""" 216 217 bssids = [] 218 self.interference_rssi = [] 219 wutils.wifi_toggle_state(self.android_devices[0], True) 220 for item in self.wifi_int_pairs: 221 ssid = item.network['SSID'] 222 bssid = item.ap.get_bssid_from_ssid(ssid, '2g') 223 bssids.append(bssid) 224 interference_rssi_dict = { 225 "ssid": ssid, 226 "bssid": bssid, 227 "chan": item.channel, 228 "rssi": 0 229 } 230 self.interference_rssi.append(interference_rssi_dict) 231 scaned_rssi = wpeutils.get_scan_rssi(self.android_devices[0], 232 bssids, 233 num_measurements=2) 234 for item in self.interference_rssi: 235 item['rssi'] = scaned_rssi[item['bssid']]['mean'] 236 self.log.info('Interference RSSI at channel {} is {} dBm'.format( 237 item['chan'], item['rssi'])) 238 wutils.wifi_toggle_state(self.android_devices[0], False) 239