Skip to content

Commit d930bfe

Browse files
Implemented missing yaml tests cases and added EFS validation in SceneHandlerImpl
1 parent 128c37a commit d930bfe

File tree

8 files changed

+979
-55
lines changed

8 files changed

+979
-55
lines changed

src/app/clusters/scenes-server/SceneHandlerImpl.cpp

+217-1
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,224 @@
1616
*/
1717

1818
#include <app/clusters/scenes-server/SceneHandlerImpl.h>
19+
#include <app/util/endpoint-config-api.h>
20+
#include <app/util/odd-sized-integers.h>
1921

2022
namespace chip {
2123
namespace scenes {
2224

25+
namespace {
26+
27+
template <int ByteSize, bool IsSigned>
28+
using OddSizedInteger = app::OddSizedInteger<ByteSize, IsSigned>;
29+
using ConcreteAttributePath = app::ConcreteAttributePath;
30+
using AttributeValuePairType = app::Clusters::ScenesManagement::Structs::AttributeValuePairStruct::Type;
31+
32+
/// ConvertDefaultValueToWorkingValue
33+
/// @brief Helper function to convert a byte array to a value of the given type.
34+
/// @param EmberAfDefaultAttributeValue & defaultValue
35+
/// @return Value converted to the given working type
36+
template <typename Type>
37+
typename app::NumericAttributeTraits<Type>::WorkingType
38+
ConvertDefaultValueToWorkingValue(const EmberAfDefaultAttributeValue & defaultValue)
39+
{
40+
if (sizeof(typename app::NumericAttributeTraits<Type>::WorkingType) <= 2)
41+
{
42+
return static_cast<typename app::NumericAttributeTraits<Type>::WorkingType>(defaultValue.defaultValue);
43+
}
44+
45+
typename app::NumericAttributeTraits<Type>::StorageType sValue;
46+
memcpy(&sValue, defaultValue.ptrToDefaultValue, sizeof(sValue));
47+
return app::NumericAttributeTraits<Type>::StorageToWorking(sValue);
48+
}
49+
50+
/// IsOnlyOneValuePopulated
51+
/// @brief Helper function to verify if only one value is populated in a given AttributeValuePairType
52+
/// @param AttributeValuePairType & type AttributeValuePairType to verify
53+
/// @return bool true if only one value is populated, false otherwise
54+
bool IsOnlyOneValuePopulated(const AttributeValuePairType & type)
55+
{
56+
int count = 0;
57+
if (type.valueUnsigned8.HasValue())
58+
count++;
59+
if (type.valueSigned8.HasValue())
60+
count++;
61+
if (type.valueUnsigned16.HasValue())
62+
count++;
63+
if (type.valueSigned16.HasValue())
64+
count++;
65+
if (type.valueUnsigned32.HasValue())
66+
count++;
67+
if (type.valueSigned32.HasValue())
68+
count++;
69+
if (type.valueUnsigned64.HasValue())
70+
count++;
71+
if (type.valueSigned64.HasValue())
72+
count++;
73+
return count == 1;
74+
}
75+
76+
/// CapAttributeID
77+
/// Cap the attribute value based on the attribute's min and max if they are defined,
78+
/// or based on the attribute's size if they are not.
79+
/// @param[in] aVPair AttributeValuePairType
80+
/// @param[in] metadata EmberAfAttributeMetadata
81+
///
82+
template <typename Type>
83+
void CapAttributeID(typename app::NumericAttributeTraits<Type>::WorkingType & Value, const EmberAfAttributeMetadata * metadata)
84+
{
85+
using IntType = app::NumericAttributeTraits<Type>;
86+
using WorkingType = typename IntType::WorkingType;
87+
88+
WorkingType maxValue;
89+
WorkingType minValue;
90+
uint16_t bitWidth = static_cast<uint16_t>(emberAfAttributeSize(metadata) * 8);
91+
92+
// Min/Max Value capps for the OddSize integers
93+
if (metadata->IsSignedIntegerAttribute())
94+
{
95+
// We use emberAfAttributeSize for cases like INT24S, INT40S, INT48S, INT56S where numeric_limits<WorkingType>::max()
96+
// wouldn't work
97+
maxValue = static_cast<WorkingType>((1ULL << (bitWidth - 1)) - 1);
98+
minValue = static_cast<WorkingType>(-(1ULL << (bitWidth - 1)));
99+
}
100+
else
101+
{
102+
// We use emberAfAttributeSize for cases like INT24U, INT40U, INT48U, INT56U where numeric_limits<WorkingType>::max()
103+
// wouldn't work
104+
if (ZCL_INT64U_ATTRIBUTE_TYPE == metadata->attributeType)
105+
{
106+
maxValue = static_cast<WorkingType>(UINT64_MAX); // Bit shift of 64 is undefined so we use UINT64_MAX
107+
}
108+
else
109+
{
110+
maxValue = static_cast<WorkingType>((1ULL << bitWidth) - 1);
111+
}
112+
minValue = static_cast<WorkingType>(0);
113+
}
114+
115+
// Ensure that the metadata's signedness matches the working type's signedness
116+
VerifyOrDie(metadata->IsSignedIntegerAttribute() == std::is_signed<WorkingType>::value);
117+
118+
if (metadata->IsBoolean())
119+
{
120+
// Caping the value to 1 in case values greater than 1 are set
121+
Value = Value ? 1 : 0;
122+
return;
123+
}
124+
125+
// Check metadata for min and max values
126+
if (metadata->HasMinMax())
127+
{
128+
const EmberAfAttributeMinMaxValue * minMaxValue = metadata->defaultValue.ptrToMinMaxValue;
129+
minValue = ConvertDefaultValueToWorkingValue<Type>(minMaxValue->minValue);
130+
maxValue = ConvertDefaultValueToWorkingValue<Type>(minMaxValue->maxValue);
131+
}
132+
133+
if (minValue > Value)
134+
{
135+
Value = minValue;
136+
}
137+
else if (maxValue < Value)
138+
{
139+
Value = maxValue;
140+
}
141+
}
142+
143+
/// @brief Validate the attribute exists for a given cluster
144+
/// @param[in] endpoint Endpoint ID
145+
/// @param[in] clusterID Cluster ID
146+
/// @param[in] aVPair AttributeValuePairType, will be mutated to cap the value if it is out of range
147+
/// @return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute) if the attribute does not exist for a given cluster or is not scenable
148+
/// @note This will allways fail for global list attributes. If we do want to make them scenable someday, we will need to
149+
/// use a different validation method.
150+
// TODO: Add check for "S" quality to determine if the attribute is scenable once suported :
151+
// https://github.com/project-chip/connectedhomeip/issues/24177
152+
CHIP_ERROR ValidateAttributePath(EndpointId endpoint, ClusterId cluster, AttributeValuePairType & aVPair)
153+
{
154+
const EmberAfAttributeMetadata * metadata = emberAfLocateAttributeMetadata(endpoint, cluster, aVPair.attributeID);
155+
156+
if (nullptr == metadata)
157+
{
158+
return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute);
159+
}
160+
161+
// There should never be more than one populated value in an ExtensionFieldSet
162+
VerifyOrReturnError(IsOnlyOneValuePopulated(aVPair), CHIP_ERROR_INVALID_ARGUMENT);
163+
164+
switch (metadata->attributeType)
165+
{
166+
case ZCL_BOOLEAN_ATTRIBUTE_TYPE:
167+
case ZCL_BITMAP8_ATTRIBUTE_TYPE:
168+
case ZCL_ENUM8_ATTRIBUTE_TYPE:
169+
case ZCL_INT8U_ATTRIBUTE_TYPE:
170+
VerifyOrReturnError(aVPair.valueUnsigned8.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
171+
CapAttributeID<uint8_t>(aVPair.valueUnsigned8.Value(), metadata);
172+
break;
173+
case ZCL_BITMAP16_ATTRIBUTE_TYPE:
174+
case ZCL_ENUM16_ATTRIBUTE_TYPE:
175+
case ZCL_INT16U_ATTRIBUTE_TYPE:
176+
VerifyOrReturnError(aVPair.valueUnsigned16.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
177+
CapAttributeID<uint16_t>(aVPair.valueUnsigned16.Value(), metadata);
178+
break;
179+
case ZCL_INT24U_ATTRIBUTE_TYPE:
180+
VerifyOrReturnError(aVPair.valueUnsigned32.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
181+
CapAttributeID<OddSizedInteger<3, false>>(aVPair.valueUnsigned32.Value(), metadata);
182+
break;
183+
case ZCL_BITMAP32_ATTRIBUTE_TYPE:
184+
case ZCL_INT32U_ATTRIBUTE_TYPE:
185+
VerifyOrReturnError(aVPair.valueUnsigned32.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
186+
CapAttributeID<uint32_t>(aVPair.valueUnsigned32.Value(), metadata);
187+
break;
188+
case ZCL_INT48U_ATTRIBUTE_TYPE:
189+
VerifyOrReturnError(aVPair.valueUnsigned64.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
190+
CapAttributeID<OddSizedInteger<6, false>>(aVPair.valueUnsigned64.Value(), metadata);
191+
break;
192+
case ZCL_INT56U_ATTRIBUTE_TYPE:
193+
VerifyOrReturnError(aVPair.valueUnsigned64.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
194+
CapAttributeID<OddSizedInteger<7, false>>(aVPair.valueUnsigned64.Value(), metadata);
195+
break;
196+
case ZCL_BITMAP64_ATTRIBUTE_TYPE:
197+
case ZCL_INT64U_ATTRIBUTE_TYPE:
198+
VerifyOrReturnError(aVPair.valueUnsigned64.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
199+
CapAttributeID<uint64_t>(aVPair.valueUnsigned64.Value(), metadata);
200+
break;
201+
case ZCL_INT8S_ATTRIBUTE_TYPE:
202+
VerifyOrReturnError(aVPair.valueSigned8.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
203+
CapAttributeID<int8_t>(aVPair.valueSigned8.Value(), metadata);
204+
break;
205+
case ZCL_INT16S_ATTRIBUTE_TYPE:
206+
VerifyOrReturnError(aVPair.valueSigned16.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
207+
CapAttributeID<int16_t>(aVPair.valueSigned16.Value(), metadata);
208+
break;
209+
case ZCL_INT24S_ATTRIBUTE_TYPE:
210+
VerifyOrReturnError(aVPair.valueSigned32.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
211+
CapAttributeID<OddSizedInteger<3, true>>(aVPair.valueSigned32.Value(), metadata);
212+
break;
213+
case ZCL_INT32S_ATTRIBUTE_TYPE:
214+
VerifyOrReturnError(aVPair.valueSigned32.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
215+
CapAttributeID<int32_t>(aVPair.valueSigned32.Value(), metadata);
216+
break;
217+
case ZCL_INT48S_ATTRIBUTE_TYPE:
218+
VerifyOrReturnError(aVPair.valueSigned64.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
219+
CapAttributeID<OddSizedInteger<6, true>>(aVPair.valueSigned64.Value(), metadata);
220+
break;
221+
case ZCL_INT56S_ATTRIBUTE_TYPE:
222+
VerifyOrReturnError(aVPair.valueSigned64.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
223+
CapAttributeID<OddSizedInteger<7, true>>(aVPair.valueSigned64.Value(), metadata);
224+
break;
225+
case ZCL_INT64S_ATTRIBUTE_TYPE:
226+
VerifyOrReturnError(aVPair.valueSigned64.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
227+
CapAttributeID<int64_t>(aVPair.valueSigned64.Value(), metadata);
228+
break;
229+
default:
230+
return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute);
231+
}
232+
233+
return CHIP_NO_ERROR;
234+
}
235+
} // namespace
236+
23237
CHIP_ERROR
24238
DefaultSceneHandlerImpl::EncodeAttributeValueList(const List<AttributeValuePairType> & aVlist, MutableByteSpan & serializedBytes)
25239
{
@@ -58,7 +272,9 @@ DefaultSceneHandlerImpl::SerializeAdd(EndpointId endpoint, const ExtensionFieldS
58272
auto pair_iterator = extensionFieldSet.attributeValueList.begin();
59273
while (pair_iterator.Next())
60274
{
61-
aVPairs[pairCount] = pair_iterator.GetValue();
275+
AttributeValuePairType currentPair = pair_iterator.GetValue();
276+
ReturnErrorOnFailure(ValidateAttributePath(endpoint, extensionFieldSet.clusterID, currentPair));
277+
aVPairs[pairCount] = currentPair;
62278
pairCount++;
63279
}
64280
ReturnErrorOnFailure(pair_iterator.GetStatus());

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

+5
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ CHIP_ERROR AddResponseOnError(CommandHandlerInterface::HandlerContext & ctx, Res
7474
{
7575
resp.status = to_underlying(Protocols::InteractionModel::Status::ResourceExhausted);
7676
}
77+
else if (CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute) == err)
78+
{
79+
// TODO: Confirm if we need to add UnsupportedAttribute status as a return for Scene Commands
80+
resp.status = to_underlying(Protocols::InteractionModel::Status::InvalidCommand);
81+
}
7782
else
7883
{
7984
resp.status = to_underlying(StatusIB(err).mStatus);

0 commit comments

Comments
 (0)