diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/MatterCastingPlayer-JNI.cpp b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/MatterCastingPlayer-JNI.cpp index 1c4a949c9f7cc5..d0409c6a27945c 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/MatterCastingPlayer-JNI.cpp +++ b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/MatterCastingPlayer-JNI.cpp @@ -24,6 +24,7 @@ #include "core/CastingApp.h" // from tv-casting-common #include "core/CastingPlayer.h" // from tv-casting-common #include "core/CastingPlayerDiscovery.h" // from tv-casting-common +#include "core/ConnectionCallbacks.h" // from tv-casting-common #include <app/clusters/bindings/BindingManager.h> #include <app/server/Server.h> @@ -92,24 +93,36 @@ JNI_METHOD(jobject, verifyOrEstablishConnection) MatterCastingPlayerJNIMgr().mConnectionSuccessHandler.SetUp(env, jSuccessCallback); MatterCastingPlayerJNIMgr().mConnectionFailureHandler.SetUp(env, jFailureCallback); - // TODO: In the following PRs. Add optional CommissionerDeclarationHandler callback parameter. - castingPlayer->VerifyOrEstablishConnection( - [](CHIP_ERROR err, CastingPlayer * playerPtr) { - ChipLogProgress(AppServer, "MatterCastingPlayer-JNI::verifyOrEstablishConnection() ConnectCallback called"); - if (err == CHIP_NO_ERROR) - { - ChipLogProgress(AppServer, "MatterCastingPlayer-JNI:: Connected to Casting Player with device ID: %s", - playerPtr->GetId()); - MatterCastingPlayerJNIMgr().mConnectionSuccessHandler.Handle(nullptr); - } - else - { - ChipLogError(AppServer, "MatterCastingPlayer-JNI:: ConnectCallback, connection error: %" CHIP_ERROR_FORMAT, - err.Format()); - MatterCastingPlayerJNIMgr().mConnectionFailureHandler.Handle(err); - } - }, - static_cast<unsigned long long int>(commissioningWindowTimeoutSec), idOptions); + auto connectCallback = [](CHIP_ERROR err, CastingPlayer * playerPtr) { + ChipLogProgress(AppServer, "MatterCastingPlayer-JNI::verifyOrEstablishConnection() ConnectCallback()"); + if (err == CHIP_NO_ERROR) + { + ChipLogProgress(AppServer, + "MatterCastingPlayer-JNI::verifyOrEstablishConnection() ConnectCallback() Connected to Casting Player " + "with device ID: %s", + playerPtr->GetId()); + // The Java jSuccessCallback is expecting a Void v callback parameter which translates to a nullptr. When calling the + // Java method from C++ via JNI, passing nullptr is equivalent to passing a Void object in Java. + MatterCastingPlayerJNIMgr().mConnectionSuccessHandler.Handle(nullptr); + } + else + { + ChipLogError( + AppServer, + "MatterCastingPlayer-JNI::verifyOrEstablishConnection() ConnectCallback() Connection error: %" CHIP_ERROR_FORMAT, + err.Format()); + MatterCastingPlayerJNIMgr().mConnectionFailureHandler.Handle(err); + } + }; + + // TODO: In the following PRs. Add optional CommissionerDeclarationHandler callback parameter for the Commissioner-Generated + // passcode commissioning flow. + matter::casting::core::ConnectionCallbacks connectionCallbacks; + connectionCallbacks.mOnConnectionComplete = connectCallback; + + // TODO: Verify why commissioningWindowTimeoutSec is a "unsigned long long int" type. Seems too big. + castingPlayer->VerifyOrEstablishConnection(connectionCallbacks, + static_cast<unsigned long long int>(commissioningWindowTimeoutSec), idOptions); return support::convertMatterErrorFromCppToJava(CHIP_NO_ERROR); } diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCastingPlayer.mm b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCastingPlayer.mm index 1e94b77090e4c7..f36c51728100ab 100644 --- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCastingPlayer.mm +++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCastingPlayer.mm @@ -21,7 +21,8 @@ #import "MCEndpoint_Internal.h" #import "MCErrorUtils.h" -#import "core/CastingPlayer.h" +#import "core/CastingPlayer.h" // from tv-casting-common +#import "core/ConnectionCallbacks.h" // from tv-casting-common #import <Foundation/Foundation.h> @@ -47,7 +48,7 @@ - (void)verifyOrEstablishConnectionWithCompletionBlock:(void (^_Nonnull)(NSError - (void)verifyOrEstablishConnectionWithCompletionBlock:(void (^_Nonnull)(NSError * _Nullable))completion timeout:(long long)timeout desiredEndpointFilter:(MCEndpointFilter * _Nullable)desiredEndpointFilter { - ChipLogProgress(AppServer, "MCCastingPlayer.verifyOrEstablishConnectionWithCompletionBlock called"); + ChipLogProgress(AppServer, "MCCastingPlayer.verifyOrEstablishConnectionWithCompletionBlock() called"); VerifyOrReturn([[MCCastingApp getSharedInstance] isRunning], ChipLogError(AppServer, "MCCastingApp NOT running")); dispatch_queue_t workQueue = [[MCCastingApp getSharedInstance] getWorkQueue]; @@ -63,18 +64,23 @@ - (void)verifyOrEstablishConnectionWithCompletionBlock:(void (^_Nonnull)(NSError CHIP_ERROR result = idOptions.addTargetAppInfo(targetAppInfo); if (result != CHIP_NO_ERROR) { - ChipLogError(AppServer, "MCCastingPlayer.verifyOrEstablishConnectionWithCompletionBlock failed to add targetAppInfo: %" CHIP_ERROR_FORMAT, result.Format()); + ChipLogError(AppServer, "MCCastingPlayer.verifyOrEstablishConnectionWithCompletionBlock() failed to add targetAppInfo: %" CHIP_ERROR_FORMAT, result.Format()); } } - // TODO: In the following PRs. Add optional CommissionerDeclarationHandler callback parameter. - _cppCastingPlayer->VerifyOrEstablishConnection( - [completion](CHIP_ERROR err, matter::casting::core::CastingPlayer * castingPlayer) { - dispatch_queue_t clientQueue = [[MCCastingApp getSharedInstance] getClientQueue]; - dispatch_async(clientQueue, ^{ - completion(err == CHIP_NO_ERROR ? nil : [MCErrorUtils NSErrorFromChipError:err]); - }); - }, timeout, idOptions); + void (^connectCallback)(CHIP_ERROR, matter::casting::core::CastingPlayer *) = ^(CHIP_ERROR err, matter::casting::core::CastingPlayer * castingPlayer) { + ChipLogProgress(AppServer, "MCCastingPlayer.verifyOrEstablishConnectionWithCompletionBlock() ConnectCallback()"); + dispatch_queue_t clientQueue = [[MCCastingApp getSharedInstance] getClientQueue]; + dispatch_async(clientQueue, ^{ + completion(err == CHIP_NO_ERROR ? nil : [MCErrorUtils NSErrorFromChipError:err]); + }); + }; + + matter::casting::core::ConnectionCallbacks connectionCallbacks; + connectionCallbacks.mOnConnectionComplete = connectCallback; + + // TODO: In the following PRs. Add optional CommissionerDeclarationHandler callback parameter for the Commissioner-Generated passcode commissioning flow. + _cppCastingPlayer->VerifyOrEstablishConnection(connectionCallbacks, timeout, idOptions); }); } diff --git a/examples/tv-casting-app/linux/simple-app-helper.cpp b/examples/tv-casting-app/linux/simple-app-helper.cpp index ceb66233a7bc6d..49f006aa9cf4ab 100644 --- a/examples/tv-casting-app/linux/simple-app-helper.cpp +++ b/examples/tv-casting-app/linux/simple-app-helper.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-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. @@ -16,6 +16,7 @@ */ #include "simple-app-helper.h" +#include "../tv-casting-common/core/ConnectionCallbacks.h" #include "clusters/Clusters.h" #include "app/clusters/bindings/BindingManager.h" @@ -28,11 +29,14 @@ #include <lib/support/CHIPMem.h> #include <lib/support/CodeUtils.h> #include <platform/CHIPDeviceLayer.h> +#include <platform/TestOnlyCommissionableDataProvider.h> // VendorId of the Endpoint on the CastingPlayer that the CastingApp desires to interact with after connection const uint16_t kDesiredEndpointVendorId = 65521; DiscoveryDelegateImpl * DiscoveryDelegateImpl::_discoveryDelegateImpl = nullptr; +bool gAwaitingCommissionerPasscodeInput = false; +std::shared_ptr<matter::casting::core::CastingPlayer> targetCastingPlayer; DiscoveryDelegateImpl * DiscoveryDelegateImpl::GetInstance() { @@ -45,14 +49,17 @@ DiscoveryDelegateImpl * DiscoveryDelegateImpl::GetInstance() void DiscoveryDelegateImpl::HandleOnAdded(matter::casting::memory::Strong<matter::casting::core::CastingPlayer> player) { - ChipLogProgress(AppServer, "DiscoveryDelegateImpl::HandleOnAdded() called"); + ChipLogProgress(AppServer, "DiscoveryDelegateImpl::HandleOnAdded()"); if (commissionersCount == 0) { - ChipLogProgress(AppServer, "Select discovered Casting Player (start index = 0) to request commissioning"); - ChipLogProgress(AppServer, "Include the cgp flag to attempt the Commissioner-Generated Passcode commissioning flow"); - - ChipLogProgress(AppServer, "Example1 Commissionee Passcode: cast request 0"); - ChipLogProgress(AppServer, "Example2 Commissioner Passcode: cast request 0 cgp"); + ChipLogProgress(AppServer, "---- Awaiting user input ----"); + ChipLogProgress(AppServer, "Select a discovered Casting Player (start index = 0) to request commissioning."); + ChipLogProgress( + AppServer, + "Include the commissioner-generated-passcode flag to attempt the Commissioner-Generated passcode commissioning flow."); + ChipLogProgress(AppServer, "Example 1 Commissionee Passcode: cast request 0"); + ChipLogProgress(AppServer, "Example 2 Commissioner Passcode: cast request 0 commissioner-generated-passcode"); + ChipLogProgress(AppServer, "---- Awaiting user input ----"); } ChipLogProgress(AppServer, "Discovered CastingPlayer #%d", commissionersCount); ++commissionersCount; @@ -168,22 +175,98 @@ void SubscribeToMediaPlaybackCurrentState(matter::casting::memory::Strong<matter kMinIntervalFloorSeconds, kMaxIntervalCeilingSeconds); } +CHIP_ERROR InitCommissionableDataProvider(LinuxCommissionableDataProvider & provider, LinuxDeviceOptions & options) +{ + ChipLogProgress(Discovery, "InitCommissionableDataProvider()"); + chip::Optional<uint32_t> setupPasscode; + + if (options.payload.setUpPINCode != 0) + { + setupPasscode.SetValue(options.payload.setUpPINCode); + ChipLogProgress(Discovery, "InitCommissionableDataProvider() using setupPasscode: %d", setupPasscode.Value()); + } + else if (!options.spake2pVerifier.HasValue()) + { + uint32_t defaultTestPasscode = 0; + chip::DeviceLayer::TestOnlyCommissionableDataProvider TestOnlyCommissionableDataProvider; + VerifyOrDie(TestOnlyCommissionableDataProvider.GetSetupPasscode(defaultTestPasscode) == CHIP_NO_ERROR); + + ChipLogError(Support, + "InitCommissionableDataProvider() *** 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<unsigned>(defaultTestPasscode)); + setupPasscode.SetValue(defaultTestPasscode); + options.payload.setUpPINCode = defaultTestPasscode; + } + else + { + ChipLogError(Support, + "InitCommissionableDataProvider() *** WARNING: 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 the minimum PBKDF iterations (1,000) for this example implementation. For TV devices and TV casting app production + // implementations, you should use a higher number of PBKDF iterations to enhance security. The default minimum iterations are + // not sufficient against brute-force and rainbow table attacks. Increasing the number of iterations will increase the + // computational time required to derive the key. This can slow down the authentication process, especially on devices with + // limited processing power like a Raspberry Pi 4. For a production implementation, you should measure the actual performance on + // the target device. + uint32_t spake2pIterationCount = + chip::Crypto::kSpake2p_Min_PBKDF_Iterations; // 1,000 - Hypothetical key derivation time: ~20 milliseconds (ms). + // uint32_t spake2pIterationCount = chip::Crypto::kSpake2p_Max_PBKDF_Iterations; // 100,000 - Hypothetical key derivation time: + // ~2 seconds. + if (options.spake2pIterations == 1000) + { + spake2pIterationCount = options.spake2pIterations; + ChipLogError(Support, + "InitCommissionableDataProvider() *** WARNING: PASE PBKDF iterations provided are the minimum allowable: %u. " + "Increase for production use to enhance security. ***", + static_cast<unsigned>(spake2pIterationCount)); + } + else if ((options.spake2pIterations > 1000)) + { + spake2pIterationCount = options.spake2pIterations; + ChipLogProgress(Support, "InitCommissionableDataProvider() PASE PBKDF iterations set to: %u.", + static_cast<unsigned>(spake2pIterationCount)); + } + else + { + ChipLogError(Support, + "InitCommissionableDataProvider() *** WARNING: PASE PBKDF iterations set to the minimum allowable: %u. " + "Increase for production use to enhance security. ***", + static_cast<unsigned>(spake2pIterationCount)); + } + + return provider.Init(options.spake2pVerifier, options.spake2pSalt, spake2pIterationCount, setupPasscode, + options.payload.discriminator.GetLongValue()); +} + void ConnectionHandler(CHIP_ERROR err, matter::casting::core::CastingPlayer * castingPlayer) { - VerifyOrReturn(err == CHIP_NO_ERROR, - ChipLogProgress(AppServer, - "ConnectionHandler(): Failed to connect to CastingPlayer(ID: %s) with err %" CHIP_ERROR_FORMAT, - castingPlayer->GetId(), err.Format())); + ChipLogProgress(AppServer, "simple-app-helper.cpp::ConnectionHandler()"); - ChipLogProgress(AppServer, "ConnectionHandler(): Successfully connected to CastingPlayer(ID: %s)", castingPlayer->GetId()); - ChipLogProgress(AppServer, "ConnectionHandler(): Triggering demo interactions with CastingPlayer(ID: %s)", + // For a connection failure, called back with an error and nullptr. + VerifyOrReturn( + err == CHIP_NO_ERROR, + ChipLogProgress( + AppServer, + "simple-app-helper.cpp::ConnectionHandler(): Failed to connect to CastingPlayer (ID: %s) with err %" CHIP_ERROR_FORMAT, + targetCastingPlayer->GetId(), err.Format())); + + ChipLogProgress(AppServer, "simple-app-helper.cpp::ConnectionHandler(): Successfully connected to CastingPlayer (ID: %s)", + castingPlayer->GetId()); + ChipLogProgress(AppServer, + "simple-app-helper.cpp::ConnectionHandler(): Triggering demo interactions with CastingPlayer (ID: %s)", castingPlayer->GetId()); std::vector<matter::casting::memory::Strong<matter::casting::core::Endpoint>> endpoints = castingPlayer->GetEndpoints(); // Find the desired Endpoint and auto-trigger some Matter Casting demo interactions auto it = std::find_if(endpoints.begin(), endpoints.end(), [](const matter::casting::memory::Strong<matter::casting::core::Endpoint> & endpoint) { - return endpoint->GetVendorId() == 65521; + return endpoint->GetVendorId() == kDesiredEndpointVendorId; }); if (it != endpoints.end()) { @@ -201,7 +284,27 @@ void ConnectionHandler(CHIP_ERROR err, matter::casting::core::CastingPlayer * ca } else { - ChipLogError(AppServer, "Desired Endpoint not found on the CastingPlayer(ID: %s)", castingPlayer->GetId()); + ChipLogError( + AppServer, + "simple-app-helper.cpp::ConnectionHandler():Desired Endpoint Vendor Id (%d) not found on the CastingPlayer (ID: %s)", + kDesiredEndpointVendorId, castingPlayer->GetId()); + } +} + +void CommissionerDeclarationCallback(const chip::Transport::PeerAddress & source, + chip::Protocols::UserDirectedCommissioning::CommissionerDeclaration cd) +{ + ChipLogProgress(AppServer, + "simple-app-helper.cpp::CommissionerDeclarationCallback() called with CommissionerDeclaration message:"); + cd.DebugLog(); + if (cd.GetCommissionerPasscode()) + { + ChipLogProgress(AppServer, "---- Awaiting user input ----"); + ChipLogProgress(AppServer, "Input the Commissioner-Generated passcode displayed on the CastingPlayer UX."); + ChipLogProgress(AppServer, "Input 1245678 to use the default passcode."); + ChipLogProgress(AppServer, "Example: cast setcommissionerpasscode 12345678"); + ChipLogProgress(AppServer, "---- Awaiting user input ----"); + gAwaitingCommissionerPasscodeInput = true; } } @@ -245,28 +348,28 @@ CHIP_ERROR CommandHandler(int argc, char ** argv) matter::casting::core::CastingPlayerDiscovery::GetInstance()->GetCastingPlayers(); VerifyOrReturnValue(0 <= index && index < castingPlayers.size(), CHIP_ERROR_INVALID_ARGUMENT, ChipLogError(AppServer, "Invalid casting player index provided: %lu", index)); - std::shared_ptr<matter::casting::core::CastingPlayer> targetCastingPlayer = castingPlayers.at(index); + targetCastingPlayer = castingPlayers.at(index); matter::casting::core::IdentificationDeclarationOptions idOptions; if (argc == 3) { - if (strcmp(argv[2], "cgp") == 0) + if (strcmp(argv[2], "commissioner-generated-passcode") == 0) { - // Attempt Commissioner-Generated Passcode (cgp) commissioning flow only if the CastingPlayer indicates support for - // it. + // Attempt Commissioner-Generated Passcode (commissioner-generated-passcode) commissioning flow only if the + // CastingPlayer indicates support for it. if (targetCastingPlayer->GetSupportsCommissionerGeneratedPasscode()) { - ChipLogProgress( - AppServer, - "CommandHandler() request %lu cgp. Attempting the Commissioner-Generated Passcode commissioning flow", - index); + ChipLogProgress(AppServer, + "CommandHandler() request %lu commissioner-generated-passcode. Attempting the " + "Commissioner-Generated Passcode commissioning flow", + index); idOptions.mCommissionerPasscode = true; } else { ChipLogError(AppServer, - "CommandHandler() request %lu cgp. Selected CastingPLayer does not support the " - "Commissioner-Generated Passcode commissioning flow", + "CommandHandler() request %lu commissioner-generated-passcode. Selected CastingPLayer does not " + "support the Commissioner-Generated Passcode commissioning flow", index); } } @@ -279,10 +382,67 @@ CHIP_ERROR CommandHandler(int argc, char ** argv) ChipLogError(AppServer, "CommandHandler() request, failed to add targetAppInfo: %" CHIP_ERROR_FORMAT, result.Format()); } - targetCastingPlayer->VerifyOrEstablishConnection(ConnectionHandler, matter::casting::core::kCommissioningWindowTimeoutSec, + matter::casting::core::ConnectionCallbacks connectionCallbacks; + connectionCallbacks.mOnConnectionComplete = ConnectionHandler; + // Provide an handler (Optional) for Commissioner's CommissionerDeclaration messages. The CommissionerDeclaration messages + // provide information indicating the Commissioner's pre-commissioning state. + connectionCallbacks.mCommissionerDeclarationCallback = CommissionerDeclarationCallback; + + targetCastingPlayer->VerifyOrEstablishConnection(connectionCallbacks, matter::casting::core::kCommissioningWindowTimeoutSec, idOptions); return CHIP_NO_ERROR; } + if (strcmp(argv[0], "setcommissionerpasscode") == 0) + { + ChipLogProgress(AppServer, "CommandHandler() setcommissionerpasscode"); + if (argc < 2) + { + return PrintAllCommands(); + } + char * eptr; + uint32_t passcode = (uint32_t) strtol(argv[1], &eptr, 10); + if (gAwaitingCommissionerPasscodeInput) + { + ChipLogProgress(AppServer, "CommandHandler() setcommissionerpasscode user enterd passcode: %d", passcode); + gAwaitingCommissionerPasscodeInput = false; + + // Per connectedhomeip/examples/platform/linux/LinuxCommissionableDataProvider.h: We don't support overriding the + // passcode post-init (it is deprecated!). Therefore we need to initiate a new provider with the user entered + // Commissioner-generated passcode, and then update the CastigApp's AppParameters to update the commissioning session's + // passcode. + LinuxDeviceOptions::GetInstance().payload.setUpPINCode = passcode; + LinuxCommissionableDataProvider gCommissionableDataProvider; + CHIP_ERROR err = CHIP_NO_ERROR; + err = InitCommissionableDataProvider(gCommissionableDataProvider, LinuxDeviceOptions::GetInstance()); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, + "CommandHandler() setcommissionerpasscode InitCommissionableDataProvider() err %" CHIP_ERROR_FORMAT, + err.Format()); + } + // Update the CommissionableDataProvider stored in this CastingApp's AppParameters and the CommissionableDataProvider to + // be used for the commissioning session. + err = matter::casting::core::CastingApp::GetInstance()->UpdateCommissionableDataProvider(&gCommissionableDataProvider); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, + "CommandHandler() setcommissionerpasscode InitCommissionableDataProvider() err %" CHIP_ERROR_FORMAT, + err.Format()); + } + + matter::casting::core::ConnectionCallbacks connectionCallbacks; + connectionCallbacks.mOnConnectionComplete = ConnectionHandler; + + // Continue Connecting to the target CastingPlayer with the user entered Commissioner-generated Passcode. + targetCastingPlayer->ContinueConnecting(connectionCallbacks, matter::casting::core::kCommissioningWindowTimeoutSec); + } + else + { + ChipLogError( + AppServer, + "CommandHandler() setcommissionerpasscode, no Commissioner-Generated passcode input expected at this time."); + } + } if (strcmp(argv[0], "print-bindings") == 0) { PrintBindings(); @@ -315,11 +475,15 @@ CHIP_ERROR PrintAllCommands() streamer_printf(sout, " discover Discover Casting Players. Usage: cast discover\r\n"); streamer_printf(sout, " stop-discovery Stop Discovery of Casting Players. Usage: cast stop-discovery\r\n"); streamer_printf(sout, - " request <index> Request connecting to discovered Casting Player with [index] using the " - "Commissionee-Generated Passcode commissioning flow. Usage: cast request 0\r\n"); + " request <index> Request connecting to discovered Casting Player with " + "[index] using the Commissionee-Generated passcode commissioning flow. Usage: cast request 0\r\n"); + streamer_printf(sout, + " request <index> commissioner-generated-passcode Request connecting to discovered Casting Player with " + "[index] using the Commissioner-Generated passcode commissioning flow. Usage: cast request 0 cgp\r\n"); streamer_printf(sout, - " request <index> cgp Request connecting to discovered Casting Player with [index] using the " - "Commissioner-Generated Passcode commissioning flow. Usage: cast request 0 cgp\r\n"); + " setcommissionerpasscode <passcode> Set the commissioning session's passcode to the " + "Commissioner-Generated passcode. Used for the the Commissioner-Generated passcode commissioning flow. Usage: " + "cast setcommissionerpasscode 12345678\r\n"); streamer_printf(sout, "\r\n"); return CHIP_NO_ERROR; diff --git a/examples/tv-casting-app/linux/simple-app-helper.h b/examples/tv-casting-app/linux/simple-app-helper.h index f8826b5dfe021f..f3bdc6edfa3bc9 100644 --- a/examples/tv-casting-app/linux/simple-app-helper.h +++ b/examples/tv-casting-app/linux/simple-app-helper.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,6 +22,8 @@ #include "core/CastingPlayerDiscovery.h" #include "core/IdentificationDeclarationOptions.h" #include "core/Types.h" +#include <LinuxCommissionableDataProvider.h> +#include <Options.h> #include <platform/CHIPDeviceLayer.h> /** @@ -68,6 +70,12 @@ class DiscoveryDelegateImpl : public matter::casting::core::DiscoveryDelegate void HandleOnUpdated(matter::casting::memory::Strong<matter::casting::core::CastingPlayer> player) override; }; +/** + * @brief Initializes a LinuxCommissionableDataProvider using configuration options provided via a LinuxDeviceOptions. It first + * checks if a setup PIN code is specified; if not, it attempts to use a temporary default passcode. + */ +CHIP_ERROR InitCommissionableDataProvider(LinuxCommissionableDataProvider & provider, LinuxDeviceOptions & options); + /** * @brief Linux tv-casting-app's onCompleted handler for CastingPlayer.VerifyOrEstablishConnection API */ diff --git a/examples/tv-casting-app/linux/simple-app.cpp b/examples/tv-casting-app/linux/simple-app.cpp index 01e33469e9b73f..41f3391da73e78 100644 --- a/examples/tv-casting-app/linux/simple-app.cpp +++ b/examples/tv-casting-app/linux/simple-app.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -45,47 +45,6 @@ using namespace matter::casting::support; // To hold SPAKE2+ verifier, discriminator, passcode LinuxCommissionableDataProvider gCommissionableDataProvider; -CHIP_ERROR InitCommissionableDataProvider(LinuxCommissionableDataProvider & provider, LinuxDeviceOptions & options) -{ - chip::Optional<uint32_t> 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<unsigned>(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<unsigned>(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. */ diff --git a/examples/tv-casting-app/tv-casting-common/BUILD.gn b/examples/tv-casting-app/tv-casting-common/BUILD.gn index a58d272abe88bf..3899773592e5dd 100644 --- a/examples/tv-casting-app/tv-casting-common/BUILD.gn +++ b/examples/tv-casting-app/tv-casting-common/BUILD.gn @@ -107,6 +107,8 @@ chip_data_model("tv-casting-common") { "core/CastingPlayerDiscovery.h", "core/Command.h", "core/CommissionerDeclarationHandler.cpp", + "core/CommissionerDeclarationHandler.h", + "core/ConnectionCallbacks.h", "core/Endpoint.cpp", "core/Endpoint.h", "core/Types.h", diff --git a/examples/tv-casting-app/tv-casting-common/core/CastingApp.cpp b/examples/tv-casting-app/tv-casting-common/core/CastingApp.cpp index 7e000134190daf..216d06b062b50e 100644 --- a/examples/tv-casting-app/tv-casting-common/core/CastingApp.cpp +++ b/examples/tv-casting-app/tv-casting-common/core/CastingApp.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,6 +19,7 @@ #include "CastingApp.h" #include "CommissionerDeclarationHandler.h" +#include "ConnectionCallbacks.h" #include "support/CastingStore.h" #include "support/ChipDeviceEventHandler.h" @@ -85,37 +86,51 @@ CHIP_ERROR CastingApp::Initialize(const AppParameters & appParameters) return CHIP_NO_ERROR; } +CHIP_ERROR CastingApp::UpdateCommissionableDataProvider(chip::DeviceLayer::CommissionableDataProvider * commissionableDataProvider) +{ + ChipLogProgress(Discovery, "CastingApp::UpdateCommissionableDataProvider()"); + chip::DeviceLayer::SetCommissionableDataProvider(commissionableDataProvider); + return mAppParameters->SetCommissionableDataProvider(commissionableDataProvider); +} + +void ReconnectHandler(CHIP_ERROR err, matter::casting::core::CastingPlayer * castingPlayer) +{ + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "CastingApp::ConnectionHandler() Could not reconnect to CastingPlayer %" CHIP_ERROR_FORMAT, + err.Format()); + } + else + { + ChipLogProgress(AppServer, "CastingApp::ConnectionHandler() Reconnected to CastingPlayer(ID: %s)", castingPlayer->GetId()); + } +} + CHIP_ERROR CastingApp::Start() { - ChipLogProgress(Discovery, "CastingApp::Start() called"); + ChipLogProgress(Discovery, "CastingApp::Start()"); VerifyOrReturnError(mState == CASTING_APP_NOT_RUNNING, CHIP_ERROR_INCORRECT_STATE); - // start Matter server + // 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 + // Perform post server startup registrations ReturnErrorOnFailure(PostStartRegistrations()); - // reconnect (or verify connection) to the CastingPlayer that the app was connected to before being stopped, if any + // Reconnect (or verify connection) to the CastingPlayer that the app was connected to before being stopped, if any if (CastingPlayer::GetTargetCastingPlayer() != nullptr) { + matter::casting::core::ConnectionCallbacks connectionCallbacks; + connectionCallbacks.mOnConnectionComplete = ReconnectHandler; + // Re-connecting to a CastingPLayer does not require a full User Directed Commissioning (UDC) process so + // CommissionerDeclaration messages are not expected. Leaving ConnectionCallbacks mCommissionerDeclarationCallback as + // nullptr. ChipLogProgress( Discovery, "CastingApp::Start() calling VerifyOrEstablishConnection() to reconnect (or verify connection) to a CastingPlayer"); - CastingPlayer::GetTargetCastingPlayer()->VerifyOrEstablishConnection( - [](CHIP_ERROR err, matter::casting::core::CastingPlayer * castingPlayer) { - if (err != CHIP_NO_ERROR) - { - ChipLogError(AppServer, "CastingApp::Start Could not reconnect to CastingPlayer %" CHIP_ERROR_FORMAT, - err.Format()); - } - else - { - ChipLogProgress(AppServer, "CastingApp::Start Reconnected to CastingPlayer(ID: %s)", castingPlayer->GetId()); - } - }); + CastingPlayer::GetTargetCastingPlayer()->VerifyOrEstablishConnection(connectionCallbacks); } return CHIP_NO_ERROR; @@ -123,7 +138,7 @@ CHIP_ERROR CastingApp::Start() CHIP_ERROR CastingApp::PostStartRegistrations() { - ChipLogProgress(Discovery, "CastingApp::PostStartRegistrations() called"); + ChipLogProgress(Discovery, "CastingApp::PostStartRegistrations()"); VerifyOrReturnError(mState == CASTING_APP_NOT_RUNNING, CHIP_ERROR_INCORRECT_STATE); auto & server = chip::Server::GetInstance(); @@ -140,8 +155,12 @@ CHIP_ERROR CastingApp::PostStartRegistrations() // Register DeviceEvent Handler ReturnErrorOnFailure(chip::DeviceLayer::PlatformMgrImpl().AddEventHandler(ChipDeviceEventHandler::Handle, 0)); + ChipLogProgress( + Discovery, + "CastingApp::PostStartRegistrations() calling GetUserDirectedCommissioningClient()->SetCommissionerDeclarationHandler()"); #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT - // Set a handler for Commissioner's CommissionerDeclaration messages. + // Set a handler for Commissioner's CommissionerDeclaration messages. This is set in + // connectedhomeip/src/protocols/user_directed_commissioning/UserDirectedCommissioning.h chip::Server::GetInstance().GetUserDirectedCommissioningClient()->SetCommissionerDeclarationHandler( CommissionerDeclarationHandler::GetInstance()); #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT @@ -152,7 +171,7 @@ CHIP_ERROR CastingApp::PostStartRegistrations() CHIP_ERROR CastingApp::Stop() { - ChipLogProgress(Discovery, "CastingApp::Stop() called"); + ChipLogProgress(Discovery, "CastingApp::Stop()"); VerifyOrReturnError(mState == CASTING_APP_RUNNING, CHIP_ERROR_INCORRECT_STATE); #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT diff --git a/examples/tv-casting-app/tv-casting-common/core/CastingApp.h b/examples/tv-casting-app/tv-casting-common/core/CastingApp.h index f286aad3cbde7f..25ab479ae3f718 100644 --- a/examples/tv-casting-app/tv-casting-common/core/CastingApp.h +++ b/examples/tv-casting-app/tv-casting-common/core/CastingApp.h @@ -52,6 +52,14 @@ class CastingApp */ CHIP_ERROR Initialize(const matter::casting::support::AppParameters & appParameters); + /** + * @brief Update the CommissionableDataProvider stored in this CastingApp's AppParameters and the CommissionableDataProvider to + * be used for the commissioning session. + * @param commissionableDataProvider the new CommissionableDataProvider to be used for the next commissioning session. + * + */ + CHIP_ERROR UpdateCommissionableDataProvider(chip::DeviceLayer::CommissionableDataProvider * commissionableDataProvider); + /** * @brief Starts the Matter server that the CastingApp runs on and registers all the necessary delegates * CastingApp. diff --git a/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.cpp b/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.cpp index a712c1733dc1a8..b3075be95304d8 100644 --- a/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.cpp +++ b/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.cpp @@ -29,7 +29,9 @@ namespace core { CastingPlayer * CastingPlayer::mTargetCastingPlayer = nullptr; -void CastingPlayer::VerifyOrEstablishConnection(ConnectCallback onCompleted, unsigned long long int commissioningWindowTimeoutSec, +// TODO: Verify why commissioningWindowTimeoutSec is a "unsigned long long int" type. Seems too big. +void CastingPlayer::VerifyOrEstablishConnection(ConnectionCallbacks connectionCallbacks, + unsigned long long int commissioningWindowTimeoutSec, IdentificationDeclarationOptions idOptions) { ChipLogProgress(AppServer, "CastingPlayer::VerifyOrEstablishConnection() called"); @@ -46,12 +48,30 @@ void CastingPlayer::VerifyOrEstablishConnection(ConnectCallback onCompleted, uns ChipLogError( AppServer, "CastingPlayer::VerifyOrEstablishConnection() called while already connecting/connected to this CastingPlayer")); + VerifyOrExit( + connectionCallbacks.mOnConnectionComplete != nullptr, + ChipLogError(AppServer, + "CastingPlayer::VerifyOrEstablishConnection() ConnectionCallbacks.mOnConnectionComplete was not provided")); mConnectionState = CASTING_PLAYER_CONNECTING; - mOnCompleted = onCompleted; + mOnCompleted = connectionCallbacks.mOnConnectionComplete; mCommissioningWindowTimeoutSec = commissioningWindowTimeoutSec; mTargetCastingPlayer = this; mIdOptions = idOptions; + // Register the handler for Commissioner's CommissionerDeclaration messages. The CommissionerDeclaration messages provide + // information indicating the Commissioner's pre-commissioning state. + if (connectionCallbacks.mCommissionerDeclarationCallback != nullptr) + { + matter::casting::core::CommissionerDeclarationHandler::GetInstance()->SetCommissionerDeclarationCallback( + connectionCallbacks.mCommissionerDeclarationCallback); + } + else + { + ChipLogProgress( + AppServer, + "CastingPlayer::VerifyOrEstablishConnection() CommissionerDeclarationCallback not provided in ConnectionCallbacks"); + } + // If *this* CastingPlayer was previously connected to, its nodeId, fabricIndex and other attributes should be present // in the CastingStore cache. If that is the case, AND, the cached data contains the endpoint desired by the client, if any, // as per IdentificationDeclarationOptions.mTargetAppInfos, simply Find or Re-establish the CASE session and return early. @@ -72,7 +92,7 @@ void CastingPlayer::VerifyOrEstablishConnection(ConnectCallback onCompleted, uns "CastingPlayer::VerifyOrEstablishConnection() calling FindOrEstablishSession on cached CastingPlayer"); *this = cachedCastingPlayers[index]; mConnectionState = CASTING_PLAYER_CONNECTING; - mOnCompleted = onCompleted; + mOnCompleted = connectionCallbacks.mOnConnectionComplete; mCommissioningWindowTimeoutSec = commissioningWindowTimeoutSec; FindOrEstablishSession( @@ -118,9 +138,33 @@ void CastingPlayer::VerifyOrEstablishConnection(ConnectCallback onCompleted, uns } else { + ChipLogProgress(AppServer, + "CastingPlayer::VerifyOrEstablishConnection() verifying User Directed Commissioning (UDC) state"); + mIdOptions.LogDetail(); + SuccessOrExit(err = support::ChipDeviceEventHandler::SetUdcStatus(true)); + + if (!GetSupportsCommissionerGeneratedPasscode() && mIdOptions.mCommissionerPasscode) + { + ChipLogError( + AppServer, + "CastingPlayer::VerifyOrEstablishConnection() the target CastingPlayer doesn't support Commissioner-Generated " + "passcode yet IdentificationDeclarationOptions.mCommissionerPasscode is set to true"); + SuccessOrExit(err = CHIP_ERROR_INVALID_ARGUMENT); + } + if (!matter::casting::core::CommissionerDeclarationHandler::GetInstance()->HasCommissionerDeclarationCallback() && + mIdOptions.mCommissionerPasscode) + { + ChipLogError(AppServer, + "CastingPlayer::VerifyOrEstablishConnection() the CommissionerDeclaration message callback has not been " + "set yet IdentificationDeclarationOptions.mCommissionerPasscode is set to true"); + SuccessOrExit(err = CHIP_ERROR_INVALID_ARGUMENT); + } + + ChipLogProgress(AppServer, "CastingPlayer::VerifyOrEstablishConnection() calling OpenBasicCommissioningWindow()"); SuccessOrExit(err = chip::Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow( chip::System::Clock::Seconds16(mCommissioningWindowTimeoutSec))); + ChipLogProgress(AppServer, "CastingPlayer::VerifyOrEstablishConnection() calling SendUserDirectedCommissioningRequest()"); #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT SuccessOrExit(err = SendUserDirectedCommissioningRequest()); #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT @@ -130,10 +174,65 @@ void CastingPlayer::VerifyOrEstablishConnection(ConnectCallback onCompleted, uns if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "CastingPlayer::VerifyOrEstablishConnection() failed with %" CHIP_ERROR_FORMAT, err.Format()); - support::ChipDeviceEventHandler::SetUdcStatus(false); - mConnectionState = CASTING_PLAYER_NOT_CONNECTED; - mCommissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec; - mTargetCastingPlayer = nullptr; + resetState(err); + } +} + +void CastingPlayer::ContinueConnecting(ConnectionCallbacks connectionCallbacks, + unsigned long long int commissioningWindowTimeoutSec) +{ + ChipLogProgress(AppServer, "CastingPlayer::ContinueConnecting()"); + CHIP_ERROR err = CHIP_NO_ERROR; + SuccessOrExit(err = support::ChipDeviceEventHandler::SetUdcStatus(true)); + VerifyOrExit( + connectionCallbacks.mOnConnectionComplete != nullptr, + ChipLogError(AppServer, "CastingPlayer::ContinueConnecting() ConnectionCallbacks.mOnConnectionComplete was not provided")); + mConnectionState = CASTING_PLAYER_CONNECTING; + mOnCompleted = connectionCallbacks.mOnConnectionComplete; + mCommissioningWindowTimeoutSec = commissioningWindowTimeoutSec; + mTargetCastingPlayer = this; + + // Register the handler for Commissioner's CommissionerDeclaration messages. The CommissionerDeclaration messages provide + // information indicating the Commissioner's pre-commissioning state. + if (connectionCallbacks.mCommissionerDeclarationCallback != nullptr) + { + matter::casting::core::CommissionerDeclarationHandler::GetInstance()->SetCommissionerDeclarationCallback( + connectionCallbacks.mCommissionerDeclarationCallback); + } + else + { + ChipLogProgress( + AppServer, + "CastingPlayer::VerifyOrEstablishConnection() CommissionerDeclarationCallback not provided in ConnectionCallbacks"); + } + + ChipLogProgress(AppServer, "CastingPlayer::ContinueConnecting() calling OpenBasicCommissioningWindow()"); + SuccessOrExit(err = chip::Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow( + chip::System::Clock::Seconds16(mCommissioningWindowTimeoutSec))); + + mIdOptions.mCommissionerPasscodeReady = true; + ChipLogProgress(AppServer, "CastingPlayer::ContinueConnecting() calling SendUserDirectedCommissioningRequest()"); +#if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT + SuccessOrExit(err = SendUserDirectedCommissioningRequest()); +#endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "CastingPlayer::ContinueConnecting() failed with %" CHIP_ERROR_FORMAT, err.Format()); + resetState(err); + } +} + +void CastingPlayer::resetState(CHIP_ERROR err) +{ + ChipLogProgress(AppServer, "CastingPlayer::resetState()"); + support::ChipDeviceEventHandler::SetUdcStatus(false); + mConnectionState = CASTING_PLAYER_NOT_CONNECTED; + mCommissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec; + mTargetCastingPlayer = nullptr; + if (mOnCompleted) + { mOnCompleted(err, nullptr); mOnCompleted = nullptr; } @@ -171,8 +270,6 @@ CHIP_ERROR CastingPlayer::SendUserDirectedCommissioningRequest() VerifyOrReturnValue(ipAddressToUse != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(AppServer, "No IP Address found to send UDC request to")); - ReturnErrorOnFailure(support::ChipDeviceEventHandler::SetUdcStatus(true)); - chip::Protocols::UserDirectedCommissioning::IdentificationDeclaration id = mIdOptions.buildIdentificationDeclarationMessage(); ReturnErrorOnFailure(chip::Server::GetInstance().SendUserDirectedCommissioningRequest( @@ -246,7 +343,7 @@ bool CastingPlayer::ContainsDesiredTargetApp( void CastingPlayer::LogDetail() const { - ChipLogProgress(AppServer, "CastingPlayer::LogDetail() called"); + ChipLogProgress(AppServer, "CastingPlayer::LogDetail()"); if (strlen(mAttributes.id) != 0) { ChipLogDetail(AppServer, "\tID: %s", mAttributes.id); @@ -308,6 +405,7 @@ ConnectionContext::ConnectionContext(void * clientContext, core::CastingPlayer * chip::OnDeviceConnected onDeviceConnectedFn, chip::OnDeviceConnectionFailure onDeviceConnectionFailureFn) { + ChipLogProgress(AppServer, "CastingPlayer::ConnectionContext()"); mClientContext = clientContext; mTargetCastingPlayer = targetCastingPlayer; mOnDeviceConnectedFn = onDeviceConnectedFn; diff --git a/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.h b/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.h index 2748eafc22cc7e..14a55e045fcb83 100644 --- a/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.h +++ b/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.h @@ -19,6 +19,7 @@ #pragma once #include "CommissionerDeclarationHandler.h" +#include "ConnectionCallbacks.h" #include "Endpoint.h" #include "IdentificationDeclarationOptions.h" #include "Types.h" @@ -86,7 +87,6 @@ enum ConnectionState class ConnectionContext; class CastingPlayer; -using ConnectCallback = std::function<void(CHIP_ERROR, CastingPlayer *)>; /** * @brief CastingPlayer represents a Matter commissioner that is able to play media to a physical @@ -119,22 +119,39 @@ class CastingPlayer : public std::enable_shared_from_this<CastingPlayer> /** * @brief Verifies that a connection exists with this CastingPlayer, or triggers a new session * request. If the CastingApp does not have the nodeId and fabricIndex of this CastingPlayer cached on disk, - * this will execute the user directed commissioning process. + * this will execute the User Directed Commissioning (UDC) process. * - * @param onCompleted for success - called back with CHIP_NO_ERROR and CastingPlayer *. - * For failure - called back with an error and nullptr. + * @param connectionCallbacks contains the ConnectCallback and CommissionerDeclarationCallback (Optional). * @param commissioningWindowTimeoutSec (Optional) time (in sec) to keep the commissioning window open, if commissioning is * required. Needs to be >= kCommissioningWindowTimeoutSec. * @param idOptions (Optional) Parameters in the IdentificationDeclaration message sent by the Commissionee to the Commissioner. * These parameters specify the information relating to the requested commissioning session. * Furthermore, attributes (such as VendorId) describe the TargetApp that the client wants to interact with after commissioning. - * If this value is passed in, VerifyOrEstablishConnection() will force User Directed Commissioning, in case the desired + * If this value is passed in, VerifyOrEstablishConnection() will force UDC, in case the desired * TargetApp is not found in the on-device CastingStore. */ - void VerifyOrEstablishConnection(ConnectCallback onCompleted, + void VerifyOrEstablishConnection(ConnectionCallbacks connectionCallbacks, unsigned long long int commissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec, IdentificationDeclarationOptions idOptions = IdentificationDeclarationOptions()); + /** + * @brief Continues the UDC process during the Commissioner-Generated passcode commissioning flow by sending a second + * IdentificationDeclaration to Commissioner containing CommissionerPasscode and CommissionerPasscodeReady set to true. At this + * point it is assumed that the following have occurred: + * 1. Client has handled the Commissioner's CommissionerDecelration message with PasscodeDialogDisplayed and + * CommissionerPasscode set to true. + * 2. Client prompted user to input Passcode from Commissioner. + * 3. Client has updated the commissioning session's PAKE verifier using the user input Passcode by updating the CastingApps + * CommissionableDataProvider + * (matter::casting::core::CastingApp::GetInstance()->UpdateCommissionableDataProvider(CommissionableDataProvider)). + * + * @param connectionCallbacks contains the ConnectCallback and CommissionerDeclarationCallback (Optional). + * @param commissioningWindowTimeoutSec (Optional) time (in sec) to keep the commissioning window open, if commissioning is + * required. Needs to be >= kCommissioningWindowTimeoutSec. + */ + void ContinueConnecting(ConnectionCallbacks connectionCallbacks, + unsigned long long int commissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec); + /** * @brief Sets the internal connection state of this CastingPlayer to "disconnected" */ @@ -206,6 +223,12 @@ class CastingPlayer : public std::enable_shared_from_this<CastingPlayer> unsigned long long int mCommissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec; ConnectCallback mOnCompleted = {}; + /** + * @brief resets this CastingPlayer's state and calls mOnCompleted with the CHIP_ERROR. Also, after calling mOnCompleted, it + * clears mOnCompleted by setting it to a nullptr. + */ + void resetState(CHIP_ERROR err); + #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT /** * @brief Sends the user directed commissioning request to this CastingPlayer diff --git a/examples/tv-casting-app/tv-casting-common/core/CommissionerDeclarationHandler.cpp b/examples/tv-casting-app/tv-casting-common/core/CommissionerDeclarationHandler.cpp index 1bedc6ce316a39..bb9ecc3c4c57d9 100644 --- a/examples/tv-casting-app/tv-casting-common/core/CommissionerDeclarationHandler.cpp +++ b/examples/tv-casting-app/tv-casting-common/core/CommissionerDeclarationHandler.cpp @@ -19,6 +19,8 @@ #include "CommissionerDeclarationHandler.h" #include "Types.h" +#include "support/ChipDeviceEventHandler.h" +#include <app/server/Server.h> namespace matter { namespace casting { @@ -36,14 +38,36 @@ CommissionerDeclarationHandler * CommissionerDeclarationHandler::GetInstance() } // TODO: In the following PRs. Implement setHandler() for CommissionerDeclaration messages and expose messages to higher layers for -// Linux, Android and iOS. +// Linux(DONE), Android(pending) and iOS(pending). void CommissionerDeclarationHandler::OnCommissionerDeclarationMessage( const chip::Transport::PeerAddress & source, chip::Protocols::UserDirectedCommissioning::CommissionerDeclaration cd) { - ChipLogProgress(AppServer, "CommissionerDeclarationHandler::OnCommissionerDeclarationMessage() called TODO: handle message"); - cd.DebugLog(); + ChipLogProgress(AppServer, + "CommissionerDeclarationHandler::OnCommissionerDeclarationMessage(), calling CloseCommissioningWindow()"); + // Close the commissioning window. Since we recived a CommissionerDeclaration message from the Commissioner, we know that + // commissioning via AccountLogin cluster failed. We will open a new commissioningWindow prior to sending the next + // IdentificationDeclaration Message to the Commissioner. + chip::Server::GetInstance().GetCommissioningWindowManager().CloseCommissioningWindow(); + support::ChipDeviceEventHandler::SetUdcStatus(false); + + if (mCmmissionerDeclarationCallback_) + { + mCmmissionerDeclarationCallback_(source, cd); + } + else + { + ChipLogError(AppServer, + "CommissionerDeclarationHandler::OnCommissionerDeclarationMessage() mCmmissionerDeclarationCallback_ not set"); + } +} + +void CommissionerDeclarationHandler::SetCommissionerDeclarationCallback( + matter::casting::core::CommissionerDeclarationCallback callback) +{ + ChipLogProgress(AppServer, "CommissionerDeclarationHandler::SetCommissionerDeclarationCallback()"); + mCmmissionerDeclarationCallback_ = std::move(callback); } -}; // namespace core -}; // namespace casting -}; // namespace matter +} // namespace core +} // namespace casting +} // namespace matter diff --git a/examples/tv-casting-app/tv-casting-common/core/CommissionerDeclarationHandler.h b/examples/tv-casting-app/tv-casting-common/core/CommissionerDeclarationHandler.h index e84ca59a59e23d..ac4189267193c1 100644 --- a/examples/tv-casting-app/tv-casting-common/core/CommissionerDeclarationHandler.h +++ b/examples/tv-casting-app/tv-casting-common/core/CommissionerDeclarationHandler.h @@ -18,6 +18,7 @@ #pragma once +#include "ConnectionCallbacks.h" #include "Types.h" namespace matter { @@ -25,7 +26,8 @@ namespace casting { namespace core { /** - * @brief React to the Commissioner's CommissionerDeclaration messages with this singleton. + * @brief React to the Commissioner's CommissionerDeclaration messages with this singleton. This is an implementation of the + * CommissionerDeclarationHandler from connectedhomeip/src/protocols/user_directed_commissioning/UserDirectedCommissioning.h. */ class CommissionerDeclarationHandler : public chip::Protocols::UserDirectedCommissioning::CommissionerDeclarationHandler { @@ -35,15 +37,33 @@ class CommissionerDeclarationHandler : public chip::Protocols::UserDirectedCommi static CommissionerDeclarationHandler * GetInstance(); + /** + * @brief Called when a Commissioner Declaration UDC message has been received. + * @param[in] source The source of the Commissioner Declaration message. + * @param[in] cd The Commissioner Declaration message. + */ void OnCommissionerDeclarationMessage(const chip::Transport::PeerAddress & source, chip::Protocols::UserDirectedCommissioning::CommissionerDeclaration cd) override; + /** + * @brief OnCommissionerDeclarationMessage() will call the CommissionerDeclarationCallback set by this function. + */ + void SetCommissionerDeclarationCallback(CommissionerDeclarationCallback callback); + + /** + * @brief returns true if the CommissionerDeclarationHandler sigleton has a CommissionerDeclarationCallback set, false + * otherwise. + */ + bool HasCommissionerDeclarationCallback() { return static_cast<bool>(mCmmissionerDeclarationCallback_); }; + private: static CommissionerDeclarationHandler * sCommissionerDeclarationHandler_; + CommissionerDeclarationCallback mCmmissionerDeclarationCallback_; + CommissionerDeclarationHandler() {} ~CommissionerDeclarationHandler() {} }; -}; // namespace core -}; // namespace casting -}; // namespace matter +} // namespace core +} // namespace casting +} // namespace matter diff --git a/examples/tv-casting-app/tv-casting-common/core/ConnectionCallbacks.h b/examples/tv-casting-app/tv-casting-common/core/ConnectionCallbacks.h new file mode 100644 index 00000000000000..01badaaf059dad --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/core/ConnectionCallbacks.h @@ -0,0 +1,61 @@ +/* + * + * 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. + */ + +#pragma once + +#include "Types.h" + +namespace matter { +namespace casting { +namespace core { + +class CastingPlayer; + +/** + * @brief Called when the User Directed Commissioning (UDC) process succeeds or fails. + * @param[in] err For success, called back with CHIP_NO_ERROR. For failure, called back with an error. + * @param[in] castingPlayer For success, called back with a CastingPlayer *. For failure, called back with a nullptr. + */ +using ConnectCallback = std::function<void(CHIP_ERROR err, CastingPlayer * castingPlayer)>; + +/** + * @brief Called when a Commissioner Declaration UDC message has been received. + * @param[in] source The source of the Commissioner Declaration message. + * @param[in] cd The Commissioner Declaration message. + */ +using CommissionerDeclarationCallback = std::function<void( + const chip::Transport::PeerAddress & source, const chip::Protocols::UserDirectedCommissioning::CommissionerDeclaration cd)>; + +/** + * @brief A container struct for User Directed Commissioning (UDC) callbacks. + */ +struct ConnectionCallbacks +{ + /** + * The callback called when the connection process has ended, regardless of whether it was successful or not. + */ + ConnectCallback mOnConnectionComplete = nullptr; + /** + * The callback called when the Commissionee receives a CommissionerDeclaration message from the Commissioner. + */ + CommissionerDeclarationCallback mCommissionerDeclarationCallback = nullptr; +}; + +} // namespace core +} // namespace casting +} // namespace matter diff --git a/examples/tv-casting-app/tv-casting-common/core/IdentificationDeclarationOptions.h b/examples/tv-casting-app/tv-casting-common/core/IdentificationDeclarationOptions.h index af1ae2aaa79d62..d0e8f8a4411b69 100644 --- a/examples/tv-casting-app/tv-casting-common/core/IdentificationDeclarationOptions.h +++ b/examples/tv-casting-app/tv-casting-common/core/IdentificationDeclarationOptions.h @@ -19,6 +19,7 @@ #pragma once #include "Types.h" +#include <app/server/Dnssd.h> #include <vector> namespace matter { @@ -61,8 +62,15 @@ class IdentificationDeclarationOptions */ bool mCancelPasscode = false; + /** + * Commissionee's (random) DNS-SD instance name. This field is mandatory and will be auto generated if not provided by the + * client. + */ + char mCommissioneeInstanceName[chip::Dnssd::Commission::kInstanceNameMaxLength + 1] = ""; + CHIP_ERROR addTargetAppInfo(const chip::Protocols::UserDirectedCommissioning::TargetAppInfo & targetAppInfo) { + ChipLogProgress(AppServer, "IdentificationDeclarationOptions::addTargetAppInfo()"); if (mTargetAppInfos.size() >= CHIP_DEVICE_CONFIG_UDC_MAX_TARGET_APPS) { ChipLogError(AppServer, @@ -94,23 +102,54 @@ class IdentificationDeclarationOptions id.SetNoPasscode(mNoPasscode); id.SetCdUponPasscodeDialog(mCdUponPasscodeDialog); id.SetCancelPasscode(mCancelPasscode); - - ChipLogProgress(AppServer, - "IdentificationDeclarationOptions::buildIdentificationDeclarationMessage() mCommissionerPasscode: %s", - mCommissionerPasscode ? "true" : "false"); id.SetCommissionerPasscode(mCommissionerPasscode); - - ChipLogProgress(AppServer, - "IdentificationDeclarationOptions::buildIdentificationDeclarationMessage() mCommissionerPasscodeReady: %s", - mCommissionerPasscodeReady ? "true" : "false"); if (mCommissionerPasscodeReady) { id.SetCommissionerPasscodeReady(true); + id.SetInstanceName(mCommissioneeInstanceName); mCommissionerPasscodeReady = false; } + else + { + ChipLogProgress(AppServer, + "IdentificationDeclarationOptions::buildIdentificationDeclarationMessage() generating InstanceName"); + CHIP_ERROR err = chip::app::DnssdServer::Instance().GetCommissionableInstanceName(mCommissioneeInstanceName, + sizeof(mCommissioneeInstanceName)); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, + "IdentificationDeclarationOptions::buildIdentificationDeclarationMessage() Failed to get mdns " + "instance name error: %" CHIP_ERROR_FORMAT, + err.Format()); + } + else + { + id.SetInstanceName(mCommissioneeInstanceName); + ChipLogProgress(AppServer, + "IdentificationDeclarationOptions::buildIdentificationDeclarationMessage() InstanceName set to: %s", + mCommissioneeInstanceName); + } + } + LogDetail(); return id; } + void LogDetail() + { + ChipLogDetail(AppServer, "IdentificationDeclarationOptions::LogDetail()"); + ChipLogDetail(AppServer, "IdentificationDeclarationOptions::mNoPasscode: %s", + mNoPasscode ? "true" : "false"); + ChipLogDetail(AppServer, "IdentificationDeclarationOptions::mCdUponPasscodeDialog: %s", + mCdUponPasscodeDialog ? "true" : "false"); + ChipLogDetail(AppServer, "IdentificationDeclarationOptions::mCommissionerPasscode: %s", + mCommissionerPasscode ? "true" : "false"); + ChipLogDetail(AppServer, "IdentificationDeclarationOptions::mCommissionerPasscodeReady: %s", + mCommissionerPasscodeReady ? "true" : "false"); + ChipLogDetail(AppServer, "IdentificationDeclarationOptions::mCancelPasscode: %s", + mCancelPasscode ? "true" : "false"); + ChipLogDetail(AppServer, "IdentificationDeclarationOptions::mCommissioneeInstanceName: %s", mCommissioneeInstanceName); + } + private: /** * Feature: Target Content Application diff --git a/examples/tv-casting-app/tv-casting-common/support/AppParameters.h b/examples/tv-casting-app/tv-casting-common/support/AppParameters.h index 080011f8953c37..e48a45ea8ccfcd 100644 --- a/examples/tv-casting-app/tv-casting-common/support/AppParameters.h +++ b/examples/tv-casting-app/tv-casting-common/support/AppParameters.h @@ -42,7 +42,6 @@ class AppParameters chip::Credentials::DeviceAttestationCredentialsProvider * deviceAttestationCredentialsProvider, chip::Credentials::DeviceAttestationVerifier * deviceAttestationVerifier, ServerInitParamsProvider * serverInitParamsProvider) - // TODO: In the following PRs. Add CommissionerDeclarationHandler. { VerifyOrReturnError(commissionableDataProvider != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(deviceAttestationCredentialsProvider != nullptr, CHIP_ERROR_INVALID_ARGUMENT); @@ -62,6 +61,14 @@ class AppParameters chip::DeviceLayer::CommissionableDataProvider * GetCommissionableDataProvider() const { return mCommissionableDataProvider; } + CHIP_ERROR SetCommissionableDataProvider(chip::DeviceLayer::CommissionableDataProvider * commissionableDataProvider) const + { + ChipLogProgress(AppServer, "AppParameters::SetCommissionableDataProvider()"); + VerifyOrReturnError(commissionableDataProvider != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + mCommissionableDataProvider = commissionableDataProvider; + return CHIP_NO_ERROR; + } + chip::Credentials::DeviceAttestationCredentialsProvider * GetDeviceAttestationCredentialsProvider() const { return mDeviceAttestationCredentialsProvider; @@ -82,7 +89,7 @@ class AppParameters * @brief Provides CommissionableData (such as setupPasscode, discriminator, etc) used to get the CastingApp commissioned * */ - chip::DeviceLayer::CommissionableDataProvider * mCommissionableDataProvider; + mutable chip::DeviceLayer::CommissionableDataProvider * mCommissionableDataProvider; /** * @brief Provides DeviceAttestationCredentials of the CastingApp during commissioning diff --git a/examples/tv-casting-app/tv-casting-common/support/ChipDeviceEventHandler.cpp b/examples/tv-casting-app/tv-casting-common/support/ChipDeviceEventHandler.cpp index bddc64f67fb620..fd6039d5256031 100644 --- a/examples/tv-casting-app/tv-casting-common/support/ChipDeviceEventHandler.cpp +++ b/examples/tv-casting-app/tv-casting-common/support/ChipDeviceEventHandler.cpp @@ -94,7 +94,7 @@ void ChipDeviceEventHandler::HandleFailSafeTimerExpired() // if UDC was in progress (when the Fail-Safe timer expired), reset TargetCastingPlayer commissioning state and return early if (sUdcInProgress) { - ChipLogProgress(AppServer, "ChipDeviceEventHandler::HandleFailSafeTimerExpired when sUdcInProgress: %d, returning early", + ChipLogProgress(AppServer, "ChipDeviceEventHandler::HandleFailSafeTimerExpired() when sUdcInProgress: %d, returning early", sUdcInProgress); sUdcInProgress = false; CastingPlayer::GetTargetCastingPlayer()->mConnectionState = CASTING_PLAYER_NOT_CONNECTED; @@ -105,17 +105,19 @@ void ChipDeviceEventHandler::HandleFailSafeTimerExpired() } // if UDC was NOT in progress (when the Fail-Safe timer expired), start UDC - ChipLogProgress(AppServer, "ChipDeviceEventHandler::HandleFailSafeTimerExpired when sUdcInProgress: %d, starting UDC", + ChipLogProgress(AppServer, "ChipDeviceEventHandler::HandleFailSafeTimerExpired() when sUdcInProgress: %d, starting UDC", sUdcInProgress); chip::DeviceLayer::SystemLayer().StartTimer( chip::System::Clock::Milliseconds32(1), [](chip::System::Layer * aSystemLayer, void * aAppState) { - ChipLogProgress(AppServer, "ChipDeviceEventHandler::Handle running OpenBasicCommissioningWindow"); + ChipLogProgress(AppServer, "ChipDeviceEventHandler::HandleFailSafeTimerExpired() running OpenBasicCommissioningWindow"); CHIP_ERROR err = chip::Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow( chip::System::Clock::Seconds16(CastingPlayer::GetTargetCastingPlayer()->mCommissioningWindowTimeoutSec)); if (err != CHIP_NO_ERROR) { - ChipLogError(AppServer, "ChipDeviceEventHandler::Handle Failed to OpenBasicCommissioningWindow %" CHIP_ERROR_FORMAT, + ChipLogError(AppServer, + "ChipDeviceEventHandler::HandleFailSafeTimerExpired() Failed to OpenBasicCommissioningWindow " + "%" CHIP_ERROR_FORMAT, err.Format()); CastingPlayer::GetTargetCastingPlayer()->mOnCompleted(err, nullptr); return; @@ -126,7 +128,8 @@ void ChipDeviceEventHandler::HandleFailSafeTimerExpired() if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, - "ChipDeviceEventHandler::Handle Failed to SendUserDirectedCommissioningRequest %" CHIP_ERROR_FORMAT, + "ChipDeviceEventHandler::HandleFailSafeTimerExpired() Failed to SendUserDirectedCommissioningRequest " + "%" CHIP_ERROR_FORMAT, err.Format()); CastingPlayer::GetTargetCastingPlayer()->mOnCompleted(err, nullptr); return; @@ -209,10 +212,10 @@ void ChipDeviceEventHandler::HandleCommissioningComplete(const chip::DeviceLayer CHIP_ERROR ChipDeviceEventHandler::SetUdcStatus(bool udcInProgress) { - ChipLogProgress(AppServer, "ChipDeviceEventHandler::SetUdcStatus called with udcInProgress: %d", udcInProgress); + ChipLogProgress(AppServer, "ChipDeviceEventHandler::SetUdcStatus() called with udcInProgress: %d", udcInProgress); if (sUdcInProgress == udcInProgress) { - ChipLogError(AppServer, "UDC in progress state is already %d", sUdcInProgress); + ChipLogError(AppServer, "ChipDeviceEventHandler::SetUdcStatus() UDC in progress state is already %d", sUdcInProgress); return CHIP_ERROR_INCORRECT_STATE; } sUdcInProgress = udcInProgress; diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp index ad085d52be8a22..7a91decb43da84 100644 --- a/src/app/server/Server.cpp +++ b/src/app/server/Server.cpp @@ -628,23 +628,27 @@ CHIP_ERROR Server::SendUserDirectedCommissioningRequest(chip::Transport::PeerAdd // only populate fields left blank by the client if (strlen(id.GetInstanceName()) == 0) { + ChipLogDetail(AppServer, "Server::SendUserDirectedCommissioningRequest() Instance Name not known"); char nameBuffer[chip::Dnssd::Commission::kInstanceNameMaxLength + 1]; err = app::DnssdServer::Instance().GetCommissionableInstanceName(nameBuffer, sizeof(nameBuffer)); if (err != CHIP_NO_ERROR) { - ChipLogError(AppServer, "Failed to get mdns instance name error: %" CHIP_ERROR_FORMAT, err.Format()); + ChipLogError( + AppServer, + "Server::SendUserDirectedCommissioningRequest() Failed to get mdns instance name error: %" CHIP_ERROR_FORMAT, + err.Format()); return err; } id.SetInstanceName(nameBuffer); + ChipLogDetail(AppServer, "Server::SendUserDirectedCommissioningRequest() Instance Name set to %s", nameBuffer); } - ChipLogDetail(AppServer, "instanceName=%s", id.GetInstanceName()); if (id.GetVendorId() == 0) { uint16_t vendorId = 0; if (DeviceLayer::GetDeviceInstanceInfoProvider()->GetVendorId(vendorId) != CHIP_NO_ERROR) { - ChipLogDetail(Discovery, "Vendor ID not known"); + ChipLogDetail(AppServer, "Server::SendUserDirectedCommissioningRequest() Vendor ID not known"); } else { @@ -657,7 +661,7 @@ CHIP_ERROR Server::SendUserDirectedCommissioningRequest(chip::Transport::PeerAdd uint16_t productId = 0; if (DeviceLayer::GetDeviceInstanceInfoProvider()->GetProductId(productId) != CHIP_NO_ERROR) { - ChipLogDetail(Discovery, "Product ID not known"); + ChipLogDetail(AppServer, "Server::SendUserDirectedCommissioningRequest() Product ID not known"); } else { @@ -671,7 +675,7 @@ CHIP_ERROR Server::SendUserDirectedCommissioningRequest(chip::Transport::PeerAdd if (!chip::DeviceLayer::ConfigurationMgr().IsCommissionableDeviceNameEnabled() || chip::DeviceLayer::ConfigurationMgr().GetCommissionableDeviceName(deviceName, sizeof(deviceName)) != CHIP_NO_ERROR) { - ChipLogDetail(Discovery, "Device Name not known"); + ChipLogDetail(AppServer, "Server::SendUserDirectedCommissioningRequest() Device Name not known"); } else { @@ -710,11 +714,12 @@ CHIP_ERROR Server::SendUserDirectedCommissioningRequest(chip::Transport::PeerAdd if (err == CHIP_NO_ERROR) { - ChipLogDetail(AppServer, "Send UDC request success"); + ChipLogDetail(AppServer, "Server::SendUserDirectedCommissioningRequest() Send UDC request success"); } else { - ChipLogError(AppServer, "Send UDC request failed, err: %" CHIP_ERROR_FORMAT, err.Format()); + ChipLogError(AppServer, "Server::SendUserDirectedCommissioningRequest() Send UDC request failed, err: %" CHIP_ERROR_FORMAT, + err.Format()); } return err; }