| /* |
| * 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. |
| */ |
| #define LOG_TAG "bt_gd_shim" |
| |
| #include <cstdint> |
| #include <functional> |
| #include <future> |
| #include <memory> |
| #include <mutex> |
| #include <queue> |
| #include <unordered_map> |
| #include <vector> |
| |
| #include "common/bind.h" |
| #include "hci/address.h" |
| #include "hci/hci_packets.h" |
| #include "l2cap/classic/dynamic_channel_manager.h" |
| #include "l2cap/classic/l2cap_classic_module.h" |
| #include "l2cap/psm.h" |
| #include "l2cap/security_policy.h" |
| #include "module.h" |
| #include "os/handler.h" |
| #include "os/log.h" |
| #include "packet/packet_view.h" |
| #include "packet/raw_builder.h" |
| #include "shim/l2cap.h" |
| |
| namespace bluetooth { |
| namespace shim { |
| |
| const ModuleFactory L2cap::Factory = ModuleFactory([]() { return new L2cap(); }); |
| |
| using ConnectionInterfaceDescriptor = uint16_t; |
| static const ConnectionInterfaceDescriptor kInvalidConnectionInterfaceDescriptor = 0; |
| static const ConnectionInterfaceDescriptor kStartConnectionInterfaceDescriptor = 64; |
| static const ConnectionInterfaceDescriptor kMaxConnections = UINT16_MAX - kStartConnectionInterfaceDescriptor - 1; |
| |
| using ServiceInterfaceCallback = |
| std::function<void(l2cap::Psm psm, l2cap::classic::DynamicChannelManager::RegistrationResult result)>; |
| using ConnectionInterfaceCallback = std::function<void(l2cap::Psm psm, std::unique_ptr<l2cap::DynamicChannel>)>; |
| |
| std::unique_ptr<packet::RawBuilder> MakeUniquePacket(const uint8_t* data, size_t len) { |
| packet::RawBuilder builder; |
| std::vector<uint8_t> bytes(data, data + len); |
| auto payload = std::make_unique<packet::RawBuilder>(); |
| payload->AddOctets(bytes); |
| return payload; |
| } |
| |
| class ConnectionInterface { |
| public: |
| ConnectionInterface(ConnectionInterfaceDescriptor cid, std::unique_ptr<l2cap::DynamicChannel> channel, |
| os::Handler* handler) |
| : cid_(cid), channel_(std::move(channel)), handler_(handler), on_data_ready_callback_(nullptr), |
| on_connection_closed_callback_(nullptr), address_(channel_->GetDevice()) { |
| channel_->RegisterOnCloseCallback( |
| handler_, common::BindOnce(&ConnectionInterface::OnConnectionClosed, common::Unretained(this))); |
| channel_->GetQueueUpEnd()->RegisterDequeue( |
| handler_, common::Bind(&ConnectionInterface::OnReadReady, common::Unretained(this))); |
| dequeue_registered_ = true; |
| } |
| |
| ~ConnectionInterface() { |
| ASSERT(!dequeue_registered_); |
| } |
| |
| void OnReadReady() { |
| std::unique_ptr<packet::PacketView<packet::kLittleEndian>> packet = channel_->GetQueueUpEnd()->TryDequeue(); |
| if (packet == nullptr) { |
| LOG_WARN("Got read ready from gd l2cap but no packet is ready"); |
| return; |
| } |
| std::vector<const uint8_t> data(packet->begin(), packet->end()); |
| ASSERT(on_data_ready_callback_ != nullptr); |
| on_data_ready_callback_(cid_, data); |
| } |
| |
| void SetReadDataReadyCallback(ReadDataReadyCallback on_data_ready) { |
| ASSERT(on_data_ready != nullptr); |
| ASSERT(on_data_ready_callback_ == nullptr); |
| on_data_ready_callback_ = on_data_ready; |
| } |
| |
| std::unique_ptr<packet::BasePacketBuilder> WriteReady() { |
| auto data = std::move(write_queue_.front()); |
| write_queue_.pop(); |
| if (write_queue_.empty()) { |
| channel_->GetQueueUpEnd()->UnregisterEnqueue(); |
| enqueue_registered_ = false; |
| } |
| return data; |
| } |
| |
| void Write(std::unique_ptr<packet::RawBuilder> packet) { |
| LOG_DEBUG("Writing packet cid:%hd size:%zd", cid_, packet->size()); |
| write_queue_.push(std::move(packet)); |
| if (!enqueue_registered_) { |
| enqueue_registered_ = true; |
| channel_->GetQueueUpEnd()->RegisterEnqueue( |
| handler_, common::Bind(&ConnectionInterface::WriteReady, common::Unretained(this))); |
| } |
| } |
| |
| void Close() { |
| if (dequeue_registered_) { |
| channel_->GetQueueUpEnd()->UnregisterDequeue(); |
| dequeue_registered_ = false; |
| } |
| ASSERT(write_queue_.empty()); |
| channel_->Close(); |
| } |
| |
| void OnConnectionClosed(hci::ErrorCode error_code) { |
| LOG_DEBUG("Channel interface closed reason:%s cid:%hd device:%s", hci::ErrorCodeText(error_code).c_str(), cid_, |
| address_.ToString().c_str()); |
| ASSERT(on_connection_closed_callback_ != nullptr); |
| on_connection_closed_callback_(cid_, static_cast<int>(error_code)); |
| } |
| |
| void SetConnectionClosedCallback(::bluetooth::shim::ConnectionClosedCallback on_connection_closed) { |
| ASSERT(on_connection_closed != nullptr); |
| ASSERT(on_connection_closed_callback_ == nullptr); |
| on_connection_closed_callback_ = std::move(on_connection_closed); |
| } |
| |
| private: |
| const ConnectionInterfaceDescriptor cid_; |
| const std::unique_ptr<l2cap::DynamicChannel> channel_; |
| os::Handler* handler_; |
| |
| ReadDataReadyCallback on_data_ready_callback_; |
| ConnectionClosedCallback on_connection_closed_callback_; |
| |
| const hci::Address address_; |
| |
| std::queue<std::unique_ptr<packet::PacketBuilder<hci::kLittleEndian>>> write_queue_; |
| |
| bool enqueue_registered_{false}; |
| bool dequeue_registered_{false}; |
| }; |
| |
| struct ConnectionInterfaceManager { |
| public: |
| ConnectionInterfaceDescriptor AddChannel(std::unique_ptr<l2cap::DynamicChannel> channel); |
| void RemoveConnection(ConnectionInterfaceDescriptor cid); |
| |
| void SetReadDataReadyCallback(ConnectionInterfaceDescriptor cid, ReadDataReadyCallback on_data_ready); |
| void SetConnectionClosedCallback(ConnectionInterfaceDescriptor cid, ConnectionClosedCallback on_closed); |
| |
| bool Write(ConnectionInterfaceDescriptor cid, std::unique_ptr<packet::RawBuilder> packet); |
| |
| size_t NumberOfActiveConnections() const { |
| return cid_to_interface_map_.size(); |
| } |
| |
| void GeneralCallback(ConnectionOpenCallback on_open, hci::Address address, l2cap::Psm psm, |
| ConnectionInterfaceDescriptor cid) { |
| on_open(address.ToString(), static_cast<uint16_t>(psm), static_cast<uint16_t>(cid)); |
| } |
| |
| void ConnectionOpened(ConnectionOpenCallback on_open, hci::Address address, l2cap::Psm psm, |
| ConnectionInterfaceDescriptor cid) { |
| LOG_DEBUG("%s address:%s psm:%hd cid:%hd", __func__, address.ToString().c_str(), psm, cid); |
| handler_->Post(common::BindOnce(&ConnectionInterfaceManager::GeneralCallback, common::Unretained(this), on_open, |
| address, psm, cid)); |
| // TODO(cmanton) queue this pending connection address/psm tuple up for deletion |
| // There may be multiple, so only remove one |
| } |
| |
| void ConnectionFailed(hci::Address address, l2cap::Psm psm) { |
| LOG_DEBUG("%s Connection Failed", __func__); |
| // TODO(cmanton) queue this pending connection address/psm tuple up for deletion |
| // There may be multiple, so only remove one |
| } |
| |
| ConnectionInterfaceManager(os::Handler* handler); |
| |
| private: |
| os::Handler* handler_; |
| ConnectionInterfaceDescriptor current_connection_interface_descriptor_; |
| |
| bool HasResources() const; |
| bool Exists(ConnectionInterfaceDescriptor id) const; |
| |
| std::unordered_map<ConnectionInterfaceDescriptor, std::unique_ptr<ConnectionInterface>> cid_to_interface_map_; |
| ConnectionInterfaceDescriptor AllocateConnectionInterfaceDescriptor(); |
| ConnectionInterfaceManager() = delete; |
| }; |
| |
| ConnectionInterfaceManager::ConnectionInterfaceManager(os::Handler* handler) |
| : handler_(handler), current_connection_interface_descriptor_(kStartConnectionInterfaceDescriptor) {} |
| |
| bool ConnectionInterfaceManager::Exists(ConnectionInterfaceDescriptor cid) const { |
| return cid_to_interface_map_.find(cid) != cid_to_interface_map_.end(); |
| } |
| |
| ConnectionInterfaceDescriptor ConnectionInterfaceManager::AllocateConnectionInterfaceDescriptor() { |
| ASSERT(HasResources()); |
| while (Exists(current_connection_interface_descriptor_)) { |
| if (++current_connection_interface_descriptor_ == kInvalidConnectionInterfaceDescriptor) { |
| current_connection_interface_descriptor_ = kStartConnectionInterfaceDescriptor; |
| } |
| } |
| return current_connection_interface_descriptor_++; |
| } |
| |
| ConnectionInterfaceDescriptor ConnectionInterfaceManager::AddChannel(std::unique_ptr<l2cap::DynamicChannel> channel) { |
| if (!HasResources()) { |
| return kInvalidConnectionInterfaceDescriptor; |
| } |
| ConnectionInterfaceDescriptor cid = AllocateConnectionInterfaceDescriptor(); |
| |
| auto channel_interface = std::make_unique<ConnectionInterface>(cid, std::move(channel), handler_); |
| cid_to_interface_map_[cid] = std::move(channel_interface); |
| return cid; |
| } |
| |
| void ConnectionInterfaceManager::RemoveConnection(ConnectionInterfaceDescriptor cid) { |
| ASSERT(cid_to_interface_map_.count(cid) == 1); |
| cid_to_interface_map_.find(cid)->second->Close(); |
| cid_to_interface_map_.erase(cid); |
| } |
| |
| bool ConnectionInterfaceManager::HasResources() const { |
| return cid_to_interface_map_.size() < kMaxConnections; |
| } |
| |
| void ConnectionInterfaceManager::SetReadDataReadyCallback(ConnectionInterfaceDescriptor cid, |
| ReadDataReadyCallback on_data_ready) { |
| ASSERT(Exists(cid)); |
| return cid_to_interface_map_[cid]->SetReadDataReadyCallback(on_data_ready); |
| } |
| |
| void ConnectionInterfaceManager::SetConnectionClosedCallback(ConnectionInterfaceDescriptor cid, |
| ConnectionClosedCallback on_closed) { |
| ASSERT(Exists(cid)); |
| return cid_to_interface_map_[cid]->SetConnectionClosedCallback(on_closed); |
| } |
| |
| bool ConnectionInterfaceManager::Write(ConnectionInterfaceDescriptor cid, std::unique_ptr<packet::RawBuilder> packet) { |
| if (!Exists(cid)) { |
| return false; |
| } |
| cid_to_interface_map_[cid]->Write(std::move(packet)); |
| return true; |
| } |
| |
| class PendingConnection { |
| public: |
| PendingConnection(ConnectionInterfaceManager* connection_interface_manager, l2cap::Psm psm, hci::Address address, |
| ConnectionOpenCallback on_open, std::promise<uint16_t> completed) |
| : connection_interface_manager_(connection_interface_manager), psm_(psm), address_(address), |
| on_open_(std::move(on_open)), completed_(std::move(completed)) {} |
| |
| void OnConnectionOpen(std::unique_ptr<l2cap::DynamicChannel> channel) { |
| LOG_DEBUG("Local initiated connection is open to device:%s for psm:%hd", address_.ToString().c_str(), psm_); |
| ConnectionInterfaceDescriptor cid = connection_interface_manager_->AddChannel(std::move(channel)); |
| completed_.set_value(cid); |
| // Attempt to avoid async race condition with upper stack |
| std::this_thread::yield(); |
| connection_interface_manager_->ConnectionOpened(std::move(on_open_), address_, psm_, cid); |
| } |
| |
| void OnConnectionFailure(l2cap::classic::DynamicChannelManager::ConnectionResult result) { |
| LOG_DEBUG("Connection failed to device:%s for psm:%hd", address_.ToString().c_str(), psm_); |
| switch (result.connection_result_code) { |
| case l2cap::classic::DynamicChannelManager::ConnectionResultCode::SUCCESS: |
| LOG_WARN("Connection failed result:success hci:%s", hci::ErrorCodeText(result.hci_error).c_str()); |
| break; |
| case l2cap::classic::DynamicChannelManager::ConnectionResultCode::FAIL_NO_SERVICE_REGISTERED: |
| LOG_DEBUG("Connection failed result:no service registered hci:%s", |
| hci::ErrorCodeText(result.hci_error).c_str()); |
| break; |
| case l2cap::classic::DynamicChannelManager::ConnectionResultCode::FAIL_HCI_ERROR: |
| LOG_DEBUG("Connection failed result:hci error hci:%s", hci::ErrorCodeText(result.hci_error).c_str()); |
| break; |
| case l2cap::classic::DynamicChannelManager::ConnectionResultCode::FAIL_L2CAP_ERROR: |
| LOG_DEBUG("Connection failed result:l2cap error hci:%s l2cap:%s", hci::ErrorCodeText(result.hci_error).c_str(), |
| l2cap::ConnectionResponseResultText(result.l2cap_connection_response_result).c_str()); |
| break; |
| } |
| completed_.set_value(kInvalidConnectionInterfaceDescriptor); |
| connection_interface_manager_->ConnectionFailed(address_, psm_); |
| } |
| |
| private: |
| ConnectionInterfaceManager* connection_interface_manager_; |
| const l2cap::Psm psm_; |
| const hci::Address address_; |
| ConnectionOpenCallback on_open_; |
| std::promise<uint16_t> completed_; |
| }; |
| |
| class ServiceInterface { |
| public: |
| ServiceInterface(ConnectionInterfaceManager* connection_interface_manager, l2cap::Psm psm, |
| ConnectionOpenCallback on_open, std::promise<void> completed) |
| : connection_interface_manager_(connection_interface_manager), psm_(psm), on_open_(on_open), |
| completed_(std::move(completed)) {} |
| |
| void OnRegistrationComplete(l2cap::classic::DynamicChannelManager::RegistrationResult result, |
| std::unique_ptr<l2cap::classic::DynamicChannelService> service) { |
| ASSERT(service_ == nullptr); |
| ASSERT(psm_ == service->GetPsm()); |
| LOG_DEBUG("Registration is complete for psm:%hd", psm_); |
| service_ = std::move(service); |
| completed_.set_value(); |
| } |
| |
| void OnConnectionOpen(std::unique_ptr<l2cap::DynamicChannel> channel) { |
| LOG_DEBUG("Remote initiated connection is open from device:%s for psm:%hd", channel->GetDevice().ToString().c_str(), |
| psm_); |
| hci::Address address = channel->GetDevice(); |
| ConnectionInterfaceDescriptor cid = connection_interface_manager_->AddChannel(std::move(channel)); |
| connection_interface_manager_->ConnectionOpened(on_open_, address, psm_, cid); |
| } |
| |
| l2cap::SecurityPolicy GetSecurityPolicy() const { |
| return security_policy_; |
| } |
| |
| void RegisterService( |
| std::function<void(l2cap::Psm, l2cap::SecurityPolicy security_policy, |
| l2cap::classic::DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete, |
| l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_connection_open)> |
| func) { |
| func(psm_, security_policy_, common::BindOnce(&ServiceInterface::OnRegistrationComplete, common::Unretained(this)), |
| common::Bind(&ServiceInterface::OnConnectionOpen, common::Unretained(this))); |
| } |
| |
| private: |
| ConnectionInterfaceManager* connection_interface_manager_; |
| const l2cap::Psm psm_; |
| ConnectionOpenCallback on_open_; |
| std::promise<void> completed_; |
| |
| std::unique_ptr<l2cap::classic::DynamicChannelService> service_; |
| |
| const l2cap::SecurityPolicy security_policy_; |
| }; |
| |
| struct L2cap::impl { |
| void RegisterService(l2cap::Psm psm, ConnectionOpenCallback on_open, std::promise<void> completed); |
| void UnregisterService(l2cap::Psm psm); |
| |
| void CreateConnection(l2cap::Psm psm, hci::Address address, ConnectionOpenCallback on_open, |
| std::promise<uint16_t> completed); |
| void CloseConnection(ConnectionInterfaceDescriptor cid); |
| |
| void SetReadDataReadyCallback(ConnectionInterfaceDescriptor cid, ReadDataReadyCallback on_data_ready); |
| void SetConnectionClosedCallback(ConnectionInterfaceDescriptor cid, ConnectionClosedCallback on_closed); |
| |
| void Write(ConnectionInterfaceDescriptor cid, std::unique_ptr<packet::RawBuilder> packet); |
| |
| void SendLoopbackResponse(std::function<void()> function); |
| |
| impl(L2cap& module, l2cap::classic::L2capClassicModule* l2cap_module); |
| |
| private: |
| L2cap& module_; |
| l2cap::classic::L2capClassicModule* l2cap_module_; |
| os::Handler* handler_; |
| ConnectionInterfaceManager connection_interface_manager_; |
| |
| std::unique_ptr<l2cap::classic::DynamicChannelManager> dynamic_channel_manager_; |
| |
| std::unordered_map<l2cap::Psm, std::shared_ptr<ServiceInterface>> psm_to_service_interface_map_; |
| std::unordered_map<l2cap::Psm, std::shared_ptr<PendingConnection>> psm_to_pending_connection_map_; |
| }; |
| |
| L2cap::impl::impl(L2cap& module, l2cap::classic::L2capClassicModule* l2cap_module) |
| : module_(module), l2cap_module_(l2cap_module), handler_(module_.GetHandler()), |
| connection_interface_manager_(handler_) { |
| dynamic_channel_manager_ = l2cap_module_->GetDynamicChannelManager(); |
| } |
| |
| void L2cap::impl::RegisterService(l2cap::Psm psm, ConnectionOpenCallback on_open, std::promise<void> completed) { |
| ASSERT(psm_to_service_interface_map_.find(psm) == psm_to_service_interface_map_.end()); |
| |
| auto service_interface = |
| std::make_shared<ServiceInterface>(&connection_interface_manager_, psm, on_open, std::move(completed)); |
| psm_to_service_interface_map_.emplace(psm, service_interface); |
| |
| // TODO(cmanton): Use the configuration option from user |
| service_interface->RegisterService( |
| [this](l2cap::Psm psm, l2cap::SecurityPolicy security_policy, |
| l2cap::classic::DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete, |
| l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_connection_open) { |
| bool rc = dynamic_channel_manager_->RegisterService(psm, l2cap::classic::DynamicChannelConfigurationOption(), |
| security_policy, std::move(on_registration_complete), |
| on_connection_open, handler_); |
| ASSERT_LOG(rc == true, "Failed to register classic service"); |
| }); |
| } |
| |
| void L2cap::impl::UnregisterService(l2cap::Psm psm) { |
| psm_to_service_interface_map_.erase(psm); |
| } |
| |
| void L2cap::impl::CreateConnection(l2cap::Psm psm, hci::Address address, ConnectionOpenCallback on_open, |
| std::promise<uint16_t> completed) { |
| LOG_DEBUG("Initiating classic connection to psm:%hd device:%s", psm, address.ToString().c_str()); |
| auto pending_connection = std::make_shared<PendingConnection>(&connection_interface_manager_, psm, address, |
| std::move(on_open), std::move(completed)); |
| // TODO(cmanton) hash psm/address pair into unordered map for pending_connection |
| // This is ok for now |
| psm_to_pending_connection_map_[psm] = pending_connection; |
| // TODO(cmanton): Add ERTM mode support by changing configuratio_option in ConnectChannel() |
| bool rc = dynamic_channel_manager_->ConnectChannel( |
| address, l2cap::classic::DynamicChannelConfigurationOption(), psm, |
| common::Bind(&PendingConnection::OnConnectionOpen, common::Unretained(pending_connection.get())), |
| common::BindOnce(&PendingConnection::OnConnectionFailure, common::Unretained(pending_connection.get())), |
| handler_); |
| ASSERT_LOG(rc == true, "Failed to create classic connection"); |
| } |
| |
| void L2cap::impl::CloseConnection(ConnectionInterfaceDescriptor cid) { |
| connection_interface_manager_.RemoveConnection(cid); |
| } |
| |
| void L2cap::impl::SetReadDataReadyCallback(ConnectionInterfaceDescriptor cid, ReadDataReadyCallback on_data_ready) { |
| connection_interface_manager_.SetReadDataReadyCallback(cid, on_data_ready); |
| } |
| |
| void L2cap::impl::SetConnectionClosedCallback(ConnectionInterfaceDescriptor cid, ConnectionClosedCallback on_closed) { |
| connection_interface_manager_.SetConnectionClosedCallback(cid, std::move(on_closed)); |
| } |
| |
| void L2cap::impl::Write(ConnectionInterfaceDescriptor cid, std::unique_ptr<packet::RawBuilder> packet) { |
| connection_interface_manager_.Write(cid, std::move(packet)); |
| } |
| |
| void L2cap::impl::SendLoopbackResponse(std::function<void()> function) { |
| function(); |
| } |
| |
| void L2cap::RegisterService(uint16_t raw_psm, ConnectionOpenCallback on_open, std::promise<void> completed) { |
| l2cap::Psm psm{raw_psm}; |
| GetHandler()->Post(common::BindOnce(&L2cap::impl::RegisterService, common::Unretained(pimpl_.get()), psm, on_open, |
| std::move(completed))); |
| } |
| |
| void L2cap::UnregisterService(uint16_t raw_psm) { |
| l2cap::Psm psm{raw_psm}; |
| GetHandler()->Post(common::Bind(&L2cap::impl::UnregisterService, common::Unretained(pimpl_.get()), psm)); |
| } |
| |
| void L2cap::CreateConnection(uint16_t raw_psm, const std::string address_string, ConnectionOpenCallback on_open, |
| std::promise<uint16_t> completed) { |
| l2cap::Psm psm{raw_psm}; |
| hci::Address address; |
| hci::Address::FromString(address_string, address); |
| |
| GetHandler()->Post(common::BindOnce(&L2cap::impl::CreateConnection, common::Unretained(pimpl_.get()), psm, address, |
| on_open, std::move(completed))); |
| } |
| |
| void L2cap::CloseConnection(uint16_t raw_cid) { |
| ConnectionInterfaceDescriptor cid(raw_cid); |
| GetHandler()->Post(common::Bind(&L2cap::impl::CloseConnection, common::Unretained(pimpl_.get()), cid)); |
| } |
| |
| void L2cap::SetReadDataReadyCallback(uint16_t raw_cid, ReadDataReadyCallback on_data_ready) { |
| ConnectionInterfaceDescriptor cid(raw_cid); |
| GetHandler()->Post( |
| common::Bind(&L2cap::impl::SetReadDataReadyCallback, common::Unretained(pimpl_.get()), cid, on_data_ready)); |
| } |
| |
| void L2cap::SetConnectionClosedCallback(uint16_t raw_cid, ConnectionClosedCallback on_closed) { |
| ConnectionInterfaceDescriptor cid(raw_cid); |
| GetHandler()->Post(common::Bind(&L2cap::impl::SetConnectionClosedCallback, common::Unretained(pimpl_.get()), cid, |
| std::move(on_closed))); |
| } |
| |
| void L2cap::Write(uint16_t raw_cid, const uint8_t* data, size_t len) { |
| ConnectionInterfaceDescriptor cid(raw_cid); |
| auto packet = MakeUniquePacket(data, len); |
| GetHandler()->Post(common::BindOnce(&L2cap::impl::Write, common::Unretained(pimpl_.get()), cid, std::move(packet))); |
| } |
| |
| void L2cap::WriteFlushable(uint16_t raw_cid, const uint8_t* data, size_t len) { |
| LOG_WARN("UNIMPLEMENTED Write flushable"); |
| return Write(raw_cid, data, len); |
| } |
| |
| void L2cap::WriteNonFlushable(uint16_t raw_cid, const uint8_t* data, size_t len) { |
| LOG_WARN("UNIMPLEMENTED Write non flushable"); |
| return Write(raw_cid, data, len); |
| } |
| |
| void L2cap::SendLoopbackResponse(std::function<void()> function) { |
| GetHandler()->Post(common::BindOnce(&L2cap::impl::SendLoopbackResponse, common::Unretained(pimpl_.get()), function)); |
| } |
| |
| /** |
| * Module methods |
| */ |
| void L2cap::ListDependencies(ModuleList* list) { |
| list->add<l2cap::classic::L2capClassicModule>(); |
| } |
| |
| void L2cap::Start() { |
| pimpl_ = std::make_unique<impl>(*this, GetDependency<l2cap::classic::L2capClassicModule>()); |
| } |
| |
| void L2cap::Stop() { |
| pimpl_.reset(); |
| } |
| |
| } // namespace shim |
| } // namespace bluetooth |