diff --git a/examples/tv-casting-app/linux/BUILD.gn b/examples/tv-casting-app/linux/BUILD.gn index 07d9af1a71c721..113c6f56219f7d 100644 --- a/examples/tv-casting-app/linux/BUILD.gn +++ b/examples/tv-casting-app/linux/BUILD.gn @@ -19,13 +19,24 @@ import("${chip_root}/src/lib/lib.gni") assert(chip_build_tools) +declare_args() { + chip_casting_simplified = false +} + executable("chip-tv-casting-app") { - sources = [ - "${chip_root}/examples/tv-casting-app/tv-casting-common/include/CHIPProjectAppConfig.h", - "CastingUtils.cpp", - "CastingUtils.h", - "main.cpp", - ] + if (chip_casting_simplified) { + sources = [ + "${chip_root}/examples/tv-casting-app/tv-casting-common/include/CHIPProjectAppConfig.h", + "simple-app.cpp", + ] + } else { + sources = [ + "${chip_root}/examples/tv-casting-app/tv-casting-common/include/CHIPProjectAppConfig.h", + "CastingUtils.cpp", + "CastingUtils.h", + "main.cpp", + ] + } deps = [ "${chip_root}/examples/common/tracing:commandline", diff --git a/examples/tv-casting-app/linux/simple-app.cpp b/examples/tv-casting-app/linux/simple-app.cpp new file mode 100644 index 00000000000000..26f9e2825ff7d9 --- /dev/null +++ b/examples/tv-casting-app/linux/simple-app.cpp @@ -0,0 +1,154 @@ +/* + * + * Copyright (c) 2023 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 "core/Types.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace matter::casting::core; +using namespace matter::casting::support; + +// To hold SPAKE2+ verifier, discriminator, passcode +LinuxCommissionableDataProvider gCommissionableDataProvider; + +CHIP_ERROR InitCommissionableDataProvider(LinuxCommissionableDataProvider & provider, LinuxDeviceOptions & options) +{ + chip::Optional setupPasscode; + + if (options.payload.setUpPINCode != 0) + { + setupPasscode.SetValue(options.payload.setUpPINCode); + } + else if (!options.spake2pVerifier.HasValue()) + { + uint32_t defaultTestPasscode = 0; + chip::DeviceLayer::TestOnlyCommissionableDataProvider TestOnlyCommissionableDataProvider; + VerifyOrDie(TestOnlyCommissionableDataProvider.GetSetupPasscode(defaultTestPasscode) == CHIP_NO_ERROR); + + ChipLogError(Support, + "*** WARNING: Using temporary passcode %u due to no neither --passcode or --spake2p-verifier-base64 " + "given on command line. This is temporary and will be deprecated. Please update your scripts " + "to explicitly configure onboarding credentials. ***", + static_cast(defaultTestPasscode)); + setupPasscode.SetValue(defaultTestPasscode); + options.payload.setUpPINCode = defaultTestPasscode; + } + else + { + // Passcode is 0, so will be ignored, and verifier will take over. Onboarding payload + // printed for debug will be invalid, but if the onboarding payload had been given + // properly to the commissioner later, PASE will succeed. + } + + // Default to minimum PBKDF iterations + uint32_t spake2pIterationCount = chip::Crypto::kSpake2p_Min_PBKDF_Iterations; + if (options.spake2pIterations != 0) + { + spake2pIterationCount = options.spake2pIterations; + } + ChipLogError(Support, "PASE PBKDF iterations set to %u", static_cast(spake2pIterationCount)); + + return provider.Init(options.spake2pVerifier, options.spake2pSalt, spake2pIterationCount, setupPasscode, + options.payload.discriminator.GetLongValue()); +} + +/** + * @brief Provides the unique ID that is used by the SDK to generate the Rotating Device ID. + */ +class RotatingDeviceIdUniqueIdProvider : public MutableByteSpanDataProvider +{ +private: + chip::MutableByteSpan rotatingDeviceIdUniqueIdSpan; + uint8_t rotatingDeviceIdUniqueId[chip::DeviceLayer::ConfigurationManager::kRotatingDeviceIDUniqueIDLength]; + +public: + RotatingDeviceIdUniqueIdProvider() + { + // generate a random Unique ID for this example app + for (size_t i = 0; i < sizeof(rotatingDeviceIdUniqueId); i++) + { + rotatingDeviceIdUniqueId[i] = chip::Crypto::GetRandU8(); + } + rotatingDeviceIdUniqueIdSpan = chip::MutableByteSpan(rotatingDeviceIdUniqueId); + } + + chip::MutableByteSpan * Get() { return &rotatingDeviceIdUniqueIdSpan; } +}; + +/** + * @brief Provides the ServerInitParams required to start the CastingApp, which in turn starts the Matter server + */ +class CommonCaseDeviceServerInitParamsProvider : public ServerInitParamsProvider +{ +private: + // For this example, we'll use CommonCaseDeviceServerInitParams + chip::CommonCaseDeviceServerInitParams serverInitParams; + +public: + chip::ServerInitParams * Get() + { + CHIP_ERROR err = serverInitParams.InitializeStaticResourcesBeforeServerInit(); + VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, + ChipLogError(AppServer, "Initialization of ServerInitParams failed %" CHIP_ERROR_FORMAT, err.Format())); + return &serverInitParams; + } +}; + +int main(int argc, char * argv[]) +{ + // Create AppParameters that need to be passed to CastingApp.Initialize() + AppParameters appParameters; + RotatingDeviceIdUniqueIdProvider rotatingDeviceIdUniqueIdProvider; + CommonCaseDeviceServerInitParamsProvider serverInitParamsProvider; + CHIP_ERROR err = CHIP_NO_ERROR; + err = InitCommissionableDataProvider(gCommissionableDataProvider, LinuxDeviceOptions::GetInstance()); + VerifyOrReturnValue( + err == CHIP_NO_ERROR, 0, + ChipLogError(AppServer, "Initialization of CommissionableDataProvider failed %" CHIP_ERROR_FORMAT, err.Format())); + err = appParameters.Create(&rotatingDeviceIdUniqueIdProvider, &gCommissionableDataProvider, + chip::Credentials::Examples::GetExampleDACProvider(), + GetDefaultDACVerifier(chip::Credentials::GetTestAttestationTrustStore()), &serverInitParamsProvider); + VerifyOrReturnValue(err == CHIP_NO_ERROR, 0, + ChipLogError(AppServer, "Creation of AppParameters failed %" CHIP_ERROR_FORMAT, err.Format())); + + // Initialize the CastingApp + err = CastingApp::GetInstance()->Initialize(appParameters); + VerifyOrReturnValue(err == CHIP_NO_ERROR, 0, + ChipLogError(AppServer, "Initialization of CastingApp failed %" CHIP_ERROR_FORMAT, err.Format())); + + // Initialize Linux KeyValueStoreMgr + chip::DeviceLayer::PersistedStorage::KeyValueStoreMgrImpl().Init(CHIP_CONFIG_KVS_PATH); + + // Start the CastingApp + err = CastingApp::GetInstance()->Start(); + VerifyOrReturnValue(err == CHIP_NO_ERROR, 0, + ChipLogError(AppServer, "CastingApp::Start failed %" CHIP_ERROR_FORMAT, err.Format())); + + chip::DeviceLayer::PlatformMgr().RunEventLoop(); + + return 0; +} diff --git a/examples/tv-casting-app/tv-casting-common/BUILD.gn b/examples/tv-casting-app/tv-casting-common/BUILD.gn index 65a4c36e90b44c..8292d8366b06da 100644 --- a/examples/tv-casting-app/tv-casting-common/BUILD.gn +++ b/examples/tv-casting-app/tv-casting-common/BUILD.gn @@ -85,6 +85,15 @@ chip_data_model("tv-casting-common") { "src/TargetVideoPlayerInfo.cpp", ] + # Add simplified casting API files here + sources += [ + "core/CastingApp.cpp", + "core/CastingApp.h", + "core/Types.h", + "support/AppParameters.h", + "support/DataProvider.h", + ] + deps = [ "${chip_root}/examples/common/tracing:commandline", "${chip_root}/src/app/tests/suites/commands/interaction_model", diff --git a/examples/tv-casting-app/tv-casting-common/core/CastingApp.cpp b/examples/tv-casting-app/tv-casting-common/core/CastingApp.cpp new file mode 100644 index 00000000000000..cc21c29c9b5f30 --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/core/CastingApp.cpp @@ -0,0 +1,128 @@ +/* + * + * Copyright (c) 2023 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 "CastingApp.h" + +#include +#include +#include +#include + +namespace matter { +namespace casting { +namespace core { + +using namespace matter::casting::support; + +CastingApp * CastingApp::_castingApp = nullptr; + +CastingApp::CastingApp() {} + +CastingApp * CastingApp::GetInstance() +{ + if (_castingApp == nullptr) + { + _castingApp = new CastingApp(); + } + return _castingApp; +} + +CHIP_ERROR CastingApp::Initialize(const AppParameters & appParameters) +{ + VerifyOrReturnError(mState == UNINITIALIZED, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(appParameters.GetCommissionableDataProvider() != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(appParameters.GetDeviceAttestationCredentialsProvider() != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(appParameters.GetServerInitParamsProvider() != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + + ReturnErrorOnFailure(chip::Platform::MemoryInit()); + ReturnErrorOnFailure(chip::DeviceLayer::PlatformMgr().InitChipStack()); + + mAppParameters = &appParameters; + + chip::DeviceLayer::SetCommissionableDataProvider(appParameters.GetCommissionableDataProvider()); + chip::Credentials::SetDeviceAttestationCredentialsProvider(appParameters.GetDeviceAttestationCredentialsProvider()); + chip::Credentials::SetDeviceAttestationVerifier(appParameters.GetDeviceAttestationVerifier()); + +#if CHIP_ENABLE_ROTATING_DEVICE_ID + MutableByteSpanDataProvider * uniqueIdProvider = appParameters.GetRotatingDeviceIdUniqueIdProvider(); + if (uniqueIdProvider != nullptr && uniqueIdProvider->Get() != nullptr) + { + ReturnErrorOnFailure(chip::DeviceLayer::ConfigurationMgr().SetRotatingDeviceIdUniqueId(*uniqueIdProvider->Get())); + } +#endif // CHIP_ENABLE_ROTATING_DEVICE_ID + + mState = NOT_RUNNING; // initialization done, set state to NOT_RUNNING + + return CHIP_NO_ERROR; +} + +CHIP_ERROR CastingApp::Start() +{ + VerifyOrReturnError(mState == NOT_RUNNING, CHIP_ERROR_INCORRECT_STATE); + + // start Matter server + chip::ServerInitParams * serverInitParams = mAppParameters->GetServerInitParamsProvider()->Get(); + VerifyOrReturnError(serverInitParams != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + ReturnErrorOnFailure(chip::Server::GetInstance().Init(*serverInitParams)); + + // perform post server startup registrations + ReturnErrorOnFailure(PostStartRegistrations()); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR CastingApp::PostStartRegistrations() +{ + VerifyOrReturnError(mState == NOT_RUNNING, CHIP_ERROR_INCORRECT_STATE); + auto & server = chip::Server::GetInstance(); + + // TODO: Set CastingApp as AppDelegate + // &server.GetCommissioningWindowManager().SetAppDelegate(??); + + // Initialize binding handlers + chip::BindingManager::GetInstance().Init( + { &server.GetFabricTable(), server.GetCASESessionManager(), &server.GetPersistentStorage() }); + + // TODO: Set FabricDelegate + // chip::Server::GetInstance().GetFabricTable().AddFabricDelegate(&mPersistenceManager); + + // TODO: Add DeviceEvent Handler + // ReturnErrorOnFailure(DeviceLayer::PlatformMgrImpl().AddEventHandler(DeviceEventCallback, 0)); + + mState = RUNNING; // CastingApp started successfully, set state to RUNNING + return CHIP_NO_ERROR; +} + +CHIP_ERROR CastingApp::Stop() +{ + VerifyOrReturnError(mState == RUNNING, CHIP_ERROR_INCORRECT_STATE); + + // TODO: add logic to capture CastingPlayers that we are currently connected to, so we can automatically reconnect with them on + // Start() again + + // Shutdown the Matter server + chip::Server::GetInstance().Shutdown(); + + mState = NOT_RUNNING; // CastingApp started successfully, set state to RUNNING + + return CHIP_ERROR_NOT_IMPLEMENTED; +} + +}; // namespace core +}; // namespace casting +}; // namespace matter diff --git a/examples/tv-casting-app/tv-casting-common/core/CastingApp.h b/examples/tv-casting-app/tv-casting-common/core/CastingApp.h new file mode 100644 index 00000000000000..1f122e1153c3ab --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/core/CastingApp.h @@ -0,0 +1,91 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "support/AppParameters.h" + +namespace matter { +namespace casting { +namespace core { + +/** + * @brief Represents CastingApp state. + * + */ +enum CastingAppState +{ + UNINITIALIZED, // Before Initialize() success + NOT_RUNNING, // After Initialize() success before Start()ing, OR After stop() success + RUNNING, // After Start() success +}; + +/** + * @brief CastingApp represents an app that can cast content to a Casting Player. + * This class is a singleton. + */ +class CastingApp +{ +public: + static CastingApp * GetInstance(); + + /** + * @brief Initializes the CastingApp with appParameters + * + * @param appParameters + * @return CHIP_ERROR + */ + CHIP_ERROR Initialize(const matter::casting::support::AppParameters & appParameters); + + /** + * @brief Starts the Matter server that the CastingApp runs on and calls PostStartRegistrations() to finish starting up the + * CastingApp. + * + * @return CHIP_ERROR - CHIP_NO_ERROR if Matter server started successfully, specific error code otherwise. + */ + CHIP_ERROR Start(); + + /** + * @brief Perform post Matter server startup registrations + * + * @return CHIP_ERROR - CHIP_NO_ERROR if all registrations succeeded, specific error code otherwise + */ + CHIP_ERROR PostStartRegistrations(); + + /** + * @brief Stops the Matter server that the CastingApp runs on + * + * @return CHIP_ERROR - CHIP_NO_ERROR if Matter server stopped successfully, specific error code otherwise. + */ + CHIP_ERROR Stop(); + +private: + CastingApp(); + static CastingApp * _castingApp; + + CastingApp(CastingApp & other) = delete; + void operator=(const CastingApp &) = delete; + + const matter::casting::support::AppParameters * mAppParameters; + + CastingAppState mState = UNINITIALIZED; +}; + +}; // namespace core +}; // namespace casting +}; // namespace matter diff --git a/examples/tv-casting-app/tv-casting-common/core/Types.h b/examples/tv-casting-app/tv-casting-common/core/Types.h new file mode 100644 index 00000000000000..a5dce75d60d181 --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/core/Types.h @@ -0,0 +1,55 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "core/CastingApp.h" +#include "support/AppParameters.h" + +#include +#include + +namespace matter { +namespace casting { + +namespace memory { + +template +using Weak = std::weak_ptr; + +template +using Strong = std::shared_ptr; + +} // namespace memory + +namespace app { + +class CastingApp; + +}; + +namespace support { + +class AppParameters; +class ByteSpanDataProvider; +class ServerInitParamsProvider; + +} // namespace support + +}; // namespace casting +}; // namespace matter diff --git a/examples/tv-casting-app/tv-casting-common/support/AppParameters.h b/examples/tv-casting-app/tv-casting-common/support/AppParameters.h new file mode 100644 index 00000000000000..9539a2919a81f5 --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/support/AppParameters.h @@ -0,0 +1,107 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "DataProvider.h" +#include "core/Types.h" + +#include +#include +#include +#include + +namespace matter { +namespace casting { +namespace support { + +/** + * @brief Parameters required to Initialize() the CastingApp + */ +class AppParameters +{ +public: + AppParameters() {} + CHIP_ERROR Create(MutableByteSpanDataProvider * rotatingDeviceIdUniqueIdProvider, + chip::DeviceLayer::CommissionableDataProvider * commissionableDataProvider, + chip::Credentials::DeviceAttestationCredentialsProvider * deviceAttestationCredentialsProvider, + chip::Credentials::DeviceAttestationVerifier * deviceAttestationVerifier, + ServerInitParamsProvider * serverInitParamsProvider) + { + VerifyOrReturnError(commissionableDataProvider != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(deviceAttestationCredentialsProvider != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(deviceAttestationVerifier != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(serverInitParamsProvider != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + + mRotatingDeviceIdUniqueIdProvider = rotatingDeviceIdUniqueIdProvider; + mCommissionableDataProvider = commissionableDataProvider; + mDeviceAttestationCredentialsProvider = deviceAttestationCredentialsProvider; + mDeviceAttestationVerifier = deviceAttestationVerifier; + mServerInitParamsProvider = serverInitParamsProvider; + + return CHIP_NO_ERROR; + } + + MutableByteSpanDataProvider * GetRotatingDeviceIdUniqueIdProvider() const { return mRotatingDeviceIdUniqueIdProvider; } + + chip::DeviceLayer::CommissionableDataProvider * GetCommissionableDataProvider() const { return mCommissionableDataProvider; } + + chip::Credentials::DeviceAttestationCredentialsProvider * GetDeviceAttestationCredentialsProvider() const + { + return mDeviceAttestationCredentialsProvider; + } + + chip::Credentials::DeviceAttestationVerifier * GetDeviceAttestationVerifier() const { return mDeviceAttestationVerifier; } + + ServerInitParamsProvider * GetServerInitParamsProvider() const { return mServerInitParamsProvider; } + +private: + /** + * @brief Provides UniqueId used to generate the RotatingDeviceId advertised by the CastingApp + * + */ + MutableByteSpanDataProvider * mRotatingDeviceIdUniqueIdProvider; + + /** + * @brief Provides CommissionableData (such as setupPasscode, discriminator, etc) used to get the CastingApp commissioned + * + */ + chip::DeviceLayer::CommissionableDataProvider * mCommissionableDataProvider; + + /** + * @brief Provides DeviceAttestationCredentials of the CastingApp during commissioning + * + */ + chip::Credentials::DeviceAttestationCredentialsProvider * mDeviceAttestationCredentialsProvider; + + /** + * @brief DeviceAttestationVerifier used by the CastingApp during commissioning + * + */ + chip::Credentials::DeviceAttestationVerifier * mDeviceAttestationVerifier; + + /** + * @brief Provides params used to initialize the Matter server run by the CastingApp + * + */ + ServerInitParamsProvider * mServerInitParamsProvider; +}; + +}; // namespace support +}; // namespace casting +}; // namespace matter diff --git a/examples/tv-casting-app/tv-casting-common/support/DataProvider.h b/examples/tv-casting-app/tv-casting-common/support/DataProvider.h new file mode 100644 index 00000000000000..811acd1b1a1512 --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/support/DataProvider.h @@ -0,0 +1,58 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "core/Types.h" + +#include +#include +#include + +namespace matter { +namespace casting { +namespace support { + +/** + * @brief Generic DataProvider template that allows the implementer to provide a pointer to some data with type T + * + * @tparam T type of the data to be provided + */ +template +class DataProvider +{ +public: + virtual ~DataProvider(){}; + virtual T * Get() { return nullptr; }; +}; + +class MutableByteSpanDataProvider : public DataProvider +{ +public: + virtual chip::MutableByteSpan * Get() { return nullptr; }; +}; + +class ServerInitParamsProvider : public DataProvider +{ +public: + virtual chip::ServerInitParams * Get() { return nullptr; }; +}; + +}; // namespace support +}; // namespace casting +}; // namespace matter