LCOV - code coverage report
Current view: top level - app - SimpleSubscriptionResumptionStorage.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 197 206 95.6 %
Date: 2024-02-15 08:20:41 Functions: 14 14 100.0 %

          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

Generated by: LCOV version 1.14