/* * Copyright 2022 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/distance_measurement_manager.h" #include #include #include "hci/acl_manager.h" #include "hci/hci_layer.h" #include "module.h" #include "os/handler.h" #include "os/log.h" namespace bluetooth { namespace hci { const ModuleFactory DistanceMeasurementManager::Factory = ModuleFactory([]() { return new DistanceMeasurementManager(); }); static constexpr uint16_t kIllegalConnectionHandle = 0xffff; static constexpr uint8_t kTxPowerNotAvailable = 0xfe; static constexpr int8_t kRSSIDropOffAt1M = 41; struct DistanceMeasurementManager::impl { ~impl() {} void start(os::Handler* handler, hci::HciLayer* hci_layer, hci::AclManager* acl_manager) { handler_ = handler; hci_layer_ = hci_layer; acl_manager_ = acl_manager; hci_layer_->RegisterLeEventHandler( hci::SubeventCode::TRANSMIT_POWER_REPORTING, handler_->BindOn(this, &impl::on_transmit_power_reporting)); } void stop() { hci_layer_->UnregisterLeEventHandler(hci::SubeventCode::TRANSMIT_POWER_REPORTING); } void register_distance_measurement_callbacks(DistanceMeasurementCallbacks* callbacks) { distance_measurement_callbacks_ = callbacks; } void start_distance_measurement( const Address& address, uint16_t frequency, DistanceMeasurementMethod method) { LOG_INFO("Address:%s, method:%d", ADDRESS_TO_LOGGABLE_CSTR(address), method); uint16_t connection_handle = acl_manager_->HACK_GetLeHandle(address); // Remove this check if we support any connection less method if (connection_handle == kIllegalConnectionHandle) { LOG_WARN("Can not find any LE connection"); distance_measurement_callbacks_->OnDistanceMeasurementStartFail( address, REASON_NO_LE_CONNECTION, method); return; } switch (method) { case METHOD_AUTO: case METHOD_RSSI: { if (rssi_trackers.find(address) == rssi_trackers.end()) { rssi_trackers[address].handle = connection_handle; rssi_trackers[address].frequency = frequency; rssi_trackers[address].remote_tx_power = kTxPowerNotAvailable; rssi_trackers[address].started = false; rssi_trackers[address].alarm = std::make_unique(handler_); hci_layer_->EnqueueCommand( LeReadRemoteTransmitPowerLevelBuilder::Create( acl_manager_->HACK_GetLeHandle(address), 0x01), handler_->BindOnceOn( this, &impl::on_read_remote_transmit_power_level_status, address)); } else { rssi_trackers[address].frequency = frequency; } } break; } } void stop_distance_measurement(const Address& address, DistanceMeasurementMethod method) { LOG_INFO("Address:%s, method:%d", ADDRESS_TO_LOGGABLE_CSTR(address), method); switch (method) { case METHOD_AUTO: case METHOD_RSSI: { if (rssi_trackers.find(address) == rssi_trackers.end()) { LOG_WARN("Can't find rssi tracker for %s ", ADDRESS_TO_LOGGABLE_CSTR(address)); } else { hci_layer_->EnqueueCommand( LeSetTransmitPowerReportingEnableBuilder::Create( rssi_trackers[address].handle, 0x00, 0x00), handler_->BindOnceOn( this, &impl::check_status)); rssi_trackers[address].alarm->Cancel(); rssi_trackers[address].alarm.reset(); rssi_trackers.erase(address); } } break; } } void read_rssi_regularly(const Address& address, uint16_t frequency) { if (rssi_trackers.find(address) == rssi_trackers.end()) { LOG_WARN("Can't find rssi tracker for %s ", ADDRESS_TO_LOGGABLE_CSTR(address)); return; } uint16_t connection_handle = acl_manager_->HACK_GetLeHandle(address); if (connection_handle == kIllegalConnectionHandle) { LOG_WARN("Can't find connection for %s ", ADDRESS_TO_LOGGABLE_CSTR(address)); if (rssi_trackers.find(address) != rssi_trackers.end()) { distance_measurement_callbacks_->OnDistanceMeasurementStopped( address, REASON_NO_LE_CONNECTION, METHOD_RSSI); rssi_trackers[address].alarm->Cancel(); rssi_trackers[address].alarm.reset(); rssi_trackers.erase(address); } return; } hci_layer_->EnqueueCommand( ReadRssiBuilder::Create(connection_handle), handler_->BindOnceOn(this, &impl::on_read_rssi_complete, address)); rssi_trackers[address].alarm->Schedule( common::BindOnce(&impl::read_rssi_regularly, common::Unretained(this), address, frequency), std::chrono::milliseconds(rssi_trackers[address].frequency)); } void on_read_remote_transmit_power_level_status(Address address, CommandStatusView view) { auto status_view = LeReadRemoteTransmitPowerLevelStatusView::Create(view); if (!status_view.IsValid()) { LOG_WARN("Invalid LeReadRemoteTransmitPowerLevelStatus event"); distance_measurement_callbacks_->OnDistanceMeasurementStartFail( address, REASON_INTERNAL_ERROR, METHOD_RSSI); rssi_trackers.erase(address); } else if (status_view.GetStatus() != ErrorCode::SUCCESS) { std::string error_code = ErrorCodeText(status_view.GetStatus()); LOG_WARN( "Received LeReadRemoteTransmitPowerLevelStatus with error code %s", error_code.c_str()); distance_measurement_callbacks_->OnDistanceMeasurementStartFail( address, REASON_INTERNAL_ERROR, METHOD_RSSI); rssi_trackers.erase(address); } } void on_transmit_power_reporting(LeMetaEventView event) { auto event_view = LeTransmitPowerReportingView::Create(event); if (!event_view.IsValid()) { LOG_WARN("Dropping invalid LeTransmitPowerReporting event"); return; } if (event_view.GetReason() == ReportingReason::LOCAL_TRANSMIT_POWER_CHANGED) { LOG_WARN("Dropping local LeTransmitPowerReporting event"); return; } Address address = Address::kEmpty; for (auto& rssi_tracker : rssi_trackers) { if (rssi_tracker.second.handle == event_view.GetConnectionHandle()) { address = rssi_tracker.first; } } if (address.IsEmpty()) { LOG_WARN("Can't find rssi tracker for connection %d", event_view.GetConnectionHandle()); return; } auto status = event_view.GetStatus(); if (status != ErrorCode::SUCCESS) { LOG_WARN( "Received LeTransmitPowerReporting with error code %s", ErrorCodeText(status).c_str()); } else { rssi_trackers[address].remote_tx_power = event_view.GetTransmitPowerLevel(); } if (event_view.GetReason() == ReportingReason::READ_COMMAND_COMPLETE && !rssi_trackers[address].started) { if (status == ErrorCode::SUCCESS) { hci_layer_->EnqueueCommand( LeSetTransmitPowerReportingEnableBuilder::Create( event_view.GetConnectionHandle(), 0x00, 0x01), handler_->BindOnceOn( this, &impl::on_set_transmit_power_reporting_enable_complete, address)); } else { LOG_WARN("Read remote transmit power level fail"); distance_measurement_callbacks_->OnDistanceMeasurementStartFail( address, REASON_INTERNAL_ERROR, METHOD_RSSI); rssi_trackers.erase(address); } } } void on_set_transmit_power_reporting_enable_complete(Address address, CommandCompleteView view) { auto complete_view = LeSetTransmitPowerReportingEnableCompleteView::Create(view); if (!complete_view.IsValid()) { LOG_WARN("Invalid LeSetTransmitPowerReportingEnableComplete event"); distance_measurement_callbacks_->OnDistanceMeasurementStartFail( address, REASON_INTERNAL_ERROR, METHOD_RSSI); rssi_trackers.erase(address); return; } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) { std::string error_code = ErrorCodeText(complete_view.GetStatus()); LOG_WARN( "Received LeSetTransmitPowerReportingEnableComplete with error code %s", error_code.c_str()); distance_measurement_callbacks_->OnDistanceMeasurementStartFail( address, REASON_INTERNAL_ERROR, METHOD_RSSI); rssi_trackers.erase(address); return; } if (rssi_trackers.find(address) == rssi_trackers.end()) { LOG_WARN("Can't find rssi tracker for %s", ADDRESS_TO_LOGGABLE_CSTR(address)); distance_measurement_callbacks_->OnDistanceMeasurementStartFail( address, REASON_INTERNAL_ERROR, METHOD_RSSI); rssi_trackers.erase(address); } else { LOG_INFO("Track rssi for address %s", ADDRESS_TO_LOGGABLE_CSTR(address)); rssi_trackers[address].started = true; distance_measurement_callbacks_->OnDistanceMeasurementStarted(address, METHOD_RSSI); read_rssi_regularly(address, rssi_trackers[address].frequency); } } void on_read_rssi_complete(Address address, CommandCompleteView view) { auto complete_view = ReadRssiCompleteView::Create(view); if (!complete_view.IsValid()) { LOG_WARN("Dropping invalid read RSSI complete event "); return; } if (rssi_trackers.find(address) == rssi_trackers.end()) { LOG_WARN("Can't find rssi tracker for %s", ADDRESS_TO_LOGGABLE_CSTR(address)); return; } double remote_tx_power = (int8_t)rssi_trackers[address].remote_tx_power; int8_t rssi = complete_view.GetRssi(); double pow_value = (remote_tx_power - rssi - kRSSIDropOffAt1M) / 20.0; double distance = pow(10.0, pow_value); distance_measurement_callbacks_->OnDistanceMeasurementResult( address, distance * 100, distance * 100, -1, -1, -1, -1, DistanceMeasurementMethod::METHOD_RSSI); } template void check_status(CommandCompleteView view) { auto status_view = View::Create(view); if (!status_view.IsValid()) { LOG_WARN("Get invalid command complete event"); } else if (status_view.GetStatus() != ErrorCode::SUCCESS) { LOG_INFO( "Got a Command complete %s, status %s", OpCodeText(view.GetCommandOpCode()).c_str(), ErrorCodeText(status_view.GetStatus()).c_str()); } } struct RSSITracker { uint16_t handle; uint16_t frequency; uint8_t remote_tx_power; bool started; std::unique_ptr alarm; }; os::Handler* handler_; hci::HciLayer* hci_layer_; hci::AclManager* acl_manager_; std::unordered_map rssi_trackers; DistanceMeasurementCallbacks* distance_measurement_callbacks_; }; DistanceMeasurementManager::DistanceMeasurementManager() { pimpl_ = std::make_unique(); } DistanceMeasurementManager::~DistanceMeasurementManager() = default; void DistanceMeasurementManager::ListDependencies(ModuleList* list) const { list->add(); list->add(); } void DistanceMeasurementManager::Start() { pimpl_->start(GetHandler(), GetDependency(), GetDependency()); } void DistanceMeasurementManager::Stop() { pimpl_->stop(); } std::string DistanceMeasurementManager::ToString() const { return "Distance Measurement Manager"; } void DistanceMeasurementManager::RegisterDistanceMeasurementCallbacks( DistanceMeasurementCallbacks* callbacks) { CallOn(pimpl_.get(), &impl::register_distance_measurement_callbacks, callbacks); } void DistanceMeasurementManager::StartDistanceMeasurement( const Address& address, uint16_t frequency, DistanceMeasurementMethod method) { CallOn(pimpl_.get(), &impl::start_distance_measurement, address, frequency, method); } void DistanceMeasurementManager::StopDistanceMeasurement( const Address& address, DistanceMeasurementMethod method) { CallOn(pimpl_.get(), &impl::stop_distance_measurement, address, method); } } // namespace hci } // namespace bluetooth