Skip to content

Commit

Permalink
ICD Management cluster server.
Browse files Browse the repository at this point in the history
  • Loading branch information
rcasallas-silabs committed Jun 6, 2023
1 parent d700f2f commit 63c9625
Show file tree
Hide file tree
Showing 22 changed files with 1,649 additions and 537 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5938,7 +5938,7 @@ endpoint 0 {
ram attribute activeModeThreshold default = 300;
callback attribute registeredClients;
ram attribute ICDCounter default = 0;
ram attribute clientsSupportedPerFabric default = 1;
ram attribute clientsSupportedPerFabric default = 2;
callback attribute generatedCommandList;
callback attribute acceptedCommandList;
callback attribute eventList;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6432,7 +6432,7 @@
"storageOption": "RAM",
"singleton": 0,
"bounded": 0,
"defaultValue": "1",
"defaultValue": "2",
"reportable": 1,
"minInterval": 1,
"maxInterval": 65534,
Expand Down Expand Up @@ -31934,5 +31934,6 @@
"endpointVersion": 1,
"deviceIdentifier": 61442
}
]
],
"log": []
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"include": "../../../../src/app/tests/suites/ciTests.json",
"disable": [
"TestClientMonitoringCluster",
"TestIcdManagementCluster",
"Test_TC_SC_4_2",
"Test_TC_SC_5_2",
"TestClusterComplexTypes",
Expand Down
1 change: 0 additions & 1 deletion scripts/tests/chiptest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ def _GetInDevelopmentTests() -> Set[str]:
"Test_AddNewFabricFromExistingFabric.yaml", # chip-repl does not support GetCommissionerRootCertificate and IssueNocChain command
"TestEqualities.yaml", # chip-repl does not support pseudo-cluster commands that return a value
"TestExampleCluster.yaml", # chip-repl does not load custom pseudo clusters
"TestClientMonitoringCluster.yaml", # Client Monitoring Tests need a rework after the XML update
"Test_TC_TIMESYNC_1_1.yaml" # Time sync SDK is not yet ready
}

Expand Down
4 changes: 1 addition & 3 deletions src/app/chip_data_model.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,7 @@ function(chip_configure_data_model APP_TARGET)
${CHIP_APP_BASE_DIR}/util/attribute-storage.cpp
${CHIP_APP_BASE_DIR}/util/attribute-table.cpp
${CHIP_APP_BASE_DIR}/util/binding-table.cpp
# Disable CM cluster table tests until update is done
# https://github.com/project-chip/connectedhomeip/issues/24425
# ${CHIP_APP_BASE_DIR}/util/ClientMonitoringRegistrationTable.cpp
${CHIP_APP_BASE_DIR}/util/IcdMonitoringTable.cpp
${CHIP_APP_BASE_DIR}/util/DataModelHandler.cpp
${CHIP_APP_BASE_DIR}/util/ember-compatibility-functions.cpp
${CHIP_APP_BASE_DIR}/util/error-mapping.cpp
Expand Down
4 changes: 2 additions & 2 deletions src/app/chip_data_model.gni
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,9 @@ template("chip_data_model") {
"${_app_root}/clusters/scenes-server/SceneTable.h",
"${_app_root}/clusters/scenes-server/SceneTableImpl.h",
"${_app_root}/clusters/scenes-server/scenes-server.h",
"${_app_root}/util/ClientMonitoringRegistrationTable.cpp",
"${_app_root}/util/ClientMonitoringRegistrationTable.h",
"${_app_root}/util/DataModelHandler.cpp",
"${_app_root}/util/IcdMonitoringTable.cpp",
"${_app_root}/util/IcdMonitoringTable.h",
"${_app_root}/util/attribute-size-util.cpp",
"${_app_root}/util/attribute-storage.cpp",
"${_app_root}/util/attribute-table.cpp",
Expand Down
194 changes: 167 additions & 27 deletions src/app/clusters/icd-management-server/icd-management-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@
#include "icd-management-server.h"

#include "app/server/Server.h"
#include <access/AccessControl.h>
#include <access/Privilege.h>
#include <app-common/zap-generated/attributes/Accessors.h>
#include <app-common/zap-generated/cluster-objects.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <app/AttributeAccessInterface.h>
#include <app/CommandHandler.h>
#include <app/ConcreteAttributePath.h>
#include <app/util/IcdMonitoringTable.h>
#include <app/util/af.h>
#include <app/util/attribute-storage.h>

Expand All @@ -32,8 +35,7 @@ using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::IcdManagement;
using namespace Protocols;

//==============================================================================
using namespace chip::Access;

namespace {

Expand Down Expand Up @@ -69,61 +71,199 @@ CHIP_ERROR IcdManagementAttributeAccess::Read(const ConcreteReadAttributePath &

CHIP_ERROR IcdManagementAttributeAccess::ReadRegisteredClients(EndpointId endpoint, AttributeValueEncoder & encoder)
{
return encoder.EncodeEmptyList();
uint16_t supported_clients = 0;
VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == Attributes::ClientsSupportedPerFabric::Get(endpoint, &supported_clients),
CHIP_ERROR_NOT_FOUND);

return encoder.EncodeList([supported_clients](const auto & subEncoder) -> CHIP_ERROR {
IcdMonitoringEntry e;

const auto & fabricTable = Server::GetInstance().GetFabricTable();
for (const auto & fabricInfo : fabricTable)
{
PersistentStorageDelegate & storage = chip::Server::GetInstance().GetPersistentStorage();
IcdMonitoringTable table(storage, fabricInfo.GetFabricIndex(), supported_clients);
for (uint16_t i = 0; i < table.Limit(); ++i)
{
CHIP_ERROR err = table.Get(i, e);
if (CHIP_ERROR_NOT_FOUND == err)
{
// No more entries in the table
break;
}
ReturnErrorOnFailure(err);
ReturnErrorOnFailure(subEncoder.Encode(e));
}
}
return CHIP_NO_ERROR;
});
}

CHIP_ERROR CheckAdmin(chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, bool & is_admin)
{
RequestPath requestPath{ .cluster = commandPath.mClusterId, .endpoint = commandPath.mEndpointId };
CHIP_ERROR err = GetAccessControl().Check(commandObj->GetSubjectDescriptor(), requestPath, Privilege::kAdminister);
if (CHIP_NO_ERROR == err)
{
is_admin = true;
}
else if (CHIP_ERROR_ACCESS_DENIED == err)
{
is_admin = false;
err = CHIP_NO_ERROR;
}
return err;
}

class IcdManagementFabricDelegate : public chip::FabricTable::Delegate
{
void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override
{
uint16_t supported_clients = 0;
if (EMBER_ZCL_STATUS_SUCCESS != Attributes::ClientsSupportedPerFabric::Get(kRootEndpointId, &supported_clients))
{
// Fallback to maximum, the remove function will loop until no more entries are found.
supported_clients = UINT16_MAX;
}
IcdMonitoringTable table(chip::Server::GetInstance().GetPersistentStorage(), fabricIndex, supported_clients);
table.RemoveAll();
}
};

IcdManagementFabricDelegate gFabricDelegate;
IcdManagementAttributeAccess gAttribute;

} // namespace

//==============================================================================

InteractionModel::Status
IcdManagementServer::RegisterClient(chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::Clusters::IcdManagement::Commands::RegisterClient::DecodableType & commandData)
InteractionModel::Status IcdManagementServer::RegisterClient(chip::app::CommandHandler * commandObj,
const chip::app::ConcreteCommandPath & commandPath,
const Commands::RegisterClient::DecodableType & commandData)
{
// TODO: Implementent logic for end device
return InteractionModel::Status::UnsupportedCommand;
uint16_t supported_clients = 0;
VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS ==
Attributes::ClientsSupportedPerFabric::Get(commandPath.mEndpointId, &supported_clients),
InteractionModel::Status::Failure);
IcdMonitoringTable table(chip::Server::GetInstance().GetPersistentStorage(), commandObj->GetAccessingFabricIndex(),
supported_clients);

// Get current entry, if exists
IcdMonitoringEntry entry;
CHIP_ERROR err = table.Find(commandData.checkInNodeID, entry);
if (CHIP_NO_ERROR == err)
{
// Existing entry: Validate Key if, and only if, the ISD has NOT administrator permissions
bool is_admin = false;
err = CheckAdmin(commandObj, commandPath, is_admin);
VerifyOrReturnError(CHIP_NO_ERROR == err, InteractionModel::Status::Failure);
if (!is_admin)
{
VerifyOrReturnError(commandData.verificationKey.HasValue(), InteractionModel::Status::Failure);
VerifyOrReturnError(commandData.verificationKey.Value().data_equal(entry.key), InteractionModel::Status::Failure);
}
}
else if (CHIP_ERROR_NOT_FOUND == err)
{
// New entry
VerifyOrReturnError(entry.index < table.Limit(), InteractionModel::Status::ResourceExhausted);
}
else
{
// Error
return InteractionModel::Status::Failure;
}

// Save
entry.checkInNodeID = commandData.checkInNodeID;
entry.monitoredSubject = commandData.monitoredSubject;
entry.key = commandData.key;
err = table.Set(entry.index, entry);
VerifyOrReturnError(CHIP_ERROR_INVALID_ARGUMENT != err, InteractionModel::Status::ConstraintError);
VerifyOrReturnError(CHIP_NO_ERROR == err, InteractionModel::Status::Failure);

return InteractionModel::Status::Success;
}

InteractionModel::Status IcdManagementServer::UnregisterClient(
chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::Clusters::IcdManagement::Commands::UnregisterClient::DecodableType & commandData)
InteractionModel::Status IcdManagementServer::UnregisterClient(chip::app::CommandHandler * commandObj,
const chip::app::ConcreteCommandPath & commandPath,
const Commands::UnregisterClient::DecodableType & commandData)
{
// TODO: Implementent logic for end device
return InteractionModel::Status::UnsupportedCommand;
uint16_t supported_clients = 0;
VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS ==
Attributes::ClientsSupportedPerFabric::Get(commandPath.mEndpointId, &supported_clients),
InteractionModel::Status::Failure);
IcdMonitoringTable table(chip::Server::GetInstance().GetPersistentStorage(), commandObj->GetAccessingFabricIndex(),
supported_clients);

// Get current entry, if exists
IcdMonitoringEntry entry;
CHIP_ERROR err = table.Find(commandData.checkInNodeID, entry);
VerifyOrReturnError(CHIP_ERROR_NOT_FOUND != err, InteractionModel::Status::NotFound);
VerifyOrReturnError(CHIP_NO_ERROR == err, InteractionModel::Status::Failure);

// Existing entry: Validate Key if, and only if, the ISD has NOT administrator permissions
bool is_admin = false;
err = CheckAdmin(commandObj, commandPath, is_admin);
VerifyOrReturnError(CHIP_NO_ERROR == err, InteractionModel::Status::Failure);

if (!is_admin)
{
VerifyOrReturnError(commandData.key.HasValue(), InteractionModel::Status::Failure);
VerifyOrReturnError(commandData.key.Value().data_equal(entry.key), InteractionModel::Status::Failure);
}

err = table.Remove(entry.index);
VerifyOrReturnError(CHIP_NO_ERROR == err, InteractionModel::Status::Failure);

return InteractionModel::Status::Success;
}

InteractionModel::Status IcdManagementServer::StayActiveRequest(const chip::app::ConcreteCommandPath & commandPath)
{
// TODO: Implementent logic for end device

// TODO: Implementent stay awake logic for end device
return InteractionModel::Status::UnsupportedCommand;
}

//==============================================================================
void emberAfIcdManagementClusterInitCallback()
{
Server::GetInstance().GetFabricTable().AddFabricDelegate(&gFabricDelegate);
}

/**
* @brief ICD Management Cluster RegisterClient Command callback (from client)
*
*/
bool emberAfIcdManagementClusterRegisterClientCallback(
chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::Clusters::IcdManagement::Commands::RegisterClient::DecodableType & commandData)
bool emberAfIcdManagementClusterRegisterClientCallback(chip::app::CommandHandler * commandObj,
const chip::app::ConcreteCommandPath & commandPath,
const Commands::RegisterClient::DecodableType & commandData)
{
IcdManagementServer server;

InteractionModel::Status status = server.RegisterClient(commandObj, commandPath, commandData);
if (InteractionModel::Status::Success == status)
{
// Response
IcdManagement::Commands::RegisterClientResponse::Type response;
if (EMBER_ZCL_STATUS_SUCCESS == Attributes::ICDCounter::Get(commandPath.mEndpointId, &response.ICDCounter))
{
commandObj->AddResponse(commandPath, response);
return true;
}
status = InteractionModel::Status::Failure;
}

// Error
commandObj->AddStatus(commandPath, status);
return true;
}

/**
* @brief ICD Management Cluster UnregisterClient Command callback (from client)
* @brief ICD Management Cluster UregisterClient Command callback (from client)
*
*/
bool emberAfIcdManagementClusterUnregisterClientCallback(
chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::Clusters::IcdManagement::Commands::UnregisterClient::DecodableType & commandData)
bool emberAfIcdManagementClusterUnregisterClientCallback(chip::app::CommandHandler * commandObj,
const chip::app::ConcreteCommandPath & commandPath,
const Commands::UnregisterClient::DecodableType & commandData)
{
IcdManagementServer server;
InteractionModel::Status status = server.UnregisterClient(commandObj, commandPath, commandData);
Expand All @@ -135,9 +275,9 @@ bool emberAfIcdManagementClusterUnregisterClientCallback(
/**
* @brief ICD Management Cluster StayActiveRequest Command callback (from client)
*/
bool emberAfIcdManagementClusterStayActiveRequestCallback(
chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::Clusters::IcdManagement::Commands::StayActiveRequest::DecodableType & commandData)
bool emberAfIcdManagementClusterStayActiveRequestCallback(chip::app::CommandHandler * commandObj,
const chip::app::ConcreteCommandPath & commandPath,
const Commands::StayActiveRequest::DecodableType & commandData)
{
IcdManagementServer server;
InteractionModel::Status status = server.StayActiveRequest(commandPath);
Expand Down
33 changes: 13 additions & 20 deletions src/app/tests/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,17 @@ source_set("binding-test-srcs") {
]
}

# source_set("client-monitoring-test-srcs") {
# sources = [
# "${chip_root}/src/app/util/ClientMonitoringRegistrationTable.cpp",
# "${chip_root}/src/app/util/ClientMonitoringRegistrationTable.h",
# ]

# public_deps = [
# "${chip_root}/src/app/common:cluster-objects",
# "${chip_root}/src/lib/core",
# ]
# }
source_set("icd-management-test-srcs") {
sources = [
"${chip_root}/src/app/util/IcdMonitoringTable.cpp",
"${chip_root}/src/app/util/IcdMonitoringTable.h",
]

public_deps = [
"${chip_root}/src/app/common:cluster-objects",
"${chip_root}/src/lib/core",
]
}

source_set("ota-requestor-test-srcs") {
sources = [
Expand Down Expand Up @@ -107,10 +107,6 @@ chip_test_suite("tests") {
"TestAttributeValueEncoder.cpp",
"TestBindingTable.cpp",
"TestBuilderParser.cpp",

# Disable CM cluster table tests until update is done
# https://github.com/project-chip/connectedhomeip/issues/24425
# "TestClientMonitoringRegistrationTable.cpp",
"TestClusterInfo.cpp",
"TestCommandInteraction.cpp",
"TestCommandPathParams.cpp",
Expand All @@ -122,6 +118,7 @@ chip_test_suite("tests") {
"TestExtensionFieldSets.cpp",
"TestFabricScopedEventLogging.cpp",
"TestICDManager.cpp",
"TestIcdMonitoringTable.cpp",
"TestInteractionModelEngine.cpp",
"TestMessageDef.cpp",
"TestNumericAttributeTraits.cpp",
Expand Down Expand Up @@ -163,15 +160,11 @@ chip_test_suite("tests") {

public_deps = [
":binding-test-srcs",

# Disable CM cluster table tests until update is done
#https://github.com/project-chip/connectedhomeip/issues/24425
# ":client-monitoring-test-srcs",
":icd-management-test-srcs",
":ota-requestor-test-srcs",
":scenes-table-test-srcs",
"${chip_root}/src/app",
"${chip_root}/src/app/common:cluster-objects",
"${chip_root}/src/app/icd:manager-srcs",
"${chip_root}/src/app/tests:helpers",
"${chip_root}/src/app/util/mock:mock_ember",
"${chip_root}/src/lib/core",
Expand Down
Loading

0 comments on commit 63c9625

Please sign in to comment.