// Copyright 2018 The Chromium Authors
// 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 <deque>
#include <utility>
#include <vector>
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/task/single_thread_task_runner.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;
// static
std::unique_ptr<LeScanManager> LeScanManager::Create(
BluetoothManagerPlatform* bluetooth_manager,
bluetooth_v2_shlib::LeScannerImpl* le_scanner) {
return std::make_unique<LeScanManagerImpl>(le_scanner);
}
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);
InitializeOnIoThread();
}
void LeScanManagerImpl::Finalize() {}
void LeScanManagerImpl::InitializeOnIoThread() {
MAKE_SURE_IO_THREAD(InitializeOnIoThread);
le_scanner_->SetDelegate(this);
}
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,
std::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::ResumeScan() {
MAKE_SURE_IO_THREAD(ResumeScan);
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 (scan_handle_ids_.empty()) {
LOG(ERROR) << "Can't set scan parameters, no scan handle";
return;
}
// We could only set scan parameters when scan is paused.
if (!le_scanner_->StopScan()) {
LOG(ERROR) << "Failed to pause scanning before setting scan parameters";
return;
}
if (!le_scanner_->SetScanParameters(scan_interval_ms, scan_window_ms)) {
LOG(ERROR) << "Failed to set scan parameters";
return;
}
if (!le_scanner_->StartScan()) {
LOG(ERROR) << "Failed to restart scanning after setting scan parameters";
return;
}
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_.
std::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(
std::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