Skip to content

Commit

Permalink
tv-casting-app cancel connection upon CancelPasscode CDC message from…
Browse files Browse the repository at this point in the history
… TV (#35331)

* tv-casting-app cancel connection upon CancelPasscode CDC message from TV

* Addressed comments by sharadb-amazon

* Fix iOS app crash due to null cpp cluster
  • Loading branch information
pgregorr-amazon authored and pull[bot] committed Oct 24, 2024
1 parent ee7d77e commit 4801354
Show file tree
Hide file tree
Showing 13 changed files with 199 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,27 +90,67 @@ - (MCCastingPlayer * _Nonnull)castingPlayer
- (MCCluster * _Nullable)clusterForType:(MCEndpointClusterType)type
{
switch (type) {
case MCEndpointClusterTypeApplicationBasic:
return [[MCApplicationBasicCluster alloc] initWithCppCluster:_cppEndpoint->GetCluster<matter::casting::clusters::application_basic::ApplicationBasicCluster>()];

case MCEndpointClusterTypeApplicationLauncher:
return [[MCApplicationLauncherCluster alloc] initWithCppCluster:_cppEndpoint->GetCluster<matter::casting::clusters::application_launcher::ApplicationLauncherCluster>()];

case MCEndpointClusterTypeContentLauncher:
return [[MCContentLauncherCluster alloc] initWithCppCluster:_cppEndpoint->GetCluster<matter::casting::clusters::content_launcher::ContentLauncherCluster>()];
case MCEndpointClusterTypeApplicationBasic: {
auto cppCluster = _cppEndpoint->GetCluster<matter::casting::clusters::application_basic::ApplicationBasicCluster>();
if (cppCluster == nullptr) {
ChipLogError(AppServer, "MCEndpoint::clusterForType() MCEndpointClusterTypeApplicationBasic, GetCluster() returned nullptr");
return nil;
}
return [[MCApplicationBasicCluster alloc] initWithCppCluster:cppCluster];
}

case MCEndpointClusterTypeKeypadInput:
return [[MCKeypadInputCluster alloc] initWithCppCluster:_cppEndpoint->GetCluster<matter::casting::clusters::keypad_input::KeypadInputCluster>()];
case MCEndpointClusterTypeApplicationLauncher: {
auto cppCluster = _cppEndpoint->GetCluster<matter::casting::clusters::application_launcher::ApplicationLauncherCluster>();
if (cppCluster == nullptr) {
ChipLogError(AppServer, "MCEndpoint::clusterForType() MCEndpointClusterTypeApplicationLauncher GetCluster() returned nullptr");
return nil;
}
return [[MCApplicationLauncherCluster alloc] initWithCppCluster:cppCluster];
}

case MCEndpointClusterTypeMediaPlayback:
return [[MCMediaPlaybackCluster alloc] initWithCppCluster:_cppEndpoint->GetCluster<matter::casting::clusters::media_playback::MediaPlaybackCluster>()];
case MCEndpointClusterTypeContentLauncher: {
auto cppCluster = _cppEndpoint->GetCluster<matter::casting::clusters::content_launcher::ContentLauncherCluster>();
if (cppCluster == nullptr) {
ChipLogError(AppServer, "MCEndpoint::clusterForType() MCEndpointClusterTypeContentLauncher GetCluster() returned nullptr");
return nil;
}
return [[MCContentLauncherCluster alloc] initWithCppCluster:cppCluster];
}

case MCEndpointClusterTypeOnOff:
return [[MCOnOffCluster alloc] initWithCppCluster:_cppEndpoint->GetCluster<matter::casting::clusters::on_off::OnOffCluster>()];
case MCEndpointClusterTypeKeypadInput: {
auto cppCluster = _cppEndpoint->GetCluster<matter::casting::clusters::keypad_input::KeypadInputCluster>();
if (cppCluster == nullptr) {
ChipLogError(AppServer, "MCEndpoint::clusterForType() MCEndpointClusterTypeKeypadInput GetCluster() returned nullptr");
return nil;
}
return [[MCKeypadInputCluster alloc] initWithCppCluster:cppCluster];
}

case MCEndpointClusterTypeTargetNavigator:
return [[MCTargetNavigatorCluster alloc] initWithCppCluster:_cppEndpoint->GetCluster<matter::casting::clusters::target_navigator::TargetNavigatorCluster>()];
case MCEndpointClusterTypeMediaPlayback: {
auto cppCluster = _cppEndpoint->GetCluster<matter::casting::clusters::media_playback::MediaPlaybackCluster>();
if (cppCluster == nullptr) {
ChipLogError(AppServer, "MCEndpoint::clusterForType() MCEndpointClusterTypeMediaPlayback GetCluster() returned nullptr");
return nil;
}
return [[MCMediaPlaybackCluster alloc] initWithCppCluster:cppCluster];
}

case MCEndpointClusterTypeOnOff: {
auto cppCluster = _cppEndpoint->GetCluster<matter::casting::clusters::on_off::OnOffCluster>();
if (cppCluster == nullptr) {
ChipLogError(AppServer, "MCEndpoint::clusterForType() MCEndpointClusterTypeOnOff GetCluster() returned nullptr");
return nil;
}
return [[MCOnOffCluster alloc] initWithCppCluster:cppCluster];
}
case MCEndpointClusterTypeTargetNavigator: {
auto cppCluster = _cppEndpoint->GetCluster<matter::casting::clusters::target_navigator::TargetNavigatorCluster>();
if (cppCluster == nullptr) {
ChipLogError(AppServer, "MCEndpoint::clusterForType() MCEndpointClusterTypeTargetNavigator GetCluster() returned nullptr");
return nil;
}
return [[MCTargetNavigatorCluster alloc] initWithCppCluster:cppCluster];
}
default:
ChipLogError(AppServer, "MCEndpointClusterType not found");
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class MCApplicationBasicReadVendorIDExampleViewModel: ObservableObject {
// validate that the selected endpoint supports the ApplicationBasic cluster
if(!endpoint.hasCluster(MCEndpointClusterTypeApplicationBasic))
{
self.Log.error("No ApplicationBasic cluster supporting endpoint found")
self.Log.error("MCApplicationBasicReadVendorIDExampleViewModel.read() No ApplicationBasic cluster supporting endpoint found")
DispatchQueue.main.async
{
self.status = "No ApplicationBasic cluster supporting endpoint found"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class MCContentLauncherLaunchURLExampleViewModel: ObservableObject {
// validate that the selected endpoint supports the ContentLauncher cluster
if(!endpoint.hasCluster(MCEndpointClusterTypeContentLauncher))
{
self.Log.error("No ContentLauncher cluster supporting endpoint found")
self.Log.error("MCContentLauncherLaunchURLExampleViewModel.invokeCommand() No ContentLauncher cluster supporting endpoint found")
DispatchQueue.main.async
{
self.status = "No ContentLauncher cluster supporting endpoint found"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class MCMediaPlaybackSubscribeToCurrentStateExampleViewModel: ObservableObject {
// validate that the selected endpoint supports the MediaPlayback cluster
if(!endpoint.hasCluster(MCEndpointClusterTypeMediaPlayback))
{
self.Log.error("No MediaPlayback cluster supporting endpoint found")
self.Log.error("MCMediaPlaybackSubscribeToCurrentStateExampleViewModel.subscribe() No MediaPlayback cluster supporting endpoint found")
DispatchQueue.main.async
{
self.status = "No MediaPlayback cluster supporting endpoint found"
Expand Down
2 changes: 1 addition & 1 deletion examples/tv-casting-app/linux/simple-app-helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ void ConnectionHandler(CHIP_ERROR err, matter::casting::core::CastingPlayer * ca
// For a connection failure, called back with an error and nullptr.
VerifyOrReturn(
err == CHIP_NO_ERROR,
ChipLogProgress(
ChipLogError(
AppServer,
"simple-app-helper.cpp::ConnectionHandler(): Failed to connect to CastingPlayer (ID: %s) with err %" CHIP_ERROR_FORMAT,
targetCastingPlayer->GetId(), err.Format()));
Expand Down
20 changes: 18 additions & 2 deletions examples/tv-casting-app/tv-casting-common/core/BaseCluster.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,28 @@ class BaseCluster
/**
* @return Pointer to the Attribute registered in this cluster, corresponding to attributeId
*/
void * GetAttribute(const chip::AttributeId attributeId) { return mAttributes[attributeId]; }
void * GetAttribute(const chip::AttributeId attributeId)
{
if (mAttributes.empty())
{
ChipLogError(AppServer, "BaseCluster::GetAttribute() mAttributes is empty");
return nullptr;
}
return mAttributes[attributeId];
}

/**
* @return Pointer to the Command registered in this cluster, corresponding to commandId
*/
void * GetCommand(const chip::CommandId commandId) { return mCommands[commandId]; }
void * GetCommand(const chip::CommandId commandId)
{
if (mCommands.empty())
{
ChipLogError(AppServer, "BaseCluster::GetCommand() mCommands is empty");
return nullptr;
}
return mCommands[commandId];
}

protected:
/**
Expand Down
35 changes: 28 additions & 7 deletions examples/tv-casting-app/tv-casting-common/core/CastingPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,14 @@ void CastingPlayer::VerifyOrEstablishConnection(ConnectionCallbacks connectionCa
// Set the callback for handling CommissionerDeclaration messages.
matter::casting::core::CommissionerDeclarationHandler::GetInstance()->SetCommissionerDeclarationCallback(
connectionCallbacks.mCommissionerDeclarationCallback);
mClientProvidedCommissionerDeclarationCallback = true;
}
else
{
ChipLogProgress(
AppServer,
"CastingPlayer::VerifyOrEstablishConnection() CommissionerDeclarationCallback not provided in ConnectionCallbacks");
mClientProvidedCommissionerDeclarationCallback = false;
}

ChipLogProgress(AppServer, "CastingPlayer::VerifyOrEstablishConnection() verifying User Directed Commissioning (UDC) state");
Expand Down Expand Up @@ -107,6 +109,7 @@ void CastingPlayer::VerifyOrEstablishConnection(ConnectionCallbacks connectionCa
// found the CastingPlayer in cache
if (it != cachedCastingPlayers.end())
{
ChipLogProgress(AppServer, "CastingPlayer::VerifyOrEstablishConnection() found this CastingPlayer in app cache");
unsigned index = (unsigned int) std::distance(cachedCastingPlayers.begin(), it);
if (ContainsDesiredTargetApp(&cachedCastingPlayers[index], idOptions.getTargetAppInfoList()))
{
Expand Down Expand Up @@ -225,16 +228,24 @@ CHIP_ERROR CastingPlayer::ContinueConnecting()

CHIP_ERROR CastingPlayer::StopConnecting()
{
ChipLogProgress(AppServer, "CastingPlayer::StopConnecting() called, while ChipDeviceEventHandler.sUdcInProgress: %s",
support::ChipDeviceEventHandler::isUdcInProgress() ? "true" : "false");
VerifyOrReturnValue(mConnectionState == CASTING_PLAYER_CONNECTING, CHIP_ERROR_INCORRECT_STATE,
ChipLogError(AppServer, "CastingPlayer::StopConnecting() called while not in connecting state"););
VerifyOrReturnValue(
mIdOptions.mCommissionerPasscode, CHIP_ERROR_INCORRECT_STATE,
ChipLogError(AppServer,
"CastingPlayer::StopConnecting() mIdOptions.mCommissionerPasscode == false, ContinueConnecting() should only "
"be called when the CastingPlayer/Commissioner-Generated passcode commissioning flow is in progress."););
// Calling the internal StopConnecting() API with the shouldSendIdentificationDeclarationMessage set to true to notify the
// CastingPlayer/Commissioner that the commissioning session was cancelled by the Casting Client/Commissionee user. This will
// result in the Casting Client/Commissionee sending a CancelPasscode IdentificationDeclaration message to the CastingPlayer.
// shouldSendIdentificationDeclarationMessage is true when StopConnecting() is called by the Client.
return this->StopConnecting(true);
}

CHIP_ERROR CastingPlayer::StopConnecting(bool shouldSendIdentificationDeclarationMessage)
{
ChipLogProgress(AppServer, "CastingPlayer::StopConnecting() called, while ChipDeviceEventHandler.sUdcInProgress: %s",
support::ChipDeviceEventHandler::isUdcInProgress() ? "true" : "false");
VerifyOrReturnValue(mConnectionState == CASTING_PLAYER_CONNECTING, CHIP_ERROR_INCORRECT_STATE,
ChipLogError(AppServer, "CastingPlayer::StopConnecting() called while not in connecting state"););
CHIP_ERROR err = CHIP_NO_ERROR;
mIdOptions.resetState();
mIdOptions.mCancelPasscode = true;
Expand All @@ -243,6 +254,16 @@ CHIP_ERROR CastingPlayer::StopConnecting()
mTargetCastingPlayer.reset();
CastingPlayerDiscovery::GetInstance()->ClearCastingPlayersInternal();

if (!shouldSendIdentificationDeclarationMessage)
{
ChipLogProgress(AppServer,
"CastingPlayer::StopConnecting() shouldSendIdentificationDeclarationMessage: %d, User Directed "
"Commissioning aborted by the CastingPlayer/Commissioner user.",
shouldSendIdentificationDeclarationMessage);
resetState(CHIP_ERROR_CONNECTION_ABORTED);
return err;
}

// If a CastingPlayer::ContinueConnecting() error occurs, StopConnecting() can be called while sUdcInProgress == true.
// sUdcInProgress should be set to false before sending the CancelPasscode IdentificationDeclaration message to the
// CastingPlayer/Commissioner.
Expand All @@ -251,9 +272,9 @@ CHIP_ERROR CastingPlayer::StopConnecting()
support::ChipDeviceEventHandler::SetUdcStatus(false);
}

ChipLogProgress(
AppServer,
"CastingPlayer::StopConnecting() calling SendUserDirectedCommissioningRequest() to indicate user canceled passcode entry");
ChipLogProgress(AppServer,
"CastingPlayer::StopConnecting() calling SendUserDirectedCommissioningRequest() to indicate "
"Client/Commissionee user canceled passcode entry");
#if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT
err = SendUserDirectedCommissioningRequest();
if (err != CHIP_NO_ERROR)
Expand Down
42 changes: 31 additions & 11 deletions examples/tv-casting-app/tv-casting-common/core/CastingPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,11 @@ class CastingPlayer : public std::enable_shared_from_this<CastingPlayer>
*/
static CastingPlayer * GetTargetCastingPlayer()
{
ChipLogProgress(AppServer, "CastingPlayer::GetTargetCastingPlayer() called");
std::shared_ptr<CastingPlayer> sharedPtr = mTargetCastingPlayer.lock();
CastingPlayer * rawPtr = nullptr;
if (sharedPtr)
{
rawPtr = sharedPtr.get();
ChipLogProgress(
AppServer,
"CastingPlayer::GetTargetCastingPlayer() Got rawPtr from mTargetCastingPlayer, sharedPtr reference count: %lu",
sharedPtr.use_count());
sharedPtr.reset();
}
else
Expand Down Expand Up @@ -207,12 +202,14 @@ class CastingPlayer : public std::enable_shared_from_this<CastingPlayer>
/**
* @brief This cancels the CastingPlayer/Commissioner-Generated passcode commissioning flow started via the
* VerifyOrEstablishConnection() API above. It constructs and sends an IdentificationDeclaration message to the
* CastingPlayer/Commissioner containing CancelPasscode set to true. It is used to indicate that the user, and thus the
* Client/Commissionee, have cancelled the commissioning process. This indicates that the CastingPlayer/Commissioner can dismiss
* any dialogs corresponding to commissioning, such as a Passcode input dialog or a Passcode display dialog.
* Note: stopConnecting() does not call the ConnectCallback() callback passed to the VerifyOrEstablishConnection() API above
* since no connection is established.
* @return CHIP_NO_ERROR if this function was called with the CastingPlayer in the correct state and an error otherwise.
* CastingPlayer/Commissioner containing CancelPasscode set to true. It is used to indicate that the Client/Commissionee user
* has cancelled the commissioning process. This indicates that the CastingPlayer/Commissioner can dismiss any dialogs
* corresponding to commissioning, such as a Passcode input dialog or a Passcode display dialog. Note: StopConnecting() does not
* call the ConnectCallback() callback passed to the VerifyOrEstablishConnection() API above since no connection is established.
* @return CHIP_NO_ERROR if this function was called with the CastingPlayer in the correct state and CHIP_ERROR_INCORRECT_STATE.
* otherwise. StopConnecting() can only be called by the client during the CastingPlayer/Commissioner-Generated passcode
* commissioning flow. Calling StopConnecting() during the Client/Commissionee-Generated commissioning flow will return a
* CHIP_ERROR_INCORRECT_STATE error.
*/
CHIP_ERROR StopConnecting();

Expand Down Expand Up @@ -295,6 +292,28 @@ class CastingPlayer : public std::enable_shared_from_this<CastingPlayer>
static memory::Weak<CastingPlayer> mTargetCastingPlayer;
uint16_t mCommissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec;
ConnectCallback mOnCompleted = {};
bool mClientProvidedCommissionerDeclarationCallback;

/**
* @brief This internal version of the StopConnecting API cancels the Client/Commissionee-Generated passcode or the
* CastingPlayer/Commissioner-Generated passcode commissioning flow started via the VerifyOrEstablishConnection() API above.
* Furthermore, StopConnecting operates in two ways as governed by the shouldSendIdentificationDeclarationMessage flag:
* 1. If shouldSendIdentificationDeclarationMessage is true. StopConnecting constructs and sends an IdentificationDeclaration
* message to the CastingPlayer/Commissioner containing CancelPasscode set to true. The CancelPasscode flag set to true conveys
* that the Client/Commissionee user has cancelled the commissioning session. This indicates that the CastingPlayer/Commissioner
* can dismiss any dialogs corresponding to commissioning, such as a Passcode input dialog or a Passcode display dialog. In this
* case, since StopConnecting was called by the Client/Commissionee, StopConnecting() does not call the ConnectCallback()
* callback passed to the VerifyOrEstablishConnection().
* 2. If shouldSendIdentificationDeclarationMessage is false. StopConnecting does not send an IdentificationDeclaration message
* to the CastingPlayer/Commissioner since the CastingPlayer/Commissioner notified the Client/Commissionee that the connection
* is aborted. If the (Optional) ConnectionCallbacks mCommissionerDeclarationCallback is not set, it calls ConnectionCallbacks
* mOnConnectionComplete callback with CHIP_ERROR_CONNECTION_ABORTED.
* @param shouldSendIdentificationDeclarationMessage if true, send the IdentificationDeclaration message to the CastingPlayer
* with CancelPasscode set to true. If false, only call the ConnectionCallbacks mCommissionerDeclarationCallback callback passed
* to the VerifyOrEstablishConnection() API above, without sending the IdentificationDeclaration message.
* @return CHIP_NO_ERROR if this function was called with the CastingPlayer in the correct state and an error otherwise.
*/
CHIP_ERROR StopConnecting(bool shouldSendIdentificationDeclarationMessage);

/**
* @brief resets this CastingPlayer's state and calls mOnCompleted with the CHIP_ERROR. Also, after calling mOnCompleted, it
Expand Down Expand Up @@ -329,6 +348,7 @@ class CastingPlayer : public std::enable_shared_from_this<CastingPlayer>
// and connect to a CastingPlayer
friend class support::ChipDeviceEventHandler;

friend class CommissionerDeclarationHandler;
friend class ConnectionContext;
friend class support::EndpointListLoader;
};
Expand Down
Loading

0 comments on commit 4801354

Please sign in to comment.