| /* |
| * Copyright 2020 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 "l2cap/le/facade.h" |
| |
| #include "grpc/grpc_event_queue.h" |
| #include "l2cap/le/dynamic_channel.h" |
| #include "l2cap/le/dynamic_channel_manager.h" |
| #include "l2cap/le/dynamic_channel_service.h" |
| #include "l2cap/le/facade.grpc.pb.h" |
| #include "l2cap/le/l2cap_le_module.h" |
| #include "l2cap/le/security_policy.h" |
| #include "l2cap/psm.h" |
| #include "packet/raw_builder.h" |
| |
| namespace bluetooth { |
| namespace l2cap { |
| namespace le { |
| |
| SecurityPolicy SecurityLevelToPolicy(SecurityLevel level) { |
| switch (level) { |
| case SecurityLevel::NO_SECURITY: |
| return SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK; |
| case SecurityLevel::UNAUTHENTICATED_PAIRING_WITH_ENCRYPTION: |
| return SecurityPolicy::ENCRYPTED_TRANSPORT; |
| case SecurityLevel::AUTHENTICATED_PAIRING_WITH_ENCRYPTION: |
| return SecurityPolicy::AUTHENTICATED_ENCRYPTED_TRANSPORT; |
| case SecurityLevel::AUTHENTICATED_PAIRING_WITH_128_BIT_KEY: |
| return SecurityPolicy::_NOT_FOR_YOU__AUTHENTICATED_PAIRING_WITH_128_BIT_KEY; |
| case SecurityLevel::AUTHORIZATION: |
| return SecurityPolicy::_NOT_FOR_YOU__AUTHORIZATION; |
| default: |
| return SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK; |
| } |
| } |
| |
| static constexpr auto kChannelOpenTimeout = std::chrono::seconds(4); |
| |
| class L2capLeModuleFacadeService : public L2capLeModuleFacade::Service { |
| public: |
| L2capLeModuleFacadeService(L2capLeModule* l2cap_layer, os::Handler* facade_handler) |
| : l2cap_layer_(l2cap_layer), facade_handler_(facade_handler) { |
| ASSERT(l2cap_layer_ != nullptr); |
| ASSERT(facade_handler_ != nullptr); |
| } |
| |
| ::grpc::Status FetchL2capData(::grpc::ServerContext* context, const ::google::protobuf::Empty* request, |
| ::grpc::ServerWriter<::bluetooth::l2cap::le::L2capPacket>* writer) override { |
| return pending_l2cap_data_.RunLoop(context, writer); |
| } |
| |
| ::grpc::Status OpenDynamicChannel(::grpc::ServerContext* context, const OpenDynamicChannelRequest* request, |
| OpenDynamicChannelResponse* response) override { |
| auto service_helper = dynamic_channel_helper_map_.find(request->psm()); |
| if (service_helper == dynamic_channel_helper_map_.end()) { |
| return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered"); |
| } |
| hci::Address peer_address; |
| ASSERT(hci::Address::FromString(request->remote().address().address(), peer_address)); |
| // TODO: Support different address type |
| hci::AddressWithType peer(peer_address, hci::AddressType::RANDOM_DEVICE_ADDRESS); |
| service_helper->second->Connect(peer); |
| response->set_status( |
| static_cast<int>(service_helper->second->channel_open_fail_reason_.l2cap_connection_response_result)); |
| return ::grpc::Status::OK; |
| } |
| |
| ::grpc::Status CloseDynamicChannel(::grpc::ServerContext* context, const CloseDynamicChannelRequest* request, |
| ::google::protobuf::Empty* response) override { |
| auto service_helper = dynamic_channel_helper_map_.find(request->psm()); |
| if (service_helper == dynamic_channel_helper_map_.end()) { |
| return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered"); |
| } |
| if (service_helper->second->channel_ == nullptr) { |
| return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not open"); |
| } |
| auto address = service_helper->second->channel_->GetDevice().GetAddress(); |
| hci::Address peer_address; |
| ASSERT(hci::Address::FromString(request->remote().address().address(), peer_address)); |
| if (address != peer_address) { |
| return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Remote address doesn't match"); |
| } |
| service_helper->second->channel_->Close(); |
| return ::grpc::Status::OK; |
| } |
| |
| ::grpc::Status SetDynamicChannel(::grpc::ServerContext* context, |
| const ::bluetooth::l2cap::le::SetEnableDynamicChannelRequest* request, |
| ::google::protobuf::Empty* response) override { |
| if (request->enable()) { |
| dynamic_channel_helper_map_.emplace(request->psm(), std::make_unique<L2capDynamicChannelHelper>( |
| this, l2cap_layer_, facade_handler_, request->psm(), |
| SecurityLevelToPolicy(request->security_level()))); |
| return ::grpc::Status::OK; |
| } else { |
| auto service_helper = dynamic_channel_helper_map_.find(request->psm()); |
| if (service_helper == dynamic_channel_helper_map_.end()) { |
| return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered"); |
| } |
| service_helper->second->service_->Unregister(common::BindOnce([] {}), facade_handler_); |
| return ::grpc::Status::OK; |
| } |
| } |
| |
| ::grpc::Status SendDynamicChannelPacket(::grpc::ServerContext* context, |
| const ::bluetooth::l2cap::le::DynamicChannelPacket* request, |
| ::google::protobuf::Empty* response) override { |
| std::unique_lock<std::mutex> lock(channel_map_mutex_); |
| if (dynamic_channel_helper_map_.find(request->psm()) == dynamic_channel_helper_map_.end()) { |
| return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered"); |
| } |
| std::vector<uint8_t> packet(request->payload().begin(), request->payload().end()); |
| if (!dynamic_channel_helper_map_[request->psm()]->SendPacket(packet)) { |
| return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not open"); |
| } |
| return ::grpc::Status::OK; |
| } |
| |
| class L2capDynamicChannelHelper { |
| public: |
| L2capDynamicChannelHelper(L2capLeModuleFacadeService* service, L2capLeModule* l2cap_layer, os::Handler* handler, |
| Psm psm, SecurityPolicy security_policy) |
| : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), psm_(psm) { |
| dynamic_channel_manager_ = l2cap_layer_->GetDynamicChannelManager(); |
| dynamic_channel_manager_->RegisterService( |
| psm, {}, security_policy, |
| common::BindOnce(&L2capDynamicChannelHelper::on_l2cap_service_registration_complete, |
| common::Unretained(this)), |
| common::Bind(&L2capDynamicChannelHelper::on_connection_open, common::Unretained(this)), handler_); |
| } |
| |
| ~L2capDynamicChannelHelper() { |
| if (channel_ != nullptr) { |
| channel_->GetQueueUpEnd()->UnregisterDequeue(); |
| channel_ = nullptr; |
| } |
| } |
| |
| void Connect(hci::AddressWithType address) { |
| dynamic_channel_manager_->ConnectChannel( |
| address, {}, psm_, common::Bind(&L2capDynamicChannelHelper::on_connection_open, common::Unretained(this)), |
| common::Bind(&L2capDynamicChannelHelper::on_connect_fail, common::Unretained(this)), handler_); |
| std::unique_lock<std::mutex> lock(channel_open_cv_mutex_); |
| if (!channel_open_cv_.wait_for(lock, kChannelOpenTimeout, [this] { return channel_ != nullptr; })) { |
| LOG_WARN("Channel is not open for psm %d", psm_); |
| } |
| } |
| |
| void on_l2cap_service_registration_complete(DynamicChannelManager::RegistrationResult registration_result, |
| std::unique_ptr<DynamicChannelService> service) { |
| if (registration_result != DynamicChannelManager::RegistrationResult::SUCCESS) { |
| LOG_ERROR("Service registration failed"); |
| } else { |
| service_ = std::move(service); |
| } |
| } |
| |
| // invoked from Facade Handler |
| void on_connection_open(std::unique_ptr<DynamicChannel> channel) { |
| { |
| std::unique_lock<std::mutex> lock(channel_open_cv_mutex_); |
| channel_ = std::move(channel); |
| } |
| channel_open_cv_.notify_all(); |
| channel_->RegisterOnCloseCallback( |
| facade_service_->facade_handler_->BindOnceOn(this, &L2capDynamicChannelHelper::on_close_callback)); |
| channel_->GetQueueUpEnd()->RegisterDequeue( |
| facade_service_->facade_handler_, |
| common::Bind(&L2capDynamicChannelHelper::on_incoming_packet, common::Unretained(this))); |
| } |
| |
| void on_close_callback(hci::ErrorCode error_code) { |
| { |
| std::unique_lock<std::mutex> lock(channel_open_cv_mutex_); |
| channel_->GetQueueUpEnd()->UnregisterDequeue(); |
| } |
| channel_ = nullptr; |
| } |
| |
| void on_connect_fail(DynamicChannelManager::ConnectionResult result) { |
| { |
| std::unique_lock<std::mutex> lock(channel_open_cv_mutex_); |
| channel_ = nullptr; |
| channel_open_fail_reason_ = result; |
| } |
| channel_open_cv_.notify_all(); |
| } |
| |
| void on_incoming_packet() { |
| auto packet = channel_->GetQueueUpEnd()->TryDequeue(); |
| std::string data = std::string(packet->begin(), packet->end()); |
| L2capPacket l2cap_data; |
| l2cap_data.set_psm(psm_); |
| l2cap_data.set_payload(data); |
| facade_service_->pending_l2cap_data_.OnIncomingEvent(l2cap_data); |
| } |
| |
| bool SendPacket(std::vector<uint8_t> packet) { |
| if (channel_ == nullptr) { |
| std::unique_lock<std::mutex> lock(channel_open_cv_mutex_); |
| if (!channel_open_cv_.wait_for(lock, kChannelOpenTimeout, [this] { return channel_ != nullptr; })) { |
| LOG_WARN("Channel is not open for psm %d", psm_); |
| return false; |
| } |
| } |
| std::promise<void> promise; |
| auto future = promise.get_future(); |
| channel_->GetQueueUpEnd()->RegisterEnqueue( |
| handler_, common::Bind(&L2capDynamicChannelHelper::enqueue_callback, common::Unretained(this), packet, |
| common::Passed(std::move(promise)))); |
| auto status = future.wait_for(std::chrono::milliseconds(500)); |
| if (status != std::future_status::ready) { |
| LOG_ERROR("Can't send packet because the previous packet wasn't sent yet"); |
| return false; |
| } |
| return true; |
| } |
| |
| std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(std::vector<uint8_t> packet, |
| std::promise<void> promise) { |
| auto packet_one = std::make_unique<packet::RawBuilder>(2000); |
| packet_one->AddOctets(packet); |
| channel_->GetQueueUpEnd()->UnregisterEnqueue(); |
| promise.set_value(); |
| return packet_one; |
| } |
| |
| L2capLeModuleFacadeService* facade_service_; |
| L2capLeModule* l2cap_layer_; |
| os::Handler* handler_; |
| std::unique_ptr<DynamicChannelManager> dynamic_channel_manager_; |
| std::unique_ptr<DynamicChannelService> service_; |
| std::unique_ptr<DynamicChannel> channel_ = nullptr; |
| Psm psm_; |
| DynamicChannelManager::ConnectionResult channel_open_fail_reason_; |
| std::condition_variable channel_open_cv_; |
| std::mutex channel_open_cv_mutex_; |
| }; |
| |
| ::grpc::Status SetFixedChannel(::grpc::ServerContext* context, const SetEnableFixedChannelRequest* request, |
| ::google::protobuf::Empty* response) override { |
| if (request->enable()) { |
| fixed_channel_helper_map_.emplace(request->cid(), std::make_unique<L2capFixedChannelHelper>( |
| this, l2cap_layer_, facade_handler_, request->cid())); |
| return ::grpc::Status::OK; |
| } else { |
| auto service_helper = fixed_channel_helper_map_.find(request->cid()); |
| if (service_helper == fixed_channel_helper_map_.end()) { |
| return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Cid not registered"); |
| } |
| service_helper->second->channel_->Release(); |
| service_helper->second->service_->Unregister(common::BindOnce([] {}), facade_handler_); |
| return ::grpc::Status::OK; |
| } |
| } |
| |
| ::grpc::Status SendFixedChannelPacket(::grpc::ServerContext* context, const FixedChannelPacket* request, |
| ::google::protobuf::Empty* response) override { |
| std::unique_lock<std::mutex> lock(channel_map_mutex_); |
| if (fixed_channel_helper_map_.find(request->cid()) == fixed_channel_helper_map_.end()) { |
| return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Cid not registered"); |
| } |
| std::vector<uint8_t> packet(request->payload().begin(), request->payload().end()); |
| if (!fixed_channel_helper_map_[request->cid()]->SendPacket(packet)) { |
| return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not open"); |
| } |
| return ::grpc::Status::OK; |
| } |
| |
| class L2capFixedChannelHelper { |
| public: |
| L2capFixedChannelHelper(L2capLeModuleFacadeService* service, L2capLeModule* l2cap_layer, os::Handler* handler, |
| Cid cid) |
| : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), cid_(cid) { |
| fixed_channel_manager_ = l2cap_layer_->GetFixedChannelManager(); |
| fixed_channel_manager_->RegisterService( |
| cid_, |
| common::BindOnce(&L2capFixedChannelHelper::on_l2cap_service_registration_complete, common::Unretained(this)), |
| common::Bind(&L2capFixedChannelHelper::on_connection_open, common::Unretained(this)), handler_); |
| } |
| |
| ~L2capFixedChannelHelper() { |
| if (channel_ != nullptr) { |
| channel_->GetQueueUpEnd()->UnregisterDequeue(); |
| channel_->Release(); |
| channel_ = nullptr; |
| } |
| } |
| |
| void Connect(hci::AddressWithType address) { |
| fixed_channel_manager_->ConnectServices( |
| address, common::BindOnce(&L2capFixedChannelHelper::on_connect_fail, common::Unretained(this)), handler_); |
| std::unique_lock<std::mutex> lock(channel_open_cv_mutex_); |
| if (!channel_open_cv_.wait_for(lock, kChannelOpenTimeout, [this] { return channel_ != nullptr; })) { |
| LOG_WARN("Channel is not open for cid %d", cid_); |
| } |
| } |
| |
| void on_l2cap_service_registration_complete(FixedChannelManager::RegistrationResult registration_result, |
| std::unique_ptr<FixedChannelService> service) { |
| if (registration_result != FixedChannelManager::RegistrationResult::SUCCESS) { |
| LOG_ERROR("Service registration failed"); |
| } else { |
| service_ = std::move(service); |
| } |
| } |
| |
| // invoked from Facade Handler |
| void on_connection_open(std::unique_ptr<FixedChannel> channel) { |
| { |
| std::unique_lock<std::mutex> lock(channel_open_cv_mutex_); |
| channel_ = std::move(channel); |
| channel_->RegisterOnCloseCallback( |
| handler_, common::BindOnce(&L2capFixedChannelHelper::on_close_callback, common::Unretained(this))); |
| channel_->Acquire(); |
| } |
| channel_open_cv_.notify_all(); |
| channel_->GetQueueUpEnd()->RegisterDequeue( |
| facade_service_->facade_handler_, |
| common::Bind(&L2capFixedChannelHelper::on_incoming_packet, common::Unretained(this))); |
| } |
| |
| void on_close_callback(hci::ErrorCode error_code) { |
| { |
| std::unique_lock<std::mutex> lock(channel_open_cv_mutex_); |
| channel_->GetQueueUpEnd()->UnregisterDequeue(); |
| } |
| channel_ = nullptr; |
| } |
| |
| void on_connect_fail(FixedChannelManager::ConnectionResult result) { |
| { |
| std::unique_lock<std::mutex> lock(channel_open_cv_mutex_); |
| channel_ = nullptr; |
| } |
| channel_open_cv_.notify_all(); |
| } |
| |
| void on_incoming_packet() { |
| auto packet = channel_->GetQueueUpEnd()->TryDequeue(); |
| std::string data = std::string(packet->begin(), packet->end()); |
| L2capPacket l2cap_data; |
| l2cap_data.set_fixed_cid(cid_); |
| l2cap_data.set_payload(data); |
| facade_service_->pending_l2cap_data_.OnIncomingEvent(l2cap_data); |
| } |
| |
| bool SendPacket(std::vector<uint8_t> packet) { |
| if (channel_ == nullptr) { |
| std::unique_lock<std::mutex> lock(channel_open_cv_mutex_); |
| if (!channel_open_cv_.wait_for(lock, kChannelOpenTimeout, [this] { return channel_ != nullptr; })) { |
| LOG_WARN("Channel is not open for cid %d", cid_); |
| return false; |
| } |
| } |
| std::promise<void> promise; |
| auto future = promise.get_future(); |
| channel_->GetQueueUpEnd()->RegisterEnqueue( |
| handler_, common::Bind(&L2capFixedChannelHelper::enqueue_callback, common::Unretained(this), packet, |
| common::Passed(std::move(promise)))); |
| auto status = future.wait_for(std::chrono::milliseconds(500)); |
| if (status != std::future_status::ready) { |
| LOG_ERROR("Can't send packet because the previous packet wasn't sent yet"); |
| return false; |
| } |
| return true; |
| } |
| |
| std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(std::vector<uint8_t> packet, |
| std::promise<void> promise) { |
| auto packet_one = std::make_unique<packet::RawBuilder>(2000); |
| packet_one->AddOctets(packet); |
| channel_->GetQueueUpEnd()->UnregisterEnqueue(); |
| promise.set_value(); |
| return packet_one; |
| } |
| |
| L2capLeModuleFacadeService* facade_service_; |
| L2capLeModule* l2cap_layer_; |
| os::Handler* handler_; |
| std::unique_ptr<FixedChannelManager> fixed_channel_manager_; |
| std::unique_ptr<FixedChannelService> service_; |
| std::unique_ptr<FixedChannel> channel_ = nullptr; |
| Cid cid_; |
| std::condition_variable channel_open_cv_; |
| std::mutex channel_open_cv_mutex_; |
| }; |
| |
| ::grpc::Status SendConnectionParameterUpdate(::grpc::ServerContext* context, const ConnectionParameter* request, |
| ::google::protobuf::Empty* response) override { |
| if (dynamic_channel_helper_map_.empty()) { |
| return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Need to open at least one dynamic channel first"); |
| } |
| auto& dynamic_channel_helper = dynamic_channel_helper_map_.begin()->second; |
| dynamic_channel_helper->channel_->GetLinkOptions()->UpdateConnectionParameter( |
| request->conn_interval_min(), request->conn_interval_max(), request->conn_latency(), |
| request->supervision_timeout(), request->min_ce_length(), request->max_ce_length()); |
| |
| return ::grpc::Status::OK; |
| } |
| |
| L2capLeModule* l2cap_layer_; |
| os::Handler* facade_handler_; |
| std::mutex channel_map_mutex_; |
| std::map<Psm, std::unique_ptr<L2capDynamicChannelHelper>> dynamic_channel_helper_map_; |
| std::map<Cid, std::unique_ptr<L2capFixedChannelHelper>> fixed_channel_helper_map_; |
| ::bluetooth::grpc::GrpcEventQueue<L2capPacket> pending_l2cap_data_{"FetchL2capData"}; |
| }; |
| |
| void L2capLeModuleFacadeModule::ListDependencies(ModuleList* list) const { |
| ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list); |
| list->add<l2cap::le::L2capLeModule>(); |
| } |
| |
| void L2capLeModuleFacadeModule::Start() { |
| ::bluetooth::grpc::GrpcFacadeModule::Start(); |
| service_ = new L2capLeModuleFacadeService(GetDependency<l2cap::le::L2capLeModule>(), GetHandler()); |
| } |
| |
| void L2capLeModuleFacadeModule::Stop() { |
| delete service_; |
| ::bluetooth::grpc::GrpcFacadeModule::Stop(); |
| } |
| |
| ::grpc::Service* L2capLeModuleFacadeModule::GetService() const { |
| return service_; |
| } |
| |
| const ModuleFactory L2capLeModuleFacadeModule::Factory = |
| ::bluetooth::ModuleFactory([]() { return new L2capLeModuleFacadeModule(); }); |
| |
| } // namespace le |
| } // namespace l2cap |
| } // namespace bluetooth |