service: Implement adding a service declaration

This CL implements initiating and starting a GATT service
declaration in GattServer.

Bug: 24245347
Change-Id: I53281d29a052c1890616b6595881108ca0eedec3
diff --git a/service/gatt_server.cpp b/service/gatt_server.cpp
index 8c6c1f7..dd3cbd2 100644
--- a/service/gatt_server.cpp
+++ b/service/gatt_server.cpp
@@ -16,6 +16,8 @@
 
 #include "service/gatt_server.h"
 
+#include "service/hal/gatt_helpers.h"
+
 using std::lock_guard;
 using std::mutex;
 
@@ -30,6 +32,15 @@
 }
 
 GattServer::~GattServer() {
+  // Automatically unregister the server.
+  VLOG(1) << "GattServer unregistering: " << server_if_;
+
+  // Unregister as observer so we no longer receive any callbacks.
+  hal::BluetoothGattInterface::Get()->RemoveServerObserver(this);
+
+  // Unregister this server, stop all services, and ignore the result.
+  // TODO(armansito): stop and remove all services here? unregister_server
+  // should really take care of that.
   hal::BluetoothGattInterface::Get()->
       GetServerHALInterface()->unregister_server(server_if_);
 }
@@ -42,6 +53,208 @@
   return server_if_;
 }
 
+std::unique_ptr<GattIdentifier> GattServer::BeginServiceDeclaration(
+    const UUID& uuid, bool is_primary) {
+  VLOG(1) << __func__ << "server_if: " << server_if_
+          << " - UUID: " << uuid.ToString()
+          << ", is_primary: " << is_primary;
+  lock_guard<mutex> lock(mutex_);
+
+  if (pending_decl_) {
+    LOG(ERROR) << "Already began service declaration";
+    return nullptr;
+  }
+
+  CHECK(!pending_id_);
+  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);
+
+  pending_decl_.reset(new ServiceDeclaration());
+  pending_decl_->num_handles++;  // 1 handle for the service decl. attribute
+  pending_decl_->service_id = *service_id;
+  pending_decl_->attributes.push(*service_id);
+
+  return service_id;
+}
+
+bool GattServer::EndServiceDeclaration(const ResultCallback& callback) {
+  VLOG(1) << __func__ << "server_if: " << server_if_;
+  lock_guard<mutex> lock(mutex_);
+
+  if (!callback) {
+    LOG(ERROR) << "|callback| cannot be NULL";
+    return false;
+  }
+
+  if (!pending_decl_) {
+    LOG(ERROR) << "Service declaration not begun";
+    return false;
+  }
+
+  if (pending_end_decl_cb_) {
+    LOG(ERROR) << "EndServiceDeclaration already in progress";
+    return false;
+  }
+
+  CHECK(!pending_id_);
+
+  // There has to be at least one entry here for the service declaration
+  // attribute.
+  CHECK(pending_decl_->num_handles > 0);
+  CHECK(!pending_decl_->attributes.empty());
+
+  std::unique_ptr<GattIdentifier> service_id = PopNextId();
+  CHECK(service_id->IsService());
+  CHECK(*service_id == pending_decl_->service_id);
+
+  btgatt_srvc_id_t hal_id;
+  hal::GetHALServiceId(*service_id, &hal_id);
+
+  bt_status_t status = hal::BluetoothGattInterface::Get()->
+      GetServerHALInterface()->add_service(
+          server_if_, &hal_id, pending_decl_->num_handles);
+  if (status != BT_STATUS_SUCCESS) {
+    LOG(ERROR) << "Failed to initiate call to populate GATT service";
+    CleanUpPendingData();
+    return false;
+  }
+
+  pending_id_ = std::move(service_id);
+  pending_end_decl_cb_ = callback;
+
+  return true;
+}
+
+void GattServer::ServiceAddedCallback(
+    hal::BluetoothGattInterface* gatt_iface,
+    int status, int server_if,
+    const btgatt_srvc_id_t& srvc_id,
+    int srvc_handle) {
+  lock_guard<mutex> lock(mutex_);
+
+  if (server_if != server_if_)
+    return;
+
+  // Construct a GATT identifier.
+  auto gatt_id = hal::GetServiceIdFromHAL(srvc_id);
+  CHECK(pending_id_);
+  CHECK(*gatt_id == *pending_id_);
+  CHECK(*gatt_id == pending_decl_->service_id);
+  CHECK(pending_id_->IsService());
+
+  VLOG(1) << __func__ << " - server_if: " << server_if
+          << " handle: " << srvc_handle
+          << " UUID: " << gatt_id->service_uuid().ToString();
+
+  if (status != BT_STATUS_SUCCESS) {
+    NotifyEndCallbackAndClearData(static_cast<BLEStatus>(status), *gatt_id);
+    return;
+  }
+
+  // Add this to the handle map.
+  pending_handle_map_[*gatt_id] = srvc_handle;
+  CHECK(-1 == pending_decl_->service_handle);
+  pending_decl_->service_handle = srvc_handle;
+
+  // More entries aren't supported yet.
+  CHECK(pending_decl_->attributes.empty());
+
+  bt_status_t start_status = gatt_iface->GetServerHALInterface()->start_service(
+      server_if_, srvc_handle, TRANSPORT_BREDR | TRANSPORT_LE);
+  if (start_status == BT_STATUS_SUCCESS)
+    return;  // Wait for the pending call.
+
+  // Notify failure.
+  NotifyEndCallbackAndClearData(static_cast<BLEStatus>(start_status), *gatt_id);
+}
+
+void GattServer::ServiceStartedCallback(
+    hal::BluetoothGattInterface* gatt_iface,
+    int status, int server_if,
+    int srvc_handle) {
+  lock_guard<mutex> lock(mutex_);
+
+  if (server_if != server_if_)
+    return;
+
+  CHECK(pending_id_);
+  CHECK(pending_decl_);
+  CHECK(pending_decl_->service_handle == srvc_handle);
+
+  VLOG(1) << __func__ << " - server_if: " << server_if
+          << " handle: " << srvc_handle;
+
+  // If we failed to start the service, remove it from the database and ignore
+  // the result.
+  if (status != BT_STATUS_SUCCESS) {
+    gatt_iface->GetServerHALInterface()->delete_service(
+        server_if_, srvc_handle);
+  }
+
+  // Complete the operation.
+  NotifyEndCallbackAndClearData(static_cast<BLEStatus>(status),
+                                pending_decl_->service_id);
+}
+
+void GattServer::ServiceStoppedCallback(
+    hal::BluetoothGattInterface* /* gatt_iface */,
+    int /* status */,
+    int /* server_if */,
+    int /* srvc_handle */) {
+  // TODO(armansito): Support stopping a service.
+}
+
+void GattServer::NotifyEndCallbackAndClearData(
+    BLEStatus status, const GattIdentifier& id) {
+  VLOG(1) << __func__ << " status: " << status;
+  CHECK(pending_end_decl_cb_);
+
+  if (status == BLE_STATUS_SUCCESS)
+    handle_map_.insert(pending_handle_map_.begin(), pending_handle_map_.end());
+
+  pending_end_decl_cb_(status, id);
+
+  CleanUpPendingData();
+}
+
+void GattServer::CleanUpPendingData() {
+  pending_id_ = nullptr;
+  pending_decl_ = nullptr;
+  pending_end_decl_cb_ = ResultCallback();
+  pending_handle_map_.clear();
+}
+
+std::unique_ptr<GattIdentifier> GattServer::PopNextId() {
+  CHECK(pending_decl_);
+
+  if (pending_decl_->attributes.empty())
+    return nullptr;
+
+  const GattIdentifier& next = pending_decl_->attributes.front();
+  std::unique_ptr<GattIdentifier> id(new GattIdentifier(next));
+
+  pending_decl_->attributes.pop();
+
+  return id;
+}
+
 // GattServerFactory implementation
 // ========================================================
 
@@ -77,7 +290,7 @@
 }
 
 void GattServerFactory::RegisterServerCallback(
-    hal::BluetoothGattInterface* /* gatt_iface */,
+    hal::BluetoothGattInterface* gatt_iface,
     int status, int server_if,
     const bt_uuid_t& app_uuid) {
   UUID uuid(app_uuid);
@@ -96,6 +309,11 @@
   BLEStatus result = BLE_STATUS_FAILURE;
   if (status == BT_STATUS_SUCCESS) {
     server.reset(new GattServer(uuid, server_if));
+
+    // Use the unsafe variant to register this as an observer to prevent a
+    // deadlock since this callback is currently holding the lock.
+    gatt_iface->AddServerObserverUnsafe(server.get());
+
     result = BLE_STATUS_SUCCESS;
   }