diff --git a/apps/bench.py b/apps/bench.py index de14eee5..51bec3e5 100644 --- a/apps/bench.py +++ b/apps/bench.py @@ -50,8 +50,10 @@ from bumble.sdp import ( SDP_PUBLIC_BROWSE_ROOT, SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, + SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, DataElement, ServiceAttribute, + Client as SdpClient, ) from bumble.transport import open_transport_or_link import bumble.rfcomm @@ -195,6 +197,50 @@ def make_sdp_records(channel): } +async def find_rfcomm_channel_with_uuid( + device: Device, connection: Connection, uuid: str +) -> int: + # Connect to the SDP Server + sdp_client = SdpClient(device) + await sdp_client.connect(connection) + + # Search for services with an L2CAP service attribute + search_result = await sdp_client.search_attributes( + [BT_L2CAP_PROTOCOL_ID], + [ + SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, + SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, + SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, + ], + ) + for attribute_list in search_result: + service_uuid = None + service_class_id_list = ServiceAttribute.find_attribute_in_list( + attribute_list, SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID + ) + if service_class_id_list: + if service_class_id_list.value: + for service_class_id in service_class_id_list.value: + service_uuid = service_class_id.value + if str(service_uuid) != uuid: + # This service doesn't have a UUID or isn't the right one. + continue + + # Look for the RFCOMM Channel number + protocol_descriptor_list = ServiceAttribute.find_attribute_in_list( + attribute_list, SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID + ) + if protocol_descriptor_list: + for protocol_descriptor in protocol_descriptor_list.value: + if len(protocol_descriptor.value) >= 2: + if protocol_descriptor.value[0].value == BT_RFCOMM_PROTOCOL_ID: + await sdp_client.disconnect() + return protocol_descriptor.value[1].value + + await sdp_client.disconnect() + return 0 + + class PacketType(enum.IntEnum): RESET = 0 SEQUENCE = 1 @@ -747,24 +793,40 @@ class L2capServer(StreamedPacketIO): # RfcommClient # ----------------------------------------------------------------------------- class RfcommClient(StreamedPacketIO): - def __init__(self, device, channel): + def __init__(self, device, channel, uuid): super().__init__() self.device = device self.channel = channel + self.uuid = uuid self.ready = asyncio.Event() async def on_connection(self, connection): connection.on('disconnection', self.on_disconnection) + # Find the channel number if not specified + channel = self.channel + if channel == 0: + print( + color(f'@@@ Discovering channel number from UUID {self.uuid}', 'cyan') + ) + channel = await find_rfcomm_channel_with_uuid( + self.device, connection, self.uuid + ) + print(color(f'@@@ Channel number = {channel}', 'cyan')) + if channel == 0: + print(color('!!! No RFComm service with this UUID found', 'red')) + await connection.disconnect() + return + # Create a client and start it print(color('*** Starting RFCOMM client...', 'blue')) rfcomm_client = bumble.rfcomm.Client(self.device, connection) rfcomm_mux = await rfcomm_client.start() print(color('*** Started', 'blue')) - print(color(f'### Opening session for channel {self.channel}...', 'yellow')) + print(color(f'### Opening session for channel {channel}...', 'yellow')) try: - rfcomm_session = await rfcomm_mux.open_dlc(self.channel) + rfcomm_session = await rfcomm_mux.open_dlc(channel) print(color('### Session open', 'yellow'), rfcomm_session) except bumble.core.ConnectionError as error: print(color(f'!!! Session open failed: {error}', 'red')) @@ -1087,7 +1149,9 @@ def create_mode_factory(ctx, default_mode): return L2capServer(device, psm=ctx.obj['l2cap_psm']) if mode == 'rfcomm-client': - return RfcommClient(device, channel=ctx.obj['rfcomm_channel']) + return RfcommClient( + device, channel=ctx.obj['rfcomm_channel'], uuid=ctx.obj['rfcomm_uuid'] + ) if mode == 'rfcomm-server': return RfcommServer(device, channel=ctx.obj['rfcomm_channel']) @@ -1166,6 +1230,11 @@ def create_role_factory(ctx, default_role): default=DEFAULT_RFCOMM_CHANNEL, help='RFComm channel to use', ) +@click.option( + '--rfcomm-uuid', + default=DEFAULT_RFCOMM_UUID, + help='RFComm service UUID to use (ignored is --rfcomm-channel is not 0)', +) @click.option( '--l2cap-psm', type=int, @@ -1208,6 +1277,7 @@ def bench( packet_count, start_delay, rfcomm_channel, + rfcomm_uuid, l2cap_psm, ): ctx.ensure_object(dict) @@ -1216,6 +1286,7 @@ def bench( ctx.obj['mode'] = mode ctx.obj['att_mtu'] = att_mtu ctx.obj['rfcomm_channel'] = rfcomm_channel + ctx.obj['rfcomm_uuid'] = rfcomm_uuid ctx.obj['l2cap_psm'] = l2cap_psm ctx.obj['packet_size'] = packet_size ctx.obj['packet_count'] = packet_count diff --git a/extras/android/BtBench/app/src/main/java/com/github/google/bumble/btbench/Model.kt b/extras/android/BtBench/app/src/main/java/com/github/google/bumble/btbench/Model.kt index 3a62519b..93755e40 100644 --- a/extras/android/BtBench/app/src/main/java/com/github/google/bumble/btbench/Model.kt +++ b/extras/android/BtBench/app/src/main/java/com/github/google/bumble/btbench/Model.kt @@ -23,7 +23,7 @@ import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import java.util.UUID -val DEFAULT_RFCOMM_UUID = UUID.fromString("0AF17D61-5DAE-4530-BE0D-6A8D54C3608B") +val DEFAULT_RFCOMM_UUID = UUID.fromString("E6D55659-C8B4-4B85-96BB-B1143AF6D3AE") const val DEFAULT_PEER_BLUETOOTH_ADDRESS = "AA:BB:CC:DD:EE:FF" const val DEFAULT_SENDER_PACKET_COUNT = 100 const val DEFAULT_SENDER_PACKET_SIZE = 1024 @@ -95,15 +95,15 @@ class AppViewModel : ViewModel() { } fun updateSenderPacketCount() { - if (senderPacketCountSlider < 0.2) { + if (senderPacketCountSlider < 0.1F) { senderPacketCount = 10 - } else if (senderPacketCountSlider < 0.4) { + } else if (senderPacketCountSlider < 0.3F) { senderPacketCount = 50 - } else if (senderPacketCountSlider < 0.6) { + } else if (senderPacketCountSlider < 0.5F) { senderPacketCount = 100 - } else if (senderPacketCountSlider < 0.8) { + } else if (senderPacketCountSlider < 0.7F) { senderPacketCount = 500 - } else if (senderPacketCountSlider < 1.0) { + } else if (senderPacketCountSlider < 0.9F) { senderPacketCount = 1000 } else { senderPacketCount = 10000 @@ -137,15 +137,15 @@ class AppViewModel : ViewModel() { } fun updateSenderPacketSize() { - if (senderPacketSizeSlider < 0.2) { + if (senderPacketSizeSlider < 0.1F) { senderPacketSize = 1 - } else if (senderPacketSizeSlider < 0.4) { + } else if (senderPacketSizeSlider < 0.3F) { senderPacketSize = 256 - } else if (senderPacketSizeSlider < 0.6) { + } else if (senderPacketSizeSlider < 0.5F) { senderPacketSize = 512 - } else if (senderPacketSizeSlider < 0.8) { + } else if (senderPacketSizeSlider < 0.7F) { senderPacketSize = 1024 - } else if (senderPacketSizeSlider < 1.0) { + } else if (senderPacketSizeSlider < 0.9F) { senderPacketSize = 2048 } else { senderPacketSize = 4096 diff --git a/extras/android/BtBench/app/src/main/java/com/github/google/bumble/btbench/Receiver.kt b/extras/android/BtBench/app/src/main/java/com/github/google/bumble/btbench/Receiver.kt index 6da96dd1..c3844b82 100644 --- a/extras/android/BtBench/app/src/main/java/com/github/google/bumble/btbench/Receiver.kt +++ b/extras/android/BtBench/app/src/main/java/com/github/google/bumble/btbench/Receiver.kt @@ -51,7 +51,9 @@ class Receiver(private val viewModel: AppViewModel, private val packetIO: Packet if (packet.flags and Packet.LAST_FLAG != 0) { Log.info("received last packet") val elapsed = now - startTime - viewModel.throughput = (bytesReceived / elapsed.toDouble(DurationUnit.SECONDS)).toInt() + val throughput = (bytesReceived / elapsed.toDouble(DurationUnit.SECONDS)).toInt() + Log.info("throughput: $throughput") + viewModel.throughput = throughput packetIO.sendPacket(AckPacket(packet.flags, packet.sequenceNumber)) } }