diff --git a/configure.ac b/configure.ac
index 83d0cca21..387d9335b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -806,6 +806,37 @@ AC_SUBST(OPENTHREAD_ENABLE_LEGACY)
AM_CONDITIONAL([OPENTHREAD_ENABLE_LEGACY], [test "${enable_legacy}" = "yes"])
AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_LEGACY],[${OPENTHREAD_ENABLE_LEGACY}],[Define to 1 if you want to use legacy network support])
+#
+# Child Supervision
+#
+
+AC_ARG_ENABLE(child_supervision,
+ [AS_HELP_STRING([--enable-child-supervision],[Enable child supervision feature @<:@default=no@:>@.])],
+ [
+ case "${enableval}" in
+
+ no|yes)
+ enable_child_supervision=${enableval}
+ ;;
+
+ *)
+ AC_MSG_ERROR([Invalid value ${enable_child_supervision} for --enable-child-supervision])
+ ;;
+ esac
+ ],
+ [enable_child_supervision=no])
+
+if test "$enable_child_supervision" = "yes"; then
+ OPENTHREAD_ENABLE_CHILD_SUPERVISION=1
+else
+ OPENTHREAD_ENABLE_CHILD_SUPERVISION=0
+fi
+
+AC_MSG_RESULT(${enable_child_supervision})
+AC_SUBST(OPENTHREAD_ENABLE_CHILD_SUPERVISION)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_CHILD_SUPERVISION], [test "${enable_child_supervision}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_CHILD_SUPERVISION],[${OPENTHREAD_ENABLE_CHILD_SUPERVISION}],[Define to 1 if you want to use child supervision feature])
+
#
# Default Logging
#
@@ -1344,6 +1375,7 @@ AC_MSG_NOTICE([
OpenThread Jam Detection support : ${enable_jam_detection}
OpenThread MAC Whitelist support : ${enable_mac_whitelist}
OpenThread Diagnostics support : ${enable_diag}
+ OpenThread Child Supervision support : ${enable_child_supervision}
OpenThread Legacy network support : ${enable_legacy}
OpenThread Default logging support : ${enable_default_logging}
OpenThread Certification log support : ${enable_cert_log}
diff --git a/etc/visual-studio/libopenthread.vcxproj b/etc/visual-studio/libopenthread.vcxproj
index 5444fff36..5d9541738 100644
--- a/etc/visual-studio/libopenthread.vcxproj
+++ b/etc/visual-studio/libopenthread.vcxproj
@@ -137,6 +137,7 @@
+
@@ -224,6 +225,7 @@
+
diff --git a/etc/visual-studio/libopenthread.vcxproj.filters b/etc/visual-studio/libopenthread.vcxproj.filters
index 193a486bb..b39290f9b 100644
--- a/etc/visual-studio/libopenthread.vcxproj.filters
+++ b/etc/visual-studio/libopenthread.vcxproj.filters
@@ -297,6 +297,9 @@
Source Files\crypto
+
+ Source Files\utils
+
Source Files\utils
@@ -554,6 +557,9 @@
Header Files\crypto
+
+ Header Files\utils
+
Header Files\utils
diff --git a/etc/visual-studio/libopenthread_k.vcxproj b/etc/visual-studio/libopenthread_k.vcxproj
index 82a21be8d..b04e77e9f 100644
--- a/etc/visual-studio/libopenthread_k.vcxproj
+++ b/etc/visual-studio/libopenthread_k.vcxproj
@@ -145,6 +145,7 @@
+
@@ -258,6 +259,7 @@
+
diff --git a/etc/visual-studio/libopenthread_k.vcxproj.filters b/etc/visual-studio/libopenthread_k.vcxproj.filters
index 0d2f01f64..97cf5ad50 100644
--- a/etc/visual-studio/libopenthread_k.vcxproj.filters
+++ b/etc/visual-studio/libopenthread_k.vcxproj.filters
@@ -297,6 +297,9 @@
Source Files\crypto
+
+ Source Files\utils
+
Source Files\utils
@@ -554,6 +557,9 @@
Header Files\crypto
+
+ Header Files\utils
+
Header Files\utils
diff --git a/include/openthread/Makefile.am b/include/openthread/Makefile.am
index f19bf5b85..d8d45fb31 100644
--- a/include/openthread/Makefile.am
+++ b/include/openthread/Makefile.am
@@ -47,6 +47,7 @@ PRETTY_SUBDIRS = \
$(NULL)
openthread_headers = \
+ child_supervision.h \
cli.h \
border_agent_proxy.h \
coap.h \
diff --git a/include/openthread/child_supervision.h b/include/openthread/child_supervision.h
new file mode 100644
index 000000000..549494830
--- /dev/null
+++ b/include/openthread/child_supervision.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2017, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ * This file includes the OenThread API for child supervision feature
+ */
+
+#ifndef OPENTHREAD_CHILD_SUPERVISION_H_
+#define OPENTHREAD_CHILD_SUPERVISION_H_
+
+#ifdef OPENTHREAD_CONFIG_FILE
+#include OPENTHREAD_CONFIG_FILE
+#else
+#include
+#endif
+
+#include "openthread/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if OPENTHREAD_ENABLE_CHILD_SUPERVISION
+
+/**
+ * @addtogroup child-supervision Child Supervision
+ *
+ * @brief
+ * This module includes functions for child supervision feature.
+ *
+ * @{
+ *
+ */
+
+/**
+ * Get the child supervision interval (in seconds).
+ *
+ * Child supervision feature provides a mechanism for parent to ensure that a message is sent to each sleepy child
+ * within the supervision interval. If there is no transmission to the child within the supervision interval,
+ * OpenThread will enqueue and send a supervision message (a data message with empty payload) to the child.
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ *
+ * @returns The child supervision interval. Zero indicates that child supervision is disabled.
+ *
+ */
+uint16_t otChildSupervisionGetInterval(otInstance *aInstance);
+
+/**
+ * Set the child supervision interval (in seconds).
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ * @param[in] aInterval The supervision interval (in seconds). Zero to disable supervision on parent.
+ *
+ */
+void otChildSupervisionSetInterval(otInstance *aInstance, uint16_t aInterval);
+
+/**
+ * Get the supervision check timeout interval (in seconds).
+ *
+ * If the device is a sleepy child and it does not hear from its parent within the specified check timeout, it initiates
+ * the re-attach process (MLE Child Update Request/Response exchange with its parent).
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ *
+ * @returns The supervision check timeout. Zero indicates that supervision check on the child is disabled.
+ *
+ */
+uint16_t otChildSupervisionGetCheckTimeout(otInstance *aInstance);
+
+/**
+ * Set the supervision check timeout interval (in seconds).
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ * @param[in] aTimeout The check timeout (in seconds). Zero to disable supervision check on the child.
+ *
+ */
+void otChildSupervisionSetCheckTimeout(otInstance *aInstance, uint16_t aTimeout);
+
+/**
+ * @}
+ *
+ */
+
+#endif // OPENTHREAD_ENABLE_CHILD_SUPERVISION
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // OPENTHREAD_CHILD_SUPERVISION_H_
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index e6d225f42..b43090a86 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -93,6 +93,7 @@ SOURCES_COMMON = \
api/border_agent_proxy_api.cpp \
api/coap_api.cpp \
api/commissioner_api.cpp \
+ api/child_supervision_api.cpp \
api/crypto_api.cpp \
api/dataset_api.cpp \
api/dataset_ftd_api.cpp \
@@ -179,6 +180,7 @@ SOURCES_COMMON = \
thread/src_match_controller.cpp \
thread/thread_netif.cpp \
thread/topology.cpp \
+ utils/child_supervision.cpp \
utils/jam_detector.cpp \
utils/missing_strlcpy.c \
utils/missing_strlcat.c \
@@ -294,6 +296,7 @@ HEADERS_COMMON = \
thread/thread_tlvs.hpp \
thread/thread_uris.hpp \
thread/topology.hpp \
+ utils/child_supervision.hpp \
utils/slaac_address.hpp \
utils/jam_detector.hpp \
utils/wrap_stdbool.h \
diff --git a/src/core/api/child_supervision_api.cpp b/src/core/api/child_supervision_api.cpp
new file mode 100644
index 000000000..b31130ea6
--- /dev/null
+++ b/src/core/api/child_supervision_api.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * This file implements the OpenThread child supervision API.
+ */
+
+#include "openthread/child_supervision.h"
+
+#include "openthread-instance.h"
+
+using namespace ot;
+
+#if OPENTHREAD_ENABLE_CHILD_SUPERVISION
+
+uint16_t otChildSupervisionGetInterval(otInstance *aInstance)
+{
+ return aInstance->mThreadNetif.GetChildSupervisor().GetSupervisionInterval();
+}
+
+void otChildSupervisionSetInterval(otInstance *aInstance, uint16_t aInterval)
+{
+ aInstance->mThreadNetif.GetChildSupervisor().SetSupervisionInterval(aInterval);
+}
+
+uint16_t otChildSupervisionGetCheckTimeout(otInstance *aInstance)
+{
+ return aInstance->mThreadNetif.GetSupervisionListener().GetTimeout();
+}
+
+void otChildSupervisionSetCheckTimeout(otInstance *aInstance, uint16_t aTimeout)
+{
+ aInstance->mThreadNetif.GetSupervisionListener().SetTimeout(aTimeout);
+}
+
+#endif // OPENTHREAD_ENABLE_CHILD_SUPERVISION
diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp
index 7d4d3a347..af4f82e36 100644
--- a/src/core/common/message.hpp
+++ b/src/core/common/message.hpp
@@ -203,9 +203,10 @@ class Message: public Buffer
public:
enum
{
- kTypeIp6 = 0, ///< A full uncompress IPv6 packet
+ kTypeIp6 = 0, ///< A full uncompressed IPv6 packet
kType6lowpan = 1, ///< A 6lowpan frame
kTypeMacDataPoll = 2, ///< A MAC data poll message
+ kTypeSupervision = 3, ///< A child supervision frame.
};
enum
@@ -215,7 +216,7 @@ public:
kSubTypeMleDiscoverRequest = 2, ///< MLE Discover Request
kSubTypeMleDiscoverResponse = 3, ///< MLE Discover Response
kSubTypeJoinerEntrust = 4, ///< Joiner Entrust
- kSubTypeMplRetransmission = 5, ///< MPL next retranmission message
+ kSubTypeMplRetransmission = 5, ///< MPL next retransmission message
kSubTypeMleGeneral = 6, ///< General MLE
kSubTypeJoinerFinalizeResponse = 7, ///< Joiner Finalize Response
kSubTypeMleChildUpdateRequest = 8, ///< MLE Child Update Request
diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h
index d58eee153..32fc4e50c 100644
--- a/src/core/openthread-core-default-config.h
+++ b/src/core/openthread-core-default-config.h
@@ -735,4 +735,49 @@
#define OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB 0
#endif
+/*
+ * @def OPENTHREAD_CONFIG_CHILD_SUPERVISION_INTERVAL
+ *
+ * The default supervision interval in seconds used by parent. Set to zero to disable the supervision process on the
+ * parent.
+ *
+ * Applicable only if child supervision feature is enabled (i.e., `OPENTHREAD_ENABLE_CHILD_SUPERVISION ` is set).
+ *
+ * Child supervision feature provides a mechanism for parent to ensure that a message is sent to each sleepy child
+ * within the supervision interval. If there is no transmission to the child within the supervision interval, child
+ * supervisor will enqueue and send a supervision message (a data message with empty payload) to the child.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_CHILD_SUPERVISION_INTERVAL
+#define OPENTHREAD_CONFIG_CHILD_SUPERVISION_INTERVAL 129
+#endif
+
+/**
+ * @def OPENTHREAD_CONFIG_SUPERVISION_CHECK_TIMEOUT
+ *
+ * The default supervision check timeout interval (in seconds) used by a device in child state. Set to zero to disable
+ * the supervision check process on the child.
+ *
+ * Applicable only if child supervision feature is enabled (i.e., `OPENTHREAD_ENABLE_CHILD_SUPERVISION` is set).
+ *
+ * If the sleepy child does not hear from its parent within the specified timeout interval, it initiates the re-attach
+ * process (MLE Child Update Request/Response exchange with its parent).
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_SUPERVISION_CHECK_TIMEOUT
+#define OPENTHREAD_CONFIG_SUPERVISION_CHECK_TIMEOUT 190
+#endif
+
+/**
+ * @def OPENTHREAD_CONFIG_SUPERVISION_MSG_NO_ACK_REQUEST
+ *
+ * Define as 1 to clear/disable 15.4 ack request in the MAC header of a supervision message.
+ *
+ * Applicable only if child supervision feature is enabled (i.e., `OPENTHREAD_ENABLE_CHILD_SUPERVISION` is set).
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_SUPERVISION_MSG_NO_ACK_REQUEST
+#define OPENTHREAD_CONFIG_SUPERVISION_MSG_NO_ACK_REQUEST 0
+#endif
+
#endif // OPENTHREAD_CORE_DEFAULT_CONFIG_H_
diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp
index d7757d872..0f5f4ac40 100644
--- a/src/core/thread/mesh_forwarder.cpp
+++ b/src/core/thread/mesh_forwarder.cpp
@@ -300,18 +300,14 @@ void MeshForwarder::ScheduleTransmissionTask(void)
{
mMacSource.mLength = sizeof(mMacSource.mShortAddress);
mMacSource.mShortAddress = mNetif.GetMac().GetShortAddress();
-
- mMacDest.mLength = sizeof(mMacDest.mShortAddress);
- mMacDest.mShortAddress = child.GetRloc16();
}
else
{
mMacSource.mLength = sizeof(mMacSource.mExtAddress);
memcpy(mMacSource.mExtAddress.m8, mNetif.GetMac().GetExtAddress(), sizeof(mMacDest.mExtAddress));
-
- mMacDest.mLength = sizeof(mMacDest.mExtAddress);
- mMacDest.mExtAddress = child.GetExtAddress();
}
+
+ child.GetMacAddress(mMacDest);
}
// To ensure fairness in handling of data requests from sleepy
@@ -421,6 +417,15 @@ ThreadError MeshForwarder::SendMessage(Message &aMessage)
case Message::kTypeMacDataPoll:
aMessage.SetDirectTransmission();
break;
+
+ case Message::kTypeSupervision:
+ child = mNetif.GetChildSupervisor().GetDestination(aMessage);
+ VerifyOrExit(child != NULL, error = kThreadError_Drop);
+ VerifyOrExit(!child->IsRxOnWhenIdle(), error = kThreadError_Drop);
+
+ aMessage.SetChildMask(mNetif.GetMle().GetChildIndex(*child));
+ mSourceMatchController.IncrementMessageCount(*child);
+ break;
}
aMessage.SetOffset(0);
@@ -493,6 +498,10 @@ Message *MeshForwarder::GetDirectTransmission(void)
case Message::kTypeMacDataPoll:
ExitNow();
+
+ case Message::kTypeSupervision:
+ error = kThreadError_Drop;
+ break;
}
switch (error)
@@ -524,12 +533,26 @@ exit:
Message *MeshForwarder::GetIndirectTransmission(Child &aChild)
{
Message *message = NULL;
+ Message *next;
uint8_t childIndex = mNetif.GetMle().GetChildIndex(aChild);
- for (message = mSendQueue.GetHead(); message; message = message->GetNext())
+ for (message = mSendQueue.GetHead(); message; message = next)
{
+ next = message->GetNext();
+
if (message->GetChildMask(childIndex))
{
+ // Skip and remove the supervision message if there are other messages queued for the child.
+
+ if ((message->GetType() == Message::kTypeSupervision) && (aChild.GetIndirectMessageCount() > 1))
+ {
+ message->ClearChildMask(childIndex);
+ mSourceMatchController.DecrementMessageCount(aChild);
+ mSendQueue.Dequeue(*message);
+ message->Free();
+ continue;
+ }
+
break;
}
}
@@ -577,16 +600,7 @@ void MeshForwarder::PrepareIndirectTransmission(Message &aMessage, const Child &
}
else
{
- if (aChild.IsIndirectSourceMatchShort())
- {
- mMacDest.mLength = sizeof(mMacDest.mShortAddress);
- mMacDest.mShortAddress = aChild.GetRloc16();
- }
- else
- {
- mMacDest.mLength = sizeof(mMacDest.mExtAddress);
- mMacDest.mExtAddress = aChild.GetExtAddress();
- }
+ aChild.GetMacAddress(mMacDest);
}
break;
@@ -607,6 +621,10 @@ void MeshForwarder::PrepareIndirectTransmission(Message &aMessage, const Child &
break;
}
+ case Message::kTypeSupervision:
+ aChild.GetMacAddress(mMacDest);
+ break;
+
default:
assert(false);
break;
@@ -825,6 +843,7 @@ void MeshForwarder::SetRxOff(void)
{
mNetif.GetMac().SetRxOnWhenIdle(false);
mDataPollManager.StopPolling();
+ mNetif.GetSupervisionListener().Stop();
}
bool MeshForwarder::GetRxOnWhenIdle(void)
@@ -839,10 +858,12 @@ void MeshForwarder::SetRxOnWhenIdle(bool aRxOnWhenIdle)
if (aRxOnWhenIdle)
{
mDataPollManager.StopPolling();
+ mNetif.GetSupervisionListener().Stop();
}
else
{
mDataPollManager.StartPolling();
+ mNetif.GetSupervisionListener().Start();
}
}
@@ -909,7 +930,7 @@ ThreadError MeshForwarder::HandleFrameRequest(Mac::Frame &aFrame)
if (mSendMessage == NULL)
{
- SendEmptyFrame(aFrame);
+ SendEmptyFrame(aFrame, false);
aFrame.SetIsARetransmission(false);
aFrame.SetMaxTxAttempts(Mac::kDirectFrameMacTxAttempts);
ExitNow();
@@ -963,6 +984,11 @@ ThreadError MeshForwarder::HandleFrameRequest(Mac::Frame &aFrame)
case Message::kTypeMacDataPoll:
error = SendPoll(*mSendMessage, aFrame);
break;
+
+ case Message::kTypeSupervision:
+ error = SendEmptyFrame(aFrame, kSupervisionMsgAckRequest);
+ mMessageNextOffset = mSendMessage->GetLength();
+ break;
}
assert(error == kThreadError_None);
@@ -1328,7 +1354,7 @@ exit:
return error;
}
-ThreadError MeshForwarder::SendEmptyFrame(Mac::Frame &aFrame)
+ThreadError MeshForwarder::SendEmptyFrame(Mac::Frame &aFrame, bool aAckRequest)
{
uint16_t fcf;
uint8_t secCtl;
@@ -1350,7 +1376,10 @@ ThreadError MeshForwarder::SendEmptyFrame(Mac::Frame &aFrame)
fcf |= (mMacDest.mLength == 2) ? Mac::Frame::kFcfDstAddrShort : Mac::Frame::kFcfDstAddrExt;
fcf |= (macSource.mLength == 2) ? Mac::Frame::kFcfSrcAddrShort : Mac::Frame::kFcfSrcAddrExt;
- // Not requesting acknowledgment for null/empty frame.
+ if (aAckRequest)
+ {
+ fcf |= Mac::Frame::kFcfAckRequest;
+ }
fcf |= Mac::Frame::kFcfSecurityEnabled;
secCtl = Mac::Frame::kKeyIdMode1;
@@ -1532,6 +1561,11 @@ void MeshForwarder::HandleSentFrame(Mac::Frame &aFrame, ThreadError aError)
mSourceMatchController.DecrementMessageCount(*child);
}
}
+
+ if (aError == kThreadError_None)
+ {
+ mNetif.GetChildSupervisor().UpdateOnSend(*child);
+ }
}
VerifyOrExit(mSendMessage != NULL);
@@ -1663,6 +1697,8 @@ void MeshForwarder::HandleReceivedFrame(Mac::Frame &aFrame)
payload = aFrame.GetPayload();
payloadLength = aFrame.GetPayloadLength();
+ mNetif.GetSupervisionListener().UpdateOnReceive(macSource, messageInfo.mLinkSecurity);
+
mDataPollManager.CheckFramePending(aFrame);
switch (aFrame.GetType())
diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp
index 309ff3612..19904818a 100644
--- a/src/core/thread/mesh_forwarder.hpp
+++ b/src/core/thread/mesh_forwarder.hpp
@@ -111,7 +111,9 @@ public:
*
* @param[in] aMessage A reference to the message.
*
- * @retval kThreadError_None Successfully enqueued the message.
+ * @retval kThreadError_None Successfully enqueued the message.
+ * @retval kThreadError_Already The message was already enqueued.
+ * @retval kThreadError_Drop The message could not be sent and should be dropped.
*
*/
ThreadError SendMessage(Message &aMessage);
@@ -232,6 +234,12 @@ private:
*
*/
kMaxPollTriggeredTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_POLLS,
+
+ /**
+ * Indicates whether to set/enable 15.4 ack request in the MAC header of a supervision message.
+ *
+ */
+ kSupervisionMsgAckRequest = (OPENTHREAD_CONFIG_SUPERVISION_MSG_NO_ACK_REQUEST == 0) ? true : false,
};
enum MessageAction ///< Defines the action parameter in `LogMessageInfo()` method.
@@ -263,7 +271,7 @@ private:
ThreadError SendPoll(Message &aMessage, Mac::Frame &aFrame);
ThreadError SendMesh(Message &aMessage, Mac::Frame &aFrame);
ThreadError SendFragment(Message &aMessage, Mac::Frame &aFrame);
- ThreadError SendEmptyFrame(Mac::Frame &aFrame);
+ ThreadError SendEmptyFrame(Mac::Frame &aFrame, bool aAckRequest);
ThreadError UpdateIp6Route(Message &aMessage);
ThreadError UpdateMeshRoute(Message &aMessage);
ThreadError HandleDatagram(Message &aMessage, const ThreadMessageInfo &aMessageInfo,
diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp
index 5bfad985f..0bf31f1e8 100644
--- a/src/core/thread/mle.cpp
+++ b/src/core/thread/mle.cpp
@@ -3234,7 +3234,7 @@ bool Mle::IsAnycastLocator(const Ip6::Address &aAddress) const
return memcmp(&mMeshLocal16, &aAddress, kRlocPrefixLength) == 0 && aAddress.mFields.m8[14] == Ip6::Address::kAloc16Mask;
}
-Router *Mle::GetParent()
+Router *Mle::GetParent(void)
{
if ((!mParent.IsStateValidOrRestoring()) && (mParentCandidate.GetState() == Neighbor::kStateValid))
{
diff --git a/src/core/thread/mle_router_ftd.hpp b/src/core/thread/mle_router_ftd.hpp
index 6f0392c60..ac4a455a9 100644
--- a/src/core/thread/mle_router_ftd.hpp
+++ b/src/core/thread/mle_router_ftd.hpp
@@ -644,6 +644,15 @@ public:
*/
void FillRouteTlv(RouteTlv &aTlv);
+ /**
+ * This method generates an MLE Child Update Request message to be sent to the parent.
+ *
+ * @retval kThreadError_None Successfully generated an MLE Child Update Request message.
+ * @retval kThreadError_NoBufs Insufficient buffers to generate the MLE Child Update Request message.
+ *
+ */
+ ThreadError SendChildUpdateRequest(void) { return Mle::SendChildUpdateRequest(); }
+
#if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB
/**
* This method sets steering data out of band
diff --git a/src/core/thread/mle_router_mtd.hpp b/src/core/thread/mle_router_mtd.hpp
index eb292e6b5..6e67681f1 100644
--- a/src/core/thread/mle_router_mtd.hpp
+++ b/src/core/thread/mle_router_mtd.hpp
@@ -122,6 +122,8 @@ public:
void FillConnectivityTlv(ConnectivityTlv &) { }
void FillRouteTlv(RouteTlv &) { }
+ ThreadError SendChildUpdateRequest(void) { return Mle::SendChildUpdateRequest(); }
+
#if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB
ThreadError SetSteeringData(otExtAddress *) { return kThreadError_NotImplemented; };
#endif // OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB
diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp
index cf0306b0f..e0e68653c 100644
--- a/src/core/thread/thread_netif.cpp
+++ b/src/core/thread/thread_netif.cpp
@@ -107,6 +107,8 @@ ThreadNetif::ThreadNetif(Ip6::Ip6 &aIp6):
mLeader(*this),
mAddressResolver(*this),
#endif // OPENTHREAD_FTD
+ mChildSupervisor(*this),
+ mSupervisionListener(*this),
mAnnounceBegin(*this),
mPanIdQuery(*this),
mEnergyScan(*this)
@@ -130,6 +132,7 @@ ThreadError ThreadNetif::Up(void)
#if OPENTHREAD_ENABLE_DNS_CLIENT
mDnsClient.Start();
#endif
+ mChildSupervisor.Start();
mMleRouter.Enable();
mIsUp = true;
}
@@ -147,6 +150,7 @@ ThreadError ThreadNetif::Down(void)
#if OPENTHREAD_ENABLE_DNS_CLIENT
mDnsClient.Stop();
#endif
+ mChildSupervisor.Stop();
mMleRouter.Disable();
mMeshForwarder.Stop();
mIp6.RemoveNetif(*this);
diff --git a/src/core/thread/thread_netif.hpp b/src/core/thread/thread_netif.hpp
index 0a9f23097..0a2bba6a1 100644
--- a/src/core/thread/thread_netif.hpp
+++ b/src/core/thread/thread_netif.hpp
@@ -66,6 +66,7 @@
#include
#include
#include
+#include
#if OPENTHREAD_ENABLE_JAM_DETECTION
#include
@@ -397,6 +398,22 @@ public:
MeshCoP::BorderAgentProxy &GetBorderAgentProxy(void) { return mBorderAgentProxy; }
#endif // OPENTHREAD_ENABLE_BORDER_AGENT_PROXY && OPENTHREAD_FTD
+ /**
+ * This method returns a reference to the child supervisor object.
+ *
+ * @returns A reference to the child supervisor object.
+ *
+ */
+ Utils::ChildSupervisor &GetChildSupervisor(void) { return mChildSupervisor; }
+
+ /**
+ * This method returns a reference to the supervision listener object.
+ *
+ * @returns A reference to the supervision listener object.
+ *
+ */
+ Utils::SupervisionListener &GetSupervisionListener(void) { return mSupervisionListener; }
+
/**
* This method returns the pointer to the parent otInstance structure.
*
@@ -462,6 +479,8 @@ private:
AddressResolver mAddressResolver;
#endif // OPENTHREAD_FTD
+ Utils::ChildSupervisor mChildSupervisor;
+ Utils::SupervisionListener mSupervisionListener;
AnnounceBeginServer mAnnounceBegin;
PanIdQueryServer mPanIdQuery;
EnergyScanServer mEnergyScan;
diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp
index 6c92b7367..fab95b15a 100644
--- a/src/core/thread/topology.hpp
+++ b/src/core/thread/topology.hpp
@@ -525,7 +525,7 @@ public:
void SetIndirectDataSequenceNumber(uint8_t aDsn) { mIndirectDsn = aDsn; }
/**
- * This method indicates whether or not to source match on the source address.
+ * This method indicates whether or not to source match on the short address.
*
* @returns TRUE if using the short address, FALSE if using the extended address.
*
@@ -533,7 +533,7 @@ public:
bool IsIndirectSourceMatchShort(void) const { return mUseShortAddress; }
/**
- * This method sets whether or not to source match on the source address.
+ * This method sets whether or not to source match on the short address.
*
* @param[in] aShort TRUE if using the short address, FALSE if using the extended address.
*
@@ -617,6 +617,30 @@ public:
*/
const Mac::Address &GetMacAddress(Mac::Address &aMacAddress) const;
+#if OPENTHREAD_ENABLE_CHILD_SUPERVISION
+
+ /**
+ * This method increments the number of seconds since last supervision of the child.
+ *
+ */
+ void IncrementSecondsSinceLastSupervision(void) { mSecondsSinceSupervision++; }
+
+ /**
+ * This method returns the number of seconds since last supervision of the child (last message to the child)
+ *
+ * @returns Number of seconds since last supervision of the child.
+ *
+ */
+ uint16_t GetSecondsSinceLastSupervision(void) const { return mSecondsSinceSupervision; }
+
+ /**
+ * This method resets the number of seconds since last supervision of the child to zero.
+ *
+ */
+ void ResetSecondsSinceLastSupervision(void) { mSecondsSinceSupervision = 0; }
+
+#endif // #if OPENTHREAD_ENABLE_CHILD_SUPERVISION
+
private:
Ip6::Address mIp6Address[kMaxIp6AddressPerChild]; ///< Registered IPv6 addresses
uint32_t mTimeout; ///< Child timeout
@@ -637,6 +661,11 @@ private:
uint16_t mQueuedMessageCount : 13; ///< Number of queued indirect messages for the child.
bool mUseShortAddress : 1; ///< Indicates whether to use short or extended address.
bool mSourceMatchPending : 1; ///< Indicates whether or not pending to add to src match table.
+
+#if OPENTHREAD_ENABLE_CHILD_SUPERVISION
+ uint16_t mSecondsSinceSupervision; ///< Number of seconds since last supervision of the child.
+#endif // OPENTHREAD_ENABLE_CHILD_SUPERVISION
+
};
/**
diff --git a/src/core/utils/child_supervision.cpp b/src/core/utils/child_supervision.cpp
new file mode 100644
index 000000000..5346a72eb
--- /dev/null
+++ b/src/core/utils/child_supervision.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2017, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * This file implements the child supervision feature.
+ */
+
+
+#include "openthread/openthread.h"
+
+#include
+#include
+#include
+
+namespace ot {
+namespace Utils {
+
+#if OPENTHREAD_ENABLE_CHILD_SUPERVISION
+
+#if OPENTHREAD_FTD
+
+ChildSupervisor::ChildSupervisor(ThreadNetif &aThreadNetif) :
+ mNetif(aThreadNetif),
+ mTimer(aThreadNetif.GetIp6().mTimerScheduler, &ChildSupervisor::HandleTimer, this),
+ mSupervisionInterval(kDefaultSupervisionInterval)
+{
+}
+
+void ChildSupervisor::Start(void)
+{
+ VerifyOrExit(mSupervisionInterval != 0);
+ VerifyOrExit(!mTimer.IsRunning());
+ mTimer.Start(kOneSecond);
+
+exit:
+ return;
+}
+
+void ChildSupervisor::Stop(void)
+{
+ mTimer.Stop();
+}
+
+void ChildSupervisor::SetSupervisionInterval(uint16_t aInterval)
+{
+ mSupervisionInterval = aInterval;
+ Start();
+}
+
+Child *ChildSupervisor::GetDestination(const Message &aMessage) const
+{
+ Child *child = NULL;
+ uint8_t childIndex;
+ uint8_t numChildren;
+
+ VerifyOrExit(aMessage.GetType() == Message::kTypeSupervision);
+
+ aMessage.Read(0, sizeof(childIndex), &childIndex);
+ child = mNetif.GetMle().GetChildren(&numChildren);
+ VerifyOrExit(childIndex < numChildren, child = NULL);
+ child += childIndex;
+
+exit:
+ return child;
+}
+
+void ChildSupervisor::SendMessage(Child &aChild)
+{
+ Message *message = NULL;
+ ThreadError error = kThreadError_None;
+ uint8_t childIndex;
+
+ VerifyOrExit(aChild.GetIndirectMessageCount() == 0);
+
+ message = mNetif.GetIp6().mMessagePool.New(Message::kTypeSupervision, sizeof(uint8_t));
+ VerifyOrExit(message != NULL);
+
+ // Supervision message is an empty payload 15.4 data frame.
+ // The child index is stored here in the message content to allow
+ // the destination of the message to be later retrieved using
+ // `ChildSupervisor::GetDestination(message)`.
+
+ childIndex = mNetif.GetMle().GetChildIndex(aChild);
+ SuccessOrExit(error = message->Append(&childIndex, sizeof(childIndex)));
+
+ SuccessOrExit(error = mNetif.SendMessage(*message));
+ message = NULL;
+
+exit:
+
+ if (message != NULL)
+ {
+ message->Free();
+ }
+}
+
+void ChildSupervisor::UpdateOnSend(Child &aChild)
+{
+ aChild.ResetSecondsSinceLastSupervision();
+}
+
+void ChildSupervisor::HandleTimer(void *aContext)
+{
+ static_cast(aContext)->HandleTimer();
+}
+
+void ChildSupervisor::HandleTimer(void)
+{
+ Child *child;
+ uint8_t numChildren;
+
+ VerifyOrExit(mSupervisionInterval != 0);
+
+ child = mNetif.GetMle().GetChildren(&numChildren);
+
+ for (uint8_t i = 0; i < numChildren; i++, child++)
+ {
+ if (!child->IsStateValidOrRestoring())
+ {
+ continue;
+ }
+
+ child->IncrementSecondsSinceLastSupervision();
+
+ if ((child->GetSecondsSinceLastSupervision() >= mSupervisionInterval) && (child->IsRxOnWhenIdle() == false))
+ {
+ SendMessage(*child);
+ }
+ }
+
+ mTimer.Start(kOneSecond);
+
+exit:
+ return;
+}
+
+#endif // #if OPENTHREAD_FTD
+
+SupervisionListener::SupervisionListener(ThreadNetif &aThreadNetif) :
+ mNetif(aThreadNetif),
+ mTimer(aThreadNetif.GetIp6().mTimerScheduler, &SupervisionListener::HandleTimer, this),
+ mTimeout(0)
+{
+ SetTimeout(kDefaultTimeout);
+}
+
+void SupervisionListener::Start(void)
+{
+ RestartTimer();
+}
+
+void SupervisionListener::Stop(void)
+{
+ mTimer.Stop();
+}
+
+void SupervisionListener::SetTimeout(uint16_t aTimeout)
+{
+ if (mTimeout != aTimeout)
+ {
+ mTimeout = aTimeout;
+ RestartTimer();
+ }
+}
+
+void SupervisionListener::UpdateOnReceive(const Mac::Address &aSourceAddress, bool aIsSecure)
+{
+ // If listener is enabled and device is a child and it received a secure frame from its parent, restart the timer.
+
+ VerifyOrExit(mTimer.IsRunning() && aIsSecure && (mNetif.GetMle().GetDeviceState() == Mle::kDeviceStateChild) &&
+ (mNetif.GetMle().GetNeighbor(aSourceAddress) == mNetif.GetMle().GetParent()));
+
+ RestartTimer();
+
+exit:
+ return;
+}
+
+void SupervisionListener::RestartTimer(void)
+{
+ // Restart the timer, if the timeout value is non-zero and the device is a sleepy child.
+
+ if ((mTimeout != 0) && (mNetif.GetMle().GetDeviceState() == Mle::kDeviceStateChild) &&
+ (mNetif.GetMeshForwarder().GetRxOnWhenIdle() == false))
+ {
+ mTimer.Start(Timer::SecToMsec(mTimeout));
+ }
+ else
+ {
+ mTimer.Stop();
+ }
+}
+
+void SupervisionListener::HandleTimer(void *aContext)
+{
+ static_cast(aContext)->HandleTimer();
+}
+
+void SupervisionListener::HandleTimer(void)
+{
+ VerifyOrExit((mNetif.GetMle().GetDeviceState() == Mle::kDeviceStateChild) &&
+ (mNetif.GetMeshForwarder().GetRxOnWhenIdle() == false));
+
+ mNetif.GetMle().SendChildUpdateRequest();
+
+exit:
+ RestartTimer();
+}
+
+#endif // #if OPENTHREAD_ENABLE_CHILD_SUPERVISION
+
+} // namespace Utils
+} // namespace ot
diff --git a/src/core/utils/child_supervision.hpp b/src/core/utils/child_supervision.hpp
new file mode 100644
index 000000000..8e6f09758
--- /dev/null
+++ b/src/core/utils/child_supervision.hpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2017, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * This file includes definitions for child supervision feature.
+ *
+ */
+
+#ifndef CHILD_SUPERVISION_HPP_
+#define CHILD_SUPERVISION_HPP_
+
+#ifdef OPENTHREAD_CONFIG_FILE
+#include OPENTHREAD_CONFIG_FILE
+#else
+#include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+namespace ot {
+
+class ThreadNetif;
+
+namespace Utils {
+
+/**
+ *
+ * Child supervision feature provides a mechanism for parent
+ * to ensure that a message is sent to each sleepy child within
+ * a fixed interval, namely the supervision interval. If there
+ * is no transmission to the child within the supervision
+ * interval, child supervisor enqueues and sends a supervision
+ * message (a data message with empty payload) to the child.
+ *
+ * On the child side, this is used to check the connectivity
+ * to the parent. If the child does not hear from its parent
+ * for a pre-specified timeout interval it assumes that it may
+ * be disconnected and tries to re-attach to the parent.
+ *
+ * The child supervision provides an alternative, more
+ * energy-efficient solution compared to requiring the sleepy child
+ * to periodically perform an MLE Child Update Request/Response
+ * exchange with the parent (as a way of verifying that it
+ * is still connected to the parent). The child supervision
+ * solution puts the burden of message transmissions on the
+ * parent instead of the typically more energy-constrained child.
+ *
+ * Note that most radios generate an auto-ack in hardware in
+ * response to a received frame, so the child cannot solely rely
+ * on the 15.4 acknowledgments it receives from parent as an
+ * indicator that it is still connected and is in parent's
+ * child table.
+ *
+ */
+
+#if OPENTHREAD_ENABLE_CHILD_SUPERVISION && OPENTHREAD_FTD
+
+/**
+ * This class implements a child supervisor.
+ *
+ */
+class ChildSupervisor
+{
+public:
+ /**
+ * This constructor initializes the object.
+ *
+ * @param[in] aThreadNetif A reference to the Thread network interface.
+ *
+ */
+ explicit ChildSupervisor(ThreadNetif &aThreadNetif);
+
+ /**
+ * This method starts the child supervision process on parent.
+ *
+ */
+ void Start(void);
+
+ /**
+ * This method stops the child supervision process on parent.
+ *
+ */
+ void Stop(void);
+
+ /**
+ * This method sets the supervision interval.
+ *
+ * Setting the supervision interval to a non-zero value will ensure to start the supervision process (if not
+ * already started).
+ *
+ * @param[in] aInterval If non-zero, the desired supervision interval (in seconds), zero to disable supervision.
+ *
+ */
+ void SetSupervisionInterval(uint16_t aInterval);
+
+ /**
+ * This method returns the supervision interval.
+ *
+ * @returns The current supervision interval (seconds), or zero if supervision is disabled.
+ *
+ */
+ uint16_t GetSupervisionInterval(void) const { return mSupervisionInterval; }
+
+ /**
+ * This method returns the destination for a supervision message.
+ *
+ * @param[in] aMessage The message for which to get the destination.
+ *
+ * @returns A pointer to the destination child of the message, or NULL if @p aMessage is not of supervision type.
+ *
+ */
+ Child *GetDestination(const Message &aMessage) const;
+
+ /**
+ * This method updates the supervision state for a child. It informs the child supervisor that a message was
+ * successfully sent to the child.
+ *
+ * @param[in] aChild The child to which a message was successfully sent.
+ *
+ */
+ void UpdateOnSend(Child &aChild);
+
+private:
+ enum
+ {
+ kDefaultSupervisionInterval = OPENTHREAD_CONFIG_CHILD_SUPERVISION_INTERVAL, // (seconds)
+ kOneSecond = 1000, // One second interval (in ms).
+ };
+
+ void SendMessage(Child &aChild);
+ static void HandleTimer(void *aContext);
+ void HandleTimer(void);
+
+ ThreadNetif &mNetif;
+ Timer mTimer;
+ uint16_t mSupervisionInterval;
+};
+
+#else // #if OPENTHREAD_ENABLE_CHILD_SUPERVISION && OPENTHREAD_FTD
+
+class ChildSupervisor
+{
+public:
+ explicit ChildSupervisor(ThreadNetif &) { }
+ void Start(void) { }
+ void Stop(void) { }
+ void SetSupervisionInterval(uint16_t) { }
+ uint16_t GetSupervisionInterval(void) const { return 0; }
+ Child *GetDestination(const Message &) const { return NULL; }
+ void UpdateOnSend(Child &) { }
+};
+
+#endif // #if OPENTHREAD_ENABLE_CHILD_SUPERVISION && OPENTHREAD_FTD
+
+#if OPENTHREAD_ENABLE_CHILD_SUPERVISION
+
+/**
+ * This class implements a child supervision listener.
+ *
+ */
+class SupervisionListener
+{
+public:
+ /**
+ * This constructor initializes the object.
+ *
+ * @param[in] aThreadNetif A reference to the Thread network interface.
+ *
+ */
+ explicit SupervisionListener(ThreadNetif &aThreadNetif);
+
+ /**
+ * This method starts the supervision listener operation.
+ *
+ */
+ void Start(void);
+
+ /**
+ * This method stops the supervision listener operation.
+ *
+ */
+ void Stop(void);
+
+ /**
+ * This method sets the supervision check timeout (in seconds).
+ *
+ * If the child does not hear from its parent within the given check timeout interval, it initiates the re-attach
+ * process (MLE Child Update Request/Response exchange with its parent). Setting the timeout to zero, disables the
+ * supervision check on the child.
+ *
+ * It is recommended to select a supervision check timeout value larger than the parent's child supervision
+ * interval plus the maximum time between the child's data poll transmissions.
+ *
+ * @param[in] aTimeout The timeout interval (in seconds), zero to disable the supervision check on the child.
+ *
+ */
+ void SetTimeout(uint16_t aTimeout);
+
+ /**
+ * This method returns the supervision check timeout interval (in seconds).
+ *
+ * @returns The check timeout interval (in seconds) or zero if the supervision check on the child is disabled.
+ *
+ */
+ uint16_t GetTimeout(void) const { return mTimeout; }
+
+ /**
+ * This method updates the supervision listener state. It informs the listener of a received frame.
+ *
+ * @param[in] aSource The source MAC address of the received frame
+ * @param[in] aIsSecure TRUE to indicate that the received frame is secure, FALSE otherwise.
+ *
+ */
+ void UpdateOnReceive(const Mac::Address &aSource, bool aIsSeucre);
+
+private:
+ enum
+ {
+ kDefaultTimeout = OPENTHREAD_CONFIG_SUPERVISION_CHECK_TIMEOUT, // (seconds)
+ };
+
+ void RestartTimer(void);
+ static void HandleTimer(void *aContext);
+ void HandleTimer(void);
+
+ ThreadNetif &mNetif;
+ Timer mTimer;
+ uint16_t mTimeout;
+};
+
+#else // #if OPENTHREAD_ENABLE_CHILD_SUPERVISION
+
+class SupervisionListener
+{
+public:
+ SupervisionListener(ThreadNetif &) { }
+ void Start(void) { }
+ void Stop(void) { }
+ void SetTimeout(uint16_t) { }
+ uint16_t GetTimeout(void) const { return 0; }
+ void UpdateOnReceive(const Mac::Address &, bool) { }
+};
+
+#endif // #if OPENTHREAD_ENABLE_CHILD_SUPERVISION
+
+/**
+ * @}
+ *
+ */
+
+} // namespace Utils
+} // namespace ot
+
+#endif // CHILD_SUPERVISION_HPP_