1778 lines
68 KiB
C++
1778 lines
68 KiB
C++
|
/*
|
|||
|
* 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/le_scanning_manager.h"
|
|||
|
|
|||
|
#include <memory>
|
|||
|
#include <unordered_map>
|
|||
|
|
|||
|
#include "hci/acl_manager.h"
|
|||
|
#include "hci/controller.h"
|
|||
|
#include "hci/hci_layer.h"
|
|||
|
#include "hci/hci_packets.h"
|
|||
|
#include "hci/le_periodic_sync_manager.h"
|
|||
|
#include "hci/le_scanning_interface.h"
|
|||
|
#include "hci/vendor_specific_event_manager.h"
|
|||
|
#include "module.h"
|
|||
|
#include "os/handler.h"
|
|||
|
#include "os/log.h"
|
|||
|
#include "storage/storage_module.h"
|
|||
|
|
|||
|
namespace bluetooth {
|
|||
|
namespace hci {
|
|||
|
|
|||
|
constexpr uint16_t kLeScanWindowMin = 0x0004;
|
|||
|
constexpr uint16_t kLeScanWindowMax = 0x4000;
|
|||
|
constexpr uint16_t kDefaultLeExtendedScanWindow = 4800;
|
|||
|
constexpr uint16_t kLeExtendedScanWindowMax = 0xFFFF;
|
|||
|
constexpr uint16_t kLeScanIntervalMin = 0x0004;
|
|||
|
constexpr uint16_t kLeScanIntervalMax = 0x4000;
|
|||
|
constexpr uint16_t kDefaultLeExtendedScanInterval = 4800;
|
|||
|
constexpr uint16_t kLeExtendedScanIntervalMax = 0xFFFF;
|
|||
|
|
|||
|
constexpr uint8_t kScannableBit = 1;
|
|||
|
constexpr uint8_t kDirectedBit = 2;
|
|||
|
constexpr uint8_t kScanResponseBit = 3;
|
|||
|
constexpr uint8_t kLegacyBit = 4;
|
|||
|
constexpr uint8_t kDataStatusBits = 5;
|
|||
|
|
|||
|
const ModuleFactory LeScanningManager::Factory = ModuleFactory([]() { return new LeScanningManager(); });
|
|||
|
|
|||
|
enum class ScanApiType {
|
|||
|
LEGACY = 1,
|
|||
|
ANDROID_HCI = 2,
|
|||
|
EXTENDED = 3,
|
|||
|
};
|
|||
|
|
|||
|
struct Scanner {
|
|||
|
Uuid app_uuid;
|
|||
|
bool in_use;
|
|||
|
};
|
|||
|
|
|||
|
class AdvertisingCache {
|
|||
|
public:
|
|||
|
const std::vector<uint8_t>& Set(const AddressWithType& address_with_type, std::vector<uint8_t> data) {
|
|||
|
auto it = Find(address_with_type);
|
|||
|
if (it != items.end()) {
|
|||
|
it->data = std::move(data);
|
|||
|
return it->data;
|
|||
|
}
|
|||
|
|
|||
|
if (items.size() > cache_max) {
|
|||
|
items.pop_back();
|
|||
|
}
|
|||
|
|
|||
|
items.emplace_front(address_with_type, std::move(data));
|
|||
|
return items.front().data;
|
|||
|
}
|
|||
|
|
|||
|
bool Exist(const AddressWithType& address_with_type) {
|
|||
|
auto it = Find(address_with_type);
|
|||
|
if (it == items.end()) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
const std::vector<uint8_t>& Append(const AddressWithType& address_with_type, std::vector<uint8_t> data) {
|
|||
|
auto it = Find(address_with_type);
|
|||
|
if (it != items.end()) {
|
|||
|
it->data.insert(it->data.end(), data.begin(), data.end());
|
|||
|
return it->data;
|
|||
|
}
|
|||
|
|
|||
|
if (items.size() > cache_max) {
|
|||
|
items.pop_back();
|
|||
|
}
|
|||
|
|
|||
|
items.emplace_front(address_with_type, std::move(data));
|
|||
|
return items.front().data;
|
|||
|
}
|
|||
|
|
|||
|
/* Clear data for device |addr_type, addr| */
|
|||
|
void Clear(AddressWithType address_with_type) {
|
|||
|
auto it = Find(address_with_type);
|
|||
|
if (it != items.end()) {
|
|||
|
items.erase(it);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void ClearAll() {
|
|||
|
items.clear();
|
|||
|
}
|
|||
|
|
|||
|
struct Item {
|
|||
|
AddressWithType address_with_type;
|
|||
|
std::vector<uint8_t> data;
|
|||
|
|
|||
|
Item(const AddressWithType& address_with_type, std::vector<uint8_t> data)
|
|||
|
: address_with_type(address_with_type), data(data) {}
|
|||
|
};
|
|||
|
|
|||
|
std::list<Item>::iterator Find(const AddressWithType& address_with_type) {
|
|||
|
for (auto it = items.begin(); it != items.end(); it++) {
|
|||
|
if (it->address_with_type == address_with_type) {
|
|||
|
return it;
|
|||
|
}
|
|||
|
}
|
|||
|
return items.end();
|
|||
|
}
|
|||
|
|
|||
|
/* we keep maximum 7 devices in the cache */
|
|||
|
const size_t cache_max = 1000;
|
|||
|
std::list<Item> items;
|
|||
|
};
|
|||
|
|
|||
|
class NullScanningCallback : public ScanningCallback {
|
|||
|
void OnScannerRegistered(const Uuid app_uuid, ScannerId scanner_id, ScanningStatus status) override {
|
|||
|
LOG_INFO("OnScannerRegistered in NullScanningCallback");
|
|||
|
}
|
|||
|
void OnSetScannerParameterComplete(ScannerId scanner_id, ScanningStatus status) override {
|
|||
|
LOG_INFO("OnSetScannerParameterComplete in NullScanningCallback");
|
|||
|
}
|
|||
|
void OnScanResult(
|
|||
|
uint16_t event_type,
|
|||
|
uint8_t address_type,
|
|||
|
Address address,
|
|||
|
uint8_t primary_phy,
|
|||
|
uint8_t secondary_phy,
|
|||
|
uint8_t advertising_sid,
|
|||
|
int8_t tx_power,
|
|||
|
int8_t rssi,
|
|||
|
uint16_t periodic_advertising_interval,
|
|||
|
std::vector<uint8_t> advertising_data) override {
|
|||
|
LOG_INFO("OnScanResult in NullScanningCallback");
|
|||
|
}
|
|||
|
void OnTrackAdvFoundLost(AdvertisingFilterOnFoundOnLostInfo on_found_on_lost_info) override {
|
|||
|
LOG_INFO("OnTrackAdvFoundLost in NullScanningCallback");
|
|||
|
}
|
|||
|
void OnBatchScanReports(
|
|||
|
int client_if, int status, int report_format, int num_records, std::vector<uint8_t> data) override {
|
|||
|
LOG_INFO("OnBatchScanReports in NullScanningCallback");
|
|||
|
}
|
|||
|
void OnBatchScanThresholdCrossed(int client_if) override {
|
|||
|
LOG_INFO("OnBatchScanThresholdCrossed in NullScanningCallback");
|
|||
|
}
|
|||
|
void OnTimeout() override {
|
|||
|
LOG_INFO("OnTimeout in NullScanningCallback");
|
|||
|
}
|
|||
|
void OnFilterEnable(Enable enable, uint8_t status) override {
|
|||
|
LOG_INFO("OnFilterEnable in NullScanningCallback");
|
|||
|
}
|
|||
|
void OnFilterParamSetup(uint8_t available_spaces, ApcfAction action, uint8_t status) override {
|
|||
|
LOG_INFO("OnFilterParamSetup in NullScanningCallback");
|
|||
|
}
|
|||
|
void OnFilterConfigCallback(
|
|||
|
ApcfFilterType filter_type, uint8_t available_spaces, ApcfAction action, uint8_t status) override {
|
|||
|
LOG_INFO("OnFilterConfigCallback in NullScanningCallback");
|
|||
|
}
|
|||
|
void OnPeriodicSyncStarted(
|
|||
|
int reg_id,
|
|||
|
uint8_t status,
|
|||
|
uint16_t sync_handle,
|
|||
|
uint8_t advertising_sid,
|
|||
|
AddressWithType address_with_type,
|
|||
|
uint8_t phy,
|
|||
|
uint16_t interval) override {
|
|||
|
LOG_INFO("OnPeriodicSyncStarted in NullScanningCallback");
|
|||
|
};
|
|||
|
void OnPeriodicSyncReport(
|
|||
|
uint16_t sync_handle, int8_t tx_power, int8_t rssi, uint8_t status, std::vector<uint8_t> data) override {
|
|||
|
LOG_INFO("OnPeriodicSyncReport in NullScanningCallback");
|
|||
|
};
|
|||
|
void OnPeriodicSyncLost(uint16_t sync_handle) override {
|
|||
|
LOG_INFO("OnPeriodicSyncLost in NullScanningCallback");
|
|||
|
};
|
|||
|
void OnPeriodicSyncTransferred(int pa_source, uint8_t status, Address address) override {
|
|||
|
LOG_INFO("OnPeriodicSyncTransferred in NullScanningCallback");
|
|||
|
};
|
|||
|
};
|
|||
|
|
|||
|
enum class BatchScanState {
|
|||
|
ERROR_STATE = 0,
|
|||
|
ENABLE_CALLED = 1,
|
|||
|
ENABLED_STATE = 2,
|
|||
|
DISABLE_CALLED = 3,
|
|||
|
DISABLED_STATE = 4,
|
|||
|
};
|
|||
|
|
|||
|
#define BTM_BLE_BATCH_SCAN_MODE_DISABLE 0
|
|||
|
#define BTM_BLE_BATCH_SCAN_MODE_PASS 1
|
|||
|
#define BTM_BLE_BATCH_SCAN_MODE_ACTI 2
|
|||
|
#define BTM_BLE_BATCH_SCAN_MODE_PASS_ACTI 3
|
|||
|
|
|||
|
struct BatchScanConfig {
|
|||
|
BatchScanState current_state;
|
|||
|
BatchScanMode scan_mode;
|
|||
|
uint32_t scan_interval;
|
|||
|
uint32_t scan_window;
|
|||
|
BatchScanDiscardRule discard_rule;
|
|||
|
ScannerId ref_value;
|
|||
|
};
|
|||
|
|
|||
|
struct LeScanningManager::impl : public LeAddressManagerCallback {
|
|||
|
impl(Module* module) : module_(module), le_scanning_interface_(nullptr) {}
|
|||
|
|
|||
|
~impl() {
|
|||
|
if (address_manager_registered_) {
|
|||
|
le_address_manager_->Unregister(this);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void start(
|
|||
|
os::Handler* handler,
|
|||
|
HciLayer* hci_layer,
|
|||
|
Controller* controller,
|
|||
|
AclManager* acl_manager,
|
|||
|
VendorSpecificEventManager* vendor_specific_event_manager,
|
|||
|
storage::StorageModule* storage_module) {
|
|||
|
module_handler_ = handler;
|
|||
|
hci_layer_ = hci_layer;
|
|||
|
controller_ = controller;
|
|||
|
acl_manager_ = acl_manager;
|
|||
|
vendor_specific_event_manager_ = vendor_specific_event_manager;
|
|||
|
storage_module_ = storage_module;
|
|||
|
le_address_manager_ = acl_manager->GetLeAddressManager();
|
|||
|
le_scanning_interface_ = hci_layer_->GetLeScanningInterface(
|
|||
|
module_handler_->BindOn(this, &LeScanningManager::impl::handle_scan_results));
|
|||
|
periodic_sync_manager_.Init(le_scanning_interface_, module_handler_);
|
|||
|
/* Check to see if the opcode is supported and C19 (support for extended advertising). */
|
|||
|
if (controller_->IsSupported(OpCode::LE_SET_EXTENDED_SCAN_PARAMETERS) &&
|
|||
|
controller->SupportsBleExtendedAdvertising()) {
|
|||
|
api_type_ = ScanApiType::EXTENDED;
|
|||
|
interval_ms_ = kDefaultLeExtendedScanInterval;
|
|||
|
window_ms_ = kDefaultLeExtendedScanWindow;
|
|||
|
} else if (controller_->IsSupported(OpCode::LE_EXTENDED_SCAN_PARAMS)) {
|
|||
|
api_type_ = ScanApiType::ANDROID_HCI;
|
|||
|
} else {
|
|||
|
api_type_ = ScanApiType::LEGACY;
|
|||
|
}
|
|||
|
is_filter_supported_ = controller_->IsSupported(OpCode::LE_ADV_FILTER);
|
|||
|
if (is_filter_supported_) {
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeAdvFilterReadExtendedFeaturesBuilder::Create(),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_apcf_read_extended_features_complete));
|
|||
|
}
|
|||
|
is_batch_scan_supported_ = controller->IsSupported(OpCode::LE_BATCH_SCAN);
|
|||
|
is_periodic_advertising_sync_transfer_sender_supported_ =
|
|||
|
controller_->SupportsBlePeriodicAdvertisingSyncTransferSender();
|
|||
|
total_num_of_advt_tracked_ = controller->GetVendorCapabilities().total_num_of_advt_tracked_;
|
|||
|
if (is_batch_scan_supported_) {
|
|||
|
vendor_specific_event_manager_->RegisterEventHandler(
|
|||
|
VseSubeventCode::BLE_THRESHOLD, handler->BindOn(this, &LeScanningManager::impl::on_storage_threshold_breach));
|
|||
|
vendor_specific_event_manager_->RegisterEventHandler(
|
|||
|
VseSubeventCode::BLE_TRACKING, handler->BindOn(this, &LeScanningManager::impl::on_advertisement_tracking));
|
|||
|
}
|
|||
|
scanners_ = std::vector<Scanner>(kMaxAppNum + 1);
|
|||
|
for (size_t i = 0; i < scanners_.size(); i++) {
|
|||
|
scanners_[i].app_uuid = Uuid::kEmpty;
|
|||
|
scanners_[i].in_use = false;
|
|||
|
}
|
|||
|
batch_scan_config_.current_state = BatchScanState::DISABLED_STATE;
|
|||
|
batch_scan_config_.ref_value = kInvalidScannerId;
|
|||
|
configure_scan();
|
|||
|
}
|
|||
|
|
|||
|
void stop() {
|
|||
|
for (auto subevent_code : LeScanningEvents) {
|
|||
|
hci_layer_->UnregisterLeEventHandler(subevent_code);
|
|||
|
}
|
|||
|
if (is_batch_scan_supported_) {
|
|||
|
// TODO implete vse module
|
|||
|
// hci_layer_->UnregisterVesEventHandler(VseSubeventCode::BLE_THRESHOLD);
|
|||
|
// hci_layer_->UnregisterVesEventHandler(VseSubeventCode::BLE_TRACKING);
|
|||
|
}
|
|||
|
batch_scan_config_.current_state = BatchScanState::DISABLED_STATE;
|
|||
|
batch_scan_config_.ref_value = kInvalidScannerId;
|
|||
|
scanning_callbacks_ = &null_scanning_callback_;
|
|||
|
periodic_sync_manager_.SetScanningCallback(scanning_callbacks_);
|
|||
|
}
|
|||
|
|
|||
|
void handle_scan_results(LeMetaEventView event) {
|
|||
|
switch (event.GetSubeventCode()) {
|
|||
|
case SubeventCode::ADVERTISING_REPORT:
|
|||
|
handle_advertising_report(LeAdvertisingReportView::Create(event));
|
|||
|
break;
|
|||
|
case SubeventCode::DIRECTED_ADVERTISING_REPORT:
|
|||
|
handle_directed_advertising_report(LeDirectedAdvertisingReportView::Create(event));
|
|||
|
break;
|
|||
|
case SubeventCode::EXTENDED_ADVERTISING_REPORT:
|
|||
|
handle_extended_advertising_report(LeExtendedAdvertisingReportView::Create(event));
|
|||
|
break;
|
|||
|
case SubeventCode::PERIODIC_ADVERTISING_SYNC_ESTABLISHED:
|
|||
|
LePeriodicAdvertisingSyncEstablishedView::Create(event);
|
|||
|
periodic_sync_manager_.HandleLePeriodicAdvertisingSyncEstablished(
|
|||
|
LePeriodicAdvertisingSyncEstablishedView::Create(event));
|
|||
|
break;
|
|||
|
case SubeventCode::PERIODIC_ADVERTISING_REPORT:
|
|||
|
periodic_sync_manager_.HandleLePeriodicAdvertisingReport(LePeriodicAdvertisingReportView::Create(event));
|
|||
|
break;
|
|||
|
case SubeventCode::PERIODIC_ADVERTISING_SYNC_LOST:
|
|||
|
periodic_sync_manager_.HandleLePeriodicAdvertisingSyncLost(LePeriodicAdvertisingSyncLostView::Create(event));
|
|||
|
break;
|
|||
|
case SubeventCode::PERIODIC_ADVERTISING_SYNC_TRANSFER_RECEIVED:
|
|||
|
periodic_sync_manager_.HandleLePeriodicAdvertisingSyncTransferReceived(
|
|||
|
LePeriodicAdvertisingSyncTransferReceivedView::Create(event));
|
|||
|
break;
|
|||
|
case SubeventCode::SCAN_TIMEOUT:
|
|||
|
scanning_callbacks_->OnTimeout();
|
|||
|
break;
|
|||
|
default:
|
|||
|
LOG_ALWAYS_FATAL("Unknown advertising subevent %s", SubeventCodeText(event.GetSubeventCode()).c_str());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
struct ExtendedEventTypeOptions {
|
|||
|
bool connectable{false};
|
|||
|
bool scannable{false};
|
|||
|
bool directed{false};
|
|||
|
bool scan_response{false};
|
|||
|
bool legacy{false};
|
|||
|
bool continuing{false};
|
|||
|
bool truncated{false};
|
|||
|
};
|
|||
|
|
|||
|
void transform_to_extended_event_type(uint16_t* extended_event_type, ExtendedEventTypeOptions o) {
|
|||
|
ASSERT(extended_event_type != nullptr);
|
|||
|
*extended_event_type = (o.connectable ? 0x0001 << 0 : 0) | (o.scannable ? 0x0001 << 1 : 0) |
|
|||
|
(o.directed ? 0x0001 << 2 : 0) | (o.scan_response ? 0x0001 << 3 : 0) |
|
|||
|
(o.legacy ? 0x0001 << 4 : 0) | (o.continuing ? 0x0001 << 5 : 0) |
|
|||
|
(o.truncated ? 0x0001 << 6 : 0);
|
|||
|
}
|
|||
|
|
|||
|
void handle_advertising_report(LeAdvertisingReportView event_view) {
|
|||
|
if (!event_view.IsValid()) {
|
|||
|
LOG_INFO("Dropping invalid advertising event");
|
|||
|
return;
|
|||
|
}
|
|||
|
std::vector<LeAdvertisingResponse> reports = event_view.GetResponses();
|
|||
|
if (reports.empty()) {
|
|||
|
LOG_INFO("Zero results in advertising event");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
for (LeAdvertisingResponse report : reports) {
|
|||
|
uint16_t extended_event_type = 0;
|
|||
|
switch (report.event_type_) {
|
|||
|
case AdvertisingEventType::ADV_IND:
|
|||
|
transform_to_extended_event_type(
|
|||
|
&extended_event_type, {.connectable = true, .scannable = true, .legacy = true});
|
|||
|
break;
|
|||
|
case AdvertisingEventType::ADV_DIRECT_IND:
|
|||
|
transform_to_extended_event_type(
|
|||
|
&extended_event_type, {.connectable = true, .directed = true, .legacy = true});
|
|||
|
break;
|
|||
|
case AdvertisingEventType::ADV_SCAN_IND:
|
|||
|
transform_to_extended_event_type(&extended_event_type, {.scannable = true, .legacy = true});
|
|||
|
break;
|
|||
|
case AdvertisingEventType::ADV_NONCONN_IND:
|
|||
|
transform_to_extended_event_type(&extended_event_type, {.legacy = true});
|
|||
|
break;
|
|||
|
case AdvertisingEventType::SCAN_RESPONSE:
|
|||
|
transform_to_extended_event_type(
|
|||
|
&extended_event_type, {.connectable = true, .scannable = true, .scan_response = true, .legacy = true});
|
|||
|
break;
|
|||
|
default:
|
|||
|
LOG_WARN("Unsupported event type:%d", (uint16_t)report.event_type_);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
process_advertising_package_content(
|
|||
|
extended_event_type,
|
|||
|
(uint8_t)report.address_type_,
|
|||
|
report.address_,
|
|||
|
(uint8_t)PrimaryPhyType::LE_1M,
|
|||
|
(uint8_t)SecondaryPhyType::NO_PACKETS,
|
|||
|
kAdvertisingDataInfoNotPresent,
|
|||
|
kTxPowerInformationNotPresent,
|
|||
|
report.rssi_,
|
|||
|
kNotPeriodicAdvertisement,
|
|||
|
report.advertising_data_);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void handle_directed_advertising_report(LeDirectedAdvertisingReportView event_view) {
|
|||
|
if (!event_view.IsValid()) {
|
|||
|
LOG_INFO("Dropping invalid advertising event");
|
|||
|
return;
|
|||
|
}
|
|||
|
std::vector<LeDirectedAdvertisingResponse> reports = event_view.GetResponses();
|
|||
|
if (reports.empty()) {
|
|||
|
LOG_INFO("Zero results in advertising event");
|
|||
|
return;
|
|||
|
}
|
|||
|
uint16_t extended_event_type = 0;
|
|||
|
transform_to_extended_event_type(&extended_event_type, {.connectable = true, .directed = true, .legacy = true});
|
|||
|
// TODO: parse report
|
|||
|
}
|
|||
|
|
|||
|
void handle_extended_advertising_report(LeExtendedAdvertisingReportView event_view) {
|
|||
|
if (!event_view.IsValid()) {
|
|||
|
LOG_INFO("Dropping invalid advertising event");
|
|||
|
return;
|
|||
|
}
|
|||
|
std::vector<LeExtendedAdvertisingResponse> reports = event_view.GetResponses();
|
|||
|
if (reports.empty()) {
|
|||
|
LOG_INFO("Zero results in advertising event");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
for (LeExtendedAdvertisingResponse report : reports) {
|
|||
|
uint16_t event_type = report.connectable_ | (report.scannable_ << kScannableBit) |
|
|||
|
(report.directed_ << kDirectedBit) | (report.scan_response_ << kScanResponseBit) |
|
|||
|
(report.legacy_ << kLegacyBit) | ((uint16_t)report.data_status_ << kDataStatusBits);
|
|||
|
process_advertising_package_content(
|
|||
|
event_type,
|
|||
|
(uint8_t)report.address_type_,
|
|||
|
report.address_,
|
|||
|
(uint8_t)report.primary_phy_,
|
|||
|
(uint8_t)report.secondary_phy_,
|
|||
|
report.advertising_sid_,
|
|||
|
report.tx_power_,
|
|||
|
report.rssi_,
|
|||
|
report.periodic_advertising_interval_,
|
|||
|
report.advertising_data_);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void process_advertising_package_content(
|
|||
|
uint16_t event_type,
|
|||
|
uint8_t address_type,
|
|||
|
Address address,
|
|||
|
uint8_t primary_phy,
|
|||
|
uint8_t secondary_phy,
|
|||
|
uint8_t advertising_sid,
|
|||
|
int8_t tx_power,
|
|||
|
int8_t rssi,
|
|||
|
uint16_t periodic_advertising_interval,
|
|||
|
std::vector<LengthAndData> advertising_data) {
|
|||
|
bool is_scannable = event_type & (1 << kScannableBit);
|
|||
|
bool is_scan_response = event_type & (1 << kScanResponseBit);
|
|||
|
bool is_legacy = event_type & (1 << kLegacyBit);
|
|||
|
|
|||
|
auto significant_data = std::vector<uint8_t>{};
|
|||
|
for (const auto& datum : advertising_data) {
|
|||
|
if (!datum.data_.empty()) {
|
|||
|
significant_data.push_back(static_cast<uint8_t>(datum.data_.size()));
|
|||
|
significant_data.insert(significant_data.end(), datum.data_.begin(), datum.data_.end());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (address_type == (uint8_t)DirectAdvertisingAddressType::NO_ADDRESS_PROVIDED) {
|
|||
|
scanning_callbacks_->OnScanResult(
|
|||
|
event_type,
|
|||
|
address_type,
|
|||
|
address,
|
|||
|
primary_phy,
|
|||
|
secondary_phy,
|
|||
|
advertising_sid,
|
|||
|
tx_power,
|
|||
|
rssi,
|
|||
|
periodic_advertising_interval,
|
|||
|
significant_data);
|
|||
|
return;
|
|||
|
} else if (address == Address::kEmpty) {
|
|||
|
LOG_WARN("Receive non-anonymous advertising report with empty address, skip!");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
AddressWithType address_with_type(address, (AddressType)address_type);
|
|||
|
|
|||
|
if (is_legacy && is_scan_response && !advertising_cache_.Exist(address_with_type)) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
bool is_start = is_legacy && is_scannable && !is_scan_response;
|
|||
|
|
|||
|
std::vector<uint8_t> const& adv_data = is_start ? advertising_cache_.Set(address_with_type, significant_data)
|
|||
|
: advertising_cache_.Append(address_with_type, significant_data);
|
|||
|
|
|||
|
uint8_t data_status = event_type >> kDataStatusBits;
|
|||
|
if (data_status == (uint8_t)DataStatus::CONTINUING) {
|
|||
|
// Waiting for whole data
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (is_scannable && !is_scan_response) {
|
|||
|
// Waiting for scan response
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
switch (address_type) {
|
|||
|
case (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS:
|
|||
|
case (uint8_t)AddressType::PUBLIC_IDENTITY_ADDRESS:
|
|||
|
address_type = (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS;
|
|||
|
break;
|
|||
|
case (uint8_t)AddressType::RANDOM_DEVICE_ADDRESS:
|
|||
|
case (uint8_t)AddressType::RANDOM_IDENTITY_ADDRESS:
|
|||
|
address_type = (uint8_t)AddressType::RANDOM_DEVICE_ADDRESS;
|
|||
|
break;
|
|||
|
}
|
|||
|
scanning_callbacks_->OnScanResult(
|
|||
|
event_type,
|
|||
|
address_type,
|
|||
|
address,
|
|||
|
primary_phy,
|
|||
|
secondary_phy,
|
|||
|
advertising_sid,
|
|||
|
tx_power,
|
|||
|
rssi,
|
|||
|
periodic_advertising_interval,
|
|||
|
adv_data);
|
|||
|
|
|||
|
advertising_cache_.Clear(address_with_type);
|
|||
|
}
|
|||
|
|
|||
|
void configure_scan() {
|
|||
|
std::vector<PhyScanParameters> parameter_vector;
|
|||
|
PhyScanParameters phy_scan_parameters;
|
|||
|
phy_scan_parameters.le_scan_window_ = window_ms_;
|
|||
|
phy_scan_parameters.le_scan_interval_ = interval_ms_;
|
|||
|
phy_scan_parameters.le_scan_type_ = le_scan_type_;
|
|||
|
parameter_vector.push_back(phy_scan_parameters);
|
|||
|
uint8_t phys_in_use = 1;
|
|||
|
|
|||
|
// The Host shall not issue set scan parameter command when scanning is enabled
|
|||
|
stop_scan();
|
|||
|
|
|||
|
if (le_address_manager_->GetAddressPolicy() != LeAddressManager::USE_PUBLIC_ADDRESS) {
|
|||
|
own_address_type_ = OwnAddressType::RANDOM_DEVICE_ADDRESS;
|
|||
|
}
|
|||
|
|
|||
|
switch (api_type_) {
|
|||
|
case ScanApiType::EXTENDED:
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeSetExtendedScanParametersBuilder::Create(
|
|||
|
own_address_type_, filter_policy_, phys_in_use, parameter_vector),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_set_scan_parameter_complete));
|
|||
|
break;
|
|||
|
case ScanApiType::ANDROID_HCI:
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeExtendedScanParamsBuilder::Create(
|
|||
|
le_scan_type_, interval_ms_, window_ms_, own_address_type_, filter_policy_),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_set_scan_parameter_complete));
|
|||
|
|
|||
|
break;
|
|||
|
case ScanApiType::LEGACY:
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
|
|||
|
LeSetScanParametersBuilder::Create(
|
|||
|
le_scan_type_, interval_ms_, window_ms_, own_address_type_, filter_policy_),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_set_scan_parameter_complete));
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void register_scanner(const Uuid app_uuid) {
|
|||
|
for (uint8_t i = 1; i <= kMaxAppNum; i++) {
|
|||
|
if (scanners_[i].in_use && scanners_[i].app_uuid == app_uuid) {
|
|||
|
LOG_ERROR("Application already registered %s", app_uuid.ToString().c_str());
|
|||
|
scanning_callbacks_->OnScannerRegistered(app_uuid, 0x00, ScanningCallback::ScanningStatus::INTERNAL_ERROR);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// valid value of scanner id : 1 ~ kMaxAppNum
|
|||
|
for (uint8_t i = 1; i <= kMaxAppNum; i++) {
|
|||
|
if (!scanners_[i].in_use) {
|
|||
|
scanners_[i].app_uuid = app_uuid;
|
|||
|
scanners_[i].in_use = true;
|
|||
|
scanning_callbacks_->OnScannerRegistered(app_uuid, i, ScanningCallback::ScanningStatus::SUCCESS);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
LOG_ERROR("Unable to register scanner, max client reached:%d", kMaxAppNum);
|
|||
|
scanning_callbacks_->OnScannerRegistered(app_uuid, 0x00, ScanningCallback::ScanningStatus::NO_RESOURCES);
|
|||
|
}
|
|||
|
|
|||
|
void unregister_scanner(ScannerId scanner_id) {
|
|||
|
if (scanner_id <= 0 || scanner_id > kMaxAppNum) {
|
|||
|
LOG_WARN("Invalid scanner id");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (scanners_[scanner_id].in_use) {
|
|||
|
scanners_[scanner_id].in_use = false;
|
|||
|
scanners_[scanner_id].app_uuid = Uuid::kEmpty;
|
|||
|
} else {
|
|||
|
LOG_WARN("Unregister scanner with unused scanner id");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void scan(bool start) {
|
|||
|
if (start) {
|
|||
|
configure_scan();
|
|||
|
start_scan();
|
|||
|
} else {
|
|||
|
if (address_manager_registered_) {
|
|||
|
le_address_manager_->Unregister(this);
|
|||
|
address_manager_registered_ = false;
|
|||
|
paused_ = false;
|
|||
|
}
|
|||
|
stop_scan();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void start_scan() {
|
|||
|
// If we receive start_scan during paused, set scan_on_resume_ to true
|
|||
|
if (paused_ && address_manager_registered_) {
|
|||
|
scan_on_resume_ = true;
|
|||
|
return;
|
|||
|
}
|
|||
|
is_scanning_ = true;
|
|||
|
if (!address_manager_registered_) {
|
|||
|
le_address_manager_->Register(this);
|
|||
|
address_manager_registered_ = true;
|
|||
|
}
|
|||
|
|
|||
|
switch (api_type_) {
|
|||
|
case ScanApiType::EXTENDED:
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeSetExtendedScanEnableBuilder::Create(
|
|||
|
Enable::ENABLED, FilterDuplicates::DISABLED /* filter duplicates */, 0, 0),
|
|||
|
module_handler_->BindOnce(impl::check_status));
|
|||
|
break;
|
|||
|
case ScanApiType::ANDROID_HCI:
|
|||
|
case ScanApiType::LEGACY:
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeSetScanEnableBuilder::Create(Enable::ENABLED, Enable::DISABLED /* filter duplicates */),
|
|||
|
module_handler_->BindOnce(impl::check_status));
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void stop_scan() {
|
|||
|
if (!is_scanning_) {
|
|||
|
LOG_INFO("Scanning already stopped, return!");
|
|||
|
return;
|
|||
|
}
|
|||
|
is_scanning_ = false;
|
|||
|
|
|||
|
switch (api_type_) {
|
|||
|
case ScanApiType::EXTENDED:
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeSetExtendedScanEnableBuilder::Create(
|
|||
|
Enable::DISABLED, FilterDuplicates::DISABLED /* filter duplicates */, 0, 0),
|
|||
|
module_handler_->BindOnce(impl::check_status));
|
|||
|
break;
|
|||
|
case ScanApiType::ANDROID_HCI:
|
|||
|
case ScanApiType::LEGACY:
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeSetScanEnableBuilder::Create(Enable::DISABLED, Enable::DISABLED /* filter duplicates */),
|
|||
|
module_handler_->BindOnce(impl::check_status));
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void set_scan_parameters(ScannerId scanner_id, LeScanType scan_type, uint16_t scan_interval, uint16_t scan_window) {
|
|||
|
uint32_t max_scan_interval = kLeScanIntervalMax;
|
|||
|
uint32_t max_scan_window = kLeScanWindowMax;
|
|||
|
if (api_type_ == ScanApiType::EXTENDED) {
|
|||
|
max_scan_interval = kLeExtendedScanIntervalMax;
|
|||
|
max_scan_window = kLeExtendedScanWindowMax;
|
|||
|
}
|
|||
|
|
|||
|
if (scan_type != LeScanType::ACTIVE && scan_type != LeScanType::PASSIVE) {
|
|||
|
LOG_ERROR("Invalid scan type");
|
|||
|
scanning_callbacks_->OnSetScannerParameterComplete(
|
|||
|
scanner_id, ScanningCallback::ScanningStatus::ILLEGAL_PARAMETER);
|
|||
|
return;
|
|||
|
}
|
|||
|
if (scan_interval > max_scan_interval || scan_interval < kLeScanIntervalMin) {
|
|||
|
LOG_ERROR("Invalid scan_interval %d", scan_interval);
|
|||
|
scanning_callbacks_->OnSetScannerParameterComplete(
|
|||
|
scanner_id, ScanningCallback::ScanningStatus::ILLEGAL_PARAMETER);
|
|||
|
return;
|
|||
|
}
|
|||
|
if (scan_window > max_scan_window || scan_window < kLeScanWindowMin) {
|
|||
|
LOG_ERROR("Invalid scan_window %d", scan_window);
|
|||
|
scanning_callbacks_->OnSetScannerParameterComplete(
|
|||
|
scanner_id, ScanningCallback::ScanningStatus::ILLEGAL_PARAMETER);
|
|||
|
return;
|
|||
|
}
|
|||
|
le_scan_type_ = scan_type;
|
|||
|
interval_ms_ = scan_interval;
|
|||
|
window_ms_ = scan_window;
|
|||
|
scanning_callbacks_->OnSetScannerParameterComplete(scanner_id, ScanningCallback::SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
void scan_filter_enable(bool enable) {
|
|||
|
if (!is_filter_supported_) {
|
|||
|
LOG_WARN("Advertising filter is not supported");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
Enable apcf_enable = enable ? Enable::ENABLED : Enable::DISABLED;
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeAdvFilterEnableBuilder::Create(apcf_enable),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
|
|||
|
}
|
|||
|
|
|||
|
bool is_bonded(Address target_address) {
|
|||
|
for (auto device : storage_module_->GetBondedDevices()) {
|
|||
|
if (device.GetAddress() == target_address) {
|
|||
|
LOG_DEBUG("Addresses match!");
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
LOG_DEBUG("Addresse DON'Ts match!");
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
void scan_filter_parameter_setup(
|
|||
|
ApcfAction action, uint8_t filter_index, AdvertisingFilterParameter advertising_filter_parameter) {
|
|||
|
if (!is_filter_supported_) {
|
|||
|
LOG_WARN("Advertising filter is not supported");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
auto entry = remove_me_later_map_.find(filter_index);
|
|||
|
switch (action) {
|
|||
|
case ApcfAction::ADD:
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeAdvFilterAddFilteringParametersBuilder::Create(
|
|||
|
filter_index,
|
|||
|
advertising_filter_parameter.feature_selection,
|
|||
|
advertising_filter_parameter.list_logic_type,
|
|||
|
advertising_filter_parameter.filter_logic_type,
|
|||
|
advertising_filter_parameter.rssi_high_thresh,
|
|||
|
advertising_filter_parameter.delivery_mode,
|
|||
|
advertising_filter_parameter.onfound_timeout,
|
|||
|
advertising_filter_parameter.onfound_timeout_cnt,
|
|||
|
advertising_filter_parameter.rssi_low_thresh,
|
|||
|
advertising_filter_parameter.onlost_timeout,
|
|||
|
advertising_filter_parameter.num_of_tracking_entries),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
|
|||
|
break;
|
|||
|
case ApcfAction::DELETE:
|
|||
|
tracker_id_map_.erase(filter_index);
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeAdvFilterDeleteFilteringParametersBuilder::Create(filter_index),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
|
|||
|
|
|||
|
// IRK Scanning
|
|||
|
if (entry != remove_me_later_map_.end()) {
|
|||
|
// Don't want to remove for a bonded device
|
|||
|
if (!is_bonded(entry->second.GetAddress())) {
|
|||
|
le_address_manager_->RemoveDeviceFromResolvingList(
|
|||
|
static_cast<PeerAddressType>(entry->second.GetAddressType()), entry->second.GetAddress());
|
|||
|
}
|
|||
|
remove_me_later_map_.erase(filter_index);
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
case ApcfAction::CLEAR:
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeAdvFilterClearFilteringParametersBuilder::Create(),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
|
|||
|
|
|||
|
// IRK Scanning
|
|||
|
if (entry != remove_me_later_map_.end()) {
|
|||
|
// Don't want to remove for a bonded device
|
|||
|
if (!is_bonded(entry->second.GetAddress())) {
|
|||
|
le_address_manager_->RemoveDeviceFromResolvingList(
|
|||
|
static_cast<PeerAddressType>(entry->second.GetAddressType()), entry->second.GetAddress());
|
|||
|
}
|
|||
|
remove_me_later_map_.erase(filter_index);
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
default:
|
|||
|
LOG_ERROR("Unknown action type: %d", (uint16_t)action);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void scan_filter_add(uint8_t filter_index, std::vector<AdvertisingPacketContentFilterCommand> filters) {
|
|||
|
if (!is_filter_supported_) {
|
|||
|
LOG_WARN("Advertising filter is not supported");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
ApcfAction apcf_action = ApcfAction::ADD;
|
|||
|
for (auto filter : filters) {
|
|||
|
/* If data is passed, both mask and data have to be the same length */
|
|||
|
if (filter.data.size() != filter.data_mask.size() && filter.data.size() != 0 && filter.data_mask.size() != 0) {
|
|||
|
LOG_ERROR("data and data_mask are of different size");
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
switch (filter.filter_type) {
|
|||
|
case ApcfFilterType::BROADCASTER_ADDRESS: {
|
|||
|
update_address_filter(apcf_action, filter_index, filter.address, filter.application_address_type, filter.irk);
|
|||
|
break;
|
|||
|
}
|
|||
|
case ApcfFilterType::SERVICE_UUID:
|
|||
|
case ApcfFilterType::SERVICE_SOLICITATION_UUID: {
|
|||
|
update_uuid_filter(apcf_action, filter_index, filter.filter_type, filter.uuid, filter.uuid_mask);
|
|||
|
break;
|
|||
|
}
|
|||
|
case ApcfFilterType::LOCAL_NAME: {
|
|||
|
update_local_name_filter(apcf_action, filter_index, filter.name);
|
|||
|
break;
|
|||
|
}
|
|||
|
case ApcfFilterType::MANUFACTURER_DATA: {
|
|||
|
update_manufacturer_data_filter(
|
|||
|
apcf_action, filter_index, filter.company, filter.company_mask, filter.data, filter.data_mask);
|
|||
|
break;
|
|||
|
}
|
|||
|
case ApcfFilterType::SERVICE_DATA: {
|
|||
|
update_service_data_filter(apcf_action, filter_index, filter.data, filter.data_mask);
|
|||
|
break;
|
|||
|
}
|
|||
|
case ApcfFilterType::AD_TYPE: {
|
|||
|
update_ad_type_filter(apcf_action, filter_index, filter.ad_type, filter.data, filter.data_mask);
|
|||
|
break;
|
|||
|
}
|
|||
|
default:
|
|||
|
LOG_ERROR("Unknown filter type: %d", (uint16_t)filter.filter_type);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
std::unordered_map<uint8_t, AddressWithType> remove_me_later_map_;
|
|||
|
|
|||
|
void update_address_filter(
|
|||
|
ApcfAction action,
|
|||
|
uint8_t filter_index,
|
|||
|
Address address,
|
|||
|
ApcfApplicationAddressType address_type,
|
|||
|
std::array<uint8_t, 16> irk) {
|
|||
|
if (action != ApcfAction::CLEAR) {
|
|||
|
/*
|
|||
|
* The vendor command (APCF Filtering 0x0157) takes Public (0) or Random (1)
|
|||
|
* or Addresses type not applicable (2).
|
|||
|
*
|
|||
|
* Advertising results have four types:
|
|||
|
*  - Public = 0
|
|||
|
*  - Random = 1
|
|||
|
*  - Public ID = 2
|
|||
|
*  - Random ID = 3
|
|||
|
*
|
|||
|
* e.g. specifying PUBLIC (0) will only return results with a public
|
|||
|
* address. It will ignore resolved addresses, since they return PUBLIC
|
|||
|
* IDENTITY (2). For this, Addresses type not applicable (0x02) must be specified.
|
|||
|
* This should also cover if the RPA is derived from RANDOM STATIC.
|
|||
|
*/
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeAdvFilterBroadcasterAddressBuilder::Create(
|
|||
|
action, filter_index, address, ApcfApplicationAddressType::NOT_APPLICABLE),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
|
|||
|
if (!is_empty_128bit(irk)) {
|
|||
|
// If an entry exists for this filter index, replace data because the filter has been
|
|||
|
// updated.
|
|||
|
auto entry = remove_me_later_map_.find(filter_index);
|
|||
|
// IRK Scanning
|
|||
|
if (entry != remove_me_later_map_.end()) {
|
|||
|
// Don't want to remove for a bonded device
|
|||
|
if (!is_bonded(entry->second.GetAddress())) {
|
|||
|
le_address_manager_->RemoveDeviceFromResolvingList(
|
|||
|
static_cast<PeerAddressType>(entry->second.GetAddressType()), entry->second.GetAddress());
|
|||
|
}
|
|||
|
remove_me_later_map_.erase(filter_index);
|
|||
|
}
|
|||
|
|
|||
|
// Now replace it with a new one
|
|||
|
std::array<uint8_t, 16> empty_irk;
|
|||
|
le_address_manager_->AddDeviceToResolvingList(
|
|||
|
static_cast<PeerAddressType>(address_type), address, irk, empty_irk);
|
|||
|
remove_me_later_map_.emplace(filter_index, AddressWithType(address, static_cast<AddressType>(address_type)));
|
|||
|
}
|
|||
|
} else {
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeAdvFilterClearBroadcasterAddressBuilder::Create(filter_index),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
|
|||
|
auto entry = remove_me_later_map_.find(filter_index);
|
|||
|
if (entry != remove_me_later_map_.end()) {
|
|||
|
// TODO(optedoblivion): If not bonded
|
|||
|
le_address_manager_->RemoveDeviceFromResolvingList(static_cast<PeerAddressType>(address_type), address);
|
|||
|
remove_me_later_map_.erase(filter_index);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
bool is_empty_128bit(const std::array<uint8_t, 16> data) {
|
|||
|
for (int i = 0; i < 16; i++) {
|
|||
|
if (data[i] != (uint8_t)0) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
void update_uuid_filter(
|
|||
|
ApcfAction action, uint8_t filter_index, ApcfFilterType filter_type, Uuid uuid, Uuid uuid_mask) {
|
|||
|
std::vector<uint8_t> combined_data = {};
|
|||
|
if (action != ApcfAction::CLEAR) {
|
|||
|
uint8_t uuid_len = uuid.GetShortestRepresentationSize();
|
|||
|
if (uuid_len == Uuid::kNumBytes16) {
|
|||
|
uint16_t data = uuid.As16Bit();
|
|||
|
combined_data.push_back((uint8_t)data);
|
|||
|
combined_data.push_back((uint8_t)(data >> 8));
|
|||
|
} else if (uuid_len == Uuid::kNumBytes32) {
|
|||
|
uint16_t data = uuid.As32Bit();
|
|||
|
combined_data.push_back((uint8_t)data);
|
|||
|
combined_data.push_back((uint8_t)(data >> 8));
|
|||
|
combined_data.push_back((uint8_t)(data >> 16));
|
|||
|
combined_data.push_back((uint8_t)(data >> 24));
|
|||
|
} else if (uuid_len == Uuid::kNumBytes128) {
|
|||
|
auto data = uuid.To128BitLE();
|
|||
|
combined_data.insert(combined_data.end(), data.begin(), data.end());
|
|||
|
} else {
|
|||
|
LOG_ERROR("illegal UUID length: %d", (uint16_t)uuid_len);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (!uuid_mask.IsEmpty()) {
|
|||
|
if (uuid_len == Uuid::kNumBytes16) {
|
|||
|
uint16_t data = uuid_mask.As16Bit();
|
|||
|
combined_data.push_back((uint8_t)data);
|
|||
|
combined_data.push_back((uint8_t)(data >> 8));
|
|||
|
} else if (uuid_len == Uuid::kNumBytes32) {
|
|||
|
uint16_t data = uuid_mask.As32Bit();
|
|||
|
combined_data.push_back((uint8_t)data);
|
|||
|
combined_data.push_back((uint8_t)(data >> 8));
|
|||
|
combined_data.push_back((uint8_t)(data >> 16));
|
|||
|
combined_data.push_back((uint8_t)(data >> 24));
|
|||
|
} else if (uuid_len == Uuid::kNumBytes128) {
|
|||
|
auto data = uuid_mask.To128BitLE();
|
|||
|
combined_data.insert(combined_data.end(), data.begin(), data.end());
|
|||
|
}
|
|||
|
} else {
|
|||
|
std::vector<uint8_t> data(uuid_len, 0xFF);
|
|||
|
combined_data.insert(combined_data.end(), data.begin(), data.end());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (filter_type == ApcfFilterType::SERVICE_UUID) {
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeAdvFilterServiceUuidBuilder::Create(action, filter_index, combined_data),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
|
|||
|
} else {
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeAdvFilterSolicitationUuidBuilder::Create(action, filter_index, combined_data),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void update_local_name_filter(ApcfAction action, uint8_t filter_index, std::vector<uint8_t> name) {
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeAdvFilterLocalNameBuilder::Create(action, filter_index, name),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
|
|||
|
}
|
|||
|
|
|||
|
void update_manufacturer_data_filter(
|
|||
|
ApcfAction action,
|
|||
|
uint8_t filter_index,
|
|||
|
uint16_t company_id,
|
|||
|
uint16_t company_id_mask,
|
|||
|
std::vector<uint8_t> data,
|
|||
|
std::vector<uint8_t> data_mask) {
|
|||
|
if (data.size() != data_mask.size()) {
|
|||
|
LOG_ERROR("manufacturer data mask should have the same length as manufacturer data");
|
|||
|
return;
|
|||
|
}
|
|||
|
std::vector<uint8_t> combined_data = {};
|
|||
|
if (action != ApcfAction::CLEAR) {
|
|||
|
combined_data.push_back((uint8_t)company_id);
|
|||
|
combined_data.push_back((uint8_t)(company_id >> 8));
|
|||
|
if (data.size() != 0) {
|
|||
|
combined_data.insert(combined_data.end(), data.begin(), data.end());
|
|||
|
}
|
|||
|
if (company_id_mask != 0) {
|
|||
|
combined_data.push_back((uint8_t)company_id_mask);
|
|||
|
combined_data.push_back((uint8_t)(company_id_mask >> 8));
|
|||
|
} else {
|
|||
|
combined_data.push_back(0xFF);
|
|||
|
combined_data.push_back(0xFF);
|
|||
|
}
|
|||
|
if (data_mask.size() != 0) {
|
|||
|
combined_data.insert(combined_data.end(), data_mask.begin(), data_mask.end());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeAdvFilterManufacturerDataBuilder::Create(action, filter_index, combined_data),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
|
|||
|
}
|
|||
|
|
|||
|
void update_service_data_filter(
|
|||
|
ApcfAction action, uint8_t filter_index, std::vector<uint8_t> data, std::vector<uint8_t> data_mask) {
|
|||
|
if (data.size() != data_mask.size()) {
|
|||
|
LOG_ERROR("service data mask should have the same length as service data");
|
|||
|
return;
|
|||
|
}
|
|||
|
std::vector<uint8_t> combined_data = {};
|
|||
|
if (action != ApcfAction::CLEAR && data.size() != 0) {
|
|||
|
combined_data.insert(combined_data.end(), data.begin(), data.end());
|
|||
|
combined_data.insert(combined_data.end(), data_mask.begin(), data_mask.end());
|
|||
|
}
|
|||
|
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeAdvFilterServiceDataBuilder::Create(action, filter_index, combined_data),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
|
|||
|
}
|
|||
|
|
|||
|
void update_ad_type_filter(
|
|||
|
ApcfAction action,
|
|||
|
uint8_t filter_index,
|
|||
|
uint8_t ad_type,
|
|||
|
std::vector<uint8_t> data,
|
|||
|
std::vector<uint8_t> data_mask) {
|
|||
|
if (!is_ad_type_filter_supported_) {
|
|||
|
LOG_ERROR("AD type filter isn't supported");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (data.size() != data_mask.size()) {
|
|||
|
LOG_ERROR("ad type mask should have the same length as ad type data");
|
|||
|
return;
|
|||
|
}
|
|||
|
std::vector<uint8_t> combined_data = {};
|
|||
|
if (action != ApcfAction::CLEAR) {
|
|||
|
combined_data.push_back((uint8_t)ad_type);
|
|||
|
combined_data.push_back((uint8_t)(data.size()));
|
|||
|
if (data.size() != 0) {
|
|||
|
combined_data.insert(combined_data.end(), data.begin(), data.end());
|
|||
|
combined_data.insert(combined_data.end(), data_mask.begin(), data_mask.end());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeAdvFilterADTypeBuilder::Create(action, filter_index, combined_data),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
|
|||
|
}
|
|||
|
|
|||
|
void batch_scan_set_storage_parameter(
|
|||
|
uint8_t batch_scan_full_max,
|
|||
|
uint8_t batch_scan_truncated_max,
|
|||
|
uint8_t batch_scan_notify_threshold,
|
|||
|
ScannerId scanner_id) {
|
|||
|
if (!is_batch_scan_supported_) {
|
|||
|
LOG_WARN("Batch scan is not supported");
|
|||
|
return;
|
|||
|
}
|
|||
|
// scanner id for OnBatchScanThresholdCrossed
|
|||
|
batch_scan_config_.ref_value = scanner_id;
|
|||
|
|
|||
|
if (batch_scan_config_.current_state == BatchScanState::ERROR_STATE ||
|
|||
|
batch_scan_config_.current_state == BatchScanState::DISABLED_STATE ||
|
|||
|
batch_scan_config_.current_state == BatchScanState::DISABLE_CALLED) {
|
|||
|
batch_scan_config_.current_state = BatchScanState::ENABLE_CALLED;
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeBatchScanEnableBuilder::Create(Enable::ENABLED),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_batch_scan_enable_complete));
|
|||
|
}
|
|||
|
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeBatchScanSetStorageParametersBuilder::Create(
|
|||
|
batch_scan_full_max, batch_scan_truncated_max, batch_scan_notify_threshold),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_batch_scan_complete));
|
|||
|
}
|
|||
|
|
|||
|
void batch_scan_enable(
|
|||
|
BatchScanMode scan_mode,
|
|||
|
uint32_t duty_cycle_scan_window_slots,
|
|||
|
uint32_t duty_cycle_scan_interval_slots,
|
|||
|
BatchScanDiscardRule batch_scan_discard_rule) {
|
|||
|
if (!is_batch_scan_supported_) {
|
|||
|
LOG_WARN("Batch scan is not supported");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (batch_scan_config_.current_state == BatchScanState::ERROR_STATE ||
|
|||
|
batch_scan_config_.current_state == BatchScanState::DISABLED_STATE ||
|
|||
|
batch_scan_config_.current_state == BatchScanState::DISABLE_CALLED) {
|
|||
|
batch_scan_config_.current_state = BatchScanState::ENABLE_CALLED;
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeBatchScanEnableBuilder::Create(Enable::ENABLED),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_batch_scan_enable_complete));
|
|||
|
}
|
|||
|
|
|||
|
batch_scan_config_.scan_mode = scan_mode;
|
|||
|
batch_scan_config_.scan_interval = duty_cycle_scan_interval_slots;
|
|||
|
batch_scan_config_.scan_window = duty_cycle_scan_window_slots;
|
|||
|
batch_scan_config_.discard_rule = batch_scan_discard_rule;
|
|||
|
/* This command starts batch scanning, if enabled */
|
|||
|
batch_scan_set_scan_parameter(
|
|||
|
scan_mode, duty_cycle_scan_window_slots, duty_cycle_scan_interval_slots, batch_scan_discard_rule);
|
|||
|
}
|
|||
|
|
|||
|
void batch_scan_disable() {
|
|||
|
if (!is_batch_scan_supported_) {
|
|||
|
LOG_WARN("Batch scan is not supported");
|
|||
|
return;
|
|||
|
}
|
|||
|
batch_scan_config_.current_state = BatchScanState::DISABLE_CALLED;
|
|||
|
batch_scan_set_scan_parameter(
|
|||
|
BatchScanMode::DISABLE,
|
|||
|
batch_scan_config_.scan_window,
|
|||
|
batch_scan_config_.scan_interval,
|
|||
|
batch_scan_config_.discard_rule);
|
|||
|
}
|
|||
|
|
|||
|
void batch_scan_set_scan_parameter(
|
|||
|
BatchScanMode scan_mode,
|
|||
|
uint32_t duty_cycle_scan_window_slots,
|
|||
|
uint32_t duty_cycle_scan_interval_slots,
|
|||
|
BatchScanDiscardRule batch_scan_discard_rule) {
|
|||
|
if (!is_batch_scan_supported_) {
|
|||
|
LOG_WARN("Batch scan is not supported");
|
|||
|
return;
|
|||
|
}
|
|||
|
AdvertisingAddressType own_address_type = AdvertisingAddressType::PUBLIC_ADDRESS;
|
|||
|
if (own_address_type_ == OwnAddressType::RANDOM_DEVICE_ADDRESS ||
|
|||
|
own_address_type_ == OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS) {
|
|||
|
own_address_type = AdvertisingAddressType::RANDOM_ADDRESS;
|
|||
|
}
|
|||
|
uint8_t truncated_mode_enabled = 0x00;
|
|||
|
uint8_t full_mode_enabled = 0x00;
|
|||
|
if (scan_mode == BatchScanMode::TRUNCATED || scan_mode == BatchScanMode::TRUNCATED_AND_FULL) {
|
|||
|
truncated_mode_enabled = 0x01;
|
|||
|
}
|
|||
|
if (scan_mode == BatchScanMode::FULL || scan_mode == BatchScanMode::TRUNCATED_AND_FULL) {
|
|||
|
full_mode_enabled = 0x01;
|
|||
|
}
|
|||
|
|
|||
|
if (scan_mode == BatchScanMode::DISABLE) {
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeBatchScanSetScanParametersBuilder::Create(
|
|||
|
truncated_mode_enabled,
|
|||
|
full_mode_enabled,
|
|||
|
duty_cycle_scan_window_slots,
|
|||
|
duty_cycle_scan_interval_slots,
|
|||
|
own_address_type,
|
|||
|
batch_scan_discard_rule),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_batch_scan_disable_complete));
|
|||
|
} else {
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeBatchScanSetScanParametersBuilder::Create(
|
|||
|
truncated_mode_enabled,
|
|||
|
full_mode_enabled,
|
|||
|
duty_cycle_scan_window_slots,
|
|||
|
duty_cycle_scan_interval_slots,
|
|||
|
own_address_type,
|
|||
|
batch_scan_discard_rule),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_batch_scan_complete));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void batch_scan_read_results(ScannerId scanner_id, uint16_t total_num_of_records, BatchScanMode scan_mode) {
|
|||
|
if (!is_batch_scan_supported_) {
|
|||
|
LOG_WARN("Batch scan is not supported");
|
|||
|
int status = static_cast<int>(ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
|
|||
|
scanning_callbacks_->OnBatchScanReports(scanner_id, status, 0, 0, {});
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (scan_mode != BatchScanMode::FULL && scan_mode != BatchScanMode::TRUNCATED) {
|
|||
|
LOG_WARN("Invalid scan mode %d", (uint16_t)scan_mode);
|
|||
|
int status = static_cast<int>(ErrorCode::INVALID_HCI_COMMAND_PARAMETERS);
|
|||
|
scanning_callbacks_->OnBatchScanReports(scanner_id, status, 0, 0, {});
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (batch_scan_result_cache_.find(scanner_id) == batch_scan_result_cache_.end()) {
|
|||
|
std::vector<uint8_t> empty_data = {};
|
|||
|
batch_scan_result_cache_.emplace(scanner_id, empty_data);
|
|||
|
}
|
|||
|
|
|||
|
le_scanning_interface_->EnqueueCommand(
|
|||
|
LeBatchScanReadResultParametersBuilder::Create(static_cast<BatchScanDataRead>(scan_mode)),
|
|||
|
module_handler_->BindOnceOn(this, &impl::on_batch_scan_read_result_complete, scanner_id, total_num_of_records));
|
|||
|
}
|
|||
|
|
|||
|
void start_sync(
|
|||
|
uint8_t sid, const AddressWithType& address_with_type, uint16_t skip, uint16_t timeout, int request_id) {
|
|||
|
if (!is_periodic_advertising_sync_transfer_sender_supported_) {
|
|||
|
LOG_WARN("PAST sender not supported on this device");
|
|||
|
int status = static_cast<int>(ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
|
|||
|
scanning_callbacks_->OnPeriodicSyncStarted(request_id, status, -1, sid, address_with_type, 0, 0);
|
|||
|
return;
|
|||
|
}
|
|||
|
PeriodicSyncStates request{
|
|||
|
.request_id = request_id,
|
|||
|
.advertiser_sid = sid,
|
|||
|
.address_with_type = address_with_type,
|
|||
|
.sync_handle = 0,
|
|||
|
.sync_state = PeriodicSyncState::PERIODIC_SYNC_STATE_IDLE,
|
|||
|
};
|
|||
|
periodic_sync_manager_.StartSync(request, skip, timeout);
|
|||
|
}
|
|||
|
|
|||
|
void stop_sync(uint16_t handle) {
|
|||
|
if (!is_periodic_advertising_sync_transfer_sender_supported_) {
|
|||
|
LOG_WARN("PAST sender not supported on this device");
|
|||
|
return;
|
|||
|
}
|
|||
|
periodic_sync_manager_.StopSync(handle);
|
|||
|
}
|
|||
|
|
|||
|
void cancel_create_sync(uint8_t sid, const Address& address) {
|
|||
|
if (!is_periodic_advertising_sync_transfer_sender_supported_) {
|
|||
|
LOG_WARN("PAST sender not supported on this device");
|
|||
|
return;
|
|||
|
}
|
|||
|
periodic_sync_manager_.CancelCreateSync(sid, address);
|
|||
|
}
|
|||
|
|
|||
|
void transfer_sync(const Address& address, uint16_t service_data, uint16_t sync_handle, int pa_source) {
|
|||
|
if (!is_periodic_advertising_sync_transfer_sender_supported_) {
|
|||
|
LOG_WARN("PAST sender not supported on this device");
|
|||
|
int status = static_cast<int>(ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
|
|||
|
scanning_callbacks_->OnPeriodicSyncTransferred(pa_source, status, address);
|
|||
|
return;
|
|||
|
}
|
|||
|
uint16_t connection_handle = acl_manager_->HACK_GetLeHandle(address);
|
|||
|
if (connection_handle == 0xFFFF) {
|
|||
|
LOG_ERROR("[PAST]: Invalid connection handle or no LE ACL link");
|
|||
|
int status = static_cast<int>(ErrorCode::UNKNOWN_CONNECTION);
|
|||
|
scanning_callbacks_->OnPeriodicSyncTransferred(pa_source, status, address);
|
|||
|
return;
|
|||
|
}
|
|||
|
periodic_sync_manager_.TransferSync(address, service_data, sync_handle, pa_source, connection_handle);
|
|||
|
}
|
|||
|
|
|||
|
void transfer_set_info(const Address& address, uint16_t service_data, uint8_t adv_handle, int pa_source) {
|
|||
|
if (!is_periodic_advertising_sync_transfer_sender_supported_) {
|
|||
|
LOG_WARN("PAST sender not supported on this device");
|
|||
|
int status = static_cast<int>(ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
|
|||
|
scanning_callbacks_->OnPeriodicSyncTransferred(pa_source, status, address);
|
|||
|
return;
|
|||
|
}
|
|||
|
uint16_t connection_handle = acl_manager_->HACK_GetLeHandle(address);
|
|||
|
if (connection_handle == 0xFFFF) {
|
|||
|
LOG_ERROR("[PAST]:Invalid connection handle or no LE ACL link");
|
|||
|
int status = static_cast<int>(ErrorCode::UNKNOWN_CONNECTION);
|
|||
|
scanning_callbacks_->OnPeriodicSyncTransferred(pa_source, status, address);
|
|||
|
return;
|
|||
|
}
|
|||
|
periodic_sync_manager_.SyncSetInfo(address, service_data, adv_handle, pa_source, connection_handle);
|
|||
|
}
|
|||
|
|
|||
|
void sync_tx_parameters(const Address& address, uint8_t mode, uint16_t skip, uint16_t timeout, int reg_id) {
|
|||
|
if (!is_periodic_advertising_sync_transfer_sender_supported_) {
|
|||
|
LOG_WARN("PAST sender not supported on this device");
|
|||
|
int status = static_cast<int>(ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
|
|||
|
AddressWithType address_with_type(address, AddressType::RANDOM_DEVICE_ADDRESS);
|
|||
|
scanning_callbacks_->OnPeriodicSyncStarted(reg_id, status, -1, -1, address_with_type, 0, 0);
|
|||
|
return;
|
|||
|
}
|
|||
|
periodic_sync_manager_.SyncTxParameters(address, mode, skip, timeout, reg_id);
|
|||
|
}
|
|||
|
|
|||
|
void track_advertiser(uint8_t filter_index, ScannerId scanner_id) {
|
|||
|
if (total_num_of_advt_tracked_ <= 0) {
|
|||
|
LOG_WARN("advertisement tracking is not supported");
|
|||
|
AdvertisingFilterOnFoundOnLostInfo on_found_on_lost_info = {};
|
|||
|
on_found_on_lost_info.scanner_id = scanner_id;
|
|||
|
on_found_on_lost_info.advertiser_info_present = AdvtInfoPresent::NO_ADVT_INFO_PRESENT;
|
|||
|
scanning_callbacks_->OnTrackAdvFoundLost(on_found_on_lost_info);
|
|||
|
return;
|
|||
|
} else if (tracker_id_map_.size() >= total_num_of_advt_tracked_) {
|
|||
|
AdvertisingFilterOnFoundOnLostInfo on_found_on_lost_info = {};
|
|||
|
on_found_on_lost_info.scanner_id = scanner_id;
|
|||
|
on_found_on_lost_info.advertiser_info_present = AdvtInfoPresent::NO_ADVT_INFO_PRESENT;
|
|||
|
scanning_callbacks_->OnTrackAdvFoundLost(on_found_on_lost_info);
|
|||
|
return;
|
|||
|
}
|
|||
|
LOG_INFO("track_advertiser scanner_id %d, filter_index %d", (uint16_t)scanner_id, (uint16_t)filter_index);
|
|||
|
tracker_id_map_[filter_index] = scanner_id;
|
|||
|
}
|
|||
|
|
|||
|
void register_scanning_callback(ScanningCallback* scanning_callbacks) {
|
|||
|
scanning_callbacks_ = scanning_callbacks;
|
|||
|
periodic_sync_manager_.SetScanningCallback(scanning_callbacks_);
|
|||
|
}
|
|||
|
|
|||
|
bool is_ad_type_filter_supported() {
|
|||
|
return is_ad_type_filter_supported_;
|
|||
|
}
|
|||
|
|
|||
|
void on_set_scan_parameter_complete(CommandCompleteView view) {
|
|||
|
switch (view.GetCommandOpCode()) {
|
|||
|
case (OpCode::LE_SET_SCAN_PARAMETERS): {
|
|||
|
auto status_view = LeSetScanParametersCompleteView::Create(view);
|
|||
|
ASSERT(status_view.IsValid());
|
|||
|
if (status_view.GetStatus() != ErrorCode::SUCCESS) {
|
|||
|
LOG_INFO(
|
|||
|
"Receive set scan parameter complete with error code %s", ErrorCodeText(status_view.GetStatus()).c_str());
|
|||
|
}
|
|||
|
} break;
|
|||
|
case (OpCode::LE_EXTENDED_SCAN_PARAMS): {
|
|||
|
auto status_view = LeExtendedScanParamsCompleteView::Create(view);
|
|||
|
ASSERT(status_view.IsValid());
|
|||
|
if (status_view.GetStatus() != ErrorCode::SUCCESS) {
|
|||
|
LOG_INFO(
|
|||
|
"Receive extended scan parameter complete with error code %s",
|
|||
|
ErrorCodeText(status_view.GetStatus()).c_str());
|
|||
|
}
|
|||
|
} break;
|
|||
|
case (OpCode::LE_SET_EXTENDED_SCAN_PARAMETERS): {
|
|||
|
auto status_view = LeSetExtendedScanParametersCompleteView::Create(view);
|
|||
|
ASSERT(status_view.IsValid());
|
|||
|
if (status_view.GetStatus() != ErrorCode::SUCCESS) {
|
|||
|
LOG_INFO(
|
|||
|
"Receive set extended scan parameter complete with error code %s",
|
|||
|
ErrorCodeText(status_view.GetStatus()).c_str());
|
|||
|
}
|
|||
|
} break;
|
|||
|
default:
|
|||
|
LOG_ALWAYS_FATAL("Unhandled event %s", OpCodeText(view.GetCommandOpCode()).c_str());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void on_advertising_filter_complete(CommandCompleteView view) {
|
|||
|
ASSERT(view.IsValid());
|
|||
|
auto status_view = LeAdvFilterCompleteView::Create(view);
|
|||
|
ASSERT(status_view.IsValid());
|
|||
|
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());
|
|||
|
}
|
|||
|
|
|||
|
ApcfOpcode apcf_opcode = status_view.GetApcfOpcode();
|
|||
|
switch (apcf_opcode) {
|
|||
|
case ApcfOpcode::ENABLE: {
|
|||
|
auto complete_view = LeAdvFilterEnableCompleteView::Create(status_view);
|
|||
|
ASSERT(complete_view.IsValid());
|
|||
|
scanning_callbacks_->OnFilterEnable(complete_view.GetApcfEnable(), (uint8_t)complete_view.GetStatus());
|
|||
|
} break;
|
|||
|
case ApcfOpcode::SET_FILTERING_PARAMETERS: {
|
|||
|
auto complete_view = LeAdvFilterSetFilteringParametersCompleteView::Create(status_view);
|
|||
|
ASSERT(complete_view.IsValid());
|
|||
|
scanning_callbacks_->OnFilterParamSetup(
|
|||
|
complete_view.GetApcfAvailableSpaces(), complete_view.GetApcfAction(), (uint8_t)complete_view.GetStatus());
|
|||
|
} break;
|
|||
|
case ApcfOpcode::BROADCASTER_ADDRESS: {
|
|||
|
auto complete_view = LeAdvFilterBroadcasterAddressCompleteView::Create(status_view);
|
|||
|
ASSERT(complete_view.IsValid());
|
|||
|
scanning_callbacks_->OnFilterConfigCallback(
|
|||
|
ApcfFilterType::BROADCASTER_ADDRESS,
|
|||
|
complete_view.GetApcfAvailableSpaces(),
|
|||
|
complete_view.GetApcfAction(),
|
|||
|
(uint8_t)complete_view.GetStatus());
|
|||
|
} break;
|
|||
|
case ApcfOpcode::SERVICE_UUID: {
|
|||
|
auto complete_view = LeAdvFilterServiceUuidCompleteView::Create(status_view);
|
|||
|
ASSERT(complete_view.IsValid());
|
|||
|
scanning_callbacks_->OnFilterConfigCallback(
|
|||
|
ApcfFilterType::SERVICE_UUID,
|
|||
|
complete_view.GetApcfAvailableSpaces(),
|
|||
|
complete_view.GetApcfAction(),
|
|||
|
(uint8_t)complete_view.GetStatus());
|
|||
|
} break;
|
|||
|
case ApcfOpcode::SERVICE_SOLICITATION_UUID: {
|
|||
|
auto complete_view = LeAdvFilterSolicitationUuidCompleteView::Create(status_view);
|
|||
|
ASSERT(complete_view.IsValid());
|
|||
|
scanning_callbacks_->OnFilterConfigCallback(
|
|||
|
ApcfFilterType::SERVICE_SOLICITATION_UUID,
|
|||
|
complete_view.GetApcfAvailableSpaces(),
|
|||
|
complete_view.GetApcfAction(),
|
|||
|
(uint8_t)complete_view.GetStatus());
|
|||
|
} break;
|
|||
|
case ApcfOpcode::LOCAL_NAME: {
|
|||
|
auto complete_view = LeAdvFilterLocalNameCompleteView::Create(status_view);
|
|||
|
ASSERT(complete_view.IsValid());
|
|||
|
scanning_callbacks_->OnFilterConfigCallback(
|
|||
|
ApcfFilterType::LOCAL_NAME,
|
|||
|
complete_view.GetApcfAvailableSpaces(),
|
|||
|
complete_view.GetApcfAction(),
|
|||
|
(uint8_t)complete_view.GetStatus());
|
|||
|
} break;
|
|||
|
case ApcfOpcode::MANUFACTURER_DATA: {
|
|||
|
auto complete_view = LeAdvFilterManufacturerDataCompleteView::Create(status_view);
|
|||
|
ASSERT(complete_view.IsValid());
|
|||
|
scanning_callbacks_->OnFilterConfigCallback(
|
|||
|
ApcfFilterType::MANUFACTURER_DATA,
|
|||
|
complete_view.GetApcfAvailableSpaces(),
|
|||
|
complete_view.GetApcfAction(),
|
|||
|
(uint8_t)complete_view.GetStatus());
|
|||
|
} break;
|
|||
|
case ApcfOpcode::SERVICE_DATA: {
|
|||
|
auto complete_view = LeAdvFilterServiceDataCompleteView::Create(status_view);
|
|||
|
ASSERT(complete_view.IsValid());
|
|||
|
scanning_callbacks_->OnFilterConfigCallback(
|
|||
|
ApcfFilterType::SERVICE_DATA,
|
|||
|
complete_view.GetApcfAvailableSpaces(),
|
|||
|
complete_view.GetApcfAction(),
|
|||
|
(uint8_t)complete_view.GetStatus());
|
|||
|
} break;
|
|||
|
case ApcfOpcode::AD_TYPE: {
|
|||
|
auto complete_view = LeAdvFilterADTypeCompleteView::Create(status_view);
|
|||
|
ASSERT(complete_view.IsValid());
|
|||
|
scanning_callbacks_->OnFilterConfigCallback(
|
|||
|
ApcfFilterType::AD_TYPE,
|
|||
|
complete_view.GetApcfAvailableSpaces(),
|
|||
|
complete_view.GetApcfAction(),
|
|||
|
(uint8_t)complete_view.GetStatus());
|
|||
|
} break;
|
|||
|
default:
|
|||
|
LOG_WARN("Unexpected event type %s", OpCodeText(view.GetCommandOpCode()).c_str());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void on_apcf_read_extended_features_complete(CommandCompleteView view) {
|
|||
|
ASSERT(view.IsValid());
|
|||
|
auto status_view = LeAdvFilterCompleteView::Create(view);
|
|||
|
if (!status_view.IsValid()) {
|
|||
|
LOG_WARN("Can not get valid LeAdvFilterCompleteView, return");
|
|||
|
return;
|
|||
|
}
|
|||
|
if (status_view.GetStatus() != ErrorCode::SUCCESS) {
|
|||
|
LOG_WARN(
|
|||
|
"Got a Command complete %s, status %s",
|
|||
|
OpCodeText(view.GetCommandOpCode()).c_str(),
|
|||
|
ErrorCodeText(status_view.GetStatus()).c_str());
|
|||
|
return;
|
|||
|
}
|
|||
|
auto complete_view = LeAdvFilterReadExtendedFeaturesCompleteView::Create(status_view);
|
|||
|
ASSERT(complete_view.IsValid());
|
|||
|
is_ad_type_filter_supported_ = complete_view.GetAdTypeFilter() == 1;
|
|||
|
LOG_INFO("set is_ad_type_filter_supported_ to %d", is_ad_type_filter_supported_);
|
|||
|
}
|
|||
|
|
|||
|
void on_batch_scan_complete(CommandCompleteView view) {
|
|||
|
ASSERT(view.IsValid());
|
|||
|
auto status_view = LeBatchScanCompleteView::Create(view);
|
|||
|
ASSERT(status_view.IsValid());
|
|||
|
if (status_view.GetStatus() != ErrorCode::SUCCESS) {
|
|||
|
LOG_INFO(
|
|||
|
"Got a Command complete %s, status %s, batch_scan_opcode %s",
|
|||
|
OpCodeText(view.GetCommandOpCode()).c_str(),
|
|||
|
ErrorCodeText(status_view.GetStatus()).c_str(),
|
|||
|
BatchScanOpcodeText(status_view.GetBatchScanOpcode()).c_str());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void on_batch_scan_enable_complete(CommandCompleteView view) {
|
|||
|
ASSERT(view.IsValid());
|
|||
|
auto status_view = LeBatchScanCompleteView::Create(view);
|
|||
|
ASSERT(status_view.IsValid());
|
|||
|
auto complete_view = LeBatchScanEnableCompleteView::Create(status_view);
|
|||
|
ASSERT(complete_view.IsValid());
|
|||
|
if (status_view.GetStatus() != ErrorCode::SUCCESS) {
|
|||
|
LOG_INFO("Got batch scan enable complete, status %s", ErrorCodeText(status_view.GetStatus()).c_str());
|
|||
|
batch_scan_config_.current_state = BatchScanState::ERROR_STATE;
|
|||
|
} else {
|
|||
|
batch_scan_config_.current_state = BatchScanState::ENABLED_STATE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void on_batch_scan_disable_complete(CommandCompleteView view) {
|
|||
|
ASSERT(view.IsValid());
|
|||
|
auto status_view = LeBatchScanCompleteView::Create(view);
|
|||
|
ASSERT(status_view.IsValid());
|
|||
|
auto complete_view = LeBatchScanSetScanParametersCompleteView::Create(status_view);
|
|||
|
ASSERT(complete_view.IsValid());
|
|||
|
ASSERT(status_view.GetStatus() == ErrorCode::SUCCESS);
|
|||
|
batch_scan_config_.current_state = BatchScanState::DISABLED_STATE;
|
|||
|
}
|
|||
|
|
|||
|
void on_batch_scan_read_result_complete(
|
|||
|
ScannerId scanner_id, uint16_t total_num_of_records, CommandCompleteView view) {
|
|||
|
ASSERT(view.IsValid());
|
|||
|
auto status_view = LeBatchScanCompleteView::Create(view);
|
|||
|
ASSERT(status_view.IsValid());
|
|||
|
auto complete_view = LeBatchScanReadResultParametersCompleteRawView::Create(status_view);
|
|||
|
ASSERT(complete_view.IsValid());
|
|||
|
if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
|
|||
|
LOG_INFO("Got batch scan read result complete, status %s", ErrorCodeText(status_view.GetStatus()).c_str());
|
|||
|
}
|
|||
|
uint8_t num_of_records = complete_view.GetNumOfRecords();
|
|||
|
auto report_format = complete_view.GetBatchScanDataRead();
|
|||
|
if (num_of_records == 0) {
|
|||
|
scanning_callbacks_->OnBatchScanReports(
|
|||
|
scanner_id, 0x00, (int)report_format, total_num_of_records, batch_scan_result_cache_[scanner_id]);
|
|||
|
batch_scan_result_cache_.erase(scanner_id);
|
|||
|
} else {
|
|||
|
auto raw_data = complete_view.GetRawData();
|
|||
|
batch_scan_result_cache_[scanner_id].insert(
|
|||
|
batch_scan_result_cache_[scanner_id].end(), raw_data.begin(), raw_data.end());
|
|||
|
total_num_of_records += num_of_records;
|
|||
|
batch_scan_read_results(scanner_id, total_num_of_records, static_cast<BatchScanMode>(report_format));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void on_storage_threshold_breach(VendorSpecificEventView event) {
|
|||
|
if (batch_scan_config_.ref_value == kInvalidScannerId) {
|
|||
|
LOG_WARN("storage threshold was not set !!");
|
|||
|
return;
|
|||
|
}
|
|||
|
scanning_callbacks_->OnBatchScanThresholdCrossed(static_cast<int>(batch_scan_config_.ref_value));
|
|||
|
}
|
|||
|
|
|||
|
void on_advertisement_tracking(VendorSpecificEventView event) {
|
|||
|
auto view = LEAdvertisementTrackingEventView::Create(event);
|
|||
|
ASSERT(view.IsValid());
|
|||
|
uint8_t filter_index = view.GetApcfFilterIndex();
|
|||
|
if (tracker_id_map_.find(filter_index) == tracker_id_map_.end()) {
|
|||
|
LOG_WARN("Advertisement track for filter_index %d is not register", (uint16_t)filter_index);
|
|||
|
return;
|
|||
|
}
|
|||
|
AdvertisingFilterOnFoundOnLostInfo on_found_on_lost_info = {};
|
|||
|
on_found_on_lost_info.scanner_id = tracker_id_map_[filter_index];
|
|||
|
on_found_on_lost_info.filter_index = filter_index;
|
|||
|
on_found_on_lost_info.advertiser_state = view.GetAdvertiserState();
|
|||
|
on_found_on_lost_info.advertiser_address = view.GetAdvertiserAddress();
|
|||
|
on_found_on_lost_info.advertiser_address_type = view.GetAdvertiserAddressType();
|
|||
|
on_found_on_lost_info.advertiser_info_present = view.GetAdvtInfoPresent();
|
|||
|
/* Extract the adv info details */
|
|||
|
if (on_found_on_lost_info.advertiser_info_present == AdvtInfoPresent::ADVT_INFO_PRESENT) {
|
|||
|
auto info_view = LEAdvertisementTrackingWithInfoEventView::Create(view);
|
|||
|
ASSERT(info_view.IsValid());
|
|||
|
on_found_on_lost_info.tx_power = info_view.GetTxPower();
|
|||
|
on_found_on_lost_info.rssi = info_view.GetRssi();
|
|||
|
on_found_on_lost_info.time_stamp = info_view.GetTimestamp();
|
|||
|
auto adv_data = info_view.GetAdvPacket();
|
|||
|
on_found_on_lost_info.adv_packet.reserve(adv_data.size());
|
|||
|
on_found_on_lost_info.adv_packet.insert(on_found_on_lost_info.adv_packet.end(), adv_data.begin(), adv_data.end());
|
|||
|
auto scan_rsp_data = info_view.GetScanResponse();
|
|||
|
on_found_on_lost_info.scan_response.reserve(scan_rsp_data.size());
|
|||
|
on_found_on_lost_info.scan_response.insert(
|
|||
|
on_found_on_lost_info.scan_response.end(), scan_rsp_data.begin(), scan_rsp_data.end());
|
|||
|
}
|
|||
|
scanning_callbacks_->OnTrackAdvFoundLost(on_found_on_lost_info);
|
|||
|
}
|
|||
|
|
|||
|
void OnPause() override {
|
|||
|
if (!address_manager_registered_) {
|
|||
|
LOG_WARN("Unregistered!");
|
|||
|
return;
|
|||
|
}
|
|||
|
paused_ = true;
|
|||
|
scan_on_resume_ = is_scanning_;
|
|||
|
stop_scan();
|
|||
|
ack_pause();
|
|||
|
}
|
|||
|
|
|||
|
void ack_pause() {
|
|||
|
le_address_manager_->AckPause(this);
|
|||
|
}
|
|||
|
|
|||
|
void OnResume() override {
|
|||
|
if (!address_manager_registered_) {
|
|||
|
LOG_WARN("Unregistered!");
|
|||
|
return;
|
|||
|
}
|
|||
|
paused_ = false;
|
|||
|
if (scan_on_resume_ == true) {
|
|||
|
start_scan();
|
|||
|
}
|
|||
|
le_address_manager_->AckResume(this);
|
|||
|
}
|
|||
|
|
|||
|
ScanApiType api_type_;
|
|||
|
|
|||
|
Module* module_;
|
|||
|
os::Handler* module_handler_;
|
|||
|
HciLayer* hci_layer_;
|
|||
|
Controller* controller_;
|
|||
|
AclManager* acl_manager_;
|
|||
|
VendorSpecificEventManager* vendor_specific_event_manager_;
|
|||
|
storage::StorageModule* storage_module_;
|
|||
|
LeScanningInterface* le_scanning_interface_;
|
|||
|
LeAddressManager* le_address_manager_;
|
|||
|
bool address_manager_registered_ = false;
|
|||
|
NullScanningCallback null_scanning_callback_;
|
|||
|
ScanningCallback* scanning_callbacks_ = &null_scanning_callback_;
|
|||
|
PeriodicSyncManager periodic_sync_manager_{&null_scanning_callback_};
|
|||
|
std::vector<Scanner> scanners_;
|
|||
|
bool is_scanning_ = false;
|
|||
|
bool scan_on_resume_ = false;
|
|||
|
bool paused_ = false;
|
|||
|
AdvertisingCache advertising_cache_;
|
|||
|
bool is_filter_supported_ = false;
|
|||
|
bool is_ad_type_filter_supported_ = false;
|
|||
|
bool is_batch_scan_supported_ = false;
|
|||
|
bool is_periodic_advertising_sync_transfer_sender_supported_ = false;
|
|||
|
|
|||
|
LeScanType le_scan_type_ = LeScanType::ACTIVE;
|
|||
|
uint32_t interval_ms_{1000};
|
|||
|
uint16_t window_ms_{1000};
|
|||
|
OwnAddressType own_address_type_{OwnAddressType::PUBLIC_DEVICE_ADDRESS};
|
|||
|
LeScanningFilterPolicy filter_policy_{LeScanningFilterPolicy::ACCEPT_ALL};
|
|||
|
BatchScanConfig batch_scan_config_;
|
|||
|
std::map<ScannerId, std::vector<uint8_t>> batch_scan_result_cache_;
|
|||
|
std::unordered_map<uint8_t, ScannerId> tracker_id_map_;
|
|||
|
uint16_t total_num_of_advt_tracked_ = 0x00;
|
|||
|
|
|||
|
static void check_status(CommandCompleteView view) {
|
|||
|
switch (view.GetCommandOpCode()) {
|
|||
|
case (OpCode::LE_SET_SCAN_ENABLE): {
|
|||
|
auto status_view = LeSetScanEnableCompleteView::Create(view);
|
|||
|
ASSERT(status_view.IsValid());
|
|||
|
LOG_ERROR(
|
|||
|
"Receive set scan enable with error code %s",
|
|||
|
ErrorCodeText(status_view.GetStatus()).c_str());
|
|||
|
} break;
|
|||
|
case (OpCode::LE_SET_EXTENDED_SCAN_ENABLE): {
|
|||
|
auto status_view = LeSetExtendedScanEnableCompleteView::Create(view);
|
|||
|
ASSERT(status_view.IsValid());
|
|||
|
LOG_ERROR(
|
|||
|
"Receive set extended scan enable with error code %s",
|
|||
|
ErrorCodeText(status_view.GetStatus()).c_str());
|
|||
|
} break;
|
|||
|
default:
|
|||
|
LOG_ALWAYS_FATAL("Unhandled event %s", OpCodeText(view.GetCommandOpCode()).c_str());
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
LeScanningManager::LeScanningManager() {
|
|||
|
pimpl_ = std::make_unique<impl>(this);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::ListDependencies(ModuleList* list) const {
|
|||
|
list->add<HciLayer>();
|
|||
|
list->add<VendorSpecificEventManager>();
|
|||
|
list->add<Controller>();
|
|||
|
list->add<AclManager>();
|
|||
|
list->add<storage::StorageModule>();
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::Start() {
|
|||
|
pimpl_->start(
|
|||
|
GetHandler(),
|
|||
|
GetDependency<HciLayer>(),
|
|||
|
GetDependency<Controller>(),
|
|||
|
GetDependency<AclManager>(),
|
|||
|
GetDependency<VendorSpecificEventManager>(),
|
|||
|
GetDependency<storage::StorageModule>());
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::Stop() {
|
|||
|
pimpl_->stop();
|
|||
|
pimpl_.reset();
|
|||
|
}
|
|||
|
|
|||
|
std::string LeScanningManager::ToString() const {
|
|||
|
return "Le Scanning Manager";
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::RegisterScanner(Uuid app_uuid) {
|
|||
|
CallOn(pimpl_.get(), &impl::register_scanner, app_uuid);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::Unregister(ScannerId scanner_id) {
|
|||
|
CallOn(pimpl_.get(), &impl::unregister_scanner, scanner_id);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::Scan(bool start) {
|
|||
|
CallOn(pimpl_.get(), &impl::scan, start);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::SetScanParameters(
|
|||
|
ScannerId scanner_id, LeScanType scan_type, uint16_t scan_interval, uint16_t scan_window) {
|
|||
|
CallOn(pimpl_.get(), &impl::set_scan_parameters, scanner_id, scan_type, scan_interval, scan_window);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::ScanFilterEnable(bool enable) {
|
|||
|
CallOn(pimpl_.get(), &impl::scan_filter_enable, enable);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::ScanFilterParameterSetup(
|
|||
|
ApcfAction action, uint8_t filter_index, AdvertisingFilterParameter advertising_filter_parameter) {
|
|||
|
CallOn(pimpl_.get(), &impl::scan_filter_parameter_setup, action, filter_index, advertising_filter_parameter);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::ScanFilterAdd(
|
|||
|
uint8_t filter_index, std::vector<AdvertisingPacketContentFilterCommand> filters) {
|
|||
|
CallOn(pimpl_.get(), &impl::scan_filter_add, filter_index, filters);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::BatchScanConifgStorage(
|
|||
|
uint8_t batch_scan_full_max,
|
|||
|
uint8_t batch_scan_truncated_max,
|
|||
|
uint8_t batch_scan_notify_threshold,
|
|||
|
ScannerId scanner_id) {
|
|||
|
CallOn(
|
|||
|
pimpl_.get(),
|
|||
|
&impl::batch_scan_set_storage_parameter,
|
|||
|
batch_scan_full_max,
|
|||
|
batch_scan_truncated_max,
|
|||
|
batch_scan_notify_threshold,
|
|||
|
scanner_id);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::BatchScanEnable(
|
|||
|
BatchScanMode scan_mode,
|
|||
|
uint32_t duty_cycle_scan_window_slots,
|
|||
|
uint32_t duty_cycle_scan_interval_slots,
|
|||
|
BatchScanDiscardRule batch_scan_discard_rule) {
|
|||
|
CallOn(
|
|||
|
pimpl_.get(),
|
|||
|
&impl::batch_scan_enable,
|
|||
|
scan_mode,
|
|||
|
duty_cycle_scan_window_slots,
|
|||
|
duty_cycle_scan_interval_slots,
|
|||
|
batch_scan_discard_rule);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::BatchScanDisable() {
|
|||
|
CallOn(pimpl_.get(), &impl::batch_scan_disable);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::BatchScanReadReport(ScannerId scanner_id, BatchScanMode scan_mode) {
|
|||
|
CallOn(pimpl_.get(), &impl::batch_scan_read_results, scanner_id, 0, scan_mode);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::StartSync(
|
|||
|
uint8_t sid, const AddressWithType& address_with_type, uint16_t skip, uint16_t timeout, int reg_id) {
|
|||
|
CallOn(pimpl_.get(), &impl::start_sync, sid, address_with_type, skip, timeout, reg_id);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::StopSync(uint16_t handle) {
|
|||
|
CallOn(pimpl_.get(), &impl::stop_sync, handle);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::CancelCreateSync(uint8_t sid, const Address& address) {
|
|||
|
CallOn(pimpl_.get(), &impl::cancel_create_sync, sid, address);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::TransferSync(
|
|||
|
const Address& address, uint16_t service_data, uint16_t sync_handle, int pa_source) {
|
|||
|
CallOn(pimpl_.get(), &impl::transfer_sync, address, service_data, sync_handle, pa_source);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::TransferSetInfo(
|
|||
|
const Address& address, uint16_t service_data, uint8_t adv_handle, int pa_source) {
|
|||
|
CallOn(pimpl_.get(), &impl::transfer_set_info, address, service_data, adv_handle, pa_source);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::SyncTxParameters(
|
|||
|
const Address& address, uint8_t mode, uint16_t skip, uint16_t timeout, int reg_id) {
|
|||
|
CallOn(pimpl_.get(), &impl::sync_tx_parameters, address, mode, skip, timeout, reg_id);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::TrackAdvertiser(uint8_t filter_index, ScannerId scanner_id) {
|
|||
|
CallOn(pimpl_.get(), &impl::track_advertiser, filter_index, scanner_id);
|
|||
|
}
|
|||
|
|
|||
|
void LeScanningManager::RegisterScanningCallback(ScanningCallback* scanning_callback) {
|
|||
|
CallOn(pimpl_.get(), &impl::register_scanning_callback, scanning_callback);
|
|||
|
}
|
|||
|
|
|||
|
bool LeScanningManager::IsAdTypeFilterSupported() const {
|
|||
|
return pimpl_->is_ad_type_filter_supported();
|
|||
|
}
|
|||
|
|
|||
|
} // namespace hci
|
|||
|
} // namespace bluetooth
|