blob: c57c3417331ec4ad1a715d3001a1e2ea01738f85 [file] [log] [blame]
/*
*
* Copyright (c) 2021 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.
*/
/**
* @file
* This file defines the initiator side of a CHIP Read Interaction.
*
*/
#include <app/AppBuildConfig.h>
#include <app/InteractionModelEngine.h>
#include <app/ReadClient.h>
namespace chip {
namespace app {
CHIP_ERROR ReadClient::Init(Messaging::ExchangeManager * apExchangeMgr, Callback * apCallback, InteractionType aInteractionType)
{
CHIP_ERROR err = CHIP_NO_ERROR;
// Error if already initialized.
VerifyOrExit(IsFree(), err = CHIP_ERROR_INCORRECT_STATE);
VerifyOrExit(apExchangeMgr != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
VerifyOrExit(mpExchangeMgr == nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
mpExchangeMgr = apExchangeMgr;
mpCallback = apCallback;
mState = ClientState::Initialized;
mMinIntervalFloorSeconds = 0;
mMaxIntervalCeilingSeconds = 0;
mSubscriptionId = 0;
mInitialReport = true;
mInteractionType = aInteractionType;
AbortExistingExchangeContext();
exit:
return err;
}
void ReadClient::Shutdown()
{
AbortExistingExchangeContext();
ShutdownInternal(CHIP_NO_ERROR);
}
void ReadClient::ShutdownInternal(CHIP_ERROR aError)
{
if (mpCallback != nullptr)
{
if (aError != CHIP_NO_ERROR)
{
mpCallback->OnError(this, aError);
}
mpCallback->OnDone(this);
mpCallback = nullptr;
}
if (IsSubscriptionType())
{
CancelLivenessCheckTimer();
}
mMinIntervalFloorSeconds = 0;
mMaxIntervalCeilingSeconds = 0;
mSubscriptionId = 0;
mInteractionType = InteractionType::Read;
mpExchangeMgr = nullptr;
mpExchangeCtx = nullptr;
mInitialReport = true;
mPeerNodeId = kUndefinedNodeId;
mFabricIndex = kUndefinedFabricIndex;
MoveToState(ClientState::Uninitialized);
}
const char * ReadClient::GetStateStr() const
{
#if CHIP_DETAIL_LOGGING
switch (mState)
{
case ClientState::Uninitialized:
return "UNINIT";
case ClientState::Initialized:
return "INIT";
case ClientState::AwaitingInitialReport:
return "AwaitingInitialReport";
case ClientState::AwaitingSubscribeResponse:
return "AwaitingSubscribeResponse";
case ClientState::SubscriptionActive:
return "SubscriptionActive";
}
#endif // CHIP_DETAIL_LOGGING
return "N/A";
}
void ReadClient::MoveToState(const ClientState aTargetState)
{
mState = aTargetState;
ChipLogDetail(DataManagement, "Client[%u] moving to [%s]", InteractionModelEngine::GetInstance()->GetReadClientArrayIndex(this),
GetStateStr());
}
CHIP_ERROR ReadClient::SendReadRequest(ReadPrepareParams & aReadPrepareParams)
{
// TODO: SendRequest parameter is too long, need to have the structure to represent it
CHIP_ERROR err = CHIP_NO_ERROR;
System::PacketBufferHandle msgBuf;
ChipLogDetail(DataManagement, "%s: Client[%u] [%5.5s]", __func__,
InteractionModelEngine::GetInstance()->GetReadClientArrayIndex(this), GetStateStr());
VerifyOrExit(ClientState::Initialized == mState, err = CHIP_ERROR_INCORRECT_STATE);
VerifyOrExit(mpCallback != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
// Discard any existing exchange context. Effectively we can only have one exchange per ReadClient
// at any one time.
AbortExistingExchangeContext();
{
System::PacketBufferTLVWriter writer;
ReadRequestMessage::Builder request;
msgBuf = System::PacketBufferHandle::New(kMaxSecureSduLengthBytes);
VerifyOrExit(!msgBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
writer.Init(std::move(msgBuf));
err = request.Init(&writer);
SuccessOrExit(err);
if (aReadPrepareParams.mEventPathParamsListSize != 0 && aReadPrepareParams.mpEventPathParamsList != nullptr)
{
EventPaths::Builder & eventPathListBuilder = request.CreateEventPathsBuilder();
SuccessOrExit(err = eventPathListBuilder.GetError());
err = GenerateEventPaths(eventPathListBuilder, aReadPrepareParams.mpEventPathParamsList,
aReadPrepareParams.mEventPathParamsListSize);
SuccessOrExit(err);
if (aReadPrepareParams.mEventNumber != 0)
{
// EventNumber is optional
request.EventNumber(aReadPrepareParams.mEventNumber);
}
}
if (aReadPrepareParams.mAttributePathParamsListSize != 0 && aReadPrepareParams.mpAttributePathParamsList != nullptr)
{
AttributePathIBs::Builder attributePathListBuilder = request.CreateAttributePathListBuilder();
SuccessOrExit(err = attributePathListBuilder.GetError());
err = GenerateAttributePathList(attributePathListBuilder, aReadPrepareParams.mpAttributePathParamsList,
aReadPrepareParams.mAttributePathParamsListSize);
SuccessOrExit(err);
}
request.EndOfReadRequestMessage();
SuccessOrExit(err = request.GetError());
err = writer.Finalize(&msgBuf);
SuccessOrExit(err);
}
mpExchangeCtx = mpExchangeMgr->NewContext(aReadPrepareParams.mSessionHandle, this);
VerifyOrExit(mpExchangeCtx != nullptr, err = CHIP_ERROR_NO_MEMORY);
mpExchangeCtx->SetResponseTimeout(aReadPrepareParams.mTimeout);
err = mpExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::ReadRequest, std::move(msgBuf),
Messaging::SendFlags(Messaging::SendMessageFlags::kExpectResponse));
SuccessOrExit(err);
mPeerNodeId = aReadPrepareParams.mSessionHandle.GetPeerNodeId();
mFabricIndex = aReadPrepareParams.mSessionHandle.GetFabricIndex();
MoveToState(ClientState::AwaitingInitialReport);
exit:
if (err != CHIP_NO_ERROR)
{
AbortExistingExchangeContext();
}
return err;
}
CHIP_ERROR ReadClient::SendStatusResponse(CHIP_ERROR aError)
{
using Protocols::InteractionModel::Status;
System::PacketBufferHandle msgBuf = System::PacketBufferHandle::New(kMaxSecureSduLengthBytes);
VerifyOrReturnLogError(!msgBuf.IsNull(), CHIP_ERROR_NO_MEMORY);
System::PacketBufferTLVWriter writer;
writer.Init(std::move(msgBuf));
StatusResponseMessage::Builder response;
ReturnLogErrorOnFailure(response.Init(&writer));
Status statusCode = Status::Success;
if (aError != CHIP_NO_ERROR)
{
statusCode = Status::InvalidSubscription;
}
response.Status(statusCode);
ReturnLogErrorOnFailure(response.GetError());
ReturnLogErrorOnFailure(writer.Finalize(&msgBuf));
VerifyOrReturnLogError(mpExchangeCtx != nullptr, CHIP_ERROR_INCORRECT_STATE);
if (IsSubscriptionType())
{
if (IsAwaitingInitialReport())
{
MoveToState(ClientState::AwaitingSubscribeResponse);
}
else
{
RefreshLivenessCheckTimer();
}
}
ReturnLogErrorOnFailure(
mpExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::StatusResponse, std::move(msgBuf),
Messaging::SendFlags(IsAwaitingSubscribeResponse() ? Messaging::SendMessageFlags::kExpectResponse
: Messaging::SendMessageFlags::kNone)));
return CHIP_NO_ERROR;
}
CHIP_ERROR ReadClient::GenerateEventPaths(EventPaths::Builder & aEventPathsBuilder, EventPathParams * apEventPathParamsList,
size_t aEventPathParamsListSize)
{
CHIP_ERROR err = CHIP_NO_ERROR;
for (size_t eventIndex = 0; eventIndex < aEventPathParamsListSize; ++eventIndex)
{
EventPathIB::Builder eventPathBuilder = aEventPathsBuilder.CreatePath();
EventPathParams eventPath = apEventPathParamsList[eventIndex];
eventPathBuilder.Node(eventPath.mNodeId)
.Event(eventPath.mEventId)
.Endpoint(eventPath.mEndpointId)
.Cluster(eventPath.mClusterId)
.EndOfEventPathIB();
SuccessOrExit(err = eventPathBuilder.GetError());
}
aEventPathsBuilder.EndOfEventPaths();
SuccessOrExit(err = aEventPathsBuilder.GetError());
exit:
return err;
}
CHIP_ERROR ReadClient::GenerateAttributePathList(AttributePathIBs::Builder & aAttributePathIBsBuilder,
AttributePathParams * apAttributePathParamsList,
size_t aAttributePathParamsListSize)
{
for (size_t index = 0; index < aAttributePathParamsListSize; index++)
{
VerifyOrReturnError(apAttributePathParamsList[index].IsValidAttributePath(), CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH);
ReturnErrorOnFailure(apAttributePathParamsList[index].BuildAttributePath(aAttributePathIBsBuilder.CreateAttributePath()));
}
aAttributePathIBsBuilder.EndOfAttributePathIBs();
return aAttributePathIBsBuilder.GetError();
}
CHIP_ERROR ReadClient::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
System::PacketBufferHandle && aPayload)
{
CHIP_ERROR err = CHIP_NO_ERROR;
VerifyOrExit(!IsFree(), err = CHIP_ERROR_INCORRECT_STATE);
VerifyOrExit(mpCallback != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::ReportData))
{
err = ProcessReportData(std::move(aPayload));
SuccessOrExit(err);
}
else if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::SubscribeResponse))
{
VerifyOrExit(apExchangeContext == mpExchangeCtx, err = CHIP_ERROR_INCORRECT_STATE);
err = ProcessSubscribeResponse(std::move(aPayload));
// Forget the context as SUBSCRIBE RESPONSE is the last message in SUBSCRIBE transaction and
// ExchangeContext::HandleMessage automatically closes a context if no other messages need to
// be sent or received.
mpExchangeCtx = nullptr;
SuccessOrExit(err);
}
else
{
err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
}
exit:
if (!IsSubscriptionType() || err != CHIP_NO_ERROR)
{
ShutdownInternal(err);
}
return err;
}
CHIP_ERROR ReadClient::AbortExistingExchangeContext()
{
if (mpExchangeCtx != nullptr)
{
mpExchangeCtx->Abort();
mpExchangeCtx = nullptr;
}
return CHIP_NO_ERROR;
}
CHIP_ERROR ReadClient::OnUnsolicitedReportData(Messaging::ExchangeContext * apExchangeContext,
System::PacketBufferHandle && aPayload)
{
mpExchangeCtx = apExchangeContext;
CHIP_ERROR err = ProcessReportData(std::move(aPayload));
mpExchangeCtx = nullptr;
if (err != CHIP_NO_ERROR)
{
ShutdownInternal(err);
}
return err;
}
CHIP_ERROR ReadClient::ProcessReportData(System::PacketBufferHandle && aPayload)
{
CHIP_ERROR err = CHIP_NO_ERROR;
ReportDataMessage::Parser report;
bool isEventReportsPresent = false;
bool isAttributeReportIBsPresent = false;
bool suppressResponse = false;
bool moreChunkedMessages = false;
uint64_t subscriptionId = 0;
EventReports::Parser EventReports;
AttributeReportIBs::Parser attributeReportIBs;
System::PacketBufferTLVReader reader;
reader.Init(std::move(aPayload));
reader.Next();
err = report.Init(reader);
SuccessOrExit(err);
#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK
err = report.CheckSchemaValidity();
SuccessOrExit(err);
#endif
err = report.GetSuppressResponse(&suppressResponse);
if (CHIP_END_OF_TLV == err)
{
err = CHIP_NO_ERROR;
}
SuccessOrExit(err);
err = report.GetSubscriptionId(&subscriptionId);
if (CHIP_NO_ERROR == err)
{
if (IsInitialReport())
{
mSubscriptionId = subscriptionId;
}
else if (!IsMatchingClient(subscriptionId))
{
err = CHIP_ERROR_INVALID_ARGUMENT;
}
}
else if (CHIP_END_OF_TLV == err)
{
if (IsSubscriptionType())
{
err = CHIP_ERROR_INVALID_ARGUMENT;
}
else
{
err = CHIP_NO_ERROR;
}
}
SuccessOrExit(err);
err = report.GetMoreChunkedMessages(&moreChunkedMessages);
if (CHIP_END_OF_TLV == err)
{
err = CHIP_NO_ERROR;
}
SuccessOrExit(err);
err = report.GetEventReports(&EventReports);
isEventReportsPresent = (err == CHIP_NO_ERROR);
if (err == CHIP_END_OF_TLV)
{
err = CHIP_NO_ERROR;
}
SuccessOrExit(err);
if (isEventReportsPresent && nullptr != mpCallback)
{
chip::TLV::TLVReader EventReportsReader;
EventReports.GetReader(&EventReportsReader);
mpCallback->OnEventData(this, EventReportsReader);
}
err = report.GetAttributeReportIBs(&attributeReportIBs);
isAttributeReportIBsPresent = (err == CHIP_NO_ERROR);
if (err == CHIP_END_OF_TLV)
{
err = CHIP_NO_ERROR;
}
SuccessOrExit(err);
if (isAttributeReportIBsPresent && nullptr != mpCallback && !moreChunkedMessages)
{
TLV::TLVReader attributeReportIBsReader;
attributeReportIBs.GetReader(&attributeReportIBsReader);
err = ProcessAttributeReportIBs(attributeReportIBsReader);
SuccessOrExit(err);
}
if (!suppressResponse)
{
// TODO: Add status report support and correspond handler in ReadHandler, particular for situation when there
// are multiple reports
}
exit:
SendStatusResponse(err);
if (!mInitialReport)
{
mpExchangeCtx = nullptr;
}
mInitialReport = false;
return err;
}
void ReadClient::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext)
{
ChipLogProgress(DataManagement, "Time out! failed to receive report data from Exchange: " ChipLogFormatExchange,
ChipLogValueExchange(apExchangeContext));
ShutdownInternal(CHIP_ERROR_TIMEOUT);
}
CHIP_ERROR ReadClient::ProcessAttributePath(AttributePathIB::Parser & aAttributePath, ClusterInfo & aClusterInfo)
{
CHIP_ERROR err = aAttributePath.GetNode(&(aClusterInfo.mNodeId));
if (err == CHIP_END_OF_TLV)
{
err = CHIP_NO_ERROR;
}
VerifyOrReturnError(err == CHIP_NO_ERROR, err = CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH);
// The ReportData must contain a concrete attribute path
err = aAttributePath.GetEndpoint(&(aClusterInfo.mEndpointId));
VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH);
err = aAttributePath.GetCluster(&(aClusterInfo.mClusterId));
VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH);
err = aAttributePath.GetAttribute(&(aClusterInfo.mAttributeId));
VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH);
err = aAttributePath.GetListIndex(&(aClusterInfo.mListIndex));
if (CHIP_END_OF_TLV == err)
{
err = CHIP_NO_ERROR;
}
VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH);
VerifyOrReturnError(aClusterInfo.IsValidAttributePath(), CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH);
return CHIP_NO_ERROR;
}
CHIP_ERROR ReadClient::ProcessAttributeReportIBs(TLV::TLVReader & aAttributeReportIBsReader)
{
CHIP_ERROR err = CHIP_NO_ERROR;
while (CHIP_NO_ERROR == (err = aAttributeReportIBsReader.Next()))
{
TLV::TLVReader dataReader;
AttributeReportIB::Parser report;
AttributeDataIB::Parser data;
AttributeStatusIB::Parser status;
AttributePathIB::Parser path;
ClusterInfo clusterInfo;
StatusIB statusIB;
TLV::TLVReader reader = aAttributeReportIBsReader;
ReturnErrorOnFailure(report.Init(reader));
err = report.GetAttributeStatus(&status);
if (CHIP_NO_ERROR == err)
{
StatusIB::Parser errorStatus;
ReturnErrorOnFailure(status.GetPath(&path));
ReturnErrorOnFailure(ProcessAttributePath(path, clusterInfo));
ReturnErrorOnFailure(status.GetErrorStatus(&errorStatus));
ReturnErrorOnFailure(errorStatus.DecodeStatusIB(statusIB));
mpCallback->OnAttributeData(
this, ConcreteAttributePath(clusterInfo.mEndpointId, clusterInfo.mClusterId, clusterInfo.mAttributeId), nullptr,
statusIB);
}
else if (CHIP_END_OF_TLV == err)
{
ReturnErrorOnFailure(report.GetAttributeData(&data));
ReturnErrorOnFailure(data.GetPath(&path));
ReturnErrorOnFailure(ProcessAttributePath(path, clusterInfo));
ReturnErrorOnFailure(data.GetData(&dataReader));
mpCallback->OnAttributeData(
this, ConcreteAttributePath(clusterInfo.mEndpointId, clusterInfo.mClusterId, clusterInfo.mAttributeId), &dataReader,
statusIB);
}
}
if (CHIP_END_OF_TLV == err)
{
err = CHIP_NO_ERROR;
}
return err;
}
CHIP_ERROR ReadClient::RefreshLivenessCheckTimer()
{
CancelLivenessCheckTimer();
ChipLogProgress(DataManagement, "Refresh LivenessCheckTime with %d seconds", mMaxIntervalCeilingSeconds);
CHIP_ERROR err = InteractionModelEngine::GetInstance()->GetExchangeManager()->GetSessionManager()->SystemLayer()->StartTimer(
System::Clock::Seconds16(mMaxIntervalCeilingSeconds), OnLivenessTimeoutCallback, this);
if (err != CHIP_NO_ERROR)
{
ShutdownInternal(err);
}
return err;
}
void ReadClient::CancelLivenessCheckTimer()
{
InteractionModelEngine::GetInstance()->GetExchangeManager()->GetSessionManager()->SystemLayer()->CancelTimer(
OnLivenessTimeoutCallback, this);
}
void ReadClient::OnLivenessTimeoutCallback(System::Layer * apSystemLayer, void * apAppState)
{
ReadClient * const client = reinterpret_cast<ReadClient *>(apAppState);
if (client->IsFree())
{
ChipLogError(DataManagement,
"ReadClient::OnLivenessTimeoutCallback invoked on a free client! This is a bug in CHIP stack!");
return;
}
ChipLogError(DataManagement, "Subscription Liveness timeout with peer node 0x%" PRIx64 ", shutting down ", client->mPeerNodeId);
client->mpExchangeCtx = nullptr;
// TODO: add a more specific error here for liveness timeout failure to distinguish between other classes of timeouts (i.e
// response timeouts).
client->ShutdownInternal(CHIP_ERROR_TIMEOUT);
}
CHIP_ERROR ReadClient::ProcessSubscribeResponse(System::PacketBufferHandle && aPayload)
{
System::PacketBufferTLVReader reader;
reader.Init(std::move(aPayload));
ReturnLogErrorOnFailure(reader.Next());
SubscribeResponseMessage::Parser subscribeResponse;
ReturnLogErrorOnFailure(subscribeResponse.Init(reader));
#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK
ReturnLogErrorOnFailure(subscribeResponse.CheckSchemaValidity());
#endif
uint64_t subscriptionId = 0;
ReturnLogErrorOnFailure(subscribeResponse.GetSubscriptionId(&subscriptionId));
VerifyOrReturnLogError(IsMatchingClient(subscriptionId), CHIP_ERROR_INVALID_ARGUMENT);
ReturnLogErrorOnFailure(subscribeResponse.GetMinIntervalFloorSeconds(&mMinIntervalFloorSeconds));
ReturnLogErrorOnFailure(subscribeResponse.GetMaxIntervalCeilingSeconds(&mMaxIntervalCeilingSeconds));
if (mpCallback != nullptr)
{
mpCallback->OnSubscriptionEstablished(this);
}
MoveToState(ClientState::SubscriptionActive);
return CHIP_NO_ERROR;
}
CHIP_ERROR ReadClient::SendSubscribeRequest(ReadPrepareParams & aReadPrepareParams)
{
CHIP_ERROR err = CHIP_NO_ERROR;
System::PacketBufferHandle msgBuf;
System::PacketBufferTLVWriter writer;
SubscribeRequestMessage::Builder request;
VerifyOrExit(ClientState::Initialized == mState, err = CHIP_ERROR_INCORRECT_STATE);
VerifyOrExit(mpExchangeCtx == nullptr, err = CHIP_ERROR_INCORRECT_STATE);
VerifyOrExit(mpCallback != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
msgBuf = System::PacketBufferHandle::New(kMaxSecureSduLengthBytes);
VerifyOrExit(!msgBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
writer.Init(std::move(msgBuf));
err = request.Init(&writer);
SuccessOrExit(err);
if (aReadPrepareParams.mEventPathParamsListSize != 0 && aReadPrepareParams.mpEventPathParamsList != nullptr)
{
EventPaths::Builder & eventPathListBuilder = request.CreateEventPathsBuilder();
SuccessOrExit(err = eventPathListBuilder.GetError());
err = GenerateEventPaths(eventPathListBuilder, aReadPrepareParams.mpEventPathParamsList,
aReadPrepareParams.mEventPathParamsListSize);
SuccessOrExit(err);
if (aReadPrepareParams.mEventNumber != 0)
{
// EventNumber is optional
request.EventNumber(aReadPrepareParams.mEventNumber);
}
}
if (aReadPrepareParams.mAttributePathParamsListSize != 0 && aReadPrepareParams.mpAttributePathParamsList != nullptr)
{
AttributePathIBs::Builder & attributePathListBuilder = request.CreateAttributePathListBuilder();
SuccessOrExit(err = attributePathListBuilder.GetError());
err = GenerateAttributePathList(attributePathListBuilder, aReadPrepareParams.mpAttributePathParamsList,
aReadPrepareParams.mAttributePathParamsListSize);
SuccessOrExit(err);
}
request.MinIntervalSeconds(aReadPrepareParams.mMinIntervalFloorSeconds)
.MaxIntervalSeconds(aReadPrepareParams.mMaxIntervalCeilingSeconds)
.KeepSubscriptions(aReadPrepareParams.mKeepSubscriptions)
.EndOfSubscribeRequestMessage();
SuccessOrExit(err = request.GetError());
err = writer.Finalize(&msgBuf);
SuccessOrExit(err);
mpExchangeCtx = mpExchangeMgr->NewContext(aReadPrepareParams.mSessionHandle, this);
VerifyOrExit(mpExchangeCtx != nullptr, err = CHIP_ERROR_NO_MEMORY);
mpExchangeCtx->SetResponseTimeout(kImMessageTimeout);
err = mpExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::SubscribeRequest, std::move(msgBuf),
Messaging::SendFlags(Messaging::SendMessageFlags::kExpectResponse));
SuccessOrExit(err);
mPeerNodeId = aReadPrepareParams.mSessionHandle.GetPeerNodeId();
mFabricIndex = aReadPrepareParams.mSessionHandle.GetFabricIndex();
MoveToState(ClientState::AwaitingInitialReport);
exit:
if (err != CHIP_NO_ERROR)
{
AbortExistingExchangeContext();
}
if (err != CHIP_NO_ERROR)
{
Shutdown();
}
return err;
}
}; // namespace app
}; // namespace chip