From 1482400327caf73a381122915a0eee519c2123db Mon Sep 17 00:00:00 2001 From: Yufeng Wang Date: Sun, 11 Aug 2024 21:24:28 -0700 Subject: [PATCH] [FS Example] Update the FS Example apps to support fabric sync setup process part I (#34906) * [FS Example] Update the FS Example apps to support fabric sync setup process * Move all command handling to device manager using StringBuilder --- .../commands/clusters/ReportCommand.cpp | 10 +- .../commands/common/CHIPCommand.h | 2 + .../fabric-sync/FabricSyncCommand.cpp | 53 ++--- .../commands/fabric-sync/FabricSyncCommand.h | 8 - .../device_manager/DeviceManager.cpp | 214 +++++++++++++++++- .../device_manager/DeviceManager.h | 74 +++++- examples/fabric-admin/rpc/RpcServer.cpp | 8 +- .../fabric-bridge-common/BUILD.gn | 2 + .../include/CommissionerControl.h | 57 +++++ .../src/CommissionerControl.cpp | 175 ++++++++++++++ examples/fabric-bridge-app/linux/main.cpp | 9 + 11 files changed, 556 insertions(+), 56 deletions(-) create mode 100644 examples/fabric-bridge-app/fabric-bridge-common/include/CommissionerControl.h create mode 100644 examples/fabric-bridge-app/fabric-bridge-common/src/CommissionerControl.cpp diff --git a/examples/fabric-admin/commands/clusters/ReportCommand.cpp b/examples/fabric-admin/commands/clusters/ReportCommand.cpp index 8eb2cc7c33ae0d..ff52a30e661342 100644 --- a/examples/fabric-admin/commands/clusters/ReportCommand.cpp +++ b/examples/fabric-admin/commands/clusters/ReportCommand.cpp @@ -46,7 +46,7 @@ void ReportCommand::OnAttributeData(const app::ConcreteDataAttributePath & path, LogErrorOnFailure(RemoteDataModelLogger::LogAttributeAsJSON(path, data)); - DeviceMgr().HandleAttributeChange(path, data); + DeviceMgr().HandleAttributeData(path, data); } void ReportCommand::OnEventData(const app::EventHeader & eventHeader, TLV::TLVReader * data, const app::StatusIB * status) @@ -73,11 +73,5 @@ void ReportCommand::OnEventData(const app::EventHeader & eventHeader, TLV::TLVRe LogErrorOnFailure(RemoteDataModelLogger::LogEventAsJSON(eventHeader, data)); - CHIP_ERROR error = DataModelLogger::LogEvent(eventHeader, data); - if (CHIP_NO_ERROR != error) - { - ChipLogError(NotSpecified, "Response Failure: Can not decode Data"); - mError = error; - return; - } + DeviceMgr().HandleEventData(eventHeader, data); } diff --git a/examples/fabric-admin/commands/common/CHIPCommand.h b/examples/fabric-admin/commands/common/CHIPCommand.h index af833f5f718652..a02adbfbb594e5 100644 --- a/examples/fabric-admin/commands/common/CHIPCommand.h +++ b/examples/fabric-admin/commands/common/CHIPCommand.h @@ -51,6 +51,8 @@ inline constexpr char kIdentityGamma[] = "gamma"; // (CASE) communcation. inline constexpr char kIdentityNull[] = "null-fabric-commissioner"; +constexpr uint16_t kMaxCommandSize = 128; + class CHIPCommand : public Command { public: diff --git a/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp index 3e050e90904867..efb7cfd222827c 100644 --- a/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp +++ b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp @@ -35,9 +35,11 @@ namespace { // Constants constexpr uint32_t kCommissionPrepareTimeMs = 500; constexpr uint16_t kMaxManaulCodeLength = 21; -constexpr uint16_t kSubscribeMinInterval = 0; -constexpr uint16_t kSubscribeMaxInterval = 60; -constexpr uint16_t kRemoteBridgePort = 5540; + +void CheckFabricBridgeSynchronizationSupport(intptr_t ignored) +{ + DeviceMgr().ReadSupportedDeviceCategories(); +} } // namespace @@ -45,8 +47,16 @@ void FabricSyncAddBridgeCommand::OnCommissioningComplete(chip::NodeId deviceId, { if (mBridgeNodeId != deviceId) { - ChipLogProgress(NotSpecified, "Commissioning complete for non-bridge device: NodeId: " ChipLogFormatX64, - ChipLogValueX64(deviceId)); + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to pair non-bridge device (0x:" ChipLogFormatX64 ") with error: %" CHIP_ERROR_FORMAT, + ChipLogValueX64(deviceId), err.Format()); + } + else + { + ChipLogProgress(NotSpecified, "Commissioning complete for non-bridge device: NodeId: " ChipLogFormatX64, + ChipLogValueX64(deviceId)); + } return; } @@ -56,11 +66,15 @@ void FabricSyncAddBridgeCommand::OnCommissioningComplete(chip::NodeId deviceId, ChipLogProgress(NotSpecified, "Successfully paired bridge device: NodeId: " ChipLogFormatX64, ChipLogValueX64(mBridgeNodeId)); - char command[kMaxCommandSize]; - snprintf(command, sizeof(command), "descriptor subscribe parts-list %d %d %ld %d", kSubscribeMinInterval, - kSubscribeMaxInterval, mBridgeNodeId, kAggragatorEndpointId); + DeviceMgr().SubscribeRemoteFabricBridge(); - PushCommand(command); + // After successful commissioning of the Commissionee, initiate Reverse Commissioning + // via the Commissioner Control Cluster. However, we must first verify that the + // remote Fabric-Bridge supports Fabric Synchronization. + // + // Note: The Fabric-Admin MUST NOT send the RequestCommissioningApproval command + // if the remote Fabric-Bridge lacks Fabric Synchronization support. + DeviceLayer::PlatformMgr().ScheduleWork(CheckFabricBridgeSynchronizationSupport, 0); } else { @@ -80,10 +94,6 @@ CHIP_ERROR FabricSyncAddBridgeCommand::RunCommand(NodeId remoteId) return CHIP_NO_ERROR; } - char command[kMaxCommandSize]; - snprintf(command, sizeof(command), "pairing already-discovered %ld %d %s %d", remoteId, kSetupPinCode, - reinterpret_cast(mRemoteAddr.data()), kRemoteBridgePort); - PairingCommand * pairingCommand = static_cast(CommandMgr().GetCommandByName("pairing", "already-discovered")); if (pairingCommand == nullptr) @@ -95,7 +105,7 @@ CHIP_ERROR FabricSyncAddBridgeCommand::RunCommand(NodeId remoteId) pairingCommand->RegisterCommissioningDelegate(this); mBridgeNodeId = remoteId; - PushCommand(command); + DeviceMgr().PairRemoteFabricBridge(remoteId, reinterpret_cast(mRemoteAddr.data())); return CHIP_NO_ERROR; } @@ -136,9 +146,6 @@ CHIP_ERROR FabricSyncRemoveBridgeCommand::RunCommand() mBridgeNodeId = bridgeNodeId; - char command[kMaxCommandSize]; - snprintf(command, sizeof(command), "pairing unpair %ld", mBridgeNodeId); - PairingCommand * pairingCommand = static_cast(CommandMgr().GetCommandByName("pairing", "unpair")); if (pairingCommand == nullptr) @@ -149,7 +156,7 @@ CHIP_ERROR FabricSyncRemoveBridgeCommand::RunCommand() pairingCommand->RegisterPairingDelegate(this); - PushCommand(command); + DeviceMgr().UnpairRemoteFabricBridge(); return CHIP_NO_ERROR; } @@ -163,9 +170,7 @@ void FabricSyncDeviceCommand::OnCommissioningWindowOpened(NodeId deviceId, CHIP_ CHIP_ERROR error = ManualSetupPayloadGenerator(payload).payloadDecimalStringRepresentation(manualCode); if (error == CHIP_NO_ERROR) { - char command[kMaxCommandSize]; NodeId nodeId = DeviceMgr().GetNextAvailableNodeId(); - snprintf(command, sizeof(command), "pairing code %ld %s", nodeId, payloadBuffer); PairingCommand * pairingCommand = static_cast(CommandMgr().GetCommandByName("pairing", "code")); @@ -180,7 +185,7 @@ void FabricSyncDeviceCommand::OnCommissioningWindowOpened(NodeId deviceId, CHIP_ usleep(kCommissionPrepareTimeMs * 1000); - PushCommand(command); + DeviceMgr().PairRemoteDevice(nodeId, payloadBuffer); } else { @@ -224,10 +229,6 @@ CHIP_ERROR FabricSyncDeviceCommand::RunCommand(EndpointId remoteId) return CHIP_NO_ERROR; } - char command[kMaxCommandSize]; - snprintf(command, sizeof(command), "pairing open-commissioning-window %ld %d %d %d %d %d", DeviceMgr().GetRemoteBridgeNodeId(), - remoteId, kEnhancedCommissioningMethod, kWindowTimeout, kIteration, kDiscriminator); - OpenCommissioningWindowCommand * openCommand = static_cast(CommandMgr().GetCommandByName("pairing", "open-commissioning-window")); @@ -238,7 +239,7 @@ CHIP_ERROR FabricSyncDeviceCommand::RunCommand(EndpointId remoteId) openCommand->RegisterDelegate(this); - PushCommand(command); + DeviceMgr().OpenRemoteDeviceCommissioningWindow(remoteId); return CHIP_NO_ERROR; } diff --git a/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h index 038094583c8554..ca1b105207f832 100644 --- a/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h +++ b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h @@ -22,14 +22,6 @@ #include #include -constexpr uint32_t kSetupPinCode = 20202021; -constexpr uint16_t kMaxCommandSize = 64; -constexpr uint16_t kDiscriminator = 3840; -constexpr uint16_t kWindowTimeout = 300; -constexpr uint16_t kIteration = 1000; -constexpr uint16_t kAggragatorEndpointId = 1; -constexpr uint8_t kEnhancedCommissioningMethod = 1; - class FabricSyncAddBridgeCommand : public CHIPCommand, public CommissioningDelegate { public: diff --git a/examples/fabric-admin/device_manager/DeviceManager.cpp b/examples/fabric-admin/device_manager/DeviceManager.cpp index 4ea0b033c47c7f..aea9722bc8fa27 100644 --- a/examples/fabric-admin/device_manager/DeviceManager.cpp +++ b/examples/fabric-admin/device_manager/DeviceManager.cpp @@ -19,6 +19,8 @@ #include "DeviceManager.h" #include +#include +#include #include #include @@ -26,6 +28,21 @@ using namespace chip; using namespace chip::app::Clusters; +namespace { + +// Constants +constexpr uint32_t kSetupPinCode = 20202021; +constexpr uint16_t kRemoteBridgePort = 5540; +constexpr uint16_t kDiscriminator = 3840; +constexpr uint16_t kWindowTimeout = 300; +constexpr uint16_t kIteration = 1000; +constexpr uint16_t kSubscribeMinInterval = 0; +constexpr uint16_t kSubscribeMaxInterval = 60; +constexpr uint16_t kAggragatorEndpointId = 1; +constexpr uint8_t kEnhancedCommissioningMethod = 1; + +} // namespace + // Define the static member DeviceManager DeviceManager::sInstance; @@ -97,8 +114,154 @@ void DeviceManager::RemoveSyncedDevice(NodeId nodeId) ChipLogValueX64(device->GetNodeId()), device->GetEndpointId()); } -void DeviceManager::HandleAttributeChange(const app::ConcreteDataAttributePath & path, TLV::TLVReader * data) +void DeviceManager::OpenDeviceCommissioningWindow(NodeId nodeId, uint32_t commissioningTimeout, uint32_t iterations, + uint32_t discriminator, const char * saltHex, const char * verifierHex) +{ + // Open the commissioning window of a device within its own fabric. + StringBuilder<512> commandBuilder; + + commandBuilder.Add("pairing open-commissioning-window "); + commandBuilder.AddFormat("%lu %d %d %d %d %d --salt hex:%s --verifier hex:%s", nodeId, kRootEndpointId, + kEnhancedCommissioningMethod, commissioningTimeout, iterations, discriminator, saltHex, verifierHex); + + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::OpenRemoteDeviceCommissioningWindow(EndpointId remoteEndpointId) +{ + // Open the commissioning window of a device from another fabric via its fabric bridge. + // This method constructs and sends a command to open the commissioning window for a device + // that is part of a different fabric, accessed through a fabric bridge. + StringBuilder commandBuilder; + + commandBuilder.Add("pairing open-commissioning-window "); + commandBuilder.AddFormat("%lu %d %d %d %d %d", mRemoteBridgeNodeId, remoteEndpointId, kEnhancedCommissioningMethod, + kWindowTimeout, kIteration, kDiscriminator); + + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::PairRemoteFabricBridge(NodeId nodeId, const char * deviceRemoteIp) +{ + StringBuilder commandBuilder; + + commandBuilder.Add("pairing already-discovered "); + commandBuilder.AddFormat("%lu %d %s %d", nodeId, kSetupPinCode, deviceRemoteIp, kRemoteBridgePort); + + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::PairRemoteDevice(chip::NodeId nodeId, const char * payload) +{ + StringBuilder commandBuilder; + + commandBuilder.Add("pairing code "); + commandBuilder.AddFormat("%lu %s", nodeId, payload); + + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::UnpairRemoteFabricBridge() +{ + StringBuilder commandBuilder; + + commandBuilder.Add("pairing unpair "); + commandBuilder.AddFormat("%lu", mRemoteBridgeNodeId); + + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::SubscribeRemoteFabricBridge() +{ + // Listen to the state changes of the remote fabric bridge. + StringBuilder commandBuilder; + + // Prepare and push the descriptor subscribe command + commandBuilder.Add("descriptor subscribe parts-list "); + commandBuilder.AddFormat("%d %d %lu %d", kSubscribeMinInterval, kSubscribeMaxInterval, mRemoteBridgeNodeId, + kAggragatorEndpointId); + PushCommand(commandBuilder.c_str()); + + // Clear the builder for the next command + commandBuilder.Reset(); + + // Prepare and push the commissioner control subscribe command + commandBuilder.Add("commissionercontrol subscribe-event commissioning-request-result "); + commandBuilder.AddFormat("%d %d %lu %d --is-urgent true", kSubscribeMinInterval, kSubscribeMaxInterval, mRemoteBridgeNodeId, + kRootEndpointId); + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::ReadSupportedDeviceCategories() +{ + if (!IsFabricSyncReady()) + { + // print to console + fprintf(stderr, "Remote Fabric Bridge is not configured yet.\n"); + return; + } + + StringBuilder commandBuilder; + + commandBuilder.Add("commissionercontrol read supported-device-categories "); + commandBuilder.AddFormat("%ld ", mRemoteBridgeNodeId); + commandBuilder.AddFormat("%d", kRootEndpointId); + + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::StartReverseCommissioning() +{ + ChipLogProgress(NotSpecified, "Starting reverse commissioning for bridge device: NodeId: " ChipLogFormatX64, + ChipLogValueX64(mRemoteBridgeNodeId)); + + uint64_t requestId = Crypto::GetRandU64(); + uint16_t vendorId = static_cast(CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID); + uint16_t productId = static_cast(CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID); + + StringBuilder commandBuilder; + commandBuilder.Add("commissionercontrol request-commissioning-approval "); + commandBuilder.AddFormat("%lu %u %u %lu %d", requestId, vendorId, productId, mRemoteBridgeNodeId, kRootEndpointId); + + mRequestId = requestId; + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::CommissionApprovedRequest(uint64_t requestId, uint16_t responseTimeoutSeconds) { + ChipLogProgress(NotSpecified, "Request the Commissioner Control Server to begin commissioning a previously approved request."); + + StringBuilder commandBuilder; + commandBuilder.Add("commissionercontrol commission-node "); + commandBuilder.AddFormat("%lu %u %lu %d", requestId, responseTimeoutSeconds, mRemoteBridgeNodeId, kRootEndpointId); + + PushCommand(commandBuilder.c_str()); +} + +void DeviceManager::HandleAttributeData(const app::ConcreteDataAttributePath & path, TLV::TLVReader * data) +{ + if (path.mClusterId == CommissionerControl::Id && + path.mAttributeId == CommissionerControl::Attributes::SupportedDeviceCategories::Id) + { + ChipLogProgress(NotSpecified, "Attribute SupportedDeviceCategories detected."); + + BitMask value; + CHIP_ERROR error = app::DataModel::Decode(*data, value); + if (error != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to decode attribute value. Error: %" CHIP_ERROR_FORMAT, error.Format()); + return; + } + + if (value.Has(CommissionerControl::SupportedDeviceCategoryBitmap::kFabricSynchronization)) + { + ChipLogProgress(NotSpecified, "Remote Fabric-Bridge supports Fabric Synchronization, start reverse commissioning."); + StartReverseCommissioning(); + } + + return; + } + if (path.mClusterId != Descriptor::Id || path.mAttributeId != Descriptor::Attributes::PartsList::Id) { return; @@ -167,9 +330,10 @@ void DeviceManager::HandleAttributeChange(const app::ConcreteDataAttributePath & if (mAutoSyncEnabled) { - char command[64]; - snprintf(command, sizeof(command), "fabricsync sync-device %d", endpoint); - PushCommand(command); + StringBuilder commandBuilder; + commandBuilder.Add("fabricsync sync-device "); + commandBuilder.AddFormat("%d", endpoint); + PushCommand(commandBuilder.c_str()); } } @@ -188,8 +352,9 @@ void DeviceManager::HandleAttributeChange(const app::ConcreteDataAttributePath & if (mAutoSyncEnabled) { - char command[64]; - snprintf(command, sizeof(command), "pairing unpair %ld", device->GetNodeId()); + StringBuilder commandBuilder; + commandBuilder.Add("pairing unpair "); + commandBuilder.AddFormat("%lu", device->GetNodeId()); PairingCommand * pairingCommand = static_cast(CommandMgr().GetCommandByName("pairing", "unpair")); @@ -200,11 +365,46 @@ void DeviceManager::HandleAttributeChange(const app::ConcreteDataAttributePath & } pairingCommand->RegisterPairingDelegate(this); - PushCommand(command); + PushCommand(commandBuilder.c_str()); } } } +void DeviceManager::HandleEventData(const chip::app::EventHeader & header, chip::TLV::TLVReader * data) +{ + if (header.mPath.mClusterId != CommissionerControl::Id || + header.mPath.mEventId != CommissionerControl::Events::CommissioningRequestResult::Id) + { + return; + } + + ChipLogProgress(NotSpecified, "CommissioningRequestResult event received."); + + CommissionerControl::Events::CommissioningRequestResult::DecodableType value; + CHIP_ERROR error = app::DataModel::Decode(*data, value); + if (error != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to decode event value. Error: %" CHIP_ERROR_FORMAT, error.Format()); + return; + } + + if (value.requestId != mRequestId) + { + ChipLogError(NotSpecified, "The RequestId does not match the RequestId provided to RequestCommissioningApproval"); + return; + } + + if (value.statusCode != static_cast(Protocols::InteractionModel::Status::Success)) + { + ChipLogError(NotSpecified, "The server is not ready to begin commissioning the requested device"); + return; + } + + // The server is ready to begin commissioning the requested device, request the Commissioner Control Server to begin + // commissioning a previously approved request. + CommissionApprovedRequest(value.requestId, kResponseTimeoutSeconds); +} + void DeviceManager::OnDeviceRemoved(NodeId deviceId, CHIP_ERROR err) { if (err != CHIP_NO_ERROR) diff --git a/examples/fabric-admin/device_manager/DeviceManager.h b/examples/fabric-admin/device_manager/DeviceManager.h index 6270cfd40e9df0..cad573a9311421 100644 --- a/examples/fabric-admin/device_manager/DeviceManager.h +++ b/examples/fabric-admin/device_manager/DeviceManager.h @@ -24,6 +24,8 @@ #include +constexpr uint16_t kResponseTimeoutSeconds = 30; + class Device { public: @@ -67,7 +69,76 @@ class DeviceManager : public PairingDelegate void RemoveSyncedDevice(chip::NodeId nodeId); - void HandleAttributeChange(const chip::app::ConcreteDataAttributePath & path, chip::TLV::TLVReader * data); + /** + * @brief Open the commissioning window for a specific device within its own fabric. + * + * This function initiates the process to open the commissioning window for a device identified by the given node ID. + * + * @param nodeId The ID of the node that should open the commissioning window. + * @param commissioningTimeout The time in seconds before the commissioning window closes. This value determines + * how long the commissioning window remains open for incoming connections. + * @param iterations The number of PBKDF (Password-Based Key Derivation Function) iterations to use + * for deriving the PAKE (Password Authenticated Key Exchange) verifier. + * @param discriminator The device-specific discriminator, determined during commissioning, which helps + * to uniquely identify the device among others. + * @param saltHex The hexadecimal-encoded salt used in the cryptographic operations for commissioning. + * @param verifierHex The hexadecimal-encoded PAKE verifier used to authenticate the commissioning process. + * + */ + void OpenDeviceCommissioningWindow(chip::NodeId nodeId, uint32_t commissioningTimeout, uint32_t iterations, + uint32_t discriminator, const char * saltHex, const char * verifierHex); + + /** + * @brief Open the commissioning window of a device from another fabric via its fabric bridge. + * + * This function initiates the process to open the commissioning window for a device that belongs to another + * fabric, accessed through a fabric bridge. + * + * @param remoteEndpointId The endpoint ID of the remote device that should open the commissioning window. + * This endpoint is associated with the device in the other fabric, accessed via the + * fabric bridge. + * + * @note This function is used when the device to be commissioned is part of a different fabric and must be + * accessed through an intermediary fabric bridge. + */ + void OpenRemoteDeviceCommissioningWindow(chip::EndpointId remoteEndpointId); + + /** + * @brief Pair a remote fabric bridge with a given node ID. + * + * This function initiates the pairing process for a remote fabric bridge using the specified parameters. + + * @param nodeId The user-defined ID for the node being commissioned. It doesn’t need to be the same ID, + * as for the first fabric. + * @param deviceRemoteIp The IP address of the remote device that is being paired as part of the fabric bridge. + */ + void PairRemoteFabricBridge(chip::NodeId nodeId, const char * deviceRemoteIp); + + /** + * @brief Pair a remote Matter device to the current fabric. + * + * This function initiates the pairing process for a remote device using the specified parameters. + + * @param nodeId The user-defined ID for the node being commissioned. It doesn’t need to be the same ID, + * as for the first fabric. + * @param payload The the QR code payload or a manual pairing code generated by the first commissioner + * instance when opened commissioning window. + */ + void PairRemoteDevice(chip::NodeId nodeId, const char * payload); + + void UnpairRemoteFabricBridge(); + + void SubscribeRemoteFabricBridge(); + + void StartReverseCommissioning(); + + void ReadSupportedDeviceCategories(); + + void CommissionApprovedRequest(uint64_t requestId, uint16_t responseTimeoutSeconds); + + void HandleAttributeData(const chip::app::ConcreteDataAttributePath & path, chip::TLV::TLVReader * data); + + void HandleEventData(const chip::app::EventHeader & header, chip::TLV::TLVReader * data); void OnDeviceRemoved(chip::NodeId deviceId, CHIP_ERROR err) override; @@ -81,6 +152,7 @@ class DeviceManager : public PairingDelegate std::set mSyncedDevices; bool mAutoSyncEnabled = false; bool mInitialized = false; + uint64_t mRequestId = 0; Device * FindDeviceByEndpoint(chip::EndpointId endpointId); Device * FindDeviceByNode(chip::NodeId nodeId); diff --git a/examples/fabric-admin/rpc/RpcServer.cpp b/examples/fabric-admin/rpc/RpcServer.cpp index cb06b359d68a71..16ff695e159712 100644 --- a/examples/fabric-admin/rpc/RpcServer.cpp +++ b/examples/fabric-admin/rpc/RpcServer.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -54,12 +55,7 @@ class FabricAdmin final : public rpc::FabricAdmin ChipLogProgress(NotSpecified, "Received OpenCommissioningWindow request: 0x%lx", nodeId); - char command[512]; - snprintf(command, sizeof(command), "pairing open-commissioning-window %ld %d %d %d %d %d --salt hex:%s --verifier hex:%s", - nodeId, kRootEndpointId, kEnhancedCommissioningMethod, commissioningTimeout, iterations, discriminator, saltHex, - verifierHex); - - PushCommand(command); + DeviceMgr().OpenDeviceCommissioningWindow(nodeId, commissioningTimeout, iterations, discriminator, saltHex, verifierHex); response.success = true; diff --git a/examples/fabric-bridge-app/fabric-bridge-common/BUILD.gn b/examples/fabric-bridge-app/fabric-bridge-common/BUILD.gn index 7f2fbcbbfe0556..b7b07d3be37bc3 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/BUILD.gn +++ b/examples/fabric-bridge-app/fabric-bridge-common/BUILD.gn @@ -45,9 +45,11 @@ source_set("fabric-bridge-lib") { "include/BridgedDeviceBasicInformationImpl.h", "include/BridgedDeviceManager.h", "include/CHIPProjectAppConfig.h", + "include/CommissionerControl.h", "src/BridgedDevice.cpp", "src/BridgedDeviceBasicInformationImpl.cpp", "src/BridgedDeviceManager.cpp", + "src/CommissionerControl.cpp", "src/ZCLCallbacks.cpp", ] diff --git a/examples/fabric-bridge-app/fabric-bridge-common/include/CommissionerControl.h b/examples/fabric-bridge-app/fabric-bridge-common/include/CommissionerControl.h new file mode 100644 index 00000000000000..b5caa6a2ce1f3d --- /dev/null +++ b/examples/fabric-bridge-app/fabric-bridge-common/include/CommissionerControl.h @@ -0,0 +1,57 @@ +/* + * + * 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 +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace CommissionerControl { + +class CommissionerControlDelegate : public Delegate +{ +public: + CHIP_ERROR HandleCommissioningApprovalRequest(const CommissioningApprovalRequest & request) override; + CHIP_ERROR ValidateCommissionNodeCommand(NodeId clientNodeId, uint64_t requestId) override; + CHIP_ERROR GetCommissioningWindowParams(CommissioningWindowParams & outParams) override; + CHIP_ERROR ReverseCommissionNode(const CommissioningWindowParams & params, const Optional & ipAddress, + const Optional & port) override; + + ~CommissionerControlDelegate() = default; + +private: + static constexpr size_t kLabelBufferSize = 64; + + uint64_t mRequestId = 0; + NodeId mClientNodeId = kUndefinedNodeId; + VendorId mVendorId = VendorId::Unspecified; + uint16_t mProductId = 0; + char mLabelBuffer[kLabelBufferSize + 1]; + Optional mLabel; +}; + +} // namespace CommissionerControl +} // namespace Clusters +} // namespace app +} // namespace chip + +CHIP_ERROR CommissionerControlInit(); +CHIP_ERROR CommissionerControlShutdown(); diff --git a/examples/fabric-bridge-app/fabric-bridge-common/src/CommissionerControl.cpp b/examples/fabric-bridge-app/fabric-bridge-common/src/CommissionerControl.cpp new file mode 100644 index 00000000000000..076ced816596d2 --- /dev/null +++ b/examples/fabric-bridge-app/fabric-bridge-common/src/CommissionerControl.cpp @@ -0,0 +1,175 @@ +/* + * + * 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 +#include +#include +#include + +using namespace chip; +using namespace chip::app; + +namespace { + +std::unique_ptr sCommissionerControlDelegate; + +} // namespace + +namespace chip { +namespace app { +namespace Clusters { +namespace CommissionerControl { + +CHIP_ERROR CommissionerControlDelegate::HandleCommissioningApprovalRequest(const CommissioningApprovalRequest & request) +{ + CommissionerControl::Events::CommissioningRequestResult::Type result; + result.requestId = request.requestId; + result.clientNodeId = request.clientNodeId; + result.fabricIndex = request.fabricIndex; + result.statusCode = static_cast(Protocols::InteractionModel::Status::Success); + + mRequestId = request.requestId; + mClientNodeId = request.clientNodeId; + mVendorId = request.vendorId; + mProductId = request.productId; + + if (request.label.HasValue()) + { + const CharSpan & labelSpan = request.label.Value(); + size_t labelLength = labelSpan.size(); + + if (labelLength >= kLabelBufferSize) + { + ChipLogError(Zcl, "Label too long to fit in buffer"); + return CHIP_ERROR_BUFFER_TOO_SMALL; + } + + if (labelLength == 0) + { + mLabel.ClearValue(); + } + else + { + memcpy(mLabelBuffer, labelSpan.data(), labelLength); + mLabelBuffer[labelLength] = '\0'; // Null-terminate the copied string + mLabel.SetValue(CharSpan(mLabelBuffer, labelLength)); + } + } + else + { + mLabel.ClearValue(); + } + + return CommissionerControlServer::Instance().GenerateCommissioningRequestResultEvent(result); +} + +CHIP_ERROR CommissionerControlDelegate::ValidateCommissionNodeCommand(NodeId clientNodeId, uint64_t requestId) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + // Verify if the CommissionNode command is sent from the same NodeId as the RequestCommissioningApproval. + VerifyOrExit(mClientNodeId == clientNodeId, err = CHIP_ERROR_WRONG_NODE_ID); + + // Verify if the provided RequestId matches the value provided to the RequestCommissioningApproval. + VerifyOrExit(mRequestId == requestId, err = CHIP_ERROR_INCORRECT_STATE); + +exit: + return err; +} + +CHIP_ERROR CommissionerControlDelegate::GetCommissioningWindowParams(CommissioningWindowParams & outParams) +{ + // TODO: Populate outParams with the required details. + // outParams.commissioningWindowParams.iterations = mIterations; + // outParams.commissioningWindowParams.commissioningTimeout = mCommissioningTimeout; + // outParams.commissioningWindowParams.discriminator = mDiscriminator; + // outParams.commissioningWindowParams.PAKEPasscodeVerifier = mPAKEPasscodeVerifier; + // outParams.commissioningWindowParams.salt = mSalt; + + // outParams.ipAddress = mIpAddress; + // outParams.port = mPort; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR CommissionerControlDelegate::ReverseCommissionNode(const CommissioningWindowParams & params, + const Optional & ipAddress, const Optional & port) +{ + return CHIP_NO_ERROR; +} + +} // namespace CommissionerControl +} // namespace Clusters +} // namespace app +} // namespace chip + +CHIP_ERROR CommissionerControlInit() +{ + CHIP_ERROR err; + + if (sCommissionerControlDelegate) + { + ChipLogError(NotSpecified, "Commissioner Control Delegate already exists."); + return CHIP_ERROR_INCORRECT_STATE; + } + + sCommissionerControlDelegate = std::make_unique(); + if (!sCommissionerControlDelegate) + { + ChipLogError(NotSpecified, "Failed to allocate memory for Commissioner Control Delegate."); + return CHIP_ERROR_NO_MEMORY; + } + + err = Clusters::CommissionerControl::CommissionerControlServer::Instance().Init(*sCommissionerControlDelegate); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Initialization failed on Commissioner Control Delegate."); + sCommissionerControlDelegate.reset(); + return err; + } + + ChipLogProgress(Zcl, "Initializing SupportedDeviceCategories of Commissioner Control Cluster for this device."); + + BitMask supportedDeviceCategories; + supportedDeviceCategories.SetField(Clusters::CommissionerControl::SupportedDeviceCategoryBitmap::kFabricSynchronization, 1); + + Protocols::InteractionModel::Status status = + Clusters::CommissionerControl::CommissionerControlServer::Instance().SetSupportedDeviceCategoriesValue( + kRootEndpointId, supportedDeviceCategories); + + if (status != Protocols::InteractionModel::Status::Success) + { + ChipLogError(NotSpecified, "Failed to set SupportedDeviceCategories: %d", static_cast(status)); + sCommissionerControlDelegate.reset(); + return CHIP_ERROR_INTERNAL; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR CommissionerControlShutdown() +{ + if (sCommissionerControlDelegate) + { + sCommissionerControlDelegate.reset(); + } + + return CHIP_NO_ERROR; +} diff --git a/examples/fabric-bridge-app/linux/main.cpp b/examples/fabric-bridge-app/linux/main.cpp index 1678ec59a81a1d..57907db8872b0a 100644 --- a/examples/fabric-bridge-app/linux/main.cpp +++ b/examples/fabric-bridge-app/linux/main.cpp @@ -22,6 +22,7 @@ #include "BridgedDeviceBasicInformationImpl.h" #include "BridgedDeviceManager.h" #include "CommissionableInit.h" +#include "CommissionerControl.h" #include #include @@ -257,11 +258,19 @@ void ApplicationInit() pollingThread.detach(); BridgeDeviceMgr().Init(); + + VerifyOrDieWithMsg(CommissionerControlInit() == CHIP_NO_ERROR, NotSpecified, + "Failed to initialize Commissioner Control Server"); } void ApplicationShutdown() { ChipLogDetail(NotSpecified, "Fabric-Bridge: ApplicationShutdown()"); + + if (CommissionerControlShutdown() != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to shutdown Commissioner Control Server"); + } } int main(int argc, char * argv[])