diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 00000000..37ad5c8b --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +9.0.1 diff --git a/internal/platform/implementation/linux/bluez_advertisement_monitor.cc b/internal/platform/implementation/linux/bluez_advertisement_monitor.cc index 084a8187..8a9a6097 100644 --- a/internal/platform/implementation/linux/bluez_advertisement_monitor.cc +++ b/internal/platform/implementation/linux/bluez_advertisement_monitor.cc @@ -1,7 +1,5 @@ #include "internal/platform/implementation/linux/bluez_advertisement_monitor.h" -#include - #include "internal/platform/byte_array.h" #include "internal/platform/implementation/ble.h" #include "internal/platform/implementation/linux/dbus.h" @@ -11,7 +9,7 @@ namespace nearby { namespace linux { namespace bluez { AdvertisementMonitor::AdvertisementMonitor( - sdbus::IConnection &system_bus, Uuid service_uuid, + sdbus::IConnection& system_bus, Uuid service_uuid, api::ble::TxPowerLevel tx_power_level, absl::string_view type, std::shared_ptr devices, api::ble::BleMedium::ScanCallback scan_callback) @@ -23,30 +21,38 @@ AdvertisementMonitor::AdvertisementMonitor( std::move(scan_callback.advertisement_found_cb)}) {} AdvertisementMonitor::AdvertisementMonitor( - sdbus::IConnection &system_bus, Uuid service_uuid, + sdbus::IConnection& system_bus, Uuid service_uuid, api::ble::TxPowerLevel tx_power_level, absl::string_view type, std::shared_ptr devices, api::ble::BleMedium::ScanningCallback scan_callback) - : AdaptorInterfaces(system_bus, bluez::advertisement_monitor_path( - std::string{service_uuid})), + : AdvertisementMonitor( + system_bus, + bluez::advertisement_monitor_path(std::string{service_uuid}), + service_uuid, tx_power_level, type, std::move(devices), + std::move(scan_callback)) {} + +AdvertisementMonitor::AdvertisementMonitor( + sdbus::IConnection& system_bus, sdbus::ObjectPath object_path, + Uuid service_uuid, api::ble::TxPowerLevel tx_power_level, + absl::string_view type, std::shared_ptr devices, + api::ble::BleMedium::ScanningCallback scan_callback) + : AdaptorInterfaces(system_bus, std::move(object_path)), devices_(std::move(devices)), - scan_callback_{std::move(scan_callback.advertisement_found_cb)}, - start_scanning_result_callback_( - std::move(scan_callback.start_scanning_result)), + scan_callback_(std::move(scan_callback)), type_(type), service_uuid_(service_uuid), tx_power_level_(tx_power_level) { registerAdaptor(); } -void AdvertisementMonitor::DeviceFound(const sdbus::ObjectPath &device) { +void AdvertisementMonitor::DeviceFound(const sdbus::ObjectPath& device) { devices_->cleanup_lost_peripherals(); auto peripheral = devices_->add_new_device(device); auto service_data = peripheral->ServiceData(); if (!service_data.has_value()) return; struct api::ble::BleAdvertisementData adv_data; - for (const auto &[uuid_str, data] : *service_data) { + for (const auto& [uuid_str, data] : *service_data) { auto uuid = UuidFromString(uuid_str); if (!uuid.has_value()) { LOG(ERROR) @@ -60,12 +66,15 @@ void AdvertisementMonitor::DeviceFound(const sdbus::ObjectPath &device) { adv_data.service_data.emplace(*uuid, std::string(bytes.begin(), bytes.end())); } - auto id = std::stoull(std::regex_replace(peripheral->GetMacAddress().ToString(), - std::regex("[:\\-]"), ""), nullptr, 16); + auto id = peripheral->GetMacAddress().address(); scan_callback_.advertisement_found_cb(id, adv_data); } -void AdvertisementMonitor::DeviceLost(const sdbus::ObjectPath &device) { +void AdvertisementMonitor::DeviceLost(const sdbus::ObjectPath& device) { + auto peripheral = devices_->get_device_by_path(device); + if (peripheral != nullptr) { + scan_callback_.advertisement_lost_cb(peripheral->GetMacAddress().address()); + } devices_->mark_peripheral_lost(device); } } // namespace bluez diff --git a/internal/platform/implementation/linux/bluez_advertisement_monitor.h b/internal/platform/implementation/linux/bluez_advertisement_monitor.h index 9cbbe57b..b3794eb6 100644 --- a/internal/platform/implementation/linux/bluez_advertisement_monitor.h +++ b/internal/platform/implementation/linux/bluez_advertisement_monitor.h @@ -47,16 +47,22 @@ class AdvertisementMonitor final absl::string_view type, std::shared_ptr devices, api::ble::BleMedium::ScanningCallback scan_callback); + AdvertisementMonitor(sdbus::IConnection& system_bus, + sdbus::ObjectPath object_path, Uuid service_uuid, + api::ble::TxPowerLevel tx_power_level, + absl::string_view type, + std::shared_ptr devices, + api::ble::BleMedium::ScanningCallback scan_callback); ~AdvertisementMonitor() { unregisterAdaptor(); } private: // Methods void Release() override {} void Activate() override { - LOG(INFO) <<__func__ << ": bluez advertisement monitor activated at path: " + LOG(INFO) << __func__ << ": bluez advertisement monitor activated at path: " << getObject().getObjectPath(); - if (start_scanning_result_callback_ != nullptr) { - start_scanning_result_callback_(absl::OkStatus()); + if (scan_callback_.start_scanning_result != nullptr) { + scan_callback_.start_scanning_result(absl::OkStatus()); } } @@ -66,9 +72,7 @@ class AdvertisementMonitor final // Properties std::string Type() override { return type_; }; int16_t RSSILowThreshold() override { return 127; }; - int16_t RSSIHighThreshold() override { - return 127; - } + int16_t RSSIHighThreshold() override { return 127; } uint16_t RSSISamplingPeriod() override { // The Windows implementation uses a sampling interval of 2 seconds. return 20; @@ -79,13 +83,11 @@ class AdvertisementMonitor final return {{0, 0x16, {static_cast(service_id_data[3] & 0xFF), - static_cast(service_id_data[2] & 0xFF)} - }}; + static_cast(service_id_data[2] & 0xFF)}}}; }; std::shared_ptr devices_; - api::ble::BleMedium::ScanCallback scan_callback_; - absl::AnyInvocable start_scanning_result_callback_; + api::ble::BleMedium::ScanningCallback scan_callback_; std::string type_; Uuid service_uuid_; diff --git a/sharing/linux/BUILD b/sharing/linux/BUILD index ed9f1483..c5a414fd 100644 --- a/sharing/linux/BUILD +++ b/sharing/linux/BUILD @@ -61,6 +61,19 @@ cc_binary( deps = [":fast_init"], ) +cc_test( + name = "nearby_fast_init_manager_test", + size = "small", + srcs = ["nearby_fast_init_manager_test.cc"], + deps = [ + ":fast_init", + "//internal/platform:base", + "//internal/platform/implementation:types", + "//internal/platform/implementation/linux", + "@com_google_googletest//:gtest_main", + ], +) + cc_binary( name = "nearby_sharing_cli", diff --git a/sharing/linux/nearby_fast_init_manager.cc b/sharing/linux/nearby_fast_init_manager.cc index a0a47c03..44963179 100644 --- a/sharing/linux/nearby_fast_init_manager.cc +++ b/sharing/linux/nearby_fast_init_manager.cc @@ -1,17 +1,64 @@ +#include #include +#include +#include +#include +#include +#include +#include "absl/synchronization/mutex.h" +#include "absl/synchronization/notification.h" +#include "internal/platform/implementation/ble.h" #include "sharing/internal/api/fast_init_ble_beacon.h" #include "sharing/internal/api/fast_initiation_manager.h" #include "internal/platform/implementation/linux/bluetooth_adapter.h" -#include "sharing/linux/nearby_fast_init_manager.h" +#include "internal/platform/implementation/linux/bluetooth_devices.h" #include "internal/platform/implementation/linux/bluez.h" +#include "internal/platform/implementation/linux/bluez_advertisement_monitor.h" +#include "internal/platform/implementation/linux/bluez_advertisement_monitor_manager.h" #include "internal/platform/implementation/linux/bluez_le_advertisement.h" +#include "internal/platform/implementation/linux/dbus.h" +#include "internal/platform/uuid.h" +#include "sharing/linux/nearby_fast_init_manager.h" namespace nearby { namespace sharing { namespace linux { +namespace { + +constexpr char kFastInitMonitorRootPath[] = + "/com/google/nearby/sharing/fast_init/monitor"; +constexpr char kFastInitMonitorPath[] = + "/com/google/nearby/sharing/fast_init/monitor/fe2c"; +constexpr size_t kFastInitServiceDataSize = + nearby::api::FastInitBleBeacon::kAdvertiseDataTotalSize - + nearby::api::FastInitBleBeacon::kFastInitServiceUuidSize; + +nearby::api::FastInitiationManager::Error MapBluezError( + const std::string& error_name) { + if (error_name == "org.bluez.Error.AlreadyExists" || + error_name == "org.bluez.Error.InProgress") { + return nearby::api::FastInitiationManager::Error::kResourceInUse; + } + if (error_name == "org.bluez.Error.NotPermitted" || + error_name == "org.bluez.Error.NotAuthorized") { + return nearby::api::FastInitiationManager::Error::kDisabledByUser; + } + if (error_name == "org.bluez.Error.NotSupported") { + return nearby::api::FastInitiationManager::Error::kHardwareNotSupported; + } + return nearby::api::FastInitiationManager::Error::kUnknown; +} + +} // namespace + +LinuxFastInitiationManager::~LinuxFastInitiationManager() { + StopScanning(nullptr); + StopAdvertising(nullptr); +} + void LinuxFastInitiationManager::StartAdvertising( nearby::api::FastInitBleBeacon::FastInitType type, std::function callback, @@ -121,22 +168,301 @@ void LinuxFastInitiationManager::StopAdvertising( callback(); } } -void StopScanning(std::function callback) { - if (callback) { - callback(); - } -} void LinuxFastInitiationManager::StartScanning( std::function devices_discovered_callback, std::function devices_not_discovered_callback, std::function error_callback) { - if (error_callback) { - error_callback( - nearby::api::FastInitiationManager::Error::kHardwareNotSupported); + std::optional error; + { + absl::MutexLock operation_lock(&scan_operation_mutex_); + error = StartScanningInternal(std::move(devices_discovered_callback), + std::move(devices_not_discovered_callback)); } + if (error.has_value() && error_callback) { + error_callback(*error); + } +} + +std::optional +LinuxFastInitiationManager::StartScanningInternal( + std::function devices_discovered_callback, + std::function devices_not_discovered_callback) { + bool already_scanning = false; + { + absl::MutexLock lock(&mutex_); + already_scanning = is_scanning_; + } + if (already_scanning) { + return nearby::api::FastInitiationManager::Error::kResourceInUse; + } + + if (adapter_ == nullptr || !adapter_->IsEnabled()) { + return nearby::api::FastInitiationManager::Error:: + kBluetoothRadioUnavailable; + } + + auto fast_init_uuid = Uuid::FromString(kFastInitServiceUuid); + if (!fast_init_uuid.has_value()) { + return nearby::api::FastInitiationManager::Error::kUnknown; + } + + auto connection = adapter_->GetConnection(); + auto monitor_manager = ::nearby::linux::bluez::AdvertisementMonitorManager:: + DiscoverAdvertisementMonitorManager(*connection, *adapter_); + if (monitor_manager == nullptr) { + return nearby::api::FastInitiationManager::Error::kHardwareNotSupported; + } + + try { + const std::vector supported_types = + monitor_manager->SupportedMonitorTypes(); + if (std::find(supported_types.begin(), supported_types.end(), + "or_patterns") == supported_types.end()) { + return nearby::api::FastInitiationManager::Error::kHardwareNotSupported; + } + } catch (const sdbus::Error& error) { + return MapBluezError(error.getName()); + } + + uint64_t generation; + try { + { + absl::MutexLock lock(&mutex_); + is_scanning_ = true; + generation = ++scan_generation_; + devices_discovered_callback_ = std::move(devices_discovered_callback); + devices_not_discovered_callback_ = + std::move(devices_not_discovered_callback); + discovered_peripherals_.clear(); + } + scan_devices_ = ::nearby::linux::GetSharedBluetoothDevices( + connection, adapter_->GetObjectPath()); + scan_root_object_manager_ = + std::make_unique<::nearby::linux::RootObjectManager>( + *connection, sdbus::ObjectPath(kFastInitMonitorRootPath)); + scan_monitor_ = + std::make_unique<::nearby::linux::bluez::AdvertisementMonitor>( + *connection, sdbus::ObjectPath(kFastInitMonitorPath), + *fast_init_uuid, nearby::api::ble::TxPowerLevel::kLow, + "or_patterns", scan_devices_, + nearby::api::ble::BleMedium::ScanningCallback{ + .advertisement_found_cb = + [this, generation]( + nearby::api::ble::BlePeripheral::UniqueId peripheral_id, + nearby::api::ble::BleAdvertisementData data) { + OnAdvertisementFound(generation, peripheral_id, + std::move(data)); + }, + .advertisement_lost_cb = + [this, + generation](nearby::api::ble::BlePeripheral::UniqueId id) { + OnAdvertisementLost(generation, id); + }, + }); + scan_monitor_manager_ = std::move(monitor_manager); + } catch (const sdbus::Error& error) { + { + absl::MutexLock lock(&mutex_); + is_scanning_ = false; + devices_discovered_callback_ = nullptr; + devices_not_discovered_callback_ = nullptr; + } + scan_monitor_.reset(); + scan_root_object_manager_.reset(); + scan_devices_.reset(); + return MapBluezError(error.getName()); + } + + absl::Notification registration_complete; + std::string registration_error_name; + scan_monitor_manager_->SetRegisterMonitorReplyCallback( + [®istration_complete, + ®istration_error_name](std::optional error) { + if (error.has_value() && error->isValid()) { + registration_error_name = error->getName(); + } + registration_complete.Notify(); + }); + try { + scan_monitor_manager_->RegisterMonitor( + scan_root_object_manager_->getObject().getObjectPath()); + registration_complete.WaitForNotification(); + } catch (const sdbus::Error& error) { + registration_error_name = error.getName(); + } + + if (!registration_error_name.empty()) { + { + absl::MutexLock lock(&mutex_); + is_scanning_ = false; + ++scan_generation_; + devices_discovered_callback_ = nullptr; + devices_not_discovered_callback_ = nullptr; + discovered_peripherals_.clear(); + } + scan_monitor_.reset(); + scan_root_object_manager_.reset(); + scan_monitor_manager_.reset(); + scan_devices_.reset(); + return MapBluezError(registration_error_name); + } + + try { + auto& bluez_adapter = adapter_->GetBluezAdapterObject(); + if (!bluez_adapter.Discovering()) { + std::map filter; + filter["Transport"] = sdbus::Variant("le"); + filter["DuplicateData"] = sdbus::Variant(true); + bluez_adapter.SetDiscoveryFilter(filter); + bluez_adapter.StartDiscovery(); + absl::MutexLock lock(&mutex_); + owns_bluez_discovery_ = true; + } + } catch (const sdbus::Error& error) { + // Release the scan application before reporting a failed start. + StopScanningInternal(); + return MapBluezError(error.getName()); + } + return std::nullopt; +} + +void LinuxFastInitiationManager::StopScanning(std::function callback) { + { + absl::MutexLock operation_lock(&scan_operation_mutex_); + StopScanningInternal(); + } + if (callback) { + callback(); + } +} + +void LinuxFastInitiationManager::StopScanningInternal() { + bool owns_discovery = false; + { + absl::MutexLock lock(&mutex_); + if (!is_scanning_) { + return; + } + is_scanning_ = false; + owns_discovery = owns_bluez_discovery_; + owns_bluez_discovery_ = false; + ++scan_generation_; + devices_discovered_callback_ = nullptr; + devices_not_discovered_callback_ = nullptr; + discovered_peripherals_.clear(); + } + + if (owns_discovery && adapter_ != nullptr) { + try { + adapter_->GetBluezAdapterObject().StopDiscovery(); + } catch (const sdbus::Error& error) { + DBUS_LOG_METHOD_CALL_ERROR(&adapter_->GetBluezAdapterObject(), + "StopDiscovery", error); + } + } + + if (scan_monitor_manager_ != nullptr && + scan_root_object_manager_ != nullptr) { + absl::Notification unregistration_complete; + scan_monitor_manager_->SetUnregisterMonitorReplyCallback( + [&unregistration_complete](std::optional) { + unregistration_complete.Notify(); + }); + try { + scan_monitor_manager_->UnregisterMonitor( + scan_root_object_manager_->getObject().getObjectPath()); + unregistration_complete.WaitForNotification(); + } catch (const sdbus::Error& error) { + DBUS_LOG_METHOD_CALL_ERROR(scan_monitor_manager_.get(), + "UnregisterMonitor", error); + } + } + + scan_monitor_.reset(); + scan_root_object_manager_.reset(); + scan_monitor_manager_.reset(); + scan_devices_.reset(); +} + +bool LinuxFastInitiationManager::IsScanning() { + absl::MutexLock lock(&mutex_); + return is_scanning_; +} + +void LinuxFastInitiationManager::OnAdvertisementFound( + uint64_t scan_generation, + nearby::api::ble::BlePeripheral::UniqueId peripheral_id, + nearby::api::ble::BleAdvertisementData advertisement_data) { + if (!IsFastInitAdvertisement(advertisement_data)) { + return; + } + + std::function callback; + { + absl::MutexLock lock(&mutex_); + if (!is_scanning_ || scan_generation != scan_generation_) { + return; + } + auto [unused, inserted] = discovered_peripherals_.insert(peripheral_id); + if (inserted && discovered_peripherals_.size() == 1) { + callback = devices_discovered_callback_; + } + } + if (callback) { + callback(); + } +} + +void LinuxFastInitiationManager::OnAdvertisementLost( + uint64_t scan_generation, + nearby::api::ble::BlePeripheral::UniqueId peripheral_id) { + std::function callback; + { + absl::MutexLock lock(&mutex_); + if (!is_scanning_ || scan_generation != scan_generation_ || + discovered_peripherals_.erase(peripheral_id) == 0) { + return; + } + if (discovered_peripherals_.empty()) { + callback = devices_not_discovered_callback_; + } + } + if (callback) { + callback(); + } +} + +bool LinuxFastInitiationManager::IsFastInitAdvertisement( + const nearby::api::ble::BleAdvertisementData& advertisement_data) const { + auto fast_init_uuid = Uuid::FromString(kFastInitServiceUuid); + if (!fast_init_uuid.has_value()) { + return false; + } + auto service_data = advertisement_data.service_data.find(*fast_init_uuid); + if (service_data == advertisement_data.service_data.end() || + service_data->second.size() != kFastInitServiceDataSize) { + return false; + } + + const auto bytes = service_data->second.AsStringView(); + for (size_t i = 0; i < nearby::api::FastInitBleBeacon::kFastInitModelIdSize; + ++i) { + if (static_cast(bytes[i]) != + nearby::api::FastInitBleBeacon::kFastInitModelId[i]) { + return false; + } + } + + const uint8_t metadata = static_cast( + bytes[nearby::api::FastInitBleBeacon::kFastInitModelIdSize]); + const uint8_t version = (metadata >> 5) & 0x07; + const uint8_t type = (metadata >> 2) & 0x07; + return version == static_cast( + nearby::api::FastInitBleBeacon::FastInitVersion::kV1) && + type <= static_cast( + nearby::api::FastInitBleBeacon::FastInitType::kSilent); } } // namespace linux } // namespace sharing } // namespace nearby - diff --git a/sharing/linux/nearby_fast_init_manager.h b/sharing/linux/nearby_fast_init_manager.h index b2c35573..ba03f1cb 100644 --- a/sharing/linux/nearby_fast_init_manager.h +++ b/sharing/linux/nearby_fast_init_manager.h @@ -16,13 +16,22 @@ #ifndef LINUX_NEARBY_SHARING_INTERNAL_API_FAST_INITIATION_MANAGER_H_ #define LINUX_NEARBY_SHARING_INTERNAL_API_FAST_INITIATION_MANAGER_H_ +#include #include +#include +#include +#include "absl/container/flat_hash_set.h" +#include "absl/synchronization/mutex.h" +#include "internal/platform/implementation/ble.h" #include "sharing/internal/api/fast_init_ble_beacon.h" #include "sharing/internal/api/fast_initiation_manager.h" #include "internal/platform/implementation/linux/bluetooth_adapter.h" +#include "internal/platform/implementation/linux/bluetooth_devices.h" #include "internal/platform/implementation/linux/bluez.h" +#include "internal/platform/implementation/linux/bluez_advertisement_monitor.h" +#include "internal/platform/implementation/linux/bluez_advertisement_monitor_manager.h" #include "internal/platform/implementation/linux/bluez_le_advertisement.h" constexpr char kFastInitServiceUuid[] = "0000fe2c-0000-1000-8000-00805f9b34fb"; @@ -36,7 +45,7 @@ class LinuxFastInitiationManager final public: explicit LinuxFastInitiationManager( nearby::api::FastInitBleBeacon& beacon, - std::shared_ptr<::nearby::linux::BluetoothAdapter> bluetooth_adapter) + std::shared_ptr<::nearby::linux::BluetoothAdapter> bluetooth_adapter) : beacon_(beacon), adapter_(std::move(bluetooth_adapter)) { if (adapter_ != nullptr) { adv_manager_ = @@ -44,12 +53,13 @@ class LinuxFastInitiationManager final *adapter_->GetConnection(), *adapter_); } } + ~LinuxFastInitiationManager() override; void StartAdvertising( nearby::api::FastInitBleBeacon::FastInitType type, std::function callback, std::function - error_callback) override ; + error_callback) override; void StopAdvertising(std::function callback) override; @@ -57,27 +67,61 @@ class LinuxFastInitiationManager final std::function devices_discovered_callback, std::function devices_not_discovered_callback, std::function - error_callback) override ; + error_callback) override; - void StopScanning(std::function callback) override { - if (callback) { - callback(); - } - } + void StopScanning(std::function callback) override; bool IsAdvertising() override { absl::MutexLock lock(&mutex_); return advertisement_ != nullptr; } - bool IsScanning() override { return false; } + bool IsScanning() override; private: + friend class LinuxFastInitiationManagerTestPeer; + + void OnAdvertisementFound( + uint64_t scan_generation, + ::nearby::api::ble::BlePeripheral::UniqueId peripheral_id, + ::nearby::api::ble::BleAdvertisementData advertisement_data); + void OnAdvertisementLost( + uint64_t scan_generation, + ::nearby::api::ble::BlePeripheral::UniqueId peripheral_id); + bool IsFastInitAdvertisement( + const ::nearby::api::ble::BleAdvertisementData& advertisement_data) const; + std::optional + StartScanningInternal(std::function devices_discovered_callback, + std::function devices_not_discovered_callback) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(scan_operation_mutex_); + void StopScanningInternal() + ABSL_EXCLUSIVE_LOCKS_REQUIRED(scan_operation_mutex_); + nearby::api::FastInitBleBeacon& beacon_; std::shared_ptr<::nearby::linux::BluetoothAdapter> adapter_; std::unique_ptr<::nearby::linux::bluez::LEAdvertisementManager> adv_manager_; + absl::Mutex scan_operation_mutex_; absl::Mutex mutex_; std::unique_ptr<::nearby::linux::bluez::LEAdvertisement> advertisement_ ABSL_GUARDED_BY(mutex_); + + bool is_scanning_ ABSL_GUARDED_BY(mutex_) = false; + bool owns_bluez_discovery_ ABSL_GUARDED_BY(mutex_) = false; + // Incremented for every start/stop so delayed BlueZ callbacks from an old + // scan cannot update the device set or notify callbacks for a newer scan. + uint64_t scan_generation_ ABSL_GUARDED_BY(mutex_) = 0; + std::function devices_discovered_callback_ ABSL_GUARDED_BY(mutex_); + std::function devices_not_discovered_callback_ + ABSL_GUARDED_BY(mutex_); + absl::flat_hash_set<::nearby::api::ble::BlePeripheral::UniqueId> + discovered_peripherals_ ABSL_GUARDED_BY(mutex_); + std::shared_ptr<::nearby::linux::BluetoothDevices> scan_devices_ + ABSL_GUARDED_BY(scan_operation_mutex_); + std::unique_ptr<::nearby::linux::RootObjectManager> scan_root_object_manager_ + ABSL_GUARDED_BY(scan_operation_mutex_); + std::unique_ptr<::nearby::linux::bluez::AdvertisementMonitorManager> + scan_monitor_manager_ ABSL_GUARDED_BY(scan_operation_mutex_); + std::unique_ptr<::nearby::linux::bluez::AdvertisementMonitor> scan_monitor_ + ABSL_GUARDED_BY(scan_operation_mutex_); }; } // namespace linux diff --git a/sharing/linux/nearby_fast_init_manager_test.cc b/sharing/linux/nearby_fast_init_manager_test.cc new file mode 100644 index 00000000..ae814214 --- /dev/null +++ b/sharing/linux/nearby_fast_init_manager_test.cc @@ -0,0 +1,116 @@ +#include "sharing/linux/nearby_fast_init_manager.h" + +#include +#include +#include +#include + +#include "absl/synchronization/mutex.h" +#include "gtest/gtest.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/implementation/ble.h" +#include "internal/platform/uuid.h" +#include "sharing/linux/nearby_fast_init_ble_beacon.h" + +namespace nearby::sharing::linux { + +class LinuxFastInitiationManagerTestPeer { + public: + static void BeginScan(LinuxFastInitiationManager& manager, + std::function discovered, + std::function not_discovered) { + absl::MutexLock lock(&manager.mutex_); + manager.is_scanning_ = true; + manager.scan_generation_ = 1; + manager.devices_discovered_callback_ = std::move(discovered); + manager.devices_not_discovered_callback_ = std::move(not_discovered); + manager.discovered_peripherals_.clear(); + } + + static void Found(LinuxFastInitiationManager& manager, uint64_t id, + api::ble::BleAdvertisementData data) { + manager.OnAdvertisementFound(1, id, std::move(data)); + } + + static void Lost(LinuxFastInitiationManager& manager, uint64_t id) { + manager.OnAdvertisementLost(1, id); + } + + static bool IsValid(LinuxFastInitiationManager& manager, + const api::ble::BleAdvertisementData& data) { + return manager.IsFastInitAdvertisement(data); + } +}; + +namespace { + +api::ble::BleAdvertisementData FastInitAdvertisement( + api::FastInitBleBeacon::FastInitType type = + api::FastInitBleBeacon::FastInitType::kNotify) { + std::string bytes(api::FastInitBleBeacon::kAdvertiseDataTotalSize - + api::FastInitBleBeacon::kFastInitServiceUuidSize, + '\0'); + for (size_t i = 0; i < api::FastInitBleBeacon::kFastInitModelIdSize; ++i) { + bytes[i] = static_cast(api::FastInitBleBeacon::kFastInitModelId[i]); + } + bytes[api::FastInitBleBeacon::kFastInitModelIdSize] = + static_cast(static_cast(type) << 2); + + api::ble::BleAdvertisementData data; + data.service_data.emplace(*Uuid::FromString(kFastInitServiceUuid), + ByteArray(bytes)); + return data; +} + +TEST(LinuxFastInitiationManagerTest, ValidatesFastInitServiceData) { + LinuxFastInitBleBeacon beacon; + LinuxFastInitiationManager manager(beacon, nullptr); + + EXPECT_TRUE(LinuxFastInitiationManagerTestPeer::IsValid( + manager, FastInitAdvertisement())); + EXPECT_TRUE(LinuxFastInitiationManagerTestPeer::IsValid( + manager, + FastInitAdvertisement(api::FastInitBleBeacon::FastInitType::kSilent))); + + auto malformed = FastInitAdvertisement(); + malformed.service_data.begin()->second.data()[0] = '\0'; + EXPECT_FALSE(LinuxFastInitiationManagerTestPeer::IsValid(manager, malformed)); +} + +TEST(LinuxFastInitiationManagerTest, ReportsOnlyPresenceTransitions) { + LinuxFastInitBleBeacon beacon; + LinuxFastInitiationManager manager(beacon, nullptr); + int discovered_count = 0; + int not_discovered_count = 0; + LinuxFastInitiationManagerTestPeer::BeginScan( + manager, [&] { ++discovered_count; }, [&] { ++not_discovered_count; }); + + LinuxFastInitiationManagerTestPeer::Found(manager, 1, + FastInitAdvertisement()); + LinuxFastInitiationManagerTestPeer::Found(manager, 1, + FastInitAdvertisement()); + LinuxFastInitiationManagerTestPeer::Found(manager, 2, + FastInitAdvertisement()); + EXPECT_EQ(discovered_count, 1); + + LinuxFastInitiationManagerTestPeer::Lost(manager, 99); + LinuxFastInitiationManagerTestPeer::Lost(manager, 1); + EXPECT_EQ(not_discovered_count, 0); + LinuxFastInitiationManagerTestPeer::Lost(manager, 2); + EXPECT_EQ(not_discovered_count, 1); +} + +TEST(LinuxFastInitiationManagerTest, StopScanningClearsLogicalState) { + LinuxFastInitBleBeacon beacon; + LinuxFastInitiationManager manager(beacon, nullptr); + LinuxFastInitiationManagerTestPeer::BeginScan(manager, [] {}, [] {}); + bool stopped = false; + + manager.StopScanning([&] { stopped = true; }); + + EXPECT_TRUE(stopped); + EXPECT_FALSE(manager.IsScanning()); +} + +} // namespace +} // namespace nearby::sharing::linux