OSDN Git Service

service: Implement IBluetoothGattServer.addDescriptor
authorArman Uguray <armansito@google.com>
Sat, 26 Sep 2015 02:31:24 +0000 (19:31 -0700)
committerArman Uguray <armansito@google.com>
Sat, 3 Oct 2015 00:13:09 +0000 (17:13 -0700)
Declared and implemented the IBluetoothGattServer.addDescriptor
API.

Bug: 24245347
Change-Id: I7d1e992321c260b9d77521b1c8d7a1e7737b35cc

service/gatt_server.cpp
service/gatt_server.h
service/hal/bluetooth_gatt_interface.cpp
service/hal/bluetooth_gatt_interface.h
service/hal/fake_bluetooth_gatt_interface.cpp
service/hal/fake_bluetooth_gatt_interface.h
service/ipc/binder/IBluetoothGattServer.cpp
service/ipc/binder/IBluetoothGattServer.h
service/ipc/binder/bluetooth_gatt_server_binder_server.cpp
service/ipc/binder/bluetooth_gatt_server_binder_server.h
service/test/gatt_server_unittest.cpp

index bb92b50..8689da3 100644 (file)
@@ -69,22 +69,8 @@ std::unique_ptr<GattIdentifier> 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<GattIdentifier> 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<GattIdentifier> GattServer::AddCharacteristic(
   return char_id;
 }
 
+std::unique_ptr<GattIdentifier> GattServer::AddDescriptor(
+    const UUID& uuid, int permissions) {
+  VLOG(1) << __func__ << " server_if: " << server_if_
+          << " - UUID: " << uuid.ToString()
+          << ", permissions: " << permissions;
+  lock_guard<mutex> 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<mutex> lock(mutex_);
@@ -186,6 +191,90 @@ bool GattServer::EndServiceDeclaration(const ResultCallback& callback) {
   return true;
 }
 
+std::unique_ptr<GattIdentifier> 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<GattIdentifier> 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<GattIdentifier> 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<mutex> 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<BLEStatus>(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<BLEStatus>(status),
+                                    pending_decl_->service_id);
+      return;
+    }
+
+    pending_id_.reset(new GattIdentifier(next_entry->id));
     return;
   }
 
index afdf0ca..fc6faf4 100644 (file)
@@ -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<GattIdentifier> 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<GattIdentifier> 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<GattIdentifier> GetIdForService(const UUID& uuid,
+                                                  bool is_primary);
+  std::unique_ptr<GattIdentifier> GetIdForCharacteristic(const UUID& uuid);
+  std::unique_ptr<GattIdentifier> 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,
index 6b6121d..fdb942b 100644 (file)
@@ -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<mutex> 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<mutex> 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 */,
index 590e52c..2d95dc7 100644 (file)
@@ -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,
index fd5e6fd..677d66f 100644 (file)
@@ -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(
index 9dc9cdb..fd61390 100644 (file)
@@ -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:
index 3c04e6c..2cfad0a 100644 (file)
@@ -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<bluetooth::GattIdentifier> 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<bluetooth::GattIdentifier>* 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;
 
index ff8a956..182ac18 100644 (file)
@@ -69,6 +69,9 @@ class IBluetoothGattServer : public android::IInterface {
       int server_if, const bluetooth::UUID& uuid,
       int properties, int permissions,
       std::unique_ptr<bluetooth::GattIdentifier>* out_id) = 0;
+  virtual bool AddDescriptor(
+      int server_if, const bluetooth::UUID& uuid, int permissions,
+      std::unique_ptr<bluetooth::GattIdentifier>* 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<bluetooth::GattIdentifier>* out_id) override;
+  bool AddDescriptor(
+      int server_if, const bluetooth::UUID& uuid, int permissions,
+      std::unique_ptr<bluetooth::GattIdentifier>* out_id) override;
   bool EndServiceDeclaration(int server_if) override;
 
  private:
index 6c13345..724753f 100644 (file)
@@ -104,6 +104,31 @@ bool BluetoothGattServerBinderServer::AddCharacteristic(
   return true;
 }
 
+bool BluetoothGattServerBinderServer::AddDescriptor(
+    int server_if, const bluetooth::UUID& uuid, int permissions,
+    std::unique_ptr<bluetooth::GattIdentifier>* out_id) {
+  VLOG(2) << __func__;
+  CHECK(out_id);
+  std::lock_guard<std::mutex> 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<std::mutex> lock(*maps_lock());
index ddc47e1..1d98d07 100644 (file)
@@ -49,6 +49,9 @@ class BluetoothGattServerBinderServer : public BnBluetoothGattServer,
       int server_if, const bluetooth::UUID& uuid,
       int properties, int permissions,
       std::unique_ptr<bluetooth::GattIdentifier>* out_id) override;
+  bool AddDescriptor(
+      int server_if, const bluetooth::UUID& uuid, int permissions,
+      std::unique_ptr<bluetooth::GattIdentifier>* out_id) override;
   bool EndServiceDeclaration(int server_if) override;
 
  private:
index 00ff56c..a06e189 100644 (file)
@@ -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