| /* |
| * |
| * Copyright (c) 2025 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 "JFAManager.h" |
| |
| #include <app-common/zap-generated/attributes/Accessors.h> |
| #include <app-common/zap-generated/ids/Attributes.h> |
| #include <app-common/zap-generated/ids/Clusters.h> |
| #include <app/ConcreteAttributePath.h> |
| |
| #include <controller/CHIPCluster.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| |
| using namespace chip; |
| using namespace chip::app; |
| using namespace chip::app::Clusters; |
| using namespace chip::Controller; |
| |
| JFAManager JFAManager::sJFA; |
| |
| CHIP_ERROR JFAManager::Init(Server & server) |
| { |
| mServer = &server; |
| mCASESessionManager = server.GetCASESessionManager(); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR JFAManager::FinalizeCommissioning(NodeId nodeId) |
| { |
| if (jfFabricIndex == kUndefinedFabricId) |
| { |
| return CHIP_ERROR_INCORRECT_STATE; |
| } |
| |
| ScopedNodeId scopedNodeId = ScopedNodeId(nodeId, jfFabricIndex); |
| |
| ConnectToNode(scopedNodeId, kStandardCommissioningComplete); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void JFAManager::HandleCommissioningCompleteEvent() |
| { |
| for (const auto & fb : mServer->GetFabricTable()) |
| { |
| FabricIndex fabricIndex = fb.GetFabricIndex(); |
| CATValues cats; |
| |
| if ((jfFabricIndex == kUndefinedFabricIndex) && mServer->GetFabricTable().FetchCATs(fabricIndex, cats) == CHIP_NO_ERROR) |
| { |
| /* When JFA is commissioned, it has to be issued a NOC with Anchor CAT and Administrator CAT */ |
| if (cats.ContainsIdentifier(kAdminCATIdentifier) && cats.ContainsIdentifier(kAnchorCATIdentifier)) |
| { |
| (void) app::Clusters::JointFabricAdministrator::Attributes::AdministratorFabricIndex::Set(1, fabricIndex); |
| |
| jfFabricIndex = fabricIndex; |
| } |
| } |
| } |
| } |
| |
| void JFAManager::ReleaseSession() |
| { |
| auto optionalSessionHandle = mSessionHolder.Get(); |
| |
| if (optionalSessionHandle.HasValue()) |
| { |
| if (optionalSessionHandle.Value()->IsActiveSession()) |
| { |
| optionalSessionHandle.Value()->AsSecureSession()->MarkAsDefunct(); |
| } |
| } |
| mSessionHolder.Release(); |
| } |
| |
| void JFAManager::ConnectToNode(ScopedNodeId scopedNodeId, OnConnectedAction onConnectedAction) |
| { |
| VerifyOrDie(mServer != nullptr); |
| |
| if ((scopedNodeId.GetFabricIndex() == kUndefinedFabricIndex) || (scopedNodeId.GetNodeId() == kUndefinedNodeId)) |
| { |
| ChipLogError(JointFabric, "Invalid node location!"); |
| return; |
| } |
| |
| // Set the action to take once connection is successfully established |
| mOnConnectedAction = onConnectedAction; |
| |
| ChipLogDetail(JointFabric, "Establishing session to node ID 0x" ChipLogFormatX64 " on fabric index %d", |
| ChipLogValueX64(scopedNodeId.GetNodeId()), scopedNodeId.GetFabricIndex()); |
| |
| mCASESessionManager->FindOrEstablishSession(scopedNodeId, &mOnConnectedCallback, &mOnConnectionFailureCallback); |
| } |
| |
| void JFAManager::OnConnected(void * context, Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle) |
| { |
| JFAManager * jfaManager = static_cast<JFAManager *>(context); |
| |
| VerifyOrDie(jfaManager != nullptr); |
| jfaManager->mSessionHolder.Grab(sessionHandle); |
| jfaManager->mExchangeMgr = &exchangeMgr; |
| |
| ChipLogProgress(JointFabric, "Established CASE"); |
| |
| switch (jfaManager->mOnConnectedAction) |
| { |
| case kStandardCommissioningComplete: { |
| jfaManager->SendCommissioningComplete(); |
| break; |
| } |
| |
| default: |
| break; |
| } |
| } |
| |
| void JFAManager::OnConnectionFailure(void * context, const ScopedNodeId & peerId, CHIP_ERROR error) |
| { |
| JFAManager * jfaManager = static_cast<JFAManager *>(context); |
| VerifyOrDie(jfaManager != nullptr); |
| |
| ChipLogError(JointFabric, "Failed to establish connection to 0x" ChipLogFormatX64 " on fabric index %d", |
| ChipLogValueX64(peerId.GetNodeId()), peerId.GetFabricIndex()); |
| |
| jfaManager->ReleaseSession(); |
| } |
| |
| CHIP_ERROR JFAManager::SendCommissioningComplete() |
| { |
| GeneralCommissioning::Commands::CommissioningComplete::Type request; |
| |
| if (!mExchangeMgr) |
| { |
| return CHIP_ERROR_UNINITIALIZED; |
| } |
| |
| ChipLogProgress(JointFabric, "SendCommissioningComplete: invoke cluster command."); |
| Controller::ClusterBase cluster(*mExchangeMgr, mSessionHolder.Get().Value(), kRootEndpointId); |
| return cluster.InvokeCommand(request, this, OnCommissioningCompleteResponse, OnCommissioningCompleteFailure); |
| } |
| |
| void JFAManager::OnCommissioningCompleteResponse( |
| void * context, const GeneralCommissioning::Commands::CommissioningCompleteResponse::DecodableType & data) |
| { |
| JFAManager * jfaManagerCore = static_cast<JFAManager *>(context); |
| VerifyOrDie(jfaManagerCore != nullptr); |
| jfaManagerCore->ReleaseSession(); |
| |
| ChipLogProgress(JointFabric, "OnCommissioningCompleteResponse, Code=%u", to_underlying(data.errorCode)); |
| |
| if (data.errorCode != GeneralCommissioning::CommissioningErrorEnum::kOk) |
| { |
| // TODO |
| } |
| else |
| { |
| // TODO |
| } |
| |
| jfaManagerCore->ReleaseSession(); |
| } |
| |
| void JFAManager::OnCommissioningCompleteFailure(void * context, CHIP_ERROR error) |
| { |
| JFAManager * jfaManagerCore = static_cast<JFAManager *>(context); |
| VerifyOrDie(jfaManagerCore != nullptr); |
| jfaManagerCore->ReleaseSession(); |
| |
| ChipLogError(JointFabric, "Received failure response %s\n", chip::ErrorStr(error)); |
| } |