blob: d907ff23c1150dfa9251f9b3facb232340f6dd6c [file] [log] [blame]
// Copyright 2016 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 <stddef.h>
#include <stdint.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "dbus/object_path.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_discovery_filter.h"
#include "device/bluetooth/bluetooth_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_gatt_connection.h"
#include "device/bluetooth/bluetooth_gatt_notify_session.h"
#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_remote_gatt_descriptor.h"
#include "device/bluetooth/bluetooth_remote_gatt_service.h"
#include "device/bluetooth/bluetooth_uuid.h"
#include "device/bluetooth/bluez/bluetooth_device_bluez.h"
#include "device/bluetooth/dbus/bluez_dbus_manager.h"
#include "device/bluetooth/dbus/fake_bluetooth_adapter_client.h"
#include "device/bluetooth/dbus/fake_bluetooth_agent_manager_client.h"
#include "device/bluetooth/dbus/fake_bluetooth_device_client.h"
#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_input_client.h"
#include "device/bluetooth/test/test_bluetooth_adapter_observer.h"
#include "testing/gtest/include/gtest/gtest.h"
using device::BluetoothAdapter;
using device::BluetoothDevice;
using device::BluetoothDiscoveryFilter;
using device::BluetoothRemoteGattCharacteristic;
using device::BluetoothGattConnection;
using device::BluetoothRemoteGattDescriptor;
using device::BluetoothRemoteGattService;
using device::BluetoothGattNotifySession;
using device::BluetoothUUID;
using device::TestBluetoothAdapterObserver;
using UUIDSet = device::BluetoothDevice::UUIDSet;
typedef std::unordered_map<device::BluetoothDevice*,
device::BluetoothDevice::UUIDSet>
DeviceToUUIDs;
namespace bluez {
namespace {
const BluetoothUUID kGenericAccessServiceUUID(
bluez::FakeBluetoothGattServiceClient::kGenericAccessServiceUUID);
const BluetoothUUID kBatteryServiceUUID(
bluez::FakeBluetoothGattServiceClient::kBatteryServiceUUID);
const BluetoothUUID kHeartRateServiceUUID(
bluez::FakeBluetoothGattServiceClient::kHeartRateServiceUUID);
const BluetoothUUID kHeartRateMeasurementUUID(
bluez::FakeBluetoothGattCharacteristicClient::kHeartRateMeasurementUUID);
const BluetoothUUID kBodySensorLocationUUID(
bluez::FakeBluetoothGattCharacteristicClient::kBodySensorLocationUUID);
const BluetoothUUID kHeartRateControlPointUUID(
bluez::FakeBluetoothGattCharacteristicClient::kHeartRateControlPointUUID);
// Compares GATT characteristic/descriptor values. Returns true, if the values
// are equal.
bool ValuesEqual(const std::vector<uint8_t>& value0,
const std::vector<uint8_t>& value1) {
if (value0.size() != value1.size())
return false;
for (size_t i = 0; i < value0.size(); ++i)
if (value0[i] != value1[i])
return false;
return true;
}
} // namespace
class BluetoothGattBlueZTest : public testing::Test {
public:
BluetoothGattBlueZTest()
: fake_bluetooth_gatt_service_client_(NULL),
success_callback_count_(0),
error_callback_count_(0) {}
void SetUp() override {
std::unique_ptr<bluez::BluezDBusManagerSetter> dbus_setter =
bluez::BluezDBusManager::GetSetterForTesting();
fake_bluetooth_device_client_ = new bluez::FakeBluetoothDeviceClient;
fake_bluetooth_gatt_service_client_ =
new bluez::FakeBluetoothGattServiceClient;
fake_bluetooth_gatt_characteristic_client_ =
new bluez::FakeBluetoothGattCharacteristicClient;
fake_bluetooth_gatt_descriptor_client_ =
new bluez::FakeBluetoothGattDescriptorClient;
dbus_setter->SetBluetoothDeviceClient(
std::unique_ptr<bluez::BluetoothDeviceClient>(
fake_bluetooth_device_client_));
dbus_setter->SetBluetoothGattServiceClient(
std::unique_ptr<bluez::BluetoothGattServiceClient>(
fake_bluetooth_gatt_service_client_));
dbus_setter->SetBluetoothGattCharacteristicClient(
std::unique_ptr<bluez::BluetoothGattCharacteristicClient>(
fake_bluetooth_gatt_characteristic_client_));
dbus_setter->SetBluetoothGattDescriptorClient(
std::unique_ptr<bluez::BluetoothGattDescriptorClient>(
fake_bluetooth_gatt_descriptor_client_));
dbus_setter->SetBluetoothAdapterClient(
std::unique_ptr<bluez::BluetoothAdapterClient>(
new bluez::FakeBluetoothAdapterClient));
dbus_setter->SetBluetoothInputClient(
std::unique_ptr<bluez::BluetoothInputClient>(
new bluez::FakeBluetoothInputClient));
dbus_setter->SetBluetoothAgentManagerClient(
std::unique_ptr<bluez::BluetoothAgentManagerClient>(
new bluez::FakeBluetoothAgentManagerClient));
GetAdapter();
adapter_->SetPowered(true, base::DoNothing(), base::DoNothing());
ASSERT_TRUE(adapter_->IsPowered());
}
void TearDown() override {
adapter_ = NULL;
update_sessions_.clear();
gatt_conn_.reset();
bluez::BluezDBusManager::Shutdown();
}
void GetAdapter() {
device::BluetoothAdapterFactory::GetAdapter(base::BindOnce(
&BluetoothGattBlueZTest::AdapterCallback, base::Unretained(this)));
base::RunLoop().Run();
ASSERT_TRUE(adapter_.get() != NULL);
ASSERT_TRUE(adapter_->IsInitialized());
ASSERT_TRUE(adapter_->IsPresent());
}
BluetoothDevice* AddLeDevice() {
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
bluez::FakeBluetoothDeviceClient::Properties* properties1 =
fake_bluetooth_device_client_->GetProperties(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
properties1->connected.ReplaceValue(true);
return adapter_->GetDevice(
bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
}
BluetoothDevice* AddDualDevice() {
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kDualPath));
bluez::FakeBluetoothDeviceClient::Properties* properties2 =
fake_bluetooth_device_client_->GetProperties(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kDualPath));
properties2->connected.ReplaceValue(true);
return adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kDualAddress);
};
void BatteryServiceShouldBeComplete(BluetoothDevice* device) {
ASSERT_TRUE(device);
ASSERT_GE(device->GetGattServices().size(), 1u);
BluetoothRemoteGattService* service = device->GetGattService(
fake_bluetooth_gatt_service_client_->GetBatteryServicePath().value());
ASSERT_TRUE(service);
EXPECT_TRUE(service->IsPrimary());
EXPECT_EQ(BluetoothUUID(
bluez::FakeBluetoothGattServiceClient::kBatteryServiceUUID),
service->GetUUID());
EXPECT_EQ(service, device->GetGattService(service->GetIdentifier()));
}
void HeartRateServiceShouldBeComplete(BluetoothDevice* device) {
ASSERT_TRUE(device);
ASSERT_GE(device->GetGattServices().size(), 1u);
BluetoothRemoteGattService* service = device->GetGattService(
fake_bluetooth_gatt_service_client_->GetHeartRateServicePath().value());
ASSERT_TRUE(service);
EXPECT_TRUE(service->IsPrimary());
EXPECT_EQ(BluetoothUUID(
bluez::FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
service->GetUUID());
EXPECT_EQ(service, device->GetGattService(service->GetIdentifier()));
EXPECT_EQ(3U, service->GetCharacteristics().size());
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
->GetBodySensorLocationPath()
.value());
ASSERT_TRUE(characteristic);
EXPECT_EQ(BluetoothUUID(bluez::FakeBluetoothGattCharacteristicClient::
kBodySensorLocationUUID),
characteristic->GetUUID());
EXPECT_TRUE(characteristic->GetDescriptors().empty());
characteristic =
service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
->GetHeartRateControlPointPath()
.value());
ASSERT_TRUE(characteristic);
EXPECT_EQ(BluetoothUUID(bluez::FakeBluetoothGattCharacteristicClient::
kHeartRateControlPointUUID),
characteristic->GetUUID());
EXPECT_TRUE(characteristic->GetDescriptors().empty());
characteristic =
service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
->GetHeartRateMeasurementPath()
.value());
ASSERT_TRUE(characteristic);
EXPECT_EQ(BluetoothUUID(bluez::FakeBluetoothGattCharacteristicClient::
kHeartRateMeasurementUUID),
characteristic->GetUUID());
ASSERT_EQ(1u, characteristic->GetDescriptors().size());
BluetoothRemoteGattDescriptor* descriptor =
characteristic->GetDescriptors()[0];
EXPECT_EQ(
BluetoothRemoteGattDescriptor::ClientCharacteristicConfigurationUuid(),
descriptor->GetUUID());
}
void AdapterCallback(scoped_refptr<BluetoothAdapter> adapter) {
adapter_ = adapter;
if (base::RunLoop::IsRunningOnCurrentThread())
base::RunLoop::QuitCurrentWhenIdleDeprecated();
}
void SuccessCallback() { ++success_callback_count_; }
void ValueCallback(const std::vector<uint8_t>& value) {
++success_callback_count_;
last_read_value_ = value;
}
void GattConnectionCallback(std::unique_ptr<BluetoothGattConnection> conn) {
++success_callback_count_;
gatt_conn_ = std::move(conn);
}
void NotifySessionCallback(
std::unique_ptr<BluetoothGattNotifySession> session) {
++success_callback_count_;
update_sessions_.push_back(std::move(session));
QuitMessageLoop();
}
void ServiceErrorCallback(BluetoothRemoteGattService::GattErrorCode err) {
++error_callback_count_;
last_service_error_ = err;
}
void ErrorCallback() { ++error_callback_count_; }
void DBusErrorCallback(const std::string& error_name,
const std::string& error_message) {
++error_callback_count_;
}
void ConnectErrorCallback(BluetoothDevice::ConnectErrorCode error) {
++error_callback_count_;
}
protected:
void QuitMessageLoop() {
if (base::RunLoop::IsRunningOnCurrentThread())
base::RunLoop::QuitCurrentWhenIdleDeprecated();
}
base::MessageLoop message_loop_;
bluez::FakeBluetoothDeviceClient* fake_bluetooth_device_client_;
bluez::FakeBluetoothGattServiceClient* fake_bluetooth_gatt_service_client_;
bluez::FakeBluetoothGattCharacteristicClient*
fake_bluetooth_gatt_characteristic_client_;
bluez::FakeBluetoothGattDescriptorClient*
fake_bluetooth_gatt_descriptor_client_;
std::unique_ptr<device::BluetoothGattConnection> gatt_conn_;
std::vector<std::unique_ptr<BluetoothGattNotifySession>> update_sessions_;
scoped_refptr<BluetoothAdapter> adapter_;
int success_callback_count_;
int error_callback_count_;
std::vector<uint8_t> last_read_value_;
BluetoothRemoteGattService::GattErrorCode last_service_error_;
};
TEST_F(BluetoothGattBlueZTest,
RetrieveGattConnectedDevicesWithDiscoveryFilter_NoFilter) {
BluetoothDevice* le = AddLeDevice();
BluetoothDevice* dual = AddDualDevice();
BluetoothDiscoveryFilter discovery_filter(device::BLUETOOTH_TRANSPORT_LE);
DeviceToUUIDs result =
adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter(
discovery_filter);
EXPECT_EQ(DeviceToUUIDs({{le, UUIDSet()}, {dual, UUIDSet()}}), result);
}
TEST_F(BluetoothGattBlueZTest,
RetrieveGattConnectedDevicesWithDiscoveryFilter_NonMatchingFilter) {
AddLeDevice();
AddDualDevice();
BluetoothDiscoveryFilter discovery_filter(device::BLUETOOTH_TRANSPORT_LE);
discovery_filter.AddUUID(kBatteryServiceUUID);
DeviceToUUIDs result =
adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter(
discovery_filter);
EXPECT_TRUE(result.empty());
}
TEST_F(
BluetoothGattBlueZTest,
RetrieveGattConnectedDevicesWithDiscoveryFilter_OneMatchingServiceOneDevice) {
AddLeDevice();
BluetoothDevice* dual = AddDualDevice();
BluetoothDiscoveryFilter discovery_filter(device::BLUETOOTH_TRANSPORT_LE);
discovery_filter.AddUUID(kGenericAccessServiceUUID);
DeviceToUUIDs result =
adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter(
discovery_filter);
EXPECT_EQ(DeviceToUUIDs({{dual, UUIDSet({kGenericAccessServiceUUID})}}),
result);
}
TEST_F(
BluetoothGattBlueZTest,
RetrieveGattConnectedDevicesWithDiscoveryFilter_OneMatchingServiceTwoDevices) {
BluetoothDevice* le = AddLeDevice();
BluetoothDevice* dual = AddDualDevice();
BluetoothDiscoveryFilter discovery_filter(device::BLUETOOTH_TRANSPORT_LE);
discovery_filter.AddUUID(kHeartRateServiceUUID);
DeviceToUUIDs result =
adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter(
discovery_filter);
EXPECT_EQ(DeviceToUUIDs({{le, UUIDSet({kHeartRateServiceUUID})},
{dual, UUIDSet({kHeartRateServiceUUID})}}),
result);
}
TEST_F(BluetoothGattBlueZTest,
RetrieveGattConnectedDevicesWithDiscoveryFilter_TwoServicesTwoDevices) {
BluetoothDevice* le = AddLeDevice();
BluetoothDevice* dual = AddDualDevice();
BluetoothDiscoveryFilter discovery_filter(device::BLUETOOTH_TRANSPORT_LE);
discovery_filter.AddUUID(kGenericAccessServiceUUID);
discovery_filter.AddUUID(kHeartRateServiceUUID);
DeviceToUUIDs result =
adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter(
discovery_filter);
EXPECT_EQ(DeviceToUUIDs({{le, UUIDSet({kHeartRateServiceUUID})},
{dual, UUIDSet({kHeartRateServiceUUID,
kGenericAccessServiceUUID})}}),
result);
}
TEST_F(
BluetoothGattBlueZTest,
RetrieveGattConnectedDevicesWithDiscoveryFilter_OneMatchingServiceOneNonMatchingService) {
AddLeDevice();
BluetoothDevice* dual = AddDualDevice();
BluetoothDiscoveryFilter discovery_filter(device::BLUETOOTH_TRANSPORT_LE);
discovery_filter.AddUUID(kGenericAccessServiceUUID);
discovery_filter.AddUUID(kBatteryServiceUUID);
DeviceToUUIDs result =
adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter(
discovery_filter);
EXPECT_EQ(DeviceToUUIDs({{dual, UUIDSet({kGenericAccessServiceUUID})}}),
result);
}
TEST_F(BluetoothGattBlueZTest, GattConnection) {
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
ASSERT_TRUE(device);
ASSERT_FALSE(device->IsConnected());
ASSERT_FALSE(device->IsGattConnected());
ASSERT_FALSE(gatt_conn_.get());
ASSERT_EQ(0, success_callback_count_);
ASSERT_EQ(0, error_callback_count_);
device->CreateGattConnection(
base::Bind(&BluetoothGattBlueZTest::GattConnectionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ConnectErrorCallback,
base::Unretained(this)));
EXPECT_EQ(1, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_TRUE(device->IsConnected());
EXPECT_TRUE(device->IsGattConnected());
ASSERT_TRUE(gatt_conn_.get());
EXPECT_TRUE(gatt_conn_->IsConnected());
EXPECT_EQ(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress,
gatt_conn_->GetDeviceAddress());
gatt_conn_->Disconnect();
EXPECT_FALSE(device->IsConnected());
EXPECT_FALSE(device->IsGattConnected());
EXPECT_FALSE(gatt_conn_->IsConnected());
device->CreateGattConnection(
base::Bind(&BluetoothGattBlueZTest::GattConnectionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ConnectErrorCallback,
base::Unretained(this)));
EXPECT_EQ(2, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_TRUE(device->IsConnected());
EXPECT_TRUE(device->IsGattConnected());
ASSERT_TRUE(gatt_conn_.get());
EXPECT_TRUE(gatt_conn_->IsConnected());
device->Disconnect(base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ErrorCallback,
base::Unretained(this)));
EXPECT_EQ(3, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_FALSE(device->IsConnected());
EXPECT_FALSE(device->IsGattConnected());
ASSERT_TRUE(gatt_conn_.get());
EXPECT_FALSE(gatt_conn_->IsConnected());
device->CreateGattConnection(
base::Bind(&BluetoothGattBlueZTest::GattConnectionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ConnectErrorCallback,
base::Unretained(this)));
EXPECT_EQ(4, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_TRUE(device->IsConnected());
EXPECT_TRUE(device->IsGattConnected());
EXPECT_TRUE(gatt_conn_->IsConnected());
fake_bluetooth_device_client_->RemoveDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
ASSERT_TRUE(gatt_conn_.get());
EXPECT_FALSE(gatt_conn_->IsConnected());
}
TEST_F(BluetoothGattBlueZTest, GattServiceAddedAndRemoved) {
// Create a fake LE device. We store the device pointer here because this is a
// test. It's unsafe to do this in production as the device might get deleted.
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
ASSERT_TRUE(device);
TestBluetoothAdapterObserver observer(adapter_);
EXPECT_EQ(0, observer.gatt_service_added_count());
EXPECT_EQ(0, observer.gatt_service_removed_count());
EXPECT_TRUE(observer.last_gatt_service_id().empty());
EXPECT_FALSE(observer.last_gatt_service_uuid().IsValid());
EXPECT_TRUE(device->GetGattServices().empty());
// Expose the fake Heart Rate Service.
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
EXPECT_EQ(1, observer.gatt_service_added_count());
EXPECT_EQ(0, observer.gatt_service_removed_count());
EXPECT_FALSE(observer.last_gatt_service_id().empty());
EXPECT_EQ(1U, device->GetGattServices().size());
EXPECT_EQ(BluetoothUUID(
bluez::FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
observer.last_gatt_service_uuid());
BluetoothRemoteGattService* service =
device->GetGattService(observer.last_gatt_service_id());
EXPECT_TRUE(service->IsPrimary());
EXPECT_EQ(service, device->GetGattServices()[0]);
EXPECT_EQ(service, device->GetGattService(service->GetIdentifier()));
EXPECT_EQ(observer.last_gatt_service_uuid(), service->GetUUID());
// Hide the service.
observer.last_gatt_service_uuid() = BluetoothUUID();
observer.last_gatt_service_id().clear();
fake_bluetooth_gatt_service_client_->HideHeartRateService();
EXPECT_EQ(1, observer.gatt_service_added_count());
EXPECT_EQ(1, observer.gatt_service_removed_count());
EXPECT_FALSE(observer.last_gatt_service_id().empty());
EXPECT_TRUE(device->GetGattServices().empty());
EXPECT_EQ(BluetoothUUID(
bluez::FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
observer.last_gatt_service_uuid());
EXPECT_EQ(NULL, device->GetGattService(observer.last_gatt_service_id()));
// Expose the service again.
observer.last_gatt_service_uuid() = BluetoothUUID();
observer.last_gatt_service_id().clear();
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
EXPECT_EQ(2, observer.gatt_service_added_count());
EXPECT_EQ(1, observer.gatt_service_removed_count());
EXPECT_FALSE(observer.last_gatt_service_id().empty());
EXPECT_EQ(1U, device->GetGattServices().size());
EXPECT_EQ(BluetoothUUID(
bluez::FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
observer.last_gatt_service_uuid());
// The object |service| points to should have been deallocated. |device|
// should contain a brand new instance.
service = device->GetGattService(observer.last_gatt_service_id());
EXPECT_EQ(service, device->GetGattServices()[0]);
EXPECT_TRUE(service->IsPrimary());
EXPECT_EQ(observer.last_gatt_service_uuid(), service->GetUUID());
// Remove the device. The observer should be notified of the removed service.
// |device| becomes invalid after this.
observer.last_gatt_service_uuid() = BluetoothUUID();
observer.last_gatt_service_id().clear();
fake_bluetooth_device_client_->RemoveDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
EXPECT_EQ(2, observer.gatt_service_added_count());
EXPECT_EQ(2, observer.gatt_service_removed_count());
EXPECT_FALSE(observer.last_gatt_service_id().empty());
EXPECT_EQ(BluetoothUUID(
bluez::FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
observer.last_gatt_service_uuid());
EXPECT_EQ(NULL, adapter_->GetDevice(
bluez::FakeBluetoothDeviceClient::kLowEnergyAddress));
}
TEST_F(BluetoothGattBlueZTest, ServicesDiscoveredBeforeAdapterIsCreated) {
// Tests that all GATT objects are created for a device whose D-Bus objects
// were already exposed and for which services have been resolved.
adapter_ = NULL;
ASSERT_FALSE(device::BluetoothAdapterFactory::HasSharedInstanceForTesting());
// Create the fake D-Bus objects.
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
bluez::FakeBluetoothDeviceClient::Properties* properties =
fake_bluetooth_device_client_->GetProperties(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
while (!fake_bluetooth_gatt_characteristic_client_->IsHeartRateVisible())
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(fake_bluetooth_gatt_service_client_->IsHeartRateVisible());
ASSERT_TRUE(fake_bluetooth_gatt_characteristic_client_->IsHeartRateVisible());
properties->services_resolved.ReplaceValue(true);
// Create the adapter. This should create all the GATT objects.
GetAdapter();
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
EXPECT_TRUE(device->IsGattServicesDiscoveryComplete());
HeartRateServiceShouldBeComplete(device);
}
TEST_F(BluetoothGattBlueZTest, ServicesDiscoveredAfterAdapterIsCreated) {
// Create a fake LE device. We store the device pointer here because this is a
// test. It's unsafe to do this in production as the device might get deleted.
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
bluez::FakeBluetoothDeviceClient::Properties* properties =
fake_bluetooth_device_client_->GetProperties(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
ASSERT_TRUE(device);
TestBluetoothAdapterObserver observer(adapter_);
// Expose the fake Heart Rate Service.
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
// Run the message loop so that the characteristics/descriptors appear.
base::RunLoop().Run();
properties->services_resolved.ReplaceValue(true);
EXPECT_TRUE(device->IsGattServicesDiscoveryComplete());
EXPECT_EQ(1u, device->GetGattServices().size());
EXPECT_EQ(device, observer.last_device());
EXPECT_EQ(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress,
observer.last_device_address());
// Disconnect from the device:
device->Disconnect(base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ErrorCallback,
base::Unretained(this)));
fake_bluetooth_gatt_service_client_->HideHeartRateService();
properties->connected.ReplaceValue(false);
properties->services_resolved.ReplaceValue(false);
EXPECT_FALSE(device->IsConnected());
EXPECT_FALSE(device->IsGattServicesDiscoveryComplete());
EXPECT_EQ(0u, device->GetGattServices().size());
// Verify that the device can be connected to again:
device->CreateGattConnection(
base::Bind(&BluetoothGattBlueZTest::GattConnectionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ConnectErrorCallback,
base::Unretained(this)));
properties->connected.ReplaceValue(true);
EXPECT_TRUE(device->IsConnected());
// Verify that service discovery can be done again:
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
// Run the message loop so that the characteristics/descriptors appear.
base::RunLoop().Run();
properties->services_resolved.ReplaceValue(true);
EXPECT_TRUE(device->IsGattServicesDiscoveryComplete());
EXPECT_EQ(1u, device->GetGattServices().size());
}
TEST_F(BluetoothGattBlueZTest, DiscoverCachedServices) {
// This unit test tests that all remote GATT objects are created for D-Bus
// objects that were already exposed and all relevant events have been
// dispatched.
adapter_ = NULL;
ASSERT_FALSE(device::BluetoothAdapterFactory::HasSharedInstanceForTesting());
// Create the fake D-Bus objects.
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
bluez::FakeBluetoothDeviceClient::Properties* properties =
fake_bluetooth_device_client_->GetProperties(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
properties->services_resolved.ReplaceValue(false);
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
while (!fake_bluetooth_gatt_characteristic_client_->IsHeartRateVisible())
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(fake_bluetooth_gatt_service_client_->IsHeartRateVisible());
ASSERT_TRUE(fake_bluetooth_gatt_characteristic_client_->IsHeartRateVisible());
// Create the adapter. This should create all the GATT objects.
GetAdapter();
TestBluetoothAdapterObserver observer(adapter_);
// The device should exist but contain no cached services while the services
// haven't been resolved.
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
EXPECT_TRUE(device->GetGattServices().empty());
EXPECT_EQ(0, observer.gatt_services_discovered_count());
EXPECT_EQ(0, observer.gatt_service_added_count());
EXPECT_EQ(0, observer.gatt_discovery_complete_count());
EXPECT_EQ(0, observer.gatt_characteristic_added_count());
EXPECT_EQ(0, observer.gatt_descriptor_added_count());
properties->services_resolved.ReplaceValue(true);
EXPECT_EQ(1, observer.gatt_services_discovered_count());
EXPECT_EQ(1, observer.gatt_service_added_count());
EXPECT_EQ(1, observer.gatt_discovery_complete_count());
EXPECT_EQ(3, observer.gatt_characteristic_added_count());
EXPECT_EQ(1, observer.gatt_descriptor_added_count());
HeartRateServiceShouldBeComplete(device);
}
TEST_F(BluetoothGattBlueZTest, DiscoverNewServices) {
// This unit test tests that all remote GATT Objects are created for D-Bus
// objects that are newly exposed.
TestBluetoothAdapterObserver observer(adapter_);
// Create the fake D-Bus objects.
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
bluez::FakeBluetoothDeviceClient::Properties* properties =
fake_bluetooth_device_client_->GetProperties(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
properties->services_resolved.ReplaceValue(false);
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
while (!fake_bluetooth_gatt_characteristic_client_->IsHeartRateVisible())
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(fake_bluetooth_gatt_service_client_->IsHeartRateVisible());
ASSERT_TRUE(fake_bluetooth_gatt_characteristic_client_->IsHeartRateVisible());
// The device should exist and contain new services even though the services
// haven't been resolved.
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
EXPECT_EQ(1u, device->GetGattServices().size());
HeartRateServiceShouldBeComplete(device);
EXPECT_EQ(0, observer.gatt_services_discovered_count());
EXPECT_EQ(0, observer.gatt_discovery_complete_count());
// The rest of the events are tested in the corresponding
// {GATTObject}AddedAndRemoved tests.
properties->services_resolved.ReplaceValue(true);
EXPECT_EQ(1, observer.gatt_services_discovered_count());
EXPECT_EQ(1, observer.gatt_discovery_complete_count());
}
TEST_F(BluetoothGattBlueZTest, DiscoverCachedAndNewServices) {
// This unit test tests that all remote GATT objects are created for D-Bus
// objects that were already exposed and for new GATT Objects.
adapter_ = NULL;
ASSERT_FALSE(device::BluetoothAdapterFactory::HasSharedInstanceForTesting());
// Create the fake D-Bus objects.
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
bluez::FakeBluetoothDeviceClient::Properties* properties =
fake_bluetooth_device_client_->GetProperties(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
properties->services_resolved.ReplaceValue(false);
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
while (!fake_bluetooth_gatt_characteristic_client_->IsHeartRateVisible())
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(fake_bluetooth_gatt_service_client_->IsHeartRateVisible());
ASSERT_TRUE(fake_bluetooth_gatt_characteristic_client_->IsHeartRateVisible());
// Create the adapter. This should create all the GATT objects.
GetAdapter();
TestBluetoothAdapterObserver observer(adapter_);
// The device should exist but contain no cached services while the services
// haven't been resolved.
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
EXPECT_EQ(0, observer.gatt_services_discovered_count());
EXPECT_EQ(0, observer.gatt_discovery_complete_count());
EXPECT_TRUE(device->GetGattServices().empty());
fake_bluetooth_gatt_service_client_->ExposeBatteryService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
ASSERT_TRUE(fake_bluetooth_gatt_service_client_->IsBatteryServiceVisible());
EXPECT_EQ(0, observer.gatt_services_discovered_count());
EXPECT_EQ(0, observer.gatt_discovery_complete_count());
EXPECT_EQ(1u, device->GetGattServices().size());
// A newly added service should be complete even if services haven't been
// resolved.
BatteryServiceShouldBeComplete(device);
properties->services_resolved.ReplaceValue(true);
EXPECT_EQ(1, observer.gatt_services_discovered_count());
EXPECT_EQ(2, observer.gatt_discovery_complete_count());
EXPECT_EQ(2u, device->GetGattServices().size());
// Now that services have been resolved both services should be present.
HeartRateServiceShouldBeComplete(device);
BatteryServiceShouldBeComplete(device);
}
TEST_F(BluetoothGattBlueZTest, GattCharacteristicAddedAndRemoved) {
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
ASSERT_TRUE(device);
TestBluetoothAdapterObserver observer(adapter_);
// Expose the fake Heart Rate service. This will asynchronously expose
// characteristics.
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
ASSERT_EQ(1, observer.gatt_service_added_count());
BluetoothRemoteGattService* service =
device->GetGattService(observer.last_gatt_service_id());
EXPECT_EQ(0, observer.gatt_service_changed_count());
EXPECT_EQ(0, observer.gatt_discovery_complete_count());
EXPECT_EQ(0, observer.gatt_characteristic_added_count());
EXPECT_EQ(0, observer.gatt_characteristic_removed_count());
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
EXPECT_TRUE(service->GetCharacteristics().empty());
// Run the message loop so that the characteristics appear.
base::RunLoop().Run();
// 3 characteristics should appear. Only 1 of the characteristics sends
// value changed signals. Service changed should be fired once for
// descriptor added.
EXPECT_EQ(0, observer.gatt_service_changed_count());
EXPECT_EQ(3, observer.gatt_characteristic_added_count());
EXPECT_EQ(0, observer.gatt_characteristic_removed_count());
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
EXPECT_EQ(3U, service->GetCharacteristics().size());
// Hide the characteristics. 3 removed signals should be received.
fake_bluetooth_gatt_characteristic_client_->HideHeartRateCharacteristics();
EXPECT_EQ(0, observer.gatt_service_changed_count());
EXPECT_EQ(3, observer.gatt_characteristic_added_count());
EXPECT_EQ(3, observer.gatt_characteristic_removed_count());
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
EXPECT_TRUE(service->GetCharacteristics().empty());
// Re-expose the heart rate characteristics. We shouldn't get another
// GattDiscoveryCompleteForService call, since the service thinks that
// discovery is done. On the bluetoothd side, characteristics will be removed
// only if the service will also be subsequently removed.
fake_bluetooth_gatt_characteristic_client_->ExposeHeartRateCharacteristics(
fake_bluetooth_gatt_service_client_->GetHeartRateServicePath());
EXPECT_EQ(0, observer.gatt_service_changed_count());
EXPECT_EQ(6, observer.gatt_characteristic_added_count());
EXPECT_EQ(3, observer.gatt_characteristic_removed_count());
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
EXPECT_EQ(3U, service->GetCharacteristics().size());
// Hide the service. All characteristics should disappear.
fake_bluetooth_gatt_service_client_->HideHeartRateService();
EXPECT_EQ(0, observer.gatt_service_changed_count());
EXPECT_EQ(6, observer.gatt_characteristic_added_count());
EXPECT_EQ(6, observer.gatt_characteristic_removed_count());
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
}
TEST_F(BluetoothGattBlueZTest, GattDescriptorAddedAndRemoved) {
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
ASSERT_TRUE(device);
TestBluetoothAdapterObserver observer(adapter_);
// Expose the fake Heart Rate service. This will asynchronously expose
// characteristics.
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
ASSERT_EQ(1, observer.gatt_service_added_count());
BluetoothRemoteGattService* service =
device->GetGattService(observer.last_gatt_service_id());
EXPECT_EQ(0, observer.gatt_service_changed_count());
EXPECT_EQ(0, observer.gatt_descriptor_added_count());
EXPECT_EQ(0, observer.gatt_descriptor_removed_count());
EXPECT_EQ(0, observer.gatt_descriptor_value_changed_count());
EXPECT_TRUE(service->GetCharacteristics().empty());
// Run the message loop so that the characteristics appear.
base::RunLoop().Run();
EXPECT_EQ(0, observer.gatt_service_changed_count());
// Only the Heart Rate Measurement characteristic has a descriptor.
EXPECT_EQ(1, observer.gatt_descriptor_added_count());
EXPECT_EQ(0, observer.gatt_descriptor_removed_count());
EXPECT_EQ(0, observer.gatt_descriptor_value_changed_count());
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
->GetBodySensorLocationPath()
.value());
ASSERT_TRUE(characteristic);
EXPECT_TRUE(characteristic->GetDescriptors().empty());
characteristic = service->GetCharacteristic(
fake_bluetooth_gatt_characteristic_client_->GetHeartRateControlPointPath()
.value());
ASSERT_TRUE(characteristic);
EXPECT_TRUE(characteristic->GetDescriptors().empty());
characteristic = service->GetCharacteristic(
fake_bluetooth_gatt_characteristic_client_->GetHeartRateMeasurementPath()
.value());
ASSERT_TRUE(characteristic);
EXPECT_EQ(1U, characteristic->GetDescriptors().size());
BluetoothRemoteGattDescriptor* descriptor =
characteristic->GetDescriptors()[0];
EXPECT_EQ(
BluetoothRemoteGattDescriptor::ClientCharacteristicConfigurationUuid(),
descriptor->GetUUID());
EXPECT_EQ(descriptor->GetUUID(), observer.last_gatt_descriptor_uuid());
EXPECT_EQ(descriptor->GetIdentifier(), observer.last_gatt_descriptor_id());
// Hide the descriptor.
fake_bluetooth_gatt_descriptor_client_->HideDescriptor(
dbus::ObjectPath(descriptor->GetIdentifier()));
EXPECT_TRUE(characteristic->GetDescriptors().empty());
EXPECT_EQ(0, observer.gatt_service_changed_count());
EXPECT_EQ(1, observer.gatt_descriptor_added_count());
EXPECT_EQ(1, observer.gatt_descriptor_removed_count());
EXPECT_EQ(0, observer.gatt_descriptor_value_changed_count());
// Expose the descriptor again.
observer.last_gatt_descriptor_id().clear();
observer.last_gatt_descriptor_uuid() = BluetoothUUID();
fake_bluetooth_gatt_descriptor_client_->ExposeDescriptor(
dbus::ObjectPath(characteristic->GetIdentifier()),
bluez::FakeBluetoothGattDescriptorClient::
kClientCharacteristicConfigurationUUID);
EXPECT_EQ(0, observer.gatt_service_changed_count());
EXPECT_EQ(1U, characteristic->GetDescriptors().size());
EXPECT_EQ(2, observer.gatt_descriptor_added_count());
EXPECT_EQ(1, observer.gatt_descriptor_removed_count());
EXPECT_EQ(0, observer.gatt_descriptor_value_changed_count());
descriptor = characteristic->GetDescriptors()[0];
EXPECT_EQ(
BluetoothRemoteGattDescriptor::ClientCharacteristicConfigurationUuid(),
descriptor->GetUUID());
EXPECT_EQ(descriptor->GetUUID(), observer.last_gatt_descriptor_uuid());
EXPECT_EQ(descriptor->GetIdentifier(), observer.last_gatt_descriptor_id());
}
TEST_F(BluetoothGattBlueZTest, GattCharacteristicValue) {
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
ASSERT_TRUE(device);
TestBluetoothAdapterObserver observer(adapter_);
// Expose the fake Heart Rate service. This will asynchronously expose
// characteristics.
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
ASSERT_EQ(1, observer.gatt_service_added_count());
BluetoothRemoteGattService* service =
device->GetGattService(observer.last_gatt_service_id());
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
// Run the message loop so that the characteristics appear.
base::RunLoop().Run();
// Issue write request to non-writable characteristics.
observer.Reset();
std::vector<uint8_t> write_value;
write_value.push_back(0x01);
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
->GetHeartRateMeasurementPath()
.value());
ASSERT_TRUE(characteristic);
EXPECT_FALSE(characteristic->IsNotifying());
EXPECT_EQ(
fake_bluetooth_gatt_characteristic_client_->GetHeartRateMeasurementPath()
.value(),
characteristic->GetIdentifier());
EXPECT_EQ(kHeartRateMeasurementUUID, characteristic->GetUUID());
characteristic->WriteRemoteCharacteristic(
write_value, base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_TRUE(observer.last_gatt_characteristic_id().empty());
EXPECT_FALSE(observer.last_gatt_characteristic_uuid().IsValid());
EXPECT_EQ(0, success_callback_count_);
EXPECT_EQ(1, error_callback_count_);
EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED,
last_service_error_);
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
characteristic = service->GetCharacteristic(
fake_bluetooth_gatt_characteristic_client_->GetBodySensorLocationPath()
.value());
ASSERT_TRUE(characteristic);
EXPECT_EQ(
fake_bluetooth_gatt_characteristic_client_->GetBodySensorLocationPath()
.value(),
characteristic->GetIdentifier());
EXPECT_EQ(kBodySensorLocationUUID, characteristic->GetUUID());
characteristic->WriteRemoteCharacteristic(
write_value, base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_TRUE(observer.last_gatt_characteristic_id().empty());
EXPECT_FALSE(observer.last_gatt_characteristic_uuid().IsValid());
EXPECT_EQ(0, success_callback_count_);
EXPECT_EQ(2, error_callback_count_);
EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_NOT_PERMITTED,
last_service_error_);
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
// Issue write request to writable characteristic. The "Body Sensor Location"
// characteristic does not send notifications and WriteValue does not result
// in a CharacteristicValueChanged event, thus no such event should be
// received.
characteristic = service->GetCharacteristic(
fake_bluetooth_gatt_characteristic_client_->GetHeartRateControlPointPath()
.value());
ASSERT_TRUE(characteristic);
EXPECT_EQ(
fake_bluetooth_gatt_characteristic_client_->GetHeartRateControlPointPath()
.value(),
characteristic->GetIdentifier());
EXPECT_EQ(kHeartRateControlPointUUID, characteristic->GetUUID());
characteristic->WriteRemoteCharacteristic(
write_value, base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_TRUE(observer.last_gatt_characteristic_id().empty());
EXPECT_FALSE(observer.last_gatt_characteristic_uuid().IsValid());
EXPECT_EQ(1, success_callback_count_);
EXPECT_EQ(2, error_callback_count_);
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
// Issue some invalid write requests to the characteristic.
// The value should still not change.
std::vector<uint8_t> invalid_write_length;
invalid_write_length.push_back(0x01);
invalid_write_length.push_back(0x00);
characteristic->WriteRemoteCharacteristic(
invalid_write_length, base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(1, success_callback_count_);
EXPECT_EQ(3, error_callback_count_);
EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_INVALID_LENGTH,
last_service_error_);
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
std::vector<uint8_t> invalid_write_value;
invalid_write_value.push_back(0x02);
characteristic->WriteRemoteCharacteristic(
invalid_write_value, base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(1, success_callback_count_);
EXPECT_EQ(4, error_callback_count_);
EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED, last_service_error_);
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
// Issue a read request.
characteristic = service->GetCharacteristic(
fake_bluetooth_gatt_characteristic_client_->GetBodySensorLocationPath()
.value());
ASSERT_TRUE(characteristic);
EXPECT_EQ(
fake_bluetooth_gatt_characteristic_client_->GetBodySensorLocationPath()
.value(),
characteristic->GetIdentifier());
EXPECT_EQ(kBodySensorLocationUUID, characteristic->GetUUID());
characteristic->ReadRemoteCharacteristic(
base::Bind(&BluetoothGattBlueZTest::ValueCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(2, success_callback_count_);
EXPECT_EQ(4, error_callback_count_);
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
EXPECT_TRUE(ValuesEqual(characteristic->GetValue(), last_read_value_));
// Test long-running actions.
fake_bluetooth_gatt_characteristic_client_->SetExtraProcessing(1);
characteristic = service->GetCharacteristic(
fake_bluetooth_gatt_characteristic_client_->GetBodySensorLocationPath()
.value());
ASSERT_TRUE(characteristic);
EXPECT_EQ(
fake_bluetooth_gatt_characteristic_client_->GetBodySensorLocationPath()
.value(),
characteristic->GetIdentifier());
EXPECT_EQ(kBodySensorLocationUUID, characteristic->GetUUID());
characteristic->ReadRemoteCharacteristic(
base::Bind(&BluetoothGattBlueZTest::ValueCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
// Callback counts shouldn't change, this one will be delayed until after
// tne next one.
EXPECT_EQ(2, success_callback_count_);
EXPECT_EQ(4, error_callback_count_);
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
// Next read should error because IN_PROGRESS
characteristic->ReadRemoteCharacteristic(
base::Bind(&BluetoothGattBlueZTest::ValueCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(5, error_callback_count_);
EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS,
last_service_error_);
// But previous call finished.
EXPECT_EQ(3, success_callback_count_);
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
EXPECT_TRUE(ValuesEqual(characteristic->GetValue(), last_read_value_));
fake_bluetooth_gatt_characteristic_client_->SetExtraProcessing(0);
// Test unauthorized actions.
fake_bluetooth_gatt_characteristic_client_->SetAuthorized(false);
characteristic->ReadRemoteCharacteristic(
base::Bind(&BluetoothGattBlueZTest::ValueCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(3, success_callback_count_);
EXPECT_EQ(6, error_callback_count_);
EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_NOT_AUTHORIZED,
last_service_error_);
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
fake_bluetooth_gatt_characteristic_client_->SetAuthorized(true);
// Test unauthenticated / needs login.
fake_bluetooth_gatt_characteristic_client_->SetAuthenticated(false);
characteristic->ReadRemoteCharacteristic(
base::Bind(&BluetoothGattBlueZTest::ValueCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(3, success_callback_count_);
EXPECT_EQ(7, error_callback_count_);
EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_NOT_PAIRED,
last_service_error_);
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
fake_bluetooth_gatt_characteristic_client_->SetAuthenticated(true);
}
// Test a read request issued from the success callback of another read request.
TEST_F(BluetoothGattBlueZTest, GattCharacteristicValue_Nested_Read_Read) {
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
ASSERT_TRUE(device);
TestBluetoothAdapterObserver observer(adapter_);
// Expose the fake Heart Rate service. This will asynchronously expose
// characteristics.
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
ASSERT_EQ(1, observer.gatt_service_added_count());
BluetoothRemoteGattService* service =
device->GetGattService(observer.last_gatt_service_id());
// Run the message loop so that the characteristics appear.
base::RunLoop().Run();
// Obtain readable Body Sensor Location characteristic.
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
->GetBodySensorLocationPath()
.value());
characteristic->ReadRemoteCharacteristic(
base::BindLambdaForTesting([&](const std::vector<uint8_t>& data) {
ValueCallback(data);
EXPECT_EQ(1, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(characteristic->GetValue(), last_read_value_);
characteristic->ReadRemoteCharacteristic(
base::Bind(&BluetoothGattBlueZTest::ValueCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
}),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(2, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(characteristic->GetValue(), last_read_value_);
}
// Test a write request issued from the success callback of another write
// request.
TEST_F(BluetoothGattBlueZTest, GattCharacteristicValue_Nested_Write_Write) {
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
ASSERT_TRUE(device);
TestBluetoothAdapterObserver observer(adapter_);
// Expose the fake Heart Rate service. This will asynchronously expose
// characteristics.
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
ASSERT_EQ(1, observer.gatt_service_added_count());
BluetoothRemoteGattService* service =
device->GetGattService(observer.last_gatt_service_id());
// Run the message loop so that the characteristics appear.
base::RunLoop().Run();
// Obtain writable Heart Rate Control Point characteristic.
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
->GetHeartRateControlPointPath()
.value());
std::vector<uint8_t> write_value = {0x01};
characteristic->WriteRemoteCharacteristic(
write_value, base::BindLambdaForTesting([&] {
SuccessCallback();
EXPECT_EQ(1, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
characteristic->WriteRemoteCharacteristic(
write_value,
base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
}),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(2, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
}
// Test a write request issued from the success callback of a read request.
TEST_F(BluetoothGattBlueZTest, GattCharacteristicValue_Nested_Read_Write) {
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
ASSERT_TRUE(device);
TestBluetoothAdapterObserver observer(adapter_);
// Expose the fake Heart Rate service. This will asynchronously expose
// characteristics.
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
ASSERT_EQ(1, observer.gatt_service_added_count());
BluetoothRemoteGattService* service =
device->GetGattService(observer.last_gatt_service_id());
// Run the message loop so that the characteristics appear.
base::RunLoop().Run();
// Obtain readable Body Sensor Location characteristic.
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
->GetBodySensorLocationPath()
.value());
characteristic->ReadRemoteCharacteristic(
base::BindLambdaForTesting([&](const std::vector<uint8_t>& data) {
ValueCallback(data);
EXPECT_EQ(1, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(characteristic->GetValue(), last_read_value_);
// Obtain writable Heart Rate Control Point characteristic.
characteristic = service->GetCharacteristic(
fake_bluetooth_gatt_characteristic_client_
->GetHeartRateControlPointPath()
.value());
characteristic->WriteRemoteCharacteristic(
std::vector<uint8_t>({0x01}),
base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
}),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(2, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
}
// Test a read request issued from the success callback of a write request.
TEST_F(BluetoothGattBlueZTest, GattCharacteristicValue_Nested_Write_Read) {
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
ASSERT_TRUE(device);
TestBluetoothAdapterObserver observer(adapter_);
// Expose the fake Heart Rate service. This will asynchronously expose
// characteristics.
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
ASSERT_EQ(1, observer.gatt_service_added_count());
BluetoothRemoteGattService* service =
device->GetGattService(observer.last_gatt_service_id());
// Run the message loop so that the characteristics appear.
base::RunLoop().Run();
// Obtain writable Heart Rate Control Point characteristic.
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
->GetHeartRateControlPointPath()
.value());
characteristic->WriteRemoteCharacteristic(
std::vector<uint8_t>({0x01}), base::BindLambdaForTesting([&] {
SuccessCallback();
EXPECT_EQ(1, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
// Obtain readable Body Sensor Location characteristic.
characteristic = service->GetCharacteristic(
fake_bluetooth_gatt_characteristic_client_
->GetBodySensorLocationPath()
.value());
characteristic->ReadRemoteCharacteristic(
base::Bind(&BluetoothGattBlueZTest::ValueCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
}),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(2, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(characteristic->GetValue(), last_read_value_);
}
TEST_F(BluetoothGattBlueZTest, GattCharacteristicProperties) {
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
ASSERT_TRUE(device);
TestBluetoothAdapterObserver observer(adapter_);
// Expose the fake Heart Rate service. This will asynchronously expose
// characteristics.
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
BluetoothRemoteGattService* service =
device->GetGattService(observer.last_gatt_service_id());
EXPECT_TRUE(service->GetCharacteristics().empty());
// Run the message loop so that the characteristics appear.
base::RunLoop().Run();
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
->GetBodySensorLocationPath()
.value());
EXPECT_EQ(BluetoothRemoteGattCharacteristic::PROPERTY_READ,
characteristic->GetProperties());
characteristic = service->GetCharacteristic(
fake_bluetooth_gatt_characteristic_client_->GetHeartRateControlPointPath()
.value());
EXPECT_EQ(BluetoothRemoteGattCharacteristic::PROPERTY_WRITE,
characteristic->GetProperties());
characteristic = service->GetCharacteristic(
fake_bluetooth_gatt_characteristic_client_->GetHeartRateMeasurementPath()
.value());
EXPECT_EQ(BluetoothRemoteGattCharacteristic::PROPERTY_NOTIFY |
BluetoothRemoteGattCharacteristic::PROPERTY_INDICATE,
static_cast<BluetoothRemoteGattCharacteristic::Property>(
characteristic->GetProperties()));
}
TEST_F(BluetoothGattBlueZTest, GattDescriptorValue) {
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
ASSERT_TRUE(device);
TestBluetoothAdapterObserver observer(adapter_);
// Expose the fake Heart Rate service. This will asynchronously expose
// characteristics.
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
ASSERT_EQ(1, observer.gatt_service_added_count());
BluetoothRemoteGattService* service =
device->GetGattService(observer.last_gatt_service_id());
EXPECT_EQ(0, observer.gatt_service_changed_count());
EXPECT_EQ(0, observer.gatt_descriptor_value_changed_count());
EXPECT_TRUE(service->GetCharacteristics().empty());
// Run the message loop so that the characteristics appear.
base::RunLoop().Run();
EXPECT_EQ(0, observer.gatt_service_changed_count());
// Only the Heart Rate Measurement characteristic has a descriptor.
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
->GetHeartRateMeasurementPath()
.value());
ASSERT_TRUE(characteristic);
EXPECT_EQ(1U, characteristic->GetDescriptors().size());
EXPECT_FALSE(characteristic->IsNotifying());
BluetoothRemoteGattDescriptor* descriptor =
characteristic->GetDescriptors()[0];
EXPECT_EQ(
BluetoothRemoteGattDescriptor::ClientCharacteristicConfigurationUuid(),
descriptor->GetUUID());
std::vector<uint8_t> desc_value = {0x00, 0x00};
/* The cached value will be empty until the first read request */
EXPECT_FALSE(ValuesEqual(desc_value, descriptor->GetValue()));
EXPECT_TRUE(descriptor->GetValue().empty());
EXPECT_EQ(0, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_TRUE(last_read_value_.empty());
// Read value. GattDescriptorValueChanged event will be sent after a
// successful read.
descriptor->ReadRemoteDescriptor(
base::Bind(&BluetoothGattBlueZTest::ValueCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(1, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_TRUE(ValuesEqual(last_read_value_, descriptor->GetValue()));
EXPECT_TRUE(ValuesEqual(desc_value, descriptor->GetValue()));
EXPECT_EQ(0, observer.gatt_service_changed_count());
EXPECT_EQ(1, observer.gatt_descriptor_value_changed_count());
// Write value. Writes to this descriptor will fail.
desc_value[0] = 0x03;
descriptor->WriteRemoteDescriptor(
desc_value, base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(1, success_callback_count_);
EXPECT_EQ(1, error_callback_count_);
EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_NOT_PERMITTED,
last_service_error_);
EXPECT_TRUE(ValuesEqual(last_read_value_, descriptor->GetValue()));
EXPECT_FALSE(ValuesEqual(desc_value, descriptor->GetValue()));
EXPECT_EQ(0, observer.gatt_service_changed_count());
EXPECT_EQ(1, observer.gatt_descriptor_value_changed_count());
// Read value. The value should remain unchanged.
descriptor->ReadRemoteDescriptor(
base::Bind(&BluetoothGattBlueZTest::ValueCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(2, success_callback_count_);
EXPECT_EQ(1, error_callback_count_);
EXPECT_TRUE(ValuesEqual(last_read_value_, descriptor->GetValue()));
EXPECT_FALSE(ValuesEqual(desc_value, descriptor->GetValue()));
EXPECT_EQ(0, observer.gatt_service_changed_count());
EXPECT_EQ(1, observer.gatt_descriptor_value_changed_count());
// Start notifications on the descriptor's characteristic. The descriptor
// value should change.
characteristic->StartNotifySession(
base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
base::RunLoop().Run();
EXPECT_EQ(3, success_callback_count_);
EXPECT_EQ(1, error_callback_count_);
EXPECT_EQ(1U, update_sessions_.size());
EXPECT_TRUE(characteristic->IsNotifying());
// Read the new descriptor value. We should receive a value updated event.
descriptor->ReadRemoteDescriptor(
base::Bind(&BluetoothGattBlueZTest::ValueCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(4, success_callback_count_);
EXPECT_EQ(1, error_callback_count_);
EXPECT_TRUE(ValuesEqual(last_read_value_, descriptor->GetValue()));
EXPECT_FALSE(ValuesEqual(desc_value, descriptor->GetValue()));
EXPECT_EQ(0, observer.gatt_service_changed_count());
EXPECT_EQ(2, observer.gatt_descriptor_value_changed_count());
}
TEST_F(BluetoothGattBlueZTest, NotifySessions) {
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
ASSERT_TRUE(device);
TestBluetoothAdapterObserver observer(adapter_);
// Expose the fake Heart Rate service. This will asynchronously expose
// characteristics.
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
ASSERT_EQ(1, observer.gatt_service_added_count());
BluetoothRemoteGattService* service =
device->GetGattService(observer.last_gatt_service_id());
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
// Run the message loop so that the characteristics appear.
base::RunLoop().Run();
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
->GetHeartRateMeasurementPath()
.value());
ASSERT_TRUE(characteristic);
EXPECT_FALSE(characteristic->IsNotifying());
EXPECT_TRUE(update_sessions_.empty());
// Request to start notifications.
characteristic->StartNotifySession(
base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
// The operation still hasn't completed but we should have received the first
// notification.
EXPECT_EQ(0, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(1, observer.gatt_characteristic_value_changed_count());
EXPECT_TRUE(update_sessions_.empty());
// Send a two more requests, which should get queued.
characteristic->StartNotifySession(
base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
characteristic->StartNotifySession(
base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(0, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(1, observer.gatt_characteristic_value_changed_count());
EXPECT_TRUE(update_sessions_.empty());
EXPECT_FALSE(characteristic->IsNotifying());
// Run the main loop. The initial call should complete. The queued call should
// succeed immediately.
base::RunLoop().Run();
EXPECT_TRUE(characteristic->IsNotifying());
EXPECT_EQ(3, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(1, observer.gatt_characteristic_value_changed_count());
EXPECT_EQ(3U, update_sessions_.size());
// Notifications should be getting sent regularly now.
base::RunLoop().Run();
EXPECT_GT(observer.gatt_characteristic_value_changed_count(), 1);
// Stop one of the sessions. The session should become inactive but the
// characteristic should still be notifying.
BluetoothGattNotifySession* session = update_sessions_[0].get();
EXPECT_TRUE(session->IsActive());
session->Stop(base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
base::Unretained(this)));
// Run message loop to stop the notify session.
base::RunLoop().Run();
EXPECT_EQ(4, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_FALSE(session->IsActive());
EXPECT_EQ(characteristic->GetIdentifier(),
session->GetCharacteristicIdentifier());
EXPECT_TRUE(characteristic->IsNotifying());
// Delete another session. Characteristic should still be notifying.
update_sessions_.pop_back();
EXPECT_EQ(2U, update_sessions_.size());
EXPECT_TRUE(characteristic->IsNotifying());
EXPECT_FALSE(update_sessions_[0]->IsActive());
EXPECT_TRUE(update_sessions_[1]->IsActive());
// Clear the last session.
update_sessions_.clear();
EXPECT_TRUE(update_sessions_.empty());
// Run message loop in order to do proper cleanup of sessions.
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(characteristic->IsNotifying());
success_callback_count_ = 0;
observer.Reset();
// Enable notifications again.
characteristic->StartNotifySession(
base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(0, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(1, observer.gatt_characteristic_value_changed_count());
EXPECT_TRUE(update_sessions_.empty());
EXPECT_FALSE(characteristic->IsNotifying());
// Run the message loop. Notifications should begin.
base::RunLoop().Run();
EXPECT_EQ(1, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(1, observer.gatt_characteristic_value_changed_count());
EXPECT_EQ(1U, update_sessions_.size());
EXPECT_TRUE(update_sessions_[0]->IsActive());
EXPECT_TRUE(characteristic->IsNotifying());
// Check that notifications are happening.
base::RunLoop().Run();
EXPECT_GT(observer.gatt_characteristic_value_changed_count(), 1);
// Request another session. This should return immediately.
characteristic->StartNotifySession(
base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
// Run message loop to stop the notify session.
base::RunLoop().Run();
EXPECT_EQ(2, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(2U, update_sessions_.size());
EXPECT_TRUE(update_sessions_[0]->IsActive());
EXPECT_TRUE(update_sessions_[1]->IsActive());
EXPECT_TRUE(characteristic->IsNotifying());
// Hide the characteristic. The sessions should become inactive.
fake_bluetooth_gatt_characteristic_client_->HideHeartRateCharacteristics();
EXPECT_EQ(2U, update_sessions_.size());
EXPECT_FALSE(update_sessions_[0]->IsActive());
EXPECT_FALSE(update_sessions_[1]->IsActive());
}
TEST_F(BluetoothGattBlueZTest, NotifySessionsMadeInactive) {
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
BluetoothDevice* device =
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
ASSERT_TRUE(device);
TestBluetoothAdapterObserver observer(adapter_);
// Expose the fake Heart Rate service. This will asynchronously expose
// characteristics.
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
ASSERT_EQ(1, observer.gatt_service_added_count());
BluetoothRemoteGattService* service =
device->GetGattService(observer.last_gatt_service_id());
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
// Run the message loop so that the characteristics appear.
base::RunLoop().Run();
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
->GetHeartRateMeasurementPath()
.value());
ASSERT_TRUE(characteristic);
EXPECT_FALSE(characteristic->IsNotifying());
EXPECT_TRUE(update_sessions_.empty());
// Send several requests to start notifications.
characteristic->StartNotifySession(
base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
characteristic->StartNotifySession(
base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
characteristic->StartNotifySession(
base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
characteristic->StartNotifySession(
base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
// The operation still hasn't completed but we should have received the first
// notification.
EXPECT_EQ(0, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(1, observer.gatt_characteristic_value_changed_count());
EXPECT_FALSE(characteristic->IsNotifying());
EXPECT_TRUE(update_sessions_.empty());
// Run the main loop. The initial call should complete. The queued calls
// should succeed immediately.
base::RunLoop().Run();
EXPECT_EQ(4, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(1, observer.gatt_characteristic_value_changed_count());
EXPECT_TRUE(characteristic->IsNotifying());
EXPECT_EQ(4U, update_sessions_.size());
for (int i = 0; i < 4; i++)
EXPECT_TRUE(update_sessions_[0]->IsActive());
// Stop notifications directly through the client. The sessions should get
// marked as inactive.
fake_bluetooth_gatt_characteristic_client_->StopNotify(
fake_bluetooth_gatt_characteristic_client_->GetHeartRateMeasurementPath(),
base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::DBusErrorCallback,
base::Unretained(this)));
EXPECT_EQ(5, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
// Run message loop to stop the notify session.
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(characteristic->IsNotifying());
EXPECT_EQ(4U, update_sessions_.size());
for (int i = 0; i < 4; i++)
EXPECT_FALSE(update_sessions_[0]->IsActive());
// It should be possible to restart notifications and the call should reset
// the session count and make a request through the client.
update_sessions_.clear();
success_callback_count_ = 0;
observer.Reset();
characteristic->StartNotifySession(
base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
// Run message loop to start the notify session.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(1, observer.gatt_characteristic_value_changed_count());
EXPECT_FALSE(characteristic->IsNotifying());
EXPECT_TRUE(update_sessions_.empty());
base::RunLoop().Run();
EXPECT_EQ(1, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(1, observer.gatt_characteristic_value_changed_count());
EXPECT_TRUE(characteristic->IsNotifying());
EXPECT_EQ(1U, update_sessions_.size());
EXPECT_TRUE(update_sessions_[0]->IsActive());
}
#if defined(OS_CHROMEOS)
TEST_F(BluetoothGattBlueZTest, ReliableWrite) {
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
BluetoothDeviceBlueZ* device = static_cast<BluetoothDeviceBlueZ*>(
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress));
ASSERT_TRUE(device);
TestBluetoothAdapterObserver observer(adapter_);
// Expose the fake Heart Rate service. This will asynchronously expose
// characteristics.
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
ASSERT_EQ(1, observer.gatt_service_added_count());
BluetoothRemoteGattService* service =
device->GetGattService(observer.last_gatt_service_id());
// Run the message loop so that the characteristics appear.
base::RunLoop().Run();
// Request to start notifications.
service
->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
->GetHeartRateMeasurementPath()
.value())
->StartNotifySession(
base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
base::RunLoop().Run();
// Obtain writable Heart Rate Control Point characteristic.
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
->GetHeartRateControlPointPath()
.value());
std::vector<uint8_t> write_value = {0x01};
// Prepare 1000 writes.
success_callback_count_ = 0;
error_callback_count_ = 0;
observer.Reset();
for (int i = 0; i < 1000; ++i) {
characteristic->PrepareWriteRemoteCharacteristic(
write_value,
base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
}
EXPECT_EQ(1000, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
// Abort.
device->AbortWrite(base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(1001, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
// Prepare another 1000 writes.
success_callback_count_ = 0;
error_callback_count_ = 0;
observer.Reset();
for (int i = 0; i < 1000; ++i) {
characteristic->PrepareWriteRemoteCharacteristic(
write_value,
base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
}
EXPECT_EQ(1000, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
// Execute.
device->ExecuteWrite(base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
EXPECT_EQ(1001, success_callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(1000, observer.gatt_characteristic_value_changed_count());
}
TEST_F(BluetoothGattBlueZTest, NotificationType) {
fake_bluetooth_device_client_->CreateDevice(
dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
BluetoothDeviceBlueZ* device = static_cast<BluetoothDeviceBlueZ*>(
adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress));
ASSERT_TRUE(device);
TestBluetoothAdapterObserver observer(adapter_);
// Expose the fake Heart Rate service. This will asynchronously expose
// characteristics.
fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
ASSERT_EQ(1, observer.gatt_service_added_count());
BluetoothRemoteGattService* service =
device->GetGattService(observer.last_gatt_service_id());
// Run the message loop so that the characteristics appear.
base::RunLoop().Run();
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
->GetHeartRateMeasurementPath()
.value());
// Request to start notifications.
characteristic->StartNotifySession(
device::BluetoothGattCharacteristic::NotificationType::kNotification,
base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
base::RunLoop().Run();
EXPECT_EQ(1, observer.gatt_characteristic_value_changed_count());
// Request to start indications.
fake_bluetooth_gatt_characteristic_client_->StopNotify(
fake_bluetooth_gatt_characteristic_client_->GetHeartRateMeasurementPath(),
base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::DBusErrorCallback,
base::Unretained(this)));
characteristic->StartNotifySession(
device::BluetoothGattCharacteristic::NotificationType::kIndication,
base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
base::Unretained(this)),
base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
base::Unretained(this)));
base::RunLoop().Run();
EXPECT_EQ(2, observer.gatt_characteristic_value_changed_count());
}
#endif
} // namespace bluez