From 96eebeeffd7f12b2b31ca5ad855a7b322c75d528 Mon Sep 17 00:00:00 2001 From: Mikael Falkvidd Date: Thu, 7 May 2026 15:45:42 +0200 Subject: [PATCH 1/5] ble: start service discovery after encryption Delay BLE service discovery until the link is encrypted instead of starting it immediately after connection. Some phones appear to reject or ignore ANCS setup while pairing/security is still in progress, leaving notifications stuck until Bluetooth is toggled on the phone. Also make ANCS descriptor setup more robust by discovering CCCDs one at a time, using each characteristic's actual handle range, subscribing sequentially, and finishing discovery only once subscription state is known. --- .../ble/AppleNotificationCenterClient.cpp | 176 +++++++++++++----- .../ble/AppleNotificationCenterClient.h | 13 ++ src/components/ble/NimbleController.cpp | 5 +- src/systemtask/Messages.h | 1 + src/systemtask/SystemTask.cpp | 14 +- src/systemtask/SystemTask.h | 1 + 6 files changed, 159 insertions(+), 51 deletions(-) diff --git a/src/components/ble/AppleNotificationCenterClient.cpp b/src/components/ble/AppleNotificationCenterClient.cpp index f7e200e9ce..65b54735c3 100644 --- a/src/components/ble/AppleNotificationCenterClient.cpp +++ b/src/components/ble/AppleNotificationCenterClient.cpp @@ -75,16 +75,20 @@ int AppleNotificationCenterClient::OnCharacteristicsDiscoveryEvent(uint16_t conn if (characteristic == nullptr && error->status == BLE_HS_EDONE) { NRF_LOG_INFO("ANCS Characteristic discovery complete"); - if (isCharacteristicDiscovered) { - ble_gattc_disc_all_dscs(connectionHandle, notificationSourceHandle, ancsEndHandle, OnANCSDescriptorDiscoveryEventCallback, this); - } - if (isDataCharacteristicDiscovered) { - ble_gattc_disc_all_dscs(connectionHandle, dataSourceHandle, ancsEndHandle, OnANCSDescriptorDiscoveryEventCallback, this); + if (lastCharacteristicValueHandle != 0) { + AssignCharacteristicEndHandle(lastCharacteristicValueHandle, ancsEndHandle); + lastCharacteristicValueHandle = 0; } - if (isCharacteristicDiscovered == isControlCharacteristicDiscovered && isCharacteristicDiscovered == isDataCharacteristicDiscovered) { + if (isCharacteristicDiscovered) { + StartNextDescriptorDiscovery(connectionHandle); + } else { MaybeFinishDiscovery(connectionHandle); } } else if (characteristic != nullptr) { + if (lastCharacteristicValueHandle != 0) { + AssignCharacteristicEndHandle(lastCharacteristicValueHandle, characteristic->def_handle - 1); + } + if (ble_uuid_cmp(¬ificationSourceChar.u, &characteristic->uuid.u) == 0) { NRF_LOG_INFO("ANCS Characteristic discovered: Notification Source"); notificationSourceHandle = characteristic->val_handle; @@ -98,6 +102,7 @@ int AppleNotificationCenterClient::OnCharacteristicsDiscoveryEvent(uint16_t conn dataSourceHandle = characteristic->val_handle; isDataCharacteristicDiscovered = true; } + lastCharacteristicValueHandle = characteristic->val_handle; } return 0; } @@ -106,40 +111,40 @@ int AppleNotificationCenterClient::OnDescriptorDiscoveryEventCallback(uint16_t c const ble_gatt_error* error, uint16_t characteristicValueHandle, const ble_gatt_dsc* descriptor) { - if (error->status == 0) { - if (characteristicValueHandle == notificationSourceHandle && ble_uuid_cmp(¬ificationSourceChar.u, &descriptor->uuid.u)) { - if (notificationSourceDescriptorHandle == 0) { - NRF_LOG_INFO("ANCS Descriptor discovered : %d", descriptor->handle); - notificationSourceDescriptorHandle = descriptor->handle; - isDescriptorFound = true; - uint8_t value[2] {1, 0}; - ble_gattc_write_flat(connectionHandle, notificationSourceDescriptorHandle, value, sizeof(value), NewAlertSubcribeCallback, this); - ble_gattc_write_flat(connectionHandle, ancsEndHandle, value, sizeof(value), NewAlertSubcribeCallback, this); - } - } else if (characteristicValueHandle == controlPointHandle && ble_uuid_cmp(&controlPointChar.u, &descriptor->uuid.u)) { - if (controlPointDescriptorHandle == 0) { - NRF_LOG_INFO("ANCS Descriptor discovered : %d", descriptor->handle); - controlPointDescriptorHandle = descriptor->handle; - isControlDescriptorFound = true; - } - } else if (characteristicValueHandle == dataSourceHandle && ble_uuid_cmp(&dataSourceChar.u, &descriptor->uuid.u)) { - if (dataSourceDescriptorHandle == 0) { - NRF_LOG_INFO("ANCS Descriptor discovered : %d", descriptor->handle); - dataSourceDescriptorHandle = descriptor->handle; - isDataDescriptorFound = true; - uint8_t value[2] {1, 0}; - ble_gattc_write_flat(connectionHandle, dataSourceDescriptorHandle, value, sizeof(value), NewAlertSubcribeCallback, this); - } + if (error->status == 0 && descriptor != nullptr) { + if (ble_uuid_cmp(&clientCharacteristicConfigDescriptorUuid.u, &descriptor->uuid.u) != 0) { + return 0; } - } else { - if (error->status != BLE_HS_EDONE) { - char errorStr[55]; - snprintf(errorStr, sizeof(errorStr), "ANCS Descriptor discovery ERROR: %d", error->status); - NRF_LOG_INFO(errorStr); + + if (characteristicValueHandle == notificationSourceHandle && notificationSourceDescriptorHandle == 0) { + NRF_LOG_INFO("ANCS Notification Source CCCD discovered : %d", descriptor->handle); + notificationSourceDescriptorHandle = descriptor->handle; + isDescriptorFound = true; + } else if (characteristicValueHandle == dataSourceHandle && dataSourceDescriptorHandle == 0) { + NRF_LOG_INFO("ANCS Data Source CCCD discovered : %d", descriptor->handle); + dataSourceDescriptorHandle = descriptor->handle; + isDataDescriptorFound = true; } - if (isDescriptorFound == isDataDescriptorFound) - MaybeFinishDiscovery(connectionHandle); + return 0; } + + if (error->status != BLE_HS_EDONE) { + char errorStr[55]; + snprintf(errorStr, sizeof(errorStr), "ANCS Descriptor discovery ERROR: %d", error->status); + NRF_LOG_INFO(errorStr); + } + + if (characteristicValueHandle == notificationSourceHandle && notificationSourceDescriptorHandle != 0 && !isNotificationSourceSubscribed) { + SubscribeToDescriptor(connectionHandle, notificationSourceDescriptorHandle); + return 0; + } + + if (characteristicValueHandle == dataSourceHandle && dataSourceDescriptorHandle != 0 && !isDataSourceSubscribed) { + SubscribeToDescriptor(connectionHandle, dataSourceDescriptorHandle); + return 0; + } + + StartNextDescriptorDiscovery(connectionHandle); return 0; } @@ -147,17 +152,22 @@ int AppleNotificationCenterClient::OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* /*attribute*/) { if (error->status == 0) { - NRF_LOG_INFO("ANCS New alert subscribe OK"); - - // Mark subscriptions complete only after both CCCDs are known - if (notificationSourceDescriptorHandle != 0 && dataSourceDescriptorHandle != 0) { - subscriptionsDone = true; + if (pendingSubscriptionDescriptorHandle == notificationSourceDescriptorHandle) { + NRF_LOG_INFO("ANCS Notification Source subscribe OK"); + isNotificationSourceSubscribed = true; + } else if (pendingSubscriptionDescriptorHandle == dataSourceDescriptorHandle) { + NRF_LOG_INFO("ANCS Data Source subscribe OK"); + isDataSourceSubscribed = true; + } else { + NRF_LOG_INFO("ANCS subscribe OK"); } } else { NRF_LOG_INFO("ANCS New alert subscribe ERROR"); } - if (isDescriptorFound == isControlDescriptorFound && isDescriptorFound == isDataDescriptorFound) - MaybeFinishDiscovery(connectionHandle); + + pendingSubscriptionDescriptorHandle = 0; + subscriptionsDone = isNotificationSourceSubscribed && (!isDataCharacteristicDiscovered || isDataSourceSubscribed); + StartNextDescriptorDiscovery(connectionHandle); return 0; } @@ -176,12 +186,73 @@ int AppleNotificationCenterClient::OnControlPointWrite(uint16_t /*connectionHand } void AppleNotificationCenterClient::MaybeFinishDiscovery(uint16_t connectionHandle) { - if (isCharacteristicDiscovered && isControlCharacteristicDiscovered && isDataCharacteristicDiscovered && isDescriptorFound && - isControlDescriptorFound && isDataDescriptorFound && subscriptionsDone) { + if (discoveryCompleteNotified) { + return; + } + + if (!isDiscovered || !isCharacteristicDiscovered) { + discoveryCompleteNotified = true; + onServiceDiscovered(connectionHandle); + return; + } + + subscriptionsDone = isNotificationSourceSubscribed && (!isDataCharacteristicDiscovered || isDataSourceSubscribed); + const bool descriptorDiscoveryDone = + isNotificationSourceDescriptorDiscoveryComplete && (!isDataCharacteristicDiscovered || isDataSourceDescriptorDiscoveryComplete); + if (subscriptionsDone || descriptorDiscoveryDone) { + discoveryCompleteNotified = true; onServiceDiscovered(connectionHandle); } } +void AppleNotificationCenterClient::StartNextDescriptorDiscovery(uint16_t connectionHandle) { + if (pendingSubscriptionDescriptorHandle != 0) { + return; + } + + if (isCharacteristicDiscovered && !isNotificationSourceDescriptorDiscoveryComplete) { + isNotificationSourceDescriptorDiscoveryComplete = true; + const uint16_t endHandle = notificationSourceEndHandle != 0 ? notificationSourceEndHandle : ancsEndHandle; + int rc = + ble_gattc_disc_all_dscs(connectionHandle, notificationSourceHandle, endHandle, OnANCSDescriptorDiscoveryEventCallback, this); + if (rc == 0) { + return; + } + NRF_LOG_INFO("ANCS Notification Source descriptor discovery start ERROR: %d", rc); + } + + if (isDataCharacteristicDiscovered && !isDataSourceDescriptorDiscoveryComplete) { + isDataSourceDescriptorDiscoveryComplete = true; + const uint16_t endHandle = dataSourceEndHandle != 0 ? dataSourceEndHandle : ancsEndHandle; + int rc = ble_gattc_disc_all_dscs(connectionHandle, dataSourceHandle, endHandle, OnANCSDescriptorDiscoveryEventCallback, this); + if (rc == 0) { + return; + } + NRF_LOG_INFO("ANCS Data Source descriptor discovery start ERROR: %d", rc); + } + + MaybeFinishDiscovery(connectionHandle); +} + +void AppleNotificationCenterClient::SubscribeToDescriptor(uint16_t connectionHandle, uint16_t descriptorHandle) { + uint8_t value[2] {1, 0}; + pendingSubscriptionDescriptorHandle = descriptorHandle; + int rc = ble_gattc_write_flat(connectionHandle, descriptorHandle, value, sizeof(value), NewAlertSubcribeCallback, this); + if (rc != 0) { + NRF_LOG_INFO("ANCS subscribe start ERROR: %d", rc); + pendingSubscriptionDescriptorHandle = 0; + StartNextDescriptorDiscovery(connectionHandle); + } +} + +void AppleNotificationCenterClient::AssignCharacteristicEndHandle(uint16_t characteristicValueHandle, uint16_t endHandle) { + if (characteristicValueHandle == notificationSourceHandle) { + notificationSourceEndHandle = endHandle; + } else if (characteristicValueHandle == dataSourceHandle) { + dataSourceEndHandle = endHandle; + } +} + void AppleNotificationCenterClient::OnNotification(ble_gap_event* event) { if (event->notify_rx.attr_handle == notificationSourceHandle || event->notify_rx.attr_handle == notificationSourceDescriptorHandle) { NRF_LOG_INFO("ANCS Notification received"); @@ -236,6 +307,10 @@ void AppleNotificationCenterClient::OnNotification(ble_gap_event* event) { // } // Request ANCS more info + if (controlPointHandle == 0 || dataSourceHandle == 0) { + return; + } + // The +4 is for the "..." at the end of the string uint8_t titleSize = maxTitleSize + 4; uint8_t subTitleSize = maxSubtitleSize + 4; @@ -439,11 +514,15 @@ void AppleNotificationCenterClient::Reset() { serviceChangedHandle = 0; serviceChangedDescriptorHandle = 0; notificationSourceHandle = 0; + notificationSourceEndHandle = 0; notificationSourceDescriptorHandle = 0; controlPointHandle = 0; controlPointDescriptorHandle = 0; dataSourceHandle = 0; + dataSourceEndHandle = 0; dataSourceDescriptorHandle = 0; + pendingSubscriptionDescriptorHandle = 0; + lastCharacteristicValueHandle = 0; isGattDiscovered = false; isGattCharacteristicDiscovered = false; isGattDescriptorFound = false; @@ -455,6 +534,11 @@ void AppleNotificationCenterClient::Reset() { isDataCharacteristicDiscovered = false; isDataDescriptorFound = false; subscriptionsDone = false; + discoveryCompleteNotified = false; + isNotificationSourceDescriptorDiscoveryComplete = false; + isDataSourceDescriptorDiscoveryComplete = false; + isNotificationSourceSubscribed = false; + isDataSourceSubscribed = false; notifications.clear(); } diff --git a/src/components/ble/AppleNotificationCenterClient.h b/src/components/ble/AppleNotificationCenterClient.h index 43c0d20e18..43ece3a58b 100644 --- a/src/components/ble/AppleNotificationCenterClient.h +++ b/src/components/ble/AppleNotificationCenterClient.h @@ -70,6 +70,7 @@ namespace Pinetime { static constexpr ble_uuid16_t gattServiceUuid = {BLE_UUID_TYPE_16, 0x1801}; static constexpr ble_uuid16_t serviceChangedCharUuid = {BLE_UUID_TYPE_16, 0x2A05}; + static constexpr ble_uuid16_t clientCharacteristicConfigDescriptorUuid = {BLE_UUID_TYPE_16, BLE_GATT_DSC_CLT_CFG_UUID16}; enum class Categories : uint8_t { Other = 0, @@ -107,16 +108,28 @@ namespace Pinetime { std::unordered_map notifications; std::string DecodeUtf8String(os_mbuf* om, uint16_t size, uint16_t offset); + void StartNextDescriptorDiscovery(uint16_t connectionHandle); + void SubscribeToDescriptor(uint16_t connectionHandle, uint16_t descriptorHandle); + void AssignCharacteristicEndHandle(uint16_t characteristicValueHandle, uint16_t endHandle); bool subscriptionsDone = false; + bool discoveryCompleteNotified = false; + bool isNotificationSourceDescriptorDiscoveryComplete = false; + bool isDataSourceDescriptorDiscoveryComplete = false; + bool isNotificationSourceSubscribed = false; + bool isDataSourceSubscribed = false; uint16_t ancsStartHandle {0}; uint16_t ancsEndHandle {0}; uint16_t notificationSourceHandle {0}; + uint16_t notificationSourceEndHandle {0}; uint16_t controlPointHandle {0}; uint16_t dataSourceHandle {0}; + uint16_t dataSourceEndHandle {0}; uint16_t notificationSourceDescriptorHandle {0}; uint16_t controlPointDescriptorHandle {0}; uint16_t dataSourceDescriptorHandle {0}; + uint16_t pendingSubscriptionDescriptorHandle {0}; + uint16_t lastCharacteristicValueHandle {0}; uint16_t gattStartHandle {0}; uint16_t gattEndHandle {0}; diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index b40fe7a3c0..dcf9fac0a0 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -211,7 +211,6 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { connectionHandle = event->connect.conn_handle; bleController.Connect(); systemTask.PushMessage(Pinetime::System::Messages::BleConnected); - // Service discovery is deferred via systemtask ble_gap_security_initiate(event->connect.conn_handle); } break; @@ -269,6 +268,10 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { desc.sec_state.authenticated, desc.sec_state.bonded, desc.sec_state.key_size); + + if (desc.sec_state.encrypted) { + systemTask.PushMessage(Pinetime::System::Messages::BleStartDiscovery); + } } break; diff --git a/src/systemtask/Messages.h b/src/systemtask/Messages.h index fee94bb747..7945fb7d1b 100644 --- a/src/systemtask/Messages.h +++ b/src/systemtask/Messages.h @@ -24,6 +24,7 @@ namespace Pinetime { OnNewHalfHour, OnChargingEvent, OnPairing, + BleStartDiscovery, SetOffAlarm, MeasureBatteryTimerExpired, BatteryPercentageUpdated, diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 56bf9273e1..8f68c026c3 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -237,8 +237,14 @@ void SystemTask::Work() { break; case Messages::BleConnected: displayApp.PushMessage(Pinetime::Applications::Display::Messages::NotifyDeviceActivity); - isBleDiscoveryTimerRunning = true; - bleDiscoveryTimer = 5; + isBleDiscoveryStarted = false; + isBleDiscoveryTimerRunning = false; + break; + case Messages::BleStartDiscovery: + if (!isBleDiscoveryStarted) { + isBleDiscoveryTimerRunning = true; + bleDiscoveryTimer = 1; + } break; case Messages::BleFirmwareUpdateStarted: GoToRunning(); @@ -383,8 +389,8 @@ void SystemTask::Work() { if (isBleDiscoveryTimerRunning) { if (bleDiscoveryTimer == 0) { isBleDiscoveryTimerRunning = false; - // Services discovery is deferred from 3 seconds to avoid the conflicts between the host communicating with the - // target and vice-versa. I'm not sure if this is the right way to handle this... + isBleDiscoveryStarted = true; + // Service discovery is started after link security is established. Keep a short defer so the security callback can unwind. nimbleController.StartDiscovery(); } else { bleDiscoveryTimer--; diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index 606ddd3492..2a543ecc85 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -131,6 +131,7 @@ namespace Pinetime { static void Process(void* instance); void Work(); bool isBleDiscoveryTimerRunning = false; + bool isBleDiscoveryStarted = false; uint8_t bleDiscoveryTimer = 0; TimerHandle_t measureBatteryTimer; uint8_t wakeLocksHeld = 0; From 85632526d85a121c364f25a99e7b84ef7b7f4dec Mon Sep 17 00:00:00 2001 From: Mikael Falkvidd Date: Thu, 7 May 2026 15:53:53 +0200 Subject: [PATCH 2/5] ble: defer ANCS discovery until link encryption Start BLE service discovery only after the connection has reached an encrypted state instead of using the generic BleConnected delay. iOS only exposes and authorizes ANCS after pairing/security is complete, so the old timer-based path could race the pairing flow and miss ANCS entirely. In that case the notification permission dialog would not appear until a later Bluetooth reconnect. Also tighten the ANCS setup path so descriptor discovery and CCCD writes are serialized and scoped to the actual Notification Source and Data Source characteristics. This removes the bogus write to the ANCS service end handle and avoids treating the Control Point as a subscribable characteristic. Finally, fix the advertising data to describe ANCS correctly. InfiniTime is an ANCS client, not an ANCS server, so the ANCS UUID should not be advertised as a local service. Keep DFU in the local 128-bit service UUID list and place ANCS in the scan response as a 128-bit Service Solicitation UUID. This keeps the legacy advertising payload within size limits and should make LE scanners such as bluetoothctl behave more reliably. --- src/components/ble/NimbleController.cpp | 27 ++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index dcf9fac0a0..27e437aab7 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -140,11 +140,9 @@ void NimbleController::Init() { void NimbleController::StartAdvertising() { struct ble_gap_adv_params adv_params; struct ble_hs_adv_fields fields; - struct ble_hs_adv_fields rsp_fields; memset(&adv_params, 0, sizeof(adv_params)); memset(&fields, 0, sizeof(fields)); - memset(&rsp_fields, 0, sizeof(rsp_fields)); adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; @@ -162,21 +160,32 @@ void NimbleController::StartAdvertising() { fields.uuids16 = &HeartRateService::heartRateServiceUuid; fields.num_uuids16 = 1; fields.uuids16_is_complete = 1; - const ble_uuid128_t uuids128[2] = {DfuService::serviceUuid, AppleNotificationCenterClient::ancsUuid}; - fields.uuids128 = uuids128; - fields.num_uuids128 = 2; + fields.uuids128 = &DfuService::serviceUuid; + fields.num_uuids128 = 1; fields.uuids128_is_complete = 1; fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; - rsp_fields.name = reinterpret_cast(deviceName); - rsp_fields.name_len = strlen(deviceName); - rsp_fields.name_is_complete = 1; + const auto deviceNameLength = strlen(deviceName); + const auto* deviceNameBytes = reinterpret_cast(deviceName); + uint8_t scanResponseData[31] {}; + size_t scanResponseDataLength = 0; + + scanResponseData[scanResponseDataLength++] = static_cast(deviceNameLength + 1); + scanResponseData[scanResponseDataLength++] = BLE_HS_ADV_TYPE_COMP_NAME; + memcpy(&scanResponseData[scanResponseDataLength], deviceNameBytes, deviceNameLength); + scanResponseDataLength += deviceNameLength; + + // ANCS is provided by iOS; advertise it as a solicited service instead of a local service UUID. + scanResponseData[scanResponseDataLength++] = 17; + scanResponseData[scanResponseDataLength++] = BLE_HS_ADV_TYPE_SOL_UUIDS128; + memcpy(&scanResponseData[scanResponseDataLength], AppleNotificationCenterClient::ancsUuid.value, sizeof(AppleNotificationCenterClient::ancsUuid.value)); + scanResponseDataLength += sizeof(AppleNotificationCenterClient::ancsUuid.value); int rc; rc = ble_gap_adv_set_fields(&fields); ASSERT(rc == 0); - rc = ble_gap_adv_rsp_set_fields(&rsp_fields); + rc = ble_gap_adv_rsp_set_data(scanResponseData, scanResponseDataLength); ASSERT(rc == 0); rc = ble_gap_adv_start(addrType, NULL, 2000, &adv_params, GAPEventCallback, this); From bd796e1ee77881c25a3dab1dc022ed654b5685e1 Mon Sep 17 00:00:00 2001 From: Mikael Falkvidd Date: Sat, 16 May 2026 22:35:26 +0200 Subject: [PATCH 3/5] Revert "ble: start service discovery after encryption" This reverts commit 96eebeeffd7f12b2b31ca5ad855a7b322c75d528. --- .../ble/AppleNotificationCenterClient.cpp | 176 +++++------------- .../ble/AppleNotificationCenterClient.h | 13 -- src/components/ble/NimbleController.cpp | 5 +- src/systemtask/Messages.h | 1 - src/systemtask/SystemTask.cpp | 14 +- src/systemtask/SystemTask.h | 1 - 6 files changed, 51 insertions(+), 159 deletions(-) diff --git a/src/components/ble/AppleNotificationCenterClient.cpp b/src/components/ble/AppleNotificationCenterClient.cpp index 65b54735c3..f7e200e9ce 100644 --- a/src/components/ble/AppleNotificationCenterClient.cpp +++ b/src/components/ble/AppleNotificationCenterClient.cpp @@ -75,20 +75,16 @@ int AppleNotificationCenterClient::OnCharacteristicsDiscoveryEvent(uint16_t conn if (characteristic == nullptr && error->status == BLE_HS_EDONE) { NRF_LOG_INFO("ANCS Characteristic discovery complete"); - if (lastCharacteristicValueHandle != 0) { - AssignCharacteristicEndHandle(lastCharacteristicValueHandle, ancsEndHandle); - lastCharacteristicValueHandle = 0; - } if (isCharacteristicDiscovered) { - StartNextDescriptorDiscovery(connectionHandle); - } else { + ble_gattc_disc_all_dscs(connectionHandle, notificationSourceHandle, ancsEndHandle, OnANCSDescriptorDiscoveryEventCallback, this); + } + if (isDataCharacteristicDiscovered) { + ble_gattc_disc_all_dscs(connectionHandle, dataSourceHandle, ancsEndHandle, OnANCSDescriptorDiscoveryEventCallback, this); + } + if (isCharacteristicDiscovered == isControlCharacteristicDiscovered && isCharacteristicDiscovered == isDataCharacteristicDiscovered) { MaybeFinishDiscovery(connectionHandle); } } else if (characteristic != nullptr) { - if (lastCharacteristicValueHandle != 0) { - AssignCharacteristicEndHandle(lastCharacteristicValueHandle, characteristic->def_handle - 1); - } - if (ble_uuid_cmp(¬ificationSourceChar.u, &characteristic->uuid.u) == 0) { NRF_LOG_INFO("ANCS Characteristic discovered: Notification Source"); notificationSourceHandle = characteristic->val_handle; @@ -102,7 +98,6 @@ int AppleNotificationCenterClient::OnCharacteristicsDiscoveryEvent(uint16_t conn dataSourceHandle = characteristic->val_handle; isDataCharacteristicDiscovered = true; } - lastCharacteristicValueHandle = characteristic->val_handle; } return 0; } @@ -111,40 +106,40 @@ int AppleNotificationCenterClient::OnDescriptorDiscoveryEventCallback(uint16_t c const ble_gatt_error* error, uint16_t characteristicValueHandle, const ble_gatt_dsc* descriptor) { - if (error->status == 0 && descriptor != nullptr) { - if (ble_uuid_cmp(&clientCharacteristicConfigDescriptorUuid.u, &descriptor->uuid.u) != 0) { - return 0; + if (error->status == 0) { + if (characteristicValueHandle == notificationSourceHandle && ble_uuid_cmp(¬ificationSourceChar.u, &descriptor->uuid.u)) { + if (notificationSourceDescriptorHandle == 0) { + NRF_LOG_INFO("ANCS Descriptor discovered : %d", descriptor->handle); + notificationSourceDescriptorHandle = descriptor->handle; + isDescriptorFound = true; + uint8_t value[2] {1, 0}; + ble_gattc_write_flat(connectionHandle, notificationSourceDescriptorHandle, value, sizeof(value), NewAlertSubcribeCallback, this); + ble_gattc_write_flat(connectionHandle, ancsEndHandle, value, sizeof(value), NewAlertSubcribeCallback, this); + } + } else if (characteristicValueHandle == controlPointHandle && ble_uuid_cmp(&controlPointChar.u, &descriptor->uuid.u)) { + if (controlPointDescriptorHandle == 0) { + NRF_LOG_INFO("ANCS Descriptor discovered : %d", descriptor->handle); + controlPointDescriptorHandle = descriptor->handle; + isControlDescriptorFound = true; + } + } else if (characteristicValueHandle == dataSourceHandle && ble_uuid_cmp(&dataSourceChar.u, &descriptor->uuid.u)) { + if (dataSourceDescriptorHandle == 0) { + NRF_LOG_INFO("ANCS Descriptor discovered : %d", descriptor->handle); + dataSourceDescriptorHandle = descriptor->handle; + isDataDescriptorFound = true; + uint8_t value[2] {1, 0}; + ble_gattc_write_flat(connectionHandle, dataSourceDescriptorHandle, value, sizeof(value), NewAlertSubcribeCallback, this); + } } - - if (characteristicValueHandle == notificationSourceHandle && notificationSourceDescriptorHandle == 0) { - NRF_LOG_INFO("ANCS Notification Source CCCD discovered : %d", descriptor->handle); - notificationSourceDescriptorHandle = descriptor->handle; - isDescriptorFound = true; - } else if (characteristicValueHandle == dataSourceHandle && dataSourceDescriptorHandle == 0) { - NRF_LOG_INFO("ANCS Data Source CCCD discovered : %d", descriptor->handle); - dataSourceDescriptorHandle = descriptor->handle; - isDataDescriptorFound = true; + } else { + if (error->status != BLE_HS_EDONE) { + char errorStr[55]; + snprintf(errorStr, sizeof(errorStr), "ANCS Descriptor discovery ERROR: %d", error->status); + NRF_LOG_INFO(errorStr); } - return 0; - } - - if (error->status != BLE_HS_EDONE) { - char errorStr[55]; - snprintf(errorStr, sizeof(errorStr), "ANCS Descriptor discovery ERROR: %d", error->status); - NRF_LOG_INFO(errorStr); - } - - if (characteristicValueHandle == notificationSourceHandle && notificationSourceDescriptorHandle != 0 && !isNotificationSourceSubscribed) { - SubscribeToDescriptor(connectionHandle, notificationSourceDescriptorHandle); - return 0; - } - - if (characteristicValueHandle == dataSourceHandle && dataSourceDescriptorHandle != 0 && !isDataSourceSubscribed) { - SubscribeToDescriptor(connectionHandle, dataSourceDescriptorHandle); - return 0; + if (isDescriptorFound == isDataDescriptorFound) + MaybeFinishDiscovery(connectionHandle); } - - StartNextDescriptorDiscovery(connectionHandle); return 0; } @@ -152,22 +147,17 @@ int AppleNotificationCenterClient::OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* /*attribute*/) { if (error->status == 0) { - if (pendingSubscriptionDescriptorHandle == notificationSourceDescriptorHandle) { - NRF_LOG_INFO("ANCS Notification Source subscribe OK"); - isNotificationSourceSubscribed = true; - } else if (pendingSubscriptionDescriptorHandle == dataSourceDescriptorHandle) { - NRF_LOG_INFO("ANCS Data Source subscribe OK"); - isDataSourceSubscribed = true; - } else { - NRF_LOG_INFO("ANCS subscribe OK"); + NRF_LOG_INFO("ANCS New alert subscribe OK"); + + // Mark subscriptions complete only after both CCCDs are known + if (notificationSourceDescriptorHandle != 0 && dataSourceDescriptorHandle != 0) { + subscriptionsDone = true; } } else { NRF_LOG_INFO("ANCS New alert subscribe ERROR"); } - - pendingSubscriptionDescriptorHandle = 0; - subscriptionsDone = isNotificationSourceSubscribed && (!isDataCharacteristicDiscovered || isDataSourceSubscribed); - StartNextDescriptorDiscovery(connectionHandle); + if (isDescriptorFound == isControlDescriptorFound && isDescriptorFound == isDataDescriptorFound) + MaybeFinishDiscovery(connectionHandle); return 0; } @@ -186,73 +176,12 @@ int AppleNotificationCenterClient::OnControlPointWrite(uint16_t /*connectionHand } void AppleNotificationCenterClient::MaybeFinishDiscovery(uint16_t connectionHandle) { - if (discoveryCompleteNotified) { - return; - } - - if (!isDiscovered || !isCharacteristicDiscovered) { - discoveryCompleteNotified = true; - onServiceDiscovered(connectionHandle); - return; - } - - subscriptionsDone = isNotificationSourceSubscribed && (!isDataCharacteristicDiscovered || isDataSourceSubscribed); - const bool descriptorDiscoveryDone = - isNotificationSourceDescriptorDiscoveryComplete && (!isDataCharacteristicDiscovered || isDataSourceDescriptorDiscoveryComplete); - if (subscriptionsDone || descriptorDiscoveryDone) { - discoveryCompleteNotified = true; + if (isCharacteristicDiscovered && isControlCharacteristicDiscovered && isDataCharacteristicDiscovered && isDescriptorFound && + isControlDescriptorFound && isDataDescriptorFound && subscriptionsDone) { onServiceDiscovered(connectionHandle); } } -void AppleNotificationCenterClient::StartNextDescriptorDiscovery(uint16_t connectionHandle) { - if (pendingSubscriptionDescriptorHandle != 0) { - return; - } - - if (isCharacteristicDiscovered && !isNotificationSourceDescriptorDiscoveryComplete) { - isNotificationSourceDescriptorDiscoveryComplete = true; - const uint16_t endHandle = notificationSourceEndHandle != 0 ? notificationSourceEndHandle : ancsEndHandle; - int rc = - ble_gattc_disc_all_dscs(connectionHandle, notificationSourceHandle, endHandle, OnANCSDescriptorDiscoveryEventCallback, this); - if (rc == 0) { - return; - } - NRF_LOG_INFO("ANCS Notification Source descriptor discovery start ERROR: %d", rc); - } - - if (isDataCharacteristicDiscovered && !isDataSourceDescriptorDiscoveryComplete) { - isDataSourceDescriptorDiscoveryComplete = true; - const uint16_t endHandle = dataSourceEndHandle != 0 ? dataSourceEndHandle : ancsEndHandle; - int rc = ble_gattc_disc_all_dscs(connectionHandle, dataSourceHandle, endHandle, OnANCSDescriptorDiscoveryEventCallback, this); - if (rc == 0) { - return; - } - NRF_LOG_INFO("ANCS Data Source descriptor discovery start ERROR: %d", rc); - } - - MaybeFinishDiscovery(connectionHandle); -} - -void AppleNotificationCenterClient::SubscribeToDescriptor(uint16_t connectionHandle, uint16_t descriptorHandle) { - uint8_t value[2] {1, 0}; - pendingSubscriptionDescriptorHandle = descriptorHandle; - int rc = ble_gattc_write_flat(connectionHandle, descriptorHandle, value, sizeof(value), NewAlertSubcribeCallback, this); - if (rc != 0) { - NRF_LOG_INFO("ANCS subscribe start ERROR: %d", rc); - pendingSubscriptionDescriptorHandle = 0; - StartNextDescriptorDiscovery(connectionHandle); - } -} - -void AppleNotificationCenterClient::AssignCharacteristicEndHandle(uint16_t characteristicValueHandle, uint16_t endHandle) { - if (characteristicValueHandle == notificationSourceHandle) { - notificationSourceEndHandle = endHandle; - } else if (characteristicValueHandle == dataSourceHandle) { - dataSourceEndHandle = endHandle; - } -} - void AppleNotificationCenterClient::OnNotification(ble_gap_event* event) { if (event->notify_rx.attr_handle == notificationSourceHandle || event->notify_rx.attr_handle == notificationSourceDescriptorHandle) { NRF_LOG_INFO("ANCS Notification received"); @@ -307,10 +236,6 @@ void AppleNotificationCenterClient::OnNotification(ble_gap_event* event) { // } // Request ANCS more info - if (controlPointHandle == 0 || dataSourceHandle == 0) { - return; - } - // The +4 is for the "..." at the end of the string uint8_t titleSize = maxTitleSize + 4; uint8_t subTitleSize = maxSubtitleSize + 4; @@ -514,15 +439,11 @@ void AppleNotificationCenterClient::Reset() { serviceChangedHandle = 0; serviceChangedDescriptorHandle = 0; notificationSourceHandle = 0; - notificationSourceEndHandle = 0; notificationSourceDescriptorHandle = 0; controlPointHandle = 0; controlPointDescriptorHandle = 0; dataSourceHandle = 0; - dataSourceEndHandle = 0; dataSourceDescriptorHandle = 0; - pendingSubscriptionDescriptorHandle = 0; - lastCharacteristicValueHandle = 0; isGattDiscovered = false; isGattCharacteristicDiscovered = false; isGattDescriptorFound = false; @@ -534,11 +455,6 @@ void AppleNotificationCenterClient::Reset() { isDataCharacteristicDiscovered = false; isDataDescriptorFound = false; subscriptionsDone = false; - discoveryCompleteNotified = false; - isNotificationSourceDescriptorDiscoveryComplete = false; - isDataSourceDescriptorDiscoveryComplete = false; - isNotificationSourceSubscribed = false; - isDataSourceSubscribed = false; notifications.clear(); } diff --git a/src/components/ble/AppleNotificationCenterClient.h b/src/components/ble/AppleNotificationCenterClient.h index 43ece3a58b..43c0d20e18 100644 --- a/src/components/ble/AppleNotificationCenterClient.h +++ b/src/components/ble/AppleNotificationCenterClient.h @@ -70,7 +70,6 @@ namespace Pinetime { static constexpr ble_uuid16_t gattServiceUuid = {BLE_UUID_TYPE_16, 0x1801}; static constexpr ble_uuid16_t serviceChangedCharUuid = {BLE_UUID_TYPE_16, 0x2A05}; - static constexpr ble_uuid16_t clientCharacteristicConfigDescriptorUuid = {BLE_UUID_TYPE_16, BLE_GATT_DSC_CLT_CFG_UUID16}; enum class Categories : uint8_t { Other = 0, @@ -108,28 +107,16 @@ namespace Pinetime { std::unordered_map notifications; std::string DecodeUtf8String(os_mbuf* om, uint16_t size, uint16_t offset); - void StartNextDescriptorDiscovery(uint16_t connectionHandle); - void SubscribeToDescriptor(uint16_t connectionHandle, uint16_t descriptorHandle); - void AssignCharacteristicEndHandle(uint16_t characteristicValueHandle, uint16_t endHandle); bool subscriptionsDone = false; - bool discoveryCompleteNotified = false; - bool isNotificationSourceDescriptorDiscoveryComplete = false; - bool isDataSourceDescriptorDiscoveryComplete = false; - bool isNotificationSourceSubscribed = false; - bool isDataSourceSubscribed = false; uint16_t ancsStartHandle {0}; uint16_t ancsEndHandle {0}; uint16_t notificationSourceHandle {0}; - uint16_t notificationSourceEndHandle {0}; uint16_t controlPointHandle {0}; uint16_t dataSourceHandle {0}; - uint16_t dataSourceEndHandle {0}; uint16_t notificationSourceDescriptorHandle {0}; uint16_t controlPointDescriptorHandle {0}; uint16_t dataSourceDescriptorHandle {0}; - uint16_t pendingSubscriptionDescriptorHandle {0}; - uint16_t lastCharacteristicValueHandle {0}; uint16_t gattStartHandle {0}; uint16_t gattEndHandle {0}; diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index 27e437aab7..694ce19821 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -220,6 +220,7 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { connectionHandle = event->connect.conn_handle; bleController.Connect(); systemTask.PushMessage(Pinetime::System::Messages::BleConnected); + // Service discovery is deferred via systemtask ble_gap_security_initiate(event->connect.conn_handle); } break; @@ -277,10 +278,6 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { desc.sec_state.authenticated, desc.sec_state.bonded, desc.sec_state.key_size); - - if (desc.sec_state.encrypted) { - systemTask.PushMessage(Pinetime::System::Messages::BleStartDiscovery); - } } break; diff --git a/src/systemtask/Messages.h b/src/systemtask/Messages.h index 7945fb7d1b..fee94bb747 100644 --- a/src/systemtask/Messages.h +++ b/src/systemtask/Messages.h @@ -24,7 +24,6 @@ namespace Pinetime { OnNewHalfHour, OnChargingEvent, OnPairing, - BleStartDiscovery, SetOffAlarm, MeasureBatteryTimerExpired, BatteryPercentageUpdated, diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 8f68c026c3..56bf9273e1 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -237,14 +237,8 @@ void SystemTask::Work() { break; case Messages::BleConnected: displayApp.PushMessage(Pinetime::Applications::Display::Messages::NotifyDeviceActivity); - isBleDiscoveryStarted = false; - isBleDiscoveryTimerRunning = false; - break; - case Messages::BleStartDiscovery: - if (!isBleDiscoveryStarted) { - isBleDiscoveryTimerRunning = true; - bleDiscoveryTimer = 1; - } + isBleDiscoveryTimerRunning = true; + bleDiscoveryTimer = 5; break; case Messages::BleFirmwareUpdateStarted: GoToRunning(); @@ -389,8 +383,8 @@ void SystemTask::Work() { if (isBleDiscoveryTimerRunning) { if (bleDiscoveryTimer == 0) { isBleDiscoveryTimerRunning = false; - isBleDiscoveryStarted = true; - // Service discovery is started after link security is established. Keep a short defer so the security callback can unwind. + // Services discovery is deferred from 3 seconds to avoid the conflicts between the host communicating with the + // target and vice-versa. I'm not sure if this is the right way to handle this... nimbleController.StartDiscovery(); } else { bleDiscoveryTimer--; diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index 2a543ecc85..606ddd3492 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -131,7 +131,6 @@ namespace Pinetime { static void Process(void* instance); void Work(); bool isBleDiscoveryTimerRunning = false; - bool isBleDiscoveryStarted = false; uint8_t bleDiscoveryTimer = 0; TimerHandle_t measureBatteryTimer; uint8_t wakeLocksHeld = 0; From 671166b49cd20a46c03f70bcc18e2a61838506e0 Mon Sep 17 00:00:00 2001 From: Mikael Falkvidd Date: Sat, 16 May 2026 22:35:32 +0200 Subject: [PATCH 4/5] Revert "ble: defer ANCS discovery until link encryption" This reverts commit 85632526d85a121c364f25a99e7b84ef7b7f4dec. --- src/components/ble/NimbleController.cpp | 27 +++++++++---------------- 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index 694ce19821..b40fe7a3c0 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -140,9 +140,11 @@ void NimbleController::Init() { void NimbleController::StartAdvertising() { struct ble_gap_adv_params adv_params; struct ble_hs_adv_fields fields; + struct ble_hs_adv_fields rsp_fields; memset(&adv_params, 0, sizeof(adv_params)); memset(&fields, 0, sizeof(fields)); + memset(&rsp_fields, 0, sizeof(rsp_fields)); adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; @@ -160,32 +162,21 @@ void NimbleController::StartAdvertising() { fields.uuids16 = &HeartRateService::heartRateServiceUuid; fields.num_uuids16 = 1; fields.uuids16_is_complete = 1; - fields.uuids128 = &DfuService::serviceUuid; - fields.num_uuids128 = 1; + const ble_uuid128_t uuids128[2] = {DfuService::serviceUuid, AppleNotificationCenterClient::ancsUuid}; + fields.uuids128 = uuids128; + fields.num_uuids128 = 2; fields.uuids128_is_complete = 1; fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; - const auto deviceNameLength = strlen(deviceName); - const auto* deviceNameBytes = reinterpret_cast(deviceName); - uint8_t scanResponseData[31] {}; - size_t scanResponseDataLength = 0; - - scanResponseData[scanResponseDataLength++] = static_cast(deviceNameLength + 1); - scanResponseData[scanResponseDataLength++] = BLE_HS_ADV_TYPE_COMP_NAME; - memcpy(&scanResponseData[scanResponseDataLength], deviceNameBytes, deviceNameLength); - scanResponseDataLength += deviceNameLength; - - // ANCS is provided by iOS; advertise it as a solicited service instead of a local service UUID. - scanResponseData[scanResponseDataLength++] = 17; - scanResponseData[scanResponseDataLength++] = BLE_HS_ADV_TYPE_SOL_UUIDS128; - memcpy(&scanResponseData[scanResponseDataLength], AppleNotificationCenterClient::ancsUuid.value, sizeof(AppleNotificationCenterClient::ancsUuid.value)); - scanResponseDataLength += sizeof(AppleNotificationCenterClient::ancsUuid.value); + rsp_fields.name = reinterpret_cast(deviceName); + rsp_fields.name_len = strlen(deviceName); + rsp_fields.name_is_complete = 1; int rc; rc = ble_gap_adv_set_fields(&fields); ASSERT(rc == 0); - rc = ble_gap_adv_rsp_set_data(scanResponseData, scanResponseDataLength); + rc = ble_gap_adv_rsp_set_fields(&rsp_fields); ASSERT(rc == 0); rc = ble_gap_adv_start(addrType, NULL, 2000, &adv_params, GAPEventCallback, this); From d52638d1203919fa30f0a3c6917a85325dfbaabc Mon Sep 17 00:00:00 2001 From: Mikael Falkvidd Date: Sat, 16 May 2026 21:59:06 +0200 Subject: [PATCH 5/5] ble: start service discovery after encryption The old timer method creates a race condition, especially when user has to enter ble pairing code. --- src/components/ble/NimbleController.cpp | 5 ++++- src/systemtask/Messages.h | 1 + src/systemtask/SystemTask.cpp | 19 +++++++------------ src/systemtask/SystemTask.h | 3 +-- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index b40fe7a3c0..dcf9fac0a0 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -211,7 +211,6 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { connectionHandle = event->connect.conn_handle; bleController.Connect(); systemTask.PushMessage(Pinetime::System::Messages::BleConnected); - // Service discovery is deferred via systemtask ble_gap_security_initiate(event->connect.conn_handle); } break; @@ -269,6 +268,10 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { desc.sec_state.authenticated, desc.sec_state.bonded, desc.sec_state.key_size); + + if (desc.sec_state.encrypted) { + systemTask.PushMessage(Pinetime::System::Messages::BleStartDiscovery); + } } break; diff --git a/src/systemtask/Messages.h b/src/systemtask/Messages.h index fee94bb747..7945fb7d1b 100644 --- a/src/systemtask/Messages.h +++ b/src/systemtask/Messages.h @@ -24,6 +24,7 @@ namespace Pinetime { OnNewHalfHour, OnChargingEvent, OnPairing, + BleStartDiscovery, SetOffAlarm, MeasureBatteryTimerExpired, BatteryPercentageUpdated, diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 56bf9273e1..5a4d5b14f8 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -237,8 +237,13 @@ void SystemTask::Work() { break; case Messages::BleConnected: displayApp.PushMessage(Pinetime::Applications::Display::Messages::NotifyDeviceActivity); - isBleDiscoveryTimerRunning = true; - bleDiscoveryTimer = 5; + isBleDiscoveryStarted = false; + break; + case Messages::BleStartDiscovery: + if (!isBleDiscoveryStarted) { + isBleDiscoveryStarted = true; + nimbleController.StartDiscovery(); + } break; case Messages::BleFirmwareUpdateStarted: GoToRunning(); @@ -380,16 +385,6 @@ void SystemTask::Work() { elapsed = xTaskGetTickCount() - lastStateUpdate; if (elapsed >= stateUpdatePeriod) { UpdateMotion(); - if (isBleDiscoveryTimerRunning) { - if (bleDiscoveryTimer == 0) { - isBleDiscoveryTimerRunning = false; - // Services discovery is deferred from 3 seconds to avoid the conflicts between the host communicating with the - // target and vice-versa. I'm not sure if this is the right way to handle this... - nimbleController.StartDiscovery(); - } else { - bleDiscoveryTimer--; - } - } monitor.Process(); NoInit_BackUpTime = dateTimeController.CurrentDateTime(); if (nrf_gpio_pin_read(PinMap::Button) == 0) { diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index 606ddd3492..fd1f9d3221 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -130,8 +130,7 @@ namespace Pinetime { static void Process(void* instance); void Work(); - bool isBleDiscoveryTimerRunning = false; - uint8_t bleDiscoveryTimer = 0; + bool isBleDiscoveryStarted = false; TimerHandle_t measureBatteryTimer; uint8_t wakeLocksHeld = 0; SystemTaskState state = SystemTaskState::Running;