Home | History | Annotate | Download | only in bt
      1 #/usr/bin/env python3.4
      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 
     17 import logging
     18 import time
     19 from acts import utils
     20 
     21 from acts.test_utils.bt.bt_constants import bt_default_timeout
     22 from acts.test_utils.bt.bt_constants import default_bluetooth_socket_timeout_ms
     23 from acts.test_utils.bt.bt_constants import default_le_connection_interval_ms
     24 from acts.test_utils.bt.bt_constants import default_le_data_length
     25 from acts.test_utils.bt.bt_constants import gatt_phy
     26 from acts.test_utils.bt.bt_constants import gatt_transport
     27 from acts.test_utils.bt.bt_constants import l2cap_coc_header_size
     28 from acts.test_utils.bt.bt_constants import le_connection_event_time_step_ms
     29 from acts.test_utils.bt.bt_constants import le_connection_interval_time_step_ms
     30 from acts.test_utils.bt.bt_constants import le_default_supervision_timeout
     31 from acts.test_utils.bt.bt_test_utils import get_mac_address_of_generic_advertisement
     32 from acts.test_utils.bt.bt_gatt_utils import setup_gatt_connection
     33 from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
     34 
     35 log = logging
     36 
     37 
     38 class BtCoCTestUtilsError(Exception):
     39     pass
     40 
     41 
     42 def 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 
    128 def 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