1#!/usr/bin/env python3 2# 3# Copyright 2018 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 17import logging 18import time 19from acts import utils 20 21from acts.test_utils.bt.bt_constants import bt_default_timeout 22from acts.test_utils.bt.bt_constants import default_bluetooth_socket_timeout_ms 23from acts.test_utils.bt.bt_constants import default_le_connection_interval_ms 24from acts.test_utils.bt.bt_constants import default_le_data_length 25from acts.test_utils.bt.bt_constants import gatt_phy 26from acts.test_utils.bt.bt_constants import gatt_transport 27from acts.test_utils.bt.bt_constants import l2cap_coc_header_size 28from acts.test_utils.bt.bt_constants import le_connection_event_time_step_ms 29from acts.test_utils.bt.bt_constants import le_connection_interval_time_step_ms 30from acts.test_utils.bt.bt_constants import le_default_supervision_timeout 31from acts.test_utils.bt.bt_test_utils import get_mac_address_of_generic_advertisement 32from acts.test_utils.bt.bt_gatt_utils import setup_gatt_connection 33from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection 34 35log = logging 36 37 38class BtCoCTestUtilsError(Exception): 39 pass 40 41 42def do_multi_connection_throughput(client_ad, list_server_ad, 43 list_client_conn_id, num_iterations, 44 number_buffers, buffer_size): 45 """Throughput measurements from one client to one-or-many servers. 46 47 Args: 48 client_ad: the Android device to perform the write. 49 list_server_ad: the list of Android server devices connected to this client. 50 list_client_conn_id: list of client connection IDs 51 num_iterations: the number of test repetitions. 52 number_buffers: the total number of data buffers to transmit per test. 53 buffer_size: the number of bytes per L2CAP data buffer. 54 55 Returns: 56 Throughput in terms of bytes per second, 0 if test failed. 57 """ 58 59 total_num_bytes = 0 60 start_write_time = time.perf_counter() 61 client_ad.log.info( 62 "do_multi_connection_throughput: Before write. Start Time={:f}, " 63 "num_iterations={}, number_buffers={}, buffer_size={}, " 64 "number_buffers*buffer_size={}, num_servers={}".format( 65 start_write_time, num_iterations, number_buffers, buffer_size, 66 number_buffers * buffer_size, len(list_server_ad))) 67 68 if (len(list_server_ad) != len(list_client_conn_id)): 69 client_ad.log.error("do_multi_connection_throughput: invalid " 70 "parameters. Num of list_server_ad({}) != " 71 "list_client_conn({})".format( 72 len(list_server_ad), len(list_client_conn_id))) 73 return 0 74 75 try: 76 for _, client_conn_id in enumerate(list_client_conn_id): 77 client_ad.log.info("do_multi_connection_throughput: " 78 "client_conn_id={}".format(client_conn_id)) 79 # Plumb the tx data queue with the first set of data buffers. 80 client_ad.droid.bluetoothConnectionThroughputSend( 81 number_buffers, buffer_size, client_conn_id) 82 except Exception as err: 83 client_ad.log.error("Failed to write data: {}".format(err)) 84 return 0 85 86 # Each Loop iteration will write and read one set of buffers. 87 for _ in range(0, (num_iterations - 1)): 88 try: 89 for _, client_conn_id in enumerate(list_client_conn_id): 90 client_ad.droid.bluetoothConnectionThroughputSend( 91 number_buffers, buffer_size, client_conn_id) 92 except Exception as err: 93 client_ad.log.error("Failed to write data: {}".format(err)) 94 return 0 95 96 for _, server_ad in enumerate(list_server_ad): 97 try: 98 server_ad.droid.bluetoothConnectionThroughputRead( 99 number_buffers, buffer_size) 100 total_num_bytes += number_buffers * buffer_size 101 except Exception as err: 102 server_ad.log.error("Failed to read data: {}".format(err)) 103 return 0 104 105 for _, server_ad in enumerate(list_server_ad): 106 try: 107 server_ad.droid.bluetoothConnectionThroughputRead( 108 number_buffers, buffer_size) 109 total_num_bytes += number_buffers * buffer_size 110 except Exception as err: 111 server_ad.log.error("Failed to read data: {}".format(err)) 112 return 0 113 114 end_read_time = time.perf_counter() 115 116 test_time = (end_read_time - start_write_time) 117 if (test_time == 0): 118 client_ad.log.error("Buffer transmits cannot take zero time") 119 return 0 120 data_rate = (1.000 * total_num_bytes) / test_time 121 log.info( 122 "Calculated using total write and read times: total_num_bytes={}, " 123 "test_time={}, data rate={:08.0f} bytes/sec, {:08.0f} bits/sec".format( 124 total_num_bytes, test_time, data_rate, (data_rate * 8))) 125 return data_rate 126 127 128def orchestrate_coc_connection( 129 client_ad, 130 server_ad, 131 is_ble, 132 secured_conn=False, 133 le_connection_interval=0, 134 le_tx_data_length=default_le_data_length, 135 accept_timeout_ms=default_bluetooth_socket_timeout_ms, 136 le_min_ce_len=0, 137 le_max_ce_len=0, 138 gatt_disconnection=True): 139 """Sets up the CoC connection between two Android devices. 140 141 Args: 142 client_ad: the Android device performing the connection. 143 server_ad: the Android device accepting the connection. 144 is_ble: using LE transport. 145 secured_conn: using secured connection 146 le_connection_interval: LE Connection interval. 0 means use default. 147 le_tx_data_length: LE Data Length used by BT Controller to transmit. 148 accept_timeout_ms: timeout while waiting for incoming connection. 149 gatt_disconnection: LE GATT disconnection, default is True, False will return 150 bluetooth_gatt and gatt_callback 151 Returns: 152 True if connection was successful or false if unsuccessful, 153 client connection ID, 154 and server connection ID 155 """ 156 server_ad.droid.bluetoothStartPairingHelper() 157 client_ad.droid.bluetoothStartPairingHelper() 158 159 adv_callback = None 160 mac_address = None 161 if is_ble: 162 try: 163 # This will start advertising and scanning. Will fail if it could 164 # not find the advertisements from server_ad 165 client_ad.log.info( 166 "Orchestrate_coc_connection: Start BLE advertisement and" 167 "scanning. Secured Connection={}".format(secured_conn)) 168 mac_address, adv_callback, scan_callback = ( 169 get_mac_address_of_generic_advertisement(client_ad, server_ad)) 170 except BtTestUtilsError as err: 171 raise BtCoCTestUtilsError( 172 "Orchestrate_coc_connection: Error in getting mac address: {}". 173 format(err)) 174 else: 175 mac_address = server_ad.droid.bluetoothGetLocalAddress() 176 adv_callback = None 177 178 # Adjust the Connection Interval (if necessary) 179 bluetooth_gatt_1 = -1 180 gatt_callback_1 = -1 181 gatt_connected = False 182 if is_ble and (le_connection_interval != 0 or le_min_ce_len != 0 or le_max_ce_len != 0): 183 client_ad.log.info( 184 "Adjusting connection interval={}, le_min_ce_len={}, le_max_ce_len={}" 185 .format(le_connection_interval, le_min_ce_len, le_max_ce_len)) 186 try: 187 bluetooth_gatt_1, gatt_callback_1 = setup_gatt_connection( 188 client_ad, 189 mac_address, 190 False, 191 transport=gatt_transport['le'], 192 opportunistic=False) 193 client_ad.droid.bleStopBleScan(scan_callback) 194 except GattTestUtilsError as err: 195 client_ad.log.error(err) 196 if (adv_callback != None): 197 server_ad.droid.bleStopBleAdvertising(adv_callback) 198 return False, None, None 199 client_ad.log.info("setup_gatt_connection returns success") 200 if (le_connection_interval != 0): 201 minInterval = le_connection_interval / le_connection_interval_time_step_ms 202 maxInterval = le_connection_interval / le_connection_interval_time_step_ms 203 else: 204 minInterval = default_le_connection_interval_ms / le_connection_interval_time_step_ms 205 maxInterval = default_le_connection_interval_ms / le_connection_interval_time_step_ms 206 if (le_min_ce_len != 0): 207 le_min_ce_len = le_min_ce_len / le_connection_event_time_step_ms 208 if (le_max_ce_len != 0): 209 le_max_ce_len = le_max_ce_len / le_connection_event_time_step_ms 210 211 return_status = client_ad.droid.gattClientRequestLeConnectionParameters( 212 bluetooth_gatt_1, minInterval, maxInterval, 0, 213 le_default_supervision_timeout, le_min_ce_len, le_max_ce_len) 214 if not return_status: 215 client_ad.log.error( 216 "gattClientRequestLeConnectionParameters returns failure") 217 if (adv_callback != None): 218 server_ad.droid.bleStopBleAdvertising(adv_callback) 219 return False, None, None 220 client_ad.log.info( 221 "gattClientRequestLeConnectionParameters returns success. Interval={}" 222 .format(minInterval)) 223 gatt_connected = True 224 # For now, we will only test with 1 Mbit Phy. 225 # TODO: Add explicit tests with 2 MBit Phy. 226 client_ad.droid.gattClientSetPreferredPhy( 227 bluetooth_gatt_1, gatt_phy['1m'], gatt_phy['1m'], 0) 228 229 server_ad.droid.bluetoothSocketConnBeginAcceptThreadPsm( 230 accept_timeout_ms, is_ble, secured_conn) 231 232 psm_value = server_ad.droid.bluetoothSocketConnGetPsm() 233 client_ad.log.info("Assigned PSM value={}".format(psm_value)) 234 235 client_ad.droid.bluetoothSocketConnBeginConnectThreadPsm( 236 mac_address, is_ble, psm_value, secured_conn) 237 238 if (le_tx_data_length != default_le_data_length) and is_ble: 239 client_ad.log.info("orchestrate_coc_connection: call " 240 "bluetoothSocketRequestMaximumTxDataLength") 241 client_ad.droid.bluetoothSocketRequestMaximumTxDataLength() 242 243 end_time = time.time() + bt_default_timeout 244 test_result = False 245 while time.time() < end_time: 246 if len(server_ad.droid.bluetoothSocketConnActiveConnections()) > 0: 247 server_ad.log.info("CoC Server Connection Active") 248 if len(client_ad.droid.bluetoothSocketConnActiveConnections()) > 0: 249 client_ad.log.info("CoC Client Connection Active") 250 test_result = True 251 break 252 time.sleep(1) 253 254 if (adv_callback != None): 255 server_ad.droid.bleStopBleAdvertising(adv_callback) 256 257 if not test_result: 258 client_ad.log.error("Failed to establish an CoC connection") 259 return False, None, None 260 261 if len(client_ad.droid.bluetoothSocketConnActiveConnections()) > 0: 262 server_ad.log.info( 263 "CoC client_ad Connection Active, num=%d", 264 len(client_ad.droid.bluetoothSocketConnActiveConnections())) 265 else: 266 server_ad.log.info("Error CoC client_ad Connection Inactive") 267 client_ad.log.info("Error CoC client_ad Connection Inactive") 268 269 # Wait for the client to be ready 270 client_conn_id = None 271 while (client_conn_id == None): 272 client_conn_id = client_ad.droid.bluetoothGetLastConnId() 273 if (client_conn_id != None): 274 break 275 time.sleep(1) 276 277 # Wait for the server to be ready 278 server_conn_id = None 279 while (server_conn_id == None): 280 server_conn_id = server_ad.droid.bluetoothGetLastConnId() 281 if (server_conn_id != None): 282 break 283 time.sleep(1) 284 285 client_ad.log.info( 286 "orchestrate_coc_connection: client conn id={}, server conn id={}". 287 format(client_conn_id, server_conn_id)) 288 289 if gatt_disconnection: 290 291 if gatt_connected: 292 disconnect_gatt_connection(client_ad, bluetooth_gatt_1, 293 gatt_callback_1) 294 client_ad.droid.gattClientClose(bluetooth_gatt_1) 295 296 return True, client_conn_id, server_conn_id 297 298 else: 299 return True, client_conn_id, server_conn_id, bluetooth_gatt_1, gatt_callback_1 300