From 62dbcbfc953a36202d96466563a8e71b8bd4ff65 Mon Sep 17 00:00:00 2001 From: "James A. Jerkins" Date: Sat, 30 Oct 2021 13:02:39 -0500 Subject: Connect and bond with a passkey This commit adds the following: Passkey pairing - passkey is displayed on watch Swipe down to clear passkey screen Connection encryption Connection bonding Automatic reconnects to a bonded peripheral Trusted device on Android Note that persisting the bond between reboots is NOT included in this commit. Therefore, rebooting the watch will cause reconnect failures. You must delete the bond from the phone to reconnect/pair. --- src/systemtask/Messages.h | 1 + src/systemtask/SystemTask.cpp | 7 +++++++ 2 files changed, 8 insertions(+) (limited to 'src/systemtask') diff --git a/src/systemtask/Messages.h b/src/systemtask/Messages.h index b7142704..516f6462 100644 --- a/src/systemtask/Messages.h +++ b/src/systemtask/Messages.h @@ -22,6 +22,7 @@ namespace Pinetime { DisableSleeping, OnNewDay, OnChargingEvent, + OnPairing, SetOffAlarm, StopRinging, MeasureBatteryTimerExpired, diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 1120b80d..2fb4de51 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -396,6 +396,13 @@ void SystemTask::Work() { case Messages::BatteryPercentageUpdated: nimbleController.NotifyBatteryLevel(batteryController.PercentRemaining()); break; + case Messages::OnPairing: + if (isSleeping && !isWakingUp) { + GoToRunning(); + } + motorController.RunForDuration(35); + displayApp.PushMessage(Pinetime::Applications::Display::Messages::ShowPairingKey); + break; default: break; -- cgit v1.2.3-70-g09d2 From 150fa3b6615e524a072a1af6d6728519381fa737 Mon Sep 17 00:00:00 2001 From: "James A. Jerkins" Date: Sat, 4 Dec 2021 14:49:49 -0600 Subject: Persist bond between reboots Save bond information in the FS after a disconnect or encryption change if the bond is not already stored. The bond is restored on boot enabling automatic reconnection to a previously bonded central. Two consecutive watch reboots with the central out of range (or BLE off) will remove the stored bond from the watch. --- src/components/ble/NimbleController.cpp | 113 ++++++++++++++++++++++++++++++-- src/components/ble/NimbleController.h | 11 +++- src/systemtask/SystemTask.cpp | 3 +- 3 files changed, 117 insertions(+), 10 deletions(-) (limited to 'src/systemtask') diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index 01901e0a..ec411989 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #undef max #undef min #include @@ -16,6 +17,7 @@ #include "components/ble/BleController.h" #include "components/ble/NotificationManager.h" #include "components/datetime/DateTimeController.h" +#include "components/fs/FS.h" #include "systemtask/SystemTask.h" using namespace Pinetime::Controllers; @@ -27,7 +29,8 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask, Controllers::Battery& batteryController, Pinetime::Drivers::SpiNorFlash& spiNorFlash, Controllers::HeartRateController& heartRateController, - Controllers::MotionController& motionController) + Controllers::MotionController& motionController, + Pinetime::Controllers::FS& fs) : systemTask {systemTask}, bleController {bleController}, dateTimeController {dateTimeController}, @@ -43,7 +46,8 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask, batteryInformationService {batteryController}, immediateAlertService {systemTask, notificationManager}, heartRateService {systemTask, heartRateController}, - motionService{systemTask, motionController}, + fs {fs}, + motionService {systemTask, motionController}, serviceDiscovery({¤tTimeClient, &alertNotificationClient}) { } @@ -123,6 +127,8 @@ void NimbleController::Init() { rc = ble_gatts_start(); ASSERT(rc == 0); + RestoreBond(); + if (!ble_gap_adv_active() && !bleController.IsConnected()) StartAdvertising(); } @@ -202,6 +208,10 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { /* Connection terminated; resume advertising. */ NRF_LOG_INFO("Disconnect event : BLE_GAP_EVENT_DISCONNECT"); NRF_LOG_INFO("disconnect reason=%d", event->disconnect.reason); + + if (event->disconnect.conn.sec_state.bonded) + PersistBond(event->disconnect.conn); + currentTimeClient.Reset(); alertNotificationClient.Reset(); connectionHandle = BLE_HS_CONN_HANDLE_NONE; @@ -230,6 +240,19 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { /* Encryption has been enabled or disabled for this connection. */ NRF_LOG_INFO("Security event : BLE_GAP_EVENT_ENC_CHANGE"); NRF_LOG_INFO("encryption change event; status=%0X ", event->enc_change.status); + + if (event->enc_change.status == 0) { + struct ble_gap_conn_desc desc; + ble_gap_conn_find(event->enc_change.conn_handle, &desc); + if (desc.sec_state.bonded) + PersistBond(desc); + + NRF_LOG_INFO("new state: encrypted=%d authenticated=%d bonded=%d key_size=%d", + desc.sec_state.encrypted, + desc.sec_state.authenticated, + desc.sec_state.bonded, + desc.sec_state.key_size); + } break; case BLE_GAP_EVENT_PASSKEY_ACTION: @@ -258,15 +281,13 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { event->subscribe.cur_notify, event->subscribe.prev_indicate); - if(event->subscribe.reason == BLE_GAP_SUBSCRIBE_REASON_TERM) { + if (event->subscribe.reason == BLE_GAP_SUBSCRIBE_REASON_TERM) { heartRateService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle); motionService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle); - } - else if(event->subscribe.prev_notify == 0 && event->subscribe.cur_notify == 1) { + } else if (event->subscribe.prev_notify == 0 && event->subscribe.cur_notify == 1) { heartRateService.SubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle); motionService.SubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle); - } - else if(event->subscribe.prev_notify == 1 && event->subscribe.cur_notify == 0) { + } else if (event->subscribe.prev_notify == 1 && event->subscribe.cur_notify == 0) { heartRateService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle); motionService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle); } @@ -340,3 +361,81 @@ void NimbleController::NotifyBatteryLevel(uint8_t level) { batteryInformationService.NotifyBatteryLevel(connectionHandle, level); } } + +void NimbleController::PersistBond(struct ble_gap_conn_desc& desc) { + union ble_store_key key; + union ble_store_value our_sec, peer_sec, peer_cccd_set[MYNEWT_VAL(BLE_STORE_MAX_CCCDS)] = {0}; + int rc; + + memset(&key, 0, sizeof key); + memset(&our_sec, 0, sizeof our_sec); + key.sec.peer_addr = desc.peer_id_addr; + rc = ble_store_read_our_sec(&key.sec, &our_sec.sec); + + if (memcmp(&our_sec.sec, &bondId, sizeof bondId) == 0) + return; + + memcpy(&bondId, &our_sec.sec, sizeof bondId); + + memset(&key, 0, sizeof key); + memset(&peer_sec, 0, sizeof peer_sec); + key.sec.peer_addr = desc.peer_id_addr; + rc += ble_store_read_peer_sec(&key.sec, &peer_sec.sec); + + if (rc == 0) { + memset(&key, 0, sizeof key); + key.cccd.peer_addr = desc.peer_id_addr; + int peer_count = 0; + ble_store_util_count(BLE_STORE_OBJ_TYPE_CCCD, &peer_count); + for (int i = 0; i < peer_count; i++) { + key.cccd.idx = peer_count; + ble_store_read_cccd(&key.cccd, &peer_cccd_set[i].cccd); + } + + /* Wakeup Spi and SpiNorFlash before accessing the file system + * This should be fixed in the FS driver + */ + systemTask.PushMessage(Pinetime::System::Messages::GoToRunning); + systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping); + vTaskDelay(10); + + lfs_file_t file_p; + + rc = fs.FileOpen(&file_p, "/bond.dat", LFS_O_WRONLY | LFS_O_CREAT); + if (rc == 0) { + fs.FileWrite(&file_p, reinterpret_cast(&our_sec.sec), sizeof our_sec); + fs.FileWrite(&file_p, reinterpret_cast(&peer_sec.sec), sizeof peer_sec); + fs.FileWrite(&file_p, reinterpret_cast(&peer_count), 1); + for (int i = 0; i < peer_count; i++) { + fs.FileWrite(&file_p, reinterpret_cast(&peer_cccd_set[i].cccd), sizeof(struct ble_store_value_cccd)); + } + fs.FileClose(&file_p); + } + systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping); + } +} + +void NimbleController::RestoreBond() { + lfs_file_t file_p; + union ble_store_value sec, cccd; + uint8_t peer_count = 0; + + if (fs.FileOpen(&file_p, "/bond.dat", LFS_O_RDONLY) == 0) { + memset(&sec, 0, sizeof sec); + fs.FileRead(&file_p, reinterpret_cast(&sec.sec), sizeof sec); + ble_store_write_our_sec(&sec.sec); + + memset(&sec, 0, sizeof sec); + fs.FileRead(&file_p, reinterpret_cast(&sec.sec), sizeof sec); + ble_store_write_peer_sec(&sec.sec); + + fs.FileRead(&file_p, &peer_count, 1); + for (int i = 0; i < peer_count; i++) { + fs.FileRead(&file_p, reinterpret_cast(&cccd.cccd), sizeof(struct ble_store_value_cccd)); + ble_store_write_cccd(&cccd.cccd); + } + + fs.FileClose(&file_p); + fs.FileDelete("/bond.dat"); + } +} diff --git a/src/components/ble/NimbleController.h b/src/components/ble/NimbleController.h index 895b87f2..944e8cad 100644 --- a/src/components/ble/NimbleController.h +++ b/src/components/ble/NimbleController.h @@ -20,6 +20,7 @@ #include "components/ble/ServiceDiscovery.h" #include "components/ble/HeartRateService.h" #include "components/ble/MotionService.h" +#include "components/fs/FS.h" namespace Pinetime { namespace Drivers { @@ -45,7 +46,8 @@ namespace Pinetime { Controllers::Battery& batteryController, Pinetime::Drivers::SpiNorFlash& spiNorFlash, Controllers::HeartRateController& heartRateController, - Controllers::MotionController& motionController); + Controllers::MotionController& motionController, + Pinetime::Controllers::FS& fs); void Init(); void StartAdvertising(); int OnGAPEvent(ble_gap_event* event); @@ -78,6 +80,9 @@ namespace Pinetime { fastAdvCount = 0; } + void PersistBond(struct ble_gap_conn_desc &desc); + void RestoreBond(); + private: static constexpr const char* deviceName = "InfiniTime"; Pinetime::System::SystemTask& systemTask; @@ -98,10 +103,12 @@ namespace Pinetime { ImmediateAlertService immediateAlertService; HeartRateService heartRateService; MotionService motionService; + Pinetime::Controllers::FS& fs; uint8_t addrType; // 1 = Random, 0 = PUBLIC uint16_t connectionHandle = BLE_HS_CONN_HANDLE_NONE; uint8_t fastAdvCount = 0; + uint8_t bondId[16] = {0}; ble_uuid128_t dfuServiceUuid { .u {.type = BLE_UUID_TYPE_128}, @@ -110,6 +117,6 @@ namespace Pinetime { ServiceDiscovery serviceDiscovery; }; - static NimbleController* nptr; + static NimbleController* nptr; } } diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 2fb4de51..215c78a5 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -109,7 +109,8 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi, batteryController, spiNorFlash, heartRateController, - motionController) { + motionController, + fs) { } void SystemTask::Start() { -- cgit v1.2.3-70-g09d2 From 048ecd41e414a9abc8c3d09423b8f5cb99304309 Mon Sep 17 00:00:00 2001 From: "James A. Jerkins" Date: Wed, 8 Dec 2021 00:10:54 -0600 Subject: Adjust BLE/LL stacks, style, comments, refactoring Increase BLE task stack +200 and decrease LL task stack -200 more braces! --- src/components/ble/NimbleController.cpp | 29 ++++++++++++++++------ src/components/ble/NimbleController.h | 13 +++++----- .../npl/freertos/src/nimble_port_freertos.c | 4 +-- src/systemtask/SystemTask.cpp | 3 ++- 4 files changed, 31 insertions(+), 18 deletions(-) (limited to 'src/systemtask') diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index ec411989..0f20aefe 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -10,10 +10,10 @@ #include #include #include -#undef max -#undef min #include #include +#undef max +#undef min #include "components/ble/BleController.h" #include "components/ble/NotificationManager.h" #include "components/datetime/DateTimeController.h" @@ -36,7 +36,9 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask, dateTimeController {dateTimeController}, notificationManager {notificationManager}, spiNorFlash {spiNorFlash}, + fs {fs}, dfuService {systemTask, bleController, spiNorFlash}, + currentTimeClient {dateTimeController}, anService {systemTask, notificationManager}, alertNotificationClient {systemTask, notificationManager}, @@ -46,7 +48,6 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask, batteryInformationService {batteryController}, immediateAlertService {systemTask, notificationManager}, heartRateService {systemTask, heartRateController}, - fs {fs}, motionService {systemTask, motionController}, serviceDiscovery({¤tTimeClient, &alertNotificationClient}) { } @@ -129,8 +130,9 @@ void NimbleController::Init() { RestoreBond(); - if (!ble_gap_adv_active() && !bleController.IsConnected()) + if (!ble_gap_adv_active() && !bleController.IsConnected()) { StartAdvertising(); + } } void NimbleController::StartAdvertising() { @@ -209,8 +211,9 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { NRF_LOG_INFO("Disconnect event : BLE_GAP_EVENT_DISCONNECT"); NRF_LOG_INFO("disconnect reason=%d", event->disconnect.reason); - if (event->disconnect.conn.sec_state.bonded) + if (event->disconnect.conn.sec_state.bonded) { PersistBond(event->disconnect.conn); + } currentTimeClient.Reset(); alertNotificationClient.Reset(); @@ -244,8 +247,9 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { if (event->enc_change.status == 0) { struct ble_gap_conn_desc desc; ble_gap_conn_find(event->enc_change.conn_handle, &desc); - if (desc.sec_state.bonded) + if (desc.sec_state.bonded) { PersistBond(desc); + } NRF_LOG_INFO("new state: encrypted=%d authenticated=%d bonded=%d key_size=%d", desc.sec_state.encrypted, @@ -257,8 +261,16 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { case BLE_GAP_EVENT_PASSKEY_ACTION: /* Authentication has been requested for this connection. + * + * BLE authentication is determined by the combination of I/O capabilities + * on the central and peripheral. When the peripheral is display only and + * the central has a keyboard and display then passkey auth is selected. + * When both the central and peripheral have displays and support yes/no + * buttons then numeric comparison is selected. We currently advertise + * display capability only so we only handle the "display" action here. + * * Standards insist that the rand() PRNG be deterministic. - * Use the nimble TRNG since rand() is predictable. + * Use the nimble TRNG here since rand() is predictable. */ NRF_LOG_INFO("Security event : BLE_GAP_EVENT_PASSKEY_ACTION"); if (event->passkey.params.action == BLE_SM_IOACT_DISP) { @@ -372,8 +384,9 @@ void NimbleController::PersistBond(struct ble_gap_conn_desc& desc) { key.sec.peer_addr = desc.peer_id_addr; rc = ble_store_read_our_sec(&key.sec, &our_sec.sec); - if (memcmp(&our_sec.sec, &bondId, sizeof bondId) == 0) + if (memcmp(&our_sec.sec, &bondId, sizeof bondId) == 0) { return; + } memcpy(&bondId, &our_sec.sec, sizeof bondId); diff --git a/src/components/ble/NimbleController.h b/src/components/ble/NimbleController.h index 944e8cad..7569ce2a 100644 --- a/src/components/ble/NimbleController.h +++ b/src/components/ble/NimbleController.h @@ -14,11 +14,11 @@ #include "components/ble/CurrentTimeService.h" #include "components/ble/DeviceInformationService.h" #include "components/ble/DfuService.h" +#include "components/ble/HeartRateService.h" #include "components/ble/ImmediateAlertService.h" #include "components/ble/MusicService.h" #include "components/ble/NavigationService.h" #include "components/ble/ServiceDiscovery.h" -#include "components/ble/HeartRateService.h" #include "components/ble/MotionService.h" #include "components/fs/FS.h" @@ -80,16 +80,17 @@ namespace Pinetime { fastAdvCount = 0; } - void PersistBond(struct ble_gap_conn_desc &desc); + private: + void PersistBond(struct ble_gap_conn_desc& desc); void RestoreBond(); - private: static constexpr const char* deviceName = "InfiniTime"; Pinetime::System::SystemTask& systemTask; Pinetime::Controllers::Ble& bleController; DateTime& dateTimeController; Pinetime::Controllers::NotificationManager& notificationManager; Pinetime::Drivers::SpiNorFlash& spiNorFlash; + Pinetime::Controllers::FS& fs; Pinetime::Controllers::DfuService dfuService; DeviceInformationService deviceInformationService; @@ -103,9 +104,9 @@ namespace Pinetime { ImmediateAlertService immediateAlertService; HeartRateService heartRateService; MotionService motionService; - Pinetime::Controllers::FS& fs; + ServiceDiscovery serviceDiscovery; - uint8_t addrType; // 1 = Random, 0 = PUBLIC + uint8_t addrType; uint16_t connectionHandle = BLE_HS_CONN_HANDLE_NONE; uint8_t fastAdvCount = 0; uint8_t bondId[16] = {0}; @@ -113,8 +114,6 @@ namespace Pinetime { ble_uuid128_t dfuServiceUuid { .u {.type = BLE_UUID_TYPE_128}, .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}}; - - ServiceDiscovery serviceDiscovery; }; static NimbleController* nptr; diff --git a/src/libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c b/src/libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c index 8ee3475a..b9902781 100644 --- a/src/libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c +++ b/src/libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c @@ -37,7 +37,7 @@ nimble_port_freertos_init(TaskFunction_t host_task_fn) * provided by NimBLE and in case of FreeRTOS it does not need to be wrapped * since it has compatible prototype. */ - xTaskCreate(nimble_port_ll_task_func, "ll", configMINIMAL_STACK_SIZE + 400, + xTaskCreate(nimble_port_ll_task_func, "ll", configMINIMAL_STACK_SIZE + 200, NULL, configMAX_PRIORITIES - 1, &ll_task_h); #endif @@ -46,6 +46,6 @@ nimble_port_freertos_init(TaskFunction_t host_task_fn) * have separate task for NimBLE host, but since something needs to handle * default queue it is just easier to make separate task which does this. */ - xTaskCreate(host_task_fn, "ble", configMINIMAL_STACK_SIZE + 400, + xTaskCreate(host_task_fn, "ble", configMINIMAL_STACK_SIZE + 600, NULL, tskIDLE_PRIORITY + 1, &host_task_h); } diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 215c78a5..79384a5b 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -259,8 +259,9 @@ void SystemTask::Work() { displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToRunning); heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::WakeUp); - if (!bleController.IsConnected()) + if (!bleController.IsConnected()) { nimbleController.RestartFastAdv(); + } isSleeping = false; isWakingUp = false; -- cgit v1.2.3-70-g09d2 From f1fc7ee6593aa7dd2212ee362729b9cd06b054a9 Mon Sep 17 00:00:00 2001 From: Tim Keller Date: Fri, 12 Nov 2021 02:11:39 +0000 Subject: Adjust systemtask to respect doNotGoToSleep. --- src/systemtask/SystemTask.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/systemtask') diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 79384a5b..02440452 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -280,6 +280,9 @@ void SystemTask::Work() { } } break; case Messages::GoToSleep: + if (doNotGoToSleep) { + return; + } isGoingToSleep = true; NRF_LOG_INFO("[systemtask] Going to sleep"); xTimerStop(idleTimer, 0); @@ -506,7 +509,7 @@ void SystemTask::OnTouchEvent() { } void SystemTask::PushMessage(System::Messages msg) { - if (msg == Messages::GoToSleep) { + if (msg == Messages::GoToSleep && !doNotGoToSleep) { isGoingToSleep = true; } -- cgit v1.2.3-70-g09d2 From cd593c3862b1cb43865fb9075273dc97dfe5b7f1 Mon Sep 17 00:00:00 2001 From: Tim Keller Date: Mon, 15 Nov 2021 15:27:36 +0000 Subject: Break not return thanks @FintasticMan --- src/systemtask/SystemTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/systemtask') diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 02440452..4076d57d 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -281,7 +281,7 @@ void SystemTask::Work() { } break; case Messages::GoToSleep: if (doNotGoToSleep) { - return; + break; } isGoingToSleep = true; NRF_LOG_INFO("[systemtask] Going to sleep"); -- cgit v1.2.3-70-g09d2