diff --git a/docs/guides/esp32/providers.md b/docs/guides/esp32/providers.md index 59b91b624a49fc..5174e1ccaceb11 100644 --- a/docs/guides/esp32/providers.md +++ b/docs/guides/esp32/providers.md @@ -15,8 +15,6 @@ Below are the providers that have been implemented: - [Device Info Provider](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32DeviceInfoProvider.h#L31) This provider provides fixed labels, supported calendar types, and supported locales from the factory partition. -- [Supported Modes](https://github.com/project-chip/connectedhomeip/blob/master/examples/platform/esp32/mode-support/static-supported-modes-manager.h#L28) - This provider offers the supported modes for the mode-select cluster. More information can be found in the [factory data guide](factory_data.md). diff --git a/examples/all-clusters-app/esp32/main/CMakeLists.txt b/examples/all-clusters-app/esp32/main/CMakeLists.txt index 7fd1d548609257..dc41290765313a 100644 --- a/examples/all-clusters-app/esp32/main/CMakeLists.txt +++ b/examples/all-clusters-app/esp32/main/CMakeLists.txt @@ -34,7 +34,6 @@ set(SRC_DIRS_LIST "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/ota" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/common" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/shell_extension" - "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/mode-support" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/icd/server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/util" @@ -106,8 +105,6 @@ set(SRC_DIRS_LIST ) -set(EXCLUDE_SRCS "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/all-clusters-app/all-clusters-common/src/static-supported-modes-manager.cpp") - if (CONFIG_ENABLE_PW_RPC) # Append additional directories for RPC build set(PRIV_INCLUDE_DIRS_LIST "${PRIV_INCLUDE_DIRS_LIST}" diff --git a/examples/all-clusters-app/esp32/main/main.cpp b/examples/all-clusters-app/esp32/main/main.cpp index af94d2b2d36afe..b85f47d883c620 100644 --- a/examples/all-clusters-app/esp32/main/main.cpp +++ b/examples/all-clusters-app/esp32/main/main.cpp @@ -41,7 +41,6 @@ #include #include #include -#include #include #include @@ -122,13 +121,6 @@ static void InitServer(intptr_t context) #if CONFIG_DEVICE_TYPE_M5STACK SetupPretendDevices(); #endif - CHIP_ERROR err = - app::Clusters::ModeSelect::StaticSupportedModesManager::getStaticSupportedModesManagerInstance().InitEndpointArray( - FIXED_ENDPOINT_COUNT); - if (err != CHIP_NO_ERROR) - { - ESP_LOGE(TAG, "Failed to initialize endpoint array for supported-modes, err:%" CHIP_ERROR_FORMAT, err.Format()); - } app::Clusters::TemperatureControl::SetInstance(&sAppSupportedTemperatureLevelsDelegate); } diff --git a/examples/chef/chef.py b/examples/chef/chef.py index 6176c6e8c280ac..f681f3a059f4fc 100755 --- a/examples/chef/chef.py +++ b/examples/chef/chef.py @@ -878,7 +878,7 @@ def main() -> int: """)) if options.do_clean: shell.run_cmd("rm -rf out") - shell.run_cmd("gn gen out") + shell.run_cmd("gn gen --export-compile-commands out") shell.run_cmd("ninja -C out") # diff --git a/examples/chip-tool/commands/clusters/ClusterCommand.h b/examples/chip-tool/commands/clusters/ClusterCommand.h index ac141fb012e259..bf5c61fd02bd3f 100644 --- a/examples/chip-tool/commands/clusters/ClusterCommand.h +++ b/examples/chip-tool/commands/clusters/ClusterCommand.h @@ -21,6 +21,7 @@ #include "DataModelLogger.h" #include "ModelCommand.h" #include +#include class ClusterCommand : public InteractionModelCommands, public ModelCommand, public chip::app::CommandSender::Callback { @@ -70,6 +71,7 @@ class ClusterCommand : public InteractionModelCommands, public ModelCommand, pub ReturnErrorOnFailure(InteractionModelCommands::SendCommand(device, endpointId, clusterId, commandId, value)); mScopedNodeId = chip::ScopedNodeId(value.checkInNodeID, device->GetSecureSession().Value()->GetFabricIndex()); mMonitoredSubject = value.monitoredSubject; + mClientType = value.clientType; memcpy(mICDSymmetricKey, value.key.data(), value.key.size()); return CHIP_NO_ERROR; } @@ -148,6 +150,7 @@ class ClusterCommand : public InteractionModelCommands, public ModelCommand, pub clientInfo.peer_node = mScopedNodeId; clientInfo.monitored_subject = mMonitoredSubject; clientInfo.start_icd_counter = value.ICDCounter; + clientInfo.client_type = mClientType; StoreICDEntryWithKey(clientInfo, chip::ByteSpan(mICDSymmetricKey)); } @@ -258,8 +261,10 @@ class ClusterCommand : public InteractionModelCommands, public ModelCommand, pub chip::ClusterId mClusterId; chip::CommandId mCommandId; chip::ScopedNodeId mScopedNodeId; - uint64_t mMonitoredSubject = static_cast(0); + uint64_t mMonitoredSubject = static_cast(0); + chip::app::Clusters::IcdManagement::ClientTypeEnum mClientType = chip::app::Clusters::IcdManagement::ClientTypeEnum::kPermanent; uint8_t mICDSymmetricKey[chip::Crypto::kAES_CCM128_Key_Length]; + CHIP_ERROR mError = CHIP_NO_ERROR; CustomArgument mPayload; }; diff --git a/examples/chip-tool/commands/icd/ICDCommand.cpp b/examples/chip-tool/commands/icd/ICDCommand.cpp index 505e6adfbfea3d..3f7bfb328d5273 100644 --- a/examples/chip-tool/commands/icd/ICDCommand.cpp +++ b/examples/chip-tool/commands/icd/ICDCommand.cpp @@ -21,6 +21,7 @@ #include #include #include +#include using namespace ::chip; using namespace ::chip::app; @@ -36,31 +37,32 @@ CHIP_ERROR ICDListCommand::RunCommand() return CHIP_ERROR_NO_MEMORY; } app::DefaultICDClientStorage::ICDClientInfoIteratorWrapper clientInfoIteratorWrapper(iter); - fprintf(stderr, " +-----------------------------------------------------------------------------+\n"); - fprintf(stderr, " | %-75s |\n", "Known ICDs:"); - fprintf(stderr, " +-----------------------------------------------------------------------------+\n"); - fprintf(stderr, " | %20s | %15s | %15s | %16s |\n", "Fabric Index:Node ID", "Start Counter", "Counter Offset", - "MonitoredSubject"); + fprintf(stderr, " +------------------------------------------------------------------------------------------+\n"); + fprintf(stderr, " | %-88s |\n", "Known ICDs:"); + fprintf(stderr, " +------------------------------------------------------------------------------------------+\n"); + fprintf(stderr, " | %20s | %15s | %15s | %16s | %10s |\n", "Fabric Index:Node ID", "Start Counter", "Counter Offset", + "MonitoredSubject", "ClientType"); while (iter->Next(info)) { - fprintf(stderr, " +-----------------------------------------------------------------------------+\n"); - fprintf(stderr, " | %3" PRIu32 ":" ChipLogFormatX64 " | %15" PRIu32 " | %15" PRIu32 " | " ChipLogFormatX64 " |\n", + fprintf(stderr, " +------------------------------------------------------------------------------------------+\n"); + fprintf(stderr, " | %3" PRIu32 ":" ChipLogFormatX64 " | %15" PRIu32 " | %15" PRIu32 " | " ChipLogFormatX64 " | %10u |\n", static_cast(info.peer_node.GetFabricIndex()), ChipLogValueX64(info.peer_node.GetNodeId()), - info.start_icd_counter, info.offset, ChipLogValueX64(info.monitored_subject)); + info.start_icd_counter, info.offset, ChipLogValueX64(info.monitored_subject), + static_cast(info.client_type)); static_assert(std::is_same::value, "The following BytesToHex can copy/encode the key bytes from sharedKey to hexadecimal format, which only " "works for RawKeySessionKeystore"); Encoding::BytesToHex(info.aes_key_handle.As(), Crypto::kAES_CCM128_Key_Length, icdAesKeyHex, sizeof(icdAesKeyHex), chip::Encoding::HexFlags::kNullTerminate); - fprintf(stderr, " | aes key: %60s |\n", icdAesKeyHex); + fprintf(stderr, " | aes key: %60s |\n", icdAesKeyHex); Encoding::BytesToHex(info.hmac_key_handle.As(), Crypto::kHMAC_CCM128_Key_Length, icdHmacKeyHex, sizeof(icdHmacKeyHex), chip::Encoding::HexFlags::kNullTerminate); - fprintf(stderr, " | hmac key: %60s |\n", icdHmacKeyHex); + fprintf(stderr, " | hmac key: %60s |\n", icdHmacKeyHex); } - fprintf(stderr, " +-----------------------------------------------------------------------------+\n"); + fprintf(stderr, " +------------------------------------------------------------------------------------------+\n"); SetCommandExitStatus(CHIP_NO_ERROR); return CHIP_NO_ERROR; } diff --git a/examples/chip-tool/commands/pairing/PairingCommand.cpp b/examples/chip-tool/commands/pairing/PairingCommand.cpp index 7bddfa3d08039c..35ce2ba1cc7aaa 100644 --- a/examples/chip-tool/commands/pairing/PairingCommand.cpp +++ b/examples/chip-tool/commands/pairing/PairingCommand.cpp @@ -157,6 +157,10 @@ CommissioningParameters PairingCommand::GetCommissioningParameters() { 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()); @@ -166,6 +170,7 @@ CommissioningParameters PairingCommand::GetCommissioningParameters() } params.SetICDCheckInNodeId(mICDCheckInNodeId.Value()); params.SetICDMonitoredSubject(mICDMonitoredSubject.Value()); + params.SetICDClientType(mICDClientType.Value()); } return params; @@ -459,7 +464,7 @@ void PairingCommand::OnICDRegistrationComplete(ScopedNodeId nodeId, uint32_t icd sizeof(icdSymmetricKeyHex), chip::Encoding::HexFlags::kNullTerminate); app::ICDClientInfo clientInfo; - clientInfo.peer_node = nodeId; + clientInfo.peer_node = chip::ScopedNodeId(mICDCheckInNodeId.Value(), nodeId.GetFabricIndex()); clientInfo.monitored_subject = mICDMonitoredSubject.Value(); clientInfo.start_icd_counter = icdCounter; diff --git a/examples/chip-tool/commands/pairing/PairingCommand.h b/examples/chip-tool/commands/pairing/PairingCommand.h index facb0f2403536b..0cf4e1de2de713 100644 --- a/examples/chip-tool/commands/pairing/PairingCommand.h +++ b/examples/chip-tool/commands/pairing/PairingCommand.h @@ -74,6 +74,8 @@ class PairingCommand : public CHIPCommand, "The check-in node id for the ICD, default: node id of the commissioner."); AddArgument("icd-monitored-subject", 0, UINT64_MAX, &mICDMonitoredSubject, "The monitored subject of the ICD, default: The node id used for icd-check-in-nodeid."); + AddArgument("icd-client-type", 0, 1, &mICDClientType, + "The ClientType of the client registering, default: Permanent client - 0"); AddArgument("icd-symmetric-key", &mICDSymmetricKey, "The 16 bytes ICD symmetric key, default: randomly generated."); AddArgument("icd-stay-active-duration", 0, UINT32_MAX, &mICDStayActiveDurationMsec, "If set, a LIT ICD that is commissioned will be requested to stay active for this many milliseconds"); @@ -245,6 +247,7 @@ class PairingCommand : public CHIPCommand, chip::Optional mICDCheckInNodeId; chip::Optional mICDSymmetricKey; chip::Optional mICDMonitoredSubject; + chip::Optional mICDClientType; chip::Optional mICDStayActiveDurationMsec; chip::app::DataModel::List mTimeZoneList; TypedComplexArgument> diff --git a/examples/contact-sensor-app/nxp/zap-lit/contact-sensor-app.matter b/examples/contact-sensor-app/nxp/zap-lit/contact-sensor-app.matter index d64a06cf2c7b6e..9bffee1c83dce2 100644 --- a/examples/contact-sensor-app/nxp/zap-lit/contact-sensor-app.matter +++ b/examples/contact-sensor-app/nxp/zap-lit/contact-sensor-app.matter @@ -1315,6 +1315,11 @@ cluster BooleanState = 69 { cluster IcdManagement = 70 { revision 2; + enum ClientTypeEnum : enum8 { + kPermanent = 0; + kEphemeral = 1; + } + enum OperatingModeEnum : enum8 { kSIT = 0; kLIT = 1; @@ -1349,6 +1354,7 @@ cluster IcdManagement = 70 { fabric_scoped struct MonitoringRegistrationStruct { fabric_sensitive node_id checkInNodeID = 1; fabric_sensitive int64u monitoredSubject = 2; + fabric_sensitive ClientTypeEnum clientType = 4; fabric_idx fabricIndex = 254; } @@ -1373,6 +1379,7 @@ cluster IcdManagement = 70 { int64u monitoredSubject = 1; octet_string<16> key = 2; optional octet_string<16> verificationKey = 3; + ClientTypeEnum clientType = 4; } response struct RegisterClientResponse = 1 { diff --git a/examples/contact-sensor-app/nxp/zap-sit/contact-sensor-app.matter b/examples/contact-sensor-app/nxp/zap-sit/contact-sensor-app.matter index d0861d55518c69..70cae9e994c4fd 100644 --- a/examples/contact-sensor-app/nxp/zap-sit/contact-sensor-app.matter +++ b/examples/contact-sensor-app/nxp/zap-sit/contact-sensor-app.matter @@ -1315,6 +1315,11 @@ cluster BooleanState = 69 { cluster IcdManagement = 70 { revision 2; + enum ClientTypeEnum : enum8 { + kPermanent = 0; + kEphemeral = 1; + } + enum OperatingModeEnum : enum8 { kSIT = 0; kLIT = 1; @@ -1349,6 +1354,7 @@ cluster IcdManagement = 70 { fabric_scoped struct MonitoringRegistrationStruct { fabric_sensitive node_id checkInNodeID = 1; fabric_sensitive int64u monitoredSubject = 2; + fabric_sensitive ClientTypeEnum clientType = 4; fabric_idx fabricIndex = 254; } @@ -1373,6 +1379,7 @@ cluster IcdManagement = 70 { int64u monitoredSubject = 1; octet_string<16> key = 2; optional octet_string<16> verificationKey = 3; + ClientTypeEnum clientType = 4; } response struct RegisterClientResponse = 1 { diff --git a/examples/fabric-admin/commands/pairing/PairingCommand.cpp b/examples/fabric-admin/commands/pairing/PairingCommand.cpp index 8a261b01a8865b..521325a796dce0 100644 --- a/examples/fabric-admin/commands/pairing/PairingCommand.cpp +++ b/examples/fabric-admin/commands/pairing/PairingCommand.cpp @@ -157,6 +157,10 @@ CommissioningParameters PairingCommand::GetCommissioningParameters() { 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()); @@ -166,6 +170,7 @@ CommissioningParameters PairingCommand::GetCommissioningParameters() } params.SetICDCheckInNodeId(mICDCheckInNodeId.Value()); params.SetICDMonitoredSubject(mICDMonitoredSubject.Value()); + params.SetICDClientType(mICDClientType.Value()); } return params; @@ -459,7 +464,7 @@ void PairingCommand::OnICDRegistrationComplete(ScopedNodeId nodeId, uint32_t icd sizeof(icdSymmetricKeyHex), chip::Encoding::HexFlags::kNullTerminate); app::ICDClientInfo clientInfo; - clientInfo.peer_node = nodeId; + clientInfo.peer_node = chip::ScopedNodeId(mICDCheckInNodeId.Value(), nodeId.GetFabricIndex()); clientInfo.monitored_subject = mICDMonitoredSubject.Value(); clientInfo.start_icd_counter = icdCounter; diff --git a/examples/fabric-admin/commands/pairing/PairingCommand.h b/examples/fabric-admin/commands/pairing/PairingCommand.h index fbd33440f5bb14..647f2c37fc78b2 100644 --- a/examples/fabric-admin/commands/pairing/PairingCommand.h +++ b/examples/fabric-admin/commands/pairing/PairingCommand.h @@ -84,6 +84,8 @@ class PairingCommand : public CHIPCommand, "The check-in node id for the ICD, default: node id of the commissioner."); AddArgument("icd-monitored-subject", 0, UINT64_MAX, &mICDMonitoredSubject, "The monitored subject of the ICD, default: The node id used for icd-check-in-nodeid."); + AddArgument("icd-client-type", 0, 1, &mICDClientType, + "The ClientType of the client registering, default: Permanent client - 0"); AddArgument("icd-symmetric-key", &mICDSymmetricKey, "The 16 bytes ICD symmetric key, default: randomly generated."); AddArgument("icd-stay-active-duration", 0, UINT32_MAX, &mICDStayActiveDurationMsec, "If set, a LIT ICD that is commissioned will be requested to stay active for this many milliseconds"); @@ -258,6 +260,7 @@ class PairingCommand : public CHIPCommand, chip::Optional mCountryCode; chip::Optional mICDRegistration; chip::Optional mICDCheckInNodeId; + chip::Optional mICDClientType; chip::Optional mICDSymmetricKey; chip::Optional mICDMonitoredSubject; chip::Optional mICDStayActiveDurationMsec; diff --git a/examples/light-switch-app/light-switch-common/light-switch-app.matter b/examples/light-switch-app/light-switch-common/light-switch-app.matter index c4f73e4b338685..1380e672e747b2 100644 --- a/examples/light-switch-app/light-switch-common/light-switch-app.matter +++ b/examples/light-switch-app/light-switch-common/light-switch-app.matter @@ -1939,6 +1939,11 @@ cluster UserLabel = 65 { cluster IcdManagement = 70 { revision 2; + enum ClientTypeEnum : enum8 { + kPermanent = 0; + kEphemeral = 1; + } + enum OperatingModeEnum : enum8 { kSIT = 0; kLIT = 1; @@ -1973,6 +1978,7 @@ cluster IcdManagement = 70 { fabric_scoped struct MonitoringRegistrationStruct { fabric_sensitive node_id checkInNodeID = 1; fabric_sensitive int64u monitoredSubject = 2; + fabric_sensitive ClientTypeEnum clientType = 4; fabric_idx fabricIndex = 254; } @@ -1997,6 +2003,7 @@ cluster IcdManagement = 70 { int64u monitoredSubject = 1; octet_string<16> key = 2; optional octet_string<16> verificationKey = 3; + ClientTypeEnum clientType = 4; } response struct RegisterClientResponse = 1 { diff --git a/examples/light-switch-app/qpg/zap/switch.matter b/examples/light-switch-app/qpg/zap/switch.matter index d105fdea187f0a..f42b1f07744809 100644 --- a/examples/light-switch-app/qpg/zap/switch.matter +++ b/examples/light-switch-app/qpg/zap/switch.matter @@ -1736,6 +1736,11 @@ cluster UserLabel = 65 { cluster IcdManagement = 70 { revision 2; + enum ClientTypeEnum : enum8 { + kPermanent = 0; + kEphemeral = 1; + } + enum OperatingModeEnum : enum8 { kSIT = 0; kLIT = 1; @@ -1770,6 +1775,7 @@ cluster IcdManagement = 70 { fabric_scoped struct MonitoringRegistrationStruct { fabric_sensitive node_id checkInNodeID = 1; fabric_sensitive int64u monitoredSubject = 2; + fabric_sensitive ClientTypeEnum clientType = 4; fabric_idx fabricIndex = 254; } @@ -1794,6 +1800,7 @@ cluster IcdManagement = 70 { int64u monitoredSubject = 1; octet_string<16> key = 2; optional octet_string<16> verificationKey = 3; + ClientTypeEnum clientType = 4; } response struct RegisterClientResponse = 1 { diff --git a/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.matter b/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.matter index 9d260248736c0c..79f26232c3a727 100644 --- a/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.matter +++ b/examples/lit-icd-app/lit-icd-common/lit-icd-server-app.matter @@ -1413,6 +1413,11 @@ cluster BooleanState = 69 { cluster IcdManagement = 70 { revision 2; + enum ClientTypeEnum : enum8 { + kPermanent = 0; + kEphemeral = 1; + } + enum OperatingModeEnum : enum8 { kSIT = 0; kLIT = 1; @@ -1447,6 +1452,7 @@ cluster IcdManagement = 70 { fabric_scoped struct MonitoringRegistrationStruct { fabric_sensitive node_id checkInNodeID = 1; fabric_sensitive int64u monitoredSubject = 2; + fabric_sensitive ClientTypeEnum clientType = 4; fabric_idx fabricIndex = 254; } @@ -1471,6 +1477,7 @@ cluster IcdManagement = 70 { int64u monitoredSubject = 1; octet_string<16> key = 2; optional octet_string<16> verificationKey = 3; + ClientTypeEnum clientType = 4; } response struct RegisterClientResponse = 1 { diff --git a/examples/lock-app/lock-common/lock-app.matter b/examples/lock-app/lock-common/lock-app.matter index 5c21291397494c..a484b095516dc6 100644 --- a/examples/lock-app/lock-common/lock-app.matter +++ b/examples/lock-app/lock-common/lock-app.matter @@ -1756,6 +1756,11 @@ cluster UserLabel = 65 { cluster IcdManagement = 70 { revision 2; + enum ClientTypeEnum : enum8 { + kPermanent = 0; + kEphemeral = 1; + } + enum OperatingModeEnum : enum8 { kSIT = 0; kLIT = 1; @@ -1790,6 +1795,7 @@ cluster IcdManagement = 70 { fabric_scoped struct MonitoringRegistrationStruct { fabric_sensitive node_id checkInNodeID = 1; fabric_sensitive int64u monitoredSubject = 2; + fabric_sensitive ClientTypeEnum clientType = 4; fabric_idx fabricIndex = 254; } @@ -1814,6 +1820,7 @@ cluster IcdManagement = 70 { int64u monitoredSubject = 1; octet_string<16> key = 2; optional octet_string<16> verificationKey = 3; + ClientTypeEnum clientType = 4; } response struct RegisterClientResponse = 1 { diff --git a/examples/lock-app/qpg/zap/lock.matter b/examples/lock-app/qpg/zap/lock.matter index 589eb097561c77..a8628316139252 100644 --- a/examples/lock-app/qpg/zap/lock.matter +++ b/examples/lock-app/qpg/zap/lock.matter @@ -1412,6 +1412,11 @@ cluster UserLabel = 65 { cluster IcdManagement = 70 { revision 2; + enum ClientTypeEnum : enum8 { + kPermanent = 0; + kEphemeral = 1; + } + enum OperatingModeEnum : enum8 { kSIT = 0; kLIT = 1; @@ -1446,6 +1451,7 @@ cluster IcdManagement = 70 { fabric_scoped struct MonitoringRegistrationStruct { fabric_sensitive node_id checkInNodeID = 1; fabric_sensitive int64u monitoredSubject = 2; + fabric_sensitive ClientTypeEnum clientType = 4; fabric_idx fabricIndex = 254; } @@ -1470,6 +1476,7 @@ cluster IcdManagement = 70 { int64u monitoredSubject = 1; octet_string<16> key = 2; optional octet_string<16> verificationKey = 3; + ClientTypeEnum clientType = 4; } response struct RegisterClientResponse = 1 { diff --git a/examples/platform/esp32/mode-support/static-supported-modes-manager.cpp b/examples/platform/esp32/mode-support/static-supported-modes-manager.cpp deleted file mode 100644 index d06a8b8b7dc809..00000000000000 --- a/examples/platform/esp32/mode-support/static-supported-modes-manager.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* - * - * Copyright (c) 2023 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 "static-supported-modes-manager.h" -#include - -using namespace chip; -using namespace chip::app::Clusters; -using namespace chip::DeviceLayer::Internal; -using namespace chip::app::Clusters::ModeSelect; -using chip::Protocols::InteractionModel::Status; - -using ModeOptionStructType = Structs::ModeOptionStruct::Type; -using SemanticTag = Structs::SemanticTagStruct::Type; - -template -using List = app::DataModel::List; - -SupportedModesManager::ModeOptionsProvider * StaticSupportedModesManager::epModeOptionsProviderList = nullptr; - -const StaticSupportedModesManager StaticSupportedModesManager::instance = StaticSupportedModesManager(); - -int StaticSupportedModesManager::mSize = 0; - -CHIP_ERROR StaticSupportedModesManager::InitEndpointArray(int size) -{ - if (epModeOptionsProviderList != nullptr) - { - ChipLogError(Zcl, "Cannot allocate epModeOptionsProviderList"); - return CHIP_ERROR_INCORRECT_STATE; - } - mSize = size; - epModeOptionsProviderList = new SupportedModesManager::ModeOptionsProvider[mSize]; - if (epModeOptionsProviderList == nullptr) - { - ChipLogError(Zcl, "Failed to allocate memory to epModeOptionsProviderList"); - return CHIP_ERROR_NO_MEMORY; - } - for (int i = 0; i < mSize; i++) - { - epModeOptionsProviderList[i] = ModeOptionsProvider(); - } - return CHIP_NO_ERROR; -} - -SupportedModesManager::ModeOptionsProvider StaticSupportedModesManager::getModeOptionsProvider(EndpointId endpointId) const -{ - if (epModeOptionsProviderList[endpointId].begin() != nullptr && epModeOptionsProviderList[endpointId].end() != nullptr) - { - return ModeOptionsProvider(epModeOptionsProviderList[endpointId].begin(), epModeOptionsProviderList[endpointId].end()); - } - - ModeOptionStructType * modeOptionStructList = nullptr; - SemanticTag * semanticTags = nullptr; - - char keyBuf[ESP32Config::kMaxConfigKeyNameLength]; - uint32_t supportedModeCount = 0; - - VerifyOrReturnValue(ESP32Config::KeyAllocator::SupportedModesCount(keyBuf, sizeof(keyBuf), endpointId) == CHIP_NO_ERROR, - ModeOptionsProvider(nullptr, nullptr)); - ESP32Config::Key countKey(ESP32Config::kConfigNamespace_ChipFactory, keyBuf); - VerifyOrReturnValue(ESP32Config::ReadConfigValue(countKey, supportedModeCount) == CHIP_NO_ERROR, - ModeOptionsProvider(nullptr, nullptr)); - - modeOptionStructList = new ModeOptionStructType[supportedModeCount]; - if (modeOptionStructList == nullptr) - { - return ModeOptionsProvider(nullptr, nullptr); - } - - epModeOptionsProviderList[endpointId] = ModeOptionsProvider(modeOptionStructList, modeOptionStructList + supportedModeCount); - - for (int index = 0; index < supportedModeCount; index++) - { - Structs::ModeOptionStruct::Type option; - uint32_t supportedModeMode = 0; - uint32_t semanticTagCount = 0; - size_t outLen = 0; - - memset(keyBuf, 0, sizeof(char) * ESP32Config::kMaxConfigKeyNameLength); - VerifyOrReturnValue(ESP32Config::KeyAllocator::SupportedModesLabel(keyBuf, sizeof(keyBuf), endpointId, index) == - CHIP_NO_ERROR, - ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); - ESP32Config::Key labelKey(ESP32Config::kConfigNamespace_ChipFactory, keyBuf); - VerifyOrReturnValue(ESP32Config::ReadConfigValueStr(labelKey, nullptr, 0, outLen) == CHIP_NO_ERROR, - ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); - - char * modeLabel = new char[outLen + 1]; - if (modeLabel == nullptr) - { - CleanUp(endpointId); - return ModeOptionsProvider(nullptr, nullptr); - } - - VerifyOrReturnValue(ESP32Config::ReadConfigValueStr(labelKey, modeLabel, outLen + 1, outLen) == CHIP_NO_ERROR, - ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); - - memset(keyBuf, 0, sizeof(char) * ESP32Config::kMaxConfigKeyNameLength); - VerifyOrReturnValue(ESP32Config::KeyAllocator::SupportedModesValue(keyBuf, sizeof(keyBuf), endpointId, index) == - CHIP_NO_ERROR, - ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); - ESP32Config::Key modeKey(ESP32Config::kConfigNamespace_ChipFactory, keyBuf); - VerifyOrReturnValue(ESP32Config::ReadConfigValue(labelKey, supportedModeMode) == CHIP_NO_ERROR, - ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); - - memset(keyBuf, 0, sizeof(char) * ESP32Config::kMaxConfigKeyNameLength); - VerifyOrReturnValue(ESP32Config::KeyAllocator::SemanticTagsCount(keyBuf, sizeof(keyBuf), endpointId, index) == - CHIP_NO_ERROR, - ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); - ESP32Config::Key stCountKey(ESP32Config::kConfigNamespace_ChipFactory, keyBuf); - VerifyOrReturnValue(ESP32Config::ReadConfigValue(stCountKey, semanticTagCount) == CHIP_NO_ERROR, - ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); - - semanticTags = new SemanticTag[semanticTagCount]; - if (semanticTags == nullptr) - { - CleanUp(endpointId); - return ModeOptionsProvider(nullptr, nullptr); - } - for (auto stIndex = 0; stIndex < semanticTagCount; stIndex++) - { - - uint32_t semanticTagValue = 0; - uint32_t semanticTagMfgCode = 0; - SemanticTag tag; - - memset(keyBuf, 0, sizeof(char) * ESP32Config::kMaxConfigKeyNameLength); - VerifyOrReturnValue(ESP32Config::KeyAllocator::SemanticTagValue(keyBuf, sizeof(keyBuf), endpointId, index, stIndex) == - CHIP_NO_ERROR, - ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); - ESP32Config::Key stValueKey(ESP32Config::kConfigNamespace_ChipFactory, keyBuf); - VerifyOrReturnValue(ESP32Config::ReadConfigValue(stValueKey, semanticTagValue) == CHIP_NO_ERROR, - ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); - - memset(keyBuf, 0, sizeof(char) * ESP32Config::kMaxConfigKeyNameLength); - VerifyOrReturnValue(ESP32Config::KeyAllocator::SemanticTagMfgCode(keyBuf, sizeof(keyBuf), endpointId, index, stIndex) == - CHIP_NO_ERROR, - ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); - ESP32Config::Key stMfgCodeKey(ESP32Config::kConfigNamespace_ChipFactory, keyBuf); - VerifyOrReturnValue(ESP32Config::ReadConfigValue(stMfgCodeKey, semanticTagMfgCode) == CHIP_NO_ERROR, - ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); - - tag.value = static_cast(semanticTagValue); - tag.mfgCode = static_cast(semanticTagMfgCode); - semanticTags[stIndex] = tag; - } - - option.label = chip::CharSpan::fromCharString(modeLabel); - option.mode = static_cast(supportedModeMode); - option.semanticTags = DataModel::List(semanticTags, semanticTagCount); - - modeOptionStructList[index] = option; - } - - return ModeOptionsProvider(modeOptionStructList, modeOptionStructList + supportedModeCount); -} - -Status StaticSupportedModesManager::getModeOptionByMode(unsigned short endpointId, unsigned char mode, - const ModeOptionStructType ** dataPtr) const -{ - auto modeOptionsProvider = this->getModeOptionsProvider(endpointId); - if (modeOptionsProvider.begin() == nullptr) - { - return Status::UnsupportedCluster; - } - auto * begin = modeOptionsProvider.begin(); - auto * end = modeOptionsProvider.end(); - - for (auto * it = begin; it != end; ++it) - { - auto & modeOption = *it; - if (modeOption.mode == mode) - { - *dataPtr = &modeOption; - return Status::Success; - } - } - ChipLogProgress(Zcl, "Cannot find the mode %u", mode); - return Status::InvalidCommand; -} - -const ModeSelect::SupportedModesManager * ModeSelect::getSupportedModesManager() -{ - return &StaticSupportedModesManager::getStaticSupportedModesManagerInstance(); -} - -void StaticSupportedModesManager::FreeSupportedModes(EndpointId endpointId) const -{ - if (epModeOptionsProviderList[endpointId].begin() != nullptr) - { - auto * begin = epModeOptionsProviderList[endpointId].begin(); - auto * end = epModeOptionsProviderList[endpointId].end(); - for (auto * it = begin; it != end; ++it) - { - auto & modeOption = *it; - delete[] modeOption.label.data(); - delete[] modeOption.semanticTags.data(); - } - delete[] begin; - } - epModeOptionsProviderList[endpointId] = ModeOptionsProvider(); -} - -void StaticSupportedModesManager::CleanUp(EndpointId endpointId) const -{ - ChipLogError(Zcl, "Supported mode data is in incorrect format"); - FreeSupportedModes(endpointId); -} diff --git a/examples/platform/esp32/mode-support/static-supported-modes-manager.h b/examples/platform/esp32/mode-support/static-supported-modes-manager.h deleted file mode 100644 index 8d6bb3c665ff0c..00000000000000 --- a/examples/platform/esp32/mode-support/static-supported-modes-manager.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * - * Copyright (c) 2023 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 chip { -namespace app { -namespace Clusters { -namespace ModeSelect { - -class StaticSupportedModesManager : public chip::app::Clusters::ModeSelect::SupportedModesManager -{ -private: - using ModeOptionStructType = Structs::ModeOptionStruct::Type; - using SemanticTag = Structs::SemanticTagStruct::Type; - static int mSize; - - static ModeOptionsProvider * epModeOptionsProviderList; - - void FreeSupportedModes(EndpointId endpointId) const; - - static const StaticSupportedModesManager instance; - -public: - // InitEndpointArray should be called only once in the application. Memory allocated to the - // epModeOptionsProviderList will be needed for the lifetime of the program, so it's never deallocated. - static CHIP_ERROR InitEndpointArray(int size); - - // DeInitEndpointArray should be called only when application need to reallocate memory of - // epModeOptionsProviderList ( Eg. Bridges ). - static void DeInitEndpointArray() - { - delete[] epModeOptionsProviderList; - epModeOptionsProviderList = nullptr; - mSize = 0; - } - - SupportedModesManager::ModeOptionsProvider getModeOptionsProvider(EndpointId endpointId) const override; - - Protocols::InteractionModel::Status getModeOptionByMode(EndpointId endpointId, uint8_t mode, - const ModeOptionStructType ** dataPtr) const override; - - void CleanUp(EndpointId endpointId) const; - - StaticSupportedModesManager() {} - - ~StaticSupportedModesManager() - { - for (int i = 0; i < mSize; i++) - { - FreeSupportedModes(i); - } - } - - static inline const StaticSupportedModesManager & getStaticSupportedModesManagerInstance() { return instance; } -}; - -const SupportedModesManager * getSupportedModesManager(); - -} // namespace ModeSelect -} // namespace Clusters -} // namespace app -} // namespace chip diff --git a/examples/smoke-co-alarm-app/smoke-co-alarm-common/smoke-co-alarm-app.matter b/examples/smoke-co-alarm-app/smoke-co-alarm-common/smoke-co-alarm-app.matter index ea86a446d156eb..9ed425b817255d 100644 --- a/examples/smoke-co-alarm-app/smoke-co-alarm-common/smoke-co-alarm-app.matter +++ b/examples/smoke-co-alarm-app/smoke-co-alarm-common/smoke-co-alarm-app.matter @@ -1732,6 +1732,11 @@ cluster UserLabel = 65 { cluster IcdManagement = 70 { revision 2; + enum ClientTypeEnum : enum8 { + kPermanent = 0; + kEphemeral = 1; + } + enum OperatingModeEnum : enum8 { kSIT = 0; kLIT = 1; @@ -1766,6 +1771,7 @@ cluster IcdManagement = 70 { fabric_scoped struct MonitoringRegistrationStruct { fabric_sensitive node_id checkInNodeID = 1; fabric_sensitive int64u monitoredSubject = 2; + fabric_sensitive ClientTypeEnum clientType = 4; fabric_idx fabricIndex = 254; } @@ -1790,6 +1796,7 @@ cluster IcdManagement = 70 { int64u monitoredSubject = 1; octet_string<16> key = 2; optional octet_string<16> verificationKey = 3; + ClientTypeEnum clientType = 4; } response struct RegisterClientResponse = 1 { diff --git a/examples/window-app/common/window-app.matter b/examples/window-app/common/window-app.matter index 1a5a56ca4e3aba..f39cb7187446bc 100644 --- a/examples/window-app/common/window-app.matter +++ b/examples/window-app/common/window-app.matter @@ -1830,6 +1830,11 @@ cluster UserLabel = 65 { cluster IcdManagement = 70 { revision 2; + enum ClientTypeEnum : enum8 { + kPermanent = 0; + kEphemeral = 1; + } + enum OperatingModeEnum : enum8 { kSIT = 0; kLIT = 1; @@ -1864,6 +1869,7 @@ cluster IcdManagement = 70 { fabric_scoped struct MonitoringRegistrationStruct { fabric_sensitive node_id checkInNodeID = 1; fabric_sensitive int64u monitoredSubject = 2; + fabric_sensitive ClientTypeEnum clientType = 4; fabric_idx fabricIndex = 254; } @@ -1888,6 +1894,7 @@ cluster IcdManagement = 70 { int64u monitoredSubject = 1; octet_string<16> key = 2; optional octet_string<16> verificationKey = 3; + ClientTypeEnum clientType = 4; } response struct RegisterClientResponse = 1 { diff --git a/scripts/tools/generate_esp32_chip_factory_bin.py b/scripts/tools/generate_esp32_chip_factory_bin.py index eab2ec6b43c229..b641ef05b6cd88 100755 --- a/scripts/tools/generate_esp32_chip_factory_bin.py +++ b/scripts/tools/generate_esp32_chip_factory_bin.py @@ -159,39 +159,6 @@ def ishex(s): except ValueError: return False -# get_supported_modes_dict() converts the list of strings to per endpoint dictionaries. -# example with semantic tags -# input : ['0/label1/1/"1\0x8000, 2\0x8000" 1/label2/1/"1\0x8000, 2\0x8000"'] -# output : {'1': [{'Label': 'label1', 'Mode': 0, 'Semantic_Tag': [{'value': 1, 'mfgCode': 32768}, {'value': 2, 'mfgCode': 32768}]}, {'Label': 'label2', 'Mode': 1, 'Semantic_Tag': [{'value': 1, 'mfgCode': 32768}, {'value': 2, 'mfgCode': 32768}]}]} - -# example without semantic tags -# input : ['0/label1/1 1/label2/1'] -# output : {'1': [{'Label': 'label1', 'Mode': 0, 'Semantic_Tag': []}, {'Label': 'label2', 'Mode': 1, 'Semantic_Tag': []}]} - - -def get_supported_modes_dict(supported_modes): - output_dict = {} - - for mode_str in supported_modes: - mode_label_strs = mode_str.split('/') - mode = mode_label_strs[0] - label = mode_label_strs[1] - ep = mode_label_strs[2] - - semantic_tags = '' - if (len(mode_label_strs) == 4): - semantic_tag_strs = mode_label_strs[3].split(', ') - semantic_tags = [{"value": int(v.split('\\')[0]), "mfgCode": int(v.split('\\')[1], 16)} for v in semantic_tag_strs] - - mode_dict = {"Label": label, "Mode": int(mode), "Semantic_Tag": semantic_tags} - - if ep in output_dict: - output_dict[ep].append(mode_dict) - else: - output_dict[ep] = [mode_dict] - - return output_dict - def check_str_range(s, min_len, max_len, name): if s and ((len(s) < min_len) or (len(s) > max_len)): @@ -302,60 +269,6 @@ def populate_factory_data(args, spake2p_params): if args.hw_ver_str: FACTORY_DATA['hw-ver-str']['value'] = args.hw_ver_str - # SupportedModes are stored as multiple entries - # - sm-sz/ : number of supported modes for the endpoint - # - sm-label// : supported modes label key for the endpoint and index - # - sm-mode// : supported modes mode key for the endpoint and index - # - sm-st-sz// : supported modes SemanticTag key for the endpoint and index - # - st-v/// : semantic tag value key for the endpoint and index and ind - # - st-mfg/// : semantic tag mfg code key for the endpoint and index and ind - if (args.supported_modes is not None): - dictionary = get_supported_modes_dict(args.supported_modes) - for ep in dictionary.keys(): - _sz = { - 'type': 'data', - 'encoding': 'u32', - 'value': len(dictionary[ep]) - } - FACTORY_DATA.update({'sm-sz/{:x}'.format(int(ep)): _sz}) - for i in range(len(dictionary[ep])): - item = dictionary[ep][i] - _label = { - 'type': 'data', - 'encoding': 'string', - 'value': item["Label"] - } - _mode = { - 'type': 'data', - 'encoding': 'u32', - 'value': item["Mode"] - } - _st_sz = { - 'type': 'data', - 'encoding': 'u32', - 'value': len(item["Semantic_Tag"]) - } - FACTORY_DATA.update({'sm-label/{:x}/{:x}'.format(int(ep), i): _label}) - FACTORY_DATA.update({'sm-mode/{:x}/{:x}'.format(int(ep), i): _mode}) - FACTORY_DATA.update({'sm-st-sz/{:x}/{:x}'.format(int(ep), i): _st_sz}) - - for j in range(len(item["Semantic_Tag"])): - entry = item["Semantic_Tag"][j] - - _value = { - 'type': 'data', - 'encoding': 'u32', - 'value': entry["value"] - } - _mfg_code = { - 'type': 'data', - 'encoding': 'u32', - 'value': entry["mfgCode"] - } - - FACTORY_DATA.update({'st-v/{:x}/{:x}/{:x}'.format(int(ep), i, j): _value}) - FACTORY_DATA.update({'st-mfg/{:x}/{:x}/{:x}'.format(int(ep), i, j): _mfg_code}) - def gen_raw_ec_keypair_from_der(key_file, pubkey_raw_file, privkey_raw_file): with open(key_file, 'rb') as f: @@ -468,9 +381,6 @@ def any_base_int(s): return int(s, 0) help=('128-bit unique identifier for generating rotating device identifier, ' 'provide 32-byte hex string, e.g. "1234567890abcdef1234567890abcdef"')) - parser.add_argument('--supported-modes', type=str, nargs='+', required=False, - help='List of supported modes, eg: mode1/label1/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode" mode2/label2/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode" mode3/label3/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode"') - parser.add_argument('-s', '--size', type=any_base_int, default=0x6000, help='The size of the partition.bin, default: 0x6000') parser.add_argument('--target', default='esp32', @@ -509,7 +419,8 @@ def set_up_factory_data(args): def generate_factory_partiton_binary(args): generate_nvs_csv(args.output_dir, FACTORY_PARTITION_CSV) if args.generate_bin: - generate_nvs_bin(args.encrypt, args.size, FACTORY_PARTITION_CSV, FACTORY_PARTITION_BIN, args.output_dir) + csv_file = os.path.join(args.output_dir, FACTORY_PARTITION_CSV) + generate_nvs_bin(args.encrypt, args.size, csv_file, FACTORY_PARTITION_BIN, args.output_dir) print_flashing_help(args.encrypt, args.output_dir, FACTORY_PARTITION_BIN) clean_up() diff --git a/src/BUILD.gn b/src/BUILD.gn index 27f449dcf8e543..e2557535d9d72d 100644 --- a/src/BUILD.gn +++ b/src/BUILD.gn @@ -50,7 +50,6 @@ if (chip_build_tests) { chip_test_group("tests") { deps = [] tests = [ - "${chip_root}/src/app/codegen-data-model/tests", "${chip_root}/src/app/data-model-interface/tests", "${chip_root}/src/access/tests", "${chip_root}/src/crypto/tests", @@ -82,7 +81,15 @@ if (chip_build_tests) { if (current_os != "zephyr" && current_os != "mbed" && chip_device_platform != "efr32") { + # Avoid these items from "one single binary" test executions. Once tests + # are split, we can re-visit this (and likely many others) + # + # In particular: + # "app/codegen-data-model/tests" contains symbols for ember mocks which + # are used by other tests + tests += [ + "${chip_root}/src/app/codegen-data-model/tests", "${chip_root}/src/setup_payload/tests", "${chip_root}/src/transport/raw/tests", ] diff --git a/src/app/clusters/icd-management-server/icd-management-server.cpp b/src/app/clusters/icd-management-server/icd-management-server.cpp index f6ab59d9f2efb0..e4ead150a1fd79 100644 --- a/src/app/clusters/icd-management-server/icd-management-server.cpp +++ b/src/app/clusters/icd-management-server/icd-management-server.cpp @@ -202,6 +202,7 @@ CHIP_ERROR IcdManagementAttributeAccess::ReadRegisteredClients(EndpointId endpoi Structs::MonitoringRegistrationStruct::Type s{ .checkInNodeID = e.checkInNodeID, .monitoredSubject = e.monitoredSubject, + .clientType = e.clientType, .fabricIndex = e.fabricIndex }; ReturnErrorOnFailure(subEncoder.Encode(s)); } @@ -252,10 +253,14 @@ Status ICDManagementServer::RegisterClient(CommandHandler * commandObj, const Co FabricIndex fabricIndex = commandObj->GetAccessingFabricIndex(); NodeId nodeId = commandData.checkInNodeID; uint64_t monitoredSubject = commandData.monitoredSubject; + ClientTypeEnum clientType = commandData.clientType; ByteSpan key = commandData.key; Optional verificationKey = commandData.verificationKey; bool isClientAdmin = false; + // Check if ClientType is valid + VerifyOrReturnError(clientType != ClientTypeEnum::kUnknownEnumValue, InteractionModel::Status::ConstraintError); + // Check if client is admin VerifyOrReturnError(CHIP_NO_ERROR == CheckAdmin(commandObj, commandPath, isClientAdmin), InteractionModel::Status::Failure); @@ -291,6 +296,8 @@ Status ICDManagementServer::RegisterClient(CommandHandler * commandObj, const Co // Save entry.checkInNodeID = nodeId; entry.monitoredSubject = monitoredSubject; + entry.clientType = clientType; + if (entry.keyHandleValid) { entry.DeleteKey(); diff --git a/src/app/clusters/icd-management-server/icd-management-server.h b/src/app/clusters/icd-management-server/icd-management-server.h index 5c6b838fa9b527..38fc1b7ee6d7d0 100644 --- a/src/app/clusters/icd-management-server/icd-management-server.h +++ b/src/app/clusters/icd-management-server/icd-management-server.h @@ -34,8 +34,6 @@ #include #endif // CHIP_CONFIG_ENABLE_ICD_CIP -using chip::Protocols::InteractionModel::Status; - namespace chip { namespace Crypto { using SymmetricKeystore = SessionKeystore; diff --git a/src/app/codegen-data-model/BUILD.gn b/src/app/codegen-data-model/BUILD.gn index 418983a2fc8b8f..5803f01a37778e 100644 --- a/src/app/codegen-data-model/BUILD.gn +++ b/src/app/codegen-data-model/BUILD.gn @@ -20,6 +20,7 @@ import("//build_overrides/chip.gni") # # Use `model.gni` to get access to: # CodegenDataModel.cpp +# CodegenDataModel_Read.cpp # CodegenDataModel.h # # The above list of files exists to satisfy the "dependency linter" diff --git a/src/app/codegen-data-model/CodegenDataModel.cpp b/src/app/codegen-data-model/CodegenDataModel.cpp index e1597dbdc4f8b4..d46deeeddaa1e0 100644 --- a/src/app/codegen-data-model/CodegenDataModel.cpp +++ b/src/app/codegen-data-model/CodegenDataModel.cpp @@ -231,13 +231,6 @@ bool CodegenDataModel::EmberCommandListIterator::Exists(const CommandId * list, return (*mCurrentHint == toCheck); } -CHIP_ERROR CodegenDataModel::ReadAttribute(const InteractionModel::ReadAttributeRequest & request, - InteractionModel::ReadState & state, AttributeValueEncoder & encoder) -{ - // TODO: this needs an implementation - return CHIP_ERROR_NOT_IMPLEMENTED; -} - CHIP_ERROR CodegenDataModel::WriteAttribute(const InteractionModel::WriteAttributeRequest & request, AttributeValueDecoder & decoder) { diff --git a/src/app/codegen-data-model/CodegenDataModel.h b/src/app/codegen-data-model/CodegenDataModel.h index f8d997451bdace..b65f38b9155e73 100644 --- a/src/app/codegen-data-model/CodegenDataModel.h +++ b/src/app/codegen-data-model/CodegenDataModel.h @@ -68,8 +68,7 @@ class CodegenDataModel : public chip::app::InteractionModel::DataModel /// Generic model implementations CHIP_ERROR Shutdown() override { return CHIP_NO_ERROR; } - CHIP_ERROR ReadAttribute(const InteractionModel::ReadAttributeRequest & request, InteractionModel::ReadState & state, - AttributeValueEncoder & encoder) override; + CHIP_ERROR ReadAttribute(const InteractionModel::ReadAttributeRequest & request, AttributeValueEncoder & encoder) override; CHIP_ERROR WriteAttribute(const InteractionModel::WriteAttributeRequest & request, AttributeValueDecoder & decoder) override; CHIP_ERROR Invoke(const InteractionModel::InvokeRequest & request, chip::TLV::TLVReader & input_arguments, InteractionModel::InvokeReply & reply) override; diff --git a/src/app/codegen-data-model/CodegenDataModel_Read.cpp b/src/app/codegen-data-model/CodegenDataModel_Read.cpp new file mode 100644 index 00000000000000..04265d37f8623f --- /dev/null +++ b/src/app/codegen-data-model/CodegenDataModel_Read.cpp @@ -0,0 +1,372 @@ +/* + * 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 "lib/core/CHIPError.h" +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace chip { +namespace app { +namespace { +using namespace chip::app::Compatibility::Internal; + +// Fetch the source for the given attribute path: either a cluster (for global ones) or attribute +// path. +// +// if returning a CHIP_ERROR, it will NEVER be CHIP_NO_ERROR. +std::variant +FindAttributeMetadata(const ConcreteAttributePath & aPath) +{ + for (auto & attr : GlobalAttributesNotInMetadata) + { + + if (attr == aPath.mAttributeId) + { + const EmberAfCluster * cluster = emberAfFindServerCluster(aPath.mEndpointId, aPath.mClusterId); + if (cluster == nullptr) + { + return (emberAfFindEndpointType(aPath.mEndpointId) == nullptr) ? CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint) + : CHIP_IM_GLOBAL_STATUS(UnsupportedCluster); + } + + return cluster; + } + } + const EmberAfAttributeMetadata * metadata = + emberAfLocateAttributeMetadata(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId); + + if (metadata == nullptr) + { + const EmberAfEndpointType * type = emberAfFindEndpointType(aPath.mEndpointId); + if (type == nullptr) + { + return CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint); + } + + const EmberAfCluster * cluster = emberAfFindClusterInType(type, aPath.mClusterId, CLUSTER_MASK_SERVER); + if (cluster == nullptr) + { + return CHIP_IM_GLOBAL_STATUS(UnsupportedCluster); + } + + // Since we know the attribute is unsupported and the endpoint/cluster are + // OK, this is the only option left. + return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); + } + + return metadata; +} + +/// Attempts to read via an attribute access interface (AAI) +/// +/// If it returns a CHIP_ERROR, then this is a FINAL result (i.e. either failure or success). +/// +/// If it returns std::nullopt, then there is no AAI to handle the given path +/// and processing should figure out the value otherwise (generally from other ember data) +std::optional TryReadViaAccessInterface(const ConcreteAttributePath & path, AttributeAccessInterface * aai, + AttributeValueEncoder & encoder) +{ + // Processing can happen only if an attribute access interface actually exists.. + if (aai == nullptr) + { + return std::nullopt; + } + + CHIP_ERROR err = aai->Read(path, encoder); + + if (err != CHIP_NO_ERROR) + { + // Implementation of 8.4.3.2 of the spec for path expansion + if (path.mExpanded && (err == CHIP_IM_GLOBAL_STATUS(UnsupportedRead))) + { + return CHIP_NO_ERROR; + } + + return err; + } + + // If the encoder tried to encode, then a value should have been written. + // - if encode, assume DONE (i.e. FINAL CHIP_NO_ERROR) + // - if no encode, say that processing must continue + return encoder.TriedEncode() ? std::make_optional(CHIP_NO_ERROR) : std::nullopt; +} + +/// Metadata of what a ember/pascal short string means (prepended by a u8 length) +struct ShortPascalString +{ + using LengthType = uint8_t; + static constexpr LengthType kNullLength = 0xFF; +}; + +/// Metadata of what a ember/pascal LONG string means (prepended by a u16 length) +struct LongPascalString +{ + using LengthType = uint16_t; + static constexpr LengthType kNullLength = 0xFFFF; +}; + +// ember assumptions ... should just work +static_assert(sizeof(ShortPascalString::LengthType) == 1); +static_assert(sizeof(LongPascalString::LengthType) == 2); + +/// Given a ByteSpan containing data from ember, interpret it +/// as a span of type OUT (i.e. ByteSpan or CharSpan) given a ENCODING +/// where ENCODING is Short or Long pascal strings. +template +std::optional ExtractEmberString(ByteSpan data) +{ + typename ENCODING::LengthType len; + + // Ember storage format for pascal-prefix data is specifically "native byte order", + // hence the use of memcpy. + VerifyOrDie(sizeof(len) <= data.size()); + memcpy(&len, data.data(), sizeof(len)); + + if (len == ENCODING::kNullLength) + { + return std::nullopt; + } + + VerifyOrDie(static_cast(len + sizeof(len)) <= data.size()); + return std::make_optional(reinterpret_cast(data.data() + sizeof(len)), len); +} + +/// Encode a value inside `encoder` +/// +/// The value encoded will be of type T (e.g. CharSpan or ByteSpan) and it will be decoded +/// via the given ENCODING (i.e. ShortPascalString or LongPascalString) +/// +/// isNullable defines if the value of NULL is allowed to be encoded. +template +CHIP_ERROR EncodeStringLike(ByteSpan data, bool isNullable, AttributeValueEncoder & encoder) +{ + std::optional value = ExtractEmberString(data); + if (!value.has_value()) + { + if (isNullable) + { + return encoder.EncodeNull(); + } + return CHIP_ERROR_INCORRECT_STATE; + } + + // encode value as-is + return encoder.Encode(*value); +} + +/// Encodes a numeric data value of type T from the given ember-encoded buffer `data`. +/// +/// isNullable defines if the value of NULL is allowed to be encoded. +template +CHIP_ERROR EncodeFromSpan(ByteSpan data, bool isNullable, AttributeValueEncoder & encoder) +{ + typename NumericAttributeTraits::StorageType value; + + VerifyOrReturnError(data.size() >= sizeof(value), CHIP_ERROR_INVALID_ARGUMENT); + memcpy(&value, data.data(), sizeof(value)); + + if (isNullable && NumericAttributeTraits::IsNullValue(value)) + { + return encoder.EncodeNull(); + } + + if (!NumericAttributeTraits::CanRepresentValue(isNullable, value)) + { + return CHIP_ERROR_INCORRECT_STATE; + } + + return encoder.Encode(NumericAttributeTraits::StorageToWorking(value)); +} + +/// Converts raw ember data from `data` into the encoder +/// +/// Uses the attribute `metadata` to determine how the data is encoded into `data` and +/// write a suitable value into `encoder`. +CHIP_ERROR EncodeEmberValue(ByteSpan data, const EmberAfAttributeMetadata * metadata, AttributeValueEncoder & encoder) +{ + VerifyOrReturnError(metadata != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + + const bool isNullable = metadata->IsNullable(); + + switch (AttributeBaseType(metadata->attributeType)) + { + case ZCL_NO_DATA_ATTRIBUTE_TYPE: // No data + return encoder.EncodeNull(); + case ZCL_BOOLEAN_ATTRIBUTE_TYPE: // Boolean + return EncodeFromSpan(data, isNullable, encoder); + case ZCL_INT8U_ATTRIBUTE_TYPE: // Unsigned 8-bit integer + return EncodeFromSpan(data, isNullable, encoder); + case ZCL_INT16U_ATTRIBUTE_TYPE: // Unsigned 16-bit integer + return EncodeFromSpan(data, isNullable, encoder); + case ZCL_INT24U_ATTRIBUTE_TYPE: // Unsigned 24-bit integer + return EncodeFromSpan>(data, isNullable, encoder); + case ZCL_INT32U_ATTRIBUTE_TYPE: // Unsigned 32-bit integer + return EncodeFromSpan(data, isNullable, encoder); + case ZCL_INT40U_ATTRIBUTE_TYPE: // Unsigned 40-bit integer + return EncodeFromSpan>(data, isNullable, encoder); + case ZCL_INT48U_ATTRIBUTE_TYPE: // Unsigned 48-bit integer + return EncodeFromSpan>(data, isNullable, encoder); + case ZCL_INT56U_ATTRIBUTE_TYPE: // Unsigned 56-bit integer + return EncodeFromSpan>(data, isNullable, encoder); + case ZCL_INT64U_ATTRIBUTE_TYPE: // Unsigned 64-bit integer + return EncodeFromSpan(data, isNullable, encoder); + case ZCL_INT8S_ATTRIBUTE_TYPE: // Signed 8-bit integer + return EncodeFromSpan(data, isNullable, encoder); + case ZCL_INT16S_ATTRIBUTE_TYPE: // Signed 16-bit integer + return EncodeFromSpan(data, isNullable, encoder); + case ZCL_INT24S_ATTRIBUTE_TYPE: // Signed 24-bit integer + return EncodeFromSpan>(data, isNullable, encoder); + case ZCL_INT32S_ATTRIBUTE_TYPE: // Signed 32-bit integer + return EncodeFromSpan(data, isNullable, encoder); + case ZCL_INT40S_ATTRIBUTE_TYPE: // Signed 40-bit integer + return EncodeFromSpan>(data, isNullable, encoder); + case ZCL_INT48S_ATTRIBUTE_TYPE: // Signed 48-bit integer + return EncodeFromSpan>(data, isNullable, encoder); + case ZCL_INT56S_ATTRIBUTE_TYPE: // Signed 56-bit integer + return EncodeFromSpan>(data, isNullable, encoder); + case ZCL_INT64S_ATTRIBUTE_TYPE: // Signed 64-bit integer + return EncodeFromSpan(data, isNullable, encoder); + case ZCL_SINGLE_ATTRIBUTE_TYPE: // 32-bit float + return EncodeFromSpan(data, isNullable, encoder); + case ZCL_DOUBLE_ATTRIBUTE_TYPE: // 64-bit float + return EncodeFromSpan(data, isNullable, encoder); + case ZCL_CHAR_STRING_ATTRIBUTE_TYPE: // Char string + return EncodeStringLike(data, isNullable, encoder); + case ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE: + return EncodeStringLike(data, isNullable, encoder); + case ZCL_OCTET_STRING_ATTRIBUTE_TYPE: // Octet string + return EncodeStringLike(data, isNullable, encoder); + case ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE: + return EncodeStringLike(data, isNullable, encoder); + default: + ChipLogError(DataManagement, "Attribute type 0x%x not handled", static_cast(metadata->attributeType)); + return CHIP_IM_GLOBAL_STATUS(UnsupportedRead); + } +} + +} // namespace + +/// separated-out ReadAttribute implementation (given existing complexity) +/// +/// Generally will: +/// - validate ACL (only for non-internal requests) +/// - Try to read attribute via the AttributeAccessInterface +/// - Try to read the value from ember RAM storage +CHIP_ERROR CodegenDataModel::ReadAttribute(const InteractionModel::ReadAttributeRequest & request, AttributeValueEncoder & encoder) +{ + ChipLogDetail(DataManagement, + "Reading attribute: Cluster=" ChipLogFormatMEI " Endpoint=0x%x AttributeId=" ChipLogFormatMEI " (expanded=%d)", + ChipLogValueMEI(request.path.mClusterId), request.path.mEndpointId, ChipLogValueMEI(request.path.mAttributeId), + request.path.mExpanded); + + // ACL check for non-internal requests + if (!request.operationFlags.Has(InteractionModel::OperationFlags::kInternal)) + { + ReturnErrorCodeIf(!request.subjectDescriptor.has_value(), CHIP_ERROR_INVALID_ARGUMENT); + + Access::RequestPath requestPath{ .cluster = request.path.mClusterId, .endpoint = request.path.mEndpointId }; + CHIP_ERROR err = Access::GetAccessControl().Check(*request.subjectDescriptor, requestPath, + RequiredPrivilege::ForReadAttribute(request.path)); + if (err != CHIP_NO_ERROR) + { + // Implementation of 8.4.3.2 of the spec for path expansion + if (request.path.mExpanded && (err == CHIP_ERROR_ACCESS_DENIED)) + { + return CHIP_NO_ERROR; + } + return err; + } + } + + auto metadata = FindAttributeMetadata(request.path); + + // Explicit failure in finding a suitable metadata + if (const CHIP_ERROR * err = std::get_if(&metadata)) + { + VerifyOrDie(*err != CHIP_NO_ERROR); + return *err; + } + + // Read via AAI + std::optional aai_result; + if (const EmberAfCluster ** cluster = std::get_if(&metadata)) + { + Compatibility::GlobalAttributeReader aai(*cluster); + aai_result = TryReadViaAccessInterface(request.path, &aai, encoder); + } + else + { + aai_result = TryReadViaAccessInterface( + request.path, GetAttributeAccessOverride(request.path.mEndpointId, request.path.mClusterId), encoder); + } + ReturnErrorCodeIf(aai_result.has_value(), *aai_result); + + if (!std::holds_alternative(metadata)) + { + // if we only got a cluster, this was for a global attribute. We cannot read ember attributes + // at this point, so give up (although GlobalAttributeReader should have returned something here). + chipDie(); + } + const EmberAfAttributeMetadata * attributeMetadata = std::get(metadata); + + // At this point, we have to use ember directly to read the data. + EmberAfAttributeSearchRecord record; + record.endpoint = request.path.mEndpointId; + record.clusterId = request.path.mClusterId; + record.attributeId = request.path.mAttributeId; + Protocols::InteractionModel::Status status = emAfReadOrWriteAttribute( + &record, &attributeMetadata, gEmberAttributeIOBufferSpan.data(), static_cast(gEmberAttributeIOBufferSpan.size()), + /* write = */ false); + + if (status != Protocols::InteractionModel::Status::Success) + { + return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(status); + } + + return EncodeEmberValue(gEmberAttributeIOBufferSpan, attributeMetadata, encoder); +} + +} // namespace app +} // namespace chip diff --git a/src/app/codegen-data-model/model.gni b/src/app/codegen-data-model/model.gni index c8dce5617b9a7b..3be7b2d2610513 100644 --- a/src/app/codegen-data-model/model.gni +++ b/src/app/codegen-data-model/model.gni @@ -24,12 +24,13 @@ import("//build_overrides/chip.gni") # As a result, the files here are NOT a source_set or similar because they cannot # be cleanly built as a stand-alone and instead have to be imported as part of # a different data model or compilation unit. -codegen_interaction_model_SOURCES = [ +codegen_data_model_SOURCES = [ "${chip_root}/src/app/codegen-data-model/CodegenDataModel.h", "${chip_root}/src/app/codegen-data-model/CodegenDataModel.cpp", + "${chip_root}/src/app/codegen-data-model/CodegenDataModel_Read.cpp", ] -codegen_interaction_model_PUBLIC_DEPS = [ +codegen_data_model_PUBLIC_DEPS = [ "${chip_root}/src/app/common:attribute-type", "${chip_root}/src/app/data-model-interface", ] diff --git a/src/app/codegen-data-model/tests/AttributeReportIBEncodeDecode.cpp b/src/app/codegen-data-model/tests/AttributeReportIBEncodeDecode.cpp new file mode 100644 index 00000000000000..29088d0cdc55e1 --- /dev/null +++ b/src/app/codegen-data-model/tests/AttributeReportIBEncodeDecode.cpp @@ -0,0 +1,130 @@ +/* + * 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 "AttributeReportIBEncodeDecode.h" + +#include +#include + +using namespace chip::app; + +namespace chip { +namespace Test { + +CHIP_ERROR DecodedAttributeData::DecodeFrom(const AttributeDataIB::Parser & parser) +{ + ReturnErrorOnFailure(parser.GetDataVersion(&dataVersion)); + + AttributePathIB::Parser pathParser; + ReturnErrorOnFailure(parser.GetPath(&pathParser)); + ReturnErrorOnFailure(pathParser.GetConcreteAttributePath(attributePath, AttributePathIB::ValidateIdRanges::kNo)); + ReturnErrorOnFailure(parser.GetData(&dataReader)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR DecodeAttributeReportIBs(ByteSpan data, std::vector & decoded_items) +{ + // Espected data format: + // CONTAINER (anonymous) + // 0x01 => Array (i.e. report data ib) + // ReportIB* + // + // Generally this is VERY hard to process ... + // + TLV::TLVReader reportIBsReader; + reportIBsReader.Init(data); + + ReturnErrorOnFailure(reportIBsReader.Next()); + if (reportIBsReader.GetType() != TLV::TLVType::kTLVType_Structure) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + TLV::TLVType outer1; + reportIBsReader.EnterContainer(outer1); + + ReturnErrorOnFailure(reportIBsReader.Next()); + if (reportIBsReader.GetType() != TLV::TLVType::kTLVType_Array) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + TLV::TLVType outer2; + reportIBsReader.EnterContainer(outer2); + + CHIP_ERROR err = CHIP_NO_ERROR; + while (CHIP_NO_ERROR == (err = reportIBsReader.Next())) + { + TLV::TLVReader attributeReportReader = reportIBsReader; + AttributeReportIB::Parser attributeReportParser; + ReturnErrorOnFailure(attributeReportParser.Init(attributeReportReader)); + + AttributeDataIB::Parser dataParser; + // NOTE: to also grab statuses, use GetAttributeStatus and check for CHIP_END_OF_TLV + ReturnErrorOnFailure(attributeReportParser.GetAttributeData(&dataParser)); + + DecodedAttributeData decoded; + ReturnErrorOnFailure(decoded.DecodeFrom(dataParser)); + decoded_items.push_back(decoded); + } + + if ((CHIP_END_OF_TLV != err) && (err != CHIP_NO_ERROR)) + { + return CHIP_NO_ERROR; + } + + ReturnErrorOnFailure(reportIBsReader.ExitContainer(outer2)); + ReturnErrorOnFailure(reportIBsReader.ExitContainer(outer1)); + + err = reportIBsReader.Next(); + + if (CHIP_ERROR_END_OF_TLV == err) + { + return CHIP_NO_ERROR; + } + if (CHIP_NO_ERROR == err) + { + // This is NOT ok ... we have multiple things in our buffer? + return CHIP_ERROR_INVALID_ARGUMENT; + } + + return err; +} + +CHIP_ERROR EncodedReportIBs::StartEncoding(app::AttributeReportIBs::Builder & builder) +{ + mEncodeWriter.Init(mTlvDataBuffer); + ReturnErrorOnFailure(mEncodeWriter.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, mOuterStructureType)); + return builder.Init(&mEncodeWriter, to_underlying(ReportDataMessage::Tag::kAttributeReportIBs)); +} + +CHIP_ERROR EncodedReportIBs::FinishEncoding(app::AttributeReportIBs::Builder & builder) +{ + builder.EndOfContainer(); + ReturnErrorOnFailure(mEncodeWriter.EndContainer(mOuterStructureType)); + ReturnErrorOnFailure(mEncodeWriter.Finalize()); + + mDecodeSpan = ByteSpan(mTlvDataBuffer, mEncodeWriter.GetLengthWritten()); + return CHIP_NO_ERROR; +} + +CHIP_ERROR EncodedReportIBs::Decode(std::vector & decoded_items) +{ + return DecodeAttributeReportIBs(mDecodeSpan, decoded_items); +} + +} // namespace Test +} // namespace chip diff --git a/src/app/codegen-data-model/tests/AttributeReportIBEncodeDecode.h b/src/app/codegen-data-model/tests/AttributeReportIBEncodeDecode.h new file mode 100644 index 00000000000000..83c079f810ccb8 --- /dev/null +++ b/src/app/codegen-data-model/tests/AttributeReportIBEncodeDecode.h @@ -0,0 +1,68 @@ +/* + * 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 +#include +#include +#include +#include + +#include + +namespace chip { +namespace Test { + +struct DecodedAttributeData +{ + chip::DataVersion dataVersion; + chip::app::ConcreteDataAttributePath attributePath; + chip::TLV::TLVReader dataReader; + + CHIP_ERROR DecodeFrom(const chip::app::AttributeDataIB::Parser & parser); +}; + +CHIP_ERROR DecodeAttributeReportIBs(ByteSpan data, std::vector & decoded_items); + +/// Maintains an internal TLV buffer for data encoding and +/// decoding for ReportIBs. +/// +/// Main use case is that explicit TLV layouts (structure and container starting) need to be +/// prepared to have a proper AttributeReportIBs::Builder/parser to exist. +class EncodedReportIBs +{ +public: + /// Initialize the report structures required to encode a + CHIP_ERROR StartEncoding(app::AttributeReportIBs::Builder & builder); + CHIP_ERROR FinishEncoding(app::AttributeReportIBs::Builder & builder); + + /// Decode the embedded attribute report IBs. + /// The TLVReaders inside data have a lifetime tied to the current object (its readers point + /// inside the current object) + CHIP_ERROR Decode(std::vector & decoded_items); + +private: + uint8_t mTlvDataBuffer[1024]; + TLV::TLVType mOuterStructureType; + TLV::TLVWriter mEncodeWriter; + ByteSpan mDecodeSpan; +}; + +} // namespace Test +} // namespace chip diff --git a/src/app/codegen-data-model/tests/BUILD.gn b/src/app/codegen-data-model/tests/BUILD.gn index a3857184c25b74..3d265a96a66b29 100644 --- a/src/app/codegen-data-model/tests/BUILD.gn +++ b/src/app/codegen-data-model/tests/BUILD.gn @@ -15,17 +15,41 @@ import("//build_overrides/chip.gni") import("${chip_root}/build/chip/chip_test_suite.gni") import("${chip_root}/src/app/codegen-data-model/model.gni") +source_set("ember_extra_files") { + sources = [ + # This IS TERRIBLE, however we want to pretend AAI exists for global + # items and we need a shared IO storage to reduce overhead between + # data-model access and ember-compatibility (we share the same buffer) + "${chip_root}/src/app/util/ember-global-attribute-access-interface.cpp", + "${chip_root}/src/app/util/ember-io-storage.cpp", + "AttributeReportIBEncodeDecode.cpp", + "AttributeReportIBEncodeDecode.h", + "EmberReadWriteOverride.cpp", + "EmberReadWriteOverride.h", + "InteractionModelTemporaryOverrides.cpp", + ] + + public_deps = [ + "${chip_root}/src/app/util/mock:mock_ember", + "${chip_root}/src/protocols", + ] +} + source_set("mock_model") { - sources = codegen_interaction_model_SOURCES + sources = codegen_data_model_SOURCES - public_deps = codegen_interaction_model_PUBLIC_DEPS + public_deps = codegen_data_model_PUBLIC_DEPS # this ties in the codegen model to an actual ember implementation - public_deps += [ "${chip_root}/src/app/util/mock:mock_ember" ] + public_deps += [ + ":ember_extra_files", + "${chip_root}/src/app/util/mock:mock_ember", + "${chip_root}/src/lib/core:string-builder-adapters", + ] } chip_test_suite("tests") { - output_name = "libCodegenInteractionModelTests" + output_name = "libCodegenDataModelTests" test_sources = [ "TestCodegenModelViaMocks.cpp" ] diff --git a/src/app/codegen-data-model/tests/EmberReadWriteOverride.cpp b/src/app/codegen-data-model/tests/EmberReadWriteOverride.cpp new file mode 100644 index 00000000000000..8c65ee29b05556 --- /dev/null +++ b/src/app/codegen-data-model/tests/EmberReadWriteOverride.cpp @@ -0,0 +1,87 @@ +/* + * 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 "EmberReadWriteOverride.h" + +#include + +using chip::Protocols::InteractionModel::Status; + +namespace { + +constexpr size_t kMaxTestIoSize = 128; + +uint8_t gEmberIoBuffer[kMaxTestIoSize]; +size_t gEmberIoBufferFill; +Status gEmberStatusCode = Status::InvalidAction; + +} // namespace + +namespace chip { +namespace Test { + +void SetEmberReadOutput(std::variant what) +{ + if (const chip::ByteSpan * span = std::get_if(&what)) + { + gEmberStatusCode = Status::Success; + + if (span->size() > sizeof(gEmberIoBuffer)) + { + ChipLogError(Test, "UNEXPECTED STATE: Too much data set for ember read output"); + gEmberStatusCode = Status::ResourceExhausted; + + return; + } + + memcpy(gEmberIoBuffer, span->data(), span->size()); + gEmberIoBufferFill = span->size(); + return; + } + + if (const Status * status = std::get_if(&what)) + { + gEmberIoBufferFill = 0; + gEmberStatusCode = *status; + return; + } + + ChipLogError(Test, "UNEXPECTED STATE: invalid ember read output setting"); + gEmberStatusCode = Status::InvalidAction; +} + +} // namespace Test +} // namespace chip + +/// TODO: this SHOULD be part of attribute-storage mocks and allow proper I/O control +/// with helpers for "ember encoding" +Status emAfReadOrWriteAttribute(const EmberAfAttributeSearchRecord * attRecord, const EmberAfAttributeMetadata ** metadata, + uint8_t * buffer, uint16_t readLength, bool write) +{ + if (gEmberStatusCode != Status::Success) + { + return gEmberStatusCode; + } + + if (gEmberIoBufferFill > readLength) + { + ChipLogError(Test, "Internal TEST error: insufficient output buffer space."); + return Status::ResourceExhausted; + } + + memcpy(buffer, gEmberIoBuffer, gEmberIoBufferFill); + return Status::Success; +} diff --git a/src/app/codegen-data-model/tests/EmberReadWriteOverride.h b/src/app/codegen-data-model/tests/EmberReadWriteOverride.h new file mode 100644 index 00000000000000..527a6cfd0d18c7 --- /dev/null +++ b/src/app/codegen-data-model/tests/EmberReadWriteOverride.h @@ -0,0 +1,33 @@ +/* + * 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 Test { + +/// specify what the next `emAfReadOrWriteAttribute` will contain +/// +/// It may return a value with success or some error. The byte span WILL BE COPIED. +void SetEmberReadOutput(std::variant what); + +} // namespace Test +} // namespace chip diff --git a/src/app/codegen-data-model/tests/InteractionModelTemporaryOverrides.cpp b/src/app/codegen-data-model/tests/InteractionModelTemporaryOverrides.cpp new file mode 100644 index 00000000000000..868a26880d3ce7 --- /dev/null +++ b/src/app/codegen-data-model/tests/InteractionModelTemporaryOverrides.cpp @@ -0,0 +1,84 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +using chip::Protocols::InteractionModel::Status; + +// TODO: most of the functions here are part of EmberCompatibilityFunctions and is NOT decoupled +// from IM current, but it SHOULD be +// Open issue https://github.com/project-chip/connectedhomeip/issues/34137 for this work. +namespace chip { +namespace app { + +bool ConcreteAttributePathExists(const ConcreteAttributePath & aPath) +{ + // TODO: this is just a noop which may be potentially invalid + return true; +} + +bool IsClusterDataVersionEqual(const ConcreteClusterPath & aConcreteClusterPath, DataVersion aRequiredVersion) +{ + // TODO: this is just a noop which may be potentially invalid + return true; +} + +const EmberAfAttributeMetadata * GetAttributeMetadata(const ConcreteAttributePath & aPath) +{ + return emberAfLocateAttributeMetadata(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId); +} + +Status ServerClusterCommandExists(const ConcreteCommandPath & aCommandPath) +{ + // TODO: this is just a noop which may be potentially invalid + return Status::Success; +} + +Status CheckEventSupportStatus(const ConcreteEventPath & aPath) +{ + // TODO: this is just a noop which may be potentially invalid + return Status::Success; +} + +CHIP_ERROR WriteSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteDataAttributePath & aPath, + TLV::TLVReader & aReader, WriteHandler * apWriteHandler) +{ + // this is just to get things to compile. eventually this method should NOT be used + return CHIP_ERROR_NOT_IMPLEMENTED; +} + +CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, + const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, + AttributeEncodeState * apEncoderState) +{ + // this is just to get things to compile. eventually this method should NOT be used + return CHIP_ERROR_NOT_IMPLEMENTED; +} + +void DispatchSingleClusterCommand(const ConcreteCommandPath & aRequestCommandPath, chip::TLV::TLVReader & aReader, + CommandHandler * apCommandObj) +{ + // TODO: total hardcoded noop +} + +} // namespace app +} // namespace chip diff --git a/src/app/codegen-data-model/tests/TestCodegenModelViaMocks.cpp b/src/app/codegen-data-model/tests/TestCodegenModelViaMocks.cpp index 4403303c70cbb3..11264bbd33a36e 100644 --- a/src/app/codegen-data-model/tests/TestCodegenModelViaMocks.cpp +++ b/src/app/codegen-data-model/tests/TestCodegenModelViaMocks.cpp @@ -1,5 +1,4 @@ /* - * * Copyright (c) 2024 Project CHIP Authors * All rights reserved. * @@ -17,12 +16,35 @@ */ #include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include +#include +#include #include +#include +#include +#include +#include #include +#include using namespace chip; using namespace chip::Test; @@ -32,6 +54,9 @@ using namespace chip::app::Clusters::Globals::Attributes; namespace { +constexpr FabricIndex kTestFabrixIndex = kMinValidFabricIndex; +constexpr NodeId kTestNodeId = 0xFFFF'1234'ABCD'4321; + constexpr EndpointId kEndpointIdThatIsMissing = kMockEndpointMin - 1; static_assert(kEndpointIdThatIsMissing != kInvalidEndpointId); @@ -39,6 +64,85 @@ static_assert(kEndpointIdThatIsMissing != kMockEndpoint1); static_assert(kEndpointIdThatIsMissing != kMockEndpoint2); static_assert(kEndpointIdThatIsMissing != kMockEndpoint3); +constexpr Access::SubjectDescriptor kAdminSubjectDescriptor{ + .fabricIndex = kTestFabrixIndex, + .authMode = Access::AuthMode::kCase, + .subject = kTestNodeId, +}; +constexpr Access::SubjectDescriptor kViewSubjectDescriptor{ + .fabricIndex = kTestFabrixIndex + 1, + .authMode = Access::AuthMode::kCase, + .subject = kTestNodeId, +}; + +constexpr Access::SubjectDescriptor kDenySubjectDescriptor{ + .fabricIndex = kTestFabrixIndex + 2, + .authMode = Access::AuthMode::kCase, + .subject = kTestNodeId, +}; + +bool operator==(const Access::SubjectDescriptor & a, const Access::SubjectDescriptor & b) +{ + if (a.fabricIndex != b.fabricIndex) + { + return false; + } + if (a.authMode != b.authMode) + { + return false; + } + if (a.subject != b.subject) + { + return false; + } + for (unsigned i = 0; i < a.cats.values.size(); i++) + { + if (a.cats.values[i] != b.cats.values[i]) + { + return false; + } + } + return true; +} + +class MockAccessControl : public Access::AccessControl::Delegate, public Access::AccessControl::DeviceTypeResolver +{ +public: + CHIP_ERROR Check(const Access::SubjectDescriptor & subjectDescriptor, const Access::RequestPath & requestPath, + Access::Privilege requestPrivilege) override + { + if (subjectDescriptor == kAdminSubjectDescriptor) + { + return CHIP_NO_ERROR; + } + if ((subjectDescriptor == kViewSubjectDescriptor) && (requestPrivilege == Access::Privilege::kView)) + { + return CHIP_NO_ERROR; + } + return CHIP_ERROR_ACCESS_DENIED; + } + + bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint) override { return true; } +}; + +class ScopedMockAccessControl +{ +public: + ScopedMockAccessControl() { Access::GetAccessControl().Init(&mMock, mMock); } + ~ScopedMockAccessControl() { Access::GetAccessControl().Finish(); } + +private: + MockAccessControl mMock; +}; + +#define MOCK_ATTRIBUTE_ID_FOR_NULLABLE_TYPE(zcl_type) MockAttributeId(zcl_type + 0x1000) +#define MOCK_ATTRIBUTE_CONFIG_NULLABLE(zcl_type) \ + MockAttributeConfig(MOCK_ATTRIBUTE_ID_FOR_NULLABLE_TYPE(zcl_type), zcl_type, ATTRIBUTE_MASK_WRITABLE | ATTRIBUTE_MASK_NULLABLE) + +#define MOCK_ATTRIBUTE_ID_FOR_NON_NULLABLE_TYPE(zcl_type) MockAttributeId(zcl_type + 0x2000) +#define MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(zcl_type) \ + MockAttributeConfig(MOCK_ATTRIBUTE_ID_FOR_NON_NULLABLE_TYPE(zcl_type), zcl_type, ATTRIBUTE_MASK_WRITABLE) + // clang-format off const MockNodeConfig gTestNodeConfig({ MockEndpointConfig(kMockEndpoint1, { @@ -88,7 +192,155 @@ const MockNodeConfig gTestNodeConfig({ ClusterRevision::Id, FeatureMap::Id, }), MockClusterConfig(MockClusterId(4), { - ClusterRevision::Id, FeatureMap::Id, + ClusterRevision::Id, + FeatureMap::Id, + // several attributes of varying data types for testing. + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_BOOLEAN_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_BITMAP8_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_BITMAP16_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_BITMAP32_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_BITMAP64_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_INT8U_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_INT16U_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_INT24U_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_INT32U_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_INT40U_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_INT48U_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_INT56U_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_INT64U_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_INT8S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_INT16S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_INT24S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_INT32S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_INT40S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_INT48S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_INT56S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_INT64S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_ENUM8_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_ENUM16_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_PRIORITY_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_STATUS_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_SINGLE_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_DOUBLE_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_OCTET_STRING_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_CHAR_STRING_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_ARRAY_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_STRUCT_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_GROUP_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_ENDPOINT_NO_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_VENDOR_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_DEVTYPE_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_FABRIC_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_FABRIC_IDX_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_ENTRY_IDX_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_DATA_VER_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_EVENT_NO_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_SEMTAG_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_NAMESPACE_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_TAG_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_SYSTIME_US_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_SYSTIME_MS_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_ELAPSED_S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_TEMPERATURE_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_POWER_MW_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_AMPERAGE_MA_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_VOLTAGE_MV_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_ENERGY_MWH_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_TOD_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_DATE_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_EPOCH_US_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_EPOCH_S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_POSIX_MS_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_PERCENT_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_PERCENT100THS_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_CLUSTER_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_ATTRIB_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_FIELD_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_EVENT_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_COMMAND_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_ACTION_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_TRANS_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_NODE_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_IPADR_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_IPV4ADR_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_IPV6ADR_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_IPV6PRE_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(ZCL_HWADR_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_BOOLEAN_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_BITMAP8_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_BITMAP16_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_BITMAP32_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_BITMAP64_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_INT8U_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_INT16U_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_INT24U_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_INT32U_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_INT40U_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_INT48U_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_INT56U_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_INT64U_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_INT8S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_INT16S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_INT24S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_INT32S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_INT40S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_INT48S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_INT56S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_INT64S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_ENUM8_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_ENUM16_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_PRIORITY_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_STATUS_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_SINGLE_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_DOUBLE_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_OCTET_STRING_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_CHAR_STRING_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_ARRAY_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_STRUCT_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_GROUP_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_ENDPOINT_NO_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_VENDOR_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_DEVTYPE_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_FABRIC_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_FABRIC_IDX_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_ENTRY_IDX_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_DATA_VER_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_EVENT_NO_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_SEMTAG_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_NAMESPACE_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_TAG_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_SYSTIME_US_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_SYSTIME_MS_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_ELAPSED_S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_TEMPERATURE_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_POWER_MW_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_AMPERAGE_MA_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_VOLTAGE_MV_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_ENERGY_MWH_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_TOD_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_DATE_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_EPOCH_US_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_EPOCH_S_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_POSIX_MS_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_PERCENT_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_PERCENT100THS_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_CLUSTER_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_ATTRIB_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_FIELD_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_EVENT_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_COMMAND_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_ACTION_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_TRANS_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_NODE_ID_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_IPADR_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_IPV4ADR_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_IPV6ADR_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_IPV6PRE_ATTRIBUTE_TYPE), + MOCK_ATTRIBUTE_CONFIG_NULLABLE(ZCL_HWADR_ATTRIBUTE_TYPE), }), }), }); @@ -100,6 +352,253 @@ struct UseMockNodeConfig ~UseMockNodeConfig() { ResetMockNodeConfig(); } }; +template +CHIP_ERROR DecodeList(TLV::TLVReader & reader, std::vector & out) +{ + TLV::TLVType outer; + ReturnErrorOnFailure(reader.EnterContainer(outer)); + while (true) + { + CHIP_ERROR err = reader.Next(); + + if (err == CHIP_END_OF_TLV) + { + return CHIP_NO_ERROR; + } + ReturnErrorOnFailure(err); + + T value; + ReturnErrorOnFailure(chip::app::DataModel::Decode(reader, value)); + out.emplace_back(std::move(value)); + } +} + +class UnsupportedReadAccessInterface : public AttributeAccessInterface +{ +public: + UnsupportedReadAccessInterface(ConcreteAttributePath path) : + AttributeAccessInterface(MakeOptional(path.mEndpointId), path.mClusterId), mPath(path) + {} + ~UnsupportedReadAccessInterface() = default; + + CHIP_ERROR Read(const ConcreteReadAttributePath & path, AttributeValueEncoder & encoder) override + { + if (static_cast(path) != mPath) + { + // returning without trying to handle means "I do not handle this" + return CHIP_NO_ERROR; + } + + return CHIP_IM_GLOBAL_STATUS(UnsupportedRead); + } + +private: + ConcreteAttributePath mPath; +}; + +class StructAttributeAccessInterface : public AttributeAccessInterface +{ +public: + StructAttributeAccessInterface(ConcreteAttributePath path) : + AttributeAccessInterface(MakeOptional(path.mEndpointId), path.mClusterId), mPath(path) + {} + ~StructAttributeAccessInterface() = default; + + CHIP_ERROR Read(const ConcreteReadAttributePath & path, AttributeValueEncoder & encoder) override + { + if (static_cast(path) != mPath) + { + // returning without trying to handle means "I do not handle this" + return CHIP_NO_ERROR; + } + + return encoder.Encode(mData); + } + + void SetReturnedData(const Clusters::UnitTesting::Structs::SimpleStruct::Type & data) { mData = data; } + Clusters::UnitTesting::Structs::SimpleStruct::Type simpleStruct; + +private: + ConcreteAttributePath mPath; + Clusters::UnitTesting::Structs::SimpleStruct::Type mData; +}; + +class ListAttributeAcessInterface : public AttributeAccessInterface +{ +public: + ListAttributeAcessInterface(ConcreteAttributePath path) : + AttributeAccessInterface(MakeOptional(path.mEndpointId), path.mClusterId), mPath(path) + {} + ~ListAttributeAcessInterface() = default; + + CHIP_ERROR Read(const ConcreteReadAttributePath & path, AttributeValueEncoder & encoder) override + { + if (static_cast(path) != mPath) + { + // returning without trying to handle means "I do not handle this" + return CHIP_NO_ERROR; + } + + return encoder.EncodeList([this](const auto & listEncoder) { + for (unsigned i = 0; i < mCount; i++) + { + mData.a = static_cast(i % 0xFF); + ReturnErrorOnFailure(listEncoder.Encode(mData)); + } + return CHIP_NO_ERROR; + }); + } + + void SetReturnedData(const Clusters::UnitTesting::Structs::SimpleStruct::Type & data) { mData = data; } + void SetReturnedDataCount(unsigned count) { mCount = count; } + Clusters::UnitTesting::Structs::SimpleStruct::Type simpleStruct; + +private: + ConcreteAttributePath mPath; + Clusters::UnitTesting::Structs::SimpleStruct::Type mData; + unsigned mCount = 0; +}; + +/// RAII registration of an attribute access interface +template +class RegisteredAttributeAccessInterface +{ +public: + template + RegisteredAttributeAccessInterface(Args &&... args) : mData(std::forward(args)...) + { + VerifyOrDie(registerAttributeAccessOverride(&mData)); + } + ~RegisteredAttributeAccessInterface() { unregisterAttributeAccessOverride(&mData); } + + T * operator->() { return &mData; } + T & operator*() { return mData; } + +private: + T mData; +}; + +/// Contains a `ReadAttributeRequest` as well as classes to convert this into a AttributeReportIBs +/// and later decode it +/// +/// It wraps boilerplate code to obtain a `AttributeValueEncoder` as well as later decoding +/// the underlying encoded data for verification. +struct TestReadRequest +{ + ReadAttributeRequest request; + + // encoded-used classes + EncodedReportIBs encodedIBs; + AttributeReportIBs::Builder reportBuilder; + std::unique_ptr encoder; + + TestReadRequest(const Access::SubjectDescriptor & subject, const ConcreteAttributePath & path) + { + // operationFlags is 0 i.e. not internal + // readFlags is 0 i.e. not fabric filtered + // dataVersion is missing (no data version filtering) + request.subjectDescriptor = subject; + request.path = path; + } + + std::unique_ptr StartEncoding(chip::app::InteractionModel::DataModel * model, + AttributeEncodeState state = AttributeEncodeState()) + { + std::optional info = model->GetClusterInfo(request.path); + if (!info.has_value()) + { + ChipLogError(Test, "Missing cluster information - no data version"); + return nullptr; + } + + DataVersion dataVersion = info->dataVersion; // NOLINT(bugprone-unchecked-optional-access) + + CHIP_ERROR err = encodedIBs.StartEncoding(reportBuilder); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Test, "FAILURE starting encoding %" CHIP_ERROR_FORMAT, err.Format()); + return nullptr; + } + + // TODO: could we test isFabricFiltered and EncodeState? + + // request.subjectDescriptor is known non-null because it is set in the constructor + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + return std::make_unique(reportBuilder, *request.subjectDescriptor, request.path, dataVersion, + false /* aIsFabricFiltered */, state); + } + + CHIP_ERROR FinishEncoding() { return encodedIBs.FinishEncoding(reportBuilder); } +}; + +template +void TestEmberScalarTypeRead(typename NumericAttributeTraits::WorkingType value) +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + TestReadRequest testRequest( + kAdminSubjectDescriptor, + ConcreteAttributePath(kMockEndpoint3, MockClusterId(4), MOCK_ATTRIBUTE_ID_FOR_NON_NULLABLE_TYPE(ZclType))); + + // Ember encoding for integers is IDENTICAL to the in-memory representation for them + typename NumericAttributeTraits::StorageType storage; + NumericAttributeTraits::WorkingToStorage(value, storage); + chip::Test::SetEmberReadOutput(ByteSpan(reinterpret_cast(&storage), sizeof(storage))); + + // Data read via the encoder + std::unique_ptr encoder = testRequest.StartEncoding(&model); + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_NO_ERROR); + ASSERT_EQ(testRequest.FinishEncoding(), CHIP_NO_ERROR); + + // Validate after read + std::vector attribute_data; + ASSERT_EQ(testRequest.encodedIBs.Decode(attribute_data), CHIP_NO_ERROR); + ASSERT_EQ(attribute_data.size(), 1u); + + DecodedAttributeData & encodedData = attribute_data[0]; + ASSERT_EQ(encodedData.attributePath, testRequest.request.path); + + typename NumericAttributeTraits::WorkingType actual; + ASSERT_EQ(chip::app::DataModel::Decode::WorkingType>(encodedData.dataReader, actual), + CHIP_NO_ERROR); + ASSERT_EQ(actual, value); +} + +template +void TestEmberScalarNullRead() +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + TestReadRequest testRequest( + kAdminSubjectDescriptor, + ConcreteAttributePath(kMockEndpoint3, MockClusterId(4), MOCK_ATTRIBUTE_ID_FOR_NULLABLE_TYPE(ZclType))); + + // Ember encoding for integers is IDENTICAL to the in-memory representation for them + typename NumericAttributeTraits::StorageType nullValue; + NumericAttributeTraits::SetNull(nullValue); + chip::Test::SetEmberReadOutput(ByteSpan(reinterpret_cast(&nullValue), sizeof(nullValue))); + + // Data read via the encoder + std::unique_ptr encoder = testRequest.StartEncoding(&model); + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_NO_ERROR); + ASSERT_EQ(testRequest.FinishEncoding(), CHIP_NO_ERROR); + + // Validate after read + std::vector attribute_data; + ASSERT_EQ(testRequest.encodedIBs.Decode(attribute_data), CHIP_NO_ERROR); + ASSERT_EQ(attribute_data.size(), 1u); + + DecodedAttributeData & encodedData = attribute_data[0]; + ASSERT_EQ(encodedData.attributePath, testRequest.request.path); + chip::app::DataModel::Nullable::WorkingType> actual; + ASSERT_EQ(chip::app::DataModel::Decode(encodedData.dataReader, actual), CHIP_NO_ERROR); + ASSERT_TRUE(actual.IsNull()); +} + } // namespace TEST(TestCodegenModelViaMocks, IterateOverEndpoints) @@ -486,3 +985,685 @@ TEST(TestCodegenModelViaMocks, IterateOverGeneratedCommands) EXPECT_FALSE(path.HasValidIds()); } } + +TEST(TestCodegenModelViaMocks, EmberAttributeReadAclDeny) +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + TestReadRequest testRequest(kDenySubjectDescriptor, + ConcreteAttributePath(kMockEndpoint1, MockClusterId(1), MockAttributeId(10))); + std::unique_ptr encoder = testRequest.StartEncoding(&model); + + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_ERROR_ACCESS_DENIED); +} + +TEST(TestCodegenModelViaMocks, ReadForInvalidGlobalAttributePath) +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + { + TestReadRequest testRequest(kAdminSubjectDescriptor, + ConcreteAttributePath(kEndpointIdThatIsMissing, MockClusterId(1), AttributeList::Id)); + std::unique_ptr encoder = testRequest.StartEncoding(&model); + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint)); + } + + { + TestReadRequest testRequest(kAdminSubjectDescriptor, + ConcreteAttributePath(kMockEndpoint1, kInvalidClusterId, AttributeList::Id)); + std::unique_ptr encoder = testRequest.StartEncoding(&model); + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_IM_GLOBAL_STATUS(UnsupportedCluster)); + } +} + +TEST(TestCodegenModelViaMocks, EmberAttributeInvalidRead) +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + // Invalid attribute + { + TestReadRequest testRequest(kAdminSubjectDescriptor, + ConcreteAttributePath(kMockEndpoint1, MockClusterId(1), MockAttributeId(10))); + std::unique_ptr encoder = testRequest.StartEncoding(&model); + + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute)); + } + + // Invalid cluster + { + TestReadRequest testRequest(kAdminSubjectDescriptor, + ConcreteAttributePath(kMockEndpoint1, MockClusterId(100), MockAttributeId(1))); + std::unique_ptr encoder = testRequest.StartEncoding(&model); + + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_IM_GLOBAL_STATUS(UnsupportedCluster)); + } + + // Invalid endpoint + { + TestReadRequest testRequest(kAdminSubjectDescriptor, + ConcreteAttributePath(kEndpointIdThatIsMissing, MockClusterId(1), MockAttributeId(1))); + std::unique_ptr encoder = testRequest.StartEncoding(&model); + + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint)); + } +} + +TEST(TestCodegenModelViaMocks, EmberAttributePathExpansionAccessDeniedRead) +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + TestReadRequest testRequest(kDenySubjectDescriptor, + ConcreteAttributePath(kMockEndpoint1, MockClusterId(1), MockAttributeId(10))); + std::unique_ptr encoder = testRequest.StartEncoding(&model); + + testRequest.request.path.mExpanded = true; + + // For expanded paths, access control failures succeed without encoding anything + // This is temporary until ACL checks are moved inside the IM/ReportEngine + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_NO_ERROR); + ASSERT_FALSE(encoder->TriedEncode()); +} + +TEST(TestCodegenModelViaMocks, AccessInterfaceUnsupportedRead) +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + const ConcreteAttributePath kTestPath(kMockEndpoint3, MockClusterId(4), + MOCK_ATTRIBUTE_ID_FOR_NON_NULLABLE_TYPE(ZCL_STRUCT_ATTRIBUTE_TYPE)); + + TestReadRequest testRequest(kAdminSubjectDescriptor, kTestPath); + RegisteredAttributeAccessInterface aai(kTestPath); + + testRequest.request.path.mExpanded = true; + + // For expanded paths, unsupported read from AAI (i.e. reading write-only data) + // succeed without attempting to encode. + // This is temporary until ACL checks are moved inside the IM/ReportEngine + std::unique_ptr encoder = testRequest.StartEncoding(&model); + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_NO_ERROR); + ASSERT_FALSE(encoder->TriedEncode()); +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadInt32S) +{ + TestEmberScalarTypeRead(-1234); +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadEnum16) +{ + TestEmberScalarTypeRead(0x1234); +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadFloat) +{ + TestEmberScalarTypeRead(0.625); +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadDouble) +{ + TestEmberScalarTypeRead(0.625); +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadInt24U) +{ + TestEmberScalarTypeRead, ZCL_INT24U_ATTRIBUTE_TYPE>(0x1234AB); +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadInt32U) +{ + TestEmberScalarTypeRead(0x1234ABCD); +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadInt40U) +{ + TestEmberScalarTypeRead, ZCL_INT40U_ATTRIBUTE_TYPE>(0x1122334455); +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadInt48U) +{ + TestEmberScalarTypeRead, ZCL_INT48U_ATTRIBUTE_TYPE>(0xAABB11223344); +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadInt56U) +{ + TestEmberScalarTypeRead, ZCL_INT56U_ATTRIBUTE_TYPE>(0xAABB11223344); +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadBool) +{ + TestEmberScalarTypeRead(true); + TestEmberScalarTypeRead(false); +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadInt8U) +{ + TestEmberScalarTypeRead(0x12); +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadNulls) +{ + TestEmberScalarNullRead(); + TestEmberScalarNullRead(); + TestEmberScalarNullRead, ZCL_INT24U_ATTRIBUTE_TYPE>(); + TestEmberScalarNullRead(); + TestEmberScalarNullRead, ZCL_INT40U_ATTRIBUTE_TYPE>(); + TestEmberScalarNullRead, ZCL_INT48U_ATTRIBUTE_TYPE>(); + TestEmberScalarNullRead, ZCL_INT56U_ATTRIBUTE_TYPE>(); + TestEmberScalarNullRead(); + + TestEmberScalarNullRead(); + TestEmberScalarNullRead(); + TestEmberScalarNullRead, ZCL_INT24S_ATTRIBUTE_TYPE>(); + TestEmberScalarNullRead(); + TestEmberScalarNullRead, ZCL_INT40S_ATTRIBUTE_TYPE>(); + TestEmberScalarNullRead, ZCL_INT48S_ATTRIBUTE_TYPE>(); + TestEmberScalarNullRead, ZCL_INT56S_ATTRIBUTE_TYPE>(); + TestEmberScalarNullRead(); + + TestEmberScalarNullRead(); + + TestEmberScalarNullRead(); + TestEmberScalarNullRead(); +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadErrorReading) +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + { + TestReadRequest testRequest( + kAdminSubjectDescriptor, + ConcreteAttributePath(kMockEndpoint3, MockClusterId(4), + MOCK_ATTRIBUTE_ID_FOR_NULLABLE_TYPE(ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE))); + + chip::Test::SetEmberReadOutput(Protocols::InteractionModel::Status::Failure); + + // Actual read via an encoder + std::unique_ptr encoder = testRequest.StartEncoding(&model); + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_IM_GLOBAL_STATUS(Failure)); + } + + { + TestReadRequest testRequest( + kAdminSubjectDescriptor, + ConcreteAttributePath(kMockEndpoint3, MockClusterId(4), + MOCK_ATTRIBUTE_ID_FOR_NULLABLE_TYPE(ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE))); + + chip::Test::SetEmberReadOutput(Protocols::InteractionModel::Status::Busy); + + // Actual read via an encoder + std::unique_ptr encoder = testRequest.StartEncoding(&model); + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_IM_GLOBAL_STATUS(Busy)); + } +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadNullOctetString) +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + TestReadRequest testRequest(kAdminSubjectDescriptor, + ConcreteAttributePath(kMockEndpoint3, MockClusterId(4), + MOCK_ATTRIBUTE_ID_FOR_NULLABLE_TYPE(ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE))); + + // NOTE: This is a pascal string of size 0xFFFF which for null strings is a null marker + char data[] = "\xFF\xFFInvalid length string is null"; + chip::Test::SetEmberReadOutput(ByteSpan(reinterpret_cast(data), sizeof(data))); + + // Actual read via an encoder + std::unique_ptr encoder = testRequest.StartEncoding(&model); + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_NO_ERROR); + ASSERT_EQ(testRequest.FinishEncoding(), CHIP_NO_ERROR); + + // Validate after read + std::vector attribute_data; + ASSERT_EQ(testRequest.encodedIBs.Decode(attribute_data), CHIP_NO_ERROR); + ASSERT_EQ(attribute_data.size(), 1u); + + DecodedAttributeData & encodedData = attribute_data[0]; + ASSERT_EQ(encodedData.attributePath, testRequest.request.path); + + // data element should be null for the given 0xFFFF length + ASSERT_EQ(encodedData.dataReader.GetType(), TLV::kTLVType_Null); + + chip::app::DataModel::Nullable actual; + ASSERT_EQ(chip::app::DataModel::Decode(encodedData.dataReader, actual), CHIP_NO_ERROR); + ASSERT_TRUE(actual.IsNull()); +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadOctetString) +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + TestReadRequest testRequest( + kAdminSubjectDescriptor, + ConcreteAttributePath(kMockEndpoint3, MockClusterId(4), + MOCK_ATTRIBUTE_ID_FOR_NON_NULLABLE_TYPE(ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE))); + + // NOTE: This is a pascal string, so actual data is "test" + // the longer encoding is to make it clear we do not encode the overflow + char data[] = "\0\0testing here with overflow"; + uint16_t len = 4; + memcpy(data, &len, sizeof(uint16_t)); + chip::Test::SetEmberReadOutput(ByteSpan(reinterpret_cast(data), sizeof(data))); + + // Actual read via an encoder + std::unique_ptr encoder = testRequest.StartEncoding(&model); + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_NO_ERROR); + ASSERT_EQ(testRequest.FinishEncoding(), CHIP_NO_ERROR); + + // Validate after read + std::vector attribute_data; + ASSERT_EQ(testRequest.encodedIBs.Decode(attribute_data), CHIP_NO_ERROR); + ASSERT_EQ(attribute_data.size(), 1u); + + const DecodedAttributeData & encodedData = attribute_data[0]; + ASSERT_EQ(encodedData.attributePath, testRequest.request.path); + + // data element should be a encoded byte string as this is what the attribute type is + ASSERT_EQ(encodedData.dataReader.GetType(), TLV::kTLVType_ByteString); + ByteSpan actual; + ASSERT_EQ(encodedData.dataReader.Get(actual), CHIP_NO_ERROR); + + ByteSpan expected(reinterpret_cast(data + 2), 4); + ASSERT_TRUE(actual.data_equal(expected)); +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadLongOctetString) +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + TestReadRequest testRequest(kAdminSubjectDescriptor, + ConcreteAttributePath(kMockEndpoint3, MockClusterId(4), + MOCK_ATTRIBUTE_ID_FOR_NON_NULLABLE_TYPE(ZCL_OCTET_STRING_ATTRIBUTE_TYPE))); + + // NOTE: This is a pascal string, so actual data is "test" + // the longer encoding is to make it clear we do not encode the overflow + const char data[] = "\x04testing here with overflow"; + chip::Test::SetEmberReadOutput(ByteSpan(reinterpret_cast(data), sizeof(data))); + + // Actual read via an encoder + std::unique_ptr encoder = testRequest.StartEncoding(&model); + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_NO_ERROR); + ASSERT_EQ(testRequest.FinishEncoding(), CHIP_NO_ERROR); + + // Validate after read + std::vector attribute_data; + ASSERT_EQ(testRequest.encodedIBs.Decode(attribute_data), CHIP_NO_ERROR); + ASSERT_EQ(attribute_data.size(), 1u); + + const DecodedAttributeData & encodedData = attribute_data[0]; + ASSERT_EQ(encodedData.attributePath, testRequest.request.path); + + // data element should be a encoded byte string as this is what the attribute type is + ASSERT_EQ(encodedData.dataReader.GetType(), TLV::kTLVType_ByteString); + ByteSpan actual; + ASSERT_EQ(encodedData.dataReader.Get(actual), CHIP_NO_ERROR); + + ByteSpan expected(reinterpret_cast(data + 1), 4); + ASSERT_TRUE(actual.data_equal(expected)); +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadShortString) +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + TestReadRequest testRequest(kAdminSubjectDescriptor, + ConcreteAttributePath(kMockEndpoint3, MockClusterId(4), + MOCK_ATTRIBUTE_ID_FOR_NON_NULLABLE_TYPE(ZCL_CHAR_STRING_ATTRIBUTE_TYPE))); + + // NOTE: This is a pascal string, so actual data is "abcde" + // the longer encoding is to make it clear we do not encode the overflow + char data[] = "\0abcdef...this is the alphabet"; + uint16_t len = 5; + memcpy(data, &len, sizeof(uint8_t)); + chip::Test::SetEmberReadOutput(ByteSpan(reinterpret_cast(data), sizeof(data))); + + // Actual read via an encoder + std::unique_ptr encoder = testRequest.StartEncoding(&model); + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_NO_ERROR); + ASSERT_EQ(testRequest.FinishEncoding(), CHIP_NO_ERROR); + + // Validate after reading + std::vector attribute_data; + ASSERT_EQ(testRequest.encodedIBs.Decode(attribute_data), CHIP_NO_ERROR); + ASSERT_EQ(attribute_data.size(), 1u); + + const DecodedAttributeData & encodedData = attribute_data[0]; + ASSERT_EQ(encodedData.attributePath, testRequest.request.path); + + // data element should be a encoded byte string as this is what the attribute type is + ASSERT_EQ(encodedData.dataReader.GetType(), TLV::kTLVType_UTF8String); + CharSpan actual; + ASSERT_EQ(encodedData.dataReader.Get(actual), CHIP_NO_ERROR); + ASSERT_TRUE(actual.data_equal("abcde"_span)); +} + +TEST(TestCodegenModelViaMocks, EmberAttributeReadLongString) +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + TestReadRequest testRequest( + kAdminSubjectDescriptor, + ConcreteAttributePath(kMockEndpoint3, MockClusterId(4), + MOCK_ATTRIBUTE_ID_FOR_NON_NULLABLE_TYPE(ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE))); + + // NOTE: This is a pascal string, so actual data is "abcde" + // the longer encoding is to make it clear we do not encode the overflow + char data[] = "\0\0abcdef...this is the alphabet"; + uint16_t len = 5; + memcpy(data, &len, sizeof(uint16_t)); + chip::Test::SetEmberReadOutput(ByteSpan(reinterpret_cast(data), sizeof(data))); + + // Actual read via an encoder + std::unique_ptr encoder = testRequest.StartEncoding(&model); + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_NO_ERROR); + ASSERT_EQ(testRequest.FinishEncoding(), CHIP_NO_ERROR); + + // Validate after reading + std::vector attribute_data; + ASSERT_EQ(testRequest.encodedIBs.Decode(attribute_data), CHIP_NO_ERROR); + ASSERT_EQ(attribute_data.size(), 1u); + + const DecodedAttributeData & encodedData = attribute_data[0]; + ASSERT_EQ(encodedData.attributePath, testRequest.request.path); + + // data element should be a encoded byte string as this is what the attribute type is + ASSERT_EQ(encodedData.dataReader.GetType(), TLV::kTLVType_UTF8String); + CharSpan actual; + ASSERT_EQ(encodedData.dataReader.Get(actual), CHIP_NO_ERROR); + ASSERT_TRUE(actual.data_equal("abcde"_span)); +} + +TEST(TestCodegenModelViaMocks, AttributeAccessInterfaceStructRead) +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + const ConcreteAttributePath kStructPath(kMockEndpoint3, MockClusterId(4), + MOCK_ATTRIBUTE_ID_FOR_NON_NULLABLE_TYPE(ZCL_STRUCT_ATTRIBUTE_TYPE)); + + TestReadRequest testRequest(kAdminSubjectDescriptor, kStructPath); + RegisteredAttributeAccessInterface aai(kStructPath); + + aai->SetReturnedData(Clusters::UnitTesting::Structs::SimpleStruct::Type{ + .a = 123, + .b = true, + .e = "foo"_span, + .g = 0.5, + .h = 0.125, + }); + + std::unique_ptr encoder = testRequest.StartEncoding(&model); + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_NO_ERROR); + ASSERT_EQ(testRequest.FinishEncoding(), CHIP_NO_ERROR); + + // Validate after read + std::vector attribute_data; + ASSERT_EQ(testRequest.encodedIBs.Decode(attribute_data), CHIP_NO_ERROR); + ASSERT_EQ(attribute_data.size(), 1u); + + DecodedAttributeData & encodedData = attribute_data[0]; + ASSERT_EQ(encodedData.attributePath, testRequest.request.path); + + ASSERT_EQ(encodedData.dataReader.GetType(), TLV::kTLVType_Structure); + Clusters::UnitTesting::Structs::SimpleStruct::DecodableType actual; + ASSERT_EQ(chip::app::DataModel::Decode(encodedData.dataReader, actual), CHIP_NO_ERROR); + + ASSERT_EQ(actual.a, 123); + ASSERT_EQ(actual.b, true); + ASSERT_EQ(actual.g, 0.5); + ASSERT_EQ(actual.h, 0.125); + ASSERT_TRUE(actual.e.data_equal("foo"_span)); +} + +TEST(TestCodegenModelViaMocks, AttributeAccessInterfaceListRead) +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + const ConcreteAttributePath kStructPath(kMockEndpoint3, MockClusterId(4), + MOCK_ATTRIBUTE_ID_FOR_NON_NULLABLE_TYPE(ZCL_ARRAY_ATTRIBUTE_TYPE)); + + TestReadRequest testRequest(kAdminSubjectDescriptor, kStructPath); + RegisteredAttributeAccessInterface aai(kStructPath); + + constexpr unsigned kDataCount = 5; + aai->SetReturnedData(Clusters::UnitTesting::Structs::SimpleStruct::Type{ + .b = true, + .e = "xyz"_span, + .g = 0.25, + .h = 0.5, + }); + aai->SetReturnedDataCount(kDataCount); + + std::unique_ptr encoder = testRequest.StartEncoding(&model); + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_NO_ERROR); + ASSERT_EQ(testRequest.FinishEncoding(), CHIP_NO_ERROR); + + // Validate after read + std::vector attribute_data; + ASSERT_EQ(testRequest.encodedIBs.Decode(attribute_data), CHIP_NO_ERROR); + ASSERT_EQ(attribute_data.size(), 1u); + + DecodedAttributeData & encodedData = attribute_data[0]; + ASSERT_EQ(encodedData.attributePath, testRequest.request.path); + + ASSERT_EQ(encodedData.dataReader.GetType(), TLV::kTLVType_Array); + + std::vector items; + ASSERT_EQ(DecodeList(encodedData.dataReader, items), CHIP_NO_ERROR); + + ASSERT_EQ(items.size(), kDataCount); + + for (unsigned i = 0; i < kDataCount; i++) + { + Clusters::UnitTesting::Structs::SimpleStruct::DecodableType & actual = items[i]; + + ASSERT_EQ(actual.a, static_cast(i & 0xFF)); + ASSERT_EQ(actual.b, true); + ASSERT_EQ(actual.g, 0.25); + ASSERT_EQ(actual.h, 0.5); + ASSERT_TRUE(actual.e.data_equal("xyz"_span)); + } +} + +TEST(TestCodegenModelViaMocks, AttributeAccessInterfaceListOverflowRead) +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + const ConcreteAttributePath kStructPath(kMockEndpoint3, MockClusterId(4), + MOCK_ATTRIBUTE_ID_FOR_NON_NULLABLE_TYPE(ZCL_ARRAY_ATTRIBUTE_TYPE)); + + TestReadRequest testRequest(kAdminSubjectDescriptor, kStructPath); + RegisteredAttributeAccessInterface aai(kStructPath); + + constexpr unsigned kDataCount = 1024; + aai->SetReturnedData(Clusters::UnitTesting::Structs::SimpleStruct::Type{ + .b = true, + .e = "thisislongertofillupfaster"_span, + .g = 0.25, + .h = 0.5, + }); + aai->SetReturnedDataCount(kDataCount); + + std::unique_ptr encoder = testRequest.StartEncoding(&model); + // NOTE: overflow, however data should be valid. Technically both NO_MEMORY and BUFFER_TOO_SMALL + // should be ok here, however we know buffer-too-small is the error in this case hence + // the compare (easier to write the test and read the output) + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_ERROR_BUFFER_TOO_SMALL); + ASSERT_EQ(testRequest.FinishEncoding(), CHIP_NO_ERROR); + + // Validate after read + std::vector attribute_data; + ASSERT_EQ(testRequest.encodedIBs.Decode(attribute_data), CHIP_NO_ERROR); + ASSERT_EQ(attribute_data.size(), 1u); + + DecodedAttributeData & encodedData = attribute_data[0]; + ASSERT_EQ(encodedData.attributePath, testRequest.request.path); + + ASSERT_EQ(encodedData.dataReader.GetType(), TLV::kTLVType_Array); + + std::vector items; + ASSERT_EQ(DecodeList(encodedData.dataReader, items), CHIP_NO_ERROR); + + // On last check, 16 items can be encoded. Set some non-zero range to be enforced here that + // SOME list items are actually encoded. Actual lower bound here IS ARBITRARY and was picked + // to just ensure non-zero item count for checks. + ASSERT_GT(items.size(), 5u); + ASSERT_LT(items.size(), kDataCount); + + for (unsigned i = 0; i < items.size(); i++) + { + Clusters::UnitTesting::Structs::SimpleStruct::DecodableType & actual = items[i]; + + ASSERT_EQ(actual.a, static_cast(i & 0xFF)); + ASSERT_EQ(actual.b, true); + ASSERT_EQ(actual.g, 0.25); + ASSERT_EQ(actual.h, 0.5); + ASSERT_TRUE(actual.e.data_equal("thisislongertofillupfaster"_span)); + } +} + +TEST(TestCodegenModelViaMocks, AttributeAccessInterfaceListIncrementalRead) +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + const ConcreteAttributePath kStructPath(kMockEndpoint3, MockClusterId(4), + MOCK_ATTRIBUTE_ID_FOR_NON_NULLABLE_TYPE(ZCL_ARRAY_ATTRIBUTE_TYPE)); + + TestReadRequest testRequest(kAdminSubjectDescriptor, kStructPath); + RegisteredAttributeAccessInterface aai(kStructPath); + + constexpr unsigned kDataCount = 1024; + constexpr unsigned kEncodeIndexStart = 101; + aai->SetReturnedData(Clusters::UnitTesting::Structs::SimpleStruct::Type{ + .b = true, + .e = "thisislongertofillupfaster"_span, + .g = 0.25, + .h = 0.5, + }); + aai->SetReturnedDataCount(kDataCount); + + AttributeEncodeState encodeState; + encodeState.SetCurrentEncodingListIndex(kEncodeIndexStart); + + std::unique_ptr encoder = testRequest.StartEncoding(&model, encodeState); + // NOTE: overflow, however data should be valid. Technically both NO_MEMORY and BUFFER_TOO_SMALL + // should be ok here, however we know buffer-too-small is the error in this case hence + // the compare (easier to write the test and read the output) + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_ERROR_BUFFER_TOO_SMALL); + ASSERT_EQ(testRequest.FinishEncoding(), CHIP_NO_ERROR); + + // Validate after read + std::vector attribute_data; + ASSERT_EQ(testRequest.encodedIBs.Decode(attribute_data), CHIP_NO_ERROR); + + // Incremental encodes are separate list items, repeated + // actual size IS ARBITRARY (current test sets it at 11) + ASSERT_GT(attribute_data.size(), 3u); + + for (unsigned i = 0; i < attribute_data.size(); i++) + { + DecodedAttributeData & encodedData = attribute_data[i]; + ASSERT_EQ(encodedData.attributePath.mEndpointId, testRequest.request.path.mEndpointId); + ASSERT_EQ(encodedData.attributePath.mClusterId, testRequest.request.path.mClusterId); + ASSERT_EQ(encodedData.attributePath.mAttributeId, testRequest.request.path.mAttributeId); + ASSERT_EQ(encodedData.attributePath.mListOp, ConcreteDataAttributePath::ListOperation::AppendItem); + + // individual structures encoded in each item + ASSERT_EQ(encodedData.dataReader.GetType(), TLV::kTLVType_Structure); + + Clusters::UnitTesting::Structs::SimpleStruct::DecodableType actual; + ASSERT_EQ(chip::app::DataModel::Decode(encodedData.dataReader, actual), CHIP_NO_ERROR); + + ASSERT_EQ(actual.a, static_cast((i + kEncodeIndexStart) & 0xFF)); + ASSERT_EQ(actual.b, true); + ASSERT_EQ(actual.g, 0.25); + ASSERT_EQ(actual.h, 0.5); + ASSERT_TRUE(actual.e.data_equal("thisislongertofillupfaster"_span)); + } +} + +TEST(TestCodegenModelViaMocks, ReadGlobalAttributeAttributeList) +{ + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + ScopedMockAccessControl accessControl; + + TestReadRequest testRequest(kAdminSubjectDescriptor, + ConcreteAttributePath(kMockEndpoint2, MockClusterId(3), AttributeList::Id)); + + // Data read via the encoder + std::unique_ptr encoder = testRequest.StartEncoding(&model); + ASSERT_EQ(model.ReadAttribute(testRequest.request, *encoder), CHIP_NO_ERROR); + ASSERT_EQ(testRequest.FinishEncoding(), CHIP_NO_ERROR); + + // Validate after read + std::vector attribute_data; + ASSERT_EQ(testRequest.encodedIBs.Decode(attribute_data), CHIP_NO_ERROR); + ASSERT_EQ(attribute_data.size(), 1u); + + DecodedAttributeData & encodedData = attribute_data[0]; + ASSERT_EQ(encodedData.attributePath, testRequest.request.path); + + ASSERT_EQ(encodedData.dataReader.GetType(), TLV::kTLVType_Array); + + std::vector items; + ASSERT_EQ(DecodeList(encodedData.dataReader, items), CHIP_NO_ERROR); + + // Mock data contains ClusterRevision and FeatureMap. + // After this, Global attributes are auto-added + std::vector expected; + + // Encoding in global-attribute-access-interface has a logic of: + // - Append global attributes in front of the first specified + // large number global attribute. + // Since ClusterRevision and FeatureMap are + // global attributes, the order here is reversed for them + for (AttributeId id : GlobalAttributesNotInMetadata) + { + expected.push_back(id); + } + expected.push_back(ClusterRevision::Id); + expected.push_back(FeatureMap::Id); + expected.push_back(MockAttributeId(1)); + expected.push_back(MockAttributeId(2)); + expected.push_back(MockAttributeId(3)); + + ASSERT_EQ(items.size(), expected.size()); + + // Since we have no std::vector formatter, comparing element by element is somewhat + // more readable in case of failure. + for (unsigned i = 0; i < items.size(); i++) + { + EXPECT_EQ(items[i], expected[i]); + } +} diff --git a/src/app/data-model-interface/DataModel.h b/src/app/data-model-interface/DataModel.h index 0ecfd6fb624532..04911fd75cccc3 100644 --- a/src/app/data-model-interface/DataModel.h +++ b/src/app/data-model-interface/DataModel.h @@ -55,22 +55,29 @@ class DataModel : public DataModelMetadataTree // event emitting, path marking and other operations virtual InteractionModelContext CurrentContext() const { return mContext; } - /// List reading has specific handling logic: - /// `state` contains in/out data about the current list reading. MUST start with kInvalidListIndex on first call + /// TEMPORARY/TRANSITIONAL requirement for transitioning from ember-specific code + /// ReadAttribute is REQUIRED to perform: + /// - ACL validation (see notes on OperationFlags::kInternal) + /// - Validation of readability/writability + /// - use request.path.mExpanded to skip encoding replies for data according + /// to 8.4.3.2 of the spec: + /// > If the path indicates attribute data that is not readable, then the path SHALL + /// be discarded. + /// > Else if reading from the attribute in the path requires a privilege that is not + /// granted to access the cluster in the path, then the path SHALL be discarded. /// /// Return codes: - /// CHIP_ERROR_MORE_LIST_DATA_AVAILABLE (NOTE: new error defined for this purpose) - /// - partial data written to the destination - /// - destination will contain AT LEAST one valid list entry fully serialized - /// - destination will be fully valid (it will be rolled back on partial list writes) + /// CHIP_ERROR_NO_MEMORY or CHIP_ERROR_BUFFER_TOO_SMALL: + /// - Indicates that list encoding had insufficient buffer space to encode elements. + /// - encoder::GetState().AllowPartialData() determines if these errors are permanent (no partial + /// data allowed) or further encoding can be retried (AllowPartialData true for list encoding) /// CHIP_IM_GLOBAL_STATUS(code): /// - error codes that are translatable in IM status codes (otherwise we expect Failure to be reported) - /// - In particular, some handlers rely on special handling for: - /// - `UnsupportedAccess` - for ACL checks (e.g. wildcard expansion may choose to skip these) /// - to check for this, CHIP_ERROR provides: /// - ::IsPart(ChipError::SdkPart::kIMGlobalStatus) -> bool /// - ::GetSdkCode() -> uint8_t to translate to the actual code - virtual CHIP_ERROR ReadAttribute(const ReadAttributeRequest & request, ReadState & state, AttributeValueEncoder & encoder) = 0; + /// other internal falures + virtual CHIP_ERROR ReadAttribute(const ReadAttributeRequest & request, AttributeValueEncoder & encoder) = 0; /// Requests a write of an attribute. /// diff --git a/src/app/data-model-interface/OperationTypes.h b/src/app/data-model-interface/OperationTypes.h index feb2e173d91600..d19621db71d26a 100644 --- a/src/app/data-model-interface/OperationTypes.h +++ b/src/app/data-model-interface/OperationTypes.h @@ -31,17 +31,29 @@ namespace InteractionModel { /// Contains common flags among all interaction model operations: read/write/invoke enum class OperationFlags : uint32_t { - kInternal = 0x0001, // Internal request for data changes (can bypass checks/ACL etc.) + // NOTE: temporary flag. This flag exists to faciliate transition from ember-compatibilty-functions + // implementation to DataModel Interface functionality. Specifically currently the + // model is expected to perform ACL and readability/writability checks. + // + // In the future, this flag will be removed and InteractionModelEngine/ReportingEngine + // will perform the required validation. + // + // Currently the flag FORCES a bypass of: + // - ACL validation (will allow any read/write) + // - Access validation (will allow reading write-only data for example) + kInternal = 0x0001, }; /// This information is available for ALL interactions: read/write/invoke struct OperationRequest { - OperationFlags operationFlags; + BitFlags operationFlags; /// Current authentication data EXCEPT for internal requests. /// - Non-internal requests MUST have this set. /// - operationFlags.Has(OperationFlags::kInternal) MUST NOT have this set + /// + /// NOTE: once kInternal flag is removed, this will become non-optional std::optional subjectDescriptor; }; @@ -57,13 +69,6 @@ struct ReadAttributeRequest : OperationRequest BitFlags readFlags; }; -struct ReadState -{ - // When reading lists, reading will start at this index. - // As list data is read, this index is incremented - ListIndex listEncodeStart = kInvalidListIndex; -}; - enum class WriteFlags : uint32_t { kTimed = 0x0001, // Write is a timed write (i.e. a Timed Request Action preceeded it) diff --git a/src/app/icd/client/DefaultICDClientStorage.cpp b/src/app/icd/client/DefaultICDClientStorage.cpp index 73e7b5f5c1fdf9..7582e8fd580c40 100644 --- a/src/app/icd/client/DefaultICDClientStorage.cpp +++ b/src/app/icd/client/DefaultICDClientStorage.cpp @@ -274,6 +274,10 @@ CHIP_ERROR DefaultICDClientStorage::Load(FabricIndex fabricIndex, std::vector(), hmacBuf.data(), sizeof(Crypto::Symmetric128BitsKeyByteArray)); + // ClientType + ReturnErrorOnFailure(reader.Next(TLV::ContextTag(ClientInfoTag::kClientType))); + ReturnErrorOnFailure(reader.Get(clientInfo.client_type)); + ReturnErrorOnFailure(reader.ExitContainer(ICDClientInfoType)); clientInfoVector.push_back(clientInfo); } @@ -327,6 +331,7 @@ CHIP_ERROR DefaultICDClientStorage::SerializeToTlv(TLV::TLVWriter & writer, cons ReturnErrorOnFailure(writer.Put(TLV::ContextTag(ClientInfoTag::kAesKeyHandle), aesBuf)); ByteSpan hmacBuf(clientInfo.hmac_key_handle.As()); ReturnErrorOnFailure(writer.Put(TLV::ContextTag(ClientInfoTag::kHmacKeyHandle), hmacBuf)); + ReturnErrorOnFailure(writer.Put(TLV::ContextTag(ClientInfoTag::kClientType), clientInfo.client_type)); ReturnErrorOnFailure(writer.EndContainer(ICDClientInfoContainerType)); } return writer.EndContainer(arrayType); diff --git a/src/app/icd/client/DefaultICDClientStorage.h b/src/app/icd/client/DefaultICDClientStorage.h index c2a95fd2a1064b..c3c4560e1d04ce 100644 --- a/src/app/icd/client/DefaultICDClientStorage.h +++ b/src/app/icd/client/DefaultICDClientStorage.h @@ -130,6 +130,7 @@ class DefaultICDClientStorage : public ICDClientStorage kMonitoredSubject = 5, kAesKeyHandle = 6, kHmacKeyHandle = 7, + kClientType = 8, }; enum class CounterTag : uint8_t @@ -156,10 +157,10 @@ class DefaultICDClientStorage : public ICDClientStorage static constexpr size_t MaxICDClientInfoSize() { // All the fields added together - return TLV::EstimateStructOverhead(sizeof(NodeId), sizeof(FabricIndex), sizeof(uint32_t) /*start_icd_counter*/, - sizeof(uint32_t) /*offset*/, sizeof(uint64_t) /*monitored_subject*/, - sizeof(Crypto::Symmetric128BitsKeyByteArray) /*aes_key_handle*/, - sizeof(Crypto::Symmetric128BitsKeyByteArray) /*hmac_key_handle*/); + return TLV::EstimateStructOverhead( + sizeof(NodeId), sizeof(FabricIndex), sizeof(uint32_t) /*start_icd_counter*/, sizeof(uint32_t) /*offset*/, + sizeof(uint64_t) /*monitored_subject*/, sizeof(Crypto::Symmetric128BitsKeyByteArray) /*aes_key_handle*/, + sizeof(Crypto::Symmetric128BitsKeyByteArray) /*hmac_key_handle*/, sizeof(uint8_t) /*client_type*/); } static constexpr size_t MaxICDCounterSize() diff --git a/src/app/icd/client/ICDClientInfo.h b/src/app/icd/client/ICDClientInfo.h index 5a20b1990744bd..45aabf2f977866 100644 --- a/src/app/icd/client/ICDClientInfo.h +++ b/src/app/icd/client/ICDClientInfo.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -30,11 +31,12 @@ namespace app { struct ICDClientInfo { ScopedNodeId peer_node; - uint32_t start_icd_counter = 0; - uint32_t offset = 0; - uint64_t monitored_subject = static_cast(0); - Crypto::Aes128KeyHandle aes_key_handle = Crypto::Aes128KeyHandle(); - Crypto::Hmac128KeyHandle hmac_key_handle = Crypto::Hmac128KeyHandle(); + uint32_t start_icd_counter = 0; + uint32_t offset = 0; + Clusters::IcdManagement::ClientTypeEnum client_type = Clusters::IcdManagement::ClientTypeEnum::kPermanent; + uint64_t monitored_subject = static_cast(0); + Crypto::Aes128KeyHandle aes_key_handle = Crypto::Aes128KeyHandle(); + Crypto::Hmac128KeyHandle hmac_key_handle = Crypto::Hmac128KeyHandle(); ICDClientInfo() {} ICDClientInfo(const ICDClientInfo & other) { *this = other; } @@ -44,6 +46,7 @@ struct ICDClientInfo peer_node = other.peer_node; start_icd_counter = other.start_icd_counter; offset = other.offset; + client_type = other.client_type; monitored_subject = other.monitored_subject; ByteSpan aes_buf(other.aes_key_handle.As()); memcpy(aes_key_handle.AsMutable(), aes_buf.data(), diff --git a/src/app/icd/server/BUILD.gn b/src/app/icd/server/BUILD.gn index c100cc7d041e84..89c39c203a7c16 100644 --- a/src/app/icd/server/BUILD.gn +++ b/src/app/icd/server/BUILD.gn @@ -83,6 +83,7 @@ source_set("manager") { "${chip_root}/src/app:subscription-info-provider", "${chip_root}/src/app:test-event-trigger", "${chip_root}/src/credentials:credentials", + "${chip_root}/src/lib/address_resolve:address_resolve", "${chip_root}/src/messaging", ] diff --git a/src/app/icd/server/ICDCheckInSender.h b/src/app/icd/server/ICDCheckInSender.h index b681bec149f667..ea562666acfc4d 100644 --- a/src/app/icd/server/ICDCheckInSender.h +++ b/src/app/icd/server/ICDCheckInSender.h @@ -32,7 +32,7 @@ class ICDCheckInSender : public AddressResolve::NodeListener { public: ICDCheckInSender(Messaging::ExchangeManager * exchangeManager); - ~ICDCheckInSender(){}; + ~ICDCheckInSender() = default; CHIP_ERROR RequestResolve(ICDMonitoringEntry & entry, FabricTable * fabricTable, uint32_t counter); diff --git a/src/app/icd/server/ICDManager.cpp b/src/app/icd/server/ICDManager.cpp index 2b4ccb2be8af4e..ee55b0b0f9b23b 100644 --- a/src/app/icd/server/ICDManager.cpp +++ b/src/app/icd/server/ICDManager.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -187,6 +188,13 @@ void ICDManager::SendCheckInMsgs() continue; } + if (entry.clientType == ClientTypeEnum::kEphemeral) + { + // If the registered client is ephemeral, do not send a Check-In message + // continue to next entry + continue; + } + if (!ShouldCheckInMsgsBeSentAtActiveModeFunction(entry.fabricIndex, entry.monitoredSubject)) { continue; @@ -248,6 +256,12 @@ bool ICDManager::CheckInMessagesWouldBeSent(const std::function -#include #include #include #include diff --git a/src/app/icd/server/ICDMonitoringTable.cpp b/src/app/icd/server/ICDMonitoringTable.cpp index 57e6fa265821f1..8148a7fe33b237 100644 --- a/src/app/icd/server/ICDMonitoringTable.cpp +++ b/src/app/icd/server/ICDMonitoringTable.cpp @@ -27,6 +27,7 @@ enum class Fields : uint8_t kMonitoredSubject = 2, kAesKeyHandle = 3, kHmacKeyHandle = 4, + kClientType = 5, }; CHIP_ERROR ICDMonitoringEntry::UpdateKey(StorageKeyName & skey) @@ -49,6 +50,8 @@ CHIP_ERROR ICDMonitoringEntry::Serialize(TLV::TLVWriter & writer) const ByteSpan hmacKeybuf(hmacKeyHandle.As()); ReturnErrorOnFailure(writer.Put(TLV::ContextTag(Fields::kHmacKeyHandle), hmacKeybuf)); + ReturnErrorOnFailure(writer.Put(TLV::ContextTag(Fields::kClientType), clientType)); + ReturnErrorOnFailure(writer.EndContainer(outer)); return CHIP_NO_ERROR; } @@ -106,6 +109,9 @@ CHIP_ERROR ICDMonitoringEntry::Deserialize(TLV::TLVReader & reader) sizeof(Crypto::Symmetric128BitsKeyByteArray)); } break; + case to_underlying(Fields::kClientType): + ReturnErrorOnFailure(reader.Get(clientType)); + break; default: break; } @@ -122,6 +128,7 @@ void ICDMonitoringEntry::Clear() this->checkInNodeID = kUndefinedNodeId; this->monitoredSubject = kUndefinedNodeId; this->keyHandleValid = false; + this->clientType = app::Clusters::IcdManagement::ClientTypeEnum::kPermanent; } CHIP_ERROR ICDMonitoringEntry::SetKey(ByteSpan keyData) @@ -210,6 +217,7 @@ ICDMonitoringEntry & ICDMonitoringEntry::operator=(const ICDMonitoringEntry & ic fabricIndex = icdMonitoringEntry.fabricIndex; checkInNodeID = icdMonitoringEntry.checkInNodeID; monitoredSubject = icdMonitoringEntry.monitoredSubject; + clientType = icdMonitoringEntry.clientType; index = icdMonitoringEntry.index; keyHandleValid = icdMonitoringEntry.keyHandleValid; symmetricKeystore = icdMonitoringEntry.symmetricKeystore; @@ -257,6 +265,7 @@ CHIP_ERROR ICDMonitoringTable::Set(uint16_t index, const ICDMonitoringEntry & en ICDMonitoringEntry e(this->mFabric, index); e.checkInNodeID = entry.checkInNodeID; e.monitoredSubject = entry.monitoredSubject; + e.clientType = entry.clientType; e.index = index; memcpy(e.aesKeyHandle.AsMutable(), diff --git a/src/app/icd/server/ICDMonitoringTable.h b/src/app/icd/server/ICDMonitoringTable.h index e996d0c757cc23..942c56fda45e71 100644 --- a/src/app/icd/server/ICDMonitoringTable.h +++ b/src/app/icd/server/ICDMonitoringTable.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -101,14 +102,15 @@ struct ICDMonitoringEntry : public PersistentData */ bool IsKeyEquivalent(ByteSpan keyData); - chip::FabricIndex fabricIndex = kUndefinedFabricIndex; - chip::NodeId checkInNodeID = kUndefinedNodeId; - uint64_t monitoredSubject = static_cast(0); - Crypto::Aes128KeyHandle aesKeyHandle = Crypto::Aes128KeyHandle(); - Crypto::Hmac128KeyHandle hmacKeyHandle = Crypto::Hmac128KeyHandle(); - bool keyHandleValid = false; - uint16_t index = 0; - Crypto::SymmetricKeystore * symmetricKeystore = nullptr; + chip::FabricIndex fabricIndex = kUndefinedFabricIndex; + chip::NodeId checkInNodeID = kUndefinedNodeId; + uint64_t monitoredSubject = static_cast(0); + app::Clusters::IcdManagement::ClientTypeEnum clientType = app::Clusters::IcdManagement::ClientTypeEnum::kPermanent; + Crypto::Aes128KeyHandle aesKeyHandle = Crypto::Aes128KeyHandle(); + Crypto::Hmac128KeyHandle hmacKeyHandle = Crypto::Hmac128KeyHandle(); + bool keyHandleValid = false; + uint16_t index = 0; + Crypto::SymmetricKeystore * symmetricKeystore = nullptr; }; /** diff --git a/src/app/icd/server/tests/TestICDManager.cpp b/src/app/icd/server/tests/TestICDManager.cpp index 45e781685e6d31..0b3c1a459441dd 100644 --- a/src/app/icd/server/tests/TestICDManager.cpp +++ b/src/app/icd/server/tests/TestICDManager.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,7 @@ using namespace chip; using namespace chip::Test; using namespace chip::app; +using namespace chip::AddressResolve; using namespace chip::System; using namespace chip::System::Clock; using namespace chip::System::Clock::Literals; diff --git a/src/app/icd/server/tests/TestICDMonitoringTable.cpp b/src/app/icd/server/tests/TestICDMonitoringTable.cpp index ef1e5b32468b7a..423bc1506b36fb 100644 --- a/src/app/icd/server/tests/TestICDMonitoringTable.cpp +++ b/src/app/icd/server/tests/TestICDMonitoringTable.cpp @@ -20,10 +20,12 @@ #include #include #include +#include #include #include using namespace chip; +using namespace chip::app::Clusters::IcdManagement; using TestSessionKeystoreImpl = Crypto::DefaultSessionKeystore; @@ -73,6 +75,7 @@ TEST(TestICDMonitoringTable, TestEntryAssignationOverload) entry.checkInNodeID = 34; entry.monitoredSubject = 32; + entry.clientType = ClientTypeEnum::kEphemeral; // Entry should be valid now EXPECT_TRUE(entry.IsValid()); @@ -88,6 +91,7 @@ TEST(TestICDMonitoringTable, TestEntryAssignationOverload) EXPECT_EQ(entry.fabricIndex, entry2.fabricIndex); EXPECT_EQ(entry.checkInNodeID, entry2.checkInNodeID); EXPECT_EQ(entry.monitoredSubject, entry2.monitoredSubject); + EXPECT_EQ(entry.clientType, entry2.clientType); EXPECT_TRUE(entry2.IsKeyEquivalent(ByteSpan(kKeyBuffer1a))); } @@ -130,6 +134,7 @@ TEST(TestICDMonitoringTable, TestSaveAndLoadRegistrationValue) ICDMonitoringEntry entry1(&keystore); entry1.checkInNodeID = kClientNodeId11; entry1.monitoredSubject = kClientNodeId12; + entry1.clientType = ClientTypeEnum::kPermanent; EXPECT_EQ(CHIP_NO_ERROR, entry1.SetKey(ByteSpan(kKeyBuffer1a))); EXPECT_EQ(CHIP_NO_ERROR, saving.Set(0, entry1)); @@ -137,6 +142,7 @@ TEST(TestICDMonitoringTable, TestSaveAndLoadRegistrationValue) ICDMonitoringEntry entry2(&keystore); entry2.checkInNodeID = kClientNodeId12; entry2.monitoredSubject = kClientNodeId11; + entry2.clientType = ClientTypeEnum::kEphemeral; EXPECT_EQ(CHIP_NO_ERROR, entry2.SetKey(ByteSpan(kKeyBuffer2a))); EXPECT_EQ(CHIP_NO_ERROR, saving.Set(1, entry2)); @@ -152,6 +158,7 @@ TEST(TestICDMonitoringTable, TestSaveAndLoadRegistrationValue) EXPECT_EQ(kTestFabricIndex1, entry.fabricIndex); EXPECT_EQ(kClientNodeId11, entry.checkInNodeID); EXPECT_EQ(kClientNodeId12, entry.monitoredSubject); + EXPECT_EQ(ClientTypeEnum::kPermanent, entry.clientType); EXPECT_TRUE(entry.IsKeyEquivalent(ByteSpan(kKeyBuffer1a))); EXPECT_EQ(memcmp(entry1.hmacKeyHandle.As(), entry.hmacKeyHandle.As(), sizeof(Crypto::Symmetric128BitsKeyByteArray)), @@ -162,6 +169,7 @@ TEST(TestICDMonitoringTable, TestSaveAndLoadRegistrationValue) EXPECT_EQ(kTestFabricIndex1, entry.fabricIndex); EXPECT_EQ(kClientNodeId12, entry.checkInNodeID); EXPECT_EQ(kClientNodeId11, entry.monitoredSubject); + EXPECT_EQ(ClientTypeEnum::kEphemeral, entry.clientType); EXPECT_TRUE(entry.IsKeyEquivalent(ByteSpan(kKeyBuffer2a))); EXPECT_EQ(memcmp(entry2.hmacKeyHandle.As(), entry.hmacKeyHandle.As(), sizeof(Crypto::Symmetric128BitsKeyByteArray)), @@ -185,6 +193,7 @@ TEST(TestICDMonitoringTable, TestSaveAndLoadRegistrationValue) EXPECT_EQ(kTestFabricIndex1, entry.fabricIndex); EXPECT_EQ(kClientNodeId12, entry.checkInNodeID); EXPECT_EQ(kClientNodeId11, entry.monitoredSubject); + EXPECT_EQ(ClientTypeEnum::kEphemeral, entry.clientType); EXPECT_TRUE(entry.IsKeyEquivalent(ByteSpan(kKeyBuffer2a))); EXPECT_EQ(memcmp(entry2.hmacKeyHandle.As(), entry.hmacKeyHandle.As(), sizeof(Crypto::Symmetric128BitsKeyByteArray)), @@ -195,6 +204,7 @@ TEST(TestICDMonitoringTable, TestSaveAndLoadRegistrationValue) EXPECT_EQ(kTestFabricIndex1, entry.fabricIndex); EXPECT_EQ(kClientNodeId13, entry.checkInNodeID); EXPECT_EQ(kClientNodeId11, entry.monitoredSubject); + EXPECT_EQ(ClientTypeEnum::kPermanent, entry.clientType); EXPECT_TRUE(entry.IsKeyEquivalent(ByteSpan(kKeyBuffer1b))); EXPECT_EQ(memcmp(entry4.hmacKeyHandle.As(), entry.hmacKeyHandle.As(), sizeof(Crypto::Symmetric128BitsKeyByteArray)), diff --git a/src/app/tests/suites/TestIcdManagementCluster.yaml b/src/app/tests/suites/TestIcdManagementCluster.yaml index a3196835915b52..68e358385b8e21 100644 --- a/src/app/tests/suites/TestIcdManagementCluster.yaml +++ b/src/app/tests/suites/TestIcdManagementCluster.yaml @@ -195,6 +195,8 @@ tests: value: 1001 - name: "Key" value: "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e" + - name: "ClientType" + value: ClientTypeEnum.Permanent response: error: CONSTRAINT_ERROR @@ -208,10 +210,12 @@ tests: value: 1001 - name: "Key" value: "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\xff" + - name: "ClientType" + value: ClientTypeEnum.Permanent response: error: CONSTRAINT_ERROR - - label: "Register 1.1" + - label: "Register 1.1 - Invalid ClientType" command: "RegisterClient" arguments: values: @@ -221,6 +225,23 @@ tests: value: 1001 - name: "Key" value: "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + - name: "ClientType" + value: ClientTypeEnum.UnknownEnumValue + response: + error: CONSTRAINT_ERROR + + - label: "Register 1.2" + command: "RegisterClient" + arguments: + values: + - name: "CheckInNodeID" + value: 101 + - name: "MonitoredSubject" + value: 1001 + - name: "Key" + value: "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + - name: "ClientType" + value: ClientTypeEnum.Permanent response: values: - name: "ICDCounter" @@ -240,6 +261,8 @@ tests: - name: "Key" value: "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + - name: "ClientType" + value: ClientTypeEnum.Ephemeral response: values: - name: "ICDCounter" @@ -258,6 +281,8 @@ tests: value: 3001 - name: "Key" value: "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + - name: "ClientType" + value: ClientTypeEnum.Permanent response: error: RESOURCE_EXHAUSTED @@ -267,8 +292,16 @@ tests: response: value: [ - { CheckInNodeID: 101, MonitoredSubject: 1001 }, - { CheckInNodeID: 201, MonitoredSubject: 2001 }, + { + CheckInNodeID: 101, + MonitoredSubject: 1001, + ClientType: ClientTypeEnum.Permanent, + }, + { + CheckInNodeID: 201, + MonitoredSubject: 2001, + ClientType: ClientTypeEnum.Ephemeral, + }, ] - label: "Register 1.1 (update)" @@ -281,6 +314,8 @@ tests: value: 1002 - name: "Key" value: "\x01\x11\x21\x31\x41\x51\x61\x71\x81\x91\xa1\xb1\xc1\xd1\xe1\xf1" + - name: "ClientType" + value: ClientTypeEnum.Ephemeral response: values: - name: "ICDCounter" @@ -295,8 +330,16 @@ tests: response: value: [ - { CheckInNodeID: 101, MonitoredSubject: 1002 }, - { CheckInNodeID: 201, MonitoredSubject: 2001 }, + { + CheckInNodeID: 101, + MonitoredSubject: 1002, + ClientType: ClientTypeEnum.Ephemeral, + }, + { + CheckInNodeID: 201, + MonitoredSubject: 2001, + ClientType: ClientTypeEnum.Ephemeral, + }, ] - label: "Register 2.2 (wrong verification key)" @@ -312,6 +355,8 @@ tests: - name: "VerificationKey" value: "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2f\x2f" + - name: "ClientType" + value: ClientTypeEnum.Permanent response: values: - name: "ICDCounter" @@ -326,8 +371,16 @@ tests: response: value: [ - { CheckInNodeID: 101, MonitoredSubject: 1002 }, - { CheckInNodeID: 201, MonitoredSubject: 2002 }, + { + CheckInNodeID: 101, + MonitoredSubject: 1002, + ClientType: ClientTypeEnum.Ephemeral, + }, + { + CheckInNodeID: 201, + MonitoredSubject: 2002, + ClientType: ClientTypeEnum.Permanent, + }, ] - label: "Unregister 1.1 (wrong key)" diff --git a/src/app/tests/suites/certification/Test_TC_ICDM_3_4.yaml b/src/app/tests/suites/certification/Test_TC_ICDM_3_4.yaml index 7bbcb729489eef..a4eb232b98e157 100644 --- a/src/app/tests/suites/certification/Test_TC_ICDM_3_4.yaml +++ b/src/app/tests/suites/certification/Test_TC_ICDM_3_4.yaml @@ -128,6 +128,9 @@ tests: value: MonitorSubID1 - name: "Key" value: Key1 + # Adding input for the test to pass for now - Full test script needs to be updated + - name: "ClientType" + value: ClientTypeEnum.Permanent response: values: - name: "ICDCounter" diff --git a/src/app/zap-templates/zcl/data-model/chip/icd-management-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/icd-management-cluster.xml index 0269b8f0f3dcad..c1e2805de8bf15 100644 --- a/src/app/zap-templates/zcl/data-model/chip/icd-management-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/icd-management-cluster.xml @@ -39,17 +39,24 @@ limitations under the License. + + + + + + + - - - + + + @@ -113,6 +120,7 @@ limitations under the License. + diff --git a/src/controller/AutoCommissioner.cpp b/src/controller/AutoCommissioner.cpp index d6bc9f5c79fcac..8232292479ff3f 100644 --- a/src/controller/AutoCommissioner.cpp +++ b/src/controller/AutoCommissioner.cpp @@ -91,6 +91,11 @@ CHIP_ERROR AutoCommissioner::VerifyICDRegistrationInfo(const CommissioningParame ChipLogError(Controller, "Missing ICD monitored subject!"); return CHIP_ERROR_INVALID_ARGUMENT; } + if (!params.GetICDClientType().HasValue()) + { + ChipLogError(Controller, "Missing ICD Client Type!"); + return CHIP_ERROR_INVALID_ARGUMENT; + } return CHIP_NO_ERROR; } @@ -270,6 +275,7 @@ CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParam mParams.SetICDSymmetricKey(ByteSpan(mICDSymmetricKey)); mParams.SetICDCheckInNodeId(params.GetICDCheckInNodeId().Value()); mParams.SetICDMonitoredSubject(params.GetICDMonitoredSubject().Value()); + mParams.SetICDClientType(params.GetICDClientType().Value()); } return CHIP_NO_ERROR; diff --git a/src/controller/CommissioningDelegate.h b/src/controller/CommissioningDelegate.h index 99f00f109d4ff0..7a96939cf49434 100644 --- a/src/controller/CommissioningDelegate.h +++ b/src/controller/CommissioningDelegate.h @@ -558,6 +558,13 @@ class CommissioningParameters return *this; } + Optional GetICDClientType() const { return mICDClientType; } + CommissioningParameters & SetICDClientType(app::Clusters::IcdManagement::ClientTypeEnum icdClientType) + { + mICDClientType = MakeOptional(icdClientType); + return *this; + } + Optional GetICDStayActiveDurationMsec() const { return mICDStayActiveDurationMsec; } CommissioningParameters & SetICDStayActiveDurationMsec(uint32_t stayActiveDurationMsec) { @@ -632,6 +639,7 @@ class CommissioningParameters Optional mICDCheckInNodeId; Optional mICDMonitoredSubject; Optional mICDSymmetricKey; + Optional mICDClientType; Optional mICDStayActiveDurationMsec; ICDRegistrationStrategy mICDRegistrationStrategy = ICDRegistrationStrategy::kIgnore; bool mCheckForMatchingFabric = false; diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter index cb858ffdcd8802..a8567d591b1f7a 100644 --- a/src/controller/data_model/controller-clusters.matter +++ b/src/controller/data_model/controller-clusters.matter @@ -2672,6 +2672,11 @@ cluster BooleanState = 69 { cluster IcdManagement = 70 { revision 2; + enum ClientTypeEnum : enum8 { + kPermanent = 0; + kEphemeral = 1; + } + enum OperatingModeEnum : enum8 { kSIT = 0; kLIT = 1; @@ -2706,6 +2711,7 @@ cluster IcdManagement = 70 { fabric_scoped struct MonitoringRegistrationStruct { fabric_sensitive node_id checkInNodeID = 1; fabric_sensitive int64u monitoredSubject = 2; + fabric_sensitive ClientTypeEnum clientType = 4; fabric_idx fabricIndex = 254; } @@ -2730,6 +2736,7 @@ cluster IcdManagement = 70 { int64u monitoredSubject = 1; octet_string<16> key = 2; optional octet_string<16> verificationKey = 3; + ClientTypeEnum clientType = 4; } response struct RegisterClientResponse = 1 { diff --git a/src/controller/java/AndroidDeviceControllerWrapper.cpp b/src/controller/java/AndroidDeviceControllerWrapper.cpp index 87f53735bcf617..af5d220295a983 100644 --- a/src/controller/java/AndroidDeviceControllerWrapper.cpp +++ b/src/controller/java/AndroidDeviceControllerWrapper.cpp @@ -526,6 +526,12 @@ CHIP_ERROR AndroidDeviceControllerWrapper::ApplyICDRegistrationInfo(chip::Contro VerifyOrReturnError(err == CHIP_NO_ERROR, err); jbyteArray jSymmetricKey = static_cast(env->CallObjectMethod(icdRegistrationInfo, getSymmetricKeyMethod)); + jmethodID getClientTypeMethod; + err = chip::JniReferences::GetInstance().FindMethod(env, icdRegistrationInfo, "getClientType", "()Ljava/lang/Integer;", + &getClientTypeMethod); + VerifyOrReturnError(err == CHIP_NO_ERROR, err); + jobject jClientType = env->CallObjectMethod(icdRegistrationInfo, getClientTypeMethod); + chip::NodeId checkInNodeId = chip::kUndefinedNodeId; if (jCheckInNodeId != nullptr) { @@ -556,6 +562,14 @@ CHIP_ERROR AndroidDeviceControllerWrapper::ApplyICDRegistrationInfo(chip::Contro } params.SetICDSymmetricKey(chip::ByteSpan(mICDSymmetricKey)); + chip::app::Clusters::IcdManagement::ClientTypeEnum clientType = chip::app::Clusters::IcdManagement::ClientTypeEnum::kPermanent; + if (jClientType != nullptr) + { + clientType = static_cast( + chip::JniReferences::GetInstance().IntegerToPrimitive(jClientType)); + } + params.SetICDClientType(clientType); + return err; } diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java index 80491515bc57d3..4b762e1ef39f37 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java @@ -18618,11 +18618,11 @@ public long initWithDevice(long devicePtr, int endpointId) { return 0L; } - public void registerClient(RegisterClientResponseCallback callback, Long checkInNodeID, Long monitoredSubject, byte[] key, Optional verificationKey) { - registerClient(callback, checkInNodeID, monitoredSubject, key, verificationKey, 0); + public void registerClient(RegisterClientResponseCallback callback, Long checkInNodeID, Long monitoredSubject, byte[] key, Optional verificationKey, Integer clientType) { + registerClient(callback, checkInNodeID, monitoredSubject, key, verificationKey, clientType, 0); } - public void registerClient(RegisterClientResponseCallback callback, Long checkInNodeID, Long monitoredSubject, byte[] key, Optional verificationKey, int timedInvokeTimeoutMs) { + public void registerClient(RegisterClientResponseCallback callback, Long checkInNodeID, Long monitoredSubject, byte[] key, Optional verificationKey, Integer clientType, int timedInvokeTimeoutMs) { final long commandId = 0L; ArrayList elements = new ArrayList<>(); @@ -18642,6 +18642,10 @@ public void registerClient(RegisterClientResponseCallback callback, Long checkIn BaseTLVType verificationKeytlvValue = verificationKey.map((nonOptionalverificationKey) -> new ByteArrayType(nonOptionalverificationKey)).orElse(new EmptyType()); elements.add(new StructElement(verificationKeyFieldID, verificationKeytlvValue)); + final long clientTypeFieldID = 4L; + BaseTLVType clientTypetlvValue = new UIntType(clientType); + elements.add(new StructElement(clientTypeFieldID, clientTypetlvValue)); + StructType commandArgs = new StructType(elements); invoke(new InvokeCallbackImpl(callback) { @Override diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java b/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java index c22d3593b9e87e..f78eaf9c83cd10 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java @@ -3483,18 +3483,22 @@ public String toString() { public static class IcdManagementClusterMonitoringRegistrationStruct { public Long checkInNodeID; public Long monitoredSubject; + public Integer clientType; public Integer fabricIndex; private static final long CHECK_IN_NODE_I_D_ID = 1L; private static final long MONITORED_SUBJECT_ID = 2L; + private static final long CLIENT_TYPE_ID = 4L; private static final long FABRIC_INDEX_ID = 254L; public IcdManagementClusterMonitoringRegistrationStruct( Long checkInNodeID, Long monitoredSubject, + Integer clientType, Integer fabricIndex ) { this.checkInNodeID = checkInNodeID; this.monitoredSubject = monitoredSubject; + this.clientType = clientType; this.fabricIndex = fabricIndex; } @@ -3502,6 +3506,7 @@ public StructType encodeTlv() { ArrayList values = new ArrayList<>(); values.add(new StructElement(CHECK_IN_NODE_I_D_ID, new UIntType(checkInNodeID))); values.add(new StructElement(MONITORED_SUBJECT_ID, new UIntType(monitoredSubject))); + values.add(new StructElement(CLIENT_TYPE_ID, new UIntType(clientType))); values.add(new StructElement(FABRIC_INDEX_ID, new UIntType(fabricIndex))); return new StructType(values); @@ -3513,6 +3518,7 @@ public static IcdManagementClusterMonitoringRegistrationStruct decodeTlv(BaseTLV } Long checkInNodeID = null; Long monitoredSubject = null; + Integer clientType = null; Integer fabricIndex = null; for (StructElement element: ((StructType)tlvValue).value()) { if (element.contextTagNum() == CHECK_IN_NODE_I_D_ID) { @@ -3525,6 +3531,11 @@ public static IcdManagementClusterMonitoringRegistrationStruct decodeTlv(BaseTLV UIntType castingValue = element.value(UIntType.class); monitoredSubject = castingValue.value(Long.class); } + } else if (element.contextTagNum() == CLIENT_TYPE_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + clientType = castingValue.value(Integer.class); + } } else if (element.contextTagNum() == FABRIC_INDEX_ID) { if (element.value(BaseTLVType.class).type() == TLVType.UInt) { UIntType castingValue = element.value(UIntType.class); @@ -3535,6 +3546,7 @@ public static IcdManagementClusterMonitoringRegistrationStruct decodeTlv(BaseTLV return new IcdManagementClusterMonitoringRegistrationStruct( checkInNodeID, monitoredSubject, + clientType, fabricIndex ); } @@ -3549,6 +3561,9 @@ public String toString() { output.append("\tmonitoredSubject: "); output.append(monitoredSubject); output.append("\n"); + output.append("\tclientType: "); + output.append(clientType); + output.append("\n"); output.append("\tfabricIndex: "); output.append(fabricIndex); output.append("\n"); diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java index 06f9941b772c21..cc2cb937c66cad 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java @@ -5859,7 +5859,7 @@ public static Command value(long id) throws NoSuchFieldError { } throw new NoSuchFieldError(); } - }public enum RegisterClientCommandField {CheckInNodeID(0),MonitoredSubject(1),Key(2),VerificationKey(3),; + }public enum RegisterClientCommandField {CheckInNodeID(0),MonitoredSubject(1),Key(2),VerificationKey(3),ClientType(4),; private final int id; RegisterClientCommandField(int id) { this.id = id; diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java index f4830c334b842f..2573b42c0184f7 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java @@ -23986,6 +23986,9 @@ public Map> getCommandMap() { CommandParameterInfo icdManagementregisterClientverificationKeyCommandParameterInfo = new CommandParameterInfo("verificationKey", Optional.class, byte[].class); icdManagementregisterClientCommandParams.put("verificationKey",icdManagementregisterClientverificationKeyCommandParameterInfo); + + CommandParameterInfo icdManagementregisterClientclientTypeCommandParameterInfo = new CommandParameterInfo("clientType", Integer.class, Integer.class); + icdManagementregisterClientCommandParams.put("clientType",icdManagementregisterClientclientTypeCommandParameterInfo); InteractionInfo icdManagementregisterClientInteractionInfo = new InteractionInfo( (cluster, callback, commandArguments) -> { ((ChipClusters.IcdManagementCluster) cluster) @@ -24002,6 +24005,9 @@ public Map> getCommandMap() { , (Optional) commandArguments.get("verificationKey") + , (Integer) + commandArguments.get("clientType") + ); }, () -> new DelegatedIcdManagementClusterRegisterClientResponseCallback(), diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/IcdManagementClusterMonitoringRegistrationStruct.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/IcdManagementClusterMonitoringRegistrationStruct.kt index 8c8a7dcc410261..fd60e6d6da4328 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/IcdManagementClusterMonitoringRegistrationStruct.kt +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/IcdManagementClusterMonitoringRegistrationStruct.kt @@ -25,12 +25,14 @@ import matter.tlv.TlvWriter class IcdManagementClusterMonitoringRegistrationStruct( val checkInNodeID: ULong, val monitoredSubject: ULong, + val clientType: UInt, val fabricIndex: UInt, ) { override fun toString(): String = buildString { append("IcdManagementClusterMonitoringRegistrationStruct {\n") append("\tcheckInNodeID : $checkInNodeID\n") append("\tmonitoredSubject : $monitoredSubject\n") + append("\tclientType : $clientType\n") append("\tfabricIndex : $fabricIndex\n") append("}\n") } @@ -40,6 +42,7 @@ class IcdManagementClusterMonitoringRegistrationStruct( startStructure(tlvTag) put(ContextSpecificTag(TAG_CHECK_IN_NODE_I_D), checkInNodeID) put(ContextSpecificTag(TAG_MONITORED_SUBJECT), monitoredSubject) + put(ContextSpecificTag(TAG_CLIENT_TYPE), clientType) put(ContextSpecificTag(TAG_FABRIC_INDEX), fabricIndex) endStructure() } @@ -48,6 +51,7 @@ class IcdManagementClusterMonitoringRegistrationStruct( companion object { private const val TAG_CHECK_IN_NODE_I_D = 1 private const val TAG_MONITORED_SUBJECT = 2 + private const val TAG_CLIENT_TYPE = 4 private const val TAG_FABRIC_INDEX = 254 fun fromTlv( @@ -57,6 +61,7 @@ class IcdManagementClusterMonitoringRegistrationStruct( tlvReader.enterStructure(tlvTag) val checkInNodeID = tlvReader.getULong(ContextSpecificTag(TAG_CHECK_IN_NODE_I_D)) val monitoredSubject = tlvReader.getULong(ContextSpecificTag(TAG_MONITORED_SUBJECT)) + val clientType = tlvReader.getUInt(ContextSpecificTag(TAG_CLIENT_TYPE)) val fabricIndex = tlvReader.getUInt(ContextSpecificTag(TAG_FABRIC_INDEX)) tlvReader.exitContainer() @@ -64,6 +69,7 @@ class IcdManagementClusterMonitoringRegistrationStruct( return IcdManagementClusterMonitoringRegistrationStruct( checkInNodeID, monitoredSubject, + clientType, fabricIndex, ) } diff --git a/src/controller/java/generated/java/matter/controller/cluster/clusters/IcdManagementCluster.kt b/src/controller/java/generated/java/matter/controller/cluster/clusters/IcdManagementCluster.kt index fd77e38a6f48fa..ddc206eeb28aad 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/clusters/IcdManagementCluster.kt +++ b/src/controller/java/generated/java/matter/controller/cluster/clusters/IcdManagementCluster.kt @@ -107,6 +107,7 @@ class IcdManagementCluster( monitoredSubject: ULong, key: ByteArray, verificationKey: ByteArray?, + clientType: UByte, timedInvokeTimeout: Duration? = null, ): RegisterClientResponse { val commandId: UInt = 0u @@ -127,6 +128,9 @@ class IcdManagementCluster( verificationKey?.let { tlvWriter.put(ContextSpecificTag(TAG_VERIFICATION_KEY_REQ), verificationKey) } + + val TAG_CLIENT_TYPE_REQ: Int = 4 + tlvWriter.put(ContextSpecificTag(TAG_CLIENT_TYPE_REQ), clientType) tlvWriter.endStructure() val request: InvokeRequest = diff --git a/src/controller/java/generated/java/matter/controller/cluster/structs/IcdManagementClusterMonitoringRegistrationStruct.kt b/src/controller/java/generated/java/matter/controller/cluster/structs/IcdManagementClusterMonitoringRegistrationStruct.kt index f9866aa93f9ce5..fbf95fd9f40918 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/structs/IcdManagementClusterMonitoringRegistrationStruct.kt +++ b/src/controller/java/generated/java/matter/controller/cluster/structs/IcdManagementClusterMonitoringRegistrationStruct.kt @@ -25,12 +25,14 @@ import matter.tlv.TlvWriter class IcdManagementClusterMonitoringRegistrationStruct( val checkInNodeID: ULong, val monitoredSubject: ULong, + val clientType: UByte, val fabricIndex: UByte, ) { override fun toString(): String = buildString { append("IcdManagementClusterMonitoringRegistrationStruct {\n") append("\tcheckInNodeID : $checkInNodeID\n") append("\tmonitoredSubject : $monitoredSubject\n") + append("\tclientType : $clientType\n") append("\tfabricIndex : $fabricIndex\n") append("}\n") } @@ -40,6 +42,7 @@ class IcdManagementClusterMonitoringRegistrationStruct( startStructure(tlvTag) put(ContextSpecificTag(TAG_CHECK_IN_NODE_I_D), checkInNodeID) put(ContextSpecificTag(TAG_MONITORED_SUBJECT), monitoredSubject) + put(ContextSpecificTag(TAG_CLIENT_TYPE), clientType) put(ContextSpecificTag(TAG_FABRIC_INDEX), fabricIndex) endStructure() } @@ -48,6 +51,7 @@ class IcdManagementClusterMonitoringRegistrationStruct( companion object { private const val TAG_CHECK_IN_NODE_I_D = 1 private const val TAG_MONITORED_SUBJECT = 2 + private const val TAG_CLIENT_TYPE = 4 private const val TAG_FABRIC_INDEX = 254 fun fromTlv( @@ -57,6 +61,7 @@ class IcdManagementClusterMonitoringRegistrationStruct( tlvReader.enterStructure(tlvTag) val checkInNodeID = tlvReader.getULong(ContextSpecificTag(TAG_CHECK_IN_NODE_I_D)) val monitoredSubject = tlvReader.getULong(ContextSpecificTag(TAG_MONITORED_SUBJECT)) + val clientType = tlvReader.getUByte(ContextSpecificTag(TAG_CLIENT_TYPE)) val fabricIndex = tlvReader.getUByte(ContextSpecificTag(TAG_FABRIC_INDEX)) tlvReader.exitContainer() @@ -64,6 +69,7 @@ class IcdManagementClusterMonitoringRegistrationStruct( return IcdManagementClusterMonitoringRegistrationStruct( checkInNodeID, monitoredSubject, + clientType, fabricIndex, ) } diff --git a/src/controller/java/src/chip/devicecontroller/ICDRegistrationInfo.java b/src/controller/java/src/chip/devicecontroller/ICDRegistrationInfo.java index be978fae28fee6..417d147238c526 100644 --- a/src/controller/java/src/chip/devicecontroller/ICDRegistrationInfo.java +++ b/src/controller/java/src/chip/devicecontroller/ICDRegistrationInfo.java @@ -24,11 +24,13 @@ public class ICDRegistrationInfo { @Nullable private final Long checkInNodeId; @Nullable private final Long monitoredSubject; @Nullable private final byte[] symmetricKey; + @Nullable private final Integer clientType; private ICDRegistrationInfo(Builder builder) { this.checkInNodeId = builder.checkInNodeId; this.monitoredSubject = builder.monitoredSubject; this.symmetricKey = builder.symmetricKey; + this.clientType = builder.clientType; } /** Returns the check in node ID. */ @@ -46,6 +48,10 @@ public byte[] getSymmetricKey() { return symmetricKey; } + public Integer getClientType() { + return clientType; + } + public static Builder newBuilder() { return new Builder(); } @@ -55,6 +61,7 @@ public static class Builder { @Nullable private Long checkInNodeId = null; @Nullable private Long monitoredSubject = null; @Nullable private byte[] symmetricKey = null; + @Nullable private Integer clientType = null; private Builder() {} @@ -81,6 +88,11 @@ public Builder setSymmetricKey(byte[] symmetricKey) { return this; } + public Builder setClientType(Integer clientType) { + this.clientType = clientType; + return this; + } + public ICDRegistrationInfo build() { return new ICDRegistrationInfo(this); } diff --git a/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp b/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp index 7cdabec93f43ca..4cebe9ea5a1829 100644 --- a/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp +++ b/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp @@ -12306,6 +12306,13 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR chip::JniReferences::GetInstance().CreateBoxedObject( newElement_0_monitoredSubjectClassName.c_str(), newElement_0_monitoredSubjectCtorSignature.c_str(), jninewElement_0_monitoredSubject, newElement_0_monitoredSubject); + jobject newElement_0_clientType; + std::string newElement_0_clientTypeClassName = "java/lang/Integer"; + std::string newElement_0_clientTypeCtorSignature = "(I)V"; + jint jninewElement_0_clientType = static_cast(entry_0.clientType); + chip::JniReferences::GetInstance().CreateBoxedObject(newElement_0_clientTypeClassName.c_str(), + newElement_0_clientTypeCtorSignature.c_str(), + jninewElement_0_clientType, newElement_0_clientType); jobject newElement_0_fabricIndex; std::string newElement_0_fabricIndexClassName = "java/lang/Integer"; std::string newElement_0_fabricIndexCtorSignature = "(I)V"; @@ -12325,9 +12332,10 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR } jmethodID monitoringRegistrationStructStructCtor_1; - err = chip::JniReferences::GetInstance().FindMethod(env, monitoringRegistrationStructStructClass_1, "", - "(Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Integer;)V", - &monitoringRegistrationStructStructCtor_1); + err = chip::JniReferences::GetInstance().FindMethod( + env, monitoringRegistrationStructStructClass_1, "", + "(Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Integer;Ljava/lang/Integer;)V", + &monitoringRegistrationStructStructCtor_1); if (err != CHIP_NO_ERROR || monitoringRegistrationStructStructCtor_1 == nullptr) { ChipLogError(Zcl, "Could not find ChipStructs$IcdManagementClusterMonitoringRegistrationStruct constructor"); @@ -12335,7 +12343,8 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR } newElement_0 = env->NewObject(monitoringRegistrationStructStructClass_1, monitoringRegistrationStructStructCtor_1, - newElement_0_checkInNodeID, newElement_0_monitoredSubject, newElement_0_fabricIndex); + newElement_0_checkInNodeID, newElement_0_monitoredSubject, newElement_0_clientType, + newElement_0_fabricIndex); chip::JniReferences::GetInstance().AddToList(value, newElement_0); } return value; diff --git a/src/controller/python/ChipDeviceController-ScriptDevicePairingDelegate.cpp b/src/controller/python/ChipDeviceController-ScriptDevicePairingDelegate.cpp index c979e0d9cd77a3..c04481a45a11ea 100644 --- a/src/controller/python/ChipDeviceController-ScriptDevicePairingDelegate.cpp +++ b/src/controller/python/ChipDeviceController-ScriptDevicePairingDelegate.cpp @@ -191,6 +191,7 @@ void ScriptDevicePairingDelegate::OnICDRegistrationComplete(ScopedNodeId nodeId, clientInfo.peer_node = nodeId; clientInfo.monitored_subject = sCommissioningParameters.GetICDMonitoredSubject().Value(); clientInfo.start_icd_counter = icdCounter; + clientInfo.client_type = sCommissioningParameters.GetICDClientType().Value(); CHIP_ERROR err = sICDClientStorage.SetKey(clientInfo, ByteSpan(sICDSymmetricKey)); if (err == CHIP_NO_ERROR) diff --git a/src/controller/python/chip/clusters/CHIPClusters.py b/src/controller/python/chip/clusters/CHIPClusters.py index 773fe342105443..37ead8e634611a 100644 --- a/src/controller/python/chip/clusters/CHIPClusters.py +++ b/src/controller/python/chip/clusters/CHIPClusters.py @@ -4064,6 +4064,7 @@ class ChipClusters: "monitoredSubject": "int", "key": "bytes", "verificationKey": "bytes", + "clientType": "int", }, }, 0x00000002: { diff --git a/src/controller/python/chip/clusters/Objects.py b/src/controller/python/chip/clusters/Objects.py index 43917192955506..4c17fd99af2d21 100644 --- a/src/controller/python/chip/clusters/Objects.py +++ b/src/controller/python/chip/clusters/Objects.py @@ -14187,6 +14187,15 @@ def descriptor(cls) -> ClusterObjectDescriptor: clusterRevision: 'uint' = None class Enums: + class ClientTypeEnum(MatterIntEnum): + kPermanent = 0x00 + kEphemeral = 0x01 + # All received enum values that are not listed above will be mapped + # to kUnknownEnumValue. This is a helper enum value that should only + # be used by code to process how it handles receiving and unknown + # enum value. This specific should never be transmitted. + kUnknownEnumValue = 2, + class OperatingModeEnum(MatterIntEnum): kSit = 0x00 kLit = 0x01 @@ -14230,11 +14239,13 @@ def descriptor(cls) -> ClusterObjectDescriptor: Fields=[ ClusterObjectFieldDescriptor(Label="checkInNodeID", Tag=1, Type=uint), ClusterObjectFieldDescriptor(Label="monitoredSubject", Tag=2, Type=uint), + ClusterObjectFieldDescriptor(Label="clientType", Tag=4, Type=IcdManagement.Enums.ClientTypeEnum), ClusterObjectFieldDescriptor(Label="fabricIndex", Tag=254, Type=uint), ]) checkInNodeID: 'uint' = 0 monitoredSubject: 'uint' = 0 + clientType: 'IcdManagement.Enums.ClientTypeEnum' = 0 fabricIndex: 'uint' = 0 class Commands: @@ -14253,12 +14264,14 @@ def descriptor(cls) -> ClusterObjectDescriptor: ClusterObjectFieldDescriptor(Label="monitoredSubject", Tag=1, Type=uint), ClusterObjectFieldDescriptor(Label="key", Tag=2, Type=bytes), ClusterObjectFieldDescriptor(Label="verificationKey", Tag=3, Type=typing.Optional[bytes]), + ClusterObjectFieldDescriptor(Label="clientType", Tag=4, Type=IcdManagement.Enums.ClientTypeEnum), ]) checkInNodeID: 'uint' = 0 monitoredSubject: 'uint' = 0 key: 'bytes' = b"" verificationKey: 'typing.Optional[bytes]' = None + clientType: 'IcdManagement.Enums.ClientTypeEnum' = 0 @dataclass class RegisterClientResponse(ClusterCommand): diff --git a/src/darwin/Framework/CHIP/templates/availability.yaml b/src/darwin/Framework/CHIP/templates/availability.yaml index 4327c61ba206aa..4ca8533a55d561 100644 --- a/src/darwin/Framework/CHIP/templates/availability.yaml +++ b/src/darwin/Framework/CHIP/templates/availability.yaml @@ -9690,6 +9690,7 @@ provisional: clusters: # Targeting Fall 2024 + - ServiceArea - ThreadBorderRouterManagement - ThreadNetworkDirectory - WiFiNetworkManagement diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm index 02ce62e1c2f0dc..1feae62ea18928 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm @@ -5040,6 +5040,7 @@ static id _Nullable DecodeAttributeValueForICDManagementCluster(AttributeId aAtt newElement_0 = [MTRICDManagementClusterMonitoringRegistrationStruct new]; newElement_0.checkInNodeID = [NSNumber numberWithUnsignedLongLong:entry_0.checkInNodeID]; newElement_0.monitoredSubject = [NSNumber numberWithUnsignedLongLong:entry_0.monitoredSubject]; + newElement_0.clientType = [NSNumber numberWithUnsignedChar:chip::to_underlying(entry_0.clientType)]; newElement_0.fabricIndex = [NSNumber numberWithUnsignedChar:entry_0.fabricIndex]; [array_0 addObject:newElement_0]; } diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h index a481ad0f37f8a4..1513b8c0f54540 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h @@ -17756,6 +17756,11 @@ typedef NS_OPTIONS(uint32_t, MTRGroupKeyManagementFeature) { MTRGroupKeyManagementFeatureCacheAndSync MTR_PROVISIONALLY_AVAILABLE = 0x1, } MTR_PROVISIONALLY_AVAILABLE; +typedef NS_ENUM(uint8_t, MTRICDManagementClientType) { + MTRICDManagementClientTypePermanent MTR_PROVISIONALLY_AVAILABLE = 0x00, + MTRICDManagementClientTypeEphemeral MTR_PROVISIONALLY_AVAILABLE = 0x01, +} MTR_PROVISIONALLY_AVAILABLE; + typedef NS_ENUM(uint8_t, MTRICDManagementOperatingMode) { MTRICDManagementOperatingModeSIT MTR_PROVISIONALLY_AVAILABLE = 0x00, MTRICDManagementOperatingModeLIT MTR_PROVISIONALLY_AVAILABLE = 0x01, diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h index 4e74aa8828a718..d95ac77034ce61 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h @@ -3618,6 +3618,8 @@ MTR_PROVISIONALLY_AVAILABLE @property (nonatomic, copy) NSData * _Nonnull key MTR_PROVISIONALLY_AVAILABLE; @property (nonatomic, copy) NSData * _Nullable verificationKey MTR_PROVISIONALLY_AVAILABLE; + +@property (nonatomic, copy) NSNumber * _Nonnull clientType MTR_PROVISIONALLY_AVAILABLE; /** * Controls whether the command is a timed command (using Timed Invoke). * diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm index 1dd6753fa889ed..08b00b6e4555f0 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm @@ -9524,6 +9524,8 @@ - (instancetype)init _key = [NSData data]; _verificationKey = nil; + + _clientType = @(0); _timedInvokeTimeoutMs = nil; _serverSideProcessingTimeout = nil; } @@ -9538,6 +9540,7 @@ - (id)copyWithZone:(NSZone * _Nullable)zone; other.monitoredSubject = self.monitoredSubject; other.key = self.key; other.verificationKey = self.verificationKey; + other.clientType = self.clientType; other.timedInvokeTimeoutMs = self.timedInvokeTimeoutMs; other.serverSideProcessingTimeout = self.serverSideProcessingTimeout; @@ -9546,7 +9549,7 @@ - (id)copyWithZone:(NSZone * _Nullable)zone; - (NSString *)description { - NSString * descriptionString = [NSString stringWithFormat:@"<%@: checkInNodeID:%@; monitoredSubject:%@; key:%@; verificationKey:%@; >", NSStringFromClass([self class]), _checkInNodeID, _monitoredSubject, [_key base64EncodedStringWithOptions:0], [_verificationKey base64EncodedStringWithOptions:0]]; + NSString * descriptionString = [NSString stringWithFormat:@"<%@: checkInNodeID:%@; monitoredSubject:%@; key:%@; verificationKey:%@; clientType:%@; >", NSStringFromClass([self class]), _checkInNodeID, _monitoredSubject, [_key base64EncodedStringWithOptions:0], [_verificationKey base64EncodedStringWithOptions:0], _clientType]; return descriptionString; } @@ -9573,6 +9576,9 @@ - (CHIP_ERROR)_encodeToTLVReader:(chip::System::PacketBufferTLVReader &)reader definedValue_0 = AsByteSpan(self.verificationKey); } } + { + encodableStruct.clientType = static_cast>(self.clientType.unsignedCharValue); + } auto buffer = chip::System::PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSizeWithoutReserve, 0); if (buffer.IsNull()) { diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h index 2d5da93d9df2f5..8ea03ce1fd2a9d 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h @@ -762,6 +762,7 @@ MTR_PROVISIONALLY_AVAILABLE @interface MTRICDManagementClusterMonitoringRegistrationStruct : NSObject @property (nonatomic, copy) NSNumber * _Nonnull checkInNodeID MTR_PROVISIONALLY_AVAILABLE; @property (nonatomic, copy) NSNumber * _Nonnull monitoredSubject MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nonnull clientType MTR_PROVISIONALLY_AVAILABLE; @property (nonatomic, copy) NSNumber * _Nonnull fabricIndex MTR_PROVISIONALLY_AVAILABLE; @end diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm index 559789940c7fb9..1ff604e5469e36 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm @@ -2774,6 +2774,8 @@ - (instancetype)init _monitoredSubject = @(0); + _clientType = @(0); + _fabricIndex = @(0); } return self; @@ -2785,6 +2787,7 @@ - (id)copyWithZone:(NSZone * _Nullable)zone other.checkInNodeID = self.checkInNodeID; other.monitoredSubject = self.monitoredSubject; + other.clientType = self.clientType; other.fabricIndex = self.fabricIndex; return other; @@ -2792,7 +2795,7 @@ - (id)copyWithZone:(NSZone * _Nullable)zone - (NSString *)description { - NSString * descriptionString = [NSString stringWithFormat:@"<%@: checkInNodeID:%@; monitoredSubject:%@; fabricIndex:%@; >", NSStringFromClass([self class]), _checkInNodeID, _monitoredSubject, _fabricIndex]; + NSString * descriptionString = [NSString stringWithFormat:@"<%@: checkInNodeID:%@; monitoredSubject:%@; clientType:%@; fabricIndex:%@; >", NSStringFromClass([self class]), _checkInNodeID, _monitoredSubject, _clientType, _fabricIndex]; return descriptionString; } diff --git a/src/include/platform/CHIPDeviceEvent.h b/src/include/platform/CHIPDeviceEvent.h index 67809adda04c73..437b20e670284f 100644 --- a/src/include/platform/CHIPDeviceEvent.h +++ b/src/include/platform/CHIPDeviceEvent.h @@ -250,6 +250,11 @@ enum PublicEventTypes * Signals that BLE is deinitialized. */ kBLEDeinitialized, + + /** + * Signals that secure session is established. + */ + kSecureSessionEstablished, }; /** @@ -533,6 +538,15 @@ struct ChipDeviceEvent final { OtaState newState; } OtaStateChanged; + + struct + { + uint64_t PeerNodeId; + uint8_t FabricIndex; + uint8_t SecureSessionType; + uint8_t TransportType; + uint16_t LocalSessionId; + } SecureSessionEstablished; }; bool IsPublic() const { return DeviceEventType::IsPublic(Type); } diff --git a/src/lib/core/CHIPError.h b/src/lib/core/CHIPError.h index aebf7804aefb10..b49088247e126c 100644 --- a/src/lib/core/CHIPError.h +++ b/src/lib/core/CHIPError.h @@ -439,6 +439,15 @@ using CHIP_ERROR = ::chip::ChipError; CHIP_SDK_ERROR(::chip::ChipError::SdkPart::kIMGlobalStatus, \ ::chip::to_underlying(::chip::Protocols::InteractionModel::Status::type)) +// Defines a runtime-value for a chip-error that contains a global IM Status. +#if CHIP_CONFIG_ERROR_SOURCE +#define CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(status_value) \ + ::chip::ChipError(::chip::ChipError::SdkPart::kIMGlobalStatus, ::chip::to_underlying(status_value), __FILE__, __LINE__) +#else +#define CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(status_value) \ + ::chip::ChipError(::chip::ChipError::SdkPart::kIMGlobalStatus, ::chip::to_underlying(status_value)) +#endif // CHIP_CONFIG_ERROR_SOURCE + // // type must be a compile-time constant as mandated by CHIP_SDK_ERROR. // diff --git a/src/lib/support/JniReferences.cpp b/src/lib/support/JniReferences.cpp index 6b8bb005305417..021f13c2fb0fb2 100644 --- a/src/lib/support/JniReferences.cpp +++ b/src/lib/support/JniReferences.cpp @@ -392,6 +392,19 @@ jdouble JniReferences::DoubleToPrimitive(jobject boxedDouble) return env->CallDoubleMethod(boxedDouble, valueMethod); } +jshort JniReferences::ShortToPrimitive(jobject boxedShort) +{ + JNIEnv * env = GetEnvForCurrentThread(); + VerifyOrReturnValue(env != nullptr, 0, ChipLogError(Support, "env cannot be nullptr")); + jclass boxedTypeCls = nullptr; + CHIP_ERROR err = chip::JniReferences::GetInstance().GetLocalClassRef(env, "java/lang/Short", boxedTypeCls); + VerifyOrReturnValue(err == CHIP_NO_ERROR, 0, + ChipLogError(Support, "ShortToPrimitive failed due to %" CHIP_ERROR_FORMAT, err.Format())); + + jmethodID valueMethod = env->GetMethodID(boxedTypeCls, "shortValue", "()S"); + return env->CallShortMethod(boxedShort, valueMethod); +} + CHIP_ERROR JniReferences::CallSubscriptionEstablished(jobject javaCallback, long subscriptionId) { CHIP_ERROR err = CHIP_NO_ERROR; diff --git a/src/lib/support/JniReferences.h b/src/lib/support/JniReferences.h index 5cea905dc00776..a9a3e29ebcb845 100644 --- a/src/lib/support/JniReferences.h +++ b/src/lib/support/JniReferences.h @@ -177,6 +177,11 @@ class JniReferences */ jdouble DoubleToPrimitive(jobject boxedObject); + /** + * Get a primitive jshort from the Java boxed type Short, using shortValue(). + */ + jshort ShortToPrimitive(jobject boxedShort); + CHIP_ERROR CreateArrayList(jobject & outList); CHIP_ERROR AddToList(jobject list, jobject objectToAdd); diff --git a/src/protocols/secure_channel/PairingSession.cpp b/src/protocols/secure_channel/PairingSession.cpp index ae4ca272858a78..6176d097c5118a 100644 --- a/src/protocols/secure_channel/PairingSession.cpp +++ b/src/protocols/secure_channel/PairingSession.cpp @@ -22,6 +22,9 @@ #include #include #include +#include +#include +#include #include namespace chip { @@ -78,6 +81,18 @@ void PairingSession::Finish() if (err == CHIP_NO_ERROR) { VerifyOrDie(mSecureSessionHolder); + DeviceLayer::ChipDeviceEvent event; + event.Type = DeviceLayer::DeviceEventType::kSecureSessionEstablished; + event.SecureSessionEstablished.TransportType = to_underlying(address.GetTransportType()); + event.SecureSessionEstablished.SecureSessionType = + to_underlying(mSecureSessionHolder->AsSecureSession()->GetSecureSessionType()); + event.SecureSessionEstablished.LocalSessionId = mSecureSessionHolder->AsSecureSession()->GetLocalSessionId(); + event.SecureSessionEstablished.PeerNodeId = mSecureSessionHolder->GetPeer().GetNodeId(); + event.SecureSessionEstablished.FabricIndex = mSecureSessionHolder->GetPeer().GetFabricIndex(); + if (DeviceLayer::PlatformMgr().PostEvent(&event) != CHIP_NO_ERROR) + { + ChipLogError(SecureChannel, "Failed to post Secure Session established event"); + } // Make sure to null out mDelegate so we don't send it any other // notifications. auto * delegate = mDelegate; diff --git a/src/python_testing/TC_ICDM_3_1.py b/src/python_testing/TC_ICDM_3_1.py index b504035f52d86f..f5a37cd763c7af 100644 --- a/src/python_testing/TC_ICDM_3_1.py +++ b/src/python_testing/TC_ICDM_3_1.py @@ -38,6 +38,7 @@ cluster = Clusters.Objects.IcdManagement commands = cluster.Commands monitoredRegistration = cluster.Structs.MonitoringRegistrationStruct +clientTypeEnum = cluster.Enums.ClientTypeEnum # Step 2 Registration entry @@ -141,7 +142,7 @@ async def test_TC_ICDM_3_1(self): self.step(2) if self.pics_guard(self.check_pics("ICDM.S.C00.Rsp")): try: - response = await self._send_single_icdm_command(commands.RegisterClient(checkInNodeID=kStep2CheckInNodeId, monitoredSubject=kStep2MonitoredSubjectStep2, key=kStep2Key)) + response = await self._send_single_icdm_command(commands.RegisterClient(checkInNodeID=kStep2CheckInNodeId, monitoredSubject=kStep2MonitoredSubjectStep2, key=kStep2Key, clientType=clientTypeEnum.kEphemeral)) except InteractionModelError as e: asserts.assert_equal( e.status, Status.Success, "Unexpected error returned") @@ -164,6 +165,8 @@ async def test_TC_ICDM_3_1(self): registeredClients[0].checkInNodeID, kStep2CheckInNodeId, "The read attribute does not match the registered value.") asserts.assert_equal( registeredClients[0].monitoredSubject, kStep2MonitoredSubjectStep2, "The read attribute does not match the registered value.") + asserts.assert_equal( + registeredClients[0].clientType, clientTypeEnum.kEphemeral, "The read attribute does not match the registered value.") self.step(4) if self.pics_guard(self.check_pics("ICDM.S.C00.Rsp")): @@ -174,12 +177,13 @@ async def test_TC_ICDM_3_1(self): newClients.append({ "checkInNodeID": i + 1, "monitoredSubject": i + 1, - "key": os.urandom(16) + "key": os.urandom(16), + "clientType": clientTypeEnum.kPermanent }) for client in newClients: try: - response = await self._send_single_icdm_command(commands.RegisterClient(checkInNodeID=client["checkInNodeID"], monitoredSubject=client["monitoredSubject"], key=client["key"])) + response = await self._send_single_icdm_command(commands.RegisterClient(checkInNodeID=client["checkInNodeID"], monitoredSubject=client["monitoredSubject"], key=client["key"], clientType=client["clientType"])) except InteractionModelError as e: asserts.assert_equal( e.status, Status.Success, "Unexpected error returned") @@ -203,11 +207,13 @@ async def test_TC_ICDM_3_1(self): client.checkInNodeID, expectedClient["checkInNodeID"], "The read attribute does not match the registered value.") asserts.assert_equal( client.monitoredSubject, expectedClient["monitoredSubject"], "The read attribute does not match the registered value.") + asserts.assert_equal( + client.clientType, expectedClient["clientType"], "The read attribute does not match the registered value.") self.step(6) if self.pics_guard(self.check_pics("ICDM.S.C00.Rsp")): try: - response = await self._send_single_icdm_command(commands.RegisterClient(checkInNodeID=0xFFFF, monitoredSubject=0xFFFF, key=os.urandom(16))) + response = await self._send_single_icdm_command(commands.RegisterClient(checkInNodeID=0xFFFF, monitoredSubject=0xFFFF, key=os.urandom(16), clientType=clientTypeEnum.kPermanent)) except InteractionModelError as e: asserts.assert_equal( e.status, Status.ResourceExhausted, "Unexpected error returned") diff --git a/src/setup_payload/python/README.md b/src/setup_payload/python/README.md index a39496104300d1..b19553be7aca1e 100644 --- a/src/setup_payload/python/README.md +++ b/src/setup_payload/python/README.md @@ -20,8 +20,3 @@ Generates and parses Manual Pairing Code and QR Code ``` For more details please refer Matter Specification - ---- - -NOTE: This tool is only capable of generating the payloads and no support to -parse the payloads. diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h b/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h index e53404d7b32737..7e52fd09a2c13c 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h @@ -1328,6 +1328,18 @@ static auto __attribute__((unused)) EnsureKnownEnumValue(GroupKeyManagement::Gro } } +static auto __attribute__((unused)) EnsureKnownEnumValue(IcdManagement::ClientTypeEnum val) +{ + using EnumType = IcdManagement::ClientTypeEnum; + switch (val) + { + case EnumType::kPermanent: + case EnumType::kEphemeral: + return val; + default: + return EnumType::kUnknownEnumValue; + } +} static auto __attribute__((unused)) EnsureKnownEnumValue(IcdManagement::OperatingModeEnum val) { using EnumType = IcdManagement::OperatingModeEnum; diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h index 27601f4e651c63..a7c433848df662 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h @@ -1601,6 +1601,18 @@ namespace BooleanState {} // namespace BooleanState namespace IcdManagement { +// Enum for ClientTypeEnum +enum class ClientTypeEnum : uint8_t +{ + kPermanent = 0x00, + kEphemeral = 0x01, + // All received enum values that are not listed above will be mapped + // to kUnknownEnumValue. This is a helper enum value that should only + // be used by code to process how it handles receiving and unknown + // enum value. This specific should never be transmitted. + kUnknownEnumValue = 2, +}; + // Enum for OperatingModeEnum enum class OperatingModeEnum : uint8_t { diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp index 289909746b0640..54bcec37353ef1 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp @@ -9622,6 +9622,10 @@ CHIP_ERROR Type::DoEncode(TLV::TLVWriter & aWriter, TLV::Tag aTag, const Optiona { encoder.Encode(to_underlying(Fields::kMonitoredSubject), monitoredSubject); } + if (includeSensitive) + { + encoder.Encode(to_underlying(Fields::kClientType), clientType); + } if (aAccessingFabricIndex.HasValue()) { encoder.Encode(to_underlying(Fields::kFabricIndex), fabricIndex); @@ -9652,6 +9656,10 @@ CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) { err = DataModel::Decode(reader, monitoredSubject); } + else if (__context_tag == to_underlying(Fields::kClientType)) + { + err = DataModel::Decode(reader, clientType); + } else if (__context_tag == to_underlying(Fields::kFabricIndex)) { err = DataModel::Decode(reader, fabricIndex); @@ -9676,6 +9684,7 @@ CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const encoder.Encode(to_underlying(Fields::kMonitoredSubject), monitoredSubject); encoder.Encode(to_underlying(Fields::kKey), key); encoder.Encode(to_underlying(Fields::kVerificationKey), verificationKey); + encoder.Encode(to_underlying(Fields::kClientType), clientType); return encoder.Finalize(); } @@ -9709,6 +9718,10 @@ CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) { err = DataModel::Decode(reader, verificationKey); } + else if (__context_tag == to_underlying(Fields::kClientType)) + { + err = DataModel::Decode(reader, clientType); + } else { } diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h index d396989d0a6edf..84f19b11e4f105 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h @@ -13130,6 +13130,7 @@ enum class Fields : uint8_t { kCheckInNodeID = 1, kMonitoredSubject = 2, + kClientType = 4, kFabricIndex = 254, }; @@ -13138,6 +13139,7 @@ struct Type public: chip::NodeId checkInNodeID = static_cast(0); uint64_t monitoredSubject = static_cast(0); + ClientTypeEnum clientType = static_cast(0); chip::FabricIndex fabricIndex = static_cast(0); CHIP_ERROR Decode(TLV::TLVReader & reader); @@ -13198,6 +13200,7 @@ enum class Fields : uint8_t kMonitoredSubject = 1, kKey = 2, kVerificationKey = 3, + kClientType = 4, }; struct Type @@ -13211,6 +13214,7 @@ struct Type uint64_t monitoredSubject = static_cast(0); chip::ByteSpan key; Optional verificationKey; + ClientTypeEnum clientType = static_cast(0); CHIP_ERROR Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; @@ -13229,6 +13233,7 @@ struct DecodableType uint64_t monitoredSubject = static_cast(0); chip::ByteSpan key; Optional verificationKey; + ClientTypeEnum clientType = static_cast(0); CHIP_ERROR Decode(TLV::TLVReader & reader); }; }; // namespace RegisterClient diff --git a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h index a9658c4a1ace4f..2cdd59a303bb63 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h @@ -4242,6 +4242,7 @@ class IcdManagementRegisterClient : public ClusterCommand AddArgument("MonitoredSubject", 0, UINT64_MAX, &mRequest.monitoredSubject); AddArgument("Key", &mRequest.key); AddArgument("VerificationKey", &mRequest.verificationKey); + AddArgument("ClientType", 0, UINT8_MAX, &mRequest.clientType); ClusterCommand::AddArguments(); } diff --git a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp index 6c7da78177ff10..6a73b61ce13720 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp +++ b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp @@ -2066,6 +2066,8 @@ CHIP_ERROR ComplexArgumentParser::Setup(const char * label, value.isMember("checkInNodeID"))); ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("MonitoringRegistrationStruct.monitoredSubject", "monitoredSubject", value.isMember("monitoredSubject"))); + ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("MonitoringRegistrationStruct.clientType", "clientType", + value.isMember("clientType"))); char labelWithMember[kMaxLabelLength]; snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "checkInNodeID"); @@ -2076,6 +2078,10 @@ CHIP_ERROR ComplexArgumentParser::Setup(const char * label, ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.monitoredSubject, value["monitoredSubject"])); valueCopy.removeMember("monitoredSubject"); + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "clientType"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.clientType, value["clientType"])); + valueCopy.removeMember("clientType"); + if (value.isMember("fabricIndex")) { snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "fabricIndex"); @@ -2090,6 +2096,7 @@ void ComplexArgumentParser::Finalize(chip::app::Clusters::IcdManagement::Structs { ComplexArgumentParser::Finalize(request.checkInNodeID); ComplexArgumentParser::Finalize(request.monitoredSubject); + ComplexArgumentParser::Finalize(request.clientType); ComplexArgumentParser::Finalize(request.fabricIndex); } diff --git a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp index 1f8be3d7029a79..a9d3f942abcd7c 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp +++ b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp @@ -1878,6 +1878,14 @@ DataModelLogger::LogValue(const char * label, size_t indent, return err; } } + { + CHIP_ERROR err = LogValue("ClientType", indent + 1, value.clientType); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'ClientType'"); + return err; + } + } { CHIP_ERROR err = LogValue("FabricIndex", indent + 1, value.fabricIndex); if (err != CHIP_NO_ERROR) diff --git a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h index 2bea6e6b3fff8a..dfac8e499e1449 100644 --- a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h @@ -47524,6 +47524,9 @@ class IcdManagementRegisterClient : public ClusterCommand { #endif // MTR_ENABLE_PROVISIONAL #if MTR_ENABLE_PROVISIONAL AddArgument("VerificationKey", &mRequest.verificationKey); +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + AddArgument("ClientType", 0, UINT8_MAX, &mRequest.clientType); #endif // MTR_ENABLE_PROVISIONAL ClusterCommand::AddArguments(); } @@ -47554,6 +47557,9 @@ class IcdManagementRegisterClient : public ClusterCommand { } else { params.verificationKey = nil; } +#endif // MTR_ENABLE_PROVISIONAL +#if MTR_ENABLE_PROVISIONAL + params.clientType = [NSNumber numberWithUnsignedChar:chip::to_underlying(mRequest.clientType)]; #endif // MTR_ENABLE_PROVISIONAL uint16_t repeatCount = mRepeatCount.ValueOr(1); uint16_t __block responsesNeeded = repeatCount;