From 5867749d475b5bb70ef810f7a15012f0d73eb313 Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Tue, 14 May 2024 00:38:57 -0400 Subject: [PATCH] Ember compatibility layer - split out GlobalAttributeAccessInterface and the I/O buffer (#33396) * Split out ember-compatibiltiy-functions with some shareable bits. When looking to implement the ember/codegen data mode, some functionality in ember-compatibility-functions needs sharing to avoid extra copy&paste bloat: - Global attribute handling via AAI split into a separate file - Raw "data I/O" buffer split into a separate file Moved privilege-storage and implemented a few more mock ember methods. * Also update linter * Remove obsolete file * Fix odd include * Add files to Matter.xcodeproject * Update src/app/util/ember-io-storage.h Co-authored-by: Boris Zbarsky * Update ember-io-storage.h Add additional comments. * Restyle --------- Co-authored-by: Andrei Litvin Co-authored-by: Boris Zbarsky --- .github/workflows/lint.yml | 4 + src/app/chip_data_model.cmake | 2 + src/app/chip_data_model.gni | 2 + src/app/tests/BUILD.gn | 1 - src/app/tests/integration/BUILD.gn | 2 - .../util/ember-compatibility-functions.cpp | 286 ++---------------- ...mber-global-attribute-access-interface.cpp | 136 +++++++++ .../ember-global-attribute-access-interface.h | 56 ++++ src/app/util/ember-io-storage.cpp | 126 ++++++++ src/app/util/ember-io-storage.h | 55 ++++ src/app/util/mock/BUILD.gn | 1 + src/app/util/mock/attribute-storage.cpp | 45 +++ .../include/zap-generated/endpoint_config.h | 2 + .../mock/privilege-storage.cpp} | 11 +- .../Matter.xcodeproj/project.pbxproj | 12 + 15 files changed, 475 insertions(+), 266 deletions(-) create mode 100644 src/app/util/ember-global-attribute-access-interface.cpp create mode 100644 src/app/util/ember-global-attribute-access-interface.h create mode 100644 src/app/util/ember-io-storage.cpp create mode 100644 src/app/util/ember-io-storage.h rename src/app/{tests/integration/RequiredPrivilegeStubs.cpp => util/mock/privilege-storage.cpp} (82%) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7336213ba81a30..4685948fd7bd12 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -119,6 +119,10 @@ jobs: --known-failure app/util/DataModelHandler.h \ --known-failure app/util/ember-compatibility-functions.cpp \ --known-failure app/util/ember-compatibility-functions.h \ + --known-failure app/util/ember-global-attribute-access-interface.cpp \ + --known-failure app/util/ember-global-attribute-access-interface.h \ + --known-failure app/util/ember-io-storage.cpp \ + --known-failure app/util/ember-io-storage.h \ --known-failure app/util/endpoint-config-api.h \ --known-failure app/util/generic-callbacks.h \ --known-failure app/util/generic-callback-stubs.cpp \ diff --git a/src/app/chip_data_model.cmake b/src/app/chip_data_model.cmake index 5573eeb275acd7..2d4149b66f6a8a 100644 --- a/src/app/chip_data_model.cmake +++ b/src/app/chip_data_model.cmake @@ -144,6 +144,8 @@ function(chip_configure_data_model APP_TARGET) ${CHIP_APP_BASE_DIR}/icd/server/ICDConfigurationData.cpp ${CHIP_APP_BASE_DIR}/util/DataModelHandler.cpp ${CHIP_APP_BASE_DIR}/util/ember-compatibility-functions.cpp + ${CHIP_APP_BASE_DIR}/util/ember-global-attribute-access-interface.cpp + ${CHIP_APP_BASE_DIR}/util/ember-io-storage.cpp ${CHIP_APP_BASE_DIR}/util/generic-callback-stubs.cpp ${CHIP_APP_BASE_DIR}/util/privilege-storage.cpp ${CHIP_APP_BASE_DIR}/util/util.cpp diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index c8a30b1b5bd36a..a68d193d241541 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -209,6 +209,8 @@ template("chip_data_model") { "${_app_root}/util/attribute-storage.cpp", "${_app_root}/util/attribute-table.cpp", "${_app_root}/util/ember-compatibility-functions.cpp", + "${_app_root}/util/ember-global-attribute-access-interface.cpp", + "${_app_root}/util/ember-io-storage.cpp", "${_app_root}/util/util.cpp", ] } diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index 49196f2fdaaf8f..df6737a713e6e4 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -29,7 +29,6 @@ static_library("helpers") { "${chip_root}/src/app/reporting/tests/MockReportScheduler.cpp", "AppTestContext.cpp", "AppTestContext.h", - "integration/RequiredPrivilegeStubs.cpp", ] cflags = [ "-Wconversion" ] diff --git a/src/app/tests/integration/BUILD.gn b/src/app/tests/integration/BUILD.gn index da0db88682f180..49aea9034e9766 100644 --- a/src/app/tests/integration/BUILD.gn +++ b/src/app/tests/integration/BUILD.gn @@ -38,7 +38,6 @@ source_set("common") { executable("chip-im-initiator") { sources = [ "${chip_root}/src/app/reporting/tests/MockReportScheduler.cpp", - "RequiredPrivilegeStubs.cpp", "chip_im_initiator.cpp", ] @@ -61,7 +60,6 @@ executable("chip-im-responder") { "${chip_root}/src/app/reporting/tests/MockReportScheduler.cpp", "MockEvents.cpp", "MockEvents.h", - "RequiredPrivilegeStubs.cpp", "chip_im_responder.cpp", ] diff --git a/src/app/util/ember-compatibility-functions.cpp b/src/app/util/ember-compatibility-functions.cpp index e87e0f564a5ff7..cc3185f78a5df6 100644 --- a/src/app/util/ember-compatibility-functions.cpp +++ b/src/app/util/ember-compatibility-functions.cpp @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include #include @@ -54,118 +56,18 @@ using chip::Protocols::InteractionModel::Status; using namespace chip; using namespace chip::app; using namespace chip::Access; +using namespace chip::app::Compatibility; +using namespace chip::app::Compatibility::Internal; namespace chip { namespace app { -namespace Compatibility { -namespace { -// On some apps, ATTRIBUTE_LARGEST can as small as 3, making compiler unhappy since data[kAttributeReadBufferSize] cannot hold -// uint64_t. Make kAttributeReadBufferSize at least 8 so it can fit all basic types. -constexpr size_t kAttributeReadBufferSize = (ATTRIBUTE_LARGEST >= 8 ? ATTRIBUTE_LARGEST : 8); - -// BasicType maps the type to basic int(8|16|32|64)(s|u) types. -EmberAfAttributeType BaseType(EmberAfAttributeType type) -{ - switch (type) - { - case ZCL_ACTION_ID_ATTRIBUTE_TYPE: // Action Id - case ZCL_FABRIC_IDX_ATTRIBUTE_TYPE: // Fabric Index - case ZCL_BITMAP8_ATTRIBUTE_TYPE: // 8-bit bitmap - case ZCL_ENUM8_ATTRIBUTE_TYPE: // 8-bit enumeration - case ZCL_STATUS_ATTRIBUTE_TYPE: // Status Code - case ZCL_PERCENT_ATTRIBUTE_TYPE: // Percentage - static_assert(std::is_same::value, - "chip::Percent is expected to be uint8_t, change this when necessary"); - return ZCL_INT8U_ATTRIBUTE_TYPE; - - case ZCL_ENDPOINT_NO_ATTRIBUTE_TYPE: // Endpoint Number - case ZCL_GROUP_ID_ATTRIBUTE_TYPE: // Group Id - case ZCL_VENDOR_ID_ATTRIBUTE_TYPE: // Vendor Id - case ZCL_ENUM16_ATTRIBUTE_TYPE: // 16-bit enumeration - case ZCL_BITMAP16_ATTRIBUTE_TYPE: // 16-bit bitmap - case ZCL_PERCENT100THS_ATTRIBUTE_TYPE: // 100ths of a percent - static_assert(std::is_same::value, - "chip::EndpointId is expected to be uint16_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::GroupId is expected to be uint16_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::Percent100ths is expected to be uint16_t, change this when necessary"); - return ZCL_INT16U_ATTRIBUTE_TYPE; - - case ZCL_CLUSTER_ID_ATTRIBUTE_TYPE: // Cluster Id - case ZCL_ATTRIB_ID_ATTRIBUTE_TYPE: // Attribute Id - case ZCL_FIELD_ID_ATTRIBUTE_TYPE: // Field Id - case ZCL_EVENT_ID_ATTRIBUTE_TYPE: // Event Id - case ZCL_COMMAND_ID_ATTRIBUTE_TYPE: // Command Id - case ZCL_TRANS_ID_ATTRIBUTE_TYPE: // Transaction Id - case ZCL_DEVTYPE_ID_ATTRIBUTE_TYPE: // Device Type Id - case ZCL_DATA_VER_ATTRIBUTE_TYPE: // Data Version - case ZCL_BITMAP32_ATTRIBUTE_TYPE: // 32-bit bitmap - case ZCL_EPOCH_S_ATTRIBUTE_TYPE: // Epoch Seconds - case ZCL_ELAPSED_S_ATTRIBUTE_TYPE: // Elapsed Seconds - static_assert(std::is_same::value, - "chip::Cluster is expected to be uint32_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::AttributeId is expected to be uint32_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::AttributeId is expected to be uint32_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::EventId is expected to be uint32_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::CommandId is expected to be uint32_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::TransactionId is expected to be uint32_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::DeviceTypeId is expected to be uint32_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::DataVersion is expected to be uint32_t, change this when necessary"); - return ZCL_INT32U_ATTRIBUTE_TYPE; - - case ZCL_AMPERAGE_MA_ATTRIBUTE_TYPE: // Amperage milliamps - case ZCL_ENERGY_MWH_ATTRIBUTE_TYPE: // Energy milliwatt-hours - case ZCL_POWER_MW_ATTRIBUTE_TYPE: // Power milliwatts - case ZCL_VOLTAGE_MV_ATTRIBUTE_TYPE: // Voltage millivolts - return ZCL_INT64S_ATTRIBUTE_TYPE; - - case ZCL_EVENT_NO_ATTRIBUTE_TYPE: // Event Number - case ZCL_FABRIC_ID_ATTRIBUTE_TYPE: // Fabric Id - case ZCL_NODE_ID_ATTRIBUTE_TYPE: // Node Id - case ZCL_BITMAP64_ATTRIBUTE_TYPE: // 64-bit bitmap - case ZCL_EPOCH_US_ATTRIBUTE_TYPE: // Epoch Microseconds - case ZCL_POSIX_MS_ATTRIBUTE_TYPE: // POSIX Milliseconds - case ZCL_SYSTIME_MS_ATTRIBUTE_TYPE: // System time Milliseconds - case ZCL_SYSTIME_US_ATTRIBUTE_TYPE: // System time Microseconds - static_assert(std::is_same::value, - "chip::EventNumber is expected to be uint64_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::FabricId is expected to be uint64_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::NodeId is expected to be uint64_t, change this when necessary"); - return ZCL_INT64U_ATTRIBUTE_TYPE; - - case ZCL_TEMPERATURE_ATTRIBUTE_TYPE: // Temperature - return ZCL_INT16S_ATTRIBUTE_TYPE; - - default: - return type; - } -} - -} // namespace - -} // namespace Compatibility - -using namespace chip::app::Compatibility; - namespace { -// Common buffer for ReadSingleClusterData & WriteSingleClusterData -uint8_t attributeData[kAttributeReadBufferSize]; template CHIP_ERROR attributeBufferToNumericTlvData(TLV::TLVWriter & writer, bool isNullable) { typename NumericAttributeTraits::StorageType value; - memcpy(&value, attributeData, sizeof(value)); + memcpy(&value, gEmberAttributeIOBufferSpan.data(), sizeof(value)); TLV::Tag tag = TLV::ContextTag(AttributeDataIB::Tag::kData); if (isNullable && NumericAttributeTraits::IsNullValue(value)) { @@ -285,143 +187,6 @@ CHIP_ERROR SendFailureStatus(const ConcreteAttributePath & aPath, AttributeRepor return aAttributeReports.EncodeAttributeStatus(aPath, StatusIB(aStatus)); } -// This reader should never actually be registered; we do manual dispatch to it -// for the one attribute it handles. -class MandatoryGlobalAttributeReader : public AttributeAccessInterface -{ -public: - MandatoryGlobalAttributeReader(const EmberAfCluster * aCluster) : - AttributeAccessInterface(MakeOptional(kInvalidEndpointId), kInvalidClusterId), mCluster(aCluster) - {} - -protected: - const EmberAfCluster * mCluster; -}; - -class GlobalAttributeReader : public MandatoryGlobalAttributeReader -{ -public: - GlobalAttributeReader(const EmberAfCluster * aCluster) : MandatoryGlobalAttributeReader(aCluster) {} - - CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; - -private: - typedef CHIP_ERROR (CommandHandlerInterface::*CommandListEnumerator)(const ConcreteClusterPath & cluster, - CommandHandlerInterface::CommandIdCallback callback, - void * context); - static CHIP_ERROR EncodeCommandList(const ConcreteClusterPath & aClusterPath, AttributeValueEncoder & aEncoder, - CommandListEnumerator aEnumerator, const CommandId * aClusterCommandList); -}; - -CHIP_ERROR GlobalAttributeReader::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) -{ - using namespace Clusters::Globals::Attributes; - switch (aPath.mAttributeId) - { - case AttributeList::Id: - return aEncoder.EncodeList([this](const auto & encoder) { - const size_t count = mCluster->attributeCount; - bool addedExtraGlobals = false; - for (size_t i = 0; i < count; ++i) - { - AttributeId id = mCluster->attributes[i].attributeId; - constexpr auto lastGlobalId = GlobalAttributesNotInMetadata[ArraySize(GlobalAttributesNotInMetadata) - 1]; -#if CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE - // The GlobalAttributesNotInMetadata shouldn't have any gaps in their ids here. - static_assert(lastGlobalId - GlobalAttributesNotInMetadata[0] == ArraySize(GlobalAttributesNotInMetadata) - 1, - "Ids in GlobalAttributesNotInMetadata not consecutive"); -#else - // If EventList is not supported. The GlobalAttributesNotInMetadata is missing one id here. - static_assert(lastGlobalId - GlobalAttributesNotInMetadata[0] == ArraySize(GlobalAttributesNotInMetadata), - "Ids in GlobalAttributesNotInMetadata not consecutive (except EventList)"); -#endif // CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE - if (!addedExtraGlobals && id > lastGlobalId) - { - for (const auto & globalId : GlobalAttributesNotInMetadata) - { - ReturnErrorOnFailure(encoder.Encode(globalId)); - } - addedExtraGlobals = true; - } - ReturnErrorOnFailure(encoder.Encode(id)); - } - if (!addedExtraGlobals) - { - for (const auto & globalId : GlobalAttributesNotInMetadata) - { - ReturnErrorOnFailure(encoder.Encode(globalId)); - } - } - return CHIP_NO_ERROR; - }); -#if CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE - case EventList::Id: - return aEncoder.EncodeList([this](const auto & encoder) { - for (size_t i = 0; i < mCluster->eventCount; ++i) - { - ReturnErrorOnFailure(encoder.Encode(mCluster->eventList[i])); - } - return CHIP_NO_ERROR; - }); -#endif // CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE - case AcceptedCommandList::Id: - return EncodeCommandList(aPath, aEncoder, &CommandHandlerInterface::EnumerateAcceptedCommands, - mCluster->acceptedCommandList); - case GeneratedCommandList::Id: - return EncodeCommandList(aPath, aEncoder, &CommandHandlerInterface::EnumerateGeneratedCommands, - mCluster->generatedCommandList); - default: - // This function is only called if attributeCluster is non-null in - // ReadSingleClusterData, which only happens for attributes listed in - // GlobalAttributesNotInMetadata. If we reach this code, someone added - // a global attribute to that list but not the above switch. - VerifyOrDieWithMsg(false, DataManagement, "Unexpected global attribute: " ChipLogFormatMEI, - ChipLogValueMEI(aPath.mAttributeId)); - return CHIP_NO_ERROR; - } -} - -CHIP_ERROR GlobalAttributeReader::EncodeCommandList(const ConcreteClusterPath & aClusterPath, AttributeValueEncoder & aEncoder, - GlobalAttributeReader::CommandListEnumerator aEnumerator, - const CommandId * aClusterCommandList) -{ - return aEncoder.EncodeList([&](const auto & encoder) { - auto * commandHandler = - InteractionModelEngine::GetInstance()->FindCommandHandler(aClusterPath.mEndpointId, aClusterPath.mClusterId); - if (commandHandler) - { - struct Context - { - decltype(encoder) & commandIdEncoder; - CHIP_ERROR err; - } context{ encoder, CHIP_NO_ERROR }; - CHIP_ERROR err = (commandHandler->*aEnumerator)( - aClusterPath, - [](CommandId command, void * closure) -> Loop { - auto * ctx = static_cast(closure); - ctx->err = ctx->commandIdEncoder.Encode(command); - if (ctx->err != CHIP_NO_ERROR) - { - return Loop::Break; - } - return Loop::Continue; - }, - &context); - if (err != CHIP_ERROR_NOT_IMPLEMENTED) - { - return context.err; - } - // Else fall through to the list in aClusterCommandList. - } - - for (const CommandId * cmd = aClusterCommandList; cmd != nullptr && *cmd != kInvalidCommandId; cmd++) - { - ReturnErrorOnFailure(encoder.Encode(*cmd)); - } - return CHIP_NO_ERROR; - }); -} - // Helper function for trying to read an attribute value via an // AttributeAccessInterface. On failure, the read has failed. On success, the // aTriedEncode outparam is set to whether the AttributeAccessInterface tried to encode a value. @@ -603,7 +368,8 @@ CHIP_ERROR ReadSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, b record.endpoint = aPath.mEndpointId; record.clusterId = aPath.mClusterId; record.attributeId = aPath.mAttributeId; - Status status = emAfReadOrWriteAttribute(&record, &attributeMetadata, attributeData, sizeof(attributeData), + Status status = emAfReadOrWriteAttribute(&record, &attributeMetadata, gEmberAttributeIOBufferSpan.data(), + static_cast(gEmberAttributeIOBufferSpan.size()), /* write = */ false); if (status == Status::Success) @@ -613,7 +379,7 @@ CHIP_ERROR ReadSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, b TLV::TLVWriter * writer = attributeDataIBBuilder.GetWriter(); VerifyOrReturnError(writer != nullptr, CHIP_NO_ERROR); TLV::Tag tag = TLV::ContextTag(AttributeDataIB::Tag::kData); - switch (BaseType(attributeType)) + switch (AttributeBaseType(attributeType)) { case ZCL_NO_DATA_ATTRIBUTE_TYPE: // No data ReturnErrorOnFailure(writer->PutNull(tag)); @@ -719,8 +485,8 @@ CHIP_ERROR ReadSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, b } case ZCL_CHAR_STRING_ATTRIBUTE_TYPE: // Char string { - char * actualData = reinterpret_cast(attributeData + 1); - uint8_t dataLength = attributeData[0]; + char * actualData = reinterpret_cast(gEmberAttributeIOBufferSpan.data() + 1); + uint8_t dataLength = gEmberAttributeIOBufferSpan[0]; if (dataLength == 0xFF) { if (isNullable) @@ -739,9 +505,10 @@ CHIP_ERROR ReadSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, b break; } case ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE: { - char * actualData = reinterpret_cast(attributeData + 2); // The pascal string contains 2 bytes length + char * actualData = + reinterpret_cast(gEmberAttributeIOBufferSpan.data() + 2); // The pascal string contains 2 bytes length uint16_t dataLength; - memcpy(&dataLength, attributeData, sizeof(dataLength)); + memcpy(&dataLength, gEmberAttributeIOBufferSpan.data(), sizeof(dataLength)); if (dataLength == 0xFFFF) { if (isNullable) @@ -761,8 +528,8 @@ CHIP_ERROR ReadSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, b } case ZCL_OCTET_STRING_ATTRIBUTE_TYPE: // Octet string { - uint8_t * actualData = attributeData + 1; - uint8_t dataLength = attributeData[0]; + uint8_t * actualData = gEmberAttributeIOBufferSpan.data() + 1; + uint8_t dataLength = gEmberAttributeIOBufferSpan[0]; if (dataLength == 0xFF) { if (isNullable) @@ -781,9 +548,9 @@ CHIP_ERROR ReadSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, b break; } case ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE: { - uint8_t * actualData = attributeData + 2; // The pascal string contains 2 bytes length + uint8_t * actualData = gEmberAttributeIOBufferSpan.data() + 2; // The pascal string contains 2 bytes length uint16_t dataLength; - memcpy(&dataLength, attributeData, sizeof(dataLength)); + memcpy(&dataLength, gEmberAttributeIOBufferSpan.data(), sizeof(dataLength)); if (dataLength == 0xFFFF) { if (isNullable) @@ -821,7 +588,8 @@ template CHIP_ERROR numericTlvDataToAttributeBuffer(TLV::TLVReader & aReader, bool isNullable, uint16_t & dataLen) { typename NumericAttributeTraits::StorageType value; - static_assert(sizeof(value) <= sizeof(attributeData), "Value cannot fit into attribute data"); + VerifyOrDie(sizeof(value) <= gEmberAttributeIOBufferSpan.size()); + if (isNullable && aReader.GetType() == TLV::kTLVType_Null) { NumericAttributeTraits::SetNull(value); @@ -834,7 +602,7 @@ CHIP_ERROR numericTlvDataToAttributeBuffer(TLV::TLVReader & aReader, bool isNull NumericAttributeTraits::WorkingToStorage(val, value); } dataLen = sizeof(value); - memcpy(attributeData, &value, sizeof(value)); + memcpy(gEmberAttributeIOBufferSpan.data(), &value, sizeof(value)); return CHIP_NO_ERROR; } @@ -847,7 +615,7 @@ CHIP_ERROR stringTlvDataToAttributeBuffer(TLV::TLVReader & aReader, bool isOctet { // Null is represented by an 0xFF or 0xFFFF length, respectively. len = std::numeric_limits::max(); - memcpy(&attributeData[0], &len, sizeof(len)); + memcpy(gEmberAttributeIOBufferSpan.data(), &len, sizeof(len)); dataLen = sizeof(len); } else @@ -859,10 +627,10 @@ CHIP_ERROR stringTlvDataToAttributeBuffer(TLV::TLVReader & aReader, bool isOctet ReturnErrorOnFailure(aReader.GetDataPtr(data)); len = static_cast(aReader.GetLength()); VerifyOrReturnError(len != std::numeric_limits::max(), CHIP_ERROR_MESSAGE_TOO_LONG); - VerifyOrReturnError(len + sizeof(len) /* length at the beginning of data */ <= sizeof(attributeData), + VerifyOrReturnError(len + sizeof(len) /* length at the beginning of data */ <= gEmberAttributeIOBufferSpan.size(), CHIP_ERROR_MESSAGE_TOO_LONG); - memcpy(&attributeData[0], &len, sizeof(len)); - memcpy(&attributeData[sizeof(len)], data, len); + memcpy(gEmberAttributeIOBufferSpan.data(), &len, sizeof(len)); + memcpy(gEmberAttributeIOBufferSpan.data() + sizeof(len), data, len); dataLen = static_cast(len + sizeof(len)); } return CHIP_NO_ERROR; @@ -870,7 +638,7 @@ CHIP_ERROR stringTlvDataToAttributeBuffer(TLV::TLVReader & aReader, bool isOctet CHIP_ERROR prepareWriteData(const EmberAfAttributeMetadata * attributeMetadata, TLV::TLVReader & aReader, uint16_t & dataLen) { - EmberAfAttributeType expectedType = BaseType(attributeMetadata->attributeType); + EmberAfAttributeType expectedType = AttributeBaseType(attributeMetadata->attributeType); bool isNullable = attributeMetadata->IsNullable(); switch (expectedType) { @@ -1031,8 +799,8 @@ CHIP_ERROR WriteSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, return apWriteHandler->AddStatus(aPath, Protocols::InteractionModel::Status::InvalidValue); } - auto status = emAfWriteAttributeExternal(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId, attributeData, - attributeMetadata->attributeType); + auto status = emAfWriteAttributeExternal(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId, + gEmberAttributeIOBufferSpan.data(), attributeMetadata->attributeType); return apWriteHandler->AddStatus(aPath, status); } diff --git a/src/app/util/ember-global-attribute-access-interface.cpp b/src/app/util/ember-global-attribute-access-interface.cpp new file mode 100644 index 00000000000000..327ab09c479512 --- /dev/null +++ b/src/app/util/ember-global-attribute-access-interface.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2021-2024 Project CHIP Authors + * + * 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 + +namespace chip { +namespace app { +namespace Compatibility { + +CHIP_ERROR GlobalAttributeReader::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + using namespace Clusters::Globals::Attributes; + switch (aPath.mAttributeId) + { + case AttributeList::Id: + return aEncoder.EncodeList([this](const auto & encoder) { + const size_t count = mCluster->attributeCount; + bool addedExtraGlobals = false; + for (size_t i = 0; i < count; ++i) + { + AttributeId id = mCluster->attributes[i].attributeId; + constexpr auto lastGlobalId = GlobalAttributesNotInMetadata[ArraySize(GlobalAttributesNotInMetadata) - 1]; +#if CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE + // The GlobalAttributesNotInMetadata shouldn't have any gaps in their ids here. + static_assert(lastGlobalId - GlobalAttributesNotInMetadata[0] == ArraySize(GlobalAttributesNotInMetadata) - 1, + "Ids in GlobalAttributesNotInMetadata not consecutive"); +#else + // If EventList is not supported. The GlobalAttributesNotInMetadata is missing one id here. + static_assert(lastGlobalId - GlobalAttributesNotInMetadata[0] == ArraySize(GlobalAttributesNotInMetadata), + "Ids in GlobalAttributesNotInMetadata not consecutive (except EventList)"); +#endif // CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE + if (!addedExtraGlobals && id > lastGlobalId) + { + for (const auto & globalId : GlobalAttributesNotInMetadata) + { + ReturnErrorOnFailure(encoder.Encode(globalId)); + } + addedExtraGlobals = true; + } + ReturnErrorOnFailure(encoder.Encode(id)); + } + if (!addedExtraGlobals) + { + for (const auto & globalId : GlobalAttributesNotInMetadata) + { + ReturnErrorOnFailure(encoder.Encode(globalId)); + } + } + return CHIP_NO_ERROR; + }); +#if CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE + case EventList::Id: + return aEncoder.EncodeList([this](const auto & encoder) { + for (size_t i = 0; i < mCluster->eventCount; ++i) + { + ReturnErrorOnFailure(encoder.Encode(mCluster->eventList[i])); + } + return CHIP_NO_ERROR; + }); +#endif // CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE + case AcceptedCommandList::Id: + return EncodeCommandList(aPath, aEncoder, &CommandHandlerInterface::EnumerateAcceptedCommands, + mCluster->acceptedCommandList); + case GeneratedCommandList::Id: + return EncodeCommandList(aPath, aEncoder, &CommandHandlerInterface::EnumerateGeneratedCommands, + mCluster->generatedCommandList); + default: + // This function is only called if attributeCluster is non-null in + // ReadSingleClusterData, which only happens for attributes listed in + // GlobalAttributesNotInMetadata. If we reach this code, someone added + // a global attribute to that list but not the above switch. + VerifyOrDieWithMsg(false, DataManagement, "Unexpected global attribute: " ChipLogFormatMEI, + ChipLogValueMEI(aPath.mAttributeId)); + return CHIP_NO_ERROR; + } +} + +CHIP_ERROR GlobalAttributeReader::EncodeCommandList(const ConcreteClusterPath & aClusterPath, AttributeValueEncoder & aEncoder, + GlobalAttributeReader::CommandListEnumerator aEnumerator, + const CommandId * aClusterCommandList) +{ + return aEncoder.EncodeList([&](const auto & encoder) { + auto * commandHandler = + InteractionModelEngine::GetInstance()->FindCommandHandler(aClusterPath.mEndpointId, aClusterPath.mClusterId); + if (commandHandler) + { + struct Context + { + decltype(encoder) & commandIdEncoder; + CHIP_ERROR err; + } context{ encoder, CHIP_NO_ERROR }; + CHIP_ERROR err = (commandHandler->*aEnumerator)( + aClusterPath, + [](CommandId command, void * closure) -> Loop { + auto * ctx = static_cast(closure); + ctx->err = ctx->commandIdEncoder.Encode(command); + if (ctx->err != CHIP_NO_ERROR) + { + return Loop::Break; + } + return Loop::Continue; + }, + &context); + if (err != CHIP_ERROR_NOT_IMPLEMENTED) + { + return context.err; + } + // Else fall through to the list in aClusterCommandList. + } + + for (const CommandId * cmd = aClusterCommandList; cmd != nullptr && *cmd != kInvalidCommandId; cmd++) + { + ReturnErrorOnFailure(encoder.Encode(*cmd)); + } + return CHIP_NO_ERROR; + }); +} + +} // namespace Compatibility +} // namespace app +} // namespace chip diff --git a/src/app/util/ember-global-attribute-access-interface.h b/src/app/util/ember-global-attribute-access-interface.h new file mode 100644 index 00000000000000..d17e1b286dbd81 --- /dev/null +++ b/src/app/util/ember-global-attribute-access-interface.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021-2024 Project CHIP Authors + * + * 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 +#include +#include + +namespace chip { +namespace app { +namespace Compatibility { + +// This reader should never actually be registered; we do manual dispatch to it +// for the one attribute it handles. +class MandatoryGlobalAttributeReader : public AttributeAccessInterface +{ +public: + MandatoryGlobalAttributeReader(const EmberAfCluster * aCluster) : + AttributeAccessInterface(MakeOptional(kInvalidEndpointId), kInvalidClusterId), mCluster(aCluster) + {} + +protected: + const EmberAfCluster * mCluster; +}; + +class GlobalAttributeReader : public MandatoryGlobalAttributeReader +{ +public: + GlobalAttributeReader(const EmberAfCluster * aCluster) : MandatoryGlobalAttributeReader(aCluster) {} + + CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; + +private: + typedef CHIP_ERROR (CommandHandlerInterface::*CommandListEnumerator)(const ConcreteClusterPath & cluster, + CommandHandlerInterface::CommandIdCallback callback, + void * context); + static CHIP_ERROR EncodeCommandList(const ConcreteClusterPath & aClusterPath, AttributeValueEncoder & aEncoder, + CommandListEnumerator aEnumerator, const CommandId * aClusterCommandList); +}; + +} // namespace Compatibility +} // namespace app +} // namespace chip diff --git a/src/app/util/ember-io-storage.cpp b/src/app/util/ember-io-storage.cpp new file mode 100644 index 00000000000000..cc5eacf733480c --- /dev/null +++ b/src/app/util/ember-io-storage.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2024 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 + +namespace chip { +namespace app { +namespace Compatibility { +namespace Internal { + +// On some apps, ATTRIBUTE_LARGEST can as small as 3, making compiler unhappy since data[kAttributeReadBufferSize] cannot hold +// uint64_t. Make kAttributeReadBufferSize at least 8 so it can fit all basic types. +constexpr size_t kAttributeReadBufferSize = (ATTRIBUTE_LARGEST >= 8 ? ATTRIBUTE_LARGEST : 8); +uint8_t attributeIOBuffer[kAttributeReadBufferSize]; + +MutableByteSpan gEmberAttributeIOBufferSpan(attributeIOBuffer); + +EmberAfAttributeType AttributeBaseType(EmberAfAttributeType type) +{ + switch (type) + { + case ZCL_ACTION_ID_ATTRIBUTE_TYPE: // Action Id + case ZCL_FABRIC_IDX_ATTRIBUTE_TYPE: // Fabric Index + case ZCL_BITMAP8_ATTRIBUTE_TYPE: // 8-bit bitmap + case ZCL_ENUM8_ATTRIBUTE_TYPE: // 8-bit enumeration + case ZCL_STATUS_ATTRIBUTE_TYPE: // Status Code + case ZCL_PERCENT_ATTRIBUTE_TYPE: // Percentage + static_assert(std::is_same::value, + "chip::Percent is expected to be uint8_t, change this when necessary"); + return ZCL_INT8U_ATTRIBUTE_TYPE; + + case ZCL_ENDPOINT_NO_ATTRIBUTE_TYPE: // Endpoint Number + case ZCL_GROUP_ID_ATTRIBUTE_TYPE: // Group Id + case ZCL_VENDOR_ID_ATTRIBUTE_TYPE: // Vendor Id + case ZCL_ENUM16_ATTRIBUTE_TYPE: // 16-bit enumeration + case ZCL_BITMAP16_ATTRIBUTE_TYPE: // 16-bit bitmap + case ZCL_PERCENT100THS_ATTRIBUTE_TYPE: // 100ths of a percent + static_assert(std::is_same::value, + "chip::EndpointId is expected to be uint16_t, change this when necessary"); + static_assert(std::is_same::value, + "chip::GroupId is expected to be uint16_t, change this when necessary"); + static_assert(std::is_same::value, + "chip::Percent100ths is expected to be uint16_t, change this when necessary"); + return ZCL_INT16U_ATTRIBUTE_TYPE; + + case ZCL_CLUSTER_ID_ATTRIBUTE_TYPE: // Cluster Id + case ZCL_ATTRIB_ID_ATTRIBUTE_TYPE: // Attribute Id + case ZCL_FIELD_ID_ATTRIBUTE_TYPE: // Field Id + case ZCL_EVENT_ID_ATTRIBUTE_TYPE: // Event Id + case ZCL_COMMAND_ID_ATTRIBUTE_TYPE: // Command Id + case ZCL_TRANS_ID_ATTRIBUTE_TYPE: // Transaction Id + case ZCL_DEVTYPE_ID_ATTRIBUTE_TYPE: // Device Type Id + case ZCL_DATA_VER_ATTRIBUTE_TYPE: // Data Version + case ZCL_BITMAP32_ATTRIBUTE_TYPE: // 32-bit bitmap + case ZCL_EPOCH_S_ATTRIBUTE_TYPE: // Epoch Seconds + case ZCL_ELAPSED_S_ATTRIBUTE_TYPE: // Elapsed Seconds + static_assert(std::is_same::value, + "chip::Cluster is expected to be uint32_t, change this when necessary"); + static_assert(std::is_same::value, + "chip::AttributeId is expected to be uint32_t, change this when necessary"); + static_assert(std::is_same::value, + "chip::AttributeId is expected to be uint32_t, change this when necessary"); + static_assert(std::is_same::value, + "chip::EventId is expected to be uint32_t, change this when necessary"); + static_assert(std::is_same::value, + "chip::CommandId is expected to be uint32_t, change this when necessary"); + static_assert(std::is_same::value, + "chip::TransactionId is expected to be uint32_t, change this when necessary"); + static_assert(std::is_same::value, + "chip::DeviceTypeId is expected to be uint32_t, change this when necessary"); + static_assert(std::is_same::value, + "chip::DataVersion is expected to be uint32_t, change this when necessary"); + return ZCL_INT32U_ATTRIBUTE_TYPE; + + case ZCL_AMPERAGE_MA_ATTRIBUTE_TYPE: // Amperage milliamps + case ZCL_ENERGY_MWH_ATTRIBUTE_TYPE: // Energy milliwatt-hours + case ZCL_POWER_MW_ATTRIBUTE_TYPE: // Power milliwatts + case ZCL_VOLTAGE_MV_ATTRIBUTE_TYPE: // Voltage millivolts + return ZCL_INT64S_ATTRIBUTE_TYPE; + + case ZCL_EVENT_NO_ATTRIBUTE_TYPE: // Event Number + case ZCL_FABRIC_ID_ATTRIBUTE_TYPE: // Fabric Id + case ZCL_NODE_ID_ATTRIBUTE_TYPE: // Node Id + case ZCL_BITMAP64_ATTRIBUTE_TYPE: // 64-bit bitmap + case ZCL_EPOCH_US_ATTRIBUTE_TYPE: // Epoch Microseconds + case ZCL_POSIX_MS_ATTRIBUTE_TYPE: // POSIX Milliseconds + case ZCL_SYSTIME_MS_ATTRIBUTE_TYPE: // System time Milliseconds + case ZCL_SYSTIME_US_ATTRIBUTE_TYPE: // System time Microseconds + static_assert(std::is_same::value, + "chip::EventNumber is expected to be uint64_t, change this when necessary"); + static_assert(std::is_same::value, + "chip::FabricId is expected to be uint64_t, change this when necessary"); + static_assert(std::is_same::value, + "chip::NodeId is expected to be uint64_t, change this when necessary"); + return ZCL_INT64U_ATTRIBUTE_TYPE; + + case ZCL_TEMPERATURE_ATTRIBUTE_TYPE: // Temperature + return ZCL_INT16S_ATTRIBUTE_TYPE; + + default: + return type; + } +} + +} // namespace Internal +} // namespace Compatibility +} // namespace app +} // namespace chip diff --git a/src/app/util/ember-io-storage.h b/src/app/util/ember-io-storage.h new file mode 100644 index 00000000000000..4297bc73d0c176 --- /dev/null +++ b/src/app/util/ember-io-storage.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 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 + +#include +#include + +namespace chip { +namespace app { +namespace Compatibility { +namespace Internal { + +/// A buffer guaranteed to be sized sufficiently large to contain any individual value from +/// the ember attribute/data store (i.e. a buffer that can be used to read ember data into +/// or as a temporary buffer to place data before asking ember to store it). +/// +/// This buffer is intended to be used for calls to `emAfReadOrWriteAttribute` and +/// `emAfWriteAttributeExternal`: it is sufficiently sized to be able to handle any +/// max-sized data that ember is aware of. +extern MutableByteSpan gEmberAttributeIOBufferSpan; + +/// Maps an attribute type that is not an integer but can be represented as an integer to the +/// corresponding basic int(8|16|32|64)(s|u) type +/// +/// For example: +/// ZCL_ENUM8_ATTRIBUTE_TYPE maps to ZCL_INT8U_ATTRIBUTE_TYPE +/// ZCL_VENDOR_ID_ATTRIBUTE_TYPE maps to ZCL_INT16U_ATTRIBUTE_TYPE +/// ZCL_BITMAP32_ATTRIBUTE_TYPE maps to ZCL_INT32U_ATTRIBUTE_TYPE +/// ZCL_VOLTAGE_MV_ATTRIBUTE_TYPE maps to ZCL_INT64S_ATTRIBUTE_TYPE +/// ... +/// +/// If the `type` cannot be mapped to a basic type (or is already a basic type) its value +/// is returned unchanged. +EmberAfAttributeType AttributeBaseType(EmberAfAttributeType type); + +} // namespace Internal +} // namespace Compatibility +} // namespace app +} // namespace chip diff --git a/src/app/util/mock/BUILD.gn b/src/app/util/mock/BUILD.gn index da44fb84e5a809..b63c49e68f2494 100644 --- a/src/app/util/mock/BUILD.gn +++ b/src/app/util/mock/BUILD.gn @@ -25,6 +25,7 @@ source_set("mock_ember") { "MockNodeConfig.cpp", "MockNodeConfig.h", "attribute-storage.cpp", + "privilege-storage.cpp", ] public_deps = [ diff --git a/src/app/util/mock/attribute-storage.cpp b/src/app/util/mock/attribute-storage.cpp index 2293a48a8403e4..e4c965e98905f4 100644 --- a/src/app/util/mock/attribute-storage.cpp +++ b/src/app/util/mock/attribute-storage.cpp @@ -152,6 +152,50 @@ uint8_t emberAfGetClusterCountForEndpoint(EndpointId endpointId) return static_cast(endpoint->clusters.size()); } +const EmberAfAttributeMetadata * emberAfLocateAttributeMetadata(EndpointId endpointId, ClusterId clusterId, AttributeId attributeId) +{ + auto ep = GetMockNodeConfig().endpointById(endpointId); + VerifyOrReturnValue(ep != nullptr, nullptr); + + auto cluster = ep->clusterById(clusterId); + VerifyOrReturnValue(cluster != nullptr, nullptr); + + auto attr = cluster->attributeById(attributeId); + VerifyOrReturnValue(attr != nullptr, nullptr); + + return &attr->attributeMetaData; +} + +const EmberAfCluster * emberAfFindClusterInType(const EmberAfEndpointType * endpointType, ClusterId clusterId, + EmberAfClusterMask mask, uint8_t * index) +{ + // This is a copy & paste implementation from ember attribute storage + // TODO: this hard-codes ember logic and is duplicated code. + uint8_t scopedIndex = 0; + + for (uint8_t i = 0; i < endpointType->clusterCount; i++) + { + const EmberAfCluster * cluster = &(endpointType->cluster[i]); + + if (mask == 0 || ((cluster->mask & mask) != 0)) + { + if (cluster->clusterId == clusterId) + { + if (index) + { + *index = scopedIndex; + } + + return cluster; + } + + scopedIndex++; + } + } + + return nullptr; +} + uint8_t emberAfClusterCount(chip::EndpointId endpoint, bool server) { return (server) ? emberAfGetClusterCountForEndpoint(endpoint) : 0; @@ -414,6 +458,7 @@ void SetMockNodeConfig(const MockNodeConfig & config) mockConfig = &config; } +/// Resets the mock attribute storage to the default configuration. void ResetMockNodeConfig() { mockConfig = nullptr; diff --git a/src/app/util/mock/include/zap-generated/endpoint_config.h b/src/app/util/mock/include/zap-generated/endpoint_config.h index 33d1e87e320896..620e6e29880f36 100644 --- a/src/app/util/mock/include/zap-generated/endpoint_config.h +++ b/src/app/util/mock/include/zap-generated/endpoint_config.h @@ -1,2 +1,4 @@ // Number of fixed endpoints #define FIXED_ENDPOINT_COUNT (3) + +#define ATTRIBUTE_LARGEST (1003) diff --git a/src/app/tests/integration/RequiredPrivilegeStubs.cpp b/src/app/util/mock/privilege-storage.cpp similarity index 82% rename from src/app/tests/integration/RequiredPrivilegeStubs.cpp rename to src/app/util/mock/privilege-storage.cpp index 2cc23056e5d34a..26ff8967944e12 100644 --- a/src/app/tests/integration/RequiredPrivilegeStubs.cpp +++ b/src/app/util/mock/privilege-storage.cpp @@ -1,6 +1,5 @@ -/* - * - * Copyright (c) 2022 Project CHIP Authors +/** + * Copyright (c) 2024 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,8 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include +#include -#include +// Privilege mocks here are MUCH more strict so that +// testing code can generally validatate access without something +// being permissive like kView. chip::Access::Privilege MatterGetAccessPrivilegeForReadAttribute(chip::ClusterId cluster, chip::AttributeId attribute) { diff --git a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj index 4cb4edf4a8edd3..36aa65dfaa2752 100644 --- a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj @@ -364,6 +364,10 @@ B4FCD5732B611EB300832859 /* MTRDiagnosticLogsDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = B4C8E6B32B3453AD00FCD54D /* MTRDiagnosticLogsDownloader.h */; }; BA09EB43247477BA00605257 /* libCHIP.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BA09EB3F2474762900605257 /* libCHIP.a */; }; D4772A46285AE98400383630 /* MTRClusterConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = D4772A45285AE98300383630 /* MTRClusterConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E04AC67D2BEEA17F00BA409B /* ember-io-storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E04AC67B2BEEA17F00BA409B /* ember-io-storage.cpp */; }; + E04AC67E2BEEA17F00BA409B /* ember-io-storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E04AC67B2BEEA17F00BA409B /* ember-io-storage.cpp */; }; + E04AC67F2BEEA17F00BA409B /* ember-global-attribute-access-interface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E04AC67C2BEEA17F00BA409B /* ember-global-attribute-access-interface.cpp */; }; + E04AC6802BEEA17F00BA409B /* ember-global-attribute-access-interface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E04AC67C2BEEA17F00BA409B /* ember-global-attribute-access-interface.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -788,6 +792,8 @@ D437613F285BDC0D0051FEA2 /* MTRTestKeys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRTestKeys.h; sourceTree = ""; }; D4376140285BDC0D0051FEA2 /* MTRTestStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRTestStorage.h; sourceTree = ""; }; D4772A45285AE98300383630 /* MTRClusterConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRClusterConstants.h; sourceTree = ""; }; + E04AC67B2BEEA17F00BA409B /* ember-io-storage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "ember-io-storage.cpp"; path = "util/ember-io-storage.cpp"; sourceTree = ""; }; + E04AC67C2BEEA17F00BA409B /* ember-global-attribute-access-interface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "ember-global-attribute-access-interface.cpp"; path = "util/ember-global-attribute-access-interface.cpp"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1033,6 +1039,8 @@ 514C79F22B62ED5500DD6D7B /* attribute-storage.cpp */, 514C79EF2B62ADDA00DD6D7B /* descriptor.cpp */, 514C79EC2B62ADCD00DD6D7B /* ember-compatibility-functions.cpp */, + E04AC67C2BEEA17F00BA409B /* ember-global-attribute-access-interface.cpp */, + E04AC67B2BEEA17F00BA409B /* ember-io-storage.cpp */, 516415FE2B6B132200D5CE11 /* DataModelHandler.cpp */, 514C79F52B62F0B900DD6D7B /* util.cpp */, 514C79FB2B62F94C00DD6D7B /* ota-provider.cpp */, @@ -1845,6 +1853,7 @@ B45373FE2A9FEC4F00807602 /* unix-fds.c in Sources */, B45374002A9FEC4F00807602 /* unix-init.c in Sources */, B45373FF2A9FEC4F00807602 /* unix-misc.c in Sources */, + E04AC67E2BEEA17F00BA409B /* ember-io-storage.cpp in Sources */, B45373FD2A9FEC4F00807602 /* unix-pipe.c in Sources */, B45373FB2A9FEC4F00807602 /* unix-service.c in Sources */, B45374012A9FEC4F00807602 /* unix-sockets.c in Sources */, @@ -1891,6 +1900,7 @@ 037C3DB62991BD5000B7EEE2 /* ModelCommandBridge.mm in Sources */, 516411322B6BF75700E67C05 /* MTRIMDispatch.mm in Sources */, 037C3DB42991BD5000B7EEE2 /* DeviceControllerDelegateBridge.mm in Sources */, + E04AC6802BEEA17F00BA409B /* ember-global-attribute-access-interface.cpp in Sources */, 039547012992D461006D42A8 /* generic-callback-stubs.cpp in Sources */, 514C79F12B62ADDA00DD6D7B /* descriptor.cpp in Sources */, ); @@ -1926,6 +1936,7 @@ 7534F12828BFF20300390851 /* MTRDeviceAttestationDelegate.mm in Sources */, B4C8E6B72B3453AD00FCD54D /* MTRDiagnosticLogsDownloader.mm in Sources */, 2C5EEEF7268A85C400CAE3D3 /* MTRDeviceConnectionBridge.mm in Sources */, + E04AC67D2BEEA17F00BA409B /* ember-io-storage.cpp in Sources */, 51B22C262740CB32008D5055 /* MTRStructsObjc.mm in Sources */, 2C222AD1255C620600E446B9 /* MTRBaseDevice.mm in Sources */, 1EC3238D271999E2002A8BF0 /* cluster-objects.cpp in Sources */, @@ -1967,6 +1978,7 @@ 5178E67E2AE098210069DF72 /* MTRCommandTimedCheck.mm in Sources */, 7596A84928762783004DAE0E /* MTRAsyncCallbackWorkQueue.mm in Sources */, B2E0D7B9245B0B5C003C5B48 /* MTRSetupPayload.mm in Sources */, + E04AC67F2BEEA17F00BA409B /* ember-global-attribute-access-interface.cpp in Sources */, B2E0D7B6245B0B5C003C5B48 /* MTRManualSetupPayloadParser.mm in Sources */, 7596A85528788557004DAE0E /* MTRClusters.mm in Sources */, 88EBF8CF27FABDD500686BC1 /* MTRDeviceAttestationDelegateBridge.mm in Sources */,