Skip to content

Commit

Permalink
Linux tv-casting-app v1.3 Commissioner-Generated passcode follow up
Browse files Browse the repository at this point in the history
  • Loading branch information
pgregorr-amazon committed May 20, 2024
1 parent 320f83e commit 85788c7
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,8 @@ JNI_METHOD(jobject, verifyOrEstablishConnection)
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);
castingPlayer->VerifyOrEstablishConnection(connectionCallbacks, static_cast<uint64_t>(commissioningWindowTimeoutSec),
idOptions);
return support::convertMatterErrorFromCppToJava(CHIP_NO_ERROR);
}

Expand Down
82 changes: 67 additions & 15 deletions examples/tv-casting-app/linux/simple-app-helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@

// VendorId of the Endpoint on the CastingPlayer that the CastingApp desires to interact with after connection
const uint16_t kDesiredEndpointVendorId = 65521;
// Indicates that the Commissioner-Generated passcode commissioning flow is in progress.
bool gCommissionerGeneratedPasscodeFlow = false;

DiscoveryDelegateImpl * DiscoveryDelegateImpl::_discoveryDelegateImpl = nullptr;
bool gAwaitingCommissionerPasscodeInput = false;
Expand Down Expand Up @@ -244,6 +246,22 @@ CHIP_ERROR InitCommissionableDataProvider(LinuxCommissionableDataProvider & prov
options.payload.discriminator.GetLongValue());
}

void PrintEndpointsVendorAndProductIDs(
const std::vector<matter::casting::memory::Strong<matter::casting::core::Endpoint>> & endpoints)
{
ChipLogProgress(AppServer, "simple-app-helper.cpp::PrintEndpointsVendorAndProductIDs() Number of Endpoints: %d",
static_cast<int>(endpoints.size()));
for (const auto & endpoint : endpoints)
{
int endpointId = endpoint->GetId();
int vendorId = endpoint->GetVendorId();
int productId = endpoint->GetProductId();
ChipLogProgress(AppServer,
"simple-app-helper.cpp::PrintEndpointsVendorAndProductIDs() Endpoint ID: %d, Vendor ID: %d, Product ID: %d",
endpointId, vendorId, productId);
}
}

void ConnectionHandler(CHIP_ERROR err, matter::casting::core::CastingPlayer * castingPlayer)
{
ChipLogProgress(AppServer, "simple-app-helper.cpp::ConnectionHandler()");
Expand All @@ -256,23 +274,49 @@ void ConnectionHandler(CHIP_ERROR err, matter::casting::core::CastingPlayer * ca
"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());
if (gCommissionerGeneratedPasscodeFlow)
{
ChipLogProgress(AppServer,
"simple-app-helper.cpp::ConnectionHandler(): Successfully connected to CastingPlayer (ID: %s) using "
"Commissioner-Generated passcode",
castingPlayer->GetId());
ChipLogProgress(AppServer, "simple-app-helper.cpp::ConnectionHandler(): Desired Endpoint ID for demo interactions: 1");
}
else
{
ChipLogProgress(AppServer, "simple-app-helper.cpp::ConnectionHandler(): Successfully connected to CastingPlayer (ID: %s)",
castingPlayer->GetId());
ChipLogProgress(AppServer,
"simple-app-helper.cpp::ConnectionHandler(): Desired Endpoint Vendor ID for demo interactions: %d",
kDesiredEndpointVendorId);
}

ChipLogProgress(AppServer, "simple-app-helper.cpp::ConnectionHandler(): Getting endpoints avaiable for demo interactions");
std::vector<matter::casting::memory::Strong<matter::casting::core::Endpoint>> endpoints = castingPlayer->GetEndpoints();
PrintEndpointsVendorAndProductIDs(endpoints);

// 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() == kDesiredEndpointVendorId;
if (gCommissionerGeneratedPasscodeFlow)
{
// Since the tv-app with Vendor ID 1111 does not implement the endpoint with Vendor ID 65521 we
// will (temporarily) use the first available endpoint for demo interactions. See
// connectedhomeip/examples/tv-app/tv-common/include/AppTv.h.
return endpoint->GetId() == 1;
}
return endpoint->GetVendorId() == kDesiredEndpointVendorId; // 65521
});
if (it != endpoints.end())
{
// The desired endpoint is endpoints[index]
unsigned index = (unsigned int) std::distance(endpoints.begin(), it);

ChipLogProgress(
AppServer,
"simple-app-helper.cpp::ConnectionHandler(): Triggering demo interactions with CastingPlayer (ID: %s). Endpoint ID: %d",
castingPlayer->GetId(), endpoints[index]->GetId());

// demonstrate invoking a command
InvokeContentLauncherLaunchURL(endpoints[index]);

Expand All @@ -286,8 +330,8 @@ void ConnectionHandler(CHIP_ERROR err, matter::casting::core::CastingPlayer * ca
{
ChipLogError(
AppServer,
"simple-app-helper.cpp::ConnectionHandler():Desired Endpoint Vendor Id (%d) not found on the CastingPlayer (ID: %s)",
kDesiredEndpointVendorId, castingPlayer->GetId());
"simple-app-helper.cpp::ConnectionHandler():Desired Endpoint Vendor ID not found on the CastingPlayer (ID: %s)",
castingPlayer->GetId());
}
}

Expand Down Expand Up @@ -350,9 +394,14 @@ CHIP_ERROR CommandHandler(int argc, char ** argv)
ChipLogError(AppServer, "Invalid casting player index provided: %lu", index));
targetCastingPlayer = castingPlayers.at(index);

gCommissionerGeneratedPasscodeFlow = false;
matter::casting::core::IdentificationDeclarationOptions idOptions;
chip::Protocols::UserDirectedCommissioning::TargetAppInfo targetAppInfo;
targetAppInfo.vendorId = kDesiredEndpointVendorId;

if (argc == 3)
{

if (strcmp(argv[2], "commissioner-generated-passcode") == 0)
{
// Attempt Commissioner-Generated Passcode (commissioner-generated-passcode) commissioning flow only if the
Expand All @@ -364,6 +413,13 @@ CHIP_ERROR CommandHandler(int argc, char ** argv)
"Commissioner-Generated Passcode commissioning flow",
index);
idOptions.mCommissionerPasscode = true;

// Override the default target Vendor ID for this example (Commissioner-Generated passcode commissioning flow).
// This tv-app target Vendor ID (1111), does not implement the AccountLogin cluster, which would otherwise auto
// commission using the Commissionee-Generated passcode upon recieving the IdentificationDeclaration Message.
// See connectedhomeip/examples/tv-app/tv-common/include/AppTv.h.
targetAppInfo.vendorId = 1111;
gCommissionerGeneratedPasscodeFlow = true;
}
else
{
Expand All @@ -374,9 +430,8 @@ CHIP_ERROR CommandHandler(int argc, char ** argv)
}
}
}
chip::Protocols::UserDirectedCommissioning::TargetAppInfo targetAppInfo;
targetAppInfo.vendorId = kDesiredEndpointVendorId;
CHIP_ERROR result = idOptions.addTargetAppInfo(targetAppInfo);

CHIP_ERROR result = idOptions.addTargetAppInfo(targetAppInfo);
if (result != CHIP_NO_ERROR)
{
ChipLogError(AppServer, "CommandHandler() request, failed to add targetAppInfo: %" CHIP_ERROR_FORMAT, result.Format());
Expand Down Expand Up @@ -430,11 +485,8 @@ CHIP_ERROR CommandHandler(int argc, char ** argv)
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);
targetCastingPlayer->ContinueConnecting();
}
else
{
Expand Down
82 changes: 35 additions & 47 deletions examples/tv-casting-app/tv-casting-common/core/CastingPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ namespace core {

CastingPlayer * CastingPlayer::mTargetCastingPlayer = nullptr;

// TODO: Verify why commissioningWindowTimeoutSec is a "unsigned long long int" type. Seems too big.
void CastingPlayer::VerifyOrEstablishConnection(ConnectionCallbacks connectionCallbacks,
unsigned long long int commissioningWindowTimeoutSec,
void CastingPlayer::VerifyOrEstablishConnection(ConnectionCallbacks connectionCallbacks, uint64_t commissioningWindowTimeoutSec,
IdentificationDeclarationOptions idOptions)
{
ChipLogProgress(AppServer, "CastingPlayer::VerifyOrEstablishConnection() called");
Expand Down Expand Up @@ -72,6 +70,25 @@ void CastingPlayer::VerifyOrEstablishConnection(ConnectionCallbacks connectionCa
"CastingPlayer::VerifyOrEstablishConnection() CommissionerDeclarationCallback not provided in ConnectionCallbacks");
}

ChipLogProgress(AppServer, "CastingPlayer::VerifyOrEstablishConnection() verifying User Directed Commissioning (UDC) state");
mIdOptions.LogDetail();
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 CommissionerDeclarationHandler CommissionerDeclaration message "
"callback has not been set, yet IdentificationDeclarationOptions.mCommissionerPasscode is set to true");
SuccessOrExit(err = CHIP_ERROR_INVALID_ARGUMENT);
}

// 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.
Expand Down Expand Up @@ -138,28 +155,11 @@ void CastingPlayer::VerifyOrEstablishConnection(ConnectionCallbacks connectionCa
}
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);
}

// We need to call OpenBasicCommissioningWindow() for both Commissionee-Generated passcode commissioning flow and
// Commissioner-Generated passcode commissioning flow. Per the Matter spec (UserDirectedCommissioning), even if the
// Commissionee sends an IdentificationDeclaration with CommissionerPasscode set to true, the Commissioner will first
// attempt to use AccountLogin in order to obtain Passcode using rotatingID. If no Passcode is obtained, Commissioner
// displays a Passcode.
ChipLogProgress(AppServer, "CastingPlayer::VerifyOrEstablishConnection() calling OpenBasicCommissioningWindow()");
SuccessOrExit(err = chip::Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow(
chip::System::Clock::Seconds16(mCommissioningWindowTimeoutSec)));
Expand All @@ -178,33 +178,19 @@ void CastingPlayer::VerifyOrEstablishConnection(ConnectionCallbacks connectionCa
}
}

void CastingPlayer::ContinueConnecting(ConnectionCallbacks connectionCallbacks,
unsigned long long int commissioningWindowTimeoutSec)
void CastingPlayer::ContinueConnecting()
{
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)
// Verify that mOnCompleted is not nullptr.
VerifyOrExit(mOnCompleted != nullptr, ChipLogError(AppServer, "CastingPlayer::ContinueConnecting() mOnCompleted == nullptr"));
if (!matter::casting::core::CommissionerDeclarationHandler::GetInstance()->HasCommissionerDeclarationCallback())
{
matter::casting::core::CommissionerDeclarationHandler::GetInstance()->SetCommissionerDeclarationCallback(
connectionCallbacks.mCommissionerDeclarationCallback);
}
else
{
ChipLogProgress(
AppServer,
"CastingPlayer::VerifyOrEstablishConnection() CommissionerDeclarationCallback not provided in ConnectionCallbacks");
ChipLogProgress(AppServer,
"CastingPlayer::ContinueConnecting() CommissionerDeclaration message callback has not been set.");
}
mConnectionState = CASTING_PLAYER_CONNECTING;
mTargetCastingPlayer = this;

ChipLogProgress(AppServer, "CastingPlayer::ContinueConnecting() calling OpenBasicCommissioningWindow()");
SuccessOrExit(err = chip::Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow(
Expand Down Expand Up @@ -272,6 +258,8 @@ CHIP_ERROR CastingPlayer::SendUserDirectedCommissioningRequest()

chip::Protocols::UserDirectedCommissioning::IdentificationDeclaration id = mIdOptions.buildIdentificationDeclarationMessage();

ReturnErrorOnFailure(support::ChipDeviceEventHandler::SetUdcStatus(true));

ReturnErrorOnFailure(chip::Server::GetInstance().SendUserDirectedCommissioningRequest(
chip::Transport::PeerAddress::UDP(*ipAddressToUse, mAttributes.port, mAttributes.interfaceId), id));

Expand Down
20 changes: 9 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 @@ -38,8 +38,8 @@ namespace core {

const int kPortMaxLength = 5; // port is uint16_t
// +1 for the : between the hostname and the port.
const int kIdMaxLength = chip::Dnssd::kHostNameMaxLength + kPortMaxLength + 1;
const unsigned long long int kCommissioningWindowTimeoutSec = 3 * 60; // 3 minutes
const int kIdMaxLength = chip::Dnssd::kHostNameMaxLength + kPortMaxLength + 1;
const uint64_t kCommissioningWindowTimeoutSec = 3 * 60; // 3 minutes

/**
* @brief Describes an Endpoint that the client wants to connect to
Expand Down Expand Up @@ -131,8 +131,8 @@ class CastingPlayer : public std::enable_shared_from_this<CastingPlayer>
* TargetApp is not found in the on-device CastingStore.
*/
void VerifyOrEstablishConnection(ConnectionCallbacks connectionCallbacks,
unsigned long long int commissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec,
IdentificationDeclarationOptions idOptions = IdentificationDeclarationOptions());
uint64_t commissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec,
IdentificationDeclarationOptions idOptions = IdentificationDeclarationOptions());

/**
* @brief Continues the UDC process during the Commissioner-Generated passcode commissioning flow by sending a second
Expand All @@ -145,12 +145,10 @@ class CastingPlayer : public std::enable_shared_from_this<CastingPlayer>
* 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.
* The same connectionCallbacks and commissioningWindowTimeoutSec parameters passed in to VerifyOrEstablishConnection() will be
* used.
*/
void ContinueConnecting(ConnectionCallbacks connectionCallbacks,
unsigned long long int commissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec);
void ContinueConnecting();

/**
* @brief Sets the internal connection state of this CastingPlayer to "disconnected"
Expand Down Expand Up @@ -220,8 +218,8 @@ class CastingPlayer : public std::enable_shared_from_this<CastingPlayer>
CastingPlayerAttributes mAttributes;
IdentificationDeclarationOptions mIdOptions;
static CastingPlayer * mTargetCastingPlayer;
unsigned long long int mCommissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec;
ConnectCallback mOnCompleted = {};
uint64_t mCommissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec;
ConnectCallback mOnCompleted = {};

/**
* @brief resets this CastingPlayer's state and calls mOnCompleted with the CHIP_ERROR. Also, after calling mOnCompleted, it
Expand Down

0 comments on commit 85788c7

Please sign in to comment.