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..7742e10f4c3a47 --- /dev/null +++ b/src/app/clusters/door-lock-server/door-lock-delegate.h @@ -0,0 +1,121 @@ +/* + * + * 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 kAliroReaderVerificationKeyMaxSize = 65; + +static constexpr size_t kAliroAttributeMaxSize_16 = 16; + +static constexpr size_t kProtocolVersionMaxSize = 2; + +/** @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. + */ + virtual CHIP_ERROR GetAliroReaderVerificationKey(MutableByteSpan & aliroVerificationKey) = 0; + + /** + * @brief Get the 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. + */ + virtual CHIP_ERROR GetAliroReaderGroupIdentifier(MutableByteSpan & aliroGroupIdentifier) = 0; + + /** + * @brief Get the Reader's subgroup identifier + * + * @param[out] groupIdentifier The MutableByteSpan to copy the group subidentifier into. On success, + * the callee must update the length to the length of the copied data. + */ + virtual CHIP_ERROR GetAliroReaderGroupSubIdentifier(MutableByteSpan & aliroGroupSubIdentifier) = 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. + */ + virtual CHIP_ERROR GetAliroExpeditedTransactionSupportedProtocolVersionAtIndex(size_t index, + MutableByteSpan & protocolVersion) = 0; + + /** + * @brief Get the Reader's group resolving key. + * + * @param[out] aliroGroupResolvingKey The MutableByteSpan to copy the aliro group resolving key into. On success, + * the callee must update the length to the length of the copied data. + */ + virtual CHIP_ERROR GetAliroGroupResolvingKey(MutableByteSpan & aliroGroupResolvingKey) = 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. + */ + virtual CHIP_ERROR GetAliroSupportedBLEUWBProtocolVersionAtIndex(size_t index, MutableByteSpan & protocolVersion) = 0; + + /** + * @brief Get the Aliro BLE Advertising Version. + * + * @param[out] aliroBLEAdvertisingVersion The BLE Advertising Version. + */ + virtual CHIP_ERROR GetAliroBLEAdvertisingVersion(uint8_t & aliroBLEAdvertisingVersion) = 0; + + /** + * @brief Get the maximum number of Aliro credential issuer keys supported. + * + * @param[out] numberOfAliroCredentialIssuerKeysSupported The number of credential issuer keys. + */ + virtual CHIP_ERROR GetNumberOfAliroCredentialIssuerKeysSupported(uint16_t & numberOfAliroCredentialIssuerKeysSupported) = 0; + + /** + * @brief Get the maximum number of Aliro endpoint keys supported. + * + * @param[out] numberOfAliroEndpointKeysSupported The number of endpoint keys. + */ + virtual CHIP_ERROR GetNumberOfAliroEndpointKeysSupported(uint16_t & numberOfAliroEndpointKeysSupported) = 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..f77ef9ea283e7b 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,36 @@ emberAfPluginDoorLockGetFaceCredentialLengthConstraints(chip::EndpointId endpoin { return false; } + +bool __attribute__((weak)) +emberAfPluginDoorLockGetNumberOfAliroCredentialIssuerKeyCredentialsSupported(chip::EndpointId endpointId, + uint16_t & maxNumberOfCredentials) +{ + return false; +} + +bool __attribute__((weak)) +emberAfPluginDoorLockGetNumberOfAliroEvictableEndpointKeyCredentialsSupported(chip::EndpointId endpointId, + uint16_t & maxNumberOfCredentials) +{ + return false; +} + +bool __attribute__((weak)) +emberAfPluginDoorLockGetNumberOfAliroNonEvictableEndpointKeyCredentialsSupported(chip::EndpointId endpointId, + uint16_t & maxNumberOfCredentials) +{ + return false; +} + +bool __attribute__((weak)) +emberAfPluginDoorLockSetAliroReaderConfig(EndpointId endpointId, const ByteSpan & signingKey, ByteSpan & verificationKey, + ByteSpan & groupIdentifier, 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..e7d087e542a433 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 < kDoorLockDelegateTableSize) + { + gDelegateTable[ep] = delegate; + } +} + +} // namespace DoorLock +} // namespace Clusters +} // namespace app +} // namespace chip + DoorLockServer DoorLockServer::instance; class DoorLockClusterFabricDelegate : public chip::FabricTable::Delegate @@ -1570,8 +1605,15 @@ bool DoorLockServer::getMaxNumberOfCredentials(chip::EndpointId endpointId, Cred status = emberAfPluginDoorLockGetNumberOfFaceCredentialsSupported(endpointId, maxNumberOfCredentials); break; case CredentialTypeEnum::kAliroCredentialIssuerKey: + status = emberAfPluginDoorLockGetNumberOfAliroCredentialIssuerKeyCredentialsSupported(endpointId, maxNumberOfCredentials); + break; case CredentialTypeEnum::kAliroEvictableEndpointKey: + status = emberAfPluginDoorLockGetNumberOfAliroEvictableEndpointKeyCredentialsSupported(endpointId, maxNumberOfCredentials); + break; case CredentialTypeEnum::kAliroNonEvictableEndpointKey: + status = + emberAfPluginDoorLockGetNumberOfAliroNonEvictableEndpointKeyCredentialsSupported(endpointId, maxNumberOfCredentials); + break; default: return false; } @@ -3812,6 +3854,110 @@ bool emberAfDoorLockClusterClearHolidayScheduleCallback( return true; } +bool emberAfDoorLockClusterSetAliroReaderConfigCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::DoorLock::Commands::SetAliroReaderConfig::DecodableType & commandData) +{ + DoorLockServer::Instance().setAliroReaderConfigCommandHandler(commandObj, commandPath, commandData.signingKey, + commandData.verificationKey, commandData.groupIdentifier, + commandData.groupResolvingKey); + return true; +} + +bool emberAfDoorLockClusterClearAliroReaderConfigCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::DoorLock::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 nullptr")); + + uint8_t buffer[kAliroReaderVerificationKeyMaxSize]; + MutableByteSpan aliroReaderVerificationKey(buffer); + + // If Aliro reader verification key attribute is not null, we can't set a new reader config without clearing the previous one, + // return INVALID_IN_STATE. + if (CHIP_NO_ERROR == delegate->GetAliroReaderVerificationKey(aliroReaderVerificationKey) && + aliroReaderVerificationKey.data() != nullptr) + { + ChipLogProgress(Zcl, "[SetAliroReaderConfig] Aliro reader verification key is not null. Return INVALID_IN_STATE"); + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + + // 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; + } + + 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; + } + + Delegate * delegate = GetDelegate(endpointID); + VerifyOrReturn(delegate != nullptr, ChipLogError(Zcl, "Delegate is nullptr")); + + uint8_t buffer[kAliroReaderVerificationKeyMaxSize]; + MutableByteSpan aliroReaderVerificationKey(buffer); + + // If Aliro reader verification key attribute is null, there is no reader config to clear. Return INVALID_IN_STATE. + if (CHIP_NO_ERROR != delegate->GetAliroReaderVerificationKey(aliroReaderVerificationKey) && + aliroReaderVerificationKey.data() == nullptr) + { + ChipLogProgress(Zcl, "[ClearAliroReaderConfig] Aliro reader verification key is null. Return INVALID_IN_STATE"); + commandObj->AddStatus(commandPath, Status::InvalidInState); + 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 +4078,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 +4114,120 @@ 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) +{ + VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is nullptr")); + + return aEncoder.EncodeList([delegate](const auto & encoder) -> CHIP_ERROR { + for (uint8_t i = 0; true; i++) + { + uint8_t buffer[kProtocolVersionMaxSize]; + 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) +{ + VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is nullptr")); + + return aEncoder.EncodeList([delegate](const auto & encoder) -> CHIP_ERROR { + for (uint8_t i = 0; true; i++) + { + uint8_t buffer[kProtocolVersionMaxSize]; + 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) +{ + VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is nullptr")); + + ReturnErrorOnFailure((delegate->*func)(data)); + 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[kAliroReaderVerificationKeyMaxSize]; + MutableByteSpan aliroReaderVerificationKey(buffer); + return ReadAliroByteSpanAttribute(&Delegate::GetAliroReaderVerificationKey, aliroReaderVerificationKey, delegate, aEncoder); + } + case AliroReaderGroupIdentifier::Id: { + uint8_t buffer[kAliroAttributeMaxSize_16]; + MutableByteSpan aliroReaderGroupIdentifier(buffer); + return ReadAliroByteSpanAttribute(&Delegate::GetAliroReaderGroupIdentifier, aliroReaderGroupIdentifier, delegate, aEncoder); + } + case AliroReaderGroupSubIdentifier::Id: { + uint8_t buffer[kAliroAttributeMaxSize_16]; + MutableByteSpan aliroReaderGroupSubIdentifier(buffer); + return ReadAliroByteSpanAttribute(&Delegate::GetAliroReaderGroupSubIdentifier, aliroReaderGroupSubIdentifier, delegate, + aEncoder); + } + case AliroExpeditedTransactionSupportedProtocolVersions::Id: { + return ReadAliroExpeditedTransactionSupportedProtocolVersions(aPath, aEncoder, delegate); + } + case AliroGroupResolvingKey::Id: { + uint8_t buffer[kAliroAttributeMaxSize_16]; + MutableByteSpan aliroGroupResolvingKey(buffer); + return ReadAliroByteSpanAttribute(&Delegate::GetAliroGroupResolvingKey, aliroGroupResolvingKey, delegate, aEncoder); + } + case AliroSupportedBLEUWBProtocolVersions::Id: { + return ReadAliroSupportedBLEUWBProtocolVersions(aPath, aEncoder, delegate); + } + case AliroBLEAdvertisingVersion::Id: { + uint8_t aliroBLEAdvertisingVersion; + ReturnErrorOnFailure(delegate->GetAliroBLEAdvertisingVersion(aliroBLEAdvertisingVersion)); + ReturnErrorOnFailure(aEncoder.Encode(aliroBLEAdvertisingVersion)); + return CHIP_NO_ERROR; + } + case NumberOfAliroCredentialIssuerKeysSupported::Id: { + uint16_t numberOfAliroCredentialIssuerKeysSupported; + ReturnErrorOnFailure(delegate->GetNumberOfAliroCredentialIssuerKeysSupported(numberOfAliroCredentialIssuerKeysSupported)); + ReturnErrorOnFailure(aEncoder.Encode(numberOfAliroCredentialIssuerKeysSupported)); + return CHIP_NO_ERROR; + } + case NumberOfAliroEndpointKeysSupported::Id: { + uint16_t numberOfAliroEndpointKeysSupported; + ReturnErrorOnFailure(delegate->GetNumberOfAliroCredentialIssuerKeysSupported(numberOfAliroEndpointKeysSupported)); + ReturnErrorOnFailure(aEncoder.Encode(numberOfAliroEndpointKeysSupported)); + 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..5565551bfc3d66 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 @@ -76,6 +78,8 @@ static constexpr size_t DOOR_LOCK_MAX_USER_NAME_SIZE = 10; /**< Maximum size of static constexpr size_t DOOR_LOCK_USER_NAME_BUFFER_SIZE = DOOR_LOCK_MAX_USER_NAME_SIZE + 1; /**< Maximum size of the user name string (in bytes). */ +static constexpr size_t DOOR_LOCK_MAX_ALIRO_VERIFICATION_KEY_SIZE = 65; /**< Maximum size of the aliro verification key. */ + struct EmberAfPluginDoorLockCredentialInfo; struct EmberAfPluginDoorLockUserInfo; @@ -88,9 +92,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 +208,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 +452,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 +550,60 @@ class DoorLockServer bool SetAttribute(chip::EndpointId endpointId, chip::AttributeId attributeId, EmberAfStatus (*setFn)(chip::EndpointId endpointId, T value), T value); + /** + * @brief Reads non generic attributes for door lock. + * + * @param aPath attribute path. + * @param aEncoder attribute value encoder. + * + * @return CHIP_NO_ERROR on success + * @return CHIP_ERROR if attribute read failed + */ + CHIP_ERROR Read(const chip::app::ConcreteReadAttributePath & aPath, chip::app::AttributeValueEncoder & aEncoder); + + /** + * @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 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. + * + * @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); + 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,76 @@ 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 the Door Lock server needs to find out + * the number of Aliro credential issuer keys supported, since there is no attribute + * that represents that value. + * + * @param[in] endpointId ID of the endpoint that contains the door lock. + * @param[out] maxNumberOfCredentials the number of Aliro credential issuer keys supported by the lock. + * + * @return false on failure, true on success. On failure, the cluster + * implementation will assume that 0 Aliro credential issuer keys are supported. + */ +bool emberAfPluginDoorLockGetNumberOfAliroCredentialIssuerKeyCredentialsSupported(chip::EndpointId endpointId, + uint16_t & maxNumberOfCredentials); + +/** + * @brief This callback is called when the Door Lock server needs to find out + * the number of Aliro evictable endpoint key credentials supported, since there is no attribute + * that represents that value. + * + * @param[in] endpointId ID of the endpoint that contains the door lock. + * @param[out] maxNumberOfCredentials the number of Aliro evictable endpoint key credentials supported by the lock. + * + * @return false on failure, true on success. On failure, the cluster + * implementation will assume that 0 Aliro evictable endpoint key credentials are supported. + */ +bool emberAfPluginDoorLockGetNumberOfAliroEvictableEndpointKeyCredentialsSupported(chip::EndpointId endpointId, + uint16_t & maxNumberOfCredentials); + +/** + * @brief This callback is called when the Door Lock server needs to find out + * the number of Aliro non-evictable endpoint key credentials supported, since there is no attribute + * that represents that value. + * + * @param[in] endpointId ID of the endpoint that contains the door lock. + * @param[out] maxNumberOfCredentials the number of Aliro non-evictable endpoint key credentials supported by the lock. + * + * @return false on failure, true on success. On failure, the cluster + * implementation will assume that 0 Aliro non-evictable endpoint key credentials are supported. + */ +bool emberAfPluginDoorLockGetNumberOfAliroNonEvictableEndpointKeyCredentialsSupported(chip::EndpointId endpointId, + uint16_t & maxNumberOfCredentials); + +/** + * @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. + * + * @note This function is used for clearing the existing Aliro reader configuration. + * + * @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);