blob: c343661a0af912b00f6a113772f8c4e9ece3c937 [file] [log] [blame]
/*
*
* 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 <app-common/zap-generated/att-storage.h>
#include <app-common/zap-generated/attribute-id.h>
#include <app-common/zap-generated/attribute-type.h>
#include <app-common/zap-generated/callback.h>
#include <app-common/zap-generated/cluster-id.h>
#include <app-common/zap-generated/command-id.h>
#include <app-common/zap-generated/enums.h>
#include <app/CommandHandler.h>
#include <app/util/af.h>
#include <lib/support/Span.h>
#include "ota-provider-delegate.h"
#include "ota-provider.h"
using namespace chip;
using chip::app::clusters::OTAProviderDelegate;
namespace {
constexpr size_t kMaxMetadataLen = 512; // The maximum length of Metadata in any OTA Provider command
constexpr size_t kUpdateTokenMaxLength = 32; // The expected length of the Update Token parameter used in multiple commands
constexpr size_t kUpdateTokenMinLength = 8; // The expected length of the Update Token parameter used in multiple commands
OTAProviderDelegate * gDelegateTable[EMBER_AF_OTA_PROVIDER_CLUSTER_SERVER_ENDPOINT_COUNT] = { nullptr };
OTAProviderDelegate * GetDelegate(EndpointId endpoint)
{
uint16_t ep = emberAfFindClusterServerEndpointIndex(endpoint, ZCL_OTA_PROVIDER_CLUSTER_ID);
return (ep == 0xFFFF ? NULL : gDelegateTable[ep]);
}
bool SendStatusIfDelegateNull(EndpointId endpoint)
{
if (GetDelegate(endpoint) == nullptr)
{
ChipLogError(Zcl, "No OTAProviderDelegate set for ep:%" PRIu16, endpoint);
emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_UNSUP_COMMAND);
return true;
}
return false;
}
} // namespace
/**
* @brief OTA Software Update Provider Cluster ApplyUpdateRequest Command callback
*
* @note It is the application's reponsibility to send the ApplyUpdateRequestResponse command after this is handled.
*
* @param updateToken Identifier for the Software Image to be applied. Should be 32 octets long.
* @param newVersion The SoftwareVersion value of the new Software Image that the client is ready to apply.
*/
bool emberAfOtaSoftwareUpdateProviderClusterApplyUpdateRequestCallback(EndpointId endpoint, app::CommandHandler * commandObj,
ByteSpan updateToken, uint32_t newVersion)
{
EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS;
OTAProviderDelegate * delegate = GetDelegate(endpoint);
ChipLogDetail(Zcl, "OTA Provider received ApplyUpdateRequest");
if (SendStatusIfDelegateNull(endpoint))
{
return true;
}
if (updateToken.size() > kUpdateTokenMaxLength || updateToken.size() < kUpdateTokenMinLength)
{
ChipLogError(Zcl, "expected size %zu for UpdateToken, got %zu", kUpdateTokenMaxLength, updateToken.size());
emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_INVALID_ARGUMENT);
}
status = delegate->HandleApplyUpdateRequest(commandObj, updateToken, newVersion);
if (status != EMBER_ZCL_STATUS_SUCCESS)
{
emberAfSendImmediateDefaultResponse(status);
}
return true;
}
/**
* @brief OTA Software Update Provider Cluster NotifyUpdateApplied Command callback
*
*
* @param updateToken Identifier for the Software Image that was applied. Should be 32 octets long.
* @param softwareVersion The current SoftwareVersion value. Should match the SoftwarVersion attribute in the
* OTA Requestor's Basic Information Cluster.
*/
bool emberAfOtaSoftwareUpdateProviderClusterNotifyUpdateAppliedCallback(chip::EndpointId endpoint,
chip::app::CommandHandler * commandObj,
chip::ByteSpan updateToken, uint32_t softwareVersion)
{
EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS;
OTAProviderDelegate * delegate = GetDelegate(endpoint);
ChipLogDetail(Zcl, "OTA Provider received NotifyUpdateUpplied");
if (SendStatusIfDelegateNull(endpoint))
{
return true;
}
if (updateToken.size() > kUpdateTokenMaxLength || updateToken.size() < kUpdateTokenMinLength)
{
ChipLogError(Zcl, "expected size %zu for UpdateToken, got %zu", kUpdateTokenMaxLength, updateToken.size());
emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_INVALID_ARGUMENT);
}
status = delegate->HandleNotifyUpdateApplied(updateToken, softwareVersion);
if (status != EMBER_ZCL_STATUS_SUCCESS)
{
emberAfSendImmediateDefaultResponse(status);
}
return true;
}
/**
* @brief OTA Software Update Provider Cluster QueryImage Command callback
*
* @param vendorId The Vendor ID applying to the OTA Requestor’s Node. Should match the value in the Basic Information Cluster.
* @param productId The Product ID applying to the OTA Requestor’s Node. Should match the value in the Basic Information Cluster.
* @param imageType A Vendor-specific numerical value that may help an OTA Provider select the correct payload.
* @param hardwareVersion The OTA Requestor’s hardware version. Should match the HardwareVersion attribute of the Client's Basic
* Information Cluster.
* @param softwareVersion The current version running on the OTA Requestor. Should match the SoftwareVersion attribute of the
* Client's Basic Information Cluster.
* @param protocolsSupported A list of OTADownloadProtocol enum values indicating download protocols supported by the OTA Requestor
* (max length 8 entries).
* @param location Optional, 2 chars. If present, it should match the Location value in the Client's Basic Information Cluster.
* @param clientCanConsent Optional. May be set by an OTA Requestor which is capable of obtaining user consent for OTA application.
* @param metadataForProvider Optional, max 512 octets. A TLV-encoded Vendor-specific payload.
*/
bool emberAfOtaSoftwareUpdateProviderClusterQueryImageCallback(chip::EndpointId endpoint, chip::app::CommandHandler * commandObj,
uint16_t vendorId, uint16_t productId, uint16_t hardwareVersion,
uint32_t softwareVersion, uint8_t protocolsSupported,
uint8_t * location, bool requestorCanConsent,
chip::ByteSpan metadataForProvider)
{
EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS;
OTAProviderDelegate * delegate = GetDelegate(endpoint);
if (SendStatusIfDelegateNull(endpoint))
{
return true;
};
ChipLogDetail(Zcl, "OTA Provider received QueryImage");
// TODO: (#7112) support location param and verify length once CHAR_STRING is supported
// Using location parameter is blocked by #5542 (use Span for string arguments). For now, there is no way to safely get the
// length of the location string because it is not guaranteed to be null-terminated.
Span<const char> locationSpan;
if (metadataForProvider.size() > kMaxMetadataLen)
{
ChipLogError(Zcl, "metadata size %zu exceeds max %zu", metadataForProvider.size(), kMaxMetadataLen);
emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_INVALID_ARGUMENT);
}
status = delegate->HandleQueryImage(commandObj, vendorId, productId, hardwareVersion, softwareVersion, protocolsSupported,
locationSpan, requestorCanConsent, metadataForProvider);
if (status != EMBER_ZCL_STATUS_SUCCESS)
{
emberAfSendImmediateDefaultResponse(status);
}
return true;
}
namespace chip {
namespace app {
namespace clusters {
void OTAProvider::SetDelegate(EndpointId endpoint, OTAProviderDelegate * delegate)
{
uint16_t ep = emberAfFindClusterServerEndpointIndex(endpoint, ZCL_OTA_PROVIDER_CLUSTER_ID);
if (ep != 0xFFFF)
{
gDelegateTable[ep] = delegate;
}
}
} // namespace clusters
} // namespace app
} // namespace chip