blob: 4fcb1ea5ca60da5cb346bf1649ff729173c00670 [file] [log] [blame]
/*
*
* Copyright (c) 2023 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 "CommissionerDeclarationHandler.h"
#include "ConnectionCallbacks.h"
#include "Endpoint.h"
#include "IdentificationDeclarationOptions.h"
#include "Types.h"
#include "support/ChipDeviceEventHandler.h"
#include "support/EndpointListLoader.h"
#include "lib/support/logging/CHIPLogging.h"
#include <inet/IPAddress.h>
#include <inet/InetInterface.h>
#include <string.h>
#include <vector>
namespace matter {
namespace casting {
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 uint16_t kCommissioningWindowTimeoutSec = 3 * 60; // 3 minutes
/**
* @brief Describes an Endpoint that the client wants to connect to
*/
struct EndpointFilter
{
// value of 0 means unspecified
uint16_t vendorId = 0;
uint16_t productId = 0;
std::vector<chip::app::Clusters::Descriptor::Structs::DeviceTypeStruct::DecodableType> requiredDeviceTypes;
};
class CastingPlayerAttributes
{
public:
char id[kIdMaxLength + 1] = {};
char deviceName[chip::Dnssd::kMaxDeviceNameLen + 1] = {};
char hostName[chip::Dnssd::kHostNameMaxLength + 1] = {};
char instanceName[chip::Dnssd::Commission::kInstanceNameMaxLength + 1] = {};
unsigned int numIPs; // Number of valid IP addresses
chip::Inet::IPAddress ipAddresses[chip::Dnssd::CommonResolutionData::kMaxIPAddresses];
chip::Inet::InterfaceId interfaceId;
uint16_t port;
uint16_t productId;
uint16_t vendorId;
uint32_t deviceType;
bool supportsCommissionerGeneratedPasscode;
chip::NodeId nodeId = 0;
chip::FabricIndex fabricIndex = 0;
};
class Endpoint;
/**
* @brief Represents CastingPlayer ConnectionState.
*
*/
enum ConnectionState
{
CASTING_PLAYER_NOT_CONNECTED,
CASTING_PLAYER_CONNECTING,
CASTING_PLAYER_CONNECTED,
};
class ConnectionContext;
class CastingPlayer;
/**
* @brief CastingPlayer represents a Matter Commissioner that is able to play media to a physical
* output or to a display screen which is part of the device.
*/
class CastingPlayer : public std::enable_shared_from_this<CastingPlayer>
{
public:
CastingPlayer(CastingPlayerAttributes playerAttributes) { mAttributes = playerAttributes; }
/**
* @brief Get the CastingPlayer object targeted currently (may not be connected)
*/
static CastingPlayer * GetTargetCastingPlayer()
{
ChipLogProgress(AppServer, "CastingPlayer::GetTargetCastingPlayer() called");
std::shared_ptr<CastingPlayer> sharedPtr = mTargetCastingPlayer.lock();
CastingPlayer * rawPtr = nullptr;
if (sharedPtr)
{
rawPtr = sharedPtr.get();
ChipLogProgress(
AppServer,
"CastingPlayer::GetTargetCastingPlayer() Got rawPtr from mTargetCastingPlayer, sharedPtr reference count: %lu",
sharedPtr.use_count());
sharedPtr.reset();
}
else
{
ChipLogError(AppServer,
"CastingPlayer::GetTargetCastingPlayer() The shared pointer observed by mTargetCastingPlayer has expired "
"(become nullptr)");
}
return rawPtr;
}
/**
* @brief Compares based on the Id
*/
bool operator==(const CastingPlayer & other) const
{
int compareResult = strcmp(this->mAttributes.id, other.mAttributes.id);
return (compareResult == 0) ? 1 : 0;
}
/**
* @brief Define the copy constructor
*/
CastingPlayer(const CastingPlayer & other);
/**
* @brief Define the assignment operator
*/
CastingPlayer & operator=(const CastingPlayer & other);
/**
* @return true if this CastingPlayer is connected to the CastingApp
*/
bool IsConnected() const { return mConnectionState == CASTING_PLAYER_CONNECTED; }
/**
* @brief Verifies that a connection exists with this CastingPlayer, or triggers a new commissioning session request. If the
* CastingApp does not have the nodeId and fabricIndex of this CastingPlayer cached on disk, this will execute the User Directed
* Commissioning (UDC) process by sending an IdentificationDeclaration message to the CastingPlayer/Commissioner. For certain
* UDC features, where a Commissioner reply is expected, this API needs to be followed up with the ContinueConnecting() API
* defiend below. See the Matter UDC specification or parameter class definitions for details on features not included in the
* description below.
*
* @param connectionCallbacks contains the ConnectCallback (Required) and CommissionerDeclarationCallback (Optional) defiend in
* ConnectCallbacks.h.
*
* For example: During CastingPlayer/Commissioner-Generated passcode commissioning, the Commissioner replies with a
* CommissionerDeclaration message with PasscodeDialogDisplayed and CommissionerPasscode set to true. Given these Commissioner
* state details, the client is expected to perform some actions, detailed in the ContinueConnecting() API below, and then call
* the ContinueConnecting() API to complete the process.
*
* @param commissioningWindowTimeoutSec (Optional) time (in sec) to keep the commissioning window open, if commissioning is
* required. Needs to be >= kCommissioningWindowTimeoutSec.
*
* @param idOptions (Optional) Parameters in the IdentificationDeclaration message sent by the Commissionee to the Commissioner.
* These parameters specify the information relating to the requested commissioning session.
*
* For example: To invoke the CastingPlayer/Commissioner-Generated passcode commissioning flow, the client would call this API
* with IdentificationDeclarationOptions containing CommissionerPasscode set to true. See IdentificationDeclarationOptions.h for
* a complete list of optional parameters.
*
* Furthermore, attributes (such as VendorId) describe the TargetApp that the client wants to interact with after commissioning.
* If this value is passed in, VerifyOrEstablishConnection() will force UDC, in case the desired
* TargetApp is not found in the on-device CastingStore.
*/
void VerifyOrEstablishConnection(ConnectionCallbacks connectionCallbacks,
uint16_t commissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec,
IdentificationDeclarationOptions idOptions = IdentificationDeclarationOptions());
/**
* @brief This is a continuation of the CastingPlayer/Commissioner-Generated passcode commissioning flow started via the
* VerifyOrEstablishConnection() API above. It continues the UDC process by sending a second IdentificationDeclaration message
* to Commissioner containing CommissionerPasscode and CommissionerPasscodeReady set to true. At this point it is assumed that
* the following have occurred:
*
* 1. Client (Commissionee) has sent the first IdentificationDeclaration message, via VerifyOrEstablishConnection(), to the
* Commissioner containing CommissionerPasscode set to true.
* 2. Commissioner generated and displayed a passcode.
* 3. The Commissioner replied with a CommissionerDecelration message with PasscodeDialogDisplayed and CommissionerPasscode set
* to true.
* 4. Client has handled the Commissioner's CommissionerDecelration message.
* 5. Client prompted user to input Passcode from Commissioner.
* 6. Client has updated the commissioning session's PAKE verifier using the user input Passcode by updating the CastingApp's
* CommissionableDataProvider
* (matter::casting::core::CastingApp::GetInstance()->UpdateCommissionableDataProvider(CommissionableDataProvider)).
*
* The same connectionCallbacks and commissioningWindowTimeoutSec parameters passed into VerifyOrEstablishConnection() will be
* used.
* @return CHIP_NO_ERROR if this function was called with the CastingPlayer in the correct state and an error otherwise.
*/
CHIP_ERROR ContinueConnecting();
/**
* @brief This cancels the CastingPlayer/Commissioner-Generated passcode commissioning flow started via the
* VerifyOrEstablishConnection() API above. It constructs and sends an IdentificationDeclaration message to the
* CastingPlayer/Commissioner containing CancelPasscode set to true. It is used to indicate that the user, and thus the
* Client/Commissionee, have cancelled the commissioning process. This indicates that the CastingPlayer/Commissioner can dismiss
* any dialogs corresponding to commissioning, such as a Passcode input dialog or a Passcode display dialog.
* Note: stopConnecting() does not call the ConnectCallback() callback passed to the VerifyOrEstablishConnection() API above
* since no connection is established.
* @return CHIP_NO_ERROR if this function was called with the CastingPlayer in the correct state and an error otherwise.
*/
CHIP_ERROR StopConnecting();
/**
* @brief Sets the internal connection state of this CastingPlayer to "disconnected"
*/
void Disconnect();
/**
* @brief Find an existing session for this CastingPlayer, or trigger a new session
* request.
*
* The caller can optionally provide `onDeviceConnected` and `onDeviceConnectionFailure` callback
* objects. If provided, these will be used to inform the caller about
* successful or failed connection establishment.
*
* If the connection is already established, the `onDeviceConnected` callback
* will be immediately called, before FindOrEstablishSession returns.
*
* The `onDeviceConnectionFailure` callback may be called before the FindOrEstablishSession
* call returns, for error cases that are detected synchronously.
*/
void FindOrEstablishSession(void * clientContext, chip::OnDeviceConnected onDeviceConnected,
chip::OnDeviceConnectionFailure onDeviceConnectionFailure);
/**
* @brief Register an endpoint on this CastingPlayer. If the provided endpoint was already registered, its information will be
* updated in the registry.
*/
void RegisterEndpoint(const memory::Strong<Endpoint> endpoint);
const std::vector<memory::Strong<Endpoint>> GetEndpoints() const { return mEndpoints; }
void LogDetail() const;
const char * GetId() const { return mAttributes.id; }
const char * GetDeviceName() const { return mAttributes.deviceName; }
const char * GetHostName() const { return mAttributes.hostName; }
const char * GetInstanceName() const { return mAttributes.instanceName; }
uint GetNumIPs() const { return mAttributes.numIPs; }
chip::Inet::IPAddress * GetIPAddresses() { return mAttributes.ipAddresses; }
uint16_t GetPort() { return mAttributes.port; }
uint16_t GetProductId() const { return mAttributes.productId; }
uint16_t GetVendorId() const { return mAttributes.vendorId; }
uint32_t GetDeviceType() const { return mAttributes.deviceType; }
bool GetSupportsCommissionerGeneratedPasscode() const { return mAttributes.supportsCommissionerGeneratedPasscode; }
chip::NodeId GetNodeId() const { return mAttributes.nodeId; }
chip::FabricIndex GetFabricIndex() const { return mAttributes.fabricIndex; }
void SetNodeId(chip::NodeId nodeId) { mAttributes.nodeId = nodeId; }
void SetFabricIndex(chip::FabricIndex fabricIndex) { mAttributes.fabricIndex = fabricIndex; }
/**
* @brief Return the current state of the CastingPlayer
*/
ConnectionState GetConnectionState() const { return mConnectionState; }
private:
std::vector<memory::Strong<Endpoint>> mEndpoints;
ConnectionState mConnectionState = CASTING_PLAYER_NOT_CONNECTED;
CastingPlayerAttributes mAttributes;
IdentificationDeclarationOptions mIdOptions;
// This is a std::weak_ptr. A std::weak_ptr is a non-owning reference to an object managed by one
// or more std::shared_ptr instances. When the last std::shared_ptr instance that owns the managed
// object is destroyed or reset, the object itself is automatically destroyed, and all
// std::weak_ptr instances that reference that object become expired.
static memory::Weak<CastingPlayer> mTargetCastingPlayer;
uint16_t mCommissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec;
ConnectCallback mOnCompleted = {};
/**
* @brief resets this CastingPlayer's state and calls mOnCompleted with the CHIP_ERROR. Also, after calling mOnCompleted, it
* clears mOnCompleted by setting it to a nullptr.
*/
void resetState(CHIP_ERROR err);
#if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT
/**
* @brief Sends the user directed commissioning request to this CastingPlayer
*/
CHIP_ERROR SendUserDirectedCommissioningRequest();
/**
* @brief Selects an IP Address to send the UDC request to.
* Prioritizes IPV4 addresses over IPV6.
*
* @return chip::Inet::IPAddress*
*/
chip::Inet::IPAddress * GetIpAddressForUDCRequest();
#endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT
/**
* @brief Checks if the cachedCastingPlayer contains at least one Endpoint/TargetApp described in the desiredTargetApps list.
* @return true - cachedCastingPlayer contains at least one endpoints with matching (non-default) values for vendorID and
* productID as described in the desiredTargetApps list, false otherwise.
*/
bool ContainsDesiredTargetApp(core::CastingPlayer * cachedCastingPlayer,
std::vector<chip::Protocols::UserDirectedCommissioning::TargetAppInfo> desiredTargetApps);
// ChipDeviceEventHandler handles chip::DeviceLayer::ChipDeviceEvent events and helps the CastingPlayer class commission with
// and connect to a CastingPlayer
friend class support::ChipDeviceEventHandler;
friend class ConnectionContext;
friend class support::EndpointListLoader;
};
class ConnectionContext
{
public:
ConnectionContext(void * clientContext, core::CastingPlayer * targetCastingPlayer, chip::OnDeviceConnected onDeviceConnected,
chip::OnDeviceConnectionFailure onDeviceConnectionFailure);
~ConnectionContext();
void * mClientContext = nullptr;
core::CastingPlayer * mTargetCastingPlayer = nullptr;
chip::OnDeviceConnected mOnDeviceConnectedFn = nullptr;
chip::OnDeviceConnectionFailure mOnDeviceConnectionFailureFn = nullptr;
chip::Callback::Callback<chip::OnDeviceConnected> * mOnConnectedCallback = nullptr;
chip::Callback::Callback<chip::OnDeviceConnectionFailure> * mOnConnectionFailureCallback = nullptr;
};
}; // namespace core
}; // namespace casting
}; // namespace matter