Skip to content

Commit f5933ba

Browse files
[nrf toup][nrfconnect] Improve NFC Commissioning
Prevent a second NFC tap from starting a new transport session during fail-safe rollback, which could crash the NFC stack after field loss. kServerReady does not always fire when IPv6 mDNS is not ready at boot, so defer NFC tag emulation until the fabric table is loaded and only report active NFC commissioning when emulation is actually running. Signed-off-by: Arkadiusz Balys <arkadiusz.balys@nordicsemi.no>
1 parent 9e70d02 commit f5933ba

2 files changed

Lines changed: 218 additions & 15 deletions

File tree

src/platform/nrfconnect/NFCCommissioningManagerImpl.cpp

Lines changed: 185 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
#include <platform/internal/CHIPDeviceLayerInternal.h>
1919
#include <platform/internal/NFCCommissioningManager.h>
2020

21+
#include <app/server/Server.h> // nogncheck
22+
#include <transport/SecureSession.h> // nogncheck
23+
#include <transport/raw/PeerAddress.h> // nogncheck
24+
2125
#include <platform/nrfconnect/NFCCommissioningManagerImpl.h>
2226

2327
#include <lib/support/CHIPMem.h>
@@ -153,27 +157,105 @@ CHIP_ERROR NFCCommissioningManagerImpl::_Init()
153157
ChipLogDetail(DeviceLayer, "Initializing NFC Commissioning Manager");
154158

155159
ResetSession();
156-
StartRawIsoDepTagEmulation();
157160
ReturnErrorOnFailure(PlatformMgr().AddEventHandler(OnPlatformEvent, reinterpret_cast<intptr_t>(this)));
158161

159-
return mRawIsoDepStarted ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL;
162+
return CHIP_NO_ERROR;
160163
}
161164

162165
void NFCCommissioningManagerImpl::OnPlatformEvent(const ChipDeviceEvent * event, intptr_t arg)
163166
{
164-
if (event->Type == DeviceEventType::kCommissioningComplete)
167+
auto * self = reinterpret_cast<NFCCommissioningManagerImpl *>(arg);
168+
169+
switch (event->Type)
165170
{
166-
reinterpret_cast<NFCCommissioningManagerImpl *>(arg)->HandleCommissioningComplete();
171+
case DeviceEventType::kServerReady:
172+
case DeviceEventType::kDnssdInitialized:
173+
self->StartNfcCommissioning();
174+
break;
175+
case DeviceEventType::kCommissioningComplete:
176+
self->HandleCommissioningComplete();
177+
break;
178+
case DeviceEventType::kFailSafeTimerExpired:
179+
self->HandleFailSafeTimerExpired();
180+
break;
181+
case DeviceEventType::kSecureSessionEstablished:
182+
self->HandleSecureSessionEstablished(event);
183+
break;
184+
default:
185+
break;
186+
}
187+
}
188+
189+
void NFCCommissioningManagerImpl::ScheduledStartNfcCommissioning(intptr_t arg)
190+
{
191+
auto * self = reinterpret_cast<NFCCommissioningManagerImpl *>(arg);
192+
VerifyOrReturn(self != nullptr);
193+
self->StartNfcCommissioning();
194+
}
195+
196+
void NFCCommissioningManagerImpl::StartNfcCommissioning()
197+
{
198+
VerifyOrReturn(!mRawIsoDepStarted);
199+
200+
if (Server::GetInstance().GetFabricTable().FabricCount() != 0)
201+
{
202+
ChipLogProgress(DeviceLayer, "Device already commissioned: NFC commissioning will stay disabled");
203+
return;
204+
}
205+
206+
StartRawIsoDepTagEmulation();
207+
if (!mRawIsoDepStarted)
208+
{
209+
ChipLogError(DeviceLayer, "Failed to start NFC tag emulation for commissioning");
167210
}
168211
}
169212

170213
void NFCCommissioningManagerImpl::HandleCommissioningComplete()
171214
{
215+
mBlockMatterAidSelection = false;
216+
mNfcEmulationPausedForFailSafe = false;
172217
ChipLogProgress(DeviceLayer, "Commissioning complete: stopping NFC commissioning");
173-
PlatformMgr().RemoveEventHandler(OnPlatformEvent, reinterpret_cast<intptr_t>(this));
174218
NFCCommissioningMgr().Shutdown();
175219
}
176220

221+
void NFCCommissioningManagerImpl::HandleFailSafeTimerExpired()
222+
{
223+
VerifyOrReturn(mBlockMatterAidSelection || mNfcEmulationPausedForFailSafe);
224+
225+
mBlockMatterAidSelection = false;
226+
mNfcEmulationPausedForFailSafe = false;
227+
228+
if (Server::GetInstance().GetFabricTable().FabricCount() != 0)
229+
{
230+
return;
231+
}
232+
233+
TEMPORARY_RETURN_IGNORED ConfigureOnboardingPayload();
234+
ChipLogProgress(DeviceLayer, "Fail-safe expired: NFC commissioning is available again");
235+
}
236+
237+
void NFCCommissioningManagerImpl::HandleSecureSessionEstablished(const ChipDeviceEvent * event)
238+
{
239+
VerifyOrReturn(event != nullptr);
240+
241+
const auto & sessionEstablished = event->SecureSessionEstablished;
242+
if (sessionEstablished.TransportType != static_cast<uint8_t>(Transport::Type::kNfc))
243+
{
244+
return;
245+
}
246+
247+
if (sessionEstablished.SecureSessionType != static_cast<uint8_t>(Transport::SecureSession::Type::kPASE))
248+
{
249+
return;
250+
}
251+
252+
SessionLock lock;
253+
mBlockMatterAidSelection = true;
254+
TEMPORARY_RETURN_IGNORED ClearOnboardingPayload();
255+
ChipLogProgress(DeviceLayer,
256+
"NFC PASE session established: blocking NFC commissioning until fail-safe completes or commissioning succeeds");
257+
}
258+
177259
CHIP_ERROR NFCCommissioningManagerImpl::ConfigureOnboardingPayload()
178260
{
179261
PayloadContents payload;
@@ -193,7 +275,14 @@ CHIP_ERROR NFCCommissioningManagerImpl::ConfigureOnboardingPayload()
193275
MutableCharSpan qrCode(qrCodeBuffer);
194276
ReturnErrorOnFailure(QRCodeBasicSetupPayloadGenerator(payload).payloadBase38Representation(qrCode));
195277

196-
return SetOnboardingPayload(qrCode.data(), qrCode.size());
278+
ReturnErrorOnFailure(SetOnboardingPayload(qrCode.data(), qrCode.size()));
279+
280+
// ConfigureOnboardingPayload() runs before Server::Init(); defer NFC start until the fabric
281+
// table is loaded. kServerReady is not reliable on all networking paths (e.g. Wi-Fi builds
282+
// without an IPv6 mDNS listener at boot), so schedule an explicit start attempt as well.
283+
TEMPORARY_RETURN_IGNORED PlatformMgr().ScheduleWork(ScheduledStartNfcCommissioning, reinterpret_cast<intptr_t>(this));
284+
285+
return CHIP_NO_ERROR;
197286
}
198287

199288
void NFCCommissioningManagerImpl::StartRawIsoDepTagEmulation()
@@ -219,15 +308,35 @@ void NFCCommissioningManagerImpl::StartRawIsoDepTagEmulation()
219308
ChipLogProgress(DeviceLayer, "NFC NDEF Tag emulation started");
220309
}
221310

311+
void NFCCommissioningManagerImpl::StopRawIsoDepTagEmulation()
312+
{
313+
VerifyOrReturn(mRawIsoDepStarted);
314+
315+
nfc_t4t_emulation_stop();
316+
nfc_t4t_done();
317+
mRawIsoDepStarted = false;
318+
}
319+
320+
void NFCCommissioningManagerImpl::PauseNfcTagEmulationForFailSafe()
321+
{
322+
VerifyOrReturn(!mNfcEmulationPausedForFailSafe);
323+
324+
mNfcEmulationPausedForFailSafe = true;
325+
StopRawIsoDepTagEmulation();
326+
ChipLogProgress(DeviceLayer, "NFC tag emulation paused until fail-safe completes");
327+
}
328+
329+
bool NFCCommissioningManagerImpl::IsSessionIdle() const
330+
{
331+
return mSelectedApplication == SelectedApplication::kNone && !mOutgoingContinuationPending && mApduLength == 0 &&
332+
mApduFragmentCount == 0 && !mAwaitingApplicationResponse && mOutgoingMessage.IsNull();
333+
}
334+
222335
void NFCCommissioningManagerImpl::_Shutdown()
223336
{
224337
ChipLogDetail(DeviceLayer, "Shutting down NFC Commissioning Manager");
225-
if (mRawIsoDepStarted)
226-
{
227-
nfc_t4t_emulation_stop();
228-
nfc_t4t_done();
229-
mRawIsoDepStarted = false;
230-
}
338+
PlatformMgr().RemoveEventHandler(OnPlatformEvent, reinterpret_cast<intptr_t>(this));
339+
StopRawIsoDepTagEmulation();
231340
ResetSession(/* notifyAborted = */ true);
232341
}
233342

@@ -378,6 +487,11 @@ void NFCCommissioningManagerImpl::OnT4TEvent(void * context, nfc_t4t_event_t eve
378487
return;
379488
}
380489

490+
if (self->mNfcEmulationPausedForFailSafe)
491+
{
492+
return;
493+
}
494+
381495
switch (event)
382496
{
383497
case NFC_T4T_EVENT_FIELD_ON:
@@ -399,17 +513,46 @@ void NFCCommissioningManagerImpl::OnT4TEvent(void * context, nfc_t4t_event_t eve
399513

400514
void NFCCommissioningManagerImpl::HandleFieldOn()
401515
{
516+
SessionLock lock;
517+
518+
if (mNfcEmulationPausedForFailSafe)
519+
{
520+
ChipLogProgress(DeviceLayer, "NFC field detected while fail-safe pause is active: ignoring");
521+
return;
522+
}
523+
524+
if (mBlockMatterAidSelection && IsSessionIdle())
525+
{
526+
ChipLogProgress(DeviceLayer, "NFC field detected while fail-safe is active: pausing tag emulation");
527+
ResetSessionState();
528+
PauseNfcTagEmulationForFailSafe();
529+
return;
530+
}
531+
402532
ChipLogDetail(DeviceLayer, "NFC field detected: an NFC Reader/Writer is polling");
403533
// A field-on event always precedes a fresh ISO-DEP activation: start with a clean slate.
404-
SessionLock lock;
405534
ResetSessionState();
406535
}
407536

408537
void NFCCommissioningManagerImpl::HandleFieldOff()
409538
{
410-
ChipLogDetail(DeviceLayer, "NFC field lost: the NFC Reader/Writer moved away");
411539
SessionLock lock;
540+
541+
if (IsSessionIdle())
542+
{
543+
if (mNfcEmulationPausedForFailSafe || !mBlockMatterAidSelection)
544+
{
545+
return;
546+
}
547+
}
548+
549+
ChipLogProgress(DeviceLayer, "NFC field lost: the NFC Reader/Writer moved away");
412550
ResetSessionState(/* notifyAborted = */ true);
551+
552+
if (mBlockMatterAidSelection && mRawIsoDepStarted)
553+
{
554+
PauseNfcTagEmulationForFailSafe();
555+
}
413556
}
414557

415558
void NFCCommissioningManagerImpl::HandleDataTransmitted()
@@ -553,6 +696,17 @@ void NFCCommissioningManagerImpl::HandleSelectByNameCommand(const uint8_t * aid,
553696

554697
if (aidLength == sizeof(kNdefAid) && memcmp(aid, kNdefAid, sizeof(kNdefAid)) == 0)
555698
{
699+
if (mBlockMatterAidSelection)
700+
{
701+
mSelectedApplication = SelectedApplication::kNone;
702+
mSelectedFile = SelectedFile::kNone;
703+
SendStatusResponse(kSwConditionsNotSatisfied1, kSwConditionsNotSatisfied2);
704+
ChipLogProgress(DeviceLayer,
705+
"Rejecting NDEF Tag Application selection: commissioning fail-safe is active, waiting for fail-safe "
706+
"to complete");
707+
return;
708+
}
709+
556710
mSelectedApplication = SelectedApplication::kNdef;
557711
SendStatusResponse(kSwSuccess1, kSwSuccess2);
558712
ChipLogProgress(DeviceLayer, "NDEF Tag Application selected");
@@ -594,6 +748,16 @@ void NFCCommissioningManagerImpl::HandleSelectByFileIdCommand(const uint8_t * fi
594748

595749
void NFCCommissioningManagerImpl::HandleMatterAidSelected()
596750
{
751+
if (mBlockMatterAidSelection)
752+
{
753+
mSelectedApplication = SelectedApplication::kNone;
754+
mSelectedFile = SelectedFile::kNone;
755+
SendStatusResponse(kSwConditionsNotSatisfied1, kSwConditionsNotSatisfied2);
756+
ChipLogProgress(DeviceLayer,
757+
"Rejecting Matter AID selection: commissioning fail-safe is active, waiting for fail-safe to complete");
758+
return;
759+
}
760+
597761
mSelectedApplication = SelectedApplication::kMatter;
598762

599763
uint16_t discriminator = 0;
@@ -634,6 +798,13 @@ void NFCCommissioningManagerImpl::HandleMatterAidSelected()
634798

635799
void NFCCommissioningManagerImpl::HandleReadBinaryCommand(const uint8_t * apdu, size_t apduLength)
636800
{
801+
if (mBlockMatterAidSelection)
802+
{
803+
SendStatusResponse(kSwConditionsNotSatisfied1, kSwConditionsNotSatisfied2);
804+
ChipLogProgress(DeviceLayer, "Rejecting READ BINARY: commissioning fail-safe is active, waiting for fail-safe to complete");
805+
return;
806+
}
807+
637808
if (mSelectedApplication != SelectedApplication::kNdef || mSelectedFile == SelectedFile::kNone)
638809
{
639810
SendStatusResponse(kSwConditionsNotSatisfied1, kSwConditionsNotSatisfied2);

src/platform/nrfconnect/NFCCommissioningManagerImpl.h

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,10 @@ class NFCCommissioningManagerImpl final : public NFCCommissioningManager, privat
6161
void SetNFCBase(Transport::NFCBase * nfcBase) override;
6262
bool CanSendToPeer(const Transport::PeerAddress & address) override;
6363
CHIP_ERROR SendToNfcTag(const Transport::PeerAddress & address, System::PacketBufferHandle && msgBuf) override;
64-
bool HasOnboardingPayload() const { return mHasOnboardingPayload; }
64+
bool HasOnboardingPayload() const
65+
{
66+
return mHasOnboardingPayload && mRawIsoDepStarted && !mBlockMatterAidSelection && !mNfcEmulationPausedForFailSafe;
67+
}
6568
CHIP_ERROR ConfigureOnboardingPayload();
6669

6770
// Maximum size of the NDEF message (URI record) served by the NDEF Tag Application's NDEF
@@ -89,6 +92,17 @@ class NFCCommissioningManagerImpl final : public NFCCommissioningManager, privat
8992
/** Stops NFC tag emulation once the device has been successfully commissioned. */
9093
void HandleCommissioningComplete();
9194

95+
/** Starts NFC tag emulation once the server is up and the device has no fabrics yet. */
96+
void StartNfcCommissioning();
97+
98+
static void ScheduledStartNfcCommissioning(intptr_t arg);
99+
100+
/** Logs that NFC Matter transport can accept a new session after fail-safe expiry. */
101+
void HandleFailSafeTimerExpired();
102+
103+
/** Blocks new Matter AID selection once an NFC PASE session is established. */
104+
void HandleSecureSessionEstablished(const ChipDeviceEvent * event);
105+
92106
// Grant header-local singleton accessors access to sInstance.
93107
friend NFCCommissioningManager & NFCCommissioningMgr();
94108
friend NFCCommissioningManagerImpl & NFCCommissioningMgrImpl();
@@ -123,6 +137,15 @@ class NFCCommissioningManagerImpl final : public NFCCommissioningManager, privat
123137
*/
124138
void StartRawIsoDepTagEmulation();
125139

140+
/** Stops raw ISO-DEP tag emulation without removing the platform event handler. */
141+
void StopRawIsoDepTagEmulation();
142+
143+
/** Stops tag emulation while fail-safe rollback is in progress. */
144+
void PauseNfcTagEmulationForFailSafe();
145+
146+
/** Returns true when there is no in-flight ISO-DEP/Matter session state. */
147+
bool IsSessionIdle() const;
148+
126149
/**
127150
* Handles the event indicating that an NFC polling device (reader) field is present.
128151
* Typically used to prepare for communication when a reader initiates contact.
@@ -315,6 +338,15 @@ class NFCCommissioningManagerImpl final : public NFCCommissioningManager, privat
315338

316339
// Set once an incoming message has been fully reassembled and handed off to the Matter stack.
317340
bool mAwaitingApplicationResponse = false;
341+
342+
// While set, NDEF reads and new Matter AID selections are rejected, and the
343+
// onboarding payload is cleared, so a second NFC tap cannot restart commissioning
344+
// during fail-safe rollback.
345+
bool mBlockMatterAidSelection = false;
346+
347+
// Set when tag emulation is stopped during fail-safe rollback. While set, all NFC
348+
// callbacks are ignored to avoid corrupting the NFC platform ring buffer on re-tap.
349+
bool mNfcEmulationPausedForFailSafe = false;
318350
};
319351

320352
/**

0 commit comments

Comments
 (0)