Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(41)

Unified Diff: chromeos/components/tether/ble_scanner.cc

Issue 2604063003: [CrOS Tether] Create BleScanner, a class which scan BLE advertisements and identifies nearby device… (Closed)
Patch Set: Adjusted comment. Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698