blob: 9eceebb366e2eb48264fe8931a9407283b31fe7e [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.
*/
#define LOG_TAG "bt_gd_neigh"
#include "neighbor/inquiry.h"
#include <memory>
#include "common/bind.h"
#include "hci/hci_layer.h"
#include "hci/hci_packets.h"
#include "module.h"
#include "os/handler.h"
#include "os/log.h"
namespace bluetooth {
namespace neighbor {
static constexpr uint8_t kGeneralInquiryAccessCode = 0x33;
static constexpr uint8_t kLimitedInquiryAccessCode = 0x00;
struct InquiryModule::impl {
void RegisterCallbacks(InquiryCallbacks inquiry_callbacks);
void UnregisterCallbacks();
void StartOneShotInquiry(bool limited, InquiryLength inquiry_length, NumResponses num_responses);
void StopOneShotInquiry();
void StartPeriodicInquiry(
bool limited,
InquiryLength inquiry_length,
NumResponses num_responses,
PeriodLength max_delay,
PeriodLength min_delay);
void StopPeriodicInquiry();
void SetScanActivity(ScanParameters params);
void SetScanType(hci::InquiryScanType scan_type);
void SetInquiryMode(hci::InquiryMode mode);
void Start();
void Stop();
bool HasCallbacks() const;
impl(InquiryModule& inquiry_module);
private:
InquiryCallbacks inquiry_callbacks_;
InquiryModule& module_;
bool active_general_one_shot_{false};
bool active_limited_one_shot_{false};
bool active_general_periodic_{false};
bool active_limited_periodic_{false};
ScanParameters inquiry_scan_;
hci::InquiryMode inquiry_mode_;
hci::InquiryScanType inquiry_scan_type_;
int8_t inquiry_response_tx_power_;
bool IsInquiryActive() const;
void EnqueueCommandComplete(std::unique_ptr<hci::CommandBuilder> command);
void EnqueueCommandStatus(std::unique_ptr<hci::CommandBuilder> command);
void OnCommandComplete(hci::CommandCompleteView view);
void OnCommandStatus(hci::CommandStatusView status);
void EnqueueCommandCompleteSync(std::unique_ptr<hci::CommandBuilder> command);
void OnCommandCompleteSync(hci::CommandCompleteView view);
void OnEvent(hci::EventView view);
std::promise<void>* command_sync_{nullptr};
hci::HciLayer* hci_layer_;
os::Handler* handler_;
};
const ModuleFactory neighbor::InquiryModule::Factory = ModuleFactory([]() { return new neighbor::InquiryModule(); });
neighbor::InquiryModule::impl::impl(neighbor::InquiryModule& module) : module_(module) {}
void neighbor::InquiryModule::impl::OnCommandCompleteSync(hci::CommandCompleteView view) {
OnCommandComplete(view);
ASSERT(command_sync_ != nullptr);
command_sync_->set_value();
}
void neighbor::InquiryModule::impl::OnCommandComplete(hci::CommandCompleteView view) {
switch (view.GetCommandOpCode()) {
case hci::OpCode::INQUIRY_CANCEL: {
auto packet = hci::InquiryCancelCompleteView::Create(view);
ASSERT(packet.IsValid());
ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
} break;
case hci::OpCode::PERIODIC_INQUIRY_MODE: {
auto packet = hci::PeriodicInquiryModeCompleteView::Create(view);
ASSERT(packet.IsValid());
ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
} break;
case hci::OpCode::EXIT_PERIODIC_INQUIRY_MODE: {
auto packet = hci::ExitPeriodicInquiryModeCompleteView::Create(view);
ASSERT(packet.IsValid());
ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
} break;
case hci::OpCode::WRITE_INQUIRY_MODE: {
auto packet = hci::WriteInquiryModeCompleteView::Create(view);
ASSERT(packet.IsValid());
ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
} break;
case hci::OpCode::READ_INQUIRY_MODE: {
auto packet = hci::ReadInquiryModeCompleteView::Create(view);
ASSERT(packet.IsValid());
ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
inquiry_mode_ = packet.GetInquiryMode();
} break;
case hci::OpCode::READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL: {
auto packet = hci::ReadInquiryResponseTransmitPowerLevelCompleteView::Create(view);
ASSERT(packet.IsValid());
ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
inquiry_response_tx_power_ = packet.GetTxPower();
} break;
case hci::OpCode::WRITE_INQUIRY_SCAN_ACTIVITY: {
auto packet = hci::WriteInquiryScanActivityCompleteView::Create(view);
ASSERT(packet.IsValid());
ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
} break;
case hci::OpCode::READ_INQUIRY_SCAN_ACTIVITY: {
auto packet = hci::ReadInquiryScanActivityCompleteView::Create(view);
ASSERT(packet.IsValid());
ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
inquiry_scan_.interval = packet.GetInquiryScanInterval();
inquiry_scan_.window = packet.GetInquiryScanWindow();
} break;
case hci::OpCode::WRITE_INQUIRY_SCAN_TYPE: {
auto packet = hci::WriteInquiryScanTypeCompleteView::Create(view);
ASSERT(packet.IsValid());
ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
} break;
case hci::OpCode::READ_INQUIRY_SCAN_TYPE: {
auto packet = hci::ReadInquiryScanTypeCompleteView::Create(view);
ASSERT(packet.IsValid());
ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
inquiry_scan_type_ = packet.GetInquiryScanType();
} break;
default:
LOG_WARN("Unhandled command:%s", hci::OpCodeText(view.GetCommandOpCode()).c_str());
break;
}
}
void neighbor::InquiryModule::impl::OnCommandStatus(hci::CommandStatusView status) {
ASSERT(status.GetStatus() == hci::ErrorCode::SUCCESS);
switch (status.GetCommandOpCode()) {
case hci::OpCode::INQUIRY: {
auto packet = hci::InquiryStatusView::Create(status);
ASSERT(packet.IsValid());
if (active_limited_one_shot_ || active_general_one_shot_) {
LOG_INFO("Inquiry started lap: %s", active_limited_one_shot_ ? "Limited" : "General");
}
} break;
default:
LOG_WARN("Unhandled command:%s", hci::OpCodeText(status.GetCommandOpCode()).c_str());
break;
}
}
void neighbor::InquiryModule::impl::OnEvent(hci::EventView view) {
switch (view.GetEventCode()) {
case hci::EventCode::INQUIRY_COMPLETE: {
auto packet = hci::InquiryCompleteView::Create(view);
ASSERT(packet.IsValid());
LOG_INFO("inquiry complete");
active_limited_one_shot_ = false;
active_general_one_shot_ = false;
inquiry_callbacks_.complete(packet.GetStatus());
} break;
case hci::EventCode::INQUIRY_RESULT: {
auto packet = hci::InquiryResultView::Create(view);
ASSERT(packet.IsValid());
LOG_INFO("Inquiry result size:%zd num_responses:%zu", packet.size(), packet.GetInquiryResults().size());
inquiry_callbacks_.result(packet);
} break;
case hci::EventCode::INQUIRY_RESULT_WITH_RSSI: {
auto packet = hci::InquiryResultWithRssiView::Create(view);
ASSERT(packet.IsValid());
LOG_INFO("Inquiry result with rssi num_responses:%zu", packet.GetInquiryResults().size());
inquiry_callbacks_.result_with_rssi(packet);
} break;
case hci::EventCode::EXTENDED_INQUIRY_RESULT: {
auto packet = hci::ExtendedInquiryResultView::Create(view);
ASSERT(packet.IsValid());
LOG_INFO(
"Extended inquiry result addr:%s repetition_mode:%s cod:%s clock_offset:%d rssi:%hhd",
packet.GetAddress().ToString().c_str(),
hci::PageScanRepetitionModeText(packet.GetPageScanRepetitionMode()).c_str(),
packet.GetClassOfDevice().ToString().c_str(),
packet.GetClockOffset(),
packet.GetRssi());
inquiry_callbacks_.extended_result(packet);
} break;
default:
LOG_ERROR("Unhandled event:%s", hci::EventCodeText(view.GetEventCode()).c_str());
break;
}
}
/**
* impl
*/
void neighbor::InquiryModule::impl::RegisterCallbacks(InquiryCallbacks callbacks) {
inquiry_callbacks_ = callbacks;
hci_layer_->RegisterEventHandler(
hci::EventCode::INQUIRY_RESULT, handler_->BindOn(this, &InquiryModule::impl::OnEvent));
hci_layer_->RegisterEventHandler(
hci::EventCode::INQUIRY_RESULT_WITH_RSSI, handler_->BindOn(this, &InquiryModule::impl::OnEvent));
hci_layer_->RegisterEventHandler(
hci::EventCode::EXTENDED_INQUIRY_RESULT, handler_->BindOn(this, &InquiryModule::impl::OnEvent));
hci_layer_->RegisterEventHandler(
hci::EventCode::INQUIRY_COMPLETE, handler_->BindOn(this, &InquiryModule::impl::OnEvent));
}
void neighbor::InquiryModule::impl::UnregisterCallbacks() {
hci_layer_->UnregisterEventHandler(hci::EventCode::INQUIRY_COMPLETE);
hci_layer_->UnregisterEventHandler(hci::EventCode::EXTENDED_INQUIRY_RESULT);
hci_layer_->UnregisterEventHandler(hci::EventCode::INQUIRY_RESULT_WITH_RSSI);
hci_layer_->UnregisterEventHandler(hci::EventCode::INQUIRY_RESULT);
inquiry_callbacks_ = {nullptr, nullptr, nullptr, nullptr};
}
void neighbor::InquiryModule::impl::EnqueueCommandComplete(std::unique_ptr<hci::CommandBuilder> command) {
hci_layer_->EnqueueCommand(std::move(command), handler_->BindOnceOn(this, &impl::OnCommandComplete));
}
void neighbor::InquiryModule::impl::EnqueueCommandStatus(std::unique_ptr<hci::CommandBuilder> command) {
hci_layer_->EnqueueCommand(std::move(command), handler_->BindOnceOn(this, &impl::OnCommandStatus));
}
void neighbor::InquiryModule::impl::EnqueueCommandCompleteSync(std::unique_ptr<hci::CommandBuilder> command) {
ASSERT(command_sync_ == nullptr);
command_sync_ = new std::promise<void>();
auto command_received = command_sync_->get_future();
hci_layer_->EnqueueCommand(std::move(command), handler_->BindOnceOn(this, &impl::OnCommandCompleteSync));
command_received.wait();
delete command_sync_;
command_sync_ = nullptr;
}
void neighbor::InquiryModule::impl::StartOneShotInquiry(
bool limited, InquiryLength inquiry_length, NumResponses num_responses) {
ASSERT(HasCallbacks());
ASSERT(!IsInquiryActive());
hci::Lap lap;
if (limited) {
active_limited_one_shot_ = true;
lap.lap_ = kLimitedInquiryAccessCode;
} else {
active_general_one_shot_ = true;
lap.lap_ = kGeneralInquiryAccessCode;
}
EnqueueCommandStatus(hci::InquiryBuilder::Create(lap, inquiry_length, num_responses));
}
void neighbor::InquiryModule::impl::StopOneShotInquiry() {
ASSERT(active_general_one_shot_ || active_limited_one_shot_);
active_general_one_shot_ = false;
active_limited_one_shot_ = false;
EnqueueCommandComplete(hci::InquiryCancelBuilder::Create());
}
void neighbor::InquiryModule::impl::StartPeriodicInquiry(
bool limited,
InquiryLength inquiry_length,
NumResponses num_responses,
PeriodLength max_delay,
PeriodLength min_delay) {
ASSERT(HasCallbacks());
ASSERT(!IsInquiryActive());
hci::Lap lap;
if (limited) {
active_limited_periodic_ = true;
lap.lap_ = kLimitedInquiryAccessCode;
} else {
active_general_periodic_ = true;
lap.lap_ = kGeneralInquiryAccessCode;
}
EnqueueCommandComplete(
hci::PeriodicInquiryModeBuilder::Create(max_delay, min_delay, lap, inquiry_length, num_responses));
}
void neighbor::InquiryModule::impl::StopPeriodicInquiry() {
ASSERT(active_general_periodic_ || active_limited_periodic_);
active_general_periodic_ = false;
active_limited_periodic_ = false;
EnqueueCommandComplete(hci::ExitPeriodicInquiryModeBuilder::Create());
}
bool neighbor::InquiryModule::impl::IsInquiryActive() const {
return active_general_one_shot_ || active_limited_one_shot_ || active_limited_periodic_ || active_general_periodic_;
}
void neighbor::InquiryModule::impl::Start() {
hci_layer_ = module_.GetDependency<hci::HciLayer>();
handler_ = module_.GetHandler();
EnqueueCommandComplete(hci::ReadInquiryResponseTransmitPowerLevelBuilder::Create());
EnqueueCommandComplete(hci::ReadInquiryScanActivityBuilder::Create());
EnqueueCommandComplete(hci::ReadInquiryScanTypeBuilder::Create());
EnqueueCommandCompleteSync(hci::ReadInquiryModeBuilder::Create());
LOG_INFO("Started inquiry module");
}
void neighbor::InquiryModule::impl::Stop() {
LOG_INFO("Inquiry scan interval:%hu window:%hu", inquiry_scan_.interval, inquiry_scan_.window);
LOG_INFO(
"Inquiry mode:%s scan_type:%s",
hci::InquiryModeText(inquiry_mode_).c_str(),
hci::InquiryScanTypeText(inquiry_scan_type_).c_str());
LOG_INFO("Inquiry response tx power:%hhd", inquiry_response_tx_power_);
LOG_INFO("Stopped inquiry module");
}
void neighbor::InquiryModule::impl::SetInquiryMode(hci::InquiryMode mode) {
EnqueueCommandComplete(hci::WriteInquiryModeBuilder::Create(mode));
inquiry_mode_ = mode;
LOG_INFO("Set inquiry mode:%s", hci::InquiryModeText(mode).c_str());
}
void neighbor::InquiryModule::impl::SetScanActivity(ScanParameters params) {
EnqueueCommandComplete(hci::WriteInquiryScanActivityBuilder::Create(params.interval, params.window));
inquiry_scan_ = params;
LOG_INFO(
"Set scan activity interval:0x%x/%.02fms window:0x%x/%.02fms",
params.interval,
ScanIntervalTimeMs(params.interval),
params.window,
ScanWindowTimeMs(params.window));
}
void neighbor::InquiryModule::impl::SetScanType(hci::InquiryScanType scan_type) {
EnqueueCommandComplete(hci::WriteInquiryScanTypeBuilder::Create(scan_type));
LOG_INFO("Set scan type:%s", hci::InquiryScanTypeText(scan_type).c_str());
}
bool neighbor::InquiryModule::impl::HasCallbacks() const {
return inquiry_callbacks_.result != nullptr && inquiry_callbacks_.result_with_rssi != nullptr &&
inquiry_callbacks_.extended_result != nullptr && inquiry_callbacks_.complete != nullptr;
}
/**
* General API here
*/
neighbor::InquiryModule::InquiryModule() : pimpl_(std::make_unique<impl>(*this)) {}
neighbor::InquiryModule::~InquiryModule() {
pimpl_.reset();
}
void neighbor::InquiryModule::RegisterCallbacks(InquiryCallbacks callbacks) {
pimpl_->RegisterCallbacks(callbacks);
}
void neighbor::InquiryModule::UnregisterCallbacks() {
pimpl_->UnregisterCallbacks();
}
void neighbor::InquiryModule::StartGeneralInquiry(InquiryLength inquiry_length, NumResponses num_responses) {
GetHandler()->Post(common::BindOnce(
&neighbor::InquiryModule::impl::StartOneShotInquiry,
common::Unretained(pimpl_.get()),
false,
inquiry_length,
num_responses));
}
void neighbor::InquiryModule::StartLimitedInquiry(InquiryLength inquiry_length, NumResponses num_responses) {
GetHandler()->Post(common::BindOnce(
&neighbor::InquiryModule::impl::StartOneShotInquiry,
common::Unretained(pimpl_.get()),
true,
inquiry_length,
num_responses));
}
void neighbor::InquiryModule::StopInquiry() {
GetHandler()->Post(
common::BindOnce(&neighbor::InquiryModule::impl::StopOneShotInquiry, common::Unretained(pimpl_.get())));
}
void neighbor::InquiryModule::StartGeneralPeriodicInquiry(
InquiryLength inquiry_length, NumResponses num_responses, PeriodLength max_delay, PeriodLength min_delay) {
GetHandler()->Post(common::BindOnce(
&neighbor::InquiryModule::impl::StartPeriodicInquiry,
common::Unretained(pimpl_.get()),
false,
inquiry_length,
num_responses,
max_delay,
min_delay));
}
void neighbor::InquiryModule::StartLimitedPeriodicInquiry(
InquiryLength inquiry_length, NumResponses num_responses, PeriodLength max_delay, PeriodLength min_delay) {
GetHandler()->Post(common::BindOnce(
&neighbor::InquiryModule::impl::StartPeriodicInquiry,
common::Unretained(pimpl_.get()),
true,
inquiry_length,
num_responses,
max_delay,
min_delay));
}
void neighbor::InquiryModule::StopPeriodicInquiry() {
GetHandler()->Post(
common::BindOnce(&neighbor::InquiryModule::impl::StopPeriodicInquiry, common::Unretained(pimpl_.get())));
}
void neighbor::InquiryModule::SetScanActivity(ScanParameters params) {
GetHandler()->Post(
common::BindOnce(&neighbor::InquiryModule::impl::SetScanActivity, common::Unretained(pimpl_.get()), params));
}
void neighbor::InquiryModule::SetInterlacedScan() {
GetHandler()->Post(common::BindOnce(
&neighbor::InquiryModule::impl::SetScanType, common::Unretained(pimpl_.get()), hci::InquiryScanType::INTERLACED));
}
void neighbor::InquiryModule::SetStandardScan() {
GetHandler()->Post(common::BindOnce(
&neighbor::InquiryModule::impl::SetScanType, common::Unretained(pimpl_.get()), hci::InquiryScanType::STANDARD));
}
void neighbor::InquiryModule::SetStandardInquiryResultMode() {
GetHandler()->Post(common::BindOnce(
&neighbor::InquiryModule::impl::SetInquiryMode, common::Unretained(pimpl_.get()), hci::InquiryMode::STANDARD));
}
void neighbor::InquiryModule::SetInquiryWithRssiResultMode() {
GetHandler()->Post(common::BindOnce(
&neighbor::InquiryModule::impl::SetInquiryMode, common::Unretained(pimpl_.get()), hci::InquiryMode::RSSI));
}
void neighbor::InquiryModule::SetExtendedInquiryResultMode() {
GetHandler()->Post(common::BindOnce(
&neighbor::InquiryModule::impl::SetInquiryMode,
common::Unretained(pimpl_.get()),
hci::InquiryMode::RSSI_OR_EXTENDED));
}
/**
* Module methods here
*/
void neighbor::InquiryModule::ListDependencies(ModuleList* list) const {
list->add<hci::HciLayer>();
}
void neighbor::InquiryModule::Start() {
pimpl_->Start();
}
void neighbor::InquiryModule::Stop() {
pimpl_->Stop();
}
} // namespace neighbor
} // namespace bluetooth