Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fabric-Sync] Port commissioner control protocol implementation #36427

Merged
merged 9 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions examples/fabric-admin/device_manager/CommissionerControl.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
/*
* 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 "CommissionerControl.h"
#include "DeviceManager.h"

Expand Down
28 changes: 28 additions & 0 deletions examples/fabric-sync/admin/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,44 @@
import("//build_overrides/chip.gni")
import("${chip_root}/src/app/chip_data_model.gni")

config("config") {
include_dirs = [
".",
"${chip_root}/examples/fabric-sync",
"${chip_root}/examples/platform/linux",
"${chip_root}/src/lib",
]
}

source_set("fabric-admin-lib") {
public_configs = [ ":config" ]

sources = [
"BridgeSubscription.cpp",
"BridgeSubscription.h",
"CommissionerControl.cpp",
"CommissionerControl.h",
"DeviceManager.cpp",
"DeviceManager.h",
"DeviceSubscription.cpp",
"DeviceSubscription.h",
"DeviceSubscriptionManager.cpp",
"DeviceSubscriptionManager.h",
"DeviceSynchronization.cpp",
"DeviceSynchronization.h",
"FabricAdmin.cpp",
"FabricAdmin.h",
"FabricSyncGetter.cpp",
"FabricSyncGetter.h",
"PairingManager.cpp",
"PairingManager.h",
"UniqueIdGetter.cpp",
"UniqueIdGetter.h",
]

deps = [
"${chip_root}/examples/fabric-sync/bridge:fabric-bridge-lib",
"${chip_root}/examples/platform/linux:app-main",
"${chip_root}/src/lib",
]
}
163 changes: 163 additions & 0 deletions examples/fabric-sync/admin/BridgeSubscription.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* 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 "BridgeSubscription.h"
#include "DeviceManager.h"

using namespace ::chip;
using namespace ::chip::app;
using chip::app::ReadClient;

namespace admin {

namespace {

constexpr uint16_t kSubscribeMinInterval = 0;
constexpr uint16_t kSubscribeMaxInterval = 60;

void OnDeviceConnectedWrapper(void * context, Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle)
{
reinterpret_cast<BridgeSubscription *>(context)->OnDeviceConnected(exchangeMgr, sessionHandle);
}

void OnDeviceConnectionFailureWrapper(void * context, const ScopedNodeId & peerId, CHIP_ERROR error)
{
reinterpret_cast<BridgeSubscription *>(context)->OnDeviceConnectionFailure(peerId, error);
}

} // namespace

BridgeSubscription::BridgeSubscription() :
mOnDeviceConnectedCallback(OnDeviceConnectedWrapper, this),
mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureWrapper, this)
{}

CHIP_ERROR BridgeSubscription::StartSubscription(Controller::DeviceController & controller, NodeId nodeId, EndpointId endpointId)
{
assertChipStackLockedByCurrentThread();

VerifyOrDie(!subscriptionStarted); // Ensure it's not called multiple times.

// Mark as started
subscriptionStarted = true;

mEndpointId = endpointId;

CHIP_ERROR err = controller.GetConnectedDevice(nodeId, &mOnDeviceConnectedCallback, &mOnDeviceConnectionFailureCallback);
if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed to connect to remote fabric sync bridge %" CHIP_ERROR_FORMAT, err.Format());
}
return err;
}

void BridgeSubscription::OnAttributeData(const ConcreteDataAttributePath & path, TLV::TLVReader * data, const StatusIB & status)
{
if (!status.IsSuccess())
{
ChipLogError(NotSpecified, "Response Failure: %" CHIP_ERROR_FORMAT, status.ToChipError().Format());
return;
}

if (data == nullptr)
{
ChipLogError(NotSpecified, "Response Failure: No Data");
return;
}

DeviceMgr().HandleAttributeData(path, *data);
}

void BridgeSubscription::OnEventData(const app::EventHeader & eventHeader, TLV::TLVReader * data, const app::StatusIB * status)
{
if (status != nullptr)
{
CHIP_ERROR error = status->ToChipError();
if (CHIP_NO_ERROR != error)
{
ChipLogError(NotSpecified, "Response Failure: %" CHIP_ERROR_FORMAT, error.Format());
return;
}
}

if (data == nullptr)
{
ChipLogError(NotSpecified, "Response Failure: No Data");
return;
}

DeviceMgr().HandleEventData(eventHeader, *data);
}

void BridgeSubscription::OnError(CHIP_ERROR error)
{
ChipLogProgress(NotSpecified, "Error on remote fabric sync bridge subscription: %" CHIP_ERROR_FORMAT, error.Format());
}

void BridgeSubscription::OnDone(ReadClient * apReadClient)
{
mClient.reset();
ChipLogProgress(NotSpecified, "The remote fabric sync bridge subscription is terminated");

// Reset the subscription state to allow retry
subscriptionStarted = false;

// TODO:(#36092) Fabric-Admin should attempt to re-subscribe when the subscription to the remote bridge is terminated.
}

void BridgeSubscription::OnDeviceConnected(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle)
{
mClient = std::make_unique<ReadClient>(app::InteractionModelEngine::GetInstance(), &exchangeMgr /* echangeMgr */,
*this /* callback */, ReadClient::InteractionType::Subscribe);
VerifyOrDie(mClient);

AttributePathParams readPaths[1];
readPaths[0] = AttributePathParams(mEndpointId, Clusters::Descriptor::Id, Clusters::Descriptor::Attributes::PartsList::Id);

EventPathParams eventPaths[1];
eventPaths[0] = EventPathParams(mEndpointId, Clusters::CommissionerControl::Id,
Clusters::CommissionerControl::Events::CommissioningRequestResult::Id);
eventPaths[0].mIsUrgentEvent = true;

ReadPrepareParams readParams(sessionHandle);

readParams.mpAttributePathParamsList = readPaths;
readParams.mAttributePathParamsListSize = 1;
readParams.mpEventPathParamsList = eventPaths;
readParams.mEventPathParamsListSize = 1;
readParams.mMinIntervalFloorSeconds = kSubscribeMinInterval;
readParams.mMaxIntervalCeilingSeconds = kSubscribeMaxInterval;
readParams.mKeepSubscriptions = true;

CHIP_ERROR err = mClient->SendRequest(readParams);

if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed to issue subscription to the Descriptor Cluster of the remote bridged device.");
OnDone(nullptr);
return;
}
}

void BridgeSubscription::OnDeviceConnectionFailure(const ScopedNodeId & peerId, CHIP_ERROR error)
{
ChipLogError(NotSpecified, "BridgeSubscription failed to connect to " ChipLogFormatX64, ChipLogValueX64(peerId.GetNodeId()));
OnDone(nullptr);
}

} // namespace admin
81 changes: 81 additions & 0 deletions examples/fabric-sync/admin/BridgeSubscription.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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 <app/ReadClient.h>
#include <controller/CHIPDeviceController.h>

#include <memory>
#include <optional>

namespace admin {

/**
* @brief Class used to subscribe to attributes and events from the remote bridged device.
*
* The Descriptor Cluster contains attributes such as the Parts List, which provides a list
* of endpoints or devices that are part of a composite device or bridge. The CommissionerControl
* Cluster generates events related to commissioning requests, which can be monitored to track
* device commissioning status.
*
* When subscribing to attributes and events of a bridged device from another fabric, the class:
* - Establishes a secure session with the device (if needed) via CASE (Chip over
* Authenticated Session Establishment) session.
* - Subscribes to the specified attributes in the Descriptor Cluster (e.g., Parts List) and
* events in the CommissionerControl Cluster (e.g., CommissioningRequestResult) of the remote
* device on the specified node and endpoint.
* - Invokes the provided callback upon successful or unsuccessful subscription, allowing
* further handling of data or errors.
*
* This class also implements the necessary callbacks to handle attribute data reports, event data,
* errors, and session establishment procedures.
*/
class BridgeSubscription : public chip::app::ReadClient::Callback
{
public:
BridgeSubscription();

CHIP_ERROR StartSubscription(chip::Controller::DeviceController & controller, chip::NodeId nodeId, chip::EndpointId endpointId);

///////////////////////////////////////////////////////////////
// ReadClient::Callback implementation
///////////////////////////////////////////////////////////////
void OnAttributeData(const chip::app::ConcreteDataAttributePath & path, chip::TLV::TLVReader * data,
const chip::app::StatusIB & status) override;
void OnEventData(const chip::app::EventHeader & eventHeader, chip::TLV::TLVReader * data,
const chip::app::StatusIB * status) override;
void OnError(CHIP_ERROR error) override;
void OnDone(chip::app::ReadClient * apReadClient) override;

///////////////////////////////////////////////////////////////
// callbacks for CASE session establishment
///////////////////////////////////////////////////////////////
void OnDeviceConnected(chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle);
void OnDeviceConnectionFailure(const chip::ScopedNodeId & peerId, CHIP_ERROR error);

private:
std::unique_ptr<chip::app::ReadClient> mClient;

chip::Callback::Callback<chip::OnDeviceConnected> mOnDeviceConnectedCallback;
chip::Callback::Callback<chip::OnDeviceConnectionFailure> mOnDeviceConnectionFailureCallback;
chip::EndpointId mEndpointId;
bool subscriptionStarted = false;
};

} // namespace admin
Loading
Loading