fix(nimble): stop leaking BLE2904 descriptor on BLE re-setup (#11453)

setupService() re-runs on every Bluetooth re-enable cycle, and every
other callback object in it is deliberately function-local static for
exactly that reason - the battery level descriptor was the one heap
allocation that was missed. The framework never frees descriptors
(~BLECharacteristic has an empty body and BLEDevice::deinit only
deletes server/advertising/scan), so each cycle leaked one BLE2904.
Make it static like its neighbors.
This commit is contained in:
Ben Meadors
2026-08-12 18:15:18 -05:00
committed by GitHub
parent f06f808391
commit 5172e58526
+8 -5
View File
@@ -1025,13 +1025,16 @@ void NimbleBluetooth::setupService()
// Setup the battery service
BLEService *batteryService = bleServer->createService(BLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
BLE2904 *batteryLevelDescriptor = new BLE2904();
batteryLevelDescriptor->setFormat(BLE2904::FORMAT_UINT8);
batteryLevelDescriptor->setNamespace(1);
batteryLevelDescriptor->setUnit(0x27ad);
// Static like the callback objects above: setupService() re-runs on every BLE re-enable, and
// the framework never frees descriptors (~BLECharacteristic is empty), so a heap allocation
// here leaks one BLE2904 per cycle.
static BLE2904 batteryLevelDescriptor;
batteryLevelDescriptor.setFormat(BLE2904::FORMAT_UINT8);
batteryLevelDescriptor.setNamespace(1);
batteryLevelDescriptor.setUnit(0x27ad);
BatteryCharacteristic = batteryService->createCharacteristic( // 0x2A19 is the Battery Level characteristic)
(uint16_t)0x2a19, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
BatteryCharacteristic->addDescriptor(batteryLevelDescriptor);
BatteryCharacteristic->addDescriptor(&batteryLevelDescriptor);
// Seed an initial 0-100 level so an early read of 0x2A19 returns a valid value.
uint8_t initialLevel = (powerStatus && powerStatus->getHasBattery()) ? powerStatus->getBatteryChargePercent() : 0;
if (initialLevel > 100)