blob: 513983673fcded42af3287d365a99d2c576f4fc2 [file] [log] [blame]
/*
* 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 "hci/hci_layer.h"
#include "common/bind.h"
#include "common/init_flags.h"
#include "common/stop_watch.h"
#include "hci/hci_metrics_logging.h"
#include "os/alarm.h"
#include "os/metrics.h"
#include "os/queue.h"
#include "packet/packet_builder.h"
#include "storage/storage_module.h"
namespace bluetooth {
namespace hci {
using bluetooth::common::BindOn;
using bluetooth::common::BindOnce;
using bluetooth::common::ContextualCallback;
using bluetooth::common::ContextualOnceCallback;
using bluetooth::hci::CommandBuilder;
using bluetooth::hci::CommandCompleteView;
using bluetooth::hci::CommandStatusView;
using bluetooth::hci::EventView;
using bluetooth::hci::LeMetaEventView;
using bluetooth::os::Handler;
using common::BidiQueue;
using common::BidiQueueEnd;
using hci::OpCode;
using hci::ResetCompleteView;
using os::Alarm;
using os::Handler;
using std::move;
using std::unique_ptr;
static void fail_if_reset_complete_not_success(CommandCompleteView complete) {
auto reset_complete = ResetCompleteView::Create(complete);
ASSERT(reset_complete.IsValid());
ASSERT(reset_complete.GetStatus() == ErrorCode::SUCCESS);
}
static void abort_after_time_out(OpCode op_code) {
bluetooth::os::LogMetricHciTimeoutEvent(static_cast<uint32_t>(op_code));
ASSERT_LOG(false, "Done waiting for debug information after HCI timeout (%s)", OpCodeText(op_code).c_str());
}
class CommandQueueEntry {
public:
CommandQueueEntry(
unique_ptr<CommandBuilder> command_packet, ContextualOnceCallback<void(CommandCompleteView)> on_complete_function)
: command(move(command_packet)), waiting_for_status_(false), on_complete(move(on_complete_function)) {}
CommandQueueEntry(
unique_ptr<CommandBuilder> command_packet, ContextualOnceCallback<void(CommandStatusView)> on_status_function)
: command(move(command_packet)), waiting_for_status_(true), on_status(move(on_status_function)) {}
unique_ptr<CommandBuilder> command;
unique_ptr<CommandView> command_view;
bool waiting_for_status_;
ContextualOnceCallback<void(CommandStatusView)> on_status;
ContextualOnceCallback<void(CommandCompleteView)> on_complete;
template <typename TView>
ContextualOnceCallback<void(TView)>* GetCallback() {
return nullptr;
}
template <>
ContextualOnceCallback<void(CommandStatusView)>* GetCallback<CommandStatusView>() {
return &on_status;
}
template <>
ContextualOnceCallback<void(CommandCompleteView)>* GetCallback<CommandCompleteView>() {
return &on_complete;
}
};
struct HciLayer::impl {
impl(hal::HciHal* hal, HciLayer& module) : hal_(hal), module_(module) {
hci_timeout_alarm_ = new Alarm(module.GetHandler());
}
~impl() {
incoming_acl_buffer_.Clear();
incoming_sco_buffer_.Clear();
incoming_iso_buffer_.Clear();
if (hci_timeout_alarm_ != nullptr) {
delete hci_timeout_alarm_;
}
if (hci_abort_alarm_ != nullptr) {
delete hci_abort_alarm_;
}
command_queue_.clear();
}
void drop(EventView event) {
LOG_INFO("Dropping event %s", EventCodeText(event.GetEventCode()).c_str());
}
void on_outbound_acl_ready() {
auto packet = acl_queue_.GetDownEnd()->TryDequeue();
std::vector<uint8_t> bytes;
BitInserter bi(bytes);
packet->Serialize(bi);
hal_->sendAclData(bytes);
}
void on_outbound_sco_ready() {
auto packet = sco_queue_.GetDownEnd()->TryDequeue();
std::vector<uint8_t> bytes;
BitInserter bi(bytes);
packet->Serialize(bi);
hal_->sendScoData(bytes);
}
void on_outbound_iso_ready() {
auto packet = iso_queue_.GetDownEnd()->TryDequeue();
std::vector<uint8_t> bytes;
BitInserter bi(bytes);
packet->Serialize(bi);
hal_->sendIsoData(bytes);
}
template <typename TResponse>
void enqueue_command(unique_ptr<CommandBuilder> command, ContextualOnceCallback<void(TResponse)> on_response) {
command_queue_.emplace_back(move(command), move(on_response));
send_next_command();
}
void on_command_status(EventView event) {
CommandStatusView response_view = CommandStatusView::Create(event);
ASSERT(response_view.IsValid());
OpCode op_code = response_view.GetCommandOpCode();
ErrorCode status = response_view.GetStatus();
if (status != ErrorCode::SUCCESS) {
LOG_ERROR(
"Received UNEXPECTED command status:%s opcode:0x%02hx (%s)",
ErrorCodeText(status).c_str(),
op_code,
OpCodeText(op_code).c_str());
}
handle_command_response<CommandStatusView>(event, "status");
}
void on_command_complete(EventView event) {
handle_command_response<CommandCompleteView>(event, "complete");
}
template <typename TResponse>
void handle_command_response(EventView event, std::string logging_id) {
TResponse response_view = TResponse::Create(event);
ASSERT(response_view.IsValid());
command_credits_ = response_view.GetNumHciCommandPackets();
OpCode op_code = response_view.GetCommandOpCode();
if (op_code == OpCode::NONE) {
send_next_command();
return;
}
bool is_status = logging_id == "status";
ASSERT_LOG(!command_queue_.empty(), "Unexpected %s event with OpCode 0x%02hx (%s)", logging_id.c_str(), op_code,
OpCodeText(op_code).c_str());
ASSERT_LOG(waiting_command_ == op_code, "Waiting for 0x%02hx (%s), got 0x%02hx (%s)", waiting_command_,
OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str());
ASSERT_LOG(command_queue_.front().waiting_for_status_ == is_status, "0x%02hx (%s) was not expecting %s event",
op_code, OpCodeText(op_code).c_str(), logging_id.c_str());
command_queue_.front().GetCallback<TResponse>()->Invoke(move(response_view));
command_queue_.pop_front();
waiting_command_ = OpCode::NONE;
if (hci_timeout_alarm_ != nullptr) {
hci_timeout_alarm_->Cancel();
send_next_command();
}
}
void on_hci_timeout(OpCode op_code) {
common::StopWatch::DumpStopWatchLog();
LOG_ERROR("Timed out waiting for 0x%02hx (%s)", op_code, OpCodeText(op_code).c_str());
// TODO: LogMetricHciTimeoutEvent(static_cast<uint32_t>(op_code));
LOG_ERROR("Flushing %zd waiting commands", command_queue_.size());
// Clear any waiting commands (there is an abort coming anyway)
command_queue_.clear();
command_credits_ = 1;
waiting_command_ = OpCode::NONE;
enqueue_command(
ControllerDebugInfoBuilder::Create(), module_.GetHandler()->BindOnce(&fail_if_reset_complete_not_success));
// Don't time out for this one;
if (hci_timeout_alarm_ != nullptr) {
hci_timeout_alarm_->Cancel();
delete hci_timeout_alarm_;
hci_timeout_alarm_ = nullptr;
}
if (hci_abort_alarm_ == nullptr) {
hci_abort_alarm_ = new Alarm(module_.GetHandler());
hci_abort_alarm_->Schedule(BindOnce(&abort_after_time_out, op_code), kHciTimeoutRestartMs);
} else {
LOG_WARN("Unable to schedul abort timer");
}
}
void send_next_command() {
if (command_credits_ == 0) {
return;
}
if (waiting_command_ != OpCode::NONE) {
return;
}
if (command_queue_.size() == 0) {
return;
}
std::shared_ptr<std::vector<uint8_t>> bytes = std::make_shared<std::vector<uint8_t>>();
BitInserter bi(*bytes);
command_queue_.front().command->Serialize(bi);
hal_->sendHciCommand(*bytes);
auto cmd_view = CommandView::Create(PacketView<kLittleEndian>(bytes));
ASSERT(cmd_view.IsValid());
OpCode op_code = cmd_view.GetOpCode();
command_queue_.front().command_view = std::make_unique<CommandView>(std::move(cmd_view));
log_link_layer_connection_command_status(command_queue_.front().command_view, ErrorCode::STATUS_UNKNOWN);
log_classic_pairing_command_status(command_queue_.front().command_view, ErrorCode::STATUS_UNKNOWN);
waiting_command_ = op_code;
command_credits_ = 0; // Only allow one outstanding command
if (hci_timeout_alarm_ != nullptr) {
hci_timeout_alarm_->Schedule(BindOnce(&impl::on_hci_timeout, common::Unretained(this), op_code), kHciTimeoutMs);
} else {
LOG_WARN("%s sent without an hci-timeout timer", OpCodeText(op_code).c_str());
}
}
void register_event(EventCode event, ContextualCallback<void(EventView)> handler) {
ASSERT_LOG(
event != EventCode::LE_META_EVENT,
"Can not register handler for %02hhx (%s)",
EventCode::LE_META_EVENT,
EventCodeText(EventCode::LE_META_EVENT).c_str());
ASSERT_LOG(event_handlers_.count(event) == 0, "Can not register a second handler for %02hhx (%s)", event,
EventCodeText(event).c_str());
event_handlers_[event] = handler;
}
void unregister_event(EventCode event) {
event_handlers_.erase(event);
}
void register_le_meta_event(ContextualCallback<void(EventView)> handler) {
ASSERT_LOG(
event_handlers_.count(EventCode::LE_META_EVENT) == 0,
"Can not register a second handler for %02hhx (%s)",
EventCode::LE_META_EVENT,
EventCodeText(EventCode::LE_META_EVENT).c_str());
event_handlers_[EventCode::LE_META_EVENT] = handler;
}
void unregister_le_meta_event() {
unregister_event(EventCode::LE_META_EVENT);
}
void register_le_event(SubeventCode event, ContextualCallback<void(LeMetaEventView)> handler) {
ASSERT_LOG(subevent_handlers_.count(event) == 0, "Can not register a second handler for %02hhx (%s)", event,
SubeventCodeText(event).c_str());
subevent_handlers_[event] = handler;
}
void unregister_le_event(SubeventCode event) {
subevent_handlers_.erase(subevent_handlers_.find(event));
}
static void abort_after_root_inflammation(uint8_t vse_error) {
ASSERT_LOG(false, "Root inflammation with reason 0x%02hhx", vse_error);
}
void handle_root_inflammation(uint8_t vse_error_reason) {
LOG_ERROR("Received a Root Inflammation Event vendor reason 0x%02hhx, scheduling an abort",
vse_error_reason);
bluetooth::os::LogMetricBluetoothHalCrashReason(Address::kEmpty, 0, vse_error_reason);
// Add Logging for crash reason
if (hci_timeout_alarm_ != nullptr) {
hci_timeout_alarm_->Cancel();
delete hci_timeout_alarm_;
hci_timeout_alarm_ = nullptr;
}
if (hci_abort_alarm_ == nullptr) {
hci_abort_alarm_ = new Alarm(module_.GetHandler());
hci_abort_alarm_->Schedule(BindOnce(&abort_after_root_inflammation, vse_error_reason), kHciTimeoutRestartMs);
} else {
LOG_WARN("Abort timer already scheduled");
}
}
void on_hci_event(EventView event) {
ASSERT(event.IsValid());
if (command_queue_.empty()) {
std::unique_ptr<CommandView> no_waiting_command{nullptr};
log_hci_event(no_waiting_command, event, module_.GetDependency<storage::StorageModule>());
} else {
log_hci_event(command_queue_.front().command_view, event, module_.GetDependency<storage::StorageModule>());
}
EventCode event_code = event.GetEventCode();
// Root Inflamation is a special case, since it aborts here
if (event_code == EventCode::VENDOR_SPECIFIC) {
auto view = VendorSpecificEventView::Create(event);
ASSERT(view.IsValid());
if (view.GetSubeventCode() == VseSubeventCode::BQR_EVENT) {
auto bqr_quality_view = BqrLinkQualityEventView::Create(BqrEventView::Create(view));
auto inflammation = BqrRootInflammationEventView::Create(bqr_quality_view);
if (bqr_quality_view.IsValid() && inflammation.IsValid()) {
handle_root_inflammation(inflammation.GetVendorSpecificErrorCode());
return;
}
}
}
if (event_handlers_.find(event_code) == event_handlers_.end()) {
LOG_WARN("Unhandled event of type 0x%02hhx (%s)", event_code, EventCodeText(event_code).c_str());
return;
}
event_handlers_[event_code].Invoke(event);
}
void on_le_meta_event(EventView event) {
LeMetaEventView meta_event_view = LeMetaEventView::Create(event);
ASSERT(meta_event_view.IsValid());
SubeventCode subevent_code = meta_event_view.GetSubeventCode();
if (subevent_handlers_.find(subevent_code) == subevent_handlers_.end()) {
LOG_WARN("Unhandled le subevent of type 0x%02hhx (%s)", subevent_code, SubeventCodeText(subevent_code).c_str());
return;
}
subevent_handlers_[subevent_code].Invoke(meta_event_view);
}
hal::HciHal* hal_;
HciLayer& module_;
// Command Handling
std::list<CommandQueueEntry> command_queue_;
std::map<EventCode, ContextualCallback<void(EventView)>> event_handlers_;
std::map<SubeventCode, ContextualCallback<void(LeMetaEventView)>> subevent_handlers_;
OpCode waiting_command_{OpCode::NONE};
uint8_t command_credits_{1}; // Send reset first
Alarm* hci_timeout_alarm_{nullptr};
Alarm* hci_abort_alarm_{nullptr};
// Acl packets
BidiQueue<AclView, AclBuilder> acl_queue_{3 /* TODO: Set queue depth */};
os::EnqueueBuffer<AclView> incoming_acl_buffer_{acl_queue_.GetDownEnd()};
// SCO packets
BidiQueue<ScoView, ScoBuilder> sco_queue_{3 /* TODO: Set queue depth */};
os::EnqueueBuffer<ScoView> incoming_sco_buffer_{sco_queue_.GetDownEnd()};
// ISO packets
BidiQueue<IsoView, IsoBuilder> iso_queue_{3 /* TODO: Set queue depth */};
os::EnqueueBuffer<IsoView> incoming_iso_buffer_{iso_queue_.GetDownEnd()};
};
// All functions here are running on the HAL thread
struct HciLayer::hal_callbacks : public hal::HciHalCallbacks {
hal_callbacks(HciLayer& module) : module_(module) {}
void hciEventReceived(hal::HciPacket event_bytes) override {
auto packet = packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(event_bytes));
EventView event = EventView::Create(packet);
module_.CallOn(module_.impl_, &impl::on_hci_event, move(event));
}
void aclDataReceived(hal::HciPacket data_bytes) override {
auto packet = packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(move(data_bytes)));
auto acl = std::make_unique<AclView>(AclView::Create(packet));
module_.impl_->incoming_acl_buffer_.Enqueue(move(acl), module_.GetHandler());
}
void scoDataReceived(hal::HciPacket data_bytes) override {
auto packet = packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(move(data_bytes)));
auto sco = std::make_unique<ScoView>(ScoView::Create(packet));
module_.impl_->incoming_sco_buffer_.Enqueue(move(sco), module_.GetHandler());
}
void isoDataReceived(hal::HciPacket data_bytes) override {
auto packet = packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(move(data_bytes)));
auto iso = std::make_unique<IsoView>(IsoView::Create(packet));
module_.impl_->incoming_iso_buffer_.Enqueue(move(iso), module_.GetHandler());
}
HciLayer& module_;
};
HciLayer::HciLayer() : impl_(nullptr), hal_callbacks_(nullptr) {}
HciLayer::~HciLayer() {
}
common::BidiQueueEnd<AclBuilder, AclView>* HciLayer::GetAclQueueEnd() {
return impl_->acl_queue_.GetUpEnd();
}
common::BidiQueueEnd<ScoBuilder, ScoView>* HciLayer::GetScoQueueEnd() {
return impl_->sco_queue_.GetUpEnd();
}
common::BidiQueueEnd<IsoBuilder, IsoView>* HciLayer::GetIsoQueueEnd() {
return impl_->iso_queue_.GetUpEnd();
}
void HciLayer::EnqueueCommand(
unique_ptr<CommandBuilder> command, ContextualOnceCallback<void(CommandCompleteView)> on_complete) {
CallOn(impl_, &impl::enqueue_command<CommandCompleteView>, move(command), move(on_complete));
}
void HciLayer::EnqueueCommand(
unique_ptr<CommandBuilder> command, ContextualOnceCallback<void(CommandStatusView)> on_status) {
CallOn(impl_, &impl::enqueue_command<CommandStatusView>, move(command), move(on_status));
}
void HciLayer::RegisterEventHandler(EventCode event, ContextualCallback<void(EventView)> handler) {
CallOn(impl_, &impl::register_event, event, handler);
}
void HciLayer::RegisterLeMetaEventHandler(ContextualCallback<void(EventView)> handler) {
CallOn(impl_, &impl::register_le_meta_event, handler);
}
void HciLayer::UnregisterEventHandler(EventCode event) {
CallOn(impl_, &impl::unregister_event, event);
}
void HciLayer::RegisterLeEventHandler(SubeventCode event, ContextualCallback<void(LeMetaEventView)> handler) {
CallOn(impl_, &impl::register_le_event, event, handler);
}
void HciLayer::UnregisterLeEventHandler(SubeventCode event) {
CallOn(impl_, &impl::unregister_le_event, event);
}
void HciLayer::on_disconnection_complete(EventView event_view) {
auto disconnection_view = DisconnectionCompleteView::Create(event_view);
if (!disconnection_view.IsValid()) {
LOG_INFO("Dropping invalid disconnection packet");
return;
}
uint16_t handle = disconnection_view.GetConnectionHandle();
ErrorCode reason = disconnection_view.GetReason();
Disconnect(handle, reason);
}
void HciLayer::Disconnect(uint16_t handle, ErrorCode reason) {
for (auto callback : disconnect_handlers_) {
callback.Invoke(handle, reason);
}
}
void HciLayer::on_read_remote_version_complete(EventView event_view) {
auto view = ReadRemoteVersionInformationCompleteView::Create(event_view);
ASSERT_LOG(view.IsValid(), "Read remote version information packet invalid");
ReadRemoteVersion(
view.GetStatus(),
view.GetConnectionHandle(),
view.GetVersion(),
view.GetManufacturerName(),
view.GetSubVersion());
}
void HciLayer::ReadRemoteVersion(
hci::ErrorCode hci_status, uint16_t handle, uint8_t version, uint16_t manufacturer_name, uint16_t sub_version) {
for (auto callback : read_remote_version_handlers_) {
callback.Invoke(hci_status, handle, version, manufacturer_name, sub_version);
}
}
AclConnectionInterface* HciLayer::GetAclConnectionInterface(
ContextualCallback<void(EventView)> event_handler,
ContextualCallback<void(uint16_t, ErrorCode)> on_disconnect,
ContextualCallback<
void(hci::ErrorCode hci_status, uint16_t, uint8_t version, uint16_t manufacturer_name, uint16_t sub_version)>
on_read_remote_version) {
for (const auto event : AclConnectionEvents) {
RegisterEventHandler(event, event_handler);
}
disconnect_handlers_.push_back(on_disconnect);
read_remote_version_handlers_.push_back(on_read_remote_version);
return &acl_connection_manager_interface_;
}
LeAclConnectionInterface* HciLayer::GetLeAclConnectionInterface(
ContextualCallback<void(LeMetaEventView)> event_handler,
ContextualCallback<void(uint16_t, ErrorCode)> on_disconnect,
ContextualCallback<
void(hci::ErrorCode hci_status, uint16_t, uint8_t version, uint16_t manufacturer_name, uint16_t sub_version)>
on_read_remote_version) {
for (const auto event : LeConnectionManagementEvents) {
RegisterLeEventHandler(event, event_handler);
}
disconnect_handlers_.push_back(on_disconnect);
read_remote_version_handlers_.push_back(on_read_remote_version);
return &le_acl_connection_manager_interface_;
}
SecurityInterface* HciLayer::GetSecurityInterface(ContextualCallback<void(EventView)> event_handler) {
for (const auto event : SecurityEvents) {
RegisterEventHandler(event, event_handler);
}
return &security_interface;
}
LeSecurityInterface* HciLayer::GetLeSecurityInterface(ContextualCallback<void(LeMetaEventView)> event_handler) {
for (const auto subevent : LeSecurityEvents) {
RegisterLeEventHandler(subevent, event_handler);
}
return &le_security_interface;
}
LeAdvertisingInterface* HciLayer::GetLeAdvertisingInterface(ContextualCallback<void(LeMetaEventView)> event_handler) {
for (const auto subevent : LeAdvertisingEvents) {
RegisterLeEventHandler(subevent, event_handler);
}
return &le_advertising_interface;
}
LeScanningInterface* HciLayer::GetLeScanningInterface(ContextualCallback<void(LeMetaEventView)> event_handler) {
for (const auto subevent : LeScanningEvents) {
RegisterLeEventHandler(subevent, event_handler);
}
return &le_scanning_interface;
}
LeIsoInterface* HciLayer::GetLeIsoInterface(ContextualCallback<void(LeMetaEventView)> event_handler) {
for (const auto subevent : LeIsoEvents) {
RegisterLeEventHandler(subevent, event_handler);
}
return &le_iso_interface;
}
const ModuleFactory HciLayer::Factory = ModuleFactory([]() { return new HciLayer(); });
void HciLayer::ListDependencies(ModuleList* list) const {
list->add<hal::HciHal>();
list->add<storage::StorageModule>();
}
void HciLayer::Start() {
auto hal = GetDependency<hal::HciHal>();
impl_ = new impl(hal, *this);
hal_callbacks_ = new hal_callbacks(*this);
Handler* handler = GetHandler();
impl_->acl_queue_.GetDownEnd()->RegisterDequeue(handler, BindOn(impl_, &impl::on_outbound_acl_ready));
impl_->sco_queue_.GetDownEnd()->RegisterDequeue(handler, BindOn(impl_, &impl::on_outbound_sco_ready));
impl_->iso_queue_.GetDownEnd()->RegisterDequeue(handler, BindOn(impl_, &impl::on_outbound_iso_ready));
RegisterEventHandler(EventCode::COMMAND_COMPLETE, handler->BindOn(impl_, &impl::on_command_complete));
RegisterEventHandler(EventCode::COMMAND_STATUS, handler->BindOn(impl_, &impl::on_command_status));
RegisterLeMetaEventHandler(handler->BindOn(impl_, &impl::on_le_meta_event));
if (bluetooth::common::init_flags::gd_acl_is_enabled() || bluetooth::common::init_flags::gd_l2cap_is_enabled()) {
RegisterEventHandler(
EventCode::DISCONNECTION_COMPLETE, handler->BindOn(this, &HciLayer::on_disconnection_complete));
RegisterEventHandler(
EventCode::READ_REMOTE_VERSION_INFORMATION_COMPLETE,
handler->BindOn(this, &HciLayer::on_read_remote_version_complete));
}
auto drop_packet = handler->BindOn(impl_, &impl::drop);
RegisterEventHandler(EventCode::PAGE_SCAN_REPETITION_MODE_CHANGE, drop_packet);
RegisterEventHandler(EventCode::MAX_SLOTS_CHANGE, drop_packet);
EnqueueCommand(ResetBuilder::Create(), handler->BindOnce(&fail_if_reset_complete_not_success));
hal->registerIncomingPacketCallback(hal_callbacks_);
}
void HciLayer::Stop() {
auto hal = GetDependency<hal::HciHal>();
hal->unregisterIncomingPacketCallback();
delete hal_callbacks_;
impl_->acl_queue_.GetDownEnd()->UnregisterDequeue();
impl_->sco_queue_.GetDownEnd()->UnregisterDequeue();
impl_->iso_queue_.GetDownEnd()->UnregisterDequeue();
delete impl_;
}
} // namespace hci
} // namespace bluetooth