Skip to content

Commit

Permalink
[ota] Automatic NotifyUpdateApplied (#16103)
Browse files Browse the repository at this point in the history
* [ota] Automatic NotifyUpdateApplied

Add two methods to OTAImageProcessor:
- IsFirstImageRun() to determine if the currently running
  image is executed for the first time
- ConfirmCurrentImage() to confirm that the current image
  is stable and the update can be left permanently.

Add storing and loading the current provider and the
update token in OTARequestor.

Then extend GenericOTARequestorDriver to automatically
confirm the current image and send NotifyUpdateApplied
command to the last provider.

* Fix build
  • Loading branch information
Damian-Nordic authored Mar 14, 2022
1 parent 38daa65 commit d86a7d4
Show file tree
Hide file tree
Showing 22 changed files with 128 additions and 34 deletions.
2 changes: 1 addition & 1 deletion examples/ota-requestor-app/p6/src/AppTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ CHIP_ERROR AppTask::Init()
chip::OTARequestorInterface * requestor = chip::GetRequestorInstance();
if (requestor != nullptr)
{
requestor->NotifyUpdateApplied(savedSoftwareVersion);
requestor->NotifyUpdateApplied();
}
}

Expand Down
30 changes: 25 additions & 5 deletions src/app/clusters/ota-requestor/GenericOTARequestorDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,41 @@ namespace chip {
namespace DeviceLayer {
namespace {

constexpr uint32_t kDelayQueryUponCommissioningSec = 30; // Delay before sending the initial image query after commissioning
constexpr uint32_t kImmediateStartDelaySec = 1; // Delay before sending a query in response to UrgentUpdateAvailable

using namespace app::Clusters::OtaSoftwareUpdateRequestor;
using namespace app::Clusters::OtaSoftwareUpdateRequestor::Structs;

constexpr uint32_t kDelayQueryUponCommissioningSec = 30; // Delay before sending the initial image query after commissioning
constexpr uint32_t kImmediateStartDelaySec = 1; // Delay before sending a query in response to UrgentUpdateAvailable
constexpr System::Clock::Seconds32 kDefaultDelayedActionTime = System::Clock::Seconds32(120);

GenericOTARequestorDriver * ToDriver(void * context)
{
return static_cast<GenericOTARequestorDriver *>(context);
}

constexpr System::Clock::Seconds32 kDefaultDelayedActionTime = System::Clock::Seconds32(120);

} // namespace

void GenericOTARequestorDriver::Init(OTARequestorInterface * requestor, OTAImageProcessorInterface * processor)
{
mRequestor = requestor;
mImageProcessor = processor;

if (mImageProcessor->IsFirstImageRun())
{
SystemLayer().ScheduleLambda([this] {
CHIP_ERROR error = mImageProcessor->ConfirmCurrentImage();

if (error != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "Failed to confirm image: %" CHIP_ERROR_FORMAT, error.Format());
return;
}

mRequestor->NotifyUpdateApplied();
});
}
}

bool GenericOTARequestorDriver::CanConsent()
{
return false;
Expand Down
17 changes: 8 additions & 9 deletions src/app/clusters/ota-requestor/GenericOTARequestorDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,16 @@ namespace DeviceLayer {
class GenericOTARequestorDriver : public OTARequestorDriver
{
public:
//// Public API methods
/**
* Called to perform some initialization including:
* - Set the OTA requestor instance used to direct download progress
* - Set the OTA image processor instance used to apply/abort the downloaded image
* Initialize OTA requestor driver.
*
* Set OTA requestor instance to be controlled by the driver, and OTA image processor, used to
* apply/abort the downloaded image.
*
* Additionally, if the current image is executed for the first time, approve the current image
* to make the update permanent, and send NotifyUpdateApplied command to the last OTA provider.
*/
void Init(OTARequestorInterface * requestor, OTAImageProcessorInterface * processor)
{
mRequestor = requestor;
mImageProcessor = processor;
}
void Init(OTARequestorInterface * requestor, OTAImageProcessorInterface * processor);

// Set the timeout (in seconds) for querying providers on the default OTA provider list; must be non-zero
void SetPeriodicQueryTimeout(uint32_t timeout)
Expand Down
24 changes: 19 additions & 5 deletions src/app/clusters/ota-requestor/OTARequestor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ void OTARequestor::OnQueryImageResponse(void * context, const QueryImageResponse
memcpy(fileDesignator.data(), update.fileDesignator.data(), update.fileDesignator.size());
fileDesignator.reduce_size(update.fileDesignator.size());
requestorCore->mFileDesignator = fileDesignator;
requestorCore->StoreCurrentUpdateInfo();

requestorCore->mOtaRequestorDriver->UpdateAvailable(update,
System::Clock::Seconds32(response.delayedActionTime.ValueOr(0)));
Expand Down Expand Up @@ -508,11 +509,8 @@ void OTARequestor::ApplyUpdate()
ConnectToProvider(kApplyUpdate);
}

void OTARequestor::NotifyUpdateApplied(uint32_t version)
void OTARequestor::NotifyUpdateApplied()
{
// New version is executing so update where applicable
mCurrentVersion = version;

// Log the VersionApplied event
uint16_t productId;
if (DeviceLayer::ConfigurationMgr().GetProductId(productId) != CHIP_NO_ERROR)
Expand All @@ -522,7 +520,7 @@ void OTARequestor::NotifyUpdateApplied(uint32_t version)
return;
}

OtaRequestorServerOnVersionApplied(version, productId);
OtaRequestorServerOnVersionApplied(mCurrentVersion, productId);

// There is no response for a notify so consider this OTA complete
RecordNewUpdateState(OTAUpdateStateEnum::kIdle, OTAChangeReasonEnum::kSuccess);
Expand Down Expand Up @@ -795,6 +793,22 @@ CHIP_ERROR OTARequestor::SendNotifyUpdateAppliedRequest(OperationalDeviceProxy &
return cluster.InvokeCommand(args, this, OnNotifyUpdateAppliedResponse, OnNotifyUpdateAppliedFailure);
}

void OTARequestor::StoreCurrentUpdateInfo()
{
// TODO: change OTA requestor storage interface to store both values at once
CHIP_ERROR error = mStorage->StoreCurrentProviderLocation(mProviderLocation.Value());

if (error == CHIP_NO_ERROR)
{
mStorage->StoreUpdateToken(mUpdateToken);
}

if (error != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "Failed to store current update: %" CHIP_ERROR_FORMAT, error.Format());
}
}

// Invoked when the device becomes commissioned
void OTARequestor::OnCommissioningCompleteRequestor(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg)
{
Expand Down
24 changes: 20 additions & 4 deletions src/app/clusters/ota-requestor/OTARequestor.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class OTARequestor : public OTARequestorInterface, public BDXDownloader::StateDe
void ApplyUpdate() override;

// Initiate the session to send NotifyUpdateApplied command
void NotifyUpdateApplied(uint32_t version) override;
void NotifyUpdateApplied() override;

// Get image update progress in percents unit
CHIP_ERROR GetUpdateProgress(EndpointId endpointId, app::DataModel::Nullable<uint8_t> & progress) override;
Expand Down Expand Up @@ -114,12 +114,23 @@ class OTARequestor : public OTARequestorInterface, public BDXDownloader::StateDe
mOtaRequestorDriver = &driver;
mBdxDownloader = &downloader;

uint32_t version;
ReturnErrorOnFailure(DeviceLayer::ConfigurationMgr().GetSoftwareVersion(version));
mCurrentVersion = version;
ReturnErrorOnFailure(DeviceLayer::ConfigurationMgr().GetSoftwareVersion(mCurrentVersion));

storage.LoadDefaultProviders(mDefaultOtaProviderList);

ProviderLocationType providerLocation;
if (storage.LoadCurrentProviderLocation(providerLocation) == CHIP_NO_ERROR)
{
mProviderLocation.SetValue(providerLocation);
}

MutableByteSpan updateToken(mUpdateTokenBuffer);
if (storage.LoadUpdateToken(updateToken) == CHIP_NO_ERROR)
{
mUpdateToken = updateToken;
}


// Schedule the initializations that needs to be performed in the CHIP context
DeviceLayer::PlatformMgr().ScheduleWork(InitState, reinterpret_cast<intptr_t>(this));

Expand Down Expand Up @@ -270,6 +281,11 @@ class OTARequestor : public OTARequestorInterface, public BDXDownloader::StateDe
*/
CHIP_ERROR SendNotifyUpdateAppliedRequest(OperationalDeviceProxy & deviceProxy);

/**
* Store current update information to KVS
*/
void StoreCurrentUpdateInfo();

/**
* Session connection callbacks
*/
Expand Down
2 changes: 1 addition & 1 deletion src/app/clusters/ota-requestor/OTARequestorInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ class OTARequestorInterface
virtual void ApplyUpdate() = 0;

// Initiate the session to send NotifyUpdateApplied command
virtual void NotifyUpdateApplied(uint32_t version) = 0;
virtual void NotifyUpdateApplied() = 0;

// Get image update progress in percents unit
virtual CHIP_ERROR GetUpdateProgress(EndpointId endpointId, chip::app::DataModel::Nullable<uint8_t> & progress) = 0;
Expand Down
11 changes: 11 additions & 0 deletions src/include/platform/OTAImageProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@ class DLL_EXPORT OTAImageProcessorInterface
*/
virtual uint64_t GetBytesDownloaded() { return mParams.downloadedBytes; }

/**
* Called to check if the current image is executed for the first time.
*/
virtual bool IsFirstImageRun() = 0;

/**
* Called to confirm the current image in case it is running tentatively after applying
* a software update.
*/
virtual CHIP_ERROR ConfirmCurrentImage() = 0;

protected:
OTAImageProgress mParams;
};
Expand Down
7 changes: 2 additions & 5 deletions src/lib/shell/commands/Ota.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,9 @@ CHIP_ERROR ApplyImageHandler(int argc, char ** argv)
CHIP_ERROR NotifyImageHandler(int argc, char ** argv)
{
VerifyOrReturnError(GetRequestorInstance() != nullptr, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(argc == 1, CHIP_ERROR_INVALID_ARGUMENT);

const intptr_t version = static_cast<intptr_t>(strtoul(argv[0], nullptr, 10));
VerifyOrReturnError(argc == 0, CHIP_ERROR_INVALID_ARGUMENT);

PlatformMgr().ScheduleWork([](intptr_t arg) { GetRequestorInstance()->NotifyUpdateApplied(static_cast<uint32_t>(arg)); },
version);
PlatformMgr().ScheduleWork([](intptr_t) { GetRequestorInstance()->NotifyUpdateApplied(); });
return CHIP_NO_ERROR;
}

Expand Down
5 changes: 3 additions & 2 deletions src/platform/Ameba/AmebaOTAImageProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,8 +374,9 @@ void AmebaOTAImageProcessor::HandleApply(intptr_t context)
OTARequestorInterface * requestor = chip::GetRequestorInstance();
if (requestor != nullptr)
{
// TODO: Use software version from Configuration Manager
requestor->NotifyUpdateApplied(imageProcessor->mSoftwareVersion);
// TODO: Implement restarting into new image instead of changing the version
DeviceLayer::ConfigurationMgr().StoreSoftwareVersion(imageProcessor->mSoftwareVersion);
requestor->NotifyUpdateApplied();
}

// Reboot
Expand Down
2 changes: 2 additions & 0 deletions src/platform/Ameba/AmebaOTAImageProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class AmebaOTAImageProcessor : public OTAImageProcessorInterface
CHIP_ERROR Apply() override;
CHIP_ERROR Abort() override;
CHIP_ERROR ProcessBlock(ByteSpan & block) override;
bool IsFirstImageRun() override { return false; }
CHIP_ERROR ConfirmCurrentImage() override { return CHIP_NO_ERROR; }
void SetOTADownloader(OTADownloader * downloader) { mDownloader = downloader; }

private:
Expand Down
2 changes: 2 additions & 0 deletions src/platform/CYW30739/OTAImageProcessorImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface
CHIP_ERROR Apply() override;
CHIP_ERROR Abort() override;
CHIP_ERROR ProcessBlock(ByteSpan & block) override;
bool IsFirstImageRun() override { return false; }
CHIP_ERROR ConfirmCurrentImage() override { return CHIP_NO_ERROR; }

void SetOTADownloader(OTADownloader * downloader) { mDownloader = downloader; }

Expand Down
2 changes: 2 additions & 0 deletions src/platform/EFR32/OTAImageProcessorImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface
CHIP_ERROR Apply() override;
CHIP_ERROR Abort() override;
CHIP_ERROR ProcessBlock(ByteSpan & block) override;
bool IsFirstImageRun() override { return false; }
CHIP_ERROR ConfirmCurrentImage() override { return CHIP_NO_ERROR; }

void SetOTADownloader(OTADownloader * downloader) { mDownloader = downloader; }
void SetOTAImageFile(CharSpan name) { mImageFile = name; }
Expand Down
2 changes: 2 additions & 0 deletions src/platform/ESP32/OTAImageProcessorImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface
CHIP_ERROR Abort() override;
CHIP_ERROR ProcessBlock(ByteSpan & block) override;
void SetOTADownloader(OTADownloader * downloader) { mDownloader = downloader; };
bool IsFirstImageRun() override { return false; }
CHIP_ERROR ConfirmCurrentImage() override { return CHIP_NO_ERROR; }

private:
static void HandlePrepareDownload(intptr_t context);
Expand Down
5 changes: 3 additions & 2 deletions src/platform/Linux/OTAImageProcessorImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@ void OTAImageProcessorImpl::HandleApply(intptr_t context)
OTARequestorInterface * requestor = chip::GetRequestorInstance();
if (requestor != nullptr)
{
// TODO: Use software version from Configuration Manager
requestor->NotifyUpdateApplied(imageProcessor->mSoftwareVersion);
// TODO: Implement restarting into new image instead of changing the version
DeviceLayer::ConfigurationMgr().StoreSoftwareVersion(imageProcessor->mSoftwareVersion);
requestor->NotifyUpdateApplied();
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/platform/Linux/OTAImageProcessorImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface
CHIP_ERROR Apply() override;
CHIP_ERROR Abort() override;
CHIP_ERROR ProcessBlock(ByteSpan & block) override;
bool IsFirstImageRun() override { return false; }
CHIP_ERROR ConfirmCurrentImage() override { return CHIP_NO_ERROR; }

void SetOTADownloader(OTADownloader * downloader) { mDownloader = downloader; }
void SetOTAImageFile(CharSpan name) { mImageFile = name; }
Expand Down
2 changes: 2 additions & 0 deletions src/platform/P6/OTAImageProcessorImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface
CHIP_ERROR Apply() override;
CHIP_ERROR Abort() override;
CHIP_ERROR ProcessBlock(ByteSpan & block) override;
bool IsFirstImageRun() override { return false; }
CHIP_ERROR ConfirmCurrentImage() override { return CHIP_NO_ERROR; }

void SetOTADownloader(OTADownloader * downloader) { mDownloader = downloader; }

Expand Down
2 changes: 2 additions & 0 deletions src/platform/cc13x2_26x2/OTAImageProcessorImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface
CHIP_ERROR Apply() override;
CHIP_ERROR Abort() override;
CHIP_ERROR ProcessBlock(ByteSpan & block) override;
bool IsFirstImageRun() override { return false; }
CHIP_ERROR ConfirmCurrentImage() override { return CHIP_NO_ERROR; }

void SetOTADownloader(OTADownloader * downloader) { mDownloader = downloader; }

Expand Down
4 changes: 4 additions & 0 deletions src/platform/mbed/OTAImageProcessorImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface
*/
CHIP_ERROR ProcessBlock(ByteSpan & block);

bool IsFirstImageRun() override { return false; }

CHIP_ERROR ConfirmCurrentImage() override { return CHIP_NO_ERROR; }

/**
* Check if memory for update image is works correctly.
*/
Expand Down
11 changes: 11 additions & 0 deletions src/platform/nrfconnect/OTAImageProcessorImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include <dfu/dfu_target.h>
#include <dfu/dfu_target_mcuboot.h>
#include <dfu/mcuboot.h>
#include <sys/reboot.h>

namespace chip {
Expand Down Expand Up @@ -100,6 +101,16 @@ CHIP_ERROR OTAImageProcessorImpl::ProcessBlock(ByteSpan & block)
});
}

bool OTAImageProcessorImpl::IsFirstImageRun()
{
return mcuboot_swap_type() == BOOT_SWAP_TYPE_REVERT;
}

CHIP_ERROR OTAImageProcessorImpl::ConfirmCurrentImage()
{
return System::MapErrorZephyr(boot_write_img_confirmed());
}

CHIP_ERROR OTAImageProcessorImpl::ProcessHeader(ByteSpan & block)
{
if (mHeaderParser.IsInitialized())
Expand Down
2 changes: 2 additions & 0 deletions src/platform/nrfconnect/OTAImageProcessorImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface
CHIP_ERROR Abort() override;
CHIP_ERROR Apply() override;
CHIP_ERROR ProcessBlock(ByteSpan & block) override;
bool IsFirstImageRun() override;
CHIP_ERROR ConfirmCurrentImage() override;

private:
CHIP_ERROR PrepareDownloadImpl();
Expand Down
2 changes: 2 additions & 0 deletions src/platform/nxp/k32w/k32w0/OTAImageProcessorImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface
CHIP_ERROR Apply() override;
CHIP_ERROR Abort() override;
CHIP_ERROR ProcessBlock(ByteSpan & block) override;
bool IsFirstImageRun() override { return false; }
CHIP_ERROR ConfirmCurrentImage() override { return CHIP_NO_ERROR; }

void SetOTADownloader(OTADownloader * downloader) { mDownloader = downloader; }
void SetOTAImageFile(CharSpan name) { mImageFile = name; }
Expand Down
2 changes: 2 additions & 0 deletions src/platform/qpg/OTAImageProcessorImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface
CHIP_ERROR Apply() override;
CHIP_ERROR Abort() override;
CHIP_ERROR ProcessBlock(ByteSpan & block) override;
bool IsFirstImageRun() override { return false; }
CHIP_ERROR ConfirmCurrentImage() override { return CHIP_NO_ERROR; }

void SetOTADownloader(OTADownloader * downloader) { mDownloader = downloader; }

Expand Down

0 comments on commit d86a7d4

Please sign in to comment.