blob: 721fb11e427d72174aba01685fbd687f21f2f0e2 [file] [log] [blame]
/*
*
* Copyright (c) 2023 Project CHIP Authors
* 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 "AppEventCommon.h"
#include "AppTask.h"
#include <cstring>
using namespace chip;
using chip::to_underlying;
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<unsigned>(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<const CredentialStruct>(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<unsigned>(userIndex),
userStatus == UserStatusEnum::kAvailable ? "available" : "occupied");
return true;
}
bool BoltLockManager::GetCredential(uint16_t credentialIndex, CredentialTypeEnum credentialType,
EmberAfPluginDoorLockCredentialInfo & credential) const
{
ChipLogProgress(Zcl, "Lock App: LockEndpoint::GetCredential [credentialIndex=%u,credentialType=%u]", credentialIndex,
to_underlying(credentialType));
if (to_underlying(credentialType) >= mCredentials.size())
{
ChipLogError(Zcl, "Cannot get the credential - index out of range [index=%d]", credentialIndex);
return false;
}
if (credentialIndex >= mCredentials.at(to_underlying(credentialType)).size() ||
(0 == credentialIndex && CredentialTypeEnum::kProgrammingPIN != credentialType))
{
ChipLogError(Zcl, "Cannot get the credential - index out of range [index=%d]", credentialIndex);
return false;
}
const auto & credentialInStorage = mCredentials[to_underlying(credentialType)][credentialIndex];
credential.status = credentialInStorage.status;
if (DlCredentialStatus::kAvailable == credential.status)
{
ChipLogDetail(Zcl, "Found unoccupied credential [index=%u]", credentialIndex);
return true;
}
credential.credentialType = credentialInStorage.credentialType;
credential.credentialData = chip::ByteSpan(credentialInStorage.credentialData, credentialInStorage.credentialDataSize);
// So far there's no way to actually create the credential outside the matter, so here we always set the creation/modification
// source to Matter
credential.creationSource = DlAssetSource::kMatterIM;
credential.createdBy = credentialInStorage.createdBy;
credential.modificationSource = DlAssetSource::kMatterIM;
credential.lastModifiedBy = credentialInStorage.modifiedBy;
ChipLogDetail(Zcl, "Found occupied credential [index=%u,type=%u,dataSize=%u,createdBy=%u,modifiedBy=%u]", credentialIndex,
to_underlying(credential.credentialType), static_cast<unsigned int>(credential.credentialData.size()),
credential.createdBy, credential.lastModifiedBy);
return true;
}
bool BoltLockManager::SetCredential(uint16_t credentialIndex, FabricIndex creator, FabricIndex modifier,
DlCredentialStatus credentialStatus, CredentialTypeEnum credentialType,
const ByteSpan & credentialData)
{
ChipLogProgress(Zcl,
"Lock App: LockEndpoint::SetCredential "
"[credentialIndex=%u,credentialStatus=%u,credentialType=%u,credentialDataSize=%u,creator=%u,modifier=%u]",
credentialIndex, to_underlying(credentialStatus), to_underlying(credentialType),
static_cast<unsigned int>(credentialData.size()), creator, modifier);
if (to_underlying(credentialType) >= mCredentials.capacity())
{
ChipLogError(Zcl, "Cannot set the credential - type out of range [type=%d]", to_underlying(credentialType));
return false;
}
if (credentialIndex >= mCredentials.at(to_underlying(credentialType)).size() ||
(0 == credentialIndex && CredentialTypeEnum::kProgrammingPIN != credentialType))
{
ChipLogError(Zcl, "Cannot set the credential - index out of range [index=%d]", credentialIndex);
return false;
}
auto & credentialInStorage = mCredentials[to_underlying(credentialType)][credentialIndex];
if (credentialData.size() > CONFIG_LOCK_CREDENTIAL_INFO_MAX_DATA_SIZE)
{
ChipLogError(Zcl,
"Cannot get the credential - data size exceeds limit "
"index=%d,dataSize=%u,maxDataSize=%u]",
credentialIndex, static_cast<unsigned int>(credentialData.size()),
static_cast<unsigned int>(CONFIG_LOCK_CREDENTIAL_INFO_MAX_DATA_SIZE));
return false;
}
credentialInStorage.status = credentialStatus;
credentialInStorage.credentialType = credentialType;
credentialInStorage.createdBy = creator;
credentialInStorage.modifiedBy = modifier;
std::memcpy(credentialInStorage.credentialData, credentialData.data(), credentialData.size());
credentialInStorage.credentialDataSize = credentialData.size();
ChipLogProgress(Zcl, "Successfully set the credential [index=%d,credentialType=%u,creator=%u,modifier=%u]", credentialIndex,
to_underlying(credentialType), credentialInStorage.createdBy, credentialInStorage.modifiedBy);
return true;
}
bool BoltLockManager::ValidatePIN(const Optional<ByteSpan> & 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;
}
// Find the credential so we can make sure it is not absent right away
auto & pinCredentials = mCredentials[to_underlying(CredentialTypeEnum::kPin)];
auto credential = std::find_if(pinCredentials.begin(), pinCredentials.end(), [&pinCode](const LockCredentialInfo & c) {
return (c.status != DlCredentialStatus::kAvailable) &&
chip::ByteSpan{ c.credentialData, c.credentialDataSize }.data_equal(pinCode.Value());
});
if (credential == pinCredentials.end())
{
ChipLogDetail(Zcl, "Lock App: specified PIN code was not found in the database");
err = OperationErrorEnum::kInvalidCredential;
return false;
}
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 = AppEvent::kEventType_Timer;
event.TimerEvent.Context = static_cast<BoltLockManager *>(k_timer_user_data_get(timer));
event.Handler = (EventHandler) BoltLockManager::ActuatorAppEventHandler;
GetAppTask().PostEvent(&event);
}
void BoltLockManager::ActuatorAppEventHandler(const AppEvent & event)
{
BoltLockManager * lock = static_cast<BoltLockManager *>(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);
}
}