/* * * Copyright (c) 2020 Project CHIP Authors * Copyright (c) 2019 Google LLC. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "BoltLockManager.h" #include "AppConfig.h" #include "AppEvent.h" #include "AppTask.h" using namespace chip; BoltLockManager BoltLockManager::sLock; void BoltLockManager::Init(StateChangeCallback callback) { mStateChangeCallback = callback; k_timer_init(&mActuatorTimer, &BoltLockManager::ActuatorTimerEventHandler, nullptr); k_timer_user_data_set(&mActuatorTimer, this); } bool BoltLockManager::GetUser(uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user) const { // userIndex is guaranteed by the caller to be between 1 and CONFIG_LOCK_NUM_USERS user = mUsers[userIndex - 1]; ChipLogProgress(Zcl, "Getting lock user %u: %s", static_cast(userIndex), user.userStatus == UserStatusEnum::kAvailable ? "available" : "occupied"); return true; } bool BoltLockManager::SetUser(uint16_t userIndex, FabricIndex creator, FabricIndex modifier, const CharSpan & userName, uint32_t uniqueId, UserStatusEnum userStatus, UserTypeEnum userType, CredentialRuleEnum credentialRule, const CredentialStruct * credentials, size_t totalCredentials) { // userIndex is guaranteed by the caller to be between 1 and CONFIG_LOCK_NUM_USERS UserData & userData = mUserData[userIndex - 1]; auto & user = mUsers[userIndex - 1]; VerifyOrReturnError(userName.size() <= DOOR_LOCK_MAX_USER_NAME_SIZE, false); VerifyOrReturnError(totalCredentials <= CONFIG_LOCK_NUM_CREDENTIALS_PER_USER, false); Platform::CopyString(userData.mName, userName); memcpy(userData.mCredentials, credentials, totalCredentials * sizeof(CredentialStruct)); user.userName = CharSpan(userData.mName, userName.size()); user.credentials = Span(userData.mCredentials, totalCredentials); user.userUniqueId = uniqueId; user.userStatus = userStatus; user.userType = userType; user.credentialRule = credentialRule; user.creationSource = DlAssetSource::kMatterIM; user.createdBy = creator; user.modificationSource = DlAssetSource::kMatterIM; user.lastModifiedBy = modifier; ChipLogProgress(Zcl, "Setting lock user %u: %s", static_cast(userIndex), userStatus == UserStatusEnum::kAvailable ? "available" : "occupied"); return true; } bool BoltLockManager::GetCredential(uint16_t credentialIndex, CredentialTypeEnum credentialType, EmberAfPluginDoorLockCredentialInfo & credential) const { VerifyOrReturnError(credentialIndex > 0 && credentialIndex <= CONFIG_LOCK_NUM_CREDENTIALS, false); credential = mCredentials[credentialIndex - 1]; ChipLogProgress(Zcl, "Getting lock credential %u: %s", static_cast(credentialIndex), credential.status == DlCredentialStatus::kAvailable ? "available" : "occupied"); return true; } bool BoltLockManager::SetCredential(uint16_t credentialIndex, FabricIndex creator, FabricIndex modifier, DlCredentialStatus credentialStatus, CredentialTypeEnum credentialType, const ByteSpan & secret) { VerifyOrReturnError(credentialIndex > 0 && credentialIndex <= CONFIG_LOCK_NUM_CREDENTIALS, false); VerifyOrReturnError(secret.size() <= kMaxCredentialLength, false); CredentialData & credentialData = mCredentialData[credentialIndex - 1]; auto & credential = mCredentials[credentialIndex - 1]; if (!secret.empty()) { memcpy(credentialData.mSecret.Alloc(secret.size()).Get(), secret.data(), secret.size()); } credential.status = credentialStatus; credential.credentialType = credentialType; credential.credentialData = ByteSpan(credentialData.mSecret.Get(), secret.size()); credential.creationSource = DlAssetSource::kMatterIM; credential.createdBy = creator; credential.modificationSource = DlAssetSource::kMatterIM; credential.lastModifiedBy = modifier; ChipLogProgress(Zcl, "Setting lock credential %u: %s", static_cast(credentialIndex), credential.status == DlCredentialStatus::kAvailable ? "available" : "occupied"); return true; } bool BoltLockManager::ValidatePIN(const Optional & pinCode, OperationErrorEnum & err) const { // Optionality of the PIN code is validated by the caller, so assume it is OK not to provide the PIN code. if (!pinCode.HasValue()) { return true; } // Check the PIN code for (const auto & credential : mCredentials) { if (credential.status == DlCredentialStatus::kAvailable || credential.credentialType != CredentialTypeEnum::kPin) { continue; } if (credential.credentialData.data_equal(pinCode.Value())) { ChipLogDetail(Zcl, "Valid lock PIN code provided"); return true; } } ChipLogDetail(Zcl, "Invalid lock PIN code provided"); err = OperationErrorEnum::kInvalidCredential; return false; } void BoltLockManager::Lock(OperationSource source) { VerifyOrReturn(mState != State::kLockingCompleted); SetState(State::kLockingInitiated, source); mActuatorOperationSource = source; k_timer_start(&mActuatorTimer, K_MSEC(kActuatorMovementTimeMs), K_NO_WAIT); } void BoltLockManager::Unlock(OperationSource source) { VerifyOrReturn(mState != State::kUnlockingCompleted); SetState(State::kUnlockingInitiated, source); mActuatorOperationSource = source; k_timer_start(&mActuatorTimer, K_MSEC(kActuatorMovementTimeMs), K_NO_WAIT); } void BoltLockManager::ActuatorTimerEventHandler(k_timer * timer) { // The timer event handler is called in the context of the system clock ISR. // Post an event to the application task queue to process the event in the // context of the application thread. AppEvent event; event.Type = AppEventType::Timer; event.TimerEvent.Context = static_cast(k_timer_user_data_get(timer)); event.Handler = BoltLockManager::ActuatorAppEventHandler; AppTask::Instance().PostEvent(event); } void BoltLockManager::ActuatorAppEventHandler(const AppEvent & event) { BoltLockManager * lock = static_cast(event.TimerEvent.Context); if (!lock) { return; } switch (lock->mState) { case State::kLockingInitiated: lock->SetState(State::kLockingCompleted, lock->mActuatorOperationSource); break; case State::kUnlockingInitiated: lock->SetState(State::kUnlockingCompleted, lock->mActuatorOperationSource); break; default: break; } } void BoltLockManager::SetState(State state, OperationSource source) { mState = state; if (mStateChangeCallback != nullptr) { mStateChangeCallback(state, source); } }