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 <bzbarsky@apple.com>
* Update ember-io-storage.h
Add additional comments.
* Restyle
---------
Co-authored-by: Andrei Litvin <andreilitvin@google.com>
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 7336213..4685948 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -119,6 +119,10 @@
--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 5573eeb..2d4149b 100644
--- a/src/app/chip_data_model.cmake
+++ b/src/app/chip_data_model.cmake
@@ -144,6 +144,8 @@
${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 c8a30b1..a68d193 100644
--- a/src/app/chip_data_model.gni
+++ b/src/app/chip_data_model.gni
@@ -209,6 +209,8 @@
"${_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 49196f2..df6737a 100644
--- a/src/app/tests/BUILD.gn
+++ b/src/app/tests/BUILD.gn
@@ -29,7 +29,6 @@
"${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 da0db88..49aea90 100644
--- a/src/app/tests/integration/BUILD.gn
+++ b/src/app/tests/integration/BUILD.gn
@@ -38,7 +38,6 @@
executable("chip-im-initiator") {
sources = [
"${chip_root}/src/app/reporting/tests/MockReportScheduler.cpp",
- "RequiredPrivilegeStubs.cpp",
"chip_im_initiator.cpp",
]
@@ -61,7 +60,6 @@
"${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 e87e0f5..cc3185f 100644
--- a/src/app/util/ember-compatibility-functions.cpp
+++ b/src/app/util/ember-compatibility-functions.cpp
@@ -33,6 +33,8 @@
#include <app/util/attribute-table-detail.h>
#include <app/util/attribute-table.h>
#include <app/util/config.h>
+#include <app/util/ember-global-attribute-access-interface.h>
+#include <app/util/ember-io-storage.h>
#include <app/util/odd-sized-integers.h>
#include <app/util/util.h>
#include <lib/core/CHIPCore.h>
@@ -54,118 +56,18 @@
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<chip::Percent, uint8_t>::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<chip::EndpointId, uint16_t>::value,
- "chip::EndpointId is expected to be uint16_t, change this when necessary");
- static_assert(std::is_same<chip::GroupId, uint16_t>::value,
- "chip::GroupId is expected to be uint16_t, change this when necessary");
- static_assert(std::is_same<chip::Percent100ths, uint16_t>::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<chip::ClusterId, uint32_t>::value,
- "chip::Cluster is expected to be uint32_t, change this when necessary");
- static_assert(std::is_same<chip::AttributeId, uint32_t>::value,
- "chip::AttributeId is expected to be uint32_t, change this when necessary");
- static_assert(std::is_same<chip::AttributeId, uint32_t>::value,
- "chip::AttributeId is expected to be uint32_t, change this when necessary");
- static_assert(std::is_same<chip::EventId, uint32_t>::value,
- "chip::EventId is expected to be uint32_t, change this when necessary");
- static_assert(std::is_same<chip::CommandId, uint32_t>::value,
- "chip::CommandId is expected to be uint32_t, change this when necessary");
- static_assert(std::is_same<chip::TransactionId, uint32_t>::value,
- "chip::TransactionId is expected to be uint32_t, change this when necessary");
- static_assert(std::is_same<chip::DeviceTypeId, uint32_t>::value,
- "chip::DeviceTypeId is expected to be uint32_t, change this when necessary");
- static_assert(std::is_same<chip::DataVersion, uint32_t>::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<chip::EventNumber, uint64_t>::value,
- "chip::EventNumber is expected to be uint64_t, change this when necessary");
- static_assert(std::is_same<chip::FabricId, uint64_t>::value,
- "chip::FabricId is expected to be uint64_t, change this when necessary");
- static_assert(std::is_same<chip::NodeId, uint64_t>::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 <typename T>
CHIP_ERROR attributeBufferToNumericTlvData(TLV::TLVWriter & writer, bool isNullable)
{
typename NumericAttributeTraits<T>::StorageType value;
- memcpy(&value, attributeData, sizeof(value));
+ memcpy(&value, gEmberAttributeIOBufferSpan.data(), sizeof(value));
TLV::Tag tag = TLV::ContextTag(AttributeDataIB::Tag::kData);
if (isNullable && NumericAttributeTraits<T>::IsNullValue(value))
{
@@ -285,143 +187,6 @@
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<Context *>(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 @@
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<uint16_t>(gEmberAttributeIOBufferSpan.size()),
/* write = */ false);
if (status == Status::Success)
@@ -613,7 +379,7 @@
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 @@
}
case ZCL_CHAR_STRING_ATTRIBUTE_TYPE: // Char string
{
- char * actualData = reinterpret_cast<char *>(attributeData + 1);
- uint8_t dataLength = attributeData[0];
+ char * actualData = reinterpret_cast<char *>(gEmberAttributeIOBufferSpan.data() + 1);
+ uint8_t dataLength = gEmberAttributeIOBufferSpan[0];
if (dataLength == 0xFF)
{
if (isNullable)
@@ -739,9 +505,10 @@
break;
}
case ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE: {
- char * actualData = reinterpret_cast<char *>(attributeData + 2); // The pascal string contains 2 bytes length
+ char * actualData =
+ reinterpret_cast<char *>(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 @@
}
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 @@
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 @@
CHIP_ERROR numericTlvDataToAttributeBuffer(TLV::TLVReader & aReader, bool isNullable, uint16_t & dataLen)
{
typename NumericAttributeTraits<T>::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<T>::SetNull(value);
@@ -834,7 +602,7 @@
NumericAttributeTraits<T>::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 @@
{
// Null is represented by an 0xFF or 0xFFFF length, respectively.
len = std::numeric_limits<T>::max();
- memcpy(&attributeData[0], &len, sizeof(len));
+ memcpy(gEmberAttributeIOBufferSpan.data(), &len, sizeof(len));
dataLen = sizeof(len);
}
else
@@ -859,10 +627,10 @@
ReturnErrorOnFailure(aReader.GetDataPtr(data));
len = static_cast<T>(aReader.GetLength());
VerifyOrReturnError(len != std::numeric_limits<T>::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<uint16_t>(len + sizeof(len));
}
return CHIP_NO_ERROR;
@@ -870,7 +638,7 @@
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 @@
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 0000000..327ab09
--- /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 <app/util/ember-global-attribute-access-interface.h>
+
+#include <app/GlobalAttributes.h>
+#include <app/InteractionModelEngine.h>
+
+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<Context *>(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 0000000..d17e1b2
--- /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 <app/AttributeAccessInterface.h>
+#include <app/CommandHandlerInterface.h>
+#include <app/util/af-types.h>
+
+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 0000000..cc5eacf
--- /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 <app/util/ember-io-storage.h>
+
+#include <app-common/zap-generated/attribute-type.h>
+#include <zap-generated/endpoint_config.h>
+
+#include <cstddef>
+
+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<chip::Percent, uint8_t>::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<chip::EndpointId, uint16_t>::value,
+ "chip::EndpointId is expected to be uint16_t, change this when necessary");
+ static_assert(std::is_same<chip::GroupId, uint16_t>::value,
+ "chip::GroupId is expected to be uint16_t, change this when necessary");
+ static_assert(std::is_same<chip::Percent100ths, uint16_t>::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<chip::ClusterId, uint32_t>::value,
+ "chip::Cluster is expected to be uint32_t, change this when necessary");
+ static_assert(std::is_same<chip::AttributeId, uint32_t>::value,
+ "chip::AttributeId is expected to be uint32_t, change this when necessary");
+ static_assert(std::is_same<chip::AttributeId, uint32_t>::value,
+ "chip::AttributeId is expected to be uint32_t, change this when necessary");
+ static_assert(std::is_same<chip::EventId, uint32_t>::value,
+ "chip::EventId is expected to be uint32_t, change this when necessary");
+ static_assert(std::is_same<chip::CommandId, uint32_t>::value,
+ "chip::CommandId is expected to be uint32_t, change this when necessary");
+ static_assert(std::is_same<chip::TransactionId, uint32_t>::value,
+ "chip::TransactionId is expected to be uint32_t, change this when necessary");
+ static_assert(std::is_same<chip::DeviceTypeId, uint32_t>::value,
+ "chip::DeviceTypeId is expected to be uint32_t, change this when necessary");
+ static_assert(std::is_same<chip::DataVersion, uint32_t>::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<chip::EventNumber, uint64_t>::value,
+ "chip::EventNumber is expected to be uint64_t, change this when necessary");
+ static_assert(std::is_same<chip::FabricId, uint64_t>::value,
+ "chip::FabricId is expected to be uint64_t, change this when necessary");
+ static_assert(std::is_same<chip::NodeId, uint64_t>::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 0000000..4297bc7
--- /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 <cstdint>
+
+#include <app/util/attribute-metadata.h>
+#include <lib/support/Span.h>
+
+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 da44fb8..b63c49e 100644
--- a/src/app/util/mock/BUILD.gn
+++ b/src/app/util/mock/BUILD.gn
@@ -25,6 +25,7 @@
"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 2293a48..e4c965e 100644
--- a/src/app/util/mock/attribute-storage.cpp
+++ b/src/app/util/mock/attribute-storage.cpp
@@ -152,6 +152,50 @@
return static_cast<uint8_t>(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 @@
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 33d1e87..620e6e2 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 81%
rename from src/app/tests/integration/RequiredPrivilegeStubs.cpp
rename to src/app/util/mock/privilege-storage.cpp
index 2cc2305..26ff896 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 <access/Privilege.h>
+#include <lib/core/DataModelTypes.h>
-#include <app/util/privilege-storage.h>
+// 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 4cb4edf..36aa65d 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 = "<group>"; };
D4376140285BDC0D0051FEA2 /* MTRTestStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRTestStorage.h; sourceTree = "<group>"; };
D4772A45285AE98300383630 /* MTRClusterConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRClusterConstants.h; sourceTree = "<group>"; };
+ 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 = "<group>"; };
+ 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 = "<group>"; };
/* 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 */,