Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[OTA] Retry a CASE session establishment after session tear down #16153

Merged
merged 1 commit into from
Mar 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions src/app/clusters/ota-requestor/GenericOTARequestorDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/app/clusters/ota-requestor/GenericOTARequestorDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
28 changes: 22 additions & 6 deletions src/app/clusters/ota-requestor/OTARequestor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -585,7 +587,21 @@ void OTARequestor::OnUpdateProgressChanged(Nullable<uint8_t> 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);
Expand All @@ -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;
Expand All @@ -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()
Expand Down
7 changes: 6 additions & 1 deletion src/app/clusters/ota-requestor/OTARequestor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
12 changes: 10 additions & 2 deletions src/app/clusters/ota-requestor/OTARequestorDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/app/clusters/ota-requestor/ota-requestor-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down