Line data Source code
1 : /*
2 : * Copyright (c) 2023 Project CHIP Authors
3 : * All rights reserved.
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 : * This file defines a basic implementation of SubscriptionResumptionStorage that
21 : * persists subscriptions in a flat list in TLV.
22 : */
23 :
24 : #include <app/SimpleSubscriptionResumptionStorage.h>
25 :
26 : #include <lib/support/Base64.h>
27 : #include <lib/support/CodeUtils.h>
28 : #include <lib/support/SafeInt.h>
29 : #include <lib/support/logging/CHIPLogging.h>
30 :
31 : namespace chip {
32 : namespace app {
33 :
34 : constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kPeerNodeIdTag;
35 : constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kFabricIndexTag;
36 : constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kSubscriptionIdTag;
37 : constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kMinIntervalTag;
38 : constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kMaxIntervalTag;
39 : constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kFabricFilteredTag;
40 : constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kAttributePathsListTag;
41 : constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEventPathsListTag;
42 : constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kAttributePathTag;
43 : constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEventPathTag;
44 : constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEndpointIdTag;
45 : constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kClusterIdTag;
46 : constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kAttributeIdTag;
47 : constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEventIdTag;
48 : constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEventPathTypeTag;
49 :
50 9 : SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::SimpleSubscriptionInfoIterator(
51 9 : SimpleSubscriptionResumptionStorage & storage) :
52 9 : mStorage(storage)
53 : {
54 9 : mNextIndex = 0;
55 9 : }
56 :
57 10 : size_t SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::Count()
58 : {
59 10 : return static_cast<size_t>(mStorage.Count());
60 : }
61 :
62 37 : bool SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::Next(SubscriptionInfo & output)
63 : {
64 393 : for (; mNextIndex < CHIP_IM_MAX_NUM_SUBSCRIPTIONS; mNextIndex++)
65 : {
66 385 : CHIP_ERROR err = mStorage.Load(mNextIndex, output);
67 385 : if (err == CHIP_NO_ERROR)
68 : {
69 : // increment index for the next call
70 29 : mNextIndex++;
71 29 : return true;
72 : }
73 :
74 356 : if (err != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
75 : {
76 2 : ChipLogError(DataManagement, "Failed to load subscription at index %u error %" CHIP_ERROR_FORMAT,
77 : static_cast<unsigned>(mNextIndex), err.Format());
78 2 : mStorage.Delete(mNextIndex);
79 : }
80 : }
81 :
82 8 : return false;
83 : }
84 :
85 9 : void SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::Release()
86 : {
87 9 : mStorage.mSubscriptionInfoIterators.ReleaseObject(this);
88 9 : }
89 :
90 7 : CHIP_ERROR SimpleSubscriptionResumptionStorage::Init(PersistentStorageDelegate * storage)
91 : {
92 7 : VerifyOrReturnError(storage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
93 7 : mStorage = storage;
94 :
95 : uint16_t countMax;
96 7 : uint16_t len = sizeof(countMax);
97 : CHIP_ERROR err =
98 7 : mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(), &countMax, len);
99 : // If there's a previous countMax and it's larger than CHIP_IM_MAX_NUM_SUBSCRIPTIONS,
100 : // clean up subscriptions beyond the limit
101 7 : if ((err == CHIP_NO_ERROR) && (countMax != CHIP_IM_MAX_NUM_SUBSCRIPTIONS))
102 : {
103 49 : for (uint16_t subscriptionIndex = CHIP_IM_MAX_NUM_SUBSCRIPTIONS; subscriptionIndex < countMax; subscriptionIndex++)
104 : {
105 48 : Delete(subscriptionIndex);
106 : }
107 : }
108 :
109 : // Always save the current CHIP_IM_MAX_NUM_SUBSCRIPTIONS
110 7 : uint16_t countMaxToSave = CHIP_IM_MAX_NUM_SUBSCRIPTIONS;
111 7 : ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName(),
112 : &countMaxToSave, sizeof(uint16_t)));
113 :
114 7 : return CHIP_NO_ERROR;
115 : }
116 :
117 9 : SubscriptionResumptionStorage::SubscriptionInfoIterator * SimpleSubscriptionResumptionStorage::IterateSubscriptions()
118 : {
119 9 : return mSubscriptionInfoIterators.CreateObject(*this);
120 : }
121 :
122 10 : uint16_t SimpleSubscriptionResumptionStorage::Count()
123 : {
124 10 : uint16_t subscriptionCount = 0;
125 490 : for (uint16_t subscriptionIndex = 0; subscriptionIndex < CHIP_IM_MAX_NUM_SUBSCRIPTIONS; subscriptionIndex++)
126 : {
127 480 : if (mStorage->SyncDoesKeyExist(DefaultStorageKeyAllocator::SubscriptionResumption(subscriptionIndex).KeyName()))
128 : {
129 31 : subscriptionCount++;
130 : }
131 : }
132 :
133 10 : return subscriptionCount;
134 : }
135 :
136 77 : CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(uint16_t subscriptionIndex)
137 : {
138 77 : return mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumption(subscriptionIndex).KeyName());
139 : }
140 :
141 1873 : CHIP_ERROR SimpleSubscriptionResumptionStorage::Load(uint16_t subscriptionIndex, SubscriptionInfo & subscriptionInfo)
142 : {
143 1873 : Platform::ScopedMemoryBuffer<uint8_t> backingBuffer;
144 1873 : backingBuffer.Calloc(MaxSubscriptionSize());
145 1873 : ReturnErrorCodeIf(backingBuffer.Get() == nullptr, CHIP_ERROR_NO_MEMORY);
146 :
147 1873 : uint16_t len = static_cast<uint16_t>(MaxSubscriptionSize());
148 1873 : ReturnErrorOnFailure(mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumption(subscriptionIndex).KeyName(),
149 : backingBuffer.Get(), len));
150 :
151 339 : TLV::ScopedBufferTLVReader reader(std::move(backingBuffer), len);
152 :
153 339 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
154 :
155 : TLV::TLVType subscriptionContainerType;
156 338 : ReturnErrorOnFailure(reader.EnterContainer(subscriptionContainerType));
157 :
158 : // Node ID
159 338 : ReturnErrorOnFailure(reader.Next(kPeerNodeIdTag));
160 338 : ReturnErrorOnFailure(reader.Get(subscriptionInfo.mNodeId));
161 :
162 : // Fabric index
163 338 : ReturnErrorOnFailure(reader.Next(kFabricIndexTag));
164 338 : ReturnErrorOnFailure(reader.Get(subscriptionInfo.mFabricIndex));
165 :
166 : // Subscription ID
167 338 : ReturnErrorOnFailure(reader.Next(kSubscriptionIdTag));
168 338 : ReturnErrorOnFailure(reader.Get(subscriptionInfo.mSubscriptionId));
169 :
170 : // Min interval
171 338 : ReturnErrorOnFailure(reader.Next(kMinIntervalTag));
172 338 : ReturnErrorOnFailure(reader.Get(subscriptionInfo.mMinInterval));
173 :
174 : // Max interval
175 338 : ReturnErrorOnFailure(reader.Next(kMaxIntervalTag));
176 338 : ReturnErrorOnFailure(reader.Get(subscriptionInfo.mMaxInterval));
177 :
178 : // Fabric filtered boolean
179 338 : ReturnErrorOnFailure(reader.Next(kFabricFilteredTag));
180 338 : ReturnErrorOnFailure(reader.Get(subscriptionInfo.mFabricFiltered));
181 :
182 : // Attribute Paths
183 338 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_List, kAttributePathsListTag));
184 : TLV::TLVType attributesListType;
185 338 : ReturnErrorOnFailure(reader.EnterContainer(attributesListType));
186 :
187 338 : size_t pathCount = 0;
188 338 : ReturnErrorOnFailure(reader.CountRemainingInContainer(&pathCount));
189 :
190 : // If a stack struct is being reused to iterate, free the previous paths ScopedMemoryBuffer
191 338 : subscriptionInfo.mAttributePaths.Free();
192 338 : if (pathCount)
193 : {
194 10 : subscriptionInfo.mAttributePaths.Calloc(pathCount);
195 10 : ReturnErrorCodeIf(subscriptionInfo.mAttributePaths.Get() == nullptr, CHIP_ERROR_NO_MEMORY);
196 30 : for (size_t pathIndex = 0; pathIndex < pathCount; pathIndex++)
197 : {
198 20 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, kAttributePathTag));
199 : TLV::TLVType attributeContainerType;
200 20 : ReturnErrorOnFailure(reader.EnterContainer(attributeContainerType));
201 :
202 20 : ReturnErrorOnFailure(reader.Next(kEndpointIdTag));
203 20 : ReturnErrorOnFailure(reader.Get(subscriptionInfo.mAttributePaths[pathIndex].mEndpointId));
204 :
205 20 : ReturnErrorOnFailure(reader.Next(kClusterIdTag));
206 20 : ReturnErrorOnFailure(reader.Get(subscriptionInfo.mAttributePaths[pathIndex].mClusterId));
207 :
208 20 : ReturnErrorOnFailure(reader.Next(kAttributeIdTag));
209 20 : ReturnErrorOnFailure(reader.Get(subscriptionInfo.mAttributePaths[pathIndex].mAttributeId));
210 :
211 20 : ReturnErrorOnFailure(reader.ExitContainer(attributeContainerType));
212 : }
213 : }
214 338 : ReturnErrorOnFailure(reader.ExitContainer(attributesListType));
215 :
216 : // Event Paths
217 338 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_List, kEventPathsListTag));
218 : TLV::TLVType eventsListType;
219 338 : ReturnErrorOnFailure(reader.EnterContainer(eventsListType));
220 :
221 338 : ReturnErrorOnFailure(reader.CountRemainingInContainer(&pathCount));
222 :
223 : // If a stack struct is being reused to iterate, free the previous paths ScopedMemoryBuffer
224 338 : subscriptionInfo.mEventPaths.Free();
225 338 : if (pathCount)
226 : {
227 9 : subscriptionInfo.mEventPaths.Calloc(pathCount);
228 9 : ReturnErrorCodeIf(subscriptionInfo.mEventPaths.Get() == nullptr, CHIP_ERROR_NO_MEMORY);
229 27 : for (size_t pathIndex = 0; pathIndex < pathCount; pathIndex++)
230 : {
231 18 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, kEventPathTag));
232 : TLV::TLVType eventContainerType;
233 18 : ReturnErrorOnFailure(reader.EnterContainer(eventContainerType));
234 :
235 : EventPathType eventPathType;
236 18 : ReturnErrorOnFailure(reader.Next(kEventPathTypeTag));
237 18 : ReturnErrorOnFailure(reader.Get(eventPathType));
238 :
239 18 : subscriptionInfo.mEventPaths[pathIndex].mIsUrgentEvent = (eventPathType == EventPathType::kUrgent);
240 :
241 18 : ReturnErrorOnFailure(reader.Next(kEndpointIdTag));
242 18 : ReturnErrorOnFailure(reader.Get(subscriptionInfo.mEventPaths[pathIndex].mEndpointId));
243 :
244 18 : ReturnErrorOnFailure(reader.Next(kClusterIdTag));
245 18 : ReturnErrorOnFailure(reader.Get(subscriptionInfo.mEventPaths[pathIndex].mClusterId));
246 :
247 18 : ReturnErrorOnFailure(reader.Next(kEventIdTag));
248 18 : ReturnErrorOnFailure(reader.Get(subscriptionInfo.mEventPaths[pathIndex].mEventId));
249 :
250 18 : ReturnErrorOnFailure(reader.ExitContainer(eventContainerType));
251 : }
252 : }
253 338 : ReturnErrorOnFailure(reader.ExitContainer(eventsListType));
254 :
255 338 : ReturnErrorOnFailure(reader.ExitContainer(subscriptionContainerType));
256 :
257 338 : return CHIP_NO_ERROR;
258 1873 : }
259 :
260 29 : CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(TLV::TLVWriter & writer, SubscriptionInfo & subscriptionInfo)
261 : {
262 : TLV::TLVType subscriptionContainerType;
263 29 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, subscriptionContainerType));
264 29 : ReturnErrorOnFailure(writer.Put(kPeerNodeIdTag, subscriptionInfo.mNodeId));
265 29 : ReturnErrorOnFailure(writer.Put(kFabricIndexTag, subscriptionInfo.mFabricIndex));
266 29 : ReturnErrorOnFailure(writer.Put(kSubscriptionIdTag, subscriptionInfo.mSubscriptionId));
267 29 : ReturnErrorOnFailure(writer.Put(kMinIntervalTag, subscriptionInfo.mMinInterval));
268 29 : ReturnErrorOnFailure(writer.Put(kMaxIntervalTag, subscriptionInfo.mMaxInterval));
269 29 : ReturnErrorOnFailure(writer.Put(kFabricFilteredTag, subscriptionInfo.mFabricFiltered));
270 :
271 : // Attribute paths
272 : TLV::TLVType attributesListType;
273 29 : ReturnErrorOnFailure(writer.StartContainer(kAttributePathsListTag, TLV::kTLVType_List, attributesListType));
274 37 : for (size_t pathIndex = 0; pathIndex < subscriptionInfo.mAttributePaths.AllocatedSize(); pathIndex++)
275 : {
276 8 : TLV::TLVType attributeContainerType = TLV::kTLVType_Structure;
277 8 : ReturnErrorOnFailure(writer.StartContainer(kAttributePathTag, TLV::kTLVType_Structure, attributeContainerType));
278 :
279 8 : ReturnErrorOnFailure(writer.Put(kEndpointIdTag, subscriptionInfo.mAttributePaths[pathIndex].mEndpointId));
280 8 : ReturnErrorOnFailure(writer.Put(kClusterIdTag, subscriptionInfo.mAttributePaths[pathIndex].mClusterId));
281 8 : ReturnErrorOnFailure(writer.Put(kAttributeIdTag, subscriptionInfo.mAttributePaths[pathIndex].mAttributeId));
282 :
283 8 : ReturnErrorOnFailure(writer.EndContainer(attributeContainerType));
284 : }
285 29 : ReturnErrorOnFailure(writer.EndContainer(attributesListType));
286 :
287 : // Event paths
288 : TLV::TLVType eventsListType;
289 29 : ReturnErrorOnFailure(writer.StartContainer(kEventPathsListTag, TLV::kTLVType_List, eventsListType));
290 33 : for (size_t pathIndex = 0; pathIndex < subscriptionInfo.mEventPaths.AllocatedSize(); pathIndex++)
291 : {
292 4 : TLV::TLVType eventContainerType = TLV::kTLVType_Structure;
293 4 : ReturnErrorOnFailure(writer.StartContainer(kEventPathTag, TLV::kTLVType_Structure, eventContainerType));
294 :
295 4 : if (subscriptionInfo.mEventPaths[pathIndex].mIsUrgentEvent)
296 : {
297 1 : ReturnErrorOnFailure(writer.Put(kEventPathTypeTag, EventPathType::kUrgent));
298 : }
299 : else
300 : {
301 3 : ReturnErrorOnFailure(writer.Put(kEventPathTypeTag, EventPathType::kNonUrgent));
302 : }
303 4 : ReturnErrorOnFailure(writer.Put(kEndpointIdTag, subscriptionInfo.mEventPaths[pathIndex].mEndpointId));
304 4 : ReturnErrorOnFailure(writer.Put(kClusterIdTag, subscriptionInfo.mEventPaths[pathIndex].mClusterId));
305 4 : ReturnErrorOnFailure(writer.Put(kEventIdTag, subscriptionInfo.mEventPaths[pathIndex].mEventId));
306 :
307 4 : ReturnErrorOnFailure(writer.EndContainer(eventContainerType));
308 : }
309 29 : ReturnErrorOnFailure(writer.EndContainer(eventsListType));
310 :
311 29 : ReturnErrorOnFailure(writer.EndContainer(subscriptionContainerType));
312 :
313 29 : return CHIP_NO_ERROR;
314 : }
315 :
316 27 : CHIP_ERROR SimpleSubscriptionResumptionStorage::Save(SubscriptionInfo & subscriptionInfo)
317 : {
318 : // Find empty index or duplicate if exists
319 : uint16_t subscriptionIndex;
320 27 : uint16_t firstEmptySubscriptionIndex = CHIP_IM_MAX_NUM_SUBSCRIPTIONS; // initialize to out of bounds as "not set"
321 1323 : for (subscriptionIndex = 0; subscriptionIndex < CHIP_IM_MAX_NUM_SUBSCRIPTIONS; subscriptionIndex++)
322 : {
323 1296 : SubscriptionInfo currentSubscriptionInfo;
324 1296 : CHIP_ERROR err = Load(subscriptionIndex, currentSubscriptionInfo);
325 :
326 : // if empty and firstEmptySubscriptionIndex isn't set yet, then mark empty spot
327 1296 : if ((firstEmptySubscriptionIndex == CHIP_IM_MAX_NUM_SUBSCRIPTIONS) && (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND))
328 : {
329 27 : firstEmptySubscriptionIndex = subscriptionIndex;
330 : }
331 :
332 : // delete duplicate
333 1296 : if (err == CHIP_NO_ERROR)
334 : {
335 279 : if ((subscriptionInfo.mNodeId == currentSubscriptionInfo.mNodeId) &&
336 276 : (subscriptionInfo.mFabricIndex == currentSubscriptionInfo.mFabricIndex) &&
337 276 : (subscriptionInfo.mSubscriptionId == currentSubscriptionInfo.mSubscriptionId))
338 : {
339 0 : Delete(subscriptionIndex);
340 : // if duplicate is the first empty spot, then also set it
341 0 : if (firstEmptySubscriptionIndex == CHIP_IM_MAX_NUM_SUBSCRIPTIONS)
342 : {
343 0 : firstEmptySubscriptionIndex = subscriptionIndex;
344 : }
345 : }
346 : }
347 1296 : }
348 :
349 : // Fail if no empty space
350 27 : if (firstEmptySubscriptionIndex == CHIP_IM_MAX_NUM_SUBSCRIPTIONS)
351 : {
352 0 : return CHIP_ERROR_NO_MEMORY;
353 : }
354 :
355 : // Now construct subscription state and save
356 27 : Platform::ScopedMemoryBuffer<uint8_t> backingBuffer;
357 27 : backingBuffer.Calloc(MaxSubscriptionSize());
358 27 : ReturnErrorCodeIf(backingBuffer.Get() == nullptr, CHIP_ERROR_NO_MEMORY);
359 :
360 27 : TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), MaxSubscriptionSize());
361 :
362 27 : ReturnErrorOnFailure(Save(writer, subscriptionInfo));
363 :
364 27 : const auto len = writer.GetLengthWritten();
365 27 : VerifyOrReturnError(CanCastTo<uint16_t>(len), CHIP_ERROR_BUFFER_TOO_SMALL);
366 :
367 27 : writer.Finalize(backingBuffer);
368 :
369 27 : ReturnErrorOnFailure(
370 : mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::SubscriptionResumption(firstEmptySubscriptionIndex).KeyName(),
371 : backingBuffer.Get(), static_cast<uint16_t>(len)));
372 :
373 27 : return CHIP_NO_ERROR;
374 27 : }
375 :
376 1 : CHIP_ERROR SimpleSubscriptionResumptionStorage::Delete(NodeId nodeId, FabricIndex fabricIndex, SubscriptionId subscriptionId)
377 : {
378 1 : bool subscriptionFound = false;
379 1 : CHIP_ERROR lastDeleteErr = CHIP_NO_ERROR;
380 :
381 1 : uint16_t remainingSubscriptionsCount = 0;
382 49 : for (uint16_t subscriptionIndex = 0; subscriptionIndex < CHIP_IM_MAX_NUM_SUBSCRIPTIONS; subscriptionIndex++)
383 : {
384 48 : SubscriptionInfo subscriptionInfo;
385 48 : CHIP_ERROR err = Load(subscriptionIndex, subscriptionInfo);
386 :
387 : // delete match
388 48 : if (err == CHIP_NO_ERROR)
389 : {
390 3 : if ((nodeId == subscriptionInfo.mNodeId) && (fabricIndex == subscriptionInfo.mFabricIndex) &&
391 1 : (subscriptionId == subscriptionInfo.mSubscriptionId))
392 : {
393 1 : subscriptionFound = true;
394 1 : CHIP_ERROR deleteErr = Delete(subscriptionIndex);
395 1 : if (deleteErr != CHIP_NO_ERROR)
396 : {
397 0 : lastDeleteErr = deleteErr;
398 : }
399 1 : }
400 : else
401 : {
402 2 : remainingSubscriptionsCount++;
403 : }
404 : }
405 48 : }
406 :
407 : // if there are no persisted subscriptions, the MaxCount can also be deleted
408 1 : if (remainingSubscriptionsCount == 0)
409 : {
410 0 : DeleteMaxCount();
411 : }
412 :
413 1 : if (lastDeleteErr != CHIP_NO_ERROR)
414 : {
415 0 : return lastDeleteErr;
416 : }
417 :
418 1 : return subscriptionFound ? CHIP_NO_ERROR : CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND;
419 : }
420 :
421 2 : CHIP_ERROR SimpleSubscriptionResumptionStorage::DeleteMaxCount()
422 : {
423 2 : return mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::SubscriptionResumptionMaxCount().KeyName());
424 : }
425 :
426 3 : CHIP_ERROR SimpleSubscriptionResumptionStorage::DeleteAll(FabricIndex fabricIndex)
427 : {
428 3 : CHIP_ERROR deleteErr = CHIP_NO_ERROR;
429 :
430 3 : uint16_t count = 0;
431 147 : for (uint16_t subscriptionIndex = 0; subscriptionIndex < CHIP_IM_MAX_NUM_SUBSCRIPTIONS; subscriptionIndex++)
432 : {
433 144 : SubscriptionInfo subscriptionInfo;
434 144 : CHIP_ERROR err = Load(subscriptionIndex, subscriptionInfo);
435 :
436 144 : if (err == CHIP_NO_ERROR)
437 : {
438 27 : if (fabricIndex == subscriptionInfo.mFabricIndex)
439 : {
440 26 : err = Delete(subscriptionIndex);
441 26 : if ((err != CHIP_NO_ERROR) && (err != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND))
442 : {
443 0 : deleteErr = err;
444 : }
445 : }
446 : else
447 : {
448 1 : count++;
449 : }
450 : }
451 144 : }
452 :
453 : // if there are no persisted subscriptions, the MaxCount can also be deleted
454 3 : if (count == 0)
455 : {
456 2 : CHIP_ERROR err = DeleteMaxCount();
457 :
458 2 : if ((err != CHIP_NO_ERROR) && (err != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND))
459 : {
460 0 : deleteErr = err;
461 : }
462 : }
463 :
464 3 : return deleteErr;
465 : }
466 :
467 : } // namespace app
468 : } // namespace chip
|