| // 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/run_loop.h" |
| #include "base/test/bind_test_util.h" |
| #include "base/test/scoped_task_environment.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/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/public/cpp/bluetooth_uuid.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::test::TaskEnvironment task_environment_; |
| |
| 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 |