[zephyr] Added support for few GeneralDiagnostics attributes (#13160)

Platform doesn't support a few optional attributes and also
mandatory BootReason event.

* Added support for attributes: UpTime, TotalOperationalHours
* Added support for event: BootReason
diff --git a/config/zephyr/Kconfig b/config/zephyr/Kconfig
index 63b9e72..b3404c5 100644
--- a/config/zephyr/Kconfig
+++ b/config/zephyr/Kconfig
@@ -108,6 +108,16 @@
 	  Device Firmware Upgrade by quering and downloading a new firmware image
 	  from an external OTA Provider node.
 
+config CHIP_OPERATIONAL_TIME_SAVE_INTERVAL
+	int "Interval of saving node operation time to flash in hours unit"
+	default 10
+	range 1 4294967295
+	help
+	  Interval in hours with which the node operation time is saved to the flash
+	  memory. Selected value is a trade off between performing frequent saves to know
+	  precisely operation time in case of device reboot and maximizing flash memory
+	  lifetime.
+
 config APP_LINK_WITH_CHIP
 	bool "Link 'app' with Connected Home over IP"
 	default y
diff --git a/src/platform/Zephyr/ConfigurationManagerImpl.cpp b/src/platform/Zephyr/ConfigurationManagerImpl.cpp
index 7c4f7ab..65f95d9 100644
--- a/src/platform/Zephyr/ConfigurationManagerImpl.cpp
+++ b/src/platform/Zephyr/ConfigurationManagerImpl.cpp
@@ -111,6 +111,22 @@
     return WriteConfigValue(ZephyrConfig::kCounterKey_RebootCount, rebootCount);
 }
 
+CHIP_ERROR ConfigurationManagerImpl::GetTotalOperationalHours(uint32_t & totalOperationalHours)
+{
+    if (!ZephyrConfig::ConfigValueExists(ZephyrConfig::kCounterKey_TotalOperationalHours))
+    {
+        totalOperationalHours = 0;
+        return CHIP_NO_ERROR;
+    }
+
+    return ZephyrConfig::ReadConfigValue(ZephyrConfig::kCounterKey_TotalOperationalHours, totalOperationalHours);
+}
+
+CHIP_ERROR ConfigurationManagerImpl::StoreTotalOperationalHours(uint32_t totalOperationalHours)
+{
+    return ZephyrConfig::WriteConfigValue(ZephyrConfig::kCounterKey_TotalOperationalHours, totalOperationalHours);
+}
+
 void ConfigurationManagerImpl::InitiateFactoryReset()
 {
     PlatformMgr().ScheduleWork(DoFactoryReset);
diff --git a/src/platform/Zephyr/ConfigurationManagerImpl.h b/src/platform/Zephyr/ConfigurationManagerImpl.h
index bcca062..44d66d3 100644
--- a/src/platform/Zephyr/ConfigurationManagerImpl.h
+++ b/src/platform/Zephyr/ConfigurationManagerImpl.h
@@ -38,6 +38,8 @@
 public:
     CHIP_ERROR GetRebootCount(uint32_t & rebootCount) override;
     CHIP_ERROR StoreRebootCount(uint32_t rebootCount) override;
+    CHIP_ERROR GetTotalOperationalHours(uint32_t & totalOperationalHours) override;
+    CHIP_ERROR StoreTotalOperationalHours(uint32_t totalOperationalHours) override;
     // This returns an instance of this class.
     static ConfigurationManagerImpl & GetDefaultInstance();
 
diff --git a/src/platform/Zephyr/DiagnosticDataProviderImpl.cpp b/src/platform/Zephyr/DiagnosticDataProviderImpl.cpp
index e627bb5..44fad86 100644
--- a/src/platform/Zephyr/DiagnosticDataProviderImpl.cpp
+++ b/src/platform/Zephyr/DiagnosticDataProviderImpl.cpp
@@ -90,6 +90,37 @@
     return err;
 }
 
+CHIP_ERROR DiagnosticDataProviderImpl::GetUpTime(uint64_t & upTime)
+{
+    System::Clock::Timestamp currentTime = System::SystemClock().GetMonotonicTimestamp();
+    System::Clock::Timestamp startTime   = PlatformMgrImpl().GetStartTime();
+
+    if (currentTime >= startTime)
+    {
+        upTime = std::chrono::duration_cast<System::Clock::Seconds64>(currentTime - startTime).count();
+        return CHIP_NO_ERROR;
+    }
+
+    return CHIP_ERROR_INVALID_TIME;
+}
+
+CHIP_ERROR DiagnosticDataProviderImpl::GetTotalOperationalHours(uint32_t & totalOperationalHours)
+{
+    uint64_t upTimeS;
+
+    ReturnErrorOnFailure(GetUpTime(upTimeS));
+
+    uint64_t totalHours      = 0;
+    const uint32_t upTimeH   = upTimeS / 3600 < UINT32_MAX ? static_cast<uint32_t>(upTimeS / 3600) : UINT32_MAX;
+    const uint64_t deltaTime = upTimeH - PlatformMgrImpl().GetSavedOperationalHoursSinceBoot();
+
+    ReturnErrorOnFailure(ConfigurationMgr().GetTotalOperationalHours(reinterpret_cast<uint32_t &>(totalHours)));
+
+    totalOperationalHours = totalHours + deltaTime < UINT32_MAX ? totalHours + deltaTime : UINT32_MAX;
+
+    return CHIP_NO_ERROR;
+}
+
 CHIP_ERROR DiagnosticDataProviderImpl::GetBootReason(uint8_t & bootReason)
 {
 #if CONFIG_HWINFO
diff --git a/src/platform/Zephyr/DiagnosticDataProviderImpl.h b/src/platform/Zephyr/DiagnosticDataProviderImpl.h
index 36eeef6..05687dc 100644
--- a/src/platform/Zephyr/DiagnosticDataProviderImpl.h
+++ b/src/platform/Zephyr/DiagnosticDataProviderImpl.h
@@ -44,6 +44,8 @@
     CHIP_ERROR GetCurrentHeapHighWatermark(uint64_t & currentHeapHighWatermark) override;
 
     CHIP_ERROR GetRebootCount(uint16_t & rebootCount) override;
+    CHIP_ERROR GetUpTime(uint64_t & upTime) override;
+    CHIP_ERROR GetTotalOperationalHours(uint32_t & totalOperationalHours) override;
     CHIP_ERROR GetBootReason(uint8_t & bootReason) override;
     CHIP_ERROR GetNetworkInterfaces(NetworkInterface ** netifpp) override;
     void ReleaseNetworkInterfaces(NetworkInterface * netifp) override;
diff --git a/src/platform/Zephyr/PlatformManagerImpl.cpp b/src/platform/Zephyr/PlatformManagerImpl.cpp
index 1efda50..f730e10 100644
--- a/src/platform/Zephyr/PlatformManagerImpl.cpp
+++ b/src/platform/Zephyr/PlatformManagerImpl.cpp
@@ -42,6 +42,8 @@
 
 PlatformManagerImpl PlatformManagerImpl::sInstance{ sChipThreadStack };
 
+static k_timer sOperationalHoursSavingTimer;
+
 #if !CONFIG_NORDIC_SECURITY_BACKEND
 static int app_entropy_source(void * data, unsigned char * output, size_t len, size_t * olen)
 {
@@ -61,6 +63,37 @@
 }
 #endif // !CONFIG_NORDIC_SECURITY_BACKEND
 
+void PlatformManagerImpl::OperationalHoursSavingTimerEventHandler(k_timer * timer)
+{
+    PlatformMgr().ScheduleWork(UpdateOperationalHours);
+}
+
+void PlatformManagerImpl::UpdateOperationalHours(intptr_t arg)
+{
+    uint64_t upTimeS;
+
+    if (GetDiagnosticDataProvider().GetUpTime(upTimeS) != CHIP_NO_ERROR)
+    {
+        ChipLogError(DeviceLayer, "Failed to get up time of the node");
+        return;
+    }
+
+    uint64_t totalOperationalHours = 0;
+    const uint32_t upTimeH         = upTimeS / 3600 < UINT32_MAX ? static_cast<uint32_t>(upTimeS / 3600) : UINT32_MAX;
+    const uint64_t deltaTime       = upTimeH - sInstance.mSavedOperationalHoursSinceBoot;
+
+    if (ConfigurationMgr().GetTotalOperationalHours(reinterpret_cast<uint32_t &>(totalOperationalHours)) == CHIP_NO_ERROR)
+    {
+        ConfigurationMgr().StoreTotalOperationalHours(
+            totalOperationalHours + deltaTime < UINT32_MAX ? totalOperationalHours + deltaTime : UINT32_MAX);
+        sInstance.mSavedOperationalHoursSinceBoot = upTimeH;
+    }
+    else
+    {
+        ChipLogError(DeviceLayer, "Failed to get total operational hours of the node");
+    }
+}
+
 CHIP_ERROR PlatformManagerImpl::_InitChipStack(void)
 {
     CHIP_ERROR err;
@@ -86,9 +119,27 @@
     err = Internal::GenericPlatformManagerImpl_Zephyr<PlatformManagerImpl>::_InitChipStack();
     SuccessOrExit(err);
 
+    // Start the timer to periodically save node operational hours.
+    k_timer_init(&sOperationalHoursSavingTimer, &PlatformManagerImpl::OperationalHoursSavingTimerEventHandler, nullptr);
+    k_timer_user_data_set(&sOperationalHoursSavingTimer, this);
+    k_timer_start(&sOperationalHoursSavingTimer, K_HOURS(CONFIG_CHIP_OPERATIONAL_TIME_SAVE_INTERVAL),
+                  K_HOURS(CONFIG_CHIP_OPERATIONAL_TIME_SAVE_INTERVAL));
+
+    ScheduleWork(OnDeviceBoot, 0);
+
 exit:
     return err;
 }
 
+void PlatformManagerImpl::OnDeviceBoot(intptr_t arg)
+{
+    GeneralDiagnosticsDelegate * generalDiagnosticsDelegate = GetDiagnosticDataProvider().GetGeneralDiagnosticsDelegate();
+
+    if (generalDiagnosticsDelegate)
+    {
+        generalDiagnosticsDelegate->OnDeviceRebooted();
+    }
+}
+
 } // namespace DeviceLayer
 } // namespace chip
diff --git a/src/platform/Zephyr/PlatformManagerImpl.h b/src/platform/Zephyr/PlatformManagerImpl.h
index cdc9240..a4bb23f 100644
--- a/src/platform/Zephyr/PlatformManagerImpl.h
+++ b/src/platform/Zephyr/PlatformManagerImpl.h
@@ -29,7 +29,7 @@
 namespace DeviceLayer {
 
 /**
- * Concrete implementation of the PlatformManager singleton object for the nRF Connect SDK platforms.
+ * Concrete implementation of the PlatformManager singleton object for the Zephyr platforms.
  */
 class PlatformManagerImpl final : public PlatformManager, public Internal::GenericPlatformManagerImpl_Zephyr<PlatformManagerImpl>
 {
@@ -46,19 +46,27 @@
 public:
     // ===== Platform-specific members that may be accessed directly by the application.
 
-    /* none so far */
+    System::Clock::Timestamp GetStartTime() { return mStartTime; }
+    uint32_t GetSavedOperationalHoursSinceBoot() { return mSavedOperationalHoursSinceBoot; }
 
 private:
     // ===== Methods that implement the PlatformManager abstract interface.
 
     CHIP_ERROR _InitChipStack(void);
 
+    static void OperationalHoursSavingTimerEventHandler(k_timer * timer);
+    static void UpdateOperationalHours(intptr_t arg);
+    static void OnDeviceBoot(intptr_t arg);
+
     // ===== Members for internal use by the following friends.
 
     friend PlatformManager & PlatformMgr(void);
     friend PlatformManagerImpl & PlatformMgrImpl(void);
     friend class Internal::BLEManagerImpl;
 
+    System::Clock::Timestamp mStartTime      = System::Clock::kZero;
+    uint32_t mSavedOperationalHoursSinceBoot = 0;
+
     explicit PlatformManagerImpl(ThreadStack & stack) : Internal::GenericPlatformManagerImpl_Zephyr<PlatformManagerImpl>(stack) {}
 
     static PlatformManagerImpl sInstance;
@@ -79,7 +87,7 @@
  * Returns the platform-specific implementation of the PlatformManager singleton object.
  *
  * chip applications can use this to gain access to features of the PlatformManager
- * that are specific to the ESP32 platform.
+ * that are specific to the Zephyr platform.
  */
 inline PlatformManagerImpl & PlatformMgrImpl()
 {
diff --git a/src/platform/Zephyr/ZephyrConfig.cpp b/src/platform/Zephyr/ZephyrConfig.cpp
index 9bdbccb..395d637 100644
--- a/src/platform/Zephyr/ZephyrConfig.cpp
+++ b/src/platform/Zephyr/ZephyrConfig.cpp
@@ -73,8 +73,9 @@
 const ZephyrConfig::Key ZephyrConfig::kConfigKey_Breadcrumb         = CONFIG_KEY(NAMESPACE_CONFIG "breadcrumb");
 
 // Keys stored in the counters namespace
-const ZephyrConfig::Key ZephyrConfig::kCounterKey_RebootCount = CONFIG_KEY(NAMESPACE_COUNTERS "reboot-count");
-const ZephyrConfig::Key ZephyrConfig::kCounterKey_BootReason  = CONFIG_KEY(NAMESPACE_COUNTERS "boot-reason");
+const ZephyrConfig::Key ZephyrConfig::kCounterKey_RebootCount           = CONFIG_KEY(NAMESPACE_COUNTERS "reboot-count");
+const ZephyrConfig::Key ZephyrConfig::kCounterKey_BootReason            = CONFIG_KEY(NAMESPACE_COUNTERS "boot-reason");
+const ZephyrConfig::Key ZephyrConfig::kCounterKey_TotalOperationalHours = CONFIG_KEY(NAMESPACE_COUNTERS "total-operational-hours");
 
 namespace {
 
diff --git a/src/platform/Zephyr/ZephyrConfig.h b/src/platform/Zephyr/ZephyrConfig.h
index a43d3bf..f6a1b23 100644
--- a/src/platform/Zephyr/ZephyrConfig.h
+++ b/src/platform/Zephyr/ZephyrConfig.h
@@ -65,6 +65,7 @@
     static const Key kConfigKey_Breadcrumb;
     static const Key kCounterKey_RebootCount;
     static const Key kCounterKey_BootReason;
+    static const Key kCounterKey_TotalOperationalHours;
 
     static CHIP_ERROR Init(void);