From 5093525bb0fed2a6bbdba651889e12ef2dbd7d42 Mon Sep 17 00:00:00 2001
From: Junior Martinez <67972863+jmartinez-silabs@users.noreply.github.com>
Date: Mon, 17 Feb 2025 18:58:12 -0500
Subject: [PATCH 1/2] =?UTF-8?q?Rename=20CLUSTER=5FMASK=5F=20and=20ATTIBUTE?=
 =?UTF-8?q?=5FMASK=20defines=20to=20differentiate=20with=20=E2=80=A6=20(#3?=
 =?UTF-8?q?7618)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Rename CLUSTER_MASK_ and ATTIBUTE_MASK defines to differentiate with the zigbee ones. Cleanup some scene defines and delete the now empty head att-storage.h

* Address comment, replace _BITMAP_ by _FLAG_
---
 .../AirQualitySensorAppAttrUpdateDelegate.cpp |    1 -
 .../linux/AllClustersCommandDelegate.cpp      |    1 -
 .../all-clusters-app/linux/main-common.cpp    |    1 -
 .../linux/main-common.cpp                     |    1 -
 examples/bridge-app/esp32/README.md           |    8 +-
 examples/bridge-app/linux/README.md           |    8 +-
 examples/bridge-app/telink/README.md          |    8 +-
 .../clusters/switch/SwitchEventHandler.cpp    |    1 -
 .../common/clusters/switch/SwitchManager.cpp  |    1 -
 examples/chef/esp32/main/main.cpp             |    1 -
 examples/fabric-bridge-app/linux/README.md    |    8 +-
 examples/fabric-sync/README.md                |  185 +++
 .../linux/LightingAppCommandDelegate.cpp      |    1 -
 .../infineon/cyw30739/src/LightingManager.cpp |    1 -
 .../linux/LightingAppCommandDelegate.cpp      |    1 -
 .../group-key-mgmt-server.cpp                 |    1 -
 .../clusters/groups-server/groups-server.cpp  |    3 +-
 .../mode-select-server/mode-select-server.cpp |    1 -
 .../clusters/on-off-server/on-off-server.cpp  |    9 +-
 .../clusters/scenes-server/scenes-server.h    |    2 +
 .../EmberMetadata.cpp                         |    2 +-
 .../tests/TestCodegenModelViaMocks.cpp        |    9 +-
 src/app/dynamic_server/DynamicDispatcher.cpp  |    7 +-
 src/app/tests/TestSceneTable.cpp              |   36 +-
 src/app/util/BUILD.gn                         |    1 -
 src/app/util/af-types.h                       |   49 +-
 src/app/util/att-storage.h                    |   33 -
 src/app/util/attribute-metadata.h             |   34 +-
 src/app/util/attribute-storage-detail.h       |    1 -
 src/app/util/attribute-storage.cpp            |   38 +-
 src/app/util/attribute-storage.h              |   13 +-
 src/app/util/attribute-table.cpp              |    2 +-
 src/app/util/endpoint-config-defines.h        |    4 +-
 src/app/util/mock/MockNodeConfig.cpp          |    4 +-
 src/app/util/mock/MockNodeConfig.h            |    4 +-
 src/app/util/mock/attribute-storage.cpp       |    3 +-
 src/controller/tests/TestWriteChunking.cpp    |    4 +-
 .../CHIP/ServerEndpoint/MTRIMDispatch.mm      |    1 -
 .../CHIP/ServerEndpoint/MTRServerCluster.mm   |    2 +-
 .../CHIP/ServerEndpoint/MTRServerEndpoint.mm  |    7 +-
 .../tests/TestEmberAttributeDataBuffer.cpp    | 1179 +++++++++++++++++
 41 files changed, 1491 insertions(+), 185 deletions(-)
 create mode 100644 examples/fabric-sync/README.md
 delete mode 100644 src/app/util/att-storage.h
 create mode 100644 src/data-model-providers/codegen/tests/TestEmberAttributeDataBuffer.cpp

diff --git a/examples/air-quality-sensor-app/linux/AirQualitySensorAppAttrUpdateDelegate.cpp b/examples/air-quality-sensor-app/linux/AirQualitySensorAppAttrUpdateDelegate.cpp
index 7aaa873a90..b17db0107d 100644
--- a/examples/air-quality-sensor-app/linux/AirQualitySensorAppAttrUpdateDelegate.cpp
+++ b/examples/air-quality-sensor-app/linux/AirQualitySensorAppAttrUpdateDelegate.cpp
@@ -23,7 +23,6 @@
 #include <app/clusters/software-diagnostics-server/software-diagnostics-server.h>
 #include <app/clusters/switch-server/switch-server.h>
 #include <app/server/Server.h>
-#include <app/util/att-storage.h>
 #include <platform/PlatformManager.h>
 
 #include <air-quality-sensor-manager.h>
diff --git a/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp b/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp
index 0d66550940..e0278f30cc 100644
--- a/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp
+++ b/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp
@@ -26,7 +26,6 @@
 #include <app/clusters/software-diagnostics-server/software-diagnostics-server.h>
 #include <app/clusters/switch-server/switch-server.h>
 #include <app/server/Server.h>
-#include <app/util/att-storage.h>
 #include <app/util/attribute-storage.h>
 #include <platform/PlatformManager.h>
 
diff --git a/examples/all-clusters-app/linux/main-common.cpp b/examples/all-clusters-app/linux/main-common.cpp
index b62d154f0d..6d8728da53 100644
--- a/examples/all-clusters-app/linux/main-common.cpp
+++ b/examples/all-clusters-app/linux/main-common.cpp
@@ -53,7 +53,6 @@
 #include <app/clusters/time-synchronization-server/time-synchronization-server.h>
 #include <app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.h>
 #include <app/server/Server.h>
-#include <app/util/att-storage.h>
 #include <app/util/attribute-storage.h>
 #include <lib/support/CHIPMem.h>
 #include <platform/DeviceInstanceInfoProvider.h>
diff --git a/examples/all-clusters-minimal-app/linux/main-common.cpp b/examples/all-clusters-minimal-app/linux/main-common.cpp
index d99927cea0..306a4b51a2 100644
--- a/examples/all-clusters-minimal-app/linux/main-common.cpp
+++ b/examples/all-clusters-minimal-app/linux/main-common.cpp
@@ -21,7 +21,6 @@
 #include <app/CommandHandler.h>
 #include <app/clusters/identify-server/identify-server.h>
 #include <app/server/Server.h>
-#include <app/util/att-storage.h>
 #include <lib/support/CHIPMem.h>
 #include <new>
 #include <platform/PlatformManager.h>
diff --git a/examples/bridge-app/esp32/README.md b/examples/bridge-app/esp32/README.md
index ac22c7f8b3..2a0bde25f5 100644
--- a/examples/bridge-app/esp32/README.md
+++ b/examples/bridge-app/esp32/README.md
@@ -54,10 +54,10 @@ defined:
     definition.
 
 -   All attributes defined with these macros will be configured as
-    `ATTRIBUTE_MASK_EXTERNAL_STORAGE` in the ZCL database and therefore will
-    rely on the application to maintain storage for the attribute. Consequently,
-    reads or writes to these attributes must be handled within the application
-    by the `emberAfExternalAttributeWriteCallback` and
+    `MATTER_ATTRIBUTE_FLAG_EXTERNAL_STORAGE` in the ZCL database and therefore
+    will rely on the application to maintain storage for the attribute.
+    Consequently, reads or writes to these attributes must be handled within the
+    application by the `emberAfExternalAttributeWriteCallback` and
     `emberAfExternalAttributeReadCallback` functions. See the bridge
     application's `main.cpp` for an example of this implementation.
 
diff --git a/examples/bridge-app/linux/README.md b/examples/bridge-app/linux/README.md
index a416923669..6502655685 100644
--- a/examples/bridge-app/linux/README.md
+++ b/examples/bridge-app/linux/README.md
@@ -45,10 +45,10 @@ defined:
     definition.
 
 -   All attributes defined with these macros will be configured as
-    `ATTRIBUTE_MASK_EXTERNAL_STORAGE` in the ZCL database and therefore will
-    rely on the application to maintain storage for the attribute. Consequently,
-    reads or writes to these attributes must be handled within the application
-    by the `emberAfExternalAttributeWriteCallback` and
+    `MATTER_ATTRIBUTE_FLAG_EXTERNAL_STORAGE` in the ZCL database and therefore
+    will rely on the application to maintain storage for the attribute.
+    Consequently, reads or writes to these attributes must be handled within the
+    application by the `emberAfExternalAttributeWriteCallback` and
     `emberAfExternalAttributeReadCallback` functions. See the bridge
     application's `main.cpp` for an example of this implementation.
 
diff --git a/examples/bridge-app/telink/README.md b/examples/bridge-app/telink/README.md
index 00b050e93c..2dccc834cb 100644
--- a/examples/bridge-app/telink/README.md
+++ b/examples/bridge-app/telink/README.md
@@ -56,10 +56,10 @@ defined:
     definition.
 
 -   All attributes defined with these macros will be configured as
-    `ATTRIBUTE_MASK_EXTERNAL_STORAGE` in the ZCL database and therefore will
-    rely on the application to maintain storage for the attribute. Consequently,
-    reads or writes to these attributes must be handled within the application
-    by the `emberAfExternalAttributeWriteCallback` and
+    `MATTER_ATTRIBUTE_FLAG_EXTERNAL_STORAGE` in the ZCL database and therefore
+    will rely on the application to maintain storage for the attribute.
+    Consequently, reads or writes to these attributes must be handled within the
+    application by the `emberAfExternalAttributeWriteCallback` and
     `emberAfExternalAttributeReadCallback` functions. See the bridge
     application's `main.cpp` for an example of this implementation.
 
diff --git a/examples/chef/common/clusters/switch/SwitchEventHandler.cpp b/examples/chef/common/clusters/switch/SwitchEventHandler.cpp
index dd32e2b907..55fae2ae70 100644
--- a/examples/chef/common/clusters/switch/SwitchEventHandler.cpp
+++ b/examples/chef/common/clusters/switch/SwitchEventHandler.cpp
@@ -20,7 +20,6 @@
 #ifdef MATTER_DM_PLUGIN_SWITCH_SERVER
 #include <app/clusters/switch-server/switch-server.h>
 #include <app/server/Server.h>
-#include <app/util/att-storage.h>
 #include <platform/PlatformManager.h>
 
 #include "SwitchEventHandler.h"
diff --git a/examples/chef/common/clusters/switch/SwitchManager.cpp b/examples/chef/common/clusters/switch/SwitchManager.cpp
index 45d39dd4bc..c308cb1111 100644
--- a/examples/chef/common/clusters/switch/SwitchManager.cpp
+++ b/examples/chef/common/clusters/switch/SwitchManager.cpp
@@ -21,7 +21,6 @@
 #include <app-common/zap-generated/attributes/Accessors.h>
 #include <app/clusters/switch-server/switch-server.h>
 #include <app/server/Server.h>
-#include <app/util/att-storage.h>
 #include <app/util/attribute-storage.h>
 #include <platform/PlatformManager.h>
 
diff --git a/examples/chef/esp32/main/main.cpp b/examples/chef/esp32/main/main.cpp
index b61fcf22bc..460ac0c657 100644
--- a/examples/chef/esp32/main/main.cpp
+++ b/examples/chef/esp32/main/main.cpp
@@ -41,7 +41,6 @@
 #include <app-common/zap-generated/callback.h>
 #include <app-common/zap-generated/cluster-objects.h>
 #include <app/server/Dnssd.h>
-#include <app/util/att-storage.h>
 #include <setup_payload/QRCodeSetupPayloadGenerator.h>
 
 #include "Display.h"
diff --git a/examples/fabric-bridge-app/linux/README.md b/examples/fabric-bridge-app/linux/README.md
index 27a01d9ffe..12a0ac2093 100644
--- a/examples/fabric-bridge-app/linux/README.md
+++ b/examples/fabric-bridge-app/linux/README.md
@@ -51,10 +51,10 @@ defined:
     definition.
 
 -   All attributes defined with these macros will be configured as
-    `ATTRIBUTE_MASK_EXTERNAL_STORAGE` in the ZCL database and therefore will
-    rely on the application to maintain storage for the attribute. Consequently,
-    reads or writes to these attributes must be handled within the application
-    by the `emberAfExternalAttributeWriteCallback` and
+    `MATTER_ATTRIBUTE_FLAG_EXTERNAL_STORAGE` in the ZCL database and therefore
+    will rely on the application to maintain storage for the attribute.
+    Consequently, reads or writes to these attributes must be handled within the
+    application by the `emberAfExternalAttributeWriteCallback` and
     `emberAfExternalAttributeReadCallback` functions. See the bridge
     application's `main.cpp` for an example of this implementation.
 
diff --git a/examples/fabric-sync/README.md b/examples/fabric-sync/README.md
new file mode 100644
index 0000000000..ab08bd42af
--- /dev/null
+++ b/examples/fabric-sync/README.md
@@ -0,0 +1,185 @@
+# Matter Linux Fabric Sync Example
+
+An example application to implement the Fabric Synchronization feature and
+demonstrates the end-to-end Fabric Synchronization feature using dynamic
+endpoints.
+
+Fabric Synchronization feature will facilitate the commissioning of end devices
+from one fabric to another without requiring user intervention for every end
+device. It defines mechanisms that can be used by multiple
+ecosystems/controllers to communicate with one another to simplify the
+experience for users.
+
+This doc is tested on **Ubuntu 22.04 LTS (aarch64)**
+
+<hr>
+
+-   [Matter Linux Fabric Sync Example](#matter-linux-fabric-sync-example)
+    -   [Theory of Operation](#theory-of-operation)
+    -   [Building](#building)
+    -   [Running the Complete Example on Ubuntu](#running-the-complete-example-on-ubuntu)
+
+<hr>
+
+## Theory of Operation
+
+### Dynamic Endpoints
+
+The Fabric-Sync Example makes use of Dynamic Endpoints. Current SDK support is
+limited for dynamic endpoints, since endpoints are typically defined (along with
+the clusters and attributes they contain) in a .zap file which then generates
+code and static structures to define the endpoints.
+
+To support endpoints that are not statically defined, the ZCL attribute storage
+mechanisms will hold additional endpoint information for `NUM_DYNAMIC_ENDPOINTS`
+additional endpoints. These additional endpoint structures must be defined by
+the application and can change at runtime.
+
+To facilitate the creation of these endpoint structures, several macros are
+defined:
+
+`DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(attrListName)`
+`DECLARE_DYNAMIC_ATTRIBUTE(attId, attType, attSizeBytes, attrMask)`
+`DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(clusterRevision)`
+
+-   These three macros are used to declare a list of attributes for use within a
+    cluster. The declaration must begin with the
+    `DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN` macro which will define the name of
+    the allocated attribute structure. Each attribute is then added by the
+    `DECLARE_DYNAMIC_ATTRIBUTE` macro. Finally,
+    `DECLARE_DYNAMIC_ATTRIBUTE_LIST_END` macro should be used to close the
+    definition.
+
+-   All attributes defined with these macros will be configured as
+    `MATTER_ATTRIBUTE_FLAG_EXTERNAL_STORAGE` in the ZCL database and therefore
+    will rely on the application to maintain storage for the attribute.
+    Consequently, reads or writes to these attributes must be handled within the
+    application by the `emberAfExternalAttributeWriteCallback` and
+    `emberAfExternalAttributeReadCallback` functions. See the bridge
+    application's `main.cpp` for an example of this implementation.
+
+`DECLARE_DYNAMIC_CLUSTER_LIST_BEGIN(clusterListName)`
+`DECLARE_DYNAMIC_CLUSTER(clusterId, clusterAttrs, role, incomingCommands, outgoingCommands)`
+`DECLARE_DYNAMIC_CLUSTER_LIST_END`
+
+-   These three macros are used to declare a list of clusters for use within a
+    endpoint. The declaration must begin with the
+    `DECLARE_DYNAMIC_CLUSTER_LIST_BEGIN` macro which will define the name of the
+    allocated cluster structure. Each cluster is then added by the
+    `DECLARE_DYNAMIC_CLUSTER` macro referencing attribute list previously
+    defined by the `DECLARE_DYNAMIC_ATTRIBUTE...` macros and the lists of
+    incoming/outgoing commands terminated by kInvalidCommandId (or nullptr if
+    there aren't any commands in the list). Finally,
+    `DECLARE_DYNAMIC_CLUSTER_LIST_END` macro should be used to close the
+    definition.
+
+`DECLARE_DYNAMIC_ENDPOINT(endpointName, clusterList)`
+
+-   This macro is used to declare an endpoint and its associated cluster list,
+    which must be previously defined by the `DECLARE_DYNAMIC_CLUSTER...` macros.
+
+## Building
+
+-   For Linux host example:
+
+    ```sh
+    source scripts/activate.sh
+    ./scripts/build/build_examples.py --target linux-x64-fabric-sync-no-ble build
+    ```
+
+-   For Raspberry Pi 4 example:
+
+    Pull Docker Images
+
+    ```sh
+    docker pull ghcr.io/project-chip/chip-build-crosscompile:112
+    ```
+
+    Run docker
+
+    ```sh
+    docker run -it -v ~/connectedhomeip:/var/connectedhomeip ghcr.io/project-chip/chip-build-crosscompile:112 /bin/bash
+    ```
+
+    Build
+
+    ```sh
+    cd /var/connectedhomeip
+
+    git config --global --add safe.directory /var/connectedhomeip
+
+    ./scripts/run_in_build_env.sh \
+     "./scripts/build/build_examples.py \
+        --target linux-arm64-fabric-sync-no-ble-clang \
+        build"
+    ```
+
+    Transfer the fabric-bridge-app binary to a Raspberry Pi
+
+    ```sh
+    scp ./fabric-sync ubuntu@xxx.xxx.xxx.xxx:/home/ubuntu
+    ```
+
+## Running the Complete Example on Ubuntu
+
+-   Building
+
+    Follow [Building](#building) section of this document.
+
+-   Run Linux Fabric Sync Example App on two Linux machine E1 and E2
+
+    ```sh
+    sudo rm -rf /tmp/chip_*
+    cd ~/connectedhomeip/
+    out/debug/fabric-sync
+    ```
+
+-   Initiate the FS Setup Process from E1 to E2
+
+    ```sh
+    > app add-bridge 1 20202021 192.168.86.246 5540
+    Done
+    > New device with Node ID: 0000000000000001 has been successfully added.
+    A new device has been added on Endpoint: 2.
+    ```
+
+-   Verify Reverse Commissioning of the Fabric-Bridge from E1 on E2
+
+    ```sh
+    > New device with Node ID: 0000000000000002 has been successfully added.
+    ```
+
+-   Pair Light Example to E2
+
+    Since Fabric-Bridge also functions as a Matter server, running it alongside
+    the Light Example app on the same machine would cause conflicts. Therefore,
+    you need to run the Matter Light Example app on a separate physical machine
+    from the one hosting Fabric-Sync.
+
+    ```sh
+    > app add-device 3 <setup-pin-code> <device-remote-ip> <device-remote-port>
+    ```
+
+    After the device is successfully added, you will observe the following
+    message on E2 with the newly assigned Node ID:
+
+    ```sh
+    > New device with Node ID: 0x3 has been successfully added.
+    ```
+
+    Additionally, you should also get notified when a new device is added to E2
+    from the E1:
+
+    ```sh
+    > A new device is added on Endpoint 3.
+    ```
+
+-   Synchronize Light Example to E1
+
+    After the Light Example is successfully paired in E2, we can start to
+    synchronize the light device to E1 using the new assigned dynamic endpointid
+    on Ecosystem 2.
+
+    ```sh
+    > app sync-device <endpointid>
+    ```
diff --git a/examples/lighting-app-data-mode-no-unique-id/linux/LightingAppCommandDelegate.cpp b/examples/lighting-app-data-mode-no-unique-id/linux/LightingAppCommandDelegate.cpp
index cc5af976eb..c6d1cdead6 100644
--- a/examples/lighting-app-data-mode-no-unique-id/linux/LightingAppCommandDelegate.cpp
+++ b/examples/lighting-app-data-mode-no-unique-id/linux/LightingAppCommandDelegate.cpp
@@ -23,7 +23,6 @@
 #include <app/clusters/software-diagnostics-server/software-diagnostics-server.h>
 #include <app/clusters/switch-server/switch-server.h>
 #include <app/server/Server.h>
-#include <app/util/att-storage.h>
 #include <app/util/attribute-storage.h>
 #include <platform/PlatformManager.h>
 
diff --git a/examples/lighting-app/infineon/cyw30739/src/LightingManager.cpp b/examples/lighting-app/infineon/cyw30739/src/LightingManager.cpp
index eb707676b5..7fd4003f36 100644
--- a/examples/lighting-app/infineon/cyw30739/src/LightingManager.cpp
+++ b/examples/lighting-app/infineon/cyw30739/src/LightingManager.cpp
@@ -19,7 +19,6 @@
 
 #include "LightingManager.h"
 #include <app-common/zap-generated/attributes/Accessors.h>
-#include <app/util/att-storage.h>
 #include <stdio.h>
 
 using namespace chip;
diff --git a/examples/lighting-app/linux/LightingAppCommandDelegate.cpp b/examples/lighting-app/linux/LightingAppCommandDelegate.cpp
index cc5af976eb..c6d1cdead6 100644
--- a/examples/lighting-app/linux/LightingAppCommandDelegate.cpp
+++ b/examples/lighting-app/linux/LightingAppCommandDelegate.cpp
@@ -23,7 +23,6 @@
 #include <app/clusters/software-diagnostics-server/software-diagnostics-server.h>
 #include <app/clusters/switch-server/switch-server.h>
 #include <app/server/Server.h>
-#include <app/util/att-storage.h>
 #include <app/util/attribute-storage.h>
 #include <platform/PlatformManager.h>
 
diff --git a/src/app/clusters/group-key-mgmt-server/group-key-mgmt-server.cpp b/src/app/clusters/group-key-mgmt-server/group-key-mgmt-server.cpp
index 39b9f59d23..6874fc9f56 100644
--- a/src/app/clusters/group-key-mgmt-server/group-key-mgmt-server.cpp
+++ b/src/app/clusters/group-key-mgmt-server/group-key-mgmt-server.cpp
@@ -22,7 +22,6 @@
 #include <app/CommandHandler.h>
 #include <app/MessageDef/StatusIB.h>
 #include <app/server/Server.h>
-#include <app/util/att-storage.h>
 #include <app/util/attribute-storage.h>
 #include <credentials/GroupDataProvider.h>
 #include <lib/support/CodeUtils.h>
diff --git a/src/app/clusters/groups-server/groups-server.cpp b/src/app/clusters/groups-server/groups-server.cpp
index add0261927..fe772caad1 100644
--- a/src/app/clusters/groups-server/groups-server.cpp
+++ b/src/app/clusters/groups-server/groups-server.cpp
@@ -22,7 +22,6 @@
 #include <app-common/zap-generated/ids/Clusters.h>
 #include <app/CommandHandler.h>
 #include <app/reporting/reporting.h>
-#include <app/util/att-storage.h>
 #include <app/util/config.h>
 #include <credentials/GroupDataProvider.h>
 #include <inttypes.h>
@@ -339,7 +338,7 @@ bool emberAfGroupsClusterRemoveAllGroupsCallback(app::CommandHandler * commandOb
         }
         iter->Release();
         ScenesManagement::ScenesServer::Instance().GroupWillBeRemoved(fabricIndex, commandPath.mEndpointId,
-                                                                      ZCL_SCENES_GLOBAL_SCENE_GROUP_ID);
+                                                                      ScenesManagement::ScenesServer::kGlobalSceneGroupId);
     }
 #endif
 
diff --git a/src/app/clusters/mode-select-server/mode-select-server.cpp b/src/app/clusters/mode-select-server/mode-select-server.cpp
index 813d16d965..db771004a5 100644
--- a/src/app/clusters/mode-select-server/mode-select-server.cpp
+++ b/src/app/clusters/mode-select-server/mode-select-server.cpp
@@ -24,7 +24,6 @@
 #include <app/CommandHandler.h>
 #include <app/ConcreteCommandPath.h>
 #include <app/clusters/mode-select-server/supported-modes-manager.h>
-#include <app/util/att-storage.h>
 #include <app/util/attribute-storage.h>
 #include <app/util/config.h>
 #include <app/util/odd-sized-integers.h>
diff --git a/src/app/clusters/on-off-server/on-off-server.cpp b/src/app/clusters/on-off-server/on-off-server.cpp
index d18aa09383..85465e085c 100644
--- a/src/app/clusters/on-off-server/on-off-server.cpp
+++ b/src/app/clusters/on-off-server/on-off-server.cpp
@@ -667,8 +667,9 @@ bool OnOffServer::offWithEffectCommand(app::CommandHandler * commandObj, const a
         if (globalSceneControl)
         {
 #ifdef MATTER_DM_PLUGIN_SCENES_MANAGEMENT
-            ScenesManagement::ScenesServer::Instance().StoreCurrentScene(fabric, endpoint, ZCL_SCENES_GLOBAL_SCENE_GROUP_ID,
-                                                                         ZCL_SCENES_GLOBAL_SCENE_SCENE_ID);
+            ScenesManagement::ScenesServer::Instance().StoreCurrentScene(fabric, endpoint,
+                                                                         ScenesManagement::ScenesServer::kGlobalSceneGroupId,
+                                                                         ScenesManagement::ScenesServer::kGlobalSceneId);
 #endif // MATTER_DM_PLUGIN_SCENES_MANAGEMENT
             OnOff::Attributes::GlobalSceneControl::Set(endpoint, false);
         }
@@ -723,8 +724,8 @@ bool OnOffServer::OnWithRecallGlobalSceneCommand(app::CommandHandler * commandOb
     }
 
 #ifdef MATTER_DM_PLUGIN_SCENES_MANAGEMENT
-    ScenesManagement::ScenesServer::Instance().RecallScene(fabric, endpoint, ZCL_SCENES_GLOBAL_SCENE_GROUP_ID,
-                                                           ZCL_SCENES_GLOBAL_SCENE_SCENE_ID);
+    ScenesManagement::ScenesServer::Instance().RecallScene(fabric, endpoint, ScenesManagement::ScenesServer::kGlobalSceneGroupId,
+                                                           ScenesManagement::ScenesServer::kGlobalSceneId);
 #endif // MATTER_DM_PLUGIN_SCENES_MANAGEMENT
 
     OnOff::Attributes::GlobalSceneControl::Set(endpoint, true);
diff --git a/src/app/clusters/scenes-server/scenes-server.h b/src/app/clusters/scenes-server/scenes-server.h
index 81c7d41f65..024b6f64f6 100644
--- a/src/app/clusters/scenes-server/scenes-server.h
+++ b/src/app/clusters/scenes-server/scenes-server.h
@@ -40,6 +40,8 @@ class ScenesServer : public CommandHandlerInterface, public AttributeAccessInter
         MATTER_DM_SCENES_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT;
     static_assert(kScenesServerMaxEndpointCount <= kEmberInvalidEndpointIndex, "Scenes endpoint count error");
     static constexpr uint8_t kScenesServerMaxFabricCount = CHIP_CONFIG_MAX_FABRICS;
+    static constexpr SceneId kGlobalSceneId              = 0x00;
+    static constexpr GroupId kGlobalSceneGroupId         = 0x0000;
 
     // FabricSceneInfo
     class FabricSceneInfo
diff --git a/src/app/codegen-data-model-provider/EmberMetadata.cpp b/src/app/codegen-data-model-provider/EmberMetadata.cpp
index 69f9e3e889..a01b70532f 100644
--- a/src/app/codegen-data-model-provider/EmberMetadata.cpp
+++ b/src/app/codegen-data-model-provider/EmberMetadata.cpp
@@ -62,7 +62,7 @@ FindAttributeMetadata(const ConcreteAttributePath & aPath)
             return Status::UnsupportedEndpoint;
         }
 
-        const EmberAfCluster * cluster = emberAfFindClusterInType(type, aPath.mClusterId, CLUSTER_MASK_SERVER);
+        const EmberAfCluster * cluster = emberAfFindClusterInType(type, aPath.mClusterId, MATTER_CLUSTER_FLAG_SERVER);
         if (cluster == nullptr)
         {
             return Status::UnsupportedCluster;
diff --git a/src/app/codegen-data-model-provider/tests/TestCodegenModelViaMocks.cpp b/src/app/codegen-data-model-provider/tests/TestCodegenModelViaMocks.cpp
index 3840de9c81..ff21ae0d28 100644
--- a/src/app/codegen-data-model-provider/tests/TestCodegenModelViaMocks.cpp
+++ b/src/app/codegen-data-model-provider/tests/TestCodegenModelViaMocks.cpp
@@ -263,11 +263,12 @@ class ScopedMockAccessControl
 
 #define MOCK_ATTRIBUTE_ID_FOR_NULLABLE_TYPE(zcl_type) MockAttributeId(zcl_type + 0x1000)
 #define MOCK_ATTRIBUTE_CONFIG_NULLABLE(zcl_type)                                                                                   \
-    MockAttributeConfig(MOCK_ATTRIBUTE_ID_FOR_NULLABLE_TYPE(zcl_type), zcl_type, ATTRIBUTE_MASK_WRITABLE | ATTRIBUTE_MASK_NULLABLE)
+    MockAttributeConfig(MOCK_ATTRIBUTE_ID_FOR_NULLABLE_TYPE(zcl_type), zcl_type,                                                   \
+                        MATTER_ATTRIBUTE_FLAG_WRITABLE | MATTER_ATTRIBUTE_FLAG_NULLABLE)
 
 #define MOCK_ATTRIBUTE_ID_FOR_NON_NULLABLE_TYPE(zcl_type) MockAttributeId(zcl_type + 0x2000)
 #define MOCK_ATTRIBUTE_CONFIG_NON_NULLABLE(zcl_type)                                                                               \
-    MockAttributeConfig(MOCK_ATTRIBUTE_ID_FOR_NON_NULLABLE_TYPE(zcl_type), zcl_type, ATTRIBUTE_MASK_WRITABLE)
+    MockAttributeConfig(MOCK_ATTRIBUTE_ID_FOR_NON_NULLABLE_TYPE(zcl_type), zcl_type, MATTER_ATTRIBUTE_FLAG_WRITABLE)
 
 // clang-format off
 const MockNodeConfig gTestNodeConfig({
@@ -325,7 +326,7 @@ const MockNodeConfig gTestNodeConfig({
             MockAttributeConfig(
               kReadOnlyAttributeId,
               ZCL_INT32U_ATTRIBUTE_TYPE,
-              ATTRIBUTE_MASK_NULLABLE    // NOTE: explicltly NOT ATTRIBUTE_MASK_WRITABLE
+              MATTER_ATTRIBUTE_FLAG_NULLABLE    // NOTE: explicltly NOT MATTER_ATTRIBUTE_FLAG_WRITABLE
             )
         }),
         MockClusterConfig(MockClusterId(4), {
@@ -481,7 +482,7 @@ const MockNodeConfig gTestNodeConfig({
 
             // Special case handling
             MockAttributeConfig(kAttributeIdReadOnly, ZCL_INT32S_ATTRIBUTE_TYPE, 0),
-            MockAttributeConfig(kAttributeIdTimedWrite, ZCL_INT32S_ATTRIBUTE_TYPE, ATTRIBUTE_MASK_WRITABLE | ATTRIBUTE_MASK_MUST_USE_TIMED_WRITE),
+            MockAttributeConfig(kAttributeIdTimedWrite, ZCL_INT32S_ATTRIBUTE_TYPE, MATTER_ATTRIBUTE_FLAG_WRITABLE | MATTER_ATTRIBUTE_FLAG_MUST_USE_TIMED_WRITE),
         }),
     }),
 });
diff --git a/src/app/dynamic_server/DynamicDispatcher.cpp b/src/app/dynamic_server/DynamicDispatcher.cpp
index e1f458f0bc..cc6ea6a1a2 100644
--- a/src/app/dynamic_server/DynamicDispatcher.cpp
+++ b/src/app/dynamic_server/DynamicDispatcher.cpp
@@ -31,7 +31,8 @@
 #include <app/MessageDef/StatusIB.h>
 #include <app/WriteHandler.h>
 #include <app/data-model/Decode.h>
-#include <app/util/att-storage.h>
+#include <app/util/attribute-storage.h>
+#include <app/util/attribute-table.h>
 #include <app/util/endpoint-config-api.h>
 #include <lib/core/CHIPError.h>
 #include <lib/core/DataModelTypes.h>
@@ -328,7 +329,7 @@ Optional<AttributeId> emberAfGetServerAttributeIdByIndex(EndpointId endpoint, Cl
 
 uint8_t emberAfClusterIndex(EndpointId endpoint, ClusterId clusterId, EmberAfClusterMask mask)
 {
-    if (endpoint == kSupportedEndpoint && clusterId == OtaSoftwareUpdateProvider::Id && (mask & CLUSTER_MASK_SERVER))
+    if (endpoint == kSupportedEndpoint && clusterId == OtaSoftwareUpdateProvider::Id && (mask & MATTER_CLUSTER_FLAG_SERVER))
     {
         return 0;
     }
@@ -352,7 +353,7 @@ const EmberAfCluster otaProviderCluster{
     .attributes           = nullptr,
     .attributeCount       = 0,
     .clusterSize          = 0,
-    .mask                 = CLUSTER_MASK_SERVER,
+    .mask                 = MATTER_CLUSTER_FLAG_SERVER,
     .functions            = nullptr,
     .acceptedCommandList  = acceptedCommands,
     .generatedCommandList = generatedCommands,
diff --git a/src/app/tests/TestSceneTable.cpp b/src/app/tests/TestSceneTable.cpp
index 2e541cb0e7..6f6093cd09 100644
--- a/src/app/tests/TestSceneTable.cpp
+++ b/src/app/tests/TestSceneTable.cpp
@@ -176,7 +176,7 @@ static EmberAfAttributeMetadata mockMetadataBool = {
     .attributeId   = 0,
     .size          = 1,
     .attributeType = ZCL_BOOLEAN_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE,
 };
 
 static EmberAfAttributeMetadata mockMetadataUint8 = {
@@ -184,7 +184,7 @@ static EmberAfAttributeMetadata mockMetadataUint8 = {
     .attributeId   = 0,
     .size          = 1,
     .attributeType = ZCL_INT8U_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE | ATTRIBUTE_MASK_NULLABLE,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE | MATTER_ATTRIBUTE_FLAG_NULLABLE,
 };
 
 static EmberAfAttributeMetadata mockMetadataUint8Max = {
@@ -192,7 +192,7 @@ static EmberAfAttributeMetadata mockMetadataUint8Max = {
     .attributeId   = 0,
     .size          = 1,
     .attributeType = ZCL_INT8U_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE | ATTRIBUTE_MASK_NULLABLE | ATTRIBUTE_MASK_MIN_MAX,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE | MATTER_ATTRIBUTE_FLAG_NULLABLE | MATTER_ATTRIBUTE_FLAG_MIN_MAX,
 };
 
 static EmberAfAttributeMetadata mockMetadataUint16 = {
@@ -200,7 +200,7 @@ static EmberAfAttributeMetadata mockMetadataUint16 = {
     .attributeId   = 0,
     .size          = 2,
     .attributeType = ZCL_INT16U_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE,
 };
 
 static EmberAfAttributeMetadata mockMetadataUint24 = {
@@ -208,7 +208,7 @@ static EmberAfAttributeMetadata mockMetadataUint24 = {
     .attributeId   = 0,
     .size          = 3,
     .attributeType = ZCL_INT24U_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE,
 };
 
 static EmberAfAttributeMetadata mockMetadataUint32 = {
@@ -216,7 +216,7 @@ static EmberAfAttributeMetadata mockMetadataUint32 = {
     .attributeId   = 0,
     .size          = 4,
     .attributeType = ZCL_INT32U_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE,
 };
 
 static EmberAfAttributeMetadata mockMetadataUint40 = {
@@ -224,7 +224,7 @@ static EmberAfAttributeMetadata mockMetadataUint40 = {
     .attributeId   = 0,
     .size          = 5,
     .attributeType = ZCL_INT40U_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE,
 };
 
 static EmberAfAttributeMetadata mockMetadataUint48 = {
@@ -232,7 +232,7 @@ static EmberAfAttributeMetadata mockMetadataUint48 = {
     .attributeId   = 0,
     .size          = 6,
     .attributeType = ZCL_INT48U_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE,
 };
 
 static EmberAfAttributeMetadata mockMetadataUint56 = {
@@ -240,7 +240,7 @@ static EmberAfAttributeMetadata mockMetadataUint56 = {
     .attributeId   = 0,
     .size          = 7,
     .attributeType = ZCL_INT56U_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE,
 };
 
 static EmberAfAttributeMetadata mockMetadataUint64 = {
@@ -248,7 +248,7 @@ static EmberAfAttributeMetadata mockMetadataUint64 = {
     .attributeId   = 0,
     .size          = 8,
     .attributeType = ZCL_INT64U_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE,
 };
 
 static EmberAfAttributeMetadata mockMetadataInt8 = {
@@ -256,7 +256,7 @@ static EmberAfAttributeMetadata mockMetadataInt8 = {
     .attributeId   = 0,
     .size          = 1,
     .attributeType = ZCL_INT8S_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE | ATTRIBUTE_MASK_MIN_MAX,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE | MATTER_ATTRIBUTE_FLAG_MIN_MAX,
 };
 
 static EmberAfAttributeMetadata mockMetadataInt16 = {
@@ -264,7 +264,7 @@ static EmberAfAttributeMetadata mockMetadataInt16 = {
     .attributeId   = 0,
     .size          = 2,
     .attributeType = ZCL_INT16S_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE | ATTRIBUTE_MASK_MIN_MAX,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE | MATTER_ATTRIBUTE_FLAG_MIN_MAX,
 };
 
 static EmberAfAttributeMetadata mockMetadataInt24 = {
@@ -272,7 +272,7 @@ static EmberAfAttributeMetadata mockMetadataInt24 = {
     .attributeId   = 0,
     .size          = 3,
     .attributeType = ZCL_INT24S_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE,
 };
 
 static EmberAfAttributeMetadata mockMetadataInt32 = {
@@ -280,7 +280,7 @@ static EmberAfAttributeMetadata mockMetadataInt32 = {
     .attributeId   = 0,
     .size          = 4,
     .attributeType = ZCL_INT32S_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE,
 };
 
 static EmberAfAttributeMetadata mockMetadataInt40 = {
@@ -288,7 +288,7 @@ static EmberAfAttributeMetadata mockMetadataInt40 = {
     .attributeId   = 0,
     .size          = 5,
     .attributeType = ZCL_INT40S_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE,
 };
 
 static EmberAfAttributeMetadata mockMetadataInt48 = {
@@ -296,7 +296,7 @@ static EmberAfAttributeMetadata mockMetadataInt48 = {
     .attributeId   = 0,
     .size          = 6,
     .attributeType = ZCL_INT48S_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE,
 };
 
 static EmberAfAttributeMetadata mockMetadataInt56 = {
@@ -304,7 +304,7 @@ static EmberAfAttributeMetadata mockMetadataInt56 = {
     .attributeId   = 0,
     .size          = 7,
     .attributeType = ZCL_INT56S_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE,
 };
 
 static EmberAfAttributeMetadata mockMetadataInt64 = {
@@ -312,7 +312,7 @@ static EmberAfAttributeMetadata mockMetadataInt64 = {
     .attributeId   = 0,
     .size          = 8,
     .attributeType = ZCL_INT64S_ATTRIBUTE_TYPE,
-    .mask          = ATTRIBUTE_MASK_WRITABLE,
+    .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE,
 };
 
 // clang-format off
diff --git a/src/app/util/BUILD.gn b/src/app/util/BUILD.gn
index 29b3bc9563..763480f159 100644
--- a/src/app/util/BUILD.gn
+++ b/src/app/util/BUILD.gn
@@ -32,7 +32,6 @@ source_set("nullable-primitives") {
 # These headers/cpp only depend on core/common
 source_set("types") {
   sources = [
-    "att-storage.h",
     "attribute-metadata.cpp",
     "attribute-metadata.h",
     "basic-types.h",
diff --git a/src/app/util/af-types.h b/src/app/util/af-types.h
index 04275a2515..dbdcaa0a46 100644
--- a/src/app/util/af-types.h
+++ b/src/app/util/af-types.h
@@ -23,7 +23,6 @@
  * @{
  */
 
-#include "att-storage.h"
 #include <stdbool.h> // For bool
 #include <stdint.h>  // For various uint*_t types
 
@@ -43,11 +42,6 @@
 #include <app-common/zap-generated/cluster-objects.h>
 #include <protocols/interaction_model/StatusCode.h>
 
-/**
- * @brief Type for the cluster mask
- */
-typedef uint8_t EmberAfClusterMask;
-
 /**
  * @brief Generic function type, used for either of the cluster function.
  *
@@ -62,6 +56,21 @@ typedef void (*EmberAfGenericClusterFunction)(void);
  */
 #define MATTER_DM_NULL_MANUFACTURER_CODE 0x0000
 
+// The following define names are relevant to the ZAP_CLUSTER_MASK macro.
+#define MATTER_CLUSTER_FLAG_INIT_FUNCTION 0x01
+#define MATTER_CLUSTER_FLAG_ATTRIBUTE_CHANGED_FUNCTION 0x02
+// Bit 2 (0x04)  and Bit3 (0x08) are free.
+#define MATTER_CLUSTER_FLAG_SHUTDOWN_FUNCTION 0x10
+#define MATTER_CLUSTER_FLAG_PRE_ATTRIBUTE_CHANGED_FUNCTION 0x20
+#define MATTER_CLUSTER_FLAG_SERVER 0x40
+#define MATTER_CLUSTER_FLAG_CLIENT 0x80
+
+/**
+ * @brief Type for the cluster mask
+ *  Value of the mask represents a single, or aggregated, MATTER_CLUSTER_FLAG_X
+ */
+typedef uint8_t EmberAfClusterMask;
+
 /**
  * @brief Struct describing cluster
  */
@@ -119,7 +128,9 @@ struct EmberAfCluster
      */
     uint16_t eventCount;
 
-    bool IsServer() const { return (mask & CLUSTER_MASK_SERVER) != 0; }
+    bool IsServer() const { return (mask & MATTER_CLUSTER_FLAG_SERVER) != 0; }
+
+    bool IsClient() const { return (mask & MATTER_CLUSTER_FLAG_CLIENT) != 0; }
 };
 
 /**
@@ -232,30 +243,6 @@ struct EmberAfDefinedEndpoint
     chip::Span<const chip::app::Clusters::Descriptor::Structs::SemanticTagStruct::Type> tagList;
 };
 
-// Cluster specific types
-
-/**
- * @brief Indicates the absence of a Scene table entry.
- */
-#define MATTER_DM_SCENE_TABLE_NULL_INDEX 0xFF
-/**
- * @brief Value used when setting or getting the endpoint in a Scene table
- * entry.  It indicates that the entry is not in use.
- */
-#define MATTER_DM_SCENE_TABLE_UNUSED_ENDPOINT_ID 0x00
-/**
- * @brief Maximum length of Scene names, not including the length byte.
- */
-#define ZCL_SCENES_CLUSTER_MAXIMUM_NAME_LENGTH 16
-/**
- * @brief The group identifier for the global scene.
- */
-#define ZCL_SCENES_GLOBAL_SCENE_GROUP_ID 0x0000
-/**
- * @brief The scene identifier for the global scene.
- */
-#define ZCL_SCENES_GLOBAL_SCENE_SCENE_ID 0x00
-
 /**
  * @brief Type for referring to the tick callback for cluster.
  *
diff --git a/src/app/util/att-storage.h b/src/app/util/att-storage.h
deleted file mode 100644
index 9befaacf7c..0000000000
--- a/src/app/util/att-storage.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- *
- *    Copyright (c) 2022 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
-
-// Cluster masks modify how clusters are used by the framework
-//
-// Does this cluster have init function?
-#define CLUSTER_MASK_INIT_FUNCTION (0x01)
-// Does this cluster have attribute changed function?
-#define CLUSTER_MASK_ATTRIBUTE_CHANGED_FUNCTION (0x02)
-// Bits 0x04 and 0x08 are free.
-// Does this cluster have shutdown function?
-#define CLUSTER_MASK_SHUTDOWN_FUNCTION (0x10)
-// Does this cluster have pre-attribute changed function?
-#define CLUSTER_MASK_PRE_ATTRIBUTE_CHANGED_FUNCTION (0x20)
-// Cluster is a server
-#define CLUSTER_MASK_SERVER (0x40)
-// Cluster is a client
-#define CLUSTER_MASK_CLIENT (0x80)
diff --git a/src/app/util/attribute-metadata.h b/src/app/util/attribute-metadata.h
index 6703e397d5..368a90418e 100644
--- a/src/app/util/attribute-metadata.h
+++ b/src/app/util/attribute-metadata.h
@@ -105,23 +105,23 @@ union EmberAfDefaultOrMinMaxAttributeValue
 };
 
 // Attribute masks modify how attributes are used by the framework
-//
+// The following define names are relevant to the ZAP_ATTRIBUTE_MASK macro.
 // Attribute that has this mask is NOT read-only
-#define ATTRIBUTE_MASK_WRITABLE (0x01)
+#define MATTER_ATTRIBUTE_FLAG_WRITABLE (0x01)
 // Attribute that has this mask is saved in non-volatile memory
-#define ATTRIBUTE_MASK_NONVOLATILE (0x02)
-// Alias until ZAP gets updated to output ATTRIBUTE_MASK_NONVOLATILE
-#define ATTRIBUTE_MASK_TOKENIZE ATTRIBUTE_MASK_NONVOLATILE
+#define MATTER_ATTRIBUTE_FLAG_NONVOLATILE (0x02)
+// Alias until ZAP gets updated to output MATTER_ATTRIBUTE_FLAG_NONVOLATILE
+#define MATTER_ATTRIBUTE_FLAG_TOKENIZE MATTER_ATTRIBUTE_FLAG_NONVOLATILE
 // Attribute that has this mask has a min/max values
-#define ATTRIBUTE_MASK_MIN_MAX (0x04)
+#define MATTER_ATTRIBUTE_FLAG_MIN_MAX (0x04)
 // Attribute requires a timed interaction to write
-#define ATTRIBUTE_MASK_MUST_USE_TIMED_WRITE (0x08)
+#define MATTER_ATTRIBUTE_FLAG_MUST_USE_TIMED_WRITE (0x08)
 // Attribute deferred to external storage
-#define ATTRIBUTE_MASK_EXTERNAL_STORAGE (0x10)
+#define MATTER_ATTRIBUTE_FLAG_EXTERNAL_STORAGE (0x10)
 // Attribute is singleton
-#define ATTRIBUTE_MASK_SINGLETON (0x20)
+#define MATTER_ATTRIBUTE_FLAG_SINGLETON (0x20)
 // Attribute is nullable
-#define ATTRIBUTE_MASK_NULLABLE (0x40)
+#define MATTER_ATTRIBUTE_FLAG_NULLABLE (0x40)
 
 /**
  * @brief Each attribute has it's metadata stored in such struct.
@@ -170,41 +170,41 @@ struct EmberAfAttributeMetadata
     /**
      * Check whether this attribute has a define min and max.
      */
-    bool HasMinMax() const { return mask & ATTRIBUTE_MASK_MIN_MAX; }
+    bool HasMinMax() const { return mask & MATTER_ATTRIBUTE_FLAG_MIN_MAX; }
 
     /**
      * Check whether this attribute is nullable.
      */
-    bool IsNullable() const { return mask & ATTRIBUTE_MASK_NULLABLE; }
+    bool IsNullable() const { return mask & MATTER_ATTRIBUTE_FLAG_NULLABLE; }
 
     /**
      * Check whether this attribute is readonly.
      */
-    bool IsReadOnly() const { return !(mask & ATTRIBUTE_MASK_WRITABLE); }
+    bool IsReadOnly() const { return !(mask & MATTER_ATTRIBUTE_FLAG_WRITABLE); }
 
     /**
      * Check whether this attribute requires a timed write.
      */
-    bool MustUseTimedWrite() const { return mask & ATTRIBUTE_MASK_MUST_USE_TIMED_WRITE; }
+    bool MustUseTimedWrite() const { return mask & MATTER_ATTRIBUTE_FLAG_MUST_USE_TIMED_WRITE; }
 
     /**
      * Check whether this attibute's storage is managed outside the built-in
      * attribute store.
      */
-    bool IsExternal() const { return mask & ATTRIBUTE_MASK_EXTERNAL_STORAGE; }
+    bool IsExternal() const { return mask & MATTER_ATTRIBUTE_FLAG_EXTERNAL_STORAGE; }
 
     /**
      * Check whether this is a "singleton" attribute, in the sense that it has a
      * single value across multiple instances of the cluster.  This is not
      * mutually exclusive with the attribute being external.
      */
-    bool IsSingleton() const { return mask & ATTRIBUTE_MASK_SINGLETON; }
+    bool IsSingleton() const { return mask & MATTER_ATTRIBUTE_FLAG_SINGLETON; }
 
     /**
      * Check whether this attribute is automatically stored in non-volatile
      * memory.
      */
-    bool IsAutomaticallyPersisted() const { return (mask & ATTRIBUTE_MASK_NONVOLATILE) && !IsExternal(); }
+    bool IsAutomaticallyPersisted() const { return (mask & MATTER_ATTRIBUTE_FLAG_NONVOLATILE) && !IsExternal(); }
 };
 
 /** @brief Returns true if the given attribute type is a string. */
diff --git a/src/app/util/attribute-storage-detail.h b/src/app/util/attribute-storage-detail.h
index 922605f3f9..6972115562 100644
--- a/src/app/util/attribute-storage-detail.h
+++ b/src/app/util/attribute-storage-detail.h
@@ -23,7 +23,6 @@
 #include <app/util/endpoint-config-api.h>
 #include <lib/support/CodeUtils.h>
 
-#include <app/util/att-storage.h>
 #include <app/util/attribute-metadata.h>
 #include <zap-generated/endpoint_config.h>
 
diff --git a/src/app/util/attribute-storage.cpp b/src/app/util/attribute-storage.cpp
index 0d1b3827e8..19736e5651 100644
--- a/src/app/util/attribute-storage.cpp
+++ b/src/app/util/attribute-storage.cpp
@@ -356,7 +356,7 @@ void emAfClusterAttributeChangedCallback(const ConcreteAttributePath & attribute
     const EmberAfCluster * cluster = emberAfFindServerCluster(attributePath.mEndpointId, attributePath.mClusterId);
     if (cluster != nullptr)
     {
-        EmberAfGenericClusterFunction f = emberAfFindClusterFunction(cluster, CLUSTER_MASK_ATTRIBUTE_CHANGED_FUNCTION);
+        EmberAfGenericClusterFunction f = emberAfFindClusterFunction(cluster, MATTER_CLUSTER_FLAG_ATTRIBUTE_CHANGED_FUNCTION);
         if (f != nullptr)
         {
             ((EmberAfClusterAttributeChangedCallback) f)(attributePath);
@@ -382,7 +382,7 @@ Status emAfClusterPreAttributeChangedCallback(const ConcreteAttributePath & attr
     // Casting and calling a function pointer on the same line results in ignoring the return
     // of the call on gcc-arm-none-eabi-9-2019-q4-major
     EmberAfClusterPreAttributeChangedCallback f = (EmberAfClusterPreAttributeChangedCallback) (emberAfFindClusterFunction(
-        cluster, CLUSTER_MASK_PRE_ATTRIBUTE_CHANGED_FUNCTION));
+        cluster, MATTER_CLUSTER_FLAG_PRE_ATTRIBUTE_CHANGED_FUNCTION));
     if (f != nullptr)
     {
         status = f(attributePath, attributeType, size, value);
@@ -399,7 +399,7 @@ static void initializeEndpoint(EmberAfDefinedEndpoint * definedEndpoint)
         const EmberAfCluster * cluster = &(epType->cluster[clusterIndex]);
         EmberAfGenericClusterFunction f;
         emberAfClusterInitCallback(definedEndpoint->endpoint, cluster->clusterId);
-        f = emberAfFindClusterFunction(cluster, CLUSTER_MASK_INIT_FUNCTION);
+        f = emberAfFindClusterFunction(cluster, MATTER_CLUSTER_FLAG_INIT_FUNCTION);
         if (f != nullptr)
         {
             ((EmberAfInitFunction) f)(definedEndpoint->endpoint);
@@ -415,7 +415,7 @@ static void shutdownEndpoint(EmberAfDefinedEndpoint * definedEndpoint)
     for (clusterIndex = 0; clusterIndex < epType->clusterCount; clusterIndex++)
     {
         const EmberAfCluster * cluster  = &(epType->cluster[clusterIndex]);
-        EmberAfGenericClusterFunction f = emberAfFindClusterFunction(cluster, CLUSTER_MASK_SHUTDOWN_FUNCTION);
+        EmberAfGenericClusterFunction f = emberAfFindClusterFunction(cluster, MATTER_CLUSTER_FLAG_SHUTDOWN_FUNCTION);
         if (f != nullptr)
         {
             ((EmberAfShutdownFunction) f)(definedEndpoint->endpoint);
@@ -539,7 +539,7 @@ static Status typeSensitiveMemCopy(ClusterId clusterId, uint8_t * dest, uint8_t
  */
 bool emAfMatchCluster(const EmberAfCluster * cluster, const EmberAfAttributeSearchRecord * attRecord)
 {
-    return (cluster->clusterId == attRecord->clusterId && (cluster->mask & CLUSTER_MASK_SERVER));
+    return (cluster->clusterId == attRecord->clusterId && (cluster->mask & MATTER_CLUSTER_FLAG_SERVER));
 }
 
 /**
@@ -607,8 +607,8 @@ Status emAfReadOrWriteAttribute(const EmberAfAttributeSearchRecord * attRecord,
 
                             {
                                 uint8_t * attributeLocation =
-                                    (am->mask & ATTRIBUTE_MASK_SINGLETON ? singletonAttributeLocation(am)
-                                                                         : attributeData + attributeOffsetIndex);
+                                    (am->mask & MATTER_ATTRIBUTE_FLAG_SINGLETON ? singletonAttributeLocation(am)
+                                                                                : attributeData + attributeOffsetIndex);
                                 uint8_t *src, *dst;
                                 if (write)
                                 {
@@ -637,7 +637,7 @@ Status emAfReadOrWriteAttribute(const EmberAfAttributeSearchRecord * attRecord,
                                 }
 
                                 // Is the attribute externally stored?
-                                if (am->mask & ATTRIBUTE_MASK_EXTERNAL_STORAGE)
+                                if (am->mask & MATTER_ATTRIBUTE_FLAG_EXTERNAL_STORAGE)
                                 {
                                     return (write ? emberAfExternalAttributeWriteCallback(attRecord->endpoint, attRecord->clusterId,
                                                                                           am, buffer)
@@ -657,7 +657,8 @@ Status emAfReadOrWriteAttribute(const EmberAfAttributeSearchRecord * attRecord,
                         else
                         { // Not the attribute we are looking for
                             // Increase the index if attribute is not externally stored
-                            if (!(am->mask & ATTRIBUTE_MASK_EXTERNAL_STORAGE) && !(am->mask & ATTRIBUTE_MASK_SINGLETON))
+                            if (!(am->mask & MATTER_ATTRIBUTE_FLAG_EXTERNAL_STORAGE) &&
+                                !(am->mask & MATTER_ATTRIBUTE_FLAG_SINGLETON))
                             {
                                 attributeOffsetIndex = static_cast<uint16_t>(attributeOffsetIndex + emberAfAttributeSize(am));
                             }
@@ -759,7 +760,7 @@ bool emberAfContainsClient(EndpointId endpoint, ClusterId clusterId)
         return false;
     }
 
-    return (emberAfFindClusterInType(emAfEndpoints[ep].endpointType, clusterId, CLUSTER_MASK_CLIENT) != nullptr);
+    return (emberAfFindClusterInType(emAfEndpoints[ep].endpointType, clusterId, MATTER_CLUSTER_FLAG_CLIENT) != nullptr);
 }
 
 // This will find the first server that has the clusterId given from the index of endpoint.
@@ -770,7 +771,7 @@ bool emberAfContainsServerFromIndex(uint16_t index, ClusterId clusterId)
         return false;
     }
 
-    return emberAfFindClusterInType(emAfEndpoints[index].endpointType, clusterId, CLUSTER_MASK_SERVER);
+    return emberAfFindClusterInType(emAfEndpoints[index].endpointType, clusterId, MATTER_CLUSTER_FLAG_SERVER);
 }
 
 namespace chip {
@@ -822,7 +823,7 @@ const EmberAfCluster * emberAfFindServerCluster(EndpointId endpoint, ClusterId c
         return nullptr;
     }
 
-    return emberAfFindClusterInType(emAfEndpoints[ep].endpointType, clusterId, CLUSTER_MASK_SERVER);
+    return emberAfFindClusterInType(emAfEndpoints[ep].endpointType, clusterId, MATTER_CLUSTER_FLAG_SERVER);
 }
 
 // Returns cluster within the endpoint; Does not ignore disabled endpoints
@@ -848,7 +849,7 @@ uint16_t emberAfGetClusterServerEndpointIndex(EndpointId endpoint, ClusterId clu
         return kEmberInvalidEndpointIndex;
     }
 
-    if (emberAfFindClusterInType(emAfEndpoints[epIndex].endpointType, cluster, CLUSTER_MASK_SERVER) == nullptr)
+    if (emberAfFindClusterInType(emAfEndpoints[epIndex].endpointType, cluster, MATTER_CLUSTER_FLAG_SERVER) == nullptr)
     {
         // The provided endpoint does not contain the given cluster server.
         return kEmberInvalidEndpointIndex;
@@ -864,7 +865,7 @@ uint16_t emberAfGetClusterServerEndpointIndex(EndpointId endpoint, ClusterId clu
             // Increase adjustedEndpointIndex for every endpoint containing the cluster server
             // before our endpoint of interest
             if (emAfEndpoints[i].endpoint != kInvalidEndpointId &&
-                (emberAfFindClusterInType(emAfEndpoints[i].endpointType, cluster, CLUSTER_MASK_SERVER) != nullptr))
+                (emberAfFindClusterInType(emAfEndpoints[i].endpointType, cluster, MATTER_CLUSTER_FLAG_SERVER) != nullptr))
             {
                 adjustedEndpointIndex++;
             }
@@ -989,7 +990,7 @@ uint8_t emberAfClusterCountByIndex(uint16_t endpointIndex, bool server)
 
 uint8_t emberAfClusterCountForEndpointType(const EmberAfEndpointType * type, bool server)
 {
-    const EmberAfClusterMask cluster_mask = server ? CLUSTER_MASK_SERVER : CLUSTER_MASK_CLIENT;
+    const EmberAfClusterMask cluster_mask = server ? MATTER_CLUSTER_FLAG_SERVER : MATTER_CLUSTER_FLAG_CLIENT;
 
     return static_cast<uint8_t>(std::count_if(type->cluster, type->cluster + type->clusterCount,
                                               [=](const EmberAfCluster & cluster) { return (cluster.mask & cluster_mask) != 0; }));
@@ -1070,7 +1071,7 @@ const EmberAfCluster * emberAfGetNthCluster(EndpointId endpoint, uint8_t n, bool
     }
 
     const EmberAfEndpointType * endpointType = emAfEndpoints[index].endpointType;
-    const EmberAfClusterMask cluster_mask    = server ? CLUSTER_MASK_SERVER : CLUSTER_MASK_CLIENT;
+    const EmberAfClusterMask cluster_mask    = server ? MATTER_CLUSTER_FLAG_SERVER : MATTER_CLUSTER_FLAG_CLIENT;
     const uint8_t clusterCount               = endpointType->clusterCount;
 
     uint8_t c = 0;
@@ -1212,7 +1213,7 @@ void emAfLoadAttributeDefaults(EndpointId endpoint, Optional<ClusterId> clusterI
                         size_t defaultValueSizeForBigEndianNudger = 0;
                         // Bypasses compiler warning about unused variable for little endian platforms.
                         (void) defaultValueSizeForBigEndianNudger;
-                        if ((am->mask & ATTRIBUTE_MASK_MIN_MAX) != 0U)
+                        if ((am->mask & MATTER_ATTRIBUTE_FLAG_MIN_MAX) != 0U)
                         {
                             // This is intentionally 2 and not 4 bytes since defaultValue in min/max
                             // attributes is still uint16_t.
@@ -1454,7 +1455,8 @@ DataVersion * emberAfDataVersionStorage(const ConcreteClusterPath & aConcreteClu
 
     // This does a second walk over endpoints to find the right one, but
     // probably worth it to avoid duplicating code.
-    auto clusterIndex = emberAfClusterIndex(aConcreteClusterPath.mEndpointId, aConcreteClusterPath.mClusterId, CLUSTER_MASK_SERVER);
+    auto clusterIndex =
+        emberAfClusterIndex(aConcreteClusterPath.mEndpointId, aConcreteClusterPath.mClusterId, MATTER_CLUSTER_FLAG_SERVER);
     if (clusterIndex == 0xFF)
     {
         // No such cluster on this endpoint.
diff --git a/src/app/util/attribute-storage.h b/src/app/util/attribute-storage.h
index 9d2dcc60bb..3b8a2e3fb9 100644
--- a/src/app/util/attribute-storage.h
+++ b/src/app/util/attribute-storage.h
@@ -18,7 +18,6 @@
 #pragma once
 
 #include <app/util/af-types.h>
-#include <app/util/att-storage.h>
 #include <app/util/attribute-metadata.h>
 #include <app/util/config.h>
 #include <app/util/endpoint-config-defines.h>
@@ -60,12 +59,12 @@ static constexpr uint16_t kEmberInvalidEndpointIndex = 0xFFFF;
     } /* cluster revision */                                                                                                       \
     }
 
-// The attrMask must contain the relevant ATTRIBUTE_MASK_* bits from
+// The attrMask must contain the relevant MATTER_ATTRIBUTE_FLAG_* bits from
 // attribute-metadata.h.  Specifically:
 //
-// * Writable attributes must have ATTRIBUTE_MASK_WRITABLE
-// * Nullable attributes (have X in the quality column in the spec) must have ATTRIBUTE_MASK_NULLABLE
-// * Attributes that have T in the Access column in the spec must have ATTRIBUTE_MASK_MUST_USE_TIMED_WRITE
+// * Writable attributes must have MATTER_ATTRIBUTE_FLAG_WRITABLE
+// * Nullable attributes (have X in the quality column in the spec) must have MATTER_ATTRIBUTE_FLAG_NULLABLE
+// * Attributes that have T in the Access column in the spec must have MATTER_ATTRIBUTE_FLAG_MUST_USE_TIMED_WRITE
 #define DECLARE_DYNAMIC_ATTRIBUTE(attId, attType, attSizeBytes, attrMask)                                                          \
     {                                                                                                                              \
         ZAP_EMPTY_DEFAULT(), attId, attSizeBytes, ZAP_TYPE(attType), attrMask | ZAP_ATTRIBUTE_MASK(EXTERNAL_STORAGE)               \
@@ -223,8 +222,8 @@ uint8_t emberAfGetClusterCountForEndpoint(chip::EndpointId endpoint);
 // Check if a cluster is implemented or not. If yes, the cluster is returned.
 //
 // mask = 0 -> find either client or server
-// mask = CLUSTER_MASK_CLIENT -> find client
-// mask = CLUSTER_MASK_SERVER -> find server
+// mask = MATTER_CLUSTER_FLAG_CLIENT -> find client
+// mask = MATTER_CLUSTER_FLAG_SERVER -> find server
 //
 // If a pointer to an index is provided, it will be updated to point to the relative index of the cluster
 // within the set of clusters that match the mask criteria.
diff --git a/src/app/util/attribute-table.cpp b/src/app/util/attribute-table.cpp
index 0d68701b63..b84104d42a 100644
--- a/src/app/util/attribute-table.cpp
+++ b/src/app/util/attribute-table.cpp
@@ -365,7 +365,7 @@ Status emAfWriteAttribute(const ConcreteAttributePath & path, const EmberAfWrite
 
     // if the value the attribute is being set to is out of range
     // return Status::ConstraintError
-    if ((metadata->mask & ATTRIBUTE_MASK_MIN_MAX) != 0U)
+    if ((metadata->mask & MATTER_ATTRIBUTE_FLAG_MIN_MAX) != 0U)
     {
         EmberAfDefaultAttributeValue minv = metadata->defaultValue.ptrToMinMaxValue->minValue;
         EmberAfDefaultAttributeValue maxv = metadata->defaultValue.ptrToMinMaxValue->maxValue;
diff --git a/src/app/util/endpoint-config-defines.h b/src/app/util/endpoint-config-defines.h
index 2a54c5d581..7da14edc50 100644
--- a/src/app/util/endpoint-config-defines.h
+++ b/src/app/util/endpoint-config-defines.h
@@ -29,5 +29,5 @@
     {                                                                                                                              \
         (uint32_t) x                                                                                                               \
     }
-#define ZAP_ATTRIBUTE_MASK(mask) ATTRIBUTE_MASK_##mask
-#define ZAP_CLUSTER_MASK(mask) CLUSTER_MASK_##mask
+#define ZAP_ATTRIBUTE_MASK(mask) MATTER_ATTRIBUTE_FLAG_##mask
+#define ZAP_CLUSTER_MASK(bit) MATTER_CLUSTER_FLAG_##bit
diff --git a/src/app/util/mock/MockNodeConfig.cpp b/src/app/util/mock/MockNodeConfig.cpp
index c6572ed0ae..a643daefad 100644
--- a/src/app/util/mock/MockNodeConfig.cpp
+++ b/src/app/util/mock/MockNodeConfig.cpp
@@ -19,7 +19,6 @@
 #include "app/util/af-types.h"
 #include <app/util/mock/MockNodeConfig.h>
 
-#include <app/util/att-storage.h>
 #include <app/util/attribute-storage.h>
 #include <initializer_list>
 #include <lib/support/CodeUtils.h>
@@ -127,9 +126,10 @@ MockClusterConfig::MockClusterConfig(ClusterId aId, std::initializer_list<MockAt
     {
         mEmberEventList.push_back(event.id);
     }
+
     mEmberCluster.clusterId      = id;
     mEmberCluster.attributeCount = static_cast<uint16_t>(attributes.size());
-    mEmberCluster.mask           = CLUSTER_MASK_SERVER;
+    mEmberCluster.mask           = MATTER_CLUSTER_FLAG_SERVER;
     mEmberCluster.eventCount     = static_cast<uint16_t>(mEmberEventList.size());
     mEmberCluster.eventList      = mEmberEventList.data();
 
diff --git a/src/app/util/mock/MockNodeConfig.h b/src/app/util/mock/MockNodeConfig.h
index 39f11052a7..6510a0e1a1 100644
--- a/src/app/util/mock/MockNodeConfig.h
+++ b/src/app/util/mock/MockNodeConfig.h
@@ -44,7 +44,7 @@ constexpr EmberAfAttributeMetadata DefaultAttributeMetadata(chip::AttributeId id
         .attributeId   = id,
         .size          = 4,
         .attributeType = ZCL_INT32U_ATTRIBUTE_TYPE,
-        .mask          = ATTRIBUTE_MASK_WRITABLE | ATTRIBUTE_MASK_NULLABLE,
+        .mask          = MATTER_ATTRIBUTE_FLAG_WRITABLE | MATTER_ATTRIBUTE_FLAG_NULLABLE,
     };
 }
 
@@ -55,7 +55,7 @@ struct MockAttributeConfig
     MockAttributeConfig(AttributeId aId) : id(aId), attributeMetaData(internal::DefaultAttributeMetadata(aId)) {}
     MockAttributeConfig(AttributeId aId, EmberAfAttributeMetadata metadata) : id(aId), attributeMetaData(metadata) {}
     MockAttributeConfig(AttributeId aId, EmberAfAttributeType type,
-                        EmberAfAttributeMask mask = ATTRIBUTE_MASK_WRITABLE | ATTRIBUTE_MASK_NULLABLE) :
+                        EmberAfAttributeMask mask = MATTER_ATTRIBUTE_FLAG_WRITABLE | MATTER_ATTRIBUTE_FLAG_NULLABLE) :
         id(aId),
         attributeMetaData(internal::DefaultAttributeMetadata(aId))
     {
diff --git a/src/app/util/mock/attribute-storage.cpp b/src/app/util/mock/attribute-storage.cpp
index 256d0a3a88..d0843d89cb 100644
--- a/src/app/util/mock/attribute-storage.cpp
+++ b/src/app/util/mock/attribute-storage.cpp
@@ -33,7 +33,6 @@
 #include <app/MessageDef/AttributeDataIB.h>
 #include <app/MessageDef/AttributeReportIB.h>
 #include <app/MessageDef/AttributeStatusIB.h>
-#include <app/util/att-storage.h>
 #include <app/util/attribute-storage.h>
 #include <app/util/endpoint-config-api.h>
 #include <app/util/mock/Constants.h>
@@ -272,7 +271,7 @@ chip::Optional<chip::AttributeId> emberAfGetServerAttributeIdByIndex(chip::Endpo
 
 uint8_t emberAfClusterIndex(chip::EndpointId endpointId, chip::ClusterId clusterId, EmberAfClusterMask mask)
 {
-    VerifyOrReturnValue(mask == 0 || (mask & CLUSTER_MASK_SERVER) != 0, UINT8_MAX); // only server clusters supported
+    VerifyOrReturnValue(mask == 0 || (mask & MATTER_CLUSTER_FLAG_SERVER) != 0, UINT8_MAX); // only server clusters supported
     ptrdiff_t index;
     auto cluster = GetMockNodeConfig().clusterByIds(endpointId, clusterId, &index);
     VerifyOrReturnValue(cluster != nullptr, UINT8_MAX);
diff --git a/src/controller/tests/TestWriteChunking.cpp b/src/controller/tests/TestWriteChunking.cpp
index 5bb7ef9521..fadba53dfe 100644
--- a/src/controller/tests/TestWriteChunking.cpp
+++ b/src/controller/tests/TestWriteChunking.cpp
@@ -97,8 +97,8 @@ class TestWriteChunking : public Test::AppContext
 
 //clang-format off
 DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(testClusterAttrsOnEndpoint)
-DECLARE_DYNAMIC_ATTRIBUTE(kTestListAttribute, ARRAY, 1, ATTRIBUTE_MASK_WRITABLE),
-    DECLARE_DYNAMIC_ATTRIBUTE(kTestListAttribute2, ARRAY, 1, ATTRIBUTE_MASK_WRITABLE), DECLARE_DYNAMIC_ATTRIBUTE_LIST_END();
+DECLARE_DYNAMIC_ATTRIBUTE(kTestListAttribute, ARRAY, 1, MATTER_ATTRIBUTE_FLAG_WRITABLE),
+    DECLARE_DYNAMIC_ATTRIBUTE(kTestListAttribute2, ARRAY, 1, MATTER_ATTRIBUTE_FLAG_WRITABLE), DECLARE_DYNAMIC_ATTRIBUTE_LIST_END();
 
 DECLARE_DYNAMIC_CLUSTER_LIST_BEGIN(testEndpointClusters)
 DECLARE_DYNAMIC_CLUSTER(Clusters::UnitTesting::Id, testClusterAttrsOnEndpoint, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr),
diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRIMDispatch.mm b/src/darwin/Framework/CHIP/ServerEndpoint/MTRIMDispatch.mm
index d21242956b..b283c8b819 100644
--- a/src/darwin/Framework/CHIP/ServerEndpoint/MTRIMDispatch.mm
+++ b/src/darwin/Framework/CHIP/ServerEndpoint/MTRIMDispatch.mm
@@ -18,7 +18,6 @@
 #include <app-common/zap-generated/cluster-objects.h>
 #include <app/CommandHandler.h>
 #include <app/ConcreteCommandPath.h>
-#include <app/util/att-storage.h>
 #include <app/util/attribute-table.h>
 
 #include <app/util/af-types.h>
diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster.mm b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster.mm
index e2f6fecbbb..2927efa181 100644
--- a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster.mm
+++ b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster.mm
@@ -312,7 +312,7 @@ - (BOOL)associateWithController:(nullable MTRDeviceController *)controller
             // Size in bytes does not matter, since we plan to
             // handle this entirely via AttributeAccessInterface.
             0,
-            // ATTRIBUTE_MASK_NULLABLE is not relevant because we
+            // MATTER_ATTRIBUTE_FLAG_NULLABLE is not relevant because we
             // are handling this all via AttributeAccessInterface.
             0)));
     }
diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint.mm b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint.mm
index 1b0f27fbaf..8e238d31e5 100644
--- a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint.mm
+++ b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint.mm
@@ -27,11 +27,10 @@
 #include <lib/support/SafeInt.h>
 #include <platform/LockTracker.h>
 
-// TODO: These af-types.h and att-storage.h and attribute-storage.h and
+// TODO: These af-types.h and attribute-storage.h and
 // endpoint-config-api.h and probably CodeUtils.h bits are a hack that should
 // eventually go away.
 #include <app/util/af-types.h>
-#include <app/util/att-storage.h>
 #include <app/util/attribute-storage.h>
 #include <app/util/endpoint-config-api.h>
 #include <lib/support/CodeUtils.h>
@@ -257,7 +256,7 @@ - (BOOL)finishAssociationWithController:(nullable MTRDeviceController *)controll
 
         metadata.clusterSize = 0; // All our attributes are external.
 
-        metadata.mask = CLUSTER_MASK_SERVER;
+        metadata.mask = MATTER_CLUSTER_FLAG_SERVER;
 
         metadata.functions = nullptr; // None of our clusters, including Descriptor, uses these.
 
@@ -278,7 +277,7 @@ - (BOOL)finishAssociationWithController:(nullable MTRDeviceController *)controll
 
         metadata.clusterSize = 0; // All our attributes are external.
 
-        metadata.mask = CLUSTER_MASK_SERVER;
+        metadata.mask = MATTER_CLUSTER_FLAG_SERVER;
 
         metadata.functions = nullptr; // Descriptor does not use these.
 
diff --git a/src/data-model-providers/codegen/tests/TestEmberAttributeDataBuffer.cpp b/src/data-model-providers/codegen/tests/TestEmberAttributeDataBuffer.cpp
new file mode 100644
index 0000000000..3d9571817a
--- /dev/null
+++ b/src/data-model-providers/codegen/tests/TestEmberAttributeDataBuffer.cpp
@@ -0,0 +1,1179 @@
+/*
+ *    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 <pw_unit_test/framework.h>
+
+#include <cmath>
+
+#include <data-model-providers/codegen/EmberAttributeDataBuffer.h>
+
+#include <app-common/zap-generated/attribute-type.h>
+#include <app/MessageDef/AttributeDataIB.h>
+#include <app/data-model/Encode.h>
+#include <app/data-model/Nullable.h>
+#include <app/util/af-types.h>
+#include <app/util/attribute-metadata.h>
+#include <app/util/attribute-storage-null-handling.h>
+#include <app/util/odd-sized-integers.h>
+#include <lib/core/CHIPError.h>
+#include <lib/core/StringBuilderAdapters.h>
+#include <lib/core/TLVReader.h>
+#include <lib/core/TLVTags.h>
+#include <lib/core/TLVTypes.h>
+#include <lib/core/TLVWriter.h>
+#include <lib/support/CodeUtils.h>
+#include <lib/support/Span.h>
+
+#include <limits>
+#include <optional>
+
+using namespace chip;
+using namespace chip::app;
+
+namespace {
+
+/// encodes a simple value in a TLV buffer
+class TLVEncodedValue
+{
+public:
+    TLVEncodedValue()  = default;
+    ~TLVEncodedValue() = default;
+
+    template <typename T>
+    TLV::TLVReader EncodeValue(const T & value)
+    {
+        const auto kTag = TLV::ContextTag(AttributeDataIB::Tag::kData);
+
+        TLV::TLVWriter writer;
+        writer.Init(mBuffer, sizeof(mBuffer));
+
+        TLV::TLVType outer;
+
+        VerifyOrDie(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outer) == CHIP_NO_ERROR);
+        VerifyOrDie(DataModel::Encode(writer, kTag, value) == CHIP_NO_ERROR);
+        VerifyOrDie(writer.EndContainer(outer) == CHIP_NO_ERROR);
+
+        VerifyOrDie(writer.Finalize() == CHIP_NO_ERROR);
+        size_t fill = writer.GetLengthWritten();
+
+        TLV::TLVReader reader;
+        reader.Init(mBuffer, fill);
+        VerifyOrDie(reader.Next() == CHIP_NO_ERROR);
+        VerifyOrDie(reader.GetTag() == TLV::AnonymousTag());
+        VerifyOrDie(reader.EnterContainer(outer) == CHIP_NO_ERROR);
+        VerifyOrDie(reader.Next() == CHIP_NO_ERROR);
+        VerifyOrDie(reader.GetTag() == kTag);
+
+        return reader;
+    }
+
+private:
+    static constexpr size_t kMaxSize = 128;
+    uint8_t mBuffer[kMaxSize];
+};
+
+class EncodeResult
+{
+public:
+    explicit EncodeResult() = default;
+    EncodeResult(CHIP_ERROR error) : mResult(error) { VerifyOrDie(error != CHIP_NO_ERROR); }
+
+    static EncodeResult Ok() { return EncodeResult(); }
+
+    bool IsSuccess() const { return !mResult.has_value(); }
+
+    bool operator==(const CHIP_ERROR & other) const { return mResult.has_value() && (*mResult == other); }
+
+    const std::optional<CHIP_ERROR> & Value() const { return mResult; }
+
+private:
+    std::optional<CHIP_ERROR> mResult;
+};
+
+template <typename T>
+bool IsEqual(const T & a, const T & b)
+{
+    return a == b;
+}
+
+template <>
+bool IsEqual<float>(const float & a, const float & b)
+{
+    if (std::isnan(a) && std::isnan(b))
+    {
+        return true;
+    }
+
+    return a == b;
+}
+
+template <>
+bool IsEqual<double>(const double & a, const double & b)
+{
+    if (std::isnan(a) && std::isnan(b))
+    {
+        return true;
+    }
+
+    return a == b;
+}
+
+template <>
+bool IsEqual<ByteSpan>(const ByteSpan & a, const ByteSpan & b)
+{
+    return a.data_equal(b);
+}
+
+template <>
+bool IsEqual<CharSpan>(const CharSpan & a, const CharSpan & b)
+{
+    return a.data_equal(b);
+}
+
+template <typename T>
+bool IsEqual(const std::optional<T> & a, const std::optional<T> & b)
+{
+    if (a.has_value() != b.has_value())
+    {
+        return false;
+    }
+
+    if (!a.has_value())
+    {
+        return true;
+    }
+
+    return IsEqual(*a, *b);
+}
+
+template <typename T>
+bool IsEqual(const DataModel::Nullable<T> & a, const DataModel::Nullable<T> & b)
+{
+    if (a.IsNull() != b.IsNull())
+    {
+        return false;
+    }
+
+    if (a.IsNull())
+    {
+        return true;
+    }
+
+    return IsEqual(a.Value(), b.Value());
+}
+
+/// Validates that an encoded value in ember takes a specific format
+template <size_t kMaxSize = 128>
+class EncodeTester
+{
+public:
+    EncodeTester(const EmberAfAttributeMetadata * meta) : mMetaData(meta) {}
+    ~EncodeTester() = default;
+
+    template <typename T, size_t N>
+    EncodeResult TryEncode(const T & value, const uint8_t (&arr)[N])
+    {
+        ByteSpan expected(arr);
+        MutableByteSpan out_span(mEmberAttributeDataBuffer);
+        Ember::EmberAttributeDataBuffer buffer(mMetaData, out_span);
+
+        TLVEncodedValue tlvEncoded;
+        TLV::TLVReader reader = tlvEncoded.EncodeValue(value);
+
+        CHIP_ERROR err = buffer.Decode(reader);
+        if (err != CHIP_NO_ERROR)
+        {
+            return err;
+        }
+
+        if (expected.size() != out_span.size())
+        {
+            ChipLogError(Test, "Decode mismatch in size: expected %u, got %u", static_cast<unsigned>(expected.size()),
+                         static_cast<unsigned>(out_span.size()));
+            return CHIP_ERROR_INTERNAL;
+        }
+
+        if (!expected.data_equal(out_span))
+        {
+            ChipLogError(Test, "Decode mismatch in content for %u bytes", static_cast<unsigned>(expected.size()));
+            return CHIP_ERROR_INTERNAL;
+        }
+
+        return EncodeResult::Ok();
+    }
+
+    template <typename T, size_t N>
+    EncodeResult TryDecode(const T & value, const uint8_t (&arr)[N])
+    {
+        // Write data to TLV
+        {
+            uint8_t mutableBuffer[N];
+            memcpy(mutableBuffer, arr, N);
+
+            MutableByteSpan data_span(mutableBuffer);
+            Ember::EmberAttributeDataBuffer buffer(mMetaData, data_span);
+
+            TLV::TLVWriter writer;
+            writer.Init(mEmberAttributeDataBuffer, sizeof(mEmberAttributeDataBuffer));
+            ReturnErrorOnFailure(buffer.Encode(writer, TLV::AnonymousTag()));
+            ReturnErrorOnFailure(writer.Finalize());
+        }
+
+        // Data was written in TLV. Take it back out
+
+        TLV::TLVReader reader;
+        reader.Init(mEmberAttributeDataBuffer, sizeof(mEmberAttributeDataBuffer));
+
+        ReturnErrorOnFailure(reader.Next());
+
+        T encodedValue;
+        ReturnErrorOnFailure(DataModel::Decode(reader, encodedValue));
+
+        if (!IsEqual(encodedValue, value))
+        {
+            ChipLogError(Test, "Encode mismatch: different data");
+            return CHIP_ERROR_INTERNAL;
+        }
+
+        return EncodeResult::Ok();
+    }
+
+private:
+    const EmberAfAttributeMetadata * mMetaData;
+    uint8_t mEmberAttributeDataBuffer[kMaxSize];
+};
+
+const EmberAfAttributeMetadata * CreateFakeMeta(EmberAfAttributeType type, bool nullable)
+{
+    static EmberAfAttributeMetadata meta = {
+        .defaultValue  = EmberAfDefaultOrMinMaxAttributeValue(static_cast<uint8_t *>(nullptr)),
+        .attributeId   = 0,
+        .size          = 0, // likely not valid, however not used for tests
+        .attributeType = ZCL_UNKNOWN_ATTRIBUTE_TYPE,
+        .mask          = 0,
+    };
+
+    meta.attributeType = type;
+    meta.mask          = nullable ? MATTER_ATTRIBUTE_FLAG_NULLABLE : 0;
+
+    return &meta;
+}
+
+} // namespace
+
+// All the tests below assume buffer ordering in little endian format
+// Since currently all chip platforms in CI are little endian, we just kept tests
+// as-is
+static_assert(!CHIP_CONFIG_BIG_ENDIAN_TARGET);
+
+TEST(TestEmberAttributeBuffer, TestEncodeUnsignedTypes)
+{
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT8U_ATTRIBUTE_TYPE, false /* nullable */));
+
+        EXPECT_TRUE(tester.TryEncode<uint8_t>(0, { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint8_t>(123, { 123 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint8_t>(0xFD, { 0xFD }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint8_t>(255, { 0xFF }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT8U_ATTRIBUTE_TYPE, true /* nullable */));
+
+        EXPECT_TRUE(tester.TryEncode<uint8_t>(0, { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint8_t>(123, { 123 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint8_t>(0xFD, { 0xFD }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<DataModel::Nullable<uint8_t>>(DataModel::NullNullable, { 0xFF }).IsSuccess());
+
+        // Not allowed to encode null-equivalent
+        EXPECT_EQ(tester.TryEncode<uint8_t>(0xFF, { 0xFF }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT16U_ATTRIBUTE_TYPE, false /* nullable */));
+
+        EXPECT_TRUE(tester.TryEncode<uint16_t>(0, { 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint16_t>(123, { 123, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint16_t>(0xFD, { 0xFD, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint16_t>(255, { 0xFF, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint16_t>(0xABCD, { 0xCD, 0xAB }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint16_t>(0xFFFF, { 0xFF, 0xFF }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT16U_ATTRIBUTE_TYPE, true /* nullable */));
+
+        EXPECT_TRUE(tester.TryEncode<uint16_t>(0, { 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint16_t>(123, { 123, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint16_t>(0xFD, { 0xFD, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint16_t>(255, { 0xFF, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint16_t>(0xABCD, { 0xCD, 0xAB }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<DataModel::Nullable<uint16_t>>(DataModel::NullNullable, { 0xFF, 0xFF }).IsSuccess());
+
+        // Not allowed to encode null-equivalent
+        EXPECT_EQ(tester.TryEncode<uint16_t>(0xFFFF, { 0xFF, 0xFF }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+    }
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT64U_ATTRIBUTE_TYPE, true /* nullable */));
+
+        EXPECT_TRUE(tester.TryEncode<uint64_t>(0, { 0, 0, 0, 0, 0, 0, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint64_t>(0x1234567, { 0x67, 0x45, 0x23, 0x01, 0, 0, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint64_t>(0xAABBCCDDEEFF1122, { 0x22, 0x11, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA }).IsSuccess());
+        EXPECT_TRUE(
+            tester.TryEncode<uint64_t>(std::numeric_limits<uint64_t>::max() - 1, { 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF })
+                .IsSuccess());
+
+        EXPECT_TRUE(tester
+                        .TryEncode<DataModel::Nullable<uint64_t>>(DataModel::NullNullable,
+                                                                  { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF })
+                        .IsSuccess());
+
+        EXPECT_EQ(
+            tester.TryEncode<uint64_t>(std::numeric_limits<uint64_t>::max(), { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }),
+            CHIP_IM_GLOBAL_STATUS(ConstraintError));
+    }
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT64U_ATTRIBUTE_TYPE, false /* nullable */));
+
+        // we should be able to encode the maximum value
+        EXPECT_TRUE(
+            tester.TryEncode<uint64_t>(std::numeric_limits<uint64_t>::max(), { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF })
+                .IsSuccess());
+    }
+
+    /// Odd sized integers
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT24U_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<uint32_t>(0, { 0, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint32_t>(0x123456, { 0x56, 0x34, 0x12 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint32_t>(0xFFFFFF, { 0xFF, 0xFF, 0xFF }).IsSuccess());
+
+        // Out of range
+        EXPECT_EQ(tester.TryEncode<uint32_t>(0x1000000, { 0 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+        EXPECT_EQ(tester.TryEncode<uint32_t>(0xFF000000, { 0 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+    }
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT24U_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<uint32_t>(0, { 0, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint32_t>(0x123456, { 0x56, 0x34, 0x12 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<DataModel::Nullable<uint32_t>>(DataModel::NullNullable, { 0xFF, 0xFF, 0xFF }).IsSuccess());
+
+        // Out of range
+        EXPECT_EQ(tester.TryEncode<uint32_t>(0x1000000, { 0 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+        // cannot encode null equivalent value
+        EXPECT_EQ(tester.TryEncode<uint32_t>(0xFFFFFF, { 0x56, 0x34, 0x12 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT40U_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<uint64_t>(0, { 0, 0, 0, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint64_t>(0x123456, { 0x56, 0x34, 0x12, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<uint64_t>(0x123456FFFF, { 0xFF, 0xFF, 0x56, 0x34, 0x12 }).IsSuccess());
+        EXPECT_TRUE(
+            tester.TryEncode<DataModel::Nullable<uint64_t>>(DataModel::NullNullable, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }).IsSuccess());
+
+        // Out of range
+        EXPECT_EQ(tester.TryEncode<uint64_t>(0x10011001100, { 0 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+        // cannot encode null equivalent value
+        EXPECT_EQ(tester.TryEncode<uint64_t>(0xFFFFFFFFFF, { 0 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+    }
+
+    // Double-check tests, not as exhaustive, to cover all other unsigned values and get
+    // more test line coverage
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT32U_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<uint64_t>(0x1234, { 0x34, 0x12, 0, 0 }).IsSuccess());
+    }
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT48U_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<uint64_t>(0x1234, { 0x34, 0x12, 0, 0, 0, 0 }).IsSuccess());
+    }
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT56U_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<uint64_t>(0x1234, { 0x34, 0x12, 0, 0, 0, 0, 0 }).IsSuccess());
+    }
+}
+
+TEST(TestEmberAttributeBuffer, TestEncodeSignedTypes)
+{
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT8S_ATTRIBUTE_TYPE, false /* nullable */));
+
+        EXPECT_TRUE(tester.TryEncode<int8_t>(0, { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int8_t>(123, { 123 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int8_t>(127, { 127 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int8_t>(-10, { 0xF6 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int8_t>(-128, { 0x80 }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT8S_ATTRIBUTE_TYPE, true /* nullable */));
+
+        EXPECT_TRUE(tester.TryEncode<int8_t>(0, { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int8_t>(123, { 123 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int8_t>(127, { 127 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int8_t>(-10, { 0xF6 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int8_t>(-127, { 0x81 }).IsSuccess());
+
+        // NULL canot be encoded
+        EXPECT_EQ(tester.TryEncode<int8_t>(std::numeric_limits<int8_t>::min(), { 0x80 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+    }
+    {
+
+        EncodeTester tester(CreateFakeMeta(ZCL_INT16S_ATTRIBUTE_TYPE, false /* nullable */));
+
+        EXPECT_TRUE(tester.TryEncode<int16_t>(0, { 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int16_t>(123, { 123, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int16_t>(127, { 127, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int16_t>(-10, { 0xF6, 0xFF }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int16_t>(-128, { 0x80, 0xFF }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int16_t>(-1234, { 0x2E, 0xFB }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int16_t>(std::numeric_limits<int16_t>::min(), { 0x0, 0x80 }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT16S_ATTRIBUTE_TYPE, true /* nullable */));
+
+        EXPECT_TRUE(tester.TryEncode<int16_t>(0, { 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int16_t>(123, { 123, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int16_t>(127, { 127, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int16_t>(-10, { 0xF6, 0xFF }).IsSuccess());
+
+        // NULL canot be encoded
+        EXPECT_EQ(tester.TryEncode<int16_t>(std::numeric_limits<int16_t>::min(), { 0x80 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+    }
+
+    // Odd size integers
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT24S_ATTRIBUTE_TYPE, false /* nullable */));
+
+        EXPECT_TRUE(tester.TryEncode<int32_t>(0, { 0, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int32_t>(0x123456, { 0x56, 0x34, 0x12 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int32_t>(-1, { 0xFF, 0xFF, 0xFF }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int32_t>(-10, { 0xF6, 0xFF, 0xFF }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int32_t>(-1234, { 0x2E, 0xFB, 0xFF }).IsSuccess());
+
+        // Out of range
+        EXPECT_EQ(tester.TryEncode<int32_t>(0x1000000, { 0 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+        EXPECT_EQ(tester.TryEncode<int32_t>(0x0F000000, { 0 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+        EXPECT_EQ(tester.TryEncode<int32_t>(-0x1000000, { 0 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+    }
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT24S_ATTRIBUTE_TYPE, true /* nullable */));
+
+        EXPECT_TRUE(tester.TryEncode<int32_t>(0, { 0, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int32_t>(0x123456, { 0x56, 0x34, 0x12 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int32_t>(-1, { 0xFF, 0xFF, 0xFF }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int32_t>(-10, { 0xF6, 0xFF, 0xFF }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int32_t>(-1234, { 0x2E, 0xFB, 0xFF }).IsSuccess());
+
+        EXPECT_TRUE(tester.TryEncode<DataModel::Nullable<uint32_t>>(DataModel::NullNullable, { 0x00, 0x00, 0x80 }).IsSuccess());
+
+        // Out of range
+        EXPECT_EQ(tester.TryEncode<int32_t>(0x1000000, { 0 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+        // cannot encode null equivalent value - this is the minimum negative value
+        // for 24-bit
+        EXPECT_EQ(tester.TryEncode<int32_t>(-(1 << 24) - 1, { 0x56, 0x34, 0x12 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+
+        // Out of range for signed - these are unsigned values that are larger
+        EXPECT_EQ(tester.TryEncode<int32_t>(0xFFFFFF, { 0x56, 0x34, 0x12 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+        EXPECT_EQ(tester.TryEncode<int32_t>(0x800000, { 0x56, 0x34, 0x12 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT40S_ATTRIBUTE_TYPE, true /* nullable */));
+
+        // NOTE: to generate encoded values, you an use commands like:
+        //
+        //    python -c 'import struct; print(", ".join(["0x%X" % v for v in struct.pack("<q", -12345678910)]))'
+        //
+        //    OUTPUT: 0xC2, 0xE3, 0x23, 0x20, 0xFD, 0xFF, 0xFF, 0xFF
+        //
+        EXPECT_TRUE(tester.TryEncode<int64_t>(0, { 0, 0, 0, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int64_t>(0x123456, { 0x56, 0x34, 0x12, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int64_t>(-1234, { 0x2E, 0xFB, 0xFF, 0xFF, 0xFF }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int64_t>(-123456789, { 0xeb, 0x32, 0xa4, 0xf8, 0xFF }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<int64_t>(-12345678910, { 0xc2, 0xe3, 0x23, 0x20, 0xfd }).IsSuccess());
+
+        EXPECT_TRUE(
+            tester.TryEncode<DataModel::Nullable<uint64_t>>(DataModel::NullNullable, { 0x00, 0x00, 0x00, 0x00, 0x80 }).IsSuccess());
+
+        // Out of range
+        EXPECT_EQ(tester.TryEncode<int64_t>(0x10011001100, { 0 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+        // cannot encode null equivalent value
+        EXPECT_EQ(tester.TryEncode<int64_t>(-(1LL << 40) - 1, { 0 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+        // negative out of range
+        EXPECT_EQ(tester.TryEncode<int64_t>(-0x10000000000, { 0 }), CHIP_IM_GLOBAL_STATUS(ConstraintError));
+    }
+
+    // Double-check tests, not as exhaustive, to cover all other unsigned values and get
+    // more test line coverage
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT32S_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<int64_t>(-1234, { 0x2E, 0xFB, 0xFF, 0xFF }).IsSuccess());
+    }
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT48S_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<int64_t>(-1234, { 0x2E, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF }).IsSuccess());
+    }
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT56S_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<int64_t>(-1234, { 0x2E, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT64S_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<int64_t>(-1234, { 0x2E, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }).IsSuccess());
+
+        // min/max ranges too
+        EXPECT_TRUE(
+            tester.TryEncode<int64_t>(std::numeric_limits<int64_t>::min() + 1, { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 })
+                .IsSuccess());
+        EXPECT_TRUE(
+            tester.TryEncode<int64_t>(std::numeric_limits<int64_t>::max(), { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F })
+                .IsSuccess());
+
+        // Reserved value for NULL
+        EXPECT_EQ(
+            tester.TryEncode<int64_t>(std::numeric_limits<int64_t>::min(), { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 }),
+            CHIP_IM_GLOBAL_STATUS(ConstraintError));
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT64S_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<int64_t>(-1234, { 0x2E, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }).IsSuccess());
+
+        EXPECT_TRUE(
+            tester.TryEncode<int64_t>(std::numeric_limits<int64_t>::min(), { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 })
+                .IsSuccess());
+        EXPECT_TRUE(
+            tester.TryEncode<int64_t>(std::numeric_limits<int64_t>::min() + 1, { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 })
+                .IsSuccess());
+        EXPECT_TRUE(
+            tester.TryEncode<int64_t>(std::numeric_limits<int64_t>::max(), { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F })
+                .IsSuccess());
+    }
+}
+
+TEST(TestEmberAttributeBuffer, TestEncodeBool)
+{
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_BOOLEAN_ATTRIBUTE_TYPE, false /* nullable */));
+
+        EXPECT_TRUE(tester.TryEncode<bool>(true, { 1 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<bool>(false, { 0 }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_BOOLEAN_ATTRIBUTE_TYPE, true /* nullable */));
+
+        EXPECT_TRUE(tester.TryEncode<bool>(true, { 1 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<bool>(false, { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<DataModel::Nullable<bool>>(DataModel::NullNullable, { 0xFF }).IsSuccess());
+    }
+}
+
+TEST(TestEmberAttributeBuffer, TestEncodeFloatingPoint)
+{
+    // NOTE: to generate encoded values, you an use commands like:
+    //
+    //    python -c 'import struct; print(", ".join(["0x%X" % v for v in struct.pack("<f", -123.55)]))'
+    //    OUTPUT: 0x9A, 0x19, 0xF7, 0x42
+    //
+    //    python -c 'import struct; print(", ".join(["0x%X" % v for v in struct.pack("<f", float("nan"))]))'
+    //    OUTPUT: 0x00, 0x00, 0xC0, 0x7F
+    //
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_SINGLE_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<float>(123.55f, { 0x9A, 0x19, 0xF7, 0x42 }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_SINGLE_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<float>(123.55f, { 0x9A, 0x19, 0xF7, 0x42 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<DataModel::Nullable<float>>(DataModel::NullNullable, { 0, 0, 0xC0, 0x7F }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_DOUBLE_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<double>(123.55, { 0x33, 0x33, 0x33, 0x33, 0x33, 0xE3, 0x5E, 0x40 }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_DOUBLE_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<double>(123.55, { 0x33, 0x33, 0x33, 0x33, 0x33, 0xE3, 0x5E, 0x40 }).IsSuccess());
+        EXPECT_TRUE(
+            tester.TryEncode<DataModel::Nullable<double>>(DataModel::NullNullable, { 0, 0, 0, 0, 0, 0, 0xF8, 0x7F }).IsSuccess());
+    }
+}
+
+TEST(TestEmberAttributeBuffer, TestEncodeStrings)
+{
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_CHAR_STRING_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<CharSpan>(""_span, { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<CharSpan>("test"_span, { 4, 't', 'e', 's', 't' }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<CharSpan>("foo"_span, { 3, 'f', 'o', 'o' }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_CHAR_STRING_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<CharSpan>(""_span, { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<CharSpan>("test"_span, { 4, 't', 'e', 's', 't' }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<DataModel::Nullable<CharSpan>>(DataModel::NullNullable, { 0xFF }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<CharSpan>(""_span, { 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<CharSpan>("test"_span, { 4, 0, 't', 'e', 's', 't' }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<CharSpan>("foo"_span, { 3, 0, 'f', 'o', 'o' }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<CharSpan>("test"_span, { 4, 0, 't', 'e', 's', 't' }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<DataModel::Nullable<CharSpan>>(DataModel::NullNullable, { 0xFF, 0xFF }).IsSuccess());
+    }
+
+    const uint8_t kOctetData[] = { 1, 2, 3 };
+
+    // Binary data
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_OCTET_STRING_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<ByteSpan>(ByteSpan({}), { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<ByteSpan>(ByteSpan(kOctetData), { 3, 1, 2, 3 }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_OCTET_STRING_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<ByteSpan>(ByteSpan({}), { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<ByteSpan>(ByteSpan(kOctetData), { 3, 1, 2, 3 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<DataModel::Nullable<ByteSpan>>(DataModel::NullNullable, { 0xFF }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<ByteSpan>(ByteSpan({}), { 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<ByteSpan>(ByteSpan(kOctetData), { 3, 0, 1, 2, 3 }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryEncode<ByteSpan>(ByteSpan({}), { 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<ByteSpan>(ByteSpan(kOctetData), { 3, 0, 1, 2, 3 }).IsSuccess());
+        EXPECT_TRUE(tester.TryEncode<DataModel::Nullable<ByteSpan>>(DataModel::NullNullable, { 0xFF, 0xFF }).IsSuccess());
+    }
+}
+
+TEST(TestEmberAttributeBuffer, TestEncodeFailures)
+{
+    {
+        // attribute type that is not handled
+        EncodeTester tester(CreateFakeMeta(ZCL_UNKNOWN_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_EQ(tester.TryEncode<DataModel::Nullable<uint32_t>>(DataModel::NullNullable, { 0 }), CHIP_IM_GLOBAL_STATUS(Failure));
+    }
+
+    {
+        // Insufficient space
+        EncodeTester<3> tester(CreateFakeMeta(ZCL_CHAR_STRING_ATTRIBUTE_TYPE, true /* nullable */));
+
+        // Empty is ok
+        EXPECT_TRUE(tester.TryEncode<CharSpan>(""_span, { 0 }).IsSuccess());
+
+        // Short strings (with and without count) is wrong.
+        EXPECT_EQ(tester.TryEncode<CharSpan>("test"_span, { 0 }), CHIP_ERROR_NO_MEMORY);
+        EXPECT_EQ(tester.TryEncode<CharSpan>("foo"_span, { 3, 'f', 'o' }), CHIP_ERROR_NO_MEMORY);
+
+        EXPECT_TRUE(tester.TryEncode<DataModel::Nullable<CharSpan>>(DataModel::NullNullable, { 0xFF }).IsSuccess());
+    }
+
+    {
+        // Insufficient space
+        EncodeTester<3> tester(CreateFakeMeta(ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE, true /* nullable */));
+
+        // Empty is ok
+        EXPECT_TRUE(tester.TryEncode<CharSpan>(""_span, { 0, 0 }).IsSuccess());
+
+        // Short strings (with and without count) is wrong.
+        EXPECT_EQ(tester.TryEncode<CharSpan>("test"_span, { 0 }), CHIP_ERROR_NO_MEMORY);
+        EXPECT_EQ(tester.TryEncode<CharSpan>("foo"_span, { 0, 3, 'f', 'o' }), CHIP_ERROR_NO_MEMORY);
+        EXPECT_EQ(tester.TryEncode<CharSpan>("test"_span, { 0xFF }), CHIP_ERROR_NO_MEMORY);
+
+        EXPECT_TRUE(tester.TryEncode<DataModel::Nullable<CharSpan>>(DataModel::NullNullable, { 0xFF, 0xFF }).IsSuccess());
+    }
+
+    {
+        // Insufficient space even for length
+        EncodeTester<1> tester(CreateFakeMeta(ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_EQ(tester.TryEncode<CharSpan>(""_span, { 0 }), CHIP_ERROR_NO_MEMORY);
+    }
+
+    // bad type casts
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_CHAR_STRING_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_EQ(tester.TryEncode<ByteSpan>(ByteSpan({}), { 0 }), CHIP_ERROR_WRONG_TLV_TYPE);
+    }
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT32U_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_EQ(tester.TryEncode<bool>(true, { 0 }), CHIP_ERROR_WRONG_TLV_TYPE);
+    }
+}
+
+TEST(TestEmberAttributeBuffer, TestNoData)
+{
+    EncodeTester tester(CreateFakeMeta(ZCL_NO_DATA_ATTRIBUTE_TYPE, true /* nullable */));
+
+    // support a always-null type
+    EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<uint32_t>>(DataModel::NullNullable, { 0 }).IsSuccess());
+    EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<float>>(DataModel::NullNullable, { 0 }).IsSuccess());
+    EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<bool>>(DataModel::NullNullable, { 0 }).IsSuccess());
+}
+
+TEST(TestEmberAttributeBuffer, TestDecodeFailures)
+{
+    {
+        // attribute type that is not handled
+        EncodeTester tester(CreateFakeMeta(ZCL_UNKNOWN_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_EQ(tester.TryDecode<DataModel::Nullable<uint32_t>>(DataModel::NullNullable, { 0 }), CHIP_IM_GLOBAL_STATUS(Failure));
+    }
+
+    {
+        // Insufficient input
+        EncodeTester<3> tester(CreateFakeMeta(ZCL_CHAR_STRING_ATTRIBUTE_TYPE, true /* nullable */));
+
+        EXPECT_EQ(tester.TryDecode<CharSpan>("test"_span, { 10 }), CHIP_ERROR_BUFFER_TOO_SMALL);
+        EXPECT_EQ(tester.TryDecode<CharSpan>("foo"_span, { 3, 'f', 'o' }), CHIP_ERROR_BUFFER_TOO_SMALL);
+    }
+
+    {
+        // Insufficient data buffer - should never happen, but test that we will error out
+        EncodeTester tester(CreateFakeMeta(ZCL_INT32U_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_EQ(tester.TryDecode<uint32_t>(123, { 1, 2, 3 }), CHIP_ERROR_BUFFER_TOO_SMALL);
+    }
+
+    {
+        // Insufficient data buffer - should never happen, but test that we will error out
+        EncodeTester tester(CreateFakeMeta(ZCL_SINGLE_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_EQ(tester.TryDecode<float>(1.5f, { 1, 2, 3 }), CHIP_ERROR_BUFFER_TOO_SMALL);
+    }
+
+    {
+        // Insufficient data buffer - should never happen, but test that we will error out
+        EncodeTester tester(CreateFakeMeta(ZCL_DOUBLE_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_EQ(tester.TryDecode<double>(1.5, { 1, 2, 3 }), CHIP_ERROR_BUFFER_TOO_SMALL);
+    }
+
+    {
+        // Bad boolean data
+        EncodeTester tester(CreateFakeMeta(ZCL_BOOLEAN_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_EQ(tester.TryDecode<bool>(true, { 123 }), CHIP_ERROR_INVALID_ARGUMENT);
+    }
+}
+
+TEST(TestEmberAttributeBuffer, TestDecodeSignedTypes)
+{
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT8S_ATTRIBUTE_TYPE, false /* nullable */));
+
+        EXPECT_TRUE(tester.TryDecode<int8_t>(0, { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int8_t>(123, { 123 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int8_t>(127, { 127 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int8_t>(-10, { 0xF6 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int8_t>(-128, { 0x80 }).IsSuccess());
+
+        // longer data is ok
+        EXPECT_TRUE(tester.TryDecode<int8_t>(-128, { 0x80, 1, 2, 3, 4 }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT8S_ATTRIBUTE_TYPE, true /* nullable */));
+
+        EXPECT_TRUE(tester.TryDecode<int8_t>(0, { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int8_t>(123, { 123 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int8_t>(127, { 127 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int8_t>(-10, { 0xF6 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int8_t>(-127, { 0x81 }).IsSuccess());
+
+        // NULL can be decoded
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<int8_t>>(DataModel::NullNullable, { 0x80 }).IsSuccess());
+
+        // decoding as nullable proceeds as normal
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<int8_t>>(-127, { 0x81 }).IsSuccess());
+    }
+
+    {
+
+        EncodeTester tester(CreateFakeMeta(ZCL_INT16S_ATTRIBUTE_TYPE, false /* nullable */));
+
+        EXPECT_TRUE(tester.TryDecode<int16_t>(0, { 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int16_t>(123, { 123, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int16_t>(127, { 127, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int16_t>(-10, { 0xF6, 0xFF }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int16_t>(-128, { 0x80, 0xFF }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int16_t>(-1234, { 0x2E, 0xFB }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int16_t>(std::numeric_limits<int16_t>::min(), { 0x0, 0x80 }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT16S_ATTRIBUTE_TYPE, true /* nullable */));
+
+        EXPECT_TRUE(tester.TryDecode<int16_t>(0, { 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int16_t>(123, { 123, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int16_t>(127, { 127, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int16_t>(-10, { 0xF6, 0xFF }).IsSuccess());
+
+        // NULL decoding
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<int16_t>>(DataModel::NullNullable, { 0x00, 0x80 }).IsSuccess());
+    }
+
+    // Odd size integers
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT24S_ATTRIBUTE_TYPE, false /* nullable */));
+
+        EXPECT_TRUE(tester.TryDecode<int32_t>(0, { 0, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int32_t>(0x123456, { 0x56, 0x34, 0x12 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int32_t>(-1, { 0xFF, 0xFF, 0xFF }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int32_t>(-10, { 0xF6, 0xFF, 0xFF }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int32_t>(-1234, { 0x2E, 0xFB, 0xFF }).IsSuccess());
+    }
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT24S_ATTRIBUTE_TYPE, true /* nullable */));
+
+        EXPECT_TRUE(tester.TryDecode<int32_t>(0, { 0, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int32_t>(0x123456, { 0x56, 0x34, 0x12 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int32_t>(-1, { 0xFF, 0xFF, 0xFF }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int32_t>(-10, { 0xF6, 0xFF, 0xFF }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int32_t>(-1234, { 0x2E, 0xFB, 0xFF }).IsSuccess());
+
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<uint32_t>>(DataModel::NullNullable, { 0x00, 0x00, 0x80 }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT40S_ATTRIBUTE_TYPE, true /* nullable */));
+
+        // NOTE: to generate encoded values, you an use commands like:
+        //
+        //    python -c 'import struct; print(", ".join(["0x%X" % v for v in struct.pack("<q", -12345678910)]))'
+        //
+        //    OUTPUT: 0xC2, 0xE3, 0x23, 0x20, 0xFD, 0xFF, 0xFF, 0xFF
+        //
+        EXPECT_TRUE(tester.TryDecode<int64_t>(0, { 0, 0, 0, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int64_t>(0x123456, { 0x56, 0x34, 0x12, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int64_t>(-1234, { 0x2E, 0xFB, 0xFF, 0xFF, 0xFF }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int64_t>(-123456789, { 0xeb, 0x32, 0xa4, 0xf8, 0xFF }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<int64_t>(-12345678910, { 0xc2, 0xe3, 0x23, 0x20, 0xfd }).IsSuccess());
+
+        EXPECT_TRUE(
+            tester.TryDecode<DataModel::Nullable<uint64_t>>(DataModel::NullNullable, { 0x00, 0x00, 0x00, 0x00, 0x80 }).IsSuccess());
+    }
+
+    // Double-check tests, not as exhaustive, to cover all other unsigned values and get
+    // more test line coverage
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT32S_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<int64_t>(-1234, { 0x2E, 0xFB, 0xFF, 0xFF }).IsSuccess());
+    }
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT48S_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<int64_t>(-1234, { 0x2E, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF }).IsSuccess());
+    }
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT56S_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<int64_t>(-1234, { 0x2E, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT64S_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<int64_t>(-1234, { 0x2E, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }).IsSuccess());
+
+        // min/max ranges too
+        EXPECT_TRUE(
+            tester.TryDecode<int64_t>(std::numeric_limits<int64_t>::min() + 1, { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 })
+                .IsSuccess());
+        EXPECT_TRUE(
+            tester.TryDecode<int64_t>(std::numeric_limits<int64_t>::max(), { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F })
+                .IsSuccess());
+
+        EXPECT_TRUE(tester
+                        .TryDecode<DataModel::Nullable<int64_t>>(DataModel::NullNullable,
+                                                                 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 })
+                        .IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT64S_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<int64_t>(-1234, { 0x2E, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }).IsSuccess());
+
+        EXPECT_TRUE(
+            tester.TryDecode<int64_t>(std::numeric_limits<int64_t>::min(), { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 })
+                .IsSuccess());
+        EXPECT_TRUE(
+            tester.TryDecode<int64_t>(std::numeric_limits<int64_t>::min() + 1, { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 })
+                .IsSuccess());
+        EXPECT_TRUE(
+            tester.TryDecode<int64_t>(std::numeric_limits<int64_t>::max(), { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F })
+                .IsSuccess());
+    }
+}
+
+TEST(TestEmberAttributeBuffer, TestDecodeUnsignedTypes)
+{
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT8U_ATTRIBUTE_TYPE, false /* nullable */));
+
+        EXPECT_TRUE(tester.TryDecode<uint8_t>(0, { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint8_t>(123, { 123 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint8_t>(0xFD, { 0xFD }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint8_t>(255, { 0xFF }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT8U_ATTRIBUTE_TYPE, true /* nullable */));
+
+        EXPECT_TRUE(tester.TryDecode<uint8_t>(0, { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint8_t>(123, { 123 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint8_t>(0xFD, { 0xFD }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<uint8_t>>(DataModel::NullNullable, { 0xFF }).IsSuccess());
+
+        // NULL decoding should work
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<uint8_t>>(DataModel::NullNullable, { 0xFF }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT16U_ATTRIBUTE_TYPE, false /* nullable */));
+
+        EXPECT_TRUE(tester.TryDecode<uint16_t>(0, { 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint16_t>(123, { 123, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint16_t>(0xFD, { 0xFD, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint16_t>(255, { 0xFF, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint16_t>(0xABCD, { 0xCD, 0xAB }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint16_t>(0xFFFF, { 0xFF, 0xFF }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT16U_ATTRIBUTE_TYPE, true /* nullable */));
+
+        EXPECT_TRUE(tester.TryDecode<uint16_t>(0, { 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint16_t>(123, { 123, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint16_t>(0xFD, { 0xFD, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint16_t>(255, { 0xFF, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint16_t>(0xABCD, { 0xCD, 0xAB }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<uint16_t>>(DataModel::NullNullable, { 0xFF, 0xFF }).IsSuccess());
+
+        // NULL SUPPORT
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<uint16_t>>(DataModel::NullNullable, { 0xFF, 0xFF }).IsSuccess());
+    }
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT64U_ATTRIBUTE_TYPE, true /* nullable */));
+
+        EXPECT_TRUE(tester.TryDecode<uint64_t>(0, { 0, 0, 0, 0, 0, 0, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint64_t>(0x1234567, { 0x67, 0x45, 0x23, 0x01, 0, 0, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint64_t>(0xAABBCCDDEEFF1122, { 0x22, 0x11, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA }).IsSuccess());
+        EXPECT_TRUE(
+            tester.TryDecode<uint64_t>(std::numeric_limits<uint64_t>::max() - 1, { 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF })
+                .IsSuccess());
+
+        EXPECT_TRUE(tester
+                        .TryDecode<DataModel::Nullable<uint64_t>>(DataModel::NullNullable,
+                                                                  { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF })
+                        .IsSuccess());
+
+        EXPECT_TRUE(tester
+                        .TryDecode<DataModel::Nullable<uint64_t>>(DataModel::NullNullable,
+                                                                  { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF })
+                        .IsSuccess());
+    }
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT64U_ATTRIBUTE_TYPE, false /* nullable */));
+
+        // we should be able to encode the maximum value
+        EXPECT_TRUE(
+            tester.TryDecode<uint64_t>(std::numeric_limits<uint64_t>::max(), { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF })
+                .IsSuccess());
+    }
+
+    /// Odd sized integers
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT24U_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<uint32_t>(0, { 0, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint32_t>(0x123456, { 0x56, 0x34, 0x12 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint32_t>(0xFFFFFF, { 0xFF, 0xFF, 0xFF }).IsSuccess());
+    }
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT24U_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<uint32_t>(0, { 0, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint32_t>(0x123456, { 0x56, 0x34, 0x12 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<uint32_t>>(DataModel::NullNullable, { 0xFF, 0xFF, 0xFF }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<uint32_t>>(0x1234, { 0x34, 0x12, 0x00 }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT40U_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<uint64_t>(0, { 0, 0, 0, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint64_t>(0x123456, { 0x56, 0x34, 0x12, 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<uint64_t>(0x123456FFFF, { 0xFF, 0xFF, 0x56, 0x34, 0x12 }).IsSuccess());
+        EXPECT_TRUE(
+            tester.TryDecode<DataModel::Nullable<uint64_t>>(DataModel::NullNullable, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }).IsSuccess());
+    }
+
+    // Double-check tests, not as exhaustive, to cover all other unsigned values and get
+    // more test line coverage
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT32U_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<uint64_t>(0x1234, { 0x34, 0x12, 0, 0 }).IsSuccess());
+    }
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT48U_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<uint64_t>(0x1234, { 0x34, 0x12, 0, 0, 0, 0 }).IsSuccess());
+    }
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_INT56U_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<uint64_t>(0x1234, { 0x34, 0x12, 0, 0, 0, 0, 0 }).IsSuccess());
+    }
+}
+
+TEST(TestEmberAttributeBuffer, TestDecodeStrings)
+{
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_CHAR_STRING_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<CharSpan>(""_span, { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<CharSpan>("test"_span, { 4, 't', 'e', 's', 't' }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<CharSpan>("foo"_span, { 3, 'f', 'o', 'o' }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_CHAR_STRING_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<CharSpan>(""_span, { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<CharSpan>("test"_span, { 4, 't', 'e', 's', 't' }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<CharSpan>>(DataModel::NullNullable, { 0xFF }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<CharSpan>(""_span, { 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<CharSpan>("test"_span, { 4, 0, 't', 'e', 's', 't' }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<CharSpan>("foo"_span, { 3, 0, 'f', 'o', 'o' }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<CharSpan>("test"_span, { 4, 0, 't', 'e', 's', 't' }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<CharSpan>>(DataModel::NullNullable, { 0xFF, 0xFF }).IsSuccess());
+    }
+
+    const uint8_t kOctetData[] = { 1, 2, 3 };
+
+    // Binary data
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_OCTET_STRING_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<ByteSpan>(ByteSpan({}), { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<ByteSpan>(ByteSpan(kOctetData), { 3, 1, 2, 3 }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_OCTET_STRING_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<ByteSpan>(ByteSpan({}), { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<ByteSpan>(ByteSpan(kOctetData), { 3, 1, 2, 3 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<ByteSpan>>(DataModel::NullNullable, { 0xFF }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<ByteSpan>(ByteSpan({}), { 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<ByteSpan>(ByteSpan(kOctetData), { 3, 0, 1, 2, 3 }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<ByteSpan>(ByteSpan({}), { 0, 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<ByteSpan>(ByteSpan(kOctetData), { 3, 0, 1, 2, 3 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<ByteSpan>>(DataModel::NullNullable, { 0xFF, 0xFF }).IsSuccess());
+    }
+}
+
+TEST(TestEmberAttributeBuffer, TestDecodeBool)
+{
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_BOOLEAN_ATTRIBUTE_TYPE, false /* nullable */));
+
+        EXPECT_TRUE(tester.TryDecode<bool>(true, { 1 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<bool>(false, { 0 }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_BOOLEAN_ATTRIBUTE_TYPE, true /* nullable */));
+
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<bool>>(true, { 1 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<bool>>(false, { 0 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<bool>>(DataModel::NullNullable, { 0xFF }).IsSuccess());
+    }
+
+    {
+        // Boolean that is NOT nullable
+        EncodeTester tester(CreateFakeMeta(ZCL_BOOLEAN_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_EQ(tester.TryDecode<DataModel::Nullable<bool>>(DataModel::NullNullable, { 0xFF }), CHIP_ERROR_INVALID_ARGUMENT);
+        EXPECT_EQ(tester.TryDecode<bool>(true, { 0xFF }), CHIP_ERROR_INVALID_ARGUMENT);
+    }
+}
+
+TEST(TestEmberAttributeBuffer, TestDecodeFloatingPoint)
+{
+    // NOTE: to generate encoded values, you an use commands like:
+    //
+    //    python -c 'import struct; print(", ".join(["0x%X" % v for v in struct.pack("<f", -123.55)]))'
+    //    OUTPUT: 0x9A, 0x19, 0xF7, 0x42
+    //
+    //    python -c 'import struct; print(", ".join(["0x%X" % v for v in struct.pack("<f", float("nan"))]))'
+    //    OUTPUT: 0x00, 0x00, 0xC0, 0x7F
+    //
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_SINGLE_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<float>(123.55f, { 0x9A, 0x19, 0xF7, 0x42 }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_SINGLE_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<float>(123.55f, { 0x9A, 0x19, 0xF7, 0x42 }).IsSuccess());
+        EXPECT_TRUE(tester.TryDecode<DataModel::Nullable<float>>(DataModel::NullNullable, { 0, 0, 0xC0, 0x7F }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_SINGLE_ATTRIBUTE_TYPE, false /* nullable */));
+        // non-nullable float
+        EXPECT_TRUE(tester.TryDecode<float>(std::nanf("0"), { 0, 0, 0xC0, 0x7F }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_DOUBLE_ATTRIBUTE_TYPE, false /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<double>(123.55, { 0x33, 0x33, 0x33, 0x33, 0x33, 0xE3, 0x5E, 0x40 }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_DOUBLE_ATTRIBUTE_TYPE, true /* nullable */));
+        EXPECT_TRUE(tester.TryDecode<double>(123.55, { 0x33, 0x33, 0x33, 0x33, 0x33, 0xE3, 0x5E, 0x40 }).IsSuccess());
+        EXPECT_TRUE(
+            tester.TryDecode<DataModel::Nullable<double>>(123.55, { 0x33, 0x33, 0x33, 0x33, 0x33, 0xE3, 0x5E, 0x40 }).IsSuccess());
+        EXPECT_TRUE(
+            tester.TryDecode<DataModel::Nullable<double>>(DataModel::NullNullable, { 0, 0, 0, 0, 0, 0, 0xF8, 0x7F }).IsSuccess());
+    }
+
+    {
+        EncodeTester tester(CreateFakeMeta(ZCL_DOUBLE_ATTRIBUTE_TYPE, false /* nullable */));
+        // non-nullable double
+        EXPECT_TRUE(tester.TryDecode<double>(std::nan("0"), { 0, 0, 0, 0, 0, 0, 0xF8, 0x7F }).IsSuccess());
+    }
+}

From 65c63813d74faa6570d8bc922d28323c44bafd63 Mon Sep 17 00:00:00 2001
From: Junior Martinez <67972863+jmartinez-silabs@users.noreply.github.com>
Date: Thu, 13 Mar 2025 17:04:12 -0400
Subject: [PATCH 2/2] Fixup cherry-pick due to deleted header still included in
 files that now don't exist in csa

---
 src/app/AttributePathExpandIterator-Ember.cpp  | 3 +--
 src/app/util/ember-compatibility-functions.cpp | 7 +++----
 2 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/src/app/AttributePathExpandIterator-Ember.cpp b/src/app/AttributePathExpandIterator-Ember.cpp
index 1149f0c702..b1d6cb2a12 100644
--- a/src/app/AttributePathExpandIterator-Ember.cpp
+++ b/src/app/AttributePathExpandIterator-Ember.cpp
@@ -22,7 +22,6 @@
 #include <app/ConcreteAttributePath.h>
 #include <app/EventManagement.h>
 #include <app/GlobalAttributes.h>
-#include <app/util/att-storage.h>
 #include <app/util/endpoint-config-api.h>
 #include <lib/core/CHIPCore.h>
 #include <lib/core/TLVDebug.h>
@@ -98,7 +97,7 @@ void AttributePathExpandIteratorEmber::PrepareClusterIndexRange(const AttributeP
     }
     else
     {
-        mClusterIndex = emberAfClusterIndex(aEndpointId, aAttributePath.mClusterId, CLUSTER_MASK_SERVER);
+        mClusterIndex = emberAfClusterIndex(aEndpointId, aAttributePath.mClusterId, MATTER_CLUSTER_FLAG_SERVER);
         // If the given cluster id does not exist on the given endpoint, it will return uint8(0xFF), then endClusterIndex
         // will be 0, means we should iterate a null cluster set (skip it).
         mEndClusterIndex = static_cast<uint8_t>(mClusterIndex + 1);
diff --git a/src/app/util/ember-compatibility-functions.cpp b/src/app/util/ember-compatibility-functions.cpp
index 0ea63cba30..af336dd39f 100644
--- a/src/app/util/ember-compatibility-functions.cpp
+++ b/src/app/util/ember-compatibility-functions.cpp
@@ -27,7 +27,6 @@
 #include <app/RequiredPrivilege.h>
 #include <app/reporting/Engine.h>
 #include <app/reporting/reporting.h>
-#include <app/util/att-storage.h>
 #include <app/util/attribute-storage-detail.h>
 #include <app/util/attribute-storage-null-handling.h>
 #include <app/util/attribute-storage.h>
@@ -95,7 +94,7 @@ Protocols::InteractionModel::Status ServerClusterCommandExists(const ConcreteCom
         return Status::UnsupportedEndpoint;
     }
 
-    const EmberAfCluster * cluster = emberAfFindClusterInType(type, aCommandPath.mClusterId, CLUSTER_MASK_SERVER);
+    const EmberAfCluster * cluster = emberAfFindClusterInType(type, aCommandPath.mClusterId, MATTER_CLUSTER_FLAG_SERVER);
     if (cluster == nullptr)
     {
         return Status::UnsupportedCluster;
@@ -223,7 +222,7 @@ Protocols::InteractionModel::Status UnsupportedAttributeStatus(const ConcreteAtt
         return Status::UnsupportedEndpoint;
     }
 
-    const EmberAfCluster * cluster = emberAfFindClusterInType(type, aPath.mClusterId, CLUSTER_MASK_SERVER);
+    const EmberAfCluster * cluster = emberAfFindClusterInType(type, aPath.mClusterId, MATTER_CLUSTER_FLAG_SERVER);
     if (cluster == nullptr)
     {
         return Status::UnsupportedCluster;
@@ -800,7 +799,7 @@ Protocols::InteractionModel::Status CheckEventSupportStatus(const ConcreteEventP
         return Status::UnsupportedEndpoint;
     }
 
-    const EmberAfCluster * cluster = emberAfFindClusterInType(type, aPath.mClusterId, CLUSTER_MASK_SERVER);
+    const EmberAfCluster * cluster = emberAfFindClusterInType(type, aPath.mClusterId, MATTER_CLUSTER_FLAG_SERVER);
     if (cluster == nullptr)
     {
         return Status::UnsupportedCluster;