Skip to content

Commit

Permalink
Provider communication service
Browse files Browse the repository at this point in the history
Add OTARequestorDriverImpl class
  • Loading branch information
ATmobica committed Dec 6, 2021
1 parent 2cbdb02 commit 346e932
Show file tree
Hide file tree
Showing 10 changed files with 540 additions and 3 deletions.
2 changes: 2 additions & 0 deletions examples/ota-requestor-app/mbed/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@ target_include_directories(${APP_TARGET} PRIVATE
${GEN_DIR}/ota-requestor-app
${APP_ROOT}/ota-requestor-common
${MBED_COMMON}/util/include
${CHIP_ROOT}/src/app/clusters/ota-requestor
)

target_sources(${APP_TARGET} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/main/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/main/ZclCallbacks.cpp
${CMAKE_CURRENT_SOURCE_DIR}/main/AppTask.cpp
${MBED_COMMON}/util/LEDWidget.cpp
${CMAKE_CURRENT_SOURCE_DIR}/main/OTARequestorImpl.cpp

${GEN_DIR}/ota-requestor-app/zap-generated/attribute-size.cpp
${GEN_DIR}/ota-requestor-app/zap-generated/callback-stub.cpp
Expand Down
46 changes: 46 additions & 0 deletions examples/ota-requestor-app/mbed/main/AppTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
#include "events/EventQueue.h"
#include "platform/Callback.h"

#include "OTARequestorImpl.h"

static bool sIsWiFiStationProvisioned = false;
static bool sIsWiFiStationEnabled = false;
static bool sIsWiFiStationConnected = false;
Expand All @@ -53,6 +55,7 @@ static bool sHaveBLEConnections = false;

static events::EventQueue sAppEventQueue;

using namespace ::chip;
using namespace ::chip::Credentials;
using namespace ::chip::DeviceLayer;

Expand Down Expand Up @@ -84,6 +87,8 @@ int AppTask::Init()
ConnectivityMgr().SetBLEAdvertisingEnabled(true);
}

OTARequestorImpl::GetInstance().Init(OnConnectProviderCallback);

chip::DeviceLayer::ConnectivityMgrImpl().StartWiFiManagement();

// Init ZCL Data Model and start server
Expand Down Expand Up @@ -157,3 +162,44 @@ int AppTask::StartApp()
sStatusLED.Animate();
}
}

void AppTask::OnConnectProviderHandler(AppEvent * aEvent)
{
ChipLogProgress(NotSpecified, "OnConnectProviderHandler");

OTARequestorImpl::GetInstance().ConnectProvider(aEvent->OTAProviderConnectEvent.nodeId,
aEvent->OTAProviderConnectEvent.fabricIndex,
aEvent->OTAProviderConnectEvent.ipAddress);
}

void AppTask::OnConnectProviderCallback(NodeId nodeId, FabricIndex fabricIndex, chip::Optional<chip::ByteSpan> ipAddress)
{
AppEvent ota_connect_provider_event;
ota_connect_provider_event.Type = AppEvent::kEventType_ota_provider_connect;
ota_connect_provider_event.OTAProviderConnectEvent.nodeId = nodeId;
ota_connect_provider_event.OTAProviderConnectEvent.fabricIndex = fabricIndex;
ota_connect_provider_event.OTAProviderConnectEvent.ipAddress = reinterpret_cast<const char *>(ipAddress.Value().data());
ota_connect_provider_event.Handler = OnConnectProviderHandler;
sAppTask.PostEvent(&ota_connect_provider_event);
}

void AppTask::PostEvent(AppEvent * aEvent)
{
auto handle = sAppEventQueue.call([event = *aEvent, this] { DispatchEvent(&event); });
if (!handle)
{
ChipLogError(NotSpecified, "Failed to post event to app task event queue: Not enough memory");
}
}

void AppTask::DispatchEvent(const AppEvent * aEvent)
{
if (aEvent->Handler)
{
aEvent->Handler(const_cast<AppEvent *>(aEvent));
}
else
{
ChipLogError(NotSpecified, "Event received with no handler. Dropping event.");
}
}
26 changes: 26 additions & 0 deletions examples/ota-requestor-app/mbed/main/OTARequestorDriverImpl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
*
* Copyright (c) 2021 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 <OTARequestorDriverImpl.h>

bool CheckImageDownloadAllowed()
{
return true;
}

void ImageDownloadComplete() {}
253 changes: 253 additions & 0 deletions examples/ota-requestor-app/mbed/main/OTARequestorImpl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
/*
*
* Copyright (c) 2021 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 <OTARequestorImpl.h>

#include <app/util/util.h>
#include <platform/CHIPDeviceLayer.h>

#include <app/OperationalDeviceProxy.h>
#include <app/server/Server.h>
#include <app/util/util.h>
#include <controller/CHIPDeviceControllerFactory.h>
#include <controller/CommissioneeDeviceProxy.h>
#include <controller/ExampleOperationalCredentialsIssuer.h>
#include <credentials/DeviceAttestationCredsProvider.h>
#include <credentials/examples/DeviceAttestationCredsExample.h>
#include <lib/support/CHIPArgParser.hpp>
#include <platform/CHIPDeviceLayer.h>
#include <zap-generated/CHIPClusters.h>

using chip::DeviceProxy;
using chip::EndpointId;
using chip::FabricIndex;
using chip::NodeId;
using chip::OnDeviceConnected;
using chip::OnDeviceConnectionFailure;
using chip::PeerId;
using chip::Server;
using chip::VendorId;
using chip::Callback::Callback;
using chip::Inet::IPAddress;
using chip::System::Layer;
using chip::Transport::PeerAddress;
using namespace chip::app::Clusters::OtaSoftwareUpdateProvider::Commands;
using namespace chip::DeviceLayer;

OTARequestorImpl OTARequestorImpl::sInstance;

constexpr const char gRequestorLocation[] = { 'U', 'S' };
constexpr EmberAfOTADownloadProtocol gRequestorProtocols[] = { EMBER_ZCL_OTA_DOWNLOAD_PROTOCOL_BDX_SYNCHRONOUS };
constexpr chip::EndpointId gOtaProviderEndpoint = 0;
constexpr bool gRequestorCanConsent = false;

static void OnProviderConnected(void * context, chip::DeviceProxy * deviceProxy);
static void OnProviderConnectionFailure(void * context, chip::NodeId deviceId, CHIP_ERROR error);

static void
OnQueryImageResponse(void * context,
const chip::app::Clusters::OtaSoftwareUpdateProvider::Commands::QueryImageResponse::DecodableType & response);
static void OnQueryImageFailure(void * context, EmberAfStatus status);

static Callback<OnDeviceConnected> mOnConnectedCallback(OnProviderConnected, nullptr);
static Callback<OnDeviceConnectionFailure> mOnConnectionFailureCallback(OnProviderConnectionFailure, nullptr);

OTARequestorImpl::OTARequestorImpl()
{
mProviderNodeId = chip::kUndefinedNodeId;
mProviderFabricIndex = chip::kUndefinedFabricIndex;
mConnectProviderCallback = nullptr;
}

bool OTARequestorImpl::HandleAnnounceOTAProvider(
chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::Clusters::OtaSoftwareUpdateRequestor::Commands::AnnounceOtaProvider::DecodableType & commandData)
{
if (commandObj == nullptr)
{
ChipLogError(SoftwareUpdate, "Cannot access get FabricIndex");
return true;
}

mProviderNodeId = commandData.providerLocation;
mProviderFabricIndex = commandObj->GetAccessingFabricIndex();
mAnnouncementReason = commandData.announcementReason;
mProviderIpAddress = commandData.metadataForNode;

ChipLogProgress(SoftwareUpdate, "OTA Requestor received AnnounceOTAProvider");
ChipLogProgress(SoftwareUpdate, " FabricIndex: %" PRIu8, mProviderFabricIndex);
ChipLogProgress(SoftwareUpdate, " ProviderNodeID: %" PRIu64 " (0x%" PRIX64 ")", mProviderNodeId, mProviderNodeId);
ChipLogProgress(SoftwareUpdate, " VendorID: %" PRIu16 " (0x%" PRIX16 ")", commandData.vendorId, commandData.vendorId);
ChipLogProgress(SoftwareUpdate, " ProviderIP: %s", reinterpret_cast<const char *>(mProviderIpAddress.Value().data()));
ChipLogProgress(SoftwareUpdate, " AnnouncementReason: %" PRIu8, mAnnouncementReason);

if (mConnectProviderCallback)
{
mConnectProviderCallback(mProviderNodeId, mProviderFabricIndex, mProviderIpAddress);
}

return false;
}

void OTARequestorImpl::ConnectProvider(NodeId peerNodeId, FabricIndex peerFabricIndex, const char * ipAddress)
{
Server * server = &(Server::GetInstance());
chip::FabricInfo * fabric = server->GetFabricTable().FindFabricWithIndex(peerFabricIndex);
if (fabric == nullptr)
{
ChipLogError(SoftwareUpdate, "Did not find fabric for index %d", peerFabricIndex);
return;
}

chip::DeviceProxyInitParams initParams = {
.sessionManager = &(server->GetSecureSessionManager()),
.exchangeMgr = &(server->GetExchangeManager()),
.idAllocator = &(server->GetSessionIDAllocator()),
.fabricInfo = fabric,
.imDelegate = chip::Platform::New<chip::Controller::DeviceControllerInteractionModelDelegate>(),
};

chip::OperationalDeviceProxy * operationalDeviceProxy =
chip::Platform::New<chip::OperationalDeviceProxy>(initParams, fabric->GetPeerIdForNode(peerNodeId));
if (operationalDeviceProxy == nullptr)
{
ChipLogError(SoftwareUpdate, "Failed in creating an instance of OperationalDeviceProxy");
return;
}

server->SetOperationalDeviceProxy(operationalDeviceProxy);

// Explicitly calling UpdateDeviceData() should not be needed once OperationalDeviceProxy can resolve IP address from node
// ID and fabric index
IPAddress ipAddr;
IPAddress::FromString(ipAddress, ipAddr);
PeerAddress addr = PeerAddress::UDP(ipAddr, CHIP_PORT);
operationalDeviceProxy->UpdateDeviceData(addr, operationalDeviceProxy->GetMRPConfig());

CHIP_ERROR err = operationalDeviceProxy->Connect(&mOnConnectedCallback, &mOnConnectionFailureCallback);
if (err != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "Cannot establish connection to peer device: %" CHIP_ERROR_FORMAT, err.Format());
}
}

void OnProviderConnected(void * context, chip::DeviceProxy * deviceProxy)
{
ChipLogProgress(SoftwareUpdate, "OnProviderConnected");
chip::Controller::OtaSoftwareUpdateProviderCluster cluster;

CHIP_ERROR err = cluster.Associate(deviceProxy, gOtaProviderEndpoint);
if (err != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "Associate() failed: %" CHIP_ERROR_FORMAT, err.Format());
return;
}

// Send QueryImage command
uint16_t vendorId = 0;
uint16_t productId = 0;
uint16_t hardwareVersion = 0;
uint16_t softwareVersion = 0;

ConfigurationMgr().GetVendorId(vendorId);
ConfigurationMgr().GetProductId(productId);
ConfigurationMgr().GetHardwareVersion(hardwareVersion);
ConfigurationMgr().GetSoftwareVersion(softwareVersion);

QueryImage::Type request;
request.vendorId = static_cast<VendorId>(vendorId);
request.productId = productId;
request.softwareVersion = softwareVersion;
request.protocolsSupported = gRequestorProtocols;
request.hardwareVersion.Emplace(hardwareVersion);
request.location.Emplace(gRequestorLocation);
request.requestorCanConsent.SetValue(gRequestorCanConsent);
err = cluster.InvokeCommand(request, /* context = */ nullptr, OnQueryImageResponse, OnQueryImageFailure);
if (err != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "QueryImage() failed: %" CHIP_ERROR_FORMAT, err.Format());
}
}

void OnProviderConnectionFailure(void * context, NodeId deviceId, CHIP_ERROR error)
{
ChipLogError(SoftwareUpdate, "Provider connection failure 0x%" PRIX64 ": %" CHIP_ERROR_FORMAT, deviceId, error.Format());
}

void OnQueryImageResponse(void * context, const QueryImageResponse::DecodableType & response)
{
ChipLogProgress(SoftwareUpdate, "OnQueryImageResponse");

OTARequestorImpl::GetInstance().HandleQueryImageResponse(
context, response.status, response.delayedActionTime.ValueOr(0), response.imageURI.ValueOr({}),
response.softwareVersion.ValueOr(0), response.softwareVersionString.ValueOr({}), response.updateToken.ValueOr({}),
response.userConsentNeeded.ValueOr(false), response.metadataForRequestor.ValueOr({}));
}

void OnQueryImageFailure(void * context, EmberAfStatus status)
{
ChipLogError(SoftwareUpdate, "QueryImage failure %" PRIu8, status);
}

bool OTARequestorImpl::HandleQueryImageResponse(void * context, uint8_t status, uint32_t delayedActionTime, chip::CharSpan imageURI,
uint32_t softwareVersion, chip::CharSpan softwareVersionString,
chip::ByteSpan updateToken, bool userConsentNeeded,
chip::ByteSpan metadataForRequestor)
{
switch (status)
{
case EMBER_ZCL_OTA_QUERY_STATUS_UPDATE_AVAILABLE:
ChipLogProgress(SoftwareUpdate, "Update available");
break;
case EMBER_ZCL_OTA_QUERY_STATUS_BUSY:
ChipLogProgress(SoftwareUpdate, "OTA provider busy");
return false;
case EMBER_ZCL_OTA_QUERY_STATUS_NOT_AVAILABLE:
ChipLogProgress(SoftwareUpdate, "Update not available");
return false;
}

ChipLogProgress(SoftwareUpdate, " Image URI: %.*s", static_cast<int>(imageURI.size()), imageURI.data());
ChipLogProgress(SoftwareUpdate, " Version: %" PRIu32, softwareVersion);

if (updateToken.size() > sizeof(mUpdateDetails.updateToken))
{
ChipLogError(SoftwareUpdate, "Update token too long");
return false;
}

memcpy(mUpdateDetails.updateToken, updateToken.data(), updateToken.size());
mUpdateDetails.updateTokenLen = static_cast<uint8_t>(updateToken.size());
mUpdateDetails.updateVersion = softwareVersion;

if (mOtaRequestorDriver)
{
auto ret = mOtaRequestorDriver->CheckImageDownloadAllowed();
if (!ret)
{
ChipLogProgress(SoftwareUpdate, "Update download not allowed");
return false;
}
}

if (mDownloadUpdateCallback)
{
mDownloadUpdateCallback();
}
return false;
}
12 changes: 11 additions & 1 deletion examples/ota-requestor-app/mbed/main/ZclCallbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <app/ConcreteAttributePath.h>
#include <lib/support/logging/CHIPLogging.h>

#include "OTARequestorImpl.h"
#include <app-common/zap-generated/cluster-objects.h>
#include <app/CommandHandler.h>
#include <app/util/af.h>
Expand All @@ -41,6 +42,15 @@ bool emberAfOtaSoftwareUpdateRequestorClusterAnnounceOtaProviderCallback(
chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::Clusters::OtaSoftwareUpdateRequestor::Commands::AnnounceOtaProvider::DecodableType & commandData)
{
emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);

EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS;

auto ret = OTARequestorImpl::GetInstance().HandleAnnounceOTAProvider(commandObj, commandPath, commandData);
if (ret)
{
status = EMBER_ZCL_STATUS_FAILURE;
}

emberAfSendImmediateDefaultResponse(status);
return true;
}
Loading

0 comments on commit 346e932

Please sign in to comment.