From 9a2af192db89267e8a1ba6480116b1e095132cc6 Mon Sep 17 00:00:00 2001 From: Sharad Binjola <31142146+sharadb-amazon@users.noreply.github.com> Date: Thu, 28 Apr 2022 16:50:01 -0700 Subject: [PATCH] Moved TargetEndpointInfo and TargetVideoPlayerInfo out of tv-casting-app/linux/main.cpp (#17826) Refactored tv-casting-app/linux/main.cpp: Created CastingServer, renamed Casting->CastingUtils --- examples/tv-casting-app/linux/BUILD.gn | 6 +- .../linux/CastingShellCommands.cpp | 13 +- .../tv-casting-app/linux/CastingUtils.cpp | 112 ++++ .../linux/{Casting.h => CastingUtils.h} | 26 +- examples/tv-casting-app/linux/main.cpp | 551 +----------------- .../tv-casting-app/tv-casting-common/BUILD.gn | 20 + .../tv-casting-common/include/CastingServer.h | 75 +++ .../include/TargetEndpointInfo.h | 42 ++ .../include/TargetVideoPlayerInfo.h | 70 +++ .../tv-casting-common/src/CastingServer.cpp | 203 +++++++ .../src/TargetEndpointInfo.cpp | 70 +++ .../src/TargetVideoPlayerInfo.cpp | 144 +++++ 12 files changed, 788 insertions(+), 544 deletions(-) create mode 100644 examples/tv-casting-app/linux/CastingUtils.cpp rename examples/tv-casting-app/linux/{Casting.h => CastingUtils.h} (59%) create mode 100644 examples/tv-casting-app/tv-casting-common/include/CastingServer.h create mode 100644 examples/tv-casting-app/tv-casting-common/include/TargetEndpointInfo.h create mode 100644 examples/tv-casting-app/tv-casting-common/include/TargetVideoPlayerInfo.h create mode 100644 examples/tv-casting-app/tv-casting-common/src/CastingServer.cpp create mode 100644 examples/tv-casting-app/tv-casting-common/src/TargetEndpointInfo.cpp create mode 100644 examples/tv-casting-app/tv-casting-common/src/TargetVideoPlayerInfo.cpp diff --git a/examples/tv-casting-app/linux/BUILD.gn b/examples/tv-casting-app/linux/BUILD.gn index a48f121ab0109a..9f6bdb831c5a65 100644 --- a/examples/tv-casting-app/linux/BUILD.gn +++ b/examples/tv-casting-app/linux/BUILD.gn @@ -23,9 +23,10 @@ assert(chip_build_tools) executable("chip-tv-casting-app") { sources = [ "${chip_root}/examples/tv-casting-app/tv-casting-common/include/CHIPProjectAppConfig.h", - "Casting.h", "CastingShellCommands.cpp", "CastingShellCommands.h", + "CastingUtils.cpp", + "CastingUtils.h", "main.cpp", ] @@ -37,6 +38,9 @@ executable("chip-tv-casting-app") { "${chip_root}/third_party/inipp", ] + include_dirs = + [ "${chip_root}/examples/tv-casting-app/tv-casting-common/include" ] + cflags = [ "-Wconversion" ] if (chip_build_libshell) { diff --git a/examples/tv-casting-app/linux/CastingShellCommands.cpp b/examples/tv-casting-app/linux/CastingShellCommands.cpp index 502f5e580361ff..b051c4be7a0339 100644 --- a/examples/tv-casting-app/linux/CastingShellCommands.cpp +++ b/examples/tv-casting-app/linux/CastingShellCommands.cpp @@ -20,7 +20,8 @@ */ #include "CastingShellCommands.h" -#include "Casting.h" +#include "CastingServer.h" +#include "CastingUtils.h" #include #include #include @@ -67,7 +68,7 @@ static CHIP_ERROR CastingHandler(int argc, char ** argv) char * eptr; chip::NodeId nodeId = (chip::NodeId) strtoull(argv[1], &eptr, 10); chip::FabricIndex fabricIndex = (chip::FabricIndex) strtol(argv[2], &eptr, 10); - return TargetVideoPlayerInfoInit(nodeId, fabricIndex); + return CastingServer::GetInstance()->TargetVideoPlayerInfoInit(nodeId, fabricIndex); } if (strcmp(argv[0], "discover") == 0) { @@ -95,7 +96,7 @@ static CHIP_ERROR CastingHandler(int argc, char ** argv) } char * url = argv[1]; char * display = argv[2]; - return ContentLauncherLaunchURL(url, display); + return CastingServer::GetInstance()->ContentLauncherLaunchURL(url, display); } if (strcmp(argv[0], "access") == 0) { @@ -106,7 +107,7 @@ static CHIP_ERROR CastingHandler(int argc, char ** argv) } char * eptr; chip::NodeId node = (chip::NodeId) strtoull(argv[1], &eptr, 0); - ReadServerClustersForNode(node); + CastingServer::GetInstance()->ReadServerClustersForNode(node); return CHIP_NO_ERROR; } #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT @@ -116,7 +117,9 @@ static CHIP_ERROR CastingHandler(int argc, char ** argv) chip::Inet::IPAddress commissioner; chip::Inet::IPAddress::FromString(argv[1], commissioner); uint16_t port = (uint16_t) strtol(argv[2], &eptr, 10); - return SendUDC(chip::Transport::PeerAddress::UDP(commissioner, port)); + PrepareForCommissioning(); + return CastingServer::GetInstance()->SendUserDirectedCommissioningRequest( + chip::Transport::PeerAddress::UDP(commissioner, port)); } #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT return CHIP_ERROR_INVALID_ARGUMENT; diff --git a/examples/tv-casting-app/linux/CastingUtils.cpp b/examples/tv-casting-app/linux/CastingUtils.cpp new file mode 100644 index 00000000000000..61fafc850ba0eb --- /dev/null +++ b/examples/tv-casting-app/linux/CastingUtils.cpp @@ -0,0 +1,112 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CastingUtils.h" + +CHIP_ERROR DiscoverCommissioners() +{ + // Send discover commissioners request + ReturnErrorOnFailure(CastingServer::GetInstance()->DiscoverCommissioners()); + + // Give commissioners some time to respond and then ScheduleWork to initiate commissioning + return DeviceLayer::SystemLayer().StartTimer( + chip::System::Clock::Milliseconds32(kCommissionerDiscoveryTimeoutInMs), + [](System::Layer *, void *) { chip::DeviceLayer::PlatformMgr().ScheduleWork(InitCommissioningFlow); }, nullptr); +} + +CHIP_ERROR RequestCommissioning(int index) +{ + const Dnssd::DiscoveredNodeData * selectedCommissioner = CastingServer::GetInstance()->GetDiscoveredCommissioner(index); + if (selectedCommissioner == nullptr) + { + ChipLogError(AppServer, "No such commissioner with index %d exists", index); + return CHIP_ERROR_INVALID_ARGUMENT; + } + PrepareForCommissioning(selectedCommissioner); + return CHIP_NO_ERROR; +} + +/** + * Enters commissioning mode, opens commissioning window, logs onboarding payload. + * If non-null selectedCommissioner is provided, sends user directed commissioning + * request to the selectedCommissioner and advertises self as commissionable node over DNS-SD + */ +void PrepareForCommissioning(const Dnssd::DiscoveredNodeData * selectedCommissioner) +{ + CastingServer::GetInstance()->InitServer(); + + CastingServer::GetInstance()->OpenBasicCommissioningWindow(); + + // Display onboarding payload + chip::DeviceLayer::ConfigurationMgr().LogDeviceConfig(); + +#if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT + if (selectedCommissioner != nullptr) + { + // Send User Directed commissioning request + // Wait 1 second to allow our commissionee DNS records to publish (needed on Mac) + int32_t expiration = 1; + ReturnOnFailure(DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds32(expiration), HandleUDCSendExpiration, + (void *) selectedCommissioner)); + } + else + { + ChipLogProgress(AppServer, "To run discovery again, enter: cast discover"); + } +#endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT +} + +void InitCommissioningFlow(intptr_t commandArg) +{ + int commissionerCount = 0; + + // Display discovered commissioner TVs to ask user to select one + for (int i = 0; i < CHIP_DEVICE_CONFIG_MAX_DISCOVERED_NODES; i++) + { + const Dnssd::DiscoveredNodeData * commissioner = CastingServer::GetInstance()->GetDiscoveredCommissioner(i); + if (commissioner != nullptr) + { + ChipLogProgress(AppServer, "Discovered Commissioner #%d", commissionerCount++); + commissioner->LogDetail(); + } + } + + if (commissionerCount > 0) + { + ChipLogProgress(AppServer, "%d commissioner(s) discovered. Select one (by number# above) to request commissioning from: ", + commissionerCount); + + ChipLogProgress(AppServer, "Example: cast request 0"); + } + else + { + ChipLogError(AppServer, "No commissioner discovered, commissioning must be initiated manually!"); + PrepareForCommissioning(); + } +} + +#if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT +void HandleUDCSendExpiration(System::Layer * aSystemLayer, void * context) +{ + Dnssd::DiscoveredNodeData * selectedCommissioner = (Dnssd::DiscoveredNodeData *) context; + + // Send User Directed commissioning request + ReturnOnFailure(CastingServer::GetInstance()->SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress::UDP( + selectedCommissioner->ipAddress[0], selectedCommissioner->port, selectedCommissioner->interfaceId))); +} +#endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT diff --git a/examples/tv-casting-app/linux/Casting.h b/examples/tv-casting-app/linux/CastingUtils.h similarity index 59% rename from examples/tv-casting-app/linux/Casting.h rename to examples/tv-casting-app/linux/CastingUtils.h index 3808727f437e8e..2491a1c0762631 100644 --- a/examples/tv-casting-app/linux/Casting.h +++ b/examples/tv-casting-app/linux/CastingUtils.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2022 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,18 +16,26 @@ * limitations under the License. */ +/** + * @brief CastingUtils carries several utility functions that can be used + * with Linux implementations of the TV Casting app. + */ #pragma once -#include -#include -#include -#include +#include "CastingServer.h" +#include "TargetEndpointInfo.h" +#include "TargetVideoPlayerInfo.h" + +constexpr uint32_t kCommissionerDiscoveryTimeoutInMs = 5 * 1000; -CHIP_ERROR TargetVideoPlayerInfoInit(chip::NodeId nodeId, chip::FabricIndex fabricIndex); CHIP_ERROR DiscoverCommissioners(); + CHIP_ERROR RequestCommissioning(int index); -void ReadServerClustersForNode(chip::NodeId nodeId); -CHIP_ERROR ContentLauncherLaunchURL(const char * contentUrl, const char * contentDisplayStr); + +void PrepareForCommissioning(const Dnssd::DiscoveredNodeData * selectedCommissioner = nullptr); + +void InitCommissioningFlow(intptr_t commandArg); + #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT -CHIP_ERROR SendUDC(chip::Transport::PeerAddress commissioner); +void HandleUDCSendExpiration(System::Layer * aSystemLayer, void * context); #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT diff --git a/examples/tv-casting-app/linux/main.cpp b/examples/tv-casting-app/linux/main.cpp index 14225b04e6ca48..683bb2c77026d8 100644 --- a/examples/tv-casting-app/linux/main.cpp +++ b/examples/tv-casting-app/linux/main.cpp @@ -16,42 +16,23 @@ * limitations under the License. */ +#include "CastingServer.h" +#include "CastingUtils.h" +#if defined(ENABLE_CHIP_SHELL) +#include "CastingShellCommands.h" +#include +#include +#endif + #include "LinuxCommissionableDataProvider.h" #include "Options.h" -#include "app/clusters/bindings/BindingManager.h" -#include -#include -#include -#include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include - -#if defined(ENABLE_CHIP_SHELL) -#include "CastingShellCommands.h" -#include -#include -#endif - -#include -#include using namespace chip; -using namespace chip::Controller; -using namespace chip::Credentials; using chip::ArgParser::HelpOptions; using chip::ArgParser::OptionDef; using chip::ArgParser::OptionSet; @@ -60,30 +41,24 @@ using namespace chip::app::Clusters::ContentLauncher::Commands; #if defined(ENABLE_CHIP_SHELL) using chip::Shell::Engine; #endif - struct TVExampleDeviceType { const char * name; uint16_t id; }; -constexpr TVExampleDeviceType kKnownDeviceTypes[] = { { "video-player", 35 }, { "dimmable-light", 257 } }; -constexpr int kKnownDeviceTypesCount = sizeof kKnownDeviceTypes / sizeof *kKnownDeviceTypes; -constexpr uint16_t kOptionDeviceType = 't'; -constexpr System::Clock::Seconds16 kCommissioningWindowTimeout = System::Clock::Seconds16(3 * 60); -constexpr uint32_t kCommissionerDiscoveryTimeoutInMs = 5 * 1000; +Dnssd::DiscoveryFilter gDiscoveryFilter = Dnssd::DiscoveryFilter(); +constexpr TVExampleDeviceType kKnownDeviceTypes[] = { { "video-player", 35 }, { "dimmable-light", 257 } }; +constexpr int kKnownDeviceTypesCount = sizeof kKnownDeviceTypes / sizeof *kKnownDeviceTypes; +constexpr uint16_t kOptionDeviceType = 't'; // TODO: Accept these values over CLI -const char * kContentUrl = "https://www.test.com/videoid"; -const char * kContentDisplayStr = "Test video"; -constexpr EndpointId kTvEndpoint = 1; +const char * kContentUrl = "https://www.test.com/videoid"; +const char * kContentDisplayStr = "Test video"; CommissionableNodeController gCommissionableNodeController; chip::System::SocketWatchToken gToken; -Dnssd::DiscoveryFilter gDiscoveryFilter = Dnssd::DiscoveryFilter(); -bool gInited = false; - -CASEClientPool gCASEClientPool; +bool gInited = false; bool HandleOptions(const char * aProgram, OptionSet * aOptions, int aIdentifier, const char * aName, const char * aValue) { @@ -131,504 +106,22 @@ HelpOptions helpOptions("tv-casting-app", "Usage: tv-casting-app [options]", "1. OptionSet * allOptions[] = { &cmdLineOptions, &helpOptions, nullptr }; -void ReadServerClusters(EndpointId endpointId); -CHIP_ERROR InitBindingHandlers() -{ - auto & server = chip::Server::GetInstance(); - chip::BindingManager::GetInstance().Init( - { &server.GetFabricTable(), server.GetCASESessionManager(), &server.GetPersistentStorage() }); - return CHIP_NO_ERROR; -} - -#if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT -void HandleUDCSendExpiration(System::Layer * aSystemLayer, void * context) -{ - Dnssd::DiscoveredNodeData * selectedCommissioner = (Dnssd::DiscoveredNodeData *) context; - - // Send User Directed commissioning request - ReturnOnFailure(Server::GetInstance().SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress::UDP( - selectedCommissioner->ipAddress[0], selectedCommissioner->port, selectedCommissioner->interfaceId))); -} -#endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT - -void InitServer() -{ - if (gInited) - { - return; - } - // DeviceLayer::PersistedStorage::KeyValueStoreMgrImpl().Init("/tmp/chip_tv_casting_kvs"); - DeviceLayer::PersistedStorage::KeyValueStoreMgrImpl().Init(CHIP_CONFIG_KVS_PATH); - - // Enter commissioning mode, open commissioning window - static chip::CommonCaseDeviceServerInitParams initParams; - (void) initParams.InitializeStaticResourcesBeforeServerInit(); - chip::Server::GetInstance().Init(initParams); - - // Initialize binding handlers - ReturnOnFailure(InitBindingHandlers()); - - gInited = true; -} - -/** - * Enters commissioning mode, opens commissioning window, logs onboarding payload. - * If non-null selectedCommissioner is provided, sends user directed commissioning - * request to the selectedCommissioner and advertises self as commissionable node over DNS-SD - */ -void PrepareForCommissioning(const Dnssd::DiscoveredNodeData * selectedCommissioner = nullptr) -{ - InitServer(); - - Server::GetInstance().GetFabricTable().DeleteAllFabrics(); - ReturnOnFailure( - Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow(kCommissioningWindowTimeout)); - - // Display onboarding payload - chip::DeviceLayer::ConfigurationMgr().LogDeviceConfig(); - -#if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT - if (selectedCommissioner != nullptr) - { - // Send User Directed commissioning request - // Wait 1 second to allow our commissionee DNS records to publish (needed on Mac) - int32_t expiration = 1; - ReturnOnFailure(DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds32(expiration), HandleUDCSendExpiration, - (void *) selectedCommissioner)); - } - else - { - ChipLogProgress(AppServer, "To run discovery again, enter: cast discover"); - } -#endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT -} - -#if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT -CHIP_ERROR SendUDC(chip::Transport::PeerAddress commissioner) -{ - PrepareForCommissioning(); - return Server::GetInstance().SendUserDirectedCommissioningRequest(commissioner); -} -#endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT - -void InitCommissioningFlow(intptr_t commandArg) -{ - int commissionerCount = 0; - - // Display discovered commissioner TVs to ask user to select one - for (int i = 0; i < CHIP_DEVICE_CONFIG_MAX_DISCOVERED_NODES; i++) - { - const Dnssd::DiscoveredNodeData * commissioner = gCommissionableNodeController.GetDiscoveredCommissioner(i); - if (commissioner != nullptr) - { - ChipLogProgress(AppServer, "Discovered Commissioner #%d", commissionerCount++); - commissioner->LogDetail(); - } - } - - if (commissionerCount > 0) - { - ChipLogProgress(AppServer, "%d commissioner(s) discovered. Select one (by number# above) to request commissioning from: ", - commissionerCount); - - ChipLogProgress(AppServer, "Example: cast request 0"); - } - else - { - ChipLogError(AppServer, "No commissioner discovered, commissioning must be initiated manually!"); - PrepareForCommissioning(); - } -} - -CHIP_ERROR DiscoverCommissioners() -{ - // Send discover commissioners request - ReturnErrorOnFailure(gCommissionableNodeController.DiscoverCommissioners(gDiscoveryFilter)); - - // Give commissioners some time to respond and then ScheduleWork to initiate commissioning - return DeviceLayer::SystemLayer().StartTimer( - chip::System::Clock::Milliseconds32(kCommissionerDiscoveryTimeoutInMs), - [](System::Layer *, void *) { chip::DeviceLayer::PlatformMgr().ScheduleWork(InitCommissioningFlow); }, nullptr); -} - -CHIP_ERROR RequestCommissioning(int index) -{ - const Dnssd::DiscoveredNodeData * selectedCommissioner = gCommissionableNodeController.GetDiscoveredCommissioner(index); - if (selectedCommissioner == nullptr) - { - ChipLogError(AppServer, "No such commissioner with index %d exists", index); - return CHIP_ERROR_INVALID_ARGUMENT; - } - PrepareForCommissioning(selectedCommissioner); - return CHIP_NO_ERROR; -} - -void OnContentLauncherSuccessResponse(void * context, const LaunchResponse::DecodableType & response) -{ - ChipLogProgress(AppServer, "ContentLauncher: Default Success Response"); -} - -void OnContentLauncherFailureResponse(void * context, CHIP_ERROR error) -{ - ChipLogError(AppServer, "ContentLauncher: Default Failure Response: %" CHIP_ERROR_FORMAT, error.Format()); -} - -class TargetEndpointInfo -{ -public: - void Initialize(EndpointId endpointId) - { - mEndpointId = endpointId; - for (size_t i = 0; i < kMaxNumberOfClustersPerEndpoint; i++) - { - mClusters[i] = kInvalidClusterId; - } - mInitialized = true; - } - - void Reset() { mInitialized = false; } - - bool HasCluster(ClusterId clusterId) - { - for (size_t i = 0; i < kMaxNumberOfClustersPerEndpoint; i++) - { - if (mClusters[i] == clusterId) - { - return true; - } - } - return false; - } - - bool AddCluster(ClusterId clusterId) - { - for (size_t i = 0; i < kMaxNumberOfClustersPerEndpoint; i++) - { - if (mClusters[i] == clusterId) - { - return true; - } - if (mClusters[i] == kInvalidClusterId) - { - mClusters[i] = clusterId; - return true; - } - } - return false; - } - - bool IsInitialized() { return mInitialized; } - - EndpointId GetEndpointId() const { return mEndpointId; } - - void PrintInfo() - { - ChipLogProgress(NotSpecified, " endpoint=%d", mEndpointId); - for (size_t i = 0; i < kMaxNumberOfClustersPerEndpoint; i++) - { - if (mClusters[i] != kInvalidClusterId) - { - - ChipLogProgress(NotSpecified, " cluster=" ChipLogFormatMEI, ChipLogValueMEI(mClusters[i])); - } - } - } - -private: - static constexpr size_t kMaxNumberOfClustersPerEndpoint = 10; - ClusterId mClusters[kMaxNumberOfClustersPerEndpoint] = {}; - EndpointId mEndpointId; - bool mInitialized = false; -}; - -class TargetVideoPlayerInfo -{ -public: - TargetVideoPlayerInfo() : - mOnConnectedCallback(HandleDeviceConnected, this), mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this) - {} - - bool IsInitialized() { return mInitialized; } - - CHIP_ERROR Initialize(NodeId nodeId, FabricIndex fabricIndex) - { - ChipLogProgress(NotSpecified, "TargetVideoPlayerInfo nodeId=0x" ChipLogFormatX64 " fabricIndex=%d", ChipLogValueX64(nodeId), - fabricIndex); - mNodeId = nodeId; - mFabricIndex = fabricIndex; - for (auto & endpointInfo : mEndpoints) - { - endpointInfo.Reset(); - } - - Server * server = &(chip::Server::GetInstance()); - chip::FabricInfo * fabric = server->GetFabricTable().FindFabricWithIndex(fabricIndex); - if (fabric == nullptr) - { - ChipLogError(AppServer, "Did not find fabric for index %d", fabricIndex); - return CHIP_ERROR_INVALID_FABRIC_ID; - } - - chip::DeviceProxyInitParams initParams = { - .sessionManager = &(server->GetSecureSessionManager()), - .sessionResumptionStorage = server->GetSessionResumptionStorage(), - .exchangeMgr = &(server->GetExchangeManager()), - .fabricTable = &(server->GetFabricTable()), - .clientPool = &gCASEClientPool, - }; - - PeerId peerID = fabric->GetPeerIdForNode(nodeId); - - // - // TODO: The code here is assuming that we can create an OperationalDeviceProxy instance and attach it immediately - // to a CASE session that just got established to us by the tv-app. While this will work most of the time, - // this is a dangerous assumption to make since it is entirely possible for that secure session to have been - // evicted in the time since that session was established to the point here when we desire to interact back - // with that peer. If that is the case, our `OnConnected` callback will not get invoked syncronously and - // mOperationalDeviceProxy will still have a value of null, triggering the check below to fail. - // - mOperationalDeviceProxy = nullptr; - CHIP_ERROR err = - server->GetCASESessionManager()->FindOrEstablishSession(peerID, &mOnConnectedCallback, &mOnConnectionFailureCallback); - if (err != CHIP_NO_ERROR) - { - ChipLogError(AppServer, "Could not establish a session to the peer"); - return err; - } - - if (mOperationalDeviceProxy == nullptr) - { - ChipLogError(AppServer, "Failed to find an existing instance of OperationalDeviceProxy to the peer"); - return CHIP_ERROR_INVALID_ARGUMENT; - } - ChipLogProgress(AppServer, "Created an instance of OperationalDeviceProxy"); - - mInitialized = true; - return CHIP_NO_ERROR; - } - - NodeId GetNodeId() const { return mNodeId; } - FabricIndex GetFabricIndex() const { return mFabricIndex; } - OperationalDeviceProxy * GetOperationalDeviceProxy() const { return mOperationalDeviceProxy; } - - TargetEndpointInfo * GetOrAddEndpoint(EndpointId endpointId) - { - if (!mInitialized) - { - return nullptr; - } - TargetEndpointInfo * endpoint = GetEndpoint(endpointId); - if (endpoint != nullptr) - { - return endpoint; - } - for (auto & endpointInfo : mEndpoints) - { - if (!endpointInfo.IsInitialized()) - { - endpointInfo.Initialize(endpointId); - return &endpointInfo; - } - } - return nullptr; - } - - TargetEndpointInfo * GetEndpoint(EndpointId endpointId) - { - if (!mInitialized) - { - return nullptr; - } - for (auto & endpointInfo : mEndpoints) - { - if (endpointInfo.IsInitialized() && endpointInfo.GetEndpointId() == endpointId) - { - return &endpointInfo; - } - } - return nullptr; - } - - bool HasEndpoint(EndpointId endpointId) - { - if (!mInitialized) - { - return false; - } - for (auto & endpointInfo : mEndpoints) - { - if (endpointInfo.IsInitialized() && endpointInfo.GetEndpointId() == endpointId) - { - return true; - } - } - return false; - } - - void PrintInfo() - { - ChipLogProgress(NotSpecified, " TargetVideoPlayerInfo nodeId=0x" ChipLogFormatX64 " fabric index=%d", - ChipLogValueX64(mNodeId), mFabricIndex); - for (auto & endpointInfo : mEndpoints) - { - if (endpointInfo.IsInitialized()) - { - endpointInfo.PrintInfo(); - } - } - } - -private: - static void HandleDeviceConnected(void * context, OperationalDeviceProxy * device) - { - TargetVideoPlayerInfo * _this = static_cast(context); - _this->mOperationalDeviceProxy = device; - _this->mInitialized = true; - ChipLogProgress(AppServer, "HandleDeviceConnected created an instance of OperationalDeviceProxy"); - } - - static void HandleDeviceConnectionFailure(void * context, PeerId peerId, CHIP_ERROR error) - { - TargetVideoPlayerInfo * _this = static_cast(context); - _this->mOperationalDeviceProxy = nullptr; - } - - static constexpr size_t kMaxNumberOfEndpoints = 5; - TargetEndpointInfo mEndpoints[kMaxNumberOfEndpoints]; - NodeId mNodeId; - FabricIndex mFabricIndex; - OperationalDeviceProxy * mOperationalDeviceProxy; - - Callback::Callback mOnConnectedCallback; - Callback::Callback mOnConnectionFailureCallback; - - bool mInitialized = false; -}; -TargetVideoPlayerInfo gTargetVideoPlayerInfo; - -void OnDescriptorReadSuccessResponse(void * context, const app::DataModel::DecodableList & responseList) -{ - TargetEndpointInfo * endpointInfo = static_cast(context); - - ChipLogProgress(AppServer, "Descriptor: Default Success Response endpoint=%d", endpointInfo->GetEndpointId()); - - auto iter = responseList.begin(); - while (iter.Next()) - { - auto & clusterId = iter.GetValue(); - endpointInfo->AddCluster(clusterId); - } - // Always print the target info after handling descriptor read response - // Even when we get nothing back for any reasons - gTargetVideoPlayerInfo.PrintInfo(); -} - -void OnDescriptorReadFailureResponse(void * context, CHIP_ERROR error) -{ - ChipLogError(AppServer, "Descriptor: Default Failure Response: %" CHIP_ERROR_FORMAT, error.Format()); -} - -void ReadServerClusters(EndpointId endpointId) -{ - OperationalDeviceProxy * operationalDeviceProxy = gTargetVideoPlayerInfo.GetOperationalDeviceProxy(); - if (operationalDeviceProxy == nullptr) - { - ChipLogError(AppServer, "Failed in getting an instance of OperationalDeviceProxy"); - return; - } - - chip::Controller::DescriptorCluster cluster; - CHIP_ERROR err = cluster.Associate(operationalDeviceProxy, endpointId); - if (err != CHIP_NO_ERROR) - { - ChipLogError(AppServer, "Associate() failed: %" CHIP_ERROR_FORMAT, err.Format()); - return; - } - - TargetEndpointInfo * endpointInfo = gTargetVideoPlayerInfo.GetOrAddEndpoint(endpointId); - - if (cluster.ReadAttribute( - endpointInfo, OnDescriptorReadSuccessResponse, OnDescriptorReadFailureResponse) != CHIP_NO_ERROR) - { - ChipLogError(Controller, "Could not read Descriptor cluster ServerList"); - } - - ChipLogProgress(Controller, "Sent descriptor read for remote endpoint=%d", endpointId); -} - -void ReadServerClustersForNode(NodeId nodeId) -{ - ChipLogProgress(NotSpecified, "ReadServerClustersForNode nodeId=0x" ChipLogFormatX64, ChipLogValueX64(nodeId)); - for (const auto & binding : BindingTable::GetInstance()) - { - ChipLogProgress(NotSpecified, - "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.ValueOr(0))); - if (binding.type == EMBER_UNICAST_BINDING && nodeId == binding.nodeId) - { - if (!gTargetVideoPlayerInfo.HasEndpoint(binding.remote)) - { - ReadServerClusters(binding.remote); - } - else - { - TargetEndpointInfo * endpointInfo = gTargetVideoPlayerInfo.GetEndpoint(binding.remote); - if (endpointInfo != nullptr && endpointInfo->IsInitialized()) - { - endpointInfo->PrintInfo(); - } - } - } - } -} - -CHIP_ERROR ContentLauncherLaunchURL(const char * contentUrl, const char * contentDisplayStr) -{ - OperationalDeviceProxy * operationalDeviceProxy = gTargetVideoPlayerInfo.GetOperationalDeviceProxy(); - if (operationalDeviceProxy == nullptr) - { - ChipLogError(AppServer, "Failed in getting an instance of OperationalDeviceProxy"); - return CHIP_ERROR_PEER_NODE_NOT_FOUND; - } - - ContentLauncherCluster cluster; - CHIP_ERROR err = cluster.Associate(operationalDeviceProxy, kTvEndpoint); - if (err != CHIP_NO_ERROR) - { - ChipLogError(AppServer, "Associate() failed: %" CHIP_ERROR_FORMAT, err.Format()); - return err; - } - LaunchURL::Type request; - request.contentURL = chip::CharSpan::fromCharString(contentUrl); - request.displayString = Optional(chip::CharSpan::fromCharString(contentDisplayStr)); - request.brandingInformation = MakeOptional(chip::app::Clusters::ContentLauncher::Structs::BrandingInformation::Type()); - cluster.InvokeCommand(request, nullptr, OnContentLauncherSuccessResponse, OnContentLauncherFailureResponse); - return CHIP_NO_ERROR; -} - -CHIP_ERROR TargetVideoPlayerInfoInit(NodeId nodeId, FabricIndex fabricIndex) -{ - InitServer(); - return gTargetVideoPlayerInfo.Initialize(nodeId, fabricIndex); -} - void DeviceEventCallback(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg) { if (event->Type == DeviceLayer::DeviceEventType::kBindingsChangedViaCluster) { - if (gTargetVideoPlayerInfo.IsInitialized()) + if (CastingServer::GetInstance()->GetTargetVideoPlayerInfo()->IsInitialized()) { - ReadServerClustersForNode(gTargetVideoPlayerInfo.GetNodeId()); + CastingServer::GetInstance()->ReadServerClustersForNode( + CastingServer::GetInstance()->GetTargetVideoPlayerInfo()->GetNodeId()); } } else if (event->Type == DeviceLayer::DeviceEventType::kCommissioningComplete) { - ReturnOnFailure(gTargetVideoPlayerInfo.Initialize(event->CommissioningComplete.PeerNodeId, - event->CommissioningComplete.PeerFabricIndex)); + ReturnOnFailure(CastingServer::GetInstance()->GetTargetVideoPlayerInfo()->Initialize( + event->CommissioningComplete.PeerNodeId, event->CommissioningComplete.PeerFabricIndex)); - ContentLauncherLaunchURL(kContentUrl, kContentDisplayStr); + CastingServer::GetInstance()->ContentLauncherLaunchURL(kContentUrl, kContentDisplayStr); } } @@ -710,7 +203,7 @@ int main(int argc, char * argv[]) } // Send discover commissioners request - SuccessOrExit(err = gCommissionableNodeController.DiscoverCommissioners(gDiscoveryFilter)); + SuccessOrExit(err = CastingServer::GetInstance()->DiscoverCommissioners()); // Give commissioners some time to respond and then ScheduleWork to initiate commissioning DeviceLayer::SystemLayer().StartTimer( diff --git a/examples/tv-casting-app/tv-casting-common/BUILD.gn b/examples/tv-casting-app/tv-casting-common/BUILD.gn index 7b88b5a710e901..28faac10bf99fc 100644 --- a/examples/tv-casting-app/tv-casting-common/BUILD.gn +++ b/examples/tv-casting-app/tv-casting-common/BUILD.gn @@ -21,5 +21,25 @@ chip_data_model("tv-casting-common") { zap_pregenerated_dir = "${chip_root}/zzz_generated/tv-casting-app/zap-generated" + + sources = [ + "include/CastingServer.h", + "include/TargetEndpointInfo.h", + "include/TargetVideoPlayerInfo.h", + "src/CastingServer.cpp", + "src/TargetEndpointInfo.cpp", + "src/TargetVideoPlayerInfo.cpp", + ] + + deps = [ + "${chip_root}/examples/platform/linux:app-main", + "${chip_root}/src/credentials:default_attestation_verifier", + "${chip_root}/src/lib", + "${chip_root}/third_party/inipp", + ] + + include_dirs = + [ "${chip_root}/examples/tv-casting-app/tv-casting-common/include" ] + is_server = true } diff --git a/examples/tv-casting-app/tv-casting-common/include/CastingServer.h b/examples/tv-casting-app/tv-casting-common/include/CastingServer.h new file mode 100644 index 00000000000000..e046818be34868 --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/include/CastingServer.h @@ -0,0 +1,75 @@ +/* + * + * Copyright (c) 2022 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 +#include +#include + +#include "TargetEndpointInfo.h" +#include "TargetVideoPlayerInfo.h" + +using namespace chip; +using namespace chip::Controller; +using namespace chip::Credentials; +using namespace chip::app::Clusters::ContentLauncher::Commands; + +constexpr System::Clock::Seconds16 kCommissioningWindowTimeout = System::Clock::Seconds16(3 * 60); +constexpr EndpointId kTvEndpoint = 1; + +/** + * @brief Represents a TV Casting server that can get the casting app commissioned + * and then have it send TV Casting/Media related commands. This is to be instantiated + * as a singleton and is to be used across Linux, Android and iOS. + */ +class CastingServer +{ +public: + CastingServer(CastingServer & other) = delete; + void operator=(const CastingServer &) = delete; + static CastingServer * GetInstance(); + + void InitServer(); + + CHIP_ERROR DiscoverCommissioners(); + const Dnssd::DiscoveredNodeData * GetDiscoveredCommissioner(int index); + CHIP_ERROR OpenBasicCommissioningWindow(); +#if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT + CHIP_ERROR SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress commissioner); +#endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT + + CHIP_ERROR InitBindingHandlers(); + TargetVideoPlayerInfo * GetTargetVideoPlayerInfo() { return &mTargetVideoPlayerInfo; } + CHIP_ERROR TargetVideoPlayerInfoInit(chip::NodeId nodeId, chip::FabricIndex fabricIndex); + void ReadServerClusters(EndpointId endpointId); + void ReadServerClustersForNode(chip::NodeId nodeId); + static void OnDescriptorReadSuccessResponse(void * context, const app::DataModel::DecodableList & responseList); + static void OnDescriptorReadFailureResponse(void * context, CHIP_ERROR error); + CHIP_ERROR ContentLauncherLaunchURL(const char * contentUrl, const char * contentDisplayStr); + static void OnContentLauncherSuccessResponse(void * context, const LaunchResponse::DecodableType & response); + static void OnContentLauncherFailureResponse(void * context, CHIP_ERROR error); + +private: + static CastingServer * castingServer_; + CastingServer() {} + + bool mInited = false; + TargetVideoPlayerInfo mTargetVideoPlayerInfo; + CommissionableNodeController mCommissionableNodeController; +}; diff --git a/examples/tv-casting-app/tv-casting-common/include/TargetEndpointInfo.h b/examples/tv-casting-app/tv-casting-common/include/TargetEndpointInfo.h new file mode 100644 index 00000000000000..b9845cbeff574d --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/include/TargetEndpointInfo.h @@ -0,0 +1,42 @@ +/* + * + * Copyright (c) 2022 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 + +using namespace chip; + +class TargetEndpointInfo +{ +public: + void Initialize(EndpointId endpointId); + void Reset() { mInitialized = false; } + bool IsInitialized() { return mInitialized; } + EndpointId GetEndpointId() const { return mEndpointId; } + + bool HasCluster(ClusterId clusterId); + bool AddCluster(ClusterId clusterId); + void PrintInfo(); + +private: + static constexpr size_t kMaxNumberOfClustersPerEndpoint = 10; + ClusterId mClusters[kMaxNumberOfClustersPerEndpoint] = {}; + EndpointId mEndpointId; + bool mInitialized = false; +}; diff --git a/examples/tv-casting-app/tv-casting-common/include/TargetVideoPlayerInfo.h b/examples/tv-casting-app/tv-casting-common/include/TargetVideoPlayerInfo.h new file mode 100644 index 00000000000000..5774a3e2261d07 --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/include/TargetVideoPlayerInfo.h @@ -0,0 +1,70 @@ +/* + * + * Copyright (c) 2022 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 "TargetEndpointInfo.h" +#include "app/clusters/bindings/BindingManager.h" +#include + +using namespace chip; + +class TargetVideoPlayerInfo +{ +public: + TargetVideoPlayerInfo() : + mOnConnectedCallback(HandleDeviceConnected, this), mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this) + {} + + bool IsInitialized() { return mInitialized; } + NodeId GetNodeId() const { return mNodeId; } + FabricIndex GetFabricIndex() const { return mFabricIndex; } + OperationalDeviceProxy * GetOperationalDeviceProxy() const { return mOperationalDeviceProxy; } + + CHIP_ERROR Initialize(NodeId nodeId, FabricIndex fabricIndex); + TargetEndpointInfo * GetOrAddEndpoint(EndpointId endpointId); + TargetEndpointInfo * GetEndpoint(EndpointId endpointId); + bool HasEndpoint(EndpointId endpointId); + void PrintInfo(); + +private: + static void HandleDeviceConnected(void * context, OperationalDeviceProxy * device) + { + TargetVideoPlayerInfo * _this = static_cast(context); + _this->mOperationalDeviceProxy = device; + _this->mInitialized = true; + ChipLogProgress(AppServer, "HandleDeviceConnected created an instance of OperationalDeviceProxy"); + } + + static void HandleDeviceConnectionFailure(void * context, PeerId peerId, CHIP_ERROR error) + { + TargetVideoPlayerInfo * _this = static_cast(context); + _this->mOperationalDeviceProxy = nullptr; + } + + static constexpr size_t kMaxNumberOfEndpoints = 5; + TargetEndpointInfo mEndpoints[kMaxNumberOfEndpoints]; + NodeId mNodeId; + FabricIndex mFabricIndex; + OperationalDeviceProxy * mOperationalDeviceProxy; + + Callback::Callback mOnConnectedCallback; + Callback::Callback mOnConnectionFailureCallback; + + bool mInitialized = false; +}; diff --git a/examples/tv-casting-app/tv-casting-common/src/CastingServer.cpp b/examples/tv-casting-app/tv-casting-common/src/CastingServer.cpp new file mode 100644 index 00000000000000..6555550999f1ab --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/src/CastingServer.cpp @@ -0,0 +1,203 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CastingServer.h" + +CastingServer * CastingServer::castingServer_ = nullptr; +; + +CastingServer * CastingServer::GetInstance() +{ + if (castingServer_ == nullptr) + { + castingServer_ = new CastingServer(); + } + return castingServer_; +} + +void CastingServer::InitServer() +{ + if (mInited) + { + return; + } + // DeviceLayer::PersistedStorage::KeyValueStoreMgrImpl().Init("/tmp/chip_tv_casting_kvs"); + DeviceLayer::PersistedStorage::KeyValueStoreMgrImpl().Init(CHIP_CONFIG_KVS_PATH); + + // Enter commissioning mode, open commissioning window + static chip::CommonCaseDeviceServerInitParams initParams; + (void) initParams.InitializeStaticResourcesBeforeServerInit(); + chip::Server::GetInstance().Init(initParams); + + // Initialize binding handlers + ReturnOnFailure(InitBindingHandlers()); + + mInited = true; +} + +CHIP_ERROR CastingServer::InitBindingHandlers() +{ + auto & server = chip::Server::GetInstance(); + chip::BindingManager::GetInstance().Init( + { &server.GetFabricTable(), server.GetCASESessionManager(), &server.GetPersistentStorage() }); + return CHIP_NO_ERROR; +} + +CHIP_ERROR CastingServer::TargetVideoPlayerInfoInit(NodeId nodeId, FabricIndex fabricIndex) +{ + InitServer(); + return mTargetVideoPlayerInfo.Initialize(nodeId, fabricIndex); +} + +CHIP_ERROR CastingServer::DiscoverCommissioners() +{ + // Send discover commissioners request + return mCommissionableNodeController.DiscoverCommissioners(Dnssd::DiscoveryFilter()); +} + +CHIP_ERROR CastingServer::OpenBasicCommissioningWindow() +{ + Server::GetInstance().GetFabricTable().DeleteAllFabrics(); + return Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow(kCommissioningWindowTimeout); +} + +#if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT +CHIP_ERROR CastingServer::SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress commissioner) +{ + return Server::GetInstance().SendUserDirectedCommissioningRequest(commissioner); +} +#endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT + +const Dnssd::DiscoveredNodeData * CastingServer::GetDiscoveredCommissioner(int index) +{ + return mCommissionableNodeController.GetDiscoveredCommissioner(index); +} + +void CastingServer::ReadServerClustersForNode(NodeId nodeId) +{ + ChipLogProgress(NotSpecified, "ReadServerClustersForNode nodeId=0x" ChipLogFormatX64, ChipLogValueX64(nodeId)); + for (const auto & binding : BindingTable::GetInstance()) + { + ChipLogProgress(NotSpecified, + "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.ValueOr(0))); + if (binding.type == EMBER_UNICAST_BINDING && nodeId == binding.nodeId) + { + if (!mTargetVideoPlayerInfo.HasEndpoint(binding.remote)) + { + ReadServerClusters(binding.remote); + } + else + { + TargetEndpointInfo * endpointInfo = mTargetVideoPlayerInfo.GetEndpoint(binding.remote); + if (endpointInfo != nullptr && endpointInfo->IsInitialized()) + { + endpointInfo->PrintInfo(); + } + } + } + } +} + +void CastingServer::ReadServerClusters(EndpointId endpointId) +{ + OperationalDeviceProxy * operationalDeviceProxy = mTargetVideoPlayerInfo.GetOperationalDeviceProxy(); + if (operationalDeviceProxy == nullptr) + { + ChipLogError(AppServer, "Failed in getting an instance of OperationalDeviceProxy"); + return; + } + + chip::Controller::DescriptorCluster cluster; + CHIP_ERROR err = cluster.Associate(operationalDeviceProxy, endpointId); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Associate() failed: %" CHIP_ERROR_FORMAT, err.Format()); + return; + } + + TargetEndpointInfo * endpointInfo = mTargetVideoPlayerInfo.GetOrAddEndpoint(endpointId); + + if (cluster.ReadAttribute( + endpointInfo, CastingServer::OnDescriptorReadSuccessResponse, CastingServer::OnDescriptorReadFailureResponse) != + CHIP_NO_ERROR) + { + ChipLogError(Controller, "Could not read Descriptor cluster ServerList"); + } + + ChipLogProgress(Controller, "Sent descriptor read for remote endpoint=%d", endpointId); +} + +void CastingServer::OnDescriptorReadSuccessResponse(void * context, const app::DataModel::DecodableList & responseList) +{ + TargetEndpointInfo * endpointInfo = static_cast(context); + + ChipLogProgress(AppServer, "Descriptor: Default Success Response endpoint=%d", endpointInfo->GetEndpointId()); + + auto iter = responseList.begin(); + while (iter.Next()) + { + auto & clusterId = iter.GetValue(); + endpointInfo->AddCluster(clusterId); + } + // Always print the target info after handling descriptor read response + // Even when we get nothing back for any reasons + CastingServer::GetInstance()->mTargetVideoPlayerInfo.PrintInfo(); +} + +void CastingServer::OnDescriptorReadFailureResponse(void * context, CHIP_ERROR error) +{ + ChipLogError(AppServer, "Descriptor: Default Failure Response: %" CHIP_ERROR_FORMAT, error.Format()); +} + +CHIP_ERROR CastingServer::ContentLauncherLaunchURL(const char * contentUrl, const char * contentDisplayStr) +{ + OperationalDeviceProxy * operationalDeviceProxy = mTargetVideoPlayerInfo.GetOperationalDeviceProxy(); + if (operationalDeviceProxy == nullptr) + { + ChipLogError(AppServer, "Failed in getting an instance of OperationalDeviceProxy"); + return CHIP_ERROR_PEER_NODE_NOT_FOUND; + } + + ContentLauncherCluster cluster; + CHIP_ERROR err = cluster.Associate(operationalDeviceProxy, kTvEndpoint); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Associate() failed: %" CHIP_ERROR_FORMAT, err.Format()); + return err; + } + LaunchURL::Type request; + request.contentURL = chip::CharSpan::fromCharString(contentUrl); + request.displayString = Optional(chip::CharSpan::fromCharString(contentDisplayStr)); + request.brandingInformation = MakeOptional(chip::app::Clusters::ContentLauncher::Structs::BrandingInformation::Type()); + cluster.InvokeCommand(request, nullptr, CastingServer::OnContentLauncherSuccessResponse, + CastingServer::OnContentLauncherFailureResponse); + return CHIP_NO_ERROR; +} + +void CastingServer::OnContentLauncherSuccessResponse(void * context, const LaunchResponse::DecodableType & response) +{ + ChipLogProgress(AppServer, "ContentLauncher: Default Success Response"); +} + +void CastingServer::OnContentLauncherFailureResponse(void * context, CHIP_ERROR error) +{ + ChipLogError(AppServer, "ContentLauncher: Default Failure Response: %" CHIP_ERROR_FORMAT, error.Format()); +} diff --git a/examples/tv-casting-app/tv-casting-common/src/TargetEndpointInfo.cpp b/examples/tv-casting-app/tv-casting-common/src/TargetEndpointInfo.cpp new file mode 100644 index 00000000000000..1b809dc23f3958 --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/src/TargetEndpointInfo.cpp @@ -0,0 +1,70 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TargetEndpointInfo.h" + +void TargetEndpointInfo::Initialize(EndpointId endpointId) +{ + mEndpointId = endpointId; + for (size_t i = 0; i < kMaxNumberOfClustersPerEndpoint; i++) + { + mClusters[i] = kInvalidClusterId; + } + mInitialized = true; +} + +bool TargetEndpointInfo::HasCluster(ClusterId clusterId) +{ + for (size_t i = 0; i < kMaxNumberOfClustersPerEndpoint; i++) + { + if (mClusters[i] == clusterId) + { + return true; + } + } + return false; +} + +bool TargetEndpointInfo::AddCluster(ClusterId clusterId) +{ + for (size_t i = 0; i < kMaxNumberOfClustersPerEndpoint; i++) + { + if (mClusters[i] == clusterId) + { + return true; + } + if (mClusters[i] == kInvalidClusterId) + { + mClusters[i] = clusterId; + return true; + } + } + return false; +} + +void TargetEndpointInfo::PrintInfo() +{ + ChipLogProgress(NotSpecified, " endpoint=%d", mEndpointId); + for (size_t i = 0; i < kMaxNumberOfClustersPerEndpoint; i++) + { + if (mClusters[i] != kInvalidClusterId) + { + + ChipLogProgress(NotSpecified, " cluster=" ChipLogFormatMEI, ChipLogValueMEI(mClusters[i])); + } + } +} diff --git a/examples/tv-casting-app/tv-casting-common/src/TargetVideoPlayerInfo.cpp b/examples/tv-casting-app/tv-casting-common/src/TargetVideoPlayerInfo.cpp new file mode 100644 index 00000000000000..1e41faa0ccd93d --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/src/TargetVideoPlayerInfo.cpp @@ -0,0 +1,144 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TargetVideoPlayerInfo.h" + +CASEClientPool gCASEClientPool; + +CHIP_ERROR TargetVideoPlayerInfo::Initialize(NodeId nodeId, FabricIndex fabricIndex) +{ + ChipLogProgress(NotSpecified, "TargetVideoPlayerInfo nodeId=0x" ChipLogFormatX64 " fabricIndex=%d", ChipLogValueX64(nodeId), + fabricIndex); + mNodeId = nodeId; + mFabricIndex = fabricIndex; + for (auto & endpointInfo : mEndpoints) + { + endpointInfo.Reset(); + } + + Server * server = &(chip::Server::GetInstance()); + chip::FabricInfo * fabric = server->GetFabricTable().FindFabricWithIndex(fabricIndex); + if (fabric == nullptr) + { + ChipLogError(AppServer, "Did not find fabric for index %d", fabricIndex); + return CHIP_ERROR_INVALID_FABRIC_ID; + } + + chip::DeviceProxyInitParams initParams = { + .sessionManager = &(server->GetSecureSessionManager()), + .sessionResumptionStorage = server->GetSessionResumptionStorage(), + .exchangeMgr = &(server->GetExchangeManager()), + .fabricTable = &(server->GetFabricTable()), + .clientPool = &gCASEClientPool, + }; + + PeerId peerID = fabric->GetPeerIdForNode(nodeId); + + // + // TODO: The code here is assuming that we can create an OperationalDeviceProxy instance and attach it immediately + // to a CASE session that just got established to us by the tv-app. While this will work most of the time, + // this is a dangerous assumption to make since it is entirely possible for that secure session to have been + // evicted in the time since that session was established to the point here when we desire to interact back + // with that peer. If that is the case, our `OnConnected` callback will not get invoked syncronously and + // mOperationalDeviceProxy will still have a value of null, triggering the check below to fail. + // + mOperationalDeviceProxy = nullptr; + CHIP_ERROR err = + server->GetCASESessionManager()->FindOrEstablishSession(peerID, &mOnConnectedCallback, &mOnConnectionFailureCallback); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Could not establish a session to the peer"); + return err; + } + + if (mOperationalDeviceProxy == nullptr) + { + ChipLogError(AppServer, "Failed to find an existing instance of OperationalDeviceProxy to the peer"); + return CHIP_ERROR_INVALID_ARGUMENT; + } + ChipLogProgress(AppServer, "Created an instance of OperationalDeviceProxy"); + + mInitialized = true; + return CHIP_NO_ERROR; +} + +TargetEndpointInfo * TargetVideoPlayerInfo::GetOrAddEndpoint(EndpointId endpointId) +{ + if (!mInitialized) + { + return nullptr; + } + TargetEndpointInfo * endpoint = GetEndpoint(endpointId); + if (endpoint != nullptr) + { + return endpoint; + } + for (auto & endpointInfo : mEndpoints) + { + if (!endpointInfo.IsInitialized()) + { + endpointInfo.Initialize(endpointId); + return &endpointInfo; + } + } + return nullptr; +} + +TargetEndpointInfo * TargetVideoPlayerInfo::GetEndpoint(EndpointId endpointId) +{ + if (!mInitialized) + { + return nullptr; + } + for (auto & endpointInfo : mEndpoints) + { + if (endpointInfo.IsInitialized() && endpointInfo.GetEndpointId() == endpointId) + { + return &endpointInfo; + } + } + return nullptr; +} + +bool TargetVideoPlayerInfo::HasEndpoint(EndpointId endpointId) +{ + if (!mInitialized) + { + return false; + } + for (auto & endpointInfo : mEndpoints) + { + if (endpointInfo.IsInitialized() && endpointInfo.GetEndpointId() == endpointId) + { + return true; + } + } + return false; +} + +void TargetVideoPlayerInfo::PrintInfo() +{ + ChipLogProgress(NotSpecified, " TargetVideoPlayerInfo nodeId=0x" ChipLogFormatX64 " fabric index=%d", ChipLogValueX64(mNodeId), + mFabricIndex); + for (auto & endpointInfo : mEndpoints) + { + if (endpointInfo.IsInitialized()) + { + endpointInfo.PrintInfo(); + } + } +}