[Nearby] Implement BleMedium scanning.
Implement BleMedium::StartScanning() and BleMedium::StopScanning(). This requires adding a ServiceDataMap (mapping from service id to service data) to bluetooth::mojom::DeviceInfo, to represent BLE advertisements. See go/nearby-chrome-bt for more details. Bug: b:154848193 Change-Id: I40489f8128bcda9409b20b43be52e73b2a9a2240 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2370094 Commit-Queue: Ryan Hansberry <hansberry@chromium.org> Reviewed-by: Daniel Cheng <dcheng@chromium.org> Reviewed-by: Reilly Grant <reillyg@chromium.org> Reviewed-by: James Vecore <vecore@google.com> Cr-Commit-Position: refs/heads/master@{#805582}
This commit is contained in:

committed by
Commit Bot

parent
d3cd5999d1
commit
ced2fad602
chrome
services
sharing
test
data
device/bluetooth
@ -139,8 +139,17 @@ ImplementationPlatform::CreateBluetoothClassicMedium(
|
||||
|
||||
std::unique_ptr<BleMedium> ImplementationPlatform::CreateBleMedium(
|
||||
api::BluetoothAdapter& adapter) {
|
||||
// TODO (hansberry): Inject bluetooth::mojom::Adapter into BleMedium.
|
||||
return std::make_unique<chrome::BleMedium>();
|
||||
// Ignore the provided |adapter| argument. It provides no interface useful
|
||||
// to implement chrome::BleMedium.
|
||||
|
||||
auto& connections = connections::NearbyConnections::GetInstance();
|
||||
bluetooth::mojom::Adapter* bluetooth_adapter =
|
||||
connections.GetBluetoothAdapter();
|
||||
|
||||
if (!bluetooth_adapter)
|
||||
return nullptr;
|
||||
|
||||
return std::make_unique<chrome::BleMedium>(bluetooth_adapter);
|
||||
}
|
||||
|
||||
std::unique_ptr<ble_v2::BleMedium> ImplementationPlatform::CreateBleV2Medium(
|
||||
|
@ -4,11 +4,13 @@
|
||||
|
||||
#include "chrome/services/sharing/nearby/platform_v2/ble_medium.h"
|
||||
|
||||
#include "chrome/services/sharing/nearby/platform_v2/bluetooth_device.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace chrome {
|
||||
|
||||
BleMedium::BleMedium() = default;
|
||||
BleMedium::BleMedium(bluetooth::mojom::Adapter* adapter) : adapter_(adapter) {}
|
||||
|
||||
BleMedium::~BleMedium() = default;
|
||||
|
||||
@ -28,15 +30,59 @@ bool BleMedium::StopAdvertising(const std::string& service_id) {
|
||||
bool BleMedium::StartScanning(const std::string& service_id,
|
||||
api::BleMedium::DiscoveredPeripheralCallback
|
||||
discovered_peripheral_callback) {
|
||||
// TODO(b/154848193): Implement this method.
|
||||
NOTIMPLEMENTED();
|
||||
auto service_uuid = device::BluetoothUUID(service_id);
|
||||
if (IsScanning() &&
|
||||
base::Contains(discovered_peripheral_callbacks_map_, service_uuid)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We only need to start discovery if no other discovery request is active.
|
||||
if (discovered_peripheral_callbacks_map_.empty()) {
|
||||
discovered_ble_peripherals_map_.clear();
|
||||
|
||||
bool success =
|
||||
adapter_->AddObserver(adapter_observer_.BindNewPipeAndPassRemote());
|
||||
if (!success) {
|
||||
adapter_observer_.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
mojo::PendingRemote<bluetooth::mojom::DiscoverySession> discovery_session;
|
||||
success = adapter_->StartDiscoverySession(&discovery_session);
|
||||
|
||||
if (!success || !discovery_session.is_valid()) {
|
||||
adapter_observer_.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
discovery_session_.Bind(std::move(discovery_session));
|
||||
discovery_session_.set_disconnect_handler(
|
||||
base::BindOnce(&BleMedium::DiscoveringChanged, base::Unretained(this),
|
||||
/*discovering=*/false));
|
||||
}
|
||||
|
||||
// A different DiscoveredPeripheralCallback is being passed on each call, so
|
||||
// each must be captured and associated with its |service_id|.
|
||||
discovered_peripheral_callbacks_map_.insert(
|
||||
{service_uuid, discovered_peripheral_callback});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BleMedium::StopScanning(const std::string& service_id) {
|
||||
// TODO(b/154848193): Implement this method.
|
||||
NOTIMPLEMENTED();
|
||||
return false;
|
||||
discovered_peripheral_callbacks_map_.erase(device::BluetoothUUID(service_id));
|
||||
if (!discovered_peripheral_callbacks_map_.empty())
|
||||
return true;
|
||||
|
||||
bool stop_discovery_success = true;
|
||||
if (discovery_session_) {
|
||||
bool message_success = discovery_session_->Stop(&stop_discovery_success);
|
||||
stop_discovery_success = stop_discovery_success && message_success;
|
||||
}
|
||||
|
||||
adapter_observer_.reset();
|
||||
discovery_session_.reset();
|
||||
|
||||
return stop_discovery_success;
|
||||
}
|
||||
|
||||
bool BleMedium::StartAcceptingConnections(
|
||||
@ -62,6 +108,127 @@ std::unique_ptr<api::BleSocket> BleMedium::Connect(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BleMedium::PresentChanged(bool present) {
|
||||
// TODO(hansberry): It is unclear to me how the API implementation can signal
|
||||
// to Core that |present| has become unexpectedly false. Need to ask
|
||||
// Nearby team.
|
||||
if (!present)
|
||||
StopScanning();
|
||||
}
|
||||
|
||||
void BleMedium::PoweredChanged(bool powered) {
|
||||
// TODO(hansberry): It is unclear to me how the API implementation can signal
|
||||
// to Core that |powered| has become unexpectedly false. Need to ask
|
||||
// Nearby team.
|
||||
if (!powered)
|
||||
StopScanning();
|
||||
}
|
||||
|
||||
void BleMedium::DiscoverableChanged(bool discoverable) {
|
||||
// Do nothing. BleMedium is not responsible for managing
|
||||
// discoverable state.
|
||||
}
|
||||
|
||||
void BleMedium::DiscoveringChanged(bool discovering) {
|
||||
// TODO(hansberry): It is unclear to me how the API implementation can signal
|
||||
// to Core that |discovering| has become unexpectedly false. Need to ask
|
||||
// Nearby team.
|
||||
if (!discovering)
|
||||
StopScanning();
|
||||
}
|
||||
|
||||
void BleMedium::DeviceAdded(bluetooth::mojom::DeviceInfoPtr device) {
|
||||
if (!IsScanning())
|
||||
return;
|
||||
|
||||
// Best-effort attempt to filter out BT Classic devices. Dual-mode (BT
|
||||
// Classic and BLE) devices which the system has paired and/or connected to
|
||||
// may also expose service data, but all BLE advertisements that we are
|
||||
// interested in are captured in an element of |service_data_map|. See
|
||||
// BluetoothClassicMedium for separate discovery of BT Classic devices.
|
||||
if (device->service_data_map.empty())
|
||||
return;
|
||||
|
||||
const std::string& address = device->address;
|
||||
auto* ble_peripheral = GetDiscoveredBlePeripheral(address);
|
||||
if (ble_peripheral)
|
||||
ble_peripheral->UpdateDeviceInfo(std::move(device));
|
||||
else
|
||||
discovered_ble_peripherals_map_.emplace(address, std::move(device));
|
||||
|
||||
// Invoking one of the callbacks in |discovered_peripheral_callbacks_map_| may
|
||||
// lead to invalidating one or all elements of
|
||||
// |discovered_peripheral_callbacks_map_|, e.g., triggering StopScanning()
|
||||
// while looping through it. Callbacks are copied to ensure they are not
|
||||
// modified as we loop through them.
|
||||
auto callbacks_map_copy = discovered_peripheral_callbacks_map_;
|
||||
for (auto& it : callbacks_map_copy) {
|
||||
// Must fetch |ble_peripheral| again because it may have been invalidated by
|
||||
// a prior callback in this loop.
|
||||
ble_peripheral = GetDiscoveredBlePeripheral(address);
|
||||
if (!ble_peripheral)
|
||||
break;
|
||||
|
||||
const auto& service_id = it.first.value();
|
||||
if (ble_peripheral->GetAdvertisementBytes(service_id).Empty())
|
||||
continue;
|
||||
|
||||
it.second.peripheral_discovered_cb(*ble_peripheral, service_id);
|
||||
}
|
||||
}
|
||||
|
||||
void BleMedium::DeviceChanged(bluetooth::mojom::DeviceInfoPtr device) {
|
||||
DeviceAdded(std::move(device));
|
||||
}
|
||||
|
||||
void BleMedium::DeviceRemoved(bluetooth::mojom::DeviceInfoPtr device) {
|
||||
if (!IsScanning())
|
||||
return;
|
||||
|
||||
const std::string& address = device->address;
|
||||
if (!GetDiscoveredBlePeripheral(address))
|
||||
return;
|
||||
|
||||
// Invoking one of the callbacks in |discovered_peripheral_callbacks_map_| may
|
||||
// lead to invalidating one or all elements of
|
||||
// |discovered_peripheral_callbacks_map_|, e.g., triggering StopScanning()
|
||||
// while looping through it. Callbacks are copied to ensure they are not
|
||||
// modified as we loop through them.
|
||||
auto callbacks_map_copy = discovered_peripheral_callbacks_map_;
|
||||
for (auto& it : callbacks_map_copy) {
|
||||
// Must fetch |ble_peripheral| again because it may have been invalidated by
|
||||
// a prior callback in this loop.
|
||||
auto* ble_peripheral = GetDiscoveredBlePeripheral(address);
|
||||
if (!ble_peripheral)
|
||||
break;
|
||||
|
||||
it.second.peripheral_lost_cb(*ble_peripheral,
|
||||
/*service_id=*/it.first.value());
|
||||
}
|
||||
|
||||
discovered_ble_peripherals_map_.erase(address);
|
||||
}
|
||||
|
||||
bool BleMedium::IsScanning() {
|
||||
return adapter_observer_.is_bound() && discovery_session_.is_bound() &&
|
||||
!discovered_peripheral_callbacks_map_.empty();
|
||||
}
|
||||
|
||||
void BleMedium::StopScanning() {
|
||||
// We cannot simply iterate over |discovered_peripheral_callbacks_map_|
|
||||
// because StopScanning() will erase the provided element.
|
||||
while (!discovered_peripheral_callbacks_map_.empty()) {
|
||||
StopScanning(/*service_id=*/discovered_peripheral_callbacks_map_.begin()
|
||||
->first.value());
|
||||
}
|
||||
}
|
||||
|
||||
chrome::BlePeripheral* BleMedium::GetDiscoveredBlePeripheral(
|
||||
const std::string& address) {
|
||||
auto it = discovered_ble_peripherals_map_.find(address);
|
||||
return it == discovered_ble_peripherals_map_.end() ? nullptr : &it->second;
|
||||
}
|
||||
|
||||
} // namespace chrome
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
@ -7,7 +7,10 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "chrome/services/sharing/nearby/platform_v2/ble_peripheral.h"
|
||||
#include "device/bluetooth/public/mojom/adapter.mojom.h"
|
||||
#include "mojo/public/cpp/bindings/receiver.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "third_party/nearby/src/cpp/platform_v2/api/ble.h"
|
||||
|
||||
namespace location {
|
||||
@ -15,9 +18,12 @@ namespace nearby {
|
||||
namespace chrome {
|
||||
|
||||
// Concrete BleMedium implementation.
|
||||
class BleMedium : public api::BleMedium {
|
||||
// api::BleMedium is a synchronous interface, so this implementation consumes
|
||||
// the synchronous signatures of bluetooth::mojom::Adapter methods.
|
||||
class BleMedium : public api::BleMedium,
|
||||
public bluetooth::mojom::AdapterObserver {
|
||||
public:
|
||||
BleMedium();
|
||||
explicit BleMedium(bluetooth::mojom::Adapter* adapter);
|
||||
~BleMedium() override;
|
||||
|
||||
BleMedium(const BleMedium&) = delete;
|
||||
@ -38,6 +44,44 @@ class BleMedium : public api::BleMedium {
|
||||
std::unique_ptr<api::BleSocket> Connect(
|
||||
api::BlePeripheral& ble_peripheral,
|
||||
const std::string& service_id) override;
|
||||
|
||||
private:
|
||||
// bluetooth::mojom::AdapterObserver:
|
||||
void PresentChanged(bool present) override;
|
||||
void PoweredChanged(bool powered) override;
|
||||
void DiscoverableChanged(bool discoverable) override;
|
||||
void DiscoveringChanged(bool discovering) override;
|
||||
void DeviceAdded(bluetooth::mojom::DeviceInfoPtr device) override;
|
||||
void DeviceChanged(bluetooth::mojom::DeviceInfoPtr device) override;
|
||||
void DeviceRemoved(bluetooth::mojom::DeviceInfoPtr device) override;
|
||||
|
||||
// Query if any service IDs are being scanned for.
|
||||
bool IsScanning();
|
||||
|
||||
// End discovery for all requested services.
|
||||
void StopScanning();
|
||||
|
||||
// Returns nullptr if no BlePeripheral at |address| exists.
|
||||
chrome::BlePeripheral* GetDiscoveredBlePeripheral(const std::string& address);
|
||||
|
||||
// This reference is owned by the top-level Nearby Connections interface and
|
||||
// will always outlive this object.
|
||||
bluetooth::mojom::Adapter* adapter_ = nullptr;
|
||||
|
||||
// |adapter_observer_| is only set and bound during active discovery so that
|
||||
// events we don't care about outside of discovery don't pile up.
|
||||
mojo::Receiver<bluetooth::mojom::AdapterObserver> adapter_observer_{this};
|
||||
|
||||
// Keyed by requested service UUID. Discovery is active as long as this map is
|
||||
// non-empty.
|
||||
std::map<device::BluetoothUUID, DiscoveredPeripheralCallback>
|
||||
discovered_peripheral_callbacks_map_;
|
||||
|
||||
// Only set while discovery is active.
|
||||
mojo::Remote<bluetooth::mojom::DiscoverySession> discovery_session_;
|
||||
|
||||
// Keyed by address of advertising remote device.
|
||||
std::map<std::string, chrome::BlePeripheral> discovered_ble_peripherals_map_;
|
||||
};
|
||||
|
||||
} // namespace chrome
|
||||
|
@ -6,9 +6,16 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/optional.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/test/bind_test_util.h"
|
||||
#include "base/test/task_environment.h"
|
||||
#include "chrome/services/sharing/nearby/platform_v2/ble_peripheral.h"
|
||||
#include "chrome/services/sharing/nearby/platform_v2/bluetooth_device.h"
|
||||
#include "chrome/services/sharing/nearby/test_support/fake_adapter.h"
|
||||
#include "mojo/public/cpp/bindings/pending_remote.h"
|
||||
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
|
||||
#include "mojo/public/cpp/bindings/shared_remote.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace location {
|
||||
@ -18,6 +25,11 @@ namespace chrome {
|
||||
namespace {
|
||||
|
||||
const char kServiceName[] = "NearbySharing";
|
||||
const char kServiceId1[] = "00000000-0000-0000-0000-000000000001";
|
||||
const char kServiceId2[] = "00000000-0000-0000-0000-000000000002";
|
||||
const char kDeviceAddress[] = "DeviceAddress";
|
||||
const char kDeviceServiceData1Str[] = "Device_Advertisement1";
|
||||
const char kDeviceServiceData2Str[] = "Device_Advertisement2";
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -28,11 +40,181 @@ class BleMediumTest : public testing::Test {
|
||||
BleMediumTest(const BleMediumTest&) = delete;
|
||||
BleMediumTest& operator=(const BleMediumTest&) = delete;
|
||||
|
||||
void SetUp() override { ble_medium_ = std::make_unique<BleMedium>(); }
|
||||
void SetUp() override {
|
||||
auto fake_adapter = std::make_unique<bluetooth::FakeAdapter>();
|
||||
fake_adapter_ = fake_adapter.get();
|
||||
|
||||
mojo::PendingRemote<bluetooth::mojom::Adapter> pending_adapter;
|
||||
|
||||
mojo::MakeSelfOwnedReceiver(
|
||||
std::move(fake_adapter),
|
||||
pending_adapter.InitWithNewPipeAndPassReceiver());
|
||||
|
||||
remote_adapter_.Bind(std::move(pending_adapter),
|
||||
/*bind_task_runner=*/nullptr);
|
||||
|
||||
ble_medium_ = std::make_unique<BleMedium>(remote_adapter_.get());
|
||||
|
||||
discovered_peripheral_callback_ = {
|
||||
.peripheral_discovered_cb =
|
||||
[this](api::BlePeripheral& peripheral,
|
||||
const std::string& service_id) {
|
||||
OnPeripheralDiscovered(peripheral, service_id);
|
||||
},
|
||||
.peripheral_lost_cb =
|
||||
[this](api::BlePeripheral& peripheral,
|
||||
const std::string& service_id) {
|
||||
OnPeripheralLost(peripheral, service_id);
|
||||
}};
|
||||
}
|
||||
|
||||
protected:
|
||||
void StartScanning(const std::string& service_id) {
|
||||
EXPECT_EQ(!scanning_service_ids_set_.empty(),
|
||||
fake_adapter_->IsDiscoverySessionActive());
|
||||
scanning_service_ids_set_.insert(service_id);
|
||||
EXPECT_TRUE(ble_medium_->StartScanning(service_id,
|
||||
discovered_peripheral_callback_));
|
||||
EXPECT_TRUE(fake_adapter_->IsDiscoverySessionActive());
|
||||
}
|
||||
|
||||
void StopScanning(const std::string& service_id) {
|
||||
base::RunLoop run_loop;
|
||||
|
||||
bool is_last_service = scanning_service_ids_set_.size() == 1u;
|
||||
if (is_last_service) {
|
||||
fake_adapter_->SetDiscoverySessionDestroyedCallback(
|
||||
run_loop.QuitClosure());
|
||||
}
|
||||
|
||||
ble_medium_->StopScanning(service_id);
|
||||
|
||||
if (is_last_service)
|
||||
run_loop.Run();
|
||||
|
||||
EXPECT_EQ(!is_last_service, fake_adapter_->IsDiscoverySessionActive());
|
||||
scanning_service_ids_set_.erase(service_id);
|
||||
}
|
||||
|
||||
void NotifyDeviceAdded(
|
||||
const std::string& address,
|
||||
const base::flat_map<device::BluetoothUUID, std::vector<uint8_t>>&
|
||||
service_data_map,
|
||||
uint32_t num_expected_peripherals_discovered) {
|
||||
base::RunLoop run_loop;
|
||||
SetOnExpectedPeripheralsDiscoveredCallback(
|
||||
run_loop.QuitClosure(), num_expected_peripherals_discovered);
|
||||
fake_adapter_->NotifyDeviceAdded(
|
||||
CreateDeviceInfo(address, service_data_map));
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
void NotifyDeviceChanged(
|
||||
const std::string& address,
|
||||
const base::flat_map<device::BluetoothUUID, std::vector<uint8_t>>&
|
||||
service_data_map,
|
||||
uint32_t num_expected_peripherals_discovered) {
|
||||
base::RunLoop run_loop;
|
||||
SetOnExpectedPeripheralsDiscoveredCallback(
|
||||
run_loop.QuitClosure(), num_expected_peripherals_discovered);
|
||||
fake_adapter_->NotifyDeviceChanged(
|
||||
CreateDeviceInfo(address, service_data_map));
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
void NotifyDeviceRemoved(const std::string& address,
|
||||
uint32_t num_expected_peripherals_lost) {
|
||||
base::RunLoop run_loop;
|
||||
SetOnExpectedPeripheralsLostCallback(run_loop.QuitClosure(),
|
||||
num_expected_peripherals_lost);
|
||||
fake_adapter_->NotifyDeviceRemoved(
|
||||
CreateDeviceInfo(address, /*service_data_map=*/{}));
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetByteVector(const std::string& str) {
|
||||
return std::vector<uint8_t>(str.begin(), str.end());
|
||||
}
|
||||
|
||||
void VerifyByteArrayEquals(const ByteArray& byte_array,
|
||||
const std::string& expected_value) {
|
||||
EXPECT_EQ(expected_value,
|
||||
std::string(byte_array.data(), byte_array.size()));
|
||||
}
|
||||
|
||||
bluetooth::FakeAdapter* fake_adapter_;
|
||||
mojo::SharedRemote<bluetooth::mojom::Adapter> remote_adapter_;
|
||||
std::unique_ptr<BleMedium> ble_medium_;
|
||||
|
||||
BleMedium::DiscoveredPeripheralCallback discovered_peripheral_callback_;
|
||||
|
||||
std::vector<std::pair<api::BlePeripheral*, std::string>>
|
||||
last_peripheral_discovered_args_;
|
||||
std::vector<std::pair<api::BlePeripheral*, std::string>>
|
||||
last_peripheral_lost_args_;
|
||||
|
||||
private:
|
||||
void SetOnExpectedPeripheralsDiscoveredCallback(
|
||||
base::OnceClosure callback,
|
||||
uint32_t num_expected_peripherals_discovered) {
|
||||
on_expected_peripherals_discovered_callback_ = std::move(callback);
|
||||
num_expected_peripherals_discovered_ = num_expected_peripherals_discovered;
|
||||
last_peripheral_discovered_args_.clear();
|
||||
}
|
||||
|
||||
void SetOnExpectedPeripheralsLostCallback(
|
||||
base::OnceClosure callback,
|
||||
uint32_t num_expected_peripherals_lost) {
|
||||
on_expected_peripherals_lost_callback_ = std::move(callback);
|
||||
num_expected_peripherals_lost_ = num_expected_peripherals_lost;
|
||||
last_peripheral_lost_args_.clear();
|
||||
}
|
||||
|
||||
void OnPeripheralDiscovered(api::BlePeripheral& ble_peripheral,
|
||||
const std::string& service_id) {
|
||||
last_peripheral_discovered_args_.emplace_back(
|
||||
std::make_pair(&ble_peripheral, service_id));
|
||||
|
||||
if (last_peripheral_discovered_args_.size() ==
|
||||
num_expected_peripherals_discovered_) {
|
||||
std::move(on_expected_peripherals_discovered_callback_).Run();
|
||||
num_expected_peripherals_discovered_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void OnPeripheralLost(api::BlePeripheral& ble_peripheral,
|
||||
const std::string& service_id) {
|
||||
last_peripheral_lost_args_.emplace_back(
|
||||
std::make_pair(&ble_peripheral, service_id));
|
||||
|
||||
if (last_peripheral_lost_args_.size() == num_expected_peripherals_lost_) {
|
||||
std::move(on_expected_peripherals_lost_callback_).Run();
|
||||
num_expected_peripherals_lost_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bluetooth::mojom::DeviceInfoPtr CreateDeviceInfo(
|
||||
const std::string& address,
|
||||
const base::flat_map<device::BluetoothUUID, std::vector<uint8_t>>&
|
||||
service_data_map) {
|
||||
// Do not set |name|. This reflects Chrome's usual representation of a BLE
|
||||
// advertisement.
|
||||
auto device_info = bluetooth::mojom::DeviceInfo::New();
|
||||
device_info->address = address;
|
||||
device_info->name_for_display = address;
|
||||
device_info->service_data_map = service_data_map;
|
||||
|
||||
return device_info;
|
||||
}
|
||||
|
||||
std::set<std::string> scanning_service_ids_set_;
|
||||
|
||||
base::OnceClosure on_expected_peripherals_discovered_callback_;
|
||||
uint32_t num_expected_peripherals_discovered_ = 0;
|
||||
|
||||
base::OnceClosure on_expected_peripherals_lost_callback_;
|
||||
uint32_t num_expected_peripherals_lost_ = 0;
|
||||
|
||||
base::test::TaskEnvironment task_environment_;
|
||||
};
|
||||
|
||||
@ -40,8 +222,77 @@ TEST_F(BleMediumTest, TestAdvertising) {
|
||||
// TODO(b/154845685): Write test.
|
||||
}
|
||||
|
||||
TEST_F(BleMediumTest, TestScanning) {
|
||||
// TODO(b/154848193): Write test.
|
||||
TEST_F(BleMediumTest, TestScanning_OneService) {
|
||||
StartScanning(kServiceId1);
|
||||
|
||||
base::flat_map<device::BluetoothUUID, std::vector<uint8_t>> service_data_map;
|
||||
service_data_map.insert_or_assign(device::BluetoothUUID(kServiceId1),
|
||||
GetByteVector(kDeviceServiceData1Str));
|
||||
|
||||
NotifyDeviceAdded(kDeviceAddress, service_data_map,
|
||||
/*num_expected_peripherals_discovered=*/1u);
|
||||
ASSERT_EQ(1u, last_peripheral_discovered_args_.size());
|
||||
auto& last_peripheral_discovered_args = last_peripheral_discovered_args_[0];
|
||||
const auto* first_discovered_ble_peripheral =
|
||||
last_peripheral_discovered_args.first;
|
||||
EXPECT_EQ(kServiceId1, last_peripheral_discovered_args.second);
|
||||
VerifyByteArrayEquals(
|
||||
first_discovered_ble_peripheral->GetAdvertisementBytes(kServiceId1),
|
||||
kDeviceServiceData1Str);
|
||||
|
||||
// The same information should be returned on a DeviceChanged event, with
|
||||
// the same BlePeripheral reference.
|
||||
NotifyDeviceChanged(kDeviceAddress, service_data_map,
|
||||
/*num_expected_peripherals_discovered=*/1u);
|
||||
ASSERT_EQ(1u, last_peripheral_discovered_args_.size());
|
||||
last_peripheral_discovered_args = last_peripheral_discovered_args_[0];
|
||||
EXPECT_EQ(first_discovered_ble_peripheral,
|
||||
last_peripheral_discovered_args.first);
|
||||
EXPECT_EQ(kServiceId1, last_peripheral_discovered_args.second);
|
||||
VerifyByteArrayEquals(
|
||||
last_peripheral_discovered_args.first->GetAdvertisementBytes(kServiceId1),
|
||||
kDeviceServiceData1Str);
|
||||
|
||||
// Again, the same BlePeripheral reference should be marked as lost.
|
||||
NotifyDeviceRemoved(kDeviceAddress, /*num_expected_peripherals_lost=*/1u);
|
||||
ASSERT_EQ(1u, last_peripheral_lost_args_.size());
|
||||
const auto& last_peripheral_lost_args = last_peripheral_lost_args_[0];
|
||||
const auto* lost_ble_peripheral = last_peripheral_lost_args.first;
|
||||
EXPECT_EQ(first_discovered_ble_peripheral, lost_ble_peripheral);
|
||||
EXPECT_EQ(kServiceId1, last_peripheral_lost_args.second);
|
||||
|
||||
StopScanning(kServiceId1);
|
||||
}
|
||||
|
||||
TEST_F(BleMediumTest, TestScanning_MultipleServices) {
|
||||
StartScanning(kServiceId1);
|
||||
StartScanning(kServiceId2);
|
||||
|
||||
base::flat_map<device::BluetoothUUID, std::vector<uint8_t>> service_data_map;
|
||||
service_data_map.insert_or_assign(device::BluetoothUUID(kServiceId1),
|
||||
GetByteVector(kDeviceServiceData1Str));
|
||||
service_data_map.insert_or_assign(device::BluetoothUUID(kServiceId2),
|
||||
GetByteVector(kDeviceServiceData2Str));
|
||||
|
||||
// Discovering a device with 2 desired service ids should trigger discovery
|
||||
// callbacks for both.
|
||||
NotifyDeviceAdded(kDeviceAddress, service_data_map,
|
||||
/*num_expected_peripherals_discovered=*/2u);
|
||||
ASSERT_EQ(2u, last_peripheral_discovered_args_.size());
|
||||
VerifyByteArrayEquals(
|
||||
last_peripheral_discovered_args_[0].first->GetAdvertisementBytes(
|
||||
kServiceId1),
|
||||
kDeviceServiceData1Str);
|
||||
VerifyByteArrayEquals(
|
||||
last_peripheral_discovered_args_[1].first->GetAdvertisementBytes(
|
||||
kServiceId2),
|
||||
kDeviceServiceData2Str);
|
||||
|
||||
NotifyDeviceRemoved(kDeviceAddress, /*num_expected_peripherals_lost=*/2u);
|
||||
ASSERT_EQ(2u, last_peripheral_lost_args_.size());
|
||||
|
||||
StopScanning(kServiceId1);
|
||||
StopScanning(kServiceId2);
|
||||
}
|
||||
|
||||
TEST_F(BleMediumTest, TestStartAcceptingConnections) {
|
||||
@ -51,8 +302,7 @@ TEST_F(BleMediumTest, TestStartAcceptingConnections) {
|
||||
}
|
||||
|
||||
TEST_F(BleMediumTest, TestConnect) {
|
||||
BluetoothDevice bluetooth_device(bluetooth::mojom::DeviceInfo::New());
|
||||
BlePeripheral ble_peripheral(bluetooth_device);
|
||||
chrome::BlePeripheral ble_peripheral(bluetooth::mojom::DeviceInfo::New());
|
||||
|
||||
// Connect() should do nothing and not return a valid api::BleSocket.
|
||||
EXPECT_FALSE(ble_medium_->Connect(ble_peripheral, kServiceName));
|
||||
|
@ -8,19 +8,31 @@ namespace location {
|
||||
namespace nearby {
|
||||
namespace chrome {
|
||||
|
||||
BlePeripheral::BlePeripheral(api::BluetoothDevice& bluetooth_device) {}
|
||||
BlePeripheral::BlePeripheral(bluetooth::mojom::DeviceInfoPtr device_info)
|
||||
: device_info_(std::move(device_info)) {}
|
||||
|
||||
BlePeripheral::~BlePeripheral() = default;
|
||||
|
||||
std::string BlePeripheral::GetName() const {
|
||||
// TODO(hansberry): Implement.
|
||||
return std::string();
|
||||
return device_info_->name_for_display;
|
||||
}
|
||||
|
||||
ByteArray BlePeripheral::GetAdvertisementBytes(
|
||||
const std::string& service_id) const {
|
||||
// TODO(hansberry): Implement.
|
||||
return ByteArray();
|
||||
const auto& service_data_map = device_info_->service_data_map;
|
||||
|
||||
auto it = service_data_map.find(device::BluetoothUUID(service_id));
|
||||
if (it == service_data_map.end())
|
||||
return ByteArray();
|
||||
|
||||
std::string service_data(it->second.begin(), it->second.end());
|
||||
return ByteArray(service_data);
|
||||
}
|
||||
|
||||
void BlePeripheral::UpdateDeviceInfo(
|
||||
bluetooth::mojom::DeviceInfoPtr device_info) {
|
||||
DCHECK_EQ(device_info_->address, device_info->address);
|
||||
device_info_ = std::move(device_info);
|
||||
}
|
||||
|
||||
} // namespace chrome
|
||||
|
@ -5,6 +5,7 @@
|
||||
#ifndef CHROME_SERVICES_SHARING_NEARBY_PLATFORM_V2_BLE_PERIPHERAL_H_
|
||||
#define CHROME_SERVICES_SHARING_NEARBY_PLATFORM_V2_BLE_PERIPHERAL_H_
|
||||
|
||||
#include "device/bluetooth/public/mojom/adapter.mojom.h"
|
||||
#include "third_party/nearby/src/cpp/platform_v2/api/ble.h"
|
||||
|
||||
namespace location {
|
||||
@ -14,7 +15,7 @@ namespace chrome {
|
||||
// Concrete BlePeripheral implementation.
|
||||
class BlePeripheral : public api::BlePeripheral {
|
||||
public:
|
||||
explicit BlePeripheral(api::BluetoothDevice& bluetooth_device);
|
||||
explicit BlePeripheral(bluetooth::mojom::DeviceInfoPtr device_info);
|
||||
~BlePeripheral() override;
|
||||
|
||||
BlePeripheral(const BlePeripheral&) = delete;
|
||||
@ -23,6 +24,11 @@ class BlePeripheral : public api::BlePeripheral {
|
||||
// api::BlePeripheral:
|
||||
std::string GetName() const override;
|
||||
ByteArray GetAdvertisementBytes(const std::string& service_id) const override;
|
||||
|
||||
void UpdateDeviceInfo(bluetooth::mojom::DeviceInfoPtr device_info);
|
||||
|
||||
private:
|
||||
bluetooth::mojom::DeviceInfoPtr device_info_;
|
||||
};
|
||||
|
||||
} // namespace chrome
|
||||
|
@ -262,6 +262,7 @@ BluetoothInternalsTest.prototype = {
|
||||
nameForDisplay: 'AAA',
|
||||
rssi: {value: -40},
|
||||
isGattConnected: false,
|
||||
serviceDataMap: {},
|
||||
services: [],
|
||||
};
|
||||
},
|
||||
@ -277,6 +278,7 @@ BluetoothInternalsTest.prototype = {
|
||||
nameForDisplay: 'BBB',
|
||||
rssi: null,
|
||||
isGattConnected: false,
|
||||
serviceDataMap: {},
|
||||
services: [],
|
||||
};
|
||||
},
|
||||
@ -291,6 +293,7 @@ BluetoothInternalsTest.prototype = {
|
||||
address: 'CC:CC:84:96:92:84',
|
||||
name: 'CCC',
|
||||
nameForDisplay: 'CCC',
|
||||
serviceDataMap: {},
|
||||
isGattConnected: false,
|
||||
};
|
||||
},
|
||||
|
@ -45,6 +45,9 @@ mojom::DeviceInfoPtr Device::ConstructDeviceInfoStruct(
|
||||
device_info->rssi->value = device->GetInquiryRSSI().value();
|
||||
}
|
||||
|
||||
for (auto const& it : device->GetServiceData())
|
||||
device_info->service_data_map.insert_or_assign(it.first, it.second);
|
||||
|
||||
return device_info;
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,13 @@ struct DeviceInfo {
|
||||
string address;
|
||||
bool is_gatt_connected;
|
||||
RSSIWrapper? rssi;
|
||||
|
||||
// Important note: the "service data" associated with each UUID is an
|
||||
// arbitrary binary blob of data provided by a likely untrustworthy device.
|
||||
// Clients are responsible for safely parsing this information; please see
|
||||
// "The Rule of 2" (//docs/security/rule-of-2.md). C++ clients must parse this
|
||||
// blob in a sandbox process.
|
||||
map<UUID, array<uint8>> service_data_map;
|
||||
};
|
||||
|
||||
struct ServiceInfo {
|
||||
|
Reference in New Issue
Block a user