Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : #include "system/SystemPacketBuffer.h"
20 : #include <app/ClusterStateCache.h>
21 : #include <app/InteractionModelEngine.h>
22 : #include <tuple>
23 :
24 : namespace chip {
25 : namespace app {
26 :
27 : namespace {
28 :
29 : // Determine how much space a StatusIB takes up on the wire.
30 36 : uint32_t SizeOfStatusIB(const StatusIB & aStatus)
31 : {
32 : // 1 byte: anonymous tag control byte for struct.
33 : // 1 byte: control byte for uint8 value.
34 : // 1 byte: context-specific tag for uint8 value.
35 : // 1 byte: the uint8 value.
36 : // 1 byte: end of container.
37 36 : uint32_t size = 5;
38 :
39 36 : if (aStatus.mClusterStatus.HasValue())
40 : {
41 : // 1 byte: control byte for uint8 value.
42 : // 1 byte: context-specific tag for uint8 value.
43 : // 1 byte: the uint8 value.
44 0 : size += 3;
45 : }
46 :
47 36 : return size;
48 : }
49 :
50 : } // anonymous namespace
51 :
52 : template <bool CanEnableDataCaching>
53 80 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetElementTLVSize(TLV::TLVReader * apData, uint32_t & aSize)
54 : {
55 80 : Platform::ScopedMemoryBufferWithSize<uint8_t> backingBuffer;
56 80 : TLV::TLVReader reader;
57 80 : reader.Init(*apData);
58 80 : size_t totalBufSize = reader.GetTotalLength();
59 80 : backingBuffer.Calloc(totalBufSize);
60 80 : VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
61 80 : TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), totalBufSize);
62 80 : ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), reader));
63 80 : aSize = writer.GetLengthWritten();
64 80 : ReturnErrorOnFailure(writer.Finalize(backingBuffer));
65 80 : return CHIP_NO_ERROR;
66 80 : }
67 :
68 : template <bool CanEnableDataCaching>
69 115 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::UpdateCache(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
70 : const StatusIB & aStatus)
71 : {
72 115 : AttributeState state;
73 115 : bool endpointIsNew = false;
74 :
75 115 : if (mCache.find(aPath.mEndpointId) == mCache.end())
76 : {
77 : //
78 : // Since we might potentially be creating a new entry at mCache[aPath.mEndpointId][aPath.mClusterId] that
79 : // wasn't there before, we need to check if an entry didn't exist there previously and remember that so that
80 : // we can appropriately notify our clients of the addition of a new endpoint.
81 : //
82 15 : endpointIsNew = true;
83 : }
84 :
85 115 : if (apData)
86 : {
87 80 : uint32_t elementSize = 0;
88 80 : ReturnErrorOnFailure(GetElementTLVSize(apData, elementSize));
89 :
90 : if constexpr (CanEnableDataCaching)
91 : {
92 80 : if (mCacheData)
93 : {
94 74 : Platform::ScopedMemoryBufferWithSize<uint8_t> backingBuffer;
95 74 : backingBuffer.Calloc(elementSize);
96 74 : VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
97 74 : TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), elementSize);
98 74 : ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), *apData));
99 74 : ReturnErrorOnFailure(writer.Finalize(backingBuffer));
100 :
101 74 : state.template Set<AttributeData>(std::move(backingBuffer));
102 74 : }
103 : else
104 : {
105 6 : state.template Set<uint32_t>(elementSize);
106 : }
107 : }
108 : else
109 : {
110 0 : state = elementSize;
111 : }
112 :
113 : //
114 : // Clear out the committed data version and only set it again once we have received all data for this cluster.
115 : // Otherwise, we may have incomplete data that looks like it's complete since it has a valid data version.
116 : //
117 80 : mCache[aPath.mEndpointId][aPath.mClusterId].mCommittedDataVersion.ClearValue();
118 :
119 : // This commits a pending data version if the last report path is valid and it is different from the current path.
120 80 : if (mLastReportDataPath.IsValidConcreteClusterPath() && mLastReportDataPath != aPath)
121 : {
122 12 : CommitPendingDataVersion();
123 : }
124 :
125 80 : bool foundEncompassingWildcardPath = false;
126 102 : for (const auto & path : mRequestPathSet)
127 : {
128 79 : if (path.IncludesAllAttributesInCluster(aPath))
129 : {
130 57 : foundEncompassingWildcardPath = true;
131 57 : break;
132 : }
133 : }
134 :
135 : // if this data item is encompassed by a wildcard path, let's go ahead and update its pending data version.
136 80 : if (foundEncompassingWildcardPath)
137 : {
138 57 : mCache[aPath.mEndpointId][aPath.mClusterId].mPendingDataVersion = aPath.mDataVersion;
139 : }
140 :
141 80 : mLastReportDataPath = aPath;
142 : }
143 : else
144 : {
145 : if constexpr (CanEnableDataCaching)
146 : {
147 35 : if (mCacheData)
148 : {
149 32 : state.template Set<StatusIB>(aStatus);
150 : }
151 : else
152 : {
153 3 : state.template Set<uint32_t>(SizeOfStatusIB(aStatus));
154 : }
155 : }
156 : else
157 : {
158 0 : state = SizeOfStatusIB(aStatus);
159 : }
160 : }
161 :
162 : //
163 : // if the endpoint didn't exist previously, let's track the insertion
164 : // so that we can inform our callback of a new endpoint being added appropriately.
165 : //
166 115 : if (endpointIsNew)
167 : {
168 15 : mAddedEndpoints.push_back(aPath.mEndpointId);
169 : }
170 :
171 115 : mCache[aPath.mEndpointId][aPath.mClusterId].mAttributes[aPath.mAttributeId] = std::move(state);
172 :
173 115 : if (mCacheData)
174 : {
175 106 : mChangedAttributeSet.insert(aPath);
176 : }
177 :
178 115 : return CHIP_NO_ERROR;
179 115 : }
180 :
181 : template <bool CanEnableDataCaching>
182 32 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::UpdateEventCache(const EventHeader & aEventHeader, TLV::TLVReader * apData,
183 : const StatusIB * apStatus)
184 : {
185 32 : if (apData)
186 : {
187 : //
188 : // If we've already seen this event before, there's no more work to be done.
189 : //
190 32 : if (mHighestReceivedEventNumber.HasValue() && aEventHeader.mEventNumber <= mHighestReceivedEventNumber.Value())
191 : {
192 5 : return CHIP_NO_ERROR;
193 : }
194 27 : if (mCacheData)
195 : {
196 21 : System::PacketBufferHandle handle = System::PacketBufferHandle::New(chip::app::kMaxSecureSduLengthBytes);
197 21 : VerifyOrReturnError(!handle.IsNull(), CHIP_ERROR_NO_MEMORY);
198 :
199 21 : System::PacketBufferTLVWriter writer;
200 21 : writer.Init(std::move(handle), false);
201 :
202 21 : ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), *apData));
203 21 : ReturnErrorOnFailure(writer.Finalize(&handle));
204 :
205 : //
206 : // Compact the buffer down to a more reasonably sized packet buffer
207 : // if we can.
208 : //
209 21 : handle.RightSize();
210 :
211 21 : EventData eventData;
212 21 : eventData.first = aEventHeader;
213 21 : eventData.second = std::move(handle);
214 :
215 21 : mEventDataCache.insert(std::move(eventData));
216 21 : }
217 27 : mHighestReceivedEventNumber.SetValue(aEventHeader.mEventNumber);
218 : }
219 0 : else if (apStatus)
220 : {
221 0 : if (mCacheData)
222 : {
223 0 : mEventStatusCache[aEventHeader.mPath] = *apStatus;
224 : }
225 : }
226 :
227 27 : return CHIP_NO_ERROR;
228 : }
229 :
230 : template <bool CanEnableDataCaching>
231 29 : void ClusterStateCacheT<CanEnableDataCaching>::OnReportBegin()
232 : {
233 29 : mLastReportDataPath = ConcreteClusterPath(kInvalidEndpointId, kInvalidClusterId);
234 29 : mChangedAttributeSet.clear();
235 29 : mAddedEndpoints.clear();
236 29 : mCallback.OnReportBegin();
237 29 : }
238 :
239 : template <bool CanEnableDataCaching>
240 41 : void ClusterStateCacheT<CanEnableDataCaching>::CommitPendingDataVersion()
241 : {
242 41 : if (!mLastReportDataPath.IsValidConcreteClusterPath())
243 : {
244 7 : return;
245 : }
246 :
247 34 : auto & lastClusterInfo = mCache[mLastReportDataPath.mEndpointId][mLastReportDataPath.mClusterId];
248 34 : if (lastClusterInfo.mPendingDataVersion.HasValue())
249 : {
250 19 : lastClusterInfo.mCommittedDataVersion = lastClusterInfo.mPendingDataVersion;
251 19 : lastClusterInfo.mPendingDataVersion.ClearValue();
252 : }
253 : }
254 :
255 : template <bool CanEnableDataCaching>
256 29 : void ClusterStateCacheT<CanEnableDataCaching>::OnReportEnd()
257 : {
258 29 : CommitPendingDataVersion();
259 29 : mLastReportDataPath = ConcreteClusterPath(kInvalidEndpointId, kInvalidClusterId);
260 29 : std::set<std::tuple<EndpointId, ClusterId>> changedClusters;
261 :
262 : //
263 : // Add the EndpointId and ClusterId into a set so that we only
264 : // convey unique combinations in the subsequent OnClusterChanged callback.
265 : //
266 131 : for (auto & path : mChangedAttributeSet)
267 : {
268 102 : mCallback.OnAttributeChanged(this, path);
269 102 : changedClusters.insert(std::make_tuple(path.mEndpointId, path.mClusterId));
270 : }
271 :
272 62 : for (auto & item : changedClusters)
273 : {
274 33 : mCallback.OnClusterChanged(this, std::get<0>(item), std::get<1>(item));
275 : }
276 :
277 44 : for (auto endpoint : mAddedEndpoints)
278 : {
279 15 : mCallback.OnEndpointAdded(this, endpoint);
280 : }
281 :
282 29 : mCallback.OnReportEnd();
283 29 : }
284 :
285 : template <>
286 128 : CHIP_ERROR ClusterStateCacheT<true>::Get(const ConcreteAttributePath & path, TLV::TLVReader & reader) const
287 : {
288 : CHIP_ERROR err;
289 128 : auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err);
290 128 : ReturnErrorOnFailure(err);
291 :
292 88 : if (attributeState->template Is<StatusIB>())
293 : {
294 3 : return CHIP_ERROR_IM_STATUS_CODE_RECEIVED;
295 : }
296 :
297 85 : if (!attributeState->template Is<AttributeData>())
298 : {
299 3 : return CHIP_ERROR_KEY_NOT_FOUND;
300 : }
301 :
302 82 : reader.Init(attributeState->template Get<AttributeData>().Get(), attributeState->template Get<AttributeData>().AllocatedSize());
303 82 : return reader.Next();
304 : }
305 :
306 : template <>
307 0 : CHIP_ERROR ClusterStateCacheT<false>::Get(const ConcreteAttributePath & path, TLV::TLVReader & reader) const
308 : {
309 0 : return CHIP_ERROR_KEY_NOT_FOUND;
310 : }
311 :
312 : template <bool CanEnableDataCaching>
313 44 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::Get(EventNumber eventNumber, TLV::TLVReader & reader) const
314 : {
315 : CHIP_ERROR err;
316 :
317 44 : auto eventData = GetEventData(eventNumber, err);
318 44 : ReturnErrorOnFailure(err);
319 :
320 44 : System::PacketBufferTLVReader bufReader;
321 :
322 44 : bufReader.Init(eventData->second.Retain());
323 44 : ReturnErrorOnFailure(bufReader.Next());
324 :
325 44 : reader.Init(bufReader);
326 44 : return CHIP_NO_ERROR;
327 44 : }
328 :
329 : template <bool CanEnableDataCaching>
330 : const typename ClusterStateCacheT<CanEnableDataCaching>::EndpointState *
331 168 : ClusterStateCacheT<CanEnableDataCaching>::GetEndpointState(EndpointId endpointId, CHIP_ERROR & err) const
332 : {
333 168 : auto endpointIter = mCache.find(endpointId);
334 168 : if (endpointIter == mCache.end())
335 : {
336 16 : err = CHIP_ERROR_KEY_NOT_FOUND;
337 16 : return nullptr;
338 : }
339 :
340 152 : err = CHIP_NO_ERROR;
341 152 : return &endpointIter->second;
342 : }
343 :
344 : template <bool CanEnableDataCaching>
345 : const typename ClusterStateCacheT<CanEnableDataCaching>::ClusterState *
346 168 : ClusterStateCacheT<CanEnableDataCaching>::GetClusterState(EndpointId endpointId, ClusterId clusterId, CHIP_ERROR & err) const
347 : {
348 168 : auto endpointState = GetEndpointState(endpointId, err);
349 168 : if (err != CHIP_NO_ERROR)
350 : {
351 16 : return nullptr;
352 : }
353 :
354 152 : auto clusterState = endpointState->find(clusterId);
355 152 : if (clusterState == endpointState->end())
356 : {
357 15 : err = CHIP_ERROR_KEY_NOT_FOUND;
358 15 : return nullptr;
359 : }
360 :
361 137 : err = CHIP_NO_ERROR;
362 137 : return &clusterState->second;
363 : }
364 :
365 : template <bool CanEnableDataCaching>
366 : const typename ClusterStateCacheT<CanEnableDataCaching>::AttributeState *
367 129 : ClusterStateCacheT<CanEnableDataCaching>::GetAttributeState(EndpointId endpointId, ClusterId clusterId, AttributeId attributeId,
368 : CHIP_ERROR & err) const
369 : {
370 129 : auto clusterState = GetClusterState(endpointId, clusterId, err);
371 129 : if (err != CHIP_NO_ERROR)
372 : {
373 31 : return nullptr;
374 : }
375 :
376 98 : auto attributeState = clusterState->mAttributes.find(attributeId);
377 98 : if (attributeState == clusterState->mAttributes.end())
378 : {
379 9 : err = CHIP_ERROR_KEY_NOT_FOUND;
380 9 : return nullptr;
381 : }
382 :
383 89 : err = CHIP_NO_ERROR;
384 89 : return &attributeState->second;
385 : }
386 :
387 : template <bool CanEnableDataCaching>
388 : const typename ClusterStateCacheT<CanEnableDataCaching>::EventData *
389 88 : ClusterStateCacheT<CanEnableDataCaching>::GetEventData(EventNumber eventNumber, CHIP_ERROR & err) const
390 : {
391 88 : EventData compareKey;
392 :
393 88 : compareKey.first.mEventNumber = eventNumber;
394 88 : auto eventData = mEventDataCache.find(std::move(compareKey));
395 88 : if (eventData == mEventDataCache.end())
396 : {
397 0 : err = CHIP_ERROR_KEY_NOT_FOUND;
398 0 : return nullptr;
399 : }
400 :
401 88 : err = CHIP_NO_ERROR;
402 88 : return &(*eventData);
403 88 : }
404 :
405 : template <bool CanEnableDataCaching>
406 115 : void ClusterStateCacheT<CanEnableDataCaching>::OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
407 : const StatusIB & aStatus)
408 : {
409 : //
410 : // Since the cache itself is a ReadClient::Callback, it may be incorrectly passed in directly when registering with the
411 : // ReadClient. This should be avoided, since that bypasses the built-in buffered reader adapter callback that is needed for
412 : // lists to work correctly.
413 : //
414 : // Instead, the right callback should be retrieved using GetBufferedCallback().
415 : //
416 : // To catch such errors, we validate that the provided concrete path never indicates a raw list item operation (which the
417 : // buffered reader will handle and convert for us).
418 : //
419 : //
420 115 : VerifyOrDie(!aPath.IsListItemOperation());
421 :
422 : // Copy the reader for forwarding
423 115 : TLV::TLVReader dataSnapshot;
424 115 : if (apData)
425 : {
426 80 : dataSnapshot.Init(*apData);
427 : }
428 :
429 115 : UpdateCache(aPath, apData, aStatus);
430 :
431 : //
432 : // Forward the call through.
433 : //
434 115 : mCallback.OnAttributeData(aPath, apData ? &dataSnapshot : nullptr, aStatus);
435 115 : }
436 :
437 : template <bool CanEnableDataCaching>
438 28 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetVersion(const ConcreteClusterPath & aPath,
439 : Optional<DataVersion> & aVersion) const
440 : {
441 28 : VerifyOrReturnError(aPath.IsValidConcreteClusterPath(), CHIP_ERROR_INVALID_ARGUMENT);
442 : CHIP_ERROR err;
443 28 : auto clusterState = GetClusterState(aPath.mEndpointId, aPath.mClusterId, err);
444 28 : ReturnErrorOnFailure(err);
445 28 : aVersion = clusterState->mCommittedDataVersion;
446 28 : return CHIP_NO_ERROR;
447 : }
448 :
449 : template <bool CanEnableDataCaching>
450 32 : void ClusterStateCacheT<CanEnableDataCaching>::OnEventData(const EventHeader & aEventHeader, TLV::TLVReader * apData,
451 : const StatusIB * apStatus)
452 : {
453 32 : VerifyOrDie(apData != nullptr || apStatus != nullptr);
454 :
455 32 : TLV::TLVReader dataSnapshot;
456 32 : if (apData)
457 : {
458 32 : dataSnapshot.Init(*apData);
459 : }
460 :
461 32 : UpdateEventCache(aEventHeader, apData, apStatus);
462 32 : mCallback.OnEventData(aEventHeader, apData ? &dataSnapshot : nullptr, apStatus);
463 32 : }
464 :
465 : template <>
466 1 : CHIP_ERROR ClusterStateCacheT<true>::GetStatus(const ConcreteAttributePath & path, StatusIB & status) const
467 : {
468 : CHIP_ERROR err;
469 :
470 1 : auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err);
471 1 : ReturnErrorOnFailure(err);
472 :
473 1 : if (!attributeState->template Is<StatusIB>())
474 : {
475 0 : return CHIP_ERROR_INVALID_ARGUMENT;
476 : }
477 :
478 1 : status = attributeState->template Get<StatusIB>();
479 1 : return CHIP_NO_ERROR;
480 : }
481 :
482 : template <>
483 0 : CHIP_ERROR ClusterStateCacheT<false>::GetStatus(const ConcreteAttributePath & path, StatusIB & status) const
484 : {
485 0 : return CHIP_ERROR_INVALID_ARGUMENT;
486 : }
487 :
488 : template <bool CanEnableDataCaching>
489 0 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetStatus(const ConcreteEventPath & path, StatusIB & status) const
490 : {
491 0 : auto statusIter = mEventStatusCache.find(path);
492 0 : if (statusIter == mEventStatusCache.end())
493 : {
494 0 : return CHIP_ERROR_KEY_NOT_FOUND;
495 : }
496 :
497 0 : status = statusIter->second;
498 0 : return CHIP_NO_ERROR;
499 : }
500 :
501 : template <bool CanEnableDataCaching>
502 545 : void ClusterStateCacheT<CanEnableDataCaching>::GetSortedFilters(std::vector<std::pair<DataVersionFilter, size_t>> & aVector) const
503 : {
504 1167 : for (auto const & endpointIter : mCache)
505 : {
506 622 : EndpointId endpointId = endpointIter.first;
507 1254 : for (auto const & clusterIter : endpointIter.second)
508 : {
509 632 : if (!clusterIter.second.mCommittedDataVersion.HasValue())
510 : {
511 65 : continue;
512 : }
513 567 : DataVersion dataVersion = clusterIter.second.mCommittedDataVersion.Value();
514 567 : size_t clusterSize = 0;
515 567 : ClusterId clusterId = clusterIter.first;
516 :
517 1263 : for (auto const & attributeIter : clusterIter.second.mAttributes)
518 : {
519 : if constexpr (CanEnableDataCaching)
520 : {
521 696 : if (attributeIter.second.template Is<StatusIB>())
522 : {
523 33 : clusterSize += SizeOfStatusIB(attributeIter.second.template Get<StatusIB>());
524 : }
525 663 : else if (attributeIter.second.template Is<uint32_t>())
526 : {
527 0 : clusterSize += attributeIter.second.template Get<uint32_t>();
528 : }
529 : else
530 : {
531 663 : VerifyOrDie(attributeIter.second.template Is<AttributeData>());
532 663 : TLV::TLVReader bufReader;
533 663 : bufReader.Init(attributeIter.second.template Get<AttributeData>().Get(),
534 663 : attributeIter.second.template Get<AttributeData>().AllocatedSize());
535 663 : ReturnOnFailure(bufReader.Next());
536 : // Skip to the end of the element.
537 663 : ReturnOnFailure(bufReader.Skip());
538 :
539 : // Compute the amount of value data
540 663 : clusterSize += bufReader.GetLengthRead();
541 : }
542 : }
543 : else
544 : {
545 0 : clusterSize += attributeIter.second;
546 : }
547 : }
548 :
549 567 : if (clusterSize == 0)
550 : {
551 : // No data in this cluster, so no point in sending a dataVersion
552 : // along at all.
553 0 : continue;
554 : }
555 :
556 567 : DataVersionFilter filter(endpointId, clusterId, dataVersion);
557 :
558 567 : aVector.push_back(std::make_pair(filter, clusterSize));
559 : }
560 : }
561 :
562 545 : std::sort(aVector.begin(), aVector.end(),
563 156 : [](const std::pair<DataVersionFilter, size_t> & x, const std::pair<DataVersionFilter, size_t> & y) {
564 156 : return x.second > y.second;
565 : });
566 0 : }
567 :
568 : template <bool CanEnableDataCaching>
569 545 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::OnUpdateDataVersionFilterList(
570 : DataVersionFilterIBs::Builder & aDataVersionFilterIBsBuilder, const Span<AttributePathParams> & aAttributePaths,
571 : bool & aEncodedDataVersionList)
572 : {
573 545 : CHIP_ERROR err = CHIP_NO_ERROR;
574 545 : TLV::TLVWriter backup;
575 :
576 : // Only put paths into mRequestPathSet if they cover clusters in their entirety and no other path in our path list
577 : // points to a specific attribute from any of those clusters.
578 : // this would help for data-out-of-sync issue when handling store data version for the particular case on two paths: (E1, C1,
579 : // wildcard), (wildcard, C1, A1)
580 1111 : for (auto & attribute1 : aAttributePaths)
581 : {
582 566 : if (attribute1.HasWildcardAttributeId())
583 : {
584 544 : bool intersected = false;
585 1104 : for (auto & attribute2 : aAttributePaths)
586 : {
587 561 : if (attribute2.HasWildcardAttributeId())
588 : {
589 555 : continue;
590 : }
591 :
592 6 : if (attribute1.Intersects(attribute2))
593 : {
594 1 : intersected = true;
595 1 : break;
596 : }
597 : }
598 :
599 544 : if (!intersected)
600 : {
601 543 : mRequestPathSet.insert(attribute1);
602 : }
603 : }
604 : }
605 :
606 545 : std::vector<std::pair<DataVersionFilter, size_t>> filterVector;
607 545 : GetSortedFilters(filterVector);
608 :
609 545 : aEncodedDataVersionList = false;
610 939 : for (auto & filter : filterVector)
611 : {
612 549 : bool intersected = false;
613 549 : aDataVersionFilterIBsBuilder.Checkpoint(backup);
614 :
615 : // if the particular cached cluster does not intersect with user provided attribute paths, skip the cached one
616 556 : for (const auto & attributePath : aAttributePaths)
617 : {
618 553 : if (attributePath.IncludesAttributesInCluster(filter.first))
619 : {
620 546 : intersected = true;
621 546 : break;
622 : }
623 : }
624 549 : if (!intersected)
625 : {
626 3 : continue;
627 : }
628 :
629 546 : SuccessOrExit(err = aDataVersionFilterIBsBuilder.EncodeDataVersionFilterIB(filter.first));
630 391 : aEncodedDataVersionList = true;
631 : }
632 :
633 545 : exit:
634 545 : if (err == CHIP_ERROR_NO_MEMORY || err == CHIP_ERROR_BUFFER_TOO_SMALL)
635 : {
636 155 : ChipLogProgress(DataManagement, "OnUpdateDataVersionFilterList out of space; rolling back");
637 155 : aDataVersionFilterIBsBuilder.Rollback(backup);
638 155 : err = CHIP_NO_ERROR;
639 : }
640 545 : return err;
641 545 : }
642 :
643 : template <bool CanEnableDataCaching>
644 9 : void ClusterStateCacheT<CanEnableDataCaching>::ClearAttributes(EndpointId endpointId)
645 : {
646 9 : mCache.erase(endpointId);
647 9 : }
648 :
649 : template <bool CanEnableDataCaching>
650 9 : void ClusterStateCacheT<CanEnableDataCaching>::ClearAttributes(const ConcreteClusterPath & cluster)
651 : {
652 : // Can't use GetEndpointState here, since that only handles const things.
653 9 : auto endpointIter = mCache.find(cluster.mEndpointId);
654 9 : if (endpointIter == mCache.end())
655 : {
656 0 : return;
657 : }
658 :
659 9 : auto & endpointState = endpointIter->second;
660 9 : endpointState.erase(cluster.mClusterId);
661 : }
662 :
663 : template <bool CanEnableDataCaching>
664 9 : void ClusterStateCacheT<CanEnableDataCaching>::ClearAttribute(const ConcreteAttributePath & attribute)
665 : {
666 : // Can't use GetClusterState here, since that only handles const things.
667 9 : auto endpointIter = mCache.find(attribute.mEndpointId);
668 9 : if (endpointIter == mCache.end())
669 : {
670 0 : return;
671 : }
672 :
673 9 : auto & endpointState = endpointIter->second;
674 9 : auto clusterIter = endpointState.find(attribute.mClusterId);
675 9 : if (clusterIter == endpointState.end())
676 : {
677 0 : return;
678 : }
679 :
680 9 : auto & clusterState = clusterIter->second;
681 9 : clusterState.mAttributes.erase(attribute.mAttributeId);
682 : }
683 :
684 : template <bool CanEnableDataCaching>
685 0 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetLastReportDataPath(ConcreteClusterPath & aPath)
686 : {
687 0 : if (mLastReportDataPath.IsValidConcreteClusterPath())
688 : {
689 0 : aPath = mLastReportDataPath;
690 0 : return CHIP_NO_ERROR;
691 : }
692 0 : return CHIP_ERROR_INCORRECT_STATE;
693 : }
694 :
695 : // Ensure that our out-of-line template methods actually get compiled.
696 : template class ClusterStateCacheT<true>;
697 : template class ClusterStateCacheT<false>;
698 :
699 : } // namespace app
700 : } // namespace chip
|