Commit 5918b49c authored by Ryan Hansberry's avatar Ryan Hansberry Committed by Commit Bot

[Nearby] Implement BleMedium advertisement registration.

Implement BleMedium::StartAdvertising() and
BleMedium::StopAdvertising(). This requires adding a
bluetooth::mojom::Adapter::RegisterAdvertisement() method and a new
bluetooth::mojom::Advertisement interface.

See go/nearby-chrome-bt for more details.

Bug: 154845685
Change-Id: Ie5b65fe11702676e29558faf7d021daefc03b15f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2378765
Commit-Queue: Ryan Hansberry <hansberry@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarJames Vecore <vecore@google.com>
Cr-Commit-Position: refs/heads/master@{#806873}
parent 1bac67ba
...@@ -12,19 +12,48 @@ namespace chrome { ...@@ -12,19 +12,48 @@ namespace chrome {
BleMedium::BleMedium(bluetooth::mojom::Adapter* adapter) : adapter_(adapter) {} BleMedium::BleMedium(bluetooth::mojom::Adapter* adapter) : adapter_(adapter) {}
BleMedium::~BleMedium() = default; BleMedium::~BleMedium() {
for (auto& it : registered_advertisements_map_) {
// Note: this call is blocking.
it.second->Unregister();
}
}
bool BleMedium::StartAdvertising(const std::string& service_id, bool BleMedium::StartAdvertising(const std::string& service_id,
const ByteArray& advertisement) { const ByteArray& advertisement) {
// TODO(b/154845685): Implement this method. StopAdvertising(service_id);
NOTIMPLEMENTED();
return false; auto service_uuid = device::BluetoothUUID(service_id);
mojo::PendingRemote<bluetooth::mojom::Advertisement> pending_advertisement;
bool success = adapter_->RegisterAdvertisement(
service_uuid,
std::vector<uint8_t>(advertisement.data(),
advertisement.data() + advertisement.size()),
&pending_advertisement);
if (!success || !pending_advertisement.is_valid())
return false;
auto& remote_advertisement =
registered_advertisements_map_
.emplace(service_uuid, std::move(pending_advertisement))
.first->second;
remote_advertisement.set_disconnect_handler(base::BindOnce(
&BleMedium::AdvertisementReleased, base::Unretained(this), service_uuid));
return true;
} }
bool BleMedium::StopAdvertising(const std::string& service_id) { bool BleMedium::StopAdvertising(const std::string& service_id) {
// TODO(b/154845685): Implement this method. auto it =
NOTIMPLEMENTED(); registered_advertisements_map_.find(device::BluetoothUUID(service_id));
return false; if (it == registered_advertisements_map_.end())
return true;
bool success = it->second->Unregister();
registered_advertisements_map_.erase(it);
return success;
} }
bool BleMedium::StartScanning(const std::string& service_id, bool BleMedium::StartScanning(const std::string& service_id,
...@@ -209,6 +238,11 @@ void BleMedium::DeviceRemoved(bluetooth::mojom::DeviceInfoPtr device) { ...@@ -209,6 +238,11 @@ void BleMedium::DeviceRemoved(bluetooth::mojom::DeviceInfoPtr device) {
discovered_ble_peripherals_map_.erase(address); discovered_ble_peripherals_map_.erase(address);
} }
void BleMedium::AdvertisementReleased(
const device::BluetoothUUID& service_uuid) {
registered_advertisements_map_.erase(service_uuid);
}
bool BleMedium::IsScanning() { bool BleMedium::IsScanning() {
return adapter_observer_.is_bound() && discovery_session_.is_bound() && return adapter_observer_.is_bound() && discovery_session_.is_bound() &&
!discovered_peripheral_callbacks_map_.empty(); !discovered_peripheral_callbacks_map_.empty();
......
...@@ -55,6 +55,9 @@ class BleMedium : public api::BleMedium, ...@@ -55,6 +55,9 @@ class BleMedium : public api::BleMedium,
void DeviceChanged(bluetooth::mojom::DeviceInfoPtr device) override; void DeviceChanged(bluetooth::mojom::DeviceInfoPtr device) override;
void DeviceRemoved(bluetooth::mojom::DeviceInfoPtr device) override; void DeviceRemoved(bluetooth::mojom::DeviceInfoPtr device) override;
// A bluetooth::mojom::Advertisement message pipe was destroyed.
void AdvertisementReleased(const device::BluetoothUUID& service_uuid);
// Query if any service IDs are being scanned for. // Query if any service IDs are being scanned for.
bool IsScanning(); bool IsScanning();
...@@ -72,6 +75,10 @@ class BleMedium : public api::BleMedium, ...@@ -72,6 +75,10 @@ class BleMedium : public api::BleMedium,
// events we don't care about outside of discovery don't pile up. // events we don't care about outside of discovery don't pile up.
mojo::Receiver<bluetooth::mojom::AdapterObserver> adapter_observer_{this}; mojo::Receiver<bluetooth::mojom::AdapterObserver> adapter_observer_{this};
// Keyed by service UUID of the advertisement.
std::map<device::BluetoothUUID, mojo::Remote<bluetooth::mojom::Advertisement>>
registered_advertisements_map_;
// Keyed by requested service UUID. Discovery is active as long as this map is // Keyed by requested service UUID. Discovery is active as long as this map is
// non-empty. // non-empty.
std::map<device::BluetoothUUID, DiscoveredPeripheralCallback> std::map<device::BluetoothUUID, DiscoveredPeripheralCallback>
......
...@@ -31,6 +31,10 @@ const char kDeviceAddress[] = "DeviceAddress"; ...@@ -31,6 +31,10 @@ const char kDeviceAddress[] = "DeviceAddress";
const char kDeviceServiceData1Str[] = "Device_Advertisement1"; const char kDeviceServiceData1Str[] = "Device_Advertisement1";
const char kDeviceServiceData2Str[] = "Device_Advertisement2"; const char kDeviceServiceData2Str[] = "Device_Advertisement2";
std::vector<uint8_t> GetByteVector(const std::string& str) {
return std::vector<uint8_t>(str.begin(), str.end());
}
} // namespace } // namespace
class BleMediumTest : public testing::Test { class BleMediumTest : public testing::Test {
...@@ -132,10 +136,6 @@ class BleMediumTest : public testing::Test { ...@@ -132,10 +136,6 @@ class BleMediumTest : public testing::Test {
run_loop.Run(); 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, void VerifyByteArrayEquals(const ByteArray& byte_array,
const std::string& expected_value) { const std::string& expected_value) {
EXPECT_EQ(expected_value, EXPECT_EQ(expected_value,
...@@ -219,7 +219,48 @@ class BleMediumTest : public testing::Test { ...@@ -219,7 +219,48 @@ class BleMediumTest : public testing::Test {
}; };
TEST_F(BleMediumTest, TestAdvertising) { TEST_F(BleMediumTest, TestAdvertising) {
// TODO(b/154845685): Write test. ASSERT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData(
device::BluetoothUUID(kServiceId1)));
ASSERT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData(
device::BluetoothUUID(kServiceId2)));
ble_medium_->StartAdvertising(kServiceId1, ByteArray(kDeviceServiceData1Str));
EXPECT_EQ(GetByteVector(kDeviceServiceData1Str),
*fake_adapter_->GetRegisteredAdvertisementServiceData(
device::BluetoothUUID(kServiceId1)));
EXPECT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData(
device::BluetoothUUID(kServiceId2)));
ble_medium_->StartAdvertising(kServiceId2, ByteArray(kDeviceServiceData2Str));
EXPECT_TRUE(fake_adapter_->GetRegisteredAdvertisementServiceData(
device::BluetoothUUID(kServiceId1)));
EXPECT_EQ(GetByteVector(kDeviceServiceData2Str),
*fake_adapter_->GetRegisteredAdvertisementServiceData(
device::BluetoothUUID(kServiceId2)));
{
base::RunLoop run_loop;
fake_adapter_->SetAdvertisementDestroyedCallback(run_loop.QuitClosure());
ble_medium_->StopAdvertising(kServiceId1);
run_loop.Run();
}
EXPECT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData(
device::BluetoothUUID(kServiceId1)));
EXPECT_TRUE(fake_adapter_->GetRegisteredAdvertisementServiceData(
device::BluetoothUUID(kServiceId2)));
{
base::RunLoop run_loop;
fake_adapter_->SetAdvertisementDestroyedCallback(run_loop.QuitClosure());
ble_medium_->StopAdvertising(kServiceId2);
run_loop.Run();
}
EXPECT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData(
device::BluetoothUUID(kServiceId1)));
EXPECT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData(
device::BluetoothUUID(kServiceId2)));
} }
TEST_F(BleMediumTest, TestScanning_OneService) { TEST_F(BleMediumTest, TestScanning_OneService) {
......
...@@ -13,6 +13,21 @@ namespace bluetooth { ...@@ -13,6 +13,21 @@ namespace bluetooth {
namespace { namespace {
class FakeAdvertisement : public mojom::Advertisement {
public:
explicit FakeAdvertisement(base::OnceClosure on_destroy_callback)
: on_destroy_callback_(std::move(on_destroy_callback)) {}
~FakeAdvertisement() override { std::move(on_destroy_callback_).Run(); }
private:
// mojom::Advertisement:
void Unregister(UnregisterCallback callback) override {
std::move(callback).Run();
}
base::OnceClosure on_destroy_callback_;
};
class FakeDiscoverySession : public mojom::DiscoverySession { class FakeDiscoverySession : public mojom::DiscoverySession {
public: public:
explicit FakeDiscoverySession(base::OnceClosure on_destroy_callback) explicit FakeDiscoverySession(base::OnceClosure on_destroy_callback)
...@@ -88,6 +103,29 @@ void FakeAdapter::AddObserver( ...@@ -88,6 +103,29 @@ void FakeAdapter::AddObserver(
std::move(callback).Run(); std::move(callback).Run();
} }
void FakeAdapter::RegisterAdvertisement(
const device::BluetoothUUID& service_uuid,
const std::vector<uint8_t>& service_data,
RegisterAdvertisementCallback callback) {
if (!should_advertisement_registration_succeed_) {
std::move(callback).Run(mojo::NullRemote());
return;
}
registered_advertisements_map_.insert({service_uuid, service_data});
auto advertisement = std::make_unique<FakeAdvertisement>(
base::BindOnce(&FakeAdapter::OnAdvertisementDestroyed,
base::Unretained(this), service_uuid));
mojo::PendingRemote<mojom::Advertisement> pending_advertisement;
mojo::MakeSelfOwnedReceiver(
std::move(advertisement),
pending_advertisement.InitWithNewPipeAndPassReceiver());
std::move(callback).Run(std::move(pending_advertisement));
}
void FakeAdapter::SetDiscoverable(bool discoverable, void FakeAdapter::SetDiscoverable(bool discoverable,
SetDiscoverableCallback callback) { SetDiscoverableCallback callback) {
discoverable_ = discoverable; discoverable_ = discoverable;
...@@ -181,6 +219,17 @@ void FakeAdapter::SetShouldDiscoverySucceed(bool should_discovery_succeed) { ...@@ -181,6 +219,17 @@ void FakeAdapter::SetShouldDiscoverySucceed(bool should_discovery_succeed) {
should_discovery_succeed_ = should_discovery_succeed; should_discovery_succeed_ = should_discovery_succeed;
} }
void FakeAdapter::SetAdvertisementDestroyedCallback(
base::OnceClosure callback) {
on_advertisement_destroyed_callback_ = std::move(callback);
}
const std::vector<uint8_t>* FakeAdapter::GetRegisteredAdvertisementServiceData(
const device::BluetoothUUID& service_uuid) {
auto it = registered_advertisements_map_.find(service_uuid);
return it == registered_advertisements_map_.end() ? nullptr : &it->second;
}
void FakeAdapter::SetDiscoverySessionDestroyedCallback( void FakeAdapter::SetDiscoverySessionDestroyedCallback(
base::OnceClosure callback) { base::OnceClosure callback) {
on_discovery_session_destroyed_callback_ = std::move(callback); on_discovery_session_destroyed_callback_ = std::move(callback);
...@@ -218,6 +267,14 @@ void FakeAdapter::AllowIncomingConnectionForServiceNameAndUuidPair( ...@@ -218,6 +267,14 @@ void FakeAdapter::AllowIncomingConnectionForServiceNameAndUuidPair(
service_uuid); service_uuid);
} }
void FakeAdapter::OnAdvertisementDestroyed(
const device::BluetoothUUID& service_uuid) {
DCHECK(!registered_advertisements_map_.empty());
registered_advertisements_map_.erase(service_uuid);
if (on_advertisement_destroyed_callback_)
std::move(on_advertisement_destroyed_callback_).Run();
}
void FakeAdapter::OnDiscoverySessionDestroyed() { void FakeAdapter::OnDiscoverySessionDestroyed() {
DCHECK(discovery_session_); DCHECK(discovery_session_);
discovery_session_ = nullptr; discovery_session_ = nullptr;
......
...@@ -27,6 +27,9 @@ class FakeAdapter : public mojom::Adapter { ...@@ -27,6 +27,9 @@ class FakeAdapter : public mojom::Adapter {
void GetInfo(GetInfoCallback callback) override; void GetInfo(GetInfoCallback callback) override;
void AddObserver(mojo::PendingRemote<mojom::AdapterObserver> observer, void AddObserver(mojo::PendingRemote<mojom::AdapterObserver> observer,
AddObserverCallback callback) override; AddObserverCallback callback) override;
void RegisterAdvertisement(const device::BluetoothUUID& service_uuid,
const std::vector<uint8_t>& service_data,
RegisterAdvertisementCallback callback) override;
void SetDiscoverable(bool discoverable, void SetDiscoverable(bool discoverable,
SetDiscoverableCallback callback) override; SetDiscoverableCallback callback) override;
void SetName(const std::string& name, SetNameCallback callback) override; void SetName(const std::string& name, SetNameCallback callback) override;
...@@ -39,6 +42,9 @@ class FakeAdapter : public mojom::Adapter { ...@@ -39,6 +42,9 @@ class FakeAdapter : public mojom::Adapter {
const device::BluetoothUUID& service_uuid, const device::BluetoothUUID& service_uuid,
CreateRfcommServiceCallback callback) override; CreateRfcommServiceCallback callback) override;
void SetAdvertisementDestroyedCallback(base::OnceClosure callback);
const std::vector<uint8_t>* GetRegisteredAdvertisementServiceData(
const device::BluetoothUUID& service_uuid);
void SetShouldDiscoverySucceed(bool should_discovery_succeed); void SetShouldDiscoverySucceed(bool should_discovery_succeed);
void SetDiscoverySessionDestroyedCallback(base::OnceClosure callback); void SetDiscoverySessionDestroyedCallback(base::OnceClosure callback);
bool IsDiscoverySessionActive(); bool IsDiscoverySessionActive();
...@@ -61,11 +67,18 @@ class FakeAdapter : public mojom::Adapter { ...@@ -61,11 +67,18 @@ class FakeAdapter : public mojom::Adapter {
bool discovering_ = false; bool discovering_ = false;
private: private:
void OnAdvertisementDestroyed(const device::BluetoothUUID& service_uuid);
void OnDiscoverySessionDestroyed(); void OnDiscoverySessionDestroyed();
mojom::DiscoverySession* discovery_session_ = nullptr; bool should_advertisement_registration_succeed_ = true;
std::map<device::BluetoothUUID, std::vector<uint8_t>>
registered_advertisements_map_;
base::OnceClosure on_advertisement_destroyed_callback_;
bool should_discovery_succeed_ = true; bool should_discovery_succeed_ = true;
mojom::DiscoverySession* discovery_session_ = nullptr;
base::OnceClosure on_discovery_session_destroyed_callback_; base::OnceClosure on_discovery_session_destroyed_callback_;
std::set<std::pair<std::string, device::BluetoothUUID>> std::set<std::pair<std::string, device::BluetoothUUID>>
allowed_connections_for_address_and_uuid_pair_; allowed_connections_for_address_and_uuid_pair_;
std::set<std::pair<std::string, device::BluetoothUUID>> std::set<std::pair<std::string, device::BluetoothUUID>>
......
...@@ -126,6 +126,11 @@ BluetoothInternalsTest.prototype = { ...@@ -126,6 +126,11 @@ BluetoothInternalsTest.prototype = {
this.methodCalled('addObserver', observer); this.methodCalled('addObserver', observer);
} }
async registerAdvertisement() {
this.methodCalled('registerAdvertisement');
return {advertisement: null};
}
async setDiscoverable() { async setDiscoverable() {
this.methodCalled('setDiscoverable'); this.methodCalled('setDiscoverable');
return {success: true}; return {success: true};
......
...@@ -20,6 +20,8 @@ is_linux_without_udev = (is_linux || is_chromeos) && !use_udev ...@@ -20,6 +20,8 @@ is_linux_without_udev = (is_linux || is_chromeos) && !use_udev
test("device_unittests") { test("device_unittests") {
sources = [ sources = [
"base/synchronization/one_writer_seqlock_unittest.cc", "base/synchronization/one_writer_seqlock_unittest.cc",
"bluetooth/adapter_unittest.cc",
"bluetooth/advertisement_unittest.cc",
"bluetooth/bluetooth_adapter_android_unittest.cc", "bluetooth/bluetooth_adapter_android_unittest.cc",
"bluetooth/bluetooth_adapter_mac_metrics_unittest.mm", "bluetooth/bluetooth_adapter_mac_metrics_unittest.mm",
"bluetooth/bluetooth_adapter_mac_unittest.mm", "bluetooth/bluetooth_adapter_mac_unittest.mm",
......
...@@ -36,6 +36,8 @@ source_set("deprecated_experimental_mojo") { ...@@ -36,6 +36,8 @@ source_set("deprecated_experimental_mojo") {
"//device/bluetooth/public/mojom/gatt_result_type_converter.h", "//device/bluetooth/public/mojom/gatt_result_type_converter.h",
"adapter.cc", "adapter.cc",
"adapter.h", "adapter.h",
"advertisement.cc",
"advertisement.h",
"device.cc", "device.cc",
"device.h", "device.h",
"discovery_session.cc", "discovery_session.cc",
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/callback_helpers.h" #include "base/callback_helpers.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "device/bluetooth/advertisement.h"
#include "device/bluetooth/bluetooth_socket.h" #include "device/bluetooth/bluetooth_socket.h"
#include "device/bluetooth/device.h" #include "device/bluetooth/device.h"
#include "device/bluetooth/discovery_session.h" #include "device/bluetooth/discovery_session.h"
...@@ -86,6 +87,31 @@ void Adapter::AddObserver(mojo::PendingRemote<mojom::AdapterObserver> observer, ...@@ -86,6 +87,31 @@ void Adapter::AddObserver(mojo::PendingRemote<mojom::AdapterObserver> observer,
std::move(callback).Run(); std::move(callback).Run();
} }
void Adapter::RegisterAdvertisement(const device::BluetoothUUID& service_uuid,
const std::vector<uint8_t>& service_data,
RegisterAdvertisementCallback callback) {
auto advertisement_data =
std::make_unique<device::BluetoothAdvertisement::Data>(
device::BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST);
auto uuid_list = std::make_unique<device::BluetoothAdvertisement::UUIDList>();
uuid_list->push_back(service_uuid.value());
advertisement_data->set_service_uuids(std::move(uuid_list));
auto service_data_map =
std::make_unique<device::BluetoothAdvertisement::ServiceData>();
service_data_map->emplace(service_uuid.value(), service_data);
advertisement_data->set_service_data(std::move(service_data_map));
auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
adapter_->RegisterAdvertisement(
std::move(advertisement_data),
base::BindOnce(&Adapter::OnRegisterAdvertisement,
weak_ptr_factory_.GetWeakPtr(), copyable_callback),
base::BindOnce(&Adapter::OnRegisterAdvertisementError,
weak_ptr_factory_.GetWeakPtr(), copyable_callback));
}
void Adapter::SetDiscoverable(bool discoverable, void Adapter::SetDiscoverable(bool discoverable,
SetDiscoverableCallback callback) { SetDiscoverableCallback callback) {
auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback)); auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
...@@ -205,6 +231,23 @@ void Adapter::OnConnectError( ...@@ -205,6 +231,23 @@ void Adapter::OnConnectError(
/*device=*/mojo::NullRemote()); /*device=*/mojo::NullRemote());
} }
void Adapter::OnRegisterAdvertisement(
RegisterAdvertisementCallback callback,
scoped_refptr<device::BluetoothAdvertisement> advertisement) {
mojo::PendingRemote<mojom::Advertisement> pending_advertisement;
mojo::MakeSelfOwnedReceiver(
std::make_unique<Advertisement>(std::move(advertisement)),
pending_advertisement.InitWithNewPipeAndPassReceiver());
std::move(callback).Run(std::move(pending_advertisement));
}
void Adapter::OnRegisterAdvertisementError(
RegisterAdvertisementCallback callback,
device::BluetoothAdvertisement::ErrorCode error_code) {
DLOG(ERROR) << "Failed to register advertisement, error code: " << error_code;
std::move(callback).Run(/*advertisement=*/mojo::NullRemote());
}
void Adapter::OnSetDiscoverable(SetDiscoverableCallback callback) { void Adapter::OnSetDiscoverable(SetDiscoverableCallback callback) {
std::move(callback).Run(/*success=*/true); std::move(callback).Run(/*success=*/true);
} }
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "device/bluetooth/bluetooth_adapter.h" #include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_advertisement.h"
#include "device/bluetooth/bluetooth_gatt_connection.h" #include "device/bluetooth/bluetooth_gatt_connection.h"
#include "device/bluetooth/public/cpp/bluetooth_uuid.h" #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
#include "device/bluetooth/public/mojom/adapter.mojom.h" #include "device/bluetooth/public/mojom/adapter.mojom.h"
...@@ -37,6 +38,9 @@ class Adapter : public mojom::Adapter, ...@@ -37,6 +38,9 @@ class Adapter : public mojom::Adapter,
void GetInfo(GetInfoCallback callback) override; void GetInfo(GetInfoCallback callback) override;
void AddObserver(mojo::PendingRemote<mojom::AdapterObserver> observer, void AddObserver(mojo::PendingRemote<mojom::AdapterObserver> observer,
AddObserverCallback callback) override; AddObserverCallback callback) override;
void RegisterAdvertisement(const device::BluetoothUUID& service_uuid,
const std::vector<uint8_t>& service_data,
RegisterAdvertisementCallback callback) override;
void SetDiscoverable(bool discoverable, void SetDiscoverable(bool discoverable,
SetDiscoverableCallback callback) override; SetDiscoverableCallback callback) override;
void SetName(const std::string& name, SetNameCallback callback) override; void SetName(const std::string& name, SetNameCallback callback) override;
...@@ -74,6 +78,13 @@ class Adapter : public mojom::Adapter, ...@@ -74,6 +78,13 @@ class Adapter : public mojom::Adapter,
void OnConnectError(ConnectToDeviceCallback callback, void OnConnectError(ConnectToDeviceCallback callback,
device::BluetoothDevice::ConnectErrorCode error_code); device::BluetoothDevice::ConnectErrorCode error_code);
void OnRegisterAdvertisement(
RegisterAdvertisementCallback callback,
scoped_refptr<device::BluetoothAdvertisement> advertisement);
void OnRegisterAdvertisementError(
RegisterAdvertisementCallback callback,
device::BluetoothAdvertisement::ErrorCode error_code);
void OnSetDiscoverable(SetDiscoverableCallback callback); void OnSetDiscoverable(SetDiscoverableCallback callback);
void OnSetDiscoverableError(SetDiscoverableCallback callback); void OnSetDiscoverableError(SetDiscoverableCallback callback);
......
// Copyright 2020 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 "device/bluetooth/adapter.h"
#include "base/callback_helpers.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h"
#include "device/bluetooth/bluetooth_advertisement.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/bluetooth/test/mock_bluetooth_advertisement.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using testing::NiceMock;
using testing::Return;
namespace {
const char kServiceId[] = "00000000-0000-0000-0000-000000000001";
const char kDeviceServiceDataStr[] = "ServiceData";
std::vector<uint8_t> GetByteVector(const std::string& str) {
return std::vector<uint8_t>(str.begin(), str.end());
}
class MockBluetoothAdapterWithAdvertisements
: public device::MockBluetoothAdapter {
public:
void RegisterAdvertisement(
std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement_data,
device::BluetoothAdapter::CreateAdvertisementCallback callback,
device::BluetoothAdapter::AdvertisementErrorCallback error_callback)
override {
last_register_advertisement_args_ =
std::make_pair(*advertisement_data->service_uuids(),
*advertisement_data->service_data());
if (should_advertisement_registration_succeed_) {
std::move(callback).Run(
base::MakeRefCounted<device::MockBluetoothAdvertisement>());
} else {
std::move(error_callback)
.Run(device::BluetoothAdvertisement::ErrorCode::
INVALID_ADVERTISEMENT_ERROR_CODE);
}
}
bool should_advertisement_registration_succeed_ = true;
base::Optional<std::pair<device::BluetoothAdvertisement::UUIDList,
device::BluetoothAdvertisement::ServiceData>>
last_register_advertisement_args_;
protected:
~MockBluetoothAdapterWithAdvertisements() override = default;
};
} // namespace
namespace bluetooth {
class AdapterTest : public testing::Test {
public:
AdapterTest() = default;
~AdapterTest() override = default;
AdapterTest(const AdapterTest&) = delete;
AdapterTest& operator=(const AdapterTest&) = delete;
void SetUp() override {
mock_bluetooth_adapter_ = base::MakeRefCounted<
NiceMock<MockBluetoothAdapterWithAdvertisements>>();
ON_CALL(*mock_bluetooth_adapter_, IsPresent()).WillByDefault(Return(true));
ON_CALL(*mock_bluetooth_adapter_, IsPowered()).WillByDefault(Return(true));
adapter_ = std::make_unique<Adapter>(mock_bluetooth_adapter_);
}
protected:
void VerifyRegisterAdvertisement(bool should_succeed) {
mock_bluetooth_adapter_->should_advertisement_registration_succeed_ =
should_succeed;
auto service_data = GetByteVector(kDeviceServiceDataStr);
mojo::Remote<mojom::Advertisement> advertisement;
base::RunLoop run_loop;
adapter_->RegisterAdvertisement(
device::BluetoothUUID(kServiceId), service_data,
base::BindLambdaForTesting([&](mojo::PendingRemote<mojom::Advertisement>
pending_advertisement) {
EXPECT_EQ(should_succeed, pending_advertisement.is_valid());
run_loop.Quit();
}));
run_loop.Run();
auto& uuid_list =
mock_bluetooth_adapter_->last_register_advertisement_args_->first;
EXPECT_EQ(1u, uuid_list.size());
EXPECT_EQ(kServiceId, uuid_list[0]);
EXPECT_EQ(
service_data,
mock_bluetooth_adapter_->last_register_advertisement_args_->second.at(
kServiceId));
}
scoped_refptr<NiceMock<MockBluetoothAdapterWithAdvertisements>>
mock_bluetooth_adapter_;
std::unique_ptr<Adapter> adapter_;
private:
base::test::TaskEnvironment task_environment_;
};
TEST_F(AdapterTest, TestRegisterAdvertisement_Success) {
VerifyRegisterAdvertisement(/*should_succeed=*/true);
}
TEST_F(AdapterTest, TestRegisterAdvertisement_Error) {
VerifyRegisterAdvertisement(/*should_succeed=*/false);
}
} // namespace bluetooth
// Copyright 2020 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 "device/bluetooth/advertisement.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
namespace bluetooth {
Advertisement::Advertisement(
scoped_refptr<device::BluetoothAdvertisement> bluetooth_advertisement)
: bluetooth_advertisement_(std::move(bluetooth_advertisement)) {}
Advertisement::~Advertisement() {
Unregister(base::DoNothing());
}
void Advertisement::Unregister(UnregisterCallback callback) {
if (!bluetooth_advertisement_)
return;
auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
bluetooth_advertisement_->Unregister(
base::BindOnce(&Advertisement::OnUnregister,
weak_ptr_factory_.GetWeakPtr(), copyable_callback),
base::BindOnce(&Advertisement::OnUnregisterError,
weak_ptr_factory_.GetWeakPtr(), copyable_callback));
}
void Advertisement::OnUnregister(UnregisterCallback callback) {
bluetooth_advertisement_.reset();
std::move(callback).Run();
}
void Advertisement::OnUnregisterError(
UnregisterCallback callback,
device::BluetoothAdvertisement::ErrorCode error_code) {
DLOG(ERROR) << "Failed to unregister advertisement, error code: "
<< error_code;
bluetooth_advertisement_.reset();
std::move(callback).Run();
}
} // namespace bluetooth
// Copyright 2020 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 DEVICE_BLUETOOTH_ADVERTISEMENT_H_
#define DEVICE_BLUETOOTH_ADVERTISEMENT_H_
#include "base/memory/ref_counted.h"
#include "device/bluetooth/bluetooth_advertisement.h"
#include "device/bluetooth/public/mojom/adapter.mojom.h"
namespace bluetooth {
// Implementation of Mojo Advertisement in
// device/bluetooth/public/mojom/adapter.mojom.
// Uses the platform abstraction of //device/bluetooth.
// An instance of this class is constructed by Adapter and strongly bound to its
// MessagePipe. When the instance is destroyed, the underlying
// BluetoothAdvertisement is destroyed.
class Advertisement : public mojom::Advertisement {
public:
explicit Advertisement(
scoped_refptr<device::BluetoothAdvertisement> bluetooth_advertisement);
~Advertisement() override;
Advertisement(const Advertisement&) = delete;
Advertisement& operator=(const Advertisement&) = delete;
// mojom::Advertisement:
void Unregister(UnregisterCallback callback) override;
private:
void OnUnregister(UnregisterCallback callback);
void OnUnregisterError(UnregisterCallback callback,
device::BluetoothAdvertisement::ErrorCode error_code);
scoped_refptr<device::BluetoothAdvertisement> bluetooth_advertisement_;
base::WeakPtrFactory<Advertisement> weak_ptr_factory_{this};
};
} // namespace bluetooth
#endif // DEVICE_BLUETOOTH_ADVERTISEMENT_H_
// Copyright 2020 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 "device/bluetooth/advertisement.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "device/bluetooth/bluetooth_advertisement.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
class FakeBluetoothAdvertisement : public device::BluetoothAdvertisement {
public:
// device::BluetoothAdvertisement:
void Unregister(SuccessCallback success_callback,
ErrorCallback error_callback) override {
called_unregister_ = true;
std::move(success_callback).Run();
}
bool called_unregister() { return called_unregister_; }
private:
~FakeBluetoothAdvertisement() override = default;
bool called_unregister_ = false;
};
} // namespace
namespace bluetooth {
class AdvertisementTest : public testing::Test {
public:
AdvertisementTest() = default;
~AdvertisementTest() override = default;
AdvertisementTest(const AdvertisementTest&) = delete;
AdvertisementTest& operator=(const AdvertisementTest&) = delete;
void SetUp() override {
fake_bluetooth_advertisement_ =
base::MakeRefCounted<FakeBluetoothAdvertisement>();
advertisement_ =
std::make_unique<Advertisement>(fake_bluetooth_advertisement_);
}
protected:
scoped_refptr<FakeBluetoothAdvertisement> fake_bluetooth_advertisement_;
std::unique_ptr<Advertisement> advertisement_;
private:
base::test::TaskEnvironment task_environment_;
};
TEST_F(AdvertisementTest, TestOnDestroyCallsUnregister) {
// When destroyed, |advertisement_| is expected to tear down its
// BluetoothAdvertisement.
ASSERT_FALSE(fake_bluetooth_advertisement_->called_unregister());
advertisement_.reset();
EXPECT_TRUE(fake_bluetooth_advertisement_->called_unregister());
}
TEST_F(AdvertisementTest, TestUnregister) {
ASSERT_FALSE(fake_bluetooth_advertisement_->called_unregister());
base::RunLoop run_loop;
advertisement_->Unregister(run_loop.QuitClosure());
run_loop.Run();
EXPECT_TRUE(fake_bluetooth_advertisement_->called_unregister());
}
} // namespace bluetooth
...@@ -49,6 +49,22 @@ struct AdapterInfo { ...@@ -49,6 +49,22 @@ struct AdapterInfo {
bool discovering; bool discovering;
}; };
// Represents an ongoing BLE advertisement. Releasing it will release the
// underlying object and stop the advertisement, but callers should prefer to
// let a call to Unregister() to finish first.
// Note: Methods which are declared [Sync] are for use by
// //chrome/services/sharing/nearby; all other usage of their synchronous
// signatures is strongly discouraged.
interface Advertisement {
// Use to gracefully stop advertising before destroying the message pipe. The
// reply callback can be used to synchronize an attempt to re-register an
// advertisement; attempting to register an advertisement without first
// releasing limited advertisement slots in the hardware may fail with a
// busy error.
[Sync]
Unregister() => ();
};
// Represents a request to discover nearby devices. // Represents a request to discover nearby devices.
// Note: Methods which are declared [Sync] are for use by // Note: Methods which are declared [Sync] are for use by
// //chrome/services/sharing/nearby; all other usage of their synchronous // //chrome/services/sharing/nearby; all other usage of their synchronous
...@@ -128,6 +144,22 @@ interface Adapter { ...@@ -128,6 +144,22 @@ interface Adapter {
[Sync] [Sync]
AddObserver(pending_remote<AdapterObserver> observer) => (); AddObserver(pending_remote<AdapterObserver> observer) => ();
// Requests the adapter to broadcast a BLE advertisement on |service_id| with
// the associated packet |service_data|. Returns null if advertisement is not
// registered successfully.
// Important notes:
// * This method registers a "non-connectable" advertisement. Any future
// effort to allow this API to support "connectable" advertisements would
// also require the addition of an API to support hosting a GATT server.
// * Bluetooth chips generally can only broadcast a few advertisements,
// sometimes even only one, simultaneously. This can be mitigated by
// operating systems "rotating" advertisements in the higher software layer,
// as Chrome OS does. Non-Chrome OS clients of this API are responsible for
// understanding their host OS's and/or hardware's limitations.
[Sync]
RegisterAdvertisement(UUID service_id, array<uint8> service_data) =>
(pending_remote<Advertisement>? advertisement);
// Requests the local device to make itself discoverable to nearby remote // Requests the local device to make itself discoverable to nearby remote
// devices. // devices.
[Sync] [Sync]
......
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