diff --git a/etc/visual-studio/libopenthread.vcxproj b/etc/visual-studio/libopenthread.vcxproj
index e9cdd7bf4..d8ab3ed10 100644
--- a/etc/visual-studio/libopenthread.vcxproj
+++ b/etc/visual-studio/libopenthread.vcxproj
@@ -124,6 +124,7 @@
+
@@ -206,6 +207,7 @@
+
diff --git a/etc/visual-studio/libopenthread.vcxproj.filters b/etc/visual-studio/libopenthread.vcxproj.filters
index 472f8e824..1fd4e610a 100644
--- a/etc/visual-studio/libopenthread.vcxproj.filters
+++ b/etc/visual-studio/libopenthread.vcxproj.filters
@@ -345,6 +345,9 @@
Source Files\thread
+
+ Source Files\thread
+
Source Files\common
@@ -458,6 +461,9 @@
Header Files\thread
+
+ Header Files\thread
+
Header Files\thread
diff --git a/etc/visual-studio/libopenthread_k.vcxproj b/etc/visual-studio/libopenthread_k.vcxproj
index fed719489..c2fac623e 100644
--- a/etc/visual-studio/libopenthread_k.vcxproj
+++ b/etc/visual-studio/libopenthread_k.vcxproj
@@ -132,6 +132,7 @@
+
@@ -243,6 +244,7 @@
+
diff --git a/etc/visual-studio/libopenthread_k.vcxproj.filters b/etc/visual-studio/libopenthread_k.vcxproj.filters
index 45120444a..288725a99 100644
--- a/etc/visual-studio/libopenthread_k.vcxproj.filters
+++ b/etc/visual-studio/libopenthread_k.vcxproj.filters
@@ -333,6 +333,9 @@
Source Files\thread
+
+ Source Files\thread
+
Source Files\meshcop
@@ -590,6 +593,9 @@
Header Files\thread
+
+ Header Files\thread
+
Header Files\meshcop
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index 3f718fb62..a710739a8 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -171,6 +171,7 @@ SOURCES_COMMON = \
net/udp6.cpp \
thread/address_resolver.cpp \
thread/announce_begin_server.cpp \
+ thread/announce_sender.cpp \
thread/child_table.cpp \
thread/data_poll_manager.cpp \
thread/energy_scan_server.cpp \
@@ -292,6 +293,7 @@ HEADERS_COMMON = \
net/udp6.hpp \
thread/address_resolver.hpp \
thread/announce_begin_server.hpp \
+ thread/announce_sender.hpp \
thread/child_table.hpp \
thread/data_poll_manager.hpp \
thread/energy_scan_server.hpp \
diff --git a/src/core/common/instance.cpp b/src/core/common/instance.cpp
index c4cd65e64..26257e9cb 100644
--- a/src/core/common/instance.cpp
+++ b/src/core/common/instance.cpp
@@ -72,6 +72,9 @@ Instance::Instance(void)
#endif
#if OPENTHREAD_ENABLE_CHANNEL_MANAGER
, mChannelManager(*this)
+#endif
+#if OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER
+ , mAnnounceSender(*this)
#endif
, mMessagePool(*this)
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
@@ -417,6 +420,13 @@ template <> Utils::ChannelManager &Instance::Get(void)
}
#endif
+#if OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER
+template <> AnnounceSender &Instance::Get(void)
+{
+ return GetAnnounceSender();
+}
+#endif
+
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
#if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API
diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp
index 45ae38004..2c48f6c34 100644
--- a/src/core/common/instance.hpp
+++ b/src/core/common/instance.hpp
@@ -56,6 +56,7 @@
#include "common/notifier.hpp"
#include "common/settings.hpp"
#include "net/ip6.hpp"
+#include "thread/announce_sender.hpp"
#include "thread/link_quality.hpp"
#include "thread/thread_netif.hpp"
#if OPENTHREAD_ENABLE_CHANNEL_MANAGER
@@ -329,6 +330,16 @@ public:
Utils::ChannelManager &GetChannelManager(void) { return mChannelManager; }
#endif
+#if OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER
+ /**
+ * This method returns a reference to AnnounceSender object.
+ *
+ * @returns A reference to the AnnounceSender object.
+ *
+ */
+ AnnounceSender &GetAnnounceSender(void) { return mAnnounceSender; }
+#endif
+
/**
* This method returns a reference to message pool object.
*
@@ -404,6 +415,10 @@ private:
Utils::ChannelManager mChannelManager;
#endif
+#if OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER
+ AnnounceSender mAnnounceSender;
+#endif
+
MessagePool mMessagePool;
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
#if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API
diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp
index a67826710..70c287d8c 100644
--- a/src/core/mac/mac.cpp
+++ b/src/core/mac/mac.cpp
@@ -73,6 +73,19 @@ const uint32_t kMaxBackoffSum = kMinBackoff + (kUnitBackoffPeriod * OT_RADIO_SYM
static_assert(kMinBackoffSum > 0, "The min backoff value should be greater than zero!");
#endif
+uint8_t ChannelMask::GetNumberOfChannels(void) const
+{
+ uint8_t num = 0;
+ uint8_t channel = kChannelIteratorFirst;
+
+ while (GetNextChannel(channel) == OT_ERROR_NONE)
+ {
+ num++;
+ }
+
+ return num;
+}
+
otError ChannelMask::GetNextChannel(uint8_t &aChannel) const
{
otError error = OT_ERROR_NOT_FOUND;
diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp
index 472b3287f..08b0f2b78 100644
--- a/src/core/mac/mac.hpp
+++ b/src/core/mac/mac.hpp
@@ -204,6 +204,14 @@ public:
*/
void Intersect(const ChannelMask &aOtherMask) { mMask &= aOtherMask.mMask; }
+ /**
+ * This method returns the number of channels in the mask.
+ *
+ * @returns Number of channels in the mask.
+ *
+ */
+ uint8_t GetNumberOfChannels(void) const;
+
/**
* This method gets the next channel in the channel mask.
*
@@ -220,6 +228,26 @@ public:
*/
otError GetNextChannel(uint8_t &aChannel) const;
+ /**
+ * This method overloads `==` operator to indicate whether two masks are equal.
+ *
+ * @param[in] aAnother A reference to another mask to compare with the current one.
+ *
+ * @returns TRUE if the two masks are equal, FALSE otherwise.
+ *
+ */
+ bool operator==(const ChannelMask &aAnother) const { return (mMask == aAnother.mMask); }
+
+ /**
+ * This method overloads `!=` operator to indicate whether two masks are different.
+ *
+ * @param[in] aAnother A reference to another mask to compare with the current one.
+ *
+ * @returns TRUE if the two masks are different, FALSE otherwise.
+ *
+ */
+ bool operator!=(const ChannelMask &aAnother) const { return (mMask != aAnother.mMask); }
+
/**
* This method converts the channel mask into a human-readable NULL-terminated string.
*
diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h
index e4764232c..409353435 100644
--- a/src/core/openthread-core-default-config.h
+++ b/src/core/openthread-core-default-config.h
@@ -1353,6 +1353,51 @@
#define OPENTHREAD_CONFIG_SEND_UNICAST_ANNOUNCE_RESPONSE 1
#endif
+/**
+ * @def OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER
+ *
+ * Define as 1 to enable `AnnounceSender` which will periodically send MLE Announce message on all channels.
+ *
+ * The list of channels is determined from the Operational Dataset's ChannelMask. The period intervals are determined
+ * by `OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_ROUTER` and `OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_REED`
+ * configuration options.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER
+#define OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER 0
+#endif
+
+/**
+ * @def OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_ROUTER
+ *
+ * Specifies the time interval (in milliseconds) between `AnnounceSender` transmit cycles on a device in Router role.
+ *
+ * In a cycle, the `AnnounceSender` sends MLE Announcement on all channels in Active Operational Dataset's ChannelMask.
+ * The transmissions on different channels happen uniformly over the given interval (i.e., if there are 16 channels,
+ * there will be 16 MLE Announcement messages each on one channel with `interval / 16` between two consecutive MLE
+ * Announcement transmissions).
+ *
+ * Applicable only if `AnnounceSender` feature is enabled (see `OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER`).
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_ROUTER
+#define OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_ROUTER 688000 // 668 seconds = 11 min and 28 sec.
+#endif
+
+/**
+ * @def OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_REED
+ *
+ * Specifies the time interval (in milliseconds) between `AnnounceSender` transmit cycles on a device in REED role.
+ *
+ * This is similar to `OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_ROUTER` but used when device is in REED role.
+ *
+ * Applicable only if `AnnounceSender` feature is enabled (see `OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER`).
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_REED
+#define OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_REED (668000 * 3)
+#endif
+
/**
* @def OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
*
diff --git a/src/core/thread/announce_begin_server.cpp b/src/core/thread/announce_begin_server.cpp
index e6ce43806..9a94f9674 100644
--- a/src/core/thread/announce_begin_server.cpp
+++ b/src/core/thread/announce_begin_server.cpp
@@ -52,12 +52,7 @@ using ot::Encoding::BigEndian::HostSwap32;
namespace ot {
AnnounceBeginServer::AnnounceBeginServer(Instance &aInstance)
- : InstanceLocator(aInstance)
- , mChannelMask(0)
- , mPeriod(0)
- , mCount(0)
- , mChannel(0)
- , mTimer(aInstance, &AnnounceBeginServer::HandleTimer, this)
+ : AnnounceSenderBase(aInstance, &AnnounceBeginServer::HandleTimer)
, mAnnounceBegin(OT_URI_PATH_ANNOUNCE_BEGIN, &AnnounceBeginServer::HandleRequest, this)
{
GetNetif().GetCoap().AddResource(mAnnounceBegin);
@@ -70,23 +65,7 @@ otError AnnounceBeginServer::SendAnnounce(uint32_t aChannelMask)
otError AnnounceBeginServer::SendAnnounce(uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod)
{
- otError error = OT_ERROR_NONE;
-
- mChannelMask = aChannelMask;
- mCount = aCount;
- mPeriod = aPeriod;
- mChannel = OT_RADIO_CHANNEL_MIN;
-
- while ((mChannelMask & (1 << mChannel)) == 0)
- {
- mChannel++;
- VerifyOrExit(mChannel <= OT_RADIO_CHANNEL_MAX, error = OT_ERROR_INVALID_ARGS);
- }
-
- mTimer.Start(mPeriod);
-
-exit:
- return error;
+ return AnnounceSenderBase::SendAnnounce(Mac::ChannelMask(aChannelMask), aCount, aPeriod, kDefaultJitter);
}
void AnnounceBeginServer::HandleRequest(void * aContext,
@@ -98,6 +77,7 @@ void AnnounceBeginServer::HandleRequest(void * aContext,
*static_cast(aMessage),
*static_cast(aMessageInfo));
}
+
void AnnounceBeginServer::HandleRequest(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
MeshCoP::ChannelMask0Tlv channelMask;
@@ -130,29 +110,7 @@ exit:
void AnnounceBeginServer::HandleTimer(Timer &aTimer)
{
- aTimer.GetOwner().HandleTimer();
-}
-
-void AnnounceBeginServer::HandleTimer(void)
-{
- GetNetif().GetMle().SendAnnounce(mChannel++, false);
-
- while (mCount > 0)
- {
- if (mChannelMask & (1 << mChannel))
- {
- mTimer.Start(mPeriod);
- break;
- }
-
- mChannel++;
-
- if (mChannel > OT_RADIO_CHANNEL_MAX)
- {
- mChannel = OT_RADIO_CHANNEL_MIN;
- mCount--;
- }
- }
+ aTimer.GetOwner().AnnounceSenderBase::HandleTimer();
}
} // namespace ot
diff --git a/src/core/thread/announce_begin_server.hpp b/src/core/thread/announce_begin_server.hpp
index 8b800fbfb..2c5dafdad 100644
--- a/src/core/thread/announce_begin_server.hpp
+++ b/src/core/thread/announce_begin_server.hpp
@@ -42,6 +42,7 @@
#include "common/locator.hpp"
#include "common/timer.hpp"
#include "net/ip6_address.hpp"
+#include "thread/announce_sender.hpp"
namespace ot {
@@ -49,7 +50,7 @@ namespace ot {
* This class implements handling Announce Begin Requests.
*
*/
-class AnnounceBeginServer : public InstanceLocator
+class AnnounceBeginServer : public AnnounceSenderBase
{
public:
/**
@@ -85,6 +86,7 @@ private:
{
kDefaultCount = 3,
kDefaultPeriod = 1000,
+ kDefaultJitter = 0,
};
static void HandleRequest(void * aContext,
@@ -94,14 +96,6 @@ private:
void HandleRequest(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
static void HandleTimer(Timer &aTimer);
- void HandleTimer(void);
-
- uint32_t mChannelMask;
- uint16_t mPeriod;
- uint8_t mCount;
- uint8_t mChannel;
-
- TimerMilli mTimer;
Coap::Resource mAnnounceBegin;
};
diff --git a/src/core/thread/announce_sender.cpp b/src/core/thread/announce_sender.cpp
new file mode 100644
index 000000000..3c7ca9941
--- /dev/null
+++ b/src/core/thread/announce_sender.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2016-2018, 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 AnnounceSender.
+ */
+
+#define WPP_NAME "announce_sender.tmh"
+
+#include "announce_sender.hpp"
+
+#include
+
+#include "common/code_utils.hpp"
+#include "common/instance.hpp"
+#include "common/logging.hpp"
+#include "common/owner-locator.hpp"
+#include "common/random.hpp"
+#include "meshcop/meshcop.hpp"
+#include "meshcop/meshcop_tlvs.hpp"
+
+namespace ot {
+
+AnnounceSenderBase::AnnounceSenderBase(Instance &aInstance, Timer::Handler aHandler)
+ : InstanceLocator(aInstance)
+ , mChannelMask()
+ , mPeriod(0)
+ , mJitter(0)
+ , mCount(0)
+ , mChannel(0)
+ , mTimer(aInstance, aHandler, this)
+{
+}
+
+otError AnnounceSenderBase::SendAnnounce(Mac::ChannelMask aMask, uint8_t aCount, uint32_t aPeriod, uint16_t aJitter)
+{
+ otError error = OT_ERROR_NONE;
+
+ VerifyOrExit(aPeriod != 0, error = OT_ERROR_INVALID_ARGS);
+ VerifyOrExit(aJitter < aPeriod, error = OT_ERROR_INVALID_ARGS);
+
+ aMask.Intersect(OT_RADIO_SUPPORTED_CHANNELS);
+ VerifyOrExit(!aMask.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+
+ mChannelMask = aMask;
+ mCount = aCount;
+ mPeriod = aPeriod;
+ mJitter = aJitter;
+ mChannel = Mac::ChannelMask::kChannelIteratorFirst;
+
+ mTimer.Start(Random::AddJitter(mPeriod, mJitter));
+
+exit:
+ return error;
+}
+
+void AnnounceSenderBase::HandleTimer(void)
+{
+ otError error;
+
+ error = mChannelMask.GetNextChannel(mChannel);
+
+ if (error == OT_ERROR_NOT_FOUND)
+ {
+ if (mCount != 0)
+ {
+ mCount--;
+ VerifyOrExit(mCount != 0);
+ }
+
+ mChannel = Mac::ChannelMask::kChannelIteratorFirst;
+ error = mChannelMask.GetNextChannel(mChannel);
+ }
+
+ assert(error == OT_ERROR_NONE);
+
+ GetNetif().GetMle().SendAnnounce(mChannel, false);
+
+ mTimer.Start(Random::AddJitter(mPeriod, mJitter));
+
+exit:
+ return;
+}
+
+#if OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER
+
+AnnounceSender::AnnounceSender(Instance &aInstance)
+ : AnnounceSenderBase(aInstance, &AnnounceSender::HandleTimer)
+ , mNotifierCallback(HandleStateChanged, this)
+{
+ aInstance.GetNotifier().RegisterCallback(mNotifierCallback);
+}
+
+void AnnounceSender::HandleTimer(Timer &aTimer)
+{
+ aTimer.GetOwner().AnnounceSenderBase::HandleTimer();
+}
+
+otError AnnounceSender::GetActiveDatasetChannelMask(Mac::ChannelMask &aMask) const
+{
+ otError error = OT_ERROR_NONE;
+ const MeshCoP::ChannelMask0Tlv *channelMaskTlv;
+ MeshCoP::Dataset dataset(MeshCoP::Tlv::kActiveTimestamp);
+
+ SuccessOrExit(error = GetNetif().GetActiveDataset().Get(dataset));
+
+ channelMaskTlv = static_cast(dataset.Get(MeshCoP::Tlv::kChannelMask));
+ VerifyOrExit(channelMaskTlv != NULL, error = OT_ERROR_NOT_FOUND);
+
+ aMask.SetMask(channelMaskTlv->GetMask());
+
+exit:
+ return error;
+}
+
+void AnnounceSender::CheckState(void)
+{
+ Mle::MleRouter & mle = GetInstance().Get();
+ uint32_t interval = kRouterTxInterval;
+ uint32_t period;
+ Mac::ChannelMask channelMask;
+ char string[Mac::ChannelMask::kInfoStringSize];
+
+ switch (mle.GetRole())
+ {
+ case OT_DEVICE_ROLE_ROUTER:
+ case OT_DEVICE_ROLE_LEADER:
+ period = kRouterTxInterval;
+ break;
+
+ case OT_DEVICE_ROLE_CHILD:
+ if (mle.IsRouterRoleEnabled() && (mle.GetDeviceMode() & Mle::ModeTlv::kModeRxOnWhenIdle))
+ {
+ period = kReedTxInterval;
+ break;
+ }
+
+ // fall through
+
+ case OT_DEVICE_ROLE_DISABLED:
+ case OT_DEVICE_ROLE_DETACHED:
+ Stop();
+ ExitNow();
+ }
+
+ VerifyOrExit(GetActiveDatasetChannelMask(channelMask) == OT_ERROR_NONE, Stop());
+ channelMask.Intersect(OT_RADIO_SUPPORTED_CHANNELS);
+ VerifyOrExit(!channelMask.IsEmpty(), Stop());
+
+ period = interval / channelMask.GetNumberOfChannels();
+
+ if (period < kMinTxPeriod)
+ {
+ period = kMinTxPeriod;
+ }
+
+ VerifyOrExit(!IsRunning() || (period != GetPeriod()) || (GetChannelMask() != channelMask));
+
+ SendAnnounce(channelMask, 0, period, kMaxJitter);
+
+ otLogInfoMle(GetInstance(), "Starting periodic MLE Announcements tx, period %u, mask %s", period,
+ channelMask.ToString(string, sizeof(string)));
+
+ OT_UNUSED_VARIABLE(string);
+
+exit:
+ return;
+}
+
+void AnnounceSender::Stop(void)
+{
+ AnnounceSenderBase::Stop();
+ otLogInfoMle(GetInstance(), "Stopping periodic MLE Announcements tx");
+}
+
+void AnnounceSender::HandleStateChanged(Notifier::Callback &aCallback, uint32_t aFlags)
+{
+ aCallback.GetOwner().HandleStateChanged(aFlags);
+}
+
+void AnnounceSender::HandleStateChanged(uint32_t aFlags)
+{
+ if ((aFlags & OT_CHANGED_THREAD_ROLE) != 0)
+ {
+ CheckState();
+ }
+}
+
+#endif // OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER
+
+} // namespace ot
diff --git a/src/core/thread/announce_sender.hpp b/src/core/thread/announce_sender.hpp
new file mode 100644
index 000000000..17f76564f
--- /dev/null
+++ b/src/core/thread/announce_sender.hpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2016-2018, 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 definition for AnnounceSender.
+ */
+
+#ifndef ANNOUNCE_SENDER_HPP_
+#define ANNOUNCE_SENDER_HPP_
+
+#include "openthread-core-config.h"
+
+#include
+
+#include "common/locator.hpp"
+#include "common/notifier.hpp"
+#include "common/timer.hpp"
+#include "mac/mac.hpp"
+
+namespace ot {
+
+/**
+ * This class implements the base class for an `AnnounceSender` and `AnnoucneBeginSever`.
+ *
+ * This class provides APIs to schedule periodic transmission of MLE Announcement messages for a given number
+ * transmissions per channel.
+ */
+class AnnounceSenderBase : public InstanceLocator
+{
+protected:
+ /**
+ * This constructor initializes the object.
+ *
+ * @param[in] aInstance A reference to the OpenThread instance.
+ * @param[in] aHandler A timer handler provided by sub-class.
+ *
+ */
+ AnnounceSenderBase(Instance &aInstance, Timer::Handler aHandler);
+
+ /**
+ * This method schedules the MLE Announce transmissions.
+ *
+ * This method schedules `aCount` MLE Announcement transmission cycles. Each cycle covers all the channel in
+ * the `aChannelMask`, with `aPeriod` time interval between any two successive MLE Announcement transmissions
+ * (possibly) on different channels from the given mask. The `aJitter` can be used to add a random jitter
+ * of `[-aJitter, aJitter]` to `aPeriod` interval. A zero value for `aCount` indicates non-stop MLE Announcement
+ * transmission cycles.
+ *
+ * @param[in] aChannelMask The channel mask providing the list of channels to use for transmission.
+ * @param[in] aCount The number of transmissions per channel. Zero indicates non-stop transmissions.
+ * @param[in] aPeriod The time between two successive MLE Announce transmissions (in milliseconds).
+ * @param[in] aJitter Maximum random jitter added to @aPeriod per transmission (in milliseconds).
+ *
+ * @retval OT_ERROR_NONE Successfully started the transmission process.
+ * @retval OT_ERROR_INVALID_ARGS @p aChanelMask is empty, or @p aPeriod is zero or smaller than @aJitter.
+ *
+ */
+ otError SendAnnounce(Mac::ChannelMask aChannelMask, uint8_t aCount, uint32_t aPeriod, uint16_t aJitter);
+
+ /**
+ * This method stops the ongoing MLE Announce transmissions.
+ *
+ */
+ void Stop(void) { mTimer.Stop(); }
+
+ /**
+ * This method indicates whether the latest scheduled MLE Announce transmission is currently in progress or is
+ * finished.
+ *
+ * @returns TRUE if the MLE Announce transmission is in progress, FALSE otherwise.
+ *
+ */
+ bool IsRunning(void) const { return mTimer.IsRunning(); }
+
+ /**
+ * This method gets the period for the latest scheduled MLE Announce transmission (the one in progress or the last
+ * finished one).
+ *
+ * @returns The period interval (in milliseconds) between two successive MLE Announcement transmissions.
+ *
+ */
+ uint32_t GetPeriod(void) const { return mPeriod; }
+
+ /**
+ * This method gets the channel mask for the latest scheduled MLE Announce transmission (the one in progress or the
+ * last finished one).
+ *
+ * @returns A constant reference to channel mask
+ *
+ */
+ const Mac::ChannelMask GetChannelMask(void) const { return mChannelMask; }
+
+ /**
+ * This method is the timer handler and must be invoked by sub-class when the timer expires from the `aHandler`
+ * callback function provided in the constructor.
+ *
+ */
+ void HandleTimer(void);
+
+private:
+ Mac::ChannelMask mChannelMask;
+ uint32_t mPeriod;
+ uint16_t mJitter;
+ uint8_t mCount;
+ uint8_t mChannel;
+ TimerMilli mTimer;
+};
+
+#if OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER
+
+/**
+ * This class implements an AnnounceSender.
+ *
+ */
+class AnnounceSender : public AnnounceSenderBase
+{
+public:
+ /**
+ * This constructor initializes the object.
+ *
+ * @param[in] aInstance A reference to the OpenThread instance.
+ *
+ */
+ AnnounceSender(Instance &aInstance);
+
+private:
+ enum
+ {
+ kRouterTxInterval = OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_ROUTER,
+ kReedTxInterval = OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_REED,
+ kMinTxPeriod = 1000, // in ms
+ kMaxJitter = 500, // in ms
+ };
+
+ otError GetActiveDatasetChannelMask(Mac::ChannelMask &aMask) const;
+ void CheckState(void);
+ void Stop(void);
+ static void HandleTimer(Timer &aTimer);
+ static void HandleStateChanged(Notifier::Callback &aCallback, uint32_t aFlags);
+ void HandleStateChanged(uint32_t aFlags);
+
+ Notifier::Callback mNotifierCallback;
+};
+
+#endif // OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER
+
+/**
+ * @}
+ */
+
+} // namespace ot
+
+#endif // ANNOUNCE_SENDER_HPP_
diff --git a/src/core/thread/mle_router_mtd.hpp b/src/core/thread/mle_router_mtd.hpp
index 2453d1785..8a8df6dbd 100644
--- a/src/core/thread/mle_router_mtd.hpp
+++ b/src/core/thread/mle_router_mtd.hpp
@@ -59,6 +59,8 @@ public:
{
}
+ bool IsRouterRoleEnabled(void) const { return false; }
+
bool IsSingleton(void) { return false; }
otError BecomeRouter(ThreadStatusTlv::Status) { return OT_ERROR_NOT_CAPABLE; }
diff --git a/tests/unit/test_mac_frame.cpp b/tests/unit/test_mac_frame.cpp
index 9d8b3e47d..135eaf3ee 100644
--- a/tests/unit/test_mac_frame.cpp
+++ b/tests/unit/test_mac_frame.cpp
@@ -120,6 +120,8 @@ void VerifyChannelMaskContent(const Mac::ChannelMask &aMask, uint8_t *aChannels,
{
VerifyOrQuit(!aMask.IsSingleChannel(), "ChannelMask.IsSingleChannel() failed\n");
}
+
+ VerifyOrQuit(aLength == aMask.GetNumberOfChannels(), "ChannelMask.GetNumberOfChannels() failed\n");
}
void TestMacChannelMask(void)
@@ -191,6 +193,17 @@ void TestMacChannelMask(void)
VerifyChannelMaskContent(mask2, channles4, sizeof(channles4));
printf("channels4 = %s\n", mask2.ToString(stringBuffer, sizeof(stringBuffer)));
+
+ mask1.Clear();
+ mask2.Clear();
+ VerifyOrQuit(mask1 == mask2, "ChannelMask.operator== failed\n");
+
+ mask1.SetMask(OT_RADIO_SUPPORTED_CHANNELS);
+ mask2.SetMask(OT_RADIO_SUPPORTED_CHANNELS);
+ VerifyOrQuit(mask1 == mask2, "ChannelMask.operator== failed\n");
+
+ mask1.Clear();
+ VerifyOrQuit(mask1 != mask2, "ChannelMask.operator== failed\n");
}
} // namespace ot