diff --git a/.gitmodules b/.gitmodules index 824bd9954e506c..4eca51b5cf03f9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -332,6 +332,7 @@ [submodule "third_party/lwip/repo"] path = third_party/lwip/repo url = https://github.com/lwip-tcpip/lwip.git + excluded-platforms = darwin [submodule "third_party/abseil-cpp/src"] path = third_party/abseil-cpp/src url = https://github.com/abseil/abseil-cpp.git diff --git a/examples/common/pigweed/rpc_services/Attributes.h b/examples/common/pigweed/rpc_services/Attributes.h index e4ced64a51d9f2..d34d7e5789c3cd 100644 --- a/examples/common/pigweed/rpc_services/Attributes.h +++ b/examples/common/pigweed/rpc_services/Attributes.h @@ -224,7 +224,7 @@ class Attributes : public pw_rpc::nanopb::Attributes::Service app::DataModel::ReadAttributeRequest request; request.path = path; request.operationFlags.Set(app::DataModel::OperationFlags::kInternal); - request.subjectDescriptor = subjectDescriptor; + request.subjectDescriptor = &subjectDescriptor; std::optional info = provider->GetClusterInfo(path); if (!info.has_value()) diff --git a/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.h b/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.h index 6430c55b9b388e..3045081643cb7e 100644 --- a/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.h +++ b/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.h @@ -42,7 +42,7 @@ class CHIPCommandBridge : public Command { "commissioner-name. Interactive mode will only set a single commissioner on the inital command. " "The commissioner node ID will be persisted until a different one is specified."); AddArgument("commissioner-shared-storage", 0, 1, &mCommissionerSharedStorage, - "Use a shared storage instance instead of individual storage for each commissioner. Default is true."); + "Use a shared storage instance instead of individual storage for each commissioner. Default is false."); AddArgument("paa-trust-store-path", &mPaaTrustStorePath, "Path to directory holding PAA certificate information. Can be absolute or relative to the current working " "directory."); diff --git a/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.mm b/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.mm index f7d3f6b5badf43..160fab30aae92b 100644 --- a/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.mm +++ b/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.mm @@ -142,7 +142,7 @@ productAttestationAuthorityCertificates = nil; } - sUseSharedStorage = mCommissionerSharedStorage.ValueOr(true); + sUseSharedStorage = mCommissionerSharedStorage.ValueOr(false); if (sUseSharedStorage) { return SetUpStackWithSharedStorage(productAttestationAuthorityCertificates); } @@ -188,6 +188,10 @@ intermediateCertificate:nil rootCertificate:certificateIssuer.rootCertificate]; [params setOperationalCertificateIssuer:certificateIssuer queue:controllerStorageQueue]; + + __auto_type * otaDelegateQueue = dispatch_queue_create("com.chip.ota", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL); + [params setOTAProviderDelegate:mOTADelegate queue:otaDelegateQueue]; + params.productAttestationAuthorityCertificates = productAttestationAuthorityCertificates; __auto_type * controller = [[MTRDeviceController alloc] initWithParameters:params error:&error]; diff --git a/examples/darwin-framework-tool/commands/common/CertificateIssuer.mm b/examples/darwin-framework-tool/commands/common/CertificateIssuer.mm index bd12878c6addd7..c86a41490b08c5 100644 --- a/examples/darwin-framework-tool/commands/common/CertificateIssuer.mm +++ b/examples/darwin-framework-tool/commands/common/CertificateIssuer.mm @@ -21,6 +21,8 @@ #include +constexpr const uint32_t kIssuerId = 12345678; + @interface CertificateIssuer () - (MTRCertificateDERBytes _Nullable)issueOperationalCertificateForNodeID:(NSNumber *)nodeID fabricID:(NSNumber *)fabricID @@ -67,7 +69,7 @@ - (void)startWithStorage:(id)storage return; } - __auto_type * rootCertificate = [MTRCertificates createRootCertificate:signingKey issuerID:nil fabricID:nil error:error]; + __auto_type * rootCertificate = [MTRCertificates createRootCertificate:signingKey issuerID:@(kIssuerId) fabricID:nil error:error]; if (nil == rootCertificate) { *error = [NSError errorWithDomain:@"Error" code:0 userInfo:@{ @"reason" : @"Error creating root certificate" }]; return; diff --git a/examples/fabric-sync/main.cpp b/examples/fabric-sync/main.cpp index 84c014dfcbf010..66541b32de91f9 100644 --- a/examples/fabric-sync/main.cpp +++ b/examples/fabric-sync/main.cpp @@ -20,14 +20,69 @@ using namespace chip; +namespace { + +constexpr char kFabricSyncLogFilePath[] = "/tmp/fabric_sync.log"; + +// File pointer for the log file +FILE * sLogFile = nullptr; + +void OpenLogFile(const char * filePath) +{ + sLogFile = fopen(filePath, "a"); + if (sLogFile == nullptr) + { + perror("Failed to open log file"); + } +} + +void CloseLogFile() +{ + if (sLogFile != nullptr) + { + fclose(sLogFile); + sLogFile = nullptr; + } +} + +void ENFORCE_FORMAT(3, 0) LoggingCallback(const char * module, uint8_t category, const char * msg, va_list args) +{ + if (sLogFile == nullptr) + { + return; + } + + uint64_t timeMs = System::SystemClock().GetMonotonicMilliseconds64().count(); + uint64_t seconds = timeMs / 1000; + uint64_t milliseconds = timeMs % 1000; + + flockfile(sLogFile); + + fprintf(sLogFile, "[%llu.%06llu] CHIP:%s: ", static_cast(seconds), + static_cast(milliseconds), module); + vfprintf(sLogFile, msg, args); + fprintf(sLogFile, "\n"); + fflush(sLogFile); + + funlockfile(sLogFile); +} + +} // namespace + void ApplicationInit() { ChipLogProgress(NotSpecified, "Fabric-Sync: ApplicationInit()"); + + OpenLogFile(kFabricSyncLogFilePath); + + // Redirect logs to the custom logging callback + Logging::SetLogRedirectCallback(LoggingCallback); } void ApplicationShutdown() { ChipLogDetail(NotSpecified, "Fabric-Sync: ApplicationShutdown()"); + CloseLogFile(); } int main(int argc, char * argv[]) diff --git a/scripts/checkout_submodules.py b/scripts/checkout_submodules.py index 0290182b5b4bff..4d527148c7d9b0 100755 --- a/scripts/checkout_submodules.py +++ b/scripts/checkout_submodules.py @@ -66,6 +66,18 @@ def load_module_info() -> None: platforms = set(filter(None, platforms)) assert not ( platforms - ALL_PLATFORMS), "Submodule's platform not contained in ALL_PLATFORMS" + + # Check for explicitly excluded platforms + excluded_platforms = module.get('excluded-platforms', '').split(',') + excluded_platforms = set(filter(None, excluded_platforms)) + assert not ( + excluded_platforms - ALL_PLATFORMS), "Submodule excluded on platforms not contained in ALL_PLATFORMS" + + if len(excluded_platforms) != 0: + if len(platforms) == 0: + platforms = ALL_PLATFORMS + platforms = platforms - excluded_platforms + recursive = module.getboolean('recursive', False) name = name.replace('submodule "', '').replace('"', '') yield Module(name=name, path=module['path'], platforms=platforms, recursive=recursive) diff --git a/src/app/CommandHandlerImpl.cpp b/src/app/CommandHandlerImpl.cpp index 2142af63494348..80373dc32ad755 100644 --- a/src/app/CommandHandlerImpl.cpp +++ b/src/app/CommandHandlerImpl.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -391,10 +392,11 @@ Status CommandHandlerImpl::ProcessCommandDataIB(CommandDataIB::Parser & aCommand VerifyOrReturnError(err == CHIP_NO_ERROR, Status::InvalidAction); { + Access::SubjectDescriptor subjectDescriptor = GetSubjectDescriptor(); DataModel::InvokeRequest request; request.path = concretePath; - request.subjectDescriptor = GetSubjectDescriptor(); + request.subjectDescriptor = &subjectDescriptor; request.invokeFlags.Set(DataModel::InvokeFlags::kTimed, IsTimedInvoke()); Status preCheckStatus = mpCallback->ValidateCommandCanBeDispatched(request); @@ -513,10 +515,11 @@ Status CommandHandlerImpl::ProcessGroupCommandDataIB(CommandDataIB::Parser & aCo const ConcreteCommandPath concretePath(mapping.endpoint_id, clusterId, commandId); { + Access::SubjectDescriptor subjectDescriptor = GetSubjectDescriptor(); DataModel::InvokeRequest request; request.path = concretePath; - request.subjectDescriptor = GetSubjectDescriptor(); + request.subjectDescriptor = &subjectDescriptor; request.invokeFlags.Set(DataModel::InvokeFlags::kTimed, IsTimedInvoke()); Status preCheckStatus = mpCallback->ValidateCommandCanBeDispatched(request); diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index d88299c29da412..40ec6e71b3210b 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -1646,10 +1646,12 @@ void InteractionModelEngine::DispatchCommand(CommandHandlerImpl & apCommandObj, { #if CHIP_CONFIG_USE_DATA_MODEL_INTERFACE + Access::SubjectDescriptor subjectDescriptor = apCommandObj.GetSubjectDescriptor(); + DataModel::InvokeRequest request; request.path = aCommandPath; request.invokeFlags.Set(DataModel::InvokeFlags::kTimed, apCommandObj.IsTimedInvoke()); - request.subjectDescriptor = apCommandObj.GetSubjectDescriptor(); + request.subjectDescriptor = &subjectDescriptor; std::optional status = GetDataModelProvider()->Invoke(request, apPayload, &apCommandObj); @@ -1702,7 +1704,7 @@ Protocols::InteractionModel::Status InteractionModelEngine::ValidateCommandCanBe Protocols::InteractionModel::Status InteractionModelEngine::CheckCommandAccess(const DataModel::InvokeRequest & aRequest) { - if (!aRequest.subjectDescriptor.has_value()) + if (aRequest.subjectDescriptor == nullptr) { return Status::UnsupportedAccess; // we require a subject for invoke } diff --git a/src/app/WriteHandler.cpp b/src/app/WriteHandler.cpp index e7787bd4dc2cfd..7a1bbc5d57c668 100644 --- a/src/app/WriteHandler.cpp +++ b/src/app/WriteHandler.cpp @@ -779,7 +779,7 @@ CHIP_ERROR WriteHandler::WriteClusterData(const Access::SubjectDescriptor & aSub DataModel::WriteAttributeRequest request; request.path = aPath; - request.subjectDescriptor = aSubject; + request.subjectDescriptor = &aSubject; request.previousSuccessPath = mLastSuccessfullyWrittenPath; request.writeFlags.Set(DataModel::WriteFlags::kTimed, IsTimedWrite()); diff --git a/src/app/codegen-data-model-provider/CodegenDataModelProvider.cpp b/src/app/codegen-data-model-provider/CodegenDataModelProvider.cpp index 70253cd1ed9ae5..dd320e5d03780a 100644 --- a/src/app/codegen-data-model-provider/CodegenDataModelProvider.cpp +++ b/src/app/codegen-data-model-provider/CodegenDataModelProvider.cpp @@ -31,6 +31,7 @@ #include #include #include +#include // separated out for code-reuse #include @@ -40,84 +41,32 @@ namespace chip { namespace app { -namespace { +namespace detail { -/// Handles going through callback-based enumeration of generated/accepted commands -/// for CommandHandlerInterface based items. -/// -/// Offers the ability to focus on some operation for finding a given -/// command id: -/// - FindFirst will return the first found element -/// - FindExact finds the element with the given id -/// - FindNext finds the element following the given id -class EnumeratorCommandFinder -{ -public: - using HandlerCallbackFunction = CHIP_ERROR (CommandHandlerInterface::*)(const ConcreteClusterPath &, - CommandHandlerInterface::CommandIdCallback, void *); - - enum class Operation - { - kFindFirst, // Find the first value in the list - kFindExact, // Find the given value - kFindNext // Find the value AFTER this value - }; - - EnumeratorCommandFinder(HandlerCallbackFunction callback) : - mCallback(callback), mOperation(Operation::kFindFirst), mTarget(kInvalidCommandId) - {} - - /// Find the given command ID that matches the given operation/path. - /// - /// If operation is kFindFirst, then path commandID is ignored. Otherwise it is used as a key to - /// kFindExact or kFindNext. - /// - /// Returns: - /// - std::nullopt if no command found using the command handler interface - /// - kInvalidCommandId if the find failed (but command handler interface does provide a list) - /// - valid id if command handler interface usage succeeds - std::optional FindCommandId(Operation operation, const ConcreteCommandPath & path); - - /// Uses FindCommandId to find the given command and loads the command entry data - std::optional FindCommandEntry(Operation operation, const ConcreteCommandPath & path); - -private: - HandlerCallbackFunction mCallback; - Operation mOperation; - CommandId mTarget; - std::optional mFound = std::nullopt; - - Loop HandlerCallback(CommandId id) - { - switch (mOperation) +Loop EnumeratorCommandFinder::HandlerCallback(CommandId id) +{ + switch (mOperation) + { + case Operation::kFindFirst: + mFound = id; + return Loop::Break; + case Operation::kFindExact: + if (mTarget == id) { - case Operation::kFindFirst: - mFound = id; + mFound = id; // found it return Loop::Break; - case Operation::kFindExact: - if (mTarget == id) - { - mFound = id; // found it - return Loop::Break; - } - break; - case Operation::kFindNext: - if (mTarget == id) - { - // Once we found the ID, get the first - mOperation = Operation::kFindFirst; - } - break; } - return Loop::Continue; // keep searching - } - - static Loop HandlerCallbackFn(CommandId id, void * context) - { - auto self = static_cast(context); - return self->HandlerCallback(id); + break; + case Operation::kFindNext: + if (mTarget == id) + { + // Once we found the ID, get the first + mOperation = Operation::kFindFirst; + } + break; } -}; + return Loop::Continue; // keep searching +} std::optional EnumeratorCommandFinder::FindCommandId(Operation operation, const ConcreteCommandPath & path) { @@ -151,6 +100,22 @@ std::optional EnumeratorCommandFinder::FindCommandId(Operation operat return mFound.value_or(kInvalidCommandId); } +} // namespace detail + +using detail::EnumeratorCommandFinder; + +namespace { + +const chip::CommandId * AcceptedCommands(const EmberAfCluster & cluster) +{ + return cluster.acceptedCommandList; +} + +const chip::CommandId * GeneratedCommands(const EmberAfCluster & cluster) +{ + return cluster.generatedCommandList; +} + /// Load the cluster information into the specified destination std::variant LoadClusterInfo(const ConcreteClusterPath & path, const EmberAfCluster & cluster) { @@ -282,20 +247,6 @@ DataModel::CommandEntry CommandEntryFrom(const ConcreteClusterPath & clusterPath return entry; } -std::optional EnumeratorCommandFinder::FindCommandEntry(Operation operation, - const ConcreteCommandPath & path) -{ - - std::optional id = FindCommandId(operation, path); - - if (!id.has_value()) - { - return std::nullopt; - } - - return (*id == kInvalidCommandId) ? DataModel::CommandEntry::kInvalid : CommandEntryFrom(path, *id); -} - // TODO: DeviceTypeEntry content is IDENTICAL to EmberAfDeviceType, so centralizing // to a common type is probably better. Need to figure out dependencies since // this would make ember return datamodel-provider types. @@ -639,6 +590,35 @@ const EmberAfCluster * CodegenDataModelProvider::FindServerCluster(const Concret return cluster; } +CommandId CodegenDataModelProvider::FindCommand(const ConcreteCommandPath & path, detail::EnumeratorCommandFinder & handlerFinder, + detail::EnumeratorCommandFinder::Operation operation, + CodegenDataModelProvider::EmberCommandListIterator & emberIterator, + CommandListGetter commandListGetter) +{ + + std::optional handlerCommandId = handlerFinder.FindCommandId(operation, path); + if (handlerCommandId.has_value()) + { + return *handlerCommandId; + } + + const EmberAfCluster * cluster = FindServerCluster(path); + VerifyOrReturnValue(cluster != nullptr, kInvalidCommandId); + + const CommandId * commandList = commandListGetter(*cluster); + + switch (operation) + { + case EnumeratorCommandFinder::Operation::kFindFirst: + return emberIterator.First(commandList).value_or(kInvalidCommandId); + case EnumeratorCommandFinder::Operation::kFindNext: + return emberIterator.Next(commandList, path.mCommandId).value_or(kInvalidCommandId); + case EnumeratorCommandFinder::Operation::kFindExact: + default: + return emberIterator.Exists(commandList, path.mCommandId) ? path.mCommandId : kInvalidCommandId; + } +} + DataModel::AttributeEntry CodegenDataModelProvider::NextAttribute(const ConcreteAttributePath & before) { const EmberAfCluster * cluster = FindServerCluster(before); @@ -686,106 +666,58 @@ std::optional CodegenDataModelProvider::GetAttributeIn DataModel::CommandEntry CodegenDataModelProvider::FirstAcceptedCommand(const ConcreteClusterPath & path) { - auto handlerInterfaceValue = EnumeratorCommandFinder(&CommandHandlerInterface::EnumerateAcceptedCommands) - .FindCommandEntry(EnumeratorCommandFinder::Operation::kFindFirst, - ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId)); + EnumeratorCommandFinder handlerFinder(&CommandHandlerInterface::EnumerateAcceptedCommands); - if (handlerInterfaceValue.has_value()) - { - return *handlerInterfaceValue; - } + CommandId commandId = + FindCommand(ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId), handlerFinder, + detail::EnumeratorCommandFinder::Operation::kFindFirst, mAcceptedCommandsIterator, AcceptedCommands); - const EmberAfCluster * cluster = FindServerCluster(path); - - VerifyOrReturnValue(cluster != nullptr, DataModel::CommandEntry::kInvalid); - - std::optional commandId = mAcceptedCommandsIterator.First(cluster->acceptedCommandList); - VerifyOrReturnValue(commandId.has_value(), DataModel::CommandEntry::kInvalid); - - return CommandEntryFrom(path, *commandId); + VerifyOrReturnValue(commandId != kInvalidCommandId, DataModel::CommandEntry::kInvalid); + return CommandEntryFrom(path, commandId); } DataModel::CommandEntry CodegenDataModelProvider::NextAcceptedCommand(const ConcreteCommandPath & before) { - // TODO: `Next` redirecting to a callback is slow O(n^2). - // see https://github.com/project-chip/connectedhomeip/issues/35790 - auto handlerInterfaceValue = EnumeratorCommandFinder(&CommandHandlerInterface::EnumerateAcceptedCommands) - .FindCommandEntry(EnumeratorCommandFinder::Operation::kFindNext, before); - - if (handlerInterfaceValue.has_value()) - { - return *handlerInterfaceValue; - } - - const EmberAfCluster * cluster = FindServerCluster(before); - VerifyOrReturnValue(cluster != nullptr, DataModel::CommandEntry::kInvalid); + EnumeratorCommandFinder handlerFinder(&CommandHandlerInterface::EnumerateAcceptedCommands); + CommandId commandId = FindCommand(before, handlerFinder, detail::EnumeratorCommandFinder::Operation::kFindNext, + mAcceptedCommandsIterator, AcceptedCommands); - std::optional commandId = mAcceptedCommandsIterator.Next(cluster->acceptedCommandList, before.mCommandId); - VerifyOrReturnValue(commandId.has_value(), DataModel::CommandEntry::kInvalid); - - return CommandEntryFrom(before, *commandId); + VerifyOrReturnValue(commandId != kInvalidCommandId, DataModel::CommandEntry::kInvalid); + return CommandEntryFrom(before, commandId); } std::optional CodegenDataModelProvider::GetAcceptedCommandInfo(const ConcreteCommandPath & path) { - auto handlerInterfaceValue = EnumeratorCommandFinder(&CommandHandlerInterface::EnumerateAcceptedCommands) - .FindCommandEntry(EnumeratorCommandFinder::Operation::kFindExact, path); - if (handlerInterfaceValue.has_value()) - { - return handlerInterfaceValue->IsValid() ? std::make_optional(handlerInterfaceValue->info) : std::nullopt; - } - - const EmberAfCluster * cluster = FindServerCluster(path); + EnumeratorCommandFinder handlerFinder(&CommandHandlerInterface::EnumerateAcceptedCommands); + CommandId commandId = FindCommand(path, handlerFinder, detail::EnumeratorCommandFinder::Operation::kFindExact, + mAcceptedCommandsIterator, AcceptedCommands); - VerifyOrReturnValue(cluster != nullptr, std::nullopt); - VerifyOrReturnValue(mAcceptedCommandsIterator.Exists(cluster->acceptedCommandList, path.mCommandId), std::nullopt); - - return CommandEntryFrom(path, path.mCommandId).info; + VerifyOrReturnValue(commandId != kInvalidCommandId, std::nullopt); + return CommandEntryFrom(path, commandId).info; } ConcreteCommandPath CodegenDataModelProvider::FirstGeneratedCommand(const ConcreteClusterPath & path) { - std::optional commandId = - EnumeratorCommandFinder(&CommandHandlerInterface::EnumerateGeneratedCommands) - .FindCommandId(EnumeratorCommandFinder::Operation::kFindFirst, - ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId)); - if (commandId.has_value()) - { - return *commandId == kInvalidCommandId ? kInvalidCommandPath - : ConcreteCommandPath(path.mEndpointId, path.mClusterId, *commandId); - } + EnumeratorCommandFinder handlerFinder(&CommandHandlerInterface::EnumerateGeneratedCommands); + CommandId commandId = + FindCommand(ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId), handlerFinder, + detail::EnumeratorCommandFinder::Operation::kFindFirst, mGeneratedCommandsIterator, GeneratedCommands); - const EmberAfCluster * cluster = FindServerCluster(path); - VerifyOrReturnValue(cluster != nullptr, kInvalidCommandPath); - - commandId = mGeneratedCommandsIterator.First(cluster->generatedCommandList); - VerifyOrReturnValue(commandId.has_value(), kInvalidCommandPath); - return ConcreteCommandPath(path.mEndpointId, path.mClusterId, *commandId); + VerifyOrReturnValue(commandId != kInvalidCommandId, kInvalidCommandPath); + return ConcreteCommandPath(path.mEndpointId, path.mClusterId, commandId); } ConcreteCommandPath CodegenDataModelProvider::NextGeneratedCommand(const ConcreteCommandPath & before) { - // TODO: `Next` redirecting to a callback is slow O(n^2). - // see https://github.com/project-chip/connectedhomeip/issues/35790 - auto nextId = EnumeratorCommandFinder(&CommandHandlerInterface::EnumerateGeneratedCommands) - .FindCommandId(EnumeratorCommandFinder::Operation::kFindNext, before); - - if (nextId.has_value()) - { - return (*nextId == kInvalidCommandId) ? kInvalidCommandPath - : ConcreteCommandPath(before.mEndpointId, before.mClusterId, *nextId); - } - - const EmberAfCluster * cluster = FindServerCluster(before); - - VerifyOrReturnValue(cluster != nullptr, kInvalidCommandPath); + EnumeratorCommandFinder handlerFinder(&CommandHandlerInterface::EnumerateGeneratedCommands); - std::optional commandId = mGeneratedCommandsIterator.Next(cluster->generatedCommandList, before.mCommandId); - VerifyOrReturnValue(commandId.has_value(), kInvalidCommandPath); + CommandId commandId = FindCommand(before, handlerFinder, detail::EnumeratorCommandFinder::Operation::kFindNext, + mGeneratedCommandsIterator, GeneratedCommands); - return ConcreteCommandPath(before.mEndpointId, before.mClusterId, *commandId); + VerifyOrReturnValue(commandId != kInvalidCommandId, kInvalidCommandPath); + return ConcreteCommandPath(before.mEndpointId, before.mClusterId, commandId); } std::optional CodegenDataModelProvider::FirstDeviceType(EndpointId endpoint) diff --git a/src/app/codegen-data-model-provider/CodegenDataModelProvider.h b/src/app/codegen-data-model-provider/CodegenDataModelProvider.h index 085ae67ec392ab..5c87e264fd2aea 100644 --- a/src/app/codegen-data-model-provider/CodegenDataModelProvider.h +++ b/src/app/codegen-data-model-provider/CodegenDataModelProvider.h @@ -16,14 +16,71 @@ */ #pragma once -#include "app/data-model-provider/ActionReturnStatus.h" +#include "app/ConcreteCommandPath.h" #include +#include +#include #include namespace chip { namespace app { +namespace detail { + +/// Handles going through callback-based enumeration of generated/accepted commands +/// for CommandHandlerInterface based items. +/// +/// Offers the ability to focus on some operation for finding a given +/// command id: +/// - FindFirst will return the first found element +/// - FindExact finds the element with the given id +/// - FindNext finds the element following the given id +class EnumeratorCommandFinder +{ +public: + using HandlerCallbackFunction = CHIP_ERROR (CommandHandlerInterface::*)(const ConcreteClusterPath &, + CommandHandlerInterface::CommandIdCallback, void *); + + enum class Operation + { + kFindFirst, // Find the first value in the list + kFindExact, // Find the given value + kFindNext // Find the value AFTER this value + }; + + EnumeratorCommandFinder(HandlerCallbackFunction callback) : + mCallback(callback), mOperation(Operation::kFindFirst), mTarget(kInvalidCommandId) + {} + + /// Find the given command ID that matches the given operation/path. + /// + /// If operation is kFindFirst, then path commandID is ignored. Otherwise it is used as a key to + /// kFindExact or kFindNext. + /// + /// Returns: + /// - std::nullopt if no command found using the command handler interface + /// - kInvalidCommandId if the find failed (but command handler interface does provide a list) + /// - valid id if command handler interface usage succeeds + std::optional FindCommandId(Operation operation, const ConcreteCommandPath & path); + +private: + HandlerCallbackFunction mCallback; + Operation mOperation; + CommandId mTarget; + std::optional mFound = std::nullopt; + + Loop HandlerCallback(CommandId id); + + static Loop HandlerCallbackFn(CommandId id, void * context) + { + auto self = static_cast(context); + return self->HandlerCallback(id); + } +}; + +} // namespace detail + /// An implementation of `InteractionModel::Model` that relies on code-generation /// via zap/ember. /// @@ -152,6 +209,12 @@ class CodegenDataModelProvider : public chip::app::DataModel::Provider /// Find the index of the given endpoint id std::optional TryFindEndpointIndex(chip::EndpointId id) const; + + using CommandListGetter = const chip::CommandId *(const EmberAfCluster &); + + CommandId FindCommand(const ConcreteCommandPath & path, detail::EnumeratorCommandFinder & handlerFinder, + detail::EnumeratorCommandFinder::Operation operation, + CodegenDataModelProvider::EmberCommandListIterator & emberIterator, CommandListGetter commandListGetter); }; } // namespace app diff --git a/src/app/codegen-data-model-provider/CodegenDataModelProvider_Read.cpp b/src/app/codegen-data-model-provider/CodegenDataModelProvider_Read.cpp index ea35356391d63d..aa357ce4dfb5cb 100644 --- a/src/app/codegen-data-model-provider/CodegenDataModelProvider_Read.cpp +++ b/src/app/codegen-data-model-provider/CodegenDataModelProvider_Read.cpp @@ -106,7 +106,7 @@ DataModel::ActionReturnStatus CodegenDataModelProvider::ReadAttribute(const Data // ACL check for non-internal requests if (!request.operationFlags.Has(DataModel::OperationFlags::kInternal)) { - VerifyOrReturnError(request.subjectDescriptor.has_value(), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(request.subjectDescriptor != nullptr, CHIP_ERROR_INVALID_ARGUMENT); Access::RequestPath requestPath{ .cluster = request.path.mClusterId, .endpoint = request.path.mEndpointId, diff --git a/src/app/codegen-data-model-provider/CodegenDataModelProvider_Write.cpp b/src/app/codegen-data-model-provider/CodegenDataModelProvider_Write.cpp index 51807fe98cf47d..de2f8886476707 100644 --- a/src/app/codegen-data-model-provider/CodegenDataModelProvider_Write.cpp +++ b/src/app/codegen-data-model-provider/CodegenDataModelProvider_Write.cpp @@ -159,7 +159,7 @@ DataModel::ActionReturnStatus CodegenDataModelProvider::WriteAttribute(const Dat if (checkAcl) { - VerifyOrReturnError(request.subjectDescriptor.has_value(), Status::UnsupportedAccess); + VerifyOrReturnError(request.subjectDescriptor != nullptr, Status::UnsupportedAccess); Access::RequestPath requestPath{ .cluster = request.path.mClusterId, .endpoint = request.path.mEndpointId, diff --git a/src/app/data-model-provider/OperationTypes.h b/src/app/data-model-provider/OperationTypes.h index 6bfffccc655f00..294bf2616435b4 100644 --- a/src/app/data-model-provider/OperationTypes.h +++ b/src/app/data-model-provider/OperationTypes.h @@ -55,7 +55,7 @@ struct OperationRequest /// - operationFlags.Has(OperationFlags::kInternal) MUST NOT have this set /// /// NOTE: once kInternal flag is removed, this will become non-optional - std::optional subjectDescriptor; + const chip::Access::SubjectDescriptor * subjectDescriptor = nullptr; /// Accessing fabric index is the subjectDescriptor fabric index (if any). /// This is a readability convenience function. @@ -63,7 +63,8 @@ struct OperationRequest /// Returns kUndefinedFabricIndex if no subject descriptor is available FabricIndex GetAccessingFabricIndex() const { - return subjectDescriptor.has_value() ? subjectDescriptor->fabricIndex : kUndefinedFabricIndex; + VerifyOrReturnValue(subjectDescriptor != nullptr, kUndefinedFabricIndex); + return subjectDescriptor->fabricIndex; } }; diff --git a/src/app/data-model-provider/tests/ReadTesting.h b/src/app/data-model-provider/tests/ReadTesting.h index f7cee20c27123b..e34a83377313ab 100644 --- a/src/app/data-model-provider/tests/ReadTesting.h +++ b/src/app/data-model-provider/tests/ReadTesting.h @@ -136,7 +136,7 @@ class ReadOperation ReadOperation(const ConcreteAttributePath & path) { mRequest.path = path; - mRequest.subjectDescriptor = kDenySubjectDescriptor; + mRequest.subjectDescriptor = &kDenySubjectDescriptor; } ReadOperation(EndpointId endpoint, ClusterId cluster, AttributeId attribute) : @@ -146,7 +146,7 @@ class ReadOperation ReadOperation & SetSubjectDescriptor(const chip::Access::SubjectDescriptor & descriptor) { VerifyOrDie(mState == State::kInitializing); - mRequest.subjectDescriptor = descriptor; + mRequest.subjectDescriptor = &descriptor; return *this; } diff --git a/src/app/data-model-provider/tests/WriteTesting.h b/src/app/data-model-provider/tests/WriteTesting.h index d18fb93fdd70cc..7651ffe37940f3 100644 --- a/src/app/data-model-provider/tests/WriteTesting.h +++ b/src/app/data-model-provider/tests/WriteTesting.h @@ -47,7 +47,7 @@ class WriteOperation WriteOperation(const ConcreteDataAttributePath & path) { mRequest.path = path; - mRequest.subjectDescriptor = kDenySubjectDescriptor; + mRequest.subjectDescriptor = &kDenySubjectDescriptor; } WriteOperation(EndpointId endpoint, ClusterId cluster, AttributeId attribute) : @@ -56,7 +56,7 @@ class WriteOperation WriteOperation & SetSubjectDescriptor(const chip::Access::SubjectDescriptor & descriptor) { - mRequest.subjectDescriptor = descriptor; + mRequest.subjectDescriptor = &descriptor; return *this; } @@ -123,7 +123,11 @@ class WriteOperation AttributeValueDecoder DecoderFor(const T & value) { mTLVReader = ReadEncodedValue(value); - return AttributeValueDecoder(mTLVReader, mRequest.subjectDescriptor.value_or(kDenySubjectDescriptor)); + if (mRequest.subjectDescriptor == nullptr) + { + AttributeValueDecoder(mTLVReader, kDenySubjectDescriptor); + } + return AttributeValueDecoder(mTLVReader, *mRequest.subjectDescriptor); } private: diff --git a/src/app/data-model/DecodableList.h b/src/app/data-model/DecodableList.h index bff54ca532bbde..b05db43c1522bf 100644 --- a/src/app/data-model/DecodableList.h +++ b/src/app/data-model/DecodableList.h @@ -27,6 +27,68 @@ namespace chip { namespace app { namespace DataModel { +namespace detail { + +/* + * Base class of DecodableList to minimize template usage + */ +class DecodableListBase +{ +public: + DecodableListBase() { ClearReader(); } + + /* + * @brief + * + * This call stores a TLV reader positioned on the list this class is to manage. + * + * Specifically, the passed-in reader should be pointing into the list just after + * having called `OpenContainer` on the list element. + */ + void SetReader(const TLV::TLVReader & reader) { mReader = reader; } + + /* + * @brief + * + * This call clears the TLV reader managed by this class, so it can be reused. + */ + void ClearReader() { mReader.Init(nullptr, 0); } + + /* + * Compute the size of the list. This can fail if the TLV is malformed. If + * this succeeds, that does not guarantee that the individual items can be + * successfully decoded; consumers should check Iterator::GetStatus() when + * actually decoding them. If there is no list then the size is considered + * to be zero. + */ + CHIP_ERROR ComputeSize(size_t * size) const + { + if (mReader.GetContainerType() == TLV::kTLVType_NotSpecified) + { + *size = 0; + return CHIP_NO_ERROR; + } + + return mReader.CountRemainingInContainer(size); + } + + CHIP_ERROR Decode(TLV::TLVReader & reader) + { + VerifyOrReturnError(reader.GetType() == TLV::kTLVType_Array, CHIP_ERROR_SCHEMA_MISMATCH); + TLV::TLVType type; + ReturnErrorOnFailure(reader.EnterContainer(type)); + SetReader(reader); + ReturnErrorOnFailure(reader.ExitContainer(type)); + return CHIP_NO_ERROR; + } + +protected: + TLV::TLVReader mReader; + chip::Optional mFabricIndex; +}; + +} // namespace detail + /* * @brief * @@ -47,30 +109,13 @@ namespace DataModel { * */ template -class DecodableList +class DecodableList : public detail::DecodableListBase { public: - DecodableList() { ClearReader(); } + DecodableList() {} static constexpr bool kIsFabricScoped = DataModel::IsFabricScoped::value; - /* - * @brief - * - * This call stores a TLV reader positioned on the list this class is to manage. - * - * Specifically, the passed-in reader should be pointing into the list just after - * having called `OpenContainer` on the list element. - */ - void SetReader(const TLV::TLVReader & reader) { mReader = reader; } - - /* - * @brief - * - * This call clears the TLV reader managed by this class, so it can be reused. - */ - void ClearReader() { mReader.Init(nullptr, 0); } - template ::value, bool> = true> void SetFabricIndex(FabricIndex fabricIndex) { @@ -189,38 +234,6 @@ class DecodableList }; Iterator begin() const { return Iterator(mReader, mFabricIndex); } - - /* - * Compute the size of the list. This can fail if the TLV is malformed. If - * this succeeds, that does not guarantee that the individual items can be - * successfully decoded; consumers should check Iterator::GetStatus() when - * actually decoding them. If there is no list then the size is considered - * to be zero. - */ - CHIP_ERROR ComputeSize(size_t * size) const - { - if (mReader.GetContainerType() == TLV::kTLVType_NotSpecified) - { - *size = 0; - return CHIP_NO_ERROR; - } - - return mReader.CountRemainingInContainer(size); - } - - CHIP_ERROR Decode(TLV::TLVReader & reader) - { - VerifyOrReturnError(reader.GetType() == TLV::kTLVType_Array, CHIP_ERROR_SCHEMA_MISMATCH); - TLV::TLVType type; - ReturnErrorOnFailure(reader.EnterContainer(type)); - SetReader(reader); - ReturnErrorOnFailure(reader.ExitContainer(type)); - return CHIP_NO_ERROR; - } - -private: - TLV::TLVReader mReader; - chip::Optional mFabricIndex; }; } // namespace DataModel diff --git a/src/app/reporting/Read-DataModel.cpp b/src/app/reporting/Read-DataModel.cpp index 64d027e57bb294..584536bdeb9606 100644 --- a/src/app/reporting/Read-DataModel.cpp +++ b/src/app/reporting/Read-DataModel.cpp @@ -47,7 +47,7 @@ DataModel::ActionReturnStatus RetrieveClusterData(DataModel::Provider * dataMode { readRequest.readFlags.Set(DataModel::ReadFlags::kFabricFiltered); } - readRequest.subjectDescriptor = subjectDescriptor; + readRequest.subjectDescriptor = &subjectDescriptor; readRequest.path = path; DataVersion version = 0; diff --git a/src/app/tests/test-interaction-model-api.cpp b/src/app/tests/test-interaction-model-api.cpp index b69c4234273cb9..33097d320bc880 100644 --- a/src/app/tests/test-interaction-model-api.cpp +++ b/src/app/tests/test-interaction-model-api.cpp @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "access/SubjectDescriptor.h" #include #include @@ -171,8 +172,13 @@ ActionReturnStatus TestImCustomDataModel::ReadAttribute(const ReadAttributeReque { AttributeEncodeState mutableState(&encoder.GetState()); // provide a state copy to start. - CHIP_ERROR err = ReadSingleClusterData(request.subjectDescriptor.value_or(Access::SubjectDescriptor()), - request.readFlags.Has(ReadFlags::kFabricFiltered), request.path, + Access::SubjectDescriptor subjectDescriptor; + if (request.subjectDescriptor != nullptr) + { + subjectDescriptor = *request.subjectDescriptor; + } + + CHIP_ERROR err = ReadSingleClusterData(subjectDescriptor, request.readFlags.Has(ReadFlags::kFabricFiltered), request.path, TestOnlyAttributeValueEncoderAccessor(encoder).Builder(), &mutableState); // state must survive CHIP_ERRORs as it is used for chunking diff --git a/src/controller/tests/data_model/DataModelFixtures.cpp b/src/controller/tests/data_model/DataModelFixtures.cpp index a5533dc51de57c..f007275c9b3bc2 100644 --- a/src/controller/tests/data_model/DataModelFixtures.cpp +++ b/src/controller/tests/data_model/DataModelFixtures.cpp @@ -18,6 +18,7 @@ #include "DataModelFixtures.h" +#include #include #include #include @@ -522,8 +523,13 @@ ActionReturnStatus CustomDataModel::ReadAttribute(const ReadAttributeRequest & r } #endif // CHIP_CONFIG_USE_EMBER_DATA_MODEL && CHIP_CONFIG_USE_DATA_MODEL_INTERFACE - CHIP_ERROR err = ReadSingleClusterData(request.subjectDescriptor.value_or(Access::SubjectDescriptor()), - request.readFlags.Has(ReadFlags::kFabricFiltered), request.path, + Access::SubjectDescriptor subjectDescriptor; + if (request.subjectDescriptor != nullptr) + { + subjectDescriptor = *request.subjectDescriptor; + } + + CHIP_ERROR err = ReadSingleClusterData(subjectDescriptor, request.readFlags.Has(ReadFlags::kFabricFiltered), request.path, TestOnlyAttributeValueEncoderAccessor(encoder).Builder(), &mutableState); // state must survive CHIP_ERRORs as it is used for chunking diff --git a/src/lwip/lwip.gni b/src/lwip/lwip.gni index 89b4808af8fa5a..cbbf0a4f74c901 100644 --- a/src/lwip/lwip.gni +++ b/src/lwip/lwip.gni @@ -14,7 +14,8 @@ declare_args() { # Have the lwIP library available. - chip_with_lwip = current_os != "zephyr" && current_os != "mbed" + chip_with_lwip = current_os != "zephyr" && current_os != "mbed" && + current_os != "mac" && current_os != "ios" # lwIP platform: standalone, freertos. lwip_platform = ""