| /* |
| * |
| * Copyright (c) 2023 Project CHIP Authors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <app/InteractionModelEngine.h> |
| #include <app/SubscriptionResumptionSessionEstablisher.h> |
| |
| namespace chip { |
| namespace app { |
| |
| class AutoDeleteEstablisher |
| { |
| public: |
| AutoDeleteEstablisher(SubscriptionResumptionSessionEstablisher * sessionEstablisher) : mSessionEstablisher(sessionEstablisher) |
| {} |
| ~AutoDeleteEstablisher() { chip::Platform::Delete(mSessionEstablisher); } |
| |
| SubscriptionResumptionSessionEstablisher * operator->() const { return mSessionEstablisher; } |
| |
| SubscriptionResumptionSessionEstablisher & operator*() const { return *mSessionEstablisher; } |
| |
| private: |
| SubscriptionResumptionSessionEstablisher * mSessionEstablisher; |
| }; |
| |
| SubscriptionResumptionSessionEstablisher::SubscriptionResumptionSessionEstablisher() : |
| mOnConnectedCallback(HandleDeviceConnected, this), mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this) |
| {} |
| |
| CHIP_ERROR |
| SubscriptionResumptionSessionEstablisher::ResumeSubscription( |
| CASESessionManager & caseSessionManager, const SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo) |
| { |
| mSubscriptionInfo.mNodeId = subscriptionInfo.mNodeId; |
| mSubscriptionInfo.mFabricIndex = subscriptionInfo.mFabricIndex; |
| mSubscriptionInfo.mSubscriptionId = subscriptionInfo.mSubscriptionId; |
| mSubscriptionInfo.mMinInterval = subscriptionInfo.mMinInterval; |
| mSubscriptionInfo.mMaxInterval = subscriptionInfo.mMaxInterval; |
| mSubscriptionInfo.mFabricFiltered = subscriptionInfo.mFabricFiltered; |
| #if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION |
| mSubscriptionInfo.mResumptionRetries = subscriptionInfo.mResumptionRetries; |
| #endif |
| // Copy the Attribute Paths and Event Paths |
| if (subscriptionInfo.mAttributePaths.AllocatedSize() > 0) |
| { |
| mSubscriptionInfo.mAttributePaths.Alloc(subscriptionInfo.mAttributePaths.AllocatedSize()); |
| if (!mSubscriptionInfo.mAttributePaths.Get()) |
| { |
| return CHIP_ERROR_NO_MEMORY; |
| } |
| for (size_t i = 0; i < mSubscriptionInfo.mAttributePaths.AllocatedSize(); ++i) |
| { |
| mSubscriptionInfo.mAttributePaths[i] = subscriptionInfo.mAttributePaths[i]; |
| } |
| } |
| if (subscriptionInfo.mEventPaths.AllocatedSize() > 0) |
| { |
| mSubscriptionInfo.mEventPaths.Alloc(subscriptionInfo.mEventPaths.AllocatedSize()); |
| if (!mSubscriptionInfo.mEventPaths.Get()) |
| { |
| return CHIP_ERROR_NO_MEMORY; |
| } |
| for (size_t i = 0; i < mSubscriptionInfo.mEventPaths.AllocatedSize(); ++i) |
| { |
| mSubscriptionInfo.mEventPaths[i] = subscriptionInfo.mEventPaths[i]; |
| } |
| } |
| |
| ScopedNodeId peerNode = ScopedNodeId(mSubscriptionInfo.mNodeId, mSubscriptionInfo.mFabricIndex); |
| caseSessionManager.FindOrEstablishSession(peerNode, &mOnConnectedCallback, &mOnConnectionFailureCallback); |
| return CHIP_NO_ERROR; |
| } |
| |
| void SubscriptionResumptionSessionEstablisher::HandleDeviceConnected(void * context, Messaging::ExchangeManager & exchangeMgr, |
| const SessionHandle & sessionHandle) |
| { |
| AutoDeleteEstablisher establisher(static_cast<SubscriptionResumptionSessionEstablisher *>(context)); |
| SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo = establisher->mSubscriptionInfo; |
| InteractionModelEngine * imEngine = InteractionModelEngine::GetInstance(); |
| |
| // Decrement the number of subscriptions to resume since we have completed our retry attempt for a given subscription. |
| // We do this before the readHandler creation since we do not care if the subscription has successfully been resumed or |
| // not. Counter only tracks the number of individual subscriptions we will try to resume. |
| imEngine->DecrementNumSubscriptionsToResume(); |
| |
| if (!imEngine->EnsureResourceForSubscription(subscriptionInfo.mFabricIndex, subscriptionInfo.mAttributePaths.AllocatedSize(), |
| subscriptionInfo.mEventPaths.AllocatedSize())) |
| { |
| // TODO - Should we keep the subscription here? |
| ChipLogProgress(InteractionModel, "no resource for subscription resumption"); |
| return; |
| } |
| ReadHandler * readHandler = |
| imEngine->mReadHandlers.CreateObject(*imEngine, imEngine->GetReportScheduler(), imEngine->GetDataModelProvider()); |
| if (readHandler == nullptr) |
| { |
| // TODO - Should we keep the subscription here? |
| ChipLogProgress(InteractionModel, "no resource for ReadHandler creation"); |
| return; |
| } |
| readHandler->OnSubscriptionResumed(sessionHandle, *establisher); |
| #if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION |
| // Reset the resumption retries to 0 if subscription is resumed |
| subscriptionInfo.mResumptionRetries = 0; |
| auto * subscriptionResumptionStorage = InteractionModelEngine::GetInstance()->GetSubscriptionResumptionStorage(); |
| if (subscriptionResumptionStorage) |
| { |
| subscriptionResumptionStorage->Save(subscriptionInfo); |
| } |
| #endif // CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION |
| } |
| |
| void SubscriptionResumptionSessionEstablisher::HandleDeviceConnectionFailure(void * context, const ScopedNodeId & peerId, |
| CHIP_ERROR error) |
| { |
| AutoDeleteEstablisher establisher(static_cast<SubscriptionResumptionSessionEstablisher *>(context)); |
| InteractionModelEngine * imEngine = InteractionModelEngine::GetInstance(); |
| SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo = establisher->mSubscriptionInfo; |
| ChipLogError(DataManagement, "Failed to establish CASE for subscription-resumption with error '%" CHIP_ERROR_FORMAT "'", |
| error.Format()); |
| |
| // Decrement the number of subscriptions to resume since we have completed our retry attempt for a given subscription. |
| // We do this here since we were not able to connect to the subscriber thus we have completed our resumption attempt. |
| // Counter only tracks the number of individual subscriptions we will try to resume. |
| imEngine->DecrementNumSubscriptionsToResume(); |
| |
| auto * subscriptionResumptionStorage = imEngine->GetSubscriptionResumptionStorage(); |
| if (!subscriptionResumptionStorage) |
| { |
| ChipLogError(DataManagement, "Failed to get subscription resumption storage"); |
| return; |
| } |
| #if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION |
| if (subscriptionInfo.mResumptionRetries <= CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION_MAX_FIBONACCI_STEP_INDEX) |
| { |
| InteractionModelEngine::GetInstance()->TryToResumeSubscriptions(); |
| subscriptionInfo.mResumptionRetries++; |
| subscriptionResumptionStorage->Save(subscriptionInfo); |
| } |
| else |
| #endif // CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION |
| { |
| // If the device fails to establish the session several times, the subscriber might be offline and its subscription |
| // read client will be deleted when the device reconnects to the subscriber. This subscription will be never used again. |
| // Clean up the persistent subscription information storage. |
| subscriptionResumptionStorage->Delete(subscriptionInfo.mNodeId, subscriptionInfo.mFabricIndex, |
| subscriptionInfo.mSubscriptionId); |
| } |
| } |
| |
| } // namespace app |
| } // namespace chip |