Commit be6d0834 authored by ejcaruso's avatar ejcaruso Committed by Commit bot

Reland "components/arc: implement multi advertising"

This plumbs through multi advertising capabilities for the
instance. Instances can register and unregister advertisements.

Now with virtual destructors to prevent memory leaks in the
StructTraits conversion.

BUG=637171,653338
TEST=use nrf connect on remote device and verify that packets
  registered from the instance are visible on the remote device,
  run tests with ASAN/LSAN turned on and verify there are no
  memory errors

Review-Url: https://codereview.chromium.org/2394683007
Cr-Commit-Position: refs/heads/master@{#423672}
parent d9b6e52a
......@@ -69,6 +69,7 @@ constexpr uint32_t kGattWritePermission =
BluetoothGattCharacteristic::Permission::
PERMISSION_WRITE_ENCRYPTED_AUTHENTICATED;
constexpr int32_t kInvalidGattAttributeHandle = -1;
constexpr int32_t kInvalidAdvertisementHandle = -1;
// Bluetooth Specification Version 4.2 Vol 3 Part F Section 3.2.2
// An attribute handle of value 0xFFFF is known as the maximum attribute handle.
constexpr int32_t kMaxGattAttributeHandle = 0xFFFF;
......@@ -78,7 +79,6 @@ constexpr int kMaxGattAttributeLength = 512;
// Copied from Android at system/bt/stack/btm/btm_ble_int.h
// https://goo.gl/k7PM6u
constexpr uint16_t kAndroidMBluetoothVersionNumber = 95;
constexpr uint16_t kMaxAdvertisement = 5;
// Bluetooth SDP Service Class ID List Attribute identifier
constexpr uint16_t kServiceClassIDListAttributeID = 0x0001;
// Timeout for Bluetooth Discovery (scan)
......@@ -1632,6 +1632,134 @@ void ArcBluetoothBridge::RemoveSdpRecord(
base::Bind(&OnRemoveServiceRecordError, callback));
}
bool ArcBluetoothBridge::GetAdvertisementHandle(int32_t* adv_handle) {
for (int i = 0; i < kMaxAdvertisements; i++) {
if (advertisements_.find(i) == advertisements_.end()) {
*adv_handle = i;
return true;
}
}
return false;
}
void ArcBluetoothBridge::ReserveAdvertisementHandle(
const ReserveAdvertisementHandleCallback& callback) {
DCHECK(CalledOnValidThread());
// Find an empty advertisement slot.
int32_t adv_handle;
if (!GetAdvertisementHandle(&adv_handle)) {
LOG(WARNING) << "Out of space for advertisement data";
callback.Run(mojom::BluetoothGattStatus::GATT_FAILURE,
kInvalidAdvertisementHandle);
return;
}
// We have a handle. Put an entry in the map to reserve it.
advertisements_[adv_handle] = nullptr;
// The advertisement will be registered when we get the call
// to SetAdvertisingData. For now, just return the adv_handle.
callback.Run(mojom::BluetoothGattStatus::GATT_SUCCESS, adv_handle);
}
void ArcBluetoothBridge::BroadcastAdvertisement(
int32_t adv_handle,
std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement,
const BroadcastAdvertisementCallback& callback) {
DCHECK(CalledOnValidThread());
if (advertisements_.find(adv_handle) == advertisements_.end()) {
callback.Run(mojom::BluetoothGattStatus::GATT_FAILURE);
return;
}
if (!advertisements_[adv_handle]) {
OnReadyToRegisterAdvertisement(callback, adv_handle,
std::move(advertisement));
return;
}
advertisements_[adv_handle]->Unregister(
base::Bind(&ArcBluetoothBridge::OnReadyToRegisterAdvertisement,
weak_factory_.GetWeakPtr(), callback, adv_handle,
base::Passed(std::move(advertisement))),
base::Bind(&ArcBluetoothBridge::OnRegisterAdvertisementError,
weak_factory_.GetWeakPtr(), callback, adv_handle));
}
void ArcBluetoothBridge::ReleaseAdvertisementHandle(
int32_t adv_handle,
const ReleaseAdvertisementHandleCallback& callback) {
DCHECK(CalledOnValidThread());
if (advertisements_.find(adv_handle) == advertisements_.end()) {
callback.Run(mojom::BluetoothGattStatus::GATT_FAILURE);
return;
}
if (!advertisements_[adv_handle]) {
advertisements_.erase(adv_handle);
callback.Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
return;
}
advertisements_[adv_handle]->Unregister(
base::Bind(&ArcBluetoothBridge::OnUnregisterAdvertisementDone,
weak_factory_.GetWeakPtr(), callback, adv_handle),
base::Bind(&ArcBluetoothBridge::OnUnregisterAdvertisementError,
weak_factory_.GetWeakPtr(), callback, adv_handle));
}
void ArcBluetoothBridge::OnReadyToRegisterAdvertisement(
const BroadcastAdvertisementCallback& callback,
int32_t adv_handle,
std::unique_ptr<device::BluetoothAdvertisement::Data> data) {
DCHECK(CalledOnValidThread());
bluetooth_adapter_->RegisterAdvertisement(
std::move(data),
base::Bind(&ArcBluetoothBridge::OnRegisterAdvertisementDone,
weak_factory_.GetWeakPtr(), callback, adv_handle),
base::Bind(&ArcBluetoothBridge::OnRegisterAdvertisementError,
weak_factory_.GetWeakPtr(), callback, adv_handle));
}
void ArcBluetoothBridge::OnRegisterAdvertisementDone(
const BroadcastAdvertisementCallback& callback,
int32_t adv_handle,
scoped_refptr<BluetoothAdvertisement> advertisement) {
DCHECK(CalledOnValidThread());
advertisements_[adv_handle] = std::move(advertisement);
callback.Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
}
void ArcBluetoothBridge::OnRegisterAdvertisementError(
const BroadcastAdvertisementCallback& callback,
int32_t adv_handle,
BluetoothAdvertisement::ErrorCode error_code) {
DCHECK(CalledOnValidThread());
LOG(WARNING) << "Failed to register advertisement for handle " << adv_handle
<< ", error code = " << error_code;
advertisements_[adv_handle] = nullptr;
callback.Run(mojom::BluetoothGattStatus::GATT_FAILURE);
}
void ArcBluetoothBridge::OnUnregisterAdvertisementDone(
const ReleaseAdvertisementHandleCallback& callback,
int32_t adv_handle) {
DCHECK(CalledOnValidThread());
advertisements_.erase(adv_handle);
callback.Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
}
void ArcBluetoothBridge::OnUnregisterAdvertisementError(
const ReleaseAdvertisementHandleCallback& callback,
int32_t adv_handle,
BluetoothAdvertisement::ErrorCode error_code) {
DCHECK(CalledOnValidThread());
LOG(WARNING) << "Failed to unregister advertisement for handle " << adv_handle
<< ", error code = " << error_code;
advertisements_.erase(adv_handle);
callback.Run(mojom::BluetoothGattStatus::GATT_FAILURE);
}
void ArcBluetoothBridge::OnDiscoveryError() {
LOG(WARNING) << "failed to change discovery state";
}
......@@ -1846,7 +1974,7 @@ ArcBluetoothBridge::GetAdapterProperties(
mojom::BluetoothLocalLEFeatures::New();
le_features->version_supported = kAndroidMBluetoothVersionNumber;
le_features->local_privacy_enabled = 0;
le_features->max_adv_instance = kMaxAdvertisement;
le_features->max_adv_instance = kMaxAdvertisements;
le_features->rpa_offload_supported = 0;
le_features->max_irk_list_size = 0;
le_features->max_adv_filter_supported = 0;
......
......@@ -21,6 +21,7 @@
#include "components/arc/instance_holder.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_advertisement.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_discovery_session.h"
#include "device/bluetooth/bluetooth_local_gatt_service.h"
......@@ -273,6 +274,17 @@ class ArcBluetoothBridge
void RemoveSdpRecord(uint32_t service_handle,
const RemoveSdpRecordCallback& callback) override;
// Set up or disable multiple advertising.
void ReserveAdvertisementHandle(
const ReserveAdvertisementHandleCallback& callback) override;
void BroadcastAdvertisement(
int32_t adv_handle,
std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement,
const BroadcastAdvertisementCallback& callback) override;
void ReleaseAdvertisementHandle(
int32_t adv_handle,
const ReleaseAdvertisementHandleCallback& callback) override;
// Chrome observer callbacks
void OnPoweredOn(
const base::Callback<void(mojom::BluetoothAdapterState)>& callback) const;
......@@ -380,6 +392,42 @@ class ArcBluetoothBridge
void OnSetAdapterProperty(mojom::BluetoothStatus success,
mojom::BluetoothPropertyPtr property);
// Callbacks for managing advertisements registered from the instance.
// Called when we have an open slot in the advertisement map and want to
// register the advertisement given by |data| for handle |adv_handle|.
void OnReadyToRegisterAdvertisement(
const BroadcastAdvertisementCallback& callback,
int32_t adv_handle,
std::unique_ptr<device::BluetoothAdvertisement::Data> data);
// Called when we've successfully registered a new advertisement for
// handle |adv_handle|.
void OnRegisterAdvertisementDone(
const BroadcastAdvertisementCallback& callback,
int32_t adv_handle,
scoped_refptr<device::BluetoothAdvertisement> advertisement);
// Called when the attempt to register an advertisement for handle
// |adv_handle| has failed. |adv_handle| remains reserved, but no
// advertisement is associated with it.
void OnRegisterAdvertisementError(
const BroadcastAdvertisementCallback& callback,
int32_t adv_handle,
device::BluetoothAdvertisement::ErrorCode error_code);
// Both of the following are called after we've tried to unregister
// the advertisement for |adv_handle|. Either way, we will no
// longer be broadcasting this advertisement, so in either case, the
// handle can be released.
void OnUnregisterAdvertisementDone(
const ReleaseAdvertisementHandleCallback& callback,
int32_t adv_handle);
void OnUnregisterAdvertisementError(
const ReleaseAdvertisementHandleCallback& callback,
int32_t adv_handle,
device::BluetoothAdvertisement::ErrorCode error_code);
// Find the next free advertisement handle and put it in *adv_handle,
// or return false if the advertisement map is full.
bool GetAdvertisementHandle(int32_t* adv_handle);
bool CalledOnValidThread();
mojo::Binding<mojom::BluetoothHost> binding_;
......@@ -411,6 +459,21 @@ class ArcBluetoothBridge
// Timer to turn adapter discoverable off.
base::OneShotTimer discoverable_off_timer_;
// Holds advertising data registered by the instance.
//
// When a handle is reserved, an entry is placed into the advertisements_
// map. This entry is not yet associated with a device::BluetoothAdvertisement
// because the instance hasn't sent us any advertising data yet, so its
// mapped value is nullptr until that happens. Thus we have three states for a
// handle:
// * unmapped -> free
// * mapped to nullptr -> reserved, awaiting data
// * mapped to a device::BluetoothAdvertisement -> in use, and the mapped
// BluetoothAdvertisement is currently registered with the adapter.
enum { kMaxAdvertisements = 5 };
std::map<int32_t, scoped_refptr<device::BluetoothAdvertisement>>
advertisements_;
base::ThreadChecker thread_checker_;
// WeakPtrFactory to use for callbacks.
......
......@@ -21,11 +21,14 @@
#include "device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.h"
#include "device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.h"
#include "device/bluetooth/dbus/fake_bluetooth_gatt_service_client.h"
#include "device/bluetooth/dbus/fake_bluetooth_le_advertising_manager_client.h"
#include "mojo/public/cpp/bindings/array.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace arc {
constexpr int kFailureAdvHandle = -1;
class ArcBluetoothBridgeTest : public testing::Test {
protected:
void AddTestDevice() {
......@@ -82,6 +85,70 @@ class ArcBluetoothBridgeTest : public testing::Test {
get_adapter_run_loop_.Run();
}
// Helper methods for multi advertisement tests.
int32_t ReserveAdvertisementHandle() {
constexpr int kSentinelHandle = -2;
last_adv_handle_ = kSentinelHandle;
arc_bluetooth_bridge_->ReserveAdvertisementHandle(
base::Bind(&ArcBluetoothBridgeTest::ReserveAdvertisementHandleCallback,
base::Unretained(this)));
base::RunLoop().RunUntilIdle();
// Make sure the callback was called.
EXPECT_NE(kSentinelHandle, last_adv_handle_);
return last_adv_handle_;
}
mojom::BluetoothGattStatus BroadcastAdvertisement(
int adv_handle,
std::unique_ptr<device::BluetoothAdvertisement::Data> data) {
last_status_ = mojom::BluetoothGattStatus::GATT_REQUEST_NOT_SUPPORTED;
arc_bluetooth_bridge_->BroadcastAdvertisement(
adv_handle, std::move(data),
base::Bind(&ArcBluetoothBridgeTest::StatusSetterCallback,
base::Unretained(this)));
base::RunLoop().RunUntilIdle();
EXPECT_NE(mojom::BluetoothGattStatus::GATT_REQUEST_NOT_SUPPORTED,
last_status_);
return last_status_;
}
mojom::BluetoothGattStatus ReleaseAdvertisementHandle(int adv_handle) {
last_status_ = mojom::BluetoothGattStatus::GATT_REQUEST_NOT_SUPPORTED;
arc_bluetooth_bridge_->ReleaseAdvertisementHandle(
adv_handle, base::Bind(&ArcBluetoothBridgeTest::StatusSetterCallback,
base::Unretained(this)));
base::RunLoop().RunUntilIdle();
EXPECT_NE(mojom::BluetoothGattStatus::GATT_REQUEST_NOT_SUPPORTED,
last_status_);
return last_status_;
}
void ReserveAdvertisementHandleCallback(mojom::BluetoothGattStatus status,
int32_t adv_handle) {
if (status == mojom::BluetoothGattStatus::GATT_FAILURE)
last_adv_handle_ = kFailureAdvHandle;
else
last_adv_handle_ = adv_handle;
}
void StatusSetterCallback(mojom::BluetoothGattStatus status) {
last_status_ = status;
}
int NumActiveAdvertisements() {
bluez::FakeBluetoothLEAdvertisingManagerClient* adv_client =
static_cast<bluez::FakeBluetoothLEAdvertisingManagerClient*>(
bluez::BluezDBusManager::Get()
->GetBluetoothLEAdvertisingManagerClient());
return adv_client->currently_registered();
}
int last_adv_handle_;
mojom::BluetoothGattStatus last_status_;
std::unique_ptr<FakeArcBridgeService> fake_arc_bridge_service_;
std::unique_ptr<FakeBluetoothInstance> fake_bluetooth_instance_;
std::unique_ptr<ArcBluetoothBridge> arc_bluetooth_bridge_;
......@@ -208,4 +275,78 @@ TEST_F(ArcBluetoothBridgeTest, GetGattDB) {
db[4]->properties);
}
// Invoke multi advertisement methods and make sure they are going down to the
// D-Bus clients.
TEST_F(ArcBluetoothBridgeTest, SingleAdvertisement) {
int32_t handle = ReserveAdvertisementHandle();
EXPECT_NE(kFailureAdvHandle, handle);
EXPECT_EQ(0, NumActiveAdvertisements());
auto adv_data = base::MakeUnique<device::BluetoothAdvertisement::Data>(
device::BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST);
mojom::BluetoothGattStatus status =
BroadcastAdvertisement(handle, std::move(adv_data));
EXPECT_EQ(mojom::BluetoothGattStatus::GATT_SUCCESS, status);
EXPECT_EQ(1, NumActiveAdvertisements());
status = ReleaseAdvertisementHandle(handle);
EXPECT_EQ(mojom::BluetoothGattStatus::GATT_SUCCESS, status);
EXPECT_EQ(0, NumActiveAdvertisements());
}
// Invoke multi advertisement methods and make sure they are going down to the
// D-Bus clients.
TEST_F(ArcBluetoothBridgeTest, MultiAdvertisement) {
int32_t handle = ReserveAdvertisementHandle();
EXPECT_NE(kFailureAdvHandle, handle);
EXPECT_EQ(0, NumActiveAdvertisements());
auto adv_data = base::MakeUnique<device::BluetoothAdvertisement::Data>(
device::BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST);
mojom::BluetoothGattStatus status =
BroadcastAdvertisement(handle, std::move(adv_data));
EXPECT_EQ(mojom::BluetoothGattStatus::GATT_SUCCESS, status);
EXPECT_EQ(1, NumActiveAdvertisements());
int32_t handle2 = ReserveAdvertisementHandle();
EXPECT_NE(kFailureAdvHandle, handle2);
auto adv_data2 = base::MakeUnique<device::BluetoothAdvertisement::Data>(
device::BluetoothAdvertisement::ADVERTISEMENT_TYPE_PERIPHERAL);
status = BroadcastAdvertisement(handle2, std::move(adv_data2));
EXPECT_EQ(mojom::BluetoothGattStatus::GATT_SUCCESS, status);
EXPECT_EQ(2, NumActiveAdvertisements());
status = ReleaseAdvertisementHandle(handle);
EXPECT_EQ(mojom::BluetoothGattStatus::GATT_SUCCESS, status);
EXPECT_EQ(1, NumActiveAdvertisements());
status = ReleaseAdvertisementHandle(handle2);
EXPECT_EQ(mojom::BluetoothGattStatus::GATT_SUCCESS, status);
EXPECT_EQ(0, NumActiveAdvertisements());
}
// This tests that we support releasing reserved but unused handles.
// TODO(ejcaruso): When Chrome supports more handles, make sure we
// will stop reserving handles before we use all of Chrome's.
TEST_F(ArcBluetoothBridgeTest, ReleaseUnusedHandles) {
constexpr size_t kMaxBluezAdvertisements =
bluez::FakeBluetoothLEAdvertisingManagerClient::kMaxBluezAdvertisements;
std::vector<int32_t> reserved_handles;
for (size_t i = 0; i < kMaxBluezAdvertisements; i++) {
int32_t handle = ReserveAdvertisementHandle();
if (handle == kFailureAdvHandle)
break;
reserved_handles.push_back(handle);
}
EXPECT_GT(reserved_handles.size(), 1Ul);
EXPECT_LE(reserved_handles.size(), kMaxBluezAdvertisements);
EXPECT_EQ(0, NumActiveAdvertisements());
for (int32_t handle : reserved_handles) {
EXPECT_EQ(ReleaseAdvertisementHandle(handle),
mojom::BluetoothGattStatus::GATT_SUCCESS);
}
}
} // namespace arc
......@@ -8,10 +8,13 @@
#include <vector>
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "device/bluetooth/bluetooth_advertisement.h"
#include "device/bluetooth/bluetooth_uuid.h"
namespace {
// BluetoothUUID helpers.
constexpr size_t kUUIDSize = 16;
bool IsNonHex(char c) {
......@@ -26,6 +29,59 @@ std::string StripNonHex(const std::string& str) {
return result;
}
// BluetoothAdvertisement helpers.
struct AdvertisementEntry {
virtual ~AdvertisementEntry() {}
virtual void AddTo(device::BluetoothAdvertisement::Data* data) {}
};
struct ServiceUUIDEntry : public AdvertisementEntry {
std::vector<device::BluetoothUUID> service_uuids;
~ServiceUUIDEntry() override {}
void AddTo(device::BluetoothAdvertisement::Data* data) override {
auto string_uuids = base::MakeUnique<std::vector<std::string>>();
for (const auto& uuid : service_uuids) {
string_uuids->emplace_back(uuid.value());
}
data->set_service_uuids(std::move(string_uuids));
}
};
struct ServiceDataEntry : public AdvertisementEntry {
uint16_t service_uuid;
std::vector<uint8_t> service_data;
~ServiceDataEntry() override {}
void AddTo(device::BluetoothAdvertisement::Data* data) override {
std::string string_uuid = base::StringPrintf("%04x", service_uuid);
data->set_service_data(
base::WrapUnique(new std::map<std::string, std::vector<uint8_t>>{
{string_uuid, service_data}}));
}
};
struct ManufacturerDataEntry : public AdvertisementEntry {
uint16_t company_id_code;
std::vector<uint8_t> blob;
~ManufacturerDataEntry() override {}
void AddTo(device::BluetoothAdvertisement::Data* data) override {
data->set_manufacturer_data(base::WrapUnique(
new std::map<uint16_t, std::vector<uint8_t>>{{company_id_code, blob}}));
}
};
uint16_t ExtractCompanyIdentifierCode(std::vector<uint8_t>* blob) {
// The company identifier code is in little-endian.
uint16_t company_id_code = (*blob)[1] << 8 | (*blob)[0];
blob->erase(blob->begin(), blob->begin() + sizeof(uint16_t));
return company_id_code;
}
} // namespace
namespace mojo {
......@@ -70,4 +126,113 @@ bool StructTraits<arc::mojom::BluetoothUUIDDataView,
return true;
}
template <>
struct EnumTraits<arc::mojom::BluetoothAdvertisementType,
device::BluetoothAdvertisement::AdvertisementType> {
static bool FromMojom(
arc::mojom::BluetoothAdvertisementType mojom_type,
device::BluetoothAdvertisement::AdvertisementType* type) {
switch (mojom_type) {
case arc::mojom::BluetoothAdvertisementType::ADV_TYPE_CONNECTABLE:
case arc::mojom::BluetoothAdvertisementType::ADV_TYPE_SCANNABLE:
*type = device::BluetoothAdvertisement::ADVERTISEMENT_TYPE_PERIPHERAL;
return true;
case arc::mojom::BluetoothAdvertisementType::ADV_TYPE_NON_CONNECTABLE:
*type = device::BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST;
return true;
}
NOTREACHED() << "Invalid type: " << static_cast<uint32_t>(mojom_type);
return false;
}
};
template <>
struct StructTraits<arc::mojom::BluetoothServiceDataDataView,
ServiceDataEntry> {
static bool Read(arc::mojom::BluetoothServiceDataDataView data,
ServiceDataEntry* output) {
output->service_uuid = data.uuid_16bit();
return data.ReadData(&output->service_data);
}
};
template <>
struct UnionTraits<arc::mojom::BluetoothAdvertisingDataDataView,
std::unique_ptr<AdvertisementEntry>> {
static bool Read(arc::mojom::BluetoothAdvertisingDataDataView data,
std::unique_ptr<AdvertisementEntry>* output) {
switch (data.tag()) {
case arc::mojom::BluetoothAdvertisingDataDataView::Tag::SERVICE_UUIDS: {
std::unique_ptr<ServiceUUIDEntry> service_uuids =
base::MakeUnique<ServiceUUIDEntry>();
if (!data.ReadServiceUuids(&service_uuids->service_uuids))
return false;
*output = std::move(service_uuids);
break;
}
case arc::mojom::BluetoothAdvertisingDataDataView::Tag::SERVICE_DATA: {
std::unique_ptr<ServiceDataEntry> service_data =
base::MakeUnique<ServiceDataEntry>();
if (!data.ReadServiceData(service_data.get()))
return false;
*output = std::move(service_data);
break;
}
case arc::mojom::BluetoothAdvertisingDataDataView::Tag::
MANUFACTURER_DATA: {
std::unique_ptr<ManufacturerDataEntry> manufacturer_data =
base::MakeUnique<ManufacturerDataEntry>();
// We get manufacturer data as a big blob. The first two bytes
// should be a company identifier code and the rest is manufacturer-
// specific.
std::vector<uint8_t> blob;
if (!data.ReadManufacturerData(&blob))
return false;
if (blob.size() < sizeof(uint16_t)) {
LOG(WARNING) << "Advertisement had malformed manufacturer data";
return false;
}
manufacturer_data->company_id_code =
ExtractCompanyIdentifierCode(&blob);
manufacturer_data->blob = std::move(blob);
*output = std::move(manufacturer_data);
break;
}
default: {
LOG(WARNING) << "Bluetooth advertising data case not implemented";
// Default AdvertisementEntry does nothing when added to the
// device::BluetoothAdvertisement::AdvertisementData, so data we
// don't know how to handle yet will be dropped but won't cause a
// failure to deserialize.
*output = base::MakeUnique<AdvertisementEntry>();
break;
}
}
return true;
}
};
// static
bool StructTraits<arc::mojom::BluetoothAdvertisementDataView,
std::unique_ptr<device::BluetoothAdvertisement::Data>>::
Read(arc::mojom::BluetoothAdvertisementDataView advertisement,
std::unique_ptr<device::BluetoothAdvertisement::Data>* output) {
device::BluetoothAdvertisement::AdvertisementType adv_type;
if (!advertisement.ReadType(&adv_type))
return false;
auto data = base::MakeUnique<device::BluetoothAdvertisement::Data>(adv_type);
std::vector<std::unique_ptr<AdvertisementEntry>> adv_entries;
if (!advertisement.ReadData(&adv_entries))
return false;
for (const auto& adv_entry : adv_entries)
adv_entry->AddTo(data.get());
data->set_include_tx_power(advertisement.include_tx_power());
*output = std::move(data);
return true;
}
} // namespace mojo
......@@ -6,7 +6,9 @@
#define COMPONENTS_ARC_BLUETOOTH_BLUETOOTH_STRUCT_TRAITS_H_
#include "components/arc/common/bluetooth.mojom.h"
#include "device/bluetooth/bluetooth_advertisement.h"
#include "device/bluetooth/bluetooth_common.h"
#include "device/bluetooth/bluetooth_uuid.h"
#include "device/bluetooth/bluez/bluetooth_service_attribute_value_bluez.h"
namespace mojo {
......@@ -100,6 +102,33 @@ struct StructTraits<arc::mojom::BluetoothUUIDDataView, device::BluetoothUUID> {
device::BluetoothUUID* output);
};
template <>
struct StructTraits<arc::mojom::BluetoothAdvertisementDataView,
std::unique_ptr<device::BluetoothAdvertisement::Data>> {
static bool Read(
arc::mojom::BluetoothAdvertisementDataView advertisement,
std::unique_ptr<device::BluetoothAdvertisement::Data>* output);
// Dummy methods.
static arc::mojom::BluetoothAdvertisementType type(
std::unique_ptr<device::BluetoothAdvertisement::Data>& input) {
NOTREACHED();
return arc::mojom::BluetoothAdvertisementType::ADV_TYPE_NON_CONNECTABLE;
}
static bool include_tx_power(
std::unique_ptr<device::BluetoothAdvertisement::Data>& input) {
NOTREACHED();
return false;
}
static mojo::Array<arc::mojom::BluetoothAdvertisingDataPtr> data(
std::unique_ptr<device::BluetoothAdvertisement::Data>& input) {
NOTREACHED();
return mojo::Array<arc::mojom::BluetoothAdvertisingDataPtr>();
}
};
} // namespace mojo
#endif // COMPONENTS_ARC_BLUETOOTH_BLUETOOTH_STRUCT_TRAITS_H_
......@@ -4,6 +4,7 @@
#include "components/arc/bluetooth/bluetooth_struct_traits.h"
#include "base/macros.h"
#include "device/bluetooth/bluetooth_uuid.h"
#include "mojo/public/cpp/bindings/array.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -16,6 +17,11 @@ constexpr uint8_t kUuidArray[] = {0x12, 0x34, 0x56, 0x78, 0x12, 0x34,
0x23, 0x45, 0x67, 0x89};
constexpr size_t kUuidSize = 16;
constexpr char kUuid16Str[] = "1234";
constexpr uint16_t kUuid16 = 0x1234;
constexpr uint8_t kServiceData[] = {0x11, 0x22, 0x33, 0x44, 0x55};
constexpr uint8_t kManufacturerData[] = {0x00, 0xe0};
template <typename MojoType, typename UserType>
mojo::StructPtr<MojoType> ConvertToMojo(UserType& input) {
mojo::Array<uint8_t> data = MojoType::Serialize(&input);
......@@ -49,6 +55,8 @@ TEST(BluetoothStructTraitsTest, DeserializeBluetoothUUID) {
for (size_t i = 0; i < kUuidSize; i++) {
uuid_mojo->uuid.push_back(kUuidArray[i]);
}
uuid_mojo->uuid =
std::vector<uint8_t>(std::begin(kUuidArray), std::end(kUuidArray));
device::BluetoothUUID uuid_device;
EXPECT_TRUE(ConvertFromMojo(std::move(uuid_mojo), &uuid_device));
EXPECT_EQ(std::string(kUuidStr), uuid_device.canonical_value());
......@@ -59,4 +67,87 @@ TEST(BluetoothStructTraitsTest, DeserializeBluetoothUUID) {
// array<uint8, 16>.
}
TEST(BluetoothStructTraitsTest, DeserializeBluetoothAdvertisement) {
arc::mojom::BluetoothAdvertisementPtr advertisement_mojo =
arc::mojom::BluetoothAdvertisement::New();
mojo::Array<arc::mojom::BluetoothAdvertisingDataPtr> adv_data;
// Create service UUIDs.
arc::mojom::BluetoothAdvertisingDataPtr data =
arc::mojom::BluetoothAdvertisingData::New();
std::vector<device::BluetoothUUID> service_uuids;
service_uuids.push_back((device::BluetoothUUID(kUuidStr)));
data->set_service_uuids(service_uuids);
adv_data.push_back(std::move(data));
// Create service data.
data = arc::mojom::BluetoothAdvertisingData::New();
arc::mojom::BluetoothServiceDataPtr service_data =
arc::mojom::BluetoothServiceData::New();
service_data->uuid_16bit = kUuid16;
service_data->data =
std::vector<uint8_t>(std::begin(kServiceData), std::end(kServiceData));
data->set_service_data(std::move(service_data));
adv_data.push_back(std::move(data));
// Create manufacturer data.
data = arc::mojom::BluetoothAdvertisingData::New();
data->set_manufacturer_data(std::vector<uint8_t>(
std::begin(kManufacturerData), std::end(kManufacturerData)));
adv_data.push_back(std::move(data));
advertisement_mojo->type =
arc::mojom::BluetoothAdvertisementType::ADV_TYPE_CONNECTABLE;
advertisement_mojo->data = std::move(adv_data);
std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement;
EXPECT_TRUE(ConvertFromMojo(std::move(advertisement_mojo), &advertisement));
EXPECT_EQ(advertisement->type(),
device::BluetoothAdvertisement::ADVERTISEMENT_TYPE_PERIPHERAL);
std::unique_ptr<device::BluetoothAdvertisement::UUIDList> converted_uuids =
advertisement->service_uuids();
EXPECT_EQ(converted_uuids->size(), 1U);
EXPECT_EQ(*converted_uuids->begin(), kUuidStr);
std::unique_ptr<device::BluetoothAdvertisement::ServiceData>
converted_service = advertisement->service_data();
EXPECT_EQ(converted_service->size(), 1U);
EXPECT_EQ(converted_service->begin()->first, kUuid16Str);
for (size_t i = 0; i < arraysize(kServiceData); i++) {
EXPECT_EQ(kServiceData[i], converted_service->begin()->second[i]);
}
std::unique_ptr<device::BluetoothAdvertisement::ManufacturerData>
converted_manufacturer = advertisement->manufacturer_data();
EXPECT_EQ(converted_manufacturer->size(), 1U);
uint16_t cic = converted_manufacturer->begin()->first;
EXPECT_EQ(cic & 0xff, kManufacturerData[0]);
EXPECT_EQ((cic >> 8) & 0xff, kManufacturerData[1]);
EXPECT_EQ(converted_manufacturer->begin()->second.size(),
static_cast<unsigned long>(arraysize(kManufacturerData) -
sizeof(uint16_t)));
}
TEST(BluetoothStructTraitsTest, DeserializeBluetoothAdvertisementFailure) {
arc::mojom::BluetoothAdvertisementPtr advertisement_mojo =
arc::mojom::BluetoothAdvertisement::New();
mojo::Array<arc::mojom::BluetoothAdvertisingDataPtr> adv_data;
// Create empty manufacturer data. Manufacturer data must include the CIC
// which is 2 bytes long.
arc::mojom::BluetoothAdvertisingDataPtr data =
arc::mojom::BluetoothAdvertisingData::New();
data->set_manufacturer_data((mojo::Array<uint8_t>()));
adv_data.push_back(std::move(data));
advertisement_mojo->type =
arc::mojom::BluetoothAdvertisementType::ADV_TYPE_CONNECTABLE;
advertisement_mojo->data = std::move(adv_data);
std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement;
EXPECT_FALSE(ConvertFromMojo(std::move(advertisement_mojo), &advertisement));
}
} // namespace mojo
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Next MinVersion: 6
// Next MinVersion: 7
module arc.mojom;
......@@ -171,6 +171,14 @@ struct BluetoothGattValue {
const int8 kUnknownPower = 127;
// Copied from Android Bluetooth package. See AdvertiseManager$AdvertiseNative
// http://goo.gl/UnKC5N
enum BluetoothAdvertisementType {
ADV_TYPE_CONNECTABLE = 0,
ADV_TYPE_SCANNABLE = 2,
ADV_TYPE_NON_CONNECTABLE = 3,
};
// Copy from Bluetooth Assigned Numbers Document, Generic Access Profile
// https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
[Extensible]
......@@ -203,6 +211,13 @@ struct BluetoothServiceData {
array<uint8> data;
};
struct BluetoothAdvertisement {
BluetoothAdvertisementType type;
bool include_tx_power;
array<BluetoothAdvertisingData> data;
// Add more here as Chrome supports it.
};
[Extensible]
enum BluetoothGattDBAttributeType {
BTGATT_DB_PRIMARY_SERVICE = 0,
......@@ -270,7 +285,7 @@ struct BluetoothCreateSdpRecordResult {
uint32 service_handle;
};
// Next Method ID: 40
// Next Method ID: 43
interface BluetoothHost {
EnableAdapter@0() => (BluetoothAdapterState state);
DisableAdapter@1() => (BluetoothAdapterState state);
......@@ -370,6 +385,15 @@ interface BluetoothHost {
=> (BluetoothCreateSdpRecordResult result);
[MinVersion=5] RemoveSdpRecord@39(uint32 service_handle)
=> (BluetoothStatus status);
// Multi-advertisement functions
[MinVersion=6] ReserveAdvertisementHandle@40()
=> (BluetoothGattStatus status, int32 adv_handle);
[MinVersion=6] BroadcastAdvertisement@41(int32 adv_handle,
BluetoothAdvertisement adv)
=> (BluetoothGattStatus status);
[MinVersion=6] ReleaseAdvertisementHandle@42(int32 adv_handle)
=> (BluetoothGattStatus status);
};
// Next Method ID: 18
......
mojom = "//components/arc/common/bluetooth.mojom"
public_headers = [
"//device/bluetooth/bluetooth_advertisement.h",
"//device/bluetooth/bluetooth_common.h",
"//device/bluetooth/bluetooth_uuid.h",
"//device/bluetooth/bluez/bluetooth_service_attribute_value_bluez.h",
......@@ -12,4 +13,5 @@ type_mappings = [
"arc.mojom.BluetoothDeviceType=device::BluetoothTransport",
"arc.mojom.BluetoothSdpAttributeType=bluez::BluetoothServiceAttributeValueBlueZ::Type",
"arc.mojom.BluetoothUUID=device::BluetoothUUID",
"arc.mojom.BluetoothAdvertisement=std::unique_ptr<device::BluetoothAdvertisement::Data>[move_only]",
]
......@@ -19,6 +19,7 @@
#include "device/bluetooth/bluetooth_advertisement.h"
#include "device/bluetooth/dbus/bluez_dbus_manager.h"
#include "device/bluetooth/dbus/fake_bluetooth_le_advertisement_service_provider.h"
#include "device/bluetooth/dbus/fake_bluetooth_le_advertising_manager_client.h"
#include "testing/gtest/include/gtest/gtest.h"
using device::BluetoothAdapter;
......@@ -207,15 +208,35 @@ TEST_F(BluetoothAdvertisementBlueZTest, RegisterSucceeded) {
ExpectSuccess();
}
TEST_F(BluetoothAdvertisementBlueZTest, DoubleRegisterFailed) {
TEST_F(BluetoothAdvertisementBlueZTest, DoubleRegisterSucceeded) {
scoped_refptr<BluetoothAdvertisement> advertisement = CreateAdvertisement();
ExpectSuccess();
EXPECT_TRUE(advertisement);
// Creating a second advertisement should give us an error.
// Creating a second advertisement should still be fine.
scoped_refptr<BluetoothAdvertisement> advertisement2 = CreateAdvertisement();
ExpectSuccess();
EXPECT_TRUE(advertisement2);
}
TEST_F(BluetoothAdvertisementBlueZTest, RegisterTooManyFailed) {
// Register the maximum available number of advertisements.
constexpr size_t kMaxBluezAdvertisements =
bluez::FakeBluetoothLEAdvertisingManagerClient::kMaxBluezAdvertisements;
std::vector<scoped_refptr<BluetoothAdvertisement>> advertisements;
scoped_refptr<BluetoothAdvertisement> current_advertisement;
for (size_t i = 0; i < kMaxBluezAdvertisements; i++) {
current_advertisement = CreateAdvertisement();
ExpectSuccess();
EXPECT_TRUE(current_advertisement);
advertisements.emplace_back(std::move(current_advertisement));
}
// The next advertisement should fail to register.
current_advertisement = CreateAdvertisement();
ExpectError(BluetoothAdvertisement::ERROR_ADVERTISEMENT_ALREADY_EXISTS);
EXPECT_FALSE(advertisement2);
EXPECT_FALSE(current_advertisement);
}
TEST_F(BluetoothAdvertisementBlueZTest, DoubleUnregisterFailed) {
......
......@@ -55,11 +55,11 @@ void FakeBluetoothLEAdvertisingManagerClient::RegisterAdvertisement(
if (iter == service_provider_map_.end()) {
error_callback.Run(bluetooth_advertising_manager::kErrorInvalidArguments,
"Advertisement object not registered");
} else if (!currently_registered_.value().empty()) {
} else if (currently_registered_.size() >= kMaxBluezAdvertisements) {
error_callback.Run(bluetooth_advertising_manager::kErrorFailed,
"Maximum advertisements reached");
} else {
currently_registered_ = advertisement_object_path;
currently_registered_.push_back(advertisement_object_path);
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
}
}
......@@ -76,16 +76,19 @@ void FakeBluetoothLEAdvertisingManagerClient::UnregisterAdvertisement(
return;
}
ServiceProviderMap::iterator iter =
service_provider_map_.find(advertisement_object_path);
if (iter == service_provider_map_.end()) {
auto service_iter = service_provider_map_.find(advertisement_object_path);
auto reg_iter =
std::find(currently_registered_.begin(), currently_registered_.end(),
advertisement_object_path);
if (service_iter == service_provider_map_.end()) {
error_callback.Run(bluetooth_advertising_manager::kErrorDoesNotExist,
"Advertisement not registered");
} else if (advertisement_object_path != currently_registered_) {
} else if (reg_iter == currently_registered_.end()) {
error_callback.Run(bluetooth_advertising_manager::kErrorDoesNotExist,
"Does not exist");
} else {
currently_registered_ = dbus::ObjectPath("");
currently_registered_.erase(reg_iter);
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
}
}
......
......@@ -61,6 +61,10 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothLEAdvertisingManagerClient
FakeBluetoothLEAdvertisementServiceProvider* GetAdvertisementServiceProvider(
const std::string& uuid);
int currently_registered() { return currently_registered_.size(); }
enum : size_t { kMaxBluezAdvertisements = 5 };
private:
// Map of a D-Bus object path to the FakeBluetoothAdvertisementServiceProvider
// registered for it; maintained by RegisterAdvertisementServiceProvider() and
......@@ -71,9 +75,8 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothLEAdvertisingManagerClient
ServiceProviderMap;
ServiceProviderMap service_provider_map_;
// Holds the currently registered advertisement. If there is no advertisement
// registered, this path is empty.
dbus::ObjectPath currently_registered_;
// Holds currently registered advertisements.
std::vector<dbus::ObjectPath> currently_registered_;
DISALLOW_COPY_AND_ASSIGN(FakeBluetoothLEAdvertisingManagerClient);
};
......
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