void OnTrackAdvFoundLost(){};
void OnBatchScanReports(int client_if, int status, int report_format, int num_records, std::vector<uint8_t> data){};
void OnTimeout(){};
+ void OnFilterEnable(Enable enable, uint8_t status){};
+ void OnFilterParamSetup(uint8_t available_spaces, ApcfAction action, uint8_t status){};
+ void OnFilterConfigCallback(
+ ApcfFilterType filter_type, uint8_t available_spaces, ApcfAction action, uint8_t status){};
LeScanningManager* le_scanning_manager_;
os::Handler* facade_handler_;
SERVICE_DATA = 0x07,
}
+// https://source.android.com/devices/bluetooth/hci_requirements#advertising-packet-content-filter
packet LeAdvFilter : LeScanningCommand (op_code = LE_ADV_FILTER) {
apcf_opcode : ApcfOpcode,
_body_,
packet LeAdvFilterComplete : CommandComplete (command_op_code = LE_ADV_FILTER) {
status : ErrorCode,
apcf_opcode : ApcfOpcode,
+ _body_,
+}
+
+packet LeAdvFilterEnable : LeAdvFilter (apcf_opcode = ENABLE) {
+ apcf_enable : Enable,
+}
+
+packet LeAdvFilterEnableComplete : LeAdvFilterComplete (apcf_opcode = ENABLE) {
+ apcf_enable : Enable,
+}
+
+enum ApcfAction : 8 {
+ ADD = 0x00,
+ DELETE = 0x01,
+ CLEAR = 0x02,
+}
+
+enum DeliveryMode : 8 {
+ IMMEDIATE = 0x00,
+ ONFOUND = 0x01,
+ BATCHED = 0x02,
+}
+
+// Bit masks for the selected features
+enum ApcfFilterType : 8 {
+ BROADCASTER_ADDRESS = 0x00,
+ SERVICE_DATA_CHANGE = 0x01,
+ SERVICE_UUID = 0x02,
+ SERVICE_SOLICITATION_UUID = 0x03,
+ LOCAL_NAME = 0x04,
+ MANUFACTURER_DATA = 0x05,
+ SERVICE_DATA = 0x06,
+}
+
+packet LeAdvFilterSetFilteringParameters : LeAdvFilter (apcf_opcode = SET_FILTERING_PARAMETERS) {
+ apcf_action : ApcfAction,
+ _body_,
+}
+
+packet LeAdvFilterAddFilteringParameters : LeAdvFilterSetFilteringParameters (apcf_action = ADD) {
+ apcf_filter_index : 8,
+ apcf_feature_selection : 16,
+ apcf_list_logic_type : 16,
+ apcf_filter_logic_type : 8,
+ rssi_high_thresh : 8,
+ delivery_mode : DeliveryMode,
+ onfound_timeout : 16,
+ onfound_timeout_cnt : 8,
+ rssi_low_thresh : 8,
+ onlost_timeout : 16,
+ num_of_tracking_entries : 16,
+}
+
+packet LeAdvFilterDeleteFilteringParameters : LeAdvFilterSetFilteringParameters (apcf_action = DELETE) {
+ apcf_filter_index : 8,
+}
+
+packet LeAdvFilterClearFilteringParameters : LeAdvFilterSetFilteringParameters (apcf_action = CLEAR) {
+}
+
+packet LeAdvFilterSetFilteringParametersComplete : LeAdvFilterComplete (apcf_opcode = SET_FILTERING_PARAMETERS) {
+ apcf_action : ApcfAction,
+ apcf_available_spaces : 8,
+}
+
+enum ApcfApplicationAddressType : 8 {
+ PUBLIC = 0x00,
+ RANDOM = 0x01,
+ NOT_APPLICABLE = 0x02,
}
+packet LeAdvFilterBroadcasterAddress : LeAdvFilter (apcf_opcode = BROADCASTER_ADDRESS) {
+ apcf_action : ApcfAction,
+ apcf_filter_index : 8,
+ apcf_broadcaster_address : Address,
+ apcf_application_address_type : ApcfApplicationAddressType,
+}
+
+packet LeAdvFilterClearBroadcasterAddress : LeAdvFilter (apcf_opcode = BROADCASTER_ADDRESS) {
+ _fixed_ = 0x02 : 8,
+ apcf_filter_index : 8,
+}
+
+packet LeAdvFilterBroadcasterAddressComplete : LeAdvFilterComplete (apcf_opcode = BROADCASTER_ADDRESS) {
+ apcf_action : ApcfAction,
+ apcf_available_spaces : 8,
+}
+
+
+packet LeAdvFilterServiceUuid : LeAdvFilter (apcf_opcode = SERVICE_UUID) {
+ apcf_action : ApcfAction,
+ apcf_filter_index : 8,
+ acpf_uuid_data : 8[],
+}
+
+packet LeAdvFilterServiceUuidComplete : LeAdvFilterComplete (apcf_opcode = SERVICE_UUID) {
+ apcf_action : ApcfAction,
+ apcf_available_spaces : 8,
+}
+
+packet LeAdvFilterSolicitationUuid : LeAdvFilter (apcf_opcode = SERVICE_SOLICITATION_UUID) {
+ apcf_action : ApcfAction,
+ apcf_filter_index : 8,
+ acpf_uuid_data : 8[],
+}
+
+packet LeAdvFilterSolicitationUuidComplete : LeAdvFilterComplete (apcf_opcode = SERVICE_SOLICITATION_UUID) {
+ apcf_action : ApcfAction,
+ apcf_available_spaces : 8,
+}
+
+packet LeAdvFilterLocalName : LeAdvFilter (apcf_opcode = LOCAL_NAME) {
+ apcf_action : ApcfAction,
+ apcf_filter_index : 8,
+ apcf_local_name : 8[],
+}
+
+packet LeAdvFilterLocalNameComplete : LeAdvFilterComplete (apcf_opcode = LOCAL_NAME) {
+ apcf_action : ApcfAction,
+ apcf_available_spaces : 8,
+}
+
+packet LeAdvFilterManufacturerData : LeAdvFilter (apcf_opcode = MANUFACTURER_DATA) {
+ apcf_action : ApcfAction,
+ apcf_filter_index : 8,
+ apcf_manufacturer_data : 8[],
+}
+
+packet LeAdvFilterManufacturerDataComplete : LeAdvFilterComplete (apcf_opcode = MANUFACTURER_DATA) {
+ apcf_action : ApcfAction,
+ apcf_available_spaces : 8,
+}
+
+packet LeAdvFilterServiceData : LeAdvFilter (apcf_opcode = SERVICE_DATA) {
+ apcf_action : ApcfAction,
+ apcf_filter_index : 8,
+ apcf_service_data : 8[],
+}
+
+packet LeAdvFilterServiceDataComplete : LeAdvFilterComplete (apcf_opcode = SERVICE_DATA) {
+ apcf_action : ApcfAction,
+ apcf_available_spaces : 8,
+}
+
+
packet LeTrackAdv : VendorCommand (op_code = LE_TRACK_ADV) {
_payload_, // placeholder (unimplemented)
}
} else {
api_type_ = ScanApiType::LEGACY;
}
+ is_filter_support_ = controller_->IsSupported(OpCode::LE_ADV_FILTER);
scanners_ = std::vector<Scanner>(kMaxAppNum + 1);
for (size_t i = 0; i < scanners_.size(); i++) {
scanners_[i].app_uuid = Uuid::kEmpty;
window_ms_ = scan_window;
}
+ void scan_filter_enable(bool enable) {
+ if (!is_filter_support_) {
+ 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));
+ }
+
+ void scan_filter_parameter_setup(
+ ApcfAction action, uint8_t filter_index, AdvertisingFilterParameter advertising_filter_parameter) {
+ if (!is_filter_support_) {
+ LOG_WARN("Advertising filter is not supported");
+ return;
+ }
+
+ 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_thres,
+ 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:
+ le_scanning_interface_->EnqueueCommand(
+ LeAdvFilterDeleteFilteringParametersBuilder::Create(filter_index),
+ module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
+ break;
+ case ApcfAction::CLEAR:
+ le_scanning_interface_->EnqueueCommand(
+ LeAdvFilterClearFilteringParametersBuilder::Create(),
+ module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
+ 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_support_) {
+ 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);
+ 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;
+ }
+ default:
+ LOG_ERROR("Unknown filter type: %d", (uint16_t)filter.filter_type);
+ break;
+ }
+ }
+ }
+
+ void update_address_filter(
+ ApcfAction action, uint8_t filter_index, Address address, ApcfApplicationAddressType address_type) {
+ if (action != ApcfAction::CLEAR) {
+ le_scanning_interface_->EnqueueCommand(
+ LeAdvFilterBroadcasterAddressBuilder::Create(action, filter_index, address, address_type),
+ module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
+ } else {
+ le_scanning_interface_->EnqueueCommand(
+ LeAdvFilterClearBroadcasterAddressBuilder::Create(filter_index),
+ module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
+ }
+ }
+
+ 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 register_scanning_callback(ScanningCallback* scanning_callbacks) {
scanning_callbacks_ = scanning_callbacks;
}
+ 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;
+ default:
+ LOG_WARN("Unexpected event type %s", OpCodeText(view.GetCommandOpCode()).c_str());
+ }
+ }
+
void OnPause() override {
paused_ = true;
scan_on_resume_ = is_scanning_;
bool scan_on_resume_ = false;
bool paused_ = false;
AdvertisingCache advertising_cache_;
+ bool is_filter_support_ = false;
LeScanType le_scan_type_ = LeScanType::ACTIVE;
uint32_t interval_ms_{1000};
CallOn(pimpl_.get(), &impl::set_scan_parameters, 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::RegisterScanningCallback(ScanningCallback* scanning_callback) {
CallOn(pimpl_.get(), &impl::register_scanning_callback, scanning_callback);
}
#include <memory>
#include "common/callback.h"
+#include "hci/address_with_type.h"
#include "hci/hci_packets.h"
#include "hci/uuid.h"
#include "module.h"
virtual void OnBatchScanReports(
int client_if, int status, int report_format, int num_records, std::vector<uint8_t> data) = 0;
virtual void OnTimeout() = 0;
+ virtual void OnFilterEnable(Enable enable, uint8_t status) = 0;
+ virtual void OnFilterParamSetup(uint8_t available_spaces, ApcfAction action, uint8_t status) = 0;
+ virtual void OnFilterConfigCallback(
+ ApcfFilterType filter_type, uint8_t available_spaces, ApcfAction action, uint8_t status) = 0;
+};
+
+class AdvertisingPacketContentFilterCommand {
+ public:
+ ApcfFilterType filter_type;
+ Address address;
+ ApcfApplicationAddressType application_address_type;
+ Uuid uuid;
+ Uuid uuid_mask;
+ std::vector<uint8_t> name;
+ uint16_t company;
+ uint16_t company_mask;
+ std::vector<uint8_t> data;
+ std::vector<uint8_t> data_mask;
+};
+
+class AdvertisingFilterParameter {
+ public:
+ uint16_t feature_selection;
+ uint16_t list_logic_type;
+ uint8_t filter_logic_type;
+ uint8_t rssi_high_thresh;
+ DeliveryMode delivery_mode;
+ uint16_t onfound_timeout;
+ uint8_t onfound_timeout_cnt;
+ uint8_t rssi_low_thres;
+ uint16_t onlost_timeout;
+ uint16_t num_of_tracking_entries;
};
class LeScanningManager : public bluetooth::Module {
void SetScanParameters(LeScanType scan_type, uint16_t scan_interval, uint16_t scan_window);
+ /* Scan filter */
+ void ScanFilterEnable(bool enable);
+
+ void ScanFilterParameterSetup(
+ ApcfAction action, uint8_t filter_index, AdvertisingFilterParameter advertising_filter_parameter);
+
+ void ScanFilterAdd(uint8_t filter_index, std::vector<AdvertisingPacketContentFilterCommand> filters);
+
void RegisterScanningCallback(ScanningCallback* scanning_callback);
static const ModuleFactory Factory;
test_hci_layer_ = new TestHciLayer; // Ownership is transferred to registry
test_controller_ = new TestController;
test_controller_->AddSupported(param_opcode_);
+ if (is_filter_support_) {
+ test_controller_->AddSupported(OpCode::LE_ADV_FILTER);
+ }
test_acl_manager_ = new TestAclManager;
fake_registry_.InjectTestModule(&HciLayer::Factory, test_hci_layer_);
fake_registry_.InjectTestModule(&Controller::Factory, test_controller_);
fake_registry_.InjectTestModule(&AclManager::Factory, test_acl_manager_);
+ client_handler_ = fake_registry_.GetTestModuleHandler(&HciLayer::Factory);
std::future<void> config_future = test_hci_layer_->GetCommandFuture();
fake_registry_.Start<LeScanningManager>(&thread_);
le_scanning_manager =
test_hci_layer_->IncomingEvent(LeSetScanParametersCompleteBuilder::Create(1, ErrorCode::SUCCESS));
}
+ void sync_client_handler() {
+ std::promise<void> promise;
+ auto future = promise.get_future();
+ client_handler_->Call(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+ auto future_status = future.wait_for(std::chrono::seconds(1));
+ ASSERT_EQ(future_status, std::future_status::ready);
+ }
+
TestModuleRegistry fake_registry_;
TestHciLayer* test_hci_layer_ = nullptr;
TestController* test_controller_ = nullptr;
(int client_if, int status, int report_format, int num_records, std::vector<uint8_t> data),
(override));
MOCK_METHOD(void, OnTimeout, (), (override));
+ MOCK_METHOD(void, OnFilterEnable, (Enable enable, uint8_t status), (override));
+ MOCK_METHOD(void, OnFilterParamSetup, (uint8_t available_spaces, ApcfAction action, uint8_t status), (override));
+ MOCK_METHOD(
+ void,
+ OnFilterConfigCallback,
+ (ApcfFilterType filter_type, uint8_t available_spaces, ApcfAction action, uint8_t status),
+ (override));
} mock_callbacks_;
OpCode param_opcode_{OpCode::LE_SET_ADVERTISING_PARAMETERS};
+ bool is_filter_support_ = false;
};
class LeAndroidHciScanningManagerTest : public LeScanningManagerTest {
protected:
void SetUp() override {
param_opcode_ = OpCode::LE_EXTENDED_SCAN_PARAMS;
+ is_filter_support_ = true;
LeScanningManagerTest::SetUp();
+ test_controller_->AddSupported(OpCode::LE_ADV_FILTER);
}
void HandleConfiguration() override {
test_hci_layer_->IncomingLeMetaEvent(LeAdvertisingReportBuilder::Create({report}));
}
+TEST_F(LeAndroidHciScanningManagerTest, scan_filter_enable_test) {
+ auto next_command_future = test_hci_layer_->GetCommandFuture();
+ le_scanning_manager->ScanFilterEnable(true);
+ auto result = next_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100)));
+ ASSERT_EQ(std::future_status::ready, result);
+ EXPECT_CALL(mock_callbacks_, OnFilterEnable);
+ test_hci_layer_->IncomingEvent(
+ LeAdvFilterEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS, Enable::ENABLED));
+ sync_client_handler();
+}
+
+TEST_F(LeAndroidHciScanningManagerTest, scan_filter_parameter_test) {
+ auto next_command_future = test_hci_layer_->GetCommandFuture();
+ AdvertisingFilterParameter advertising_filter_parameter{};
+ advertising_filter_parameter.delivery_mode = DeliveryMode::IMMEDIATE;
+ le_scanning_manager->ScanFilterParameterSetup(ApcfAction::ADD, 0x01, advertising_filter_parameter);
+ auto result = next_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100)));
+ ASSERT_EQ(std::future_status::ready, result);
+ EXPECT_CALL(mock_callbacks_, OnFilterParamSetup);
+ test_hci_layer_->IncomingEvent(
+ LeAdvFilterSetFilteringParametersCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS, ApcfAction::ADD, 0x0a));
+ sync_client_handler();
+}
+
+TEST_F(LeAndroidHciScanningManagerTest, scan_filter_add_test) {
+ auto next_command_future = test_hci_layer_->GetCommandFuture();
+ std::vector<AdvertisingPacketContentFilterCommand> filters = {};
+ AdvertisingPacketContentFilterCommand filter{};
+ filter.filter_type = ApcfFilterType::BROADCASTER_ADDRESS;
+ filter.address = Address::kEmpty;
+ filter.application_address_type = ApcfApplicationAddressType::RANDOM;
+ filters.push_back(filter);
+ le_scanning_manager->ScanFilterAdd(0x01, filters);
+ EXPECT_CALL(mock_callbacks_, OnFilterConfigCallback);
+ test_hci_layer_->IncomingEvent(
+ LeAdvFilterBroadcasterAddressCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS, ApcfAction::ADD, 0x0a));
+ sync_client_handler();
+}
+
TEST_F(LeExtendedScanningManagerTest, start_scan_test) {
auto next_command_future = test_hci_layer_->GetCommandFuture();
le_scanning_manager->Scan(true);
int num_records,
std::vector<uint8_t> data){};
void Btm::ScanningCallbacks::OnTimeout(){};
+void Btm::ScanningCallbacks::OnFilterEnable(bluetooth::hci::Enable enable,
+ uint8_t status){};
+void Btm::ScanningCallbacks::OnFilterParamSetup(
+ uint8_t available_spaces, bluetooth::hci::ApcfAction action,
+ uint8_t status){};
+void Btm::ScanningCallbacks::OnFilterConfigCallback(
+ bluetooth::hci::ApcfFilterType filter_type, uint8_t available_spaces,
+ bluetooth::hci::ApcfAction action, uint8_t status){};
Btm::Btm(os::Handler* handler, neighbor::InquiryModule* inquiry)
: scanning_timer_(handler), observing_timer_(handler) {
void OnBatchScanReports(int client_if, int status, int report_format,
int num_records, std::vector<uint8_t> data);
void OnTimeout();
+ void OnFilterEnable(bluetooth::hci::Enable enable, uint8_t status);
+ void OnFilterParamSetup(uint8_t available_spaces,
+ bluetooth::hci::ApcfAction action, uint8_t status);
+ void OnFilterConfigCallback(bluetooth::hci::ApcfFilterType filter_type,
+ uint8_t available_spaces,
+ bluetooth::hci::ApcfAction action,
+ uint8_t status);
};
ScanningCallbacks scanning_callbacks_;
/** Setup scan filter params */
void ScanFilterParamSetup(
- uint8_t client_if, uint8_t action, uint8_t filt_index,
+ uint8_t client_if, uint8_t action, uint8_t filter_index,
std::unique_ptr<btgatt_filt_param_setup_t> filt_param,
FilterParamSetupCallback cb) {
LOG(INFO) << __func__ << " in shim layer";
+
+ auto apcf_action = static_cast<bluetooth::hci::ApcfAction>(action);
+ bluetooth::hci::AdvertisingFilterParameter advertising_filter_parameter;
+
+ if (filt_param != nullptr) {
+ if (filt_param && filt_param->dely_mode == 1) {
+ // TODO refactor BTM_BleTrackAdvertiser
+ }
+ advertising_filter_parameter.feature_selection = filt_param->feat_seln;
+ advertising_filter_parameter.list_logic_type =
+ filt_param->list_logic_type;
+ advertising_filter_parameter.filter_logic_type =
+ filt_param->filt_logic_type;
+ advertising_filter_parameter.rssi_high_thresh =
+ filt_param->rssi_high_thres;
+ advertising_filter_parameter.delivery_mode =
+ static_cast<bluetooth::hci::DeliveryMode>(filt_param->dely_mode);
+ if (filt_param && filt_param->dely_mode == 1) {
+ advertising_filter_parameter.onfound_timeout =
+ filt_param->found_timeout;
+ advertising_filter_parameter.onfound_timeout_cnt =
+ filt_param->found_timeout_cnt;
+ advertising_filter_parameter.rssi_low_thres =
+ filt_param->rssi_low_thres;
+ advertising_filter_parameter.onlost_timeout = filt_param->lost_timeout;
+ advertising_filter_parameter.num_of_tracking_entries =
+ filt_param->num_of_tracking_entries;
+ }
+ }
+
+ bluetooth::shim::GetScanning()->ScanFilterParameterSetup(
+ apcf_action, filter_index, advertising_filter_parameter);
+ // TODO refactor callback mechanism
+ do_in_jni_thread(FROM_HERE, base::Bind(cb, 0, 0, 0));
}
/** Configure a scan filter condition */
void ScanFilterAdd(int filter_index, std::vector<ApcfCommand> filters,
FilterConfigCallback cb) {
LOG(INFO) << __func__ << " in shim layer";
+ std::vector<bluetooth::hci::AdvertisingPacketContentFilterCommand>
+ new_filters = {};
+ for (size_t i = 0; i < filters.size(); i++) {
+ bluetooth::hci::AdvertisingPacketContentFilterCommand command{};
+ if (!parse_filter_command(command, filters[i])) {
+ LOG_ERROR("invalid apcf command");
+ return;
+ }
+ new_filters.push_back(command);
+ }
+ bluetooth::shim::GetScanning()->ScanFilterAdd(filter_index, new_filters);
+ do_in_jni_thread(FROM_HERE, base::Bind(cb, 0, 0, 0, 0));
}
/** Clear all scan filter conditions for specific filter index*/
- void ScanFilterClear(int filt_index, FilterConfigCallback cb) {
+ void ScanFilterClear(int filter_index, FilterConfigCallback cb) {
LOG(INFO) << __func__ << " in shim layer";
+ // This function doesn't used in java layer
}
/** Enable / disable scan filter feature*/
void ScanFilterEnable(bool enable, EnableCallback cb) {
LOG(INFO) << __func__ << " in shim layer";
+ bluetooth::shim::GetScanning()->ScanFilterEnable(enable);
+
+ uint8_t action = enable ? 1 : 0;
+ do_in_jni_thread(FROM_HERE, base::Bind(cb, action, 0));
}
/** Sets the LE scan interval and window in units of N*0.625 msec */
auto scan_type = static_cast<bluetooth::hci::LeScanType>(0x01);
bluetooth::shim::GetScanning()->SetScanParameters(scan_type, scan_interval,
scan_window);
+ do_in_jni_thread(FROM_HERE, base::Bind(cb, 0));
}
/* Configure the batchscan storage */
int num_records, std::vector<uint8_t> data) {}
void OnTimeout() {}
+ void OnFilterEnable(bluetooth::hci::Enable enable, uint8_t status){};
+
+ void OnFilterParamSetup(uint8_t available_spaces,
+ bluetooth::hci::ApcfAction action, uint8_t status){};
+
+ void OnFilterConfigCallback(bluetooth::hci::ApcfFilterType filter_type,
+ uint8_t available_spaces,
+ bluetooth::hci::ApcfAction action,
+ uint8_t status){};
+
ScanningCallbacks* scanning_callbacks_;
+
+ private:
+ bool parse_filter_command(
+ bluetooth::hci::AdvertisingPacketContentFilterCommand&
+ advertising_packet_content_filter_command,
+ ApcfCommand apcf_command) {
+ advertising_packet_content_filter_command.filter_type =
+ static_cast<bluetooth::hci::ApcfFilterType>(apcf_command.type);
+ bluetooth::hci::Address address;
+ bluetooth::hci::Address::FromString(apcf_command.address.ToString(),
+ address);
+ advertising_packet_content_filter_command.address = address;
+ advertising_packet_content_filter_command.application_address_type =
+ static_cast<bluetooth::hci::ApcfApplicationAddressType>(
+ apcf_command.addr_type);
+
+ if (!apcf_command.uuid.IsEmpty()) {
+ uint8_t uuid_len = apcf_command.uuid.GetShortestRepresentationSize();
+ switch (uuid_len) {
+ case bluetooth::Uuid::kNumBytes16: {
+ advertising_packet_content_filter_command.uuid =
+ bluetooth::hci::Uuid::From16Bit(apcf_command.uuid.As16Bit());
+ } break;
+ case bluetooth::Uuid::kNumBytes32: {
+ advertising_packet_content_filter_command.uuid =
+ bluetooth::hci::Uuid::From32Bit(apcf_command.uuid.As32Bit());
+ } break;
+ case bluetooth::Uuid::kNumBytes128: {
+ advertising_packet_content_filter_command.uuid =
+ bluetooth::hci::Uuid::From128BitBE(
+ apcf_command.uuid.To128BitBE());
+ } break;
+ default:
+ LOG_WARN("illegal UUID length %d", (uint16_t)uuid_len);
+ return false;
+ }
+ }
+
+ if (!apcf_command.uuid_mask.IsEmpty()) {
+ uint8_t uuid_len = apcf_command.uuid.GetShortestRepresentationSize();
+ switch (uuid_len) {
+ case bluetooth::Uuid::kNumBytes16: {
+ advertising_packet_content_filter_command.uuid_mask =
+ bluetooth::hci::Uuid::From16Bit(apcf_command.uuid_mask.As16Bit());
+ } break;
+ case bluetooth::Uuid::kNumBytes32: {
+ advertising_packet_content_filter_command.uuid_mask =
+ bluetooth::hci::Uuid::From32Bit(apcf_command.uuid_mask.As32Bit());
+ } break;
+ case bluetooth::Uuid::kNumBytes128: {
+ advertising_packet_content_filter_command.uuid_mask =
+ bluetooth::hci::Uuid::From128BitBE(
+ apcf_command.uuid_mask.To128BitBE());
+ } break;
+ default:
+ LOG_WARN("illegal UUID length %d", (uint16_t)uuid_len);
+ return false;
+ }
+ }
+
+ advertising_packet_content_filter_command.name.assign(
+ apcf_command.name.begin(), apcf_command.name.end());
+ advertising_packet_content_filter_command.company = apcf_command.company;
+ advertising_packet_content_filter_command.company_mask =
+ apcf_command.company_mask;
+ advertising_packet_content_filter_command.data.assign(
+ apcf_command.data.begin(), apcf_command.data.end());
+ advertising_packet_content_filter_command.data_mask.assign(
+ apcf_command.data_mask.begin(), apcf_command.data_mask.end());
+ return true;
+ }
};
BleScannerInterfaceImpl* bt_le_scanner_instance = nullptr;