blob: fdb8e40abac0b70e566bf552074d0109cdbbe864 [file] [log] [blame]
Jerry Johnsc8027a52021-12-13 17:20:15 -08001/*
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"
Jerry Johns964adbc2022-04-14 16:42:04 -070020#include <app/ClusterStateCache.h>
Jerry Johnsc8027a52021-12-13 17:20:15 -080021#include <app/InteractionModelEngine.h>
22#include <tuple>
23
24namespace chip {
25namespace app {
26
Boris Zbarsky343634a2023-04-24 13:37:18 -040027namespace {
28
29// Determine how much space a StatusIB takes up on the wire.
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -040030uint32_t SizeOfStatusIB(const StatusIB & aStatus)
Boris Zbarsky343634a2023-04-24 13:37:18 -040031{
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.
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -040037 uint32_t size = 5;
Boris Zbarsky343634a2023-04-24 13:37:18 -040038
39 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 size += 3;
45 }
46
47 return size;
48}
49
50} // anonymous namespace
51
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -040052template <bool CanEnableDataCaching>
53CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetElementTLVSize(TLV::TLVReader * apData, uint32_t & aSize)
yunhanw-google3ef81a82022-05-19 10:46:23 -070054{
55 Platform::ScopedMemoryBufferWithSize<uint8_t> backingBuffer;
56 TLV::TLVReader reader;
57 reader.Init(*apData);
58 size_t totalBufSize = reader.GetTotalLength();
59 backingBuffer.Calloc(totalBufSize);
60 VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
61 TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), totalBufSize);
62 ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), reader));
63 aSize = writer.GetLengthWritten();
64 ReturnErrorOnFailure(writer.Finalize(backingBuffer));
65 return CHIP_NO_ERROR;
66}
67
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -040068template <bool CanEnableDataCaching>
69CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::UpdateCache(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
70 const StatusIB & aStatus)
Jerry Johnsc8027a52021-12-13 17:20:15 -080071{
72 AttributeState state;
yunhanw-google57a2df32022-04-14 11:04:47 -070073 bool endpointIsNew = false;
74
75 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 endpointIsNew = true;
83 }
Jerry Johnsc8027a52021-12-13 17:20:15 -080084
85 if (apData)
86 {
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -040087 uint32_t elementSize = 0;
Boris Zbarsky343634a2023-04-24 13:37:18 -040088 ReturnErrorOnFailure(GetElementTLVSize(apData, elementSize));
89
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -040090 if constexpr (CanEnableDataCaching)
yunhanw-google0e896b42023-01-30 08:31:00 -080091 {
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -040092 if (mCacheData)
93 {
94 Platform::ScopedMemoryBufferWithSize<uint8_t> backingBuffer;
95 backingBuffer.Calloc(elementSize);
96 VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
97 TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), elementSize);
98 ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), *apData));
99 ReturnErrorOnFailure(writer.Finalize(backingBuffer));
Jerry Johnsc8027a52021-12-13 17:20:15 -0800100
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400101 state.template Set<AttributeData>(std::move(backingBuffer));
102 }
103 else
104 {
105 state.template Set<uint32_t>(elementSize);
106 }
Boris Zbarsky343634a2023-04-24 13:37:18 -0400107 }
108 else
109 {
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400110 state = elementSize;
yunhanw-google0e896b42023-01-30 08:31:00 -0800111 }
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400112
yunhanw-google57a2df32022-04-14 11:04:47 -0700113 //
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 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 if (mLastReportDataPath.IsValidConcreteClusterPath() && mLastReportDataPath != aPath)
121 {
122 CommitPendingDataVersion();
123 }
124
125 bool foundEncompassingWildcardPath = false;
126 for (const auto & path : mRequestPathSet)
127 {
128 if (path.IncludesAllAttributesInCluster(aPath))
129 {
130 foundEncompassingWildcardPath = true;
131 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 if (foundEncompassingWildcardPath)
137 {
138 mCache[aPath.mEndpointId][aPath.mClusterId].mPendingDataVersion = aPath.mDataVersion;
139 }
Jerry Johns964adbc2022-04-14 16:42:04 -0700140
yunhanw-google57a2df32022-04-14 11:04:47 -0700141 mLastReportDataPath = aPath;
Jerry Johnsc8027a52021-12-13 17:20:15 -0800142 }
143 else
144 {
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400145 if constexpr (CanEnableDataCaching)
yunhanw-google0e896b42023-01-30 08:31:00 -0800146 {
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400147 if (mCacheData)
148 {
149 state.template Set<StatusIB>(aStatus);
150 }
151 else
152 {
153 state.template Set<uint32_t>(SizeOfStatusIB(aStatus));
154 }
yunhanw-google0e896b42023-01-30 08:31:00 -0800155 }
Boris Zbarsky343634a2023-04-24 13:37:18 -0400156 else
157 {
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400158 state = SizeOfStatusIB(aStatus);
Boris Zbarsky343634a2023-04-24 13:37:18 -0400159 }
Jerry Johnsc8027a52021-12-13 17:20:15 -0800160 }
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 //
yunhanw-google57a2df32022-04-14 11:04:47 -0700166 if (endpointIsNew)
Jerry Johnsc8027a52021-12-13 17:20:15 -0800167 {
168 mAddedEndpoints.push_back(aPath.mEndpointId);
169 }
170
Boris Zbarsky343634a2023-04-24 13:37:18 -0400171 mCache[aPath.mEndpointId][aPath.mClusterId].mAttributes[aPath.mAttributeId] = std::move(state);
172
yunhanw-google0e896b42023-01-30 08:31:00 -0800173 if (mCacheData)
174 {
yunhanw-google0e896b42023-01-30 08:31:00 -0800175 mChangedAttributeSet.insert(aPath);
176 }
177
Jerry Johnsc8027a52021-12-13 17:20:15 -0800178 return CHIP_NO_ERROR;
179}
180
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400181template <bool CanEnableDataCaching>
182CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::UpdateEventCache(const EventHeader & aEventHeader, TLV::TLVReader * apData,
183 const StatusIB * apStatus)
Jerry Johns964adbc2022-04-14 16:42:04 -0700184{
185 if (apData)
186 {
187 //
188 // If we've already seen this event before, there's no more work to be done.
189 //
190 if (mHighestReceivedEventNumber.HasValue() && aEventHeader.mEventNumber <= mHighestReceivedEventNumber.Value())
191 {
192 return CHIP_NO_ERROR;
193 }
yunhanw-google0e896b42023-01-30 08:31:00 -0800194 if (mCacheData)
195 {
196 System::PacketBufferHandle handle = System::PacketBufferHandle::New(chip::app::kMaxSecureSduLengthBytes);
197 VerifyOrReturnError(!handle.IsNull(), CHIP_ERROR_NO_MEMORY);
Jerry Johns964adbc2022-04-14 16:42:04 -0700198
yunhanw-google0e896b42023-01-30 08:31:00 -0800199 System::PacketBufferTLVWriter writer;
200 writer.Init(std::move(handle), false);
Jerry Johns964adbc2022-04-14 16:42:04 -0700201
yunhanw-google0e896b42023-01-30 08:31:00 -0800202 ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), *apData));
203 ReturnErrorOnFailure(writer.Finalize(&handle));
Jerry Johns964adbc2022-04-14 16:42:04 -0700204
yunhanw-google0e896b42023-01-30 08:31:00 -0800205 //
206 // Compact the buffer down to a more reasonably sized packet buffer
207 // if we can.
208 //
209 handle.RightSize();
Jerry Johns964adbc2022-04-14 16:42:04 -0700210
yunhanw-google0e896b42023-01-30 08:31:00 -0800211 EventData eventData;
212 eventData.first = aEventHeader;
213 eventData.second = std::move(handle);
Jerry Johns964adbc2022-04-14 16:42:04 -0700214
yunhanw-google0e896b42023-01-30 08:31:00 -0800215 mEventDataCache.insert(std::move(eventData));
216 }
Jerry Johns964adbc2022-04-14 16:42:04 -0700217 mHighestReceivedEventNumber.SetValue(aEventHeader.mEventNumber);
218 }
219 else if (apStatus)
220 {
yunhanw-google0e896b42023-01-30 08:31:00 -0800221 if (mCacheData)
222 {
223 mEventStatusCache[aEventHeader.mPath] = *apStatus;
224 }
Jerry Johns964adbc2022-04-14 16:42:04 -0700225 }
226
227 return CHIP_NO_ERROR;
228}
229
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400230template <bool CanEnableDataCaching>
231void ClusterStateCacheT<CanEnableDataCaching>::OnReportBegin()
Jerry Johnsc8027a52021-12-13 17:20:15 -0800232{
yunhanw-google57a2df32022-04-14 11:04:47 -0700233 mLastReportDataPath = ConcreteClusterPath(kInvalidEndpointId, kInvalidClusterId);
Jerry Johnsc8027a52021-12-13 17:20:15 -0800234 mChangedAttributeSet.clear();
235 mAddedEndpoints.clear();
Michael Spanga2f17432022-01-26 20:52:14 -0500236 mCallback.OnReportBegin();
Jerry Johnsc8027a52021-12-13 17:20:15 -0800237}
238
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400239template <bool CanEnableDataCaching>
240void ClusterStateCacheT<CanEnableDataCaching>::CommitPendingDataVersion()
yunhanw-google57a2df32022-04-14 11:04:47 -0700241{
242 if (!mLastReportDataPath.IsValidConcreteClusterPath())
243 {
244 return;
245 }
246
247 auto & lastClusterInfo = mCache[mLastReportDataPath.mEndpointId][mLastReportDataPath.mClusterId];
248 if (lastClusterInfo.mPendingDataVersion.HasValue())
249 {
250 lastClusterInfo.mCommittedDataVersion = lastClusterInfo.mPendingDataVersion;
251 lastClusterInfo.mPendingDataVersion.ClearValue();
252 }
253}
254
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400255template <bool CanEnableDataCaching>
256void ClusterStateCacheT<CanEnableDataCaching>::OnReportEnd()
Jerry Johnsc8027a52021-12-13 17:20:15 -0800257{
yunhanw-google57a2df32022-04-14 11:04:47 -0700258 CommitPendingDataVersion();
259 mLastReportDataPath = ConcreteClusterPath(kInvalidEndpointId, kInvalidClusterId);
Jerry Johnsc8027a52021-12-13 17:20:15 -0800260 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 for (auto & path : mChangedAttributeSet)
267 {
268 mCallback.OnAttributeChanged(this, path);
269 changedClusters.insert(std::make_tuple(path.mEndpointId, path.mClusterId));
270 }
271
272 for (auto & item : changedClusters)
273 {
274 mCallback.OnClusterChanged(this, std::get<0>(item), std::get<1>(item));
275 }
276
277 for (auto endpoint : mAddedEndpoints)
278 {
279 mCallback.OnEndpointAdded(this, endpoint);
280 }
281
Michael Spanga2f17432022-01-26 20:52:14 -0500282 mCallback.OnReportEnd();
Jerry Johnsc8027a52021-12-13 17:20:15 -0800283}
284
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400285template <>
286CHIP_ERROR ClusterStateCacheT<true>::Get(const ConcreteAttributePath & path, TLV::TLVReader & reader) const
Jerry Johns964adbc2022-04-14 16:42:04 -0700287{
288 CHIP_ERROR err;
Jerry Johns964adbc2022-04-14 16:42:04 -0700289 auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err);
290 ReturnErrorOnFailure(err);
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400291
292 if (attributeState->template Is<StatusIB>())
Jerry Johns964adbc2022-04-14 16:42:04 -0700293 {
294 return CHIP_ERROR_IM_STATUS_CODE_RECEIVED;
295 }
296
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400297 if (!attributeState->template Is<AttributeData>())
Boris Zbarsky343634a2023-04-24 13:37:18 -0400298 {
299 return CHIP_ERROR_KEY_NOT_FOUND;
300 }
301
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400302 reader.Init(attributeState->template Get<AttributeData>().Get(), attributeState->template Get<AttributeData>().AllocatedSize());
yunhanw-google3ef81a82022-05-19 10:46:23 -0700303 return reader.Next();
Jerry Johns964adbc2022-04-14 16:42:04 -0700304}
305
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400306template <>
307CHIP_ERROR ClusterStateCacheT<false>::Get(const ConcreteAttributePath & path, TLV::TLVReader & reader) const
308{
309 return CHIP_ERROR_KEY_NOT_FOUND;
310}
311
312template <bool CanEnableDataCaching>
313CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::Get(EventNumber eventNumber, TLV::TLVReader & reader) const
Jerry Johns964adbc2022-04-14 16:42:04 -0700314{
315 CHIP_ERROR err;
316
317 auto eventData = GetEventData(eventNumber, err);
318 ReturnErrorOnFailure(err);
319
320 System::PacketBufferTLVReader bufReader;
321
322 bufReader.Init(eventData->second.Retain());
323 ReturnErrorOnFailure(bufReader.Next());
324
325 reader.Init(bufReader);
326 return CHIP_NO_ERROR;
327}
328
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400329template <bool CanEnableDataCaching>
330const typename ClusterStateCacheT<CanEnableDataCaching>::EndpointState *
331ClusterStateCacheT<CanEnableDataCaching>::GetEndpointState(EndpointId endpointId, CHIP_ERROR & err) const
Jerry Johns964adbc2022-04-14 16:42:04 -0700332{
333 auto endpointIter = mCache.find(endpointId);
334 if (endpointIter == mCache.end())
335 {
336 err = CHIP_ERROR_KEY_NOT_FOUND;
337 return nullptr;
338 }
339
340 err = CHIP_NO_ERROR;
341 return &endpointIter->second;
342}
343
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400344template <bool CanEnableDataCaching>
345const typename ClusterStateCacheT<CanEnableDataCaching>::ClusterState *
346ClusterStateCacheT<CanEnableDataCaching>::GetClusterState(EndpointId endpointId, ClusterId clusterId, CHIP_ERROR & err) const
Jerry Johns964adbc2022-04-14 16:42:04 -0700347{
348 auto endpointState = GetEndpointState(endpointId, err);
349 if (err != CHIP_NO_ERROR)
350 {
351 return nullptr;
352 }
353
354 auto clusterState = endpointState->find(clusterId);
355 if (clusterState == endpointState->end())
356 {
357 err = CHIP_ERROR_KEY_NOT_FOUND;
358 return nullptr;
359 }
360
361 err = CHIP_NO_ERROR;
362 return &clusterState->second;
363}
364
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400365template <bool CanEnableDataCaching>
366const typename ClusterStateCacheT<CanEnableDataCaching>::AttributeState *
367ClusterStateCacheT<CanEnableDataCaching>::GetAttributeState(EndpointId endpointId, ClusterId clusterId, AttributeId attributeId,
368 CHIP_ERROR & err) const
Jerry Johns964adbc2022-04-14 16:42:04 -0700369{
370 auto clusterState = GetClusterState(endpointId, clusterId, err);
371 if (err != CHIP_NO_ERROR)
372 {
373 return nullptr;
374 }
375
376 auto attributeState = clusterState->mAttributes.find(attributeId);
377 if (attributeState == clusterState->mAttributes.end())
378 {
379 err = CHIP_ERROR_KEY_NOT_FOUND;
380 return nullptr;
381 }
382
383 err = CHIP_NO_ERROR;
384 return &attributeState->second;
385}
386
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400387template <bool CanEnableDataCaching>
388const typename ClusterStateCacheT<CanEnableDataCaching>::EventData *
389ClusterStateCacheT<CanEnableDataCaching>::GetEventData(EventNumber eventNumber, CHIP_ERROR & err) const
Jerry Johns964adbc2022-04-14 16:42:04 -0700390{
391 EventData compareKey;
392
393 compareKey.first.mEventNumber = eventNumber;
394 auto eventData = mEventDataCache.find(std::move(compareKey));
395 if (eventData == mEventDataCache.end())
396 {
397 err = CHIP_ERROR_KEY_NOT_FOUND;
398 return nullptr;
399 }
400
401 err = CHIP_NO_ERROR;
402 return &(*eventData);
403}
404
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400405template <bool CanEnableDataCaching>
406void ClusterStateCacheT<CanEnableDataCaching>::OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
407 const StatusIB & aStatus)
Jerry Johnsc8027a52021-12-13 17:20:15 -0800408{
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 VerifyOrDie(!aPath.IsListItemOperation());
421
Kundok Park035911e2022-03-23 22:16:54 -0700422 // Copy the reader for forwarding
423 TLV::TLVReader dataSnapshot;
424 if (apData)
425 {
426 dataSnapshot.Init(*apData);
427 }
428
Jerry Johnsc8027a52021-12-13 17:20:15 -0800429 UpdateCache(aPath, apData, aStatus);
430
431 //
432 // Forward the call through.
433 //
Kundok Park035911e2022-03-23 22:16:54 -0700434 mCallback.OnAttributeData(aPath, apData ? &dataSnapshot : nullptr, aStatus);
Jerry Johnsc8027a52021-12-13 17:20:15 -0800435}
436
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400437template <bool CanEnableDataCaching>
438CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetVersion(const ConcreteClusterPath & aPath,
439 Optional<DataVersion> & aVersion) const
yunhanw-google57a2df32022-04-14 11:04:47 -0700440{
yunhanw-google3ef81a82022-05-19 10:46:23 -0700441 VerifyOrReturnError(aPath.IsValidConcreteClusterPath(), CHIP_ERROR_INVALID_ARGUMENT);
yunhanw-google57a2df32022-04-14 11:04:47 -0700442 CHIP_ERROR err;
yunhanw-google3ef81a82022-05-19 10:46:23 -0700443 auto clusterState = GetClusterState(aPath.mEndpointId, aPath.mClusterId, err);
yunhanw-google57a2df32022-04-14 11:04:47 -0700444 ReturnErrorOnFailure(err);
445 aVersion = clusterState->mCommittedDataVersion;
446 return CHIP_NO_ERROR;
447}
448
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400449template <bool CanEnableDataCaching>
450void ClusterStateCacheT<CanEnableDataCaching>::OnEventData(const EventHeader & aEventHeader, TLV::TLVReader * apData,
451 const StatusIB * apStatus)
Jerry Johnsc8027a52021-12-13 17:20:15 -0800452{
Jerry Johns964adbc2022-04-14 16:42:04 -0700453 VerifyOrDie(apData != nullptr || apStatus != nullptr);
454
455 TLV::TLVReader dataSnapshot;
456 if (apData)
Jerry Johnsc8027a52021-12-13 17:20:15 -0800457 {
Jerry Johns964adbc2022-04-14 16:42:04 -0700458 dataSnapshot.Init(*apData);
Jerry Johnsc8027a52021-12-13 17:20:15 -0800459 }
460
Jerry Johns964adbc2022-04-14 16:42:04 -0700461 UpdateEventCache(aEventHeader, apData, apStatus);
462 mCallback.OnEventData(aEventHeader, apData ? &dataSnapshot : nullptr, apStatus);
Jerry Johnsc8027a52021-12-13 17:20:15 -0800463}
464
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400465template <>
466CHIP_ERROR ClusterStateCacheT<true>::GetStatus(const ConcreteAttributePath & path, StatusIB & status) const
Jerry Johnsc8027a52021-12-13 17:20:15 -0800467{
468 CHIP_ERROR err;
469
470 auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err);
471 ReturnErrorOnFailure(err);
472
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400473 if (!attributeState->template Is<StatusIB>())
Jerry Johnsc8027a52021-12-13 17:20:15 -0800474 {
475 return CHIP_ERROR_INVALID_ARGUMENT;
476 }
477
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400478 status = attributeState->template Get<StatusIB>();
Jerry Johnsc8027a52021-12-13 17:20:15 -0800479 return CHIP_NO_ERROR;
480}
481
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400482template <>
483CHIP_ERROR ClusterStateCacheT<false>::GetStatus(const ConcreteAttributePath & path, StatusIB & status) const
484{
485 return CHIP_ERROR_INVALID_ARGUMENT;
486}
487
488template <bool CanEnableDataCaching>
489CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetStatus(const ConcreteEventPath & path, StatusIB & status) const
Jerry Johns964adbc2022-04-14 16:42:04 -0700490{
491 auto statusIter = mEventStatusCache.find(path);
492 if (statusIter == mEventStatusCache.end())
493 {
494 return CHIP_ERROR_KEY_NOT_FOUND;
495 }
496
497 status = statusIter->second;
498 return CHIP_NO_ERROR;
499}
500
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400501template <bool CanEnableDataCaching>
502void ClusterStateCacheT<CanEnableDataCaching>::GetSortedFilters(std::vector<std::pair<DataVersionFilter, size_t>> & aVector) const
yunhanw-google57a2df32022-04-14 11:04:47 -0700503{
504 for (auto const & endpointIter : mCache)
505 {
506 EndpointId endpointId = endpointIter.first;
507 for (auto const & clusterIter : endpointIter.second)
508 {
509 if (!clusterIter.second.mCommittedDataVersion.HasValue())
510 {
511 continue;
512 }
513 DataVersion dataVersion = clusterIter.second.mCommittedDataVersion.Value();
Boris Zbarsky343634a2023-04-24 13:37:18 -0400514 size_t clusterSize = 0;
yunhanw-google57a2df32022-04-14 11:04:47 -0700515 ClusterId clusterId = clusterIter.first;
516
517 for (auto const & attributeIter : clusterIter.second.mAttributes)
518 {
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400519 if constexpr (CanEnableDataCaching)
yunhanw-google57a2df32022-04-14 11:04:47 -0700520 {
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400521 if (attributeIter.second.template Is<StatusIB>())
522 {
523 clusterSize += SizeOfStatusIB(attributeIter.second.template Get<StatusIB>());
524 }
525 else if (attributeIter.second.template Is<uint32_t>())
526 {
527 clusterSize += attributeIter.second.template Get<uint32_t>();
528 }
529 else
530 {
531 VerifyOrDie(attributeIter.second.template Is<AttributeData>());
532 TLV::TLVReader bufReader;
533 bufReader.Init(attributeIter.second.template Get<AttributeData>().Get(),
534 attributeIter.second.template Get<AttributeData>().AllocatedSize());
535 ReturnOnFailure(bufReader.Next());
536 // Skip to the end of the element.
537 ReturnOnFailure(bufReader.Skip());
538
539 // Compute the amount of value data
540 clusterSize += bufReader.GetLengthRead();
541 }
yunhanw-google57a2df32022-04-14 11:04:47 -0700542 }
543 else
544 {
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400545 clusterSize += attributeIter.second;
yunhanw-google57a2df32022-04-14 11:04:47 -0700546 }
547 }
Boris Zbarsky343634a2023-04-24 13:37:18 -0400548
yunhanw-google57a2df32022-04-14 11:04:47 -0700549 if (clusterSize == 0)
550 {
Boris Zbarsky343634a2023-04-24 13:37:18 -0400551 // No data in this cluster, so no point in sending a dataVersion
552 // along at all.
yunhanw-google57a2df32022-04-14 11:04:47 -0700553 continue;
554 }
555
556 DataVersionFilter filter(endpointId, clusterId, dataVersion);
557
558 aVector.push_back(std::make_pair(filter, clusterSize));
559 }
560 }
Boris Zbarsky343634a2023-04-24 13:37:18 -0400561
yunhanw-google57a2df32022-04-14 11:04:47 -0700562 std::sort(aVector.begin(), aVector.end(),
563 [](const std::pair<DataVersionFilter, size_t> & x, const std::pair<DataVersionFilter, size_t> & y) {
564 return x.second > y.second;
565 });
566}
567
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400568template <bool CanEnableDataCaching>
569CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::OnUpdateDataVersionFilterList(
570 DataVersionFilterIBs::Builder & aDataVersionFilterIBsBuilder, const Span<AttributePathParams> & aAttributePaths,
571 bool & aEncodedDataVersionList)
yunhanw-google57a2df32022-04-14 11:04:47 -0700572{
573 CHIP_ERROR err = CHIP_NO_ERROR;
574 TLV::TLVWriter backup;
575
yunhanw-google9490a5f2022-05-20 09:44:23 -0700576 // 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 for (auto & attribute1 : aAttributePaths)
yunhanw-google57a2df32022-04-14 11:04:47 -0700581 {
yunhanw-google9490a5f2022-05-20 09:44:23 -0700582 if (attribute1.HasWildcardAttributeId())
yunhanw-google57a2df32022-04-14 11:04:47 -0700583 {
yunhanw-google9490a5f2022-05-20 09:44:23 -0700584 bool intersected = false;
585 for (auto & attribute2 : aAttributePaths)
586 {
587 if (attribute2.HasWildcardAttributeId())
588 {
589 continue;
590 }
591
592 if (attribute1.Intersects(attribute2))
593 {
594 intersected = true;
595 break;
596 }
597 }
598
599 if (!intersected)
600 {
601 mRequestPathSet.insert(attribute1);
602 }
yunhanw-google57a2df32022-04-14 11:04:47 -0700603 }
604 }
605
606 std::vector<std::pair<DataVersionFilter, size_t>> filterVector;
607 GetSortedFilters(filterVector);
608
609 aEncodedDataVersionList = false;
610 for (auto & filter : filterVector)
611 {
612 bool intersected = false;
613 aDataVersionFilterIBsBuilder.Checkpoint(backup);
614
615 // if the particular cached cluster does not intersect with user provided attribute paths, skip the cached one
616 for (const auto & attributePath : aAttributePaths)
617 {
618 if (attributePath.IncludesAttributesInCluster(filter.first))
619 {
620 intersected = true;
621 break;
622 }
623 }
624 if (!intersected)
625 {
626 continue;
627 }
628
Boris Zbarskye5b79ae2024-09-30 18:19:36 -0400629 SuccessOrExit(err = aDataVersionFilterIBsBuilder.EncodeDataVersionFilterIB(filter.first));
yunhanw-google57a2df32022-04-14 11:04:47 -0700630 aEncodedDataVersionList = true;
631 }
632
633exit:
634 if (err == CHIP_ERROR_NO_MEMORY || err == CHIP_ERROR_BUFFER_TOO_SMALL)
635 {
636 ChipLogProgress(DataManagement, "OnUpdateDataVersionFilterList out of space; rolling back");
637 aDataVersionFilterIBsBuilder.Rollback(backup);
638 err = CHIP_NO_ERROR;
639 }
640 return err;
641}
642
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400643template <bool CanEnableDataCaching>
Boris Zbarskyfe4764a2024-07-05 14:08:02 -0400644void ClusterStateCacheT<CanEnableDataCaching>::ClearAttributes(EndpointId endpointId)
645{
646 mCache.erase(endpointId);
647}
648
649template <bool CanEnableDataCaching>
650void ClusterStateCacheT<CanEnableDataCaching>::ClearAttributes(const ConcreteClusterPath & cluster)
651{
652 // Can't use GetEndpointState here, since that only handles const things.
653 auto endpointIter = mCache.find(cluster.mEndpointId);
654 if (endpointIter == mCache.end())
655 {
656 return;
657 }
658
659 auto & endpointState = endpointIter->second;
660 endpointState.erase(cluster.mClusterId);
661}
662
663template <bool CanEnableDataCaching>
664void ClusterStateCacheT<CanEnableDataCaching>::ClearAttribute(const ConcreteAttributePath & attribute)
665{
666 // Can't use GetClusterState here, since that only handles const things.
667 auto endpointIter = mCache.find(attribute.mEndpointId);
668 if (endpointIter == mCache.end())
669 {
670 return;
671 }
672
673 auto & endpointState = endpointIter->second;
674 auto clusterIter = endpointState.find(attribute.mClusterId);
675 if (clusterIter == endpointState.end())
676 {
677 return;
678 }
679
680 auto & clusterState = clusterIter->second;
681 clusterState.mAttributes.erase(attribute.mAttributeId);
682}
683
684template <bool CanEnableDataCaching>
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400685CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetLastReportDataPath(ConcreteClusterPath & aPath)
yunhanw-googlef2cd15f2022-09-26 16:25:02 -0700686{
yunhanw-googlec1bfd9d2022-11-17 12:42:14 -0800687 if (mLastReportDataPath.IsValidConcreteClusterPath())
688 {
689 aPath = mLastReportDataPath;
690 return CHIP_NO_ERROR;
691 }
692 return CHIP_ERROR_INCORRECT_STATE;
yunhanw-googlef2cd15f2022-09-26 16:25:02 -0700693}
Boris Zbarsky2fcac2e2024-04-19 16:20:14 -0400694
695// Ensure that our out-of-line template methods actually get compiled.
696template class ClusterStateCacheT<true>;
697template class ClusterStateCacheT<false>;
698
Jerry Johnsc8027a52021-12-13 17:20:15 -0800699} // namespace app
700} // namespace chip