From 7b003eb97a032ae16fb5e793bd641635319724c4 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Mon, 8 Apr 2019 16:18:21 -0700 Subject: [PATCH] Cert for HAL adapter Introducing cert framework and a simple test case for HCI HAL transportation adapter Test-prerequisite: gRPC python. Install from pip (https://grpc.io/blog/installation) Test: 1. start HCI rootcanal or HIDL transport $ANDROID_HOST_OUT/nativetest64/root-canal/root-canal 2. start stack with facade $ANDROID_HOST_OUT/bin/stack_with_facade 3. run a simple test case $ANDROID_HOST_OUT/nativetest64/bluetooth_cert_test/bluetooth_cert_test Or: One-liner: python cert/run_cert.py Change-Id: I3cf2f54605ce2709d1c149b1221dedc8dc5dca92 --- gd/Android.bp | 124 +++++++- gd/cert/run_cert.py | 125 ++++++++ gd/facade/facade_manager.cc | 108 +++++++ gd/facade/facade_manager.h | 97 ++++++ gd/facade/stack_with_grpc_main.cc | 61 ++++ gd/grpc/async_grpc.h | 165 ++++++++++ gd/hal/Android.bp | 14 + gd/hal/cert/simple_hal_test.cc | 92 ++++++ gd/hal/facade/api.proto | 40 +++ gd/hal/facade/facade.cc | 331 +++++++++++++++++++++ gd/hal/facade/facade.h | 74 +++++ gd/stack_manager.cc | 1 + .../test_vendor_lib/desktop/test_environment.cc | 2 + 13 files changed, 1233 insertions(+), 1 deletion(-) create mode 100644 gd/cert/run_cert.py create mode 100644 gd/facade/facade_manager.cc create mode 100644 gd/facade/facade_manager.h create mode 100644 gd/facade/stack_with_grpc_main.cc create mode 100644 gd/grpc/async_grpc.h create mode 100644 gd/hal/cert/simple_hal_test.cc create mode 100644 gd/hal/facade/api.proto create mode 100644 gd/hal/facade/facade.cc create mode 100644 gd/hal/facade/facade.h diff --git a/gd/Android.bp b/gd/Android.bp index 4d0a55f72..307b73a39 100644 --- a/gd/Android.bp +++ b/gd/Android.bp @@ -101,7 +101,90 @@ cc_library { "stack_manager.cc", ":BluetoothCommonSources", ":BluetoothPacketSources", - ] + ], + generated_headers: [ + "BluetoothGeneratedPackets_h", + ], +} + +cc_binary { + name: "stack_with_facade", + defaults: [ + "gd_defaults", + ], + host_supported: true, + srcs: [ + "stack_manager.cc", + "facade/stack_with_grpc_main.cc", + "facade/facade_manager.cc", + ":BluetoothCertFacade_hci_hal", + ], + generated_headers: [ + "BluetoothGeneratedPackets_h", + "BluetoothCertFacadeGeneratedStub_h", + ], + generated_sources: [ + "BluetoothCertFacadeGeneratedStub_cc", + ], + static_libs: [ + "libbluetooth_gd", + ], + shared_libs: [ + "libgrpc++_unsecure", + "libprotobuf-cpp-full", + ], + target: { + android: { + shared_libs: [ + "android.hardware.bluetooth@1.0", + "libhwbinder", + "libhidlbase", + "libhidltransport", + "libutils", + ], + }, + }, + cflags: [ + "-Wno-unused-parameter", + "-Wno-implicit-fallthrough", + "-Wno-unused-result", + ], + sanitize: { + address: true, + }, +} + +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, + }, + cflags: [ + "-Wno-unused-parameter", + "-Wno-implicit-fallthrough", + "-Wno-unused-result", + ], } cc_test { @@ -203,3 +286,42 @@ genrule { "l2cap/l2cap_packets.h", ], } + +filegroup { + name: "BluetoothCertFacadeProto", + srcs: [ + "hal/facade/api.proto", + ], +} + +genrule { + name: "BluetoothCertFacadeGeneratedStub_h", + tools: [ + "aprotoc", + "protoc-gen-grpc-cpp-plugin", + ], + cmd: "$(location aprotoc) -Isystem/bt/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)", + srcs: [ + ":BluetoothCertFacadeProto", + ], + out: [ + "hal/facade/api.grpc.pb.h", + "hal/facade/api.pb.h", + ], +} + +genrule { + name: "BluetoothCertFacadeGeneratedStub_cc", + tools: [ + "aprotoc", + "protoc-gen-grpc-cpp-plugin", + ], + cmd: "$(location aprotoc) -Isystem/bt/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)", + srcs: [ + ":BluetoothCertFacadeProto", + ], + out: [ + "hal/facade/api.grpc.pb.cc", + "hal/facade/api.pb.cc", + ], +} diff --git a/gd/cert/run_cert.py b/gd/cert/run_cert.py new file mode 100644 index 000000000..fa48c1956 --- /dev/null +++ b/gd/cert/run_cert.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +# +# 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. + +import signal +import sys +import subprocess +import os +import argparse + +ANDROID_BUILD_TOP = os.environ.get('ANDROID_BUILD_TOP') + +if ANDROID_BUILD_TOP is None: + print("Please lunch a target first") + sys.exit(1) + +HOST_OUT = os.environ.get('ANDROID_HOST_OUT') + +BUILD_TARGETS = [ + "root-canal", + "stack_with_facade", + "bluetooth_cert_test", +] + +SOONG_UI_BASH = "build/soong/soong_ui.bash" +ROOTCANAL = HOST_OUT + "/nativetest64/root-canal/root-canal" +STACK_WITH_FACADE = HOST_OUT + "/bin/stack_with_facade" +TEST_SUITE = HOST_OUT + "/nativetest64/bluetooth_cert_test/bluetooth_cert_test" + + +def _build(num_tasks): + build_cmd = [SOONG_UI_BASH, "--make-mode"] + BUILD_TARGETS + [ + "-j" + str(num_tasks)] + print(build_cmd) + p = subprocess.call(build_cmd, cwd=ANDROID_BUILD_TOP, env=os.environ.copy()) + if p != 0: + print('BUILD FAILED, return code: {0}'.format(str(p))) + sys.exit(1) + + +def main(): + parser = argparse.ArgumentParser(description='Run cert tests.') + parser.add_argument( + '--skip-make', + type=bool, + nargs='?', + dest='skip_make', + const=-1, + default=False, + help='skip building and run test immediately') + parser.add_argument( + '-j', + type=int, + nargs='?', + dest='num_tasks', + const=-1, + default=40, + help='Number of tasks to run at the same time') + parser.add_argument( + '--rootcanal_test_port', + nargs='?', + dest='rootcanal_test_port', + const=-1, + default="6401", + help='Rootcanal test channel port') + parser.add_argument( + '--rootcanal_hci_port', + nargs='?', + dest='rootcanal_hci_port', + const=-1, + default="6402", + help='Rootcanal HCI channel port') + parser.add_argument( + '--rootcanal_link_layer_port', + nargs='?', + dest='rootcanal_link_layer_port', + const=-1, + default="6403", + help='Rootcanal Link Layer device channel port') + parser.add_argument( + '--grpc_port', + nargs='?', + dest='grpc_port', + const=-1, + default="8899", + help='gRPC port') + args = parser.parse_args() + if not args.skip_make: + _build(args.num_tasks) + rootcanal_args = [ROOTCANAL, + args.rootcanal_test_port, + args.rootcanal_hci_port, + args.rootcanal_link_layer_port] + p_rootcanal = subprocess.Popen(rootcanal_args, + cwd=ANDROID_BUILD_TOP, + env=os.environ.copy()) + stack_with_facade_args = [STACK_WITH_FACADE, + "--port=" + args.grpc_port, + "--rootcanal-port=" + args.rootcanal_hci_port] + p_stack_with_facade = subprocess.Popen(stack_with_facade_args, + cwd=ANDROID_BUILD_TOP, + env=os.environ.copy()) + p_test_suite = subprocess.Popen(TEST_SUITE, cwd=ANDROID_BUILD_TOP, + env=os.environ.copy()) + p_test_suite.wait() + p_stack_with_facade.send_signal(signal.SIGINT) + p_stack_with_facade.wait() + p_rootcanal.send_signal(signal.SIGINT) + p_rootcanal.wait() + + +if __name__ == '__main__': + main() diff --git a/gd/facade/facade_manager.cc b/gd/facade/facade_manager.cc new file mode 100644 index 000000000..95decb01f --- /dev/null +++ b/gd/facade/facade_manager.cc @@ -0,0 +1,108 @@ +/* + * 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 "facade/facade_manager.h" + +#include "grpc/async_grpc.h" +#include "hal/facade/facade.h" +#include "os/log.h" +#include "stack_manager.h" + +using ::bluetooth::hal::HciPacket; + +using ::grpc::Server; +using ::grpc::ServerBuilder; + +namespace { + +::bluetooth::facade::CertFacade* module_enum_to_module(const ::bluetooth::facade::FacadeManager::Module& module) { + switch (module) { + case ::bluetooth::facade::FacadeManager::Module::HciHal: + return ::bluetooth::hal::facade::GetFacadeModule(); + } + return nullptr; +} +} // namespace + +namespace bluetooth { +namespace facade { + +void FacadeManager::EnableModule(Module module) { + enabled_modules_.push_back(module); +} + +void FacadeManager::StartUp() { + StackManager::Get()->StartUp(); + LOG_INFO("%d", FacadeConfig::Get()->GetGrpcPort()); + start_server("0.0.0.0", FacadeConfig::Get()->GetGrpcPort()); + + for (const auto& enabled_module : enabled_modules_) { + auto* module = module_enum_to_module(enabled_module); + module->StartUp(get_grpc_completion_queue()); + } +} + +void FacadeManager::start_server(const std::string& address, int port) { + std::string listening_port = address + ":" + std::to_string(port); + ServerBuilder builder; + builder.AddListeningPort(listening_port, ::grpc::InsecureServerCredentials()); + + grpc_completion_queue_ = builder.AddCompletionQueue(); + for (const auto& enabled_module : enabled_modules_) { + auto* module = module_enum_to_module(enabled_module); + builder.RegisterService(module->GetModuleGrpcService()); + } + + server_ = builder.BuildAndStart(); +} + +void FacadeManager::ShutDown() { + stop_server(); + + for (const auto& enabled_module : enabled_modules_) { + auto* module = module_enum_to_module(enabled_module); + module->ShutDown(); + } + + StackManager::Get()->ShutDown(); +} + +void FacadeManager::stop_server() { + server_->Shutdown(); + grpc_completion_queue_->Shutdown(); +} + +::grpc::ServerCompletionQueue* FacadeManager::get_grpc_completion_queue() { + auto* queue = grpc_completion_queue_.get(); + ASSERT(queue != nullptr); + return queue; +} + +void FacadeManager::GrpcMainLoop() { + void* tag; + bool ok; + while (true) { + if (!grpc_completion_queue_->Next(&tag, &ok)) { + LOG_INFO("gRPC is shutdown"); + break; + } + auto* data = static_cast(tag); + (*data)(ok); + } +} + +} // namespace facade +} // namespace bluetooth diff --git a/gd/facade/facade_manager.h b/gd/facade/facade_manager.h new file mode 100644 index 000000000..969ba6a96 --- /dev/null +++ b/gd/facade/facade_manager.h @@ -0,0 +1,97 @@ +/* + * 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 + +#include + +#include "hal/hci_hal.h" + +namespace bluetooth { +namespace facade { + +class FacadeConfig { + public: + static FacadeConfig* Get() { + static FacadeConfig instance; + return &instance; + } + + void SetGrpcPort(int port) { + grpc_port_ = port; + } + + int GetGrpcPort() { + return grpc_port_; + } + + private: + FacadeConfig() = default; + int grpc_port_ = 8899; +}; + +class FacadeManager { + public: + enum class Module { + HciHal, + }; + + static FacadeManager* Get() { + static FacadeManager instance; + return &instance; + } + + void EnableModule(Module module); + + void StartUp(); + + void ShutDown(); + + // Blocks for incoming gRPC requests + void GrpcMainLoop(); + + private: + std::unique_ptr<::grpc::Server> server_ = nullptr; + std::unique_ptr<::grpc::ServerCompletionQueue> grpc_completion_queue_ = nullptr; + std::list enabled_modules_; + void start_server(const std::string& address, int port); + void stop_server(); + ::grpc::ServerCompletionQueue* get_grpc_completion_queue(); +}; + +// Cert facade for each layer +class CertFacade { + public: + virtual ~CertFacade() = default; + + // Initialize gRPC service, asynchronous request handlers, and other resources here. + // This should be invoked after CompletionQueue is started. + virtual void StartUp(::grpc::ServerCompletionQueue* cq) {} + + // Do the clean up here + // This should be invoked before CompletionQueue is shutdown. + virtual void ShutDown() {} + + // Each facade has a gRPC service that implements stubs from its api proto. The service instance should exist all + // the time, so static storage is recommended. + virtual ::grpc::Service* GetModuleGrpcService() const = 0; +}; + +} // namespace facade +} // namespace bluetooth diff --git a/gd/facade/stack_with_grpc_main.cc b/gd/facade/stack_with_grpc_main.cc new file mode 100644 index 000000000..054b44b17 --- /dev/null +++ b/gd/facade/stack_with_grpc_main.cc @@ -0,0 +1,61 @@ +/* + * 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 "facade/facade_manager.h" +#include "hal/hci_hal_host_rootcanal.h" + +#include +#include +#include + +using ::bluetooth::facade::FacadeConfig; +using ::bluetooth::facade::FacadeManager; +using ::bluetooth::hal::HciHalHostRootcanalConfig; + +namespace { +void interrupt_handler(int) { + FacadeManager::Get()->ShutDown(); +} +} // namespace + +// The entry point for the binary with libbluetooth + facades +int main(int argc, const char** argv) { + signal(SIGINT, interrupt_handler); + + const std::string arg_grpc_port = "--port="; + const std::string arg_rootcanal_port = "--rootcanal-port="; + for (int i = 1; i < argc; i++) { + std::string arg = argv[i]; + if (arg.find(arg_grpc_port) == 0) { + auto port_number = arg.substr(arg_grpc_port.size()); + FacadeConfig::Get()->SetGrpcPort(std::stoi(port_number)); + } + if (arg.find(arg_rootcanal_port) == 0) { + auto port_number = arg.substr(arg_rootcanal_port.size()); + HciHalHostRootcanalConfig::Get()->SetPort(std::stoi(port_number)); + } + } + + // TODO: This should be run-time configurable + FacadeManager::Get()->EnableModule(FacadeManager::Module::HciHal); + + FacadeManager::Get()->StartUp(); + auto wait_thread = std::thread([] { FacadeManager::Get()->GrpcMainLoop(); }); + wait_thread.join(); + FacadeManager::Get()->ShutDown(); + + return 0; +} diff --git a/gd/grpc/async_grpc.h b/gd/grpc/async_grpc.h new file mode 100644 index 000000000..7cde95d9d --- /dev/null +++ b/gd/grpc/async_grpc.h @@ -0,0 +1,165 @@ +/* + * 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 +#include +#include + +#include + +#include "os/log.h" + +namespace bluetooth { +namespace grpc { + +// To be passed to gRPC async invocations as tag. +// Function is called when the CompletionQueue.Next() returns this tag. +// Then, user needs to delete this object. +using GrpcAsyncEventCallback = std::function; + +template +class GrpcAsyncServerStreamingHandler { + public: + virtual ~GrpcAsyncServerStreamingHandler() = default; + + // Implementation for requesting the next specific type RPC, using provided parameters. + virtual void OnReadyForNextRequest(::grpc::ServerContext*, REQ* req, ::grpc::ServerAsyncWriter* res, + ::grpc::CompletionQueue* new_call_cq, + ::grpc::ServerCompletionQueue* notification_cq, void* tag) = 0; + + virtual void OnRpcRequestReceived(REQ req) = 0; + + virtual void OnRpcRequestFailed() {} + + virtual void OnRpcFinished() {} + + virtual void OnWriteSuccess() {} +}; + +// Provides API to upper layer users to control (request, write, finish) a server-streaming asynchronous RPC. +// When each API is done, callback will be sent to the given GrpcAsyncServerStreamingHandler. +// Each control box can take one active RPC at one time. + +// TODO: problems with this control box: +// 1. RequestNewRpc is async, but Write and Stop is blocking users. Do we want to do this? +// 2. Callback to user is done in the gRPC thread. Let's create a pool thread to give it to user? +// 3. Currently it uses promise to synchronize between events. If we use os/handler it should be easier. +template +class GrpcAsyncServerStreamingControlBox { + public: + GrpcAsyncServerStreamingControlBox(GrpcAsyncServerStreamingHandler* async_handler, + ::grpc::ServerCompletionQueue* cq) + : async_handler_(async_handler), cq_(cq) {} + + void RequestNewRpc() { + ASSERT(my_state_ == MyState::IDLE); + context_ = std::make_unique<::grpc::ServerContext>(); + req_ = std::make_unique(); + res_ = std::make_unique<::grpc::ServerAsyncWriter>(context_.get()); + request_done_ = std::make_unique([this](bool ok) { this->RequestDone(ok); }); + async_handler_->OnReadyForNextRequest(context_.get(), req_.get(), res_.get(), cq_, cq_, request_done_.get()); + my_state_ = MyState::REQUESTING; + } + + void Write(const RES& res) { + std::unique_lock lock(mutex_); + if (my_state_ == MyState::IDLE || my_state_ == MyState::REQUESTING) { + LOG_INFO("stream already stopped"); + return; + } + ASSERT(my_state_ == MyState::OPEN); + write_done_ = std::make_unique([this](bool ok) { this->WriteDone(ok); }); + my_state_ = MyState::WRITING; + res_->Write(res, write_done_.get()); + promise_ = new std::promise(); + auto future = promise_->get_future(); + future.wait(); + } + + void StopStreaming() { + std::unique_lock lock(mutex_); + ASSERT(my_state_ == MyState::OPEN); + rpc_finish_ = std::make_unique([this](bool ok) { this->RpcFinish(ok); }); + my_state_ = MyState::FINISHING; + res_->Finish(::grpc::Status::OK, rpc_finish_.get()); + promise_ = new std::promise(); + auto future = promise_->get_future(); + future.wait(); + } + + private: + void RequestDone(bool ok) { + ASSERT(my_state_ == MyState::REQUESTING); + if (ok) { + async_handler_->OnRpcRequestReceived(*req_); + my_state_ = MyState::OPEN; + } else { + clean_up(); + async_handler_->OnRpcRequestFailed(); + my_state_ = MyState::IDLE; + } + } + + void WriteDone(bool ok) { + ASSERT(my_state_ == MyState::WRITING); + if (ok) { + my_state_ = MyState::OPEN; + async_handler_->OnWriteSuccess(); + } else { + clean_up(); + my_state_ = MyState::IDLE; + async_handler_->OnRpcFinished(); + } + promise_->set_value(); + } + + void RpcFinish(bool ok) { + ASSERT(ok); + ASSERT(my_state_ == MyState::FINISHING); + clean_up(); + my_state_ = MyState::IDLE; + async_handler_->OnRpcFinished(); + promise_->set_value(); + } + + void clean_up() { + context_ = nullptr; + req_ = nullptr; + res_ = nullptr; + } + + mutable std::mutex mutex_; + std::promise* promise_ = nullptr; + + GrpcAsyncServerStreamingHandler* async_handler_; + ::grpc::ServerCompletionQueue* cq_; + + std::unique_ptr<::grpc::ServerContext> context_ = nullptr; + std::unique_ptr req_ = nullptr; + std::unique_ptr<::grpc::ServerAsyncWriter> res_ = nullptr; + + std::unique_ptr request_done_ = nullptr; + std::unique_ptr write_done_ = nullptr; + std::unique_ptr rpc_finish_ = nullptr; + + enum class MyState { IDLE, REQUESTING, OPEN, WRITING, FINISHING } my_state_ = MyState::IDLE; +}; + +} // namespace grpc +} // namespace bluetooth diff --git a/gd/hal/Android.bp b/gd/hal/Android.bp index 543772239..93ef8c84c 100644 --- a/gd/hal/Android.bp +++ b/gd/hal/Android.bp @@ -25,3 +25,17 @@ filegroup { "hci_hal_android_hidl_test.cc", ], } + +filegroup { + name: "BluetoothCertFacade_hci_hal", + srcs: [ + "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 new file mode 100644 index 000000000..b5879a25d --- /dev/null +++ b/gd/hal/cert/simple_hal_test.cc @@ -0,0 +1,92 @@ +/* + * 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 "facade/facade_manager.h" +#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 = ::bluetooth::facade::FacadeConfig::Get()->GetGrpcPort(); + 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/facade/api.proto b/gd/hal/facade/api.proto new file mode 100644 index 000000000..879046728 --- /dev/null +++ b/gd/hal/facade/api.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; + +package bluetooth.hal.facade; + +import "google/protobuf/empty.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 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) {} +} + +message LoopbackModeSettings { + bool enable = 1; +} + +message HciEvtPacket { + bytes payload = 1; +} + +message HciCmdPacket { + bytes payload = 1; +} + +message HciAclPacket { + bytes payload = 1; +} + +message HciScoPacket { + bytes payload = 1; +} diff --git a/gd/hal/facade/facade.cc b/gd/hal/facade/facade.cc new file mode 100644 index 000000000..9163fb301 --- /dev/null +++ b/gd/hal/facade/facade.cc @@ -0,0 +1,331 @@ +/* + * 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 "facade.h" + +#include +#include +#include + +#include "grpc/async_grpc.h" +#include "hal/facade/api.grpc.pb.h" +#include "hal/hci_hal.h" +#include "hci/hci_packets.h" +#include "os/log.h" + +using ::grpc::ServerAsyncResponseWriter; +using ::grpc::ServerAsyncWriter; +using ::grpc::ServerCompletionQueue; +using ::grpc::ServerContext; + +namespace bluetooth { +namespace hal { +namespace facade { + +namespace { + +HalFacadeModule hci_cert_module_instance_; + +void stop_stream_hci_evt(); +void stop_stream_hci_acl(); +void stop_stream_hci_sco(); + +class HciTransportationSyncService : public HciTransportation::Service { + public: + ::grpc::Status SetLoopbackMode(::grpc::ServerContext* context, + const ::bluetooth::hal::facade::LoopbackModeSettings* request, + ::google::protobuf::Empty* response) override { + bool enable = request->enable(); + auto packet = hci::WriteLoopbackModeBuilder::Create(enable ? hci::LoopbackMode::ENABLE_LOCAL + : hci::LoopbackMode::NO_LOOPBACK); + std::shared_ptr> packet_bytes = std::make_shared>(); + hci::BitInserter it(*packet_bytes); + packet->Serialize(it); + GetBluetoothHciHal()->sendHciCommand(*packet_bytes); + return ::grpc::Status::OK; + } + + ::grpc::Status SendHciCmd(::grpc::ServerContext* context, const ::bluetooth::hal::facade::HciCmdPacket* request, + ::google::protobuf::Empty* response) override { + std::string req_string = request->payload(); + ::bluetooth::hal::GetBluetoothHciHal()->sendHciCommand(std::vector(req_string.begin(), req_string.end())); + return ::grpc::Status::OK; + } + + ::grpc::Status SendHciAcl(::grpc::ServerContext* context, const ::bluetooth::hal::facade::HciAclPacket* request, + ::google::protobuf::Empty* response) override { + std::string req_string = request->payload(); + ::bluetooth::hal::GetBluetoothHciHal()->sendAclData(std::vector(req_string.begin(), req_string.end())); + return ::grpc::Status::OK; + } + + ::grpc::Status SendHciSco(::grpc::ServerContext* context, const ::bluetooth::hal::facade::HciScoPacket* request, + ::google::protobuf::Empty* response) override { + std::string req_string = request->payload(); + ::bluetooth::hal::GetBluetoothHciHal()->sendScoData(std::vector(req_string.begin(), req_string.end())); + return ::grpc::Status::OK; + } + + ::grpc::Status UnregisterHciEvt(::grpc::ServerContext* context, const ::google::protobuf::Empty* request, + ::google::protobuf::Empty* response) override { + stop_stream_hci_evt(); + return ::grpc::Status::OK; + } + ::grpc::Status UnregisterHciAcl(::grpc::ServerContext* context, const ::google::protobuf::Empty* request, + ::google::protobuf::Empty* response) override { + stop_stream_hci_acl(); + return ::grpc::Status::OK; + } + + ::grpc::Status UnregisterHciSco(::grpc::ServerContext* context, const ::google::protobuf::Empty* request, + ::google::protobuf::Empty* response) override { + stop_stream_hci_sco(); + return ::grpc::Status::OK; + } +}; + +using HciTransportationAsyncService = + HciTransportation::WithAsyncMethod_RegisterHciEvt>>; + +HciTransportationAsyncService async_service_; + +class RegisterHciEvtImpl : public grpc::GrpcAsyncServerStreamingHandler<::google::protobuf::Empty, HciEvtPacket>, + public HalFacadeModule::HciEvtListener { + public: + explicit RegisterHciEvtImpl(HciTransportationAsyncService* service, ::grpc::ServerCompletionQueue* cq) + : service_(service), streaming_control_box_(this, cq) { + streaming_control_box_.RequestNewRpc(); + } + + void OnReadyForNextRequest(::grpc::ServerContext* context, google::protobuf::Empty* req, + ServerAsyncWriter* res, ::grpc::CompletionQueue* new_call_cq, + ServerCompletionQueue* notification_cq, void* tag) override { + service_->RequestRegisterHciEvt(context, req, res, new_call_cq, notification_cq, tag); + } + + void OnRpcRequestReceived(google::protobuf::Empty) override { + hci_cert_module_instance_.RegisterHciEvtListener(this); + } + + void OnRpcFinished() override { + streaming_control_box_.RequestNewRpc(); + } + + void operator()(const hal::HciPacket& hciPacket) override { + HciEvtPacket packet; + packet.set_payload(std::string(hciPacket.begin(), hciPacket.end())); + streaming_control_box_.Write(packet); + } + + void StopStream() { + hci_cert_module_instance_.UnregisterHciEvtListener(this); + streaming_control_box_.StopStreaming(); + } + + private: + HciTransportationAsyncService* service_; + grpc::GrpcAsyncServerStreamingControlBox<::google::protobuf::Empty, HciEvtPacket> streaming_control_box_; +}; + +class RegisterHciAclImpl : public grpc::GrpcAsyncServerStreamingHandler<::google::protobuf::Empty, HciAclPacket>, + public HalFacadeModule::HciAclListener { + public: + explicit RegisterHciAclImpl(HciTransportationAsyncService* service, ::grpc::ServerCompletionQueue* cq) + : service_(service), streaming_control_box_(this, cq) { + streaming_control_box_.RequestNewRpc(); + } + + void OnReadyForNextRequest(::grpc::ServerContext* context, google::protobuf::Empty* req, + ServerAsyncWriter* res, ::grpc::CompletionQueue* new_call_cq, + ServerCompletionQueue* notification_cq, void* tag) override { + service_->RequestRegisterHciAcl(context, req, res, new_call_cq, notification_cq, tag); + } + + void OnRpcRequestReceived(google::protobuf::Empty) override { + hci_cert_module_instance_.RegisterHciAclListener(this); + } + + void OnRpcFinished() override { + streaming_control_box_.RequestNewRpc(); + } + + void operator()(const hal::HciPacket& hciPacket) override { + HciAclPacket packet; + packet.set_payload(std::string(hciPacket.begin(), hciPacket.end())); + streaming_control_box_.Write(packet); + } + + void StopStream() { + hci_cert_module_instance_.UnregisterHciAclListener(this); + streaming_control_box_.StopStreaming(); + } + + private: + HciTransportationAsyncService* service_; + grpc::GrpcAsyncServerStreamingControlBox<::google::protobuf::Empty, HciAclPacket> streaming_control_box_; +}; + +class RegisterHciScoImpl : public grpc::GrpcAsyncServerStreamingHandler<::google::protobuf::Empty, HciScoPacket>, + public HalFacadeModule::HciScoListener { + public: + explicit RegisterHciScoImpl(HciTransportationAsyncService* service, ::grpc::ServerCompletionQueue* cq) + : service_(service), streaming_control_box_(this, cq) { + streaming_control_box_.RequestNewRpc(); + } + + void OnReadyForNextRequest(::grpc::ServerContext* context, google::protobuf::Empty* req, + ServerAsyncWriter* res, ::grpc::CompletionQueue* new_call_cq, + ServerCompletionQueue* notification_cq, void* tag) override { + service_->RequestRegisterHciSco(context, req, res, new_call_cq, notification_cq, tag); + } + + void OnRpcRequestReceived(google::protobuf::Empty) override { + hci_cert_module_instance_.RegisterHciScoListener(this); + } + + void OnRpcFinished() override { + streaming_control_box_.RequestNewRpc(); + } + + void operator()(const hal::HciPacket& hciPacket) override { + HciScoPacket packet; + packet.set_payload(std::string(hciPacket.begin(), hciPacket.end())); + streaming_control_box_.Write(packet); + } + + void StopStream() { + hci_cert_module_instance_.UnregisterHciScoListener(this); + streaming_control_box_.StopStreaming(); + } + + private: + HciTransportationAsyncService* service_; + grpc::GrpcAsyncServerStreamingControlBox<::google::protobuf::Empty, HciScoPacket> streaming_control_box_; +}; + +struct GrpcHelper { + explicit GrpcHelper(::grpc::ServerCompletionQueue* cq) + : hci_evt_impl_(&async_service_, cq), hci_acl_impl_(&async_service_, cq), hci_sco_impl_(&async_service_, cq) {} + + RegisterHciEvtImpl hci_evt_impl_; + RegisterHciAclImpl hci_acl_impl_; + RegisterHciScoImpl hci_sco_impl_; +}; +GrpcHelper* grpc_helper_instance_ = nullptr; + +void stop_stream_hci_evt() { + grpc_helper_instance_->hci_evt_impl_.StopStream(); +} + +void stop_stream_hci_acl() { + grpc_helper_instance_->hci_acl_impl_.StopStream(); +} + +void stop_stream_hci_sco() { + grpc_helper_instance_->hci_sco_impl_.StopStream(); +} + +} // namespace + +class IncomingPacketCallback : public ::bluetooth::hal::BluetoothHciHalCallbacks { + public: + explicit IncomingPacketCallback(HalFacadeModule* hal_cert_module) : hal_cert_module_(hal_cert_module) {} + + void hciEventReceived(bluetooth::hal::HciPacket event) override { + std::unique_lock lock(hal_cert_module_->mutex_); + for (auto* listener : hal_cert_module_->registered_evt_listener_) { + (*listener)(event); + } + } + + void aclDataReceived(bluetooth::hal::HciPacket data) override { + std::unique_lock lock(hal_cert_module_->mutex_); + for (auto* listener : hal_cert_module_->registered_acl_listener_) { + (*listener)(data); + } + } + + void scoDataReceived(bluetooth::hal::HciPacket data) override { + std::unique_lock lock(hal_cert_module_->mutex_); + for (auto* listener : hal_cert_module_->registered_sco_listener_) { + (*listener)(data); + } + } + + private: + HalFacadeModule* hal_cert_module_; +}; + +static IncomingPacketCallback* incoming_packet_callback_; + +void HalFacadeModule::StartUp(::grpc::ServerCompletionQueue* cq) { + std::unique_lock lock(mutex_); + incoming_packet_callback_ = new IncomingPacketCallback(this); + hal::GetBluetoothHciHal()->registerIncomingPacketCallback(incoming_packet_callback_); + + grpc_helper_instance_ = new GrpcHelper(cq); +} + +void HalFacadeModule::ShutDown() { + std::unique_lock lock(mutex_); + delete grpc_helper_instance_; + grpc_helper_instance_ = nullptr; + delete incoming_packet_callback_; + incoming_packet_callback_ = nullptr; +} + +::grpc::Service* HalFacadeModule::GetModuleGrpcService() const { + return &async_service_; +} + +void HalFacadeModule::RegisterHciEvtListener(HciEvtListener* listener) { + std::unique_lock lock(mutex_); + registered_evt_listener_.push_back(listener); +} + +void HalFacadeModule::UnregisterHciEvtListener(HciEvtListener* listener) { + std::unique_lock lock(mutex_); + registered_evt_listener_.remove(listener); +} + +void HalFacadeModule::RegisterHciAclListener(HciAclListener* listener) { + std::unique_lock lock(mutex_); + registered_acl_listener_.push_back(listener); +} + +void HalFacadeModule::UnregisterHciAclListener(HciAclListener* listener) { + std::unique_lock lock(mutex_); + registered_acl_listener_.remove(listener); +} + +void HalFacadeModule::RegisterHciScoListener(HciScoListener* listener) { + std::unique_lock lock(mutex_); + registered_sco_listener_.push_back(listener); +} + +void HalFacadeModule::UnregisterHciScoListener(HciScoListener* listener) { + std::unique_lock lock(mutex_); + registered_sco_listener_.remove(listener); +} + +::bluetooth::facade::CertFacade* GetFacadeModule() { + return &hci_cert_module_instance_; +} + +} // namespace facade +} // namespace hal +} // namespace bluetooth diff --git a/gd/hal/facade/facade.h b/gd/hal/facade/facade.h new file mode 100644 index 000000000..45ab91909 --- /dev/null +++ b/gd/hal/facade/facade.h @@ -0,0 +1,74 @@ +/* + * 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 + +#include + +#include "facade/facade_manager.h" + +namespace bluetooth { +namespace hal { +namespace facade { +// Get cert facade. This instance has static storage. +::bluetooth::facade::CertFacade* GetFacadeModule(); + +class HalFacadeModule : public ::bluetooth::facade::CertFacade { + public: + void StartUp(::grpc::ServerCompletionQueue* cq) override; + + void ShutDown() override; + + ::grpc::Service* GetModuleGrpcService() const override; + + struct HciEvtListener { + virtual ~HciEvtListener() = default; + virtual void operator()(const hal::HciPacket&) {} + }; + + void RegisterHciEvtListener(HciEvtListener* listener); + void UnregisterHciEvtListener(HciEvtListener* listener); + + struct HciAclListener { + virtual ~HciAclListener() = default; + virtual void operator()(const hal::HciPacket&) {} + }; + + void RegisterHciAclListener(HciAclListener* listener); + void UnregisterHciAclListener(HciAclListener* listener); + + struct HciScoListener { + virtual ~HciScoListener() = default; + virtual void operator()(const hal::HciPacket&) {} + }; + + void RegisterHciScoListener(HciScoListener* listener); + void UnregisterHciScoListener(HciScoListener* listener); + + private: + std::mutex mutex_; + friend class IncomingPacketCallback; + std::list registered_evt_listener_; + std::list registered_acl_listener_; + std::list registered_sco_listener_; +}; + +} // namespace facade +} // namespace hal +} // namespace bluetooth diff --git a/gd/stack_manager.cc b/gd/stack_manager.cc index f06bf87cc..8e859e80a 100644 --- a/gd/stack_manager.cc +++ b/gd/stack_manager.cc @@ -63,6 +63,7 @@ void StackManager::StartUp() { void StackManager::ShutDown() { // Delete HCI layer delete main_thread_; + main_thread_ = nullptr; ::bluetooth::hal::GetBluetoothHciHal()->close(); } } // namespace bluetooth diff --git a/vendor_libs/test_vendor_lib/desktop/test_environment.cc b/vendor_libs/test_vendor_lib/desktop/test_environment.cc index 542738d75..05103c9f6 100644 --- a/vendor_libs/test_vendor_lib/desktop/test_environment.cc +++ b/vendor_libs/test_vendor_lib/desktop/test_environment.cc @@ -143,6 +143,8 @@ int TestEnvironment::ConnectToRemoteServer(const std::string& server, int port) void TestEnvironment::SetUpTestChannel() { int socket_fd = test_channel_transport_.SetUp(test_port_); + test_channel_.SetTimerPeriod({"100"}); + test_channel_.StartTimer({}); test_channel_.RegisterSendResponse( [](const std::string& response) { ALOGI("No test channel: %s", response.c_str()); }); -- 2.11.0