/* * * 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 #include #include #include #include #include #include #include #include #include "ota-provider-delegate.h" #include "ota-provider.h" using namespace chip; using namespace chip::app::Clusters; using namespace chip::app::Clusters::OtaSoftwareUpdateProvider; using chip::app::Clusters::OTAProviderDelegate; using Protocols::InteractionModel::Status; // MATTER_DM_OTA_SOFTWARE_UPDATE_PROVIDER_CLUSTER_SERVER_ENDPOINT_COUNT is only defined if the // cluster is actually enabled in the ZAP config. To allow operation in setups // where that's not the case (and custom dispatch is used), define it here as // needed. #ifndef MATTER_DM_OTA_SOFTWARE_UPDATE_PROVIDER_CLUSTER_SERVER_ENDPOINT_COUNT #define MATTER_DM_OTA_SOFTWARE_UPDATE_PROVIDER_CLUSTER_SERVER_ENDPOINT_COUNT 0 #endif // MATTER_DM_OTA_SOFTWARE_UPDATE_PROVIDER_CLUSTER_SERVER_ENDPOINT_COUNT static constexpr size_t kOtaProviderDelegateTableSize = MATTER_DM_OTA_SOFTWARE_UPDATE_PROVIDER_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; static_assert(kOtaProviderDelegateTableSize <= kEmberInvalidEndpointIndex, "OtaProvider Delegate table size error"); namespace { constexpr size_t kLocationLen = 2; // The expected length of the location parameter in QueryImage 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[kOtaProviderDelegateTableSize] = { nullptr }; OTAProviderDelegate * GetDelegate(EndpointId endpoint) { uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, OtaSoftwareUpdateProvider::Id, MATTER_DM_OTA_SOFTWARE_UPDATE_PROVIDER_CLUSTER_SERVER_ENDPOINT_COUNT); return (ep >= kOtaProviderDelegateTableSize ? nullptr : gDelegateTable[ep]); } bool SendStatusIfDelegateNull(app::CommandHandler * commandObj, const app::ConcreteCommandPath & path) { if (GetDelegate(path.mEndpointId) == nullptr) { ChipLogError(Zcl, "No OTAProviderDelegate set for ep:%u", path.mEndpointId); commandObj->AddStatus(path, Status::UnsupportedCommand); 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. */ bool emberAfOtaSoftwareUpdateProviderClusterApplyUpdateRequestCallback( app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, const Commands::ApplyUpdateRequest::DecodableType & commandData) { auto & updateToken = commandData.updateToken; EndpointId endpoint = commandPath.mEndpointId; OTAProviderDelegate * delegate = GetDelegate(endpoint); ChipLogProgress(Zcl, "OTA Provider received ApplyUpdateRequest"); ChipLogDetail(Zcl, " Update Token: %u", static_cast(commandData.updateToken.size())); ChipLogDetail(Zcl, " New Version: %" PRIu32, commandData.newVersion); if (SendStatusIfDelegateNull(commandObj, commandPath)) { return true; } if (updateToken.size() > kUpdateTokenMaxLength || updateToken.size() < kUpdateTokenMinLength) { ChipLogError(Zcl, "expected size %u for UpdateToken, got %u", static_cast(kUpdateTokenMaxLength), static_cast(updateToken.size())); commandObj->AddStatus(commandPath, Status::InvalidCommand); return true; } delegate->HandleApplyUpdateRequest(commandObj, commandPath, commandData); return true; } /** * @brief OTA Software Update Provider Cluster NotifyUpdateApplied Command callback */ bool emberAfOtaSoftwareUpdateProviderClusterNotifyUpdateAppliedCallback( app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, const Commands::NotifyUpdateApplied::DecodableType & commandData) { auto & updateToken = commandData.updateToken; EndpointId endpoint = commandPath.mEndpointId; OTAProviderDelegate * delegate = GetDelegate(endpoint); ChipLogProgress(Zcl, "OTA Provider received NotifyUpdateApplied"); ChipLogDetail(Zcl, " Update Token: %u", static_cast(commandData.updateToken.size())); ChipLogDetail(Zcl, " Software Version: %" PRIu32, commandData.softwareVersion); if (SendStatusIfDelegateNull(commandObj, commandPath)) { return true; } if (updateToken.size() > kUpdateTokenMaxLength || updateToken.size() < kUpdateTokenMinLength) { ChipLogError(Zcl, "expected size %u for UpdateToken, got %u", static_cast(kUpdateTokenMaxLength), static_cast(updateToken.size())); commandObj->AddStatus(commandPath, Status::InvalidCommand); return true; } delegate->HandleNotifyUpdateApplied(commandObj, commandPath, commandData); return true; } /** * @brief OTA Software Update Provider Cluster QueryImage Command callback */ bool emberAfOtaSoftwareUpdateProviderClusterQueryImageCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, const Commands::QueryImage::DecodableType & commandData) { auto & vendorId = commandData.vendorID; auto & productId = commandData.productID; auto & hardwareVersion = commandData.hardwareVersion; auto & softwareVersion = commandData.softwareVersion; auto & protocolsSupported = commandData.protocolsSupported; auto & location = commandData.location; auto & requestorCanConsent = commandData.requestorCanConsent; auto & metadataForProvider = commandData.metadataForProvider; (void) vendorId; (void) productId; (void) softwareVersion; EndpointId endpoint = commandPath.mEndpointId; OTAProviderDelegate * delegate = GetDelegate(endpoint); if (SendStatusIfDelegateNull(commandObj, commandPath)) { return true; }; ChipLogProgress(Zcl, "OTA Provider received QueryImage"); ChipLogDetail(Zcl, " VendorID: 0x%x", vendorId); ChipLogDetail(Zcl, " ProductID: %u", productId); ChipLogDetail(Zcl, " SoftwareVersion: %" PRIu32, softwareVersion); ChipLogDetail(Zcl, " ProtocolsSupported: ["); auto protocolIter = protocolsSupported.begin(); while (protocolIter.Next()) { ChipLogDetail(Zcl, " %u", to_underlying(protocolIter.GetValue())); } ChipLogDetail(Zcl, " ]"); if (hardwareVersion.HasValue()) { ChipLogDetail(Zcl, " HardwareVersion: %u", hardwareVersion.Value()); } if (location.HasValue()) { ChipLogDetail(Zcl, " Location: %.*s", static_cast(location.Value().size()), location.Value().data()); } if (requestorCanConsent.HasValue()) { ChipLogDetail(Zcl, " RequestorCanConsent: %u", requestorCanConsent.Value()); } if (metadataForProvider.HasValue()) { ChipLogDetail(Zcl, " MetadataForProvider: %u", static_cast(metadataForProvider.Value().size())); } if (location.HasValue() && location.Value().size() != kLocationLen) { ChipLogError(Zcl, "location param length %u != expected length %u", static_cast(location.Value().size()), static_cast(kLocationLen)); commandObj->AddStatus(commandPath, Status::InvalidCommand); return true; } if (metadataForProvider.HasValue() && metadataForProvider.Value().size() > kMaxMetadataLen) { ChipLogError(Zcl, "metadata size %u exceeds max %u", static_cast(metadataForProvider.Value().size()), static_cast(kMaxMetadataLen)); commandObj->AddStatus(commandPath, Status::InvalidCommand); return true; } delegate->HandleQueryImage(commandObj, commandPath, commandData); return true; } namespace chip { namespace app { namespace Clusters { namespace OTAProvider { void SetDelegate(EndpointId endpoint, OTAProviderDelegate * delegate) { uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, OtaSoftwareUpdateProvider::Id, MATTER_DM_OTA_SOFTWARE_UPDATE_PROVIDER_CLUSTER_SERVER_ENDPOINT_COUNT); if (ep < kOtaProviderDelegateTableSize) { gDelegateTable[ep] = delegate; } } } // namespace OTAProvider } // namespace Clusters } // namespace app } // namespace chip void MatterOtaSoftwareUpdateProviderPluginServerInitCallback() {}