2222#include " NimBLEDevice.h"
2323#include " NimBLELog.h"
2424
25+ #include " services/gap/ble_svc_gap.h"
26+ #include " services/gatt/ble_svc_gatt.h"
27+
28+
2529static const char * LOG_TAG = " NimBLEServer" ;
2630static NimBLEServerCallbacks defaultCallbacks;
2731
@@ -37,9 +41,20 @@ NimBLEServer::NimBLEServer() {
3741 m_pServerCallbacks = &defaultCallbacks;
3842 m_gattsStarted = false ;
3943 m_advertiseOnDisconnect = true ;
44+ m_svcChanged = false ;
4045} // NimBLEServer
4146
4247
48+ /* *
49+ * @brief Destructor: frees all resources / attributes created.
50+ */
51+ NimBLEServer::~NimBLEServer () {
52+ for (auto &it : m_svcVec) {
53+ delete it;
54+ }
55+ }
56+
57+
4358/* *
4459 * @brief Create a %BLE Service.
4560 * @param [in] uuid The UUID of the new service.
@@ -71,6 +86,12 @@ NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid, uint32_t numH
7186 NimBLEService* pService = new NimBLEService (uuid, numHandles, this );
7287 m_svcVec.push_back (pService); // Save a reference to this service being on this server.
7388
89+ if (m_gattsStarted) {
90+ ble_svc_gatt_changed (0x0001 , 0xffff );
91+ m_svcChanged = true ;
92+ resetGATT ();
93+ }
94+
7495 NIMBLE_LOGD (LOG_TAG, " << createService" );
7596 return pService;
7697} // createService
@@ -146,8 +167,16 @@ void NimBLEServer::start() {
146167
147168 NIMBLE_LOGI(LOG_TAG, "Service changed characterisic handle: %d", m_svcChgChrHdl);
148169*/
149- // Build a vector of characteristics with Notify / Indicate capabilities for event handling
170+ // Get the assigned service handles and build a vector of characteristics
171+ // with Notify / Indicate capabilities for event handling
150172 for (auto &svc : m_svcVec) {
173+ if (svc->m_removed == 0 ) {
174+ rc = ble_gatts_find_svc (&svc->getUUID ().getNative ()->u , &svc->m_handle );
175+ if (rc != 0 ) {
176+ abort ();
177+ }
178+ }
179+
151180 for (auto &chr : svc->m_chrVec ) {
152181 // if Notify / Indicate is enabled but we didn't create the descriptor
153182 // we do it now.
@@ -260,6 +289,11 @@ size_t NimBLEServer::getConnectedCount() {
260289 server->m_connectedPeersVec .end (),
261290 event->disconnect .conn .conn_handle ),
262291 server->m_connectedPeersVec .end ());
292+
293+ if (server->m_svcChanged ) {
294+ server->resetGATT ();
295+ }
296+
263297 server->m_pServerCallbacks ->onDisconnect (server);
264298
265299 if (server->m_advertiseOnDisconnect ) {
@@ -445,6 +479,111 @@ void NimBLEServer::setCallbacks(NimBLEServerCallbacks* pCallbacks) {
445479} // setCallbacks
446480
447481
482+ /* *
483+ * @brief Remove a service from the server.
484+ *
485+ * @details Immediately removes access to the service by clients, sends a service changed indication,
486+ * and removes the service (if applicable) from the advertisments.
487+ * The service is not deleted unless the deleteSvc parameter is true, otherwise the service remains
488+ * available and can be re-added in the future. If desired a removed but not deleted service can
489+ * be deleted later by calling this method with deleteSvc set to true.
490+ *
491+ * @note The service will not be removed from the database until all open connections are closed
492+ * as it requires resetting the GATT server. In the interim the service will have it's visibility disabled.
493+ *
494+ * @note Advertising will need to be restarted by the user after calling this as we must stop
495+ * advertising in order to remove the service.
496+ *
497+ * @param [in] service The service object to remove.
498+ * @param [in] deleteSvc true if the service should be deleted.
499+ */
500+ void NimBLEServer::removeService (NimBLEService* service, bool deleteSvc) {
501+ // Check if the service was already removed and if so check if this
502+ // is being called to delete the object and do so if requested.
503+ // Otherwise, ignore the call and return.
504+ if (service->m_removed > 0 ) {
505+ if (deleteSvc) {
506+ for (auto it = m_svcVec.begin (); it != m_svcVec.end (); ++it) {
507+ if ((*it)->getUUID () == service->getUUID ()) {
508+ delete *it;
509+ m_svcVec.erase (it);
510+ break ;
511+ }
512+ }
513+ }
514+
515+ return ;
516+ }
517+
518+ int rc = ble_gatts_svc_set_visibility (service->getHandle (), 0 );
519+ if (rc !=0 ) {
520+ return ;
521+ }
522+
523+ service->m_removed = deleteSvc ? 2 : 1 ;
524+ m_svcChanged = true ;
525+
526+ ble_svc_gatt_changed (0x0001 , 0xffff );
527+ resetGATT ();
528+ NimBLEDevice::getAdvertising ()->removeServiceUUID (service->getUUID ());
529+ }
530+
531+
532+ /* *
533+ * @brief Adds a service which was already created, but removed from availability.
534+ *
535+ * @note If it is desired to advertise the service it must be added by
536+ * calling NimBLEAdvertising::addServiceUUID.
537+ *
538+ * @param [in} service The service object to add.
539+ */
540+ void NimBLEServer::addService (NimBLEService* service) {
541+ // If adding a service that was not removed just return.
542+ if (service->m_removed == 0 ) {
543+ return ;
544+ }
545+
546+ service->m_removed = 0 ;
547+ m_svcChanged = true ;
548+
549+ ble_svc_gatt_changed (0x0001 , 0xffff );
550+ resetGATT ();
551+ }
552+
553+
554+ /* *
555+ * @brief Resets the GATT server, used when services are added/removed after initialization.
556+ */
557+ void NimBLEServer::resetGATT () {
558+ if (getConnectedCount () > 0 ) {
559+ return ;
560+ }
561+
562+ NimBLEDevice::stopAdvertising ();
563+ ble_gatts_reset ();
564+ ble_svc_gap_init ();
565+ ble_svc_gatt_init ();
566+
567+ for (auto it = m_svcVec.begin (); it != m_svcVec.end (); ) {
568+ if ((*it)->m_removed > 0 ) {
569+ if ((*it)->m_removed == 2 ) {
570+ delete *it;
571+ it = m_svcVec.erase (it);
572+ } else {
573+ ++it;
574+ }
575+ continue ;
576+ }
577+
578+ (*it)->start ();
579+ ++it;
580+ }
581+
582+ m_svcChanged = false ;
583+ m_gattsStarted = false ;
584+ }
585+
586+
448587/* *
449588 * @brief Start advertising.
450589 *
0 commit comments