diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst index 9f2ab11b2af0..5abee4d5404c 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -563,6 +563,7 @@ Matter samples * :ref:`matter_lock_sample` sample: * Removed support for nRF54H20 devices. + * Updated the API of ``AppTask``, ``BoltLockManager``, and ``AccessManager`` to provide additional information for the ``LockOperation`` event. Networking samples ------------------ diff --git a/samples/matter/lock/src/access/access_manager.cpp b/samples/matter/lock/src/access/access_manager.cpp index f8d5ca29fac6..8e4bbab678e9 100644 --- a/samples/matter/lock/src/access/access_manager.cpp +++ b/samples/matter/lock/src/access/access_manager.cpp @@ -18,8 +18,8 @@ using namespace DoorLockData; template void AccessManager::Init(SetCredentialCallback setCredentialClbk, - ClearCredentialCallback clearCredentialClbk, - ValidateCredentialCallback validateCredentialClbk) + ClearCredentialCallback clearCredentialClbk, + ValidateCredentialCallback validateCredentialClbk) { InitializeAllCredentials(); mSetCredentialCallback = setCredentialClbk; @@ -54,7 +54,8 @@ bool AccessManager::ValidateCustom(CredentialTypeEnum type, chip: } template -bool AccessManager::ValidatePIN(const Optional &pinCode, OperationErrorEnum &err) +bool AccessManager::ValidatePIN(const Optional &pinCode, OperationErrorEnum &err, + Nullable &result) { /* Optionality of the PIN code is validated by the caller, so assume it is OK not to provide the PIN * code. */ @@ -66,17 +67,34 @@ bool AccessManager::ValidatePIN(const Optional &pinCode /* Check the PIN code */ for (size_t index = 1; index <= CONFIG_LOCK_MAX_NUM_CREDENTIALS_PER_TYPE; ++index) { - if (CHIP_NO_ERROR == mCredentials.GetCredentials(CredentialTypeEnum::kPin, credential, index)) { - if (credential.status == DlCredentialStatus::kAvailable) { - continue; - } - - if (credential.credentialData.data_equal(pinCode.Value())) { - LOG_DBG("Valid lock PIN code provided"); - return true; - } + if (CHIP_NO_ERROR != mCredentials.GetCredentials(CredentialTypeEnum::kPin, credential, index)) { + err = OperationErrorEnum::kInvalidCredential; + continue; } - err = OperationErrorEnum::kInvalidCredential; + + if (credential.status == DlCredentialStatus::kAvailable) { + continue; + } + + if (!credential.credentialData.data_equal(pinCode.Value())) { + err = OperationErrorEnum::kInvalidCredential; + continue; + } + + uint32_t credentialUserId; + if (GetCredentialUserId(index, CredentialTypeEnum::kPin, credentialUserId) == CHIP_NO_ERROR) { + result = ValidatePINResult{ + .mUserId = static_cast(credentialUserId), + .mCredential = + LockOpCredentials{ CredentialTypeEnum::kPin, static_cast(index) }, + }; + } else { + result = {}; + } + + LOG_DBG("Valid lock PIN code provided"); + err = OperationErrorEnum::kUnspecified; + return true; } LOG_DBG("Invalid lock PIN code provided"); return false; @@ -134,9 +152,8 @@ template void AccessManager::SetR { if (mRequirePINForRemoteOperation != require) { mRequirePINForRemoteOperation = require; - if (!AccessStorage::Instance().Store(AccessStorage::Type::RequirePIN, - &mRequirePINForRemoteOperation, - sizeof(mRequirePINForRemoteOperation))) { + if (!AccessStorage::Instance().Store(AccessStorage::Type::RequirePIN, &mRequirePINForRemoteOperation, + sizeof(mRequirePINForRemoteOperation))) { LOG_ERR("Cannot store RequirePINforRemoteOperation."); } } @@ -148,4 +165,4 @@ template class AccessManager; template class AccessManager; template class AccessManager; template class AccessManager; + DoorLockData::FACE>; diff --git a/samples/matter/lock/src/access/access_manager.h b/samples/matter/lock/src/access/access_manager.h index 43bcd9d69a52..882f572bcbb4 100644 --- a/samples/matter/lock/src/access/access_manager.h +++ b/samples/matter/lock/src/access/access_manager.h @@ -13,6 +13,11 @@ template class AccessManager { public: + struct ValidatePINResult { + uint16_t mUserId; + LockOpCredentials mCredential; + }; + /** * @brief Signature of the callback fired when the credential is set. * @@ -234,9 +239,11 @@ template class AccessManager { * * @param pinCode PIN code data. * @param err specific error code enumeration. + * @param result user and credential data set on success. * @return true on success, false otherwise. */ - bool ValidatePIN(const Optional &pinCode, OperationErrorEnum &err); + bool ValidatePIN(const Optional &pinCode, OperationErrorEnum &err, + Nullable &result); bool ValidateCustom(CredentialTypeEnum type, chip::MutableByteSpan &secret); /** diff --git a/samples/matter/lock/src/app_task.cpp b/samples/matter/lock/src/app_task.cpp index b477e489f95f..48e84719bcc9 100644 --- a/samples/matter/lock/src/app_task.cpp +++ b/samples/matter/lock/src/app_task.cpp @@ -130,9 +130,9 @@ void AppTask::SwitchTransportTriggerHandler(const SwitchButtonAction &action) } #endif -void AppTask::LockStateChanged(BoltLockManager::State state, BoltLockManager::OperationSource source) +void AppTask::LockStateChanged(const BoltLockManager::StateData &stateData) { - switch (state) { + switch (stateData.mState) { case BoltLockManager::State::kLockingInitiated: LOG_INF("Lock action initiated"); Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).Blink(50, 50); @@ -164,14 +164,36 @@ void AppTask::LockStateChanged(BoltLockManager::State state, BoltLockManager::Op } /* Handle changing attribute state in the application */ - Instance().UpdateClusterState(state, source); + Instance().UpdateClusterState(stateData); } -void AppTask::UpdateClusterState(BoltLockManager::State state, BoltLockManager::OperationSource source) +void AppTask::UpdateClusterState(const BoltLockManager::StateData &stateData) { + BoltLockManager::StateData *stateDataCopy = Platform::New(stateData); + + if (stateDataCopy == nullptr) { + LOG_ERR("Failed to allocate memory for BoltLockManager::StateData"); + return; + } + + CHIP_ERROR err = SystemLayer().ScheduleLambda([stateDataCopy]() { + UpdateClusterStateHandler(*stateDataCopy); + Platform::Delete(stateDataCopy); + }); + + if (err != CHIP_NO_ERROR) { + LOG_ERR("Failed to schedule lambda: %" CHIP_ERROR_FORMAT, err.Format()); + Platform::Delete(stateDataCopy); + } +} + +void AppTask::UpdateClusterStateHandler(const BoltLockManager::StateData &stateData) +{ + using namespace chip::app::Clusters::DoorLock::Attributes; + DlLockState newLockState; - switch (state) { + switch (stateData.mState) { case BoltLockManager::State::kLockingCompleted: newLockState = DlLockState::kLocked; break; @@ -183,29 +205,43 @@ void AppTask::UpdateClusterState(BoltLockManager::State state, BoltLockManager:: break; } - SystemLayer().ScheduleLambda([newLockState, source] { - chip::app::DataModel::Nullable currentLockState; - chip::app::Clusters::DoorLock::Attributes::LockState::Get(kLockEndpointId, currentLockState); + Nullable currentLockState; + LockState::Get(kLockEndpointId, currentLockState); + + if (currentLockState.IsNull()) { + /* Initialize lock state with start value, but not invoke lock/unlock. */ + LockState::Set(kLockEndpointId, newLockState); + } else { + LOG_INF("Updating LockState attribute"); + + Nullable userId; + Nullable> credentials; + List credentialList; - if (currentLockState.IsNull()) { - /* Initialize lock state with start value, but not invoke lock/unlock. */ - chip::app::Clusters::DoorLock::Attributes::LockState::Set(kLockEndpointId, newLockState); - } else { - LOG_INF("Updating LockState attribute"); + if (!stateData.mValidatePINResult.IsNull()) { + userId = { stateData.mValidatePINResult.Value().mUserId }; - if (!DoorLockServer::Instance().SetLockState(kLockEndpointId, newLockState, source)) { - LOG_ERR("Failed to update LockState attribute"); - } + /* `DoorLockServer::SetLockState` exptects list of `LockOpCredentials`, + however in case of PIN validation it makes no sense to have more than one + credential corresponding to validation result. For simplicity we wrap single + credential in list here. */ + credentialList = { &stateData.mValidatePINResult.Value().mCredential, 1 }; + credentials = { credentialList }; } - }); + + if (!DoorLockServer::Instance().SetLockState(kLockEndpointId, newLockState, stateData.mSource, userId, + credentials, stateData.mFabricIdx, stateData.mNodeId)) { + LOG_ERR("Failed to update LockState attribute"); + } + } } #ifdef CONFIG_CHIP_NUS void AppTask::NUSLockCallback(void *context) { LOG_DBG("Received LOCK command from NUS"); - if (BoltLockMgr().mState == BoltLockManager::State::kLockingCompleted || - BoltLockMgr().mState == BoltLockManager::State::kLockingInitiated) { + if (BoltLockMgr().GetState().mState == BoltLockManager::State::kLockingCompleted || + BoltLockMgr().GetState().mState == BoltLockManager::State::kLockingInitiated) { LOG_INF("Device is already locked"); } else { Nrf::PostTask([] { LockActionEventHandler(); }); @@ -215,8 +251,8 @@ void AppTask::NUSLockCallback(void *context) void AppTask::NUSUnlockCallback(void *context) { LOG_DBG("Received UNLOCK command from NUS"); - if (BoltLockMgr().mState == BoltLockManager::State::kUnlockingCompleted || - BoltLockMgr().mState == BoltLockManager::State::kUnlockingInitiated) { + if (BoltLockMgr().GetState().mState == BoltLockManager::State::kUnlockingCompleted || + BoltLockMgr().GetState().mState == BoltLockManager::State::kUnlockingInitiated) { LOG_INF("Device is already unlocked"); } else { Nrf::PostTask([] { LockActionEventHandler(); }); diff --git a/samples/matter/lock/src/app_task.h b/samples/matter/lock/src/app_task.h index 5e835e945a7f..41602c26f67a 100644 --- a/samples/matter/lock/src/app_task.h +++ b/samples/matter/lock/src/app_task.h @@ -31,7 +31,7 @@ class AppTask { CHIP_ERROR StartApp(); - void UpdateClusterState(BoltLockManager::State state, BoltLockManager::OperationSource source); + void UpdateClusterState(const BoltLockManager::StateData &stateData); static void IdentifyStartHandler(Identify *); static void IdentifyStopHandler(Identify *); @@ -40,7 +40,8 @@ class AppTask { static void LockActionEventHandler(); static void ButtonEventHandler(Nrf::ButtonState state, Nrf::ButtonMask hasChanged); - static void LockStateChanged(BoltLockManager::State state, BoltLockManager::OperationSource source); + static void LockStateChanged(const BoltLockManager::StateData &stateData); + static void UpdateClusterStateHandler(const BoltLockManager::StateData &stateData); #ifdef CONFIG_THREAD_WIFI_SWITCHING static void SwitchTransportEventHandler(); @@ -54,7 +55,8 @@ class AppTask { #endif #ifdef CONFIG_NCS_SAMPLE_MATTER_TEST_EVENT_TRIGGERS - constexpr static Nrf::Matter::TestEventTrigger::EventTriggerId kDoorLockJammedEventTriggerId = 0xFFFF'FFFF'3277'4000; + constexpr static Nrf::Matter::TestEventTrigger::EventTriggerId kDoorLockJammedEventTriggerId = + 0xFFFF'FFFF'3277'4000; static CHIP_ERROR DoorLockJammedEventCallback(Nrf::Matter::TestEventTrigger::TriggerValue); #endif }; diff --git a/samples/matter/lock/src/bolt_lock_manager.cpp b/samples/matter/lock/src/bolt_lock_manager.cpp index 76dac2406448..0ebda2f9ff8f 100644 --- a/samples/matter/lock/src/bolt_lock_manager.cpp +++ b/samples/matter/lock/src/bolt_lock_manager.cpp @@ -37,7 +37,7 @@ bool BoltLockManager::SetUser(uint16_t userIndex, FabricIndex creator, FabricInd size_t totalCredentials) { return AccessMgr::Instance().SetUser(userIndex, creator, modifier, userName, uniqueId, userStatus, userType, - credentialRule, credentials, totalCredentials); + credentialRule, credentials, totalCredentials); } bool BoltLockManager::GetCredential(uint16_t credentialIndex, CredentialTypeEnum credentialType, @@ -50,8 +50,8 @@ bool BoltLockManager::SetCredential(uint16_t credentialIndex, FabricIndex creato DlCredentialStatus credentialStatus, CredentialTypeEnum credentialType, const ByteSpan &secret) { - return AccessMgr::Instance().SetCredential(credentialIndex, creator, modifier, credentialStatus, - credentialType, secret); + return AccessMgr::Instance().SetCredential(credentialIndex, creator, modifier, credentialStatus, credentialType, + secret); } #ifdef CONFIG_LOCK_SCHEDULES @@ -67,7 +67,7 @@ DlStatus BoltLockManager::SetWeekDaySchedule(uint8_t weekdayIndex, uint16_t user uint8_t endHour, uint8_t endMinute) { return AccessMgr::Instance().SetWeekDaySchedule(weekdayIndex, userIndex, status, daysMask, startHour, - startMinute, endHour, endMinute); + startMinute, endHour, endMinute); } DlStatus BoltLockManager::GetYearDaySchedule(uint8_t yearDayIndex, uint16_t userIndex, @@ -91,15 +91,15 @@ DlStatus BoltLockManager::SetHolidaySchedule(uint8_t holidayIndex, DlScheduleSta uint32_t localEndTime, OperatingModeEnum operatingMode) { return AccessMgr::Instance().SetHolidaySchedule(holidayIndex, status, localStartTime, localEndTime, - operatingMode); + operatingMode); } #endif /* CONFIG_LOCK_SCHEDULES */ - -bool BoltLockManager::ValidatePIN(const Optional &pinCode, OperationErrorEnum &err) +bool BoltLockManager::ValidatePIN(const Optional &pinCode, OperationErrorEnum &err, + Nullable &result) { - return AccessMgr::Instance().ValidatePIN(pinCode, err); + return AccessMgr::Instance().ValidatePIN(pinCode, err, result); } void BoltLockManager::SetRequirePIN(bool require) @@ -111,21 +111,23 @@ bool BoltLockManager::GetRequirePIN() return AccessMgr::Instance().GetRequirePIN(); } -void BoltLockManager::Lock(OperationSource source) +void BoltLockManager::Lock(const OperationSource source, const Nullable &fabricIdx, + const Nullable &nodeId, const Nullable &validatePINResult) { - VerifyOrReturn(mState != State::kLockingCompleted); - SetState(State::kLockingInitiated, source); + VerifyOrReturn(mStateData.mState != State::kLockingCompleted); + StateData newStateData{ State::kLockingInitiated, source, fabricIdx, nodeId, validatePINResult }; + SetStateData(newStateData); - mActuatorOperationSource = source; k_timer_start(&mActuatorTimer, K_MSEC(kActuatorMovementTimeMs), K_NO_WAIT); } -void BoltLockManager::Unlock(OperationSource source) +void BoltLockManager::Unlock(const OperationSource source, const Nullable &fabricIdx, + const Nullable &nodeId, const Nullable &validatePINResult) { - VerifyOrReturn(mState != State::kUnlockingCompleted); - SetState(State::kUnlockingInitiated, source); + VerifyOrReturn(mStateData.mState != State::kUnlockingCompleted); + StateData newStateData{ State::kUnlockingInitiated, source, fabricIdx, nodeId, validatePINResult }; + SetStateData(newStateData); - mActuatorOperationSource = source; k_timer_start(&mActuatorTimer, K_MSEC(kActuatorMovementTimeMs), K_NO_WAIT); } @@ -146,24 +148,33 @@ void BoltLockManager::ActuatorAppEventHandler(const BoltLockManagerEvent &event) { BoltLockManager *lock = reinterpret_cast(event.manager); - switch (lock->mState) { + switch (lock->mStateData.mState) { case State::kLockingInitiated: - lock->SetState(State::kLockingCompleted, lock->mActuatorOperationSource); + lock->SetState(State::kLockingCompleted); break; case State::kUnlockingInitiated: - lock->SetState(State::kUnlockingCompleted, lock->mActuatorOperationSource); + lock->SetState(State::kUnlockingCompleted); break; default: break; } } -void BoltLockManager::SetState(State state, OperationSource source) +void BoltLockManager::SetState(State state) +{ + mStateData.mState = state; + + if (mStateChangeCallback != nullptr) { + mStateChangeCallback(mStateData); + } +} + +void BoltLockManager::SetStateData(const StateData &stateData) { - mState = state; + mStateData = stateData; if (mStateChangeCallback != nullptr) { - mStateChangeCallback(state, source); + mStateChangeCallback(mStateData); } } diff --git a/samples/matter/lock/src/bolt_lock_manager.h b/samples/matter/lock/src/bolt_lock_manager.h index b66f7b4adf36..208fe6c17d94 100644 --- a/samples/matter/lock/src/bolt_lock_manager.h +++ b/samples/matter/lock/src/bolt_lock_manager.h @@ -18,6 +18,8 @@ struct BoltLockManagerEvent; class BoltLockManager { + using AccessMgr = AccessManager; + public: static constexpr size_t kMaxCredentialLength = 128; @@ -38,14 +40,24 @@ class BoltLockManager { }; using OperationSource = chip::app::Clusters::DoorLock::OperationSourceEnum; - using StateChangeCallback = void (*)(State, OperationSource); + using ValidatePINResult = AccessMgr::ValidatePINResult; + + struct StateData { + State mState; + OperationSource mSource; + Nullable mFabricIdx; + Nullable mNodeId; + Nullable mValidatePINResult; + }; + + using StateChangeCallback = void (*)(const StateData &); static constexpr uint32_t kActuatorMovementTimeMs = 2000; void Init(StateChangeCallback callback); - State GetState() const { return mState; } - bool IsLocked() const { return mState == State::kLockingCompleted; } + const StateData &GetState() const { return mStateData; } + bool IsLocked() const { return mStateData.mState == State::kLockingCompleted; } bool GetUser(uint16_t userIndex, EmberAfPluginDoorLockUserInfo &user); bool SetUser(uint16_t userIndex, chip::FabricIndex creator, chip::FabricIndex modifier, @@ -74,10 +86,15 @@ class BoltLockManager { uint32_t localEndTime, OperatingModeEnum operatingMode); #endif /* CONFIG_LOCK_SCHEDULES */ - bool ValidatePIN(const Optional &pinCode, OperationErrorEnum &err); + bool ValidatePIN(const Optional &pinCode, OperationErrorEnum &err, + Nullable &result); - void Lock(OperationSource source); - void Unlock(OperationSource source); + void Lock(const OperationSource source, const Nullable &fabricIdx = NullNullable, + const Nullable &nodeId = NullNullable, + const Nullable &validatePINResult = NullNullable); + void Unlock(const OperationSource source, const Nullable &fabricIdx = NullNullable, + const Nullable &nodeId = NullNullable, + const Nullable &validatePINResult = NullNullable); void SetRequirePIN(bool require); bool GetRequirePIN(); @@ -85,18 +102,17 @@ class BoltLockManager { void FactoryReset(); private: - using AccessMgr = AccessManager; friend class AppTask; - void SetState(State state, OperationSource source); + void SetState(State state); + void SetStateData(const StateData &stateData); static void ActuatorTimerEventHandler(k_timer *timer); static void ActuatorAppEventHandler(const BoltLockManagerEvent &event); friend BoltLockManager &BoltLockMgr(); - State mState = State::kLockingCompleted; + StateData mStateData = { State::kLockingCompleted, OperationSource::kButton, {}, {}, {} }; StateChangeCallback mStateChangeCallback = nullptr; - OperationSource mActuatorOperationSource = OperationSource::kButton; k_timer mActuatorTimer = {}; static BoltLockManager sLock; diff --git a/samples/matter/lock/src/zcl_callbacks.cpp b/samples/matter/lock/src/zcl_callbacks.cpp index 10336b74a069..ba9d9a057d86 100644 --- a/samples/matter/lock/src/zcl_callbacks.cpp +++ b/samples/matter/lock/src/zcl_callbacks.cpp @@ -25,19 +25,7 @@ void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath &a { VerifyOrReturn(attributePath.mClusterId == DoorLock::Id); - if (attributePath.mAttributeId == DoorLock::Attributes::LockState::Id) { - /* Post events only if current lock state is different than given */ - switch (*value) { - case to_underlying(DlLockState::kLocked): - BoltLockMgr().Lock(BoltLockManager::OperationSource::kRemote); - break; - case to_underlying(DlLockState::kUnlocked): - BoltLockMgr().Unlock(BoltLockManager::OperationSource::kRemote); - break; - default: - break; - } - } else if (attributePath.mAttributeId == DoorLock::Attributes::RequirePINforRemoteOperation::Id) { + if (attributePath.mAttributeId == DoorLock::Attributes::RequirePINforRemoteOperation::Id) { BoltLockMgr().SetRequirePIN(*value); } } @@ -75,28 +63,30 @@ bool emberAfPluginDoorLockOnDoorLockCommand(EndpointId endpointId, const Nullabl const Nullable &nodeId, const Optional &pinCode, OperationErrorEnum &err) { - bool result = BoltLockMgr().ValidatePIN(pinCode, err); + Nullable validatePINResult; + bool success = BoltLockMgr().ValidatePIN(pinCode, err, validatePINResult); /* Handle changing attribute state on command reception */ - if (result) { - BoltLockMgr().Lock(BoltLockManager::OperationSource::kRemote); + if (success) { + BoltLockMgr().Lock(BoltLockManager::OperationSource::kRemote, fabricIdx, nodeId, validatePINResult); } - return result; + return success; } bool emberAfPluginDoorLockOnDoorUnlockCommand(EndpointId endpointId, const Nullable &fabricIdx, const Nullable &nodeId, const Optional &pinCode, OperationErrorEnum &err) { - bool result = BoltLockMgr().ValidatePIN(pinCode, err); + Nullable validatePINResult; + bool success = BoltLockMgr().ValidatePIN(pinCode, err, validatePINResult); /* Handle changing attribute state on command reception */ - if (result) { - BoltLockMgr().Unlock(BoltLockManager::OperationSource::kRemote); + if (success) { + BoltLockMgr().Unlock(BoltLockManager::OperationSource::kRemote, fabricIdx, nodeId, validatePINResult); } - return result; + return success; } void emberAfDoorLockClusterInitCallback(EndpointId endpoint) @@ -134,8 +124,9 @@ void emberAfDoorLockClusterInitCallback(EndpointId endpoint) "number of holiday schedules"); #endif /* CONFIG_LOCK_SCHEDULES */ - AppTask::Instance().UpdateClusterState(BoltLockMgr().GetState(), - BoltLockManager::OperationSource::kUnspecified); + BoltLockManager::StateData state = BoltLockMgr().GetState(); + state.mSource = BoltLockManager::OperationSource::kUnspecified; + AppTask::Instance().UpdateClusterState(state); } #ifdef CONFIG_LOCK_SCHEDULES