diff --git a/src/core/thread/indirect_sender.cpp b/src/core/thread/indirect_sender.cpp index b5c1d6550..bea98179a 100644 --- a/src/core/thread/indirect_sender.cpp +++ b/src/core/thread/indirect_sender.cpp @@ -338,6 +338,7 @@ Error IndirectSender::PrepareFrameForChild(Mac::TxFrame &aFrame, FrameContext &a if (message == nullptr) { PrepareEmptyFrame(aFrame, aChild, /* aAckRequest */ true); + aContext.mMessageNextOffset = 0; ExitNow(); } @@ -422,10 +423,28 @@ void IndirectSender::HandleSentFrameToChild(const Mac::TxFrame &aFrame, VerifyOrExit(mEnabled); + if (aError == kErrorNone) + { + Get().UpdateOnSend(aChild); + } + + // A zero `nextOffset` indicates that the sent frame is an empty + // frame generated by `PrepareFrameForChild()` when there was no + // indirect message in the send queue for the child. This can happen + // in the (not common) case where the radio platform does not + // support the "source address match" feature and always includes + // "frame pending" flag in acks to data poll frames. In such a case, + // `IndirectSender` prepares and sends an empty frame to the child + // after it sends a data poll. Here in `HandleSentFrameToChild()` we + // exit quickly if we detect the "send done" is for the empty frame + // to ensure we do not update any newly added indirect message after + // preparing the empty frame. + + VerifyOrExit(nextOffset != 0); + switch (aError) { case kErrorNone: - Get().UpdateOnSend(aChild); break; case kErrorNoAck: