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 """Sets up the CoC connection between two Android devices. 139 140 Args: 141 client_ad: the Android device performing the connection. 142 server_ad: the Android device accepting the connection. 143 is_ble: using LE transport. 144 secured_conn: using secured connection 145 le_connection_interval: LE Connection interval. 0 means use default. 146 le_tx_data_length: LE Data Length used by BT Controller to transmit. 147 accept_timeout_ms: timeout while waiting for incoming connection. 148 Returns: 149 True if connection was successful or false if unsuccessful, 150 client connection ID, 151 and server connection ID 152 """ 153 server_ad.droid.bluetoothStartPairingHelper() 154 client_ad.droid.bluetoothStartPairingHelper() 155 156 adv_callback = None 157 mac_address = None 158 if is_ble: 159 try: 160 # This will start advertising and scanning. Will fail if it could 161 # not find the advertisements from server_ad 162 client_ad.log.info( 163 "Orchestrate_coc_connection: Start BLE advertisement and" 164 "scanning. Secured Connection={}".format(secured_conn)) 165 mac_address, adv_callback, scan_callback = ( 166 get_mac_address_of_generic_advertisement(client_ad, server_ad)) 167 except BtTestUtilsError as err: 168 raise BtCoCTestUtilsError( 169 "Orchestrate_coc_connection: Error in getting mac address: {}". 170 format(err)) 171 else: 172 mac_address = server_ad.droid.bluetoothGetLocalAddress() 173 adv_callback = None 174 175 # Adjust the Connection Interval (if necessary) 176 bluetooth_gatt_1 = -1 177 gatt_callback_1 = -1 178 gatt_connected = False 179 if is_ble and (le_connection_interval != 0 or le_min_ce_len != 0 or le_max_ce_len != 0): 180 client_ad.log.info( 181 "Adjusting connection interval={}, le_min_ce_len={}, le_max_ce_len={}" 182 .format(le_connection_interval, le_min_ce_len, le_max_ce_len)) 183 try: 184 bluetooth_gatt_1, gatt_callback_1 = setup_gatt_connection( 185 client_ad, 186 mac_address, 187 False, 188 transport=gatt_transport['le'], 189 opportunistic=False) 190 client_ad.droid.bleStopBleScan(scan_callback) 191 except GattTestUtilsError as err: 192 client_ad.log.error(err) 193 if (adv_callback != None): 194 server_ad.droid.bleStopBleAdvertising(adv_callback) 195 return False, None, None 196 client_ad.log.info("setup_gatt_connection returns success") 197 if (le_connection_interval != 0): 198 minInterval = le_connection_interval / le_connection_interval_time_step_ms 199 maxInterval = le_connection_interval / le_connection_interval_time_step_ms 200 else: 201 minInterval = default_le_connection_interval_ms / le_connection_interval_time_step_ms 202 maxInterval = default_le_connection_interval_ms / le_connection_interval_time_step_ms 203 if (le_min_ce_len != 0): 204 le_min_ce_len = le_min_ce_len / le_connection_event_time_step_ms 205 if (le_max_ce_len != 0): 206 le_max_ce_len = le_max_ce_len / le_connection_event_time_step_ms 207 208 return_status = client_ad.droid.gattClientRequestLeConnectionParameters( 209 bluetooth_gatt_1, minInterval, maxInterval, 0, 210 le_default_supervision_timeout, le_min_ce_len, le_max_ce_len) 211 if not return_status: 212 client_ad.log.error( 213 "gattClientRequestLeConnectionParameters returns failure") 214 if (adv_callback != None): 215 server_ad.droid.bleStopBleAdvertising(adv_callback) 216 return False, None, None 217 client_ad.log.info( 218 "gattClientRequestLeConnectionParameters returns success. Interval={}" 219 .format(minInterval)) 220 gatt_connected = True 221 # For now, we will only test with 1 Mbit Phy. 222 # TODO: Add explicit tests with 2 MBit Phy. 223 client_ad.droid.gattClientSetPreferredPhy( 224 bluetooth_gatt_1, gatt_phy['1m'], gatt_phy['1m'], 0) 225 226 server_ad.droid.bluetoothSocketConnBeginAcceptThreadPsm( 227 accept_timeout_ms, is_ble, secured_conn) 228 229 psm_value = server_ad.droid.bluetoothSocketConnGetPsm() 230 client_ad.log.info("Assigned PSM value={}".format(psm_value)) 231 232 client_ad.droid.bluetoothSocketConnBeginConnectThreadPsm( 233 mac_address, is_ble, psm_value, secured_conn) 234 235 if (le_tx_data_length != default_le_data_length) and is_ble: 236 client_ad.log.info("orchestrate_coc_connection: call " 237 "bluetoothSocketRequestMaximumTxDataLength") 238 client_ad.droid.bluetoothSocketRequestMaximumTxDataLength() 239 240 end_time = time.time() + bt_default_timeout 241 test_result = False 242 while time.time() < end_time: 243 if len(server_ad.droid.bluetoothSocketConnActiveConnections()) > 0: 244 server_ad.log.info("CoC Server Connection Active") 245 if len(client_ad.droid.bluetoothSocketConnActiveConnections()) > 0: 246 client_ad.log.info("CoC Client Connection Active") 247 test_result = True 248 break 249 time.sleep(1) 250 251 if (adv_callback != None): 252 server_ad.droid.bleStopBleAdvertising(adv_callback) 253 254 if not test_result: 255 client_ad.log.error("Failed to establish an CoC connection") 256 return False, None, None 257 258 if len(client_ad.droid.bluetoothSocketConnActiveConnections()) > 0: 259 server_ad.log.info( 260 "CoC client_ad Connection Active, num=%d", 261 len(client_ad.droid.bluetoothSocketConnActiveConnections())) 262 else: 263 server_ad.log.info("Error CoC client_ad Connection Inactive") 264 client_ad.log.info("Error CoC client_ad Connection Inactive") 265 266 # Wait for the client to be ready 267 client_conn_id = None 268 while (client_conn_id == None): 269 client_conn_id = client_ad.droid.bluetoothGetLastConnId() 270 if (client_conn_id != None): 271 break 272 time.sleep(1) 273 274 # Wait for the server to be ready 275 server_conn_id = None 276 while (server_conn_id == None): 277 server_conn_id = server_ad.droid.bluetoothGetLastConnId() 278 if (server_conn_id != None): 279 break 280 time.sleep(1) 281 282 client_ad.log.info( 283 "orchestrate_coc_connection: client conn id={}, server conn id={}". 284 format(client_conn_id, server_conn_id)) 285 286 if gatt_connected: 287 disconnect_gatt_connection(client_ad, bluetooth_gatt_1, 288 gatt_callback_1) 289 client_ad.droid.gattClientClose(bluetooth_gatt_1) 290 291 return True, client_conn_id, server_conn_id 292