| /* |
| * 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/controller.h" |
| |
| #include <android_bluetooth_flags.h> |
| |
| #include <future> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "common/init_flags.h" |
| #include "dumpsys_data_generated.h" |
| #include "hci/controller_interface.h" |
| #include "hci/event_checkers.h" |
| #include "hci/hci_layer.h" |
| #include "hci_controller_generated.h" |
| #include "os/log.h" |
| #include "os/metrics.h" |
| #include "os/system_properties.h" |
| #include "sysprops/sysprops_module.h" |
| |
| namespace bluetooth { |
| namespace hci { |
| |
| constexpr uint8_t kMinEncryptionKeySize = 7; // #define MIN_ENCRYPTION_KEY_SIZE 7 |
| |
| constexpr bool kDefaultVendorCapabilitiesEnabled = true; |
| static const std::string kPropertyVendorCapabilitiesEnabled = |
| "bluetooth.core.le.vendor_capabilities.enabled"; |
| |
| using os::Handler; |
| |
| struct Controller::impl { |
| impl(Controller& module) : module_(module) {} |
| |
| void Start(hci::HciLayer* hci) { |
| hci_ = hci; |
| Handler* handler = module_.GetHandler(); |
| hci_->RegisterEventHandler( |
| EventCode::NUMBER_OF_COMPLETED_PACKETS, handler->BindOn(this, &Controller::impl::NumberOfCompletedPackets)); |
| |
| set_event_mask(kDefaultEventMask); |
| write_le_host_support(Enable::ENABLED, Enable::DISABLED); |
| hci_->EnqueueCommand(ReadLocalNameBuilder::Create(), |
| handler->BindOnceOn(this, &Controller::impl::read_local_name_complete_handler)); |
| hci_->EnqueueCommand(ReadLocalVersionInformationBuilder::Create(), |
| handler->BindOnceOn(this, &Controller::impl::read_local_version_information_complete_handler)); |
| hci_->EnqueueCommand(ReadLocalSupportedCommandsBuilder::Create(), |
| handler->BindOnceOn(this, &Controller::impl::read_local_supported_commands_complete_handler)); |
| |
| hci_->EnqueueCommand( |
| LeReadLocalSupportedFeaturesBuilder::Create(), |
| handler->BindOnceOn(this, &Controller::impl::le_read_local_supported_features_handler)); |
| |
| hci_->EnqueueCommand( |
| LeReadSupportedStatesBuilder::Create(), |
| handler->BindOnceOn(this, &Controller::impl::le_read_supported_states_handler)); |
| |
| // Wait for all extended features read |
| std::promise<void> features_promise; |
| auto features_future = features_promise.get_future(); |
| |
| hci_->EnqueueCommand(ReadLocalExtendedFeaturesBuilder::Create(0x00), |
| handler->BindOnceOn(this, &Controller::impl::read_local_extended_features_complete_handler, |
| std::move(features_promise))); |
| features_future.wait(); |
| |
| le_set_event_mask(MaskLeEventMask(local_version_information_.hci_version_, kDefaultLeEventMask)); |
| |
| hci_->EnqueueCommand(ReadBufferSizeBuilder::Create(), |
| handler->BindOnceOn(this, &Controller::impl::read_buffer_size_complete_handler)); |
| |
| if (common::init_flags::set_min_encryption_is_enabled() && is_supported(OpCode::SET_MIN_ENCRYPTION_KEY_SIZE)) { |
| hci_->EnqueueCommand( |
| SetMinEncryptionKeySizeBuilder::Create(kMinEncryptionKeySize), |
| handler->BindOnceOn(this, &Controller::impl::set_min_encryption_key_size_handler)); |
| } |
| |
| if (is_supported(OpCode::LE_READ_BUFFER_SIZE_V2)) { |
| hci_->EnqueueCommand( |
| LeReadBufferSizeV2Builder::Create(), |
| handler->BindOnceOn(this, &Controller::impl::le_read_buffer_size_v2_handler)); |
| } else { |
| hci_->EnqueueCommand( |
| LeReadBufferSizeV1Builder::Create(), |
| handler->BindOnceOn(this, &Controller::impl::le_read_buffer_size_handler)); |
| } |
| |
| if (is_supported(OpCode::READ_LOCAL_SUPPORTED_CODECS_V1)) { |
| hci_->EnqueueCommand( |
| ReadLocalSupportedCodecsV1Builder::Create(), |
| handler->BindOnceOn(this, &Controller::impl::read_local_supported_codecs_v1_handler)); |
| } |
| |
| hci_->EnqueueCommand( |
| LeReadFilterAcceptListSizeBuilder::Create(), |
| handler->BindOnceOn(this, &Controller::impl::le_read_accept_list_size_handler)); |
| |
| if (is_supported(OpCode::LE_READ_RESOLVING_LIST_SIZE) && module_.SupportsBlePrivacy()) { |
| hci_->EnqueueCommand( |
| LeReadResolvingListSizeBuilder::Create(), |
| handler->BindOnceOn(this, &Controller::impl::le_read_resolving_list_size_handler)); |
| } else { |
| LOG_INFO("LE_READ_RESOLVING_LIST_SIZE not supported, defaulting to 0"); |
| le_resolving_list_size_ = 0; |
| } |
| |
| if (is_supported(OpCode::LE_READ_MAXIMUM_DATA_LENGTH) && module_.SupportsBleDataPacketLengthExtension()) { |
| hci_->EnqueueCommand(LeReadMaximumDataLengthBuilder::Create(), |
| handler->BindOnceOn(this, &Controller::impl::le_read_maximum_data_length_handler)); |
| } else { |
| LOG_INFO("LE_READ_MAXIMUM_DATA_LENGTH not supported, defaulting to 0"); |
| le_maximum_data_length_.supported_max_rx_octets_ = 0; |
| le_maximum_data_length_.supported_max_rx_time_ = 0; |
| le_maximum_data_length_.supported_max_tx_octets_ = 0; |
| le_maximum_data_length_.supported_max_tx_time_ = 0; |
| } |
| |
| // SSP is managed by security layer once enabled |
| write_simple_pairing_mode(Enable::ENABLED); |
| if (module_.SupportsSecureConnections()) { |
| hci_->EnqueueCommand( |
| WriteSecureConnectionsHostSupportBuilder::Create(Enable::ENABLED), |
| handler->BindOnceOn( |
| this, &Controller::impl::write_secure_connections_host_support_complete_handler)); |
| } |
| if (is_supported(OpCode::LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH) && module_.SupportsBleDataPacketLengthExtension()) { |
| hci_->EnqueueCommand( |
| LeReadSuggestedDefaultDataLengthBuilder::Create(), |
| handler->BindOnceOn(this, &Controller::impl::le_read_suggested_default_data_length_handler)); |
| } else { |
| LOG_INFO("LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH not supported, defaulting to 27 (0x1B)"); |
| le_suggested_default_data_length_ = 27; |
| } |
| |
| if (is_supported(OpCode::LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH) && module_.SupportsBleExtendedAdvertising()) { |
| hci_->EnqueueCommand( |
| LeReadMaximumAdvertisingDataLengthBuilder::Create(), |
| handler->BindOnceOn(this, &Controller::impl::le_read_maximum_advertising_data_length_handler)); |
| } else { |
| LOG_INFO("LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH not supported, defaulting to 31 (0x1F)"); |
| le_maximum_advertising_data_length_ = 31; |
| } |
| |
| if (is_supported(OpCode::LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS) && |
| module_.SupportsBleExtendedAdvertising()) { |
| hci_->EnqueueCommand( |
| LeReadNumberOfSupportedAdvertisingSetsBuilder::Create(), |
| handler->BindOnceOn(this, &Controller::impl::le_read_number_of_supported_advertising_sets_handler)); |
| } else { |
| LOG_INFO("LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS not supported, defaulting to 1"); |
| le_number_supported_advertising_sets_ = 1; |
| } |
| |
| if (is_supported(OpCode::LE_READ_PERIODIC_ADVERTISER_LIST_SIZE) && |
| module_.SupportsBlePeriodicAdvertising()) { |
| hci_->EnqueueCommand( |
| LeReadPeriodicAdvertiserListSizeBuilder::Create(), |
| handler->BindOnceOn(this, &Controller::impl::le_read_periodic_advertiser_list_size_handler)); |
| } else { |
| LOG_INFO("LE_READ_PERIODIC_ADVERTISER_LIST_SIZE not supported, defaulting to 0"); |
| le_periodic_advertiser_list_size_ = 0; |
| } |
| if (is_supported(OpCode::LE_SET_HOST_FEATURE) && module_.SupportsBleConnectedIsochronousStreamCentral()) { |
| hci_->EnqueueCommand( |
| LeSetHostFeatureBuilder::Create(LeHostFeatureBits::CONNECTED_ISO_STREAM_HOST_SUPPORT, Enable::ENABLED), |
| handler->BindOnceOn(this, &Controller::impl::le_set_host_feature_handler)); |
| } |
| |
| if (common::init_flags::subrating_is_enabled() && is_supported(OpCode::LE_SET_HOST_FEATURE) && |
| module_.SupportsBleConnectionSubrating()) { |
| hci_->EnqueueCommand( |
| LeSetHostFeatureBuilder::Create( |
| LeHostFeatureBits::CONNECTION_SUBRATING_HOST_SUPPORT, Enable::ENABLED), |
| handler->BindOnceOn(this, &Controller::impl::le_set_host_feature_handler)); |
| } |
| |
| if (is_supported(OpCode::READ_DEFAULT_ERRONEOUS_DATA_REPORTING)) { |
| hci_->EnqueueCommand( |
| ReadDefaultErroneousDataReportingBuilder::Create(), |
| handler->BindOnceOn( |
| this, &Controller::impl::read_default_erroneous_data_reporting_handler)); |
| } |
| |
| // Skip vendor capabilities check if configured. |
| if (os::GetSystemPropertyBool( |
| kPropertyVendorCapabilitiesEnabled, kDefaultVendorCapabilitiesEnabled)) { |
| // More commands can be enqueued from le_get_vendor_capabilities_handler |
| std::promise<void> vendor_promise; |
| auto vendor_future = vendor_promise.get_future(); |
| hci_->EnqueueCommand( |
| LeGetVendorCapabilitiesBuilder::Create(), |
| handler->BindOnceOn( |
| this, |
| &Controller::impl::le_get_vendor_capabilities_handler, |
| std::move(vendor_promise))); |
| vendor_future.wait(); |
| } else { |
| vendor_capabilities_.is_supported_ = 0x00; |
| } |
| |
| // We only need to synchronize the last read. Make BD_ADDR to be the last one. |
| std::promise<void> promise; |
| auto future = promise.get_future(); |
| hci_->EnqueueCommand( |
| ReadBdAddrBuilder::Create(), |
| handler->BindOnceOn(this, &Controller::impl::read_controller_mac_address_handler, std::move(promise))); |
| future.wait(); |
| } |
| |
| void Stop() { |
| hci_ = nullptr; |
| } |
| |
| void NumberOfCompletedPackets(EventView event) { |
| if (acl_credits_callback_.IsEmpty()) { |
| LOG_WARN("Received event when AclManager is not listening"); |
| return; |
| } |
| auto complete_view = NumberOfCompletedPacketsView::Create(event); |
| ASSERT(complete_view.IsValid()); |
| for (auto completed_packets : complete_view.GetCompletedPackets()) { |
| uint16_t handle = completed_packets.connection_handle_; |
| uint16_t credits = completed_packets.host_num_of_completed_packets_; |
| acl_credits_callback_.Invoke(handle, credits); |
| if (!acl_monitor_credits_callback_.IsEmpty()) { |
| acl_monitor_credits_callback_.Invoke(handle, credits); |
| } |
| } |
| } |
| |
| void register_completed_acl_packets_callback(CompletedAclPacketsCallback callback) { |
| ASSERT(acl_credits_callback_.IsEmpty()); |
| acl_credits_callback_ = callback; |
| } |
| |
| void unregister_completed_acl_packets_callback() { |
| ASSERT(!acl_credits_callback_.IsEmpty()); |
| acl_credits_callback_ = {}; |
| } |
| |
| void register_completed_monitor_acl_packets_callback(CompletedAclPacketsCallback callback) { |
| ASSERT(acl_monitor_credits_callback_.IsEmpty()); |
| acl_monitor_credits_callback_ = callback; |
| } |
| |
| void unregister_completed_monitor_acl_packets_callback() { |
| ASSERT(!acl_monitor_credits_callback_.IsEmpty()); |
| acl_monitor_credits_callback_ = {}; |
| } |
| |
| void register_monitor_completed_acl_packets_callback(CompletedAclPacketsCallback callback) { |
| ASSERT(acl_monitor_credits_callback_.IsEmpty()); |
| acl_monitor_credits_callback_ = callback; |
| } |
| |
| void unregister_monitor_completed_acl_packets_callback() { |
| ASSERT(!acl_monitor_credits_callback_.IsEmpty()); |
| acl_monitor_credits_callback_ = {}; |
| } |
| |
| void write_secure_connections_host_support_complete_handler(CommandCompleteView view) { |
| auto complete_view = WriteSecureConnectionsHostSupportCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| } |
| |
| void read_local_name_complete_handler(CommandCompleteView view) { |
| auto complete_view = ReadLocalNameCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| std::array<uint8_t, 248> local_name_array = complete_view.GetLocalName(); |
| |
| local_name_ = std::string(local_name_array.begin(), local_name_array.end()); |
| // erase \0 |
| local_name_.erase(std::find(local_name_.begin(), local_name_.end(), '\0'), local_name_.end()); |
| } |
| |
| void read_local_version_information_complete_handler(CommandCompleteView view) { |
| auto complete_view = ReadLocalVersionInformationCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| |
| local_version_information_ = complete_view.GetLocalVersionInformation(); |
| bluetooth::os::LogMetricBluetoothLocalVersions( |
| local_version_information_.manufacturer_name_, |
| static_cast<uint8_t>(local_version_information_.lmp_version_), |
| local_version_information_.lmp_subversion_, |
| static_cast<uint8_t>(local_version_information_.hci_version_), |
| local_version_information_.hci_revision_); |
| } |
| |
| void read_local_supported_commands_complete_handler(CommandCompleteView view) { |
| auto complete_view = ReadLocalSupportedCommandsCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| local_supported_commands_ = complete_view.GetSupportedCommands(); |
| } |
| |
| void read_local_extended_features_complete_handler(std::promise<void> promise, CommandCompleteView view) { |
| auto complete_view = ReadLocalExtendedFeaturesCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| uint8_t page_number = complete_view.GetPageNumber(); |
| extended_lmp_features_array_.push_back(complete_view.GetExtendedLmpFeatures()); |
| bluetooth::os::LogMetricBluetoothLocalSupportedFeatures(page_number, complete_view.GetExtendedLmpFeatures()); |
| // Query all extended features |
| if (page_number < complete_view.GetMaximumPageNumber()) { |
| page_number++; |
| hci_->EnqueueCommand( |
| ReadLocalExtendedFeaturesBuilder::Create(page_number), |
| module_.GetHandler()->BindOnceOn(this, &Controller::impl::read_local_extended_features_complete_handler, |
| std::move(promise))); |
| } else { |
| promise.set_value(); |
| } |
| } |
| |
| void read_buffer_size_complete_handler(CommandCompleteView view) { |
| auto complete_view = ReadBufferSizeCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| acl_buffer_length_ = complete_view.GetAclDataPacketLength(); |
| acl_buffers_ = complete_view.GetTotalNumAclDataPackets(); |
| |
| sco_buffer_length_ = complete_view.GetSynchronousDataPacketLength(); |
| sco_buffers_ = complete_view.GetTotalNumSynchronousDataPackets(); |
| } |
| |
| void read_controller_mac_address_handler(std::promise<void> promise, CommandCompleteView view) { |
| auto complete_view = ReadBdAddrCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| mac_address_ = complete_view.GetBdAddr(); |
| promise.set_value(); |
| } |
| |
| void le_read_buffer_size_handler(CommandCompleteView view) { |
| auto complete_view = LeReadBufferSizeV1CompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| le_buffer_size_ = complete_view.GetLeBufferSize(); |
| |
| // If LE buffer size is zero, then buffers returned by Read_Buffer_Size are shared between BR/EDR and LE. |
| if (le_buffer_size_.total_num_le_packets_ == 0) { |
| ASSERT(acl_buffers_ != 0); |
| le_buffer_size_.total_num_le_packets_ = acl_buffers_ / 2; |
| acl_buffers_ -= le_buffer_size_.total_num_le_packets_; |
| le_buffer_size_.le_data_packet_length_ = acl_buffer_length_; |
| } |
| } |
| |
| void read_local_supported_codecs_v1_handler(CommandCompleteView view) { |
| auto complete_view = ReadLocalSupportedCodecsV1CompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG( |
| status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| local_supported_codec_ids_ = complete_view.GetSupportedCodecs(); |
| local_supported_vendor_codec_ids_ = complete_view.GetVendorSpecificCodecs(); |
| } |
| |
| void set_min_encryption_key_size_handler(CommandCompleteView view) { |
| auto complete_view = SetMinEncryptionKeySizeCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| } |
| |
| void le_read_buffer_size_v2_handler(CommandCompleteView view) { |
| auto complete_view = LeReadBufferSizeV2CompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| le_buffer_size_ = complete_view.GetLeBufferSize(); |
| iso_buffer_size_ = complete_view.GetIsoBufferSize(); |
| |
| // If LE buffer size is zero, then buffers returned by Read_Buffer_Size are shared between BR/EDR and LE. |
| if (le_buffer_size_.total_num_le_packets_ == 0) { |
| ASSERT(acl_buffers_ != 0); |
| le_buffer_size_.total_num_le_packets_ = acl_buffers_ / 2; |
| acl_buffers_ -= le_buffer_size_.total_num_le_packets_; |
| le_buffer_size_.le_data_packet_length_ = acl_buffer_length_; |
| } |
| } |
| |
| void le_set_host_feature_handler(CommandCompleteView view) { |
| auto complete_view = LeSetHostFeatureCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| } |
| |
| void read_default_erroneous_data_reporting_handler(CommandCompleteView view) { |
| ASSERT(view.GetCommandOpCode() == OpCode::READ_DEFAULT_ERRONEOUS_DATA_REPORTING); |
| auto complete_view = ReadDefaultErroneousDataReportingCompleteView::Create(view); |
| // Check to see that the opcode was correct. |
| // ASSERT(complete_view.IsValid()) is not used here to avoid process abort. |
| // Some devices, such as mokey_go32, may claim to support it but do not |
| // actually do so (b/277589118). |
| if (!complete_view.IsValid()) { |
| LOG_ERROR("invalid command complete view"); |
| return; |
| } |
| |
| ErrorCode status = complete_view.GetStatus(); |
| // This is an optional feature to enhance audio quality. It is okay |
| // to just return if the status is not SUCCESS. |
| if (status != ErrorCode::SUCCESS) { |
| LOG_ERROR("Unexpected status: %s", ErrorCodeText(status).c_str()); |
| return; |
| } |
| |
| Enable erroneous_data_reporting = complete_view.GetErroneousDataReporting(); |
| LOG_INFO("erroneous data reporting: %hhu", erroneous_data_reporting); |
| |
| // Enable Erroneous Data Reporting if it is disabled and the write command is supported. |
| if (erroneous_data_reporting == Enable::DISABLED && |
| is_supported(OpCode::WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING)) { |
| std::unique_ptr<WriteDefaultErroneousDataReportingBuilder> packet = |
| WriteDefaultErroneousDataReportingBuilder::Create(Enable::ENABLED); |
| hci_->EnqueueCommand( |
| std::move(packet), |
| module_.GetHandler()->BindOnceOn( |
| this, &Controller::impl::write_default_erroneous_data_reporting_handler)); |
| } |
| } |
| |
| void write_default_erroneous_data_reporting_handler(CommandCompleteView view) { |
| ASSERT(view.GetCommandOpCode() == OpCode::WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING); |
| auto complete_view = WriteDefaultErroneousDataReportingCompleteView::Create(view); |
| // Check to see that the opcode was correct. |
| // ASSERT(complete_view.IsValid()) is not used here to avoid process abort. |
| // Some devices, such as mokey_go32, may claim to support it but do not |
| // actually do so (b/277589118). |
| if (!complete_view.IsValid()) { |
| LOG_ERROR("invalid command complete view"); |
| return; |
| } |
| |
| ErrorCode status = complete_view.GetStatus(); |
| // This is an optional feature to enhance audio quality. It is okay |
| // to just return if the status is not SUCCESS. |
| if (status != ErrorCode::SUCCESS) { |
| LOG_ERROR("Unexpected status: %s", ErrorCodeText(status).c_str()); |
| return; |
| } |
| } |
| |
| void le_read_local_supported_features_handler(CommandCompleteView view) { |
| auto complete_view = LeReadLocalSupportedFeaturesCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| le_local_supported_features_ = complete_view.GetLeFeatures(); |
| } |
| |
| void le_read_supported_states_handler(CommandCompleteView view) { |
| auto complete_view = LeReadSupportedStatesCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| le_supported_states_ = complete_view.GetLeStates(); |
| } |
| |
| void le_read_accept_list_size_handler(CommandCompleteView view) { |
| auto complete_view = LeReadFilterAcceptListSizeCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| le_accept_list_size_ = complete_view.GetFilterAcceptListSize(); |
| } |
| |
| void le_read_resolving_list_size_handler(CommandCompleteView view) { |
| auto complete_view = LeReadResolvingListSizeCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| le_resolving_list_size_ = complete_view.GetResolvingListSize(); |
| } |
| |
| void le_read_maximum_data_length_handler(CommandCompleteView view) { |
| auto complete_view = LeReadMaximumDataLengthCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| le_maximum_data_length_ = complete_view.GetLeMaximumDataLength(); |
| } |
| |
| void le_read_suggested_default_data_length_handler(CommandCompleteView view) { |
| auto complete_view = LeReadSuggestedDefaultDataLengthCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| le_suggested_default_data_length_ = complete_view.GetTxOctets(); |
| } |
| |
| void le_read_maximum_advertising_data_length_handler(CommandCompleteView view) { |
| auto complete_view = LeReadMaximumAdvertisingDataLengthCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| le_maximum_advertising_data_length_ = complete_view.GetMaximumAdvertisingDataLength(); |
| } |
| |
| void le_read_number_of_supported_advertising_sets_handler(CommandCompleteView view) { |
| auto complete_view = LeReadNumberOfSupportedAdvertisingSetsCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| le_number_supported_advertising_sets_ = complete_view.GetNumberSupportedAdvertisingSets(); |
| } |
| |
| void le_read_periodic_advertiser_list_size_handler(CommandCompleteView view) { |
| auto complete_view = LeReadPeriodicAdvertiserListSizeCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| ErrorCode status = complete_view.GetStatus(); |
| ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str()); |
| le_periodic_advertiser_list_size_ = complete_view.GetPeriodicAdvertiserListSize(); |
| } |
| |
| void le_get_vendor_capabilities_handler( |
| std::promise<void> vendor_promise, CommandCompleteView view) { |
| auto complete_view = LeGetVendorCapabilitiesCompleteView::Create(view); |
| |
| vendor_capabilities_.is_supported_ = 0x00; |
| vendor_capabilities_.max_advt_instances_ = 0x00; |
| vendor_capabilities_.offloaded_resolution_of_private_address_ = 0x00; |
| vendor_capabilities_.total_scan_results_storage_ = 0x00; |
| vendor_capabilities_.max_irk_list_sz_ = 0x00; |
| vendor_capabilities_.filtering_support_ = 0x00; |
| vendor_capabilities_.max_filter_ = 0x00; |
| vendor_capabilities_.activity_energy_info_support_ = 0x00; |
| vendor_capabilities_.version_supported_ = 0x00; |
| vendor_capabilities_.version_supported_ = 0x00; |
| vendor_capabilities_.total_num_of_advt_tracked_ = 0x00; |
| vendor_capabilities_.extended_scan_support_ = 0x00; |
| vendor_capabilities_.debug_logging_supported_ = 0x00; |
| vendor_capabilities_.le_address_generation_offloading_support_ = 0x00; |
| vendor_capabilities_.a2dp_source_offload_capability_mask_ = 0x00; |
| vendor_capabilities_.bluetooth_quality_report_support_ = 0x00; |
| vendor_capabilities_.a2dp_offload_v2_support_ = 0x00; |
| |
| if (!complete_view.IsValid()) { |
| vendor_promise.set_value(); |
| return; |
| } |
| vendor_capabilities_.is_supported_ = 0x01; |
| |
| // v0.55 |
| BaseVendorCapabilities base_vendor_capabilities = complete_view.GetBaseVendorCapabilities(); |
| vendor_capabilities_.max_advt_instances_ = base_vendor_capabilities.max_advt_instances_; |
| vendor_capabilities_.offloaded_resolution_of_private_address_ = |
| base_vendor_capabilities.offloaded_resolution_of_private_address_; |
| vendor_capabilities_.total_scan_results_storage_ = |
| base_vendor_capabilities.total_scan_results_storage_; |
| vendor_capabilities_.max_irk_list_sz_ = base_vendor_capabilities.max_irk_list_sz_; |
| vendor_capabilities_.filtering_support_ = base_vendor_capabilities.filtering_support_; |
| vendor_capabilities_.max_filter_ = base_vendor_capabilities.max_filter_; |
| vendor_capabilities_.activity_energy_info_support_ = |
| base_vendor_capabilities.activity_energy_info_support_; |
| |
| if (complete_view.GetPayload().size() == 0) { |
| vendor_capabilities_.version_supported_ = 55; |
| vendor_promise.set_value(); |
| return; |
| } |
| |
| // v0.95 |
| auto v95 = LeGetVendorCapabilitiesComplete095View::Create(complete_view); |
| if (!v95.IsValid()) { |
| LOG_INFO("invalid data for hci requirements v0.95"); |
| vendor_promise.set_value(); |
| return; |
| } |
| vendor_capabilities_.version_supported_ = v95.GetVersionSupported(); |
| vendor_capabilities_.total_num_of_advt_tracked_ = v95.GetTotalNumOfAdvtTracked(); |
| vendor_capabilities_.extended_scan_support_ = v95.GetExtendedScanSupport(); |
| vendor_capabilities_.debug_logging_supported_ = v95.GetDebugLoggingSupported(); |
| if (vendor_capabilities_.version_supported_ <= 95 || complete_view.GetPayload().size() == 0) { |
| vendor_promise.set_value(); |
| return; |
| } |
| |
| // v0.96 |
| auto v96 = LeGetVendorCapabilitiesComplete096View::Create(v95); |
| if (!v96.IsValid()) { |
| LOG_INFO("invalid data for hci requirements v0.96"); |
| vendor_promise.set_value(); |
| return; |
| } |
| vendor_capabilities_.le_address_generation_offloading_support_ = |
| v96.GetLeAddressGenerationOffloadingSupport(); |
| if (vendor_capabilities_.version_supported_ <= 96 || complete_view.GetPayload().size() == 0) { |
| vendor_promise.set_value(); |
| return; |
| } |
| |
| // v0.98 |
| auto v98 = LeGetVendorCapabilitiesComplete098View::Create(v96); |
| if (!v98.IsValid()) { |
| LOG_INFO("invalid data for hci requirements v0.98"); |
| vendor_promise.set_value(); |
| return; |
| } |
| vendor_capabilities_.a2dp_source_offload_capability_mask_ = |
| v98.GetA2dpSourceOffloadCapabilityMask(); |
| vendor_capabilities_.bluetooth_quality_report_support_ = v98.GetBluetoothQualityReportSupport(); |
| |
| // v1.03 |
| auto v103 = LeGetVendorCapabilitiesComplete103View::Create(v98); |
| if (!v103.IsValid()) { |
| LOG_INFO("invalid data for hci requirements v1.03"); |
| vendor_promise.set_value(); |
| return; |
| } |
| vendor_capabilities_.dynamic_audio_buffer_support_ = v103.GetDynamicAudioBufferSupport(); |
| |
| if (IS_FLAG_ENABLED(a2dp_offload_codec_extensibility)) { |
| // v1.04 |
| auto v104 = LeGetVendorCapabilitiesComplete104View::Create(v103); |
| if (!v104.IsValid()) { |
| LOG_INFO("invalid data for hci requirements v1.04"); |
| } else { |
| vendor_capabilities_.a2dp_offload_v2_support_ = v104.GetA2dpOffloadV2Support(); |
| } |
| |
| if (vendor_capabilities_.dynamic_audio_buffer_support_) { |
| hci_->EnqueueCommand( |
| DabGetAudioBufferTimeCapabilityBuilder::Create(), |
| module_.GetHandler()->BindOnceOn( |
| this, |
| &Controller::impl::le_get_dynamic_audio_buffer_support_handler, |
| std::move(vendor_promise))); |
| return; |
| } |
| |
| vendor_promise.set_value(); |
| } else { |
| if (vendor_capabilities_.dynamic_audio_buffer_support_ == 0) { |
| vendor_promise.set_value(); |
| return; |
| } |
| hci_->EnqueueCommand( |
| DabGetAudioBufferTimeCapabilityBuilder::Create(), |
| module_.GetHandler()->BindOnceOn( |
| this, |
| &Controller::impl::le_get_dynamic_audio_buffer_support_handler, |
| std::move(vendor_promise))); |
| } |
| } |
| |
| void le_get_dynamic_audio_buffer_support_handler( |
| std::promise<void> vendor_promise, CommandCompleteView view) { |
| vendor_promise.set_value(); |
| auto dab_complete_view = DynamicAudioBufferCompleteView::Create(view); |
| if (!dab_complete_view.IsValid()) { |
| LOG_WARN("Invalid command complete"); |
| return; |
| } |
| |
| if (dab_complete_view.GetStatus() != ErrorCode::SUCCESS) { |
| LOG_WARN("Unexpected error code %s", ErrorCodeText(dab_complete_view.GetStatus()).c_str()); |
| return; |
| } |
| |
| auto complete_view = DabGetAudioBufferTimeCapabilityCompleteView::Create(dab_complete_view); |
| if (!complete_view.IsValid()) { |
| LOG_WARN("Invalid get complete"); |
| return; |
| } |
| dab_supported_codecs_ = complete_view.GetAudioCodecTypeSupported(); |
| dab_codec_capabilities_ = complete_view.GetAudioCodecCapabilities(); |
| } |
| |
| void set_controller_dab_audio_buffer_time_complete(CommandCompleteView complete) { |
| auto dab_complete = DynamicAudioBufferCompleteView::Create(complete); |
| if (!dab_complete.IsValid()) { |
| LOG_WARN("Invalid command complete"); |
| return; |
| } |
| |
| if (dab_complete.GetStatus() != ErrorCode::SUCCESS) { |
| LOG_WARN("Unexpected return code %s", ErrorCodeText(dab_complete.GetStatus()).c_str()); |
| return; |
| } |
| |
| auto dab_set_complete = DabSetAudioBufferTimeCompleteView::Create(dab_complete); |
| |
| if (!dab_set_complete.IsValid()) { |
| LOG_WARN("Invalid set complete"); |
| return; |
| } |
| |
| LOG_INFO( |
| "Configured Media Tx Buffer, time returned = %d", |
| dab_set_complete.GetCurrentBufferTimeMs()); |
| } |
| |
| void set_controller_dab_audio_buffer_time(uint16_t buffer_time_ms) { |
| hci_->EnqueueCommand( |
| DabSetAudioBufferTimeBuilder::Create(buffer_time_ms), |
| module_.GetHandler()->BindOnceOn( |
| this, &Controller::impl::set_controller_dab_audio_buffer_time_complete)); |
| } |
| |
| void set_event_mask(uint64_t event_mask) { |
| std::unique_ptr<SetEventMaskBuilder> packet = SetEventMaskBuilder::Create(event_mask); |
| hci_->EnqueueCommand( |
| std::move(packet), |
| module_.GetHandler()->BindOnce(check_complete<SetEventMaskCompleteView>)); |
| } |
| |
| void write_le_host_support(Enable enable, Enable deprecated_host_bit) { |
| if (deprecated_host_bit == Enable::ENABLED) { |
| // Since Bluetooth Core Spec 4.1, this bit should be 0 |
| LOG_WARN("Setting deprecated Simultaneous LE BR/EDR Host bit"); |
| } |
| std::unique_ptr<WriteLeHostSupportBuilder> packet = WriteLeHostSupportBuilder::Create(enable, deprecated_host_bit); |
| hci_->EnqueueCommand( |
| std::move(packet), |
| module_.GetHandler()->BindOnce(check_complete<WriteLeHostSupportCompleteView>)); |
| } |
| |
| void write_simple_pairing_mode(Enable enable) { |
| std::unique_ptr<WriteSimplePairingModeBuilder> packet = WriteSimplePairingModeBuilder::Create(enable); |
| hci_->EnqueueCommand( |
| std::move(packet), |
| module_.GetHandler()->BindOnce(check_complete<WriteSimplePairingModeCompleteView>)); |
| } |
| |
| void reset() { |
| std::unique_ptr<ResetBuilder> packet = ResetBuilder::Create(); |
| hci_->EnqueueCommand( |
| std::move(packet), module_.GetHandler()->BindOnce(check_complete<ResetCompleteView>)); |
| } |
| |
| void le_rand(LeRandCallback cb) { |
| std::unique_ptr<LeRandBuilder> packet = LeRandBuilder::Create(); |
| hci_->EnqueueCommand( |
| std::move(packet), |
| module_.GetHandler()->BindOnceOn( |
| this, &Controller::impl::le_rand_cb<LeRandCompleteView>, std::move(cb))); |
| } |
| |
| template <class T> |
| void le_rand_cb(LeRandCallback cb, CommandCompleteView view) { |
| ASSERT(view.IsValid()); |
| auto status_view = T::Create(view); |
| ASSERT(status_view.IsValid()); |
| ASSERT(status_view.GetStatus() == ErrorCode::SUCCESS); |
| std::move(cb).Run(status_view.GetRandomNumber()); |
| } |
| |
| void set_event_filter(std::unique_ptr<SetEventFilterBuilder> packet) { |
| hci_->EnqueueCommand( |
| std::move(packet), |
| module_.GetHandler()->BindOnce(check_complete<SetEventFilterCompleteView>)); |
| } |
| |
| void write_local_name(std::string local_name) { |
| ASSERT(local_name.length() <= 248); |
| // Fill remaining char with 0 |
| local_name.append(std::string(248 - local_name.length(), '\0')); |
| std::array<uint8_t, 248> local_name_array; |
| std::copy(std::begin(local_name), std::end(local_name), std::begin(local_name_array)); |
| |
| std::unique_ptr<WriteLocalNameBuilder> packet = WriteLocalNameBuilder::Create(local_name_array); |
| hci_->EnqueueCommand( |
| std::move(packet), |
| module_.GetHandler()->BindOnce(check_complete<WriteLocalNameCompleteView>)); |
| } |
| |
| void host_buffer_size(uint16_t host_acl_data_packet_length, uint8_t host_synchronous_data_packet_length, |
| uint16_t host_total_num_acl_data_packets, uint16_t host_total_num_synchronous_data_packets) { |
| std::unique_ptr<HostBufferSizeBuilder> packet = |
| HostBufferSizeBuilder::Create(host_acl_data_packet_length, host_synchronous_data_packet_length, |
| host_total_num_acl_data_packets, host_total_num_synchronous_data_packets); |
| hci_->EnqueueCommand( |
| std::move(packet), |
| module_.GetHandler()->BindOnce(check_complete<HostBufferSizeCompleteView>)); |
| } |
| |
| void le_set_event_mask(uint64_t le_event_mask) { |
| std::unique_ptr<LeSetEventMaskBuilder> packet = LeSetEventMaskBuilder::Create(le_event_mask); |
| hci_->EnqueueCommand( |
| std::move(packet), |
| module_.GetHandler()->BindOnce(check_complete<LeSetEventMaskCompleteView>)); |
| } |
| |
| #define OP_CODE_MAPPING(name) \ |
| case OpCode::name: { \ |
| uint16_t index = (uint16_t)OpCodeIndex::name; \ |
| uint16_t byte_index = index / 10; \ |
| uint16_t bit_index = index % 10; \ |
| bool supported = local_supported_commands_[byte_index] & (1 << bit_index); \ |
| if (!supported) { \ |
| LOG_DEBUG("unsupported command opcode: 0x%04x", (uint16_t)OpCode::name); \ |
| } \ |
| return supported; \ |
| } |
| |
| void Dump( |
| std::promise<flatbuffers::Offset<ControllerData>> promise, flatbuffers::FlatBufferBuilder* fb_builder) const; |
| |
| bool is_supported(OpCode op_code) { |
| switch (op_code) { |
| OP_CODE_MAPPING(INQUIRY) |
| OP_CODE_MAPPING(INQUIRY_CANCEL) |
| OP_CODE_MAPPING(PERIODIC_INQUIRY_MODE) |
| OP_CODE_MAPPING(EXIT_PERIODIC_INQUIRY_MODE) |
| OP_CODE_MAPPING(CREATE_CONNECTION) |
| OP_CODE_MAPPING(DISCONNECT) |
| OP_CODE_MAPPING(CREATE_CONNECTION_CANCEL) |
| OP_CODE_MAPPING(ACCEPT_CONNECTION_REQUEST) |
| OP_CODE_MAPPING(REJECT_CONNECTION_REQUEST) |
| OP_CODE_MAPPING(LINK_KEY_REQUEST_REPLY) |
| OP_CODE_MAPPING(LINK_KEY_REQUEST_NEGATIVE_REPLY) |
| OP_CODE_MAPPING(PIN_CODE_REQUEST_REPLY) |
| OP_CODE_MAPPING(PIN_CODE_REQUEST_NEGATIVE_REPLY) |
| OP_CODE_MAPPING(CHANGE_CONNECTION_PACKET_TYPE) |
| OP_CODE_MAPPING(AUTHENTICATION_REQUESTED) |
| OP_CODE_MAPPING(SET_CONNECTION_ENCRYPTION) |
| OP_CODE_MAPPING(CHANGE_CONNECTION_LINK_KEY) |
| OP_CODE_MAPPING(CENTRAL_LINK_KEY) |
| OP_CODE_MAPPING(REMOTE_NAME_REQUEST) |
| OP_CODE_MAPPING(REMOTE_NAME_REQUEST_CANCEL) |
| OP_CODE_MAPPING(READ_REMOTE_SUPPORTED_FEATURES) |
| OP_CODE_MAPPING(READ_REMOTE_EXTENDED_FEATURES) |
| OP_CODE_MAPPING(READ_REMOTE_VERSION_INFORMATION) |
| OP_CODE_MAPPING(READ_CLOCK_OFFSET) |
| OP_CODE_MAPPING(READ_LMP_HANDLE) |
| OP_CODE_MAPPING(SETUP_SYNCHRONOUS_CONNECTION) |
| OP_CODE_MAPPING(ACCEPT_SYNCHRONOUS_CONNECTION) |
| OP_CODE_MAPPING(REJECT_SYNCHRONOUS_CONNECTION) |
| OP_CODE_MAPPING(IO_CAPABILITY_REQUEST_REPLY) |
| OP_CODE_MAPPING(USER_CONFIRMATION_REQUEST_REPLY) |
| OP_CODE_MAPPING(USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY) |
| OP_CODE_MAPPING(USER_PASSKEY_REQUEST_REPLY) |
| OP_CODE_MAPPING(USER_PASSKEY_REQUEST_NEGATIVE_REPLY) |
| OP_CODE_MAPPING(REMOTE_OOB_DATA_REQUEST_REPLY) |
| OP_CODE_MAPPING(REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY) |
| OP_CODE_MAPPING(IO_CAPABILITY_REQUEST_NEGATIVE_REPLY) |
| OP_CODE_MAPPING(ENHANCED_SETUP_SYNCHRONOUS_CONNECTION) |
| OP_CODE_MAPPING(ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION) |
| OP_CODE_MAPPING(TRUNCATED_PAGE) |
| OP_CODE_MAPPING(TRUNCATED_PAGE_CANCEL) |
| OP_CODE_MAPPING(SET_CONNECTIONLESS_PERIPHERAL_BROADCAST) |
| OP_CODE_MAPPING(SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_RECEIVE) |
| OP_CODE_MAPPING(START_SYNCHRONIZATION_TRAIN) |
| OP_CODE_MAPPING(RECEIVE_SYNCHRONIZATION_TRAIN) |
| OP_CODE_MAPPING(REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY) |
| OP_CODE_MAPPING(HOLD_MODE) |
| OP_CODE_MAPPING(SNIFF_MODE) |
| OP_CODE_MAPPING(EXIT_SNIFF_MODE) |
| OP_CODE_MAPPING(PARK_STATE) |
| OP_CODE_MAPPING(EXIT_PARK_STATE) |
| OP_CODE_MAPPING(QOS_SETUP) |
| OP_CODE_MAPPING(ROLE_DISCOVERY) |
| OP_CODE_MAPPING(SWITCH_ROLE) |
| OP_CODE_MAPPING(READ_LINK_POLICY_SETTINGS) |
| OP_CODE_MAPPING(WRITE_LINK_POLICY_SETTINGS) |
| OP_CODE_MAPPING(READ_DEFAULT_LINK_POLICY_SETTINGS) |
| OP_CODE_MAPPING(WRITE_DEFAULT_LINK_POLICY_SETTINGS) |
| OP_CODE_MAPPING(FLOW_SPECIFICATION) |
| OP_CODE_MAPPING(SNIFF_SUBRATING) |
| OP_CODE_MAPPING(SET_EVENT_MASK) |
| OP_CODE_MAPPING(RESET) |
| OP_CODE_MAPPING(SET_EVENT_FILTER) |
| OP_CODE_MAPPING(FLUSH) |
| OP_CODE_MAPPING(READ_PIN_TYPE) |
| OP_CODE_MAPPING(WRITE_PIN_TYPE) |
| OP_CODE_MAPPING(READ_STORED_LINK_KEY) |
| OP_CODE_MAPPING(WRITE_STORED_LINK_KEY) |
| OP_CODE_MAPPING(DELETE_STORED_LINK_KEY) |
| OP_CODE_MAPPING(WRITE_LOCAL_NAME) |
| OP_CODE_MAPPING(READ_LOCAL_NAME) |
| OP_CODE_MAPPING(READ_CONNECTION_ACCEPT_TIMEOUT) |
| OP_CODE_MAPPING(WRITE_CONNECTION_ACCEPT_TIMEOUT) |
| OP_CODE_MAPPING(READ_PAGE_TIMEOUT) |
| OP_CODE_MAPPING(WRITE_PAGE_TIMEOUT) |
| OP_CODE_MAPPING(READ_SCAN_ENABLE) |
| OP_CODE_MAPPING(WRITE_SCAN_ENABLE) |
| OP_CODE_MAPPING(READ_PAGE_SCAN_ACTIVITY) |
| OP_CODE_MAPPING(WRITE_PAGE_SCAN_ACTIVITY) |
| OP_CODE_MAPPING(READ_INQUIRY_SCAN_ACTIVITY) |
| OP_CODE_MAPPING(WRITE_INQUIRY_SCAN_ACTIVITY) |
| OP_CODE_MAPPING(READ_AUTHENTICATION_ENABLE) |
| OP_CODE_MAPPING(WRITE_AUTHENTICATION_ENABLE) |
| OP_CODE_MAPPING(READ_CLASS_OF_DEVICE) |
| OP_CODE_MAPPING(WRITE_CLASS_OF_DEVICE) |
| OP_CODE_MAPPING(READ_VOICE_SETTING) |
| OP_CODE_MAPPING(WRITE_VOICE_SETTING) |
| OP_CODE_MAPPING(READ_AUTOMATIC_FLUSH_TIMEOUT) |
| OP_CODE_MAPPING(WRITE_AUTOMATIC_FLUSH_TIMEOUT) |
| OP_CODE_MAPPING(READ_NUM_BROADCAST_RETRANSMITS) |
| OP_CODE_MAPPING(WRITE_NUM_BROADCAST_RETRANSMITS) |
| OP_CODE_MAPPING(READ_HOLD_MODE_ACTIVITY) |
| OP_CODE_MAPPING(WRITE_HOLD_MODE_ACTIVITY) |
| OP_CODE_MAPPING(READ_TRANSMIT_POWER_LEVEL) |
| OP_CODE_MAPPING(READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE) |
| OP_CODE_MAPPING(WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE) |
| OP_CODE_MAPPING(SET_CONTROLLER_TO_HOST_FLOW_CONTROL) |
| OP_CODE_MAPPING(HOST_BUFFER_SIZE) |
| OP_CODE_MAPPING(HOST_NUMBER_OF_COMPLETED_PACKETS) |
| OP_CODE_MAPPING(READ_LINK_SUPERVISION_TIMEOUT) |
| OP_CODE_MAPPING(WRITE_LINK_SUPERVISION_TIMEOUT) |
| OP_CODE_MAPPING(READ_NUMBER_OF_SUPPORTED_IAC) |
| OP_CODE_MAPPING(READ_CURRENT_IAC_LAP) |
| OP_CODE_MAPPING(WRITE_CURRENT_IAC_LAP) |
| OP_CODE_MAPPING(SET_AFH_HOST_CHANNEL_CLASSIFICATION) |
| OP_CODE_MAPPING(READ_INQUIRY_SCAN_TYPE) |
| OP_CODE_MAPPING(WRITE_INQUIRY_SCAN_TYPE) |
| OP_CODE_MAPPING(READ_INQUIRY_MODE) |
| OP_CODE_MAPPING(WRITE_INQUIRY_MODE) |
| OP_CODE_MAPPING(READ_PAGE_SCAN_TYPE) |
| OP_CODE_MAPPING(WRITE_PAGE_SCAN_TYPE) |
| OP_CODE_MAPPING(READ_AFH_CHANNEL_ASSESSMENT_MODE) |
| OP_CODE_MAPPING(WRITE_AFH_CHANNEL_ASSESSMENT_MODE) |
| OP_CODE_MAPPING(READ_EXTENDED_INQUIRY_RESPONSE) |
| OP_CODE_MAPPING(WRITE_EXTENDED_INQUIRY_RESPONSE) |
| OP_CODE_MAPPING(REFRESH_ENCRYPTION_KEY) |
| OP_CODE_MAPPING(READ_SIMPLE_PAIRING_MODE) |
| OP_CODE_MAPPING(WRITE_SIMPLE_PAIRING_MODE) |
| OP_CODE_MAPPING(READ_LOCAL_OOB_DATA) |
| OP_CODE_MAPPING(READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL) |
| OP_CODE_MAPPING(WRITE_INQUIRY_TRANSMIT_POWER_LEVEL) |
| OP_CODE_MAPPING(READ_DEFAULT_ERRONEOUS_DATA_REPORTING) |
| OP_CODE_MAPPING(WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING) |
| OP_CODE_MAPPING(ENHANCED_FLUSH) |
| OP_CODE_MAPPING(SEND_KEYPRESS_NOTIFICATION) |
| OP_CODE_MAPPING(SET_EVENT_MASK_PAGE_2) |
| OP_CODE_MAPPING(READ_FLOW_CONTROL_MODE) |
| OP_CODE_MAPPING(WRITE_FLOW_CONTROL_MODE) |
| OP_CODE_MAPPING(READ_ENHANCED_TRANSMIT_POWER_LEVEL) |
| OP_CODE_MAPPING(READ_LE_HOST_SUPPORT) |
| OP_CODE_MAPPING(WRITE_LE_HOST_SUPPORT) |
| OP_CODE_MAPPING(SET_MWS_CHANNEL_PARAMETERS) |
| OP_CODE_MAPPING(SET_EXTERNAL_FRAME_CONFIGURATION) |
| OP_CODE_MAPPING(SET_MWS_SIGNALING) |
| OP_CODE_MAPPING(SET_MWS_TRANSPORT_LAYER) |
| OP_CODE_MAPPING(SET_MWS_SCAN_FREQUENCY_TABLE) |
| OP_CODE_MAPPING(SET_MWS_PATTERN_CONFIGURATION) |
| OP_CODE_MAPPING(SET_RESERVED_LT_ADDR) |
| OP_CODE_MAPPING(DELETE_RESERVED_LT_ADDR) |
| OP_CODE_MAPPING(SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_DATA) |
| OP_CODE_MAPPING(READ_SYNCHRONIZATION_TRAIN_PARAMETERS) |
| OP_CODE_MAPPING(WRITE_SYNCHRONIZATION_TRAIN_PARAMETERS) |
| OP_CODE_MAPPING(READ_SECURE_CONNECTIONS_HOST_SUPPORT) |
| OP_CODE_MAPPING(WRITE_SECURE_CONNECTIONS_HOST_SUPPORT) |
| OP_CODE_MAPPING(READ_AUTHENTICATED_PAYLOAD_TIMEOUT) |
| OP_CODE_MAPPING(WRITE_AUTHENTICATED_PAYLOAD_TIMEOUT) |
| OP_CODE_MAPPING(READ_LOCAL_OOB_EXTENDED_DATA) |
| OP_CODE_MAPPING(READ_EXTENDED_PAGE_TIMEOUT) |
| OP_CODE_MAPPING(WRITE_EXTENDED_PAGE_TIMEOUT) |
| OP_CODE_MAPPING(READ_EXTENDED_INQUIRY_LENGTH) |
| OP_CODE_MAPPING(WRITE_EXTENDED_INQUIRY_LENGTH) |
| OP_CODE_MAPPING(SET_ECOSYSTEM_BASE_INTERVAL) |
| OP_CODE_MAPPING(CONFIGURE_DATA_PATH) |
| OP_CODE_MAPPING(SET_MIN_ENCRYPTION_KEY_SIZE) |
| OP_CODE_MAPPING(READ_LOCAL_VERSION_INFORMATION) |
| OP_CODE_MAPPING(READ_LOCAL_SUPPORTED_FEATURES) |
| OP_CODE_MAPPING(READ_LOCAL_EXTENDED_FEATURES) |
| OP_CODE_MAPPING(READ_BUFFER_SIZE) |
| OP_CODE_MAPPING(READ_BD_ADDR) |
| OP_CODE_MAPPING(READ_DATA_BLOCK_SIZE) |
| OP_CODE_MAPPING(READ_LOCAL_SUPPORTED_CODECS_V1) |
| OP_CODE_MAPPING(READ_LOCAL_SIMPLE_PAIRING_OPTIONS) |
| OP_CODE_MAPPING(READ_LOCAL_SUPPORTED_CODECS_V2) |
| OP_CODE_MAPPING(READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES) |
| OP_CODE_MAPPING(READ_LOCAL_SUPPORTED_CONTROLLER_DELAY) |
| OP_CODE_MAPPING(READ_FAILED_CONTACT_COUNTER) |
| OP_CODE_MAPPING(RESET_FAILED_CONTACT_COUNTER) |
| OP_CODE_MAPPING(READ_LINK_QUALITY) |
| OP_CODE_MAPPING(READ_RSSI) |
| OP_CODE_MAPPING(READ_AFH_CHANNEL_MAP) |
| OP_CODE_MAPPING(READ_CLOCK) |
| OP_CODE_MAPPING(READ_ENCRYPTION_KEY_SIZE) |
| OP_CODE_MAPPING(GET_MWS_TRANSPORT_LAYER_CONFIGURATION) |
| OP_CODE_MAPPING(SET_TRIGGERED_CLOCK_CAPTURE) |
| OP_CODE_MAPPING(READ_LOOPBACK_MODE) |
| OP_CODE_MAPPING(WRITE_LOOPBACK_MODE) |
| OP_CODE_MAPPING(ENABLE_DEVICE_UNDER_TEST_MODE) |
| OP_CODE_MAPPING(WRITE_SIMPLE_PAIRING_DEBUG_MODE) |
| OP_CODE_MAPPING(WRITE_SECURE_CONNECTIONS_TEST_MODE) |
| OP_CODE_MAPPING(LE_SET_EVENT_MASK) |
| OP_CODE_MAPPING(LE_READ_BUFFER_SIZE_V1) |
| OP_CODE_MAPPING(LE_READ_LOCAL_SUPPORTED_FEATURES) |
| OP_CODE_MAPPING(LE_SET_RANDOM_ADDRESS) |
| OP_CODE_MAPPING(LE_SET_ADVERTISING_PARAMETERS) |
| OP_CODE_MAPPING(LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER) |
| OP_CODE_MAPPING(LE_SET_ADVERTISING_DATA) |
| OP_CODE_MAPPING(LE_SET_SCAN_RESPONSE_DATA) |
| OP_CODE_MAPPING(LE_SET_ADVERTISING_ENABLE) |
| OP_CODE_MAPPING(LE_SET_SCAN_PARAMETERS) |
| OP_CODE_MAPPING(LE_SET_SCAN_ENABLE) |
| OP_CODE_MAPPING(LE_CREATE_CONNECTION) |
| OP_CODE_MAPPING(LE_CREATE_CONNECTION_CANCEL) |
| OP_CODE_MAPPING(LE_READ_FILTER_ACCEPT_LIST_SIZE) |
| OP_CODE_MAPPING(LE_CLEAR_FILTER_ACCEPT_LIST) |
| OP_CODE_MAPPING(LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST) |
| OP_CODE_MAPPING(LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST) |
| OP_CODE_MAPPING(LE_CONNECTION_UPDATE) |
| OP_CODE_MAPPING(LE_SET_HOST_CHANNEL_CLASSIFICATION) |
| OP_CODE_MAPPING(LE_READ_CHANNEL_MAP) |
| OP_CODE_MAPPING(LE_READ_REMOTE_FEATURES) |
| OP_CODE_MAPPING(LE_ENCRYPT) |
| OP_CODE_MAPPING(LE_RAND) |
| OP_CODE_MAPPING(LE_START_ENCRYPTION) |
| OP_CODE_MAPPING(LE_LONG_TERM_KEY_REQUEST_REPLY) |
| OP_CODE_MAPPING(LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY) |
| OP_CODE_MAPPING(LE_READ_SUPPORTED_STATES) |
| OP_CODE_MAPPING(LE_RECEIVER_TEST_V1) |
| OP_CODE_MAPPING(LE_TRANSMITTER_TEST_V1) |
| OP_CODE_MAPPING(LE_TEST_END) |
| OP_CODE_MAPPING(LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY) |
| OP_CODE_MAPPING(LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY) |
| OP_CODE_MAPPING(LE_SET_DATA_LENGTH) |
| OP_CODE_MAPPING(LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH) |
| OP_CODE_MAPPING(LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH) |
| OP_CODE_MAPPING(LE_READ_LOCAL_P_256_PUBLIC_KEY) |
| OP_CODE_MAPPING(LE_GENERATE_DHKEY_V1) |
| OP_CODE_MAPPING(LE_ADD_DEVICE_TO_RESOLVING_LIST) |
| OP_CODE_MAPPING(LE_REMOVE_DEVICE_FROM_RESOLVING_LIST) |
| OP_CODE_MAPPING(LE_CLEAR_RESOLVING_LIST) |
| OP_CODE_MAPPING(LE_READ_RESOLVING_LIST_SIZE) |
| OP_CODE_MAPPING(LE_READ_PEER_RESOLVABLE_ADDRESS) |
| OP_CODE_MAPPING(LE_READ_LOCAL_RESOLVABLE_ADDRESS) |
| OP_CODE_MAPPING(LE_SET_ADDRESS_RESOLUTION_ENABLE) |
| OP_CODE_MAPPING(LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT) |
| OP_CODE_MAPPING(LE_READ_MAXIMUM_DATA_LENGTH) |
| OP_CODE_MAPPING(LE_READ_PHY) |
| OP_CODE_MAPPING(LE_SET_DEFAULT_PHY) |
| OP_CODE_MAPPING(LE_SET_PHY) |
| OP_CODE_MAPPING(LE_RECEIVER_TEST_V2) |
| OP_CODE_MAPPING(LE_TRANSMITTER_TEST_V2) |
| OP_CODE_MAPPING(LE_SET_ADVERTISING_SET_RANDOM_ADDRESS) |
| OP_CODE_MAPPING(LE_SET_EXTENDED_ADVERTISING_PARAMETERS) |
| OP_CODE_MAPPING(LE_SET_EXTENDED_ADVERTISING_DATA) |
| OP_CODE_MAPPING(LE_SET_EXTENDED_SCAN_RESPONSE_DATA) |
| OP_CODE_MAPPING(LE_SET_EXTENDED_ADVERTISING_ENABLE) |
| OP_CODE_MAPPING(LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH) |
| OP_CODE_MAPPING(LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS) |
| OP_CODE_MAPPING(LE_REMOVE_ADVERTISING_SET) |
| OP_CODE_MAPPING(LE_CLEAR_ADVERTISING_SETS) |
| OP_CODE_MAPPING(LE_SET_PERIODIC_ADVERTISING_PARAMETERS) |
| OP_CODE_MAPPING(LE_SET_PERIODIC_ADVERTISING_DATA) |
| OP_CODE_MAPPING(LE_SET_PERIODIC_ADVERTISING_ENABLE) |
| OP_CODE_MAPPING(LE_SET_EXTENDED_SCAN_PARAMETERS) |
| OP_CODE_MAPPING(LE_SET_EXTENDED_SCAN_ENABLE) |
| OP_CODE_MAPPING(LE_EXTENDED_CREATE_CONNECTION) |
| OP_CODE_MAPPING(LE_PERIODIC_ADVERTISING_CREATE_SYNC) |
| OP_CODE_MAPPING(LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL) |
| OP_CODE_MAPPING(LE_PERIODIC_ADVERTISING_TERMINATE_SYNC) |
| OP_CODE_MAPPING(LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST) |
| OP_CODE_MAPPING(LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST) |
| OP_CODE_MAPPING(LE_CLEAR_PERIODIC_ADVERTISER_LIST) |
| OP_CODE_MAPPING(LE_READ_PERIODIC_ADVERTISER_LIST_SIZE) |
| OP_CODE_MAPPING(LE_READ_TRANSMIT_POWER) |
| OP_CODE_MAPPING(LE_READ_RF_PATH_COMPENSATION_POWER) |
| OP_CODE_MAPPING(LE_WRITE_RF_PATH_COMPENSATION_POWER) |
| OP_CODE_MAPPING(LE_SET_PRIVACY_MODE) |
| OP_CODE_MAPPING(LE_RECEIVER_TEST_V3) |
| OP_CODE_MAPPING(LE_TRANSMITTER_TEST_V3) |
| OP_CODE_MAPPING(LE_SET_CONNECTIONLESS_CTE_TRANSMIT_PARAMETERS) |
| OP_CODE_MAPPING(LE_SET_CONNECTIONLESS_CTE_TRANSMIT_ENABLE) |
| OP_CODE_MAPPING(LE_SET_CONNECTIONLESS_IQ_SAMPLING_ENABLE) |
| OP_CODE_MAPPING(LE_SET_CONNECTION_CTE_RECEIVE_PARAMETERS) |
| OP_CODE_MAPPING(LE_SET_CONNECTION_CTE_TRANSMIT_PARAMETERS) |
| OP_CODE_MAPPING(LE_CONNECTION_CTE_REQUEST_ENABLE) |
| OP_CODE_MAPPING(LE_CONNECTION_CTE_RESPONSE_ENABLE) |
| OP_CODE_MAPPING(LE_READ_ANTENNA_INFORMATION) |
| OP_CODE_MAPPING(LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE) |
| OP_CODE_MAPPING(LE_PERIODIC_ADVERTISING_SYNC_TRANSFER) |
| OP_CODE_MAPPING(LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER) |
| OP_CODE_MAPPING(LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS) |
| OP_CODE_MAPPING(LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS) |
| OP_CODE_MAPPING(LE_GENERATE_DHKEY_V2) |
| OP_CODE_MAPPING(LE_MODIFY_SLEEP_CLOCK_ACCURACY) |
| OP_CODE_MAPPING(LE_READ_BUFFER_SIZE_V2) |
| OP_CODE_MAPPING(LE_READ_ISO_TX_SYNC) |
| OP_CODE_MAPPING(LE_SET_CIG_PARAMETERS) |
| OP_CODE_MAPPING(LE_SET_CIG_PARAMETERS_TEST) |
| OP_CODE_MAPPING(LE_CREATE_CIS) |
| OP_CODE_MAPPING(LE_REMOVE_CIG) |
| OP_CODE_MAPPING(LE_ACCEPT_CIS_REQUEST) |
| OP_CODE_MAPPING(LE_REJECT_CIS_REQUEST) |
| OP_CODE_MAPPING(LE_CREATE_BIG) |
| OP_CODE_MAPPING(LE_CREATE_BIG_TEST) |
| OP_CODE_MAPPING(LE_TERMINATE_BIG) |
| OP_CODE_MAPPING(LE_BIG_CREATE_SYNC) |
| OP_CODE_MAPPING(LE_BIG_TERMINATE_SYNC) |
| OP_CODE_MAPPING(LE_REQUEST_PEER_SCA) |
| OP_CODE_MAPPING(LE_SETUP_ISO_DATA_PATH) |
| OP_CODE_MAPPING(LE_REMOVE_ISO_DATA_PATH) |
| OP_CODE_MAPPING(LE_ISO_TRANSMIT_TEST) |
| OP_CODE_MAPPING(LE_ISO_RECEIVE_TEST) |
| OP_CODE_MAPPING(LE_ISO_READ_TEST_COUNTERS) |
| OP_CODE_MAPPING(LE_ISO_TEST_END) |
| OP_CODE_MAPPING(LE_SET_HOST_FEATURE) |
| OP_CODE_MAPPING(LE_READ_ISO_LINK_QUALITY) |
| OP_CODE_MAPPING(LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL) |
| OP_CODE_MAPPING(LE_READ_REMOTE_TRANSMIT_POWER_LEVEL) |
| OP_CODE_MAPPING(LE_SET_PATH_LOSS_REPORTING_PARAMETERS) |
| OP_CODE_MAPPING(LE_SET_PATH_LOSS_REPORTING_ENABLE) |
| OP_CODE_MAPPING(LE_SET_TRANSMIT_POWER_REPORTING_ENABLE) |
| OP_CODE_MAPPING(LE_TRANSMITTER_TEST_V4) |
| OP_CODE_MAPPING(LE_SET_DATA_RELATED_ADDRESS_CHANGES) |
| OP_CODE_MAPPING(LE_SET_DEFAULT_SUBRATE) |
| OP_CODE_MAPPING(LE_SUBRATE_REQUEST) |
| |
| // deprecated |
| case OpCode::ADD_SCO_CONNECTION: |
| return false; |
| |
| // vendor specific |
| case OpCode::LE_GET_VENDOR_CAPABILITIES: |
| return vendor_capabilities_.is_supported_ == 0x01; |
| case OpCode::LE_MULTI_ADVT: |
| return vendor_capabilities_.max_advt_instances_ != 0x00; |
| case OpCode::LE_BATCH_SCAN: |
| return vendor_capabilities_.total_scan_results_storage_ != 0x00; |
| case OpCode::LE_ADV_FILTER: |
| return vendor_capabilities_.filtering_support_ == 0x01; |
| case OpCode::LE_ENERGY_INFO: |
| return vendor_capabilities_.activity_energy_info_support_ == 0x01; |
| case OpCode::LE_EXTENDED_SCAN_PARAMS: |
| return vendor_capabilities_.extended_scan_support_ == 0x01; |
| case OpCode::CONTROLLER_DEBUG_INFO: |
| return vendor_capabilities_.debug_logging_supported_ == 0x01; |
| case OpCode::CONTROLLER_A2DP_OPCODE: |
| return vendor_capabilities_.a2dp_source_offload_capability_mask_ != 0x00; |
| case OpCode::CONTROLLER_BQR: |
| return vendor_capabilities_.bluetooth_quality_report_support_ == 0x01; |
| case OpCode::DYNAMIC_AUDIO_BUFFER: |
| return vendor_capabilities_.dynamic_audio_buffer_support_ > 0x00; |
| // Before MSFT extension is fully supported, return false for the following MSFT_OPCODE_XXXX for now. |
| case OpCode::MSFT_OPCODE_INTEL: |
| return false; |
| case OpCode::MSFT_OPCODE_MEDIATEK: |
| return false; |
| case OpCode::MSFT_OPCODE_QUALCOMM: |
| return false; |
| // undefined in local_supported_commands_ |
| case OpCode::READ_LOCAL_SUPPORTED_COMMANDS: |
| return true; |
| case OpCode::NONE: |
| return false; |
| case OpCode::LE_CS_READ_LOCAL_SUPPORTED_CAPABILITIES: |
| case OpCode::LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES: |
| case OpCode::LE_CS_WRITE_CACHED_REMOTE_SUPPORTED_CAPABILITIES: |
| case OpCode::LE_CS_SECURITY_ENABLE: |
| case OpCode::LE_CS_SET_DEFAULT_SETTINGS: |
| case OpCode::LE_CS_READ_REMOTE_FAE_TABLE: |
| case OpCode::LE_CS_WRITE_CACHED_REMOTE_FAE_TABLE: |
| case OpCode::LE_CS_CREATE_CONFIG: |
| case OpCode::LE_CS_REMOVE_CONFIG: |
| case OpCode::LE_CS_SET_CHANNEL_CLASSIFICATION: |
| case OpCode::LE_CS_PROCEDURE_ENABLE: |
| case OpCode::LE_CS_TEST: |
| case OpCode::LE_CS_TEST_END: |
| case OpCode::LE_CS_SET_PROCEDURE_PARAMETERS: |
| // TODO add to OP_CODE_MAPPING list |
| return false; |
| } |
| return false; |
| } |
| #undef OP_CODE_MAPPING |
| |
| Controller& module_; |
| |
| HciLayer* hci_; |
| |
| CompletedAclPacketsCallback acl_credits_callback_{}; |
| CompletedAclPacketsCallback acl_monitor_credits_callback_{}; |
| LocalVersionInformation local_version_information_{}; |
| std::array<uint8_t, 64> local_supported_commands_{}; |
| std::vector<uint64_t> extended_lmp_features_array_{}; |
| uint16_t acl_buffer_length_{}; |
| uint16_t acl_buffers_{}; |
| uint8_t sco_buffer_length_{}; |
| uint16_t sco_buffers_{}; |
| Address mac_address_{}; |
| std::string local_name_{}; |
| LeBufferSize le_buffer_size_{}; |
| std::vector<uint8_t> local_supported_codec_ids_{}; |
| std::vector<uint32_t> local_supported_vendor_codec_ids_{}; |
| LeBufferSize iso_buffer_size_{}; |
| uint64_t le_local_supported_features_{}; |
| uint64_t le_supported_states_{}; |
| uint8_t le_accept_list_size_{}; |
| uint8_t le_resolving_list_size_{}; |
| LeMaximumDataLength le_maximum_data_length_{}; |
| uint16_t le_maximum_advertising_data_length_{}; |
| uint16_t le_suggested_default_data_length_{}; |
| uint8_t le_number_supported_advertising_sets_{}; |
| uint8_t le_periodic_advertiser_list_size_{}; |
| VendorCapabilities vendor_capabilities_{}; |
| uint32_t dab_supported_codecs_{}; |
| std::array<DynamicAudioBufferCodecCapability, 32> dab_codec_capabilities_{}; |
| }; // namespace hci |
| |
| Controller::Controller() : impl_(std::make_unique<impl>(*this)) {} |
| |
| Controller::~Controller() = default; |
| |
| void Controller::RegisterCompletedAclPacketsCallback(CompletedAclPacketsCallback cb) { |
| CallOn(impl_.get(), &impl::register_completed_acl_packets_callback, cb); |
| } |
| |
| void Controller::UnregisterCompletedAclPacketsCallback() { |
| CallOn(impl_.get(), &impl::unregister_completed_acl_packets_callback); |
| } |
| |
| void Controller::RegisterCompletedMonitorAclPacketsCallback(CompletedAclPacketsCallback cb) { |
| CallOn(impl_.get(), &impl::register_completed_monitor_acl_packets_callback, cb); |
| } |
| |
| void Controller::UnregisterCompletedMonitorAclPacketsCallback() { |
| CallOn(impl_.get(), &impl::unregister_completed_monitor_acl_packets_callback); |
| } |
| |
| std::string Controller::GetLocalName() const { |
| return impl_->local_name_; |
| } |
| |
| LocalVersionInformation Controller::GetLocalVersionInformation() const { |
| return impl_->local_version_information_; |
| } |
| |
| std::vector<uint8_t> Controller::GetLocalSupportedBrEdrCodecIds() const { |
| return impl_->local_supported_codec_ids_; |
| } |
| |
| #define BIT(x) (0x1ULL << (x)) |
| |
| #define LOCAL_FEATURE_ACCESSOR(name, page, bit) \ |
| bool Controller::name() const { \ |
| return GetLocalFeatures(page) & BIT(bit); \ |
| } |
| |
| LOCAL_FEATURE_ACCESSOR(Supports3SlotPackets, 0, 0) |
| LOCAL_FEATURE_ACCESSOR(Supports5SlotPackets, 0, 1) |
| LOCAL_FEATURE_ACCESSOR(SupportsRoleSwitch, 0, 5) |
| LOCAL_FEATURE_ACCESSOR(SupportsHoldMode, 0, 6) |
| LOCAL_FEATURE_ACCESSOR(SupportsSniffMode, 0, 7) |
| LOCAL_FEATURE_ACCESSOR(SupportsParkMode, 0, 8) |
| LOCAL_FEATURE_ACCESSOR(SupportsSco, 0, 11) |
| LOCAL_FEATURE_ACCESSOR(SupportsHv2Packets, 0, 12) |
| LOCAL_FEATURE_ACCESSOR(SupportsHv3Packets, 0, 13) |
| LOCAL_FEATURE_ACCESSOR(SupportsClassic2mPhy, 0, 25) |
| LOCAL_FEATURE_ACCESSOR(SupportsClassic3mPhy, 0, 26) |
| LOCAL_FEATURE_ACCESSOR(SupportsInterlacedInquiryScan, 0, 28) |
| LOCAL_FEATURE_ACCESSOR(SupportsRssiWithInquiryResults, 0, 30) |
| LOCAL_FEATURE_ACCESSOR(SupportsEv3Packets, 0, 31) |
| LOCAL_FEATURE_ACCESSOR(SupportsEv4Packets, 0, 32) |
| LOCAL_FEATURE_ACCESSOR(SupportsEv5Packets, 0, 33) |
| LOCAL_FEATURE_ACCESSOR(SupportsBle, 0, 38) |
| LOCAL_FEATURE_ACCESSOR(Supports3SlotEdrPackets, 0, 39) |
| LOCAL_FEATURE_ACCESSOR(Supports5SlotEdrPackets, 0, 40) |
| LOCAL_FEATURE_ACCESSOR(SupportsSniffSubrating, 0, 41) |
| LOCAL_FEATURE_ACCESSOR(SupportsEncryptionPause, 0, 42) |
| LOCAL_FEATURE_ACCESSOR(SupportsEsco2mPhy, 0, 45) |
| LOCAL_FEATURE_ACCESSOR(SupportsEsco3mPhy, 0, 46) |
| LOCAL_FEATURE_ACCESSOR(Supports3SlotEscoEdrPackets, 0, 47) |
| LOCAL_FEATURE_ACCESSOR(SupportsExtendedInquiryResponse, 0, 48) |
| LOCAL_FEATURE_ACCESSOR(SupportsSimultaneousLeBrEdr, 0, 49) |
| LOCAL_FEATURE_ACCESSOR(SupportsSimplePairing, 0, 51) |
| LOCAL_FEATURE_ACCESSOR(SupportsNonFlushablePb, 0, 54) |
| |
| LOCAL_FEATURE_ACCESSOR(SupportsSecureConnections, 2, 8) |
| |
| #define LOCAL_LE_FEATURE_ACCESSOR(name, bit) \ |
| bool Controller::name() const { \ |
| return GetLocalLeFeatures() & BIT(bit); \ |
| } |
| |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleEncryption, 0) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleConnectionParametersRequest, 1) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleExtendedReject, 2) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBlePeripheralInitiatedFeaturesExchange, 3) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBlePing, 4) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleDataPacketLengthExtension, 5) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBlePrivacy, 6) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleExtendedScannerFilterPolicies, 7) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBle2mPhy, 8) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleStableModulationIndexTx, 9) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleStableModulationIndexRx, 10) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleCodedPhy, 11) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleExtendedAdvertising, 12) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBlePeriodicAdvertising, 13) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleChannelSelectionAlgorithm2, 14) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBlePowerClass1, 15) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleMinimumUsedChannels, 16) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleConnectionCteRequest, 17) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleConnectionCteResponse, 18) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleConnectionlessCteTransmitter, 19) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleConnectionlessCteReceiver, 20) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleAntennaSwitchingDuringCteTx, 21) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleAntennaSwitchingDuringCteRx, 22) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleReceivingConstantToneExtensions, 23) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBlePeriodicAdvertisingSyncTransferSender, 24) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBlePeriodicAdvertisingSyncTransferRecipient, 25) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleSleepClockAccuracyUpdates, 26) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleRemotePublicKeyValidation, 27) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleConnectedIsochronousStreamCentral, 28) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleConnectedIsochronousStreamPeripheral, 29) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleIsochronousBroadcaster, 30) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleSynchronizedReceiver, 31) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleIsochronousChannelsHostSupport, 32) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBlePowerControlRequest, 33) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBlePowerChangeIndication, 34) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBlePathLossMonitoring, 35) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBlePeriodicAdvertisingAdi, 36) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleConnectionSubrating, 37) |
| LOCAL_LE_FEATURE_ACCESSOR(SupportsBleConnectionSubratingHost, 38) |
| |
| uint64_t Controller::GetLocalFeatures(uint8_t page_number) const { |
| if (page_number < impl_->extended_lmp_features_array_.size()) { |
| return impl_->extended_lmp_features_array_[page_number]; |
| } |
| return 0x00; |
| } |
| |
| uint16_t Controller::GetAclPacketLength() const { |
| return impl_->acl_buffer_length_; |
| } |
| |
| uint16_t Controller::GetNumAclPacketBuffers() const { |
| return impl_->acl_buffers_; |
| } |
| |
| uint8_t Controller::GetScoPacketLength() const { |
| return impl_->sco_buffer_length_; |
| } |
| |
| uint16_t Controller::GetNumScoPacketBuffers() const { |
| return impl_->sco_buffers_; |
| } |
| |
| Address Controller::GetMacAddress() const { |
| return impl_->mac_address_; |
| } |
| |
| void Controller::SetEventMask(uint64_t event_mask) { |
| CallOn(impl_.get(), &impl::set_event_mask, event_mask); |
| } |
| |
| void Controller::Reset() { |
| CallOn(impl_.get(), &impl::reset); |
| } |
| |
| void Controller::LeRand(LeRandCallback cb) { |
| CallOn(impl_.get(), &impl::le_rand, std::move(cb)); |
| } |
| |
| void Controller::SetEventFilterClearAll() { |
| std::unique_ptr<SetEventFilterClearAllBuilder> packet = SetEventFilterClearAllBuilder::Create(); |
| CallOn(impl_.get(), &impl::set_event_filter, std::move(packet)); |
| } |
| |
| void Controller::SetEventFilterInquiryResultAllDevices() { |
| std::unique_ptr<SetEventFilterInquiryResultAllDevicesBuilder> packet = |
| SetEventFilterInquiryResultAllDevicesBuilder::Create(); |
| CallOn(impl_.get(), &impl::set_event_filter, std::move(packet)); |
| } |
| |
| void Controller::SetEventFilterInquiryResultClassOfDevice(ClassOfDevice class_of_device, |
| ClassOfDevice class_of_device_mask) { |
| std::unique_ptr<SetEventFilterInquiryResultClassOfDeviceBuilder> packet = |
| SetEventFilterInquiryResultClassOfDeviceBuilder::Create(class_of_device, class_of_device_mask); |
| CallOn(impl_.get(), &impl::set_event_filter, std::move(packet)); |
| } |
| |
| void Controller::SetEventFilterInquiryResultAddress(Address address) { |
| std::unique_ptr<SetEventFilterInquiryResultAddressBuilder> packet = |
| SetEventFilterInquiryResultAddressBuilder::Create(address); |
| CallOn(impl_.get(), &impl::set_event_filter, std::move(packet)); |
| } |
| |
| void Controller::SetEventFilterConnectionSetupAllDevices(AutoAcceptFlag auto_accept_flag) { |
| std::unique_ptr<SetEventFilterConnectionSetupAllDevicesBuilder> packet = |
| SetEventFilterConnectionSetupAllDevicesBuilder::Create(auto_accept_flag); |
| CallOn(impl_.get(), &impl::set_event_filter, std::move(packet)); |
| } |
| |
| void Controller::SetEventFilterConnectionSetupClassOfDevice(ClassOfDevice class_of_device, |
| ClassOfDevice class_of_device_mask, |
| AutoAcceptFlag auto_accept_flag) { |
| std::unique_ptr<SetEventFilterConnectionSetupClassOfDeviceBuilder> packet = |
| SetEventFilterConnectionSetupClassOfDeviceBuilder::Create(class_of_device, class_of_device_mask, |
| auto_accept_flag); |
| CallOn(impl_.get(), &impl::set_event_filter, std::move(packet)); |
| } |
| |
| void Controller::SetEventFilterConnectionSetupAddress(Address address, AutoAcceptFlag auto_accept_flag) { |
| std::unique_ptr<SetEventFilterConnectionSetupAddressBuilder> packet = |
| SetEventFilterConnectionSetupAddressBuilder::Create(address, auto_accept_flag); |
| CallOn(impl_.get(), &impl::set_event_filter, std::move(packet)); |
| } |
| |
| void Controller::WriteLocalName(std::string local_name) { |
| impl_->local_name_ = local_name; |
| CallOn(impl_.get(), &impl::write_local_name, local_name); |
| } |
| |
| void Controller::HostBufferSize(uint16_t host_acl_data_packet_length, uint8_t host_synchronous_data_packet_length, |
| uint16_t host_total_num_acl_data_packets, |
| uint16_t host_total_num_synchronous_data_packets) { |
| CallOn( |
| impl_.get(), |
| &impl::host_buffer_size, |
| host_acl_data_packet_length, |
| host_synchronous_data_packet_length, |
| host_total_num_acl_data_packets, |
| host_total_num_synchronous_data_packets); |
| } |
| |
| void Controller::LeSetEventMask(uint64_t le_event_mask) { |
| CallOn(impl_.get(), &impl::le_set_event_mask, le_event_mask); |
| } |
| |
| LeBufferSize Controller::GetLeBufferSize() const { |
| return impl_->le_buffer_size_; |
| } |
| |
| uint64_t Controller::GetLocalLeFeatures() const { |
| return impl_->le_local_supported_features_; |
| } |
| |
| LeBufferSize Controller::GetControllerIsoBufferSize() const { |
| return impl_->iso_buffer_size_; |
| } |
| |
| uint64_t Controller::GetControllerLeLocalSupportedFeatures() const { |
| return impl_->le_local_supported_features_; |
| } |
| |
| uint64_t Controller::GetLeSupportedStates() const { |
| return impl_->le_supported_states_; |
| } |
| |
| uint8_t Controller::GetLeFilterAcceptListSize() const { |
| return impl_->le_accept_list_size_; |
| } |
| |
| uint8_t Controller::GetLeResolvingListSize() const { |
| return impl_->le_resolving_list_size_; |
| } |
| |
| LeMaximumDataLength Controller::GetLeMaximumDataLength() const { |
| return impl_->le_maximum_data_length_; |
| } |
| |
| uint16_t Controller::GetLeMaximumAdvertisingDataLength() const { |
| return impl_->le_maximum_advertising_data_length_; |
| } |
| |
| uint16_t Controller::GetLeSuggestedDefaultDataLength() const { |
| return impl_->le_suggested_default_data_length_; |
| } |
| |
| uint8_t Controller::GetLeNumberOfSupportedAdverisingSets() const { |
| return impl_->le_number_supported_advertising_sets_; |
| } |
| |
| Controller::VendorCapabilities Controller::GetVendorCapabilities() const { |
| return impl_->vendor_capabilities_; |
| } |
| |
| uint32_t Controller::GetDabSupportedCodecs() const { |
| return impl_->dab_supported_codecs_; |
| } |
| |
| const std::array<DynamicAudioBufferCodecCapability, 32>& Controller::GetDabCodecCapabilities() |
| const { |
| return impl_->dab_codec_capabilities_; |
| } |
| |
| void Controller::SetDabAudioBufferTime(uint16_t buffer_time_ms) { |
| if (impl_->vendor_capabilities_.dynamic_audio_buffer_support_ == 0) { |
| LOG_WARN("Dynamic Audio Buffer not supported"); |
| return; |
| } |
| impl_->set_controller_dab_audio_buffer_time(buffer_time_ms); |
| } |
| |
| uint8_t Controller::GetLePeriodicAdvertiserListSize() const { |
| return impl_->le_periodic_advertiser_list_size_; |
| } |
| |
| bool Controller::IsSupported(bluetooth::hci::OpCode op_code) const { |
| return impl_->is_supported(op_code); |
| } |
| |
| uint64_t Controller::MaskLeEventMask(HciVersion version, uint64_t mask) { |
| if (!common::init_flags::subrating_is_enabled()) { |
| mask = mask & ~(static_cast<uint64_t>(LLFeaturesBits::CONNECTION_SUBRATING_HOST_SUPPORT)); |
| } |
| if (version >= HciVersion::V_5_3) { |
| return mask; |
| } else if (version >= HciVersion::V_5_2) { |
| return mask & kLeEventMask52; |
| } else if (version >= HciVersion::V_5_1) { |
| return mask & kLeEventMask51; |
| } else if (version >= HciVersion::V_5_0) { |
| return mask & kLeEventMask50; |
| } else if (version >= HciVersion::V_4_2) { |
| return mask & kLeEventMask42; |
| } else { |
| return mask & kLeEventMask41; |
| } |
| } |
| |
| const ModuleFactory Controller::Factory = ModuleFactory([]() { return new Controller(); }); |
| |
| void Controller::ListDependencies(ModuleList* list) const { |
| list->add<hci::HciLayer>(); |
| list->add<sysprops::SyspropsModule>(); |
| } |
| |
| void Controller::Start() { |
| impl_->Start(GetDependency<hci::HciLayer>()); |
| } |
| |
| void Controller::Stop() { |
| impl_->Stop(); |
| } |
| |
| std::string Controller::ToString() const { |
| return "Controller"; |
| } |
| |
| void Controller::impl::Dump( |
| std::promise<flatbuffers::Offset<ControllerData>> promise, flatbuffers::FlatBufferBuilder* fb_builder) const { |
| ASSERT(fb_builder != nullptr); |
| auto title = fb_builder->CreateString("----- Hci Controller Dumpsys -----"); |
| |
| auto local_version_information_data = CreateLocalVersionInformationData( |
| *fb_builder, |
| fb_builder->CreateString(HciVersionText(local_version_information_.hci_version_)), |
| local_version_information_.hci_revision_, |
| fb_builder->CreateString(LmpVersionText(local_version_information_.lmp_version_)), |
| local_version_information_.manufacturer_name_, |
| local_version_information_.lmp_subversion_); |
| |
| auto acl_buffer_size_data = BufferSizeData(acl_buffer_length_, acl_buffers_); |
| |
| auto sco_buffer_size_data = BufferSizeData(sco_buffer_length_, sco_buffers_); |
| |
| auto le_buffer_size_data = |
| BufferSizeData(le_buffer_size_.le_data_packet_length_, le_buffer_size_.total_num_le_packets_); |
| |
| auto iso_buffer_size_data = |
| BufferSizeData(iso_buffer_size_.le_data_packet_length_, iso_buffer_size_.total_num_le_packets_); |
| |
| auto le_maximum_data_length_data = LeMaximumDataLengthData( |
| le_maximum_data_length_.supported_max_tx_octets_, |
| le_maximum_data_length_.supported_max_tx_time_, |
| le_maximum_data_length_.supported_max_rx_octets_, |
| le_maximum_data_length_.supported_max_rx_time_); |
| |
| std::vector<LocalSupportedCommandsData> local_supported_commands_vector; |
| for (uint8_t index = 0; index < local_supported_commands_.size(); index++) { |
| local_supported_commands_vector.push_back(LocalSupportedCommandsData(index, local_supported_commands_[index])); |
| } |
| auto local_supported_commands_data = fb_builder->CreateVectorOfStructs(local_supported_commands_vector); |
| |
| auto vendor_capabilities_data = VendorCapabilitiesData( |
| vendor_capabilities_.is_supported_, |
| vendor_capabilities_.max_advt_instances_, |
| vendor_capabilities_.offloaded_resolution_of_private_address_, |
| vendor_capabilities_.total_scan_results_storage_, |
| vendor_capabilities_.max_irk_list_sz_, |
| vendor_capabilities_.filtering_support_, |
| vendor_capabilities_.max_filter_, |
| vendor_capabilities_.activity_energy_info_support_, |
| vendor_capabilities_.version_supported_, |
| vendor_capabilities_.total_num_of_advt_tracked_, |
| vendor_capabilities_.extended_scan_support_, |
| vendor_capabilities_.debug_logging_supported_, |
| vendor_capabilities_.le_address_generation_offloading_support_, |
| vendor_capabilities_.a2dp_source_offload_capability_mask_, |
| vendor_capabilities_.bluetooth_quality_report_support_); |
| |
| auto extended_lmp_features_vector = fb_builder->CreateVector(extended_lmp_features_array_); |
| |
| // Create the root table |
| ControllerDataBuilder builder(*fb_builder); |
| |
| builder.add_title(title); |
| builder.add_local_version_information(local_version_information_data); |
| |
| builder.add_acl_buffer_size(&acl_buffer_size_data); |
| builder.add_sco_buffer_size(&sco_buffer_size_data); |
| builder.add_iso_buffer_size(&iso_buffer_size_data); |
| builder.add_le_buffer_size(&le_buffer_size_data); |
| |
| builder.add_le_accept_list_size(le_accept_list_size_); |
| builder.add_le_resolving_list_size(le_resolving_list_size_); |
| |
| builder.add_le_maximum_data_length(&le_maximum_data_length_data); |
| builder.add_le_maximum_advertising_data_length(le_maximum_advertising_data_length_); |
| builder.add_le_suggested_default_data_length(le_suggested_default_data_length_); |
| builder.add_le_number_supported_advertising_sets(le_number_supported_advertising_sets_); |
| builder.add_le_periodic_advertiser_list_size(le_periodic_advertiser_list_size_); |
| |
| builder.add_local_supported_commands(local_supported_commands_data); |
| builder.add_extended_lmp_features_array(extended_lmp_features_vector); |
| builder.add_le_local_supported_features(le_local_supported_features_); |
| builder.add_le_supported_states(le_supported_states_); |
| builder.add_vendor_capabilities(&vendor_capabilities_data); |
| |
| flatbuffers::Offset<ControllerData> dumpsys_data = builder.Finish(); |
| promise.set_value(dumpsys_data); |
| } |
| |
| DumpsysDataFinisher Controller::GetDumpsysData(flatbuffers::FlatBufferBuilder* fb_builder) const { |
| ASSERT(fb_builder != nullptr); |
| |
| std::promise<flatbuffers::Offset<ControllerData>> promise; |
| auto future = promise.get_future(); |
| impl_->Dump(std::move(promise), fb_builder); |
| |
| auto dumpsys_data = future.get(); |
| |
| return [dumpsys_data](DumpsysDataBuilder* dumpsys_builder) { |
| dumpsys_builder->add_hci_controller_dumpsys_data(dumpsys_data); |
| }; |
| } |
| |
| } // namespace hci |
| } // namespace bluetooth |