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.
main
James A. Jerkins 2021-12-04 14:49:49 +07:00 committed by JF
parent 1e4130a9cf
commit 150fa3b661
3 changed files with 117 additions and 10 deletions

@ -9,6 +9,7 @@
#include <host/ble_hs_id.h> #include <host/ble_hs_id.h>
#include <host/util/util.h> #include <host/util/util.h>
#include <controller/ble_ll.h> #include <controller/ble_ll.h>
#include <controller/ble_hw.h>
#undef max #undef max
#undef min #undef min
#include <services/gap/ble_svc_gap.h> #include <services/gap/ble_svc_gap.h>
@ -16,6 +17,7 @@
#include "components/ble/BleController.h" #include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h" #include "components/ble/NotificationManager.h"
#include "components/datetime/DateTimeController.h" #include "components/datetime/DateTimeController.h"
#include "components/fs/FS.h"
#include "systemtask/SystemTask.h" #include "systemtask/SystemTask.h"
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
@ -27,7 +29,8 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
Controllers::Battery& batteryController, Controllers::Battery& batteryController,
Pinetime::Drivers::SpiNorFlash& spiNorFlash, Pinetime::Drivers::SpiNorFlash& spiNorFlash,
Controllers::HeartRateController& heartRateController, Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController) Controllers::MotionController& motionController,
Pinetime::Controllers::FS& fs)
: systemTask {systemTask}, : systemTask {systemTask},
bleController {bleController}, bleController {bleController},
dateTimeController {dateTimeController}, dateTimeController {dateTimeController},
@ -43,7 +46,8 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
batteryInformationService {batteryController}, batteryInformationService {batteryController},
immediateAlertService {systemTask, notificationManager}, immediateAlertService {systemTask, notificationManager},
heartRateService {systemTask, heartRateController}, heartRateService {systemTask, heartRateController},
motionService{systemTask, motionController}, fs {fs},
motionService {systemTask, motionController},
serviceDiscovery({&currentTimeClient, &alertNotificationClient}) { serviceDiscovery({&currentTimeClient, &alertNotificationClient}) {
} }
@ -123,6 +127,8 @@ void NimbleController::Init() {
rc = ble_gatts_start(); rc = ble_gatts_start();
ASSERT(rc == 0); ASSERT(rc == 0);
RestoreBond();
if (!ble_gap_adv_active() && !bleController.IsConnected()) if (!ble_gap_adv_active() && !bleController.IsConnected())
StartAdvertising(); StartAdvertising();
} }
@ -202,6 +208,10 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) {
/* Connection terminated; resume advertising. */ /* Connection terminated; resume advertising. */
NRF_LOG_INFO("Disconnect event : BLE_GAP_EVENT_DISCONNECT"); NRF_LOG_INFO("Disconnect event : BLE_GAP_EVENT_DISCONNECT");
NRF_LOG_INFO("disconnect reason=%d", event->disconnect.reason); NRF_LOG_INFO("disconnect reason=%d", event->disconnect.reason);
if (event->disconnect.conn.sec_state.bonded)
PersistBond(event->disconnect.conn);
currentTimeClient.Reset(); currentTimeClient.Reset();
alertNotificationClient.Reset(); alertNotificationClient.Reset();
connectionHandle = BLE_HS_CONN_HANDLE_NONE; 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. */ /* Encryption has been enabled or disabled for this connection. */
NRF_LOG_INFO("Security event : BLE_GAP_EVENT_ENC_CHANGE"); NRF_LOG_INFO("Security event : BLE_GAP_EVENT_ENC_CHANGE");
NRF_LOG_INFO("encryption change event; status=%0X ", event->enc_change.status); 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; break;
case BLE_GAP_EVENT_PASSKEY_ACTION: case BLE_GAP_EVENT_PASSKEY_ACTION:
@ -258,15 +281,13 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) {
event->subscribe.cur_notify, event->subscribe.cur_notify,
event->subscribe.prev_indicate); 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); heartRateService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
motionService.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); heartRateService.SubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
motionService.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); heartRateService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
motionService.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); 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<uint8_t*>(&our_sec.sec), sizeof our_sec);
fs.FileWrite(&file_p, reinterpret_cast<uint8_t*>(&peer_sec.sec), sizeof peer_sec);
fs.FileWrite(&file_p, reinterpret_cast<const uint8_t*>(&peer_count), 1);
for (int i = 0; i < peer_count; i++) {
fs.FileWrite(&file_p, reinterpret_cast<uint8_t*>(&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<uint8_t*>(&sec.sec), sizeof sec);
ble_store_write_our_sec(&sec.sec);
memset(&sec, 0, sizeof sec);
fs.FileRead(&file_p, reinterpret_cast<uint8_t*>(&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<uint8_t*>(&cccd.cccd), sizeof(struct ble_store_value_cccd));
ble_store_write_cccd(&cccd.cccd);
}
fs.FileClose(&file_p);
fs.FileDelete("/bond.dat");
}
}

@ -20,6 +20,7 @@
#include "components/ble/ServiceDiscovery.h" #include "components/ble/ServiceDiscovery.h"
#include "components/ble/HeartRateService.h" #include "components/ble/HeartRateService.h"
#include "components/ble/MotionService.h" #include "components/ble/MotionService.h"
#include "components/fs/FS.h"
namespace Pinetime { namespace Pinetime {
namespace Drivers { namespace Drivers {
@ -45,7 +46,8 @@ namespace Pinetime {
Controllers::Battery& batteryController, Controllers::Battery& batteryController,
Pinetime::Drivers::SpiNorFlash& spiNorFlash, Pinetime::Drivers::SpiNorFlash& spiNorFlash,
Controllers::HeartRateController& heartRateController, Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController); Controllers::MotionController& motionController,
Pinetime::Controllers::FS& fs);
void Init(); void Init();
void StartAdvertising(); void StartAdvertising();
int OnGAPEvent(ble_gap_event* event); int OnGAPEvent(ble_gap_event* event);
@ -78,6 +80,9 @@ namespace Pinetime {
fastAdvCount = 0; fastAdvCount = 0;
} }
void PersistBond(struct ble_gap_conn_desc &desc);
void RestoreBond();
private: private:
static constexpr const char* deviceName = "InfiniTime"; static constexpr const char* deviceName = "InfiniTime";
Pinetime::System::SystemTask& systemTask; Pinetime::System::SystemTask& systemTask;
@ -98,10 +103,12 @@ namespace Pinetime {
ImmediateAlertService immediateAlertService; ImmediateAlertService immediateAlertService;
HeartRateService heartRateService; HeartRateService heartRateService;
MotionService motionService; MotionService motionService;
Pinetime::Controllers::FS& fs;
uint8_t addrType; // 1 = Random, 0 = PUBLIC uint8_t addrType; // 1 = Random, 0 = PUBLIC
uint16_t connectionHandle = BLE_HS_CONN_HANDLE_NONE; uint16_t connectionHandle = BLE_HS_CONN_HANDLE_NONE;
uint8_t fastAdvCount = 0; uint8_t fastAdvCount = 0;
uint8_t bondId[16] = {0};
ble_uuid128_t dfuServiceUuid { ble_uuid128_t dfuServiceUuid {
.u {.type = BLE_UUID_TYPE_128}, .u {.type = BLE_UUID_TYPE_128},
@ -110,6 +117,6 @@ namespace Pinetime {
ServiceDiscovery serviceDiscovery; ServiceDiscovery serviceDiscovery;
}; };
static NimbleController* nptr; static NimbleController* nptr;
} }
} }

@ -109,7 +109,8 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi,
batteryController, batteryController,
spiNorFlash, spiNorFlash,
heartRateController, heartRateController,
motionController) { motionController,
fs) {
} }
void SystemTask::Start() { void SystemTask::Start() {