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