blob: 7df0ad3e704dba42843ed308185ab1d09b6a78d6 [file] [log] [blame]
/**
*
* Copyright (c) 2023-2025 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.
*/
/**
* @file API declarations for time sync cluster.
*/
#pragma once
#include <app/clusters/time-synchronization-server/DefaultTimeSyncDelegate.h>
#include <app/clusters/time-synchronization-server/TimeSyncDataProvider.h>
#include <app/clusters/time-synchronization-server/time-synchronization-delegate.h>
#include <app/server-cluster/DefaultServerCluster.h>
#include <app/server-cluster/OptionalAttributeSet.h>
#include <clusters/TimeSynchronization/Metadata.h>
#include <platform/DeviceInfoProvider.h>
#include <app/server/Server.h>
// NOTE: this is part of AppConfig, so this has to be checked for AFTER the inclusion
// of that header
#if TIME_SYNC_ENABLE_TSC_FEATURE
#include <app/ReadClient.h>
#endif
namespace chip::app::Clusters {
namespace TimeSynchronization {
/**
* @brief Describes the state of time zone and DSTOffset in use.
*/
enum class TimeState : uint8_t
{
kInvalid = 0, // No valid offset available
kActive = 1, // An offset is currently being used
kChanged = 2, // An offset expired or changed to a new value
kStopped = 3, // Permanent item in use
};
/**
* @brief Flags for tracking event types to emit.
*/
enum class TimeSyncEventFlag : uint8_t
{
kNone = 0,
kDSTTableEmpty = 1,
kDSTStatus = 2,
kTimeZoneStatus = 4,
kTimeFailure = 8,
kMissingTTSource = 16,
};
} // namespace TimeSynchronization
class TimeSynchronizationCluster : public DefaultServerCluster,
public FabricTable::Delegate
#if TIME_SYNC_ENABLE_TSC_FEATURE
,
public ReadClient::Callback
#endif
{
public:
// NOTE: this set is smaller than the full optional attributes supported by time synchronization
// as other attributes are controlled by feature flags
using OptionalAttributeSet = app::OptionalAttributeSet<TimeSynchronization::Attributes::TimeSource::Id>;
struct StartupConfiguration
{
TimeSynchronization::Attributes::SupportsDNSResolve::TypeInfo::Type supportsDNSResolve{ false };
TimeSynchronization::Attributes::NTPServerAvailable::TypeInfo::Type ntpServerAvailable{ false };
TimeSynchronization::TimeZoneDatabaseEnum timeZoneDatabase{ TimeSynchronization::TimeZoneDatabaseEnum::kNone };
TimeSynchronization::TimeSourceEnum timeSource{ TimeSynchronization::TimeSourceEnum::kNone };
TimeSynchronization::Delegate * delegate{ nullptr };
};
TimeSynchronizationCluster(EndpointId endpoint, const BitFlags<TimeSynchronization::Feature> features,
const OptionalAttributeSet & optionalAttributeSet, const StartupConfiguration & config);
CHIP_ERROR Startup(ServerClusterContext & context) override;
void Shutdown(ClusterShutdownType type) override;
// Fabric Table delegate functions
void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override;
#if TIME_SYNC_ENABLE_TSC_FEATURE
// CASE connection functions
void OnDeviceConnectedFn(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle);
void OnDeviceConnectionFailureFn();
// ReadClient::Callback functions
void OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, const StatusIB & aStatus) override;
void OnDone(ReadClient * apReadClient) override;
#endif
// Platform event handler functions
void OnPlatformEventFn(const DeviceLayer::ChipDeviceEvent & event);
void OnTimeSyncCompletionFn(TimeSynchronization::TimeSourceEnum timeSource, TimeSynchronization::GranularityEnum granularity);
void OnFallbackNTPCompletionFn(bool timeSyncSuccessful);
DataModel::ActionReturnStatus ReadAttribute(const DataModel::ReadAttributeRequest & request,
AttributeValueEncoder & encoder) override;
CHIP_ERROR Attributes(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<DataModel::AttributeEntry> & builder) override;
std::optional<DataModel::ActionReturnStatus> InvokeCommand(const DataModel::InvokeRequest & request,
TLV::TLVReader & input_arguments, CommandHandler * handler) override;
CHIP_ERROR AcceptedCommands(const ConcreteClusterPath & path,
ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & builder) override;
CHIP_ERROR GeneratedCommands(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<CommandId> & builder) override;
const TimeSynchronization::GranularityEnum & GetGranularity() const { return mGranularity; }
TimeSynchronization::Delegate * GetDelegate() const { return mDelegate; }
void SetDelegate(TimeSynchronization::Delegate * delegate)
{
VerifyOrDie(delegate != nullptr);
mDelegate = delegate;
}
private:
CHIP_ERROR SetTrustedTimeSource(const DataModel::Nullable<TimeSynchronization::Structs::TrustedTimeSourceStruct::Type> & tts);
inline CHIP_ERROR SetDefaultNTP(const DataModel::Nullable<CharSpan> & dntp);
void InitTimeZone();
CHIP_ERROR SetTimeZone(const DataModel::DecodableList<TimeSynchronization::Structs::TimeZoneStruct::Type> & tzL);
CHIP_ERROR LoadTimeZone();
CHIP_ERROR ClearTimeZone();
void InitDSTOffset();
CHIP_ERROR SetDSTOffset(const DataModel::DecodableList<TimeSynchronization::Structs::DSTOffsetStruct::Type> & dstL);
CHIP_ERROR LoadDSTOffset();
CHIP_ERROR ClearDSTOffset();
inline const DataModel::Nullable<TimeSynchronization::Structs::TrustedTimeSourceStruct::Type> & GetTrustedTimeSource() const;
inline Span<TimeSyncDataProvider::TimeZoneStore> & GetTimeZone();
inline DataModel::List<TimeSynchronization::Structs::DSTOffsetStruct::Type> & GetDSTOffset();
inline CHIP_ERROR GetDefaultNtp(MutableCharSpan & dntp);
CHIP_ERROR SetUTCTime(uint64_t utcTime, TimeSynchronization::GranularityEnum granularity,
TimeSynchronization::TimeSourceEnum source);
CHIP_ERROR GetLocalTime(DataModel::Nullable<uint64_t> & localTime);
TimeSyncDataProvider & GetDataProvider() { return mTimeSyncDataProvider; }
TimeSynchronization::TimeState UpdateTimeZoneState();
TimeSynchronization::TimeState UpdateDSTOffsetState();
inline TimeSynchronization::TimeSyncEventFlag GetEventFlag() const;
inline void ClearEventFlag(TimeSynchronization::TimeSyncEventFlag flag);
static constexpr size_t kMaxDefaultNTPSize = 128;
DataModel::Nullable<TimeSynchronization::Structs::TrustedTimeSourceStruct::Type> mTrustedTimeSource;
TimeSyncDataProvider::TimeZoneObj mTimeZoneObj{ Span<TimeSyncDataProvider::TimeZoneStore>(mTz), 0 };
TimeSyncDataProvider::DSTOffsetObj mDstOffsetObj{ DataModel::List<TimeSynchronization::Structs::DSTOffsetStruct::Type>(mDst),
0 };
TimeSynchronization::GranularityEnum mGranularity = TimeSynchronization::GranularityEnum::kNoTimeGranularity;
TimeSyncDataProvider::TimeZoneStore mTz[CHIP_CONFIG_TIME_ZONE_LIST_MAX_SIZE];
TimeSynchronization::Structs::DSTOffsetStruct::Type mDst[CHIP_CONFIG_DST_OFFSET_LIST_MAX_SIZE];
const BitFlags<TimeSynchronization::Feature> mFeatures;
const OptionalAttributeSet mOptionalAttributeSet;
TimeSynchronization::Attributes::SupportsDNSResolve::TypeInfo::Type mSupportsDNSResolve;
TimeSynchronization::Attributes::NTPServerAvailable::TypeInfo::Type mNTPServerAvailable;
TimeSynchronization::TimeZoneDatabaseEnum mTimeZoneDatabase;
TimeSynchronization::TimeSourceEnum mTimeSource;
TimeSynchronization::Delegate * mDelegate = nullptr;
TimeSyncDataProvider mTimeSyncDataProvider;
TimeSynchronization::TimeSyncEventFlag mEventFlag = TimeSynchronization::TimeSyncEventFlag::kNone;
CHIP_ERROR AttemptToGetTimeFromTrustedNode();
#if TIME_SYNC_ENABLE_TSC_FEATURE
chip::Callback::Callback<OnDeviceConnected> mOnDeviceConnectedCallback;
chip::Callback::Callback<OnDeviceConnectionFailure> mOnDeviceConnectionFailureCallback;
struct TimeReadInfo
{
TimeReadInfo(InteractionModelEngine * apImEngine, Messaging::ExchangeManager * apExchangeMgr,
ReadClient::Callback & apCallback, ReadClient::InteractionType aInteractionType) :
readClient(apImEngine, apExchangeMgr, apCallback, aInteractionType)
{
utcTime.SetNull();
}
TimeSynchronization::Attributes::UTCTime::TypeInfo::DecodableType utcTime;
TimeSynchronization::Attributes::Granularity::TypeInfo::DecodableType granularity =
TimeSynchronization::GranularityEnum::kNoTimeGranularity;
ReadClient readClient;
};
Platform::UniquePtr<TimeReadInfo> mTimeReadInfo;
#endif
chip::Callback::Callback<TimeSynchronization::OnTimeSyncCompletion> mOnTimeSyncCompletion;
chip::Callback::Callback<TimeSynchronization::OnFallbackNTPCompletion> mOnFallbackNTPCompletion;
// Called when the platform is set up - attempts to get time using the recommended source list in the spec.
void AttemptToGetTime();
// Attempts to get fallback NTP from the delegate (last available source)
// If successful, the function will set mGranulatiry and the time source
// If unsuccessful, it will emit a TimeFailure event.
void AttemptToGetFallbackNTPTimeFromDelegate();
std::optional<DataModel::ActionReturnStatus>
HandleSetUTCTime(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
const TimeSynchronization::Commands::SetUTCTime::DecodableType & commandData);
std::optional<DataModel::ActionReturnStatus>
HandleSetTrustedTimeSource(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
const TimeSynchronization::Commands::SetTrustedTimeSource::DecodableType & commandData);
std::optional<DataModel::ActionReturnStatus>
HandleSetTimeZone(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
const TimeSynchronization::Commands::SetTimeZone::DecodableType & commandData);
std::optional<DataModel::ActionReturnStatus>
HandleSetDSTOffset(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
const TimeSynchronization::Commands::SetDSTOffset::DecodableType & commandData);
std::optional<DataModel::ActionReturnStatus>
HandleSetDefaultNTP(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
const TimeSynchronization::Commands::SetDefaultNTP::DecodableType & commandData);
DataModel::EventsGenerator * GetEventsGenerator() const
{
return mContext != nullptr ? &mContext->interactionContext.eventsGenerator : nullptr;
}
};
} // namespace chip::app::Clusters