Skip to content

Restore GAP device name after GATT reset#426

Open
chmorgan wants to merge 1 commit into
h2zero:masterfrom
chmorgan:feature/restore-after-ble-svc-gap-init
Open

Restore GAP device name after GATT reset#426
chmorgan wants to merge 1 commit into
h2zero:masterfrom
chmorgan:feature/restore-after-ble-svc-gap-init

Conversation

@chmorgan

@chmorgan chmorgan commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

NOTE: Codex 5.5 created this patch and writeup etc but I reviewed it and it looks like what I would have done by hand, and tested it here and it does resolve the issue and explains why I've been seeing my device misnamed for a bit. Not sure how or when I broke it, or if the issue was library side or a nimble change but figured others might be hitting this issue.

ESP-IDF NimBLE can reset the GAP Device Name when the standard GAP service is reinitialized during GATT server startup. NimBLEDevice::init(deviceName) calls NimBLEDevice::setDeviceName(deviceName), but that happens before the server is started. Later, starting advertising starts the server and resets/re-registers the GAP service, causing the C GAP name backing storage to be rebuilt from CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME. In projects where that Kconfig default remains "nimble", the advertised Local Name can be correct while the Generic Access Device Name characteristic reports "nimble".

The failing sequence is:

-> pAdvertising->start()
-> NimBLEAdvertising::start()
-> NimBLEDevice::getServer()->start()
-> NimBLEServer::resetGATT()
-> ble_gatts_reset()
-> ble_svc_gap_init()
-> ESP-IDF NimBLE resets GAP name to CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME
-> "nimble"
-> ble_svc_gatt_init()

Fix this by saving the device name supplied through NimBLEDevice::setDeviceName() and reapplying it immediately after ble_svc_gap_init() in NimBLEServer::resetGATT(). This preserves the C++ wrapper contract that the configured device name remains effective even when the wrapper internally resets the GATT/GAP service tables.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed BLE device name preservation during GATT resets, ensuring the device name persists correctly across system operations.

ESP-IDF NimBLE can reset the GAP Device Name when the standard GAP service is reinitialized during GATT server startup. NimBLEDevice::init(deviceName) calls NimBLEDevice::setDeviceName(deviceName), but that happens before the server is started. Later, starting advertising starts the server and resets/re-registers the GAP service, causing the C GAP name backing storage to be rebuilt from CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME. In projects where that Kconfig default remains "nimble", the advertised Local Name can be correct while the Generic Access Device Name characteristic reports "nimble".

The failing sequence is:

  -> pAdvertising->start()
     -> NimBLEAdvertising::start()
        -> NimBLEDevice::getServer()->start()
           -> NimBLEServer::resetGATT()
              -> ble_gatts_reset()
              -> ble_svc_gap_init()
                 -> ESP-IDF NimBLE resets GAP name to CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME
                 -> "nimble"
              -> ble_svc_gatt_init()

Fix this by saving the device name supplied through NimBLEDevice::setDeviceName() and reapplying it immediately after ble_svc_gap_init() in NimBLEServer::resetGATT(). This preserves the C++ wrapper contract that the configured device name remains effective even when the wrapper internally resets the GATT/GAP service tables.
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

NimBLEDevice gains two new private static members (m_deviceName and m_deviceNameSet). setDeviceName caches the provided name after a successful GAP update. NimBLEServer::resetGATT now reapplies the cached device name before GATT/GAP initialization and returns false if that reapplication fails.

Changes

Device Name Persistence Across GATT Reset

Layer / File(s) Summary
Device name static state and caching in setDeviceName
src/NimBLEDevice.h, src/NimBLEDevice.cpp
Declares and defines m_deviceName (std::string) and m_deviceNameSet (bool) as private static members; setDeviceName assigns both after a successful GAP name update.
resetGATT reapplies cached device name
src/NimBLEServer.cpp
resetGATT() checks m_deviceNameSet and calls setDeviceName(m_deviceName) before GATT/GAP init, aborting with false if the call fails.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~5 minutes

Poem

Arr, the device name sailed off into the void,
When GATT reset struck — the crew was annoyed!
Now m_deviceName holds fast to the mast,
And resetGATT restores it, steady and vast!
Yarrr, no name be lost on this BLE sea! 🏴‍☠️

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Restore GAP device name after GATT reset' accurately and concisely summarizes the main change: preserving the device name during GATT reinitialization.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/NimBLEServer.cpp`:
- Around line 894-896: The static members NimBLEDevice::m_deviceName and
NimBLEDevice::m_deviceNameSet are accessed without synchronization in the
resetGATT() method, creating a potential race condition if another thread calls
setDeviceName() concurrently. To fix this, add a static mutex member (like
m_deviceNameMutex) to the NimBLEDevice class, then protect all accesses to
m_deviceName and m_deviceNameSet with std::lock_guard in both the
setDeviceName() method and the resetGATT() method. In resetGATT(), acquire the
lock, cache the values of m_deviceNameSet and m_deviceName into local variables,
release the lock, then perform the conditional call to setDeviceName() with the
cached name outside the critical section.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a7ecf420-5f09-4fa4-8875-aea742681393

📥 Commits

Reviewing files that changed from the base of the PR and between 1eddc28 and 4063b55.

📒 Files selected for processing (3)
  • src/NimBLEDevice.cpp
  • src/NimBLEDevice.h
  • src/NimBLEServer.cpp

Comment thread src/NimBLEServer.cpp
Comment on lines +894 to +896
if (NimBLEDevice::m_deviceNameSet && !NimBLEDevice::setDeviceName(NimBLEDevice::m_deviceName)) {
return false;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

Shiver me timbers! A potential race condition be lurkin' in these waters!

Arr matey, while yer logic fer reapplyin' the cached device name be sound as a Spanish doubloon, there be a concurrency concern that could make yer ship run aground:

The static members NimBLEDevice::m_deviceName and NimBLEDevice::m_deviceNameSet be accessed here without any synchronization. If some scallywag calls NimBLEDevice::setDeviceName() from another thread whilst this resetGATT() be executin', ye could have yerself a classic race condition! One thread be writin' to m_deviceName while another be readin' from it - that be a recipe fer trouble on the high seas.

Now, in most BLE applications, these operations be serialized by the nature o' the code flow. But from a library design standpoint, there be no enforcement o' this serialization, savvy?

🏴‍☠️ Possible solutions fer this concurrency concern

Option 1: Document that BLE operations must be serialized by the application (low effort, shifts responsibility to user)

Option 2: Add a mutex to protect access to the static members (higher effort, makes library thread-safe by design)

// In NimBLEDevice.h, add a static mutex
static std::mutex m_deviceNameMutex;

// In setDeviceName:
{
    std::lock_guard<std::mutex> lock(m_deviceNameMutex);
    m_deviceName = deviceName;
    m_deviceNameSet = true;
}

// In resetGATT:
std::string cachedName;
bool nameWasSet;
{
    std::lock_guard<std::mutex> lock(NimBLEDevice::m_deviceNameMutex);
    cachedName = NimBLEDevice::m_deviceName;
    nameWasSet = NimBLEDevice::m_deviceNameSet;
}
if (nameWasSet && !NimBLEDevice::setDeviceName(cachedName)) {
    return false;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/NimBLEServer.cpp` around lines 894 - 896, The static members
NimBLEDevice::m_deviceName and NimBLEDevice::m_deviceNameSet are accessed
without synchronization in the resetGATT() method, creating a potential race
condition if another thread calls setDeviceName() concurrently. To fix this, add
a static mutex member (like m_deviceNameMutex) to the NimBLEDevice class, then
protect all accesses to m_deviceName and m_deviceNameSet with std::lock_guard in
both the setDeviceName() method and the resetGATT() method. In resetGATT(),
acquire the lock, cache the values of m_deviceNameSet and m_deviceName into
local variables, release the lock, then perform the conditional call to
setDeviceName() with the cached name outside the critical section.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant