def connect_over_bredr(self): """Perform GATT connection over BREDR""" self.bluetooth_gatt, self.gatt_callback = setup_gatt_connection( self.dut, self.target_mac_addr, False, transport=gatt_transport['bredr'])
def connect_over_le(self, autoconnect): """Perform GATT connection over LE""" self.bluetooth_gatt, self.gatt_callback = setup_gatt_connection( self.dut, self.mac_addr, autoconnect, transport=gatt_transport['le']) self.discovered_services_index = None
def test_gatt_connect_iterate_uuids(self): """Test the discovery of uuids of a peripheral This test will prompt the user to press "Enter" when the peripheral is in a connecable advertisement state. Once the user presses enter, this script connects an Android device to the periphal and attempt to discover all services, characteristics, and descriptors. Steps: 1. Wait for user input to confirm peripheral is advertising. 2. Perform GATT connection to peripheral 3. Upon successful connection, iterate through all services, characteristics, and descriptors. 5. Disconnect from peripheral Expected Result: Device services, characteristics, and descriptors should all be read. Returns: Pass if True Fail if False TAGS: LE, GATT Priority: 2 """ try: bluetooth_gatt, gatt_callback = (setup_gatt_connection( self.cen_ad, self.ble_mac_address, self.AUTOCONNECT, gatt_transport['le'])) except GattTestUtilsError as err: self.log.error(err) return False if self.cen_ad.droid.gattClientDiscoverServices(bluetooth_gatt): expected_event = gatt_cb_strings['gatt_serv_disc'].format( gatt_callback) try: event = self.cen_ad.ed.pop_event(expected_event, self.DEFAULT_TIMEOUT) discovered_services_index = event['data']['ServicesIndex'] except Empty: self.log.error( gatt_cb_err['gatt_serv_disc'].format(expected_event)) return False log_gatt_server_uuids(self.cen_ad, discovered_services_index) try: disconnect_gatt_connection(self.cen_ad, bluetooth_gatt, gatt_callback) self.cen_ad.droid.gattClientClose(bluetooth_gatt) except GattTestUtilsError as err: self.log.error(err) return False self.cen_ad.droid.gattClientClose(bluetooth_gatt) return True
def test_gatt_connect_stress(self): """Test the round trip speed of connecting to a peripheral many times This test will prompt the user to press "Enter" when the peripheral is in a connecable advertisement state. Once the user presses enter, this script will measure the amount of time it takes to establish a GATT connection to the peripheral. The test will then disconnect. It will attempt to repeat this process multiple times. Steps: 1. Wait for user input to confirm peripheral is advertising. 2. Start timer 3. Perform GATT connection to peripheral 4. Upon successful connection, stop timer 5. Disconnect from peripheral 6. Repeat steps 2-5 1000 times. Expected Result: Test should measure 1000 iterations of connect/disconnect cycles. Returns: Pass if True Fail if False TAGS: LE, GATT Priority: 2 """ filter_list, scan_settings, scan_callback = generate_ble_scan_objects( self.cen_ad.droid) self.cen_ad.droid.bleStartBleScan(filter_list, scan_settings, scan_callback) self.AUTOCONNECT = False iterations = 1000 n = 0 while n < iterations: self.start_timer() try: bluetooth_gatt, gatt_callback = (setup_gatt_connection( self.cen_ad, self.ble_mac_address, self.AUTOCONNECT, gatt_transport['le'])) except GattTestUtilsError as err: self.log.error(err) return False self.log.info("Total time (ms): {}".format(self.end_timer())) try: disconnect_gatt_connection(self.cen_ad, bluetooth_gatt, gatt_callback) self.cen_ad.droid.gattClientClose(bluetooth_gatt) except GattTestUtilsError as err: self.log.error(err) return False n += 1 return True
def test_gatt_connect_in_quick_succession(self): """Test GATT connections multiple times. Test establishing a gatt connection between a GATT server and GATT client with multiple iterations. Steps: 1. Start a generic advertisement. 2. Start a generic scanner. 3. Find the advertisement and extract the mac address. 4. Stop the first scanner. 5. Create a GATT connection between the scanner and advertiser. 6. Disconnect the GATT connection. 7. Repeat steps 5 and 6 twenty times. Expected Result: Verify that a connection was established and then disconnected successfully twenty times. Returns: Pass if True Fail if False TAGS: LE, Advertising, Filtering, Scanning, GATT, Stress Priority: 1 """ gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback() gatt_server = self.per_ad.droid.gattServerOpenGattServer( gatt_server_cb) self.gatt_server_list.append(gatt_server) mac_address, adv_callback = get_mac_address_of_generic_advertisement( self.cen_ad, self.per_ad) autoconnect = False for i in range(1000): self.log.info("Starting connection iteration {}".format(i + 1)) try: bluetooth_gatt, gatt_callback = setup_gatt_connection( self.cen_ad, mac_address, autoconnect) except GattTestUtilsError as err: self.log.error(err) return False test_result = self._orchestrate_gatt_disconnection(bluetooth_gatt, gatt_callback) self.cen_ad.droid.gattClientClose(bluetooth_gatt) if not test_result: self.log.info("Failed to disconnect from peripheral device.") return False self.adv_instances.append(adv_callback) return True
def test_gatt_connect_without_scanning(self): """Test the round trip speed of connecting to a peripheral This test will prompt the user to press "Enter" when the peripheral is in a connecable advertisement state. Once the user presses enter, this script will measure the amount of time it takes to establish a GATT connection to the peripheral. The test will then disconnect Steps: 1. Wait for user input to confirm peripheral is advertising. 2. Start timer 3. Perform GATT connection to peripheral 4. Upon successful connection, stop timer 5. Disconnect from peripheral Expected Result: Device should be connected successfully Returns: Pass if True Fail if False TAGS: LE, GATT Priority: 1 """ self.AUTOCONNECT = False start_time = self._get_time_in_milliseconds() try: bluetooth_gatt, gatt_callback = (setup_gatt_connection( self.cen_ad, self.ble_mac_address, self.AUTOCONNECT, gatt_transport['le'])) except GattTestUtilsError as err: self.log.error(err) return False end_time = self._get_time_in_milliseconds() self.log.info("Total time (ms): {}".format(end_time - start_time)) try: disconnect_gatt_connection(self.cen_ad, bluetooth_gatt, gatt_callback) self.cen_ad.droid.gattClientClose(bluetooth_gatt) except GattTestUtilsError as err: self.log.error(err) return False self.cen_ad.droid.gattClientClose(bluetooth_gatt)
def test_concurrent_gatt_connections(self): """Test max concurrent GATT connections Connect to all peripherals. Steps: 1. Scan 2. Save addresses 3. Connect all addresses of the peripherals Expected Result: All connections successful. Returns: Pass if True Fail if False TAGS: Bluetooth, GATT Priority: 2 """ address_list = self.obtain_address_list_from_scan() if address_list is None: return False # Connect to all addresses for address_tuple in address_list: address = address_tuple[1] try: autoconnect = False bluetooth_gatt, gatt_callback = setup_gatt_connection( self.pri_dut, address, autoconnect) self.log.info("Successfully connected to {}".format(address)) except Exception as err: self.log.error( "Failed to establish connection to {}".format(address)) return False if (len( self.pri_dut.droid.bluetoothGetConnectedLeDevices( bt_profile_constants['gatt_server'])) != self.max_connections): self.log.error("Did not reach max connection count.") return False return True
def connect_over_le_based_off_name(self, autoconnect, name): """Perform GATT connection over LE""" self.dut.droid.bleSetScanSettingsScanMode( ble_scan_settings_modes['low_latency']) filter_list = self.dut.droid.bleGenFilterList() scan_settings = self.dut.droid.bleBuildScanSetting() scan_callback = self.dut.droid.bleGenScanCallback() event_name = scan_result.format(scan_callback) self.dut.droid.bleSetScanFilterDeviceName("BLE Rect") self.dut.droid.bleBuildScanFilter(filter_list) self.dut.droid.bleStartBleScan(filter_list, scan_settings, scan_callback) try: event = self.dut.ed.pop_event(event_name, 10) self.log.info("Found scan result: {}".format(event)) except Exception: self.log.info("Didn't find any scan results.") mac_addr = event['data']['Result']['deviceInfo']['address'] self.bluetooth_gatt, self.gatt_callback = setup_gatt_connection( self.dut, mac_addr, autoconnect, transport=gatt_transport['le']) self.dut.droid.bleStopBleScan(scan_callback) self.discovered_services_index = None
def orchestrate_coc_connection( client_ad, server_ad, is_ble, secured_conn=False, le_connection_interval=0, le_tx_data_length=default_le_data_length, accept_timeout_ms=default_bluetooth_socket_timeout_ms, le_min_ce_len=0, le_max_ce_len=0, gatt_disconnection=True): """Sets up the CoC connection between two Android devices. Args: client_ad: the Android device performing the connection. server_ad: the Android device accepting the connection. is_ble: using LE transport. secured_conn: using secured connection le_connection_interval: LE Connection interval. 0 means use default. le_tx_data_length: LE Data Length used by BT Controller to transmit. accept_timeout_ms: timeout while waiting for incoming connection. gatt_disconnection: LE GATT disconnection, default is True, False will return bluetooth_gatt and gatt_callback Returns: True if connection was successful or false if unsuccessful, client connection ID, and server connection ID """ server_ad.droid.bluetoothStartPairingHelper() client_ad.droid.bluetoothStartPairingHelper() adv_callback = None mac_address = None if is_ble: try: # This will start advertising and scanning. Will fail if it could # not find the advertisements from server_ad client_ad.log.info( "Orchestrate_coc_connection: Start BLE advertisement and" "scanning. Secured Connection={}".format(secured_conn)) mac_address, adv_callback, scan_callback = ( get_mac_address_of_generic_advertisement(client_ad, server_ad)) except BtTestUtilsError as err: raise BtCoCTestUtilsError( "Orchestrate_coc_connection: Error in getting mac address: {}". format(err)) else: mac_address = server_ad.droid.bluetoothGetLocalAddress() adv_callback = None # Adjust the Connection Interval (if necessary) bluetooth_gatt_1 = -1 gatt_callback_1 = -1 gatt_connected = False if is_ble and (le_connection_interval != 0 or le_min_ce_len != 0 or le_max_ce_len != 0): client_ad.log.info( "Adjusting connection interval={}, le_min_ce_len={}, le_max_ce_len={}" .format(le_connection_interval, le_min_ce_len, le_max_ce_len)) try: bluetooth_gatt_1, gatt_callback_1 = setup_gatt_connection( client_ad, mac_address, False, transport=gatt_transport['le'], opportunistic=False) client_ad.droid.bleStopBleScan(scan_callback) except GattTestUtilsError as err: client_ad.log.error(err) if (adv_callback != None): server_ad.droid.bleStopBleAdvertising(adv_callback) return False, None, None client_ad.log.info("setup_gatt_connection returns success") if (le_connection_interval != 0): minInterval = le_connection_interval / le_connection_interval_time_step_ms maxInterval = le_connection_interval / le_connection_interval_time_step_ms else: minInterval = default_le_connection_interval_ms / le_connection_interval_time_step_ms maxInterval = default_le_connection_interval_ms / le_connection_interval_time_step_ms if (le_min_ce_len != 0): le_min_ce_len = le_min_ce_len / le_connection_event_time_step_ms if (le_max_ce_len != 0): le_max_ce_len = le_max_ce_len / le_connection_event_time_step_ms return_status = client_ad.droid.gattClientRequestLeConnectionParameters( bluetooth_gatt_1, minInterval, maxInterval, 0, le_default_supervision_timeout, le_min_ce_len, le_max_ce_len) if not return_status: client_ad.log.error( "gattClientRequestLeConnectionParameters returns failure") if (adv_callback != None): server_ad.droid.bleStopBleAdvertising(adv_callback) return False, None, None client_ad.log.info( "gattClientRequestLeConnectionParameters returns success. Interval={}" .format(minInterval)) gatt_connected = True # For now, we will only test with 1 Mbit Phy. # TODO: Add explicit tests with 2 MBit Phy. client_ad.droid.gattClientSetPreferredPhy( bluetooth_gatt_1, gatt_phy['1m'], gatt_phy['1m'], 0) server_ad.droid.bluetoothSocketConnBeginAcceptThreadPsm( accept_timeout_ms, is_ble, secured_conn) psm_value = server_ad.droid.bluetoothSocketConnGetPsm() client_ad.log.info("Assigned PSM value={}".format(psm_value)) client_ad.droid.bluetoothSocketConnBeginConnectThreadPsm( mac_address, is_ble, psm_value, secured_conn) if (le_tx_data_length != default_le_data_length) and is_ble: client_ad.log.info("orchestrate_coc_connection: call " "bluetoothSocketRequestMaximumTxDataLength") client_ad.droid.bluetoothSocketRequestMaximumTxDataLength() end_time = time.time() + bt_default_timeout test_result = False while time.time() < end_time: if len(server_ad.droid.bluetoothSocketConnActiveConnections()) > 0: server_ad.log.info("CoC Server Connection Active") if len(client_ad.droid.bluetoothSocketConnActiveConnections()) > 0: client_ad.log.info("CoC Client Connection Active") test_result = True break time.sleep(1) if (adv_callback != None): server_ad.droid.bleStopBleAdvertising(adv_callback) if not test_result: client_ad.log.error("Failed to establish an CoC connection") return False, None, None if len(client_ad.droid.bluetoothSocketConnActiveConnections()) > 0: server_ad.log.info( "CoC client_ad Connection Active, num=%d", len(client_ad.droid.bluetoothSocketConnActiveConnections())) else: server_ad.log.info("Error CoC client_ad Connection Inactive") client_ad.log.info("Error CoC client_ad Connection Inactive") # Wait for the client to be ready client_conn_id = None while (client_conn_id == None): client_conn_id = client_ad.droid.bluetoothGetLastConnId() if (client_conn_id != None): break time.sleep(1) # Wait for the server to be ready server_conn_id = None while (server_conn_id == None): server_conn_id = server_ad.droid.bluetoothGetLastConnId() if (server_conn_id != None): break time.sleep(1) client_ad.log.info( "orchestrate_coc_connection: client conn id={}, server conn id={}". format(client_conn_id, server_conn_id)) if gatt_disconnection: if gatt_connected: disconnect_gatt_connection(client_ad, bluetooth_gatt_1, gatt_callback_1) client_ad.droid.gattClientClose(bluetooth_gatt_1) return True, client_conn_id, server_conn_id else: return True, client_conn_id, server_conn_id, bluetooth_gatt_1, gatt_callback_1
def test_gatt_connect_autoconnect(self): """Test GATT connection over LE. Test re-establishing a gat connection using autoconnect set to True in order to test connection whitelist. Steps: 1. Start a generic advertisement. 2. Start a generic scanner. 3. Find the advertisement and extract the mac address. 4. Stop the first scanner. 5. Create a GATT connection between the scanner and advertiser. 6. Disconnect the GATT connection. 7. Create a GATT connection with autoconnect set to True 8. Disconnect the GATT connection. Expected Result: Verify that a connection was re-established and then disconnected successfully. Returns: Pass if True Fail if False TAGS: LE, Advertising, Filtering, Scanning, GATT Priority: 0 """ gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback() gatt_server = self.per_ad.droid.gattServerOpenGattServer( gatt_server_cb) self.gatt_server_list.append(gatt_server) autoconnect = False mac_address, adv_callback = ( get_mac_address_of_generic_advertisement(self.cen_ad, self.per_ad)) try: bluetooth_gatt, gatt_callback = setup_gatt_connection( self.cen_ad, mac_address, autoconnect) self.bluetooth_gatt_list.append(bluetooth_gatt) except GattTestUtilsError as err: self.log.error(err) return False try: disconnect_gatt_connection(self.cen_ad, bluetooth_gatt, gatt_callback) if bluetooth_gatt in self.bluetooth_gatt_list: self.bluetooth_gatt_list.remove(bluetooth_gatt) except GattTestUtilsError as err: self.log.error(err) return False autoconnect = True bluetooth_gatt = self.cen_ad.droid.gattClientConnectGatt( gatt_callback, mac_address, autoconnect, GattTransport.TRANSPORT_AUTO.value) self.bluetooth_gatt_list.append(bluetooth_gatt) expected_event = GattCbStrings.GATT_CONN_CHANGE.value.format( gatt_callback) try: event = self.cen_ad.ed.pop_event(expected_event, self.default_timeout) except Empty: self.log.error( GattCbErr.GATT_CONN_CHANGE_ERR.value.format(expected_event)) test_result = False return True
def test_gatt_notification_longev(self): """Test GATT characterisitic notifications for long periods of time This test will prompt the user to press "Enter" when the peripheral is in a connecable advertisement state. Once the user presses enter, this script aims to set characteristic notification to true on the config file's SERVICE_UUID, NOTIFIABLE_CHAR_UUID, and CCC_DESC_UUID. This test assumes the peripheral will constantly write data to a notifiable characteristic. Steps: 1. Wait for user input to confirm peripheral is advertising. 2. Perform Bluetooth pairing to GATT mac address 3. Perform a GATT connection to the periheral 4. Get the discovered service uuid that matches the user's input in the config file 4. Write to the CCC descriptor to enable notifications 5. Enable notifications on the user's input Characteristic UUID 6. Continuously wait for Characteristic Changed events which equate to recieving notifications for 15 minutes. Expected Result: There should be no disconnects and we should constantly receive Characteristic Changed information. Values should vary upon user interaction with the peripheral. Returns: Pass if True Fail if False TAGS: LE, GATT Priority: 1 """ #pair devices if not self._pair_with_peripheral(): return False try: bluetooth_gatt, gatt_callback = (setup_gatt_connection( self.cen_ad, self.ble_mac_address, self.AUTOCONNECT, gatt_transport['le'])) except GattTestUtilsError as err: self.log.error(err) return False if self.cen_ad.droid.gattClientDiscoverServices(bluetooth_gatt): expected_event = gatt_cb_strings['gatt_serv_disc'].format( gatt_callback) try: event = self.cen_ad.ed.pop_event(expected_event, self.DEFAULT_TIMEOUT) discovered_services_index = event['data']['ServicesIndex'] except Empty: self.log.error( gatt_cb_err['gatt_serv_disc'].format(expected_event)) return False # TODO: in setup save service_cound and discovered_services_index # programatically services_count = self.cen_ad.droid.gattClientGetDiscoveredServicesCount( discovered_services_index) test_service_index = None for i in range(services_count): disc_service_uuid = ( self.cen_ad.droid.gattClientGetDiscoveredServiceUuid( discovered_services_index, i)) if disc_service_uuid == self.SERVICE_UUID: test_service_index = i break if not test_service_index: self.log.error("Service not found.") return False self.cen_ad.droid.gattClientDescriptorSetValue( bluetooth_gatt, discovered_services_index, test_service_index, self.NOTIFIABLE_CHAR_UUID, self.CCC_DESC_UUID, gatt_descriptor['enable_notification_value']) self.cen_ad.droid.gattClientWriteDescriptor(bluetooth_gatt, discovered_services_index, test_service_index, self.NOTIFIABLE_CHAR_UUID, self.CCC_DESC_UUID) self.cen_ad.droid.gattClientSetCharacteristicNotification( bluetooth_gatt, discovered_services_index, test_service_index, self.NOTIFIABLE_CHAR_UUID, True) # set 15 minute notification test time notification_test_time = 900 end_time = time.time() + notification_test_time expected_event = gatt_cb_strings['char_change'].format(gatt_callback) while time.time() < end_time: try: event = self.cen_ad.ed.pop_event(expected_event, self.DEFAULT_TIMEOUT) self.log.info(event) except Empty as err: print(err) self.log.error( gatt_cb_err['char_change_err'].format(expected_event)) return False return True
def test_data_transfer_to_concurrent_gatt_connections(self): """Test writing GATT descriptors concurrently to many peripherals. Connect to all peripherals and write gatt descriptors concurrently. Steps: 1. Scan the addresses by names 2. Save mac addresses of the peripherals 3. Connect all addresses of the peripherals and write gatt descriptors Expected Result: All connections and data transfers are successful. Returns: Pass if True Fail if False TAGS: Bluetooth, GATT Priority: 2 """ address_list = self.obtain_address_list_from_scan() if address_list is None: return False # Connect to all addresses executor = concurrent.futures.ThreadPoolExecutor(max_workers=10) for address_tuple in address_list: ad, address = address_tuple gatts = GattServerLib(log=self.log, dut=ad) gatt_server, gatt_server_callback = gatts.setup_gatts_db( database=gatt_server_read_descriptor_sample) try: bluetooth_gatt, gatt_callback = setup_gatt_connection( self.pri_dut, address, autoconnect=False) self.log.info("Successfully connected to {}".format(address)) except Exception as err: self.log.error( "Failed to establish connection to {}".format(address)) return False if self.pri_dut.droid.gattClientDiscoverServices(bluetooth_gatt): event = self.pri_dut.ed.pop_event( "GattConnect{}onServicesDiscovered".format(bluetooth_gatt), self.bt_default_timeout) discovered_services_index = event['data']['ServicesIndex'] else: self.log.info("Failed to discover services.") return False services_count = self.pri_dut.droid.gattClientGetDiscoveredServicesCount( discovered_services_index) arguments_list = [ self.pri_dut.droid, self.pri_dut.ed, ad.droid, ad.ed, gatt_server, gatt_server_callback, bluetooth_gatt, services_count, discovered_services_index, 100 ] self.list_of_arguments_list.append(arguments_list) for arguments_list in self.list_of_arguments_list: executor.submit(run_continuous_write_descriptor, *arguments_list) executor.shutdown(wait=True) if (len( self.pri_dut.droid.bluetoothGetConnectedLeDevices( bt_profile_constants['gatt_server'])) != self.max_connections): self.log.error("Failed to write concurrently.") return False return True
def test_gatt_connect_opportunistic(self): """Test opportunistic GATT connection over LE. Test establishing a gatt connection between a GATT server and GATT client in opportunistic mode. Steps: 1. Start a generic advertisement. 2. Start a generic scanner. 3. Find the advertisement and extract the mac address. 4. Stop the first scanner. 5. Create GATT connection 1 between the scanner and advertiser normally 6. Create GATT connection 2 between the scanner and advertiser using opportunistic mode 7. Disconnect GATT connection 1 Expected Result: Verify GATT connection 2 automatically disconnects when GATT connection 1 disconnect Returns: Pass if True Fail if False TAGS: LE, Advertising, Filtering, Scanning, GATT Priority: 0 """ gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback() gatt_server = self.per_ad.droid.gattServerOpenGattServer( gatt_server_cb) self.gatt_server_list.append(gatt_server) mac_address, adv_callback = ( get_mac_address_of_generic_advertisement(self.cen_ad, self.per_ad)) # Make GATT connection 1 try: bluetooth_gatt_1, gatt_callback_1 = setup_gatt_connection( self.cen_ad, mac_address, False, transport=gatt_transport['auto'], opportunistic=False) self.bluetooth_gatt_list.append(bluetooth_gatt_1) except GattTestUtilsError as err: self.log.error(err) return False # Make GATT connection 2 try: bluetooth_gatt_2, gatt_callback_2 = setup_gatt_connection( self.cen_ad, mac_address, False, transport=gatt_transport['auto'], opportunistic=True) self.bluetooth_gatt_list.append(bluetooth_gatt_2) except GattTestUtilsError as err: self.log.error(err) return False # Disconnect GATT connection 1 try: disconnect_gatt_connection(self.cen_ad, bluetooth_gatt_1, gatt_callback_1) close_gatt_client(self.cen_ad, bluetooth_gatt_1) if bluetooth_gatt_1 in self.bluetooth_gatt_list: self.bluetooth_gatt_list.remove(bluetooth_gatt_1) except GattTestUtilsError as err: self.log.error(err) return False # Confirm that GATT connection 2 also disconnects wait_for_gatt_disconnect_event(self.cen_ad, gatt_callback_2) close_gatt_client(self.cen_ad, bluetooth_gatt_2) if bluetooth_gatt_2 in self.bluetooth_gatt_list: self.bluetooth_gatt_list.remove(bluetooth_gatt_2) return True