| // Copyright 2021 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/quick_pair/scanning/fast_pair/fast_pair_scanner_impl.h" |
| |
| #include "ash/quick_pair/common/constants.h" |
| #include "ash/quick_pair/common/logging.h" |
| #include "base/containers/contains.h" |
| #include "base/time/time.h" |
| #include "device/bluetooth/bluetooth_adapter_factory.h" |
| #include "device/bluetooth/bluetooth_low_energy_scan_filter.h" |
| |
| namespace { |
| |
| constexpr int16_t kFilterDeviceFoundThreshold = -80; |
| constexpr base::TimeDelta kFilterDeviceFoundTimeout = |
| base::TimeDelta::FromSeconds(1); |
| constexpr int16_t kFilterDeviceLostThreshold = -100; |
| constexpr base::TimeDelta kFilterDeviceLostTimeout = |
| base::TimeDelta::FromSeconds(5); |
| constexpr uint8_t kFilterPatternStartPosition = 0; |
| const std::vector<uint8_t> kFastPairFilterPatternValue = {0x2c, 0xfe}; |
| |
| std::ostream& operator<<( |
| std::ostream& out, |
| const device::BluetoothLowEnergyScanSession::ErrorCode& error_code) { |
| switch (error_code) { |
| case device::BluetoothLowEnergyScanSession::ErrorCode::kFailed: |
| out << "[Failed]"; |
| break; |
| } |
| return out; |
| } |
| } // namespace |
| |
| namespace ash { |
| namespace quick_pair { |
| |
| FastPairScannerImpl::FastPairScannerImpl() { |
| device::BluetoothAdapterFactory::Get()->GetAdapter(base::BindOnce( |
| &FastPairScannerImpl::OnGetAdapter, weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| FastPairScannerImpl::~FastPairScannerImpl() = default; |
| |
| void FastPairScannerImpl::OnGetAdapter( |
| scoped_refptr<device::BluetoothAdapter> adapter) { |
| adapter_ = adapter; |
| adapter_observation_.Observe(adapter_.get()); |
| |
| device::BluetoothLowEnergyScanFilter::Pattern pattern( |
| kFilterPatternStartPosition, |
| device::BluetoothLowEnergyScanFilter::AdvertisementDataType::kServiceData, |
| kFastPairFilterPatternValue); |
| auto filter = device::BluetoothLowEnergyScanFilter::Create( |
| kFilterDeviceFoundThreshold, kFilterDeviceLostThreshold, |
| kFilterDeviceFoundTimeout, kFilterDeviceLostTimeout, {pattern}); |
| if (!filter) { |
| QP_LOG(ERROR) << "Bluetooth Low Energy Scan Session failed to start due to " |
| "failure to create filter."; |
| return; |
| } |
| |
| background_scan_session_ = adapter_->StartLowEnergyScanSession( |
| std::move(filter), weak_ptr_factory_.GetWeakPtr()); |
| } |
| |
| void FastPairScannerImpl::AddObserver(FastPairScanner::Observer* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void FastPairScannerImpl::RemoveObserver(FastPairScanner::Observer* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| void FastPairScannerImpl::OnSessionStarted( |
| device::BluetoothLowEnergyScanSession* scan_session, |
| absl::optional<device::BluetoothLowEnergyScanSession::ErrorCode> |
| error_code) { |
| if (error_code) { |
| QP_LOG(ERROR) << "Bluetooth Low Energy Scan Session failed to start with " |
| "the following error: " |
| << error_code.value(); |
| return; |
| } |
| } |
| |
| void FastPairScannerImpl::OnSessionInvalidated( |
| device::BluetoothLowEnergyScanSession* scan_session) { |
| // TODO(crbug.com/1227519) Handle Session Invalidation by adding exponential |
| // retry to restart the scanner. |
| background_scan_session_.reset(); |
| device_address_advertisement_data_map_.clear(); |
| } |
| |
| void FastPairScannerImpl::OnDeviceFound( |
| device::BluetoothLowEnergyScanSession* scan_session, |
| device::BluetoothDevice* device) { |
| const std::vector<uint8_t>* service_data = |
| device->GetServiceDataForUUID(kFastPairBluetoothUuid); |
| if (!service_data) { |
| QP_LOG(WARNING) << "No Fast Pair service data found on device"; |
| return; |
| } |
| |
| device_address_advertisement_data_map_[device->GetAddress()].insert( |
| *service_data); |
| NotifyDeviceFound(device); |
| } |
| |
| void FastPairScannerImpl::DeviceChanged(device::BluetoothAdapter* adapter, |
| device::BluetoothDevice* device) { |
| std::string device_address = device->GetAddress(); |
| const std::vector<uint8_t>* service_data = |
| device->GetServiceDataForUUID(kFastPairBluetoothUuid); |
| |
| // If the advertisement data we have received does not pertain to a device |
| // we have seen already from the scanner, or if the advertisement data for |
| // a device we have already seen is not new, then early return and do not |
| // notify observers or add data to the device address advertisement data map. |
| if (!base::Contains(device_address_advertisement_data_map_, device_address) || |
| base::Contains(device_address_advertisement_data_map_[device_address], |
| *service_data)) { |
| return; |
| } |
| |
| device_address_advertisement_data_map_[device_address].insert(*service_data); |
| NotifyDeviceFound(device); |
| } |
| |
| void FastPairScannerImpl::NotifyDeviceFound(device::BluetoothDevice* device) { |
| for (auto& observer : observers_) |
| observer.OnDeviceFound(device); |
| } |
| |
| void FastPairScannerImpl::OnDeviceLost( |
| device::BluetoothLowEnergyScanSession* scan_session, |
| device::BluetoothDevice* device) { |
| device_address_advertisement_data_map_.erase(device->GetAddress()); |
| for (auto& observer : observers_) |
| observer.OnDeviceLost(device); |
| } |
| |
| } // namespace quick_pair |
| } // namespace ash |