1#/usr/bin/env python3 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 statistics 18from acts import asserts 19from acts.base_test import BaseTestClass 20from acts.signals import TestPass 21from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest 22from acts.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection 23from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test 24from acts.test_utils.bt.bt_test_utils import verify_server_and_client_connected 25from acts.test_utils.bt.loggers.bluetooth_metric_logger import BluetoothMetricLogger 26from acts.test_utils.bt.loggers.protos import bluetooth_metric_pb2 as proto_module 27from acts.utils import set_location_service 28 29 30class BluetoothThroughputTest(BaseTestClass): 31 """Connects two Android phones and tests the throughput between them. 32 33 Attributes: 34 client_device: An Android device object that will be sending data 35 server_device: An Android device object that will be receiving data 36 bt_logger: The proxy logger instance for each test case 37 data_transfer_type: Data transfer protocol used for the test 38 """ 39 40 def setup_class(self): 41 super().setup_class() 42 43 # Sanity check of the devices under test 44 # TODO(b/119051823): Investigate using a config validator to replace this. 45 if len(self.android_devices) < 2: 46 raise ValueError( 47 'Not enough android phones detected (need at least two)') 48 49 # Data will be sent from the client_device to the server_device 50 self.client_device = self.android_devices[0] 51 self.server_device = self.android_devices[1] 52 self.bt_logger = BluetoothMetricLogger.for_test_case() 53 self.data_transfer_type = proto_module.BluetoothDataTestResult.RFCOMM 54 self.log.info('Successfully found required devices.') 55 56 def setup_test(self): 57 setup_multiple_devices_for_bt_test(self.android_devices) 58 self._connect_rfcomm() 59 60 def teardown_test(self): 61 if verify_server_and_client_connected( 62 self.client_device, self.server_device, log=False): 63 self.client_device.droid.bluetoothSocketConnStop() 64 self.server_device.droid.bluetoothSocketConnStop() 65 66 def _connect_rfcomm(self): 67 """Establishes an RFCOMM connection between two phones. 68 69 Connects the client device to the server device given the hardware 70 address of the server device. 71 """ 72 73 set_location_service(self.client_device, True) 74 set_location_service(self.server_device, True) 75 server_address = self.server_device.droid.bluetoothGetLocalAddress() 76 self.log.info('Pairing and connecting devices') 77 asserts.assert_true(self.client_device.droid 78 .bluetoothDiscoverAndBond(server_address), 79 'Failed to pair and connect devices') 80 81 # Create RFCOMM connection 82 asserts.assert_true(orchestrate_rfcomm_connection 83 (self.client_device, self.server_device), 84 'Failed to establish RFCOMM connection') 85 86 def _measure_throughput(self, num_of_buffers, buffer_size): 87 """Measures the throughput of a data transfer. 88 89 Sends data from the client device that is read by the server device. 90 Calculates the throughput for the transfer. 91 92 Args: 93 num_of_buffers: An integer value designating the number of buffers 94 to be sent. 95 buffer_size: An integer value designating the size of each buffer, 96 in bytes. 97 98 Returns: 99 The throughput of the transfer in bytes per second. 100 """ 101 102 # TODO(b/119638242): Need to fix throughput send/receive methods 103 (self.client_device.droid 104 .bluetoothConnectionThroughputSend(num_of_buffers, buffer_size)) 105 106 throughput = (self.server_device.droid 107 .bluetoothConnectionThroughputRead(num_of_buffers, 108 buffer_size)) 109 return throughput 110 111 @BluetoothBaseTest.bt_test_wrap 112 def test_bluetooth_throughput_large_buffer(self): 113 """Tests the throughput over a series of data transfers with large 114 buffer size. 115 """ 116 117 metrics = {} 118 throughput_list = [] 119 120 for transfer in range(300): 121 throughput = self._measure_throughput(1, 300) 122 self.log.info('Throughput: {} bytes-per-sec'.format(throughput)) 123 throughput_list.append(throughput) 124 125 metrics['data_transfer_protocol'] = self.data_transfer_type 126 metrics['data_packet_size'] = 300 127 metrics['data_throughput_min_bytes_per_second'] = int( 128 min(throughput_list)) 129 metrics['data_throughput_max_bytes_per_second'] = int( 130 max(throughput_list)) 131 metrics['data_throughput_avg_bytes_per_second'] = int(statistics.mean( 132 throughput_list)) 133 134 proto = self.bt_logger.get_results(metrics, 135 self.__class__.__name__, 136 self.server_device, 137 self.client_device) 138 139 asserts.assert_true(metrics['data_throughput_min_bytes_per_second'] > 0, 140 'Minimum throughput must be greater than 0!', 141 extras=proto) 142 raise TestPass('Throughput test (large buffer) completed successfully', 143 extras=proto) 144 145 @BluetoothBaseTest.bt_test_wrap 146 def test_bluetooth_throughput_medium_buffer(self): 147 """Tests the throughput over a series of data transfers with medium 148 buffer size. 149 """ 150 151 metrics = {} 152 throughput_list = [] 153 154 for transfer in range(300): 155 throughput = self._measure_throughput(1, 100) 156 self.log.info('Throughput: {} bytes-per-sec'.format(throughput)) 157 throughput_list.append(throughput) 158 159 metrics['data_transfer_protocol'] = self.data_transfer_type 160 metrics['data_packet_size'] = 100 161 metrics['data_throughput_min_bytes_per_second'] = int( 162 min(throughput_list)) 163 metrics['data_throughput_max_bytes_per_second'] = int( 164 max(throughput_list)) 165 metrics['data_throughput_avg_bytes_per_second'] = int(statistics.mean( 166 throughput_list)) 167 168 proto = self.bt_logger.get_results(metrics, 169 self.__class__.__name__, 170 self.server_device, 171 self.client_device) 172 173 asserts.assert_true(metrics['data_throughput_min_bytes_per_second'] > 0, 174 'Minimum throughput must be greater than 0!', 175 extras=proto) 176 raise TestPass('Throughput test (medium buffer) completed successfully', 177 extras=proto) 178 179 @BluetoothBaseTest.bt_test_wrap 180 def test_bluetooth_throughput_small_buffer(self): 181 """Tests the throughput over a series of data transfers with small 182 buffer size. 183 """ 184 185 metrics = {} 186 throughput_list = [] 187 188 for transfer in range(300): 189 throughput = self._measure_throughput(1, 10) 190 self.log.info('Throughput: {} bytes-per-sec'.format(throughput)) 191 throughput_list.append(throughput) 192 193 metrics['data_transfer_protocol'] = self.data_transfer_type 194 metrics['data_packet_size'] = 10 195 metrics['data_throughput_min_bytes_per_second'] = int( 196 min(throughput_list)) 197 metrics['data_throughput_max_bytes_per_second'] = int( 198 max(throughput_list)) 199 metrics['data_throughput_avg_bytes_per_second'] = int(statistics.mean( 200 throughput_list)) 201 202 proto = self.bt_logger.get_results(metrics, 203 self.__class__.__name__, 204 self.server_device, 205 self.client_device) 206 207 asserts.assert_true(metrics['data_throughput_min_bytes_per_second'] > 0, 208 'Minimum throughput must be greater than 0!', 209 extras=proto) 210 raise TestPass('Throughput test (small buffer) completed successfully', 211 extras=proto) 212 213 @BluetoothBaseTest.bt_test_wrap 214 def test_maximum_buffer_size(self): 215 """Calculates the maximum allowed buffer size for one packet.""" 216 217 current_buffer_size = 1 218 while True: 219 self.log.info('Trying buffer size {}'.format(current_buffer_size)) 220 try: 221 throughput = self._measure_throughput(1, current_buffer_size) 222 except Exception: 223 buffer_msg = ('Max buffer size: {} bytes'. 224 format(current_buffer_size - 1)) 225 throughput_msg = ('Max throughput: {} bytes-per-second'. 226 format(throughput)) 227 self.log.info(buffer_msg) 228 self.log.info(throughput_msg) 229 return True 230 current_buffer_size += 1 231