Skip to content

Commit 7cb963e

Browse files
chrisdecenzolazarkovrestyled-io[bot]restyled-commits
authored
Linux sample implementation for Messages cluster (#32043)
* Linux sample implementation for Messages cluster * cleanup the chip:: and chip::app prefixes * CI fixes * Address comments * Make memory management less error prone * Fix CI * more feedback * fix CI * Restyled by clang-format (#32123) Co-authored-by: Restyled.io <commits@restyled.io> * address comments * Restyled by clang-format (#32136) Co-authored-by: Restyled.io <commits@restyled.io> * fix CI * address comments * address comments * Restyled by clang-format (#32144) Co-authored-by: Restyled.io <commits@restyled.io> * address comments * address comments * address comments --------- Co-authored-by: Lazar Kovacic <lkovacic@amazon.com> Co-authored-by: restyled-io[bot] <32688539+restyled-io[bot]@users.noreply.github.com> Co-authored-by: Restyled.io <commits@restyled.io>
1 parent bc86710 commit 7cb963e

File tree

4 files changed

+234
-40
lines changed

4 files changed

+234
-40
lines changed

examples/tv-app/tv-common/clusters/messages/MessagesManager.cpp

+56-26
Original file line numberDiff line numberDiff line change
@@ -18,64 +18,94 @@
1818
#include "MessagesManager.h"
1919

2020
#include <app-common/zap-generated/attributes/Accessors.h>
21+
#include <vector>
2122

2223
using namespace std;
24+
using namespace chip;
2325
using namespace chip::app;
2426
using namespace chip::app::Clusters::Messages;
25-
using Message = chip::app::Clusters::Messages::Structs::MessageStruct::Type;
27+
using Message = chip::app::Clusters::Messages::Structs::MessageStruct::Type;
28+
using MessageResponseOption = chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type;
2629

2730
// Commands
28-
void MessagesManager::HandlePresentMessagesRequest(
29-
const chip::ByteSpan & messageId, const MessagePriorityEnum & priority,
30-
const chip::BitMask<MessageControlBitmap> & messageControl, const chip::app::DataModel::Nullable<uint32_t> & startTime,
31-
const chip::app::DataModel::Nullable<uint16_t> & duration, const chip::CharSpan & messageText,
32-
const chip::Optional<chip::app::DataModel::DecodableList<MessageResponseOption>> & responses)
31+
CHIP_ERROR MessagesManager::HandlePresentMessagesRequest(
32+
const ByteSpan & messageId, const MessagePriorityEnum & priority, const BitMask<MessageControlBitmap> & messageControl,
33+
const DataModel::Nullable<uint32_t> & startTime, const DataModel::Nullable<uint16_t> & duration, const CharSpan & messageText,
34+
const Optional<DataModel::DecodableList<MessageResponseOption>> & responses)
3335
{
34-
Message message{
35-
// TODO: Enable id
36-
chip::ByteSpan(), priority, messageControl, startTime, duration,
37-
// TODO: Enable text
38-
chip::CharSpan()
39-
// TODO: Convert responses to Optional<chip::app::DataModel::List<const
40-
// chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type>> message.responses = responses;
41-
};
36+
ChipLogProgress(Zcl, "HandlePresentMessagesRequest message:%s", std::string(messageText.data(), messageText.size()).c_str());
37+
38+
auto cachedMessage = CachedMessage(messageId, priority, messageControl, startTime, duration,
39+
std::string(messageText.data(), messageText.size()));
40+
if (responses.HasValue())
41+
{
42+
auto iter = responses.Value().begin();
43+
while (iter.Next())
44+
{
45+
auto & response = iter.GetValue();
46+
47+
CachedMessageOption option(response.messageResponseID.Value(),
48+
std::string(response.label.Value().data(), response.label.Value().size()));
49+
50+
cachedMessage.AddOption(option);
51+
}
52+
}
53+
54+
mCachedMessages.push_back(cachedMessage);
4255

43-
mMessages.push_back(message);
4456
// Add your code to present Message
57+
ChipLogProgress(Zcl, "HandlePresentMessagesRequest complete");
58+
return CHIP_NO_ERROR;
4559
}
4660

47-
void MessagesManager::HandleCancelMessagesRequest(const chip::app::DataModel::DecodableList<chip::ByteSpan> & messageIds)
61+
CHIP_ERROR MessagesManager::HandleCancelMessagesRequest(const DataModel::DecodableList<ByteSpan> & messageIds)
4862
{
49-
// TODO: Cancel Message
63+
auto iter = messageIds.begin();
64+
while (iter.Next())
65+
{
66+
auto & id = iter.GetValue();
67+
68+
mCachedMessages.remove_if([id](CachedMessage & entry) { return entry.MessageIdMatches(id); });
69+
// per spec, the command succeeds even when the message id does not match an existing message
70+
}
71+
return CHIP_NO_ERROR;
5072
}
5173

5274
// Attributes
53-
CHIP_ERROR MessagesManager::HandleGetMessages(chip::app::AttributeValueEncoder & aEncoder)
75+
CHIP_ERROR MessagesManager::HandleGetMessages(AttributeValueEncoder & aEncoder)
5476
{
5577
return aEncoder.EncodeList([this](const auto & encoder) -> CHIP_ERROR {
56-
for (Message & entry : mMessages)
78+
for (CachedMessage & entry : mCachedMessages)
5779
{
58-
ReturnErrorOnFailure(encoder.Encode(entry));
80+
ReturnErrorOnFailure(encoder.Encode(entry.GetMessage()));
5981
}
6082
return CHIP_NO_ERROR;
6183
});
6284
}
6385

64-
CHIP_ERROR MessagesManager::HandleGetActiveMessageIds(chip::app::AttributeValueEncoder & aEncoder)
86+
CHIP_ERROR MessagesManager::HandleGetActiveMessageIds(AttributeValueEncoder & aEncoder)
6587
{
6688
return aEncoder.EncodeList([this](const auto & encoder) -> CHIP_ERROR {
67-
for (Message & entry : mMessages)
89+
for (CachedMessage & entry : mCachedMessages)
6890
{
69-
ReturnErrorOnFailure(encoder.Encode(entry.messageID));
91+
ReturnErrorOnFailure(encoder.Encode(entry.GetMessage().messageID));
7092
}
7193
return CHIP_NO_ERROR;
7294
});
7395
}
7496

7597
// Global Attributes
76-
uint32_t MessagesManager::GetFeatureMap(chip::EndpointId endpoint)
98+
uint32_t MessagesManager::GetFeatureMap(EndpointId endpoint)
7799
{
78-
uint32_t featureMap = 0;
79-
Attributes::FeatureMap::Get(endpoint, &featureMap);
100+
BitMask<Feature> FeatureMap;
101+
FeatureMap.Set(Feature::kReceivedConfirmation);
102+
FeatureMap.Set(Feature::kConfirmationResponse);
103+
FeatureMap.Set(Feature::kConfirmationReply);
104+
FeatureMap.Set(Feature::kProtectedMessages);
105+
106+
uint32_t featureMap = FeatureMap.Raw();
107+
ChipLogProgress(Zcl, "GetFeatureMap featureMap=%d", featureMap);
108+
// forcing to all features since this implementation supports all
109+
// Attributes::FeatureMap::Get(endpoint, &featureMap);
80110
return featureMap;
81111
}

examples/tv-app/tv-common/clusters/messages/MessagesManager.h

+100-3
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,117 @@
2121

2222
#include <iostream>
2323
#include <list>
24+
#include <vector>
25+
26+
struct CachedMessageOption
27+
{
28+
CachedMessageOption(uint32_t id, std::string label) :
29+
mLabel(label), mOption{ chip::MakeOptional(id), chip::MakeOptional(chip::CharSpan::fromCharString(mLabel.c_str())) }
30+
{}
31+
32+
CachedMessageOption(const CachedMessageOption & option) :
33+
mLabel(option.mLabel),
34+
mOption{ option.mOption.messageResponseID, chip::MakeOptional(chip::CharSpan::fromCharString(mLabel.c_str())) }
35+
{}
36+
37+
CachedMessageOption & operator=(const CachedMessageOption & option) = delete;
38+
39+
chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type GetMessageOption() { return mOption; }
40+
41+
~CachedMessageOption() {}
42+
43+
protected:
44+
std::string mLabel;
45+
chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type mOption;
46+
};
47+
48+
struct CachedMessage
49+
{
50+
CachedMessage(const CachedMessage & message) :
51+
mPriority(message.mPriority), mMessageControl(message.mMessageControl), mStartTime(message.mStartTime),
52+
mDuration(message.mDuration), mMessageText(message.mMessageText), mOptions(message.mOptions)
53+
{
54+
memcpy(mMessageIdBuffer, message.mMessageIdBuffer, sizeof(mMessageIdBuffer));
55+
56+
for (CachedMessageOption & entry : mOptions)
57+
{
58+
mResponseOptions.push_back(entry.GetMessageOption());
59+
}
60+
}
61+
62+
CachedMessage & operator=(const CachedMessage & message) = delete;
63+
64+
CachedMessage(const chip::ByteSpan & messageId, const chip::app::Clusters::Messages::MessagePriorityEnum & priority,
65+
const chip::BitMask<chip::app::Clusters::Messages::MessageControlBitmap> & messageControl,
66+
const chip::app::DataModel::Nullable<uint32_t> & startTime,
67+
const chip::app::DataModel::Nullable<uint16_t> & duration, std::string messageText) :
68+
mPriority(priority),
69+
mMessageControl(messageControl), mStartTime(startTime), mDuration(duration), mMessageText(messageText)
70+
{
71+
memcpy(mMessageIdBuffer, messageId.data(), sizeof(mMessageIdBuffer));
72+
}
73+
74+
bool MessageIdMatches(const chip::ByteSpan & id) { return chip::ByteSpan(mMessageIdBuffer).data_equal(id); }
75+
76+
void AddOption(CachedMessageOption option)
77+
{
78+
mOptions.push_back(option);
79+
mResponseOptions.push_back(option.GetMessageOption());
80+
}
81+
82+
chip::app::Clusters::Messages::Structs::MessageStruct::Type GetMessage()
83+
{
84+
if (mResponseOptions.size() > 0)
85+
{
86+
chip::app::DataModel::List<chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type> options(
87+
mResponseOptions.data(), mResponseOptions.size());
88+
chip::app::Clusters::Messages::Structs::MessageStruct::Type message{ chip::ByteSpan(mMessageIdBuffer),
89+
mPriority,
90+
mMessageControl,
91+
mStartTime,
92+
mDuration,
93+
chip::CharSpan::fromCharString(
94+
mMessageText.c_str()),
95+
chip::MakeOptional(options) };
96+
return message;
97+
}
98+
chip::app::Clusters::Messages::Structs::MessageStruct::Type message{ chip::ByteSpan(mMessageIdBuffer),
99+
mPriority,
100+
mMessageControl,
101+
mStartTime,
102+
mDuration,
103+
chip::CharSpan::fromCharString(mMessageText.c_str()) };
104+
return message;
105+
}
106+
107+
~CachedMessage() {}
108+
109+
protected:
110+
const chip::app::Clusters::Messages::MessagePriorityEnum mPriority;
111+
const chip::BitMask<chip::app::Clusters::Messages::MessageControlBitmap> mMessageControl;
112+
const chip::app::DataModel::Nullable<uint32_t> mStartTime;
113+
const chip::app::DataModel::Nullable<uint16_t> mDuration;
114+
115+
std::string mMessageText;
116+
uint8_t mMessageIdBuffer[chip::app::Clusters::Messages::kMessageIdLength];
117+
118+
std::vector<chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type> mResponseOptions;
119+
std::list<CachedMessageOption> mOptions;
120+
};
24121

25122
class MessagesManager : public chip::app::Clusters::Messages::Delegate
26123
{
27124
public:
28125
// Commands
29-
void HandlePresentMessagesRequest(
126+
CHIP_ERROR HandlePresentMessagesRequest(
30127
const chip::ByteSpan & messageId, const chip::app::Clusters::Messages::MessagePriorityEnum & priority,
31128
const chip::BitMask<chip::app::Clusters::Messages::MessageControlBitmap> & messageControl,
32129
const chip::app::DataModel::Nullable<uint32_t> & startTime, const chip::app::DataModel::Nullable<uint16_t> & duration,
33130
const chip::CharSpan & messageText,
34131
const chip::Optional<
35132
chip::app::DataModel::DecodableList<chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type>> &
36133
responses) override;
37-
void HandleCancelMessagesRequest(const chip::app::DataModel::DecodableList<chip::ByteSpan> & messageIds) override;
134+
CHIP_ERROR HandleCancelMessagesRequest(const chip::app::DataModel::DecodableList<chip::ByteSpan> & messageIds) override;
38135

39136
// Attributes
40137
CHIP_ERROR HandleGetMessages(chip::app::AttributeValueEncoder & aEncoder) override;
@@ -44,5 +141,5 @@ class MessagesManager : public chip::app::Clusters::Messages::Delegate
44141
uint32_t GetFeatureMap(chip::EndpointId endpoint) override;
45142

46143
protected:
47-
std::list<chip::app::Clusters::Messages::Structs::MessageStruct::Type> mMessages;
144+
std::list<CachedMessage> mCachedMessages;
48145
};

src/app/clusters/messages-server/messages-delegate.h

+12-8
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,23 @@ namespace app {
2929
namespace Clusters {
3030
namespace Messages {
3131

32-
using MessageResponseOption = chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type;
32+
constexpr static size_t kMessageIdLength = 16;
33+
constexpr static size_t kMessageTextLengthMax = 256;
34+
constexpr static size_t kMessageMaxOptionCount = 4;
35+
constexpr static size_t kMessageResponseIdMin = 1;
36+
constexpr static size_t kMessageResponseLabelMaxLength = 32;
3337

3438
class Delegate
3539
{
3640
public:
3741
// Commands
38-
virtual void
39-
HandlePresentMessagesRequest(const ByteSpan & messageId, const MessagePriorityEnum & priority,
40-
const chip::BitMask<MessageControlBitmap> & messageControl,
41-
const DataModel::Nullable<uint32_t> & startTime, const DataModel::Nullable<uint16_t> & duration,
42-
const CharSpan & messageText,
43-
const chip::Optional<DataModel::DecodableList<MessageResponseOption>> & responses) = 0;
44-
virtual void HandleCancelMessagesRequest(const DataModel::DecodableList<chip::ByteSpan> & messageIds) = 0;
42+
virtual CHIP_ERROR HandlePresentMessagesRequest(
43+
const ByteSpan & messageId, const MessagePriorityEnum & priority,
44+
const chip::BitMask<MessageControlBitmap> & messageControl, const DataModel::Nullable<uint32_t> & startTime,
45+
const DataModel::Nullable<uint16_t> & duration, const CharSpan & messageText,
46+
const chip::Optional<DataModel::DecodableList<chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type>> &
47+
responses) = 0;
48+
virtual CHIP_ERROR HandleCancelMessagesRequest(const DataModel::DecodableList<chip::ByteSpan> & messageIds) = 0;
4549

4650
// Attributes
4751
virtual CHIP_ERROR HandleGetMessages(app::AttributeValueEncoder & aEncoder) = 0;

src/app/clusters/messages-server/messages-server.cpp

+66-3
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,58 @@ bool emberAfMessagesClusterPresentMessagesRequestCallback(
182182
auto & responses = commandData.responses;
183183

184184
Delegate * delegate = GetDelegate(endpoint);
185-
VerifyOrExit(isDelegateNull(delegate, endpoint) != true, err = CHIP_ERROR_INCORRECT_STATE);
185+
VerifyOrExit(isDelegateNull(delegate, endpoint) != true, status = Status::NotFound);
186+
187+
VerifyOrExit(messageId.size() == kMessageIdLength,
188+
ChipLogProgress(Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback invalid message id length");
189+
status = Status::ConstraintError);
190+
191+
VerifyOrExit(messageText.size() <= kMessageTextLengthMax,
192+
ChipLogProgress(Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback invalid message text length");
193+
status = Status::ConstraintError);
194+
195+
if (responses.HasValue())
196+
{
197+
size_t size = 0;
198+
err = responses.Value().ComputeSize(&size);
199+
VerifyOrExit(err == CHIP_NO_ERROR,
200+
ChipLogProgress(Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback size check failed");
201+
status = Status::ConstraintError);
202+
203+
VerifyOrExit(
204+
delegate->HasFeature(endpoint, Feature::kConfirmationResponse),
205+
ChipLogProgress(
206+
Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback responses sent but response feature not supported");
207+
status = Status::InvalidCommand);
208+
209+
VerifyOrExit(size <= kMessageMaxOptionCount,
210+
ChipLogProgress(Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback too many options");
211+
status = Status::ConstraintError);
212+
213+
auto iter = responses.Value().begin();
214+
while (iter.Next())
215+
{
216+
auto & response = iter.GetValue();
217+
218+
// response feature is checked above
219+
VerifyOrExit(response.messageResponseID.HasValue() && response.label.HasValue(),
220+
ChipLogProgress(Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback missing response id or label");
221+
status = Status::InvalidCommand);
222+
223+
VerifyOrExit(response.messageResponseID.Value() >= kMessageResponseIdMin,
224+
ChipLogProgress(Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback responseID value check failed");
225+
status = Status::ConstraintError);
186226

187-
delegate->HandlePresentMessagesRequest(messageId, priority, messageControl, startTime, duration, messageText, responses);
227+
VerifyOrExit(response.label.Value().size() <= kMessageResponseLabelMaxLength,
228+
ChipLogProgress(Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback label length check failed");
229+
status = Status::ConstraintError);
230+
}
231+
VerifyOrExit(iter.GetStatus() == CHIP_NO_ERROR,
232+
ChipLogProgress(Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback TLV parsing error");
233+
status = Status::InvalidAction);
234+
}
235+
236+
err = delegate->HandlePresentMessagesRequest(messageId, priority, messageControl, startTime, duration, messageText, responses);
188237

189238
exit:
190239
if (err != CHIP_NO_ERROR)
@@ -214,7 +263,21 @@ bool emberAfMessagesClusterCancelMessagesRequestCallback(
214263
Delegate * delegate = GetDelegate(endpoint);
215264
VerifyOrExit(isDelegateNull(delegate, endpoint) != true, err = CHIP_ERROR_INCORRECT_STATE);
216265

217-
delegate->HandleCancelMessagesRequest(messageIds);
266+
{
267+
auto iter = messageIds.begin();
268+
while (iter.Next())
269+
{
270+
auto & id = iter.GetValue();
271+
VerifyOrExit(id.size() >= kMessageIdLength,
272+
ChipLogProgress(Zcl, "emberAfMessagesClusterCancelMessagesRequestCallback message id size check failed");
273+
status = Status::ConstraintError);
274+
}
275+
VerifyOrExit(iter.GetStatus() == CHIP_NO_ERROR,
276+
ChipLogProgress(Zcl, "emberAfMessagesClusterCancelMessagesRequestCallback TLV parsing error");
277+
status = Status::InvalidAction);
278+
}
279+
280+
err = delegate->HandleCancelMessagesRequest(messageIds);
218281

219282
exit:
220283
if (err != CHIP_NO_ERROR)

0 commit comments

Comments
 (0)