summaryrefslogtreecommitdiffstats
path: root/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'device/bluetooth/bluetooth_gatt_chromeos_unittest.cc')
-rw-r--r--device/bluetooth/bluetooth_gatt_chromeos_unittest.cc1237
1 files changed, 1237 insertions, 0 deletions
diff --git a/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc b/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc
new file mode 100644
index 0000000..cc5860d
--- /dev/null
+++ b/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc
@@ -0,0 +1,1237 @@
+// Copyright 2014 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 "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.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_gatt_characteristic.h"
+#include "device/bluetooth/bluetooth_gatt_connection.h"
+#include "device/bluetooth/bluetooth_gatt_descriptor.h"
+#include "device/bluetooth/bluetooth_gatt_notify_session.h"
+#include "device/bluetooth/bluetooth_gatt_service.h"
+#include "device/bluetooth/bluetooth_uuid.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::BluetoothGattCharacteristic;
+using device::BluetoothGattConnection;
+using device::BluetoothGattDescriptor;
+using device::BluetoothGattService;
+using device::BluetoothGattNotifySession;
+using device::BluetoothUUID;
+using device::TestBluetoothAdapterObserver;
+
+namespace chromeos {
+
+namespace {
+
+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>& value0,
+ const std::vector<uint8>& 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 BluetoothGattChromeOSTest : public testing::Test {
+ public:
+ BluetoothGattChromeOSTest()
+ : fake_bluetooth_gatt_service_client_(NULL),
+ success_callback_count_(0),
+ error_callback_count_(0) {
+ }
+
+ void SetUp() override {
+ scoped_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(
+ scoped_ptr<bluez::BluetoothDeviceClient>(
+ fake_bluetooth_device_client_));
+ dbus_setter->SetBluetoothGattServiceClient(
+ scoped_ptr<bluez::BluetoothGattServiceClient>(
+ fake_bluetooth_gatt_service_client_));
+ dbus_setter->SetBluetoothGattCharacteristicClient(
+ scoped_ptr<bluez::BluetoothGattCharacteristicClient>(
+ fake_bluetooth_gatt_characteristic_client_));
+ dbus_setter->SetBluetoothGattDescriptorClient(
+ scoped_ptr<bluez::BluetoothGattDescriptorClient>(
+ fake_bluetooth_gatt_descriptor_client_));
+ dbus_setter->SetBluetoothAdapterClient(
+ scoped_ptr<bluez::BluetoothAdapterClient>(
+ new bluez::FakeBluetoothAdapterClient));
+ dbus_setter->SetBluetoothInputClient(
+ scoped_ptr<bluez::BluetoothInputClient>(
+ new bluez::FakeBluetoothInputClient));
+ dbus_setter->SetBluetoothAgentManagerClient(
+ scoped_ptr<bluez::BluetoothAgentManagerClient>(
+ new bluez::FakeBluetoothAgentManagerClient));
+
+ GetAdapter();
+
+ adapter_->SetPowered(
+ true,
+ base::Bind(&base::DoNothing),
+ base::Bind(&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::Bind(&BluetoothGattChromeOSTest::AdapterCallback,
+ base::Unretained(this)));
+ ASSERT_TRUE(adapter_.get() != NULL);
+ ASSERT_TRUE(adapter_->IsInitialized());
+ ASSERT_TRUE(adapter_->IsPresent());
+ }
+
+ void AdapterCallback(scoped_refptr<BluetoothAdapter> adapter) {
+ adapter_ = adapter;
+ }
+
+ void SuccessCallback() {
+ ++success_callback_count_;
+ }
+
+ void ValueCallback(const std::vector<uint8>& value) {
+ ++success_callback_count_;
+ last_read_value_ = value;
+ }
+
+ void GattConnectionCallback(scoped_ptr<BluetoothGattConnection> conn) {
+ ++success_callback_count_;
+ gatt_conn_ = conn.Pass();
+ }
+
+ void NotifySessionCallback(scoped_ptr<BluetoothGattNotifySession> session) {
+ ++success_callback_count_;
+ update_sessions_.push_back(session.release());
+ QuitMessageLoop();
+ }
+
+ void ServiceErrorCallback(BluetoothGattService::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::MessageLoop::current() &&
+ base::MessageLoop::current()->is_running())
+ base::MessageLoop::current()->Quit();
+ }
+
+ 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_;
+ scoped_ptr<device::BluetoothGattConnection> gatt_conn_;
+ ScopedVector<BluetoothGattNotifySession> update_sessions_;
+ scoped_refptr<BluetoothAdapter> adapter_;
+
+ int success_callback_count_;
+ int error_callback_count_;
+ std::vector<uint8> last_read_value_;
+ BluetoothGattService::GattErrorCode last_service_error_;
+};
+
+TEST_F(BluetoothGattChromeOSTest, 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(gatt_conn_.get());
+ ASSERT_EQ(0, success_callback_count_);
+ ASSERT_EQ(0, error_callback_count_);
+
+ device->CreateGattConnection(
+ base::Bind(&BluetoothGattChromeOSTest::GattConnectionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(device->IsConnected());
+ ASSERT_TRUE(gatt_conn_.get());
+ EXPECT_TRUE(gatt_conn_->IsConnected());
+ EXPECT_EQ(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress,
+ gatt_conn_->GetDeviceAddress());
+
+ gatt_conn_->Disconnect();
+ EXPECT_TRUE(device->IsConnected());
+ EXPECT_FALSE(gatt_conn_->IsConnected());
+
+ device->CreateGattConnection(
+ base::Bind(&BluetoothGattChromeOSTest::GattConnectionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(2, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(device->IsConnected());
+ ASSERT_TRUE(gatt_conn_.get());
+ EXPECT_TRUE(gatt_conn_->IsConnected());
+
+ device->Disconnect(
+ base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(3, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ ASSERT_TRUE(gatt_conn_.get());
+ EXPECT_FALSE(gatt_conn_->IsConnected());
+
+ device->CreateGattConnection(
+ base::Bind(&BluetoothGattChromeOSTest::GattConnectionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(4, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(device->IsConnected());
+ 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(BluetoothGattChromeOSTest, 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());
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id());
+ EXPECT_FALSE(service->IsLocal());
+ 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_FALSE(service->IsLocal());
+ 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(BluetoothGattChromeOSTest, 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());
+
+ BluetoothGattService* 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::MessageLoop::current()->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(1, observer.gatt_discovery_complete_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(1, observer.gatt_discovery_complete_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(BluetoothGattChromeOSTest, 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());
+
+ BluetoothGattService* 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::MessageLoop::current()->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());
+
+ BluetoothGattCharacteristic* 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());
+
+ BluetoothGattDescriptor* descriptor = characteristic->GetDescriptors()[0];
+ EXPECT_FALSE(descriptor->IsLocal());
+ EXPECT_EQ(BluetoothGattDescriptor::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_FALSE(descriptor->IsLocal());
+ EXPECT_EQ(BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid(),
+ descriptor->GetUUID());
+ EXPECT_EQ(descriptor->GetUUID(), observer.last_gatt_descriptor_uuid());
+ EXPECT_EQ(descriptor->GetIdentifier(), observer.last_gatt_descriptor_id());
+}
+
+TEST_F(BluetoothGattChromeOSTest, AdapterAddedAfterGattService) {
+ // This unit test tests that all remote GATT objects are created for D-Bus
+ // objects that were already exposed.
+ 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));
+ 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();
+ BluetoothDevice* device =
+ adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+ EXPECT_EQ(1U, device->GetGattServices().size());
+
+ BluetoothGattService* service = device->GetGattServices()[0];
+ ASSERT_TRUE(service);
+ EXPECT_FALSE(service->IsLocal());
+ EXPECT_TRUE(service->IsPrimary());
+ EXPECT_EQ(BluetoothUUID(
+ bluez::FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
+ service->GetUUID());
+ EXPECT_EQ(service, device->GetGattServices()[0]);
+ EXPECT_EQ(service, device->GetGattService(service->GetIdentifier()));
+ EXPECT_FALSE(service->IsLocal());
+ EXPECT_EQ(3U, service->GetCharacteristics().size());
+
+ BluetoothGattCharacteristic* characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetBodySensorLocationPath().value());
+ ASSERT_TRUE(characteristic);
+ EXPECT_EQ(BluetoothUUID(bluez::FakeBluetoothGattCharacteristicClient::
+ kBodySensorLocationUUID),
+ characteristic->GetUUID());
+ EXPECT_FALSE(characteristic->IsLocal());
+ 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_FALSE(characteristic->IsLocal());
+ 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());
+ EXPECT_FALSE(characteristic->IsLocal());
+ EXPECT_EQ(1U, characteristic->GetDescriptors().size());
+
+ BluetoothGattDescriptor* descriptor = characteristic->GetDescriptors()[0];
+ ASSERT_TRUE(descriptor);
+ EXPECT_EQ(BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid(),
+ descriptor->GetUUID());
+ EXPECT_FALSE(descriptor->IsLocal());
+}
+
+TEST_F(BluetoothGattChromeOSTest, 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());
+
+ BluetoothGattService* 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::MessageLoop::current()->Run();
+
+ // Issue write request to non-writable characteristics.
+ observer.Reset();
+
+ std::vector<uint8> write_value;
+ write_value.push_back(0x01);
+ BluetoothGattCharacteristic* 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(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::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(BluetoothGattService::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(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::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(BluetoothGattService::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(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::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> invalid_write_length;
+ invalid_write_length.push_back(0x01);
+ invalid_write_length.push_back(0x00);
+ characteristic->WriteRemoteCharacteristic(
+ invalid_write_length,
+ base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, success_callback_count_);
+ EXPECT_EQ(3, error_callback_count_);
+ EXPECT_EQ(BluetoothGattService::GATT_ERROR_INVALID_LENGTH,
+ last_service_error_);
+ EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
+
+ std::vector<uint8> invalid_write_value;
+ invalid_write_value.push_back(0x02);
+ characteristic->WriteRemoteCharacteristic(
+ invalid_write_value,
+ base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, success_callback_count_);
+ EXPECT_EQ(4, error_callback_count_);
+ EXPECT_EQ(BluetoothGattService::GATT_ERROR_FAILED, last_service_error_);
+ EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
+
+ // Issue a read request. A successful read results in a
+ // CharacteristicValueChanged notification.
+ 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(&BluetoothGattChromeOSTest::ValueCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, success_callback_count_);
+ EXPECT_EQ(4, error_callback_count_);
+ EXPECT_EQ(1, 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(&BluetoothGattChromeOSTest::ValueCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::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(1, observer.gatt_characteristic_value_changed_count());
+
+ // Next read should error because IN_PROGRESS
+ characteristic->ReadRemoteCharacteristic(
+ base::Bind(&BluetoothGattChromeOSTest::ValueCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(5, error_callback_count_);
+ EXPECT_EQ(BluetoothGattService::GATT_ERROR_IN_PROGRESS, last_service_error_);
+
+ // But previous call finished.
+ EXPECT_EQ(3, success_callback_count_);
+ EXPECT_EQ(2, 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(&BluetoothGattChromeOSTest::ValueCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(3, success_callback_count_);
+ EXPECT_EQ(6, error_callback_count_);
+ EXPECT_EQ(BluetoothGattService::GATT_ERROR_NOT_AUTHORIZED,
+ last_service_error_);
+ EXPECT_EQ(2, 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(&BluetoothGattChromeOSTest::ValueCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(3, success_callback_count_);
+ EXPECT_EQ(7, error_callback_count_);
+ EXPECT_EQ(BluetoothGattService::GATT_ERROR_NOT_PAIRED, last_service_error_);
+ EXPECT_EQ(2, observer.gatt_characteristic_value_changed_count());
+ fake_bluetooth_gatt_characteristic_client_->SetAuthenticated(true);
+}
+
+TEST_F(BluetoothGattChromeOSTest, 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));
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id());
+
+ EXPECT_TRUE(service->GetCharacteristics().empty());
+
+ // Run the message loop so that the characteristics appear.
+ base::MessageLoop::current()->Run();
+
+ BluetoothGattCharacteristic *characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetBodySensorLocationPath().value());
+ EXPECT_EQ(BluetoothGattCharacteristic::PROPERTY_READ,
+ characteristic->GetProperties());
+
+ characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetHeartRateControlPointPath().value());
+ EXPECT_EQ(BluetoothGattCharacteristic::PROPERTY_WRITE,
+ characteristic->GetProperties());
+
+ characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetHeartRateMeasurementPath().value());
+ EXPECT_EQ(BluetoothGattCharacteristic::PROPERTY_NOTIFY,
+ characteristic->GetProperties());
+}
+
+TEST_F(BluetoothGattChromeOSTest, 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());
+
+ BluetoothGattService* 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_descriptor_value_changed_count());
+ EXPECT_TRUE(service->GetCharacteristics().empty());
+
+ // Run the message loop so that the characteristics appear.
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(0, observer.gatt_service_changed_count());
+ EXPECT_EQ(1, observer.gatt_discovery_complete_count());
+
+ // Only the Heart Rate Measurement characteristic has a descriptor.
+ BluetoothGattCharacteristic* characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetHeartRateMeasurementPath().value());
+ ASSERT_TRUE(characteristic);
+ EXPECT_EQ(1U, characteristic->GetDescriptors().size());
+ EXPECT_FALSE(characteristic->IsNotifying());
+
+ BluetoothGattDescriptor* descriptor = characteristic->GetDescriptors()[0];
+ EXPECT_FALSE(descriptor->IsLocal());
+ EXPECT_EQ(BluetoothGattDescriptor::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(&BluetoothGattChromeOSTest::ValueCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::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(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, success_callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(BluetoothGattService::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(&BluetoothGattChromeOSTest::ValueCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::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(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ base::MessageLoop::current()->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(&BluetoothGattChromeOSTest::ValueCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::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(BluetoothGattChromeOSTest, 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());
+
+ BluetoothGattService* 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::MessageLoop::current()->Run();
+
+ BluetoothGattCharacteristic* 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(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::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(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::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_TRUE(characteristic->IsNotifying());
+
+ // Run the main loop. The initial call should complete. The queued call should
+ // succeed immediately.
+ base::MessageLoop::current()->Run();
+
+ 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::MessageLoop::current()->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];
+ EXPECT_TRUE(session->IsActive());
+ session->Stop(base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)));
+ 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());
+ EXPECT_FALSE(characteristic->IsNotifying());
+
+ success_callback_count_ = 0;
+ observer.Reset();
+
+ // Enable notifications again.
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::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_TRUE(characteristic->IsNotifying());
+
+ // Run the message loop. Notifications should begin.
+ base::MessageLoop::current()->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::MessageLoop::current()->Run();
+ EXPECT_GT(observer.gatt_characteristic_value_changed_count(), 1);
+
+ // Request another session. This should return immediately.
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ 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(BluetoothGattChromeOSTest, 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());
+
+ BluetoothGattService* 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::MessageLoop::current()->Run();
+
+ BluetoothGattCharacteristic* 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(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::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(characteristic->IsNotifying());
+ EXPECT_TRUE(update_sessions_.empty());
+
+ // Run the main loop. The initial call should complete. The queued calls
+ // should succeed immediately.
+ base::MessageLoop::current()->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(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::DBusErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(5, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ 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(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::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(characteristic->IsNotifying());
+ EXPECT_TRUE(update_sessions_.empty());
+
+ base::MessageLoop::current()->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());
+}
+
+} // namespace chromeos