InfiniTime.git

commit 29f8074fcb844cf9668a5bf071e9cffa47299c99

Author: JF <jf@codingfield.com>

Refactoring of BLE service discovery : it is now implemented into the classes of the services.

 src/CMakeLists.txt | 3 
 src/components/ble/AlertNotificationClient.cpp | 154 ++++++++++++-------
 src/components/ble/AlertNotificationClient.h | 18 -
 src/components/ble/BleClient.h | 12 +
 src/components/ble/CurrentTimeClient.cpp | 114 ++++++++-----
 src/components/ble/CurrentTimeClient.h | 14 -
 src/components/ble/NimbleController.cpp | 108 -------------
 src/components/ble/NimbleController.h | 3 
 src/components/ble/ServiceDiscovery.cpp | 31 ++++
 src/components/ble/ServiceDiscovery.h | 24 +++


diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 4d691ede93b31eb38f32a1cfff2f58366859ca6b..6c9ce24b6baedd105c955e56a2a69a7fb4dedc90 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -369,6 +369,7 @@         components/ble/AlertNotificationService.cpp
         components/ble/MusicService.cpp
         components/ble/BatteryInformationService.cpp
         components/ble/ImmediateAlertService.cpp
+        components/ble/ServiceDiscovery.cpp
         components/firmwarevalidator/FirmwareValidator.cpp
         drivers/Cst816s.cpp
         FreeRTOS/port.c
@@ -447,6 +448,8 @@         components/ble/DfuService.h
         components/firmwarevalidator/FirmwareValidator.h
         components/ble/BatteryInformationService.h
         components/ble/ImmediateAlertService.h
+        components/ble/ServiceDiscovery.h
+        components/ble/BleClient.h
         drivers/Cst816s.h
         FreeRTOS/portmacro.h
         FreeRTOS/portmacro_cmsis.h




diff --git a/src/components/ble/AlertNotificationClient.cpp b/src/components/ble/AlertNotificationClient.cpp
index 29bc2f73a7e315d4806f8391af61a1b1559984b7..abe41099b1f22f6ddf6349071ddf9540604d86f6 100644
--- a/src/components/ble/AlertNotificationClient.cpp
+++ b/src/components/ble/AlertNotificationClient.cpp
@@ -3,84 +3,127 @@ #include "NotificationManager.h"
 
 #include "AlertNotificationClient.h"
 
-
 using namespace Pinetime::Controllers;
 constexpr ble_uuid16_t AlertNotificationClient::ansServiceUuid;
-
 constexpr ble_uuid16_t AlertNotificationClient::supportedNewAlertCategoryUuid;
-constexpr ble_uuid16_t AlertNotificationClient::supportedUnreadAlertCategoryUuid ;
+constexpr ble_uuid16_t AlertNotificationClient::supportedUnreadAlertCategoryUuid;
 constexpr ble_uuid16_t AlertNotificationClient::newAlertUuid;
 constexpr ble_uuid16_t AlertNotificationClient::unreadAlertStatusUuid;
 constexpr ble_uuid16_t AlertNotificationClient::controlPointUuid;
 
-int Pinetime::Controllers::NewAlertSubcribeCallback(uint16_t conn_handle,
-                     const struct ble_gatt_error *error,
-                     struct ble_gatt_attr *attr,
-                     void *arg) {
-  auto client = static_cast<AlertNotificationClient*>(arg);
-  return client->OnNewAlertSubcribe(conn_handle, error, attr);
+namespace {
+  int
+  OnDiscoveryEventCallback(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service,
+                           void *arg) {
+    auto client = static_cast<AlertNotificationClient *>(arg);
+    return client->OnDiscoveryEvent(conn_handle, error, service);
+  }
+
+  int OnAlertNotificationCharacteristicDiscoveredCallback(uint16_t conn_handle, const struct ble_gatt_error *error,
+                                                          const struct ble_gatt_chr *chr, void *arg) {
+    auto client = static_cast<AlertNotificationClient *>(arg);
+    return client->OnCharacteristicsDiscoveryEvent(conn_handle, error, chr);
+  }
+
+  int OnAlertNotificationDescriptorDiscoveryEventCallback(uint16_t conn_handle,
+                                                          const struct ble_gatt_error *error,
+                                                          uint16_t chr_val_handle,
+                                                          const struct ble_gatt_dsc *dsc,
+                                                          void *arg) {
+    auto client = static_cast<AlertNotificationClient *>(arg);
+    return client->OnDescriptorDiscoveryEventCallback(conn_handle, error, chr_val_handle, dsc);
+  }
+
+  int NewAlertSubcribeCallback(uint16_t conn_handle,
+                               const struct ble_gatt_error *error,
+                               struct ble_gatt_attr *attr,
+                               void *arg) {
+    auto client = static_cast<AlertNotificationClient *>(arg);
+    return client->OnNewAlertSubcribe(conn_handle, error, attr);
+  }
 }
 
-AlertNotificationClient::AlertNotificationClient(Pinetime::System::SystemTask& systemTask,
-        Pinetime::Controllers::NotificationManager& notificationManager) :
-        systemTask{systemTask}, notificationManager{notificationManager}{
+AlertNotificationClient::AlertNotificationClient(Pinetime::System::SystemTask &systemTask,
+                                                 Pinetime::Controllers::NotificationManager &notificationManager) :
+        systemTask{systemTask}, notificationManager{notificationManager} {
+}
 
-}
+bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
+                                               const ble_gatt_svc *service) {
+  if (service == nullptr && error->status == BLE_HS_EDONE) {
+    if (isDiscovered) {
+      NRF_LOG_INFO("ANS Discovery found, starting characteristics discovery");
 
-bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service) {
-  if(service == nullptr && error->status == BLE_HS_EDONE) {
-    NRF_LOG_INFO("ANS Discovery complete");
+      ble_gattc_disc_all_chrs(connectionHandle, ansStartHandle, ansEndHandle,
+                              OnAlertNotificationCharacteristicDiscoveredCallback, this);
+    } else {
+      NRF_LOG_INFO("ANS not found");
+      onServiceDiscovered(connectionHandle);
+    }
     return true;
   }
 
-  if(service != nullptr && ble_uuid_cmp(((ble_uuid_t*)&ansServiceUuid), &service->uuid.u) == 0) {
-    NRF_LOG_INFO("ANS discovered : 0x%x", service->start_handle);
-      ansStartHandle = service->start_handle;
-      ansEndHandle = service->end_handle;
-      isDiscovered = true;
+  if (service != nullptr && ble_uuid_cmp(((ble_uuid_t *) &ansServiceUuid), &service->uuid.u) == 0) {
+    NRF_LOG_INFO("ANS discovered : 0x%x - 0x%x", service->start_handle, service->end_handle);
+    ansStartHandle = service->start_handle;
+    ansEndHandle = service->end_handle;
+    isDiscovered = true;
   }
   return false;
 }
 
 int AlertNotificationClient::OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
-                                                                                    const ble_gatt_chr *characteristic) {
-  if(error->status != 0 && error->status != BLE_HS_EDONE) {
+                                                             const ble_gatt_chr *characteristic) {
+  if (error->status != 0 && error->status != BLE_HS_EDONE) {
     NRF_LOG_INFO("ANS Characteristic discovery ERROR");
+    onServiceDiscovered(connectionHandle);
     return 0;
   }
 
-  if(characteristic == nullptr && error->status == BLE_HS_EDONE) {
+  if (characteristic == nullptr && error->status == BLE_HS_EDONE) {
     NRF_LOG_INFO("ANS Characteristic discovery complete");
+    if (isCharacteristicDiscovered) {
+      ble_gattc_disc_all_dscs(connectionHandle,
+                              newAlertHandle, ansEndHandle,
+                              OnAlertNotificationDescriptorDiscoveryEventCallback, this);
+    } else
+      onServiceDiscovered(connectionHandle);
   } else {
-    if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&supportedNewAlertCategoryUuid), &characteristic->uuid.u) == 0) {
+    if (characteristic != nullptr &&
+        ble_uuid_cmp(((ble_uuid_t *) &supportedNewAlertCategoryUuid), &characteristic->uuid.u) == 0) {
       NRF_LOG_INFO("ANS Characteristic discovered : supportedNewAlertCategoryUuid");
       supportedNewAlertCategoryHandle = characteristic->val_handle;
-    } else if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&supportedUnreadAlertCategoryUuid), &characteristic->uuid.u) == 0) {
+    } else if (characteristic != nullptr &&
+               ble_uuid_cmp(((ble_uuid_t *) &supportedUnreadAlertCategoryUuid), &characteristic->uuid.u) == 0) {
       NRF_LOG_INFO("ANS Characteristic discovered : supportedUnreadAlertCategoryUuid");
       supportedUnreadAlertCategoryHandle = characteristic->val_handle;
-    } else if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&newAlertUuid), &characteristic->uuid.u) == 0) {
+    } else if (characteristic != nullptr &&
+               ble_uuid_cmp(((ble_uuid_t *) &newAlertUuid), &characteristic->uuid.u) == 0) {
       NRF_LOG_INFO("ANS Characteristic discovered : newAlertUuid");
       newAlertHandle = characteristic->val_handle;
       newAlertDefHandle = characteristic->def_handle;
-    } else if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&unreadAlertStatusUuid), &characteristic->uuid.u) == 0) {
+      isCharacteristicDiscovered = true;
+    } else if (characteristic != nullptr &&
+               ble_uuid_cmp(((ble_uuid_t *) &unreadAlertStatusUuid), &characteristic->uuid.u) == 0) {
       NRF_LOG_INFO("ANS Characteristic discovered : unreadAlertStatusUuid");
       unreadAlertStatusHandle = characteristic->val_handle;
-    } else if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&controlPointUuid), &characteristic->uuid.u) == 0) {
+    } else if (characteristic != nullptr &&
+               ble_uuid_cmp(((ble_uuid_t *) &controlPointUuid), &characteristic->uuid.u) == 0) {
       NRF_LOG_INFO("ANS Characteristic discovered : controlPointUuid");
       controlPointHandle = characteristic->val_handle;
-    }else
-      NRF_LOG_INFO("ANS Characteristic discovered : 0x%x", characteristic->val_handle);
-    }
+    } else NRF_LOG_INFO("ANS Characteristic discovered : 0x%x", characteristic->val_handle);
+  }
   return 0;
 }
 
 int AlertNotificationClient::OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error *error,
                                                 ble_gatt_attr *attribute) {
-  if(error->status == 0) {
+  if (error->status == 0) {
     NRF_LOG_INFO("ANS New alert subscribe OK");
   } else {
     NRF_LOG_INFO("ANS New alert subscribe ERROR");
   }
+  onServiceDiscovered(connectionHandle);
 
   return 0;
 }
@@ -88,35 +131,40 @@
 int AlertNotificationClient::OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error,
                                                                 uint16_t characteristicValueHandle,
                                                                 const ble_gatt_dsc *descriptor) {
-  if(error->status == 0) {
-    if(characteristicValueHandle == newAlertHandle && ble_uuid_cmp(((ble_uuid_t*)&newAlertUuid), &descriptor->uuid.u)) {
-      if(newAlertDescriptorHandle == 0) {
+  if (error->status == 0) {
+    if (characteristicValueHandle == newAlertHandle &&
+        ble_uuid_cmp(((ble_uuid_t *) &newAlertUuid), &descriptor->uuid.u)) {
+      if (newAlertDescriptorHandle == 0) {
         NRF_LOG_INFO("ANS Descriptor discovered : %d", descriptor->handle);
         newAlertDescriptorHandle = descriptor->handle;
+        isDescriptorFound = true;
         uint8_t value[2];
         value[0] = 1;
         value[1] = 0;
         ble_gattc_write_flat(connectionHandle, newAlertDescriptorHandle, value, sizeof(value), NewAlertSubcribeCallback, this);
       }
     }
+  } else {
+    if (!isDescriptorFound)
+      onServiceDiscovered(connectionHandle);
   }
   return 0;
 }
 
 void AlertNotificationClient::OnNotification(ble_gap_event *event) {
-  if(event->notify_rx.attr_handle == newAlertHandle) {
+  if (event->notify_rx.attr_handle == newAlertHandle) {
     constexpr size_t stringTerminatorSize = 1; // end of string '\0'
     constexpr size_t headerSize = 3;
-    const auto maxMessageSize {NotificationManager::MaximumMessageSize()};
+    const auto maxMessageSize{NotificationManager::MaximumMessageSize()};
     const auto maxBufferSize{maxMessageSize + headerSize};
 
     const auto dbgPacketLen = OS_MBUF_PKTLEN(event->notify_rx.om);
     size_t bufferSize = min(dbgPacketLen + stringTerminatorSize, maxBufferSize);
-    auto messageSize = min(maxMessageSize, (bufferSize-headerSize));
+    auto messageSize = min(maxMessageSize, (bufferSize - headerSize));
 
     NotificationManager::Notification notif;
-    os_mbuf_copydata(event->notify_rx.om, headerSize, messageSize-1, notif.message.data());
-    notif.message[messageSize-1] = '\0';
+    os_mbuf_copydata(event->notify_rx.om, headerSize, messageSize - 1, notif.message.data());
+    notif.message[messageSize - 1] = '\0';
     notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
     notificationManager.Push(std::move(notif));
 
@@ -124,22 +172,6 @@     systemTask.PushMessage(Pinetime::System::SystemTask::Messages::OnNewNotification);
   }
 }
 
-bool AlertNotificationClient::IsDiscovered() const {
-  return isDiscovered;
-}
-
-uint16_t AlertNotificationClient::StartHandle() const {
-  return ansStartHandle;
-}
-
-uint16_t AlertNotificationClient::EndHandle() const {
-  return ansEndHandle;
-}
-
-uint16_t AlertNotificationClient::NewAlerthandle() const {
-  return newAlertHandle;
-}
-
 void AlertNotificationClient::Reset() {
   ansStartHandle = 0;
   ansEndHandle = 0;
@@ -151,4 +183,12 @@   newAlertDefHandle = 0;
   unreadAlertStatusHandle = 0;
   controlPointHandle = 0;
   isDiscovered = false;
+  isCharacteristicDiscovered = false;
+  isDescriptorFound = false;
+}
+
+void AlertNotificationClient::Discover(uint16_t connectionHandle, std::function<void(uint16_t)> onServiceDiscovered) {
+  NRF_LOG_INFO("[ANS] Starting discovery");
+  this->onServiceDiscovered = onServiceDiscovered;
+  ble_gattc_disc_svc_by_uuid(connectionHandle, &ansServiceUuid.u, OnDiscoveryEventCallback, this);
 }




diff --git a/src/components/ble/AlertNotificationClient.h b/src/components/ble/AlertNotificationClient.h
index ebd5d56fbe5215d19127874beb80a0b769abf82a..bc0df51e1513bd1b1e3b5f54156136fc5083ea5f 100644
--- a/src/components/ble/AlertNotificationClient.h
+++ b/src/components/ble/AlertNotificationClient.h
@@ -3,16 +3,12 @@
 #include <cstdint>
 #include <array>
 #include <host/ble_gap.h>
+#include "BleClient.h"
 
 
 namespace Pinetime {
   namespace Controllers {
-    int NewAlertSubcribeCallback(uint16_t conn_handle,
-                                 const struct ble_gatt_error *error,
-                                 struct ble_gatt_attr *attr,
-                                 void *arg);
-
-    class AlertNotificationClient {
+    class AlertNotificationClient : public BleClient {
       public:
         explicit AlertNotificationClient(Pinetime::System::SystemTask &systemTask,
                                          Pinetime::Controllers::NotificationManager &notificationManager);
@@ -24,14 +20,9 @@         int OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error *error, ble_gatt_attr *attribute);
         int OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error,
                                                uint16_t characteristicValueHandle, const ble_gatt_dsc *descriptor);
         void OnNotification(ble_gap_event *event);
-        bool IsDiscovered() const;
-        uint16_t StartHandle() const;
-        uint16_t EndHandle() const;
         void Reset();
+        void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) override;
 
-        static constexpr const ble_uuid16_t &Uuid() { return ansServiceUuid; }
-
-        uint16_t NewAlerthandle() const;
       private:
         static constexpr uint16_t ansServiceId{0x1811};
         static constexpr uint16_t supportedNewAlertCategoryId = 0x2a47;
@@ -77,6 +68,9 @@         uint16_t controlPointHandle = 0;
         bool isDiscovered = false;
         Pinetime::System::SystemTask &systemTask;
         Pinetime::Controllers::NotificationManager &notificationManager;
+        std::function<void(uint16_t)> onServiceDiscovered;
+        bool isCharacteristicDiscovered = false;
+        bool isDescriptorFound = false;
     };
   }
 }
\ No newline at end of file




diff --git a/src/components/ble/BleClient.h b/src/components/ble/BleClient.h
new file mode 100644
index 0000000000000000000000000000000000000000..559f6c8dc83c57e7ec6c5189ae8d923852e8430a
--- /dev/null
+++ b/src/components/ble/BleClient.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <functional>
+
+namespace Pinetime {
+  namespace Controllers{
+    class BleClient {
+      public:
+        virtual void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) = 0;
+    };
+  }
+}
\ No newline at end of file




diff --git a/src/components/ble/CurrentTimeClient.cpp b/src/components/ble/CurrentTimeClient.cpp
index 24027e5f61fe6d6442a4d289469a7e497ceab3d2..92f9374b44622f3124f0a2b16184889b594604b9 100644
--- a/src/components/ble/CurrentTimeClient.cpp
+++ b/src/components/ble/CurrentTimeClient.cpp
@@ -6,7 +6,25 @@
 constexpr ble_uuid16_t CurrentTimeClient::ctsServiceUuid;
 constexpr ble_uuid16_t CurrentTimeClient::currentTimeCharacteristicUuid;
 
-CurrentTimeClient::CurrentTimeClient(DateTime& dateTimeController) : dateTimeController{dateTimeController} {
+namespace {
+  int OnDiscoveryEventCallback(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg) {
+    auto client = static_cast<CurrentTimeClient *>(arg);
+    return client->OnDiscoveryEvent(conn_handle, error, service);
+  }
+
+  int OnCurrentTimeCharacteristicDiscoveredCallback(uint16_t conn_handle, const struct ble_gatt_error *error,
+                                                    const struct ble_gatt_chr *chr, void *arg) {
+    auto client = static_cast<CurrentTimeClient *>(arg);
+    return client->OnCharacteristicDiscoveryEvent(conn_handle, error, chr);
+  }
+
+  int CurrentTimeReadCallback(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) {
+    auto client = static_cast<CurrentTimeClient *>(arg);
+    return client->OnCurrentTimeReadResult(conn_handle, error, attr);
+  }
+}
+
+CurrentTimeClient::CurrentTimeClient(DateTime &dateTimeController) : dateTimeController{dateTimeController} {
 
 }
 
@@ -14,30 +32,47 @@ void CurrentTimeClient::Init() {
 
 }
 
-bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service) {
-    if(service == nullptr && error->status == BLE_HS_EDONE) {
-        NRF_LOG_INFO("CTS Discovery complete");
-        return true;
+bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
+                                         const ble_gatt_svc *service) {
+  if (service == nullptr && error->status == BLE_HS_EDONE) {
+    if (isDiscovered) {
+      NRF_LOG_INFO("CTS found, starting characteristics discovery");
+
+      ble_gattc_disc_all_chrs(connectionHandle, ctsStartHandle, ctsEndHandle,
+                              OnCurrentTimeCharacteristicDiscoveredCallback, this);
+    } else {
+      NRF_LOG_INFO("CTS not found");
+      onServiceDiscovered(connectionHandle);
     }
+    return true;
+  }
 
-    if(service != nullptr && ble_uuid_cmp(((ble_uuid_t*)&ctsServiceUuid), &service->uuid.u) == 0) {
-        NRF_LOG_INFO("CTS discovered : 0x%x",  service->start_handle);
-        isDiscovered = true;
-        ctsStartHandle = service->start_handle;
-        ctsEndHandle = service->end_handle;
-        return false;
-    }
+  if (service != nullptr && ble_uuid_cmp(((ble_uuid_t *) &ctsServiceUuid), &service->uuid.u) == 0) {
+    NRF_LOG_INFO("CTS discovered : 0x%x - 0x%x", service->start_handle, service->end_handle);
+    isDiscovered = true;
+    ctsStartHandle = service->start_handle;
+    ctsEndHandle = service->end_handle;
     return false;
+  }
+  return false;
 }
 
 int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error *error,
                                                       const ble_gatt_chr *characteristic) {
   if (characteristic == nullptr && error->status == BLE_HS_EDONE) {
-    NRF_LOG_INFO("CTS Characteristic discovery complete");
+    if (isCharacteristicDiscovered) {
+      NRF_LOG_INFO("CTS Characteristic discovery complete, fetching time");
+      ble_gattc_read(conn_handle, currentTimeHandle, CurrentTimeReadCallback, this);
+    } else {
+      NRF_LOG_INFO("CTS Characteristic discovery unsuccessful");
+      onServiceDiscovered(conn_handle);
+    }
+
     return 0;
   }
 
-  if (characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t *) &currentTimeCharacteristicUuid), &characteristic->uuid.u) == 0) {
+  if (characteristic != nullptr &&
+      ble_uuid_cmp(((ble_uuid_t *) &currentTimeCharacteristicUuid), &characteristic->uuid.u) == 0) {
     NRF_LOG_INFO("CTS Characteristic discovered : 0x%x", characteristic->val_handle);
     isCharacteristicDiscovered = true;
     currentTimeHandle = characteristic->val_handle;
@@ -45,36 +80,23 @@   }
   return 0;
 }
 
-int CurrentTimeClient::OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error, const ble_gatt_attr *attribute) {
-    if(error->status == 0) {
-        // TODO check that attribute->handle equals the handle discovered in OnCharacteristicDiscoveryEvent
-        CtsData result;
-        os_mbuf_copydata(attribute->om, 0, sizeof(CtsData), &result);
-        NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", result.year,
-                     result.month, result.dayofmonth,
-                     result.hour, result.minute, result.second);
-        dateTimeController.SetTime(result.year, result.month, result.dayofmonth,
-                                   0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
-    } else {
-        NRF_LOG_INFO("Error retrieving current time: %d", error->status);
-    }
-    return 0;
-}
-
-bool CurrentTimeClient::IsDiscovered() const {
-    return isDiscovered;
-}
-
-uint16_t CurrentTimeClient::StartHandle() const {
-    return ctsStartHandle;
-}
+int CurrentTimeClient::OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error,
+                                               const ble_gatt_attr *attribute) {
+  if (error->status == 0) {
+    // TODO check that attribute->handle equals the handle discovered in OnCharacteristicDiscoveryEvent
+    CtsData result;
+    os_mbuf_copydata(attribute->om, 0, sizeof(CtsData), &result);
+    NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", result.year,
+                 result.month, result.dayofmonth,
+                 result.hour, result.minute, result.second);
+    dateTimeController.SetTime(result.year, result.month, result.dayofmonth,
+                               0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG));
+  } else {
+    NRF_LOG_INFO("Error retrieving current time: %d", error->status);
+  }
 
-uint16_t CurrentTimeClient::EndHandle() const {
-    return ctsEndHandle;
-}
-
-uint16_t CurrentTimeClient::CurrentTimeHandle() const {
-    return currentTimeHandle;
+  onServiceDiscovered(conn_handle);
+  return 0;
 }
 
 void CurrentTimeClient::Reset() {
@@ -82,6 +104,8 @@   isDiscovered = false;
   isCharacteristicDiscovered = false;
 }
 
-bool CurrentTimeClient::IsCharacteristicDiscovered() const {
-  return isCharacteristicDiscovered;
+void CurrentTimeClient::Discover(uint16_t connectionHandle, std::function<void(uint16_t)> onServiceDiscovered) {
+  NRF_LOG_INFO("[CTS] Starting discovery");
+  this->onServiceDiscovered = onServiceDiscovered;
+  ble_gattc_disc_svc_by_uuid(connectionHandle, &ctsServiceUuid.u, OnDiscoveryEventCallback, this);
 }




diff --git a/src/components/ble/CurrentTimeClient.h b/src/components/ble/CurrentTimeClient.h
index fcc124c2b006cf914b11a9f05ce6e0c94b244139..93139399a68cab62748b73026daa2c21f352c144 100644
--- a/src/components/ble/CurrentTimeClient.h
+++ b/src/components/ble/CurrentTimeClient.h
@@ -3,12 +3,13 @@ #include 
 #include <array>
 
 #include "components/datetime/DateTimeController.h"
+#include "BleClient.h"
 #include <host/ble_gap.h>
 
 namespace Pinetime {
     namespace Controllers {
 
-        class CurrentTimeClient {
+        class CurrentTimeClient : public BleClient {
         public:
             explicit CurrentTimeClient(DateTime& dateTimeController);
             void Init();
@@ -17,14 +18,11 @@             bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service);
             int OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error *error,
                                                const ble_gatt_chr *characteristic);
             int OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error, const ble_gatt_attr *attribute);
-            bool IsDiscovered() const;
-            bool IsCharacteristicDiscovered() const;
-            uint16_t StartHandle() const;
-            uint16_t EndHandle() const;
-            uint16_t CurrentTimeHandle() const;
             static constexpr const ble_uuid16_t* Uuid() { return &CurrentTimeClient::ctsServiceUuid; }
             static constexpr const ble_uuid16_t* CurrentTimeCharacteristicUuid() { return &CurrentTimeClient::currentTimeCharacteristicUuid; }
-        private:
+            void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) override;
+
+          private:
             typedef struct __attribute__((packed)) {
                 uint16_t year;
                 uint8_t month;
@@ -55,7 +53,7 @@             uint16_t ctsEndHandle;
 
             bool isCharacteristicDiscovered = false;
             uint16_t currentTimeHandle;
-
+            std::function<void(uint16_t)> onServiceDiscovered;
         };
     }
 }
\ No newline at end of file




diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp
index 577c897a70095ea64bf392560bc86b49ec7d7b89..af7f402919a6e0b0f1126a77c54cf7aa6ba5774b 100644
--- a/src/components/ble/NimbleController.cpp
+++ b/src/components/ble/NimbleController.cpp
@@ -1,10 +1,7 @@
-
 #include "components/datetime/DateTimeController.h"
-
 #include <systemtask/SystemTask.h>
 #include "components/ble/NotificationManager.h"
 #include <hal/nrf_rtc.h>
-
 #include "NimbleController.h"
 #include "MusicService.h"
 #include <services/gatt/ble_svc_gatt.h>
@@ -14,14 +11,8 @@ #include 
 #include <host/ble_hs.h>
 #include <host/ble_gap.h>
 
-
-
 using namespace Pinetime::Controllers;
 
-// TODO I'm not satisfied by how this code looks like (AlertNotificationClient and CurrentTimeClient must
-// expose too much data, too many callbacks -> NimbleController -> CTS/ANS client.
-// Let's try to improve this code (and keep it working!)
-
 NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
                                    Pinetime::Controllers::Ble& bleController,
         DateTime& dateTimeController,
@@ -40,8 +31,8 @@         alertNotificationClient{systemTask, notificationManager},
         currentTimeService{dateTimeController},
         musicService{systemTask},
         batteryInformationService{batteryController},
-        immediateAlertService{systemTask, notificationManager} {
-
+        immediateAlertService{systemTask, notificationManager},
+        serviceDiscovery({&currentTimeClient, &alertNotificationClient}) {
 }
 
 int GAPEventCallback(struct ble_gap_event *event, void *arg) {
@@ -49,33 +40,6 @@   auto nimbleController = static_cast(arg);
   return nimbleController->OnGAPEvent(event);
 }
 
-int CurrentTimeCharacteristicDiscoveredCallback(uint16_t conn_handle, const struct ble_gatt_error *error,
-                                                const struct ble_gatt_chr *chr, void *arg) {
-  auto client = static_cast<NimbleController*>(arg);
-  return client->OnCTSCharacteristicDiscoveryEvent(conn_handle, error, chr);
-}
-
-int AlertNotificationCharacteristicDiscoveredCallback(uint16_t conn_handle, const struct ble_gatt_error *error,
-                                                const struct ble_gatt_chr *chr, void *arg) {
-  auto client = static_cast<NimbleController*>(arg);
-  return client->OnANSCharacteristicDiscoveryEvent(conn_handle, error, chr);
-}
-
-int CurrentTimeReadCallback(uint16_t conn_handle, const struct ble_gatt_error *error,
-                                   struct ble_gatt_attr *attr, void *arg) {
-  auto client = static_cast<NimbleController*>(arg);
-  return client->OnCurrentTimeReadResult(conn_handle, error, attr);
-}
-
-int AlertNotificationDescriptorDiscoveryEventCallback(uint16_t conn_handle,
-                                                                             const struct ble_gatt_error *error,
-                                                                             uint16_t chr_val_handle,
-                                                                             const struct ble_gatt_dsc *dsc,
-                                                                             void *arg) {
-  auto client = static_cast<NimbleController*>(arg);
-  return client->OnANSDescriptorDiscoveryEventCallback(conn_handle, error, chr_val_handle, dsc);
-}
-
 void NimbleController::Init() {
   while (!ble_hs_synced()) {}
 
@@ -158,15 +122,6 @@   // the advertising should be improve (better error handling, and advertise for 3 minutes after
   // the application has been woken up, for example.
 }
 
-int OnAllSvrDisco(uint16_t conn_handle,
-                                 const struct ble_gatt_error *error,
-                                 const struct ble_gatt_svc *service,
-                                 void *arg) {
-  auto nimbleController = static_cast<NimbleController*>(arg);
-  return nimbleController->OnDiscoveryEvent(conn_handle, error, service);
-  return 0;
-}
-
 int NimbleController::OnGAPEvent(ble_gap_event *event) {
   switch (event->type) {
     case BLE_GAP_EVENT_ADV_COMPLETE:
@@ -271,65 +226,8 @@   }
   return 0;
 }
 
-int NimbleController::OnDiscoveryEvent(uint16_t i, const ble_gatt_error *error, const ble_gatt_svc *service) {
-  if(service == nullptr && error->status == BLE_HS_EDONE) {
-    NRF_LOG_INFO("Service Discovery complete");
-    if(currentTimeClient.IsDiscovered()) {
-      ble_gattc_disc_all_chrs(connectionHandle, currentTimeClient.StartHandle(), currentTimeClient.EndHandle(),
-                              CurrentTimeCharacteristicDiscoveredCallback, this);
-
-    } else if(alertNotificationClient.IsDiscovered()) {
-      ble_gattc_disc_all_chrs(connectionHandle, alertNotificationClient.StartHandle(), alertNotificationClient.EndHandle(),
-                              AlertNotificationCharacteristicDiscoveredCallback, this);
-    }
-  }
-
-  alertNotificationClient.OnDiscoveryEvent(i, error, service);
-  currentTimeClient.OnDiscoveryEvent(i, error, service);
-  return 0;
-}
-
-int NimbleController::OnCTSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
-                                                        const ble_gatt_chr *characteristic) {
-  if(characteristic == nullptr && error->status == BLE_HS_EDONE && currentTimeClient.IsCharacteristicDiscovered()) {
-    NRF_LOG_INFO("CTS characteristic Discovery complete");
-    auto res = ble_gattc_read(connectionHandle, currentTimeClient.CurrentTimeHandle(), CurrentTimeReadCallback, this);
-    return res;
-  }
-  return currentTimeClient.OnCharacteristicDiscoveryEvent(connectionHandle, error, characteristic);
-}
-
-int NimbleController::OnANSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error,
-                                                        const ble_gatt_chr *characteristic) {
-  if(characteristic == nullptr && error->status == BLE_HS_EDONE) {
-    NRF_LOG_INFO("ANS characteristic Discovery complete");
-    ble_gattc_disc_all_dscs(connectionHandle,
-            alertNotificationClient.NewAlerthandle(), alertNotificationClient.EndHandle(),
-            AlertNotificationDescriptorDiscoveryEventCallback, this);
-    return 0;
-  }
-  return alertNotificationClient.OnCharacteristicsDiscoveryEvent(connectionHandle, error, characteristic);
-}
-
-int NimbleController::OnCurrentTimeReadResult(uint16_t connectionHandle, const ble_gatt_error *error, ble_gatt_attr *attribute) {
-  currentTimeClient.OnCurrentTimeReadResult(connectionHandle, error, attribute);
-
-  if (alertNotificationClient.IsDiscovered()) {
-    ble_gattc_disc_all_chrs(connectionHandle, alertNotificationClient.StartHandle(),
-                            alertNotificationClient.EndHandle(),
-                            AlertNotificationCharacteristicDiscoveredCallback, this);
-  }
-  return 0;
-}
-
-int NimbleController::OnANSDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error,
-                                                            uint16_t characteristicValueHandle,
-                                                            const ble_gatt_dsc *descriptor) {
-  return alertNotificationClient.OnDescriptorDiscoveryEventCallback(connectionHandle, error, characteristicValueHandle, descriptor);
-}
-
 void NimbleController::StartDiscovery() {
-  ble_gattc_disc_all_svcs(connectionHandle, OnAllSvrDisco, this);
+  serviceDiscovery.StartDiscovery(connectionHandle);
 }
 
 




diff --git a/src/components/ble/NimbleController.h b/src/components/ble/NimbleController.h
index 9d20caffdc9f9983252acac5068e2321fe2025d4..8ddec1f9ec5180ab6aad3f0f9df12c8f397edeff 100644
--- a/src/components/ble/NimbleController.h
+++ b/src/components/ble/NimbleController.h
@@ -11,6 +11,7 @@ #include "CurrentTimeService.h"
 #include "MusicService.h"
 #include "BatteryInformationService.h"
 #include "ImmediateAlertService.h"
+#include "ServiceDiscovery.h"
 #include <host/ble_gap.h>
 
 namespace Pinetime {
@@ -71,6 +72,8 @@                 .u { .type = BLE_UUID_TYPE_128},
                 .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15,
                           0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}
         };
+
+        ServiceDiscovery serviceDiscovery;
     };
   }
 }




diff --git a/src/components/ble/ServiceDiscovery.cpp b/src/components/ble/ServiceDiscovery.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4b29d89cdc8094f61df6335ec9c17f33b81e8111
--- /dev/null
+++ b/src/components/ble/ServiceDiscovery.cpp
@@ -0,0 +1,31 @@
+#include <libraries/log/nrf_log.h>
+#include "ServiceDiscovery.h"
+using namespace Pinetime::Controllers;
+
+ServiceDiscovery::ServiceDiscovery(std::array<BleClient*, 2>&& clients) : clients{clients} {
+
+}
+
+void ServiceDiscovery::StartDiscovery(uint16_t connectionHandle) {
+  NRF_LOG_INFO("[Discovery] Starting discovery");
+  clientIterator = clients.begin();
+  DiscoverNextService(connectionHandle);
+}
+
+void ServiceDiscovery::OnServiceDiscovered(uint16_t connectionHandle) {
+  clientIterator++;
+  if(clientIterator != clients.end()) {
+    DiscoverNextService(connectionHandle);
+  } else {
+    NRF_LOG_INFO("End of service discovery");
+  }
+}
+
+void ServiceDiscovery::DiscoverNextService(uint16_t connectionHandle) {
+  NRF_LOG_INFO("[Discovery] Discover next service");
+
+  auto discoverNextService = [this](uint16_t connectionHandle){
+    this->OnServiceDiscovered(connectionHandle);
+  };
+  (*clientIterator)->Discover(connectionHandle, discoverNextService);
+}
\ No newline at end of file




diff --git a/src/components/ble/ServiceDiscovery.h b/src/components/ble/ServiceDiscovery.h
new file mode 100644
index 0000000000000000000000000000000000000000..c86fc4ec7a302d0f617236d684f9c7c9b30cda88
--- /dev/null
+++ b/src/components/ble/ServiceDiscovery.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <array>
+#include <functional>
+#include <memory>
+#include "BleClient.h"
+
+namespace Pinetime {
+  namespace Controllers {
+    class ServiceDiscovery {
+      public:
+        ServiceDiscovery(std::array<BleClient*, 2>&& bleClients);
+
+        void StartDiscovery(uint16_t connectionHandle);
+
+
+      private:
+        BleClient** clientIterator;
+        std::array<BleClient*, 2> clients;
+        void OnServiceDiscovered(uint16_t connectionHandle);
+        void DiscoverNextService(uint16_t connectionHandle);
+    };
+  }
+}