Retry required for GetGattServicesAsync() when connecting to BLE peripheral using LE privacy.

Garin Marlow 0 Reputation points
2025-06-02T18:40:24.9566667+00:00

When getting GATT services from a paired peripheral using LE privacy, the first call to GetGattServicesAsync() always times out after 7 seconds with a status of 'Unreachable'. Our current workarounds we've identified are to (1) scan for a device advertisement before starting the connection process and calling GetGattServicesAsync() or (2) add a short delay before calling GetGattServicesAsync(). Neither workaround is ideal, and we would like to know if there is a particular event available at the API level that would indicate we can proceed to get GATT services.

Doing a little debugging, the Windows Event Viewer seems to indicate during these GetGattServicesAsync() retries Windows is initially trying to connect to the peripheral using the peripherals random address. The RPA is not resolved until the following GetGattServicesAsync() attempt is made.

Example of the Windows Events logged during the two calls to GetGattServicesAsync():

Windows Event after first GetGattServicesAsync() attempt: An attempt to connect to a remote device 0x7C48E6E75BB2 failed.

Windows Event after second GetGattServicesAsync() attempt: A connection to a remote device 0x49778D4D2D0A was successfully established.

According to logs of the Windows Bluetooth Stack, captured using this tool when this issue occurs and analyzed in the Windows Performance Analyzer (see screenshot below), it seems like the critical event to await is an internal EFW event called BthIRKResolution but that event is not available at the API level. Assuming BthIRKResolution relates to a background Windows task to resolve the random address of the peripheral using the IRK exchanged during pairing, then what event at the API level would indicate Windows has resolved the RPA of a peripheral that we can await before GetGattServicesAsync() can be successfully called?

image.png

For reference, here is the C# code we have as a workaround to this behavior with some additional context of what we believe is going on in the Windows background.
User's image

Developer technologies | C#
Developer technologies | C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Surya Amrutha Vaishnavi Lanka (INFOSYS LIMITED) 1,115 Reputation points Microsoft External Staff
    2025-11-17T12:26:13.2566667+00:00

    Thanks for reaching out!

    1. Use GetGattServicesAsync() with retry logic:
      • Call GetGattServicesAsync() once.
      • If the status is not GattCommunicationStatus.Success, wait briefly (e.g., 500–1000 ms) and retry.
      • Limit retries to avoid infinite loops.
    2. sample code to check
         private async Task<bool> TryGetGattServicesAsync(BluetoothLEDevice bleDevice, int maxRetries = 3, int delayMs = 1000)
         {
             for (int attempt = 1; attempt <= maxRetries; attempt++)
             {
                 var gattResult = await bleDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached);
                 
                 if (gattResult.Status == GattCommunicationStatus.Success)
                 {
                     _logger.LogInformation($"GATT services retrieved successfully on attempt {attempt}.");
                     return true;
                 }
         
                 _logger.LogWarning($"Attempt {attempt} failed with status: {gattResult.Status}. Retrying...");
                 await Task.Delay(delayMs);
             }
         
             _logger.LogError("Failed to retrieve GATT services after retries.");
             return false;
         }
      

    Additional points can be checked

    1. Avoid scanning before connection unless necessary; it adds overhead.
    2. Use BluetoothCacheMode.Uncached to ensure fresh data.
    3. Monitor connection status via ConnectionStatusChanged event on BluetoothLEDevice if possible.

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.