[ESP32] Fix few attributes with fixed quality in DeviceInfoProvider (#32893)

* [ESP32] Fix few attributes with fixed quality in DeviceInfoProvider

Fixed labels, supported locales, supported calendar types were being
read from the nvs(flash) and during OTA its a hassle if one wants to
upgrade these values. Added few APIs to set the data for these
attributes in ESP32DeviceInfoProvider.

* Restyled by clang-format

* Restyled by prettier-markdown

* fix the lint errors

* Add back the original Device info provider which reads from the nvs

Add StaticESP32DeviceInfoProvider along with APIs to set data

Remove changes from example and add a guide along with usage

---------

Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/docs/guides/esp32/README.md b/docs/guides/esp32/README.md
index e487a58..443058a 100644
--- a/docs/guides/esp32/README.md
+++ b/docs/guides/esp32/README.md
@@ -18,3 +18,4 @@
 -   [Matter OTA](ota.md)
 -   [Generating and Using ESP Secure Cert Partition](secure_cert_partition.md)
 -   [BLE Settings](ble_settings.md)
+-   [Providers](providers.md)
diff --git a/docs/guides/esp32/factory_data.md b/docs/guides/esp32/factory_data.md
index 9ea2e12..638d3aa 100644
--- a/docs/guides/esp32/factory_data.md
+++ b/docs/guides/esp32/factory_data.md
@@ -44,13 +44,9 @@
     -   Serial Number
     -   Unique identifier
 
--   Device information
-    -   Fixed Labels
-    -   Supported locales
-    -   Supported calendar types
-    -   Supported modes
-        -   Note: As per spec at max size of label should be 64 and `\0` will be
-            added at the end.
+-   Supported modes
+    -   Note: As per spec at max size of label should be 64 and `\0` will be
+        added at the end.
 
 ### Configuration Options
 
diff --git a/docs/guides/esp32/providers.md b/docs/guides/esp32/providers.md
new file mode 100644
index 0000000..59b91b6
--- /dev/null
+++ b/docs/guides/esp32/providers.md
@@ -0,0 +1,76 @@
+## Providers Implemented for ESP32 Platform
+
+The ESP32 platform has implemented several providers that can be used with data
+stored in the factory or by setting fixed data.
+
+Below are the providers that have been implemented:
+
+-   [Commissionable Data Provider](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32FactoryDataProvider.h#L47)
+    This provider reads the discriminator and setup pincode related parameters
+    from the factory partition.
+-   [Device Attestation Credentials Provider](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32FactoryDataProvider.h#L56)
+    This provider manages the attestation data.
+-   [Device Instance Info Provider](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32FactoryDataProvider.h#L86)
+    This provider reads basic device information from the factory partition.
+-   [Device Info Provider](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32DeviceInfoProvider.h#L31)
+    This provider provides fixed labels, supported calendar types, and supported
+    locales from the factory partition.
+-   [Supported Modes](https://github.com/project-chip/connectedhomeip/blob/master/examples/platform/esp32/mode-support/static-supported-modes-manager.h#L28)
+    This provider offers the supported modes for the mode-select cluster.
+
+More information can be found in the [factory data guide](factory_data.md).
+
+### Device Info Provider
+
+Currently, there are two implementations for this provider:
+
+1. [Reads data stored in the factory partition](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32FactoryDataProvider.h#L56)
+   _(This will be deprecated in the future)_
+2. [Provides APIs to set fixed data that gets read later](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/StaticESP32DeviceInfoProvider.h)
+
+-   New products should use the `StaticESP32DeviceInfoProvider`. Utilize the
+    `Set...()` APIs to set the fixed data.
+-   Existing products using the first implementation can continue to use it if
+    they do not wish to change the data.
+-   For products using the first implementation and wanting to change the fixed
+    data via OTA, they should switch to the second implementation in the OTA
+    image and use the `Set...()` APIs to set the fixed data.
+
+#### Example:
+
+```cpp
+#include <platform/ESP32/StaticESP32FactoryDataProvider.h>
+
+DeviceLayer::StaticESP32DeviceInfoProvider deviceInfoProvider;
+
+// Define array for Supported Calendar Types
+using namespace chip::app::Clusters::TimeFormatLocalization::CalendarTypeEnum;
+CalendarTypeEnum supportedCalendarTypes[] = {
+    CalendarTypeEnum::kGregorian, CalendarTypeEnum::kCoptic,
+    CalendarTypeEnum::kEthiopian, CalendarTypeEnum::kChinese,
+};
+
+// Define array for Supported Locales
+const char* supportedLocales[] = {
+    "en-US",
+    "en-EU",
+};
+
+// Define array for Fixed labels { EndpointId, Label, Value }
+struct StaticESP32DeviceInfoProvider::FixedLabelEntry fixedLabels[] = {
+    { 0, "Room", "Bedroom 2" },
+    { 0, "Orientation", "North" },
+    { 0, "Direction", "Up" },
+};
+
+Span<CalendarTypeEnum> sSupportedCalendarTypes(supportedCalendarTypes);
+Span<const char*> sSupportedLocales(supportedLocales);
+Span<StaticESP32DeviceInfoProvider::FixedLabelEntry> sFixedLabels(fixedLabels);
+
+{
+    deviceInfoProvider.SetSupportedLocales(sSupportedLocales);
+    deviceInfoProvider.SetSupportedCalendarTypes(sSupportedCalendarTypes);
+    deviceInfoProvider.SetFixedLabels(sFixedLabels);
+    DeviceLayer::SetDeviceInfoProvider(&deviceInfoProvider);
+}
+```
diff --git a/scripts/tools/generate_esp32_chip_factory_bin.py b/scripts/tools/generate_esp32_chip_factory_bin.py
index a4ccce5..ff00dae 100755
--- a/scripts/tools/generate_esp32_chip_factory_bin.py
+++ b/scripts/tools/generate_esp32_chip_factory_bin.py
@@ -18,15 +18,12 @@
 
 import argparse
 import base64
-import enum
 import logging
 import os
 import sys
 from types import SimpleNamespace
 
 import cryptography.x509
-from bitarray import bitarray
-from bitarray.util import ba2int
 from esp_secure_cert.tlv_format import generate_partition_ds, generate_partition_no_ds, tlv_priv_key_t, tlv_priv_key_type_t
 
 CHIP_TOPDIR = os.path.dirname(os.path.realpath(__file__))[:-len(os.path.join('scripts', 'tools'))]
@@ -152,52 +149,9 @@
         'encoding': 'hex2bin',
         'value': None,
     },
-    # DeviceInfoProvider
-    'cal-types': {
-        'type': 'data',
-        'encoding': 'u32',
-        'value': None,
-    },
-    'locale-sz': {
-        'type': 'data',
-        'encoding': 'u32',
-        'value': None,
-    },
-
-    # Other device info provider keys are dynamically generated
-    # in the respective functions.
 }
 
 
-class CalendarTypes(enum.Enum):
-    Buddhist = 0
-    Chinese = 1
-    Coptic = 2
-    Ethiopian = 3
-    Gregorian = 4
-    Hebrew = 5
-    Indian = 6
-    Islamic = 7
-    Japanese = 8
-    Korean = 9
-    Persian = 10
-    Taiwanese = 11
-
-
-# Supported Calendar types is stored as a bit array in one uint32_t.
-def calendar_types_to_uint32(calendar_types):
-    result = bitarray(32, endian='little')
-    result.setall(0)
-    for calendar_type in calendar_types:
-        try:
-            result[CalendarTypes[calendar_type].value] = 1
-        except KeyError:
-            logging.error('Unknown calendar type: %s', calendar_type)
-            logging.error('Supported calendar types: %s', ', '.join(CalendarTypes.__members__))
-            sys.exit(1)
-    return ba2int(result)
-
-
 def ishex(s):
     try:
         _ = int(s, 16)
@@ -205,31 +159,6 @@
     except ValueError:
         return False
 
-# get_fixed_label_dict() converts the list of strings to per endpoint dictionaries.
-# example input  : ['0/orientation/up', '1/orientation/down', '2/orientation/down']
-# example output : {'0': [{'orientation': 'up'}], '1': [{'orientation': 'down'}], '2': [{'orientation': 'down'}]}
-
-
-def get_fixed_label_dict(fixed_labels):
-    fl_dict = {}
-    for fl in fixed_labels:
-        _l = fl.split('/')
-
-        if len(_l) != 3:
-            logging.error('Invalid fixed label: %s', fl)
-            sys.exit(1)
-
-        if not (ishex(_l[0]) and (len(_l[1]) > 0 and len(_l[1]) < 16) and (len(_l[2]) > 0 and len(_l[2]) < 16)):
-            logging.error('Invalid fixed label: %s', fl)
-            sys.exit(1)
-
-        if _l[0] not in fl_dict.keys():
-            fl_dict[_l[0]] = list()
-
-        fl_dict[_l[0]].append({_l[1]: _l[2]})
-
-    return fl_dict
-
 # get_supported_modes_dict() converts the list of strings to per endpoint dictionaries.
 # example with semantic tags
 # input  : ['0/label1/1/"1\0x8000, 2\0x8000" 1/label2/1/"1\0x8000, 2\0x8000"']
@@ -373,52 +302,6 @@
     if args.hw_ver_str:
         FACTORY_DATA['hw-ver-str']['value'] = args.hw_ver_str
 
-    if args.calendar_types:
-        FACTORY_DATA['cal-types']['value'] = calendar_types_to_uint32(args.calendar_types)
-
-    # Supported locale is stored as multiple entries, key format: "locale/<index>, example key: "locale/0"
-    if args.locales:
-        FACTORY_DATA['locale-sz']['value'] = len(args.locales)
-
-        for i in range(len(args.locales)):
-            _locale = {
-                'type': 'data',
-                'encoding': 'string',
-                'value': args.locales[i]
-            }
-            FACTORY_DATA.update({'locale/{:x}'.format(i): _locale})
-
-    # Each endpoint can contains the fixed lables
-    #  - fl-sz/<index>     : number of fixed labels for the endpoint
-    #  - fl-k/<ep>/<index> : fixed label key for the endpoint and index
-    #  - fl-v/<ep>/<index> : fixed label value for the endpoint and index
-    if args.fixed_labels:
-        dict = get_fixed_label_dict(args.fixed_labels)
-        for key in dict.keys():
-            _sz = {
-                'type': 'data',
-                'encoding': 'u32',
-                'value': len(dict[key])
-            }
-            FACTORY_DATA.update({'fl-sz/{:x}'.format(int(key)): _sz})
-
-            for i in range(len(dict[key])):
-                entry = dict[key][i]
-
-                _label_key = {
-                    'type': 'data',
-                    'encoding': 'string',
-                    'value': list(entry.keys())[0]
-                }
-                _label_value = {
-                    'type': 'data',
-                    'encoding': 'string',
-                    'value': list(entry.values())[0]
-                }
-
-                FACTORY_DATA.update({'fl-k/{:x}/{:x}'.format(int(key), i): _label_key})
-                FACTORY_DATA.update({'fl-v/{:x}/{:x}'.format(int(key), i): _label_value})
-
     # SupportedModes are stored as multiple entries
     #  - sm-sz/<ep>                 : number of supported modes for the endpoint
     #  - sm-label/<ep>/<index>      : supported modes label key for the endpoint and index
@@ -584,13 +467,6 @@
                         help=('128-bit unique identifier for generating rotating device identifier, '
                               'provide 32-byte hex string, e.g. "1234567890abcdef1234567890abcdef"'))
 
-    # These will be used by DeviceInfoProvider
-    parser.add_argument('--calendar-types', nargs='+',
-                        help=('List of supported calendar types.\nSupported Calendar Types: Buddhist, Chinese, Coptic, Ethiopian, '
-                              'Gregorian, Hebrew, Indian, Islamic, Japanese, Korean, Persian, Taiwanese'))
-    parser.add_argument('--locales', nargs='+', help='List of supported locales, Language Tag as defined by BCP47, eg. en-US en-GB')
-    parser.add_argument('--fixed-labels', nargs='+',
-                        help='List of fixed labels, eg: "0/orientation/up" "1/orientation/down" "2/orientation/down"')
     parser.add_argument('--supported-modes', type=str, nargs='+', required=False,
                         help='List of supported modes, eg: mode1/label1/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode"  mode2/label2/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode"  mode3/label3/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode"')
 
diff --git a/src/platform/ESP32/BUILD.gn b/src/platform/ESP32/BUILD.gn
index b1d3650..167d0eb 100644
--- a/src/platform/ESP32/BUILD.gn
+++ b/src/platform/ESP32/BUILD.gn
@@ -185,6 +185,8 @@
     sources += [
       "ESP32DeviceInfoProvider.cpp",
       "ESP32DeviceInfoProvider.h",
+      "StaticESP32DeviceInfoProvider.cpp",
+      "StaticESP32DeviceInfoProvider.h",
     ]
   }
 
diff --git a/src/platform/ESP32/StaticESP32DeviceInfoProvider.cpp b/src/platform/ESP32/StaticESP32DeviceInfoProvider.cpp
new file mode 100644
index 0000000..d65bdf8
--- /dev/null
+++ b/src/platform/ESP32/StaticESP32DeviceInfoProvider.cpp
@@ -0,0 +1,122 @@
+/*
+
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    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 <lib/support/CodeUtils.h>
+#include <platform/ESP32/StaticESP32DeviceInfoProvider.h>
+
+namespace chip {
+namespace DeviceLayer {
+
+StaticESP32DeviceInfoProvider & StaticESP32DeviceInfoProvider::GetDefaultInstance(void)
+{
+    static StaticESP32DeviceInfoProvider sInstance;
+    return sInstance;
+}
+
+DeviceInfoProvider::FixedLabelIterator * StaticESP32DeviceInfoProvider::IterateFixedLabel(EndpointId endpoint)
+{
+    return chip::Platform::New<StaticFixedLabelIteratorImpl>(endpoint, mFixedLabels);
+}
+
+StaticESP32DeviceInfoProvider::StaticFixedLabelIteratorImpl::StaticFixedLabelIteratorImpl(EndpointId endpoint,
+                                                                                          const Span<FixedLabelEntry> & labels)
+{
+    mEndpoint = endpoint;
+    mLabels   = labels;
+    mIndex    = 0;
+}
+
+size_t StaticESP32DeviceInfoProvider::StaticFixedLabelIteratorImpl::Count()
+{
+    size_t count = 0;
+    for (size_t i = 0; i < mLabels.size(); i++)
+    {
+        const FixedLabelEntry & entry = mLabels.data()[i];
+
+        if (entry.endpointId == mEndpoint)
+        {
+            count++;
+        }
+    }
+    return count;
+}
+
+bool StaticESP32DeviceInfoProvider::StaticFixedLabelIteratorImpl::Next(FixedLabelType & output)
+{
+    ChipLogDetail(DeviceLayer, "Get the fixed label with index:%u at endpoint:%d", static_cast<unsigned>(mIndex), mEndpoint);
+
+    while (mIndex < mLabels.size())
+    {
+        const FixedLabelEntry & entry = mLabels.data()[mIndex++];
+        if (entry.endpointId == mEndpoint)
+        {
+            output.label = entry.label;
+            output.value = entry.value;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+DeviceInfoProvider::SupportedLocalesIterator * StaticESP32DeviceInfoProvider::IterateSupportedLocales()
+{
+    return chip::Platform::New<StaticSupportedLocalesIteratorImpl>(mSupportedLocales);
+}
+
+StaticESP32DeviceInfoProvider::StaticSupportedLocalesIteratorImpl::StaticSupportedLocalesIteratorImpl(
+    const Span<CharSpan> & locales)
+{
+    mLocales = locales;
+}
+
+size_t StaticESP32DeviceInfoProvider::StaticSupportedLocalesIteratorImpl::Count()
+{
+    return mLocales.empty() ? 0 : mLocales.size();
+}
+
+bool StaticESP32DeviceInfoProvider::StaticSupportedLocalesIteratorImpl::Next(CharSpan & output)
+{
+    VerifyOrReturnValue(mIndex < mLocales.size(), false);
+    output = mLocales.data()[mIndex++];
+    return true;
+}
+
+DeviceInfoProvider::SupportedCalendarTypesIterator * StaticESP32DeviceInfoProvider::IterateSupportedCalendarTypes()
+{
+    return chip::Platform::New<StaticSupportedCalendarTypesIteratorImpl>(mSupportedCalendarTypes);
+}
+
+StaticESP32DeviceInfoProvider::StaticSupportedCalendarTypesIteratorImpl::StaticSupportedCalendarTypesIteratorImpl(
+    const Span<CalendarType> & calendarTypes)
+{
+    mCalendarTypes = calendarTypes;
+}
+
+size_t StaticESP32DeviceInfoProvider::StaticSupportedCalendarTypesIteratorImpl::Count()
+{
+    return mCalendarTypes.empty() ? 0 : mCalendarTypes.size();
+}
+
+bool StaticESP32DeviceInfoProvider::StaticSupportedCalendarTypesIteratorImpl::Next(CalendarType & output)
+{
+    VerifyOrReturnValue(mIndex < mCalendarTypes.size(), false);
+    output = mCalendarTypes.data()[mIndex++];
+    return true;
+}
+
+} // namespace DeviceLayer
+} // namespace chip
diff --git a/src/platform/ESP32/StaticESP32DeviceInfoProvider.h b/src/platform/ESP32/StaticESP32DeviceInfoProvider.h
new file mode 100644
index 0000000..860110c
--- /dev/null
+++ b/src/platform/ESP32/StaticESP32DeviceInfoProvider.h
@@ -0,0 +1,141 @@
+/*
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+#pragma once
+
+#include <platform/ESP32/ESP32DeviceInfoProvider.h>
+
+namespace chip {
+namespace DeviceLayer {
+
+class StaticESP32DeviceInfoProvider : public ESP32DeviceInfoProvider
+{
+public:
+    StaticESP32DeviceInfoProvider() = default;
+    ~StaticESP32DeviceInfoProvider() override {}
+
+    // Iterators
+    FixedLabelIterator * IterateFixedLabel(EndpointId endpoint);
+    SupportedLocalesIterator * IterateSupportedLocales();
+    SupportedCalendarTypesIterator * IterateSupportedCalendarTypes();
+
+    static StaticESP32DeviceInfoProvider & GetDefaultInstance();
+
+    struct FixedLabelEntry
+    {
+        EndpointId endpointId;
+        CharSpan label;
+        CharSpan value;
+    };
+
+    /**
+     * @brief API to set the supported calendar types
+     *
+     * @param[in] supportedCalendarTypes Span of type chip::app::Clusters::TimeFormatLocalization::CalendarTypeEnum
+     *              containing the supported calendar types. The underlying data must remain allocated throughout
+     *              the lifetime of the device, as the API does not make a copy.
+     *
+     * @return CHIP_ERROR indicating the success or failure of the operation.
+     */
+    CHIP_ERROR SetSupportedCalendarTypes(const Span<CalendarType> & supportedCalendarTypes)
+    {
+        VerifyOrReturnError(!supportedCalendarTypes.empty(), CHIP_ERROR_INVALID_ARGUMENT);
+        mSupportedCalendarTypes = supportedCalendarTypes;
+        return CHIP_NO_ERROR;
+    }
+
+    /**
+     * @brief API to set the supported Locales
+     *
+     * @param[in] supportedLocales Span of type chip::CharSpan containing the supported locales.
+     *              The underlying data must remain allocated throughout the lifetime of the device,
+     *              as the API does not make a copy.
+     *
+     * @return CHIP_ERROR indicating the success or failure of the operation.
+     */
+    CHIP_ERROR SetSupportedLocales(const Span<CharSpan> & supportedLocales)
+    {
+        VerifyOrReturnError(!supportedLocales.empty(), CHIP_ERROR_INVALID_ARGUMENT);
+        mSupportedLocales = supportedLocales;
+        return CHIP_NO_ERROR;
+    }
+
+    /**
+     * @brief API to set the fixed labels
+     *
+     * @param[in] fixedLabels Span of type chip::DeviceLayer::StaticESP32DeviceInfoProvider::FixedLabelEntry
+     *              containing the fixed labels for supported endpoints.
+     *              The underlying data must remain allocated throughout the lifetime of the device,
+     *              as the API does not make a copy.
+     *
+     * @return CHIP_ERROR indicating the success or failure of the operation.
+     */
+    CHIP_ERROR SetFixedLabels(const Span<FixedLabelEntry> & supportedFixedLabels)
+    {
+        VerifyOrReturnError(!supportedFixedLabels.empty(), CHIP_ERROR_INVALID_ARGUMENT);
+        mFixedLabels = supportedFixedLabels;
+        return CHIP_NO_ERROR;
+    }
+
+protected:
+    class StaticFixedLabelIteratorImpl : public FixedLabelIterator
+    {
+    public:
+        StaticFixedLabelIteratorImpl(EndpointId endpoint, const Span<FixedLabelEntry> & labels);
+        size_t Count();
+        bool Next(FixedLabelType & output);
+        void Release() { chip::Platform::Delete(this); }
+
+    private:
+        EndpointId mEndpoint = 0;
+        size_t mIndex        = 0;
+        Span<FixedLabelEntry> mLabels;
+    };
+
+    class StaticSupportedLocalesIteratorImpl : public SupportedLocalesIterator
+    {
+    public:
+        StaticSupportedLocalesIteratorImpl(const Span<CharSpan> & locales);
+        size_t Count();
+        bool Next(CharSpan & output);
+        void Release() { chip::Platform::Delete(this); }
+
+    private:
+        size_t mIndex = 0;
+        Span<CharSpan> mLocales;
+    };
+
+    class StaticSupportedCalendarTypesIteratorImpl : public SupportedCalendarTypesIterator
+    {
+    public:
+        StaticSupportedCalendarTypesIteratorImpl(const Span<CalendarType> & calendarTypes);
+        size_t Count();
+        bool Next(CalendarType & output);
+        void Release() { chip::Platform::Delete(this); }
+
+    private:
+        size_t mIndex = 0;
+        Span<CalendarType> mCalendarTypes;
+    };
+
+private:
+    Span<CalendarType> mSupportedCalendarTypes;
+    Span<CharSpan> mSupportedLocales;
+    Span<FixedLabelEntry> mFixedLabels;
+};
+
+} // namespace DeviceLayer
+} // namespace chip