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