diff options
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | blueberry/__init__.py (renamed from facade/__init__.py) | 0 | ||||
-rw-r--r-- | interact/__init__.py | 18 | ||||
-rw-r--r-- | interact/a2dp.py | 51 | ||||
-rw-r--r-- | interact/l2cap.py | 48 | ||||
-rw-r--r-- | proto/blueberry/a2dp.proto | 78 | ||||
-rw-r--r-- | proto/blueberry/host.proto | 43 | ||||
-rw-r--r-- | proto/facade/common.proto | 37 | ||||
-rw-r--r-- | proto/facade/l2cap.proto | 129 | ||||
-rw-r--r-- | proto/facade/neighbor.proto | 61 | ||||
-rw-r--r-- | proto/facade/rootservice.proto | 32 | ||||
-rwxr-xr-x | protoc-gen-custom_grpc | 9 | ||||
-rwxr-xr-x | setup.py | 6 |
13 files changed, 197 insertions, 320 deletions
@@ -1,5 +1,6 @@ virtualenv out/ __pycache__ -facade/* -!facade/__init__.py +blueberry/* +!blueberry/__init__.py +.eggs/
\ No newline at end of file diff --git a/facade/__init__.py b/blueberry/__init__.py index e69de29..e69de29 100644 --- a/facade/__init__.py +++ b/blueberry/__init__.py diff --git a/interact/__init__.py b/interact/__init__.py index 0f08de1..faa92b2 100644 --- a/interact/__init__.py +++ b/interact/__init__.py @@ -1,9 +1,19 @@ import grpc -from . import l2cap +from . import a2dp + +from blueberry.host_grpc import Host GRPC_PORT = 8999 -def run(profile: str, interaction_id: str, pts_addr: bytes): +def run(profile: str, interaction_id: str, test: str, pts_addr: bytes): + channel = grpc.insecure_channel(f'localhost:{GRPC_PORT}') + print(f'{profile} mmi: {interaction_id}') + if profile == "A2DP": + a2dp.interact(channel, interaction_id, test, pts_addr) + channel.close() + +def read_local_address() -> bytes: channel = grpc.insecure_channel(f'localhost:{GRPC_PORT}') - if profile == "L2CAP": - l2cap.interact(channel, interaction_id, pts_addr) + bluetooth_address = Host(channel).ReadLocalAddress(wait_for_ready=True) + channel.close() + return bluetooth_address.address diff --git a/interact/a2dp.py b/interact/a2dp.py new file mode 100644 index 0000000..66d46de --- /dev/null +++ b/interact/a2dp.py @@ -0,0 +1,51 @@ +from grpc import Channel + +from blueberry.a2dp_grpc import A2DP +from blueberry.host_grpc import Host + +from blueberry.a2dp_pb2 import Sink, Source +from blueberry.host_pb2 import Connection + +_connection: Connection = None; +_sink: Sink = None; +_source: Source = None; + +def interact(channel: Channel, interaction_id: str, test: str, pts_addr: bytes): + global _connection, _sink, _source; + a2dp = A2DP(channel) + host = Host(channel) + if interaction_id == "TSC_AVDTP_mmi_iut_accept_connect": + host.SetConnectable(connectable=True) + elif interaction_id == "TSC_AVDTP_mmi_iut_initiate_start": + _connection = host.Connect(address=pts_addr).connection + if "SNK" in test: + _sink = a2dp.OpenSink(connection=_connection).sink + elif interaction_id == "TSC_AVDTP_mmi_iut_initiate_out_of_range": + if not _connection: + _connection = Connection(cookie=pts_addr) + host.Disconnect(connection=_connection) + _connection = None + _sink = None + _source = None + elif interaction_id == "TSC_AVDTP_mmi_iut_accept_discover": + pass + elif interaction_id == "TSC_AVDTP_mmi_iut_initiate_set_configuration": + _connection = host.Connect(address=pts_addr).connection + if "SRC" in test: + _source = a2dp.OpenSource(connection=_connection).source + elif interaction_id == "TSC_AVDTP_mmi_iut_accept_close_stream": + pass + elif interaction_id == "TSC_AVDTP_mmi_iut_accept_get_capabilities": + pass + elif interaction_id == "TSC_AVDTP_mmi_iut_accept_set_configuration": + pass + elif interaction_id == "TSC_AVDTP_mmi_iut_accept_open_stream": + pass + elif interaction_id == "TSC_AVDTP_mmi_iut_accept_start": + pass + elif interaction_id == "TSC_AVDTP_mmi_iut_confirm_streaming": + pass + elif interaction_id == "TSC_AVDTP_mmi_iut_accept_reconnect": + pass + else: + print(f'MMI NOT IMPLEMENTED: {interaction_id}')
\ No newline at end of file diff --git a/interact/l2cap.py b/interact/l2cap.py deleted file mode 100644 index 4ee8e7d..0000000 --- a/interact/l2cap.py +++ /dev/null @@ -1,48 +0,0 @@ -import os - -from grpc import Channel - -from facade import l2cap_grpc, neighbor_grpc -from facade.common_pb2 import BluetoothAddress -from facade.l2cap_pb2 import RetransmissionFlowControlMode - -PSM = 1 # TODO: Add it to either utils.py or config file - -def interact(channel: Channel, interaction_id: str, pts_addr: bytes): - print(f'mmi_id: {interaction_id}') - addr = BluetoothAddress(address=pts_addr) - l2cap = l2cap_grpc.L2capClassicModuleFacade(channel) - neighbor = neighbor_grpc.NeighborFacade(channel) - if interaction_id == "MMI_TESTER_ENABLE_CONNECTION": - neighbor.EnablePageScan(enabled=True) - l2cap.SetDynamicChannel( - psm=PSM, - enable=True, - retransmission_mode=RetransmissionFlowControlMode.BASIC - ) - if interaction_id == "MMI_IUT_SEND_CONFIG_REQ": - pass - if interaction_id == "MMI_IUT_SEND_L2CAP_DATA": - payload = b'\x00' + os.urandom(40) + b'\x00' - l2cap.SendDynamicChannelPacket( - remote=addr, - psm=PSM, - payload=payload - ) - if interaction_id == "MMI_IUT_INITIATE_ACL_CONNECTION": - l2cap.SetDynamicChannel( - psm=PSM, - enable=True, - retransmission_mode=RetransmissionFlowControlMode.BASIC - ) - l2cap.OpenChannel( - remote=addr, - psm=PSM, - mode=RetransmissionFlowControlMode.BASIC - ) - if interaction_id == ("MMI_IUT_DISABLE_CONNECTION" or "MMI_IUT_SEND_DISCONNECT_RSP"): - l2cap.CloseChannel(psm=PSM) - if interaction_id == "MMI_IUT_SEND_ACL_DISCONNECTON": - pass - if interaction_id == "MMI_IUT_SEND_CONFIG_RSP": - pass diff --git a/proto/blueberry/a2dp.proto b/proto/blueberry/a2dp.proto new file mode 100644 index 0000000..803ab0a --- /dev/null +++ b/proto/blueberry/a2dp.proto @@ -0,0 +1,78 @@ +syntax = "proto3"; + +package blueberry; + +import "blueberry/host.proto"; + +service A2DP { + rpc OpenSource(OpenSourceRequest) returns (OpenSourceResponse); + rpc OpenSink(OpenSinkRequest) returns (OpenSinkResponse); + rpc Start(StartRequest) returns (StartResponse); + rpc Suspend(SuspendRequest) returns (SuspendResponse); + rpc Close(CloseRequest) returns (CloseResponse); + rpc Abort(AbortRequest) returns (AbortResponse); +} + +message Source { + bytes cookie = 1; +} + +message Sink { + bytes cookie = 1; +} + +message OpenSourceRequest { + Connection connection = 1; +} + +message OpenSourceResponse { + oneof response { + Source source = 1; + } +} + +message OpenSinkRequest { + Connection connection = 1; +} + +message OpenSinkResponse { + oneof response { + Sink sink = 1; + } +} + +message StartRequest { + oneof response { + Sink sink = 1; + Source source = 2; + } +} + +message StartResponse {} + +message SuspendRequest { + oneof response { + Sink sink = 1; + Source source = 2; + } +} + +message SuspendResponse {} + +message CloseRequest { + oneof response { + Sink sink = 1; + Source source = 2; + } +} + +message CloseResponse {} + +message AbortRequest { + oneof response { + Sink sink = 1; + Source source = 2; + } +} + +message AbortResponse {}
\ No newline at end of file diff --git a/proto/blueberry/host.proto b/proto/blueberry/host.proto new file mode 100644 index 0000000..85697e5 --- /dev/null +++ b/proto/blueberry/host.proto @@ -0,0 +1,43 @@ +syntax = "proto3"; + +package blueberry; + +import "google/protobuf/empty.proto"; + +service Host { + rpc Reset(google.protobuf.Empty) returns (google.protobuf.Empty); + rpc Connect(ConnectRequest) returns (ConnectResponse); + rpc Disconnect(DisconnectRequest) returns (DisconnectResponse); + rpc ReadLocalAddress(google.protobuf.Empty) returns (ReadLocalAddressResponse); + rpc SetConnectable(SetConnectableRequest) returns (SetConnectableResponse); +} + +message Connection { + bytes cookie = 1; +} + +message ConnectRequest { + bytes address = 1; +} + +message ConnectResponse { + oneof response { + Connection connection = 1; + } +} + +message DisconnectRequest { + Connection connection = 1; +} + +message DisconnectResponse {} + +message ReadLocalAddressResponse { + bytes address = 1; +} + +message SetConnectableRequest { + bool connectable = 1; +} + +message SetConnectableResponse {}
\ No newline at end of file diff --git a/proto/facade/common.proto b/proto/facade/common.proto deleted file mode 100644 index 222caca..0000000 --- a/proto/facade/common.proto +++ /dev/null @@ -1,37 +0,0 @@ -syntax = "proto3"; - -package bluetooth.facade; - -message Empty {} - -message Data { - bytes payload = 1; -} - -message BluetoothAddress { - bytes address = 1; -} - -enum BluetoothAddressTypeEnum { - PUBLIC_DEVICE_ADDRESS = 0x0; - RANDOM_DEVICE_ADDRESS = 0x1; - PUBLIC_IDENTITY_ADDRESS = 0x2; - RANDOM_IDENTITY_ADDRESS = 0x3; -} - -enum BluetoothOwnAddressTypeEnum { - USE_PUBLIC_DEVICE_ADDRESS = 0x0; - USE_RANDOM_DEVICE_ADDRESS = 0x1; - RESOLVABLE_OR_PUBLIC_ADDRESS = 0x2; - RESOLVABLE_OR_RANDOM_ADDRESS = 0x3; -} - -message BluetoothAddressWithType { - BluetoothAddress address = 1; - BluetoothAddressTypeEnum type = 2; -} - -enum BluetoothPeerAddressTypeEnum { - PUBLIC_DEVICE_OR_IDENTITY_ADDRESS = 0x0; - RANDOM_DEVICE_OR_IDENTITY_ADDRESS = 0x1; -} diff --git a/proto/facade/l2cap.proto b/proto/facade/l2cap.proto deleted file mode 100644 index 7951ec4..0000000 --- a/proto/facade/l2cap.proto +++ /dev/null @@ -1,129 +0,0 @@ -syntax = "proto3"; - -package bluetooth.l2cap.classic; - -import "facade/common.proto"; - -service L2capClassicModuleFacade { - rpc FetchConnectionComplete(facade.Empty) returns (stream ConnectionCompleteEvent) { - // Testing Android Bluetooth stack only. Optional for other stack. - } - rpc FetchConnectionClose(facade.Empty) returns (stream ConnectionCloseEvent) { - // Testing Android Bluetooth stack only. Optional for other stack. - } - rpc OpenChannel(OpenChannelRequest) returns (facade.Empty) {} - rpc CloseChannel(CloseChannelRequest) returns (facade.Empty) {} - rpc FetchL2capData(facade.Empty) returns (stream L2capPacket) {} - rpc SetDynamicChannel(SetEnableDynamicChannelRequest) returns (facade.Empty) {} - rpc SendDynamicChannelPacket(DynamicChannelPacket) returns (facade.Empty) {} - rpc SetTrafficPaused(SetTrafficPausedRequest) returns (facade.Empty) {} - rpc GetChannelQueueDepth(facade.Empty) returns (GetChannelQueueDepthResponse) { - // Get the buffer size of channel queue end for L2CAP user (how many packets we can buffer - // before L2CAP user dequeues. - } - rpc InitiateConnectionForSecurity(facade.BluetoothAddress) returns (facade.Empty) {} - rpc FetchSecurityConnectionEvents(facade.Empty) returns (stream LinkSecurityInterfaceCallbackEvent) {} - rpc SecurityLinkEnsureAuthenticated(facade.BluetoothAddress) returns (facade.Empty) {} - rpc SecurityLinkHold(facade.BluetoothAddress) returns (facade.Empty) {} - rpc SecurityLinkDisconnect(facade.BluetoothAddress) returns (facade.Empty) {} - rpc SecurityLinkRelease(facade.BluetoothAddress) returns (facade.Empty) {} -} - -enum LinkSecurityInterfaceCallbackEventType { - ON_CONNECTED = 0; - ON_DISCONNECTED = 1; - ON_AUTHENTICATION_COMPLETE = 2; - ON_ENCRYPTION_CHANGE = 3; - ON_READ_REMOTE_VERSION_INFO = 4; - ON_READ_REMOTE_EXTENDED_FEATURES = 5; -} - -message LinkSecurityInterfaceCallbackEvent { - facade.BluetoothAddress address = 1; - LinkSecurityInterfaceCallbackEventType event_type = 2; -} - -message RegisterChannelRequest { - uint32 channel = 1; -} - -message ConnectionCompleteEvent { - facade.BluetoothAddress remote = 1; -} - -message ConnectionCloseEvent { - facade.BluetoothAddress remote = 1; - uint32 reason = 2; -} - -enum RetransmissionFlowControlMode { - BASIC = 0; - ERTM = 1; - ERTM_OPTIONAL = 2; -} - -message OpenChannelRequest { - facade.BluetoothAddress remote = 1; - uint32 psm = 2; - RetransmissionFlowControlMode mode = 3; -} - -message CloseChannelRequest { - uint32 psm = 1; -} - -enum ChannelSignalEventType { - OPEN = 0; - CLOSE = 1; - CONFIGURE = 2; -} - -message ChannelSignalEvent { - uint32 cid = 1; - ChannelSignalEventType type = 2; -} - -enum SendL2capPacketResultType { - OK = 0; - BAD_CID = 1; -} - -message SendL2capPacketResult { - SendL2capPacketResultType result_type = 1; -} - -message L2capPacket { - oneof channel_type { - uint32 psm = 1; - uint32 fixed_cid = 2; - } - bytes payload = 3; -} - -message SetEnableDynamicChannelRequest { - uint32 psm = 1; - bool enable = 2; - RetransmissionFlowControlMode retransmission_mode = 3; -} - -message DynamicChannelPacket { - facade.BluetoothAddress remote = 1; - uint32 psm = 2; - bytes payload = 3; -} - -message SetTrafficPausedRequest { - bool paused = 1; - uint32 psm = 2; -} - -message GetChannelQueueDepthResponse { - uint32 size = 1; -} - -enum ClassicSecurityPolicy { - ENCRYPTED_TRANSPORT = 0; - AUTHENTICATED_ENCRYPTED_TRANSPORT = 1; - BEST = 2; - _SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK = 3; -} diff --git a/proto/facade/neighbor.proto b/proto/facade/neighbor.proto deleted file mode 100644 index 03defe1..0000000 --- a/proto/facade/neighbor.proto +++ /dev/null @@ -1,61 +0,0 @@ -syntax = "proto3"; - -package bluetooth.neighbor; - -import "facade/common.proto"; - -service NeighborFacade { - rpc SetConnectability(EnableMsg) returns (facade.Empty) {} - rpc SetDiscoverability(DiscoverabilitiyMsg) returns (facade.Empty) {} - rpc SetInquiryMode(InquiryMsg) returns (stream InquiryResultMsg) { - // Sets inquiry mode and fetches inquiry result HCI packet - } - rpc ReadRemoteName(RemoteNameRequestMsg) returns (facade.Empty) {} - rpc GetRemoteNameEvents(facade.Empty) returns (stream RemoteNameResponseMsg) {} - // TODO: Should we use a blocking call for ReadRemoteName instead? (Note: blocking model may not work for GD stack) - rpc EnableInquiryScan(EnableMsg) returns (facade.Empty) {} - rpc EnablePageScan(EnableMsg) returns (facade.Empty) {} -} - -message EnableMsg { - bool enabled = 1; -} - -enum DiscoverabilityMode { - OFF = 0; - LIMITED = 1; - GENERAL = 2; -} - -message DiscoverabilitiyMsg { - DiscoverabilityMode mode = 1; -} - -enum ResultMode { - STANDARD = 0; - RSSI = 1; - EXTENDED = 2; -} - -message InquiryMsg { - DiscoverabilityMode inquiry_mode = 1; - ResultMode result_mode = 2; - uint32 length_1_28s = 3; - uint32 max_results = 4; // 0 is unlimited -} - -message InquiryResultMsg { - bytes packet = 1; -} - -message RemoteNameRequestMsg { - bytes address = 1; - uint32 page_scan_repetition_mode = 2; // r0, r1, r2 - uint32 clock_offset = 3; -} - -message RemoteNameResponseMsg { - uint32 status = 1; - bytes address = 2; - bytes name = 3; -} diff --git a/proto/facade/rootservice.proto b/proto/facade/rootservice.proto deleted file mode 100644 index 63ec41a..0000000 --- a/proto/facade/rootservice.proto +++ /dev/null @@ -1,32 +0,0 @@ -syntax = "proto3"; -package bluetooth.facade; - -import "facade/common.proto"; - -service RootFacade { - rpc StartStack(StartStackRequest) returns (StartStackResponse) {} - rpc StopStack(StopStackRequest) returns (StopStackResponse) {} -} - -enum BluetoothModule { - HAL = 0; - HCI = 1; - HCI_INTERFACES = 2; - L2CAP = 3; - SECURITY = 4; - SHIM = 5; -} - -message StartStackRequest { - BluetoothModule module_under_test = 1; -} - -message StartStackResponse {} - -message StopStackRequest {} - -message StopStackResponse {} - -service ReadOnlyProperty { - rpc ReadLocalAddress(Empty) returns (BluetoothAddress) {} -} diff --git a/protoc-gen-custom_grpc b/protoc-gen-custom_grpc index a45dc19..3a0c8be 100755 --- a/protoc-gen-custom_grpc +++ b/protoc-gen-custom_grpc @@ -10,11 +10,14 @@ def eprint(*args, **kwargs): request = CodeGeneratorRequest.FromString(sys.stdin.buffer.read()) +def has_type(proto_file, type_name): + return any(filter(lambda x: x.name == type_name, proto_file.message_type)) def import_type(imports, type): + # eprint(f'type: {type} request: {request.proto_file}') package = type[1:type.rindex('.')] type_name = type[type.rindex('.')+1:] - file = next(filter(lambda x: x.package == package, request.proto_file)) + file = next(filter(lambda x: x.package == package and has_type(x, type_name), request.proto_file)) python_path = file.name.replace('.proto', '').replace('/', '.') as_name = python_path.replace('.', '_dot_') + '__pb2' module_path = python_path[:python_path.rindex('.')] @@ -34,12 +37,12 @@ def generate_method(imports, file, service, method): raise Error("TODO: stream as input type") return ( - f'def {method.name}(self, **kwargs):\n' + f'def {method.name}(self, wait_for_ready=None, **kwargs):\n' f' return self.channel.{input_mode}_{output_mode}(\n' f" '/{file.package}.{service.name}/{method.name}',\n" f' request_serializer={input_type}.SerializeToString,\n' f' response_deserializer={output_type}.FromString\n' - f' )({input_type}(**kwargs))' + f' )({input_type}(**kwargs), wait_for_ready=wait_for_ready)' ).split('\n') @@ -27,10 +27,8 @@ class BuildGrpc(Command): '-I=proto', '--python_out=.', '--custom_grpc_out=.', - 'facade/l2cap.proto', - 'facade/neighbor.proto', - 'facade/common.proto', - 'facade/rootservice.proto', + 'blueberry/host.proto', + 'blueberry/a2dp.proto' ]) |