From 4adb042422fdd1e9705672d50222c36df2d1031f Mon Sep 17 00:00:00 2001 From: Chienyuan Date: Thu, 25 Apr 2019 15:11:54 -0700 Subject: [PATCH] HAL: Add facade for fetching incoming HCI packets Adding the event stream primitives. General event stream request is defined in facade/common.proto. "grpc/grpc_event_stream.h" provides helper class templates GrpcEventStreamCallback and GrpcEventStream to help user handle EventStream request without writing boilerplate code. Test: cert/run_cert.sh Change-Id: I00b51fc7b1faefb7c97cc647876f41854872a415 --- gd/Android.bp | 37 +++++------------ gd/facade/common.proto | 20 ++++++++++ gd/grpc/grpc_event_stream.h | 91 ++++++++++++++++++++++++++++++++++++++++++ gd/hal/Android.bp | 7 ---- gd/hal/cert/simple_hal_test.cc | 91 ------------------------------------------ gd/hal/cert/simple_hal_test.py | 21 ++++++++++ gd/hal/facade/api.proto | 13 +++--- gd/hal/facade/facade.cc | 61 +++++++++++++++++++++++----- 8 files changed, 199 insertions(+), 142 deletions(-) create mode 100644 gd/facade/common.proto create mode 100644 gd/grpc/grpc_event_stream.h delete mode 100644 gd/hal/cert/simple_hal_test.cc diff --git a/gd/Android.bp b/gd/Android.bp index da660aba9..3dd849e7b 100644 --- a/gd/Android.bp +++ b/gd/Android.bp @@ -154,34 +154,6 @@ cc_binary { } cc_test { - name: "bluetooth_cert_test", - defaults: [ - "gd_defaults", - ], - host_supported: true, - srcs: [ - ":BluetoothCertCppClient_hci_hal", - ], - static_libs : [ - "libbluetooth_gd", - ], - shared_libs: [ - "libgrpc++_unsecure", - "libprotobuf-cpp-full", - ], - generated_headers: [ - "BluetoothGeneratedPackets_h", - "BluetoothCertFacadeGeneratedStub_h", - ], - generated_sources: [ - "BluetoothCertFacadeGeneratedStub_cc", - ], - sanitize: { - address: true, - }, -} - -cc_test { name: "bluetooth_test_gd", test_suites: ["device-tests"], defaults: [ @@ -285,6 +257,7 @@ genrule { filegroup { name: "BluetoothCertFacadeProto", srcs: [ + "facade/common.proto", "hal/facade/api.proto", ], } @@ -300,6 +273,8 @@ genrule { ":BluetoothCertFacadeProto", ], out: [ + "facade/common.grpc.pb.h", + "facade/common.pb.h", "hal/facade/api.grpc.pb.h", "hal/facade/api.pb.h", ], @@ -316,6 +291,8 @@ genrule { ":BluetoothCertFacadeProto", ], out: [ + "facade/common.grpc.pb.cc", + "facade/common.pb.cc", "hal/facade/api.grpc.pb.cc", "hal/facade/api.pb.cc", ], @@ -329,6 +306,7 @@ genrule { ], cmd: "$(location aprotoc) -Isystem/bt/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-python-plugin) $(in) --grpc_out=$(genDir) --python_out=$(genDir); " + "touch $(genDir)/__init__.py; " + + "touch $(genDir)/facade/__init__.py; " + "touch $(genDir)/hal/__init__.py; " + "touch $(genDir)/hal/facade/__init__.py; ", srcs: [ @@ -336,6 +314,9 @@ genrule { ], out: [ "__init__.py", + "facade/__init__.py", + "facade/common_pb2_grpc.py", + "facade/common_pb2.py", "hal/__init__.py", "hal/facade/__init__.py", "hal/facade/api_pb2_grpc.py", diff --git a/gd/facade/common.proto b/gd/facade/common.proto new file mode 100644 index 000000000..e3ee429cd --- /dev/null +++ b/gd/facade/common.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; + +package bluetooth.facade; + +enum EventSubscriptionMode { + UNCHANGED = 0; + SUBSCRIBE = 1; + UNSUBSCRIBE = 2; +} + +enum EventFetchMode { + NONE = 0; + ALL_CURRENT = 1; + AT_LEAST_ONE = 2; +} + +message EventStreamRequest { + EventSubscriptionMode subscription_mode = 1; + EventFetchMode fetch_mode = 2; +} diff --git a/gd/grpc/grpc_event_stream.h b/gd/grpc/grpc_event_stream.h new file mode 100644 index 000000000..0941ecc7a --- /dev/null +++ b/gd/grpc/grpc_event_stream.h @@ -0,0 +1,91 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "common/blocking_queue.h" +#include "facade/common.pb.h" +#include "os/log.h" + +namespace bluetooth { +namespace grpc { + +template +class GrpcEventStreamCallback { + public: + virtual ~GrpcEventStreamCallback() = default; + virtual void OnSubscribe() {} + virtual void OnUnsubscribe() {} + virtual void OnWriteResponse(RES* response, const EVENT& event) = 0; +}; + +template +class GrpcEventStream { + public: + explicit GrpcEventStream(GrpcEventStreamCallback* callback) : callback_(callback) {} + + void OnIncomingEvent(const EVENT& event) { + if (subscribed_) { + event_queue_.push(event); + } + } + + ::grpc::Status HandleRequest(::grpc::ServerContext* context, const ::bluetooth::facade::EventStreamRequest* request, + ::grpc::ServerWriter* writer) { + ::bluetooth::facade::EventSubscriptionMode subscription_mode = request->subscription_mode(); + ::bluetooth::facade::EventFetchMode fetch_mode = request->fetch_mode(); + + if (subscription_mode == ::bluetooth::facade::SUBSCRIBE) { + callback_->OnSubscribe(); + subscribed_ = true; + } + + if (fetch_mode == ::bluetooth::facade::AT_LEAST_ONE) { + RES response; + EVENT event = event_queue_.take(); + callback_->OnWriteResponse(&response, event); + writer->Write(response); + } + + // fetch all current remaining items and append to AT_LEAST_ONE query if present + if (fetch_mode == ::bluetooth::facade::ALL_CURRENT || fetch_mode == ::bluetooth::facade::AT_LEAST_ONE) { + while (!event_queue_.empty()) { + RES response; + EVENT event = event_queue_.take(); + callback_->OnWriteResponse(&response, event); + writer->Write(response); + } + } + + if (subscription_mode == ::bluetooth::facade::UNSUBSCRIBE) { + subscribed_ = false; + event_queue_.clear(); + callback_->OnUnsubscribe(); + } + + return ::grpc::Status::OK; + } + + private: + common::BlockingQueue event_queue_; + GrpcEventStreamCallback* callback_; + bool subscribed_ = false; +}; + +} // namespace grpc +} // namespace bluetooth diff --git a/gd/hal/Android.bp b/gd/hal/Android.bp index cb4ca82c9..9e10c2c85 100644 --- a/gd/hal/Android.bp +++ b/gd/hal/Android.bp @@ -39,10 +39,3 @@ filegroup { "facade/facade.cc", ], } - -filegroup { - name: "BluetoothCertCppClient_hci_hal", - srcs: [ - "cert/simple_hal_test.cc", - ], -} diff --git a/gd/hal/cert/simple_hal_test.cc b/gd/hal/cert/simple_hal_test.cc deleted file mode 100644 index 8804a233d..000000000 --- a/gd/hal/cert/simple_hal_test.cc +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include -#include -#include - -#include "hal/facade/api.grpc.pb.h" -#include "hci/hci_packets.h" -#include "os/log.h" - -using grpc::ClientContext; - -namespace bluetooth { -namespace hal { -namespace cert { - -using ::bluetooth::hal::facade::HciCmdPacket; -using ::bluetooth::hal::facade::HciEvtPacket; -using ::bluetooth::hal::facade::HciTransportation; -using ::bluetooth::hal::facade::LoopbackModeSettings; - -class HalAdapterCertTest : public ::testing::Test { - protected: - void SetUp() override { - int port = 8899; - std::string channel = "localhost:" + std::to_string(port); - stub_ = HciTransportation::NewStub(grpc::CreateChannel(channel, grpc::InsecureChannelCredentials())); - } - void TearDown() override { - stub_.reset(); - } - - std::unique_ptr stub_; -}; - -TEST_F(HalAdapterCertTest, enable_loopback_mode) { - ClientContext set_loopback_mode_context; - LoopbackModeSettings settings; - settings.set_enable(true); - ::google::protobuf::Empty empty; - ::grpc::Status status = stub_->SetLoopbackMode(&set_loopback_mode_context, settings, &empty); - EXPECT_EQ(status.ok(), true); - - ClientContext register_hci_evt_context; - - auto reader = stub_->RegisterHciEvt(®ister_hci_evt_context, empty); - - auto packet = hci::DisconnectBuilder::Create(2, hci::DisconnectReason::PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED); - std::shared_ptr> packet_bytes = std::make_shared>(); - hci::BitInserter it(*packet_bytes); - packet->Serialize(it); - - std::string payload(packet_bytes->begin(), packet_bytes->end()); - - ClientContext send_hci_cmd_context; - HciCmdPacket cmd; - cmd.set_payload(payload); - status = stub_->SendHciCmd(&send_hci_cmd_context, cmd, &empty); - EXPECT_EQ(status.ok(), true); - - HciEvtPacket received_packet; - reader->Read(&received_packet); - - ClientContext unregister_hci_evt_context; - - status = stub_->UnregisterHciEvt(&unregister_hci_evt_context, empty, &empty); - EXPECT_EQ(status.ok(), true); - - // EXPECT_EQ(reader->Read(&received_packet), false); -} - -} // namespace cert -} // namespace hal -} // namespace bluetooth diff --git a/gd/hal/cert/simple_hal_test.py b/gd/hal/cert/simple_hal_test.py index f9b4547d0..8c019cef7 100644 --- a/gd/hal/cert/simple_hal_test.py +++ b/gd/hal/cert/simple_hal_test.py @@ -22,6 +22,7 @@ sys.path.append(os.environ['ANDROID_BUILD_TOP'] + '/system/bt/gd') from cert.gd_base_test import GdBaseTestClass +from facade import common_pb2 from hal.facade import api_pb2 class SimpleHalTest(GdBaseTestClass): @@ -29,3 +30,23 @@ class SimpleHalTest(GdBaseTestClass): response = self.gd_devices[0].hal.SetLoopbackMode(api_pb2.LoopbackModeSettings(enable=True)) print("Response " + str(response)) + def test_fetch_hci_event(self): + response = self.gd_devices[0].hal.SetLoopbackMode(api_pb2.LoopbackModeSettings(enable=True)) + + request = common_pb2.EventStreamRequest(subscription_mode=common_pb2.SUBSCRIBE, + fetch_mode=common_pb2.NONE) + response = self.gd_devices[0].hal.FetchHciEvent(request) + + inquiry_string = b'\x01\x04\x05\x33\x8b\x9e\x30\x01' + response = self.gd_devices[0].hal.SendHciCommand(api_pb2.HciCmdPacket(payload=inquiry_string)) + + request = common_pb2.EventStreamRequest(subscription_mode=common_pb2.UNCHANGED, + fetch_mode=common_pb2.AT_LEAST_ONE) + response = self.gd_devices[0].hal.FetchHciEvent(request) + + for event in response: + print(event.payload) + + request = common_pb2.EventStreamRequest(subscription_mode=common_pb2.UNSUBSCRIBE, + fetch_mode=common_pb2.NONE) + response = self.gd_devices[0].hal.FetchHciEvent(request) diff --git a/gd/hal/facade/api.proto b/gd/hal/facade/api.proto index 879046728..c49bb1b98 100644 --- a/gd/hal/facade/api.proto +++ b/gd/hal/facade/api.proto @@ -3,20 +3,19 @@ syntax = "proto3"; package bluetooth.hal.facade; import "google/protobuf/empty.proto"; +import "facade/common.proto"; // The HCI HAL transportation layer certification API surface definition service HciTransportation { rpc SetLoopbackMode(LoopbackModeSettings) returns (google.protobuf.Empty) {} - rpc SendHciCmd(HciCmdPacket) returns (google.protobuf.Empty) {} + rpc SendHciCommand(HciCmdPacket) returns (google.protobuf.Empty) {} rpc SendHciAcl(HciAclPacket) returns (google.protobuf.Empty) {} rpc SendHciSco(HciScoPacket) returns (google.protobuf.Empty) {} - rpc RegisterHciEvt(google.protobuf.Empty) returns (stream HciEvtPacket) {} - rpc RegisterHciAcl(google.protobuf.Empty) returns (stream HciAclPacket) {} - rpc RegisterHciSco(google.protobuf.Empty) returns (stream HciScoPacket) {} - rpc UnregisterHciEvt(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc UnregisterHciAcl(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc UnregisterHciSco(google.protobuf.Empty) returns (google.protobuf.Empty) {} + + rpc FetchHciEvent(bluetooth.facade.EventStreamRequest) returns (stream HciEvtPacket) {} + rpc FetchHciAcl(bluetooth.facade.EventStreamRequest) returns (stream HciAclPacket) {} + rpc FetchHciSco(bluetooth.facade.EventStreamRequest) returns (stream HciScoPacket) {} } message LoopbackModeSettings { diff --git a/gd/hal/facade/facade.cc b/gd/hal/facade/facade.cc index 815ff8f9f..32d7e0bcd 100644 --- a/gd/hal/facade/facade.cc +++ b/gd/hal/facade/facade.cc @@ -18,9 +18,9 @@ #include #include -#include -#include "grpc/async_grpc.h" +#include "common/blocking_queue.h" +#include "grpc/grpc_event_stream.h" #include "hal/facade/api.grpc.pb.h" #include "hal/hci_hal.h" #include "hci/hci_packets.h" @@ -39,8 +39,9 @@ class HciTransportationService : public HciTransportation::Service, public ::bluetooth::hal::HciHalCallbacks { public: - HciTransportationService(HciHal* hal) : hal_(hal) { - } + HciTransportationService(HciHal* hal) + : hal_(hal), hci_event_stream_(&hci_event_stream_callback_), hci_acl_stream_(&hci_acl_stream_callback_), + hci_sco_stream_(&hci_sco_stream_callback_) {} ::grpc::Status SetLoopbackMode(::grpc::ServerContext* context, const ::bluetooth::hal::facade::LoopbackModeSettings* request, @@ -55,8 +56,8 @@ class HciTransportationService return ::grpc::Status::OK; } - ::grpc::Status SendHciCmd(::grpc::ServerContext* context, const ::bluetooth::hal::facade::HciCmdPacket* request, - ::google::protobuf::Empty* response) override { + ::grpc::Status SendHciCommand(::grpc::ServerContext* context, const ::bluetooth::hal::facade::HciCmdPacket* request, + ::google::protobuf::Empty* response) override { std::string req_string = request->payload(); hal_->sendHciCommand(std::vector(req_string.begin(), req_string.end())); return ::grpc::Status::OK; @@ -76,19 +77,61 @@ class HciTransportationService return ::grpc::Status::OK; } + ::grpc::Status FetchHciEvent(::grpc::ServerContext* context, const ::bluetooth::facade::EventStreamRequest* request, + ::grpc::ServerWriter* writer) override { + return hci_event_stream_.HandleRequest(context, request, writer); + }; + + ::grpc::Status FetchHciAcl(::grpc::ServerContext* context, const ::bluetooth::facade::EventStreamRequest* request, + ::grpc::ServerWriter* writer) override { + return hci_acl_stream_.HandleRequest(context, request, writer); + }; + + ::grpc::Status FetchHciSco(::grpc::ServerContext* context, const ::bluetooth::facade::EventStreamRequest* request, + ::grpc::ServerWriter* writer) override { + return hci_sco_stream_.HandleRequest(context, request, writer); + }; + void hciEventReceived(bluetooth::hal::HciPacket event) override { - // TODO + std::string response_str = std::string(event.begin(), event.end()); + hci_event_stream_.OnIncomingEvent(event); } void aclDataReceived(bluetooth::hal::HciPacket data) override { - // TODO + hci_acl_stream_.OnIncomingEvent(data); } void scoDataReceived(bluetooth::hal::HciPacket data) override { - // TODO + hci_sco_stream_.OnIncomingEvent(data); } + private: HciHal* hal_; + + class HciEventStreamCallback : public ::bluetooth::grpc::GrpcEventStreamCallback { + public: + void OnWriteResponse(HciEvtPacket* response, const HciPacket& event) override { + std::string response_str = std::string(event.begin(), event.end()); + response->set_payload(std::string(event.begin(), event.end())); + } + } hci_event_stream_callback_; + ::bluetooth::grpc::GrpcEventStream hci_event_stream_; + + class HciAclStreamCallback : public ::bluetooth::grpc::GrpcEventStreamCallback { + public: + void OnWriteResponse(HciAclPacket* response, const HciPacket& event) override { + response->set_payload(std::string(event.begin(), event.end())); + } + } hci_acl_stream_callback_; + ::bluetooth::grpc::GrpcEventStream hci_acl_stream_; + + class HciScoStreamCallback : public ::bluetooth::grpc::GrpcEventStreamCallback { + public: + void OnWriteResponse(HciScoPacket* response, const HciPacket& event) override { + response->set_payload(std::string(event.begin(), event.end())); + } + } hci_sco_stream_callback_; + ::bluetooth::grpc::GrpcEventStream hci_sco_stream_; }; void HalFacadeModule::ListDependencies(ModuleList* list) { -- 2.11.0