blob: 11da3c3d496f9260bfbdba61ab2a57131e17cf6d [file] [log] [blame]
/*
*
* 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>
#include <app/codegen-data-model/Instance.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->GetDataModel());
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