| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chromecast/device/bluetooth/le/le_scan_manager_impl.h" |
| |
| #include <algorithm> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/stl_util.h" |
| #include "chromecast/base/bind_to_task_runner.h" |
| #include "chromecast/device/bluetooth/bluetooth_util.h" |
| #include "chromecast/public/cast_media_shlib.h" |
| |
| #define RUN_ON_IO_THREAD(method, ...) \ |
| io_task_runner_->PostTask( \ |
| FROM_HERE, base::BindOnce(&LeScanManagerImpl::method, \ |
| weak_factory_.GetWeakPtr(), ##__VA_ARGS__)); |
| |
| #define MAKE_SURE_IO_THREAD(method, ...) \ |
| DCHECK(io_task_runner_); \ |
| if (!io_task_runner_->BelongsToCurrentThread()) { \ |
| RUN_ON_IO_THREAD(method, ##__VA_ARGS__) \ |
| return; \ |
| } |
| |
| #define EXEC_CB_AND_RET(cb, ret, ...) \ |
| do { \ |
| if (cb) { \ |
| std::move(cb).Run(ret, ##__VA_ARGS__); \ |
| } \ |
| return; \ |
| } while (0) |
| |
| namespace chromecast { |
| namespace bluetooth { |
| |
| namespace { |
| |
| const int kMaxMessagesInQueue = 5; |
| |
| } // namespace |
| |
| // static |
| constexpr int LeScanManagerImpl::kMaxScanResultEntries; |
| |
| class LeScanManagerImpl::ScanHandleImpl : public LeScanManager::ScanHandle { |
| public: |
| explicit ScanHandleImpl(LeScanManagerImpl* manager, int32_t id) |
| : on_destroyed_(BindToCurrentSequence( |
| base::BindOnce(&LeScanManagerImpl::NotifyScanHandleDestroyed, |
| manager->weak_factory_.GetWeakPtr(), |
| id))) {} |
| ~ScanHandleImpl() override { std::move(on_destroyed_).Run(); } |
| |
| private: |
| base::OnceClosure on_destroyed_; |
| }; |
| |
| LeScanManagerImpl::LeScanManagerImpl( |
| bluetooth_v2_shlib::LeScannerImpl* le_scanner) |
| : le_scanner_(le_scanner), |
| observers_(new base::ObserverListThreadSafe<Observer>()), |
| weak_factory_(this) {} |
| |
| LeScanManagerImpl::~LeScanManagerImpl() = default; |
| |
| void LeScanManagerImpl::Initialize( |
| scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) { |
| io_task_runner_ = std::move(io_task_runner); |
| } |
| |
| void LeScanManagerImpl::Finalize() {} |
| |
| void LeScanManagerImpl::AddObserver(Observer* observer) { |
| observers_->AddObserver(observer); |
| } |
| |
| void LeScanManagerImpl::RemoveObserver(Observer* observer) { |
| observers_->RemoveObserver(observer); |
| } |
| |
| void LeScanManagerImpl::RequestScan(RequestScanCallback cb) { |
| MAKE_SURE_IO_THREAD(RequestScan, BindToCurrentSequence(std::move(cb))); |
| LOG(INFO) << __func__; |
| |
| if (scan_handle_ids_.empty()) { |
| if (!le_scanner_->StartScan()) { |
| LOG(ERROR) << "Failed to enable scanning"; |
| std::move(cb).Run(nullptr); |
| return; |
| } |
| LOG(INFO) << "Enabling scan"; |
| observers_->Notify(FROM_HERE, &Observer::OnScanEnableChanged, true); |
| } |
| |
| int32_t id = next_scan_handle_id_++; |
| auto handle = std::make_unique<ScanHandleImpl>(this, id); |
| scan_handle_ids_.insert(id); |
| |
| std::move(cb).Run(std::move(handle)); |
| } |
| |
| void LeScanManagerImpl::GetScanResults(GetScanResultsCallback cb, |
| base::Optional<ScanFilter> scan_filter) { |
| MAKE_SURE_IO_THREAD(GetScanResults, BindToCurrentSequence(std::move(cb)), |
| std::move(scan_filter)); |
| std::move(cb).Run(GetScanResultsInternal(std::move(scan_filter))); |
| } |
| |
| void LeScanManagerImpl::ClearScanResults() { |
| MAKE_SURE_IO_THREAD(ClearScanResults); |
| addr_to_scan_results_.clear(); |
| } |
| |
| void LeScanManagerImpl::PauseScan() { |
| MAKE_SURE_IO_THREAD(PauseScan); |
| if (scan_handle_ids_.empty()) { |
| LOG(ERROR) << "Can't pause scan, no scan handle"; |
| return; |
| } |
| |
| if (!le_scanner_->StopScan()) { |
| LOG(ERROR) << "Failed to pause scanning"; |
| } |
| } |
| |
| void LeScanManagerImpl::RestartScan() { |
| MAKE_SURE_IO_THREAD(RestartScan); |
| if (scan_handle_ids_.empty()) { |
| LOG(ERROR) << "Can't restart scan, no scan handle"; |
| return; |
| } |
| |
| if (!le_scanner_->StartScan()) { |
| LOG(ERROR) << "Failed to restart scanning"; |
| } |
| } |
| |
| void LeScanManagerImpl::SetScanParameters(int scan_interval_ms, |
| int scan_window_ms) { |
| MAKE_SURE_IO_THREAD(SetScanParameters, scan_interval_ms, scan_window_ms); |
| |
| if (!le_scanner_->SetScanParameters(scan_interval_ms, scan_window_ms)) { |
| LOG(ERROR) << "Failed to set scan parameters"; |
| } |
| |
| LOG(INFO) << __func__ << " scan_interval: " << scan_interval_ms |
| << "ms scan_window: " << scan_window_ms << "ms"; |
| } |
| |
| void LeScanManagerImpl::OnScanResult( |
| const bluetooth_v2_shlib::LeScanner::ScanResult& scan_result_shlib) { |
| LeScanResult scan_result; |
| if (!scan_result.SetAdvData(scan_result_shlib.adv_data)) { |
| // Error logged. |
| return; |
| } |
| scan_result.addr = scan_result_shlib.addr; |
| scan_result.rssi = scan_result_shlib.rssi; |
| |
| auto& previous_scan_results = addr_to_scan_results_[scan_result.addr]; |
| if (previous_scan_results.size() > 0) { |
| // Remove results with the same data as the current result to avoid |
| // duplicate messages in the queue |
| previous_scan_results.remove_if( |
| [&scan_result](const auto& previous_result) { |
| return previous_result.adv_data == scan_result.adv_data; |
| }); |
| |
| // Remove scan_result.addr to avoid duplicate addresses in |
| // recent_scan_result_addr_list_. |
| base::Erase(scan_result_addr_list_, scan_result.addr); |
| } |
| |
| previous_scan_results.push_front(scan_result); |
| if (previous_scan_results.size() > kMaxMessagesInQueue) { |
| previous_scan_results.pop_back(); |
| } |
| |
| // Update recent_scan_result_addr_list_. |
| scan_result_addr_list_.push_front(scan_result.addr); |
| while (scan_result_addr_list_.size() > kMaxScanResultEntries) { |
| // Remove least recently used address in recent_scan_result_addr_list_. |
| auto least_recently_used_addr = scan_result_addr_list_.back(); |
| scan_result_addr_list_.pop_back(); |
| addr_to_scan_results_.erase(least_recently_used_addr); |
| } |
| |
| // Update observers. |
| observers_->Notify(FROM_HERE, &Observer::OnNewScanResult, scan_result); |
| } |
| |
| // Returns a list of all scan results. The results are sorted by RSSI. |
| std::vector<LeScanResult> LeScanManagerImpl::GetScanResultsInternal( |
| base::Optional<ScanFilter> scan_filter) { |
| DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| std::vector<LeScanResult> results; |
| for (const auto& pair : addr_to_scan_results_) { |
| for (const auto& scan_result : pair.second) { |
| if (!scan_filter || scan_filter->Matches(scan_result)) { |
| results.push_back(scan_result); |
| } |
| } |
| } |
| |
| std::sort(results.begin(), results.end(), |
| [](const LeScanResult& d1, const LeScanResult& d2) { |
| return d1.rssi > d2.rssi; |
| }); |
| |
| return results; |
| } |
| |
| void LeScanManagerImpl::NotifyScanHandleDestroyed(int32_t id) { |
| DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| |
| size_t num_removed = scan_handle_ids_.erase(id); |
| DCHECK_EQ(num_removed, 1u); |
| if (scan_handle_ids_.empty()) { |
| if (!le_scanner_->StopScan()) { |
| LOG(ERROR) << "Failed to disable scanning"; |
| } else { |
| LOG(INFO) << "Disabling scan"; |
| observers_->Notify(FROM_HERE, &Observer::OnScanEnableChanged, false); |
| } |
| } |
| } |
| |
| } // namespace bluetooth |
| } // namespace chromecast |