| // 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/dbus_thread_manager.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 "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 TestObserver : public BluetoothAdapter::Observer { |
| public: |
| TestObserver(scoped_refptr<BluetoothAdapter> adapter) |
| : gatt_service_added_count_(0), |
| gatt_service_removed_count_(0), |
| gatt_service_changed_count_(0), |
| gatt_discovery_complete_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), |
| adapter_(adapter) { |
| adapter_->AddObserver(this); |
| } |
| |
| virtual ~TestObserver() { |
| adapter_->RemoveObserver(this); |
| } |
| |
| // BluetoothAdapter::Observer overrides. |
| virtual void GattServiceAdded(BluetoothAdapter* adapter, |
| BluetoothDevice* device, |
| BluetoothGattService* service) OVERRIDE { |
| ASSERT_EQ(adapter_.get(), adapter); |
| ASSERT_EQ(service->GetDevice(), device); |
| |
| ++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(BluetoothAdapter* adapter, |
| BluetoothDevice* device, |
| BluetoothGattService* service) OVERRIDE { |
| ASSERT_EQ(adapter_.get(), adapter); |
| ASSERT_EQ(service->GetDevice(), device); |
| |
| ++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(); |
| } |
| |
| virtual void GattDiscoveryCompleteForService( |
| BluetoothAdapter* adapter, |
| BluetoothGattService* service) OVERRIDE { |
| ASSERT_EQ(adapter_.get(), adapter); |
| ++gatt_discovery_complete_count_; |
| |
| QuitMessageLoop(); |
| } |
| |
| virtual void GattServiceChanged(BluetoothAdapter* adapter, |
| BluetoothGattService* service) OVERRIDE { |
| ASSERT_EQ(adapter_.get(), adapter); |
| ++gatt_service_changed_count_; |
| |
| QuitMessageLoop(); |
| } |
| |
| virtual void GattCharacteristicAdded( |
| BluetoothAdapter* adapter, |
| BluetoothGattCharacteristic* characteristic) OVERRIDE { |
| ASSERT_EQ(adapter_.get(), adapter); |
| |
| ++gatt_characteristic_added_count_; |
| last_gatt_characteristic_id_ = characteristic->GetIdentifier(); |
| last_gatt_characteristic_uuid_ = characteristic->GetUUID(); |
| |
| ASSERT_TRUE(characteristic->GetService()); |
| EXPECT_EQ(characteristic->GetService()->GetCharacteristic( |
| last_gatt_characteristic_id_), |
| characteristic); |
| |
| QuitMessageLoop(); |
| } |
| |
| virtual void GattCharacteristicRemoved( |
| BluetoothAdapter* adapter, |
| BluetoothGattCharacteristic* characteristic) OVERRIDE { |
| ASSERT_EQ(adapter_.get(), adapter); |
| |
| ++gatt_characteristic_removed_count_; |
| last_gatt_characteristic_id_ = characteristic->GetIdentifier(); |
| last_gatt_characteristic_uuid_ = characteristic->GetUUID(); |
| |
| // The service should return NULL for this characteristic. |
| ASSERT_TRUE(characteristic->GetService()); |
| EXPECT_FALSE(characteristic->GetService()->GetCharacteristic( |
| last_gatt_characteristic_id_)); |
| |
| QuitMessageLoop(); |
| } |
| |
| virtual void GattCharacteristicValueChanged( |
| BluetoothAdapter* adapter, |
| BluetoothGattCharacteristic* characteristic, |
| const std::vector<uint8>& value) OVERRIDE { |
| ASSERT_EQ(adapter_.get(), adapter); |
| |
| ++gatt_characteristic_value_changed_count_; |
| last_gatt_characteristic_id_ = characteristic->GetIdentifier(); |
| last_gatt_characteristic_uuid_ = characteristic->GetUUID(); |
| last_changed_characteristic_value_ = value; |
| |
| ASSERT_TRUE(characteristic->GetService()); |
| EXPECT_EQ(characteristic->GetService()->GetCharacteristic( |
| last_gatt_characteristic_id_), |
| characteristic); |
| |
| QuitMessageLoop(); |
| } |
| |
| virtual void GattDescriptorAdded( |
| BluetoothAdapter* adapter, |
| BluetoothGattDescriptor* descriptor) OVERRIDE { |
| ASSERT_EQ(adapter_.get(), adapter); |
| |
| ++gatt_descriptor_added_count_; |
| last_gatt_descriptor_id_ = descriptor->GetIdentifier(); |
| last_gatt_descriptor_uuid_ = descriptor->GetUUID(); |
| |
| ASSERT_TRUE(descriptor->GetCharacteristic()); |
| EXPECT_EQ(descriptor->GetCharacteristic()->GetDescriptor( |
| last_gatt_descriptor_id_), |
| descriptor); |
| |
| QuitMessageLoop(); |
| } |
| |
| virtual void GattDescriptorRemoved( |
| BluetoothAdapter* adapter, |
| BluetoothGattDescriptor* descriptor) OVERRIDE { |
| ASSERT_EQ(adapter_.get(), adapter); |
| |
| ++gatt_descriptor_removed_count_; |
| last_gatt_descriptor_id_ = descriptor->GetIdentifier(); |
| last_gatt_descriptor_uuid_ = descriptor->GetUUID(); |
| |
| // The characteristic should return NULL for this descriptor.. |
| ASSERT_TRUE(descriptor->GetCharacteristic()); |
| EXPECT_FALSE(descriptor->GetCharacteristic()->GetDescriptor( |
| last_gatt_descriptor_id_)); |
| |
| QuitMessageLoop(); |
| } |
| |
| virtual void GattDescriptorValueChanged( |
| BluetoothAdapter* adapter, |
| BluetoothGattDescriptor* descriptor, |
| const std::vector<uint8>& value) OVERRIDE { |
| ASSERT_EQ(adapter_.get(), adapter); |
| |
| ++gatt_descriptor_value_changed_count_; |
| last_gatt_descriptor_id_ = descriptor->GetIdentifier(); |
| last_gatt_descriptor_uuid_ = descriptor->GetUUID(); |
| last_changed_descriptor_value_ = value; |
| |
| ASSERT_TRUE(descriptor->GetCharacteristic()); |
| EXPECT_EQ(descriptor->GetCharacteristic()->GetDescriptor( |
| last_gatt_descriptor_id_), |
| descriptor); |
| |
| QuitMessageLoop(); |
| } |
| |
| int gatt_service_added_count_; |
| int gatt_service_removed_count_; |
| int gatt_service_changed_count_; |
| int gatt_discovery_complete_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_service_id_; |
| BluetoothUUID last_gatt_service_uuid_; |
| 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(); |
| } |
| |
| 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() { |
| scoped_ptr<DBusThreadManagerSetter> dbus_setter = |
| chromeos::DBusThreadManager::GetSetterForTesting(); |
| 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; |
| dbus_setter->SetBluetoothDeviceClient( |
| scoped_ptr<BluetoothDeviceClient>( |
| fake_bluetooth_device_client_)); |
| dbus_setter->SetBluetoothGattServiceClient( |
| scoped_ptr<BluetoothGattServiceClient>( |
| fake_bluetooth_gatt_service_client_)); |
| dbus_setter->SetBluetoothGattCharacteristicClient( |
| scoped_ptr<BluetoothGattCharacteristicClient>( |
| fake_bluetooth_gatt_characteristic_client_)); |
| dbus_setter->SetBluetoothGattDescriptorClient( |
| scoped_ptr<BluetoothGattDescriptorClient>( |
| fake_bluetooth_gatt_descriptor_client_)); |
| dbus_setter->SetBluetoothAdapterClient( |
| scoped_ptr<BluetoothAdapterClient>(new FakeBluetoothAdapterClient)); |
| dbus_setter->SetBluetoothInputClient( |
| scoped_ptr<BluetoothInputClient>(new FakeBluetoothInputClient)); |
| dbus_setter->SetBluetoothAgentManagerClient( |
| scoped_ptr<BluetoothAgentManagerClient>( |
| new FakeBluetoothAgentManagerClient)); |
| |
| 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); |
| |
| TestObserver 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(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); |
| |
| TestObserver observer(adapter_); |
| |
| // 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_); |
| |
| 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(FakeBluetoothAdapterClient::kAdapterPath), |
| dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath)); |
| BluetoothDevice* device = adapter_->GetDevice( |
| FakeBluetoothDeviceClient::kLowEnergyAddress); |
| ASSERT_TRUE(device); |
| |
| TestObserver observer(adapter_); |
| |
| // 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_); |
| |
| 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()), |
| 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(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); |
| |
| TestObserver observer(adapter_); |
| |
| // 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_); |
| |
| 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-writeable characteristics. |
| observer.last_gatt_characteristic_id_.clear(); |
| 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(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(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::ErrorCallback, |
| 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(0, 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(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 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, 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); |
| |
| TestObserver observer(adapter_); |
| |
| // 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_); |
| |
| 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); |
| |
| TestObserver observer(adapter_); |
| |
| // 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_); |
| |
| 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()); |
| |
| 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(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::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(0, observer.gatt_service_changed_count_); |
| EXPECT_EQ(1, 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(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(FakeBluetoothAdapterClient::kAdapterPath), |
| dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath)); |
| BluetoothDevice* device = |
| adapter_->GetDevice(FakeBluetoothDeviceClient::kLowEnergyAddress); |
| ASSERT_TRUE(device); |
| |
| TestObserver observer(adapter_); |
| |
| // 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_); |
| |
| 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::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, 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, 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.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, 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::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); |
| |
| TestObserver observer(adapter_); |
| |
| // 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_); |
| |
| 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::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, 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.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, 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 |