blob: 29ccd42d2f3ca0ecda3af8b2b32d33dd9c8a65d2 [file]
/*
*
* Copyright (c) 2026 Project CHIP Authors
* All rights reserved.
*
* 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 "SubscriptionManager.h"
#include <app/AttributePathParams.h>
#include <app/InteractionModelEngine.h>
#include <app/server/Server.h>
#include <credentials/FabricTable.h>
#include <lib/core/ScopedNodeId.h>
#include <lib/support/CodeUtils.h>
#include <platform/CHIPDeviceLayer.h>
#include <utility>
namespace chip {
namespace Silabs {
using chip::app::AttributePathParams;
using chip::app::ConcreteDataAttributePath;
using chip::app::InteractionModelEngine;
using chip::app::ReadClient;
using chip::app::ReadPrepareParams;
using chip::app::StatusIB;
// ---------------------------------------------------------------------------
// Instance
// ---------------------------------------------------------------------------
SubscriptionManager & SubscriptionManager::Instance()
{
static SubscriptionManager sInstance;
return sInstance;
}
SubscriptionManager::SubscriptionManager()
{
for (Handle i = 0; i < kMaxSubscriptions; ++i)
{
mSlots[i].callback.Init(this, i);
}
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
CHIP_ERROR SubscriptionManager::Subscribe(const Info & info, Handle * outHandle, uint16_t minIntervalSeconds,
uint16_t maxIntervalSeconds)
{
if (outHandle != nullptr)
{
*outHandle = kInvalidHandle;
}
const FabricTable & fabricTable = Server::GetInstance().GetFabricTable();
VerifyOrReturnError(fabricTable.FindFabricWithIndex(info.fabricIndex) != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
Handle slot = FindFreeSlot();
VerifyOrReturnError(slot != kInvalidHandle, CHIP_ERROR_NO_MEMORY);
auto * pathParams = Platform::New<AttributePathParams>(info.endpointId, info.clusterId, info.attributeId);
VerifyOrReturnError(pathParams != nullptr, CHIP_ERROR_NO_MEMORY);
ReadPrepareParams readParams;
readParams.mpAttributePathParamsList = pathParams;
readParams.mAttributePathParamsListSize = 1;
readParams.mMinIntervalFloorSeconds = minIntervalSeconds;
readParams.mMaxIntervalCeilingSeconds = maxIntervalSeconds;
readParams.mKeepSubscriptions = true;
readParams.mIsFabricFiltered = true;
auto * readClient = Platform::New<ReadClient>(InteractionModelEngine::GetInstance(), nullptr, mSlots[slot].callback,
ReadClient::InteractionType::Subscribe);
if (readClient == nullptr)
{
Platform::Delete(pathParams);
return CHIP_ERROR_NO_MEMORY;
}
ScopedNodeId peer(info.nodeId, info.fabricIndex);
CHIP_ERROR err = readClient->SendAutoResubscribeRequest(peer, std::move(readParams));
if (err != CHIP_NO_ERROR)
{
// On failure, ReadClient has already invoked OnDeallocatePaths, which freed pathParams.
Platform::Delete(readClient);
return err;
}
mSlots[slot].client = readClient;
mSlots[slot].info = info;
if (outHandle != nullptr)
{
*outHandle = slot;
}
return CHIP_NO_ERROR;
}
uint8_t SubscriptionManager::ActiveCount() const
{
uint8_t count = 0;
for (uint8_t i = 0; i < kMaxSubscriptions; ++i)
{
if (mSlots[i].client != nullptr)
{
++count;
}
}
return count;
}
bool SubscriptionManager::IsActive(Handle handle) const
{
return handle < kMaxSubscriptions && mSlots[handle].client != nullptr;
}
const SubscriptionManager::Info * SubscriptionManager::GetInfo(Handle handle) const
{
if (!IsActive(handle))
{
return nullptr;
}
return &mSlots[handle].info;
}
// ---------------------------------------------------------------------------
// Internal helpers
// ---------------------------------------------------------------------------
SubscriptionManager::Handle SubscriptionManager::FindFreeSlot()
{
for (Handle i = 0; i < kMaxSubscriptions; ++i)
{
if (mSlots[i].client == nullptr)
{
return i;
}
}
return kInvalidHandle;
}
void SubscriptionManager::ReleaseSlot(Handle handle)
{
if (handle >= kMaxSubscriptions)
{
return;
}
mSlots[handle].client = nullptr;
mSlots[handle].info = Info{};
}
// ---------------------------------------------------------------------------
// SlotCallback
// ---------------------------------------------------------------------------
void SubscriptionManager::SlotCallback::Init(SubscriptionManager * owner, Handle handle)
{
mOwner = owner;
mHandle = handle;
}
void SubscriptionManager::SlotCallback::OnAttributeData(const ConcreteDataAttributePath & path, chip::TLV::TLVReader * data,
const StatusIB & status)
{
if (mOwner == nullptr || mOwner->mDelegate == nullptr)
{
return;
}
mOwner->mDelegate->OnAttributeData(mHandle, mOwner->mSlots[mHandle].info, path, data, status);
}
void SubscriptionManager::SlotCallback::OnSubscriptionEstablished(chip::SubscriptionId subscriptionId)
{
if (mOwner == nullptr || mOwner->mDelegate == nullptr)
{
return;
}
mOwner->mDelegate->OnSubscriptionEstablished(mHandle, mOwner->mSlots[mHandle].info, subscriptionId);
}
void SubscriptionManager::SlotCallback::OnError(CHIP_ERROR error)
{
if (mOwner == nullptr || mOwner->mDelegate == nullptr)
{
return;
}
mOwner->mDelegate->OnError(mHandle, mOwner->mSlots[mHandle].info, error);
}
void SubscriptionManager::SlotCallback::OnDone(ReadClient * client)
{
if (mOwner == nullptr)
{
return;
}
Info info = mOwner->mSlots[mHandle].info;
mOwner->ReleaseSlot(mHandle);
if (mOwner->mDelegate != nullptr)
{
mOwner->mDelegate->OnSubscriptionTerminated(mHandle, info);
}
Platform::Delete(client);
}
void SubscriptionManager::SlotCallback::OnDeallocatePaths(ReadPrepareParams && readPrepareParams)
{
if (readPrepareParams.mpAttributePathParamsList != nullptr)
{
Platform::Delete(readPrepareParams.mpAttributePathParamsList);
readPrepareParams.mpAttributePathParamsList = nullptr;
readPrepareParams.mAttributePathParamsListSize = 0;
}
}
} // namespace Silabs
} // namespace chip