| /* |
| * |
| * Copyright (c) 2025 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 <AllDevicesExampleDeviceInfoProviderImpl.h> |
| #include <AppMainLoop.h> |
| #include <AppRootNode.h> |
| #include <LinuxCommissionableDataProvider.h> |
| #include <TracingCommandLineArgument.h> |
| #include <access/examples/GroupAuxiliaryAccessControlDelegate.h> |
| #include <app/DefaultSafeAttributePersistenceProvider.h> |
| #include <app/DeviceLoadStatusProvider.h> |
| #include <app/InteractionModelEngine.h> |
| #include <app/SafeAttributePersistenceProvider.h> |
| #include <app/TestEventTriggerDelegate.h> |
| #include <app/persistence/DefaultAttributePersistenceProvider.h> |
| #include <app/server-cluster/ServerClusterInterfaceRegistry.h> |
| #include <app/server/Dnssd.h> |
| #include <app/server/Server.h> |
| #include <app_options/AppOptions.h> |
| #include <credentials/examples/DeviceAttestationCredsExample.h> |
| #include <devices/device-factory/DeviceFactory.h> |
| #include <platform/CommissionableDataProvider.h> |
| #include <platform/DiagnosticDataProvider.h> |
| #include <platform/PlatformManager.h> |
| #include <setup_payload/OnboardingCodesUtil.h> |
| #include <string> |
| #include <system/SystemLayer.h> |
| |
| #include <TermHandling.h> |
| |
| using namespace chip; |
| using namespace chip::app; |
| using namespace chip::Platform; |
| using namespace chip::DeviceLayer; |
| using namespace chip::app::Clusters; |
| using namespace chip::ArgParser; |
| |
| namespace { |
| AppMainLoopImplementation * gMainLoopImplementation = nullptr; |
| |
| AllDevicesExampleDeviceInfoProviderImpl gExampleDeviceInfoProvider; |
| Credentials::GroupDataProviderImpl gGroupDataProvider; |
| chip::app::DefaultSafeAttributePersistenceProvider gSafeAttributePersistenceProvider; |
| DefaultTimerDelegate gTimerDelegate; |
| |
| // To hold SPAKE2+ verifier, discriminator, passcode |
| LinuxCommissionableDataProvider gCommissionableDataProvider; |
| |
| void StopSignalHandler(int /* signal */) |
| { |
| if (gMainLoopImplementation != nullptr) |
| { |
| gMainLoopImplementation->SignalSafeStopMainLoop(); |
| } |
| else |
| { |
| Server::GetInstance().GenerateShutDownEvent(); |
| // Usage of VerifyOrDie in the nested lambda instead of SuccessOrDie is intentional: |
| // The SuccessOrDie macro uses a `__err` assignment and some compilers complain about |
| // variable shadowing. |
| SuccessOrDie(SystemLayer().ScheduleLambda([]() { VerifyOrDie(PlatformMgr().StopEventLoopTask() == CHIP_NO_ERROR); })); |
| } |
| } |
| |
| class CodeDrivenDataModelDevices |
| { |
| public: |
| struct Context |
| { |
| chip::PersistentStorageDelegate & storageDelegate; |
| CommissioningWindowManager & commissioningWindowManager; |
| DeviceLayer::ConfigurationManager & configurationManager; |
| DeviceLayer::DeviceControlServer & deviceControlServer; |
| FabricTable & fabricTable; |
| Access::AccessControl & accessControl; |
| PersistentStorageDelegate & persistentStorage; |
| FailSafeContext & failSafeContext; |
| DeviceLayer::DeviceInstanceInfoProvider & deviceInstanceInfoProvider; |
| DeviceLayer::PlatformManager & platformManager; |
| Credentials::GroupDataProvider & groupDataProvider; |
| SessionManager & sessionManager; |
| DnssdServer & dnssdServer; |
| DeviceLoadStatusProvider & deviceLoadStatusProvider; |
| DeviceLayer::DiagnosticDataProvider & diagnosticDataProvider; |
| TestEventTriggerDelegate * testEventTriggerDelegate; |
| Credentials::DeviceAttestationCredentialsProvider & dacProvider; |
| EventManagement & eventManagement; |
| SafeAttributePersistenceProvider & safeAttributePersistenceProvider; |
| TimerDelegate & timerDelegate; |
| #if CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED |
| TermsAndConditionsProvider & termsAndConditionsProvider; |
| #endif // CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED |
| }; |
| |
| CodeDrivenDataModelDevices(const Context & context) : |
| mContext(context), mDataModelProvider(mContext.storageDelegate, mAttributePersistence), |
| mRootNode( |
| { |
| .commissioningWindowManager = mContext.commissioningWindowManager, // |
| .configurationManager = mContext.configurationManager, // |
| .deviceControlServer = mContext.deviceControlServer, // |
| .fabricTable = mContext.fabricTable, // |
| .accessControl = mContext.accessControl, // |
| .persistentStorage = mContext.persistentStorage, // |
| .failSafeContext = mContext.failSafeContext, // |
| .deviceInstanceInfoProvider = mContext.deviceInstanceInfoProvider, // |
| .platformManager = mContext.platformManager, // |
| .groupDataProvider = mContext.groupDataProvider, // |
| .sessionManager = mContext.sessionManager, // |
| .dnssdServer = mContext.dnssdServer, // |
| .deviceLoadStatusProvider = mContext.deviceLoadStatusProvider, // |
| .diagnosticDataProvider = mContext.diagnosticDataProvider, // |
| .testEventTriggerDelegate = mContext.testEventTriggerDelegate, // |
| .dacProvider = mContext.dacProvider, // |
| .eventManagement = mContext.eventManagement, // |
| .safeAttributePersistenceProvider = mContext.safeAttributePersistenceProvider, // |
| .timerDelegate = mContext.timerDelegate, // |
| #if CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED |
| .termsAndConditionsProvider = mContext.termsAndConditionsProvider, |
| #endif // CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED |
| }, |
| []() { |
| BitFlags<AppRootNode::EnabledFeatures> features; |
| #if CHIP_DEVICE_CONFIG_ENABLE_WIFI |
| features.Set(AppRootNode::EnabledFeatures::kWiFi, AppOptions::EnableWiFi()); |
| #endif |
| return features; |
| }()) |
| {} |
| |
| CHIP_ERROR Startup() |
| { |
| ReturnErrorOnFailure(mAttributePersistence.Init(&mContext.storageDelegate)); |
| ReturnErrorOnFailure(mRootNode.RootDevice().Register(kRootEndpointId, mDataModelProvider, kInvalidEndpointId)); |
| |
| for (const auto & config : AppOptions::GetDeviceConfigs()) |
| { |
| auto device = DeviceFactory::GetInstance().Create(config.type); |
| VerifyOrReturnError(device, CHIP_ERROR_NO_MEMORY); |
| ReturnErrorOnFailure(device->Register(config.endpoint, mDataModelProvider, kInvalidEndpointId)); |
| mConstructedDevices.push_back(std::move(device)); |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void Shutdown() |
| { |
| for (auto & device : mConstructedDevices) |
| { |
| device->Unregister(mDataModelProvider); |
| } |
| mConstructedDevices.clear(); |
| mRootNode.RootDevice().Unregister(mDataModelProvider); |
| } |
| |
| chip::app::CodeDrivenDataModelProvider & DataModelProvider() { return mDataModelProvider; } |
| |
| private: |
| Context mContext; |
| chip::app::DefaultAttributePersistenceProvider mAttributePersistence; |
| |
| chip::app::CodeDrivenDataModelProvider mDataModelProvider; |
| |
| AppRootNode mRootNode; |
| std::vector<std::unique_ptr<DeviceInterface>> mConstructedDevices; |
| }; |
| |
| void RunApplication(AppMainLoopImplementation * mainLoop = nullptr) |
| { |
| gMainLoopImplementation = mainLoop; |
| |
| DeviceFactory::GetInstance().Init(DeviceFactory::Context{ |
| .groupDataProvider = gGroupDataProvider, // |
| .fabricTable = Server::GetInstance().GetFabricTable(), // |
| .timerDelegate = gTimerDelegate, // |
| |
| }); |
| |
| static chip::CommonCaseDeviceServerInitParams initParams; |
| |
| SuccessOrDie(initParams.InitializeStaticResourcesBeforeServerInit()); |
| |
| #if CHIP_CONFIG_ENABLE_GROUPCAST |
| static chip::Access::Examples::GroupAuxiliaryAccessControlDelegate groupAuxDelegate(&gGroupDataProvider, |
| &Server::GetInstance().GetFabricTable()); |
| initParams.groupAuxiliaryAccessControlDelegate = &groupAuxDelegate; |
| gGroupDataProvider.SetGroupcastEnabled(true); |
| #endif // CHIP_CONFIG_ENABLE_GROUPCAST |
| |
| gGroupDataProvider.SetStorageDelegate(initParams.persistentStorageDelegate); |
| Credentials::SetGroupDataProvider(&gGroupDataProvider); |
| |
| DeviceLayer::DeviceInstanceInfoProvider * provider = DeviceLayer::GetDeviceInstanceInfoProvider(); |
| if (provider == nullptr) |
| { |
| ChipLogError(AppServer, "Failed to get the DeviceInstanceInfoProvider."); |
| chipDie(); |
| } |
| |
| // Initialize the safe attribute persistence provider |
| SuccessOrDie(gSafeAttributePersistenceProvider.Init(initParams.persistentStorageDelegate)); |
| SetSafeAttributePersistenceProvider(&gSafeAttributePersistenceProvider); |
| |
| // Set the global DAC provider before server/cluster init so any integration path that |
| // snapshots the provider during construction sees a valid implementation. |
| SetDeviceAttestationCredentialsProvider(Credentials::Examples::GetExampleDACProvider()); |
| |
| static CodeDrivenDataModelDevices devices({ |
| .storageDelegate = *initParams.persistentStorageDelegate, // |
| .commissioningWindowManager = Server::GetInstance().GetCommissioningWindowManager(), // |
| .configurationManager = DeviceLayer::ConfigurationMgr(), // |
| .deviceControlServer = DeviceLayer::DeviceControlServer::DeviceControlSvr(), // |
| .fabricTable = Server::GetInstance().GetFabricTable(), // |
| .accessControl = Server::GetInstance().GetAccessControl(), // |
| .persistentStorage = Server::GetInstance().GetPersistentStorage(), // |
| .failSafeContext = Server::GetInstance().GetFailSafeContext(), // |
| .deviceInstanceInfoProvider = *provider, // |
| .platformManager = DeviceLayer::PlatformMgr(), // |
| .groupDataProvider = gGroupDataProvider, // |
| .sessionManager = Server::GetInstance().GetSecureSessionManager(), // |
| .dnssdServer = DnssdServer::Instance(), // |
| .deviceLoadStatusProvider = *InteractionModelEngine::GetInstance(), // |
| .diagnosticDataProvider = DeviceLayer::GetDiagnosticDataProvider(), // |
| .testEventTriggerDelegate = initParams.testEventTriggerDelegate, // |
| .dacProvider = *Credentials::GetDeviceAttestationCredentialsProvider(), // |
| .eventManagement = EventManagement::GetInstance(), // |
| .safeAttributePersistenceProvider = gSafeAttributePersistenceProvider, // |
| .timerDelegate = gTimerDelegate, // |
| |
| #if CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED |
| .termsAndConditionsProvider = TermsAndConditionsManager::GetInstance(), |
| #endif // CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED |
| }); |
| |
| SuccessOrDie(devices.Startup()); |
| |
| initParams.dataModelProvider = &devices.DataModelProvider(); |
| initParams.groupDataProvider = &gGroupDataProvider; |
| initParams.operationalServicePort = CHIP_PORT; |
| initParams.userDirectedCommissioningPort = CHIP_UDC_PORT; |
| initParams.interfaceId = Inet::InterfaceId::Null(); |
| |
| chip::CommandLineApp::TracingSetup tracing_setup; |
| tracing_setup.EnableTracingFor("json:log"); |
| |
| // Init ZCL Data Model and CHIP App Server |
| CHIP_ERROR err = Server::GetInstance().Init(initParams); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(AppServer, "Server init failed: %" CHIP_ERROR_FORMAT, err.Format()); |
| chipDie(); |
| } |
| |
| // Now that the server has started and we are done with our startup logging, |
| // log our discovery/onboarding information again so it's not lost in the |
| // noise. |
| ConfigurationMgr().LogDeviceConfig(); |
| |
| chip::PayloadContents payload; |
| |
| payload.version = 0; |
| payload.rendezvousInformation.SetValue(RendezvousInformationFlag::kBLE); |
| |
| if (GetCommissionableDataProvider()->GetSetupPasscode(payload.setUpPINCode) != CHIP_NO_ERROR) |
| { |
| payload.setUpPINCode = CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE; |
| } |
| |
| uint16_t discriminator = 0; |
| SuccessOrDie(GetCommissionableDataProvider()->GetSetupDiscriminator(discriminator)); |
| payload.discriminator.SetLongValue(discriminator); |
| |
| SuccessOrDie(chip::DeviceLayer::GetDeviceInstanceInfoProvider()->GetVendorId(payload.vendorID)); |
| SuccessOrDie(chip::DeviceLayer::GetDeviceInstanceInfoProvider()->GetProductId(payload.productID)); |
| PrintOnboardingCodes(payload); |
| |
| chip::app::SetTerminateHandler(StopSignalHandler); |
| |
| // This message is used as a marker for when the application process has started. |
| // See: scripts/tests/chiptest/test_definition.py |
| // TODO: A cleaner and more generic mechanism needs to be developed as a follow-up. |
| // Currently other places (OTA, TV) also scrape logs for information and a better way should be |
| // possible. |
| ChipLogProgress(DeviceLayer, "===== APP STATUS: Starting event loop ====="); |
| |
| if (mainLoop != nullptr) |
| { |
| mainLoop->RunMainLoop(); |
| } |
| else |
| { |
| DeviceLayer::PlatformMgr().RunEventLoop(); |
| } |
| gMainLoopImplementation = nullptr; |
| |
| devices.Shutdown(); |
| Server::GetInstance().Shutdown(); |
| DeviceLayer::PlatformMgr().Shutdown(); |
| tracing_setup.StopTracing(); |
| } |
| |
| void EventHandler(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg) |
| { |
| (void) arg; |
| if (event->Type == DeviceLayer::DeviceEventType::kCHIPoBLEConnectionEstablished) |
| { |
| ChipLogProgress(DeviceLayer, "Receive kCHIPoBLEConnectionEstablished"); |
| } |
| else if ((event->Type == chip::DeviceLayer::DeviceEventType::kInternetConnectivityChange)) |
| { |
| // Restart the server on connectivity change |
| DnssdServer::Instance().StartServer(); |
| } |
| } |
| |
| CHIP_ERROR InitCommissionableDataProvider(LinuxCommissionableDataProvider & provider) |
| { |
| auto discriminator = static_cast<uint16_t>(CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR); |
| chip::Optional<uint16_t> discriminatorFromParam = LinuxDeviceOptions::GetInstance().discriminator; |
| if (discriminatorFromParam.HasValue()) |
| { |
| discriminator = discriminatorFromParam.Value(); |
| } |
| |
| const auto setupPasscode = MakeOptional(static_cast<uint32_t>(CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE)); |
| const uint32_t spake2pIterationCount = Crypto::kSpake2p_Min_PBKDF_Iterations; |
| |
| Optional<std::vector<uint8_t>> serializedSpake2pVerifier = NullOptional; |
| Optional<std::vector<uint8_t>> spake2pSalt = NullOptional; |
| |
| return provider.Init( // |
| serializedSpake2pVerifier, // |
| spake2pSalt, // |
| spake2pIterationCount, // |
| setupPasscode, // |
| discriminator // |
| ); |
| } |
| |
| CHIP_ERROR Initialize(int argc, char * argv[]) |
| { |
| ChipLogProgress(AppServer, "Initializing..."); |
| ReturnErrorOnFailure(Platform::MemoryInit()); |
| ReturnErrorOnFailure(ParseArguments(argc, argv, AppOptions::GetOptions())); |
| ReturnErrorOnFailure(DeviceLayer::PersistedStorage::KeyValueStoreMgrImpl().Init(CHIP_CONFIG_KVS_PATH)); |
| ReturnErrorOnFailure(DeviceLayer::PlatformMgr().InitChipStack()); |
| |
| ReturnErrorOnFailure(InitCommissionableDataProvider(gCommissionableDataProvider)); |
| DeviceLayer::SetCommissionableDataProvider(&gCommissionableDataProvider); |
| DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider); |
| ConfigurationMgr().LogDeviceConfig(); |
| |
| ReturnErrorOnFailure(DeviceLayer::PlatformMgrImpl().AddEventHandler(EventHandler, 0)); |
| |
| #if CONFIG_NETWORK_LAYER_BLE |
| ReturnErrorOnFailure(DeviceLayer::ConnectivityMgr().SetBLEDeviceName(nullptr)); |
| ReturnErrorOnFailure(DeviceLayer::Internal::BLEMgrImpl().ConfigureBle(0, false)); |
| ReturnErrorOnFailure(DeviceLayer::ConnectivityMgr().SetBLEAdvertisingEnabled(true)); |
| #endif |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| } // namespace |
| |
| void ApplicationShutdown() {} |
| |
| int main(int argc, char * argv[]) |
| { |
| ChipLogProgress(AppServer, "Initializing"); |
| |
| if (CHIP_ERROR err = Initialize(argc, argv); err != CHIP_NO_ERROR) |
| { |
| ChipLogError(AppServer, "Initialize() failed: %" CHIP_ERROR_FORMAT, err.Format()); |
| chipDie(); |
| } |
| |
| ChipLogProgress(AppServer, "Hello from all-devices-app!"); |
| RunApplication(); |
| |
| return 0; |
| } |