| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/nearby_sharing/fast_initiation/fast_initiation_scanner.h" |
| |
| #include <stdint.h> |
| |
| #include <optional> |
| #include <vector> |
| |
| #include "base/time/time.h" |
| #include "chrome/browser/nearby_sharing/fast_initiation/constants.h" |
| #include "chrome/browser/nearby_sharing/nearby_share_metrics.h" |
| #include "components/cross_device/logging/logging.h" |
| #include "device/bluetooth/bluetooth_low_energy_scan_filter.h" |
| |
| namespace { |
| |
| // The length of time that a device must be in range before it is reported via a |
| // "device found" event. |
| constexpr base::TimeDelta kBackgroundScanningDeviceFoundTimeout = |
| base::Seconds(1); |
| |
| // The length of time that a device must be out of range before this is reported |
| // via a "device lost" event. |
| constexpr base::TimeDelta kBackgroundScanningDeviceLostTimeout = |
| base::Seconds(7); |
| } // namespace |
| |
| // static |
| FastInitiationScanner::Factory* |
| FastInitiationScanner::Factory::factory_instance_ = nullptr; |
| |
| // static |
| std::unique_ptr<FastInitiationScanner> FastInitiationScanner::Factory::Create( |
| scoped_refptr<device::BluetoothAdapter> adapter) { |
| if (factory_instance_) |
| return factory_instance_->CreateInstance(adapter); |
| |
| return std::make_unique<FastInitiationScanner>(adapter); |
| } |
| |
| // static |
| bool FastInitiationScanner::Factory::IsHardwareSupportAvailable( |
| device::BluetoothAdapter* adapter) { |
| if (factory_instance_) |
| return factory_instance_->IsHardwareSupportAvailable(); |
| |
| // The function only returns correct status when adapter is powered. |
| return adapter->IsPowered() && |
| adapter->GetLowEnergyScanSessionHardwareOffloadingStatus() == |
| device::BluetoothAdapter:: |
| LowEnergyScanSessionHardwareOffloadingStatus::kSupported; |
| } |
| |
| // static |
| void FastInitiationScanner::Factory::SetFactoryForTesting( |
| FastInitiationScanner::Factory* factory) { |
| factory_instance_ = factory; |
| } |
| |
| FastInitiationScanner::FastInitiationScanner( |
| scoped_refptr<device::BluetoothAdapter> adapter) |
| : adapter_(adapter) { |
| DCHECK(adapter_ && adapter_->IsPresent() && adapter_->IsPowered()); |
| } |
| |
| FastInitiationScanner::~FastInitiationScanner() = default; |
| |
| void FastInitiationScanner::StartScanning( |
| base::RepeatingClosure devices_detected_callback, |
| base::RepeatingClosure devices_not_detected_callback, |
| base::OnceClosure scanner_invalidated_callback) { |
| devices_detected_callback_ = std::move(devices_detected_callback); |
| devices_not_detected_callback_ = std::move(devices_not_detected_callback); |
| scanner_invalidated_callback_ = std::move(scanner_invalidated_callback); |
| |
| std::vector<uint8_t> pattern_value; |
| // Add the service UUID in reversed byte order. |
| pattern_value.insert(pattern_value.begin(), |
| std::rbegin(kFastInitiationServiceId), |
| std::rend(kFastInitiationServiceId)); |
| // Add the service data in the original order. |
| pattern_value.insert(pattern_value.end(), std::begin(kFastInitiationModelId), |
| std::end(kFastInitiationModelId)); |
| |
| device::BluetoothLowEnergyScanFilter::Pattern pattern( |
| /*start_position=*/0, |
| device::BluetoothLowEnergyScanFilter::AdvertisementDataType::kServiceData, |
| std::move(pattern_value)); |
| auto filter = device::BluetoothLowEnergyScanFilter::Create( |
| device::BluetoothLowEnergyScanFilter::Range::kNear, |
| kBackgroundScanningDeviceFoundTimeout, |
| kBackgroundScanningDeviceLostTimeout, {pattern}, |
| /*rssi_sampling_period=*/std::nullopt); |
| if (!filter) { |
| CD_LOG(ERROR, Feature::NS) |
| << __func__ |
| << ": Failed to start Fast Initiation scanning due to " |
| "failure to create filter."; |
| std::move(scanner_invalidated_callback_).Run(); |
| return; |
| } |
| |
| background_scan_session_ = adapter_->StartLowEnergyScanSession( |
| std::move(filter), /*delegate=*/weak_ptr_factory_.GetWeakPtr()); |
| } |
| |
| void FastInitiationScanner::OnSessionStarted( |
| device::BluetoothLowEnergyScanSession* scan_session, |
| std::optional<device::BluetoothLowEnergyScanSession::ErrorCode> |
| error_code) { |
| if (error_code) { |
| CD_LOG(WARNING, Feature::NS) |
| << __func__ |
| << ": Error, error_code=" << static_cast<int>(error_code.value()); |
| std::move(scanner_invalidated_callback_).Run(); |
| } else { |
| CD_LOG(VERBOSE, Feature::NS) << __func__ << ": Success"; |
| } |
| RecordNearbyShareBackgroundScanningSessionStarted(/*success=*/!error_code); |
| } |
| |
| void FastInitiationScanner::OnDeviceFound( |
| device::BluetoothLowEnergyScanSession* scan_session, |
| device::BluetoothDevice* device) { |
| CD_LOG(VERBOSE, Feature::NS) << __func__; |
| size_t device_count_prev = detected_devices_.size(); |
| detected_devices_.insert(device->GetAddress()); |
| |
| // Invoke the callback when we go from zero devices to more than zero. |
| if (device_count_prev == 0) { |
| devices_detected_callback_.Run(); |
| RecordNearbyShareBackgroundScanningDevicesDetected(); |
| devices_detected_timestamp_ = base::TimeTicks::Now(); |
| } |
| } |
| |
| void FastInitiationScanner::OnDeviceLost( |
| device::BluetoothLowEnergyScanSession* scan_session, |
| device::BluetoothDevice* device) { |
| CD_LOG(VERBOSE, Feature::NS) << __func__; |
| size_t device_count_prev = detected_devices_.size(); |
| if (detected_devices_.erase(device->GetAddress()) == 0) { |
| CD_LOG(WARNING, Feature::NS) |
| << __func__ << ": Received device lost event for device not in list."; |
| } |
| |
| // Invoke the callback when we go from more than zero devices to zero. |
| if (device_count_prev > 0 && detected_devices_.empty()) { |
| devices_not_detected_callback_.Run(); |
| RecordNearbyShareBackgroundScanningDevicesDetectedDuration( |
| base::TimeTicks::Now() - devices_detected_timestamp_); |
| } |
| } |
| |
| void FastInitiationScanner::OnSessionInvalidated( |
| device::BluetoothLowEnergyScanSession* scan_session) { |
| CD_LOG(VERBOSE, Feature::NS) << __func__; |
| detected_devices_.clear(); |
| std::move(scanner_invalidated_callback_).Run(); |
| } |