From 135dab9d10678a4e5790803347c4e047a5a12b66 Mon Sep 17 00:00:00 2001 From: Junior Martinez Date: Thu, 1 Feb 2024 14:25:21 +0000 Subject: [PATCH] Pull request #1525: Cherry-Pick Add handling for SetAliroReaderConfig and ClearAliroReaderConfig commands (#31700) Merge in WMN_TOOLS/matter from cherry-pick/dl_aliro to RC_2.3.0-1.3 Squashed commit of the following: commit 471bcc1d95bc35bb13a7e02e481d81ec9e4f00d7 Author: Nivi Sarkar <55898241+nivi-apple@users.noreply.github.com> Date: Tue Jan 30 16:37:46 2024 -0800 Add handling for SetAliroReaderConfig and ClearAliroReaderConfig commands (#31700) * Add handling for SetAliroReaderConfig and ClearAliroReaderConfig commands Add a delegate for the door lock to provide the attribute data for the aliro attributes * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Address review comments * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Addressed more review comments * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Add an AttributeNullabilityType enum instead of boolean to indicate if an attribute is nullable or not * Restyled by clang-format * Make AttributeNullabilityType enum private --------- Co-authored-by: Boris Zbarsky Co-authored-by: Restyled.io --- .../door-lock-server/door-lock-delegate.h | 133 ++++++++ .../door-lock-server-callback.cpp | 12 + .../door-lock-server/door-lock-server.cpp | 292 +++++++++++++++++- .../door-lock-server/door-lock-server.h | 123 +++++++- 4 files changed, 557 insertions(+), 3 deletions(-) create mode 100644 src/app/clusters/door-lock-server/door-lock-delegate.h diff --git a/src/app/clusters/door-lock-server/door-lock-delegate.h b/src/app/clusters/door-lock-server/door-lock-delegate.h new file mode 100644 index 00000000000000..8908926822aa02 --- /dev/null +++ b/src/app/clusters/door-lock-server/door-lock-delegate.h @@ -0,0 +1,133 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * + * 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 DoorLock { + +static constexpr size_t kAliroReaderVerificationKeySize = 65; + +static constexpr size_t kAliroReaderGroupIdentifierSize = 16; + +static constexpr size_t kAliroReaderGroupSubIdentifierSize = 16; + +static constexpr size_t kAliroGroupResolvingKeySize = 16; + +static constexpr size_t kAliroProtocolVersionSize = 2; + +static constexpr size_t kAliroSigningKeySize = 32; + +/** @brief + * Defines methods for implementing application-specific logic for the door lock cluster. + * It defines the interfaces that a door lock should implement to support Aliro provisioning attributes. + */ + +class Delegate +{ +public: + Delegate() = default; + + virtual ~Delegate() = default; + + /** + * @brief Get the Aliro verification key component of the Reader's key pair. + * + * @param[out] verificationKey The MutableByteSpan to copy the verification key into. On success, + * the callee must update the length to the length of the copied data. If the value of + * the attribute is null, the callee must set the MutableByteSpan to empty. + */ + virtual CHIP_ERROR GetAliroReaderVerificationKey(MutableByteSpan & verificationKey) = 0; + + /** + * @brief Get the Aliro Reader's group identifier + * + * @param[out] groupIdentifier The MutableByteSpan to copy the group identifier into. On success, + * the callee must update the length to the length of the copied data. If the value of + * the attribute is null, the callee must set the MutableByteSpan to empty. + */ + virtual CHIP_ERROR GetAliroReaderGroupIdentifier(MutableByteSpan & groupIdentifier) = 0; + + /** + * @brief Get the Aliro Reader's group subidentifier + * + * @param[out] groupSubIdentifier The MutableByteSpan to copy the group subidentifier into. On success, + * the callee must update the length to the length of the copied data. The MutableByteSpan + * must not be empty since the attribute is not nullable. + */ + virtual CHIP_ERROR GetAliroReaderGroupSubIdentifier(MutableByteSpan & groupSubIdentifier) = 0; + + /** + * @brief Get the Aliro expedited transaction supported protocol version at the given index. + * + * @param[in] index The index of the protocol version in the list. + * @param[out] protocolVersion The MutableByteSpan to copy the expedited transaction supported protocol version at the given + * index into. On success, the callee must update the length to the length of the copied data. + * @return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED if the index is out of range for the attribute list. + */ + virtual CHIP_ERROR GetAliroExpeditedTransactionSupportedProtocolVersionAtIndex(size_t index, + MutableByteSpan & protocolVersion) = 0; + + /** + * @brief Get the Reader's group resolving key. + * + * @param[out] groupResolvingKey The MutableByteSpan to copy the group resolving key into. On success, + * the callee must update the length to the length of the copied data. If the value of + * the attribute is null, the callee must set the MutableByteSpan to empty. + */ + virtual CHIP_ERROR GetAliroGroupResolvingKey(MutableByteSpan & groupResolvingKey) = 0; + + /** + * @brief Get the Aliro supported BLE UWB protocol version at the given index. + * + * @param[in] index The index of the protocol version in the list. + * @param[out] protocolVersion The MutableByteSpan to copy the supported BLE UWB protocol version at the given index into. + * On success, the callee must update the length to the length of the copied data. + * @return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED if the index is out of range for the attribute list. + */ + virtual CHIP_ERROR GetAliroSupportedBLEUWBProtocolVersionAtIndex(size_t index, MutableByteSpan & protocolVersion) = 0; + + /** + * @brief Get the Aliro BLE Advertising Version. + * + * @return The BLE Advertising Version. + */ + virtual uint8_t GetAliroBLEAdvertisingVersion() = 0; + + /** + * @brief Get the maximum number of Aliro credential issuer keys supported. + * + * @return The max number of Aliro credential issuer keys supported. + */ + virtual uint16_t GetNumberOfAliroCredentialIssuerKeysSupported() = 0; + + /** + * @brief Get the maximum number of Aliro endpoint keys supported. + * + * @return The max number of Aliro endpoint keys supported. + */ + virtual uint16_t GetNumberOfAliroEndpointKeysSupported() = 0; +}; + +} // namespace DoorLock +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/door-lock-server/door-lock-server-callback.cpp b/src/app/clusters/door-lock-server/door-lock-server-callback.cpp index a1644286c1af1a..64ab48232c4f40 100644 --- a/src/app/clusters/door-lock-server/door-lock-server-callback.cpp +++ b/src/app/clusters/door-lock-server/door-lock-server-callback.cpp @@ -243,3 +243,15 @@ emberAfPluginDoorLockGetFaceCredentialLengthConstraints(chip::EndpointId endpoin { return false; } + +bool __attribute__((weak)) +emberAfPluginDoorLockSetAliroReaderConfig(EndpointId endpointId, const ByteSpan & signingKey, const ByteSpan & verificationKey, + const ByteSpan & groupIdentifier, const Optional groupResolvingKey) +{ + return false; +} + +bool __attribute__((weak)) emberAfPluginDoorLockClearAliroReaderConfig(chip::EndpointId endpointId) +{ + return false; +} diff --git a/src/app/clusters/door-lock-server/door-lock-server.cpp b/src/app/clusters/door-lock-server/door-lock-server.cpp index b51a10e6f02744..5b975d5d88262c 100644 --- a/src/app/clusters/door-lock-server/door-lock-server.cpp +++ b/src/app/clusters/door-lock-server/door-lock-server.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -40,6 +41,7 @@ using namespace chip; using namespace chip::app; using namespace chip::app::DataModel; using namespace chip::app::Clusters::DoorLock; +using namespace chip::app::Clusters::DoorLock::Attributes; using chip::Protocols::InteractionModel::Status; static constexpr uint8_t DOOR_LOCK_SCHEDULE_MAX_HOUR = 23; @@ -47,6 +49,39 @@ static constexpr uint8_t DOOR_LOCK_SCHEDULE_MAX_MINUTE = 59; static constexpr uint32_t DOOR_LOCK_MAX_LOCK_TIMEOUT_SEC = MAX_INT32U_VALUE / MILLISECOND_TICKS_PER_SECOND; +static constexpr size_t kDoorLockDelegateTableSize = + EMBER_AF_DOOR_LOCK_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; + +static_assert(kDoorLockDelegateTableSize <= kEmberInvalidEndpointIndex, "Door Lock Delegate table size error"); + +namespace chip { +namespace app { +namespace Clusters { +namespace DoorLock { + +Delegate * gDelegateTable[kDoorLockDelegateTableSize] = { nullptr }; + +Delegate * GetDelegate(EndpointId endpoint) +{ + uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, DoorLock::Id, EMBER_AF_DOOR_LOCK_CLUSTER_SERVER_ENDPOINT_COUNT); + return (ep >= kDoorLockDelegateTableSize ? nullptr : gDelegateTable[ep]); +} + +void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate) +{ + uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, DoorLock::Id, EMBER_AF_DOOR_LOCK_CLUSTER_SERVER_ENDPOINT_COUNT); + // if endpoint is found + if (ep < ArraySize(gDelegateTable)) + { + gDelegateTable[ep] = delegate; + } +} + +} // namespace DoorLock +} // namespace Clusters +} // namespace app +} // namespace chip + DoorLockServer DoorLockServer::instance; class DoorLockClusterFabricDelegate : public chip::FabricTable::Delegate @@ -1569,9 +1604,33 @@ bool DoorLockServer::getMaxNumberOfCredentials(chip::EndpointId endpointId, Cred case CredentialTypeEnum::kFace: status = emberAfPluginDoorLockGetNumberOfFaceCredentialsSupported(endpointId, maxNumberOfCredentials); break; - case CredentialTypeEnum::kAliroCredentialIssuerKey: + case CredentialTypeEnum::kAliroCredentialIssuerKey: { + Delegate * delegate = GetDelegate(endpointId); + if (delegate == nullptr) + { + ChipLogError(Zcl, "Delegate is null"); + return false; + } + + maxNumberOfCredentials = delegate->GetNumberOfAliroCredentialIssuerKeysSupported(); + status = true; + break; + } case CredentialTypeEnum::kAliroEvictableEndpointKey: - case CredentialTypeEnum::kAliroNonEvictableEndpointKey: + case CredentialTypeEnum::kAliroNonEvictableEndpointKey: { + Delegate * delegate = GetDelegate(endpointId); + if (delegate == nullptr) + { + ChipLogError(Zcl, "Delegate is null"); + return false; + } + + // For AliroEvictableEndpointKey and AliroNonEvictableEndpointKey credential type, return the total + // number of endpoint keys supported. + maxNumberOfCredentials = delegate->GetNumberOfAliroEndpointKeysSupported(); + status = true; + break; + } default: return false; } @@ -3812,6 +3871,108 @@ bool emberAfDoorLockClusterClearHolidayScheduleCallback( return true; } +bool emberAfDoorLockClusterSetAliroReaderConfigCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::SetAliroReaderConfig::DecodableType & commandData) +{ + DoorLockServer::Instance().setAliroReaderConfigCommandHandler(commandObj, commandPath, commandData.signingKey, + commandData.verificationKey, commandData.groupIdentifier, + commandData.groupResolvingKey); + return true; +} + +bool emberAfDoorLockClusterClearAliroReaderConfigCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::ClearAliroReaderConfig::DecodableType & commandData) +{ + DoorLockServer::Instance().clearAliroReaderConfigCommandHandler(commandObj, commandPath); + return true; +} + +void DoorLockServer::setAliroReaderConfigCommandHandler(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const ByteSpan & signingKey, const ByteSpan & verificationKey, + const ByteSpan & groupIdentifier, + const Optional & groupResolvingKey) +{ + EndpointId endpointID = commandPath.mEndpointId; + ChipLogProgress(Zcl, "[SetAliroReaderConfig] Incoming command [endpointId=%d]", endpointID); + + // If Aliro Provisioning feature is not supported, return UNSUPPORTED_COMMAND. + if (!SupportsAliroProvisioning(endpointID)) + { + ChipLogProgress(Zcl, "[SetAliroReaderConfig] Aliro Provisioning is not supported [endpointId=%d]", endpointID); + commandObj->AddStatus(commandPath, Status::UnsupportedCommand); + return; + } + + Delegate * delegate = GetDelegate(endpointID); + VerifyOrReturn(delegate != nullptr, ChipLogError(Zcl, "Delegate is null")); + + // If Aliro BLE UWB feature is supported and groupResolvingKey is not provided in the command, return INVALID_COMMAND. + if (SupportsAliroBLEUWB(endpointID) && !groupResolvingKey.HasValue()) + { + ChipLogProgress(Zcl, "[SetAliroReaderConfig] Aliro BLE UWB supported but Group Resolving Key is not provided"); + commandObj->AddStatus(commandPath, Status::InvalidCommand); + return; + } + + // Check if the size of the signingKey, verificationKey, groupIdentifier, groupResolvingKey parameters conform to the spec. + // Return INVALID_COMMAND if not. + if (signingKey.size() != kAliroSigningKeySize || verificationKey.size() != kAliroReaderVerificationKeySize || + groupIdentifier.size() != kAliroReaderGroupIdentifierSize || + (groupResolvingKey.HasValue() && groupResolvingKey.Value().size() != kAliroGroupResolvingKeySize)) + { + ChipLogProgress(Zcl, + "[SetAliroReaderConfig] One or more parameters in the command do not meet the size constraint as per spec"); + commandObj->AddStatus(commandPath, Status::ConstraintError); + return; + } + + uint8_t buffer[kAliroReaderVerificationKeySize]; + MutableByteSpan readerVerificationKey(buffer); + + CHIP_ERROR err = delegate->GetAliroReaderVerificationKey(readerVerificationKey); + + // If Aliro reader verification key attribute was not read successfuly, return INVALID_IN_STATE. Or if the verification key was + // read and is not null (i.e not empty), we can't set a new reader config without clearing the previous one, return + // INVALID_IN_STATE. + if (err != CHIP_NO_ERROR || !readerVerificationKey.empty()) + { + ChipLogProgress( + Zcl, "[SetAliroReaderConfig] Aliro reader verification key was not read or is not null. Return INVALID_IN_STATE"); + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + + EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS; + if (!emberAfPluginDoorLockSetAliroReaderConfig(endpointID, signingKey, verificationKey, groupIdentifier, groupResolvingKey)) + { + ChipLogProgress(Zcl, "[SetAliroReaderConfig] Unable to set aliro reader config [endpointId=%d]", endpointID); + status = EMBER_ZCL_STATUS_FAILURE; + } + sendClusterResponse(commandObj, commandPath, status); +} + +void DoorLockServer::clearAliroReaderConfigCommandHandler(CommandHandler * commandObj, const ConcreteCommandPath & commandPath) +{ + EndpointId endpointID = commandPath.mEndpointId; + ChipLogProgress(Zcl, "[ClearAliroReaderConfig] Incoming command [endpointId=%d]", endpointID); + + // If Aliro Provisioning feature is not supported, return UNSUPPORTED_COMMAND. + if (!SupportsAliroProvisioning(endpointID)) + { + ChipLogProgress(Zcl, "[ClearAliroReaderConfig] Aliro Provisioning is not supported [endpointId=%d]", endpointID); + commandObj->AddStatus(commandPath, Status::UnsupportedCommand); + return; + } + + EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS; + if (!emberAfPluginDoorLockClearAliroReaderConfig(endpointID)) + { + ChipLogProgress(Zcl, "[SetAliroReaderConfig] Unable to set aliro reader config [endpointId=%d]", endpointID); + status = EMBER_ZCL_STATUS_FAILURE; + } + sendClusterResponse(commandObj, commandPath, status); +} + // ============================================================================= // SDK callbacks // ============================================================================= @@ -3932,6 +4093,8 @@ void MatterDoorLockPluginServerInitCallback() { ChipLogProgress(Zcl, "Door Lock server initialized"); Server::GetInstance().GetFabricTable().AddFabricDelegate(&gFabricDelegate); + + registerAttributeAccessOverride(&DoorLockServer::Instance()); } void MatterDoorLockClusterServerAttributeChangedCallback(const app::ConcreteAttributePath & attributePath) {} @@ -3966,3 +4129,128 @@ void DoorLockServer::DoorLockOnAutoRelockCallback(System::Layer *, void * callba ChipLogProgress(Zcl, "Door Auto relock timer expired. %s", "Already locked."); } } + +CHIP_ERROR DoorLockServer::ReadAliroExpeditedTransactionSupportedProtocolVersions(const ConcreteReadAttributePath & aPath, + AttributeValueEncoder & aEncoder, + Delegate * delegate) +{ + VerifyOrReturnValue(delegate != nullptr, aEncoder.EncodeEmptyList()); + + return aEncoder.EncodeList([delegate](const auto & encoder) -> CHIP_ERROR { + for (uint8_t i = 0; true; i++) + { + uint8_t buffer[kAliroProtocolVersionSize]; + MutableByteSpan protocolVersion(buffer); + auto err = delegate->GetAliroExpeditedTransactionSupportedProtocolVersionAtIndex(i, protocolVersion); + if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) + { + return CHIP_NO_ERROR; + } + ReturnErrorOnFailure(err); + ReturnErrorOnFailure(encoder.Encode(protocolVersion)); + } + }); +} + +CHIP_ERROR DoorLockServer::ReadAliroSupportedBLEUWBProtocolVersions(const ConcreteReadAttributePath & aPath, + AttributeValueEncoder & aEncoder, Delegate * delegate) +{ + VerifyOrReturnValue(delegate != nullptr, aEncoder.EncodeEmptyList()); + + return aEncoder.EncodeList([delegate](const auto & encoder) -> CHIP_ERROR { + for (uint8_t i = 0; true; i++) + { + uint8_t buffer[kAliroProtocolVersionSize]; + MutableByteSpan protocolVersion(buffer); + auto err = delegate->GetAliroSupportedBLEUWBProtocolVersionAtIndex(i, protocolVersion); + if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) + { + return CHIP_NO_ERROR; + } + ReturnErrorOnFailure(err); + ReturnErrorOnFailure(encoder.Encode(protocolVersion)); + } + }); +} + +CHIP_ERROR DoorLockServer::ReadAliroByteSpanAttribute(CHIP_ERROR (Delegate::*func)(MutableByteSpan &), MutableByteSpan & data, + Delegate * delegate, AttributeValueEncoder & aEncoder, + AttributeNullabilityType nullabilityType) +{ + VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null")); + + ReturnErrorOnFailure((delegate->*func)(data)); + if (nullabilityType == AttributeNullabilityType::kNullable && data.empty()) + { + ReturnErrorOnFailure(aEncoder.EncodeNull()); + } + else + { + ReturnErrorOnFailure(aEncoder.Encode(data)); + } + return CHIP_NO_ERROR; +} + +// Implements the read functionality for the AttributeAccessInterface. +CHIP_ERROR DoorLockServer::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + if (aPath.mClusterId != Clusters::DoorLock::Id) + { + // We shouldn't have been called at all. + return CHIP_ERROR_INVALID_ARGUMENT; + } + + Delegate * delegate = GetDelegate(aPath.mEndpointId); + + switch (aPath.mAttributeId) + { + case AliroReaderVerificationKey::Id: { + uint8_t buffer[kAliroReaderVerificationKeySize]; + MutableByteSpan readerVerificationKey(buffer); + return ReadAliroByteSpanAttribute(&Delegate::GetAliroReaderVerificationKey, readerVerificationKey, delegate, aEncoder, + AttributeNullabilityType::kNullable); + } + case AliroReaderGroupIdentifier::Id: { + uint8_t buffer[kAliroReaderGroupIdentifierSize]; + MutableByteSpan readerGroupIdentifier(buffer); + return ReadAliroByteSpanAttribute(&Delegate::GetAliroReaderGroupIdentifier, readerGroupIdentifier, delegate, aEncoder, + AttributeNullabilityType::kNullable); + } + case AliroReaderGroupSubIdentifier::Id: { + uint8_t buffer[kAliroReaderGroupSubIdentifierSize]; + MutableByteSpan readerGroupSubIdentifier(buffer); + return ReadAliroByteSpanAttribute(&Delegate::GetAliroReaderGroupSubIdentifier, readerGroupSubIdentifier, delegate, aEncoder, + AttributeNullabilityType::kNotNullable); + } + case AliroExpeditedTransactionSupportedProtocolVersions::Id: { + return ReadAliroExpeditedTransactionSupportedProtocolVersions(aPath, aEncoder, delegate); + } + case AliroGroupResolvingKey::Id: { + uint8_t buffer[kAliroGroupResolvingKeySize]; + MutableByteSpan groupResolvingKey(buffer); + return ReadAliroByteSpanAttribute(&Delegate::GetAliroGroupResolvingKey, groupResolvingKey, delegate, aEncoder, + AttributeNullabilityType::kNullable); + } + case AliroSupportedBLEUWBProtocolVersions::Id: { + return ReadAliroSupportedBLEUWBProtocolVersions(aPath, aEncoder, delegate); + } + case AliroBLEAdvertisingVersion::Id: { + uint8_t bleAdvertisingVersion = delegate->GetAliroBLEAdvertisingVersion(); + ReturnErrorOnFailure(aEncoder.Encode(bleAdvertisingVersion)); + return CHIP_NO_ERROR; + } + case NumberOfAliroCredentialIssuerKeysSupported::Id: { + uint16_t numberOfCredentialIssuerKeysSupported = delegate->GetNumberOfAliroCredentialIssuerKeysSupported(); + ReturnErrorOnFailure(aEncoder.Encode(numberOfCredentialIssuerKeysSupported)); + return CHIP_NO_ERROR; + } + case NumberOfAliroEndpointKeysSupported::Id: { + uint16_t numberOfEndpointKeysSupported = delegate->GetNumberOfAliroEndpointKeysSupported(); + ReturnErrorOnFailure(aEncoder.Encode(numberOfEndpointKeysSupported)); + return CHIP_NO_ERROR; + } + default: + break; + } + return CHIP_NO_ERROR; +} diff --git a/src/app/clusters/door-lock-server/door-lock-server.h b/src/app/clusters/door-lock-server/door-lock-server.h index a8007f211641a4..675a1450baf28a 100644 --- a/src/app/clusters/door-lock-server/door-lock-server.h +++ b/src/app/clusters/door-lock-server/door-lock-server.h @@ -24,7 +24,9 @@ #pragma once +#include "door-lock-delegate.h" #include +#include #include #include #include @@ -88,9 +90,10 @@ struct EmberAfDoorLockEndpointContext /** * @brief Door Lock Server Plugin class. */ -class DoorLockServer +class DoorLockServer : public chip::app::AttributeAccessInterface { public: + DoorLockServer() : AttributeAccessInterface(chip::Optional::Missing(), chip::app::Clusters::DoorLock::Id) {} static DoorLockServer & Instance(); using Feature = chip::app::Clusters::DoorLock::Feature; @@ -203,6 +206,23 @@ class DoorLockServer inline bool SupportsUnbolt(chip::EndpointId endpointId) { return GetFeatures(endpointId).Has(Feature::kUnbolt); } + /** + * @brief Checks if Aliro Provisioning feature is supported on the given endpoint + * + * @param endpointId endpointId ID of the endpoint which contains the lock. + */ + inline bool SupportsAliroProvisioning(chip::EndpointId endpointId) + { + return GetFeatures(endpointId).Has(Feature::kAliroProvisioning); + } + + /** + * @brief Checks if Aliro BLE UWB feature is supported on the given endpoint + * + * @param endpointId endpointId ID of the endpoint which contains the lock. + */ + inline bool SupportsAliroBLEUWB(chip::EndpointId endpointId) { return GetFeatures(endpointId).Has(Feature::kAliroBLEUWB); } + /** * @brief Allows the application to register a custom callback which will be called after the default DoorLock * OnFabricRemoved implementation. At that point the door lock cluster has done any @@ -430,6 +450,13 @@ class DoorLockServer void clearHolidayScheduleCommandHandler(chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, uint8_t holidayIndex); + void setAliroReaderConfigCommandHandler(chip::app::CommandHandler * commandObj, + const chip::app::ConcreteCommandPath & commandPath, const chip::ByteSpan & signingKey, + const chip::ByteSpan & verificationKey, const chip::ByteSpan & groupIdentifier, + const Optional & groupResolvingKey); + void clearAliroReaderConfigCommandHandler(chip::app::CommandHandler * commandObj, + const chip::app::ConcreteCommandPath & commandPath); + bool RemoteOperationEnabled(chip::EndpointId endpointId) const; EmberAfDoorLockEndpointContext * getContext(chip::EndpointId endpointId); @@ -521,6 +548,62 @@ class DoorLockServer bool SetAttribute(chip::EndpointId endpointId, chip::AttributeId attributeId, EmberAfStatus (*setFn)(chip::EndpointId endpointId, T value), T value); + // AttributeAccessInterface's Read API + CHIP_ERROR Read(const chip::app::ConcreteReadAttributePath & aPath, chip::app::AttributeValueEncoder & aEncoder) override; + + /** + * @brief Reads AliroExpeditedTransactionSupportedProtocolVersions attribute for door lock + * + * @param aPath attribute path. + * @param aEncoder attribute value encoder. + * @param delegate door lock cluster delegate that will provide the value + * + * @return CHIP_NO_ERROR on success + * @return CHIP_ERROR if attribute read failed + */ + CHIP_ERROR ReadAliroExpeditedTransactionSupportedProtocolVersions(const chip::app::ConcreteReadAttributePath & aPath, + chip::app::AttributeValueEncoder & aEncoder, + chip::app::Clusters::DoorLock::Delegate * delegate); + + /** + * @brief Reads AliroSupportedBLEUWBProtocolVersions attribute for door lock + * + * @param aPath attribute path. + * @param aEncoder attribute value encoder. + * @param delegate door lock cluster delegate that will provide the value + * + * @return CHIP_NO_ERROR on success + * @return CHIP_ERROR if attribute read failed + */ + CHIP_ERROR ReadAliroSupportedBLEUWBProtocolVersions(const chip::app::ConcreteReadAttributePath & aPath, + chip::app::AttributeValueEncoder & aEncoder, + chip::app::Clusters::DoorLock::Delegate * delegate); + + /** + * @brief Indicates whether an attribute can be nullable or not. + */ + enum class AttributeNullabilityType : uint8_t + { + kNullable = 0, /**< Indicates if an attribute is nullable */ + kNotNullable = 1, /**< Indicates if an attribute is not nullable */ + }; + + /** + * @brief Utility to read aliro attributes of type ByteSpan + * + * @param func getter function for the attribute. + * @param data buffer for the data. + * @param delegate door lock cluster delegate that will provide the value + * @param aEncoder attribute value encoder. + * @param nullabilityType enum value indicating whether the attribute is nullable or not. + * + * @return CHIP_NO_ERROR on success + * @return CHIP_ERROR if attribute read failed + */ + CHIP_ERROR ReadAliroByteSpanAttribute(CHIP_ERROR (chip::app::Clusters::DoorLock::Delegate::*func)(chip::MutableByteSpan & data), + chip::MutableByteSpan & data, chip::app::Clusters::DoorLock::Delegate * delegate, + chip::app::AttributeValueEncoder & aEncoder, AttributeNullabilityType nullabilityType); + friend bool emberAfDoorLockClusterLockDoorCallback(chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, @@ -601,6 +684,14 @@ class DoorLockServer chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, const chip::app::Clusters::DoorLock::Commands::ClearYearDaySchedule::DecodableType & commandData); + friend bool emberAfDoorLockClusterSetAliroReaderConfigCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::DoorLock::Commands::SetAliroReaderConfig::DecodableType & commandData); + + friend bool emberAfDoorLockClusterClearAliroReaderConfigCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::DoorLock::Commands::ClearAliroReaderConfig::DecodableType & commandData); + static constexpr size_t kDoorLockClusterServerMaxEndpointCount = EMBER_AF_DOOR_LOCK_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; static_assert(kDoorLockClusterServerMaxEndpointCount <= kEmberInvalidEndpointIndex, "DoorLock Endpoint count error"); @@ -1167,3 +1258,33 @@ bool emberAfPluginDoorLockGetFingerVeinCredentialLengthConstraints(chip::Endpoin * @return false on failure, true on success. */ bool emberAfPluginDoorLockGetFaceCredentialLengthConstraints(chip::EndpointId endpointId, uint8_t & minLen, uint8_t & maxLen); + +/** + * @brief This callback is called when Door Lock cluster needs to communicate the Aliro reader configuration to the door lock. + * + * @note This function is used for communicating the Aliro signing key, verification key, group identifier and group resolving key + * to the lock. + * + * @param endpointId ID of the endpoint which contains the lock. + * @param[in] signingKey Signing key component of the Reader's key pair. + * @param[in] verificationKey Verification key component of the Reader's key pair. + * @param[in] groupIdentifier Reader group identifier for the lock. + * @param[in] groupResolvingKey Group resolving key for the lock if Aliro BLE UWB feature is supported + * + * @retval true, if the Aliro reader config was successfully communicated to the door lock. + * @retval false, if error occurred while communicating the Aliro reader config. + */ +bool emberAfPluginDoorLockSetAliroReaderConfig(chip::EndpointId endpointId, const chip::ByteSpan & signingKey, + const chip::ByteSpan & verificationKey, const chip::ByteSpan & groupIdentifier, + const Optional & groupResolvingKey); + +/** + * @brief This callback is called when Door Lock cluster needs to clear an existing Aliro reader configuration from the door lock. + * + * + * @param endpointId ID of the endpoint which contains the lock. + * + * @retval true, if the Aliro reader config was successfully cleared from the door lock. + * @retval false, if error occurred while clearing the Aliro reader config. + */ +bool emberAfPluginDoorLockClearAliroReaderConfig(chip::EndpointId endpointId);