Commit 045d7d48 authored by James Vecore's avatar James Vecore Committed by Commit Bot

[Nearby] Add use_scan_response to RegisterAdvertisement

Nearby Share requires using the scan response in BLE advertising due to
the size of the data required. Previously using the scan response was
not possible in ChromeOS. These changes added some limited and
temporary support for a new d-bus property to specify the scan response
data: https://crrev.com/c/2458306 https://crrev.com/c/2459250.

This CL changes the adapter mojom interface to allow specifying that the
service data should be added the scan response and not the original
advertisement data when registering an advertisement. Due to the limited
and temporary Platform support (AD type 0x16 only), we kept the api
change as simple as possible. This change will likely be reverted or
modified depending on the shape of the final scan response API in
upstream bluez. Tracking that here: https://crbug.com/1136920


Bug: 1135699
Change-Id: I0513197efb1ab62cb75a2151fc738b89b5e9ec67
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2465132Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Commit-Queue: James Vecore <vecore@google.com>
Cr-Commit-Position: refs/heads/master@{#816726}
parent 4e254d25
......@@ -40,7 +40,7 @@ bool BleMedium::StartAdvertising(
service_uuid,
std::vector<uint8_t>(advertisement.data(),
advertisement.data() + advertisement.size()),
&pending_advertisement);
/*use_scan_data=*/true, &pending_advertisement);
if (!success || !pending_advertisement.is_valid())
return false;
......
......@@ -106,6 +106,7 @@ void FakeAdapter::AddObserver(
void FakeAdapter::RegisterAdvertisement(
const device::BluetoothUUID& service_uuid,
const std::vector<uint8_t>& service_data,
bool use_scan_response,
RegisterAdvertisementCallback callback) {
if (!should_advertisement_registration_succeed_) {
std::move(callback).Run(mojo::NullRemote());
......
......@@ -29,6 +29,7 @@ class FakeAdapter : public mojom::Adapter {
AddObserverCallback callback) override;
void RegisterAdvertisement(const device::BluetoothUUID& service_uuid,
const std::vector<uint8_t>& service_data,
bool use_scan_response,
RegisterAdvertisementCallback callback) override;
void SetDiscoverable(bool discoverable,
SetDiscoverableCallback callback) override;
......
......@@ -89,6 +89,7 @@ void Adapter::AddObserver(mojo::PendingRemote<mojom::AdapterObserver> observer,
void Adapter::RegisterAdvertisement(const device::BluetoothUUID& service_uuid,
const std::vector<uint8_t>& service_data,
bool use_scan_response,
RegisterAdvertisementCallback callback) {
auto advertisement_data =
std::make_unique<device::BluetoothAdvertisement::Data>(
......@@ -98,10 +99,38 @@ void Adapter::RegisterAdvertisement(const device::BluetoothUUID& service_uuid,
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));
if (!use_scan_response) {
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));
} else {
// Require the service uuid to be in 128-bit format.
DCHECK_EQ(service_uuid.format(),
device::BluetoothUUID::Format::kFormat128Bit);
auto scan_response_data_map =
std::make_unique<device::BluetoothAdvertisement::ScanResponseData>();
// Start with the original scan response data.
std::vector<uint8_t> scan_response_data(service_data.begin(),
service_data.end());
// Now insert in front of the service data the identifying 2-bytes of the
// service id assuming this is a valid 16-bit uuid. For example, the uuid:
// 0000fef3-0000-1000-8000-00805f9b34fb can be uniquely defined by two bytes
// ****fef3-****-****-****-************ the rest is the same for all 16-bit
// uuids as defined by the Bluetooth spec. We insert them in little endian
// ordering 0xf3 first, then 0xfe in for this example.
auto service_id_bytes = service_uuid.GetBytes();
// Take bytes 2 and 3.
auto id_bytes = base::make_span(service_id_bytes).subspan(2, 2);
// Add them in reverse order (little endian).
scan_response_data.insert(scan_response_data.begin(), id_bytes.rbegin(),
id_bytes.rend());
// The platform API only supports AD Type 0x16 "Service Data" which assumes
// as 16-bit service id.
scan_response_data_map->emplace(0x16, scan_response_data);
advertisement_data->set_scan_response_data(
std::move(scan_response_data_map));
}
auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
adapter_->RegisterAdvertisement(
......
......@@ -40,6 +40,7 @@ class Adapter : public mojom::Adapter,
AddObserverCallback callback) override;
void RegisterAdvertisement(const device::BluetoothUUID& service_uuid,
const std::vector<uint8_t>& service_data,
bool use_scan_response,
RegisterAdvertisementCallback callback) override;
void SetDiscoverable(bool discoverable,
SetDiscoverableCallback callback) override;
......
......@@ -21,7 +21,7 @@ using testing::Return;
namespace {
const char kServiceId[] = "00000000-0000-0000-0000-000000000001";
const char kServiceId[] = "0000abcd-0000-0000-0000-000000000001";
const char kDeviceServiceDataStr[] = "ServiceData";
std::vector<uint8_t> GetByteVector(const std::string& str) {
......@@ -36,9 +36,7 @@ class MockBluetoothAdapterWithAdvertisements
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());
last_advertisement_data_ = std::move(advertisement_data);
if (should_advertisement_registration_succeed_) {
std::move(callback).Run(
......@@ -51,9 +49,8 @@ class MockBluetoothAdapterWithAdvertisements
}
bool should_advertisement_registration_succeed_ = true;
base::Optional<std::pair<device::BluetoothAdvertisement::UUIDList,
device::BluetoothAdvertisement::ServiceData>>
last_register_advertisement_args_;
std::unique_ptr<device::BluetoothAdvertisement::Data>
last_advertisement_data_;
protected:
~MockBluetoothAdapterWithAdvertisements() override = default;
......@@ -80,7 +77,7 @@ class AdapterTest : public testing::Test {
}
protected:
void VerifyRegisterAdvertisement(bool should_succeed) {
void RegisterAdvertisement(bool should_succeed, bool use_scan_data) {
mock_bluetooth_adapter_->should_advertisement_registration_succeed_ =
should_succeed;
......@@ -90,21 +87,46 @@ class AdapterTest : public testing::Test {
base::RunLoop run_loop;
adapter_->RegisterAdvertisement(
device::BluetoothUUID(kServiceId), service_data,
/*use_scan_data=*/use_scan_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));
void VerifyAdvertisement() {
auto service_data = GetByteVector(kDeviceServiceDataStr);
auto uuid_list =
mock_bluetooth_adapter_->last_advertisement_data_->service_uuids();
EXPECT_EQ(1u, uuid_list->size());
EXPECT_EQ(kServiceId, (*uuid_list)[0]);
auto last_service_data =
mock_bluetooth_adapter_->last_advertisement_data_->service_data();
EXPECT_EQ(service_data, last_service_data->at(kServiceId));
EXPECT_FALSE(mock_bluetooth_adapter_->last_advertisement_data_
->scan_response_data());
}
void VerifyAdvertisementWithScanData() {
auto service_data = GetByteVector(kDeviceServiceDataStr);
auto uuid_list =
mock_bluetooth_adapter_->last_advertisement_data_->service_uuids();
EXPECT_EQ(1u, uuid_list->size());
EXPECT_EQ(kServiceId, (*uuid_list)[0]);
EXPECT_FALSE(
mock_bluetooth_adapter_->last_advertisement_data_->service_data());
auto last_scan_response_data =
mock_bluetooth_adapter_->last_advertisement_data_->scan_response_data();
ASSERT_TRUE(base::Contains(*last_scan_response_data, 0x16));
const auto& raw_data = (*last_scan_response_data)[0x16];
// First two bytes should be the identifying bits of the kServiceId UUID.
// They should be in litten endian order (reversed).
EXPECT_EQ(0xCD, raw_data[0]);
EXPECT_EQ(0xAB, raw_data[1]);
EXPECT_EQ(service_data,
std::vector<uint8_t>(raw_data.begin() + 2, raw_data.end()));
}
scoped_refptr<NiceMock<MockBluetoothAdapterWithAdvertisements>>
......@@ -116,11 +138,18 @@ class AdapterTest : public testing::Test {
};
TEST_F(AdapterTest, TestRegisterAdvertisement_Success) {
VerifyRegisterAdvertisement(/*should_succeed=*/true);
RegisterAdvertisement(/*should_succeed=*/true, /*use_scan_data=*/false);
VerifyAdvertisement();
}
TEST_F(AdapterTest, TestRegisterAdvertisement_Error) {
VerifyRegisterAdvertisement(/*should_succeed=*/false);
RegisterAdvertisement(/*should_succeed=*/false, /*use_scan_data=*/false);
VerifyAdvertisement();
}
TEST_F(AdapterTest, TestRegisterAdvertisement_ScanResponseData) {
RegisterAdvertisement(/*should_succeed=*/true, /*use_scan_data=*/true);
VerifyAdvertisementWithScanData();
}
} // namespace bluetooth
......@@ -147,6 +147,16 @@ interface Adapter {
// 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.
//
// When |use_scan_response| is true, the |service_data| is added to the scan
// response instead of the initial advertisement. A few assumptions are made:
//
// 1) The |service_id| provided is a valid 16-bit UUID in 128-bit format.
// The identifying 16-bits will be extracted for the short service id used
// in the scan response.
// 2) The Ad Type is assumed to be 0x16 as that is all that is supported by
// the Platform right now.
//
// Important notes:
// * This method registers a "non-connectable" advertisement. Any future
// effort to allow this API to support "connectable" advertisements would
......@@ -157,8 +167,8 @@ interface Adapter {
// 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);
RegisterAdvertisement(UUID service_id, array<uint8> service_data,
bool use_scan_response) => (pending_remote<Advertisement>? advertisement);
// Requests the local device to make itself discoverable to nearby remote
// devices.
......
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