Commit 250c9d73 authored by Kyle Horimoto's avatar Kyle Horimoto Committed by Commit Bot

[CrOS MultiDevice] Create BleScanner.

This class is based on the chromeos::tether::BleScanner class, but it
has several key distinctions:
(1) It must support scanning for devices which belong to multiple
    accounts, so it supports having more than one "local device". The
    function used to direct BleScanner to start or stop scanning is
    SetScanFilters().
(2) It only has a single listener, so I've changed the Obsever pattern
    to a Delegate pattern.
(3) Since SecureChannelService cannot be temporarily disabled, there is
    no longer a need for the asynchronous shutdown flow, so the
    OnDiscoverySessionStateChanged() callback is no longer needed.
(4) The artificial limit of 2 devices has been removed. In
    SecureChannel, it should be possible to scan for as many nearby
    devices as the client requests.

Bug: 824568, 752273
Change-Id: Idaf6b65a6465d41e7979a81d8024b162a32b9b49
Reviewed-on: https://chromium-review.googlesource.com/1084319
Commit-Queue: Kyle Horimoto <khorimoto@chromium.org>
Reviewed-by: default avatarRyan Hansberry <hansberry@chromium.org>
Cr-Commit-Position: refs/heads/master@{#564758}
parent 1a05b76c
......@@ -15,6 +15,10 @@ static_library("secure_channel") {
"ble_constants.h",
"ble_initiator_failure_type.h",
"ble_listener_failure_type.h",
"ble_scanner.cc",
"ble_scanner.h",
"ble_scanner_impl.cc",
"ble_scanner_impl.h",
"ble_service_data_helper.cc",
"ble_service_data_helper.h",
"ble_service_data_helper_impl.cc",
......@@ -87,6 +91,8 @@ static_library("test_support") {
sources = [
"fake_active_connection_manager.cc",
"fake_active_connection_manager.h",
"fake_ble_scanner.cc",
"fake_ble_scanner.h",
"fake_ble_service_data_helper.cc",
"fake_ble_service_data_helper.h",
"fake_ble_synchronizer.cc",
......@@ -138,6 +144,7 @@ source_set("unit_tests") {
sources = [
"active_connection_manager_impl_unittest.cc",
"authenticated_channel_impl_unittest.cc",
"ble_scanner_impl_unittest.cc",
"ble_service_data_helper_impl_unittest.cc",
"ble_synchronizer_unittest.cc",
"client_connection_parameters_impl_unittest.cc",
......
// Copyright 2018 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/services/secure_channel/ble_scanner.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "chromeos/components/proximity_auth/logging/logging.h"
namespace chromeos {
namespace secure_channel {
BleScanner::BleScanner(Delegate* delegate) : delegate_(delegate) {}
BleScanner::~BleScanner() = default;
void BleScanner::AddScanFilter(const DeviceIdPair& scan_filter) {
if (base::ContainsKey(scan_filters_, scan_filter)) {
PA_LOG(ERROR) << "BleScanner::AddScanFilter(): Tried to add a scan filter "
<< "which already existed. Filter: " << scan_filter;
NOTREACHED();
}
scan_filters_.insert(scan_filter);
HandleScanFilterChange();
}
void BleScanner::RemoveScanFilter(const DeviceIdPair& scan_filter) {
if (!base::ContainsKey(scan_filters_, scan_filter)) {
PA_LOG(ERROR) << "BleScanner::RemoveScanFilter(): Tried to remove a scan "
<< "filter which was not present. Filter: " << scan_filter;
NOTREACHED();
}
scan_filters_.erase(scan_filter);
HandleScanFilterChange();
}
void BleScanner::NotifyReceivedAdvertisementFromDevice(
const cryptauth::RemoteDeviceRef& remote_device,
device::BluetoothDevice* bluetooth_device,
bool is_background_advertisement) {
delegate_->OnReceivedAdvertisement(remote_device, bluetooth_device,
is_background_advertisement);
}
} // namespace secure_channel
} // namespace chromeos
// Copyright 2018 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.
#ifndef CHROMEOS_SERVICES_SECURE_CHANNEL_BLE_SCANNER_H_
#define CHROMEOS_SERVICES_SECURE_CHANNEL_BLE_SCANNER_H_
#include <unordered_set>
#include "base/macros.h"
#include "chromeos/services/secure_channel/device_id_pair.h"
#include "components/cryptauth/remote_device_ref.h"
namespace device {
class BluetoothDevice;
}
namespace chromeos {
namespace secure_channel {
// Performs BLE scans and notifies its delegate when an advertisement has been
// received from a remote device.
class BleScanner {
public:
class Delegate {
public:
virtual ~Delegate() = default;
virtual void OnReceivedAdvertisement(
cryptauth::RemoteDeviceRef remote_device,
device::BluetoothDevice* bluetooth_device,
bool is_background_advertisement) = 0;
};
virtual ~BleScanner();
// Adds a scan filter for the provided DeviceIdPair. If no scan filters were
// previously present, adding a scan filter will start a BLE discovery session
// and attempt to create a connection.
void AddScanFilter(const DeviceIdPair& scan_filter);
// Removes a scan filter for the provided DeviceIdPair. If this function
// removes the only remaining filter, the ongoing BLE discovery session will
// stop.
void RemoveScanFilter(const DeviceIdPair& scan_filter);
protected:
BleScanner(Delegate* delegate);
virtual void HandleScanFilterChange() = 0;
bool should_discovery_session_be_active() { return !scan_filters_.empty(); }
const DeviceIdPairSet& scan_filters() { return scan_filters_; }
void NotifyReceivedAdvertisementFromDevice(
const cryptauth::RemoteDeviceRef& remote_device,
device::BluetoothDevice* bluetooth_device,
bool is_background_advertisement);
private:
Delegate* delegate_;
DeviceIdPairSet scan_filters_;
DISALLOW_COPY_AND_ASSIGN(BleScanner);
};
} // namespace secure_channel
} // namespace chromeos
#endif // CHROMEOS_SERVICES_SECURE_CHANNEL_BLE_SCANNER_H_
// Copyright 2018 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/services/secure_channel/ble_scanner_impl.h"
#include <iostream>
#include <sstream>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "base/strings/string_util.h"
#include "chromeos/components/proximity_auth/logging/logging.h"
#include "chromeos/services/secure_channel/ble_constants.h"
#include "chromeos/services/secure_channel/ble_service_data_helper.h"
#include "chromeos/services/secure_channel/ble_synchronizer_base.h"
#include "components/cryptauth/proto/cryptauth_api.pb.h"
#include "components/cryptauth/remote_device_ref.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_discovery_session.h"
#include "device/bluetooth/bluetooth_uuid.h"
namespace chromeos {
namespace secure_channel {
namespace {
// TODO(hansberry): Share this constant with BleServiceDataHelper.
const size_t kMinNumBytesInServiceData = 2;
} // namespace
// static
BleScannerImpl::Factory* BleScannerImpl::Factory::test_factory_ = nullptr;
// static
BleScannerImpl::Factory* BleScannerImpl::Factory::Get() {
if (test_factory_)
return test_factory_;
static base::NoDestructor<Factory> factory;
return factory.get();
}
// static
void BleScannerImpl::Factory::SetFactoryForTesting(Factory* test_factory) {
test_factory_ = test_factory;
}
std::unique_ptr<BleScanner> BleScannerImpl::Factory::BuildInstance(
Delegate* delegate,
secure_channel::BleServiceDataHelper* service_data_helper,
BleSynchronizerBase* ble_synchronizer,
scoped_refptr<device::BluetoothAdapter> adapter) {
return base::WrapUnique(new BleScannerImpl(delegate, service_data_helper,
ble_synchronizer, adapter));
}
BleScannerImpl::ServiceDataProvider::~ServiceDataProvider() = default;
const std::vector<uint8_t>*
BleScannerImpl::ServiceDataProvider::ExtractProximityAuthServiceData(
device::BluetoothDevice* bluetooth_device) {
return bluetooth_device->GetServiceDataForUUID(
device::BluetoothUUID(kAdvertisingServiceUuid));
}
BleScannerImpl::BleScannerImpl(
Delegate* delegate,
secure_channel::BleServiceDataHelper* service_data_helper,
BleSynchronizerBase* ble_synchronizer,
scoped_refptr<device::BluetoothAdapter> adapter)
: BleScanner(delegate),
service_data_helper_(service_data_helper),
ble_synchronizer_(ble_synchronizer),
adapter_(adapter),
service_data_provider_(std::make_unique<ServiceDataProvider>()),
weak_ptr_factory_(this) {
adapter_->AddObserver(this);
}
BleScannerImpl::~BleScannerImpl() {
adapter_->RemoveObserver(this);
}
void BleScannerImpl::HandleScanFilterChange() {
UpdateDiscoveryStatus();
}
void BleScannerImpl::DeviceAdded(device::BluetoothAdapter* adapter,
device::BluetoothDevice* bluetooth_device) {
DCHECK_EQ(adapter_.get(), adapter);
HandleDeviceUpdated(bluetooth_device);
}
void BleScannerImpl::DeviceChanged(device::BluetoothAdapter* adapter,
device::BluetoothDevice* bluetooth_device) {
DCHECK_EQ(adapter_.get(), adapter);
HandleDeviceUpdated(bluetooth_device);
}
void BleScannerImpl::UpdateDiscoveryStatus() {
if (should_discovery_session_be_active())
EnsureDiscoverySessionActive();
else
EnsureDiscoverySessionNotActive();
}
bool BleScannerImpl::IsDiscoverySessionActive() {
ResetDiscoverySessionIfNotActive();
return discovery_session_.get() != nullptr;
}
void BleScannerImpl::ResetDiscoverySessionIfNotActive() {
if (!discovery_session_ || discovery_session_->IsActive())
return;
PA_LOG(ERROR) << "BluetoothDiscoverySession became out of sync. Session is "
<< "no longer active, but it was never stopped successfully. "
<< "Resetting session.";
// |discovery_session_| should be deleted as part of
// OnDiscoverySessionStopped() whenever the session is no longer active.
// However, a Bluetooth issue (https://crbug.com/768521) sometimes causes the
// session to become inactive without Stop() ever succeeding. If this
// occurs, reset state accordingly.
discovery_session_.reset();
discovery_session_weak_ptr_factory_.reset();
is_initializing_discovery_session_ = false;
is_stopping_discovery_session_ = false;
weak_ptr_factory_.InvalidateWeakPtrs();
}
void BleScannerImpl::EnsureDiscoverySessionActive() {
if (IsDiscoverySessionActive() || is_initializing_discovery_session_)
return;
is_initializing_discovery_session_ = true;
ble_synchronizer_->StartDiscoverySession(
base::Bind(&BleScannerImpl::OnDiscoverySessionStarted,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BleScannerImpl::OnStartDiscoverySessionError,
weak_ptr_factory_.GetWeakPtr()));
}
void BleScannerImpl::OnDiscoverySessionStarted(
std::unique_ptr<device::BluetoothDiscoverySession> discovery_session) {
PA_LOG(INFO) << "Started discovery session successfully.";
is_initializing_discovery_session_ = false;
discovery_session_ = std::move(discovery_session);
discovery_session_weak_ptr_factory_ =
std::make_unique<base::WeakPtrFactory<device::BluetoothDiscoverySession>>(
discovery_session_.get());
UpdateDiscoveryStatus();
}
void BleScannerImpl::OnStartDiscoverySessionError() {
is_initializing_discovery_session_ = false;
PA_LOG(ERROR) << "Error starting discovery session.";
UpdateDiscoveryStatus();
}
void BleScannerImpl::EnsureDiscoverySessionNotActive() {
if (!IsDiscoverySessionActive() || is_stopping_discovery_session_)
return;
is_stopping_discovery_session_ = true;
ble_synchronizer_->StopDiscoverySession(
discovery_session_weak_ptr_factory_->GetWeakPtr(),
base::Bind(&BleScannerImpl::OnDiscoverySessionStopped,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BleScannerImpl::OnStopDiscoverySessionError,
weak_ptr_factory_.GetWeakPtr()));
}
void BleScannerImpl::OnDiscoverySessionStopped() {
is_stopping_discovery_session_ = false;
PA_LOG(INFO) << "Stopped discovery session successfully.";
discovery_session_.reset();
discovery_session_weak_ptr_factory_.reset();
UpdateDiscoveryStatus();
}
void BleScannerImpl::OnStopDiscoverySessionError() {
is_stopping_discovery_session_ = false;
PA_LOG(ERROR) << "Error stopping discovery session.";
UpdateDiscoveryStatus();
}
void BleScannerImpl::HandleDeviceUpdated(
device::BluetoothDevice* bluetooth_device) {
DCHECK(bluetooth_device);
const std::vector<uint8_t>* service_data =
service_data_provider_->ExtractProximityAuthServiceData(bluetooth_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;
}
// 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());
auto potential_result = service_data_helper_->IdentifyRemoteDevice(
service_data_str, scan_filters());
// There was service data for the ProximityAuth UUID, but it did not apply to
// any active scan filters. The advertisement was likely from a nearby device
// attempting a ProximityAuth connection for another account.
if (!potential_result)
return;
// Prepare a hex string of |service_data_str|.
std::stringstream ss;
ss << "0x" << std::hex;
for (const auto& character : service_data_str)
ss << static_cast<uint32_t>(character);
PA_LOG(INFO) << "BleScannerImpl::HandleDeviceUpdated(): Received scan result "
<< "from device with ID \""
<< potential_result->first.GetTruncatedDeviceIdForLogs() << "\""
<< ". Service data: " << ss.str()
<< ", Background advertisement: "
<< (potential_result->second ? "true" : "false");
NotifyReceivedAdvertisementFromDevice(
potential_result->first, bluetooth_device, potential_result->second);
}
void BleScannerImpl::SetServiceDataProviderForTesting(
std::unique_ptr<ServiceDataProvider> service_data_provider) {
service_data_provider_ = std::move(service_data_provider);
}
} // namespace secure_channel
} // namespace chromeos
// Copyright 2018 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.
#ifndef CHROMEOS_SERVICES_SECURE_CHANNEL_BLE_SCANNER_IMPL_H_
#define CHROMEOS_SERVICES_SECURE_CHANNEL_BLE_SCANNER_IMPL_H_
#include <memory>
#include <vector>
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/services/secure_channel/ble_scanner.h"
#include "device/bluetooth/bluetooth_adapter.h"
namespace device {
class BluetoothDevice;
class BluetoothDiscoverySession;
} // namespace device
namespace chromeos {
namespace secure_channel {
class BleServiceDataHelper;
class BleSynchronizerBase;
// Concrete BleScanner implementation.
class BleScannerImpl : public BleScanner,
public device::BluetoothAdapter::Observer {
public:
class Factory {
public:
static Factory* Get();
static void SetFactoryForTesting(Factory* test_factory);
virtual std::unique_ptr<BleScanner> BuildInstance(
Delegate* delegate,
secure_channel::BleServiceDataHelper* service_data_helper,
BleSynchronizerBase* ble_synchronizer,
scoped_refptr<device::BluetoothAdapter> adapter);
private:
static Factory* test_factory_;
};
~BleScannerImpl() override;
private:
friend class SecureChannelBleScannerImplTest;
// Extracts the service data corresponding to the ProximityAuth service UUID.
// This is encapsulated within a class because device::BluetoothDevice does
// not provide a way to override this functionality for tests.
class ServiceDataProvider {
public:
virtual ~ServiceDataProvider();
virtual const std::vector<uint8_t>* ExtractProximityAuthServiceData(
device::BluetoothDevice* bluetooth_device);
};
BleScannerImpl(Delegate* delegate,
secure_channel::BleServiceDataHelper* service_data_helper,
BleSynchronizerBase* ble_synchronizer,
scoped_refptr<device::BluetoothAdapter> adapter);
// BleScanner:
void HandleScanFilterChange() override;
// device::BluetoothAdapter::Observer:
void DeviceAdded(device::BluetoothAdapter* adapter,
device::BluetoothDevice* bluetooth_device) override;
void DeviceChanged(device::BluetoothAdapter* adapter,
device::BluetoothDevice* bluetooth_device) override;
void UpdateDiscoveryStatus();
bool IsDiscoverySessionActive();
void ResetDiscoverySessionIfNotActive();
void EnsureDiscoverySessionActive();
void OnDiscoverySessionStarted(
std::unique_ptr<device::BluetoothDiscoverySession> discovery_session);
void OnStartDiscoverySessionError();
void EnsureDiscoverySessionNotActive();
void OnDiscoverySessionStopped();
void OnStopDiscoverySessionError();
void HandleDeviceUpdated(device::BluetoothDevice* bluetooth_device);
void SetServiceDataProviderForTesting(
std::unique_ptr<ServiceDataProvider> service_data_provider);
secure_channel::BleServiceDataHelper* service_data_helper_;
BleSynchronizerBase* ble_synchronizer_;
scoped_refptr<device::BluetoothAdapter> adapter_;
std::unique_ptr<ServiceDataProvider> service_data_provider_;
bool is_initializing_discovery_session_ = false;
bool is_stopping_discovery_session_ = false;
std::unique_ptr<device::BluetoothDiscoverySession> discovery_session_;
std::unique_ptr<base::WeakPtrFactory<device::BluetoothDiscoverySession>>
discovery_session_weak_ptr_factory_;
base::WeakPtrFactory<BleScannerImpl> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(BleScannerImpl);
};
} // namespace secure_channel
} // namespace chromeos
#endif // CHROMEOS_SERVICES_SECURE_CHANNEL_BLE_SCANNER_IMPL_H_
This diff is collapsed.
// Copyright 2018 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/services/secure_channel/fake_ble_scanner.h"
namespace chromeos {
namespace secure_channel {
FakeBleScanner::FakeBleScanner(Delegate* delegate) : BleScanner(delegate) {}
FakeBleScanner::~FakeBleScanner() = default;
void FakeBleScanner::HandleScanFilterChange() {
++num_scan_filter_changes_handled_;
}
FakeBleScannerDelegate::FakeBleScannerDelegate() = default;
FakeBleScannerDelegate::~FakeBleScannerDelegate() = default;
void FakeBleScannerDelegate::OnReceivedAdvertisement(
cryptauth::RemoteDeviceRef remote_device,
device::BluetoothDevice* bluetooth_device,
bool is_background_advertisement) {
handled_scan_results_.push_back(std::make_tuple(
remote_device, bluetooth_device, is_background_advertisement));
}
} // namespace secure_channel
} // namespace chromeos
// Copyright 2018 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.
#ifndef CHROMEOS_SERVICES_SECURE_CHANNEL_FAKE_BLE_SCANNER_H_
#define CHROMEOS_SERVICES_SECURE_CHANNEL_FAKE_BLE_SCANNER_H_
#include <tuple>
#include <vector>
#include "base/macros.h"
#include "chromeos/services/secure_channel/ble_scanner.h"
namespace chromeos {
namespace secure_channel {
// Test BleScanner implementation.
class FakeBleScanner : public BleScanner {
public:
FakeBleScanner(Delegate* delegate);
~FakeBleScanner() override;
size_t num_scan_filter_changes_handled() const {
return num_scan_filter_changes_handled_;
}
// Public for testing.
using BleScanner::NotifyReceivedAdvertisementFromDevice;
private:
void HandleScanFilterChange() override;
size_t num_scan_filter_changes_handled_ = 0u;
DISALLOW_COPY_AND_ASSIGN(FakeBleScanner);
};
// Test BleScanner::Delegate implementation.
class FakeBleScannerDelegate : public BleScanner::Delegate {
public:
FakeBleScannerDelegate();
~FakeBleScannerDelegate() override;
using ScannedResultList = std::vector<
std::tuple<cryptauth::RemoteDeviceRef, device::BluetoothDevice*, bool>>;
const ScannedResultList& handled_scan_results() const {
return handled_scan_results_;
}
private:
void OnReceivedAdvertisement(cryptauth::RemoteDeviceRef remote_device,
device::BluetoothDevice* bluetooth_device,
bool is_background_advertisement) override;
ScannedResultList handled_scan_results_;
DISALLOW_COPY_AND_ASSIGN(FakeBleScannerDelegate);
};
} // namespace secure_channel
} // namespace chromeos
#endif // CHROMEOS_SERVICES_SECURE_CHANNEL_FAKE_BLE_SCANNER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment