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 0 : 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 0 : uint32_t size = 5;
38 :
39 0 : if (aStatus.mClusterStatus.has_value())
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 0 : return size;
48 : }
49 :
50 : } // anonymous namespace
51 :
52 : template <bool CanEnableDataCaching>
53 157 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetElementTLVSize(TLV::TLVReader * apData, uint32_t & aSize)
54 : {
55 157 : Platform::ScopedMemoryBufferWithSize<uint8_t> backingBuffer;
56 157 : TLV::TLVReader reader;
57 157 : reader.Init(*apData);
58 157 : size_t totalBufSize = reader.GetTotalLength();
59 157 : backingBuffer.Calloc(totalBufSize);
60 157 : VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
61 157 : TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), totalBufSize);
62 157 : ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), reader));
63 157 : aSize = writer.GetLengthWritten();
64 157 : ReturnErrorOnFailure(writer.Finalize(backingBuffer));
65 157 : return CHIP_NO_ERROR;
66 157 : }
67 :
68 : template <bool CanEnableDataCaching>
69 159 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::UpdateCache(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
70 : const StatusIB & aStatus)
71 : {
72 159 : AttributeState state;
73 159 : bool endpointIsNew = false;
74 :
75 159 : 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 33 : endpointIsNew = true;
83 : }
84 :
85 159 : if (apData)
86 : {
87 157 : uint32_t elementSize = 0;
88 157 : ReturnErrorOnFailure(GetElementTLVSize(apData, elementSize));
89 :
90 : if constexpr (CanEnableDataCaching)
91 : {
92 157 : if (mCacheData)
93 : {
94 148 : Platform::ScopedMemoryBufferWithSize<uint8_t> backingBuffer;
95 148 : backingBuffer.Calloc(elementSize);
96 148 : VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
97 148 : TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), elementSize);
98 148 : ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), *apData));
99 148 : ReturnErrorOnFailure(writer.Finalize(backingBuffer));
100 :
101 148 : state.template Set<AttributeData>(std::move(backingBuffer));
102 148 : }
103 : else
104 : {
105 9 : 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 157 : 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 157 : if (mLastReportDataPath.IsValidConcreteClusterPath() && mLastReportDataPath != aPath)
121 : {
122 19 : CommitPendingDataVersion();
123 : }
124 :
125 157 : bool foundEncompassingWildcardPath = false;
126 194 : for (const auto & path : mRequestPathSet)
127 : {
128 124 : if (path.IncludesAllAttributesInCluster(aPath))
129 : {
130 87 : foundEncompassingWildcardPath = true;
131 87 : 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 157 : if (foundEncompassingWildcardPath)
137 : {
138 87 : mCache[aPath.mEndpointId][aPath.mClusterId].mPendingDataVersion = aPath.mDataVersion;
139 : }
140 :
141 157 : mLastReportDataPath = aPath;
142 : }
143 : else
144 : {
145 : if constexpr (CanEnableDataCaching)
146 : {
147 2 : if (mCacheData)
148 : {
149 2 : state.template Set<StatusIB>(aStatus);
150 : }
151 : else
152 : {
153 0 : 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 159 : if (endpointIsNew)
167 : {
168 33 : mAddedEndpoints.push_back(aPath.mEndpointId);
169 : }
170 :
171 159 : mCache[aPath.mEndpointId][aPath.mClusterId].mAttributes[aPath.mAttributeId] = std::move(state);
172 :
173 159 : if (mCacheData)
174 : {
175 150 : mChangedAttributeSet.insert(aPath);
176 : }
177 :
178 159 : return CHIP_NO_ERROR;
179 159 : }
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 6 : void ClusterStateCacheT<CanEnableDataCaching>::NotifySubscriptionStillActive(const ReadClient & aReadClient)
232 : {
233 6 : mCallback.NotifySubscriptionStillActive(aReadClient);
234 6 : }
235 :
236 : template <bool CanEnableDataCaching>
237 35 : void ClusterStateCacheT<CanEnableDataCaching>::OnReportBegin()
238 : {
239 35 : mLastReportDataPath = ConcreteClusterPath(kInvalidEndpointId, kInvalidClusterId);
240 35 : mChangedAttributeSet.clear();
241 35 : mAddedEndpoints.clear();
242 35 : mCallback.OnReportBegin();
243 35 : }
244 :
245 : template <bool CanEnableDataCaching>
246 54 : void ClusterStateCacheT<CanEnableDataCaching>::CommitPendingDataVersion()
247 : {
248 54 : if (!mLastReportDataPath.IsValidConcreteClusterPath())
249 : {
250 7 : return;
251 : }
252 :
253 47 : auto & lastClusterInfo = mCache[mLastReportDataPath.mEndpointId][mLastReportDataPath.mClusterId];
254 47 : if (lastClusterInfo.mPendingDataVersion.HasValue())
255 : {
256 19 : lastClusterInfo.mCommittedDataVersion = lastClusterInfo.mPendingDataVersion;
257 19 : lastClusterInfo.mPendingDataVersion.ClearValue();
258 : }
259 : }
260 :
261 : template <bool CanEnableDataCaching>
262 35 : void ClusterStateCacheT<CanEnableDataCaching>::OnReportEnd()
263 : {
264 35 : CommitPendingDataVersion();
265 35 : mLastReportDataPath = ConcreteClusterPath(kInvalidEndpointId, kInvalidClusterId);
266 35 : std::set<std::tuple<EndpointId, ClusterId>> changedClusters;
267 :
268 : //
269 : // Add the EndpointId and ClusterId into a set so that we only
270 : // convey unique combinations in the subsequent OnClusterChanged callback.
271 : //
272 143 : for (auto & path : mChangedAttributeSet)
273 : {
274 108 : mCallback.OnAttributeChanged(this, path);
275 108 : changedClusters.insert(std::make_tuple(path.mEndpointId, path.mClusterId));
276 : }
277 :
278 74 : for (auto & item : changedClusters)
279 : {
280 39 : mCallback.OnClusterChanged(this, std::get<0>(item), std::get<1>(item));
281 : }
282 :
283 52 : for (auto endpoint : mAddedEndpoints)
284 : {
285 17 : mCallback.OnEndpointAdded(this, endpoint);
286 : }
287 :
288 35 : mCallback.OnReportEnd();
289 35 : }
290 :
291 : template <>
292 156 : CHIP_ERROR ClusterStateCacheT<true>::Get(const ConcreteAttributePath & path, TLV::TLVReader & reader) const
293 : {
294 : CHIP_ERROR err;
295 156 : auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err);
296 156 : ReturnErrorOnFailure(err);
297 :
298 116 : if (attributeState->template Is<StatusIB>())
299 : {
300 3 : return CHIP_ERROR_IM_STATUS_CODE_RECEIVED;
301 : }
302 :
303 113 : if (!attributeState->template Is<AttributeData>())
304 : {
305 3 : return CHIP_ERROR_KEY_NOT_FOUND;
306 : }
307 :
308 110 : reader.Init(attributeState->template Get<AttributeData>().Get(), attributeState->template Get<AttributeData>().AllocatedSize());
309 110 : return reader.Next();
310 : }
311 :
312 : template <>
313 0 : CHIP_ERROR ClusterStateCacheT<false>::Get(const ConcreteAttributePath & path, TLV::TLVReader & reader) const
314 : {
315 0 : return CHIP_ERROR_KEY_NOT_FOUND;
316 : }
317 :
318 : template <bool CanEnableDataCaching>
319 44 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::Get(EventNumber eventNumber, TLV::TLVReader & reader) const
320 : {
321 : CHIP_ERROR err;
322 :
323 44 : auto eventData = GetEventData(eventNumber, err);
324 44 : ReturnErrorOnFailure(err);
325 :
326 44 : System::PacketBufferTLVReader bufReader;
327 :
328 44 : bufReader.Init(eventData->second.Retain());
329 44 : ReturnErrorOnFailure(bufReader.Next());
330 :
331 44 : reader.Init(bufReader);
332 44 : return CHIP_NO_ERROR;
333 44 : }
334 :
335 : template <bool CanEnableDataCaching>
336 : const typename ClusterStateCacheT<CanEnableDataCaching>::EndpointState *
337 205 : ClusterStateCacheT<CanEnableDataCaching>::GetEndpointState(EndpointId endpointId, CHIP_ERROR & err) const
338 : {
339 205 : auto endpointIter = mCache.find(endpointId);
340 205 : if (endpointIter == mCache.end())
341 : {
342 16 : err = CHIP_ERROR_KEY_NOT_FOUND;
343 16 : return nullptr;
344 : }
345 :
346 189 : err = CHIP_NO_ERROR;
347 189 : return &endpointIter->second;
348 : }
349 :
350 : template <bool CanEnableDataCaching>
351 : const typename ClusterStateCacheT<CanEnableDataCaching>::ClusterState *
352 205 : ClusterStateCacheT<CanEnableDataCaching>::GetClusterState(EndpointId endpointId, ClusterId clusterId, CHIP_ERROR & err) const
353 : {
354 205 : auto endpointState = GetEndpointState(endpointId, err);
355 410 : if (err != CHIP_NO_ERROR)
356 : {
357 16 : return nullptr;
358 : }
359 :
360 189 : auto clusterState = endpointState->find(clusterId);
361 189 : if (clusterState == endpointState->end())
362 : {
363 15 : err = CHIP_ERROR_KEY_NOT_FOUND;
364 15 : return nullptr;
365 : }
366 :
367 174 : err = CHIP_NO_ERROR;
368 174 : return &clusterState->second;
369 : }
370 :
371 : template <bool CanEnableDataCaching>
372 : const typename ClusterStateCacheT<CanEnableDataCaching>::AttributeState *
373 157 : ClusterStateCacheT<CanEnableDataCaching>::GetAttributeState(EndpointId endpointId, ClusterId clusterId, AttributeId attributeId,
374 : CHIP_ERROR & err) const
375 : {
376 157 : auto clusterState = GetClusterState(endpointId, clusterId, err);
377 314 : if (err != CHIP_NO_ERROR)
378 : {
379 31 : return nullptr;
380 : }
381 :
382 126 : auto attributeState = clusterState->mAttributes.find(attributeId);
383 126 : if (attributeState == clusterState->mAttributes.end())
384 : {
385 9 : err = CHIP_ERROR_KEY_NOT_FOUND;
386 9 : return nullptr;
387 : }
388 :
389 117 : err = CHIP_NO_ERROR;
390 117 : return &attributeState->second;
391 : }
392 :
393 : template <bool CanEnableDataCaching>
394 : const typename ClusterStateCacheT<CanEnableDataCaching>::EventData *
395 88 : ClusterStateCacheT<CanEnableDataCaching>::GetEventData(EventNumber eventNumber, CHIP_ERROR & err) const
396 : {
397 88 : EventData compareKey;
398 :
399 88 : compareKey.first.mEventNumber = eventNumber;
400 88 : auto eventData = mEventDataCache.find(std::move(compareKey));
401 88 : if (eventData == mEventDataCache.end())
402 : {
403 0 : err = CHIP_ERROR_KEY_NOT_FOUND;
404 0 : return nullptr;
405 : }
406 :
407 88 : err = CHIP_NO_ERROR;
408 88 : return &(*eventData);
409 88 : }
410 :
411 : template <bool CanEnableDataCaching>
412 159 : void ClusterStateCacheT<CanEnableDataCaching>::OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
413 : const StatusIB & aStatus)
414 : {
415 : //
416 : // Since the cache itself is a ReadClient::Callback, it may be incorrectly passed in directly when registering with the
417 : // ReadClient. This should be avoided, since that bypasses the built-in buffered reader adapter callback that is needed for
418 : // lists to work correctly.
419 : //
420 : // Instead, the right callback should be retrieved using GetBufferedCallback().
421 : //
422 : // To catch such errors, we validate that the provided concrete path never indicates a raw list item operation (which the
423 : // buffered reader will handle and convert for us).
424 : //
425 : //
426 159 : VerifyOrDie(!aPath.IsListItemOperation());
427 :
428 : // Copy the reader for forwarding
429 159 : TLV::TLVReader dataSnapshot;
430 159 : if (apData)
431 : {
432 157 : dataSnapshot.Init(*apData);
433 : }
434 :
435 159 : TEMPORARY_RETURN_IGNORED UpdateCache(aPath, apData, aStatus);
436 :
437 : //
438 : // Forward the call through.
439 : //
440 159 : mCallback.OnAttributeData(aPath, apData ? &dataSnapshot : nullptr, aStatus);
441 159 : }
442 :
443 : template <bool CanEnableDataCaching>
444 28 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetVersion(const ConcreteClusterPath & aPath,
445 : Optional<DataVersion> & aVersion) const
446 : {
447 28 : VerifyOrReturnError(aPath.IsValidConcreteClusterPath(), CHIP_ERROR_INVALID_ARGUMENT);
448 : CHIP_ERROR err;
449 28 : auto clusterState = GetClusterState(aPath.mEndpointId, aPath.mClusterId, err);
450 28 : ReturnErrorOnFailure(err);
451 28 : aVersion = clusterState->mCommittedDataVersion;
452 28 : return CHIP_NO_ERROR;
453 : }
454 :
455 : template <bool CanEnableDataCaching>
456 32 : void ClusterStateCacheT<CanEnableDataCaching>::OnEventData(const EventHeader & aEventHeader, TLV::TLVReader * apData,
457 : const StatusIB * apStatus)
458 : {
459 32 : VerifyOrDie(apData != nullptr || apStatus != nullptr);
460 :
461 32 : TLV::TLVReader dataSnapshot;
462 32 : if (apData)
463 : {
464 32 : dataSnapshot.Init(*apData);
465 : }
466 :
467 32 : TEMPORARY_RETURN_IGNORED UpdateEventCache(aEventHeader, apData, apStatus);
468 32 : mCallback.OnEventData(aEventHeader, apData ? &dataSnapshot : nullptr, apStatus);
469 32 : }
470 :
471 : template <>
472 1 : CHIP_ERROR ClusterStateCacheT<true>::GetStatus(const ConcreteAttributePath & path, StatusIB & status) const
473 : {
474 : CHIP_ERROR err;
475 :
476 1 : auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err);
477 1 : ReturnErrorOnFailure(err);
478 :
479 1 : if (!attributeState->template Is<StatusIB>())
480 : {
481 0 : return CHIP_ERROR_INVALID_ARGUMENT;
482 : }
483 :
484 1 : status = attributeState->template Get<StatusIB>();
485 1 : return CHIP_NO_ERROR;
486 : }
487 :
488 : template <>
489 0 : CHIP_ERROR ClusterStateCacheT<false>::GetStatus(const ConcreteAttributePath & path, StatusIB & status) const
490 : {
491 0 : return CHIP_ERROR_INVALID_ARGUMENT;
492 : }
493 :
494 : template <bool CanEnableDataCaching>
495 0 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetStatus(const ConcreteEventPath & path, StatusIB & status) const
496 : {
497 0 : auto statusIter = mEventStatusCache.find(path);
498 0 : if (statusIter == mEventStatusCache.end())
499 : {
500 0 : return CHIP_ERROR_KEY_NOT_FOUND;
501 : }
502 :
503 0 : status = statusIter->second;
504 0 : return CHIP_NO_ERROR;
505 : }
506 :
507 : template <bool CanEnableDataCaching>
508 547 : void ClusterStateCacheT<CanEnableDataCaching>::GetSortedFilters(std::vector<std::pair<DataVersionFilter, size_t>> & aVector) const
509 : {
510 1169 : for (auto const & endpointIter : mCache)
511 : {
512 622 : EndpointId endpointId = endpointIter.first;
513 1254 : for (auto const & clusterIter : endpointIter.second)
514 : {
515 632 : if (!clusterIter.second.mCommittedDataVersion.HasValue())
516 : {
517 65 : continue;
518 : }
519 567 : DataVersion dataVersion = clusterIter.second.mCommittedDataVersion.Value();
520 567 : size_t clusterSize = 0;
521 567 : ClusterId clusterId = clusterIter.first;
522 :
523 1263 : for (auto const & attributeIter : clusterIter.second.mAttributes)
524 : {
525 : if constexpr (CanEnableDataCaching)
526 : {
527 696 : if (attributeIter.second.template Is<StatusIB>())
528 : {
529 0 : clusterSize += SizeOfStatusIB(attributeIter.second.template Get<StatusIB>());
530 : }
531 696 : else if (attributeIter.second.template Is<uint32_t>())
532 : {
533 0 : clusterSize += attributeIter.second.template Get<uint32_t>();
534 : }
535 : else
536 : {
537 696 : VerifyOrDie(attributeIter.second.template Is<AttributeData>());
538 696 : TLV::TLVReader bufReader;
539 696 : bufReader.Init(attributeIter.second.template Get<AttributeData>().Get(),
540 696 : attributeIter.second.template Get<AttributeData>().AllocatedSize());
541 696 : ReturnOnFailure(bufReader.Next());
542 : // Skip to the end of the element.
543 696 : ReturnOnFailure(bufReader.Skip());
544 :
545 : // Compute the amount of value data
546 696 : clusterSize += bufReader.GetLengthRead();
547 : }
548 : }
549 : else
550 : {
551 0 : clusterSize += attributeIter.second;
552 : }
553 : }
554 :
555 567 : if (clusterSize == 0)
556 : {
557 : // No data in this cluster, so no point in sending a dataVersion
558 : // along at all.
559 0 : continue;
560 : }
561 :
562 567 : DataVersionFilter filter(endpointId, clusterId, dataVersion);
563 :
564 567 : aVector.push_back(std::make_pair(filter, clusterSize));
565 : }
566 : }
567 :
568 547 : std::sort(aVector.begin(), aVector.end(),
569 156 : [](const std::pair<DataVersionFilter, size_t> & x, const std::pair<DataVersionFilter, size_t> & y) {
570 156 : return x.second > y.second;
571 : });
572 0 : }
573 :
574 : template <bool CanEnableDataCaching>
575 547 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::OnUpdateDataVersionFilterList(
576 : DataVersionFilterIBs::Builder & aDataVersionFilterIBsBuilder, const Span<AttributePathParams> & aAttributePaths,
577 : bool & aEncodedDataVersionList)
578 : {
579 547 : CHIP_ERROR err = CHIP_NO_ERROR;
580 547 : TLV::TLVWriter backup;
581 :
582 : // Only put paths into mRequestPathSet if they cover clusters in their entirety and no other path in our path list
583 : // points to a specific attribute from any of those clusters.
584 : // this would help for data-out-of-sync issue when handling store data version for the particular case on two paths: (E1, C1,
585 : // wildcard), (wildcard, C1, A1)
586 1115 : for (auto & attribute1 : aAttributePaths)
587 : {
588 568 : if (attribute1.HasWildcardAttributeId())
589 : {
590 544 : bool intersected = false;
591 1104 : for (auto & attribute2 : aAttributePaths)
592 : {
593 561 : if (attribute2.HasWildcardAttributeId())
594 : {
595 555 : continue;
596 : }
597 :
598 6 : if (attribute1.Intersects(attribute2))
599 : {
600 1 : intersected = true;
601 1 : break;
602 : }
603 : }
604 :
605 544 : if (!intersected)
606 : {
607 543 : mRequestPathSet.insert(attribute1);
608 : }
609 : }
610 : }
611 :
612 547 : std::vector<std::pair<DataVersionFilter, size_t>> filterVector;
613 547 : GetSortedFilters(filterVector);
614 :
615 547 : aEncodedDataVersionList = false;
616 941 : for (auto & filter : filterVector)
617 : {
618 549 : bool intersected = false;
619 549 : aDataVersionFilterIBsBuilder.Checkpoint(backup);
620 :
621 : // if the particular cached cluster does not intersect with user provided attribute paths, skip the cached one
622 556 : for (const auto & attributePath : aAttributePaths)
623 : {
624 553 : if (attributePath.IncludesAttributesInCluster(filter.first))
625 : {
626 546 : intersected = true;
627 546 : break;
628 : }
629 : }
630 549 : if (!intersected)
631 : {
632 3 : continue;
633 : }
634 :
635 546 : SuccessOrExit(err = aDataVersionFilterIBsBuilder.EncodeDataVersionFilterIB(filter.first));
636 391 : aEncodedDataVersionList = true;
637 : }
638 :
639 547 : exit:
640 1639 : if (err == CHIP_ERROR_NO_MEMORY || err == CHIP_ERROR_BUFFER_TOO_SMALL)
641 : {
642 155 : ChipLogProgress(DataManagement, "OnUpdateDataVersionFilterList out of space; rolling back");
643 155 : aDataVersionFilterIBsBuilder.Rollback(backup);
644 155 : err = CHIP_NO_ERROR;
645 : }
646 1094 : return err;
647 547 : }
648 :
649 : template <bool CanEnableDataCaching>
650 9 : void ClusterStateCacheT<CanEnableDataCaching>::ClearAttributes(EndpointId endpointId)
651 : {
652 9 : mCache.erase(endpointId);
653 9 : }
654 :
655 : template <bool CanEnableDataCaching>
656 9 : void ClusterStateCacheT<CanEnableDataCaching>::ClearAttributes(const ConcreteClusterPath & cluster)
657 : {
658 : // Can't use GetEndpointState here, since that only handles const things.
659 9 : auto endpointIter = mCache.find(cluster.mEndpointId);
660 9 : if (endpointIter == mCache.end())
661 : {
662 0 : return;
663 : }
664 :
665 9 : auto & endpointState = endpointIter->second;
666 9 : endpointState.erase(cluster.mClusterId);
667 : }
668 :
669 : template <bool CanEnableDataCaching>
670 9 : void ClusterStateCacheT<CanEnableDataCaching>::ClearAttribute(const ConcreteAttributePath & attribute)
671 : {
672 : // Can't use GetClusterState here, since that only handles const things.
673 9 : auto endpointIter = mCache.find(attribute.mEndpointId);
674 9 : if (endpointIter == mCache.end())
675 : {
676 0 : return;
677 : }
678 :
679 9 : auto & endpointState = endpointIter->second;
680 9 : auto clusterIter = endpointState.find(attribute.mClusterId);
681 9 : if (clusterIter == endpointState.end())
682 : {
683 0 : return;
684 : }
685 :
686 9 : auto & clusterState = clusterIter->second;
687 9 : clusterState.mAttributes.erase(attribute.mAttributeId);
688 : }
689 :
690 : template <bool CanEnableDataCaching>
691 0 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetLastReportDataPath(ConcreteClusterPath & aPath)
692 : {
693 0 : if (mLastReportDataPath.IsValidConcreteClusterPath())
694 : {
695 0 : aPath = mLastReportDataPath;
696 0 : return CHIP_NO_ERROR;
697 : }
698 0 : return CHIP_ERROR_INCORRECT_STATE;
699 : }
700 :
701 : // Ensure that our out-of-line template methods actually get compiled.
702 : template class ClusterStateCacheT<true>;
703 : template class ClusterStateCacheT<false>;
704 :
705 : } // namespace app
706 : } // namespace chip
|