Skip to content

Commit 440814d

Browse files
committed
Address PR comments
1 parent dc35725 commit 440814d

9 files changed

+173
-120
lines changed

src/app/BUILD.gn

+1-1
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ static_library("app") {
271271
"ChunkedWriteCallback.cpp",
272272
"ChunkedWriteCallback.h",
273273
"CommandHandler.cpp",
274-
"CommandResponderInterface.h",
274+
"CommandHandlerExchangeInterface.h",
275275
"CommandResponseHelper.h",
276276
"CommandResponseSender.cpp",
277277
"DefaultAttributePersistenceProvider.cpp",

src/app/CommandHandler.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ CHIP_ERROR CommandHandler::AllocateBuffer()
9999
return CHIP_NO_ERROR;
100100
}
101101

102-
Status CommandHandler::OnInvokeCommandRequest(CommandResponderInterface & commandResponder, System::PacketBufferHandle && payload,
102+
Status CommandHandler::OnInvokeCommandRequest(CommandHandlerExchangeInterface & commandResponder, System::PacketBufferHandle && payload,
103103
bool isTimedInvoke)
104104
{
105105
VerifyOrDieWithMsg(mState == State::Idle, DataManagement, "state should be Idle");
@@ -845,7 +845,7 @@ CHIP_ERROR CommandHandler::FinalizeInvokeResponseMessage(bool aHasMoreChunks)
845845
return CHIP_NO_ERROR;
846846
}
847847

848-
void CommandHandler::SetCommandResponder(CommandResponderInterface * commandResponder)
848+
void CommandHandler::SetCommandResponder(CommandHandlerExchangeInterface * commandResponder)
849849
{
850850
VerifyOrDieWithMsg(mState == State::Idle, DataManagement, "CommandResponseSender can only be set in idle state");
851851
mpResponder = commandResponder;
@@ -923,7 +923,7 @@ CHIP_ERROR TestOnlyExtractCommandPathFromNextInvokeRequest(TLV::TLVReader & invo
923923
// This method intentionally duplicates code from other sections. While code consolidation
924924
// is generally preferred, here we prioritize generating a clear crash message to aid in
925925
// troubleshooting test failures.
926-
void CommandHandler::TestOnlyInvokeCommandRequestWithFaultsInjected(CommandResponderInterface & commandResponder,
926+
void CommandHandler::TestOnlyInvokeCommandRequestWithFaultsInjected(CommandHandlerExchangeInterface & commandResponder,
927927
System::PacketBufferHandle && payload, bool isTimedInvoke,
928928
NlFaultInjectionType faultType)
929929
{

src/app/CommandHandler.h

+7-7
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
#include "CommandPathRegistry.h"
3434

35-
#include <app/CommandResponderInterface.h>
35+
#include <app/CommandHandlerExchangeInterface.h>
3636
#include <app/ConcreteCommandPath.h>
3737
#include <app/data-model/Encode.h>
3838
#include <lib/core/CHIPCore.h>
@@ -181,7 +181,7 @@ class CommandHandler
181181
{
182182
public:
183183
CommandPathRegistry * commandPathRegistry = nullptr;
184-
CommandResponderInterface * commandResponder = nullptr;
184+
CommandHandlerExchangeInterface * commandResponder = nullptr;
185185
};
186186

187187
/*
@@ -216,7 +216,7 @@ class CommandHandler
216216
* command responder object must outlive this CommandHandler object. It is only safe to
217217
* release after client recieved OnDone callback.
218218
*/
219-
Protocols::InteractionModel::Status OnInvokeCommandRequest(CommandResponderInterface & commandResponder,
219+
Protocols::InteractionModel::Status OnInvokeCommandRequest(CommandHandlerExchangeInterface & commandResponder,
220220
System::PacketBufferHandle && payload, bool isTimedInvoke);
221221

222222
/**
@@ -386,7 +386,7 @@ class CommandHandler
386386
{
387387
if (mpResponder)
388388
{
389-
mpResponder->FlushAcksRightNow();
389+
mpResponder->HandlingSlowCommand();
390390
}
391391
}
392392

@@ -428,7 +428,7 @@ class CommandHandler
428428
*/
429429
// TODO(#30453): After refactoring CommandHandler for better unit testability, create a
430430
// unit test specifically for the fault injection behavior.
431-
void TestOnlyInvokeCommandRequestWithFaultsInjected(CommandResponderInterface & commandResponder,
431+
void TestOnlyInvokeCommandRequestWithFaultsInjected(CommandHandlerExchangeInterface & commandResponder,
432432
System::PacketBufferHandle && payload, bool isTimedInvoke,
433433
NlFaultInjectionType faultType);
434434
#endif // CHIP_WITH_NLFAULTINJECTION
@@ -654,7 +654,7 @@ class CommandHandler
654654
return FinishCommand(/* aEndDataStruct = */ false);
655655
}
656656

657-
void SetCommandResponder(CommandResponderInterface * commandResponder);
657+
void SetCommandResponder(CommandHandlerExchangeInterface * commandResponder);
658658

659659
/**
660660
* Check whether the InvokeRequest we are handling is targeted to a group.
@@ -688,7 +688,7 @@ class CommandHandler
688688
CommandPathRegistry * mCommandPathRegistry = &mBasicCommandPathRegistry;
689689
Optional<uint16_t> mRefForResponse;
690690

691-
CommandResponderInterface * mpResponder = nullptr;
691+
CommandHandlerExchangeInterface * mpResponder = nullptr;
692692

693693
State mState = State::Idle;
694694
State mBackupState;
+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright (c) 2024 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+
#pragma once
19+
20+
#include <access/SubjectDescriptor.h>
21+
#include <lib/core/DataModelTypes.h>
22+
#include <lib/core/GroupId.h>
23+
#include <lib/core/Optional.h>
24+
#include <messaging/ExchangeContext.h>
25+
#include <protocols/interaction_model/StatusCode.h>
26+
#include <system/SystemPacketBuffer.h>
27+
28+
namespace chip {
29+
namespace app {
30+
31+
/**
32+
* Interface for sending InvokeResponseMessage(s).
33+
*
34+
* Provides information about the associated exchange context.
35+
*
36+
* Design Rationale: This interface enhances unit testability and allows applications to
37+
* customize InvokeResponse behavior. For example, a bridge application might locally execute
38+
* a command using `emberAf...` methods without intending to sending a response on the exchange.
39+
* These ember methods require providing an instance of CommandHandler where a status response
40+
* is added (see github.com/project-chip/connectedhomeip/issues/32030).
41+
*/
42+
class CommandHandlerExchangeInterface
43+
{
44+
public:
45+
virtual ~CommandHandlerExchangeInterface() = default;
46+
47+
/**
48+
* Gets the inner exchange context object associated with incoming
49+
* InvokeRequestMessage, without ownership.
50+
*
51+
* @return The inner exchange context, might be nullptr if no
52+
* exchange context has been assigned or the context
53+
* has been released.
54+
*/
55+
virtual Messaging::ExchangeContext * GetExchangeContext() const = 0;
56+
57+
// TODO(#30453): Follow up refactor. It should be possible to remove
58+
// GetSubjectDescriptor and GetAccessingFabricIndex, as CommandHandler can get these
59+
// values from ExchangeContext.
60+
61+
/**
62+
* Gets subject descriptor of the exchange.
63+
*
64+
* WARNING: This method should only be called when the caller is certain the
65+
* session has not been evicted.
66+
*/
67+
virtual Access::SubjectDescriptor GetSubjectDescriptor() const = 0;
68+
69+
/**
70+
* Gets accessing fabic index of the exchange.
71+
*
72+
* WARNING: This method should only be called when the caller is certain the
73+
* session has not been evicted.
74+
*/
75+
virtual FabricIndex GetAccessingFabricIndex() const = 0;
76+
/**
77+
* If the incoming session is a group session, returns its group ID. Otherwise,
78+
* returns a null optional.
79+
*/
80+
virtual Optional<GroupId> GetGroupId() const = 0;
81+
82+
/**
83+
* @brief Called to indicates slow command is being processed.
84+
*
85+
* Facilitates communication on the exchange (if needed) that slow command is being
86+
* processed. For example, sending a standalone ack when using MRP.
87+
*/
88+
virtual void HandlingSlowCommand() = 0;
89+
90+
/**
91+
* @brief Adds a completed InvokeResponseMessage to the queue for sending to requester.
92+
*
93+
* Called by CommandHandler.
94+
*/
95+
virtual void AddInvokeResponseToSend(System::PacketBufferHandle && aPacket) = 0;
96+
/**
97+
* @brief Called to indicate that an InvokeResponse was dropped.
98+
*
99+
* Called by CommandHandler to relay this information to the requester.
100+
*/
101+
virtual void ResponseDropped() = 0;
102+
};
103+
104+
} // namespace app
105+
} // namespace chip

src/app/CommandResponderInterface.h

-64
This file was deleted.

src/app/CommandResponseSender.cpp

+36-10
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ CHIP_ERROR CommandResponseSender::OnMessageReceived(Messaging::ExchangeContext *
3939
VerifyOrExit(err == CHIP_NO_ERROR, failureStatusToSend.SetValue(Status::InvalidAction));
4040

4141
err = SendCommandResponse();
42-
// If SendCommandResponse() fails, we must still close the exchange. Since sending an
43-
// InvokeResponseMessage seems problematic, we'll send a StatusResponse with 'Failure'
44-
// instead.
42+
// If SendCommandResponse() fails, we must close the exchange. We signal the failure to the
43+
// requester with a StatusResponse ('Failure'). Since we're in the middle of processing an
44+
// incoming message, we close the exchange by indicating that we don't expect a further response.
4545
VerifyOrExit(err == CHIP_NO_ERROR, failureStatusToSend.SetValue(Status::Failure));
4646

4747
bool moreToSend = !mChunks.IsNull();
@@ -76,8 +76,6 @@ void CommandResponseSender::OnResponseTimeout(Messaging::ExchangeContext * apExc
7676
{
7777
ChipLogDetail(DataManagement, "CommandResponseSender: Timed out waiting for response from requester mState=[%10.10s]",
7878
GetStateStr());
79-
// We don't need to consider remaining async CommandHandlers here. We become the exchange
80-
// delegate only after CommandHandler::OnDone is called.
8179
Close();
8280
}
8381

@@ -99,9 +97,9 @@ void CommandResponseSender::StartSendingCommandResponses()
9997
// {
10098
// SendStatusResponse(Status::Failure);
10199
// }
102-
// Close();
103-
// return;
104100
// ```
101+
Close();
102+
return;
105103
}
106104

107105
if (HasMoreToSend())
@@ -115,6 +113,31 @@ void CommandResponseSender::StartSendingCommandResponses()
115113
}
116114
}
117115

116+
void CommandResponseSender::OnDone(CommandHandler & apCommandObj)
117+
{
118+
if (mState == State::ErrorSentDelayCloseUntilOnDone)
119+
{
120+
// We have already sent a message to the client indicating that we are not expecting
121+
// a response.
122+
Close();
123+
return;
124+
}
125+
StartSendingCommandResponses();
126+
}
127+
128+
void CommandResponseSender::DispatchCommand(CommandHandler & apCommandObj, const ConcreteCommandPath & aCommandPath,
129+
TLV::TLVReader & apPayload)
130+
{
131+
VerifyOrReturn(mpCommandHandlerCallback);
132+
mpCommandHandlerCallback->DispatchCommand(apCommandObj, aCommandPath, apPayload);
133+
}
134+
135+
Status CommandResponseSender::CommandExists(const ConcreteCommandPath & aCommandPath)
136+
{
137+
VerifyOrReturnValue(mpCommandHandlerCallback, Protocols::InteractionModel::Status::UnsupportedCommand);
138+
return mpCommandHandlerCallback->CommandExists(aCommandPath);
139+
}
140+
118141
CHIP_ERROR CommandResponseSender::SendCommandResponse()
119142
{
120143
VerifyOrReturnError(HasMoreToSend(), CHIP_ERROR_INCORRECT_STATE);
@@ -153,6 +176,9 @@ const char * CommandResponseSender::GetStateStr() const
153176

154177
case State::AllInvokeResponsesSent:
155178
return "AllInvokeResponsesSent";
179+
180+
case State::ErrorSentDelayCloseUntilOnDone:
181+
return "ErrorSentDelayCloseUntilOnDone";
156182
}
157183
#endif // CHIP_DETAIL_LOGGING
158184
return "N/A";
@@ -185,8 +211,8 @@ void CommandResponseSender::OnInvokeCommandRequest(Messaging::ExchangeContext *
185211
mExchangeCtx.Grab(ec);
186212
mExchangeCtx->WillSendMessage();
187213

188-
// Grabbing Handle to prevent OnDone from being called before OnInvokeCommandRequest returns.
189-
// This allows us to send a StatusResponse error instead of the queued up InvokeResponseMessages.
214+
// Grabbing Handle to prevent mCommandHandler from calling OnDone before OnInvokeCommandRequest returns.
215+
// This allows us to send a StatusResponse error instead of any potentially queued up InvokeResponseMessages.
190216
CommandHandler::Handle workHandle(&mCommandHandler);
191217
Status status = mCommandHandler.OnInvokeCommandRequest(*this, std::move(payload), isTimedInvoke);
192218
if (status != Status::Success)
@@ -196,7 +222,7 @@ void CommandResponseSender::OnInvokeCommandRequest(Messaging::ExchangeContext *
196222
// The API contract of OnInvokeCommandRequest requires the CommandResponder instance to outlive
197223
// the CommandHandler. Therefore, we cannot safely call Close() here, even though we have
198224
// finished sending data. Closing must be deferred until the CommandHandler::OnDone callback.
199-
mDelayCallingCloseUntilOnDone = true;
225+
MoveToState(State::ErrorSentDelayCloseUntilOnDone);
200226
}
201227
}
202228

0 commit comments

Comments
 (0)