Chromium Code Reviews| Index: chromeos/components/tether/ble_scanner.cc |
| diff --git a/chromeos/components/tether/ble_scanner.cc b/chromeos/components/tether/ble_scanner.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..e5b45444bc9c950d34eae9a9864f09ec48a510a5 |
| --- /dev/null |
| +++ b/chromeos/components/tether/ble_scanner.cc |
| @@ -0,0 +1,284 @@ |
| +// Copyright 2016 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 "chromeos/components/tether/ble_scanner.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/memory/ptr_util.h" |
| +#include "base/strings/string_util.h" |
| +#include "chromeos/components/tether/ble_constants.h" |
| +#include "components/cryptauth/proto/cryptauth_api.pb.h" |
| +#include "components/cryptauth/remote_device.h" |
| +#include "components/proximity_auth/logging/logging.h" |
| +#include "device/bluetooth/bluetooth_device.h" |
| +#include "device/bluetooth/bluetooth_discovery_session.h" |
| +#include "device/bluetooth/bluetooth_uuid.h" |
| + |
| +namespace chromeos { |
| + |
| +namespace tether { |
| + |
| +namespace { |
| + |
| +const int kMinDiscoveryRSSI = -90; |
|
Ryan Hansberry
2017/01/04 00:01:03
nit: briefly comment on where this number comes fr
Kyle Horimoto
2017/01/04 18:35:43
Done.
|
| +const size_t kMinNumBytesInServiceData = 4; |
|
Ryan Hansberry
2017/01/04 00:01:03
nit: make this (2 * cryptauth::EidGenerator::kNumB
Kyle Horimoto
2017/01/04 18:35:43
Done.
|
| + |
| +} // namespace |
| + |
| +BleScanner::DelegateImpl::DelegateImpl() {} |
| + |
| +BleScanner::DelegateImpl::~DelegateImpl() {} |
| + |
| +bool BleScanner::DelegateImpl::IsBluetoothAdapterAvailable() const { |
| + return device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable(); |
| +} |
| + |
| +void BleScanner::DelegateImpl::GetAdapter( |
| + const device::BluetoothAdapterFactory::AdapterCallback& callback) { |
| + device::BluetoothAdapterFactory::GetAdapter(callback); |
| +} |
| + |
| +const std::vector<uint8_t>* BleScanner::DelegateImpl::GetServiceDataForUUID( |
| + const device::BluetoothUUID& service_uuid, |
| + device::BluetoothDevice* device) { |
| + return device->GetServiceDataForUUID(service_uuid); |
| +} |
| + |
| +BleScanner::BleScanner( |
| + const LocalDeviceDataProvider* local_device_data_provider) |
| + : BleScanner(base::MakeUnique<DelegateImpl>(), |
| + cryptauth::EidGenerator::GetInstance(), |
| + local_device_data_provider) {} |
| + |
| +BleScanner::~BleScanner() {} |
| + |
| +BleScanner::BleScanner( |
| + std::unique_ptr<Delegate> delegate, |
| + const cryptauth::EidGenerator* eid_generator, |
| + const LocalDeviceDataProvider* local_device_data_provider) |
| + : delegate_(std::move(delegate)), |
| + eid_generator_(eid_generator), |
| + local_device_data_provider_(local_device_data_provider), |
| + initializing_adapter_(false), |
| + initializing_discovery_session_(false), |
| + discovery_session_(nullptr), |
| + weak_ptr_factory_(this) {} |
| + |
| +bool BleScanner::RegisterScanFilterForDevice( |
| + const cryptauth::RemoteDevice& remote_device) { |
| + if (!delegate_->IsBluetoothAdapterAvailable()) { |
| + PA_LOG(ERROR) << "Bluetooth is not supported on this platform."; |
| + return false; |
| + } |
| + |
| + if (registered_devices_.size() >= kMaxConcurrentAdvertisements) { |
| + // Each scan filter corresponds to an advertisement. Thus, the number of |
| + // concurrent advertisements cannot exceed the maximum number of conrurrent |
|
Ryan Hansberry
2017/01/04 00:01:03
concurrent*
Kyle Horimoto
2017/01/04 18:35:43
Done.
|
| + // advertisements. |
| + return false; |
| + } |
| + |
| + std::vector<cryptauth::BeaconSeed> local_device_beacon_seeds; |
| + if (!local_device_data_provider_->GetLocalDeviceData( |
| + nullptr, &local_device_beacon_seeds)) { |
| + // If the local device's beacon seeds could not be fetched, a scan filter |
| + // cannot be generated. |
| + return false; |
| + } |
| + |
| + std::unique_ptr<cryptauth::EidGenerator::EidData> scan_filters = |
| + eid_generator_->GenerateBackgroundScanFilter(local_device_beacon_seeds); |
| + if (!scan_filters) { |
| + // If a background scan filter cannot be generated, give up. |
| + return false; |
| + } |
| + |
| + registered_devices_.push_back(remote_device); |
| + UpdateDiscoveryStatus(); |
| + |
| + return true; |
| +} |
| + |
| +bool BleScanner::UnregisterScanFilterForDevice( |
| + const cryptauth::RemoteDevice& device) { |
| + for (auto iter = registered_devices_.begin(); |
| + iter != registered_devices_.end(); iter++) { |
|
Ryan Hansberry
2017/01/04 00:01:03
nit: iterator incrementation should generally be d
Kyle Horimoto
2017/01/04 18:35:43
Done.
|
| + if (iter->GetDeviceId() == device.GetDeviceId()) { |
| + registered_devices_.erase(iter); |
| + UpdateDiscoveryStatus(); |
| + return true; |
| + } |
| + } |
| + |
| + return false; |
| +} |
| + |
| +bool BleScanner::IsDeviceRegistered(const std::string& device_id) { |
| + for (auto iter = registered_devices_.begin(); |
|
Ryan Hansberry
2017/01/04 00:01:03
super-nit: I find that 'it' is the more common sho
Kyle Horimoto
2017/01/04 18:35:43
Done.
|
| + iter != registered_devices_.end(); iter++) { |
|
Ryan Hansberry
2017/01/04 00:01:03
Same comment about prefix incrementing.
Kyle Horimoto
2017/01/04 18:35:43
Done.
|
| + if (iter->GetDeviceId() == device_id) { |
| + return true; |
| + } |
| + } |
| + |
| + return false; |
| +} |
| + |
| +void BleScanner::AddObserver(Observer* observer) { |
| + observer_list_.AddObserver(observer); |
| +} |
| + |
| +void BleScanner::RemoveObserver(Observer* observer) { |
| + observer_list_.RemoveObserver(observer); |
| +} |
| + |
| +void BleScanner::AdapterPoweredChanged(device::BluetoothAdapter* adapter, |
| + bool powered) { |
| + DCHECK_EQ(adapter_.get(), adapter); |
| + PA_LOG(INFO) << "Adapter power changed. Powered = " << powered; |
| + UpdateDiscoveryStatus(); |
| +} |
| + |
| +void BleScanner::DeviceAdded(device::BluetoothAdapter* adapter, |
| + device::BluetoothDevice* device) { |
| + DCHECK_EQ(adapter_.get(), adapter); |
| + HandleDeviceUpdated(device); |
| +} |
| + |
| +void BleScanner::DeviceChanged(device::BluetoothAdapter* adapter, |
| + device::BluetoothDevice* device) { |
| + DCHECK_EQ(adapter_.get(), adapter); |
| + HandleDeviceUpdated(device); |
| +} |
| + |
| +void BleScanner::UpdateDiscoveryStatus() { |
| + if (registered_devices_.empty()) { |
| + StopDiscoverySession(); |
| + return; |
| + } |
| + |
| + if (initializing_adapter_) { |
| + return; |
| + } else if (!adapter_) { |
| + InitializeBluetoothAdapter(); |
| + return; |
| + } |
| + |
| + if (!adapter_->IsPowered()) { |
| + // If the adapter has powered off, no devices can be discovered. |
| + StopDiscoverySession(); |
| + return; |
| + } |
| + |
| + if (initializing_discovery_session_) { |
| + return; |
| + } else if (!discovery_session_ || |
| + (discovery_session_ && !discovery_session_->IsActive())) { |
| + StartDiscoverySession(); |
| + } |
| +} |
| + |
| +void BleScanner::InitializeBluetoothAdapter() { |
| + PA_LOG(INFO) << "Initializing Bluetooth adapter."; |
|
Ryan Hansberry
2017/01/04 00:01:03
Should we be using ProximityAuth logging?
Kyle Horimoto
2017/01/04 18:35:43
For now, yes. We may want to come up with a CrOS T
|
| + initializing_adapter_ = true; |
| + delegate_->GetAdapter(base::Bind(&BleScanner::OnAdapterInitialized, |
| + weak_ptr_factory_.GetWeakPtr())); |
| +} |
| + |
| +void BleScanner::OnAdapterInitialized( |
| + scoped_refptr<device::BluetoothAdapter> adapter) { |
| + DCHECK(initializing_adapter_ && !discovery_session_ && |
| + !initializing_discovery_session_); |
| + PA_LOG(INFO) << "Bluetooth adapter initialized."; |
| + initializing_adapter_ = false; |
| + |
| + adapter_ = adapter; |
| + adapter_->AddObserver(this); |
| + |
| + UpdateDiscoveryStatus(); |
| +} |
| + |
| +void BleScanner::StartDiscoverySession() { |
| + DCHECK(adapter_); |
| + PA_LOG(INFO) << "Starting discovery session."; |
| + initializing_discovery_session_ = true; |
| + |
| + // Discover only low energy (LE) devices with strong enough signal. |
| + std::unique_ptr<device::BluetoothDiscoveryFilter> filter = |
| + base::MakeUnique<device::BluetoothDiscoveryFilter>( |
| + device::BLUETOOTH_TRANSPORT_LE); |
| + filter->SetRSSI(kMinDiscoveryRSSI); |
| + |
| + adapter_->StartDiscoverySessionWithFilter( |
| + std::move(filter), base::Bind(&BleScanner::OnDiscoverySessionStarted, |
|
Ryan Hansberry
2017/01/04 00:01:03
This needs to be placed on another line (the 2nd a
Kyle Horimoto
2017/01/04 18:35:43
Nope, the Chromium lint tool did this.
|
| + weak_ptr_factory_.GetWeakPtr()), |
| + base::Bind(&BleScanner::OnStartDiscoverySessionError, |
| + weak_ptr_factory_.GetWeakPtr())); |
| +} |
| + |
| +void BleScanner::OnDiscoverySessionStarted( |
| + std::unique_ptr<device::BluetoothDiscoverySession> discovery_session) { |
| + PA_LOG(INFO) << "Discovery session started. Scanning fully initialized."; |
| + initializing_discovery_session_ = false; |
| + discovery_session_ = std::move(discovery_session); |
| +} |
| + |
| +void BleScanner::OnStartDiscoverySessionError() { |
| + PA_LOG(WARNING) << "Error starting discovery session. Initialization failed."; |
| + initializing_discovery_session_ = false; |
| +} |
| + |
| +void BleScanner::StopDiscoverySession() { |
| + if (!discovery_session_) { |
| + // If there is no discovery session to stop, return early. |
| + return; |
| + } |
| + |
| + PA_LOG(WARNING) << "Stopping discovery session."; |
| + discovery_session_.reset(); |
| +} |
| + |
| +void BleScanner::HandleDeviceUpdated(device::BluetoothDevice* device) { |
| + DCHECK(device); |
| + |
| + const std::vector<uint8_t>* service_data = delegate_->GetServiceDataForUUID( |
| + device::BluetoothUUID(kAdvertisingServiceUuid), device); |
| + if (!service_data || service_data->size() < kMinNumBytesInServiceData) { |
| + // If there is no service data or the service data is of insufficient |
| + // length, there is not enough information to create a connection. |
| + return; |
|
Ryan Hansberry
2017/01/04 00:01:03
nit: add a log.
Kyle Horimoto
2017/01/04 18:35:43
I don't want to add a log here since it would be v
|
| + } |
| + |
| + // Convert the service data from a std::vector<uint8_t> to a std::string. |
| + std::string service_data_str; |
| + char* string_contents_ptr = |
| + base::WriteInto(&service_data_str, service_data->size() + 1); |
| + memcpy(string_contents_ptr, service_data->data(), service_data->size() + 1); |
| + |
| + CheckForMatchingScanFilters(device, service_data_str); |
| +} |
| + |
| +void BleScanner::CheckForMatchingScanFilters(device::BluetoothDevice* device, |
| + std::string& service_data) { |
| + std::vector<cryptauth::BeaconSeed> beacon_seeds; |
| + if (!local_device_data_provider_->GetLocalDeviceData(nullptr, |
| + &beacon_seeds)) { |
| + // If no beacon seeds are available, the scan cannot be checked for a match. |
| + return; |
| + } |
| + |
| + const cryptauth::RemoteDevice* identified_device = |
| + eid_generator_->IdentifyRemoteDeviceByAdvertisement( |
| + service_data, registered_devices_, beacon_seeds); |
| + |
| + if (identified_device) { |
| + for (auto& observer : observer_list_) { |
| + observer.OnReceivedAdvertisementFromDevice(device, *identified_device); |
| + } |
| + } |
|
Ryan Hansberry
2017/01/04 00:01:03
nit: add a log for when a device cannot be identif
Kyle Horimoto
2017/01/04 18:35:43
Done.
|
| +} |
| + |
| +} // namespace tether |
| + |
| +} // namespace chromeos |