From d9b0552a7f75737b5c8c60cb87f892b1b9323acf Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Fri, 25 Sep 2015 19:31:24 -0700 Subject: [PATCH] service: Implement IBluetoothGattServer.addDescriptor Declared and implemented the IBluetoothGattServer.addDescriptor API. Bug: 24245347 Change-Id: I7d1e992321c260b9d77521b1c8d7a1e7737b35cc --- service/gatt_server.cpp | 205 ++++++++++++++--- service/gatt_server.h | 24 +- service/hal/bluetooth_gatt_interface.cpp | 31 ++- service/hal/bluetooth_gatt_interface.h | 7 + service/hal/fake_bluetooth_gatt_interface.cpp | 22 +- service/hal/fake_bluetooth_gatt_interface.h | 6 + service/ipc/binder/IBluetoothGattServer.cpp | 39 ++++ service/ipc/binder/IBluetoothGattServer.h | 6 + .../binder/bluetooth_gatt_server_binder_server.cpp | 25 +++ .../binder/bluetooth_gatt_server_binder_server.h | 3 + service/test/gatt_server_unittest.cpp | 244 +++++++++++++++++++++ 11 files changed, 577 insertions(+), 35 deletions(-) diff --git a/service/gatt_server.cpp b/service/gatt_server.cpp index bb92b5095..8689da3ef 100644 --- a/service/gatt_server.cpp +++ b/service/gatt_server.cpp @@ -69,22 +69,8 @@ std::unique_ptr GattServer::BeginServiceDeclaration( CHECK(!pending_decl_); CHECK(!pending_end_decl_cb_); - // Calculate the instance ID for this service by searching through the handle - // map to see how many occurrences of the same service UUID we find. - int inst_id = 0; - for (const auto& iter : handle_map_) { - const GattIdentifier* gatt_id = &iter.first; - - if (!gatt_id->IsService()) - continue; - - if (gatt_id->service_uuid() == uuid) - ++inst_id; - } - - // Pass empty string for the address as this is a local service. - auto service_id = GattIdentifier::CreateServiceId( - "", inst_id, uuid, is_primary); + auto service_id = GetIdForService(uuid, is_primary); + CHECK(service_id); // Pass 0 for permissions and properties as this is a service decl. AttributeEntry entry( @@ -111,24 +97,13 @@ std::unique_ptr GattServer::AddCharacteristic( return nullptr; } - // Calculate the instance ID for this characteristic by searching through the - // pending entries. - int inst_id = 0; - for (const auto& entry : pending_decl_->attributes) { - const GattIdentifier* gatt_id = &entry.id; - - if (!gatt_id->IsCharacteristic()) - continue; - - if (gatt_id->characteristic_uuid() == uuid) - ++inst_id; + if (pending_end_decl_cb_) { + LOG(ERROR) << "EndServiceDeclaration in progress, cannot modify service"; + return nullptr; } - CHECK(pending_decl_->service_id.IsService()); - - auto char_id = GattIdentifier::CreateCharacteristicId( - inst_id, uuid, pending_decl_->service_id); - + auto char_id = GetIdForCharacteristic(uuid); + CHECK(char_id); AttributeEntry entry(*char_id, properties, permissions); // 2 handles for the characteristic declaration and the value attributes. @@ -138,6 +113,36 @@ std::unique_ptr GattServer::AddCharacteristic( return char_id; } +std::unique_ptr GattServer::AddDescriptor( + const UUID& uuid, int permissions) { + VLOG(1) << __func__ << " server_if: " << server_if_ + << " - UUID: " << uuid.ToString() + << ", permissions: " << permissions; + lock_guard lock(mutex_); + + if (!pending_decl_) { + LOG(ERROR) << "Service declaration not begun"; + return nullptr; + } + + if (pending_end_decl_cb_) { + LOG(ERROR) << "EndServiceDeclaration in progress, cannot modify service"; + return nullptr; + } + + auto desc_id = GetIdForDescriptor(uuid); + if (!desc_id) + return nullptr; + + AttributeEntry entry(*desc_id, kCharacteristicPropertyNone, permissions); + + // 1 handle for the descriptor attribute. + pending_decl_->num_handles += 1; + pending_decl_->attributes.push_back(entry); + + return desc_id; +} + bool GattServer::EndServiceDeclaration(const ResultCallback& callback) { VLOG(1) << __func__ << " server_if: " << server_if_; lock_guard lock(mutex_); @@ -186,6 +191,90 @@ bool GattServer::EndServiceDeclaration(const ResultCallback& callback) { return true; } +std::unique_ptr GattServer::GetIdForService( + const UUID& uuid, bool is_primary) { + // Calculate the instance ID for this service by searching through the handle + // map to see how many occurrences of the same service UUID we find. + int inst_id = 0; + for (const auto& iter : handle_map_) { + const GattIdentifier* gatt_id = &iter.first; + + if (!gatt_id->IsService()) + continue; + + if (gatt_id->service_uuid() == uuid) + ++inst_id; + } + + // Pass empty string for the address as this is a local service. + return GattIdentifier::CreateServiceId("", inst_id, uuid, is_primary); +} + +std::unique_ptr GattServer::GetIdForCharacteristic( + const UUID& uuid) { + CHECK(pending_decl_); + + // Calculate the instance ID for this characteristic by searching through the + // pending entries. + int inst_id = 0; + for (const auto& entry : pending_decl_->attributes) { + const GattIdentifier& gatt_id = entry.id; + + if (!gatt_id.IsCharacteristic()) + continue; + + if (gatt_id.characteristic_uuid() == uuid) + ++inst_id; + } + + CHECK(pending_decl_->service_id.IsService()); + + return GattIdentifier::CreateCharacteristicId( + inst_id, uuid, pending_decl_->service_id); +} + +std::unique_ptr GattServer::GetIdForDescriptor( + const UUID& uuid) { + CHECK(pending_decl_); + + // Calculate the instance ID for this descriptor by searching through the + // pending entries. We iterate in reverse until we find a characteristic + // entry. + CHECK(!pending_decl_->attributes.empty()); + int inst_id = 0; + bool char_found = false; + GattIdentifier char_id; + for (auto iter = pending_decl_->attributes.end() - 1; + iter != pending_decl_->attributes.begin(); // Begin is always a service + --iter) { + const GattIdentifier& gatt_id = iter->id; + + if (gatt_id.IsCharacteristic()) { + // Found the owning characteristic. + char_found = true; + char_id = gatt_id; + break; + } + + if (!gatt_id.IsDescriptor()) { + // A descriptor must be preceded by a descriptor or a characteristic. + LOG(ERROR) << "Descriptors must come directly after a characteristic or " + << "another descriptor."; + return nullptr; + } + + if (gatt_id.descriptor_uuid() == uuid) + ++inst_id; + } + + if (!char_found) { + LOG(ERROR) << "No characteristic found to add the descriptor to."; + return nullptr; + } + + return GattIdentifier::CreateDescriptorId(inst_id, uuid, char_id); +} + void GattServer::ServiceAddedCallback( hal::BluetoothGattInterface* gatt_iface, int status, int server_if, @@ -254,6 +343,39 @@ void GattServer::CharacteristicAddedCallback( HandleNextEntry(gatt_iface); } +void GattServer::DescriptorAddedCallback( + hal::BluetoothGattInterface* gatt_iface, + int status, int server_if, + const bt_uuid_t& uuid, + int service_handle, + int desc_handle) { + lock_guard lock(mutex_); + + if (server_if != server_if_) + return; + + CHECK(pending_decl_); + CHECK(pending_decl_->service_handle == service_handle); + CHECK(pending_id_); + CHECK(pending_id_->IsDescriptor()); + CHECK(pending_id_->descriptor_uuid() == UUID(uuid)); + + VLOG(1) << __func__ << " - status: " << status + << " server_if: " << server_if + << " service_handle: " << service_handle + << " desc_handle: " << desc_handle; + + if (status != BT_STATUS_SUCCESS) { + NotifyEndCallbackAndClearData(static_cast(status), + pending_decl_->service_id); + return; + } + + // Add this to the handle map and contiue. + pending_handle_map_[*pending_id_] = desc_handle; + HandleNextEntry(gatt_iface); +} + void GattServer::ServiceStartedCallback( hal::BluetoothGattInterface* gatt_iface, int status, int server_if, @@ -349,7 +471,26 @@ void GattServer::HandleNextEntry(hal::BluetoothGattInterface* gatt_iface) { } pending_id_.reset(new GattIdentifier(next_entry->id)); + return; + } + + if (next_entry->id.IsDescriptor()) { + bt_uuid_t desc_uuid = next_entry->id.descriptor_uuid().GetBlueDroid(); + bt_status_t status = gatt_iface->GetServerHALInterface()-> + add_descriptor( + server_if_, + pending_decl_->service_handle, + &desc_uuid, + next_entry->permissions); + // Terminate the procedure in the case of an error. + if (status != BT_STATUS_SUCCESS) { + NotifyEndCallbackAndClearData(static_cast(status), + pending_decl_->service_id); + return; + } + + pending_id_.reset(new GattIdentifier(next_entry->id)); return; } diff --git a/service/gatt_server.h b/service/gatt_server.h index afdf0ca5e..fc6faf4fa 100644 --- a/service/gatt_server.h +++ b/service/gatt_server.h @@ -62,10 +62,19 @@ class GattServer : public BluetoothClientInstance, // Inserts a new characteristic definition into a previously begun service // declaration. Returns the assigned identifier for the characteristic, or - // nullptr in the case of an error. + // nullptr if a service declaration wasn't begun or a call to + // EndServiceDeclaration is still in progress. std::unique_ptr AddCharacteristic( const UUID& uuid, int properties, int permissions); + // Inserts a new descriptor definition into a previous begun service + // declaration. Returns the assigned identifier for the descriptor, or + // nullptr if a service declaration wasn't begun, a call to + // EndServiceDeclaration is still in progress, or a characteristic definition + // doesn't properly precede this definition. + std::unique_ptr AddDescriptor( + const UUID& uuid, int permissions); + // Ends a previously started service declaration. This method immediately // returns false if a service declaration hasn't been started. Otherwise, // |callback| will be called asynchronously with the result of the operation. @@ -108,6 +117,13 @@ class GattServer : public BluetoothClientInstance, // obtained from the factory. GattServer(const UUID& uuid, int server_if); + // Returns a GattIdentifier for the attribute with the given UUID within the + // current pending service declaration. + std::unique_ptr GetIdForService(const UUID& uuid, + bool is_primary); + std::unique_ptr GetIdForCharacteristic(const UUID& uuid); + std::unique_ptr GetIdForDescriptor(const UUID& uuid); + // hal::BluetoothGattInterface::ServerObserver overrides: void ServiceAddedCallback( hal::BluetoothGattInterface* gatt_iface, @@ -120,6 +136,12 @@ class GattServer : public BluetoothClientInstance, const bt_uuid_t& uuid, int service_handle, int char_handle) override; + void DescriptorAddedCallback( + hal::BluetoothGattInterface* gatt_iface, + int status, int server_if, + const bt_uuid_t& uuid, + int service_handle, + int desc_handle) override; void ServiceStartedCallback( hal::BluetoothGattInterface* gatt_iface, int status, int server_if, diff --git a/service/hal/bluetooth_gatt_interface.cpp b/service/hal/bluetooth_gatt_interface.cpp index 6b6121d95..fdb942b14 100644 --- a/service/hal/bluetooth_gatt_interface.cpp +++ b/service/hal/bluetooth_gatt_interface.cpp @@ -160,6 +160,25 @@ void CharacteristicAddedCallback( g_interface, status, server_if, *uuid, srvc_handle, char_handle)); } +void DescriptorAddedCallback( + int status, int server_if, + bt_uuid_t* uuid, + int srvc_handle, + int desc_handle) { + lock_guard lock(g_instance_lock); + VLOG(2) << __func__ << " - status: " << status << " server_if: " << server_if + << " srvc_handle: " << srvc_handle << " desc_handle: " << desc_handle; + VERIFY_INTERFACE_OR_RETURN(); + + if (!uuid) { + LOG(WARNING) << "|uuid| is NULL; ignoring DescriptorAddedCallback"; + return; + } + + FOR_EACH_SERVER_OBSERVER(DescriptorAddedCallback( + g_interface, status, server_if, *uuid, srvc_handle, desc_handle)); +} + void ServiceStartedCallback(int status, int server_if, int srvc_handle) { lock_guard lock(g_instance_lock); VLOG(2) << __func__ << " - status: " << status << " server_if: " << server_if @@ -224,7 +243,7 @@ const btgatt_server_callbacks_t gatt_server_callbacks = { ServiceAddedCallback, nullptr, // included_service_added_cb, CharacteristicAddedCallback, - nullptr, // descriptor_added_cb, + DescriptorAddedCallback, ServiceStartedCallback, ServiceStoppedCallback, nullptr, // service_deleted_cb, @@ -428,6 +447,16 @@ void BluetoothGattInterface::ServerObserver::CharacteristicAddedCallback( // Do nothing. } +void BluetoothGattInterface::ServerObserver::DescriptorAddedCallback( + BluetoothGattInterface* /* gatt_iface */, + int /* status */, + int /* server_if */, + const bt_uuid_t& /* uuid */, + int /* srvc_handle */, + int /* desc_handle */) { + // Do nothing. +} + void BluetoothGattInterface::ServerObserver::ServiceStartedCallback( BluetoothGattInterface* /* gatt_iface */, int /* status */, diff --git a/service/hal/bluetooth_gatt_interface.h b/service/hal/bluetooth_gatt_interface.h index 590e52cae..2d95dc733 100644 --- a/service/hal/bluetooth_gatt_interface.h +++ b/service/hal/bluetooth_gatt_interface.h @@ -94,6 +94,13 @@ class BluetoothGattInterface { int srvc_handle, int char_handle); + virtual void DescriptorAddedCallback( + BluetoothGattInterface* gatt_iface, + int status, int server_if, + const bt_uuid_t& uuid, + int srvc_handle, + int desc_handle); + virtual void ServiceStartedCallback( BluetoothGattInterface* gatt_iface, int status, int server_if, diff --git a/service/hal/fake_bluetooth_gatt_interface.cpp b/service/hal/fake_bluetooth_gatt_interface.cpp index fd5e6fd34..677d66fde 100644 --- a/service/hal/fake_bluetooth_gatt_interface.cpp +++ b/service/hal/fake_bluetooth_gatt_interface.cpp @@ -106,6 +106,16 @@ bt_status_t FakeAddCharacteristic(int server_if, int srvc_handle, return BT_STATUS_FAIL; } +bt_status_t FakeAddDescriptor(int server_if, int srvc_handle, + bt_uuid_t* uuid, + int permissions) { + if (g_server_handler) + return g_server_handler->AddDescriptor( + server_if, srvc_handle, uuid, permissions); + + return BT_STATUS_FAIL; +} + bt_status_t FakeStartService( int server_if, int srvc_handle, int transport) { if (g_server_handler) @@ -169,7 +179,7 @@ btgatt_server_interface_t fake_btgatts_iface = { FakeAddService, nullptr, // add_included_service FakeAddCharacteristic, - nullptr, // add_descriptor + FakeAddDescriptor, FakeStartService, nullptr, // stop_service FakeDeleteService, @@ -255,6 +265,16 @@ void FakeBluetoothGattInterface::NotifyCharacteristicAddedCallback( this, status, server_if, uuid, srvc_handle, char_handle)); } +void FakeBluetoothGattInterface::NotifyDescriptorAddedCallback( + int status, int server_if, + const bt_uuid_t& uuid, + int srvc_handle, int desc_handle) { + FOR_EACH_OBSERVER( + ServerObserver, server_observers_, + DescriptorAddedCallback( + this, status, server_if, uuid, srvc_handle, desc_handle)); +} + void FakeBluetoothGattInterface::NotifyServiceStartedCallback( int status, int server_if, int srvc_handle) { FOR_EACH_OBSERVER( diff --git a/service/hal/fake_bluetooth_gatt_interface.h b/service/hal/fake_bluetooth_gatt_interface.h index 9dc9cdb8b..fd61390cf 100644 --- a/service/hal/fake_bluetooth_gatt_interface.h +++ b/service/hal/fake_bluetooth_gatt_interface.h @@ -61,6 +61,9 @@ class FakeBluetoothGattInterface : public BluetoothGattInterface { virtual bt_status_t AddCharacteristic(int server_if, int srvc_handle, bt_uuid_t *uuid, int properties, int permissions) = 0; + virtual bt_status_t AddDescriptor(int server_if, int srvc_handle, + bt_uuid_t* uuid, + int permissions) = 0; virtual bt_status_t StartService( int server_if, int srvc_handle, int transport) = 0; virtual bt_status_t DeleteService(int server_if, int srvc_handle) = 0; @@ -92,6 +95,9 @@ class FakeBluetoothGattInterface : public BluetoothGattInterface { void NotifyCharacteristicAddedCallback(int status, int server_if, const bt_uuid_t& uuid, int srvc_handle, int char_handle); + void NotifyDescriptorAddedCallback(int status, int server_if, + const bt_uuid_t& uuid, + int srvc_handle, int desc_handle); void NotifyServiceStartedCallback(int status, int server_if, int srvc_handle); // BluetoothGattInterface overrides: diff --git a/service/ipc/binder/IBluetoothGattServer.cpp b/service/ipc/binder/IBluetoothGattServer.cpp index 3c04e6cd8..2cfad0ae3 100644 --- a/service/ipc/binder/IBluetoothGattServer.cpp +++ b/service/ipc/binder/IBluetoothGattServer.cpp @@ -99,6 +99,24 @@ status_t BnBluetoothGattServer::onTransact( return android::NO_ERROR; } + case ADD_DESCRIPTOR_TRANSACTION: { + int server_if = data.readInt32(); + auto uuid = CreateUUIDFromParcel(data); + CHECK(uuid); + int permissions = data.readInt32(); + + std::unique_ptr out_id; + bool result = AddDescriptor(server_if, *uuid, permissions, &out_id); + + reply->writeInt32(result); + + if (result) { + CHECK(out_id); + WriteGattIdentifierToParcel(*out_id, reply); + } + + return android::NO_ERROR; + } case END_SERVICE_DECLARATION_TRANSACTION: { int server_if = data.readInt32(); bool result = EndServiceDeclaration(server_if); @@ -194,6 +212,27 @@ bool BpBluetoothGattServer::AddCharacteristic( return result; } +bool BpBluetoothGattServer::AddDescriptor( + int server_if, const bluetooth::UUID& uuid, int permissions, + std::unique_ptr* out_id) { + CHECK(out_id); + Parcel data, reply; + + data.writeInterfaceToken(IBluetoothGattServer::getInterfaceDescriptor()); + data.writeInt32(server_if); + WriteUUIDToParcel(uuid, &data); + data.writeInt32(permissions); + + remote()->transact(IBluetoothGattServer::ADD_DESCRIPTOR_TRANSACTION, + data, &reply); + + bool result = reply.readInt32(); + if (result) + *out_id = CreateGattIdentifierFromParcel(reply); + + return result; +} + bool BpBluetoothGattServer::EndServiceDeclaration(int server_if) { Parcel data, reply; diff --git a/service/ipc/binder/IBluetoothGattServer.h b/service/ipc/binder/IBluetoothGattServer.h index ff8a956e5..182ac1892 100644 --- a/service/ipc/binder/IBluetoothGattServer.h +++ b/service/ipc/binder/IBluetoothGattServer.h @@ -69,6 +69,9 @@ class IBluetoothGattServer : public android::IInterface { int server_if, const bluetooth::UUID& uuid, int properties, int permissions, std::unique_ptr* out_id) = 0; + virtual bool AddDescriptor( + int server_if, const bluetooth::UUID& uuid, int permissions, + std::unique_ptr* out_id) = 0; virtual bool EndServiceDeclaration(int server_if) = 0; // TODO(armansito): Complete the API definition. @@ -114,6 +117,9 @@ class BpBluetoothGattServer int server_if, const bluetooth::UUID& uuid, int properties, int permissions, std::unique_ptr* out_id) override; + bool AddDescriptor( + int server_if, const bluetooth::UUID& uuid, int permissions, + std::unique_ptr* out_id) override; bool EndServiceDeclaration(int server_if) override; private: diff --git a/service/ipc/binder/bluetooth_gatt_server_binder_server.cpp b/service/ipc/binder/bluetooth_gatt_server_binder_server.cpp index 6c133452a..724753ffb 100644 --- a/service/ipc/binder/bluetooth_gatt_server_binder_server.cpp +++ b/service/ipc/binder/bluetooth_gatt_server_binder_server.cpp @@ -104,6 +104,31 @@ bool BluetoothGattServerBinderServer::AddCharacteristic( return true; } +bool BluetoothGattServerBinderServer::AddDescriptor( + int server_if, const bluetooth::UUID& uuid, int permissions, + std::unique_ptr* out_id) { + VLOG(2) << __func__; + CHECK(out_id); + std::lock_guard lock(*maps_lock()); + + auto gatt_server = GetGattServer(server_if); + if (!gatt_server) { + LOG(ERROR) << "Unknown server_if: " << server_if; + return false; + } + + auto desc_id = gatt_server->AddDescriptor(uuid, permissions); + if (!desc_id) { + LOG(ERROR) << "Failed to add descriptor - server_if: " + << server_if << " UUID: " << uuid.ToString(); + return false; + } + + out_id->swap(desc_id); + + return true; +} + bool BluetoothGattServerBinderServer::EndServiceDeclaration(int server_if) { VLOG(2) << __func__; std::lock_guard lock(*maps_lock()); diff --git a/service/ipc/binder/bluetooth_gatt_server_binder_server.h b/service/ipc/binder/bluetooth_gatt_server_binder_server.h index ddc47e152..1d98d0790 100644 --- a/service/ipc/binder/bluetooth_gatt_server_binder_server.h +++ b/service/ipc/binder/bluetooth_gatt_server_binder_server.h @@ -49,6 +49,9 @@ class BluetoothGattServerBinderServer : public BnBluetoothGattServer, int server_if, const bluetooth::UUID& uuid, int properties, int permissions, std::unique_ptr* out_id) override; + bool AddDescriptor( + int server_if, const bluetooth::UUID& uuid, int permissions, + std::unique_ptr* out_id) override; bool EndServiceDeclaration(int server_if) override; private: diff --git a/service/test/gatt_server_unittest.cpp b/service/test/gatt_server_unittest.cpp index 00ff56cea..a06e18953 100644 --- a/service/test/gatt_server_unittest.cpp +++ b/service/test/gatt_server_unittest.cpp @@ -37,6 +37,7 @@ class MockGattHandler MOCK_METHOD1(UnregisterServer, bt_status_t(int)); MOCK_METHOD3(AddService, bt_status_t(int, btgatt_srvc_id_t*, int)); MOCK_METHOD5(AddCharacteristic, bt_status_t(int, int, bt_uuid_t*, int, int)); + MOCK_METHOD4(AddDescriptor, bt_status_t(int, int, bt_uuid_t*, int)); MOCK_METHOD3(StartService, bt_status_t(int, int, int)); MOCK_METHOD2(DeleteService, bt_status_t(int, int)); @@ -406,6 +407,10 @@ TEST_F(GattServerPostRegisterTest, AddCharacteristic) { int char_handle1 = 0x0004; EXPECT_TRUE(gatt_server_->EndServiceDeclaration(callback)); + // Cannot add any more characteristics while EndServiceDeclaration is in + // progress. + EXPECT_EQ(nullptr, gatt_server_->AddCharacteristic(char_uuid, props, perms)); + EXPECT_CALL(*mock_handler_, AddCharacteristic(_, _, _, _, _)) .Times(8) .WillOnce(Return(BT_STATUS_FAIL)) // char_id0 - try 1 @@ -520,5 +525,244 @@ TEST_F(GattServerPostRegisterTest, AddCharacteristic) { EXPECT_EQ(4, cb_count); } +TEST_F(GattServerPostRegisterTest, AddDescriptor) { + // Set up some values for UUIDs, permissions, and properties. + const UUID service_uuid = UUID::GetRandom(); + const UUID char_uuid0 = UUID::GetRandom(); + const UUID char_uuid1 = UUID::GetRandom(); + const UUID desc_uuid = UUID::GetRandom(); + bt_uuid_t hal_char_uuid0 = char_uuid0.GetBlueDroid(); + bt_uuid_t hal_char_uuid1 = char_uuid1.GetBlueDroid(); + bt_uuid_t hal_desc_uuid = desc_uuid.GetBlueDroid(); + const int props = bluetooth::kCharacteristicPropertyRead | + bluetooth::kCharacteristicPropertyNotify; + const int perms = kAttributePermissionReadEncrypted; + + // Service declaration not started. + EXPECT_EQ(nullptr, gatt_server_->AddDescriptor(desc_uuid, perms)); + + // Start a service declaration. + auto service_id = gatt_server_->BeginServiceDeclaration(service_uuid, true); + btgatt_srvc_id_t hal_id; + hal::GetHALServiceId(*service_id, &hal_id); + + // No characteristic was inserted. + EXPECT_EQ(nullptr, gatt_server_->AddDescriptor(desc_uuid, perms)); + + // Add two characeristics. + auto char_id0 = gatt_server_->AddCharacteristic(char_uuid0, props, perms); + auto char_id1 = gatt_server_->AddCharacteristic(char_uuid1, props, perms); + + // Add a descriptor. + auto desc_id = gatt_server_->AddDescriptor(desc_uuid, perms); + EXPECT_NE(nullptr, desc_id); + EXPECT_TRUE(desc_id->IsDescriptor()); + EXPECT_TRUE(*desc_id->GetOwningCharacteristicId() == *char_id1); + EXPECT_TRUE(*desc_id->GetOwningServiceId() == *service_id); + + // Add a second descriptor with the same UUID. + auto desc_id1 = gatt_server_->AddDescriptor(desc_uuid, perms); + EXPECT_NE(nullptr, desc_id1); + EXPECT_TRUE(*desc_id1 != *desc_id); + EXPECT_TRUE(desc_id1->IsDescriptor()); + EXPECT_TRUE(*desc_id1->GetOwningCharacteristicId() == *char_id1); + EXPECT_TRUE(*desc_id1->GetOwningServiceId() == *service_id); + + // Expect calls for 7 handles. + EXPECT_CALL(*mock_handler_, AddService(kDefaultServerId, _, 7)) + .WillRepeatedly(Return(BT_STATUS_SUCCESS)); + EXPECT_CALL(*mock_handler_, AddCharacteristic(_, _, _, _, _)) + .WillRepeatedly(Return(BT_STATUS_SUCCESS)); + + GattIdentifier cb_id; + BLEStatus cb_status; + int cb_count = 0; + auto callback = [&](BLEStatus in_status, const GattIdentifier& in_id) { + cb_id = in_id; + cb_status = in_status; + cb_count++; + }; + + int srvc_handle = 0x0001; + int char_handle0 = 0x0002; + int char_handle1 = 0x0004; + int desc_handle0 = 0x0005; + int desc_handle1 = 0x0006; + + EXPECT_TRUE(gatt_server_->EndServiceDeclaration(callback)); + + // Cannot add any more descriptors while EndServiceDeclaration is in progress. + EXPECT_EQ(nullptr, gatt_server_->AddDescriptor(desc_uuid, perms)); + + fake_hal_gatt_iface_->NotifyServiceAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_id, srvc_handle); + EXPECT_EQ(0, cb_count); + + EXPECT_CALL(*mock_handler_, AddDescriptor(_, _, _, _)) + .Times(8) + .WillOnce(Return(BT_STATUS_FAIL)) // desc_id0 - try 1 + .WillOnce(Return(BT_STATUS_SUCCESS)) // desc_id0 - try 2 + .WillOnce(Return(BT_STATUS_SUCCESS)) // desc_id0 - try 3 + .WillOnce(Return(BT_STATUS_FAIL)) // desc_id1 - try 3 + .WillOnce(Return(BT_STATUS_SUCCESS)) // desc_id0 - try 4 + .WillOnce(Return(BT_STATUS_SUCCESS)) // desc_id1 - try 4 + .WillOnce(Return(BT_STATUS_SUCCESS)) // desc_id0 - try 5 + .WillOnce(Return(BT_STATUS_SUCCESS)); // desc_id1 - try 5 + + // Notify success for both characteristics. First descriptor call will fail. + fake_hal_gatt_iface_->NotifyCharacteristicAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_char_uuid0, + srvc_handle, char_handle0); + EXPECT_EQ(0, cb_count); + fake_hal_gatt_iface_->NotifyCharacteristicAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_char_uuid1, + srvc_handle, char_handle1); + EXPECT_EQ(1, cb_count); + EXPECT_NE(BLE_STATUS_SUCCESS, cb_status); + EXPECT_TRUE(cb_id == *service_id); + + // Restart (try 2) + cb_count = 0; + service_id = gatt_server_->BeginServiceDeclaration(service_uuid, true); + hal::GetHALServiceId(*service_id, &hal_id); + char_id0 = gatt_server_->AddCharacteristic(char_uuid0, props, perms); + char_id1 = gatt_server_->AddCharacteristic(char_uuid1, props, perms); + desc_id = gatt_server_->AddDescriptor(desc_uuid, perms); + ASSERT_NE(nullptr, desc_id); + desc_id1 = gatt_server_->AddDescriptor(desc_uuid, perms); + ASSERT_NE(nullptr, desc_id1); + EXPECT_TRUE(gatt_server_->EndServiceDeclaration(callback)); + + fake_hal_gatt_iface_->NotifyServiceAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_id, srvc_handle); + EXPECT_EQ(0, cb_count); + fake_hal_gatt_iface_->NotifyCharacteristicAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_char_uuid0, + srvc_handle, char_handle0); + EXPECT_EQ(0, cb_count); + fake_hal_gatt_iface_->NotifyCharacteristicAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_char_uuid1, + srvc_handle, char_handle1); + EXPECT_EQ(0, cb_count); + + // Notify failure for first descriptor. + fake_hal_gatt_iface_->NotifyDescriptorAddedCallback( + BT_STATUS_FAIL, kDefaultServerId, hal_desc_uuid, + srvc_handle, desc_handle0); + EXPECT_EQ(1, cb_count); + EXPECT_NE(BLE_STATUS_SUCCESS, cb_status); + EXPECT_TRUE(cb_id == *service_id); + + // Restart (try 3) + cb_count = 0; + service_id = gatt_server_->BeginServiceDeclaration(service_uuid, true); + hal::GetHALServiceId(*service_id, &hal_id); + char_id0 = gatt_server_->AddCharacteristic(char_uuid0, props, perms); + char_id1 = gatt_server_->AddCharacteristic(char_uuid1, props, perms); + desc_id = gatt_server_->AddDescriptor(desc_uuid, perms); + ASSERT_NE(nullptr, desc_id); + desc_id1 = gatt_server_->AddDescriptor(desc_uuid, perms); + ASSERT_NE(nullptr, desc_id1); + EXPECT_TRUE(gatt_server_->EndServiceDeclaration(callback)); + + fake_hal_gatt_iface_->NotifyServiceAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_id, srvc_handle); + EXPECT_EQ(0, cb_count); + fake_hal_gatt_iface_->NotifyCharacteristicAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_char_uuid0, + srvc_handle, char_handle0); + EXPECT_EQ(0, cb_count); + fake_hal_gatt_iface_->NotifyCharacteristicAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_char_uuid1, + srvc_handle, char_handle1); + EXPECT_EQ(0, cb_count); + + // Notify success for first descriptor; the second descriptor will fail + // immediately. + fake_hal_gatt_iface_->NotifyDescriptorAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_desc_uuid, + srvc_handle, desc_handle0); + EXPECT_EQ(1, cb_count); + EXPECT_NE(BLE_STATUS_SUCCESS, cb_status); + EXPECT_TRUE(cb_id == *service_id); + + // Restart (try 4) + cb_count = 0; + service_id = gatt_server_->BeginServiceDeclaration(service_uuid, true); + hal::GetHALServiceId(*service_id, &hal_id); + char_id0 = gatt_server_->AddCharacteristic(char_uuid0, props, perms); + char_id1 = gatt_server_->AddCharacteristic(char_uuid1, props, perms); + desc_id = gatt_server_->AddDescriptor(desc_uuid, perms); + ASSERT_NE(nullptr, desc_id); + desc_id1 = gatt_server_->AddDescriptor(desc_uuid, perms); + ASSERT_NE(nullptr, desc_id1); + EXPECT_TRUE(gatt_server_->EndServiceDeclaration(callback)); + + fake_hal_gatt_iface_->NotifyServiceAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_id, srvc_handle); + EXPECT_EQ(0, cb_count); + fake_hal_gatt_iface_->NotifyCharacteristicAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_char_uuid0, + srvc_handle, char_handle0); + EXPECT_EQ(0, cb_count); + fake_hal_gatt_iface_->NotifyCharacteristicAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_char_uuid1, + srvc_handle, char_handle1); + EXPECT_EQ(0, cb_count); + + // Notify success for first first descriptor and failure for second + // descriptor. + fake_hal_gatt_iface_->NotifyDescriptorAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_desc_uuid, + srvc_handle, desc_handle0); + EXPECT_EQ(0, cb_count); + + fake_hal_gatt_iface_->NotifyDescriptorAddedCallback( + BT_STATUS_FAIL, kDefaultServerId, hal_desc_uuid, + srvc_handle, desc_handle1); + EXPECT_EQ(1, cb_count); + EXPECT_NE(BLE_STATUS_SUCCESS, cb_status); + EXPECT_TRUE(cb_id == *service_id); + + // Restart (try 5) + cb_count = 0; + service_id = gatt_server_->BeginServiceDeclaration(service_uuid, true); + hal::GetHALServiceId(*service_id, &hal_id); + char_id0 = gatt_server_->AddCharacteristic(char_uuid0, props, perms); + char_id1 = gatt_server_->AddCharacteristic(char_uuid1, props, perms); + desc_id = gatt_server_->AddDescriptor(desc_uuid, perms); + ASSERT_NE(nullptr, desc_id); + desc_id1 = gatt_server_->AddDescriptor(desc_uuid, perms); + ASSERT_NE(nullptr, desc_id1); + EXPECT_TRUE(gatt_server_->EndServiceDeclaration(callback)); + + fake_hal_gatt_iface_->NotifyServiceAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_id, srvc_handle); + EXPECT_EQ(0, cb_count); + fake_hal_gatt_iface_->NotifyCharacteristicAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_char_uuid0, + srvc_handle, char_handle0); + EXPECT_EQ(0, cb_count); + fake_hal_gatt_iface_->NotifyCharacteristicAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_char_uuid1, + srvc_handle, char_handle1); + EXPECT_EQ(0, cb_count); + + // Notify success for both descriptors. + fake_hal_gatt_iface_->NotifyDescriptorAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_desc_uuid, + srvc_handle, desc_handle0); + EXPECT_EQ(0, cb_count); + + // The second descriptor callback should trigger the end routine. + EXPECT_CALL(*mock_handler_, StartService(kDefaultServerId, srvc_handle, _)) + .Times(1) + .WillOnce(Return(BT_STATUS_SUCCESS)); + fake_hal_gatt_iface_->NotifyDescriptorAddedCallback( + BT_STATUS_SUCCESS, kDefaultServerId, hal_desc_uuid, + srvc_handle, desc_handle1); + EXPECT_EQ(0, cb_count); +} + } // namespace } // namespace bluetooth -- 2.11.0