Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021-2023 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : /**
19 : * @file
20 : * Contains the functions for compatibility with ember ZCL inner state
21 : * when calling ember callbacks.
22 : */
23 :
24 : #include <access/AccessControl.h>
25 : #include <app/CommandHandlerInterface.h>
26 : #include <app/ConcreteAttributePath.h>
27 : #include <app/ConcreteEventPath.h>
28 : #include <app/GlobalAttributes.h>
29 : #include <app/InteractionModelEngine.h>
30 : #include <app/RequiredPrivilege.h>
31 : #include <app/att-storage.h>
32 : #include <app/reporting/Engine.h>
33 : #include <app/reporting/reporting.h>
34 : #include <app/util/af.h>
35 : #include <app/util/attribute-storage-null-handling.h>
36 : #include <app/util/attribute-storage.h>
37 : #include <app/util/attribute-table.h>
38 : #include <app/util/config.h>
39 : #include <app/util/odd-sized-integers.h>
40 : #include <app/util/util.h>
41 : #include <lib/core/CHIPCore.h>
42 : #include <lib/core/TLV.h>
43 : #include <lib/support/CodeUtils.h>
44 : #include <lib/support/SafeInt.h>
45 : #include <lib/support/TypeTraits.h>
46 : #include <platform/LockTracker.h>
47 : #include <protocols/interaction_model/Constants.h>
48 :
49 : #include <app-common/zap-generated/attribute-type.h>
50 :
51 : #include <zap-generated/endpoint_config.h>
52 :
53 : #include <limits>
54 :
55 : using chip::Protocols::InteractionModel::Status;
56 :
57 : using namespace chip;
58 : using namespace chip::app;
59 : using namespace chip::Access;
60 :
61 : namespace chip {
62 : namespace app {
63 : namespace Compatibility {
64 : namespace {
65 : // On some apps, ATTRIBUTE_LARGEST can as small as 3, making compiler unhappy since data[kAttributeReadBufferSize] cannot hold
66 : // uint64_t. Make kAttributeReadBufferSize at least 8 so it can fit all basic types.
67 : constexpr size_t kAttributeReadBufferSize = (ATTRIBUTE_LARGEST >= 8 ? ATTRIBUTE_LARGEST : 8);
68 :
69 : // BasicType maps the type to basic int(8|16|32|64)(s|u) types.
70 0 : EmberAfAttributeType BaseType(EmberAfAttributeType type)
71 : {
72 0 : switch (type)
73 : {
74 0 : case ZCL_ACTION_ID_ATTRIBUTE_TYPE: // Action Id
75 : case ZCL_FABRIC_IDX_ATTRIBUTE_TYPE: // Fabric Index
76 : case ZCL_BITMAP8_ATTRIBUTE_TYPE: // 8-bit bitmap
77 : case ZCL_ENUM8_ATTRIBUTE_TYPE: // 8-bit enumeration
78 : case ZCL_STATUS_ATTRIBUTE_TYPE: // Status Code
79 : case ZCL_PERCENT_ATTRIBUTE_TYPE: // Percentage
80 : static_assert(std::is_same<chip::Percent, uint8_t>::value,
81 : "chip::Percent is expected to be uint8_t, change this when necessary");
82 0 : return ZCL_INT8U_ATTRIBUTE_TYPE;
83 :
84 0 : case ZCL_ENDPOINT_NO_ATTRIBUTE_TYPE: // Endpoint Number
85 : case ZCL_GROUP_ID_ATTRIBUTE_TYPE: // Group Id
86 : case ZCL_VENDOR_ID_ATTRIBUTE_TYPE: // Vendor Id
87 : case ZCL_ENUM16_ATTRIBUTE_TYPE: // 16-bit enumeration
88 : case ZCL_BITMAP16_ATTRIBUTE_TYPE: // 16-bit bitmap
89 : case ZCL_PERCENT100THS_ATTRIBUTE_TYPE: // 100ths of a percent
90 : static_assert(std::is_same<chip::EndpointId, uint16_t>::value,
91 : "chip::EndpointId is expected to be uint16_t, change this when necessary");
92 : static_assert(std::is_same<chip::GroupId, uint16_t>::value,
93 : "chip::GroupId is expected to be uint16_t, change this when necessary");
94 : static_assert(std::is_same<chip::Percent100ths, uint16_t>::value,
95 : "chip::Percent100ths is expected to be uint16_t, change this when necessary");
96 0 : return ZCL_INT16U_ATTRIBUTE_TYPE;
97 :
98 0 : case ZCL_CLUSTER_ID_ATTRIBUTE_TYPE: // Cluster Id
99 : case ZCL_ATTRIB_ID_ATTRIBUTE_TYPE: // Attribute Id
100 : case ZCL_FIELD_ID_ATTRIBUTE_TYPE: // Field Id
101 : case ZCL_EVENT_ID_ATTRIBUTE_TYPE: // Event Id
102 : case ZCL_COMMAND_ID_ATTRIBUTE_TYPE: // Command Id
103 : case ZCL_TRANS_ID_ATTRIBUTE_TYPE: // Transaction Id
104 : case ZCL_DEVTYPE_ID_ATTRIBUTE_TYPE: // Device Type Id
105 : case ZCL_DATA_VER_ATTRIBUTE_TYPE: // Data Version
106 : case ZCL_BITMAP32_ATTRIBUTE_TYPE: // 32-bit bitmap
107 : case ZCL_EPOCH_S_ATTRIBUTE_TYPE: // Epoch Seconds
108 : case ZCL_ELAPSED_S_ATTRIBUTE_TYPE: // Elapsed Seconds
109 : static_assert(std::is_same<chip::ClusterId, uint32_t>::value,
110 : "chip::Cluster is expected to be uint32_t, change this when necessary");
111 : static_assert(std::is_same<chip::AttributeId, uint32_t>::value,
112 : "chip::AttributeId is expected to be uint32_t, change this when necessary");
113 : static_assert(std::is_same<chip::AttributeId, uint32_t>::value,
114 : "chip::AttributeId is expected to be uint32_t, change this when necessary");
115 : static_assert(std::is_same<chip::EventId, uint32_t>::value,
116 : "chip::EventId is expected to be uint32_t, change this when necessary");
117 : static_assert(std::is_same<chip::CommandId, uint32_t>::value,
118 : "chip::CommandId is expected to be uint32_t, change this when necessary");
119 : static_assert(std::is_same<chip::TransactionId, uint32_t>::value,
120 : "chip::TransactionId is expected to be uint32_t, change this when necessary");
121 : static_assert(std::is_same<chip::DeviceTypeId, uint32_t>::value,
122 : "chip::DeviceTypeId is expected to be uint32_t, change this when necessary");
123 : static_assert(std::is_same<chip::DataVersion, uint32_t>::value,
124 : "chip::DataVersion is expected to be uint32_t, change this when necessary");
125 0 : return ZCL_INT32U_ATTRIBUTE_TYPE;
126 :
127 0 : case ZCL_AMPERAGE_MA_ATTRIBUTE_TYPE: // Amperage milliamps
128 : case ZCL_ENERGY_MWH_ATTRIBUTE_TYPE: // Energy milliwatt-hours
129 : case ZCL_POWER_MW_ATTRIBUTE_TYPE: // Power milliwatts
130 : case ZCL_VOLTAGE_MV_ATTRIBUTE_TYPE: // Voltage millivolts
131 0 : return ZCL_INT64S_ATTRIBUTE_TYPE;
132 :
133 0 : case ZCL_EVENT_NO_ATTRIBUTE_TYPE: // Event Number
134 : case ZCL_FABRIC_ID_ATTRIBUTE_TYPE: // Fabric Id
135 : case ZCL_NODE_ID_ATTRIBUTE_TYPE: // Node Id
136 : case ZCL_BITMAP64_ATTRIBUTE_TYPE: // 64-bit bitmap
137 : case ZCL_EPOCH_US_ATTRIBUTE_TYPE: // Epoch Microseconds
138 : case ZCL_POSIX_MS_ATTRIBUTE_TYPE: // POSIX Milliseconds
139 : case ZCL_SYSTIME_MS_ATTRIBUTE_TYPE: // System time Milliseconds
140 : case ZCL_SYSTIME_US_ATTRIBUTE_TYPE: // System time Microseconds
141 : static_assert(std::is_same<chip::EventNumber, uint64_t>::value,
142 : "chip::EventNumber is expected to be uint64_t, change this when necessary");
143 : static_assert(std::is_same<chip::FabricId, uint64_t>::value,
144 : "chip::FabricId is expected to be uint64_t, change this when necessary");
145 : static_assert(std::is_same<chip::NodeId, uint64_t>::value,
146 : "chip::NodeId is expected to be uint64_t, change this when necessary");
147 0 : return ZCL_INT64U_ATTRIBUTE_TYPE;
148 :
149 0 : case ZCL_TEMPERATURE_ATTRIBUTE_TYPE: // Temperature
150 0 : return ZCL_INT16S_ATTRIBUTE_TYPE;
151 :
152 0 : default:
153 0 : return type;
154 : }
155 : }
156 :
157 : } // namespace
158 :
159 : } // namespace Compatibility
160 :
161 : using namespace chip::app::Compatibility;
162 :
163 : namespace {
164 : // Common buffer for ReadSingleClusterData & WriteSingleClusterData
165 : uint8_t attributeData[kAttributeReadBufferSize];
166 :
167 : template <typename T>
168 0 : CHIP_ERROR attributeBufferToNumericTlvData(TLV::TLVWriter & writer, bool isNullable)
169 : {
170 : typename NumericAttributeTraits<T>::StorageType value;
171 0 : memcpy(&value, attributeData, sizeof(value));
172 0 : TLV::Tag tag = TLV::ContextTag(AttributeDataIB::Tag::kData);
173 0 : if (isNullable && NumericAttributeTraits<T>::IsNullValue(value))
174 : {
175 0 : return writer.PutNull(tag);
176 : }
177 :
178 0 : if (!NumericAttributeTraits<T>::CanRepresentValue(isNullable, value))
179 : {
180 0 : return CHIP_ERROR_INCORRECT_STATE;
181 : }
182 :
183 0 : return NumericAttributeTraits<T>::Encode(writer, tag, value);
184 : }
185 :
186 : } // anonymous namespace
187 :
188 7 : Protocols::InteractionModel::Status ServerClusterCommandExists(const ConcreteCommandPath & aCommandPath)
189 : {
190 : using Protocols::InteractionModel::Status;
191 :
192 7 : const EmberAfEndpointType * type = emberAfFindEndpointType(aCommandPath.mEndpointId);
193 7 : if (type == nullptr)
194 : {
195 1 : return Status::UnsupportedEndpoint;
196 : }
197 :
198 6 : const EmberAfCluster * cluster = emberAfFindClusterInType(type, aCommandPath.mClusterId, CLUSTER_MASK_SERVER);
199 6 : if (cluster == nullptr)
200 : {
201 0 : return Status::UnsupportedCluster;
202 : }
203 :
204 : auto * commandHandler =
205 6 : InteractionModelEngine::GetInstance()->FindCommandHandler(aCommandPath.mEndpointId, aCommandPath.mClusterId);
206 6 : if (commandHandler)
207 : {
208 : struct Context
209 : {
210 : bool commandExists;
211 : CommandId targetCommand;
212 6 : } context{ false, aCommandPath.mCommandId };
213 :
214 6 : CHIP_ERROR err = commandHandler->EnumerateAcceptedCommands(
215 : aCommandPath,
216 2 : [](CommandId command, void * closure) -> Loop {
217 2 : auto * ctx = static_cast<Context *>(closure);
218 2 : if (ctx->targetCommand == command)
219 : {
220 2 : ctx->commandExists = true;
221 2 : return Loop::Break;
222 : }
223 0 : return Loop::Continue;
224 : },
225 : &context);
226 :
227 : // We now have three cases:
228 : // 1) handler returned CHIP_ERROR_NOT_IMPLEMENTED. In that case we
229 : // should fall back to looking at cluster->acceptedCommandList
230 : // 2) handler returned success. In that case, the handler is the source
231 : // of truth about the set of accepted commands, and
232 : // context.commandExists indicates whether a aCommandPath.mCommandId
233 : // was in the set, and we should return either Success or
234 : // UnsupportedCommand accordingly.
235 : // 3) Some other status was returned. In this case we should probably
236 : // err on the side of not allowing the command, since we have no idea
237 : // whether to allow it or not.
238 6 : if (err != CHIP_ERROR_NOT_IMPLEMENTED)
239 : {
240 3 : if (err == CHIP_NO_ERROR)
241 : {
242 3 : return context.commandExists ? Status::Success : Status::UnsupportedCommand;
243 : }
244 :
245 0 : return Status::Failure;
246 : }
247 : }
248 :
249 3 : for (const CommandId * cmd = cluster->acceptedCommandList; cmd != nullptr && *cmd != kInvalidCommandId; cmd++)
250 : {
251 1 : if (*cmd == aCommandPath.mCommandId)
252 : {
253 1 : return Status::Success;
254 : }
255 : }
256 :
257 2 : return Status::UnsupportedCommand;
258 : }
259 :
260 : namespace {
261 :
262 2471 : CHIP_ERROR ReadClusterDataVersion(const ConcreteClusterPath & aConcreteClusterPath, DataVersion & aDataVersion)
263 : {
264 2471 : DataVersion * version = emberAfDataVersionStorage(aConcreteClusterPath);
265 2471 : if (version == nullptr)
266 : {
267 0 : ChipLogError(DataManagement, "Endpoint %x, Cluster " ChipLogFormatMEI " not found in ReadClusterDataVersion!",
268 : aConcreteClusterPath.mEndpointId, ChipLogValueMEI(aConcreteClusterPath.mClusterId));
269 0 : return CHIP_ERROR_NOT_FOUND;
270 : }
271 2471 : aDataVersion = *version;
272 2471 : return CHIP_NO_ERROR;
273 : }
274 :
275 2476 : void IncreaseClusterDataVersion(const ConcreteClusterPath & aConcreteClusterPath)
276 : {
277 2476 : DataVersion * version = emberAfDataVersionStorage(aConcreteClusterPath);
278 2476 : if (version == nullptr)
279 : {
280 47 : ChipLogError(DataManagement, "Endpoint %x, Cluster " ChipLogFormatMEI " not found in IncreaseClusterDataVersion!",
281 : aConcreteClusterPath.mEndpointId, ChipLogValueMEI(aConcreteClusterPath.mClusterId));
282 : }
283 : else
284 : {
285 2429 : (*(version))++;
286 2429 : ChipLogDetail(DataManagement, "Endpoint %x, Cluster " ChipLogFormatMEI " update version to %" PRIx32,
287 : aConcreteClusterPath.mEndpointId, ChipLogValueMEI(aConcreteClusterPath.mClusterId), *(version));
288 : }
289 2476 : }
290 :
291 0 : CHIP_ERROR SendSuccessStatus(AttributeReportIB::Builder & aAttributeReport, AttributeDataIB::Builder & aAttributeDataIBBuilder)
292 : {
293 0 : ReturnErrorOnFailure(aAttributeDataIBBuilder.EndOfAttributeDataIB());
294 0 : return aAttributeReport.EndOfAttributeReportIB();
295 : }
296 :
297 0 : CHIP_ERROR SendFailureStatus(const ConcreteAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports,
298 : Protocols::InteractionModel::Status aStatus, TLV::TLVWriter * aReportCheckpoint)
299 : {
300 0 : if (aReportCheckpoint != nullptr)
301 : {
302 0 : aAttributeReports.Rollback(*aReportCheckpoint);
303 : }
304 0 : return aAttributeReports.EncodeAttributeStatus(aPath, StatusIB(aStatus));
305 : }
306 :
307 : // This reader should never actually be registered; we do manual dispatch to it
308 : // for the one attribute it handles.
309 : class MandatoryGlobalAttributeReader : public AttributeAccessInterface
310 : {
311 : public:
312 2471 : MandatoryGlobalAttributeReader(const EmberAfCluster * aCluster) :
313 2471 : AttributeAccessInterface(MakeOptional(kInvalidEndpointId), kInvalidClusterId), mCluster(aCluster)
314 2471 : {}
315 :
316 : protected:
317 : const EmberAfCluster * mCluster;
318 : };
319 :
320 : class GlobalAttributeReader : public MandatoryGlobalAttributeReader
321 : {
322 : public:
323 2471 : GlobalAttributeReader(const EmberAfCluster * aCluster) : MandatoryGlobalAttributeReader(aCluster) {}
324 :
325 : CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;
326 :
327 : private:
328 : typedef CHIP_ERROR (CommandHandlerInterface::*CommandListEnumerator)(const ConcreteClusterPath & cluster,
329 : CommandHandlerInterface::CommandIdCallback callback,
330 : void * context);
331 : static CHIP_ERROR EncodeCommandList(const ConcreteClusterPath & aClusterPath, AttributeValueEncoder & aEncoder,
332 : CommandListEnumerator aEnumerator, const CommandId * aClusterCommandList);
333 : };
334 :
335 715 : CHIP_ERROR GlobalAttributeReader::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder)
336 : {
337 : using namespace Clusters::Globals::Attributes;
338 715 : switch (aPath.mAttributeId)
339 : {
340 279 : case AttributeList::Id:
341 279 : return aEncoder.EncodeList([this](const auto & encoder) {
342 249 : const size_t count = mCluster->attributeCount;
343 249 : bool addedExtraGlobals = false;
344 1629 : for (size_t i = 0; i < count; ++i)
345 : {
346 1424 : AttributeId id = mCluster->attributes[i].attributeId;
347 1424 : constexpr auto lastGlobalId = GlobalAttributesNotInMetadata[ArraySize(GlobalAttributesNotInMetadata) - 1];
348 : #if CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE
349 : // The GlobalAttributesNotInMetadata shouldn't have any gaps in their ids here.
350 : static_assert(lastGlobalId - GlobalAttributesNotInMetadata[0] == ArraySize(GlobalAttributesNotInMetadata) - 1,
351 : "Ids in GlobalAttributesNotInMetadata not consecutive");
352 : #else
353 : // If EventList is not supported. The GlobalAttributesNotInMetadata is missing one id here.
354 : static_assert(lastGlobalId - GlobalAttributesNotInMetadata[0] == ArraySize(GlobalAttributesNotInMetadata),
355 : "Ids in GlobalAttributesNotInMetadata not consecutive (except EventList)");
356 : #endif // CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE
357 1424 : if (!addedExtraGlobals && id > lastGlobalId)
358 : {
359 880 : for (const auto & globalId : GlobalAttributesNotInMetadata)
360 : {
361 669 : ReturnErrorOnFailure(encoder.Encode(globalId));
362 : }
363 211 : addedExtraGlobals = true;
364 : }
365 1406 : ReturnErrorOnFailure(encoder.Encode(id));
366 : }
367 205 : if (!addedExtraGlobals)
368 : {
369 0 : for (const auto & globalId : GlobalAttributesNotInMetadata)
370 : {
371 0 : ReturnErrorOnFailure(encoder.Encode(globalId));
372 : }
373 : }
374 205 : return CHIP_NO_ERROR;
375 279 : });
376 : #if CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE
377 : case EventList::Id:
378 : return aEncoder.EncodeList([this](const auto & encoder) {
379 : for (size_t i = 0; i < mCluster->eventCount; ++i)
380 : {
381 : ReturnErrorOnFailure(encoder.Encode(mCluster->eventList[i]));
382 : }
383 : return CHIP_NO_ERROR;
384 : });
385 : #endif // CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE
386 231 : case AcceptedCommandList::Id:
387 231 : return EncodeCommandList(aPath, aEncoder, &CommandHandlerInterface::EnumerateAcceptedCommands,
388 231 : mCluster->acceptedCommandList);
389 205 : case GeneratedCommandList::Id:
390 205 : return EncodeCommandList(aPath, aEncoder, &CommandHandlerInterface::EnumerateGeneratedCommands,
391 205 : mCluster->generatedCommandList);
392 0 : default:
393 : // This function is only called if attributeCluster is non-null in
394 : // ReadSingleClusterData, which only happens for attributes listed in
395 : // GlobalAttributesNotInMetadata. If we reach this code, someone added
396 : // a global attribute to that list but not the above switch.
397 0 : VerifyOrDieWithMsg(false, DataManagement, "Unexpected global attribute: " ChipLogFormatMEI,
398 : ChipLogValueMEI(aPath.mAttributeId));
399 : return CHIP_NO_ERROR;
400 : }
401 : }
402 :
403 436 : CHIP_ERROR GlobalAttributeReader::EncodeCommandList(const ConcreteClusterPath & aClusterPath, AttributeValueEncoder & aEncoder,
404 : GlobalAttributeReader::CommandListEnumerator aEnumerator,
405 : const CommandId * aClusterCommandList)
406 : {
407 436 : return aEncoder.EncodeList([&](const auto & encoder) {
408 : auto * commandHandler =
409 416 : InteractionModelEngine::GetInstance()->FindCommandHandler(aClusterPath.mEndpointId, aClusterPath.mClusterId);
410 416 : if (commandHandler)
411 : {
412 : struct Context
413 : {
414 : decltype(encoder) & commandIdEncoder;
415 : CHIP_ERROR err;
416 6 : } context{ encoder, CHIP_NO_ERROR };
417 6 : CHIP_ERROR err = (commandHandler->*aEnumerator)(
418 : aClusterPath,
419 2 : [](CommandId command, void * closure) -> Loop {
420 2 : auto * ctx = static_cast<Context *>(closure);
421 2 : ctx->err = ctx->commandIdEncoder.Encode(command);
422 2 : if (ctx->err != CHIP_NO_ERROR)
423 : {
424 0 : return Loop::Break;
425 : }
426 2 : return Loop::Continue;
427 : },
428 : &context);
429 6 : if (err != CHIP_ERROR_NOT_IMPLEMENTED)
430 : {
431 3 : return context.err;
432 : }
433 : // Else fall through to the list in aClusterCommandList.
434 : }
435 :
436 414 : for (const CommandId * cmd = aClusterCommandList; cmd != nullptr && *cmd != kInvalidCommandId; cmd++)
437 : {
438 1 : ReturnErrorOnFailure(encoder.Encode(*cmd));
439 : }
440 413 : return CHIP_NO_ERROR;
441 436 : });
442 : }
443 :
444 : // Helper function for trying to read an attribute value via an
445 : // AttributeAccessInterface. On failure, the read has failed. On success, the
446 : // aTriedEncode outparam is set to whether the AttributeAccessInterface tried to encode a value.
447 2471 : CHIP_ERROR ReadViaAccessInterface(FabricIndex aAccessingFabricIndex, bool aIsFabricFiltered,
448 : const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports,
449 : AttributeValueEncoder::AttributeEncodeState * aEncoderState,
450 : AttributeAccessInterface * aAccessInterface, bool * aTriedEncode)
451 : {
452 : AttributeValueEncoder::AttributeEncodeState state =
453 2471 : (aEncoderState == nullptr ? AttributeValueEncoder::AttributeEncodeState() : *aEncoderState);
454 2471 : DataVersion version = 0;
455 2471 : ReturnErrorOnFailure(ReadClusterDataVersion(aPath, version));
456 2471 : AttributeValueEncoder valueEncoder(aAttributeReports, aAccessingFabricIndex, aPath, version, aIsFabricFiltered, state);
457 2471 : CHIP_ERROR err = aAccessInterface->Read(aPath, valueEncoder);
458 :
459 2471 : if (err == CHIP_IM_GLOBAL_STATUS(UnsupportedRead) && aPath.mExpanded)
460 : {
461 : //
462 : // Set this to true to ensure our caller will return immediately without proceeding further.
463 : //
464 0 : *aTriedEncode = true;
465 0 : return CHIP_NO_ERROR;
466 : }
467 :
468 2471 : if (err != CHIP_NO_ERROR)
469 : {
470 : // If the err is not CHIP_NO_ERROR, means the encoding was aborted, then the valueEncoder may save its state.
471 : // The state is used by list chunking feature for now.
472 228 : if (aEncoderState != nullptr)
473 : {
474 228 : *aEncoderState = valueEncoder.GetState();
475 : }
476 228 : return err;
477 : }
478 :
479 2243 : *aTriedEncode = valueEncoder.TriedEncode();
480 2243 : return CHIP_NO_ERROR;
481 2471 : }
482 :
483 : // Determine the appropriate status response for an unsupported attribute for
484 : // the given path. Must be called when the attribute is known to be unsupported
485 : // (i.e. we found no attribute metadata for it).
486 0 : Protocols::InteractionModel::Status UnsupportedAttributeStatus(const ConcreteAttributePath & aPath)
487 : {
488 : using Protocols::InteractionModel::Status;
489 :
490 0 : const EmberAfEndpointType * type = emberAfFindEndpointType(aPath.mEndpointId);
491 0 : if (type == nullptr)
492 : {
493 0 : return Status::UnsupportedEndpoint;
494 : }
495 :
496 0 : const EmberAfCluster * cluster = emberAfFindClusterInType(type, aPath.mClusterId, CLUSTER_MASK_SERVER);
497 0 : if (cluster == nullptr)
498 : {
499 0 : return Status::UnsupportedCluster;
500 : }
501 :
502 : // Since we know the attribute is unsupported and the endpoint/cluster are
503 : // OK, this is the only option left.
504 0 : return Status::UnsupportedAttribute;
505 : }
506 :
507 : // Will set at most one of the out-params (aAttributeCluster or
508 : // aAttributeMetadata) to non-null. Both null means attribute not supported,
509 : // aAttributeCluster non-null means this is a supported global attribute that
510 : // does not have metadata.
511 4905 : void FindAttributeMetadata(const ConcreteAttributePath & aPath, const EmberAfCluster ** aAttributeCluster,
512 : const EmberAfAttributeMetadata ** aAttributeMetadata)
513 : {
514 4905 : *aAttributeCluster = nullptr;
515 4905 : *aAttributeMetadata = nullptr;
516 :
517 18264 : for (auto & attr : GlobalAttributesNotInMetadata)
518 : {
519 14074 : if (attr == aPath.mAttributeId)
520 : {
521 715 : *aAttributeCluster = emberAfFindServerCluster(aPath.mEndpointId, aPath.mClusterId);
522 715 : return;
523 : }
524 : }
525 :
526 4190 : *aAttributeMetadata = emberAfLocateAttributeMetadata(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId);
527 : }
528 :
529 : } // anonymous namespace
530 :
531 3 : bool ConcreteAttributePathExists(const ConcreteAttributePath & aPath)
532 : {
533 12 : for (auto & attr : GlobalAttributesNotInMetadata)
534 : {
535 9 : if (attr == aPath.mAttributeId)
536 : {
537 0 : return (emberAfFindServerCluster(aPath.mEndpointId, aPath.mClusterId) != nullptr);
538 : }
539 : }
540 3 : return (emberAfLocateAttributeMetadata(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId) != nullptr);
541 : }
542 :
543 2471 : CHIP_ERROR ReadSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered,
544 : const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports,
545 : AttributeValueEncoder::AttributeEncodeState * apEncoderState)
546 : {
547 2471 : ChipLogDetail(DataManagement,
548 : "Reading attribute: Cluster=" ChipLogFormatMEI " Endpoint=%x AttributeId=" ChipLogFormatMEI " (expanded=%d)",
549 : ChipLogValueMEI(aPath.mClusterId), aPath.mEndpointId, ChipLogValueMEI(aPath.mAttributeId), aPath.mExpanded);
550 :
551 : // Check attribute existence. This includes attributes with registered metadata, but also specially handled
552 : // mandatory global attributes (which just check for cluster on endpoint).
553 :
554 2471 : const EmberAfCluster * attributeCluster = nullptr;
555 2471 : const EmberAfAttributeMetadata * attributeMetadata = nullptr;
556 2471 : FindAttributeMetadata(aPath, &attributeCluster, &attributeMetadata);
557 :
558 2471 : if (attributeCluster == nullptr && attributeMetadata == nullptr)
559 : {
560 0 : return SendFailureStatus(aPath, aAttributeReports, UnsupportedAttributeStatus(aPath), nullptr);
561 : }
562 :
563 : // Check access control. A failed check will disallow the operation, and may or may not generate an attribute report
564 : // depending on whether the path was expanded.
565 :
566 : {
567 2471 : Access::RequestPath requestPath{ .cluster = aPath.mClusterId, .endpoint = aPath.mEndpointId };
568 2471 : Access::Privilege requestPrivilege = RequiredPrivilege::ForReadAttribute(aPath);
569 2471 : CHIP_ERROR err = Access::GetAccessControl().Check(aSubjectDescriptor, requestPath, requestPrivilege);
570 2471 : if (err != CHIP_NO_ERROR)
571 : {
572 0 : ReturnErrorCodeIf(err != CHIP_ERROR_ACCESS_DENIED, err);
573 0 : if (aPath.mExpanded)
574 : {
575 0 : return CHIP_NO_ERROR;
576 : }
577 :
578 0 : return SendFailureStatus(aPath, aAttributeReports, Protocols::InteractionModel::Status::UnsupportedAccess, nullptr);
579 : }
580 : }
581 :
582 : {
583 : // Special handling for mandatory global attributes: these are always for attribute list, using a special
584 : // reader (which can be lightweight constructed even from nullptr).
585 2471 : GlobalAttributeReader reader(attributeCluster);
586 : AttributeAccessInterface * attributeOverride =
587 2471 : (attributeCluster != nullptr) ? &reader : GetAttributeAccessOverride(aPath.mEndpointId, aPath.mClusterId);
588 2471 : if (attributeOverride)
589 : {
590 2471 : bool triedEncode = false;
591 2471 : ReturnErrorOnFailure(ReadViaAccessInterface(aSubjectDescriptor.fabricIndex, aIsFabricFiltered, aPath, aAttributeReports,
592 : apEncoderState, attributeOverride, &triedEncode));
593 2243 : ReturnErrorCodeIf(triedEncode, CHIP_NO_ERROR);
594 : }
595 2471 : }
596 :
597 : // Read attribute using Ember, if it doesn't have an override.
598 :
599 0 : TLV::TLVWriter backup;
600 0 : aAttributeReports.Checkpoint(backup);
601 :
602 0 : AttributeReportIB::Builder & attributeReport = aAttributeReports.CreateAttributeReport();
603 0 : ReturnErrorOnFailure(aAttributeReports.GetError());
604 :
605 0 : AttributeDataIB::Builder & attributeDataIBBuilder = attributeReport.CreateAttributeData();
606 0 : ReturnErrorOnFailure(attributeDataIBBuilder.GetError());
607 :
608 0 : DataVersion version = 0;
609 0 : ReturnErrorOnFailure(ReadClusterDataVersion(aPath, version));
610 0 : attributeDataIBBuilder.DataVersion(version);
611 0 : ReturnErrorOnFailure(attributeDataIBBuilder.GetError());
612 :
613 0 : AttributePathIB::Builder & attributePathIBBuilder = attributeDataIBBuilder.CreatePath();
614 0 : ReturnErrorOnFailure(attributeDataIBBuilder.GetError());
615 :
616 0 : CHIP_ERROR err = attributePathIBBuilder.Endpoint(aPath.mEndpointId)
617 0 : .Cluster(aPath.mClusterId)
618 0 : .Attribute(aPath.mAttributeId)
619 0 : .EndOfAttributePathIB();
620 0 : ReturnErrorOnFailure(err);
621 :
622 : EmberAfAttributeSearchRecord record;
623 0 : record.endpoint = aPath.mEndpointId;
624 0 : record.clusterId = aPath.mClusterId;
625 0 : record.attributeId = aPath.mAttributeId;
626 0 : Status status = emAfReadOrWriteAttribute(&record, &attributeMetadata, attributeData, sizeof(attributeData),
627 : /* write = */ false);
628 :
629 0 : if (status == Status::Success)
630 : {
631 0 : EmberAfAttributeType attributeType = attributeMetadata->attributeType;
632 0 : bool isNullable = attributeMetadata->IsNullable();
633 0 : TLV::TLVWriter * writer = attributeDataIBBuilder.GetWriter();
634 0 : VerifyOrReturnError(writer != nullptr, CHIP_NO_ERROR);
635 0 : TLV::Tag tag = TLV::ContextTag(AttributeDataIB::Tag::kData);
636 0 : switch (BaseType(attributeType))
637 : {
638 0 : case ZCL_NO_DATA_ATTRIBUTE_TYPE: // No data
639 0 : ReturnErrorOnFailure(writer->PutNull(tag));
640 0 : break;
641 0 : case ZCL_BOOLEAN_ATTRIBUTE_TYPE: // Boolean
642 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<bool>(*writer, isNullable));
643 0 : break;
644 0 : case ZCL_INT8U_ATTRIBUTE_TYPE: // Unsigned 8-bit integer
645 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<uint8_t>(*writer, isNullable));
646 0 : break;
647 0 : case ZCL_INT16U_ATTRIBUTE_TYPE: // Unsigned 16-bit integer
648 : {
649 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<uint16_t>(*writer, isNullable));
650 0 : break;
651 : }
652 0 : case ZCL_INT24U_ATTRIBUTE_TYPE: // Unsigned 24-bit integer
653 : {
654 : using IntType = OddSizedInteger<3, false>;
655 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
656 0 : break;
657 : }
658 0 : case ZCL_INT32U_ATTRIBUTE_TYPE: // Unsigned 32-bit integer
659 : {
660 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<uint32_t>(*writer, isNullable));
661 0 : break;
662 : }
663 0 : case ZCL_INT40U_ATTRIBUTE_TYPE: // Unsigned 40-bit integer
664 : {
665 : using IntType = OddSizedInteger<5, false>;
666 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
667 0 : break;
668 : }
669 0 : case ZCL_INT48U_ATTRIBUTE_TYPE: // Unsigned 48-bit integer
670 : {
671 : using IntType = OddSizedInteger<6, false>;
672 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
673 0 : break;
674 : }
675 0 : case ZCL_INT56U_ATTRIBUTE_TYPE: // Unsigned 56-bit integer
676 : {
677 : using IntType = OddSizedInteger<7, false>;
678 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
679 0 : break;
680 : }
681 0 : case ZCL_INT64U_ATTRIBUTE_TYPE: // Unsigned 64-bit integer
682 : {
683 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<uint64_t>(*writer, isNullable));
684 0 : break;
685 : }
686 0 : case ZCL_INT8S_ATTRIBUTE_TYPE: // Signed 8-bit integer
687 : {
688 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<int8_t>(*writer, isNullable));
689 0 : break;
690 : }
691 0 : case ZCL_INT16S_ATTRIBUTE_TYPE: // Signed 16-bit integer
692 : {
693 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<int16_t>(*writer, isNullable));
694 0 : break;
695 : }
696 0 : case ZCL_INT24S_ATTRIBUTE_TYPE: // Signed 24-bit integer
697 : {
698 : using IntType = OddSizedInteger<3, true>;
699 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
700 0 : break;
701 : }
702 0 : case ZCL_INT32S_ATTRIBUTE_TYPE: // Signed 32-bit integer
703 : {
704 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<int32_t>(*writer, isNullable));
705 0 : break;
706 : }
707 0 : case ZCL_INT40S_ATTRIBUTE_TYPE: // Signed 40-bit integer
708 : {
709 : using IntType = OddSizedInteger<5, true>;
710 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
711 0 : break;
712 : }
713 0 : case ZCL_INT48S_ATTRIBUTE_TYPE: // Signed 48-bit integer
714 : {
715 : using IntType = OddSizedInteger<6, true>;
716 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
717 0 : break;
718 : }
719 0 : case ZCL_INT56S_ATTRIBUTE_TYPE: // Signed 56-bit integer
720 : {
721 : using IntType = OddSizedInteger<7, true>;
722 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
723 0 : break;
724 : }
725 0 : case ZCL_INT64S_ATTRIBUTE_TYPE: // Signed 64-bit integer
726 : {
727 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<int64_t>(*writer, isNullable));
728 0 : break;
729 : }
730 0 : case ZCL_SINGLE_ATTRIBUTE_TYPE: // 32-bit float
731 : {
732 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<float>(*writer, isNullable));
733 0 : break;
734 : }
735 0 : case ZCL_DOUBLE_ATTRIBUTE_TYPE: // 64-bit float
736 : {
737 0 : ReturnErrorOnFailure(attributeBufferToNumericTlvData<double>(*writer, isNullable));
738 0 : break;
739 : }
740 0 : case ZCL_CHAR_STRING_ATTRIBUTE_TYPE: // Char string
741 : {
742 0 : char * actualData = reinterpret_cast<char *>(attributeData + 1);
743 0 : uint8_t dataLength = attributeData[0];
744 0 : if (dataLength == 0xFF)
745 : {
746 0 : if (isNullable)
747 : {
748 0 : ReturnErrorOnFailure(writer->PutNull(tag));
749 : }
750 : else
751 : {
752 0 : return CHIP_ERROR_INCORRECT_STATE;
753 : }
754 : }
755 : else
756 : {
757 0 : ReturnErrorOnFailure(writer->PutString(tag, actualData, dataLength));
758 : }
759 0 : break;
760 : }
761 0 : case ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE: {
762 0 : char * actualData = reinterpret_cast<char *>(attributeData + 2); // The pascal string contains 2 bytes length
763 : uint16_t dataLength;
764 0 : memcpy(&dataLength, attributeData, sizeof(dataLength));
765 0 : if (dataLength == 0xFFFF)
766 : {
767 0 : if (isNullable)
768 : {
769 0 : ReturnErrorOnFailure(writer->PutNull(tag));
770 : }
771 : else
772 : {
773 0 : return CHIP_ERROR_INCORRECT_STATE;
774 : }
775 : }
776 : else
777 : {
778 0 : ReturnErrorOnFailure(writer->PutString(tag, actualData, dataLength));
779 : }
780 0 : break;
781 : }
782 0 : case ZCL_OCTET_STRING_ATTRIBUTE_TYPE: // Octet string
783 : {
784 0 : uint8_t * actualData = attributeData + 1;
785 0 : uint8_t dataLength = attributeData[0];
786 0 : if (dataLength == 0xFF)
787 : {
788 0 : if (isNullable)
789 : {
790 0 : ReturnErrorOnFailure(writer->PutNull(tag));
791 : }
792 : else
793 : {
794 0 : return CHIP_ERROR_INCORRECT_STATE;
795 : }
796 : }
797 : else
798 : {
799 0 : ReturnErrorOnFailure(writer->Put(tag, chip::ByteSpan(actualData, dataLength)));
800 : }
801 0 : break;
802 : }
803 0 : case ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE: {
804 0 : uint8_t * actualData = attributeData + 2; // The pascal string contains 2 bytes length
805 : uint16_t dataLength;
806 0 : memcpy(&dataLength, attributeData, sizeof(dataLength));
807 0 : if (dataLength == 0xFFFF)
808 : {
809 0 : if (isNullable)
810 : {
811 0 : ReturnErrorOnFailure(writer->PutNull(tag));
812 : }
813 : else
814 : {
815 0 : return CHIP_ERROR_INCORRECT_STATE;
816 : }
817 : }
818 : else
819 : {
820 0 : ReturnErrorOnFailure(writer->Put(tag, chip::ByteSpan(actualData, dataLength)));
821 : }
822 0 : break;
823 : }
824 0 : default:
825 0 : ChipLogError(DataManagement, "Attribute type 0x%x not handled", static_cast<int>(attributeType));
826 0 : status = Status::UnsupportedRead;
827 : }
828 : }
829 :
830 0 : if (status == Protocols::InteractionModel::Status::Success)
831 : {
832 0 : return SendSuccessStatus(attributeReport, attributeDataIBBuilder);
833 : }
834 :
835 0 : return SendFailureStatus(aPath, aAttributeReports, status, &backup);
836 : }
837 :
838 : namespace {
839 :
840 : template <typename T>
841 0 : CHIP_ERROR numericTlvDataToAttributeBuffer(TLV::TLVReader & aReader, bool isNullable, uint16_t & dataLen)
842 : {
843 : typename NumericAttributeTraits<T>::StorageType value;
844 : static_assert(sizeof(value) <= sizeof(attributeData), "Value cannot fit into attribute data");
845 0 : if (isNullable && aReader.GetType() == TLV::kTLVType_Null)
846 : {
847 0 : NumericAttributeTraits<T>::SetNull(value);
848 : }
849 : else
850 : {
851 : typename NumericAttributeTraits<T>::WorkingType val;
852 0 : ReturnErrorOnFailure(aReader.Get(val));
853 0 : VerifyOrReturnError(NumericAttributeTraits<T>::CanRepresentValue(isNullable, val), CHIP_ERROR_INVALID_ARGUMENT);
854 0 : NumericAttributeTraits<T>::WorkingToStorage(val, value);
855 : }
856 0 : dataLen = sizeof(value);
857 0 : memcpy(attributeData, &value, sizeof(value));
858 0 : return CHIP_NO_ERROR;
859 : }
860 :
861 : template <typename T>
862 0 : CHIP_ERROR stringTlvDataToAttributeBuffer(TLV::TLVReader & aReader, bool isOctetString, bool isNullable, uint16_t & dataLen)
863 : {
864 0 : const uint8_t * data = nullptr;
865 : T len;
866 0 : if (isNullable && aReader.GetType() == TLV::kTLVType_Null)
867 : {
868 : // Null is represented by an 0xFF or 0xFFFF length, respectively.
869 0 : len = std::numeric_limits<T>::max();
870 0 : memcpy(&attributeData[0], &len, sizeof(len));
871 0 : dataLen = sizeof(len);
872 : }
873 : else
874 : {
875 0 : VerifyOrReturnError((isOctetString && aReader.GetType() == TLV::TLVType::kTLVType_ByteString) ||
876 : (!isOctetString && aReader.GetType() == TLV::TLVType::kTLVType_UTF8String),
877 : CHIP_ERROR_INVALID_ARGUMENT);
878 0 : VerifyOrReturnError(CanCastTo<T>(aReader.GetLength()), CHIP_ERROR_MESSAGE_TOO_LONG);
879 0 : ReturnErrorOnFailure(aReader.GetDataPtr(data));
880 0 : len = static_cast<T>(aReader.GetLength());
881 0 : VerifyOrReturnError(len != std::numeric_limits<T>::max(), CHIP_ERROR_MESSAGE_TOO_LONG);
882 0 : VerifyOrReturnError(len + sizeof(len) /* length at the beginning of data */ <= sizeof(attributeData),
883 : CHIP_ERROR_MESSAGE_TOO_LONG);
884 0 : memcpy(&attributeData[0], &len, sizeof(len));
885 0 : memcpy(&attributeData[sizeof(len)], data, len);
886 0 : dataLen = static_cast<uint16_t>(len + sizeof(len));
887 : }
888 0 : return CHIP_NO_ERROR;
889 : }
890 :
891 0 : CHIP_ERROR prepareWriteData(const EmberAfAttributeMetadata * attributeMetadata, TLV::TLVReader & aReader, uint16_t & dataLen)
892 : {
893 0 : EmberAfAttributeType expectedType = BaseType(attributeMetadata->attributeType);
894 0 : bool isNullable = attributeMetadata->IsNullable();
895 0 : switch (expectedType)
896 : {
897 0 : case ZCL_BOOLEAN_ATTRIBUTE_TYPE: // Boolean
898 0 : return numericTlvDataToAttributeBuffer<bool>(aReader, isNullable, dataLen);
899 0 : case ZCL_INT8U_ATTRIBUTE_TYPE: // Unsigned 8-bit integer
900 0 : return numericTlvDataToAttributeBuffer<uint8_t>(aReader, isNullable, dataLen);
901 0 : case ZCL_INT16U_ATTRIBUTE_TYPE: // Unsigned 16-bit integer
902 0 : return numericTlvDataToAttributeBuffer<uint16_t>(aReader, isNullable, dataLen);
903 0 : case ZCL_INT24U_ATTRIBUTE_TYPE: // Unsigned 24-bit integer
904 : {
905 : using IntType = OddSizedInteger<3, false>;
906 0 : return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
907 : }
908 0 : case ZCL_INT32U_ATTRIBUTE_TYPE: // Unsigned 32-bit integer
909 0 : return numericTlvDataToAttributeBuffer<uint32_t>(aReader, isNullable, dataLen);
910 0 : case ZCL_INT40U_ATTRIBUTE_TYPE: // Unsigned 40-bit integer
911 : {
912 : using IntType = OddSizedInteger<5, false>;
913 0 : return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
914 : }
915 0 : case ZCL_INT48U_ATTRIBUTE_TYPE: // Unsigned 48-bit integer
916 : {
917 : using IntType = OddSizedInteger<6, false>;
918 0 : return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
919 : }
920 0 : case ZCL_INT56U_ATTRIBUTE_TYPE: // Unsigned 56-bit integer
921 : {
922 : using IntType = OddSizedInteger<7, false>;
923 0 : return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
924 : }
925 0 : case ZCL_INT64U_ATTRIBUTE_TYPE: // Unsigned 64-bit integer
926 0 : return numericTlvDataToAttributeBuffer<uint64_t>(aReader, isNullable, dataLen);
927 0 : case ZCL_INT8S_ATTRIBUTE_TYPE: // Signed 8-bit integer
928 0 : return numericTlvDataToAttributeBuffer<int8_t>(aReader, isNullable, dataLen);
929 0 : case ZCL_INT16S_ATTRIBUTE_TYPE: // Signed 16-bit integer
930 0 : return numericTlvDataToAttributeBuffer<int16_t>(aReader, isNullable, dataLen);
931 0 : case ZCL_INT24S_ATTRIBUTE_TYPE: // Signed 24-bit integer
932 : {
933 : using IntType = OddSizedInteger<3, true>;
934 0 : return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
935 : }
936 0 : case ZCL_INT32S_ATTRIBUTE_TYPE: // Signed 32-bit integer
937 0 : return numericTlvDataToAttributeBuffer<int32_t>(aReader, isNullable, dataLen);
938 0 : case ZCL_INT40S_ATTRIBUTE_TYPE: // Signed 40-bit integer
939 : {
940 : using IntType = OddSizedInteger<5, true>;
941 0 : return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
942 : }
943 0 : case ZCL_INT48S_ATTRIBUTE_TYPE: // Signed 48-bit integer
944 : {
945 : using IntType = OddSizedInteger<6, true>;
946 0 : return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
947 : }
948 0 : case ZCL_INT56S_ATTRIBUTE_TYPE: // Signed 56-bit integer
949 : {
950 : using IntType = OddSizedInteger<7, true>;
951 0 : return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
952 : }
953 0 : case ZCL_INT64S_ATTRIBUTE_TYPE: // Signed 64-bit integer
954 0 : return numericTlvDataToAttributeBuffer<int64_t>(aReader, isNullable, dataLen);
955 0 : case ZCL_SINGLE_ATTRIBUTE_TYPE: // 32-bit float
956 0 : return numericTlvDataToAttributeBuffer<float>(aReader, isNullable, dataLen);
957 0 : case ZCL_DOUBLE_ATTRIBUTE_TYPE: // 64-bit float
958 0 : return numericTlvDataToAttributeBuffer<double>(aReader, isNullable, dataLen);
959 0 : case ZCL_OCTET_STRING_ATTRIBUTE_TYPE: // Octet string
960 : case ZCL_CHAR_STRING_ATTRIBUTE_TYPE: // Char string
961 0 : return stringTlvDataToAttributeBuffer<uint8_t>(aReader, expectedType == ZCL_OCTET_STRING_ATTRIBUTE_TYPE, isNullable,
962 0 : dataLen);
963 0 : case ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE: // Long octet string
964 : case ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE: // Long char string
965 0 : return stringTlvDataToAttributeBuffer<uint16_t>(aReader, expectedType == ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE, isNullable,
966 0 : dataLen);
967 0 : default:
968 0 : ChipLogError(DataManagement, "Attribute type %x not handled", static_cast<int>(expectedType));
969 0 : return CHIP_ERROR_INVALID_DATA_LIST;
970 : }
971 : }
972 : } // namespace
973 :
974 2440 : const EmberAfAttributeMetadata * GetAttributeMetadata(const ConcreteAttributePath & aPath)
975 : {
976 2440 : return emberAfLocateAttributeMetadata(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId);
977 : }
978 :
979 2434 : CHIP_ERROR WriteSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, const ConcreteDataAttributePath & aPath,
980 : TLV::TLVReader & aReader, WriteHandler * apWriteHandler)
981 : {
982 : // Check attribute existence. This includes attributes with registered metadata, but also specially handled
983 : // mandatory global attributes (which just check for cluster on endpoint).
984 2434 : const EmberAfCluster * attributeCluster = nullptr;
985 2434 : const EmberAfAttributeMetadata * attributeMetadata = nullptr;
986 2434 : FindAttributeMetadata(aPath, &attributeCluster, &attributeMetadata);
987 :
988 2434 : if (attributeCluster == nullptr && attributeMetadata == nullptr)
989 : {
990 0 : return apWriteHandler->AddStatus(aPath, UnsupportedAttributeStatus(aPath));
991 : }
992 :
993 : // All the global attributes we don't have metadata for are readonly.
994 2434 : if (attributeMetadata == nullptr || attributeMetadata->IsReadOnly())
995 : {
996 0 : return apWriteHandler->AddStatus(aPath, Protocols::InteractionModel::Status::UnsupportedWrite);
997 : }
998 :
999 : {
1000 2434 : Access::RequestPath requestPath{ .cluster = aPath.mClusterId, .endpoint = aPath.mEndpointId };
1001 2434 : Access::Privilege requestPrivilege = RequiredPrivilege::ForWriteAttribute(aPath);
1002 2434 : CHIP_ERROR err = CHIP_NO_ERROR;
1003 2434 : if (!apWriteHandler->ACLCheckCacheHit({ aPath, requestPrivilege }))
1004 : {
1005 406 : err = Access::GetAccessControl().Check(aSubjectDescriptor, requestPath, requestPrivilege);
1006 : }
1007 2434 : if (err != CHIP_NO_ERROR)
1008 : {
1009 0 : ReturnErrorCodeIf(err != CHIP_ERROR_ACCESS_DENIED, err);
1010 : // TODO: when wildcard/group writes are supported, handle them to discard rather than fail with status
1011 0 : return apWriteHandler->AddStatus(aPath, Protocols::InteractionModel::Status::UnsupportedAccess);
1012 : }
1013 2434 : apWriteHandler->CacheACLCheckResult({ aPath, requestPrivilege });
1014 : }
1015 :
1016 2434 : if (attributeMetadata->MustUseTimedWrite() && !apWriteHandler->IsTimedWrite())
1017 : {
1018 0 : return apWriteHandler->AddStatus(aPath, Protocols::InteractionModel::Status::NeedsTimedInteraction);
1019 : }
1020 :
1021 2434 : if (aPath.mDataVersion.HasValue() && !IsClusterDataVersionEqual(aPath, aPath.mDataVersion.Value()))
1022 : {
1023 0 : ChipLogError(DataManagement, "Write Version mismatch for Endpoint %x, Cluster " ChipLogFormatMEI, aPath.mEndpointId,
1024 : ChipLogValueMEI(aPath.mClusterId));
1025 0 : return apWriteHandler->AddStatus(aPath, Protocols::InteractionModel::Status::DataVersionMismatch);
1026 : }
1027 :
1028 2434 : if (auto * attrOverride = GetAttributeAccessOverride(aPath.mEndpointId, aPath.mClusterId))
1029 : {
1030 2434 : AttributeValueDecoder valueDecoder(aReader, aSubjectDescriptor);
1031 2434 : ReturnErrorOnFailure(attrOverride->Write(aPath, valueDecoder));
1032 :
1033 2429 : if (valueDecoder.TriedDecode())
1034 : {
1035 2429 : MatterReportingAttributeChangeCallback(aPath);
1036 2429 : return apWriteHandler->AddStatus(aPath, Protocols::InteractionModel::Status::Success);
1037 : }
1038 : }
1039 :
1040 0 : CHIP_ERROR preparationError = CHIP_NO_ERROR;
1041 0 : uint16_t dataLen = 0;
1042 0 : if ((preparationError = prepareWriteData(attributeMetadata, aReader, dataLen)) != CHIP_NO_ERROR)
1043 : {
1044 0 : ChipLogDetail(Zcl, "Failed to prepare data to write: %" CHIP_ERROR_FORMAT, preparationError.Format());
1045 0 : return apWriteHandler->AddStatus(aPath, Protocols::InteractionModel::Status::InvalidValue);
1046 : }
1047 :
1048 0 : if (dataLen > attributeMetadata->size)
1049 : {
1050 0 : ChipLogDetail(Zcl, "Data to write exceedes the attribute size claimed.");
1051 0 : return apWriteHandler->AddStatus(aPath, Protocols::InteractionModel::Status::InvalidValue);
1052 : }
1053 :
1054 0 : auto status = emAfWriteAttributeExternal(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId, attributeData,
1055 0 : attributeMetadata->attributeType);
1056 0 : return apWriteHandler->AddStatus(aPath, status);
1057 : }
1058 :
1059 0 : bool IsClusterDataVersionEqual(const ConcreteClusterPath & aConcreteClusterPath, DataVersion aRequiredVersion)
1060 : {
1061 0 : DataVersion * version = emberAfDataVersionStorage(aConcreteClusterPath);
1062 0 : if (version == nullptr)
1063 : {
1064 0 : ChipLogError(DataManagement, "Endpoint %x, Cluster " ChipLogFormatMEI " not found in IsClusterDataVersionEqual!",
1065 : aConcreteClusterPath.mEndpointId, ChipLogValueMEI(aConcreteClusterPath.mClusterId));
1066 0 : return false;
1067 : }
1068 :
1069 0 : return (*(version)) == aRequiredVersion;
1070 : }
1071 :
1072 0 : bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint)
1073 : {
1074 : CHIP_ERROR err;
1075 0 : auto deviceTypeList = emberAfDeviceTypeListFromEndpoint(endpoint, err);
1076 0 : if (err != CHIP_NO_ERROR)
1077 : {
1078 0 : return false;
1079 : }
1080 :
1081 0 : for (auto & device : deviceTypeList)
1082 : {
1083 0 : if (device.deviceId == deviceType)
1084 : {
1085 0 : return true;
1086 : }
1087 : }
1088 :
1089 0 : return false;
1090 : }
1091 :
1092 0 : Protocols::InteractionModel::Status CheckEventSupportStatus(const ConcreteEventPath & aPath)
1093 : {
1094 : using Protocols::InteractionModel::Status;
1095 :
1096 0 : const EmberAfEndpointType * type = emberAfFindEndpointType(aPath.mEndpointId);
1097 0 : if (type == nullptr)
1098 : {
1099 0 : return Status::UnsupportedEndpoint;
1100 : }
1101 :
1102 0 : const EmberAfCluster * cluster = emberAfFindClusterInType(type, aPath.mClusterId, CLUSTER_MASK_SERVER);
1103 0 : if (cluster == nullptr)
1104 : {
1105 0 : return Status::UnsupportedCluster;
1106 : }
1107 :
1108 : #if CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE
1109 : for (size_t i = 0; i < cluster->eventCount; ++i)
1110 : {
1111 : if (cluster->eventList[i] == aPath.mEventId)
1112 : {
1113 : return Status::Success;
1114 : }
1115 : }
1116 :
1117 : return Status::UnsupportedEvent;
1118 : #else
1119 : // No way to tell. Just claim supported.
1120 0 : return Status::Success;
1121 : #endif // CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE
1122 : }
1123 :
1124 : } // namespace app
1125 : } // namespace chip
1126 :
1127 2476 : void MatterReportingAttributeChangeCallback(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId)
1128 : {
1129 : // Attribute writes have asserted this already, but this assert should catch
1130 : // applications notifying about changes from their end.
1131 2476 : assertChipStackLockedByCurrentThread();
1132 :
1133 2476 : AttributePathParams info;
1134 2476 : info.mClusterId = clusterId;
1135 2476 : info.mAttributeId = attributeId;
1136 2476 : info.mEndpointId = endpoint;
1137 :
1138 2476 : IncreaseClusterDataVersion(ConcreteClusterPath(endpoint, clusterId));
1139 2476 : InteractionModelEngine::GetInstance()->GetReportingEngine().SetDirty(info);
1140 2476 : }
1141 :
1142 2429 : void MatterReportingAttributeChangeCallback(const ConcreteAttributePath & aPath)
1143 : {
1144 2429 : return MatterReportingAttributeChangeCallback(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId);
1145 : }
1146 :
1147 24 : void MatterReportingAttributeChangeCallback(EndpointId endpoint)
1148 : {
1149 : // Attribute writes have asserted this already, but this assert should catch
1150 : // applications notifying about changes from their end.
1151 24 : assertChipStackLockedByCurrentThread();
1152 :
1153 24 : AttributePathParams info;
1154 24 : info.mEndpointId = endpoint;
1155 :
1156 : // We are adding or enabling a whole endpoint, in this case, we do not touch the cluster data version.
1157 :
1158 24 : InteractionModelEngine::GetInstance()->GetReportingEngine().SetDirty(info);
1159 24 : }
|