Skip to content
Open
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: 23 additions & 1 deletion examples/ota-provider-app/linux/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <json/json.h>
#include <ota-provider-common/BdxOtaSender.h>
#include <ota-provider-common/OTAProviderExample.h>
#include <ota-provider-common/OtaProviderAppCommandDelegate.h>

#include "AppMain.h"

Expand Down Expand Up @@ -59,6 +60,8 @@ constexpr uint16_t kOptionIgnoreApplyUpdate = 'y';
constexpr uint16_t kOptionPollInterval = 'P';

OTAProviderExample gOtaProvider;
NamedPipeCommands sChipNamedPipeCommands;
OtaProviderAppCommandDelegate sOtaProviderAppCommandDelegate;
chip::ota::DefaultOTAProviderUserConsent gUserConsentProvider;

// Global variables used for passing the CLI arguments to the OTAProviderExample object
Expand Down Expand Up @@ -403,9 +406,28 @@ void ApplicationInit()
}

chip::app::Clusters::OTAProvider::SetDelegate(kOtaProviderEndpoint, &gOtaProvider);

std::string path = std::string(LinuxDeviceOptions::GetInstance().app_pipe);
std::string path_out = std::string(LinuxDeviceOptions::GetInstance().app_pipe_out);

if ((!path.empty()) and (sChipNamedPipeCommands.Start(path, path_out, &sOtaProviderAppCommandDelegate) != CHIP_NO_ERROR))
{
ChipLogError(NotSpecified, "Failed to start CHIP NamedPipeCommand");
sChipNamedPipeCommands.Stop();
}
else
{
sOtaProviderAppCommandDelegate.SetPipes(&sChipNamedPipeCommands);
}
}

void ApplicationShutdown() {}
void ApplicationShutdown()
{
if (sChipNamedPipeCommands.Stop() != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed to stop CHIP NamedPipeCommands");
}
}

namespace {
class OtaProviderAppMainLoopImplementation : public AppMainLoopImplementation
Expand Down
8 changes: 7 additions & 1 deletion examples/ota-provider-app/ota-provider-common/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import("//build_overrides/chip.gni")
import("${chip_root}/src/app/chip_data_model.gni")

config("config") {
include_dirs = [ ".." ]
include_dirs = [
"..",
"${chip_root}/examples/platform/linux",
]
}

chip_data_model("ota-provider-common") {
Expand All @@ -28,9 +31,12 @@ chip_data_model("ota-provider-common") {
"BdxOtaSender.h",
"OTAProviderExample.cpp",
"OTAProviderExample.h",
"OtaProviderAppCommandDelegate.cpp",
"OtaProviderAppCommandDelegate.h",
]

deps = [
"${chip_root}/examples/platform/linux:app-main",
"${chip_root}/src/app/clusters/ota-provider:user-consent",
"${chip_root}/src/protocols/bdx",
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,11 +322,35 @@ void OTAProviderExample::SendQueryImageResponse(app::CommandHandler * commandObj
commandObj->AddResponse(commandPath, response);
}

void OTAProviderExample::SaveCommandSnapshot(const QueryImage::DecodableType & commandData)
{
mVendorId = commandData.vendorID;
mProductId = commandData.productID;
mHardwareVersion = commandData.hardwareVersion.ValueOr(0);
mSoftwareVersion = commandData.softwareVersion;
mRequestorCanConsent = commandData.requestorCanConsent.ValueOr(false);

mLocation.clear();
if (commandData.location.HasValue())
{
mLocation = NullTerminated(commandData.location.Value()).c_str();
}

mProtocolsSupported.clear();
auto iter = commandData.protocolsSupported.begin();
while (iter.Next())
{
mProtocolsSupported.push_back(iter.GetValue());
}
}

void OTAProviderExample::HandleQueryImage(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath,
const QueryImage::DecodableType & commandData)
{
bool requestorCanConsent = commandData.requestorCanConsent.ValueOr(false);

SaveCommandSnapshot(commandData);

if (mIgnoreQueryImageCount > 0)
{
ChipLogDetail(SoftwareUpdate, "Skip sending QueryImageResponse, ignore count: %" PRIu32, mIgnoreQueryImageCount);
Expand Down
41 changes: 27 additions & 14 deletions examples/ota-provider-app/ota-provider-common/OTAProviderExample.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,19 @@
/**
* A reference implementation for an OTA Provider. Includes a method for providing a path to a local OTA file to serve.
*/
class OTAProviderExample : public chip::app::Clusters::OTAProviderDelegate
{
class OTAProviderExample : public chip::app::Clusters::OTAProviderDelegate {
public:
OTAProviderExample();

using OTAQueryStatus = chip::app::Clusters::OtaSoftwareUpdateProvider::OTAQueryStatus;
using OTAQueryStatus = chip::app::Clusters::OtaSoftwareUpdateProvider::OTAQueryStatus;
using OTAApplyUpdateAction = chip::app::Clusters::OtaSoftwareUpdateProvider::OTAApplyUpdateAction;

static constexpr uint16_t SW_VER_STR_MAX_LEN = 64;
static constexpr uint16_t OTA_URL_MAX_LEN = 512;
static constexpr size_t kFilepathBufLen = 256;
static constexpr size_t kUriMaxLen = 256;
static constexpr uint16_t OTA_URL_MAX_LEN = 512;
static constexpr size_t kFilepathBufLen = 256;
static constexpr size_t kUriMaxLen = 256;

typedef struct DeviceSoftwareVersionModel
{
typedef struct DeviceSoftwareVersionModel {
chip::VendorId vendorId;
uint16_t productId;
uint32_t softwareVersion;
Expand Down Expand Up @@ -91,15 +89,23 @@ class OTAProviderExample : public chip::app::Clusters::OTAProviderDelegate

void SetMaxBDXBlockSize(uint16_t blockSize) { mMaxBDXBlockSize = blockSize; }

uint32_t GetVendorId() { return mVendorId; }
uint32_t GetProductId() { return mProductId; }
short unsigned int GetHardwareVersion() { return mHardwareVersion; }
uint32_t GetSoftwareVersion() { return mSoftwareVersion; }
std::vector<chip::app::Clusters::OtaSoftwareUpdateProvider::DownloadProtocolEnum> GetProtocolsSupported() { return mProtocolsSupported; }
bool GetRequestorCanConsent() { return mRequestorCanConsent; }
std::string GetLocation() { return mLocation; }

private:
bool SelectOTACandidate(const uint16_t requestorVendorID, const uint16_t requestorProductID,
const uint32_t requestorSoftwareVersion,
OTAProviderExample::DeviceSoftwareVersionModel & finalCandidate);
const uint32_t requestorSoftwareVersion,
OTAProviderExample::DeviceSoftwareVersionModel & finalCandidate);

chip::ota::UserConsentSubject
GetUserConsentSubject(const chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::Clusters::OtaSoftwareUpdateProvider::Commands::QueryImage::DecodableType & commandData,
uint32_t targetVersion);
const chip::app::Clusters::OtaSoftwareUpdateProvider::Commands::QueryImage::DecodableType & commandData,
uint32_t targetVersion);

bool ParseOTAHeader(chip::OTAImageHeaderParser & parser, const char * otaFilePath, chip::OTAImageHeader & header);

Expand All @@ -108,7 +114,8 @@ class OTAProviderExample : public chip::app::Clusters::OTAProviderDelegate
*/
void
SendQueryImageResponse(chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::Clusters::OtaSoftwareUpdateProvider::Commands::QueryImage::DecodableType & commandData);
const chip::app::Clusters::OtaSoftwareUpdateProvider::Commands::QueryImage::DecodableType & commandData);
void SaveCommandSnapshot(const chip::app::Clusters::OtaSoftwareUpdateProvider::Commands::QueryImage::DecodableType & commandData);

BdxOtaSender mBdxOtaSender;
std::vector<DeviceSoftwareVersionModel> mCandidates;
Expand All @@ -122,8 +129,14 @@ class OTAProviderExample : public chip::app::Clusters::OTAProviderDelegate
uint32_t mDelayedApplyActionTimeSec;
chip::ota::OTAProviderUserConsentDelegate * mUserConsentDelegate;
bool mUserConsentNeeded;
uint32_t mSoftwareVersion;
char mSoftwareVersionString[SW_VER_STR_MAX_LEN];
uint32_t mPollInterval;
uint16_t mMaxBDXBlockSize;
uint32_t mVendorId;
uint32_t mProductId;
short unsigned int mHardwareVersion;
uint32_t mSoftwareVersion;
std::vector<chip::app::Clusters::OtaSoftwareUpdateProvider::DownloadProtocolEnum> mProtocolsSupported;
bool mRequestorCanConsent;
std::string mLocation;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
*json
* Copyright (c) 2025 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "OtaProviderAppCommandDelegate.h"
#include <platform/PlatformManager.h>

using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::DeviceLayer;

OtaProviderAppCommandHandler * OtaProviderAppCommandHandler::FromJSON(const char * json, OtaProviderAppCommandDelegate * delegate)
{
Json::Reader reader;
Json::Value value;

if (!reader.parse(json, value))
{
ChipLogError(NotSpecified,
"OTA Provider Example: Error parsing JSON with error %s:", reader.getFormattedErrorMessages().c_str());
return nullptr;
}

if (value.empty() || !value.isObject())
{
ChipLogError(NotSpecified, "OTA Provider Example: Invalid JSON command received");
return nullptr;
}

if (!value.isMember("Name") || !value["Name"].isString())
{
ChipLogError(NotSpecified, "OTA Provider Example: Invalid JSON command received: command name is missing");
return nullptr;
}

return chip::Platform::New<OtaProviderAppCommandHandler>(std::move(value), delegate);
}

static std::string ToString(const Json::Value & v)
{
Json::StreamWriterBuilder w;
w["identation"] = "";
return Json::writeString(w, v);
}

Json::Value OtaProviderAppCommandHandler::BuildOtaProviderSnapshot(uint16_t endpoint)
{
Json::Value payload(Json::objectValue);

payload["VendorID"] = gOtaProvider.GetVendorId();
payload["ProductID"] = gOtaProvider.GetProductId();
payload["SoftwareVersion"] = gOtaProvider.GetSoftwareVersion();
payload["HardwareVersion"] = gOtaProvider.GetHardwareVersion();
payload["Location"] = gOtaProvider.GetLocation();
payload["RequestorCanConsent"] = gOtaProvider.GetRequestorCanConsent();

const auto & protos = gOtaProvider.GetProtocolsSupported();

Json::Value arr(Json::arrayValue);
for (chip::app::Clusters::OtaSoftwareUpdateProvider::DownloadProtocolEnum p : protos)
{
arr.append(Json::UInt(p));
}
payload["ProtocolsSupported"] = arr;

return payload;
}

void OtaProviderAppCommandHandler::HandleCommand(intptr_t context)
{
auto * self = reinterpret_cast<OtaProviderAppCommandHandler *>(context);
std::string name;
std::string cluster;
uint16_t endpoint = 0;
OtaProviderAppCommandDelegate * delegate = nullptr;

VerifyOrExit(!self->mJsonValue.empty(), ChipLogError(NotSpecified, "Invalid JSON event command received"));

name = self->mJsonValue["Name"].asString();
cluster = self->mJsonValue.get("Cluster", "").asString();
endpoint = static_cast<uint16_t>(self->mJsonValue.get("Endpoint", 0).asUInt());
delegate = self->mDelegate;

if (name == "QueryImageSnapshot")
{
Json::Value out(Json::objectValue);
out["Name"] = "SnapshotResponse";
out["Cluster"] = cluster;
out["Endpoint"] = endpoint;

if (cluster == "OtaSoftwareUpdateProvider")
{
out["Payload"] = self->BuildOtaProviderSnapshot(endpoint);
}
else
{
out["Error"] = "Unsupported cluster for snapshot";
}

if (delegate && delegate->GetPipes())
{
delegate->GetPipes()->WriteToOutPipe(ToString(out));
}
}
else
{
ChipLogError(NotSpecified, "Unhandled command: Should never happen");
}

exit:
chip::Platform::Delete(self);
}

void OtaProviderAppCommandDelegate::OnEventCommandReceived(const char * json)
{
auto handler = OtaProviderAppCommandHandler::FromJSON(json, this);
if (nullptr == handler)
{
ChipLogError(NotSpecified, "OTA Provider App: Unable to instantiate a command handler");
return;
}

chip::DeviceLayer::PlatformMgr().ScheduleWork(OtaProviderAppCommandHandler::HandleCommand, reinterpret_cast<intptr_t>(handler));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
*
* Copyright (c) 2025 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#include "NamedPipeCommands.h"

#include <json/json.h>
#include <ota-provider-common/OTAProviderExample.h>
#include <platform/DiagnosticDataProvider.h>

#include <string>

class OtaProviderAppCommandDelegate;
extern OTAProviderExample gOtaProvider;

class OtaProviderAppCommandHandler {
public:
static OtaProviderAppCommandHandler * FromJSON(const char * json, OtaProviderAppCommandDelegate * delegate);

static void HandleCommand(intptr_t context);
Json::Value BuildOtaProviderSnapshot(uint16_t endpoint);

OtaProviderAppCommandHandler(Json::Value && v, OtaProviderAppCommandDelegate * d)
: mJsonValue(std::move(v))
, mDelegate(d)
{
}

private:
Json::Value mJsonValue;
OtaProviderAppCommandDelegate * mDelegate = nullptr;
};

class OtaProviderAppCommandDelegate : public NamedPipeCommandDelegate {
public:
void OnEventCommandReceived(const char * json) override;
void SetPipes(NamedPipeCommands * pipes) { mPipes = pipes; }
NamedPipeCommands * GetPipes() const { return mPipes; }

private:
NamedPipeCommands * mPipes = nullptr;
};
Loading
Loading