Skip to content

Commit e3cadb8

Browse files
tehampsonrestyled-commitsyunhanw-google
authored andcommitted
Create new ICD Manager in fabric-admin to service KeepActive Command (project-chip#34894)
--------- Co-authored-by: Restyled.io <commits@restyled.io> Co-authored-by: yunhanw <yunhanw@google.com>
1 parent 3d63b55 commit e3cadb8

File tree

9 files changed

+377
-13
lines changed

9 files changed

+377
-13
lines changed

examples/fabric-admin/BUILD.gn

+4
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,12 @@ static_library("fabric-admin-utils") {
6969
"commands/common/Commands.h",
7070
"commands/common/CredentialIssuerCommands.h",
7171
"commands/common/HexConversion.h",
72+
"commands/common/IcdManager.cpp",
73+
"commands/common/IcdManager.h",
7274
"commands/common/RemoteDataModelLogger.cpp",
7375
"commands/common/RemoteDataModelLogger.h",
76+
"commands/common/StayActiveSender.cpp",
77+
"commands/common/StayActiveSender.h",
7478
"commands/fabric-sync/FabricSyncCommand.cpp",
7579
"commands/pairing/DeviceSynchronization.cpp",
7680
"commands/pairing/DeviceSynchronization.h",

examples/fabric-admin/commands/common/CHIPCommand.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "CHIPCommand.h"
2020

21+
#include "IcdManager.h"
2122
#include <controller/CHIPDeviceControllerFactory.h>
2223
#include <credentials/attestation_verifier/FileAttestationTrustStore.h>
2324
#include <lib/core/CHIPConfig.h>
@@ -52,7 +53,6 @@ chip::Credentials::GroupDataProviderImpl CHIPCommand::sGroupDataProvider{ kMaxGr
5253
// All fabrics share the same ICD client storage.
5354
chip::app::DefaultICDClientStorage CHIPCommand::sICDClientStorage;
5455
chip::Crypto::RawKeySessionKeystore CHIPCommand::sSessionKeystore;
55-
chip::app::DefaultCheckInDelegate CHIPCommand::sCheckInDelegate;
5656
chip::app::CheckInHandler CHIPCommand::sCheckInHandler;
5757

5858
namespace {
@@ -148,9 +148,9 @@ CHIP_ERROR CHIPCommand::MaybeSetUpStack()
148148

149149
auto engine = chip::app::InteractionModelEngine::GetInstance();
150150
VerifyOrReturnError(engine != nullptr, CHIP_ERROR_INCORRECT_STATE);
151-
ReturnLogErrorOnFailure(sCheckInDelegate.Init(&sICDClientStorage, engine));
151+
ReturnLogErrorOnFailure(IcdManager::Instance().Init(&sICDClientStorage, engine));
152152
ReturnLogErrorOnFailure(sCheckInHandler.Init(DeviceControllerFactory::GetInstance().GetSystemState()->ExchangeMgr(),
153-
&sICDClientStorage, &sCheckInDelegate, engine));
153+
&sICDClientStorage, &IcdManager::Instance(), engine));
154154

155155
CommissionerIdentity nullIdentity{ kIdentityNull, chip::kUndefinedNodeId };
156156
ReturnLogErrorOnFailure(InitializeCommissioner(nullIdentity, kIdentityNullFabricId));

examples/fabric-admin/commands/common/CHIPCommand.h

-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,6 @@ class CHIPCommand : public Command
168168

169169
static chip::Credentials::GroupDataProviderImpl sGroupDataProvider;
170170
static chip::app::DefaultICDClientStorage sICDClientStorage;
171-
static chip::app::DefaultCheckInDelegate sCheckInDelegate;
172171
static chip::app::CheckInHandler sCheckInHandler;
173172
CredentialIssuerCommands * mCredIssuerCmds;
174173

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
19+
#include "IcdManager.h"
20+
21+
IcdManager IcdManager::sInstance;
22+
23+
IcdManager & IcdManager::Instance()
24+
{
25+
return sInstance;
26+
}
27+
28+
void IcdManager::OnCheckInComplete(const chip::app::ICDClientInfo & clientInfo)
29+
{
30+
DefaultCheckInDelegate::OnCheckInComplete(clientInfo);
31+
if (mDelegate)
32+
{
33+
mDelegate->OnCheckInCompleted(clientInfo);
34+
}
35+
}
36+
37+
void IcdManager::SetDelegate(Delegate * delegate)
38+
{
39+
// To keep IcdManager simple, there is an assumption that there is only ever
40+
// one delegate set and it's lifetime is identical to IcdManager. In the
41+
// future this assumption can change should there be a need, but that will
42+
// require code changes to IcdManager. For now we will crash if someone tries
43+
// to call SetDelegate for a second time or if delegate is non-null.
44+
VerifyOrDie(delegate);
45+
VerifyOrDie(!mDelegate);
46+
mDelegate = delegate;
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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+
19+
#pragma once
20+
21+
#include <app/icd/client/DefaultCheckInDelegate.h>
22+
23+
/**
24+
* @brief Manages check-ins from ICD devices.
25+
*
26+
* Intended to be used as a thin CheckInDelegate. This allows a delegate register
27+
* themselves so they can be aware when ICD device checks-in allowing the
28+
* delegate to interact with the ICD device during the short window that it is
29+
* awake.
30+
*/
31+
class IcdManager : public chip::app::DefaultCheckInDelegate
32+
{
33+
public:
34+
class Delegate
35+
{
36+
public:
37+
virtual ~Delegate() = default;
38+
virtual void OnCheckInCompleted(const chip::app::ICDClientInfo & clientInfo) = 0;
39+
};
40+
41+
static IcdManager & Instance();
42+
void OnCheckInComplete(const chip::app::ICDClientInfo & clientInfo) override;
43+
44+
// There is an assumption delegate assigned only happens once and that it lives
45+
// for the entirety of the lifetime of fabric admin.
46+
void SetDelegate(Delegate * delegate);
47+
48+
private:
49+
static IcdManager sInstance;
50+
Delegate * mDelegate = nullptr;
51+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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+
#include "StayActiveSender.h"
19+
20+
#include <app-common/zap-generated/cluster-objects.h>
21+
#include <app/ConcreteCommandPath.h>
22+
#include <controller/InvokeInteraction.h>
23+
#include <support/CHIPMem.h>
24+
25+
CHIP_ERROR StayActiveSender::SendStayActiveCommand(uint32_t stayActiveDurationMs, const chip::ScopedNodeId & peerNode,
26+
chip::app::InteractionModelEngine * engine, OnDoneCallbackType onDone)
27+
{
28+
ConstructorOnlyInternallyCallable internal;
29+
auto stayActiveSender = chip::Platform::New<StayActiveSender>(internal, stayActiveDurationMs, peerNode,
30+
chip::app::InteractionModelEngine::GetInstance(), onDone);
31+
VerifyOrReturnError(stayActiveSender != nullptr, CHIP_ERROR_NO_MEMORY);
32+
CHIP_ERROR err = stayActiveSender->EstablishSessionToPeer();
33+
if (CHIP_NO_ERROR != err)
34+
{
35+
chip::Platform::Delete(stayActiveSender);
36+
}
37+
return err;
38+
}
39+
40+
StayActiveSender::StayActiveSender(const ConstructorOnlyInternallyCallable & _, uint32_t stayActiveDurationMs,
41+
const chip::ScopedNodeId & peerNode, chip::app::InteractionModelEngine * engine,
42+
OnDoneCallbackType onDone) :
43+
mStayActiveDurationMs(stayActiveDurationMs),
44+
mPeerNode(peerNode), mpImEngine(engine), mOnDone(onDone), mOnConnectedCallback(HandleDeviceConnected, this),
45+
mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this)
46+
{}
47+
48+
CHIP_ERROR StayActiveSender::SendStayActiveCommand(chip::Messaging::ExchangeManager & exchangeMgr,
49+
const chip::SessionHandle & sessionHandle)
50+
{
51+
auto onSuccess = [&](const chip::app::ConcreteCommandPath & commandPath, const chip::app::StatusIB & status,
52+
const auto & dataResponse) {
53+
uint32_t promisedActiveDurationMs = dataResponse.promisedActiveDuration;
54+
ChipLogProgress(ICD, "StayActive command succeeded with promised duration %u", promisedActiveDurationMs);
55+
mOnDone(promisedActiveDurationMs);
56+
chip::Platform::Delete(this);
57+
};
58+
59+
auto onFailure = [&](CHIP_ERROR error) {
60+
ChipLogError(ICD, "StayActive command failed: %" CHIP_ERROR_FORMAT, error.Format());
61+
chip::Platform::Delete(this);
62+
};
63+
64+
chip::EndpointId endpointId = 0;
65+
chip::app::Clusters::IcdManagement::Commands::StayActiveRequest::Type request;
66+
request.stayActiveDuration = mStayActiveDurationMs;
67+
return chip::Controller::InvokeCommandRequest(&exchangeMgr, sessionHandle, endpointId, request, onSuccess, onFailure);
68+
}
69+
70+
CHIP_ERROR StayActiveSender::EstablishSessionToPeer()
71+
{
72+
ChipLogProgress(ICD, "Trying to establish a CASE session to extend the active period for lit icd device");
73+
auto * caseSessionManager = mpImEngine->GetCASESessionManager();
74+
VerifyOrReturnError(caseSessionManager != nullptr, CHIP_ERROR_INVALID_CASE_PARAMETER);
75+
caseSessionManager->FindOrEstablishSession(mPeerNode, &mOnConnectedCallback, &mOnConnectionFailureCallback);
76+
return CHIP_NO_ERROR;
77+
}
78+
79+
void StayActiveSender::HandleDeviceConnected(void * context, chip::Messaging::ExchangeManager & exchangeMgr,
80+
const chip::SessionHandle & sessionHandle)
81+
{
82+
StayActiveSender * const _this = static_cast<StayActiveSender *>(context);
83+
VerifyOrDie(_this != nullptr);
84+
85+
CHIP_ERROR err = _this->SendStayActiveCommand(exchangeMgr, sessionHandle);
86+
if (CHIP_NO_ERROR != err)
87+
{
88+
ChipLogError(ICD, "Failed to send stay active command");
89+
chip::Platform::Delete(_this);
90+
}
91+
}
92+
93+
void StayActiveSender::HandleDeviceConnectionFailure(void * context, const chip::ScopedNodeId & peerId, CHIP_ERROR err)
94+
{
95+
StayActiveSender * const _this = static_cast<StayActiveSender *>(context);
96+
VerifyOrDie(_this != nullptr);
97+
ChipLogError(ICD, "Failed to establish CASE for stay active command with error '%" CHIP_ERROR_FORMAT "'", err.Format());
98+
chip::Platform::Delete(_this);
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
*
3+
* Copyright (c) 2024 Project CHIP Authors
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 <stddef.h>
21+
22+
#include <app/InteractionModelEngine.h>
23+
#include <lib/core/ScopedNodeId.h>
24+
#include <messaging/ExchangeMgr.h>
25+
26+
/**
27+
* @brief StayActiveSender contains all the data and methods needed for active period extension of an ICD client.
28+
*
29+
* Lifetime of instance of StayActiveSender is entirely self managed.
30+
*/
31+
class StayActiveSender
32+
{
33+
private:
34+
// Ideally StayActiveSender would be a private constructor, unfortunately that is not possible as Platform::New
35+
// does not have access to private constructors. As a workaround we have defined this private struct that can
36+
// be forwarded by Platform::New that allows us to enforce that the only way StayActiveSender is constructed is
37+
// if SendStayActiveCommand is called.
38+
struct ConstructorOnlyInternallyCallable
39+
{
40+
};
41+
42+
public:
43+
using OnDoneCallbackType = std::function<void(uint32_t promisedActiveDurationMs)>;
44+
45+
/**
46+
* @brief Attempts to send a StayActiveRequest command
47+
*
48+
* @param[in] stayActiveDurationMs StayActiveRequest command parameter.
49+
* @param[in] peerNode Peer node we sending StayActiveRequest command to
50+
* @param[in] engine Interaction Model Engine instance for sending command.
51+
* @param[in] onDone Upon this function returning success, it is expected that onDone will be called after we
52+
* have successfully recieved a response
53+
*
54+
* @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error code.
55+
*/
56+
static CHIP_ERROR SendStayActiveCommand(uint32_t stayActiveDurationMs, const chip::ScopedNodeId & peerNode,
57+
chip::app::InteractionModelEngine * engine, OnDoneCallbackType onDone);
58+
59+
// Ideally this would be a private constructor, unfortunately that is not possible as Platform::New does not
60+
// have access to private constructors. As a workaround we have defined a private struct that can be forwarded
61+
// by Platform::New that allows us to enforce that the only way this is constructed is if SendStayActiveCommand
62+
// is called.
63+
StayActiveSender(const ConstructorOnlyInternallyCallable & _, uint32_t stayActiveDurationMs,
64+
const chip::ScopedNodeId & peerNode, chip::app::InteractionModelEngine * engine, OnDoneCallbackType onDone);
65+
66+
private:
67+
/**
68+
* @brief Sets up a CASE session with the peer to extend the client's active period with that peer.
69+
* Returns error if we did not even manage to kick off a CASE attempt.
70+
*/
71+
CHIP_ERROR EstablishSessionToPeer();
72+
73+
// CASE session callbacks
74+
/**
75+
*@brief Callback received on successfully establishing a CASE session in order to keep the 'lit icd device' active
76+
*
77+
* @param[in] context - context of the client establishing the CASE session
78+
* @param[in] exchangeMgr - exchange manager to use for the re-registration
79+
* @param[in] sessionHandle - session handle to use for the re-registration
80+
*/
81+
static void HandleDeviceConnected(void * context, chip::Messaging::ExchangeManager & exchangeMgr,
82+
const chip::SessionHandle & sessionHandle);
83+
/**
84+
* @brief Callback received on failure to establish a CASE session
85+
*
86+
* @param[in] context - context of the client establishing the CASE session
87+
* @param[in] peerId - Scoped Node ID of the peer node
88+
* @param[in] err - failure reason
89+
*/
90+
static void HandleDeviceConnectionFailure(void * context, const chip::ScopedNodeId & peerId, CHIP_ERROR err);
91+
92+
/**
93+
* @brief Used to send a stayActive command to the peer
94+
*
95+
* @param[in] exchangeMgr - exchange manager to use for the re-registration
96+
* @param[in] sessionHandle - session handle to use for the re-registration
97+
*/
98+
CHIP_ERROR SendStayActiveCommand(chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle);
99+
100+
uint32_t mStayActiveDurationMs = 0;
101+
chip::ScopedNodeId mPeerNode;
102+
chip::app::InteractionModelEngine * mpImEngine = nullptr;
103+
OnDoneCallbackType mOnDone;
104+
105+
chip::Callback::Callback<chip::OnDeviceConnected> mOnConnectedCallback;
106+
chip::Callback::Callback<chip::OnDeviceConnectionFailure> mOnConnectionFailureCallback;
107+
};

examples/fabric-admin/commands/pairing/PairingCommand.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ void PairingCommand::OnICDRegistrationComplete(ScopedNodeId nodeId, uint32_t icd
464464
sizeof(icdSymmetricKeyHex), chip::Encoding::HexFlags::kNullTerminate);
465465

466466
app::ICDClientInfo clientInfo;
467-
clientInfo.peer_node = chip::ScopedNodeId(mICDCheckInNodeId.Value(), nodeId.GetFabricIndex());
467+
clientInfo.peer_node = nodeId;
468468
clientInfo.monitored_subject = mICDMonitoredSubject.Value();
469469
clientInfo.start_icd_counter = icdCounter;
470470

0 commit comments

Comments
 (0)