Skip to content

Commit b2e283c

Browse files
committed
bluetooth: fast_pair: fmdn: add API for checking the provisioning state
Added the new FMDN API - bt_fast_pair_fmdn_is_provisioned. The API can be used to check the device provisioning state synchronously. Changed the bt_fast_pair_fmdn_info_cb.provisioning_state_changed callback behavior. The callback no longer reports the initial provisioning state after the Fast Pair subsystem is enabled with the bt_fast_pair_enable API. Aligned the affected Fast Pair projects that use the FMDN extension. Ref: NCSDK-30856 Signed-off-by: Kamil Piszczek <Kamil.Piszczek@nordicsemi.no>
1 parent 0f6ada8 commit b2e283c

File tree

8 files changed

+171
-103
lines changed

8 files changed

+171
-103
lines changed

include/bluetooth/services/fast_pair/fmdn.h

+24-9
Original file line numberDiff line numberDiff line change
@@ -538,17 +538,18 @@ struct bt_fast_pair_fmdn_info_cb {
538538
/** @brief Indicate provisioning state changes.
539539
*
540540
* This callback is called to indicate that the FMDN accessory has been
541-
* successfully provisioned or unprovisioned.
541+
* successfully provisioned or unprovisioned by the connected Bluetooth
542+
* peer.
542543
*
543-
* This callback also reports the initial provisioning state when the
544-
* user enables Fast Pair with the @ref bt_fast_pair_enable API.
544+
* This callback does not report the initial provisioning state when the
545+
* user enables Fast Pair with the @ref bt_fast_pair_enable API. To check
546+
* the initial state, use the @ref bt_fast_pair_fmdn_is_provisioned API.
545547
*
546-
* The first callback is executed in the workqueue context after the
547-
* @ref bt_fast_pair_enable function call. Subsequent callbacks are
548-
* also executed in the cooperative thread context. You can learn about
549-
* the exact thread context by analyzing the @kconfig{CONFIG_BT_RECV_CONTEXT}
550-
* configuration choice. By default, this callback is executed in the
551-
* Bluetooth-specific workqueue thread (@kconfig{CONFIG_BT_RECV_WORKQ_BT}).
548+
* This callback is executed in the cooperative thread context. You
549+
* can learn about the exact thread context by analyzing the
550+
* @kconfig{CONFIG_BT_RECV_CONTEXT} configuration choice. By default, this
551+
* callback is executed in the Bluetooth-specific workqueue thread
552+
* (@kconfig{CONFIG_BT_RECV_WORKQ_BT}).
552553
*
553554
* @param provisioned true if the accessory has been successfully provisioned.
554555
* false if the accessory has been successfully unprovisioned.
@@ -559,6 +560,20 @@ struct bt_fast_pair_fmdn_info_cb {
559560
sys_snode_t node;
560561
};
561562

563+
/** @brief Check the FMDN provisioning state.
564+
*
565+
* This function can be used to synchronously check the FMDN provisioning state.
566+
* To track the provisioning state asynchronously, use the
567+
* @ref bt_fast_pair_fmdn_info_cb.provisioning_state_changed callback.
568+
*
569+
* The function shall only be used after the Fast Pair module is enabled with the
570+
* @ref bt_fast_pair_enable API. In the disabled state, this function always returns
571+
* false.
572+
*
573+
* @return True if the device is provisioned, false otherwise.
574+
*/
575+
bool bt_fast_pair_fmdn_is_provisioned(void);
576+
562577
/** @brief Register the information callbacks in the FMDN module.
563578
*
564579
* This function registers the information callbacks. You can call this function only

samples/bluetooth/fast_pair/locator_tag/src/fp_adv.c

+49-12
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ static void fp_advertising_update(void)
324324
static void fp_adv_connected(struct bt_le_ext_adv *adv, struct bt_le_ext_adv_connected_info *info)
325325
{
326326
__ASSERT_NO_MSG(!fp_conn);
327+
__ASSERT_NO_MSG(fp_adv_set);
327328

328329
fp_adv_set_active = false;
329330
fp_adv_state_changed_notify(fp_adv_set_active);
@@ -344,10 +345,14 @@ static bool fp_adv_rpa_expired(struct bt_le_ext_adv *adv)
344345
__ASSERT_NO_MSG(!k_is_preempt_thread());
345346
__ASSERT_NO_MSG(!k_is_in_isr());
346347

347-
__ASSERT_NO_MSG(is_enabled);
348+
__ASSERT_NO_MSG(fp_adv_set);
348349

349350
LOG_INF("Fast Pair: RPA expired");
350351

352+
if (!fp_adv_set_active) {
353+
LOG_INF("Fast Pair: RPA rotation in the inactive advertising state");
354+
}
355+
351356
if (!uptime) {
352357
uptime = k_uptime_get();
353358
} else {
@@ -369,6 +374,18 @@ static bool fp_adv_rpa_expired(struct bt_le_ext_adv *adv)
369374
LOG_INF("Fast Pair: setting RPA timeout to %d [s]",
370375
next_rpa_timeout);
371376
}
377+
} else {
378+
if (!fp_adv_set_active) {
379+
/* Keep the RPA in the valid state to ensure that the RPA expired callback
380+
* will be received on a forced RPA rotation during the FMDN unprovisioning
381+
* operation. The forced RPA rotation allows the Fast Pair advertising set
382+
* to take control from the FMDN advertising set over the RPA timeout. The
383+
* RPA expired callback will not be received if any RPA rotation was
384+
* previously allowed here in the provisioned state and with inactive Fast
385+
* Pair advertising set.
386+
*/
387+
expire_rpa = false;
388+
}
372389
}
373390

374391
if (fp_adv_mode == APP_FP_ADV_MODE_DISCOVERABLE) {
@@ -394,6 +411,25 @@ static bool fp_adv_rpa_expired(struct bt_le_ext_adv *adv)
394411
return expire_rpa;
395412
}
396413

414+
static int fp_adv_set_rotate(void)
415+
{
416+
int err;
417+
struct bt_le_oob oob;
418+
419+
__ASSERT(fp_adv_set, "Fast Pair: invalid state of the advertising set");
420+
421+
/* Force the RPA rotation to synchronize the Fast Pair advertising
422+
* payload with its RPA address using rpa_expired callback.
423+
*/
424+
err = bt_le_oob_get_local(fp_adv_param.id, &oob);
425+
if (err) {
426+
LOG_ERR("Fast Pair: bt_le_oob_get_local failed: %d", err);
427+
return err;
428+
}
429+
430+
return 0;
431+
}
432+
397433
static int fp_adv_set_setup(void)
398434
{
399435
int err;
@@ -502,16 +538,7 @@ static void fp_adv_provisioning_state_changed(bool provisioned)
502538
}
503539

504540
if (!provisioned) {
505-
int err;
506-
struct bt_le_oob oob;
507-
508-
/* Force the RPA rotation to synchronize the Fast Pair advertising
509-
* payload with its RPA address using rpa_expired callback.
510-
*/
511-
err = bt_le_oob_get_local(fp_adv_param.id, &oob);
512-
if (err) {
513-
LOG_ERR("Fast Pair: bt_le_oob_get_local failed: %d", err);
514-
}
541+
(void) fp_adv_set_rotate();
515542
} else {
516543
fp_adv_rpa_suspension_cancel();
517544
}
@@ -688,13 +715,23 @@ int app_fp_adv_enable(void)
688715
return 0;
689716
}
690717

718+
fp_account_key_present = bt_fast_pair_has_account_key();
719+
fmdn_provisioned = bt_fast_pair_fmdn_is_provisioned();
720+
691721
err = fp_adv_set_setup();
692722
if (err) {
693723
LOG_ERR("Fast Pair: fp_adv_set_setup failed (err %d)", err);
694724
return err;
695725
}
696726

697-
fp_account_key_present = bt_fast_pair_has_account_key();
727+
if (!fmdn_provisioned) {
728+
err = fp_adv_set_rotate();
729+
if (err) {
730+
LOG_ERR("Fast Pair: fp_adv_set_rotate failed: %d", err);
731+
(void) fp_adv_set_teardown();
732+
return err;
733+
}
734+
}
698735

699736
fp_adv_mode_update();
700737

samples/bluetooth/fast_pair/locator_tag/src/main.c

+71-51
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,29 @@ APP_FP_ADV_TRIGGER_REGISTER(fp_adv_trigger_fmdn_provisioning, "fmdn_provisioning
7979
*/
8080
APP_FP_ADV_TRIGGER_REGISTER(fp_adv_trigger_ui, "ui");
8181

82-
static bool factory_reset_executed;
8382
static enum factory_reset_trigger factory_reset_trigger;
8483

8584
static void init_work_handle(struct k_work *w);
8685

8786
static K_SEM_DEFINE(init_work_sem, 0, 1);
8887
static K_WORK_DEFINE(init_work, init_work_handle);
8988

89+
static void fmdn_provisioning_state_set(bool provisioned)
90+
{
91+
__ASSERT_NO_MSG(bt_fast_pair_is_ready());
92+
__ASSERT_NO_MSG(bt_fast_pair_fmdn_is_provisioned() == provisioned);
93+
94+
if (fmdn_provisioned == provisioned) {
95+
return;
96+
}
97+
98+
LOG_INF("FMDN: state changed to %s",
99+
provisioned ? "provisioned" : "unprovisioned");
100+
101+
app_ui_state_change_indicate(APP_UI_STATE_PROVISIONED, provisioned);
102+
fmdn_provisioned = provisioned;
103+
}
104+
90105
static void fmdn_factory_reset_prepare(void)
91106
{
92107
/* Disable advertising requests related to the FMDN. */
@@ -100,9 +115,21 @@ static void fmdn_factory_reset_prepare(void)
100115

101116
static void fmdn_factory_reset_executed(void)
102117
{
118+
if (factory_reset_trigger != FACTORY_RESET_TRIGGER_NONE) {
119+
LOG_INF("The device has been reset to factory settings");
120+
LOG_INF("Please press a button to put the device in the Fast Pair discoverable "
121+
"advertising mode");
122+
}
123+
103124
/* Clear the trigger state for the scheduled factory reset operations. */
104125
factory_reset_trigger = FACTORY_RESET_TRIGGER_NONE;
105-
factory_reset_executed = true;
126+
127+
if (bt_fast_pair_is_ready()) {
128+
fp_account_key_present = bt_fast_pair_has_account_key();
129+
}
130+
__ASSERT_NO_MSG(!fp_account_key_present);
131+
132+
__ASSERT_NO_MSG(!fmdn_provisioned);
106133
}
107134

108135
APP_FACTORY_RESET_CALLBACKS_REGISTER(factory_reset_cbs, fmdn_factory_reset_prepare,
@@ -421,75 +448,66 @@ static void fmdn_conn_authenticated(struct bt_conn *conn)
421448
fmdn_conn_auth_bm_conn_status_set(conn, true);
422449
}
423450

424-
static bool fmdn_provisioning_state_is_first_cb_after_bootup(void)
451+
static void fmdn_provisioning_state_changed(bool provisioned)
425452
{
426-
static bool first_cb_after_bootup = true;
427-
bool is_first_cb_after_bootup = first_cb_after_bootup;
453+
fmdn_provisioning_state_set(provisioned);
454+
if (provisioned) {
455+
/* Fast Pair Implementation Guidelines for the locator tag use case:
456+
* cancel the provisioning timeout.
457+
*/
458+
if (factory_reset_trigger == FACTORY_RESET_TRIGGER_PROVISIONING_TIMEOUT) {
459+
fmdn_factory_reset_cancel();
460+
}
428461

429-
first_cb_after_bootup = false;
462+
app_fp_adv_request(&fp_adv_trigger_fmdn_provisioning, false);
430463

431-
return is_first_cb_after_bootup;
432-
}
464+
fp_adv_ui_request = false;
465+
app_fp_adv_request(&fp_adv_trigger_ui, fp_adv_ui_request);
466+
} else {
467+
/* Fast Pair Implementation Guidelines for the locator tag use case:
468+
* trigger the reset to factory settings on the unprovisioning operation.
469+
*
470+
* Delay the factory reset operation to allow the local device
471+
* to send a response to the unprovisioning command and give
472+
* the connected peer necessary time to finalize its operations
473+
* and shutdown the connection.
474+
*/
475+
fmdn_factory_reset_schedule(FACTORY_RESET_TRIGGER_KEY_STATE_MISMATCH,
476+
K_SECONDS(FACTORY_RESET_DELAY));
433477

434-
static void fmdn_provisioning_state_changed(bool provisioned)
435-
{
436-
bool clock_sync_required = fmdn_provisioning_state_is_first_cb_after_bootup() &&
437-
provisioned;
478+
app_fp_adv_request(&fp_adv_trigger_clock_sync, false);
479+
}
480+
}
438481

439-
LOG_INF("FMDN: state changed to %s",
440-
provisioned ? "provisioned" : "unprovisioned");
482+
static struct bt_fast_pair_fmdn_info_cb fmdn_info_cb = {
483+
.clock_synced = fmdn_clock_synced,
484+
.conn_authenticated = fmdn_conn_authenticated,
485+
.provisioning_state_changed = fmdn_provisioning_state_changed,
486+
};
441487

442-
app_ui_state_change_indicate(APP_UI_STATE_PROVISIONED, provisioned);
443-
fmdn_provisioned = provisioned;
488+
static void fmdn_provisioning_state_init(void)
489+
{
490+
bool provisioned;
444491

445-
/* Fast Pair Implementation Guidelines for the locator tag use case:
446-
* cancel the provisioning timeout.
447-
*/
448-
if (provisioned &&
449-
(factory_reset_trigger == FACTORY_RESET_TRIGGER_PROVISIONING_TIMEOUT)) {
450-
fmdn_factory_reset_cancel();
451-
}
492+
provisioned = bt_fast_pair_fmdn_is_provisioned();
493+
fmdn_provisioning_state_set(provisioned);
452494

453495
/* Fast Pair Implementation Guidelines for the locator tag use case:
454-
* trigger the reset to factory settings on the unprovisioning operation
455-
* or on the loss of the Owner Account Key.
496+
* trigger the reset to factory settings on the mismatch between the
497+
* Owner Account Key and the FMDN provisioning state.
456498
*/
457499
fp_account_key_present = bt_fast_pair_has_account_key();
458500
if (fp_account_key_present != provisioned) {
459-
/* Delay the factory reset operation to allow the local device
460-
* to send a response to the unprovisioning command and give
461-
* the connected peer necessary time to finalize its operations
462-
* and shutdown the connection.
463-
*/
464-
fmdn_factory_reset_schedule(
465-
FACTORY_RESET_TRIGGER_KEY_STATE_MISMATCH,
466-
K_SECONDS(FACTORY_RESET_DELAY));
501+
fmdn_factory_reset_schedule(FACTORY_RESET_TRIGGER_KEY_STATE_MISMATCH, K_NO_WAIT);
467502
return;
468503
}
469504

470-
/* Triggered on the unprovisioning operation. */
471-
if (factory_reset_executed) {
472-
LOG_INF("The device has been reset to factory settings");
473-
LOG_INF("Please press a button to put the device in the Fast Pair discoverable "
474-
"advertising mode");
475-
476-
factory_reset_executed = false;
477-
return;
478-
}
479-
480-
app_fp_adv_request(&fp_adv_trigger_clock_sync, clock_sync_required);
481-
app_fp_adv_request(&fp_adv_trigger_fmdn_provisioning, false);
505+
app_fp_adv_request(&fp_adv_trigger_clock_sync, provisioned);
482506

483507
fp_adv_ui_request = !provisioned;
484508
app_fp_adv_request(&fp_adv_trigger_ui, fp_adv_ui_request);
485509
}
486510

487-
static struct bt_fast_pair_fmdn_info_cb fmdn_info_cb = {
488-
.clock_synced = fmdn_clock_synced,
489-
.conn_authenticated = fmdn_conn_authenticated,
490-
.provisioning_state_changed = fmdn_provisioning_state_changed,
491-
};
492-
493511
static void fp_adv_state_changed(bool enabled)
494512
{
495513
app_ui_state_change_indicate(APP_UI_STATE_FP_ADV, enabled);
@@ -686,6 +704,8 @@ static void init_work_handle(struct k_work *w)
686704
return;
687705
}
688706

707+
fmdn_provisioning_state_init();
708+
689709
k_sem_give(&init_work_sem);
690710
}
691711

subsys/bluetooth/services/fast_pair/fmdn/beacon_actions.c

+6-6
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ static ssize_t provisioning_state_read_handle(struct bt_conn *conn,
229229
struct fp_account_key account_key;
230230
uint8_t eid[PROVISIONING_STATE_RSP_EID_LEN];
231231
uint8_t provisioning_state_flags;
232-
const bool provisioned = fp_fmdn_state_is_provisioned();
232+
const bool provisioned = bt_fast_pair_fmdn_is_provisioned();
233233
static const uint8_t req_data_len = PROVISIONING_STATE_REQ_PAYLOAD_LEN;
234234
uint8_t rsp_data_len;
235235
enum provisioning_state_bit_flag {
@@ -348,7 +348,7 @@ static ssize_t ephemeral_identity_key_set_handle(struct bt_conn *conn,
348348
struct fp_account_key account_key;
349349
uint8_t *encrypted_eik;
350350
uint8_t new_eik[EPHEMERAL_IDENTITY_KEY_SET_REQ_EIK_LEN];
351-
const bool provisioned = fp_fmdn_state_is_provisioned();
351+
const bool provisioned = bt_fast_pair_fmdn_is_provisioned();
352352
const uint8_t req_data_len = provisioned ?
353353
EPHEMERAL_IDENTITY_KEY_SET_REQ_PROVISIONED_PAYLOAD_LEN :
354354
EPHEMERAL_IDENTITY_KEY_SET_REQ_UNPROVISIONED_PAYLOAD_LEN;
@@ -483,7 +483,7 @@ static ssize_t ephemeral_identity_key_clear_handle(struct bt_conn *conn,
483483
struct fp_account_key account_key;
484484
uint8_t *current_eik_hash;
485485
uint8_t *random_nonce;
486-
const bool provisioned = fp_fmdn_state_is_provisioned();
486+
const bool provisioned = bt_fast_pair_fmdn_is_provisioned();
487487
static const uint8_t req_data_len = EPHEMERAL_IDENTITY_KEY_CLEAR_REQ_PAYLOAD_LEN;
488488
static const uint8_t rsp_data_len = EPHEMERAL_IDENTITY_KEY_CLEAR_RSP_PAYLOAD_LEN;
489489

@@ -933,7 +933,7 @@ static ssize_t activate_utp_mode_handle(struct bt_conn *conn,
933933
return BT_GATT_ERR(BEACON_ACTIONS_ATT_ERR_INVALID_VALUE);
934934
}
935935

936-
if (!fp_fmdn_state_is_provisioned()) {
936+
if (!bt_fast_pair_fmdn_is_provisioned()) {
937937
LOG_ERR("Beacon Actions: Activate Unwanted Tracking Protection mode request:"
938938
" Device is not provisioned");
939939

@@ -1029,7 +1029,7 @@ static ssize_t deactivate_utp_mode_handle(struct bt_conn *conn,
10291029
return BT_GATT_ERR(BEACON_ACTIONS_ATT_ERR_INVALID_VALUE);
10301030
}
10311031

1032-
if (!fp_fmdn_state_is_provisioned()) {
1032+
if (!bt_fast_pair_fmdn_is_provisioned()) {
10331033
LOG_ERR("Beacon Actions: Deactivate Unwanted Tracking Protection mode request:"
10341034
" Device is not provisioned");
10351035

@@ -1139,7 +1139,7 @@ int bt_fast_pair_fmdn_ring_state_update(
11391139
}
11401140

11411141
/* Check if connected peers should be notified about the ring state change. */
1142-
if (!fp_fmdn_state_is_provisioned()) {
1142+
if (!bt_fast_pair_fmdn_is_provisioned()) {
11431143
return 0;
11441144
}
11451145

0 commit comments

Comments
 (0)