Loading framework/tests/bumble/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ android_test_helper_app { "opencensus-java-contrib-grpc-metrics", "pandora_experimental-grpc-java", "pandora_experimental-proto-java", "truth-java8-extension", ], // Include all test java and kotlin files. Loading framework/tests/bumble/src/android/bluetooth/GattClientTest.java +124 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.bluetooth; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth8.assertThat; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.any; Loading Loading @@ -45,7 +46,11 @@ import org.mockito.InOrder; import org.mockito.invocation.Invocation; import java.util.Collection; import java.util.UUID; import pandora.GattProto.GattCharacteristicParams; import pandora.GattProto.GattServiceParams; import pandora.GattProto.RegisterServiceRequest; import pandora.HostProto.AdvertiseRequest; import pandora.HostProto.AdvertiseResponse; import pandora.HostProto.OwnAddressType; Loading @@ -57,6 +62,8 @@ public class GattClientTest { private static final int MTU_REQUESTED = 23; private static final int ANOTHER_MTU_REQUESTED = 42; private static final UUID GAP_UUID = UUID.fromString("00001800-0000-1000-8000-00805f9b34fb"); @ClassRule public static final AdoptShellPermissionsRule PERM = new AdoptShellPermissionsRule(); @Rule public final PandoraDevice mBumble = new PandoraDevice(); Loading Loading @@ -131,6 +138,123 @@ public class GattClientTest { verifyNoMoreInteractions(gattCallback); } @Test public void clientGattDiscoverServices() throws Exception { BluetoothGattCallback gattCallback = mock(BluetoothGattCallback.class); BluetoothGatt gatt = connectGattAndWaitConnection(gattCallback); try { verify(gattCallback, timeout(1000)) .onConnectionStateChange(any(), anyInt(), eq(BluetoothProfile.STATE_CONNECTED)); gatt.discoverServices(); verify(gattCallback, timeout(10000)) .onServicesDiscovered(any(), eq(BluetoothGatt.GATT_SUCCESS)); assertThat(gatt.getServices().stream().map(BluetoothGattService::getUuid)) .contains(GAP_UUID); } finally { disconnectAndWaitDisconnection(gatt, gattCallback); } } @Test public void clientGattReadCharacteristics() throws Exception { BluetoothGattCallback gattCallback = mock(BluetoothGattCallback.class); BluetoothGatt gatt = connectGattAndWaitConnection(gattCallback); try { verify(gattCallback, timeout(1000)) .onConnectionStateChange(any(), anyInt(), eq(BluetoothProfile.STATE_CONNECTED)); gatt.discoverServices(); verify(gattCallback, timeout(10000)) .onServicesDiscovered(any(), eq(BluetoothGatt.GATT_SUCCESS)); BluetoothGattService firstService = gatt.getServices().get(0); BluetoothGattCharacteristic firstCharacteristic = firstService.getCharacteristics().get(0); gatt.readCharacteristic(firstCharacteristic); verify(gattCallback, timeout(5000)).onCharacteristicRead(any(), any(), any(), anyInt()); } finally { disconnectAndWaitDisconnection(gatt, gattCallback); } } @Test public void clientGattWriteCharacteristic() throws Exception { registerWritableGattService(); BluetoothGattCallback gattCallback = mock(BluetoothGattCallback.class); BluetoothGatt gatt = connectGattAndWaitConnection(gattCallback); try { verify(gattCallback, timeout(1000)) .onConnectionStateChange(any(), anyInt(), eq(BluetoothProfile.STATE_CONNECTED)); gatt.discoverServices(); verify(gattCallback, timeout(10000)) .onServicesDiscovered(any(), eq(BluetoothGatt.GATT_SUCCESS)); BluetoothGattCharacteristic characteristic = null; outer: for (BluetoothGattService candidateService : gatt.getServices()) { for (BluetoothGattCharacteristic candidateCharacteristic : candidateService.getCharacteristics()) { if ((candidateCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0) { characteristic = candidateCharacteristic; break outer; } } } byte[] newValue = new byte[] {13}; gatt.writeCharacteristic( characteristic, newValue, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); verify(gattCallback, timeout(5000)) .onCharacteristicWrite( any(), eq(characteristic), eq(BluetoothGatt.GATT_SUCCESS)); } finally { disconnectAndWaitDisconnection(gatt, gattCallback); } } private void registerWritableGattService() { String characteristicUuidString = "11111111-1111-1111-1111-111111111111"; String serviceUuidString = "00000000-0000-0000-0000-000000000000"; GattCharacteristicParams characteristicParams = GattCharacteristicParams.newBuilder() .setProperties(BluetoothGattCharacteristic.PROPERTY_WRITE) .setUuid(characteristicUuidString) .build(); GattServiceParams serviceParams = GattServiceParams.newBuilder() .addCharacteristics(characteristicParams) .setUuid(serviceUuidString) .build(); RegisterServiceRequest request = RegisterServiceRequest.newBuilder().setService(serviceParams).build(); mBumble.gattBlocking().registerService(request); } private void advertiseWithBumble() { AdvertiseRequest request = AdvertiseRequest.newBuilder() Loading framework/tests/bumble/src/android/bluetooth/PandoraDevice.java +11 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import org.junit.rules.ExternalResource; import java.util.concurrent.TimeUnit; import pandora.DckGrpc; import pandora.GATTGrpc; import pandora.HostGrpc; import pandora.HostProto; import pandora.SecurityGrpc; Loading Loading @@ -119,4 +120,14 @@ public final class PandoraDevice extends ExternalResource { public SecurityGrpc.SecurityStub security() { return SecurityGrpc.newStub(mChannel); } /** Get Pandora GATT service */ public GATTGrpc.GATTStub gatt() { return GATTGrpc.newStub(mChannel); } /** Get Pandora GATT blocking service */ public GATTGrpc.GATTBlockingStub gattBlocking() { return GATTGrpc.newBlockingStub(mChannel); } } pandora/server/bumble_experimental/gatt.py +89 −77 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ import asyncio import grpc import logging from bumble.att import Attribute from bumble.core import ProtocolError from bumble.device import Connection as BumbleConnection, Device, Peer from bumble.gatt import Characteristic, Descriptor, Service from bumble.gatt_client import CharacteristicProxy, ServiceProxy from bumble.pandora import utils from pandora_experimental.gatt_grpc_aio import GATTServicer Loading @@ -41,6 +43,8 @@ from pandora_experimental.gatt_pb2 import ( ReadCharacteristicResponse, ReadCharacteristicsFromUuidRequest, ReadCharacteristicsFromUuidResponse, RegisterServiceRequest, RegisterServiceResponse, WriteRequest, WriteResponse, ) Loading Loading @@ -100,9 +104,8 @@ class GATTService(GATTServicer): return WriteResponse(handle=request.handle, status=status) @utils.rpc async def DiscoverServiceByUuid( self, request: DiscoverServiceByUuidRequest, context: grpc.ServicerContext ) -> DiscoverServicesResponse: async def DiscoverServiceByUuid(self, request: DiscoverServiceByUuidRequest, context: grpc.ServicerContext) -> DiscoverServicesResponse: connection_handle = int.from_bytes(request.connection.cookie.value, 'big') logging.info(f"DiscoverServiceByUuid: {connection_handle}") Loading @@ -119,8 +122,7 @@ class GATTService(GATTServicer): await asyncio.gather(*(feed_service(service) for service in services)) return DiscoverServicesResponse( services=[ return DiscoverServicesResponse(services=[ GattService( handle=service.handle, type=int.from_bytes(bytes(service.type), 'little'), Loading @@ -136,21 +138,16 @@ class GATTService(GATTServicer): handle=descriptor.handle, # type: ignore permissions=0, # TODO uuid=str(descriptor.type), # type: ignore ) for descriptor in characteristic.descriptors # type: ignore ) for descriptor in characteristic.descriptors # type: ignore ], ) for characteristic in service.characteristics # type: ignore ) for characteristic in service.characteristics # type: ignore ], ) for service in services ] ) ) for service in services ]) @utils.rpc async def DiscoverServices( self, request: DiscoverServicesRequest, context: grpc.ServicerContext ) -> DiscoverServicesResponse: async def DiscoverServices(self, request: DiscoverServicesRequest, context: grpc.ServicerContext) -> DiscoverServicesResponse: connection_handle = int.from_bytes(request.connection.cookie.value, 'big') logging.info(f"DiscoverServices: {connection_handle}") Loading @@ -166,8 +163,7 @@ class GATTService(GATTServicer): await asyncio.gather(*(feed_service(service) for service in services)) return DiscoverServicesResponse( services=[ return DiscoverServicesResponse(services=[ GattService( handle=service.handle, type=int.from_bytes(bytes(service.type), 'little'), Loading @@ -183,16 +179,12 @@ class GATTService(GATTServicer): handle=descriptor.handle, # type: ignore permissions=0, # TODO uuid=str(descriptor.type), # type: ignore ) for descriptor in characteristic.descriptors # type: ignore ) for descriptor in characteristic.descriptors # type: ignore ], ) for characteristic in service.characteristics # type: ignore ) for characteristic in service.characteristics # type: ignore ], ) for service in services ] ) ) for service in services ]) # TODO: implement `DiscoverServicesSdp` Loading @@ -202,9 +194,8 @@ class GATTService(GATTServicer): return ClearCacheResponse() @utils.rpc async def ReadCharacteristicFromHandle( self, request: ReadCharacteristicRequest, context: grpc.ServicerContext ) -> ReadCharacteristicResponse: async def ReadCharacteristicFromHandle(self, request: ReadCharacteristicRequest, context: grpc.ServicerContext) -> ReadCharacteristicResponse: connection_handle = int.from_bytes(request.connection.cookie.value, 'big') logging.info(f"ReadCharacteristicFromHandle: {connection_handle}") Loading @@ -222,9 +213,8 @@ class GATTService(GATTServicer): return ReadCharacteristicResponse(value=AttValue(value=value), status=status) @utils.rpc async def ReadCharacteristicsFromUuid( self, request: ReadCharacteristicsFromUuidRequest, context: grpc.ServicerContext ) -> ReadCharacteristicsFromUuidResponse: async def ReadCharacteristicsFromUuid(self, request: ReadCharacteristicsFromUuidRequest, context: grpc.ServicerContext) -> ReadCharacteristicsFromUuidResponse: connection_handle = int.from_bytes(request.connection.cookie.value, 'big') logging.info(f"ReadCharacteristicsFromUuid: {connection_handle}") Loading @@ -237,15 +227,12 @@ class GATTService(GATTServicer): try: characteristics = await peer.read_characteristics_by_uuid(request.uuid, service_mock) # type: ignore return ReadCharacteristicsFromUuidResponse( characteristics_read=[ return ReadCharacteristicsFromUuidResponse(characteristics_read=[ ReadCharacteristicResponse( value=AttValue(value=value, handle=handle), # type: ignore status=SUCCESS, ) for handle, value in characteristics # type: ignore ] ) ) for handle, value in characteristics # type: ignore ]) except ProtocolError as e: return ReadCharacteristicsFromUuidResponse( Loading @@ -254,8 +241,8 @@ class GATTService(GATTServicer): @utils.rpc async def ReadCharacteristicDescriptorFromHandle( self, request: ReadCharacteristicDescriptorRequest, context: grpc.ServicerContext ) -> ReadCharacteristicDescriptorResponse: self, request: ReadCharacteristicDescriptorRequest, context: grpc.ServicerContext) -> ReadCharacteristicDescriptorResponse: connection_handle = int.from_bytes(request.connection.cookie.value, 'big') logging.info(f"ReadCharacteristicDescriptorFromHandle: {connection_handle}") Loading @@ -271,3 +258,28 @@ class GATTService(GATTServicer): status = e.error_code # type: ignore return ReadCharacteristicDescriptorResponse(value=AttValue(value=value), status=status) @utils.rpc def RegisterService(self, request: RegisterServiceRequest, context: grpc.ServicerContext) -> RegisterServiceResponse: logging.info(f"RegisterService") serviceUUID = request.service.uuid characteristics = [ Characteristic( properties=Characteristic.Properties(characteristicParam.properties), permissions=Attribute.Permissions(characteristicParam.permissions), uuid=characteristicParam.uuid, descriptors=[ Descriptor( attribute_type=descParam.uuid, permissions=Attribute.Permissions(descParam.permissions), ) for descParam in characteristicParam.descriptors ], ) for characteristicParam in request.service.characteristics ] service = Service(serviceUUID, characteristics) self.device.add_service(service) # type: ignore[no-untyped-call] logging.info(f"RegisterService complete") return RegisterServiceResponse() Loading
framework/tests/bumble/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ android_test_helper_app { "opencensus-java-contrib-grpc-metrics", "pandora_experimental-grpc-java", "pandora_experimental-proto-java", "truth-java8-extension", ], // Include all test java and kotlin files. Loading
framework/tests/bumble/src/android/bluetooth/GattClientTest.java +124 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.bluetooth; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth8.assertThat; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.any; Loading Loading @@ -45,7 +46,11 @@ import org.mockito.InOrder; import org.mockito.invocation.Invocation; import java.util.Collection; import java.util.UUID; import pandora.GattProto.GattCharacteristicParams; import pandora.GattProto.GattServiceParams; import pandora.GattProto.RegisterServiceRequest; import pandora.HostProto.AdvertiseRequest; import pandora.HostProto.AdvertiseResponse; import pandora.HostProto.OwnAddressType; Loading @@ -57,6 +62,8 @@ public class GattClientTest { private static final int MTU_REQUESTED = 23; private static final int ANOTHER_MTU_REQUESTED = 42; private static final UUID GAP_UUID = UUID.fromString("00001800-0000-1000-8000-00805f9b34fb"); @ClassRule public static final AdoptShellPermissionsRule PERM = new AdoptShellPermissionsRule(); @Rule public final PandoraDevice mBumble = new PandoraDevice(); Loading Loading @@ -131,6 +138,123 @@ public class GattClientTest { verifyNoMoreInteractions(gattCallback); } @Test public void clientGattDiscoverServices() throws Exception { BluetoothGattCallback gattCallback = mock(BluetoothGattCallback.class); BluetoothGatt gatt = connectGattAndWaitConnection(gattCallback); try { verify(gattCallback, timeout(1000)) .onConnectionStateChange(any(), anyInt(), eq(BluetoothProfile.STATE_CONNECTED)); gatt.discoverServices(); verify(gattCallback, timeout(10000)) .onServicesDiscovered(any(), eq(BluetoothGatt.GATT_SUCCESS)); assertThat(gatt.getServices().stream().map(BluetoothGattService::getUuid)) .contains(GAP_UUID); } finally { disconnectAndWaitDisconnection(gatt, gattCallback); } } @Test public void clientGattReadCharacteristics() throws Exception { BluetoothGattCallback gattCallback = mock(BluetoothGattCallback.class); BluetoothGatt gatt = connectGattAndWaitConnection(gattCallback); try { verify(gattCallback, timeout(1000)) .onConnectionStateChange(any(), anyInt(), eq(BluetoothProfile.STATE_CONNECTED)); gatt.discoverServices(); verify(gattCallback, timeout(10000)) .onServicesDiscovered(any(), eq(BluetoothGatt.GATT_SUCCESS)); BluetoothGattService firstService = gatt.getServices().get(0); BluetoothGattCharacteristic firstCharacteristic = firstService.getCharacteristics().get(0); gatt.readCharacteristic(firstCharacteristic); verify(gattCallback, timeout(5000)).onCharacteristicRead(any(), any(), any(), anyInt()); } finally { disconnectAndWaitDisconnection(gatt, gattCallback); } } @Test public void clientGattWriteCharacteristic() throws Exception { registerWritableGattService(); BluetoothGattCallback gattCallback = mock(BluetoothGattCallback.class); BluetoothGatt gatt = connectGattAndWaitConnection(gattCallback); try { verify(gattCallback, timeout(1000)) .onConnectionStateChange(any(), anyInt(), eq(BluetoothProfile.STATE_CONNECTED)); gatt.discoverServices(); verify(gattCallback, timeout(10000)) .onServicesDiscovered(any(), eq(BluetoothGatt.GATT_SUCCESS)); BluetoothGattCharacteristic characteristic = null; outer: for (BluetoothGattService candidateService : gatt.getServices()) { for (BluetoothGattCharacteristic candidateCharacteristic : candidateService.getCharacteristics()) { if ((candidateCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0) { characteristic = candidateCharacteristic; break outer; } } } byte[] newValue = new byte[] {13}; gatt.writeCharacteristic( characteristic, newValue, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); verify(gattCallback, timeout(5000)) .onCharacteristicWrite( any(), eq(characteristic), eq(BluetoothGatt.GATT_SUCCESS)); } finally { disconnectAndWaitDisconnection(gatt, gattCallback); } } private void registerWritableGattService() { String characteristicUuidString = "11111111-1111-1111-1111-111111111111"; String serviceUuidString = "00000000-0000-0000-0000-000000000000"; GattCharacteristicParams characteristicParams = GattCharacteristicParams.newBuilder() .setProperties(BluetoothGattCharacteristic.PROPERTY_WRITE) .setUuid(characteristicUuidString) .build(); GattServiceParams serviceParams = GattServiceParams.newBuilder() .addCharacteristics(characteristicParams) .setUuid(serviceUuidString) .build(); RegisterServiceRequest request = RegisterServiceRequest.newBuilder().setService(serviceParams).build(); mBumble.gattBlocking().registerService(request); } private void advertiseWithBumble() { AdvertiseRequest request = AdvertiseRequest.newBuilder() Loading
framework/tests/bumble/src/android/bluetooth/PandoraDevice.java +11 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import org.junit.rules.ExternalResource; import java.util.concurrent.TimeUnit; import pandora.DckGrpc; import pandora.GATTGrpc; import pandora.HostGrpc; import pandora.HostProto; import pandora.SecurityGrpc; Loading Loading @@ -119,4 +120,14 @@ public final class PandoraDevice extends ExternalResource { public SecurityGrpc.SecurityStub security() { return SecurityGrpc.newStub(mChannel); } /** Get Pandora GATT service */ public GATTGrpc.GATTStub gatt() { return GATTGrpc.newStub(mChannel); } /** Get Pandora GATT blocking service */ public GATTGrpc.GATTBlockingStub gattBlocking() { return GATTGrpc.newBlockingStub(mChannel); } }
pandora/server/bumble_experimental/gatt.py +89 −77 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ import asyncio import grpc import logging from bumble.att import Attribute from bumble.core import ProtocolError from bumble.device import Connection as BumbleConnection, Device, Peer from bumble.gatt import Characteristic, Descriptor, Service from bumble.gatt_client import CharacteristicProxy, ServiceProxy from bumble.pandora import utils from pandora_experimental.gatt_grpc_aio import GATTServicer Loading @@ -41,6 +43,8 @@ from pandora_experimental.gatt_pb2 import ( ReadCharacteristicResponse, ReadCharacteristicsFromUuidRequest, ReadCharacteristicsFromUuidResponse, RegisterServiceRequest, RegisterServiceResponse, WriteRequest, WriteResponse, ) Loading Loading @@ -100,9 +104,8 @@ class GATTService(GATTServicer): return WriteResponse(handle=request.handle, status=status) @utils.rpc async def DiscoverServiceByUuid( self, request: DiscoverServiceByUuidRequest, context: grpc.ServicerContext ) -> DiscoverServicesResponse: async def DiscoverServiceByUuid(self, request: DiscoverServiceByUuidRequest, context: grpc.ServicerContext) -> DiscoverServicesResponse: connection_handle = int.from_bytes(request.connection.cookie.value, 'big') logging.info(f"DiscoverServiceByUuid: {connection_handle}") Loading @@ -119,8 +122,7 @@ class GATTService(GATTServicer): await asyncio.gather(*(feed_service(service) for service in services)) return DiscoverServicesResponse( services=[ return DiscoverServicesResponse(services=[ GattService( handle=service.handle, type=int.from_bytes(bytes(service.type), 'little'), Loading @@ -136,21 +138,16 @@ class GATTService(GATTServicer): handle=descriptor.handle, # type: ignore permissions=0, # TODO uuid=str(descriptor.type), # type: ignore ) for descriptor in characteristic.descriptors # type: ignore ) for descriptor in characteristic.descriptors # type: ignore ], ) for characteristic in service.characteristics # type: ignore ) for characteristic in service.characteristics # type: ignore ], ) for service in services ] ) ) for service in services ]) @utils.rpc async def DiscoverServices( self, request: DiscoverServicesRequest, context: grpc.ServicerContext ) -> DiscoverServicesResponse: async def DiscoverServices(self, request: DiscoverServicesRequest, context: grpc.ServicerContext) -> DiscoverServicesResponse: connection_handle = int.from_bytes(request.connection.cookie.value, 'big') logging.info(f"DiscoverServices: {connection_handle}") Loading @@ -166,8 +163,7 @@ class GATTService(GATTServicer): await asyncio.gather(*(feed_service(service) for service in services)) return DiscoverServicesResponse( services=[ return DiscoverServicesResponse(services=[ GattService( handle=service.handle, type=int.from_bytes(bytes(service.type), 'little'), Loading @@ -183,16 +179,12 @@ class GATTService(GATTServicer): handle=descriptor.handle, # type: ignore permissions=0, # TODO uuid=str(descriptor.type), # type: ignore ) for descriptor in characteristic.descriptors # type: ignore ) for descriptor in characteristic.descriptors # type: ignore ], ) for characteristic in service.characteristics # type: ignore ) for characteristic in service.characteristics # type: ignore ], ) for service in services ] ) ) for service in services ]) # TODO: implement `DiscoverServicesSdp` Loading @@ -202,9 +194,8 @@ class GATTService(GATTServicer): return ClearCacheResponse() @utils.rpc async def ReadCharacteristicFromHandle( self, request: ReadCharacteristicRequest, context: grpc.ServicerContext ) -> ReadCharacteristicResponse: async def ReadCharacteristicFromHandle(self, request: ReadCharacteristicRequest, context: grpc.ServicerContext) -> ReadCharacteristicResponse: connection_handle = int.from_bytes(request.connection.cookie.value, 'big') logging.info(f"ReadCharacteristicFromHandle: {connection_handle}") Loading @@ -222,9 +213,8 @@ class GATTService(GATTServicer): return ReadCharacteristicResponse(value=AttValue(value=value), status=status) @utils.rpc async def ReadCharacteristicsFromUuid( self, request: ReadCharacteristicsFromUuidRequest, context: grpc.ServicerContext ) -> ReadCharacteristicsFromUuidResponse: async def ReadCharacteristicsFromUuid(self, request: ReadCharacteristicsFromUuidRequest, context: grpc.ServicerContext) -> ReadCharacteristicsFromUuidResponse: connection_handle = int.from_bytes(request.connection.cookie.value, 'big') logging.info(f"ReadCharacteristicsFromUuid: {connection_handle}") Loading @@ -237,15 +227,12 @@ class GATTService(GATTServicer): try: characteristics = await peer.read_characteristics_by_uuid(request.uuid, service_mock) # type: ignore return ReadCharacteristicsFromUuidResponse( characteristics_read=[ return ReadCharacteristicsFromUuidResponse(characteristics_read=[ ReadCharacteristicResponse( value=AttValue(value=value, handle=handle), # type: ignore status=SUCCESS, ) for handle, value in characteristics # type: ignore ] ) ) for handle, value in characteristics # type: ignore ]) except ProtocolError as e: return ReadCharacteristicsFromUuidResponse( Loading @@ -254,8 +241,8 @@ class GATTService(GATTServicer): @utils.rpc async def ReadCharacteristicDescriptorFromHandle( self, request: ReadCharacteristicDescriptorRequest, context: grpc.ServicerContext ) -> ReadCharacteristicDescriptorResponse: self, request: ReadCharacteristicDescriptorRequest, context: grpc.ServicerContext) -> ReadCharacteristicDescriptorResponse: connection_handle = int.from_bytes(request.connection.cookie.value, 'big') logging.info(f"ReadCharacteristicDescriptorFromHandle: {connection_handle}") Loading @@ -271,3 +258,28 @@ class GATTService(GATTServicer): status = e.error_code # type: ignore return ReadCharacteristicDescriptorResponse(value=AttValue(value=value), status=status) @utils.rpc def RegisterService(self, request: RegisterServiceRequest, context: grpc.ServicerContext) -> RegisterServiceResponse: logging.info(f"RegisterService") serviceUUID = request.service.uuid characteristics = [ Characteristic( properties=Characteristic.Properties(characteristicParam.properties), permissions=Attribute.Permissions(characteristicParam.permissions), uuid=characteristicParam.uuid, descriptors=[ Descriptor( attribute_type=descParam.uuid, permissions=Attribute.Permissions(descParam.permissions), ) for descParam in characteristicParam.descriptors ], ) for characteristicParam in request.service.characteristics ] service = Service(serviceUUID, characteristics) self.device.add_service(service) # type: ignore[no-untyped-call] logging.info(f"RegisterService complete") return RegisterServiceResponse()