diff --git a/src/app/clusters/ota-requestor/GenericOTARequestorDriver.cpp b/src/app/clusters/ota-requestor/GenericOTARequestorDriver.cpp index ebf0f1890b090d..3cad932d261dd5 100644 --- a/src/app/clusters/ota-requestor/GenericOTARequestorDriver.cpp +++ b/src/app/clusters/ota-requestor/GenericOTARequestorDriver.cpp @@ -83,11 +83,27 @@ bool GenericOTARequestorDriver::ProviderLocationsEqual(const ProviderLocationTyp void GenericOTARequestorDriver::HandleError(UpdateFailureState state, CHIP_ERROR error) {} -void GenericOTARequestorDriver::HandleIdleState() +void GenericOTARequestorDriver::HandleIdleState(IdleStateReason reason) { - // Default provider timer runs if and only if the OTARequestor's update state is kIdle. - // Must (re)start the timer every time we enter the kIdle state - StartDefaultProviderTimer(); + switch (reason) + { + case IdleStateReason::kUnknown: + ChipLogProgress(SoftwareUpdate, "Unknown idle state reason so set the periodic timer for a next attempt"); + StartDefaultProviderTimer(); + break; + case IdleStateReason::kIdle: + // There is no current OTA update in progress so start the periodic query timer + StartDefaultProviderTimer(); + break; + case IdleStateReason::kInvalidSession: + // An invalid session is detected which may be temporary so try to query again + ProviderLocationType providerLocation; + if (DetermineProviderLocation(providerLocation) == true) + { + DeviceLayer::SystemLayer().ScheduleLambda([this] { mRequestor->TriggerImmediateQueryInternal(); }); + } + break; + } } void GenericOTARequestorDriver::UpdateAvailable(const UpdateDescription & update, System::Clock::Seconds32 delay) diff --git a/src/app/clusters/ota-requestor/GenericOTARequestorDriver.h b/src/app/clusters/ota-requestor/GenericOTARequestorDriver.h index 68c3fe39010506..31a1f1e84c9a8a 100644 --- a/src/app/clusters/ota-requestor/GenericOTARequestorDriver.h +++ b/src/app/clusters/ota-requestor/GenericOTARequestorDriver.h @@ -57,7 +57,7 @@ class GenericOTARequestorDriver : public OTARequestorDriver bool CanConsent() override; uint16_t GetMaxDownloadBlockSize() override; void HandleError(UpdateFailureState state, CHIP_ERROR error) override; - void HandleIdleState() 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; void UpdateDownloaded() override; diff --git a/src/app/clusters/ota-requestor/OTARequestor.cpp b/src/app/clusters/ota-requestor/OTARequestor.cpp index 4fbc75c0d58742..bfa2d7ab07f0c0 100644 --- a/src/app/clusters/ota-requestor/OTARequestor.cpp +++ b/src/app/clusters/ota-requestor/OTARequestor.cpp @@ -198,10 +198,12 @@ void OTARequestor::OnQueryImageFailure(void * context, CHIP_ERROR error) ChipLogError(SoftwareUpdate, "Received QueryImage failure response: %" CHIP_ERROR_FORMAT, error.Format()); + // A previously valid CASE session may have become invalid if (error == CHIP_ERROR_TIMEOUT) { ChipLogError(SoftwareUpdate, "CASE session may be invalid, tear down session"); requestorCore->DisconnectFromProvider(); + error = CHIP_ERROR_CONNECTION_CLOSED_UNEXPECTEDLY; } requestorCore->RecordErrorUpdateState(UpdateFailureState::kQuerying, error); @@ -585,7 +587,21 @@ void OTARequestor::OnUpdateProgressChanged(Nullable percent) OtaRequestorServerSetUpdateStateProgress(percent); } -void OTARequestor::RecordNewUpdateState(OTAUpdateStateEnum newState, OTAChangeReasonEnum reason) +IdleStateReason OTARequestor::MapErrorToIdleStateReason(CHIP_ERROR error) +{ + if (error == CHIP_NO_ERROR) + { + return IdleStateReason::kIdle; + } + else if (error == CHIP_ERROR_CONNECTION_CLOSED_UNEXPECTEDLY) + { + return IdleStateReason::kInvalidSession; + } + + return IdleStateReason::kUnknown; +} + +void OTARequestor::RecordNewUpdateState(OTAUpdateStateEnum newState, OTAChangeReasonEnum reason, CHIP_ERROR error) { // Set server UpdateState attribute OtaRequestorServerSetUpdateState(newState); @@ -607,12 +623,12 @@ void OTARequestor::RecordNewUpdateState(OTAUpdateStateEnum newState, OTAChangeRe } OtaRequestorServerOnStateTransition(mCurrentUpdateState, newState, reason, targetSoftwareVersion); - // Inform the driver that the OTARequestor has entered the kIdle state. A driver implementation - // may choose to restart the default provider timer in this case if ((newState == OTAUpdateStateEnum::kIdle) && (mCurrentUpdateState != OTAUpdateStateEnum::kIdle)) { - // TODO: Make this API a general state change - mOtaRequestorDriver->HandleIdleState(); + IdleStateReason idleStateReason = MapErrorToIdleStateReason(error); + + // Inform the driver that the OTARequestor has entered the Idle state + mOtaRequestorDriver->HandleIdleState(idleStateReason); } mCurrentUpdateState = newState; @@ -631,7 +647,7 @@ void OTARequestor::RecordErrorUpdateState(UpdateFailureState failureState, CHIP_ OtaRequestorServerOnDownloadError(mTargetVersion, imageProcessor->GetBytesDownloaded(), progressPercent, platformCode); // Whenever an error occurs, always reset to Idle state - RecordNewUpdateState(OTAUpdateStateEnum::kIdle, reason); + RecordNewUpdateState(OTAUpdateStateEnum::kIdle, reason, error); } CHIP_ERROR OTARequestor::GenerateUpdateToken() diff --git a/src/app/clusters/ota-requestor/OTARequestor.h b/src/app/clusters/ota-requestor/OTARequestor.h index 4c195732c39586..638605ac28e67f 100644 --- a/src/app/clusters/ota-requestor/OTARequestor.h +++ b/src/app/clusters/ota-requestor/OTARequestor.h @@ -203,10 +203,15 @@ class OTARequestor : public OTARequestorInterface, public BDXDownloader::StateDe */ static void InitState(intptr_t context); + /** + * Map a CHIP_ERROR to an IdleStateReason enum type + */ + IdleStateReason MapErrorToIdleStateReason(CHIP_ERROR error); + /** * Record the new update state by updating the corresponding server attribute and logging a StateTransition event */ - void RecordNewUpdateState(OTAUpdateStateEnum newState, OTAChangeReasonEnum reason); + void RecordNewUpdateState(OTAUpdateStateEnum newState, OTAChangeReasonEnum reason, CHIP_ERROR error = CHIP_NO_ERROR); /** * Record the error update state by informing the driver of the error and calling `RecordNewUpdateState` diff --git a/src/app/clusters/ota-requestor/OTARequestorDriver.h b/src/app/clusters/ota-requestor/OTARequestorDriver.h index f67e2b7eb92c67..cb6bdb6994410f 100644 --- a/src/app/clusters/ota-requestor/OTARequestorDriver.h +++ b/src/app/clusters/ota-requestor/OTARequestorDriver.h @@ -62,6 +62,14 @@ enum class UpdateNotFoundReason ConnectionFailed, }; +// The reasons for why the OTA Requestor has entered idle state +enum class IdleStateReason +{ + kUnknown, + kIdle, + kInvalidSession, +}; + // 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 @@ -80,8 +88,8 @@ class OTARequestorDriver /// Called when an error occurs at any OTA requestor operation virtual void HandleError(UpdateFailureState state, CHIP_ERROR error) = 0; - // Called when the OTA Requestor enters the kIdle update state - virtual void HandleIdleState() = 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; /// Called when the latest query found a software update virtual void UpdateAvailable(const UpdateDescription & update, System::Clock::Seconds32 delay) = 0; diff --git a/src/app/clusters/ota-requestor/ota-requestor-server.cpp b/src/app/clusters/ota-requestor/ota-requestor-server.cpp index c853f4401679f3..a6e987cb05711c 100644 --- a/src/app/clusters/ota-requestor/ota-requestor-server.cpp +++ b/src/app/clusters/ota-requestor/ota-requestor-server.cpp @@ -200,7 +200,7 @@ void OtaRequestorServerOnStateTransition(OTAUpdateStateEnum previousState, OTAUp { if (previousState == newState) { - ChipLogError(Zcl, "Previous state and new state are the same, no event to log"); + ChipLogError(Zcl, "Previous state and new state are the same (%d), no event to log", to_underlying(newState)); return; }