Set `dataModelProvider` as a required argument for `Controller::FactoryInitParams` (#36613)

* Add dataModelProvider to Factor init parameters for the codegen data model provider

* make setting the same data model on interaction model provider a noop

* Fix compile

* Fix links

* Restyle

* Restyled by prettier-markdown

* Fix text

* Fix some deps

* Restyled by gn

* Remove dependencies/auto-init of codegen data model in the interaction model. We now fully expect all applications to provide a data model provider

* Fix include

* Update src/app/InteractionModelEngine.cpp

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>

* Address some code review comments

* Fix unit tests

* Restyle

* Add more comments about why we have very intentional ordering

---------

Co-authored-by: Andrei Litvin <andreilitvin@google.com>
Co-authored-by: Restyled.io <commits@restyled.io>
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
diff --git a/docs/upgrading.md b/docs/upgrading.md
index de9ba38..517a83f 100644
--- a/docs/upgrading.md
+++ b/docs/upgrading.md
@@ -93,11 +93,12 @@
 -   `chip::app::GetAttributeAccessOverride` replaced by
     `chip::app::AttributeAccessInterfaceRegistry::Instance().Get`
 
-### `ServerInitParams::dataModelProvider` in `Server::Init`
+### `ServerInitParams::dataModelProvider` in `Server::Init` and `FactoryInitParams`
 
-Server initialization requires a set data model provider to work rather than
-auto-initializing ember-compatible code-generated data models.
+Server and controller initialization require a set data model provider to work
+rather than auto-initializing ember-compatible code-generated data models.
 
 To preserve `codegen/zap` generated logic, use
 `CodegenDataModelProviderInstance` (see changes in
-<https://github.com/project-chip/connectedhomeip/pull/36558>).
+[36558](https://github.com/project-chip/connectedhomeip/pull/36558) and
+[36613](https://github.com/project-chip/connectedhomeip/pull/36613) ).
diff --git a/examples/chip-tool/commands/common/CHIPCommand.cpp b/examples/chip-tool/commands/common/CHIPCommand.cpp
index 4f14cb8..ffa873b 100644
--- a/examples/chip-tool/commands/common/CHIPCommand.cpp
+++ b/examples/chip-tool/commands/common/CHIPCommand.cpp
@@ -18,6 +18,7 @@
 
 #include "CHIPCommand.h"
 
+#include <app/codegen-data-model-provider/Instance.h>
 #include <commands/icd/ICDCommand.h>
 #include <controller/CHIPDeviceControllerFactory.h>
 #include <credentials/attestation_verifier/FileAttestationTrustStore.h>
@@ -137,6 +138,7 @@
     factoryInitParams.opCertStore              = &mOpCertStore;
     factoryInitParams.enableServerInteractions = NeedsOperationalAdvertising();
     factoryInitParams.sessionKeystore          = &sSessionKeystore;
+    factoryInitParams.dataModelProvider        = chip::app::CodegenDataModelProviderInstance();
 
     // Init group data provider that will be used for all group keys and IPKs for the
     // chip-tool-configured fabrics. This is OK to do once since the fabric tables
diff --git a/examples/fabric-admin/commands/common/CHIPCommand.cpp b/examples/fabric-admin/commands/common/CHIPCommand.cpp
index e7f4071..1f761fc 100644
--- a/examples/fabric-admin/commands/common/CHIPCommand.cpp
+++ b/examples/fabric-admin/commands/common/CHIPCommand.cpp
@@ -19,6 +19,8 @@
 #include "CHIPCommand.h"
 
 #include "IcdManager.h"
+
+#include <app/codegen-data-model-provider/Instance.h>
 #include <controller/CHIPDeviceControllerFactory.h>
 #include <credentials/attestation_verifier/FileAttestationTrustStore.h>
 #include <device_manager/PairingManager.h>
@@ -121,6 +123,7 @@
     factoryInitParams.opCertStore              = &mOpCertStore;
     factoryInitParams.enableServerInteractions = NeedsOperationalAdvertising();
     factoryInitParams.sessionKeystore          = &sSessionKeystore;
+    factoryInitParams.dataModelProvider        = chip::app::CodegenDataModelProviderInstance();
 
     // Init group data provider that will be used for all group keys and IPKs for the
     // fabric-admin-configured fabrics. This is OK to do once since the fabric tables
diff --git a/examples/platform/linux/BUILD.gn b/examples/platform/linux/BUILD.gn
index 58536d5..1d4f191 100644
--- a/examples/platform/linux/BUILD.gn
+++ b/examples/platform/linux/BUILD.gn
@@ -164,7 +164,10 @@
     "${chip_root}/src/controller:controller",
     "${chip_root}/src/lib",
   ]
-  deps = [ "${chip_root}/src/app/server" ]
+  deps = [
+    "${chip_root}/src/app/codegen-data-model-provider:instance-header",
+    "${chip_root}/src/app/server",
+  ]
 
   if (chip_enable_transport_trace) {
     deps += [ "${chip_root}/examples/common/tracing:trace_handlers" ]
diff --git a/examples/platform/linux/CommissionerMain.cpp b/examples/platform/linux/CommissionerMain.cpp
index 300d246..54ad104 100644
--- a/examples/platform/linux/CommissionerMain.cpp
+++ b/examples/platform/linux/CommissionerMain.cpp
@@ -24,6 +24,7 @@
 #if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
 
 #include <app/clusters/network-commissioning/network-commissioning.h>
+#include <app/codegen-data-model-provider/Instance.h>
 #include <app/server/Dnssd.h>
 #include <app/server/OnboardingCodesUtil.h>
 #include <app/server/Server.h>
@@ -134,6 +135,7 @@
     factoryParams.fabricIndependentStorage = &gServerStorage;
     factoryParams.fabricTable              = &Server::GetInstance().GetFabricTable();
     factoryParams.sessionKeystore          = &gSessionKeystore;
+    factoryParams.dataModelProvider        = chip::app::CodegenDataModelProviderInstance();
 
     gGroupDataProvider.SetStorageDelegate(&gServerStorage);
     gGroupDataProvider.SetSessionKeystore(factoryParams.sessionKeystore);
diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn
index 26f9515..2e017dd 100644
--- a/src/app/BUILD.gn
+++ b/src/app/BUILD.gn
@@ -206,10 +206,6 @@
     "${chip_root}/src/app:global-attributes",
   ]
 
-  # Temporary dependency: codegen data provider instance should be provided
-  # by the application
-  deps += [ "${chip_root}/src/app/codegen-data-model-provider:instance-header" ]
-
   public_deps = [
     ":app_config",
     ":command-handler-impl",
diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp
index 0dd4df6..9b76dd3 100644
--- a/src/app/InteractionModelEngine.cpp
+++ b/src/app/InteractionModelEngine.cpp
@@ -50,10 +50,6 @@
 #include <lib/support/FibonacciUtils.h>
 #include <protocols/interaction_model/StatusCode.h>
 
-// TODO: defaulting to codegen should eventually be an application choice and not
-//       hard-coded in the interaction model
-#include <app/codegen-data-model-provider/Instance.h>
-
 namespace chip {
 namespace app {
 namespace {
@@ -1798,9 +1794,15 @@
 
 DataModel::Provider * InteractionModelEngine::SetDataModelProvider(DataModel::Provider * model)
 {
-    // Alternting data model should not be done while IM is actively handling requests.
+    // Altering data model should not be done while IM is actively handling requests.
     VerifyOrDie(mReadHandlers.begin() == mReadHandlers.end());
 
+    if (model == mDataModelProvider)
+    {
+        // no-op, just return
+        return model;
+    }
+
     DataModel::Provider * oldModel = mDataModelProvider;
     if (oldModel != nullptr)
     {
@@ -1830,14 +1832,11 @@
     return oldModel;
 }
 
-DataModel::Provider * InteractionModelEngine::GetDataModelProvider()
+DataModel::Provider * InteractionModelEngine::GetDataModelProvider() const
 {
-    if (mDataModelProvider == nullptr)
-    {
-        // These should be called within the CHIP processing loop.
-        assertChipStackLockedByCurrentThread();
-        SetDataModelProvider(CodegenDataModelProviderInstance());
-    }
+    // These should be called within the CHIP processing loop.
+    assertChipStackLockedByCurrentThread();
+
     return mDataModelProvider;
 }
 
diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h
index ef4b041..3fa3791 100644
--- a/src/app/InteractionModelEngine.h
+++ b/src/app/InteractionModelEngine.h
@@ -404,10 +404,7 @@
     }
 #endif
 
-    // Temporarily NOT const because the data model provider will be auto-set
-    // to codegen on first usage. This behaviour will be changed once each
-    // application must explicitly set the data model provider.
-    DataModel::Provider * GetDataModelProvider();
+    DataModel::Provider * GetDataModelProvider() const;
 
     // MUST NOT be used while the interaction model engine is running as interaction
     // model functionality (e.g. active reads/writes/subscriptions) rely on data model
diff --git a/src/app/SubscriptionResumptionSessionEstablisher.cpp b/src/app/SubscriptionResumptionSessionEstablisher.cpp
index e6f1ba0..f9ea25c 100644
--- a/src/app/SubscriptionResumptionSessionEstablisher.cpp
+++ b/src/app/SubscriptionResumptionSessionEstablisher.cpp
@@ -17,7 +17,6 @@
 
 #include <app/InteractionModelEngine.h>
 #include <app/SubscriptionResumptionSessionEstablisher.h>
-#include <app/codegen-data-model-provider/Instance.h>
 
 namespace chip {
 namespace app {
diff --git a/src/app/tests/TestAclEvent.cpp b/src/app/tests/TestAclEvent.cpp
index bbded46..13b3e36 100644
--- a/src/app/tests/TestAclEvent.cpp
+++ b/src/app/tests/TestAclEvent.cpp
@@ -24,6 +24,7 @@
 #include <app/InteractionModelEngine.h>
 #include <app/MessageDef/AttributeReportIBs.h>
 #include <app/MessageDef/EventDataIB.h>
+#include <app/codegen-data-model-provider/Instance.h>
 #include <app/reporting/tests/MockReportScheduler.h>
 #include <app/tests/AppTestContext.h>
 #include <app/tests/test-interaction-model-api.h>
@@ -217,6 +218,7 @@
 
     auto * engine = chip::app::InteractionModelEngine::GetInstance();
 
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     EXPECT_EQ(engine->Init(&GetExchangeManager(), &GetFabricTable(), app::reporting::GetDefaultReportScheduler()), CHIP_NO_ERROR);
 
     // A custom AccessControl::Delegate has been installed that grants privilege to any cluster except the test cluster.
diff --git a/src/app/tests/TestInteractionModelEngine.cpp b/src/app/tests/TestInteractionModelEngine.cpp
index 0a4a8bd..acc19c1 100644
--- a/src/app/tests/TestInteractionModelEngine.cpp
+++ b/src/app/tests/TestInteractionModelEngine.cpp
@@ -88,6 +88,7 @@
 
     InteractionModelEngine * engine = InteractionModelEngine::GetInstance();
 
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     EXPECT_EQ(engine->Init(&GetExchangeManager(), &GetFabricTable(), app::reporting::GetDefaultReportScheduler()), CHIP_NO_ERROR);
 
     SingleLinkedListNode<AttributePathParams> * attributePathParamsList = nullptr;
@@ -123,6 +124,7 @@
 
     InteractionModelEngine * engine = InteractionModelEngine::GetInstance();
 
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     EXPECT_EQ(CHIP_NO_ERROR, engine->Init(&GetExchangeManager(), &GetFabricTable(), app::reporting::GetDefaultReportScheduler()));
 
     SingleLinkedListNode<AttributePathParams> * attributePathParamsList = nullptr;
@@ -259,6 +261,7 @@
     ASSERT_TRUE(exchangeCtx1);
 
     // InteractionModelEngine init
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     EXPECT_EQ(CHIP_NO_ERROR, engine->Init(&GetExchangeManager(), &GetFabricTable(), reporting::GetDefaultReportScheduler()));
 
     // Verify that there are no active subscriptions
@@ -306,6 +309,7 @@
     ASSERT_TRUE(exchangeCtx1);
 
     // InteractionModelEngine init
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     EXPECT_EQ(CHIP_NO_ERROR, engine->Init(&GetExchangeManager(), &GetFabricTable(), reporting::GetDefaultReportScheduler()));
 
     // Verify that both Alice and Bob have no active subscriptions
@@ -364,6 +368,7 @@
     ASSERT_TRUE(exchangeCtx2);
 
     // InteractionModelEngine init
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     EXPECT_EQ(CHIP_NO_ERROR, engine->Init(&GetExchangeManager(), &GetFabricTable(), reporting::GetDefaultReportScheduler()));
 
     // Verify that both Alice and Bob have no active subscriptions
@@ -446,6 +451,7 @@
     ASSERT_TRUE(exchangeCtx22);
 
     // InteractionModelEngine init
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     EXPECT_EQ(CHIP_NO_ERROR, engine->Init(&GetExchangeManager(), &GetFabricTable(), reporting::GetDefaultReportScheduler()));
 
     // Verify that both Alice and Bob have no active subscriptions
@@ -525,6 +531,7 @@
     FabricIndex bobFabricIndex = 1;
 
     // InteractionModelEngine init
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     EXPECT_EQ(CHIP_NO_ERROR, engine->Init(&GetExchangeManager(), &GetFabricTable(), reporting::GetDefaultReportScheduler()));
 
     // Make sure we are using CASE sessions, because there is no defunct-marking for PASE.
@@ -575,6 +582,7 @@
 
     EXPECT_EQ(subscriptionStorage.Init(&storage), CHIP_NO_ERROR);
 
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     EXPECT_EQ(CHIP_NO_ERROR,
               engine->Init(&GetExchangeManager(), &GetFabricTable(), app::reporting::GetDefaultReportScheduler(), nullptr,
                            &subscriptionStorage));
@@ -630,6 +638,7 @@
 
     InteractionModelEngine * engine = InteractionModelEngine::GetInstance();
 
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     EXPECT_EQ(engine->Init(&GetExchangeManager(), &GetFabricTable(), app::reporting::GetDefaultReportScheduler()), CHIP_NO_ERROR);
 
     uint32_t timeTillNextResubscriptionMs;
@@ -661,6 +670,7 @@
     constexpr uint8_t kNumberOfSubsToResume = 5;
     uint8_t numberOfSubsRemaining           = kNumberOfSubsToResume;
 
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     EXPECT_EQ(engine->Init(&GetExchangeManager(), &GetFabricTable(), app::reporting::GetDefaultReportScheduler()), CHIP_NO_ERROR);
 
 #if CHIP_CONFIG_ENABLE_ICD_CIP && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
diff --git a/src/controller/CHIPDeviceControllerFactory.cpp b/src/controller/CHIPDeviceControllerFactory.cpp
index e7e7189..a5fe94d 100644
--- a/src/controller/CHIPDeviceControllerFactory.cpp
+++ b/src/controller/CHIPDeviceControllerFactory.cpp
@@ -24,6 +24,7 @@
 
 #include <controller/CHIPDeviceControllerFactory.h>
 
+#include <app/InteractionModelEngine.h>
 #include <app/OperationalSessionSetup.h>
 #include <app/TimerDelegates.h>
 #include <app/reporting/ReportSchedulerImpl.h>
@@ -100,6 +101,10 @@
     params.certificateValidityPolicy = mCertificateValidityPolicy;
     params.sessionResumptionStorage  = mSessionResumptionStorage;
 
+    // re-initialization keeps any previously initialized values. The only place where
+    // a provider exists is in the InteractionModelEngine, so just say "keep it as is".
+    params.dataModelProvider = app::InteractionModelEngine::GetInstance()->GetDataModelProvider();
+
     return InitSystemState(params);
 }
 
@@ -127,6 +132,13 @@
     ChipLogError(Controller, "Warning: Device Controller Factory should be with a CHIP Device Layer...");
 #endif // CONFIG_DEVICE_LAYER
 
+    if (params.dataModelProvider == nullptr)
+    {
+        ChipLogError(AppServer, "Device Controller Factory requires a `dataModelProvider` value.");
+        ChipLogError(AppServer, "For backwards compatibility, you likely can use `CodegenDataModelProviderInstance()`");
+    }
+
+    VerifyOrReturnError(params.dataModelProvider != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     VerifyOrReturnError(stateParams.systemLayer != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     VerifyOrReturnError(stateParams.udpEndPointManager != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
 
@@ -238,6 +250,16 @@
     ReturnErrorOnFailure(stateParams.unsolicitedStatusHandler->Init(stateParams.exchangeMgr));
     ReturnErrorOnFailure(stateParams.bdxTransferServer->Init(stateParams.systemLayer, stateParams.exchangeMgr));
 
+    chip::app::InteractionModelEngine * interactionModelEngine = chip::app::InteractionModelEngine::GetInstance();
+
+    // Note placement of this BEFORE `InitDataModelHandler` since InitDataModelHandler may
+    // rely on ember (does emberAfInit() and configure which may load data from NVM).
+    //
+    // Expected forward path is that we will move move and more things inside datamodel
+    // provider (e.g. storage settings) so we want datamodelprovider available before
+    // `InitDataModelHandler`.
+    interactionModelEngine->SetDataModelProvider(params.dataModelProvider);
+
     InitDataModelHandler();
 
     ReturnErrorOnFailure(Dnssd::Resolver::Instance().Init(stateParams.udpEndPointManager));
@@ -295,8 +317,8 @@
     stateParams.caseSessionManager = Platform::New<CASESessionManager>();
     ReturnErrorOnFailure(stateParams.caseSessionManager->Init(stateParams.systemLayer, sessionManagerConfig));
 
-    ReturnErrorOnFailure(chip::app::InteractionModelEngine::GetInstance()->Init(
-        stateParams.exchangeMgr, stateParams.fabricTable, stateParams.reportScheduler, stateParams.caseSessionManager));
+    ReturnErrorOnFailure(interactionModelEngine->Init(stateParams.exchangeMgr, stateParams.fabricTable, stateParams.reportScheduler,
+                                                      stateParams.caseSessionManager));
 
     // store the system state
     mSystemState = chip::Platform::New<DeviceControllerSystemState>(std::move(stateParams));
diff --git a/src/controller/CHIPDeviceControllerFactory.h b/src/controller/CHIPDeviceControllerFactory.h
index 16e2ad4..0efe451 100644
--- a/src/controller/CHIPDeviceControllerFactory.h
+++ b/src/controller/CHIPDeviceControllerFactory.h
@@ -160,6 +160,11 @@
     /* The port used for operational communication to listen for and send messages over UDP/TCP.
      * The default value of `0` will pick any available port. */
     uint16_t listenPort = 0;
+
+    // MUST NOT be null during initialization: every application must define the
+    // data model it wants to use. Backwards-compatibility can use `CodegenDataModelProviderInstance`
+    // for ember/zap-generated models.
+    chip::app::DataModel::Provider * dataModelProvider = nullptr;
 };
 
 class DeviceControllerFactory
diff --git a/src/controller/java/AndroidDeviceControllerWrapper.cpp b/src/controller/java/AndroidDeviceControllerWrapper.cpp
index 3eb6dd8..2582e8f 100644
--- a/src/controller/java/AndroidDeviceControllerWrapper.cpp
+++ b/src/controller/java/AndroidDeviceControllerWrapper.cpp
@@ -25,17 +25,16 @@
 
 #include <string.h>
 
+#include <app/codegen-data-model-provider/Instance.h>
 #include <app/server/Dnssd.h>
-
-#include <lib/support/CodeUtils.h>
-#include <lib/support/JniReferences.h>
-#include <lib/support/JniTypeWrappers.h>
-
 #include <controller/CHIPDeviceControllerFactory.h>
 #include <controller/java/AndroidICDClient.h>
 #include <credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h>
 #include <credentials/attestation_verifier/DeviceAttestationVerifier.h>
 #include <lib/core/TLV.h>
+#include <lib/support/CodeUtils.h>
+#include <lib/support/JniReferences.h>
+#include <lib/support/JniTypeWrappers.h>
 #include <lib/support/PersistentStorageMacros.h>
 #include <lib/support/SafeInt.h>
 #include <lib/support/ScopedBuffer.h>
@@ -210,6 +209,7 @@
     setupParams.defaultCommissioner            = &wrapper->mAutoCommissioner;
     initParams.fabricIndependentStorage        = wrapperStorage;
     initParams.sessionKeystore                 = &wrapper->mSessionKeystore;
+    initParams.dataModelProvider               = app::CodegenDataModelProviderInstance();
 
     wrapper->mGroupDataProvider.SetStorageDelegate(wrapperStorage);
     wrapper->mGroupDataProvider.SetSessionKeystore(initParams.sessionKeystore);
diff --git a/src/controller/python/ChipDeviceController-ScriptBinding.cpp b/src/controller/python/ChipDeviceController-ScriptBinding.cpp
index 11fdf76..b5dab55 100644
--- a/src/controller/python/ChipDeviceController-ScriptBinding.cpp
+++ b/src/controller/python/ChipDeviceController-ScriptBinding.cpp
@@ -42,6 +42,7 @@
 
 #include <app/DeviceProxy.h>
 #include <app/InteractionModelEngine.h>
+#include <app/codegen-data-model-provider/Instance.h>
 #include <app/icd/client/CheckInHandler.h>
 #include <app/icd/client/DefaultCheckInDelegate.h>
 #include <app/icd/client/DefaultICDClientStorage.h>
@@ -271,6 +272,7 @@
 
     factoryParams.fabricIndependentStorage = storageAdapter;
     factoryParams.sessionKeystore          = &sSessionKeystore;
+    factoryParams.dataModelProvider        = app::CodegenDataModelProviderInstance();
 
     sICDClientStorage.Init(storageAdapter, &sSessionKeystore);
 
diff --git a/src/controller/python/chip/internal/CommissionerImpl.cpp b/src/controller/python/chip/internal/CommissionerImpl.cpp
index 7092024..3182450 100644
--- a/src/controller/python/chip/internal/CommissionerImpl.cpp
+++ b/src/controller/python/chip/internal/CommissionerImpl.cpp
@@ -16,6 +16,7 @@
  */
 #include <memory>
 
+#include <app/codegen-data-model-provider/Instance.h>
 #include <controller/CHIPDeviceController.h>
 #include <controller/CHIPDeviceControllerFactory.h>
 #include <controller/ExampleOperationalCredentialsIssuer.h>
@@ -135,6 +136,7 @@
 
         factoryParams.fabricIndependentStorage = &gServerStorage;
         factoryParams.sessionKeystore          = &gSessionKeystore;
+        factoryParams.dataModelProvider        = chip::app::CodegenDataModelProviderInstance();
 
         // Initialize group data provider for local group key state and IPKs
         gGroupDataProvider.SetStorageDelegate(&gServerStorage);
diff --git a/src/controller/tests/TestEventChunking.cpp b/src/controller/tests/TestEventChunking.cpp
index d0f4999..17e8ece 100644
--- a/src/controller/tests/TestEventChunking.cpp
+++ b/src/controller/tests/TestEventChunking.cpp
@@ -288,6 +288,7 @@
 
     // Initialize the ember side server logic
     CodegenDataModelProviderInstance()->Shutdown();
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     InitDataModelHandler();
 
     // Register our fake dynamic endpoint.
@@ -355,6 +356,7 @@
 
     // Initialize the ember side server logic
     CodegenDataModelProviderInstance()->Shutdown();
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     InitDataModelHandler();
 
     // Register our fake dynamic endpoint.
@@ -432,6 +434,7 @@
 
     // Initialize the ember side server logic
     CodegenDataModelProviderInstance()->Shutdown();
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     InitDataModelHandler();
 
     // Register our fake dynamic endpoint.
diff --git a/src/controller/tests/TestReadChunking.cpp b/src/controller/tests/TestReadChunking.cpp
index 989d910..8614b6a 100644
--- a/src/controller/tests/TestReadChunking.cpp
+++ b/src/controller/tests/TestReadChunking.cpp
@@ -25,6 +25,7 @@
 #include "app-common/zap-generated/ids/Attributes.h"
 #include "app-common/zap-generated/ids/Clusters.h"
 #include "app/ConcreteAttributePath.h"
+#include "app/codegen-data-model-provider/Instance.h"
 #include "protocols/interaction_model/Constants.h"
 #include <app-common/zap-generated/cluster-objects.h>
 #include <app/AppConfig.h>
@@ -481,6 +482,7 @@
     app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance();
 
     // Initialize the ember side server logic
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     InitDataModelHandler();
 
     // Register our fake dynamic endpoint.
@@ -543,6 +545,7 @@
     app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance();
 
     // Initialize the ember side server logic
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     InitDataModelHandler();
 
     // Register our fake dynamic endpoint.
@@ -644,6 +647,7 @@
     app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance();
 
     // Initialize the ember side server logic
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     InitDataModelHandler();
 
     app::InteractionModelEngine::GetInstance()->GetReportingEngine().SetWriterReserved(0);
@@ -695,6 +699,7 @@
     app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance();
 
     // Initialize the ember side server logic
+    engine->SetDataModelProvider(CodegenDataModelProviderInstance());
     InitDataModelHandler();
 
     // Register our fake dynamic endpoint.
diff --git a/src/controller/tests/TestWriteChunking.cpp b/src/controller/tests/TestWriteChunking.cpp
index 4efc098..f2f5a08 100644
--- a/src/controller/tests/TestWriteChunking.cpp
+++ b/src/controller/tests/TestWriteChunking.cpp
@@ -21,16 +21,16 @@
 
 #include <pw_unit_test/framework.h>
 
-#include "app-common/zap-generated/ids/Attributes.h"
-#include "app-common/zap-generated/ids/Clusters.h"
-#include "app/ConcreteAttributePath.h"
-#include "protocols/interaction_model/Constants.h"
 #include <app-common/zap-generated/cluster-objects.h>
+#include <app-common/zap-generated/ids/Attributes.h>
+#include <app-common/zap-generated/ids/Clusters.h>
 #include <app/AttributeAccessInterface.h>
 #include <app/AttributeAccessInterfaceRegistry.h>
 #include <app/CommandHandlerInterface.h>
+#include <app/ConcreteAttributePath.h>
 #include <app/InteractionModelEngine.h>
 #include <app/WriteClient.h>
+#include <app/codegen-data-model-provider/Instance.h>
 #include <app/data-model/Decode.h>
 #include <app/tests/AppTestContext.h>
 #include <app/util/DataModelHandler.h>
@@ -39,6 +39,7 @@
 #include <lib/core/ErrorStr.h>
 #include <lib/core/StringBuilderAdapters.h>
 #include <lib/support/logging/CHIPLogging.h>
+#include <protocols/interaction_model/Constants.h>
 
 using namespace chip;
 using namespace chip::app;
@@ -211,6 +212,7 @@
     auto sessionHandle = GetSessionBobToAlice();
 
     // Initialize the ember side server logic
+    app::InteractionModelEngine::GetInstance()->SetDataModelProvider(CodegenDataModelProviderInstance());
     InitDataModelHandler();
 
     // Register our fake dynamic endpoint.
@@ -284,6 +286,7 @@
     bool atLeastOneRequestFailed = false;
 
     // Initialize the ember side server logic
+    app::InteractionModelEngine::GetInstance()->SetDataModelProvider(CodegenDataModelProviderInstance());
     InitDataModelHandler();
 
     // Register our fake dynamic endpoint.
@@ -363,6 +366,7 @@
     auto sessionHandle = GetSessionBobToAlice();
 
     // Initialize the ember side server logic
+    app::InteractionModelEngine::GetInstance()->SetDataModelProvider(CodegenDataModelProviderInstance());
     InitDataModelHandler();
 
     // Register our fake dynamic endpoint.
@@ -437,6 +441,7 @@
     auto sessionHandle = GetSessionBobToAlice();
 
     // Initialize the ember side server logic
+    app::InteractionModelEngine::GetInstance()->SetDataModelProvider(CodegenDataModelProviderInstance());
     InitDataModelHandler();
 
     // Register our fake dynamic endpoint.
@@ -585,6 +590,7 @@
 TEST_F(TestWriteChunking, TestTransactionalList)
 {
     // Initialize the ember side server logic
+    app::InteractionModelEngine::GetInstance()->SetDataModelProvider(CodegenDataModelProviderInstance());
     InitDataModelHandler();
 
     // Register our fake dynamic endpoint.
diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm
index ce20869..4eb7328 100644
--- a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm
+++ b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm
@@ -50,6 +50,7 @@
 
 #import <os/lock.h>
 
+#include <app/codegen-data-model-provider/Instance.h>
 #include <app/server/Dnssd.h>
 #include <controller/CHIPDeviceControllerFactory.h>
 #include <credentials/CHIPCert.h>
@@ -398,6 +399,7 @@
             params.opCertStore = _opCertStore;
             params.certificateValidityPolicy = &_certificateValidityPolicy;
             params.sessionResumptionStorage = _sessionResumptionStorage;
+            params.dataModelProvider = app::CodegenDataModelProviderInstance();
             SuccessOrExit(err = _controllerFactory->Init(params));
         }