diff --git a/examples/fabric-sync/admin/BUILD.gn b/examples/fabric-sync/admin/BUILD.gn index 2ccc30ca2f7976..e53590388e6f76 100644 --- a/examples/fabric-sync/admin/BUILD.gn +++ b/examples/fabric-sync/admin/BUILD.gn @@ -44,8 +44,12 @@ source_set("fabric-admin-lib") { "FabricAdmin.h", "FabricSyncGetter.cpp", "FabricSyncGetter.h", + "IcdManager.cpp", + "IcdManager.h", "PairingManager.cpp", "PairingManager.h", + "StayActiveSender.cpp", + "StayActiveSender.h", "UniqueIdGetter.cpp", "UniqueIdGetter.h", ] @@ -53,6 +57,8 @@ source_set("fabric-admin-lib") { deps = [ "${chip_root}/examples/fabric-sync/bridge:fabric-bridge-lib", "${chip_root}/examples/platform/linux:app-main", + "${chip_root}/src/app/icd/client:handler", + "${chip_root}/src/app/icd/client:manager", "${chip_root}/src/lib", ] } diff --git a/examples/fabric-sync/admin/FabricAdmin.cpp b/examples/fabric-sync/admin/FabricAdmin.cpp index ed2126d34e2422..afc9456b2aa890 100644 --- a/examples/fabric-sync/admin/FabricAdmin.cpp +++ b/examples/fabric-sync/admin/FabricAdmin.cpp @@ -16,6 +16,9 @@ */ #include "FabricAdmin.h" +#include +#include +#include using namespace ::chip; @@ -28,16 +31,35 @@ constexpr uint32_t kCommissionPrepareTimeMs = 500; } // namespace FabricAdmin FabricAdmin::sInstance; +app::DefaultICDClientStorage FabricAdmin::sICDClientStorage; +app::CheckInHandler FabricAdmin::sCheckInHandler; FabricAdmin & FabricAdmin::Instance() { if (!sInstance.mInitialized) { - sInstance.Init(); + VerifyOrDie(sInstance.Init() == CHIP_NO_ERROR); } return sInstance; } +CHIP_ERROR FabricAdmin::Init() +{ + IcdManager::Instance().SetDelegate(&sInstance); + + ReturnLogErrorOnFailure(sICDClientStorage.Init(GetPersistentStorageDelegate(), GetSessionKeystore())); + + auto engine = chip::app::InteractionModelEngine::GetInstance(); + VerifyOrReturnError(engine != nullptr, CHIP_ERROR_INCORRECT_STATE); + ReturnLogErrorOnFailure(IcdManager::Instance().Init(&sICDClientStorage, engine)); + ReturnLogErrorOnFailure(sCheckInHandler.Init(Controller::DeviceControllerFactory::GetInstance().GetSystemState()->ExchangeMgr(), + &sICDClientStorage, &IcdManager::Instance(), engine)); + + mInitialized = true; + + return CHIP_NO_ERROR; +} + CHIP_ERROR FabricAdmin::OpenCommissioningWindow(Controller::CommissioningWindowVerifierParams params, FabricIndex fabricIndex) { ScopedNodeId scopedNodeId(params.GetNodeId(), fabricIndex); @@ -116,6 +138,46 @@ CHIP_ERROR FabricAdmin::KeepActive(ScopedNodeId scopedNodeId, uint32_t stayActiv return CHIP_NO_ERROR; } +void FabricAdmin::OnCheckInCompleted(const app::ICDClientInfo & clientInfo) +{ + // Accessing mPendingCheckIn should only be done while holding ChipStackLock + assertChipStackLockedByCurrentThread(); + ScopedNodeId scopedNodeId = clientInfo.peer_node; + auto it = mPendingCheckIn.find(scopedNodeId); + VerifyOrReturn(it != mPendingCheckIn.end()); + + KeepActiveDataForCheckIn checkInData = it->second; + // Removed from pending map as check-in from this node has occured and we will handle the pending KeepActive + // request. + mPendingCheckIn.erase(scopedNodeId); + + auto timeNow = System::SystemClock().GetMonotonicTimestamp(); + if (timeNow > checkInData.mRequestExpiryTimestamp) + { + ChipLogError(NotSpecified, + "ICD check-in for device we have been waiting, came after KeepActive expiry. Request dropped for ID: " + "[%d:0x " ChipLogFormatX64 "]", + scopedNodeId.GetFabricIndex(), ChipLogValueX64(scopedNodeId.GetNodeId())); + return; + } + + // TODO https://github.com/CHIP-Specifications/connectedhomeip-spec/issues/10448. Spec does + // not define what to do if we fail to send the StayActiveRequest. We are assuming that any + // further attempts to send a StayActiveRequest will result in a similar failure. Because + // there is no mechanism for us to communicate with the client that sent out the KeepActive + // command that there was a failure, we simply fail silently. After spec issue is + // addressed, we can implement what spec defines here. + auto onDone = [=](uint32_t promisedActiveDuration) { + bridge::FabricBridge::Instance().ActiveChanged(scopedNodeId, promisedActiveDuration); + }; + CHIP_ERROR err = StayActiveSender::SendStayActiveCommand(checkInData.mStayActiveDurationMs, clientInfo.peer_node, + app::InteractionModelEngine::GetInstance(), onDone); + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to send StayActive command %s", err.AsString()); + } +} + void FabricAdmin::OnCommissioningComplete(NodeId deviceId, CHIP_ERROR err) { if (mNodeId != deviceId) diff --git a/examples/fabric-sync/admin/FabricAdmin.h b/examples/fabric-sync/admin/FabricAdmin.h index f2634156ee67cc..85547373f1238b 100644 --- a/examples/fabric-sync/admin/FabricAdmin.h +++ b/examples/fabric-sync/admin/FabricAdmin.h @@ -18,7 +18,12 @@ #pragma once #include "DeviceManager.h" +#include "IcdManager.h" +#include "StayActiveSender.h" +#include +#include +#include #include #include #include @@ -37,10 +42,11 @@ struct ScopedNodeIdHasher } }; -class FabricAdmin final : public bridge::FabricAdminDelegate, public PairingDelegate +class FabricAdmin final : public bridge::FabricAdminDelegate, public PairingDelegate, public IcdManager::Delegate { public: static FabricAdmin & Instance(); + static chip::app::DefaultICDClientStorage & GetDefaultICDClientStorage() { return sICDClientStorage; } CHIP_ERROR OpenCommissioningWindow(chip::Controller::CommissioningWindowVerifierParams params, chip::FabricIndex fabricIndex) override; @@ -51,6 +57,8 @@ class FabricAdmin final : public bridge::FabricAdminDelegate, public PairingDele CHIP_ERROR KeepActive(chip::ScopedNodeId scopedNodeId, uint32_t stayActiveDurationMs, uint32_t timeoutMs) override; + void OnCheckInCompleted(const chip::app::ICDClientInfo & clientInfo) override; + void OnCommissioningComplete(chip::NodeId deviceId, CHIP_ERROR err) override; void ScheduleSendingKeepActiveOnCheckIn(chip::ScopedNodeId scopedNodeId, uint32_t stayActiveDurationMs, uint32_t timeoutMs); @@ -89,11 +97,13 @@ class FabricAdmin final : public bridge::FabricAdminDelegate, public PairingDele std::unordered_map mPendingCheckIn; static FabricAdmin sInstance; + static chip::app::DefaultICDClientStorage sICDClientStorage; + static chip::app::CheckInHandler sCheckInHandler; bool mInitialized = false; chip::NodeId mNodeId = chip::kUndefinedNodeId; - void Init() { mInitialized = true; } + CHIP_ERROR Init(); }; } // namespace admin diff --git a/examples/fabric-sync/admin/IcdManager.cpp b/examples/fabric-sync/admin/IcdManager.cpp new file mode 100644 index 00000000000000..75c49a7fd9707b --- /dev/null +++ b/examples/fabric-sync/admin/IcdManager.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024 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 "IcdManager.h" + +namespace admin { + +IcdManager IcdManager::sInstance; + +IcdManager & IcdManager::Instance() +{ + return sInstance; +} + +void IcdManager::OnCheckInComplete(const chip::app::ICDClientInfo & clientInfo) +{ + DefaultCheckInDelegate::OnCheckInComplete(clientInfo); + if (mDelegate) + { + mDelegate->OnCheckInCompleted(clientInfo); + } +} + +void IcdManager::SetDelegate(Delegate * delegate) +{ + // To keep IcdManager simple, there is an assumption that there is only ever + // one delegate set and it's lifetime is identical to IcdManager. In the + // future this assumption can change should there be a need, but that will + // require code changes to IcdManager. For now we will crash if someone tries + // to call SetDelegate for a second time or if delegate is non-null. + VerifyOrDie(delegate); + VerifyOrDie(!mDelegate); + mDelegate = delegate; +} + +} // namespace admin diff --git a/examples/fabric-sync/admin/IcdManager.h b/examples/fabric-sync/admin/IcdManager.h new file mode 100644 index 00000000000000..0700363bf5b411 --- /dev/null +++ b/examples/fabric-sync/admin/IcdManager.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 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. + * + */ + +#pragma once + +#include + +namespace admin { + +/** + * @brief Manages check-ins from ICD devices. + * + * Intended to be used as a thin CheckInDelegate. This allows a delegate register + * themselves so they can be aware when ICD device checks-in allowing the + * delegate to interact with the ICD device during the short window that it is + * awake. + */ +class IcdManager : public chip::app::DefaultCheckInDelegate +{ +public: + class Delegate + { + public: + virtual ~Delegate() = default; + virtual void OnCheckInCompleted(const chip::app::ICDClientInfo & clientInfo) = 0; + }; + + static IcdManager & Instance(); + void OnCheckInComplete(const chip::app::ICDClientInfo & clientInfo) override; + + // There is an assumption delegate assigned only happens once and that it lives + // for the entirety of the lifetime of fabric admin. + void SetDelegate(Delegate * delegate); + +private: + static IcdManager sInstance; + Delegate * mDelegate = nullptr; +}; + +} // namespace admin diff --git a/examples/fabric-sync/admin/PairingManager.cpp b/examples/fabric-sync/admin/PairingManager.cpp index eeb8ad09ff2a76..63a2c67c842912 100644 --- a/examples/fabric-sync/admin/PairingManager.cpp +++ b/examples/fabric-sync/admin/PairingManager.cpp @@ -18,6 +18,7 @@ #include "PairingManager.h" #include "DeviceManager.h" #include "DeviceSynchronization.h" +#include "FabricAdmin.h" #include #include @@ -103,7 +104,7 @@ PairingManager::PairingManager() : CHIP_ERROR PairingManager::Init(Controller::DeviceCommissioner * commissioner) { VerifyOrReturnError(commissioner != nullptr, CHIP_ERROR_INCORRECT_STATE); - + FabricAdmin::Instance().GetDefaultICDClientStorage().UpdateFabricList(commissioner->GetFabricIndex()); mCommissioner = commissioner; return CHIP_NO_ERROR; @@ -294,10 +295,20 @@ void PairingManager::OnCommissioningComplete(NodeId nodeId, CHIP_ERROR err) // mCommissioner has a lifetime that is the entire life of the application itself // so it is safe to provide to StartDeviceSynchronization. - DeviceSynchronizer::Instance().StartDeviceSynchronization(mCommissioner, nodeId, false); + DeviceSynchronizer::Instance().StartDeviceSynchronization(mCommissioner, nodeId, mDeviceIsICD); } else { + // When ICD device commissioning fails, the ICDClientInfo stored in OnICDRegistrationComplete needs to be removed. + if (mDeviceIsICD) + { + CHIP_ERROR deleteEntryError = FabricAdmin::Instance().GetDefaultICDClientStorage().DeleteEntry( + ScopedNodeId(nodeId, mCommissioner->GetFabricIndex())); + if (deleteEntryError != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to delete ICD entry: %s", ErrorStr(err)); + } + } ChipLogProgress(NotSpecified, "Device commissioning Failure: %s", ErrorStr(err)); } } @@ -329,6 +340,49 @@ void PairingManager::OnReadCommissioningInfo(const Controller::ReadCommissioning info.icd.idleModeDuration, info.icd.activeModeDuration, info.icd.activeModeThreshold); } +void PairingManager::OnICDRegistrationComplete(ScopedNodeId nodeId, uint32_t icdCounter) +{ + char icdSymmetricKeyHex[Crypto::kAES_CCM128_Key_Length * 2 + 1]; + + Encoding::BytesToHex(mICDSymmetricKey.Value().data(), mICDSymmetricKey.Value().size(), icdSymmetricKeyHex, + sizeof(icdSymmetricKeyHex), Encoding::HexFlags::kNullTerminate); + + app::ICDClientInfo clientInfo; + clientInfo.peer_node = nodeId; + clientInfo.monitored_subject = mICDMonitoredSubject.Value(); + clientInfo.start_icd_counter = icdCounter; + auto & ICDClientStorage = FabricAdmin::Instance().GetDefaultICDClientStorage(); + + CHIP_ERROR err = ICDClientStorage.SetKey(clientInfo, mICDSymmetricKey.Value()); + if (err == CHIP_NO_ERROR) + { + err = ICDClientStorage.StoreEntry(clientInfo); + } + + if (err != CHIP_NO_ERROR) + { + ICDClientStorage.RemoveKey(clientInfo); + ChipLogError(NotSpecified, "Failed to persist symmetric key for " ChipLogFormatX64 ": %s", + ChipLogValueX64(nodeId.GetNodeId()), err.AsString()); + return; + } + + mDeviceIsICD = true; + + ChipLogProgress(NotSpecified, "Saved ICD Symmetric key for " ChipLogFormatX64, ChipLogValueX64(nodeId.GetNodeId())); + ChipLogProgress(NotSpecified, + "ICD Registration Complete for device " ChipLogFormatX64 " / Check-In NodeID: " ChipLogFormatX64 + " / Monitored Subject: " ChipLogFormatX64 " / Symmetric Key: %s / ICDCounter %u", + ChipLogValueX64(nodeId.GetNodeId()), ChipLogValueX64(mICDCheckInNodeId.Value()), + ChipLogValueX64(mICDMonitoredSubject.Value()), icdSymmetricKeyHex, icdCounter); +} + +void PairingManager::OnICDStayActiveComplete(ScopedNodeId deviceId, uint32_t promisedActiveDuration) +{ + ChipLogProgress(NotSpecified, "ICD Stay Active Complete for device " ChipLogFormatX64 " / promisedActiveDuration: %u", + ChipLogValueX64(deviceId.GetNodeId()), promisedActiveDuration); +} + void PairingManager::OnDiscoveredDevice(const Dnssd::CommissionNodeData & nodeData) { // Ignore nodes with closed commissioning window @@ -436,6 +490,39 @@ CommissioningParameters PairingManager::GetCommissioningParameters() params.SetSkipCommissioningComplete(false); params.SetDeviceAttestationDelegate(this); + if (mICDRegistration.ValueOr(false)) + { + params.SetICDRegistrationStrategy(ICDRegistrationStrategy::kBeforeComplete); + + if (!mICDSymmetricKey.HasValue()) + { + Crypto::DRBG_get_bytes(mRandomGeneratedICDSymmetricKey, sizeof(mRandomGeneratedICDSymmetricKey)); + mICDSymmetricKey.SetValue(ByteSpan(mRandomGeneratedICDSymmetricKey)); + } + if (!mICDCheckInNodeId.HasValue()) + { + mICDCheckInNodeId.SetValue(mCommissioner->GetNodeId()); + } + if (!mICDMonitoredSubject.HasValue()) + { + mICDMonitoredSubject.SetValue(mICDCheckInNodeId.Value()); + } + if (!mICDClientType.HasValue()) + { + mICDClientType.SetValue(app::Clusters::IcdManagement::ClientTypeEnum::kPermanent); + } + // These Optionals must have values now. + // The commissioner will verify these values. + params.SetICDSymmetricKey(mICDSymmetricKey.Value()); + if (mICDStayActiveDurationMsec.HasValue()) + { + params.SetICDStayActiveDurationMsec(mICDStayActiveDurationMsec.Value()); + } + params.SetICDCheckInNodeId(mICDCheckInNodeId.Value()); + params.SetICDMonitoredSubject(mICDMonitoredSubject.Value()); + params.SetICDClientType(mICDClientType.Value()); + } + return params; } @@ -482,6 +569,7 @@ void PairingManager::OnCurrentFabricRemove(void * context, NodeId nodeId, CHIP_E void PairingManager::InitPairingCommand() { mCommissioner->RegisterPairingDelegate(this); + mDeviceIsICD = false; } CHIP_ERROR PairingManager::PairDeviceWithCode(NodeId nodeId, const char * payload) diff --git a/examples/fabric-sync/admin/PairingManager.h b/examples/fabric-sync/admin/PairingManager.h index 9d109911e195df..37e9aedec65bdb 100644 --- a/examples/fabric-sync/admin/PairingManager.h +++ b/examples/fabric-sync/admin/PairingManager.h @@ -149,6 +149,8 @@ class PairingManager : public chip::Controller::DevicePairingDelegate, void OnPairingDeleted(CHIP_ERROR error) override; void OnReadCommissioningInfo(const chip::Controller::ReadCommissioningInfo & info) override; void OnCommissioningComplete(chip::NodeId deviceId, CHIP_ERROR error) override; + void OnICDRegistrationComplete(chip::ScopedNodeId deviceId, uint32_t icdCounter) override; + void OnICDStayActiveComplete(chip::ScopedNodeId deviceId, uint32_t promisedActiveDuration) override; /////////// DeviceDiscoveryDelegate Interface ///////// void OnDiscoveredDevice(const chip::Dnssd::CommissionNodeData & nodeData) override; @@ -176,11 +178,20 @@ class PairingManager : public chip::Controller::DevicePairingDelegate, chip::ByteSpan mSalt; uint16_t mDiscriminator = 0; uint32_t mSetupPINCode = 0; + bool mDeviceIsICD = false; + uint8_t mRandomGeneratedICDSymmetricKey[chip::Crypto::kAES_CCM128_Key_Length]; uint8_t mVerifierBuffer[chip::Crypto::kSpake2p_VerifierSerialized_Length]; uint8_t mSaltBuffer[chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length]; char mRemoteIpAddr[chip::Inet::IPAddress::kMaxStringLength]; char mOnboardingPayload[kMaxManualCodeLength + 1]; + chip::Optional mICDRegistration; + chip::Optional mICDCheckInNodeId; + chip::Optional mICDClientType; + chip::Optional mICDSymmetricKey; + chip::Optional mICDMonitoredSubject; + chip::Optional mICDStayActiveDurationMsec; + /** * Holds the unique_ptr to the current CommissioningWindowOpener. * Only one commissioning window opener can be active at a time. diff --git a/examples/fabric-sync/admin/StayActiveSender.cpp b/examples/fabric-sync/admin/StayActiveSender.cpp new file mode 100644 index 00000000000000..51927c4d598999 --- /dev/null +++ b/examples/fabric-sync/admin/StayActiveSender.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024 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 "StayActiveSender.h" + +#include +#include +#include +#include + +namespace admin { + +CHIP_ERROR StayActiveSender::SendStayActiveCommand(uint32_t stayActiveDurationMs, const chip::ScopedNodeId & peerNode, + chip::app::InteractionModelEngine * engine, OnDoneCallbackType onDone) +{ + ConstructorOnlyInternallyCallable internal; + auto stayActiveSender = chip::Platform::New(internal, stayActiveDurationMs, peerNode, + chip::app::InteractionModelEngine::GetInstance(), onDone); + VerifyOrReturnError(stayActiveSender != nullptr, CHIP_ERROR_NO_MEMORY); + CHIP_ERROR err = stayActiveSender->EstablishSessionToPeer(); + if (CHIP_NO_ERROR != err) + { + chip::Platform::Delete(stayActiveSender); + } + return err; +} + +StayActiveSender::StayActiveSender(const ConstructorOnlyInternallyCallable & _, uint32_t stayActiveDurationMs, + const chip::ScopedNodeId & peerNode, chip::app::InteractionModelEngine * engine, + OnDoneCallbackType onDone) : + mStayActiveDurationMs(stayActiveDurationMs), + mPeerNode(peerNode), mpImEngine(engine), mOnDone(onDone), mOnConnectedCallback(HandleDeviceConnected, this), + mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this) +{} + +CHIP_ERROR StayActiveSender::SendStayActiveCommand(chip::Messaging::ExchangeManager & exchangeMgr, + const chip::SessionHandle & sessionHandle) +{ + auto onSuccess = [&](const chip::app::ConcreteCommandPath & commandPath, const chip::app::StatusIB & status, + const auto & dataResponse) { + uint32_t promisedActiveDurationMs = dataResponse.promisedActiveDuration; + ChipLogProgress(ICD, "StayActive command succeeded with promised duration %u", promisedActiveDurationMs); + mOnDone(promisedActiveDurationMs); + chip::Platform::Delete(this); + }; + + auto onFailure = [&](CHIP_ERROR error) { + ChipLogError(ICD, "StayActive command failed: %" CHIP_ERROR_FORMAT, error.Format()); + chip::Platform::Delete(this); + }; + + chip::EndpointId endpointId = 0; + chip::app::Clusters::IcdManagement::Commands::StayActiveRequest::Type request; + request.stayActiveDuration = mStayActiveDurationMs; + return chip::Controller::InvokeCommandRequest(&exchangeMgr, sessionHandle, endpointId, request, onSuccess, onFailure); +} + +CHIP_ERROR StayActiveSender::EstablishSessionToPeer() +{ + ChipLogProgress(ICD, "Trying to establish a CASE session to extend the active period for lit icd device"); + auto * caseSessionManager = mpImEngine->GetCASESessionManager(); + VerifyOrReturnError(caseSessionManager != nullptr, CHIP_ERROR_INVALID_CASE_PARAMETER); + caseSessionManager->FindOrEstablishSession(mPeerNode, &mOnConnectedCallback, &mOnConnectionFailureCallback); + return CHIP_NO_ERROR; +} + +void StayActiveSender::HandleDeviceConnected(void * context, chip::Messaging::ExchangeManager & exchangeMgr, + const chip::SessionHandle & sessionHandle) +{ + StayActiveSender * const _this = static_cast(context); + VerifyOrDie(_this != nullptr); + + CHIP_ERROR err = _this->SendStayActiveCommand(exchangeMgr, sessionHandle); + if (CHIP_NO_ERROR != err) + { + ChipLogError(ICD, "Failed to send stay active command"); + chip::Platform::Delete(_this); + } +} + +void StayActiveSender::HandleDeviceConnectionFailure(void * context, const chip::ScopedNodeId & peerId, CHIP_ERROR err) +{ + StayActiveSender * const _this = static_cast(context); + VerifyOrDie(_this != nullptr); + ChipLogError(ICD, "Failed to establish CASE for stay active command with error '%" CHIP_ERROR_FORMAT "'", err.Format()); + chip::Platform::Delete(_this); +} + +} // namespace admin diff --git a/examples/fabric-sync/admin/StayActiveSender.h b/examples/fabric-sync/admin/StayActiveSender.h new file mode 100644 index 00000000000000..dd4a66ddeda59e --- /dev/null +++ b/examples/fabric-sync/admin/StayActiveSender.h @@ -0,0 +1,111 @@ +/* + * + * Copyright (c) 2024 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. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace admin { + +/** + * @brief StayActiveSender contains all the data and methods needed for active period extension of an ICD client. + * + * Lifetime of instance of StayActiveSender is entirely self managed. + */ +class StayActiveSender +{ +private: + // Ideally StayActiveSender would be a private constructor, unfortunately that is not possible as Platform::New + // does not have access to private constructors. As a workaround we have defined this private struct that can + // be forwarded by Platform::New that allows us to enforce that the only way StayActiveSender is constructed is + // if SendStayActiveCommand is called. + struct ConstructorOnlyInternallyCallable + { + }; + +public: + using OnDoneCallbackType = std::function; + + /** + * @brief Attempts to send a StayActiveRequest command + * + * @param[in] stayActiveDurationMs StayActiveRequest command parameter. + * @param[in] peerNode Peer node we sending StayActiveRequest command to + * @param[in] engine Interaction Model Engine instance for sending command. + * @param[in] onDone Upon this function returning success, it is expected that onDone will be called after we + * have successfully recieved a response + * + * @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error code. + */ + static CHIP_ERROR SendStayActiveCommand(uint32_t stayActiveDurationMs, const chip::ScopedNodeId & peerNode, + chip::app::InteractionModelEngine * engine, OnDoneCallbackType onDone); + + // Ideally this would be a private constructor, unfortunately that is not possible as Platform::New does not + // have access to private constructors. As a workaround we have defined a private struct that can be forwarded + // by Platform::New that allows us to enforce that the only way this is constructed is if SendStayActiveCommand + // is called. + StayActiveSender(const ConstructorOnlyInternallyCallable & _, uint32_t stayActiveDurationMs, + const chip::ScopedNodeId & peerNode, chip::app::InteractionModelEngine * engine, OnDoneCallbackType onDone); + +private: + /** + * @brief Sets up a CASE session with the peer to extend the client's active period with that peer. + * Returns error if we did not even manage to kick off a CASE attempt. + */ + CHIP_ERROR EstablishSessionToPeer(); + + // CASE session callbacks + /** + *@brief Callback received on successfully establishing a CASE session in order to keep the 'lit icd device' active + * + * @param[in] context - context of the client establishing the CASE session + * @param[in] exchangeMgr - exchange manager to use for the re-registration + * @param[in] sessionHandle - session handle to use for the re-registration + */ + static void HandleDeviceConnected(void * context, chip::Messaging::ExchangeManager & exchangeMgr, + const chip::SessionHandle & sessionHandle); + /** + * @brief Callback received on failure to establish a CASE session + * + * @param[in] context - context of the client establishing the CASE session + * @param[in] peerId - Scoped Node ID of the peer node + * @param[in] err - failure reason + */ + static void HandleDeviceConnectionFailure(void * context, const chip::ScopedNodeId & peerId, CHIP_ERROR err); + + /** + * @brief Used to send a stayActive command to the peer + * + * @param[in] exchangeMgr - exchange manager to use for the re-registration + * @param[in] sessionHandle - session handle to use for the re-registration + */ + CHIP_ERROR SendStayActiveCommand(chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle); + + uint32_t mStayActiveDurationMs = 0; + chip::ScopedNodeId mPeerNode; + chip::app::InteractionModelEngine * mpImEngine = nullptr; + OnDoneCallbackType mOnDone; + + chip::Callback::Callback mOnConnectedCallback; + chip::Callback::Callback mOnConnectionFailureCallback; +}; + +} // namespace admin diff --git a/examples/platform/linux/AppMain.h b/examples/platform/linux/AppMain.h index 94bf4d3e609da1..21ba04df7dabba 100644 --- a/examples/platform/linux/AppMain.h +++ b/examples/platform/linux/AppMain.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -90,7 +91,9 @@ void ChipLinuxAppMainLoop(AppMainLoopImplementation * impl = nullptr); #if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE +using chip::PersistentStorageDelegate; using chip::Controller::DeviceCommissioner; +using chip::Crypto::SessionKeystore; using chip::Transport::PeerAddress; CHIP_ERROR CommissionerPairOnNetwork(uint32_t pincode, uint16_t disc, PeerAddress address); @@ -98,6 +101,8 @@ CHIP_ERROR CommissionerPairUDC(uint32_t pincode, size_t index); DeviceCommissioner * GetDeviceCommissioner(); CommissionerDiscoveryController * GetCommissionerDiscoveryController(); +SessionKeystore * GetSessionKeystore(); +PersistentStorageDelegate * GetPersistentStorageDelegate(); #endif // CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE