summaryrefslogtreecommitdiffstats
path: root/chromium/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc')
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc1346
1 files changed, 1346 insertions, 0 deletions
diff --git a/chromium/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc b/chromium/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc
new file mode 100644
index 00000000000..673ed5f35fb
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc
@@ -0,0 +1,1346 @@
+// 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 "chromeos/dbus/fake_bluetooth_adapter_client.h"
+#include "chromeos/dbus/fake_bluetooth_agent_manager_client.h"
+#include "chromeos/dbus/fake_bluetooth_device_client.h"
+#include "chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h"
+#include "chromeos/dbus/fake_bluetooth_gatt_descriptor_client.h"
+#include "chromeos/dbus/fake_bluetooth_gatt_service_client.h"
+#include "chromeos/dbus/fake_bluetooth_input_client.h"
+#include "chromeos/dbus/fake_dbus_thread_manager.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 "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;
+
+namespace chromeos {
+
+namespace {
+
+const BluetoothUUID kHeartRateMeasurementUUID(
+ FakeBluetoothGattCharacteristicClient::kHeartRateMeasurementUUID);
+const BluetoothUUID kBodySensorLocationUUID(
+ FakeBluetoothGattCharacteristicClient::kBodySensorLocationUUID);
+const BluetoothUUID kHeartRateControlPointUUID(
+ 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;
+}
+
+class TestDeviceObserver : public BluetoothDevice::Observer {
+ public:
+ TestDeviceObserver(scoped_refptr<BluetoothAdapter> adapter,
+ BluetoothDevice* device)
+ : gatt_service_added_count_(0),
+ gatt_service_removed_count_(0),
+ device_address_(device->GetAddress()),
+ adapter_(adapter) {
+ device->AddObserver(this);
+ }
+
+ virtual ~TestDeviceObserver() {
+ BluetoothDevice* device = adapter_->GetDevice(device_address_);
+ if (device)
+ device->RemoveObserver(this);
+ }
+
+ // BluetoothDevice::Observer overrides.
+ virtual void GattServiceAdded(
+ BluetoothDevice* device,
+ BluetoothGattService* service) OVERRIDE {
+ ASSERT_EQ(device_address_, device->GetAddress());
+
+ ++gatt_service_added_count_;
+ last_gatt_service_id_ = service->GetIdentifier();
+ last_gatt_service_uuid_ = service->GetUUID();
+
+ EXPECT_FALSE(service->IsLocal());
+ EXPECT_TRUE(service->IsPrimary());
+
+ EXPECT_EQ(device->GetGattService(last_gatt_service_id_), service);
+
+ QuitMessageLoop();
+ }
+
+ virtual void GattServiceRemoved(
+ BluetoothDevice* device,
+ BluetoothGattService* service) OVERRIDE {
+ ASSERT_EQ(device_address_, device->GetAddress());
+
+ ++gatt_service_removed_count_;
+ last_gatt_service_id_ = service->GetIdentifier();
+ last_gatt_service_uuid_ = service->GetUUID();
+
+ EXPECT_FALSE(service->IsLocal());
+ EXPECT_TRUE(service->IsPrimary());
+
+ // The device should return NULL for this service.
+ EXPECT_FALSE(device->GetGattService(last_gatt_service_id_));
+
+ QuitMessageLoop();
+ }
+
+ int gatt_service_added_count_;
+ int gatt_service_removed_count_;
+ std::string last_gatt_service_id_;
+ BluetoothUUID last_gatt_service_uuid_;
+
+ private:
+ // Some tests use a message loop since background processing is simulated;
+ // break out of those loops.
+ void QuitMessageLoop() {
+ if (base::MessageLoop::current() &&
+ base::MessageLoop::current()->is_running())
+ base::MessageLoop::current()->Quit();
+ }
+
+ std::string device_address_;
+ scoped_refptr<BluetoothAdapter> adapter_;
+};
+
+class TestGattServiceObserver : public BluetoothGattService::Observer {
+ public:
+ TestGattServiceObserver(scoped_refptr<BluetoothAdapter> adapter,
+ BluetoothDevice* device,
+ BluetoothGattService* service)
+ : gatt_service_changed_count_(0),
+ gatt_characteristic_added_count_(0),
+ gatt_characteristic_removed_count_(0),
+ gatt_characteristic_value_changed_count_(0),
+ gatt_descriptor_added_count_(0),
+ gatt_descriptor_removed_count_(0),
+ gatt_descriptor_value_changed_count_(0),
+ device_address_(device->GetAddress()),
+ gatt_service_id_(service->GetIdentifier()),
+ adapter_(adapter) {
+ service->AddObserver(this);
+ }
+
+ virtual ~TestGattServiceObserver() {
+ // See if either the device or the service even exist.
+ BluetoothDevice* device = adapter_->GetDevice(device_address_);
+ if (!device)
+ return;
+
+ BluetoothGattService* service = device->GetGattService(gatt_service_id_);
+ if (!service)
+ return;
+
+ service->RemoveObserver(this);
+ }
+
+ // BluetoothGattService::Observer overrides.
+ virtual void GattServiceChanged(BluetoothGattService* service) OVERRIDE {
+ ASSERT_EQ(gatt_service_id_, service->GetIdentifier());
+ ++gatt_service_changed_count_;
+
+ QuitMessageLoop();
+ }
+
+ virtual void GattCharacteristicAdded(
+ BluetoothGattService* service,
+ BluetoothGattCharacteristic* characteristic) OVERRIDE {
+ ASSERT_EQ(gatt_service_id_, service->GetIdentifier());
+
+ ++gatt_characteristic_added_count_;
+ last_gatt_characteristic_id_ = characteristic->GetIdentifier();
+ last_gatt_characteristic_uuid_ = characteristic->GetUUID();
+
+ EXPECT_EQ(service->GetCharacteristic(last_gatt_characteristic_id_),
+ characteristic);
+ EXPECT_EQ(service, characteristic->GetService());
+
+ QuitMessageLoop();
+ }
+
+ virtual void GattCharacteristicRemoved(
+ BluetoothGattService* service,
+ BluetoothGattCharacteristic* characteristic) OVERRIDE {
+ ASSERT_EQ(gatt_service_id_, service->GetIdentifier());
+
+ ++gatt_characteristic_removed_count_;
+ last_gatt_characteristic_id_ = characteristic->GetIdentifier();
+ last_gatt_characteristic_uuid_ = characteristic->GetUUID();
+
+ // The service should return NULL for this characteristic.
+ EXPECT_FALSE(service->GetCharacteristic(last_gatt_characteristic_id_));
+ EXPECT_EQ(service, characteristic->GetService());
+
+ QuitMessageLoop();
+ }
+
+ virtual void GattCharacteristicValueChanged(
+ BluetoothGattService* service,
+ BluetoothGattCharacteristic* characteristic,
+ const std::vector<uint8>& value) OVERRIDE {
+ ASSERT_EQ(gatt_service_id_, service->GetIdentifier());
+
+ ++gatt_characteristic_value_changed_count_;
+ last_gatt_characteristic_id_ = characteristic->GetIdentifier();
+ last_gatt_characteristic_uuid_ = characteristic->GetUUID();
+ last_changed_characteristic_value_ = value;
+
+ EXPECT_EQ(service->GetCharacteristic(last_gatt_characteristic_id_),
+ characteristic);
+ EXPECT_EQ(service, characteristic->GetService());
+
+ QuitMessageLoop();
+ }
+
+ virtual void GattDescriptorAdded(
+ BluetoothGattCharacteristic* characteristic,
+ BluetoothGattDescriptor* descriptor) OVERRIDE {
+ ASSERT_EQ(gatt_service_id_, characteristic->GetService()->GetIdentifier());
+
+ ++gatt_descriptor_added_count_;
+ last_gatt_descriptor_id_ = descriptor->GetIdentifier();
+ last_gatt_descriptor_uuid_ = descriptor->GetUUID();
+
+ EXPECT_EQ(characteristic->GetDescriptor(last_gatt_descriptor_id_),
+ descriptor);
+ EXPECT_EQ(characteristic, descriptor->GetCharacteristic());
+
+ QuitMessageLoop();
+ }
+
+ virtual void GattDescriptorRemoved(
+ BluetoothGattCharacteristic* characteristic,
+ BluetoothGattDescriptor* descriptor) OVERRIDE {
+ ASSERT_EQ(gatt_service_id_, characteristic->GetService()->GetIdentifier());
+
+ ++gatt_descriptor_removed_count_;
+ last_gatt_descriptor_id_ = descriptor->GetIdentifier();
+ last_gatt_descriptor_uuid_ = descriptor->GetUUID();
+
+ // The characteristic should return NULL for this descriptor..
+ EXPECT_FALSE(characteristic->GetDescriptor(last_gatt_descriptor_id_));
+ EXPECT_EQ(characteristic, descriptor->GetCharacteristic());
+
+ QuitMessageLoop();
+ }
+
+ virtual void GattDescriptorValueChanged(
+ BluetoothGattCharacteristic* characteristic,
+ BluetoothGattDescriptor* descriptor,
+ const std::vector<uint8>& value) OVERRIDE {
+ ASSERT_EQ(gatt_service_id_, characteristic->GetService()->GetIdentifier());
+
+ ++gatt_descriptor_value_changed_count_;
+ last_gatt_descriptor_id_ = descriptor->GetIdentifier();
+ last_gatt_descriptor_uuid_ = descriptor->GetUUID();
+ last_changed_descriptor_value_ = value;
+
+ EXPECT_EQ(characteristic->GetDescriptor(last_gatt_descriptor_id_),
+ descriptor);
+ EXPECT_EQ(characteristic, descriptor->GetCharacteristic());
+
+ QuitMessageLoop();
+ }
+
+ int gatt_service_changed_count_;
+ int gatt_characteristic_added_count_;
+ int gatt_characteristic_removed_count_;
+ int gatt_characteristic_value_changed_count_;
+ int gatt_descriptor_added_count_;
+ int gatt_descriptor_removed_count_;
+ int gatt_descriptor_value_changed_count_;
+ std::string last_gatt_characteristic_id_;
+ BluetoothUUID last_gatt_characteristic_uuid_;
+ std::vector<uint8> last_changed_characteristic_value_;
+ std::string last_gatt_descriptor_id_;
+ BluetoothUUID last_gatt_descriptor_uuid_;
+ std::vector<uint8> last_changed_descriptor_value_;
+
+ private:
+ // Some tests use a message loop since background processing is simulated;
+ // break out of those loops.
+ void QuitMessageLoop() {
+ if (base::MessageLoop::current() &&
+ base::MessageLoop::current()->is_running())
+ base::MessageLoop::current()->Quit();
+ }
+
+ std::string device_address_;
+ std::string gatt_service_id_;
+ scoped_refptr<BluetoothAdapter> adapter_;
+};
+
+} // namespace
+
+class BluetoothGattChromeOSTest : public testing::Test {
+ public:
+ BluetoothGattChromeOSTest()
+ : fake_bluetooth_gatt_service_client_(NULL),
+ success_callback_count_(0),
+ error_callback_count_(0) {
+ }
+
+ virtual void SetUp() {
+ FakeDBusThreadManager* fake_dbus_thread_manager = new FakeDBusThreadManager;
+ fake_bluetooth_device_client_ = new FakeBluetoothDeviceClient;
+ fake_bluetooth_gatt_service_client_ =
+ new FakeBluetoothGattServiceClient;
+ fake_bluetooth_gatt_characteristic_client_ =
+ new FakeBluetoothGattCharacteristicClient;
+ fake_bluetooth_gatt_descriptor_client_ =
+ new FakeBluetoothGattDescriptorClient;
+ fake_dbus_thread_manager->SetBluetoothDeviceClient(
+ scoped_ptr<BluetoothDeviceClient>(
+ fake_bluetooth_device_client_));
+ fake_dbus_thread_manager->SetBluetoothGattServiceClient(
+ scoped_ptr<BluetoothGattServiceClient>(
+ fake_bluetooth_gatt_service_client_));
+ fake_dbus_thread_manager->SetBluetoothGattCharacteristicClient(
+ scoped_ptr<BluetoothGattCharacteristicClient>(
+ fake_bluetooth_gatt_characteristic_client_));
+ fake_dbus_thread_manager->SetBluetoothGattDescriptorClient(
+ scoped_ptr<BluetoothGattDescriptorClient>(
+ fake_bluetooth_gatt_descriptor_client_));
+ fake_dbus_thread_manager->SetBluetoothAdapterClient(
+ scoped_ptr<BluetoothAdapterClient>(new FakeBluetoothAdapterClient));
+ fake_dbus_thread_manager->SetBluetoothInputClient(
+ scoped_ptr<BluetoothInputClient>(new FakeBluetoothInputClient));
+ fake_dbus_thread_manager->SetBluetoothAgentManagerClient(
+ scoped_ptr<BluetoothAgentManagerClient>(
+ new FakeBluetoothAgentManagerClient));
+ DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager);
+
+ GetAdapter();
+
+ adapter_->SetPowered(
+ true,
+ base::Bind(&base::DoNothing),
+ base::Bind(&base::DoNothing));
+ ASSERT_TRUE(adapter_->IsPowered());
+ }
+
+ virtual void TearDown() {
+ adapter_ = NULL;
+ update_sessions_.clear();
+ gatt_conn_.reset();
+ DBusThreadManager::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 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_;
+
+ FakeBluetoothDeviceClient* fake_bluetooth_device_client_;
+ FakeBluetoothGattServiceClient* fake_bluetooth_gatt_service_client_;
+ FakeBluetoothGattCharacteristicClient*
+ fake_bluetooth_gatt_characteristic_client_;
+ 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_;
+};
+
+TEST_F(BluetoothGattChromeOSTest, GattConnection) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ 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(FakeBluetoothDeviceClient::kLowEnergyAddress,
+ gatt_conn_->GetDeviceAddress());
+
+ gatt_conn_->Disconnect(
+ base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ 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(3, 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(4, 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(5, 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(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(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(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+ 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(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(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(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(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(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(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(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(FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
+ observer.last_gatt_service_uuid_);
+ EXPECT_EQ(
+ NULL, adapter_->GetDevice(FakeBluetoothDeviceClient::kLowEnergyAddress));
+}
+
+TEST_F(BluetoothGattChromeOSTest, GattCharacteristicAddedAndRemoved) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ ASSERT_EQ(1, observer.gatt_service_added_count_);
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id_);
+
+ TestGattServiceObserver service_observer(adapter_, device, service);
+ EXPECT_EQ(0, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_added_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_removed_count_);
+ EXPECT_EQ(0, service_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(4, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(3, service_observer.gatt_characteristic_added_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_removed_count_);
+ EXPECT_EQ(0, service_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(8, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(3, service_observer.gatt_characteristic_added_count_);
+ EXPECT_EQ(3, service_observer.gatt_characteristic_removed_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(service->GetCharacteristics().empty());
+
+ // Re-expose the heart rate characteristics.
+ fake_bluetooth_gatt_characteristic_client_->ExposeHeartRateCharacteristics(
+ fake_bluetooth_gatt_service_client_->GetHeartRateServicePath());
+ EXPECT_EQ(12, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(6, service_observer.gatt_characteristic_added_count_);
+ EXPECT_EQ(3, service_observer.gatt_characteristic_removed_count_);
+ EXPECT_EQ(0, service_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(16, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(6, service_observer.gatt_characteristic_added_count_);
+ EXPECT_EQ(6, service_observer.gatt_characteristic_removed_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
+}
+
+TEST_F(BluetoothGattChromeOSTest, GattDescriptorAddedAndRemoved) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ ASSERT_EQ(1, observer.gatt_service_added_count_);
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id_);
+
+ TestGattServiceObserver service_observer(adapter_, device, service);
+ EXPECT_EQ(0, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(0, service_observer.gatt_descriptor_added_count_);
+ EXPECT_EQ(0, service_observer.gatt_descriptor_removed_count_);
+ EXPECT_EQ(0, service_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(4, service_observer.gatt_service_changed_count_);
+
+ // Only the Heart Rate Measurement characteristic has a descriptor.
+ EXPECT_EQ(1, service_observer.gatt_descriptor_added_count_);
+ EXPECT_EQ(0, service_observer.gatt_descriptor_removed_count_);
+ EXPECT_EQ(0, service_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(),
+ service_observer.last_gatt_descriptor_uuid_);
+ EXPECT_EQ(descriptor->GetIdentifier(),
+ service_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(5, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(1, service_observer.gatt_descriptor_added_count_);
+ EXPECT_EQ(1, service_observer.gatt_descriptor_removed_count_);
+ EXPECT_EQ(0, service_observer.gatt_descriptor_value_changed_count_);
+
+ // Expose the descriptor again.
+ service_observer.last_gatt_descriptor_id_.clear();
+ service_observer.last_gatt_descriptor_uuid_ = BluetoothUUID();
+ fake_bluetooth_gatt_descriptor_client_->ExposeDescriptor(
+ dbus::ObjectPath(characteristic->GetIdentifier()),
+ FakeBluetoothGattDescriptorClient::
+ kClientCharacteristicConfigurationUUID);
+ EXPECT_EQ(6, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(1U, characteristic->GetDescriptors().size());
+ EXPECT_EQ(2, service_observer.gatt_descriptor_added_count_);
+ EXPECT_EQ(1, service_observer.gatt_descriptor_removed_count_);
+ EXPECT_EQ(0, service_observer.gatt_descriptor_value_changed_count_);
+
+ descriptor = characteristic->GetDescriptors()[0];
+ EXPECT_FALSE(descriptor->IsLocal());
+ EXPECT_EQ(BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid(),
+ descriptor->GetUUID());
+ EXPECT_EQ(descriptor->GetUUID(), service_observer.last_gatt_descriptor_uuid_);
+ EXPECT_EQ(descriptor->GetIdentifier(),
+ service_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(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(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(
+ 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(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(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(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(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(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ ASSERT_EQ(1, observer.gatt_service_added_count_);
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id_);
+
+ TestGattServiceObserver service_observer(adapter_, device, service);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
+
+ // Run the message loop so that the characteristics appear.
+ base::MessageLoop::current()->Run();
+
+ // Issue write request to non-writeable characteristics.
+ service_observer.last_gatt_characteristic_id_.clear();
+ service_observer.last_gatt_characteristic_uuid_ = BluetoothUUID();
+
+ 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::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_TRUE(service_observer.last_gatt_characteristic_id_.empty());
+ EXPECT_FALSE(service_observer.last_gatt_characteristic_uuid_.IsValid());
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(0, service_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::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_TRUE(service_observer.last_gatt_characteristic_id_.empty());
+ EXPECT_FALSE(service_observer.last_gatt_characteristic_uuid_.IsValid());
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(2, error_callback_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
+
+ // Issue write request to writeable 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::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_TRUE(service_observer.last_gatt_characteristic_id_.empty());
+ EXPECT_FALSE(service_observer.last_gatt_characteristic_uuid_.IsValid());
+ EXPECT_EQ(1, success_callback_count_);
+ EXPECT_EQ(2, error_callback_count_);
+ EXPECT_EQ(0, service_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::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, success_callback_count_);
+ EXPECT_EQ(2, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(ValuesEqual(characteristic->GetValue(), last_read_value_));
+}
+
+TEST_F(BluetoothGattChromeOSTest, GattCharacteristicProperties) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id_);
+
+ TestGattServiceObserver service_observer(adapter_, device, service);
+ 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::kPropertyRead,
+ characteristic->GetProperties());
+
+ characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetHeartRateControlPointPath().value());
+ EXPECT_EQ(BluetoothGattCharacteristic::kPropertyWrite,
+ characteristic->GetProperties());
+
+ characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetHeartRateMeasurementPath().value());
+ EXPECT_EQ(BluetoothGattCharacteristic::kPropertyNotify,
+ characteristic->GetProperties());
+}
+
+TEST_F(BluetoothGattChromeOSTest, GattDescriptorValue) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ ASSERT_EQ(1, observer.gatt_service_added_count_);
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id_);
+
+ TestGattServiceObserver service_observer(adapter_, device, service);
+ EXPECT_EQ(0, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(0, service_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(4, service_observer.gatt_service_changed_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());
+
+ BluetoothGattDescriptor* descriptor = characteristic->GetDescriptors()[0];
+ EXPECT_FALSE(descriptor->IsLocal());
+ EXPECT_EQ(BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid(),
+ descriptor->GetUUID());
+
+ std::vector<uint8> desc_value;
+ desc_value.push_back(1);
+ desc_value.push_back(0);
+
+ /* 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::ErrorCallback,
+ 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(4, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(1, service_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::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, 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(4, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(1, service_observer.gatt_descriptor_value_changed_count_);
+
+ // Read new value.
+ descriptor->ReadRemoteDescriptor(
+ base::Bind(&BluetoothGattChromeOSTest::ValueCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ 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(4, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(2, service_observer.gatt_descriptor_value_changed_count_);
+}
+
+TEST_F(BluetoothGattChromeOSTest, NotifySessions) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device =
+ adapter_->GetDevice(FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ ASSERT_EQ(1, observer.gatt_service_added_count_);
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id_);
+
+ TestGattServiceObserver service_observer(adapter_, device, service);
+ EXPECT_EQ(0, service_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::ErrorCallback,
+ 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, service_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::ErrorCallback,
+ base::Unretained(this)));
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_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, service_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(service_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;
+ service_observer.gatt_characteristic_value_changed_count_ = 0;
+
+ // Enable notifications again.
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_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, service_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(service_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::ErrorCallback,
+ 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(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device =
+ adapter_->GetDevice(FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ ASSERT_EQ(1, observer.gatt_service_added_count_);
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id_);
+
+ TestGattServiceObserver service_observer(adapter_, device, service);
+ EXPECT_EQ(0, service_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::ErrorCallback,
+ base::Unretained(this)));
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ 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, service_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, service_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;
+ service_observer.gatt_characteristic_value_changed_count_ = 0;
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_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, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(characteristic->IsNotifying());
+ EXPECT_EQ(1U, update_sessions_.size());
+ EXPECT_TRUE(update_sessions_[0]->IsActive());
+}
+
+} // namespace chromeos