diff options
-rw-r--r-- | api/direct_bt/BTDevice.hpp | 14 | ||||
-rw-r--r-- | api/direct_bt/BTGattHandler.hpp | 11 | ||||
-rw-r--r-- | java/org/direct_bt/BTDevice.java | 23 | ||||
-rw-r--r-- | src/direct_bt/BTDevice.cpp | 52 | ||||
-rw-r--r-- | src/direct_bt/BTGattHandler.cpp | 41 |
5 files changed, 84 insertions, 57 deletions
diff --git a/api/direct_bt/BTDevice.hpp b/api/direct_bt/BTDevice.hpp index fc109d56..56333965 100644 --- a/api/direct_bt/BTDevice.hpp +++ b/api/direct_bt/BTDevice.hpp @@ -1033,19 +1033,25 @@ namespace direct_bt { std::shared_ptr<BTGattHandler> getGattHandler() noexcept; /** - * Returns a list of shared GATTService available on this device if successful, - * otherwise returns an empty list if an error occurred. + * Returns a complete list of shared BTGattService available on this device, + * initially retrieved via GATT discovery. + * + * In case of transmission error, zero services or no GattGenericAccessSvc, + * method will return zero services indicating an error. + * In this case, user can assume that the connection is or will be disconnected. * * Method is only functional on a remote BTDevice in BTRole::Slave, a GATT server (GATTRole::Server), * i.e. the local BTAdapter acting as a BTRole::Master GATT client. * * The HCI connectLE(..) or connectBREDR(..) must be performed first, see {@link #connectDefault()}. * - * If this method has been called for the first time or no services have been detected yet: + * If this method has been called for the first time: * - the client MTU exchange will be performed - * - a list of GATTService will be retrieved + * - a complete list of BTGattService inclusive their BTGattChar and BTGattDesc will be retrieved + * - the GattGenericAccessSvc is extracted from the services, see getGattGenericAccess(). * * A GATT connection will be created via connectGATT() if not established yet. + * @see getGattGenericAccess() */ jau::darray<BTGattServiceRef> getGattServices() noexcept; diff --git a/api/direct_bt/BTGattHandler.hpp b/api/direct_bt/BTGattHandler.hpp index 4dc08c9e..5f20d56c 100644 --- a/api/direct_bt/BTGattHandler.hpp +++ b/api/direct_bt/BTGattHandler.hpp @@ -555,17 +555,17 @@ namespace direct_bt { * <p> * BT Core Spec v5.2: Vol 3, Part G GATT: 4.4.1 Discover All Primary Services * </p> - * Method returns reference to BTGattHandler's internal BTGattService vector of discovered services + * Populates the internal internal BTGattService vector of discovered services. * * Service discovery may consume 500ms - 2000ms, depending on bandwidth. * - * Method usually called via initClientGatt() and is only exposed special applications. + * Method called from initClientGatt(). * * @param shared_this shared pointer of this instance, used to forward a weak_ptr to BTGattService for back-reference. Reference is validated. - * @return BTGattHandler's internal BTGattService vector of discovered services + * @return true if successful, otherwise false * @see initClientGatt() */ - jau::darray<BTGattServiceRef> & discoverCompletePrimaryServices(std::shared_ptr<BTGattHandler> shared_this) noexcept; + bool discoverCompletePrimaryServices(std::shared_ptr<BTGattHandler> shared_this) noexcept; public: /** @@ -635,12 +635,13 @@ namespace direct_bt { * Initialize the connection and internal data set for GATT client operations: * - Exchange MTU * - Discover all primary services, its characteristics and its descriptors + * - Extracts the GattGenericAccessSvc from the services, see getGenericAccess() * * Service discovery may consume 500ms - 2000ms, depending on bandwidth. * * @param shared_this the shared BTGattHandler reference * @param already_init if already initialized true, will hold true, otherwise false - * @return true if already initialized or newly initialized, otherwise false + * @return true if already initialized or successfully newly initialized with at least GattGenericAccessSvc available, otherwise false * @see clientMTUExchange() * @see discoverCompletePrimaryServices() */ diff --git a/java/org/direct_bt/BTDevice.java b/java/org/direct_bt/BTDevice.java index efa640f0..57187d6b 100644 --- a/java/org/direct_bt/BTDevice.java +++ b/java/org/direct_bt/BTDevice.java @@ -628,20 +628,23 @@ public interface BTDevice extends BTObject boolean isValid(); /** - * Returns a list of shared BTGattService available on this device if successful, - * otherwise returns an empty list if an error occurred. - * <p> + * Returns a complete list of shared BTGattService available on this device, + * initially retrieved via GATT discovery. + * + * In case of transmission error, zero services or no GATT GenericAccess, + * method will return zero services indicating an error. + * In this case, user can assume that the connection is or will be disconnected. + * * Method is only functional on a remote BTDevice in {@link BTRole#Slave}, a GATT server, * i.e. the local BTAdapter acting as a {@link BTRole#Master} GATT client. - * </p> - * <p> + * * The HCI connectLE(..) or connectBREDR(..) must be performed first, see {@link #connectDefault()}. - * </p> - * <p> - * If this method has been called for the first time or no services have been detected yet: + * + * If this method has been called for the first time: * - the client MTU exchange will be performed - * - a list of GATTService will be retrieved - * </p> + * - a complete list of BTGattService inclusive their BTGattChar and BTGattDesc will be retrieved + * - the GATT GenericAccess is extracted from the services. + * * @since 2.4.0 */ List<BTGattService> getGattServices(); diff --git a/src/direct_bt/BTDevice.cpp b/src/direct_bt/BTDevice.cpp index 22887aee..1f89018d 100644 --- a/src/direct_bt/BTDevice.cpp +++ b/src/direct_bt/BTDevice.cpp @@ -1962,41 +1962,41 @@ jau::darray<BTGattServiceRef> BTDevice::getGattServices() noexcept { } bool gatt_already_init = false; - const bool gatt_client_init = gh->initClientGatt(gh, gatt_already_init); - jau::darray<BTGattServiceRef>& gattServices = gh->getServices(); - if( !gatt_client_init ) { - ERR_PRINT2("BTDevice::getGattServices: Client GATT Initialization failed"); - return gattServices; // copy previous discovery result (zero sized) + if( !gh->initClientGatt(gh, gatt_already_init) ) { + ERR_PRINT2("Client GATT Initialization failed"); + return jau::darray<BTGattServiceRef>(); // return zero size } if( gatt_already_init ) { - return gattServices; // copy previous discovery result + return gh->getServices(); // copy previous discovery result } - // FIXME: GATTHandler::sendWithReply() == nullptr but gattServices.size() > 0 !!! - if( gattServices.size() == 0 ) { // nothing discovered - ERR_PRINT2("BTDevice::getGattServices: No primary services discovered"); - return gattServices; + + jau::darray<BTGattServiceRef> result = gh->getServices(); // copy + if( result.size() == 0 ) { // nothing discovered, actually a redundant check done @ BTGattHandler::initClientGatt() 1st + ERR_PRINT2("No primary services discovered"); + return jau::darray<BTGattServiceRef>(); // return zero size } // discovery success, parse GenericAccess std::shared_ptr<GattGenericAccessSvc> gattGenericAccess = gh->getGenericAccess(); - if( nullptr != gattGenericAccess ) { - const uint64_t ts = jau::getCurrentMilliseconds(); - EIRDataType updateMask = update(*gattGenericAccess, ts); - DBG_PRINT("BTDevice::getGattServices: GenericAccess updated %s:\n %s\n -> %s", - to_string(updateMask).c_str(), gattGenericAccess->toString().c_str(), toString().c_str()); - if( EIRDataType::NONE != updateMask ) { - std::shared_ptr<BTDevice> sharedInstance = getSharedInstance(); - if( nullptr == sharedInstance ) { - ERR_PRINT("Device unknown to adapter and not tracked: %s", toString().c_str()); - } else { - adapter.sendDeviceUpdated("getGattServices", sharedInstance, ts, updateMask); - } + if( nullptr == gattGenericAccess ) { + // no GenericAccess discovered, actually a redundant check done @ BTGattHandler::initClientGatt() 1st + ERR_PRINT2("No GenericAccess: %s", toString().c_str()); + return jau::darray<BTGattServiceRef>(); // return zero size + } + + const uint64_t ts = jau::getCurrentMilliseconds(); + EIRDataType updateMask = update(*gattGenericAccess, ts); + DBG_PRINT("BTDevice::getGattServices: GenericAccess updated %s:\n %s\n -> %s", + to_string(updateMask).c_str(), gattGenericAccess->toString().c_str(), toString().c_str()); + if( EIRDataType::NONE != updateMask ) { + std::shared_ptr<BTDevice> sharedInstance = getSharedInstance(); + if( nullptr == sharedInstance ) { + ERR_PRINT("Device unknown to adapter and not tracked: %s", toString().c_str()); + } else { + adapter.sendDeviceUpdated("getGattServices", sharedInstance, ts, updateMask); } - } else { - // else: Actually an error w/o valid mandatory GenericAccess - WARN_PRINT("No GenericAccess: %s", toString().c_str()); } - return gattServices; // return copy + return result; // return the copy, copy elision shall be used } std::shared_ptr<GattGenericAccessSvc> BTDevice::getGattGenericAccess() { diff --git a/src/direct_bt/BTGattHandler.cpp b/src/direct_bt/BTGattHandler.cpp index ebb30be2..60cddd23 100644 --- a/src/direct_bt/BTGattHandler.cpp +++ b/src/direct_bt/BTGattHandler.cpp @@ -905,7 +905,7 @@ BTGattCharRef BTGattHandler::findCharacterisicsByValueHandle(const BTGattService bool BTGattHandler::initClientGatt(std::shared_ptr<BTGattHandler> shared_this, bool& already_init) noexcept { const std::lock_guard<std::recursive_mutex> lock(mtx_command); - already_init = clientMTUExchanged && services.size() > 0; + already_init = clientMTUExchanged && services.size() > 0 && nullptr != genericAccess; if( already_init ) { return true; } @@ -929,36 +929,53 @@ bool BTGattHandler::initClientGatt(std::shared_ptr<BTGattHandler> shared_this, b DBG_PRINT("GATTHandler::initClientGatt: Local GATT Client: MTU Exchanged: server %u -> used %u, %s", serverMTU.load(), usedMTU.load(), toString().c_str()); } - if( services.size() > 0 ) { + if( services.size() > 0 && nullptr != genericAccess ) { // already initialized return true; } + services.clear(); // Service discovery may consume 500ms - 2000ms, depending on bandwidth DBG_PRINT("GATTHandler::initClientGatt: Local GATT Client: Service Discovery Start: %s", toString().c_str()); - jau::darray<BTGattServiceRef>& gattServices = discoverCompletePrimaryServices(shared_this); - if( gattServices.size() == 0 ) { // nothing discovered - ERR_PRINT2("No primary services discovered"); + if( !discoverCompletePrimaryServices(shared_this) ) { + ERR_PRINT2("Failed service discovery"); + services.clear(); + disconnect(true /* disconnect_device */, true /* ioerr_cause */); + return false; + } + if( services.size() == 0 ) { // nothing discovered + ERR_PRINT2("No services discovered"); + services.clear(); + disconnect(true /* disconnect_device */, false /* ioerr_cause */); + return false; + } + genericAccess = getGenericAccess(services); + if( nullptr == genericAccess ) { + ERR_PRINT2("No GenericAccess discovered"); + services.clear(); disconnect(true /* disconnect_device */, false /* ioerr_cause */); return false; } - DBG_PRINT("GATTHandler::initClientGatt: %zu Services Discovered: %s", gattServices.size(), toString().c_str()); + DBG_PRINT("GATTHandler::initClientGatt: End: %zu services discovered: %s, %s", + services.size(), genericAccess->toString().c_str(), toString().c_str()); return true; } -jau::darray<BTGattServiceRef> & BTGattHandler::discoverCompletePrimaryServices(std::shared_ptr<BTGattHandler> shared_this) noexcept { +bool BTGattHandler::discoverCompletePrimaryServices(std::shared_ptr<BTGattHandler> shared_this) noexcept { const std::lock_guard<std::recursive_mutex> lock(mtx_command); // RAII-style acquire and relinquish via destructor if( !discoverPrimaryServices(shared_this, services) ) { - return services; + return false; } for(auto it = services.begin(); it != services.end(); it++) { BTGattServiceRef primSrv = *it; - if( discoverCharacteristics(primSrv) ) { - discoverDescriptors(primSrv); + if( !discoverCharacteristics(primSrv) ) { + return false; + } + if( !discoverDescriptors(primSrv) ) { + return false; } } - genericAccess = getGenericAccess(services); - return services; + return true; } bool BTGattHandler::discoverPrimaryServices(std::shared_ptr<BTGattHandler> shared_this, jau::darray<BTGattServiceRef> & result) noexcept { |