blob: 49f006aa9cf4ab4ed2d9ef831b051205e7f42c69 [file] [log] [blame]
/*
*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "simple-app-helper.h"
#include "../tv-casting-common/core/ConnectionCallbacks.h"
#include "clusters/Clusters.h"
#include "app/clusters/bindings/BindingManager.h"
#include <inttypes.h>
#include <lib/core/CHIPCore.h>
#include <lib/shell/Commands.h>
#include <lib/shell/Engine.h>
#include <lib/shell/commands/Help.h>
#include <lib/support/CHIPArgParser.hpp>
#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()
{
if (_discoveryDelegateImpl == nullptr)
{
_discoveryDelegateImpl = new DiscoveryDelegateImpl();
}
return _discoveryDelegateImpl;
}
void DiscoveryDelegateImpl::HandleOnAdded(matter::casting::memory::Strong<matter::casting::core::CastingPlayer> player)
{
ChipLogProgress(AppServer, "DiscoveryDelegateImpl::HandleOnAdded()");
if (commissionersCount == 0)
{
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;
player->LogDetail();
}
void DiscoveryDelegateImpl::HandleOnUpdated(matter::casting::memory::Strong<matter::casting::core::CastingPlayer> player)
{
ChipLogProgress(AppServer, "DiscoveryDelegateImpl::HandleOnUpdated() Updated CastingPlayer with ID: %s", player->GetId());
}
void InvokeContentLauncherLaunchURL(matter::casting::memory::Strong<matter::casting::core::Endpoint> endpoint)
{
// get contentLauncherCluster from the endpoint
matter::casting::memory::Strong<matter::casting::clusters::content_launcher::ContentLauncherCluster> contentLauncherCluster =
endpoint->GetCluster<matter::casting::clusters::content_launcher::ContentLauncherCluster>();
VerifyOrReturn(contentLauncherCluster != nullptr);
// get the launchURLCommand from the contentLauncherCluster
matter::casting::core::Command<chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type> * launchURLCommand =
static_cast<matter::casting::core::Command<chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type> *>(
contentLauncherCluster->GetCommand(chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Id));
VerifyOrReturn(launchURLCommand != nullptr, ChipLogError(AppServer, "LaunchURL command not found on ContentLauncherCluster"));
// create the LaunchURL request
chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type request;
request.contentURL = chip::CharSpan::fromCharString(kContentURL);
request.displayString = chip::Optional<chip::CharSpan>(chip::CharSpan::fromCharString(kContentDisplayStr));
request.brandingInformation =
chip::MakeOptional(chip::app::Clusters::ContentLauncher::Structs::BrandingInformationStruct::Type());
// call Invoke on launchURLCommand while passing in success/failure callbacks
launchURLCommand->Invoke(
request, nullptr,
[](void * context, const chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type::ResponseType & response) {
ChipLogProgress(AppServer, "LaunchURL Success with response.data: %.*s", static_cast<int>(response.data.Value().size()),
response.data.Value().data());
},
[](void * context, CHIP_ERROR error) {
ChipLogError(AppServer, "LaunchURL Failure with err %" CHIP_ERROR_FORMAT, error.Format());
},
chip::MakeOptional(kTimedInvokeCommandTimeoutMs)); // time out after kTimedInvokeCommandTimeoutMs
}
void ReadApplicationBasicVendorID(matter::casting::memory::Strong<matter::casting::core::Endpoint> endpoint)
{
// get applicationBasicCluster from the endpoint
matter::casting::memory::Strong<matter::casting::clusters::application_basic::ApplicationBasicCluster> applicationBasicCluster =
endpoint->GetCluster<matter::casting::clusters::application_basic::ApplicationBasicCluster>();
VerifyOrReturn(applicationBasicCluster != nullptr);
// get the vendorIDAttribute from the applicationBasicCluster
matter::casting::core::Attribute<chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo> * vendorIDAttribute =
static_cast<matter::casting::core::Attribute<chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo> *>(
applicationBasicCluster->GetAttribute(chip::app::Clusters::ApplicationBasic::Attributes::VendorID::Id));
VerifyOrReturn(vendorIDAttribute != nullptr,
ChipLogError(AppServer, "VendorID attribute not found on ApplicationBasicCluster"));
// call Read on vendorIDAttribute while passing in success/failure callbacks
vendorIDAttribute->Read(
nullptr,
[](void * context,
chip::Optional<chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo::DecodableArgType> before,
chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo::DecodableArgType after) {
if (before.HasValue())
{
ChipLogProgress(AppServer, "Read VendorID value: %d [Before reading value: %d]", after, before.Value());
}
else
{
ChipLogProgress(AppServer, "Read VendorID value: %d", after);
}
},
[](void * context, CHIP_ERROR error) {
ChipLogError(AppServer, "VendorID Read failure with err %" CHIP_ERROR_FORMAT, error.Format());
});
}
void SubscribeToMediaPlaybackCurrentState(matter::casting::memory::Strong<matter::casting::core::Endpoint> endpoint)
{
// get mediaPlaybackCluster from the endpoint
matter::casting::memory::Strong<matter::casting::clusters::media_playback::MediaPlaybackCluster> mediaPlaybackCluster =
endpoint->GetCluster<matter::casting::clusters::media_playback::MediaPlaybackCluster>();
VerifyOrReturn(mediaPlaybackCluster != nullptr);
// get the currentStateAttribute from the applicationBasicCluster
matter::casting::core::Attribute<chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo> *
currentStateAttribute =
static_cast<matter::casting::core::Attribute<chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo> *>(
mediaPlaybackCluster->GetAttribute(chip::app::Clusters::MediaPlayback::Attributes::CurrentState::Id));
VerifyOrReturn(currentStateAttribute != nullptr,
ChipLogError(AppServer, "CurrentState attribute not found on MediaPlaybackCluster"));
// call Subscribe on currentStateAttribute while passing in success/failure callbacks
currentStateAttribute->Subscribe(
nullptr,
[](void * context,
chip::Optional<chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo::DecodableArgType> before,
chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo::DecodableArgType after) {
if (before.HasValue())
{
ChipLogProgress(AppServer, "Read CurrentState value: %d [Before reading value: %d]", static_cast<int>(after),
static_cast<int>(before.Value()));
}
else
{
ChipLogProgress(AppServer, "Read CurrentState value: %d", static_cast<int>(after));
}
},
[](void * context, CHIP_ERROR error) {
ChipLogError(AppServer, "CurrentState Read failure with err %" CHIP_ERROR_FORMAT, error.Format());
},
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)
{
ChipLogProgress(AppServer, "simple-app-helper.cpp::ConnectionHandler()");
// 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() == kDesiredEndpointVendorId;
});
if (it != endpoints.end())
{
// The desired endpoint is endpoints[index]
unsigned index = (unsigned int) std::distance(endpoints.begin(), it);
// demonstrate invoking a command
InvokeContentLauncherLaunchURL(endpoints[index]);
// demonstrate reading an attribute
ReadApplicationBasicVendorID(endpoints[index]);
// demonstrate subscribing to an attribute
SubscribeToMediaPlaybackCurrentState(endpoints[index]);
}
else
{
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;
}
}
#if defined(ENABLE_CHIP_SHELL)
void RegisterCommands()
{
static const chip::Shell::shell_command_t sDeviceComand = { &CommandHandler, "cast",
"Casting commands. Usage: cast [command_name]" };
// Register the root `device` command with the top-level shell.
chip::Shell::Engine::Root().RegisterCommands(&sDeviceComand, 1);
}
CHIP_ERROR CommandHandler(int argc, char ** argv)
{
if (argc == 0 || strcmp(argv[0], "help") == 0)
{
return PrintAllCommands();
}
if (strcmp(argv[0], "discover") == 0)
{
ChipLogProgress(AppServer, "CommandHandler() discover");
return matter::casting::core::CastingPlayerDiscovery::GetInstance()->StartDiscovery(kTargetPlayerDeviceType);
}
if (strcmp(argv[0], "stop-discovery") == 0)
{
ChipLogProgress(AppServer, "CommandHandler() stop-discovery");
return matter::casting::core::CastingPlayerDiscovery::GetInstance()->StopDiscovery();
}
if (strcmp(argv[0], "request") == 0)
{
ChipLogProgress(AppServer, "CommandHandler() request");
if (argc < 2)
{
return PrintAllCommands();
}
char * eptr;
unsigned long index = static_cast<unsigned long>(strtol(argv[1], &eptr, 10));
std::vector<matter::casting::memory::Strong<matter::casting::core::CastingPlayer>> castingPlayers =
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));
targetCastingPlayer = castingPlayers.at(index);
matter::casting::core::IdentificationDeclarationOptions idOptions;
if (argc == 3)
{
if (strcmp(argv[2], "commissioner-generated-passcode") == 0)
{
// 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 commissioner-generated-passcode. Attempting the "
"Commissioner-Generated Passcode commissioning flow",
index);
idOptions.mCommissionerPasscode = true;
}
else
{
ChipLogError(AppServer,
"CommandHandler() request %lu commissioner-generated-passcode. Selected CastingPLayer does not "
"support the Commissioner-Generated Passcode commissioning flow",
index);
}
}
}
chip::Protocols::UserDirectedCommissioning::TargetAppInfo targetAppInfo;
targetAppInfo.vendorId = kDesiredEndpointVendorId;
CHIP_ERROR result = idOptions.addTargetAppInfo(targetAppInfo);
if (result != CHIP_NO_ERROR)
{
ChipLogError(AppServer, "CommandHandler() request, failed to add targetAppInfo: %" CHIP_ERROR_FORMAT, result.Format());
}
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();
return CHIP_NO_ERROR;
}
if (strcmp(argv[0], "print-fabrics") == 0)
{
PrintFabrics();
return CHIP_NO_ERROR;
}
if (strcmp(argv[0], "delete-fabric") == 0)
{
char * eptr;
chip::FabricIndex fabricIndex = (chip::FabricIndex) strtol(argv[1], &eptr, 10);
chip::Server::GetInstance().GetFabricTable().Delete(fabricIndex);
return CHIP_NO_ERROR;
}
return CHIP_ERROR_INVALID_ARGUMENT;
}
CHIP_ERROR PrintAllCommands()
{
chip::Shell::streamer_t * sout = chip::Shell::streamer_get();
streamer_printf(sout, " help Usage: cast <subcommand>\r\n");
streamer_printf(sout, " print-bindings Usage: cast print-bindings\r\n");
streamer_printf(sout, " print-fabrics Usage: cast print-fabrics\r\n");
streamer_printf(
sout,
" delete-fabric <index> Delete a fabric from the casting client's fabric store. Usage: cast delete-fabric 1\r\n");
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");
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,
" 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;
}
void PrintBindings()
{
for (const auto & binding : chip::BindingTable::GetInstance())
{
ChipLogProgress(AppServer,
"Binding type=%d fab=%d nodeId=0x" ChipLogFormatX64
" groupId=%d local endpoint=%d remote endpoint=%d cluster=" ChipLogFormatMEI,
binding.type, binding.fabricIndex, ChipLogValueX64(binding.nodeId), binding.groupId, binding.local,
binding.remote, ChipLogValueMEI(binding.clusterId.value_or(0)));
}
}
void PrintFabrics()
{
// set fabric to be the first in the list
for (const auto & fb : chip::Server::GetInstance().GetFabricTable())
{
chip::FabricIndex fabricIndex = fb.GetFabricIndex();
ChipLogError(AppServer, "Next Fabric index=%d", fabricIndex);
if (!fb.IsInitialized())
{
ChipLogError(AppServer, " -- Not initialized");
continue;
}
chip::NodeId myNodeId = fb.GetNodeId();
ChipLogProgress(AppServer,
"---- Current Fabric nodeId=0x" ChipLogFormatX64 " fabricId=0x" ChipLogFormatX64 " fabricIndex=%d",
ChipLogValueX64(myNodeId), ChipLogValueX64(fb.GetFabricId()), fabricIndex);
}
}
#endif // ENABLE_CHIP_SHELL