diff --git a/src/app/clusters/ota-requestor/GenericOTARequestorDriver.cpp b/src/app/clusters/ota-requestor/GenericOTARequestorDriver.cpp index cc2bdb109a82e3..a94a2aa2348fa2 100644 --- a/src/app/clusters/ota-requestor/GenericOTARequestorDriver.cpp +++ b/src/app/clusters/ota-requestor/GenericOTARequestorDriver.cpp @@ -85,7 +85,7 @@ void GenericOTARequestorDriver::Init(OTARequestorInterface * requestor, OTAImage else { // Start the first periodic query timer - StartDefaultProviderTimer(); + StartSelectedTimer(SelectedTimer::kPeriodicQueryTimer); } } @@ -118,17 +118,23 @@ bool GenericOTARequestorDriver::ProviderLocationsEqual(const ProviderLocationTyp void GenericOTARequestorDriver::HandleError(UpdateFailureState state, CHIP_ERROR error) {} +void GenericOTARequestorDriver::HandleIdleStateExit() +{ + // Start watchdog timer to monitor new Query Image session + StartSelectedTimer(SelectedTimer::kWatchdogTimer); +} + void GenericOTARequestorDriver::HandleIdleState(IdleStateReason reason) { switch (reason) { case IdleStateReason::kUnknown: ChipLogProgress(SoftwareUpdate, "Unknown idle state reason so set the periodic timer for a next attempt"); - StartDefaultProviderTimer(); + StartSelectedTimer(SelectedTimer::kPeriodicQueryTimer); break; case IdleStateReason::kIdle: // There is no current OTA update in progress so start the periodic query timer - StartDefaultProviderTimer(); + StartSelectedTimer(SelectedTimer::kPeriodicQueryTimer); break; case IdleStateReason::kInvalidSession: // An invalid session is detected which may be temporary so try to query the same provider again @@ -201,7 +207,7 @@ void GenericOTARequestorDriver::UpdateNotFound(UpdateNotFoundReason reason, Syst else { ChipLogProgress(SoftwareUpdate, "UpdateNotFound, not scheduling further retries"); - StartDefaultProviderTimer(); + StartSelectedTimer(SelectedTimer::kPeriodicQueryTimer); } } @@ -240,7 +246,7 @@ void GenericOTARequestorDriver::UpdateDiscontinued() UpdateCancelled(); // Restart the periodic default provider timer - StartDefaultProviderTimer(); + StartSelectedTimer(SelectedTimer::kPeriodicQueryTimer); } // Cancel all OTA update timers @@ -336,15 +342,15 @@ void GenericOTARequestorDriver::SendQueryImage() UpdateCancelled(); // Default provider timer only runs when there is no ongoing query/update; must stop it now. - // TriggerImmediateQueryInternal() will cause the state to change from kIdle - StopDefaultProviderTimer(); + // TriggerImmediateQueryInternal() will cause the state to change from kIdle, which will start + // the Watchdog timer. (Clean this up with Issue#16151) mProviderRetryCount++; DeviceLayer::SystemLayer().ScheduleLambda([this] { mRequestor->TriggerImmediateQueryInternal(); }); } -void GenericOTARequestorDriver::DefaultProviderTimerHandler(System::Layer * systemLayer, void * appState) +void GenericOTARequestorDriver::PeriodicQueryTimerHandler(System::Layer * systemLayer, void * appState) { ChipLogProgress(SoftwareUpdate, "Default Provider timer handler is invoked"); @@ -353,7 +359,7 @@ void GenericOTARequestorDriver::DefaultProviderTimerHandler(System::Layer * syst bool listExhausted = false; if (GetNextProviderLocation(providerLocation, listExhausted) != true) { - StartDefaultProviderTimer(); + StartSelectedTimer(SelectedTimer::kPeriodicQueryTimer); return; } @@ -362,28 +368,76 @@ void GenericOTARequestorDriver::DefaultProviderTimerHandler(System::Layer * syst SendQueryImage(); } -void GenericOTARequestorDriver::StartDefaultProviderTimer() +void GenericOTARequestorDriver::StartPeriodicQueryTimer() { - ChipLogProgress(SoftwareUpdate, "Starting the Default Provider timer, timeout: %u seconds", + ChipLogProgress(SoftwareUpdate, "Starting the periodic query timer, timeout: %u seconds", (unsigned int) mPeriodicQueryTimeInterval); ScheduleDelayedAction( System::Clock::Seconds32(mPeriodicQueryTimeInterval), [](System::Layer *, void * context) { - (static_cast(context))->DefaultProviderTimerHandler(nullptr, context); + (static_cast(context))->PeriodicQueryTimerHandler(nullptr, context); }, this); } -void GenericOTARequestorDriver::StopDefaultProviderTimer() +void GenericOTARequestorDriver::StopPeriodicQueryTimer() { - ChipLogProgress(SoftwareUpdate, "Stopping the Default Provider timer"); + ChipLogProgress(SoftwareUpdate, "Stopping the Periodic Query timer"); CancelDelayedAction( [](System::Layer *, void * context) { - (static_cast(context))->DefaultProviderTimerHandler(nullptr, context); + (static_cast(context))->PeriodicQueryTimerHandler(nullptr, context); + }, + this); +} + +void GenericOTARequestorDriver::WatchdogTimerHandler(System::Layer * systemLayer, void * appState) +{ + ChipLogError(SoftwareUpdate, "Watchdog timer detects state stuck at %u. Cancelling download and resetting state.", + to_underlying(mRequestor->GetCurrentUpdateState())); + + // Something went wrong and OTA requestor is stuck in a non-idle state for too long. + // Let's just cancel download, reset state, and re-start periodic query timer. + UpdateDiscontinued(); + mRequestor->CancelImageUpdate(); + StartPeriodicQueryTimer(); +} + +void GenericOTARequestorDriver::StartWatchdogTimer() +{ + ChipLogProgress(SoftwareUpdate, "Starting the watchdog timer, timeout: %u seconds", (unsigned int) mWatchdogTimeInterval); + ScheduleDelayedAction( + System::Clock::Seconds32(mWatchdogTimeInterval), + [](System::Layer *, void * context) { + (static_cast(context))->WatchdogTimerHandler(nullptr, context); }, this); } +void GenericOTARequestorDriver::StopWatchdogTimer() +{ + ChipLogProgress(SoftwareUpdate, "Stopping the watchdog timer"); + CancelDelayedAction( + [](System::Layer *, void * context) { + (static_cast(context))->WatchdogTimerHandler(nullptr, context); + }, + this); +} + +void GenericOTARequestorDriver::StartSelectedTimer(SelectedTimer timer) +{ + switch (timer) + { + case SelectedTimer::kPeriodicQueryTimer: + StopWatchdogTimer(); + StartPeriodicQueryTimer(); + break; + case SelectedTimer::kWatchdogTimer: + StopPeriodicQueryTimer(); + StartWatchdogTimer(); + break; + } +} + /** * Returns the next available Provider location. The algorithm is to simply loop through the list of DefaultOtaProviders as a * circular list and return the next value (based on the last used provider). If the list of DefaultOtaProviders is empty, FALSE is diff --git a/src/app/clusters/ota-requestor/GenericOTARequestorDriver.h b/src/app/clusters/ota-requestor/GenericOTARequestorDriver.h index 5b2e51762ea0c3..25bc2ba8a359cd 100644 --- a/src/app/clusters/ota-requestor/GenericOTARequestorDriver.h +++ b/src/app/clusters/ota-requestor/GenericOTARequestorDriver.h @@ -56,6 +56,7 @@ class GenericOTARequestorDriver : public OTARequestorDriver bool CanConsent() override; uint16_t GetMaxDownloadBlockSize() override; void HandleError(UpdateFailureState state, CHIP_ERROR error) override; + void HandleIdleStateExit() override; void HandleIdleState(IdleStateReason reason) override; void UpdateAvailable(const UpdateDescription & update, System::Clock::Seconds32 delay) override; void UpdateNotFound(UpdateNotFoundReason reason, System::Clock::Seconds32 delay) override; @@ -71,9 +72,13 @@ class GenericOTARequestorDriver : public OTARequestorDriver bool GetNextProviderLocation(ProviderLocationType & providerLocation, bool & listExhausted) override; protected: - void StartDefaultProviderTimer(); - void StopDefaultProviderTimer(); - void DefaultProviderTimerHandler(System::Layer * systemLayer, void * appState); + void StartPeriodicQueryTimer(); + void StopPeriodicQueryTimer(); + void PeriodicQueryTimerHandler(System::Layer * systemLayer, void * appState); + void StartWatchdogTimer(); + void StopWatchdogTimer(); + void WatchdogTimerHandler(System::Layer * systemLayer, void * appState); + void StartSelectedTimer(SelectedTimer timer); void ScheduleDelayedAction(System::Clock::Seconds32 delay, System::TimerCompleteCallback action, void * aAppState); void CancelDelayedAction(System::TimerCompleteCallback action, void * aAppState); bool ProviderLocationsEqual(const ProviderLocationType & a, const ProviderLocationType & b); @@ -82,6 +87,7 @@ class GenericOTARequestorDriver : public OTARequestorDriver OTAImageProcessorInterface * mImageProcessor = nullptr; uint32_t mOtaStartDelaySec = 0; uint32_t mPeriodicQueryTimeInterval = (24 * 60 * 60); // Timeout for querying providers on the default OTA provider list + uint32_t mWatchdogTimeInterval = (6 * 60 * 60); // Timeout (in seconds) for checking if Requestor has reverted back to idle mode // Maximum number of times to retry a BUSY OTA provider before moving to the next available one static constexpr uint8_t kMaxBusyProviderRetryCount = 3; uint8_t mProviderRetryCount; // Track retry count for the current provider diff --git a/src/app/clusters/ota-requestor/OTARequestor.cpp b/src/app/clusters/ota-requestor/OTARequestor.cpp index f6b19f5981922c..561c29f5c7f582 100644 --- a/src/app/clusters/ota-requestor/OTARequestor.cpp +++ b/src/app/clusters/ota-requestor/OTARequestor.cpp @@ -400,7 +400,7 @@ void OTARequestor::CancelImageUpdate() mOtaRequestorDriver->UpdateCancelled(); - RecordNewUpdateState(OTAUpdateStateEnum::kIdle, OTAChangeReasonEnum::kUnknown); + Reset(); } CHIP_ERROR OTARequestor::GetUpdateStateProgressAttribute(EndpointId endpointId, app::DataModel::Nullable & progress) @@ -658,6 +658,7 @@ void OTARequestor::RecordNewUpdateState(OTAUpdateStateEnum newState, OTAChangeRe } OtaRequestorServerOnStateTransition(mCurrentUpdateState, newState, reason, targetSoftwareVersion); + // Issue#16151 tracks re-factoring error and state transitioning handling. if ((newState == OTAUpdateStateEnum::kIdle) && (mCurrentUpdateState != OTAUpdateStateEnum::kIdle)) { IdleStateReason idleStateReason = MapErrorToIdleStateReason(error); @@ -665,6 +666,10 @@ void OTARequestor::RecordNewUpdateState(OTAUpdateStateEnum newState, OTAChangeRe // Inform the driver that the OTARequestor has entered the Idle state mOtaRequestorDriver->HandleIdleState(idleStateReason); } + else if ((mCurrentUpdateState == OTAUpdateStateEnum::kIdle) && (newState != OTAUpdateStateEnum::kIdle)) + { + mOtaRequestorDriver->HandleIdleStateExit(); + } mCurrentUpdateState = newState; } diff --git a/src/app/clusters/ota-requestor/OTARequestorDriver.h b/src/app/clusters/ota-requestor/OTARequestorDriver.h index fe2e52fabb3788..6ed7a74e0e208c 100644 --- a/src/app/clusters/ota-requestor/OTARequestorDriver.h +++ b/src/app/clusters/ota-requestor/OTARequestorDriver.h @@ -69,6 +69,13 @@ enum class IdleStateReason kInvalidSession, }; +// The current selected OTA Requestor timer to be running +enum class SelectedTimer +{ + kPeriodicQueryTimer, + kWatchdogTimer, +}; + // Interface class to abstract the OTA-related business logic. Each application // must implement this interface. All calls must be non-blocking unless stated otherwise class OTARequestorDriver @@ -87,6 +94,9 @@ class OTARequestorDriver /// Called when an error occurs at any OTA requestor operation virtual void HandleError(UpdateFailureState state, CHIP_ERROR error) = 0; + /// Called when OTA Requestor has exited the Idle state for which the driver may need to take various actions + virtual void HandleIdleStateExit() = 0; + // Called when the OTA Requestor has entered the Idle state for which the driver may need to take various actions virtual void HandleIdleState(IdleStateReason reason) = 0;